@raydiant/playlist-window-manager 0.29.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@ export default class IFrameRenderer implements PresentationWindowRenderer {
14
14
  sendMessageObject(windowId: string, type: string, payload?: any): void;
15
15
  muteWindow(windowId: string): void;
16
16
  unMuteWindow(windowId: string): void;
17
+ setWindowBackground(windowId: string, color: string): void;
17
18
  destroy(): void;
18
19
  private getWindow;
19
20
  private globalMessageHandler;
@@ -36,6 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
36
36
  }
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ var surfaces_1 = require("./surfaces");
39
40
  var namespace = 'raydiant';
40
41
  var IFrameRenderer = /** @class */ (function () {
41
42
  function IFrameRenderer(container) {
@@ -55,6 +56,12 @@ var IFrameRenderer = /** @class */ (function () {
55
56
  if (!window_1) {
56
57
  throw new Error("Failed add receive message callback to window, window with window id: '".concat(windowId, "' was not found"));
57
58
  }
59
+ // A surface costs a hardware decoder, so only the window's own frame may
60
+ // ask for one; any frame can post here and name any window.
61
+ if (surfaces_1.VIDEO_REQUESTS.indexOf(type) !== -1 &&
62
+ event.source !== window_1.frame.contentWindow) {
63
+ return;
64
+ }
58
65
  if (window_1.receiveMessageCallback) {
59
66
  window_1.receiveMessageCallback(type, payload);
60
67
  }
@@ -218,6 +225,13 @@ var IFrameRenderer = /** @class */ (function () {
218
225
  // There is no mute/unmute API for iframes so we rely on the window manager overriding any
219
226
  // mute presentation properties.
220
227
  };
228
+ IFrameRenderer.prototype.setWindowBackground = function (windowId, color) {
229
+ var window = this.getWindow(windowId);
230
+ if (!window) {
231
+ throw new Error("Failed to set window background, missing window id '".concat(windowId, "'"));
232
+ }
233
+ window.frame.style.backgroundColor = color;
234
+ };
221
235
  IFrameRenderer.prototype.destroy = function () {
222
236
  window.removeEventListener('message', this.globalMessageHandler, false);
223
237
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ var IFrameRenderer_1 = __importDefault(require("./IFrameRenderer"));
7
+ function setup() {
8
+ var container = document.createElement('div');
9
+ document.body.appendChild(container);
10
+ var renderer = new IFrameRenderer_1.default(container);
11
+ var received = [];
12
+ renderer.createWindow({
13
+ windowId: 'w1',
14
+ backgroundColor: '#000000',
15
+ receiveMessageCallback: function (type, payload) { return received.push([type, payload]); },
16
+ errorCallback: function () { return undefined; },
17
+ });
18
+ var frame = container.querySelector('iframe');
19
+ var post = function (type, source) {
20
+ window.dispatchEvent(new MessageEvent('message', {
21
+ data: "raydiant:".concat(JSON.stringify({ windowId: 'w1', type: type })),
22
+ source: source,
23
+ }));
24
+ };
25
+ var teardown = function () {
26
+ renderer.destroy();
27
+ document.body.removeChild(container);
28
+ };
29
+ return { renderer: renderer, frame: frame, received: received, post: post, teardown: teardown };
30
+ }
31
+ describe('only the window’s own frame asks for a video surface', function () {
32
+ test.each(['open_video', 'video_bounds', 'video_hole_open', 'close_video'])("%s from another frame's source is dropped", function (type) {
33
+ var _a = setup(), frame = _a.frame, received = _a.received, post = _a.post, teardown = _a.teardown;
34
+ var other = document.createElement('iframe');
35
+ document.body.appendChild(other);
36
+ post(type, other.contentWindow);
37
+ expect(received).toEqual([]);
38
+ post(type, frame.contentWindow);
39
+ expect(received).toEqual([[type, undefined]]);
40
+ document.body.removeChild(other);
41
+ teardown();
42
+ });
43
+ test("every other message keeps today's routing by window id", function () {
44
+ var _a = setup(), received = _a.received, post = _a.post, teardown = _a.teardown;
45
+ post('presentation_ready', null);
46
+ expect(received).toEqual([['presentation_ready', undefined]]);
47
+ teardown();
48
+ });
49
+ });
50
+ describe('the frame background', function () {
51
+ test('setWindowBackground sets the frame’s inline background', function () {
52
+ var _a = setup(), renderer = _a.renderer, frame = _a.frame, teardown = _a.teardown;
53
+ expect(frame.style.backgroundColor).toBe('rgb(0, 0, 0)');
54
+ renderer.setWindowBackground('w1', 'transparent');
55
+ expect(frame.style.backgroundColor).toBe('transparent');
56
+ renderer.setWindowBackground('w1', '#000000');
57
+ expect(frame.style.backgroundColor).toBe('rgb(0, 0, 0)');
58
+ teardown();
59
+ });
60
+ });
61
+ describe('no colour scheme', function () {
62
+ test('a created frame declares no color-scheme', function () {
63
+ var _a = setup(), frame = _a.frame, renderer = _a.renderer, teardown = _a.teardown;
64
+ renderer.showWindow('w1', false);
65
+ renderer.setWindowBackground('w1', 'transparent');
66
+ expect(frame.style.getPropertyValue('color-scheme')).toBe('');
67
+ expect(frame.getAttribute('style')).not.toMatch(/color-scheme/);
68
+ teardown();
69
+ });
70
+ });
@@ -2,6 +2,7 @@ import * as RE from '@raydiant/playlist-rule-engine';
2
2
  import EventEmitter from 'eventemitter3';
3
3
  import { PresentationWindowRenderer, Device, WindowBounds, Theme, Logger, WorkerState, Charge, ChargeResponse, Terminal } from './types';
4
4
  import PlaylistWindowManagerState from './PlaylistWindowManagerState';
5
+ import { SurfaceHost } from './surfaces';
5
6
  export interface PlaylistWindowManagerOptions {
6
7
  ruleEngine: RE.RuleEngine;
7
8
  state: PlaylistWindowManagerState;
@@ -22,6 +23,7 @@ export interface PlaylistWindowManagerOptions {
22
23
  onDisableInterruptLock?: () => void;
23
24
  getTerminal?: () => Terminal | null;
24
25
  getOnlineStatus?: () => boolean;
26
+ surfaces?: SurfaceHost;
25
27
  }
26
28
  export default class PlaylistWindowManager extends EventEmitter {
27
29
  private options;
@@ -45,6 +47,8 @@ export default class PlaylistWindowManager extends EventEmitter {
45
47
  private forceMute;
46
48
  private isFocused;
47
49
  private workerState;
50
+ private surfaceBroker;
51
+ private pendingPreload;
48
52
  constructor(options: PlaylistWindowManagerOptions);
49
53
  private currentPresentationId;
50
54
  private nextPresentationId;
@@ -84,6 +88,8 @@ export default class PlaylistWindowManager extends EventEmitter {
84
88
  sendTerminal(terminal: Terminal | null): void;
85
89
  sendOnlineStatus(isOnline: boolean): void;
86
90
  private createWindow;
91
+ private showAfterRevealMs;
92
+ private surfaceSwapDelayMs;
87
93
  private loadCurrentWindow;
88
94
  private shouldPreload;
89
95
  private preloadNextWindow;
@@ -94,6 +100,7 @@ export default class PlaylistWindowManager extends EventEmitter {
94
100
  private removeCurrentWindow;
95
101
  private removeNextWindow;
96
102
  private swapWindows;
103
+ private swapWithSurfaces;
97
104
  private evalRuleEngine;
98
105
  private handleEnableInterruptLock;
99
106
  private handleDisableInterruptLock;
@@ -25,6 +25,29 @@ var __assign = (this && this.__assign) || function () {
25
25
  };
26
26
  return __assign.apply(this, arguments);
27
27
  };
28
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ var desc = Object.getOwnPropertyDescriptor(m, k);
31
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
32
+ desc = { enumerable: true, get: function() { return m[k]; } };
33
+ }
34
+ Object.defineProperty(o, k2, desc);
35
+ }) : (function(o, m, k, k2) {
36
+ if (k2 === undefined) k2 = k;
37
+ o[k2] = m[k];
38
+ }));
39
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
40
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
41
+ }) : function(o, v) {
42
+ o["default"] = v;
43
+ });
44
+ var __importStar = (this && this.__importStar) || function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
28
51
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
29
52
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
30
53
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -71,6 +94,7 @@ var PresentationWindow_1 = __importDefault(require("./PresentationWindow"));
71
94
  var WindowUnresponsiveError_1 = __importDefault(require("./WindowUnresponsiveError"));
72
95
  var WindowTimeoutError_1 = __importDefault(require("./WindowTimeoutError"));
73
96
  var WindowLoadError_1 = __importDefault(require("./WindowLoadError"));
97
+ var SurfaceBroker_1 = __importStar(require("./SurfaceBroker"));
74
98
  // Wait at least 3 seconds before restarting or moving onto the next window
75
99
  // to prevent log spam causing devices to become unresponsive.
76
100
  var minTimeBetweenCompletes = 3000;
@@ -97,6 +121,10 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
97
121
  _this.forceMute = false;
98
122
  _this.isFocused = false;
99
123
  _this.workerState = {};
124
+ _this.surfaceBroker = null;
125
+ // The surface swap the next window's preload waits to finish. A newer
126
+ // preload or the next window's removal clears it, cancelling that preload.
127
+ _this.pendingPreload = null;
100
128
  _this.handleEnableInterruptLock = function () {
101
129
  var onEnableInterruptLock = _this.options.onEnableInterruptLock;
102
130
  if (onEnableInterruptLock) {
@@ -164,6 +192,12 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
164
192
  }
165
193
  // Clear current window error flag.
166
194
  this.currentWindowError = null;
195
+ // A complete while the next window's preload waits for its surface swap
196
+ // preloads it now, and the current window restarts while it loads, as
197
+ // for any next window not yet ready.
198
+ if (this.pendingPreload && this.nextPresentation) {
199
+ this.preloadNextWindow();
200
+ }
167
201
  if (!(!this.isLocked() &&
168
202
  !this.restartCurrentWindowOnEnd &&
169
203
  this.currentWindow &&
@@ -318,6 +352,9 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
318
352
  _this.bounds = options.bounds;
319
353
  _this.themeOverride = options.themeOverride;
320
354
  _this.forceMute = options.forceMute || false;
355
+ if (options.surfaces) {
356
+ _this.surfaceBroker = new SurfaceBroker_1.default(options.surfaces, options.logger);
357
+ }
321
358
  return _this;
322
359
  }
323
360
  PlaylistWindowManager.prototype.currentPresentationId = function () {
@@ -546,7 +583,14 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
546
583
  return [4 /*yield*/, Promise.all([this.removeCurrentWindow(), this.removeNextWindow()])];
547
584
  case 1:
548
585
  _a.sent();
549
- return [2 /*return*/];
586
+ if (!this.surfaceBroker) return [3 /*break*/, 3];
587
+ // Also closes a surface whose window is still leaving a swap.
588
+ return [4 /*yield*/, this.surfaceBroker.closeAll()];
589
+ case 2:
590
+ // Also closes a surface whose window is still leaving a swap.
591
+ _a.sent();
592
+ _a.label = 3;
593
+ case 3: return [2 /*return*/];
550
594
  }
551
595
  });
552
596
  });
@@ -702,35 +746,24 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
702
746
  var presentation = this.state.getPresentation(presentationId);
703
747
  var applicationVersion = this.state.getApplicationVersion(presentation.applicationDeploymentId);
704
748
  var windowId = (0, uuid_1.v4)();
705
- return new PresentationWindow_1.default({
706
- windowManager: this,
707
- renderer: this.renderer,
708
- logger: logger,
709
- windowId: windowId,
710
- windowName: windowName,
711
- parentWindowId: parentWindowId,
712
- presentation: presentation,
713
- applicationVersion: applicationVersion,
714
- device: device,
715
- playlistId: pr.playlistId,
716
- playlistName: this.ruleEngine.state.playlist(pr.playlistId).name,
717
- rule: pr.rule,
718
- publishedAt: this.state.getPublishedAt(),
719
- theme: this.getTheme(presentationId),
720
- forceMute: this.forceMute,
721
- readyTimeoutDurationSeconds: readyTimeoutDurationSeconds,
722
- appRendererUrl: appRendererUrl,
723
- appRendererOfflineSupport: appRendererOfflineSupport,
724
- bounds: this.bounds,
725
- backgroundColor: backgroundColor,
726
- playlistWindowManagerState: this.state,
727
- ruleEngineState: this.ruleEngine.state,
728
- globalRule: this.ruleEngine.globalRule,
729
- isPreview: isPreview,
730
- workerState: this.workerState,
731
- getTerminal: this.options.getTerminal,
732
- getOnlineStatus: this.options.getOnlineStatus,
733
- });
749
+ return new PresentationWindow_1.default(__assign({ windowManager: this, renderer: this.renderer, logger: logger, windowId: windowId, windowName: windowName, parentWindowId: parentWindowId, presentation: presentation, applicationVersion: applicationVersion, device: device, playlistId: pr.playlistId, playlistName: this.ruleEngine.state.playlist(pr.playlistId).name, rule: pr.rule, publishedAt: this.state.getPublishedAt(), theme: this.getTheme(presentationId), forceMute: this.forceMute, readyTimeoutDurationSeconds: readyTimeoutDurationSeconds, appRendererUrl: appRendererUrl, appRendererOfflineSupport: appRendererOfflineSupport, bounds: this.bounds, backgroundColor: backgroundColor, playlistWindowManagerState: this.state, ruleEngineState: this.ruleEngine.state, globalRule: this.ruleEngine.globalRule, isPreview: isPreview, workerState: this.workerState, getTerminal: this.options.getTerminal, getOnlineStatus: this.options.getOnlineStatus }, (this.surfaceBroker
750
+ ? {
751
+ surfaceBroker: this.surfaceBroker,
752
+ surfaceShowDeadlineMs: this.surfaceSwapDelayMs(),
753
+ }
754
+ : {})));
755
+ };
756
+ PlaylistWindowManager.prototype.showAfterRevealMs = function () {
757
+ var surfaces = this.options.surfaces;
758
+ return surfaces && surfaces.showAfterRevealMs !== undefined
759
+ ? surfaces.showAfterRevealMs
760
+ : SurfaceBroker_1.DEFAULT_SHOW_AFTER_REVEAL_MS;
761
+ };
762
+ PlaylistWindowManager.prototype.surfaceSwapDelayMs = function () {
763
+ var closeWindowDelayMs = this.options.closeWindowDelayMs;
764
+ return closeWindowDelayMs !== undefined
765
+ ? closeWindowDelayMs
766
+ : SurfaceBroker_1.DEFAULT_SURFACE_SWAP_DELAY_MS;
734
767
  };
735
768
  PlaylistWindowManager.prototype.loadCurrentWindow = function () {
736
769
  return __awaiter(this, void 0, void 0, function () {
@@ -784,6 +817,9 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
784
817
  switch (_a.label) {
785
818
  case 0:
786
819
  logger = this.options.logger;
820
+ // A publish, a preview change or a complete preloads now, in place of a
821
+ // preload still waiting for its surface swap.
822
+ this.pendingPreload = null;
787
823
  if (!this.nextPresentation) {
788
824
  logger.warn("Attempted to preload next window but nextPresentation not set");
789
825
  return [2 /*return*/];
@@ -889,6 +925,7 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
889
925
  return __generator(this, function (_a) {
890
926
  switch (_a.label) {
891
927
  case 0:
928
+ this.pendingPreload = null;
892
929
  if (!this.nextWindow) return [3 /*break*/, 2];
893
930
  return [4 /*yield*/, this.nextWindow.remove()];
894
931
  case 1:
@@ -904,7 +941,7 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
904
941
  };
905
942
  PlaylistWindowManager.prototype.swapWindows = function () {
906
943
  return __awaiter(this, void 0, void 0, function () {
907
- var nextPreviewPresentationId, preview, result, nextPresentation, shouldPreload, closeWindowPromise;
944
+ var nextPreviewPresentationId, preview, result, nextPresentation, shouldPreload, isSurfaceSwap, closeWindowPromise, windowToClose_1, incomingWindow_1, closeWindowDelayMs_1, preloadAfterSwap;
908
945
  var _this = this;
909
946
  return __generator(this, function (_a) {
910
947
  switch (_a.label) {
@@ -935,40 +972,56 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
935
972
  }
936
973
  // Clear the pending interrupt whenever we swap the current window for the next one.
937
974
  this.hasPendingInterrupt = false;
938
- // Next window is ready, show the next one, hide the current window, and
939
- // then remove the current window. Show the next window before hiding the
940
- // current to avoid flashes when rendering with BrowserWindows on device.
941
- if (this.nextWindow) {
975
+ isSurfaceSwap = !!this.surfaceBroker &&
976
+ ((!!this.currentWindow && this.currentWindow.holdsSurface()) ||
977
+ (!!this.nextWindow && this.nextWindow.holdsSurface()));
978
+ if (isSurfaceSwap) {
979
+ closeWindowPromise = this.swapWithSurfaces(this.currentWindow, this.nextWindow);
980
+ }
981
+ else if (this.nextWindow) {
982
+ // Next window is ready, show the next one, hide the current window, and
983
+ // then remove the current window. Show the next window before hiding the
984
+ // current to avoid flashes when rendering with BrowserWindows on device.
942
985
  this.nextWindow.show(this.isFocused);
943
986
  }
944
- if (!this.currentWindow) return [3 /*break*/, 3];
987
+ if (!(!isSurfaceSwap && this.currentWindow)) return [3 /*break*/, 3];
945
988
  if (!(this.options.closeWindowDelayMs !== undefined)) return [3 /*break*/, 1];
946
- // Don't await here because this will cause a slight delay when playing the next window.
947
- // Instead we want to wait for window close at the end of this method.
948
- closeWindowPromise = new Promise(function (resolve) {
949
- // Capture the current window reference to close because this.currentWindow will be
950
- // set to the next window before the timeout fires.
951
- var windowToClose = _this.currentWindow;
952
- setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
953
- return __generator(this, function (_a) {
954
- switch (_a.label) {
955
- case 0:
956
- if (!windowToClose) return [3 /*break*/, 2];
957
- // We can't use this.removeCurrentWindow because the currentWindow reference has already
958
- // changed by the time the timeout fires.
959
- return [4 /*yield*/, windowToClose.remove()];
960
- case 1:
961
- // We can't use this.removeCurrentWindow because the currentWindow reference has already
962
- // changed by the time the timeout fires.
963
- _a.sent();
964
- _a.label = 2;
965
- case 2:
966
- resolve(undefined);
967
- return [2 /*return*/];
968
- }
969
- });
970
- }); }, _this.options.closeWindowDelayMs);
971
- });
989
+ windowToClose_1 = this.currentWindow;
990
+ incomingWindow_1 = this.nextWindow;
991
+ closeWindowDelayMs_1 = this.options.closeWindowDelayMs;
992
+ // The outgoing window stays on screen only as a visual backdrop from
993
+ // here on: stop delivering its events (a late complete or error must
994
+ // not end the incoming presentation) and mute it so audio does not
995
+ // bleed into the incoming presentation during the overlap.
996
+ windowToClose_1.detachCallbacks();
997
+ windowToClose_1.mute();
998
+ closeWindowPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
999
+ return __generator(this, function (_a) {
1000
+ switch (_a.label) {
1001
+ case 0:
1002
+ if (!incomingWindow_1) return [3 /*break*/, 2];
1003
+ return [4 /*yield*/, incomingWindow_1.waitForFirstPaint(closeWindowDelayMs_1)];
1004
+ case 1:
1005
+ _a.sent();
1006
+ return [3 /*break*/, 4];
1007
+ case 2: return [4 /*yield*/, new Promise(function (resolve) {
1008
+ return setTimeout(resolve, closeWindowDelayMs_1);
1009
+ })];
1010
+ case 3:
1011
+ _a.sent();
1012
+ _a.label = 4;
1013
+ case 4:
1014
+ // We can't use this.removeCurrentWindow because the currentWindow
1015
+ // reference has already changed by the time the wait finishes.
1016
+ return [4 /*yield*/, windowToClose_1.remove()];
1017
+ case 5:
1018
+ // We can't use this.removeCurrentWindow because the currentWindow
1019
+ // reference has already changed by the time the wait finishes.
1020
+ _a.sent();
1021
+ return [2 /*return*/];
1022
+ }
1023
+ });
1024
+ }); })();
972
1025
  return [3 /*break*/, 3];
973
1026
  case 1:
974
1027
  this.currentWindow.hide();
@@ -994,8 +1047,17 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
994
1047
  }
995
1048
  // Play new current window.
996
1049
  this.playCurrentWindow();
997
- // Preload the next presentation if there is one.
998
- if (shouldPreload) {
1050
+ preloadAfterSwap = null;
1051
+ if (shouldPreload && isSurfaceSwap) {
1052
+ // Once the swap has finished, so the next surface's decoder does not
1053
+ // start during it: on the Stick a preload started in the swap held the
1054
+ // outgoing clip's last frame for about 70 ms. The next window is the
1055
+ // current one now.
1056
+ this.nextWindow = null;
1057
+ preloadAfterSwap = closeWindowPromise;
1058
+ this.pendingPreload = preloadAfterSwap;
1059
+ }
1060
+ else if (shouldPreload) {
999
1061
  this.preloadNextWindow();
1000
1062
  }
1001
1063
  else {
@@ -1009,11 +1071,70 @@ var PlaylistWindowManager = /** @class */ (function (_super) {
1009
1071
  case 4:
1010
1072
  _a.sent();
1011
1073
  _a.label = 5;
1012
- case 5: return [2 /*return*/];
1074
+ case 5:
1075
+ if (preloadAfterSwap && this.pendingPreload === preloadAfterSwap) {
1076
+ this.preloadNextWindow();
1077
+ }
1078
+ return [2 /*return*/];
1013
1079
  }
1014
1080
  });
1015
1081
  });
1016
1082
  };
1083
+ // The swap when either window holds a video surface. The outgoing surface
1084
+ // is muted and marked leaving here, in the swap's own task; the rest runs
1085
+ // on the two windows held here, so a publish, a preview check or a preload
1086
+ // error during its waits acts on the new next window, never these two.
1087
+ PlaylistWindowManager.prototype.swapWithSurfaces = function (outgoing, incoming) {
1088
+ var _this = this;
1089
+ var delayMs = this.surfaceSwapDelayMs();
1090
+ if (outgoing) {
1091
+ // A late complete or error from the outgoing window must not end the
1092
+ // incoming presentation, and two surfaces never sound at once.
1093
+ outgoing.detachCallbacks();
1094
+ outgoing.muteSurfaceOrFrame();
1095
+ }
1096
+ return (function () { return __awaiter(_this, void 0, void 0, function () {
1097
+ var isOutgoingHidden;
1098
+ return __generator(this, function (_a) {
1099
+ switch (_a.label) {
1100
+ case 0:
1101
+ isOutgoingHidden = false;
1102
+ if (!incoming) return [3 /*break*/, 3];
1103
+ // The new is shown before the old is hidden: an incoming surface's
1104
+ // frame is revealed over its open hole and the outgoing frame hidden
1105
+ // in that same task, so neither the outgoing paint-over nor its theme
1106
+ // shows through the incoming hole.
1107
+ return [4 /*yield*/, incoming.show(this.isFocused, function () {
1108
+ if (outgoing) {
1109
+ outgoing.hide();
1110
+ isOutgoingHidden = true;
1111
+ }
1112
+ }, this.showAfterRevealMs())];
1113
+ case 1:
1114
+ // The new is shown before the old is hidden: an incoming surface's
1115
+ // frame is revealed over its open hole and the outgoing frame hidden
1116
+ // in that same task, so neither the outgoing paint-over nor its theme
1117
+ // shows through the incoming hole.
1118
+ _a.sent();
1119
+ if (!(outgoing && !incoming.wasRemoved())) return [3 /*break*/, 3];
1120
+ return [4 /*yield*/, incoming.waitForFirstPaint(delayMs)];
1121
+ case 2:
1122
+ _a.sent();
1123
+ _a.label = 3;
1124
+ case 3:
1125
+ if (!outgoing) return [3 /*break*/, 5];
1126
+ if (!isOutgoingHidden) {
1127
+ outgoing.hide();
1128
+ }
1129
+ return [4 /*yield*/, outgoing.remove()];
1130
+ case 4:
1131
+ _a.sent();
1132
+ _a.label = 5;
1133
+ case 5: return [2 /*return*/];
1134
+ }
1135
+ });
1136
+ }); })();
1137
+ };
1017
1138
  PlaylistWindowManager.prototype.evalRuleEngine = function (warnIfPreloadMismatch) {
1018
1139
  if (warnIfPreloadMismatch === void 0) { warnIfPreloadMismatch = true; }
1019
1140
  var logger = this.options.logger;
@@ -0,0 +1 @@
1
+ export {};