diff options
Diffstat (limited to '')
-rw-r--r-- | browser/actors/DOMFullscreenChild.sys.mjs | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/browser/actors/DOMFullscreenChild.sys.mjs b/browser/actors/DOMFullscreenChild.sys.mjs new file mode 100644 index 0000000000..4088def44b --- /dev/null +++ b/browser/actors/DOMFullscreenChild.sys.mjs @@ -0,0 +1,164 @@ +/* vim: set ts=2 sw=2 sts=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +export class DOMFullscreenChild extends JSWindowActorChild { + receiveMessage(aMessage) { + let window = this.contentWindow; + let windowUtils = window?.windowUtils; + + switch (aMessage.name) { + case "DOMFullscreen:Entered": { + if (!windowUtils) { + // If we are not able to enter fullscreen, tell the parent to just + // exit. + this.sendAsyncMessage("DOMFullscreen:Exit", {}); + break; + } + + let remoteFrameBC = aMessage.data.remoteFrameBC; + if (remoteFrameBC) { + let remoteFrame = remoteFrameBC.embedderElement; + if (!remoteFrame) { + // This could happen when the page navigate away and trigger a + // process switching during fullscreen transition, tell the parent + // to just exit. + this.sendAsyncMessage("DOMFullscreen:Exit", {}); + break; + } + this._isNotTheRequestSource = true; + windowUtils.remoteFrameFullscreenChanged(remoteFrame); + } else { + this._waitForMozAfterPaint = true; + this._lastTransactionId = windowUtils.lastTransactionId; + if ( + !windowUtils.handleFullscreenRequests() && + !this.document.fullscreenElement + ) { + // If we don't actually have any pending fullscreen request + // to handle, neither we have been in fullscreen, tell the + // parent to just exit. + this.sendAsyncMessage("DOMFullscreen:Exit", {}); + } + } + break; + } + case "DOMFullscreen:CleanUp": { + let isNotTheRequestSource = !!aMessage.data.remoteFrameBC; + // If we've exited fullscreen at this point, no need to record + // transaction id or call exit fullscreen. This is especially + // important for pre-e10s, since in that case, it is possible + // that no more paint would be triggered after this point. + if (this.document.fullscreenElement) { + this._isNotTheRequestSource = isNotTheRequestSource; + // Need to wait for the MozAfterPaint after exiting fullscreen if + // this is the request source. + this._waitForMozAfterPaint = !this._isNotTheRequestSource; + // windowUtils could be null if the associated window is not current + // active window. In this case, document must be in the process of + // exiting fullscreen, it is okay to not ask it to exit fullscreen. + if (windowUtils) { + this._lastTransactionId = windowUtils.lastTransactionId; + windowUtils.exitFullscreen(); + } + } else if (isNotTheRequestSource) { + // If we are not the request source and have exited fullscreen, reply + // Exited to parent as parent is waiting for our reply. + this.sendAsyncMessage("DOMFullscreen:Exited", {}); + } else { + // If we've already exited fullscreen, it is possible that no more + // paint would be triggered, so don't wait for MozAfterPaint. + // TODO: There might be some way to move this code around a bit to + // make it easier to follow. Somehow handle the "local" case in + // one place and the isNotTheRequestSource case after that. + this.sendAsyncMessage("DOMFullscreen:Painted", {}); + } + break; + } + case "DOMFullscreen:Painted": { + Services.obs.notifyObservers(window, "fullscreen-painted"); + break; + } + } + } + + handleEvent(aEvent) { + if (this.hasBeenDestroyed()) { + // Make sure that this actor is alive before going further because + // if it's not the case, any attempt to send a message or access + // objects such as 'contentWindow' will fail. (See bug 1590138) + return; + } + + switch (aEvent.type) { + case "MozDOMFullscreen:Request": { + this.sendAsyncMessage("DOMFullscreen:Request", {}); + break; + } + case "MozDOMFullscreen:NewOrigin": { + this.sendAsyncMessage("DOMFullscreen:NewOrigin", { + originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix, + }); + break; + } + case "MozDOMFullscreen:Exit": { + this.sendAsyncMessage("DOMFullscreen:Exit", {}); + break; + } + case "MozDOMFullscreen:Entered": + case "MozDOMFullscreen:Exited": { + if (this._isNotTheRequestSource) { + // Fullscreen change event for a frame in the + // middle (content frame embedding the oop frame where the + // request comes from) + + delete this._isNotTheRequestSource; + this.sendAsyncMessage(aEvent.type.replace("Moz", ""), {}); + break; + } + + if (this._waitForMozAfterPaint) { + delete this._waitForMozAfterPaint; + this._listeningWindow = this.contentWindow.windowRoot; + this._listeningWindow.addEventListener("MozAfterPaint", this); + } + + if (!this.document || !this.document.fullscreenElement) { + // If we receive any fullscreen change event, and find we are + // actually not in fullscreen, also ask the parent to exit to + // ensure that the parent always exits fullscreen when we do. + this.sendAsyncMessage("DOMFullscreen:Exit", {}); + } + break; + } + case "MozAfterPaint": { + // Only send Painted signal after we actually finish painting + // the transition for the fullscreen change. + // Note that this._lastTransactionId is not set when in pre-e10s + // mode, so we need to check that explicitly. + if ( + !this._lastTransactionId || + aEvent.transactionId > this._lastTransactionId + ) { + this._listeningWindow.removeEventListener("MozAfterPaint", this); + delete this._listeningWindow; + this.sendAsyncMessage("DOMFullscreen:Painted", {}); + } + break; + } + } + } + + hasBeenDestroyed() { + // The 'didDestroy' callback is not always getting called. + // So we can't rely on it here. Instead, we will try to access + // the browsing context to judge wether the actor has + // been destroyed or not. + try { + return !this.browsingContext; + } catch { + return true; + } + } +} |