DOMFullscreenChild.sys.mjs (6508B)
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/. */ 5 6 export class DOMFullscreenChild extends JSWindowActorChild { 7 receiveMessage(aMessage) { 8 let window = this.contentWindow; 9 let windowUtils = window?.windowUtils; 10 11 switch (aMessage.name) { 12 case "DOMFullscreen:Entered": { 13 if (!windowUtils) { 14 // If we are not able to enter fullscreen, tell the parent to just 15 // exit. 16 this.sendAsyncMessage("DOMFullscreen:Exit", {}); 17 break; 18 } 19 20 let remoteFrameBC = aMessage.data.remoteFrameBC; 21 if (remoteFrameBC) { 22 let remoteFrame = remoteFrameBC.embedderElement; 23 if (!remoteFrame) { 24 // This could happen when the page navigate away and trigger a 25 // process switching during fullscreen transition, tell the parent 26 // to just exit. 27 this.sendAsyncMessage("DOMFullscreen:Exit", {}); 28 break; 29 } 30 this._isNotTheRequestSource = true; 31 windowUtils.remoteFrameFullscreenChanged(remoteFrame); 32 } else { 33 this._waitForMozAfterPaint = true; 34 this._lastTransactionId = windowUtils.lastTransactionId; 35 if ( 36 !windowUtils.handleFullscreenRequests() && 37 !this.document.fullscreenElement 38 ) { 39 // If we don't actually have any pending fullscreen request 40 // to handle, neither we have been in fullscreen, tell the 41 // parent to just exit. 42 this.sendAsyncMessage("DOMFullscreen:Exit", {}); 43 } 44 } 45 break; 46 } 47 case "DOMFullscreen:CleanUp": { 48 let isNotTheRequestSource = !!aMessage.data.remoteFrameBC; 49 // If we've exited fullscreen at this point, no need to record 50 // transaction id or call exit fullscreen. This is especially 51 // important for pre-e10s, since in that case, it is possible 52 // that no more paint would be triggered after this point. 53 if (this.document.fullscreenElement) { 54 this._isNotTheRequestSource = isNotTheRequestSource; 55 // Need to wait for the MozAfterPaint after exiting fullscreen if 56 // this is the request source. 57 this._waitForMozAfterPaint = !this._isNotTheRequestSource; 58 // windowUtils could be null if the associated window is not current 59 // active window. In this case, document must be in the process of 60 // exiting fullscreen, it is okay to not ask it to exit fullscreen. 61 if (windowUtils) { 62 this._lastTransactionId = windowUtils.lastTransactionId; 63 windowUtils.exitFullscreen(); 64 } 65 } else if (isNotTheRequestSource) { 66 // If we are not the request source and have exited fullscreen, reply 67 // Exited to parent as parent is waiting for our reply. 68 this.sendAsyncMessage("DOMFullscreen:Exited", {}); 69 } else { 70 // If we've already exited fullscreen, it is possible that no more 71 // paint would be triggered, so don't wait for MozAfterPaint. 72 // TODO: There might be some way to move this code around a bit to 73 // make it easier to follow. Somehow handle the "local" case in 74 // one place and the isNotTheRequestSource case after that. 75 this.sendAsyncMessage("DOMFullscreen:Painted", {}); 76 } 77 break; 78 } 79 case "DOMFullscreen:Painted": { 80 Services.obs.notifyObservers(window, "fullscreen-painted"); 81 break; 82 } 83 } 84 } 85 86 handleEvent(aEvent) { 87 if (this.hasBeenDestroyed()) { 88 // Make sure that this actor is alive before going further because 89 // if it's not the case, any attempt to send a message or access 90 // objects such as 'contentWindow' will fail. (See bug 1590138) 91 return; 92 } 93 94 switch (aEvent.type) { 95 case "MozDOMFullscreen:Request": { 96 this.sendAsyncMessage("DOMFullscreen:Request", {}); 97 break; 98 } 99 case "MozDOMFullscreen:NewOrigin": { 100 this.sendAsyncMessage("DOMFullscreen:NewOrigin", { 101 originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix, 102 }); 103 break; 104 } 105 case "MozDOMFullscreen:Exit": { 106 this.sendAsyncMessage("DOMFullscreen:Exit", {}); 107 break; 108 } 109 case "MozDOMFullscreen:Entered": 110 case "MozDOMFullscreen:Exited": { 111 if (this._isNotTheRequestSource) { 112 // Fullscreen change event for a frame in the 113 // middle (content frame embedding the oop frame where the 114 // request comes from) 115 116 delete this._isNotTheRequestSource; 117 this.sendAsyncMessage(aEvent.type.replace("Moz", ""), {}); 118 break; 119 } 120 121 if (this._waitForMozAfterPaint) { 122 delete this._waitForMozAfterPaint; 123 this._listeningWindow = this.contentWindow.windowRoot; 124 this._listeningWindow.addEventListener("MozAfterPaint", this); 125 } 126 127 if (!this.document || !this.document.fullscreenElement) { 128 // If we receive any fullscreen change event, and find we are 129 // actually not in fullscreen, also ask the parent to exit to 130 // ensure that the parent always exits fullscreen when we do. 131 this.sendAsyncMessage("DOMFullscreen:Exit", {}); 132 } 133 break; 134 } 135 case "MozAfterPaint": { 136 // Only send Painted signal after we actually finish painting 137 // the transition for the fullscreen change. 138 // Note that this._lastTransactionId is not set when in pre-e10s 139 // mode, so we need to check that explicitly. 140 if ( 141 !this._lastTransactionId || 142 aEvent.transactionId > this._lastTransactionId 143 ) { 144 this._listeningWindow.removeEventListener("MozAfterPaint", this); 145 delete this._listeningWindow; 146 this.sendAsyncMessage("DOMFullscreen:Painted", {}); 147 } 148 break; 149 } 150 } 151 } 152 153 hasBeenDestroyed() { 154 // The 'didDestroy' callback is not always getting called. 155 // So we can't rely on it here. Instead, we will try to access 156 // the browsing context to judge wether the actor has 157 // been destroyed or not. 158 try { 159 return !this.browsingContext; 160 } catch { 161 return true; 162 } 163 } 164 }