1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 var EXPORTED_SYMBOLS = ["DOMFullscreenParent"];
9 class DOMFullscreenParent extends JSWindowActorParent {
10 // These properties get set by browser-fullScreenAndPointerLock.js.
11 // TODO: Bug 1743703 - Consider moving the messaging component of
12 // browser-fullScreenAndPointerLock.js into the actor
13 waitingForChildEnterFullscreen = false;
14 waitingForChildExitFullscreen = false;
15 // Cache the next message recipient actor and in-process browsing context that
16 // is computed by _getNextMsgRecipientActor() of
17 // browser-fullScreenAndPointerLock.js, this is used to ensure the fullscreen
18 // cleanup messages goes the same route as fullscreen request, especially for
19 // the cleanup that happens after actor is destroyed.
20 // TODO: Bug 1743703 - Consider moving the messaging component of
21 // browser-fullScreenAndPointerLock.js into the actor
22 nextMsgRecipient = null;
24 updateFullscreenWindowReference(aWindow) {
25 if (aWindow.document.documentElement.hasAttribute("inDOMFullscreen")) {
26 this._fullscreenWindow = aWindow;
28 delete this._fullscreenWindow;
32 cleanupDomFullscreen(aWindow) {
33 if (!aWindow.FullScreen) {
37 // If we don't need to wait for child reply, i.e. cleanupDomFullscreen
38 // doesn't message to child, and we've exit the fullscreen, there won't be
39 // DOMFullscreen:Painted message from child and it is possible that no more
40 // paint would be triggered, so just notify fullscreen-painted observer.
42 !aWindow.FullScreen.cleanupDomFullscreen(this) &&
43 !aWindow.document.fullscreen
45 Services.obs.notifyObservers(aWindow, "fullscreen-painted");
50 * Clean up fullscreen state and resume chrome UI if window is in fullscreen
51 * and this actor is the one where the original fullscreen enter or
54 _cleanupFullscreenStateAndResumeChromeUI(aWindow) {
55 this.cleanupDomFullscreen(aWindow);
56 if (this.requestOrigin == this && aWindow.document.fullscreen) {
57 aWindow.windowUtils.remoteFrameFullscreenReverted();
62 this._didDestroy = true;
64 let window = this._fullscreenWindow;
66 let topBrowsingContext = this.browsingContext.top;
67 let browser = topBrowsingContext.embedderElement;
73 this.waitingForChildExitFullscreen ||
74 this.waitingForChildEnterFullscreen
76 this.waitingForChildExitFullscreen = false;
77 this.waitingForChildEnterFullscreen = false;
78 // We were destroyed while waiting for our DOMFullscreenChild to exit
79 // or enter fullscreen, run cleanup steps anyway.
80 this._cleanupFullscreenStateAndResumeChromeUI(browser.ownerGlobal);
85 if (this.waitingForChildEnterFullscreen) {
86 this.waitingForChildEnterFullscreen = false;
87 if (window.document.fullscreen) {
88 // We were destroyed while waiting for our DOMFullscreenChild
89 // to transition to fullscreen so we abort the entire
90 // fullscreen transition to prevent getting stuck in a
91 // partial fullscreen state. We need to go through the
92 // document since window.Fullscreen could be undefined
95 // This could reject if we're not currently in fullscreen
96 // so just ignore rejection.
97 window.document.exitFullscreen().catch(() => {});
100 this.cleanupDomFullscreen(window);
103 // Need to resume Chrome UI if the window is still in fullscreen UI
104 // to avoid the window stays in fullscreen problem. (See Bug 1620341)
105 if (window.document.documentElement.hasAttribute("inDOMFullscreen")) {
106 this.cleanupDomFullscreen(window);
107 if (window.windowUtils) {
108 window.windowUtils.remoteFrameFullscreenReverted();
110 } else if (this.waitingForChildExitFullscreen) {
111 this.waitingForChildExitFullscreen = false;
112 // We were destroyed while waiting for our DOMFullscreenChild to exit
113 // run cleanup steps anyway.
114 this._cleanupFullscreenStateAndResumeChromeUI(window);
116 this.updateFullscreenWindowReference(window);
119 receiveMessage(aMessage) {
120 let topBrowsingContext = this.browsingContext.top;
121 let browser = topBrowsingContext.embedderElement;
124 // No need to go further when the browser is not accessible anymore
125 // (which can happen when the tab is closed for instance),
129 let window = browser.ownerGlobal;
130 switch (aMessage.name) {
131 case "DOMFullscreen:Request": {
132 this.waitingForChildExitFullscreen = false;
133 this.requestOrigin = this;
134 this.addListeners(window);
135 window.windowUtils.remoteFrameFullscreenChanged(browser);
138 case "DOMFullscreen:NewOrigin": {
139 // Don't show the warning if we've already exited fullscreen.
140 if (window.document.fullscreen) {
141 window.PointerlockFsWarning.showFullScreen(
142 aMessage.data.originNoSuffix
145 this.updateFullscreenWindowReference(window);
148 case "DOMFullscreen:Entered": {
149 this.nextMsgRecipient = null;
150 this.waitingForChildEnterFullscreen = false;
151 window.FullScreen.enterDomFullscreen(browser, this);
152 this.updateFullscreenWindowReference(window);
155 case "DOMFullscreen:Exit": {
156 this.waitingForChildEnterFullscreen = false;
157 window.windowUtils.remoteFrameFullscreenReverted();
160 case "DOMFullscreen:Exited": {
161 this.waitingForChildExitFullscreen = false;
162 this.cleanupDomFullscreen(window);
163 this.updateFullscreenWindowReference(window);
166 case "DOMFullscreen:Painted": {
167 this.waitingForChildExitFullscreen = false;
168 Services.obs.notifyObservers(window, "fullscreen-painted");
169 this.sendAsyncMessage("DOMFullscreen:Painted", {});
170 TelemetryStopwatch.finish("FULLSCREEN_CHANGE_MS");
176 handleEvent(aEvent) {
177 let window = aEvent.currentTarget.ownerGlobal;
178 switch (aEvent.type) {
179 case "MozDOMFullscreen:Entered": {
180 // The event target is the element which requested the DOM
181 // fullscreen. If we were entering DOM fullscreen for a remote
182 // browser, the target would be the browser which was the parameter of
183 // `remoteFrameFullscreenChanged` call. If the fullscreen
184 // request was initiated from an in-process browser, we need
185 // to get its corresponding browser here.
187 if (aEvent.target.ownerGlobal == window) {
188 browser = aEvent.target;
190 browser = aEvent.target.ownerGlobal.docShell.chromeEventHandler;
193 // Addon installation should be cancelled when entering fullscreen for security and usability reasons.
194 // Installation prompts in fullscreen can trick the user into installing unwanted addons.
195 // In fullscreen the notification box does not have a clear visual association with its parent anymore.
196 if (window.gXPInstallObserver) {
197 window.gXPInstallObserver.removeAllNotifications(browser);
200 TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
201 window.FullScreen.enterDomFullscreen(browser, this);
202 this.updateFullscreenWindowReference(window);
205 case "MozDOMFullscreen:Exited": {
206 TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
208 // Make sure that the actor has not been destroyed before
209 // accessing its browsing context. Otherwise, a error may
210 // occur and hence cleanupDomFullscreen not executed, resulting
211 // in the browser window being in an unstable state.
213 if (!this.hasBeenDestroyed() && !this.requestOrigin) {
214 this.requestOrigin = this;
216 this.cleanupDomFullscreen(window);
217 this.updateFullscreenWindowReference(window);
218 this.removeListeners(window);
224 addListeners(aWindow) {
225 aWindow.addEventListener(
226 "MozDOMFullscreen:Entered",
228 /* useCapture */ true,
232 aWindow.addEventListener(
233 "MozDOMFullscreen:Exited",
235 /* useCapture */ true,
236 /* wantsUntrusted */ false
240 removeListeners(aWindow) {
241 aWindow.removeEventListener("MozDOMFullscreen:Entered", this, true);
242 aWindow.removeEventListener("MozDOMFullscreen:Exited", this, true);
246 * Get the actor where the original fullscreen
247 * enter or exit request comes from.
249 get requestOrigin() {
250 let requestOrigin = this.browsingContext.top.fullscreenRequestOrigin;
251 return requestOrigin && requestOrigin.get();
255 * Store the actor where the original fullscreen
256 * enter or exit request comes from in the top level
259 set requestOrigin(aActor) {
261 this.browsingContext.top.fullscreenRequestOrigin = Cu.getWeakReference(
265 delete this.browsingContext.top.fullscreenRequestOrigin;
270 if (this._didDestroy) {
274 // The 'didDestroy' callback is not always getting called.
275 // So we can't rely on it here. Instead, we will try to access
276 // the browsing context to judge wether the actor has
277 // been destroyed or not.
279 return !this.browsingContext;