gecko-dev/browser/actors/DOMFullscreenChild.sys.mjs

165 lines
6.4 KiB
JavaScript

/* 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;
}
}
}