2019-10-11 17:30:28 +03:00
|
|
|
/* 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/. */
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
var EXPORTED_SYMBOLS = ["DOMFullscreenParent"];
|
|
|
|
|
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
|
|
|
|
class DOMFullscreenParent extends JSWindowActorParent {
|
2021-12-17 12:15:10 +03:00
|
|
|
// These properties get set by browser-fullScreenAndPointerLock.js.
|
|
|
|
// TODO: Bug 1743703 - Consider moving the messaging component of
|
|
|
|
// browser-fullScreenAndPointerLock.js into the actor
|
|
|
|
waitingForChildEnterFullscreen = false;
|
|
|
|
waitingForChildExitFullscreen = false;
|
|
|
|
// Cache the next message recipient actor and in-process browsing context that
|
|
|
|
// is computed by _getNextMsgRecipientActor() of
|
|
|
|
// browser-fullScreenAndPointerLock.js, this is used to ensure the fullscreen
|
|
|
|
// cleanup messages goes the same route as fullscreen request, especially for
|
|
|
|
// the cleanup that happens after actor is destroyed.
|
|
|
|
// TODO: Bug 1743703 - Consider moving the messaging component of
|
|
|
|
// browser-fullScreenAndPointerLock.js into the actor
|
|
|
|
nextMsgRecipient = null;
|
2020-10-06 01:38:03 +03:00
|
|
|
|
2020-06-08 18:24:54 +03:00
|
|
|
updateFullscreenWindowReference(aWindow) {
|
|
|
|
if (aWindow.document.documentElement.hasAttribute("inDOMFullscreen")) {
|
|
|
|
this._fullscreenWindow = aWindow;
|
|
|
|
} else {
|
|
|
|
delete this._fullscreenWindow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-17 12:15:10 +03:00
|
|
|
cleanupDomFullscreen(aWindow) {
|
|
|
|
if (!aWindow.FullScreen) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't need to wait for child reply, i.e. cleanupDomFullscreen
|
|
|
|
// doesn't message to child, and we've exit the fullscreen, there won't be
|
|
|
|
// DOMFullscreen:Painted message from child and it is possible that no more
|
|
|
|
// paint would be triggered, so just notify fullscreen-painted observer.
|
|
|
|
if (
|
|
|
|
!aWindow.FullScreen.cleanupDomFullscreen(this) &&
|
|
|
|
!aWindow.document.fullscreen
|
|
|
|
) {
|
|
|
|
Services.obs.notifyObservers(aWindow, "fullscreen-painted");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 18:24:54 +03:00
|
|
|
didDestroy() {
|
2021-12-17 12:15:10 +03:00
|
|
|
this._didDestroy = true;
|
|
|
|
|
2020-06-08 18:24:54 +03:00
|
|
|
let window = this._fullscreenWindow;
|
|
|
|
if (!window) {
|
2021-12-17 12:15:10 +03:00
|
|
|
if (this.waitingForChildExitFullscreen) {
|
|
|
|
this.waitingForChildExitFullscreen = false;
|
|
|
|
// We were destroyed while waiting for our DOMFullscreenChild to exit
|
|
|
|
// and have exited fullscreen, run cleanup steps anyway.
|
|
|
|
let topBrowsingContext = this.browsingContext.top;
|
|
|
|
let browser = topBrowsingContext.embedderElement;
|
|
|
|
if (browser) {
|
|
|
|
this.cleanupDomFullscreen(browser.ownerGlobal);
|
|
|
|
}
|
|
|
|
}
|
2020-06-08 18:24:54 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-17 12:15:10 +03:00
|
|
|
if (this.waitingForChildEnterFullscreen) {
|
|
|
|
this.waitingForChildEnterFullscreen = false;
|
|
|
|
if (window.document.fullscreen) {
|
|
|
|
// We were destroyed while waiting for our DOMFullscreenChild
|
|
|
|
// to transition to fullscreen so we abort the entire
|
|
|
|
// fullscreen transition to prevent getting stuck in a
|
|
|
|
// partial fullscreen state. We need to go through the
|
|
|
|
// document since window.Fullscreen could be undefined
|
|
|
|
// at this point.
|
|
|
|
//
|
|
|
|
// This could reject if we're not currently in fullscreen
|
|
|
|
// so just ignore rejection.
|
|
|
|
window.document.exitFullscreen().catch(() => {});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.cleanupDomFullscreen(window);
|
2020-10-06 01:38:03 +03:00
|
|
|
}
|
|
|
|
|
2020-06-08 18:24:54 +03:00
|
|
|
// Need to resume Chrome UI if the window is still in fullscreen UI
|
|
|
|
// to avoid the window stays in fullscreen problem. (See Bug 1620341)
|
|
|
|
if (window.document.documentElement.hasAttribute("inDOMFullscreen")) {
|
2021-12-17 12:15:10 +03:00
|
|
|
this.cleanupDomFullscreen(window);
|
2020-06-08 18:24:54 +03:00
|
|
|
if (window.windowUtils) {
|
|
|
|
window.windowUtils.remoteFrameFullscreenReverted();
|
|
|
|
}
|
2021-12-17 12:15:10 +03:00
|
|
|
} else if (this.waitingForChildExitFullscreen) {
|
|
|
|
this.waitingForChildExitFullscreen = false;
|
|
|
|
// We were destroyed while waiting for our DOMFullscreenChild to exit and
|
|
|
|
// have exited fullscreen, run cleanup steps anyway.
|
|
|
|
this.cleanupDomFullscreen(window);
|
2020-06-08 18:24:54 +03:00
|
|
|
}
|
|
|
|
this.updateFullscreenWindowReference(window);
|
|
|
|
}
|
|
|
|
|
2019-10-11 17:30:28 +03:00
|
|
|
receiveMessage(aMessage) {
|
|
|
|
let topBrowsingContext = this.browsingContext.top;
|
|
|
|
let browser = topBrowsingContext.embedderElement;
|
2019-10-30 19:15:49 +03:00
|
|
|
|
|
|
|
if (!browser) {
|
|
|
|
// No need to go further when the browser is not accessible anymore
|
|
|
|
// (which can happen when the tab is closed for instance),
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-11 17:30:28 +03:00
|
|
|
let window = browser.ownerGlobal;
|
|
|
|
switch (aMessage.name) {
|
|
|
|
case "DOMFullscreen:Request": {
|
2021-12-17 12:15:10 +03:00
|
|
|
this.waitingForChildExitFullscreen = false;
|
|
|
|
this.nextMsgRecipient = null;
|
2019-10-11 17:30:28 +03:00
|
|
|
this.requestOrigin = this;
|
2019-10-11 17:30:28 +03:00
|
|
|
this.addListeners(window);
|
|
|
|
window.windowUtils.remoteFrameFullscreenChanged(browser);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "DOMFullscreen:NewOrigin": {
|
|
|
|
// Don't show the warning if we've already exited fullscreen.
|
2019-10-17 18:25:51 +03:00
|
|
|
if (window.document.fullscreen) {
|
2019-10-11 17:30:28 +03:00
|
|
|
window.PointerlockFsWarning.showFullScreen(
|
|
|
|
aMessage.data.originNoSuffix
|
|
|
|
);
|
|
|
|
}
|
2021-12-17 12:15:10 +03:00
|
|
|
this.updateFullscreenWindowReference(window);
|
2019-10-11 17:30:28 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "DOMFullscreen:Entered": {
|
2021-12-17 12:15:10 +03:00
|
|
|
this.nextMsgRecipient = null;
|
|
|
|
this.waitingForChildEnterFullscreen = false;
|
2019-10-11 17:30:28 +03:00
|
|
|
window.FullScreen.enterDomFullscreen(browser, this);
|
2020-06-08 18:24:54 +03:00
|
|
|
this.updateFullscreenWindowReference(window);
|
2019-10-11 17:30:28 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "DOMFullscreen:Exit": {
|
2021-12-17 12:15:10 +03:00
|
|
|
this.waitingForChildEnterFullscreen = false;
|
2019-10-11 17:30:28 +03:00
|
|
|
window.windowUtils.remoteFrameFullscreenReverted();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "DOMFullscreen:Exited": {
|
2021-12-17 12:15:10 +03:00
|
|
|
this.waitingForChildExitFullscreen = false;
|
|
|
|
this.cleanupDomFullscreen(window);
|
2020-06-08 18:24:54 +03:00
|
|
|
this.updateFullscreenWindowReference(window);
|
2019-10-11 17:30:28 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case "DOMFullscreen:Painted": {
|
2021-12-17 12:15:10 +03:00
|
|
|
this.waitingForChildExitFullscreen = false;
|
2019-10-11 17:30:28 +03:00
|
|
|
Services.obs.notifyObservers(window, "fullscreen-painted");
|
|
|
|
this.sendAsyncMessage("DOMFullscreen:Painted", {});
|
|
|
|
TelemetryStopwatch.finish("FULLSCREEN_CHANGE_MS");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleEvent(aEvent) {
|
2019-10-30 19:15:49 +03:00
|
|
|
let window = aEvent.currentTarget.ownerGlobal;
|
2019-10-11 17:30:28 +03:00
|
|
|
switch (aEvent.type) {
|
|
|
|
case "MozDOMFullscreen:Entered": {
|
|
|
|
// The event target is the element which requested the DOM
|
|
|
|
// fullscreen. If we were entering DOM fullscreen for a remote
|
|
|
|
// browser, the target would be the browser which was the parameter of
|
|
|
|
// `remoteFrameFullscreenChanged` call. If the fullscreen
|
|
|
|
// request was initiated from an in-process browser, we need
|
|
|
|
// to get its corresponding browser here.
|
|
|
|
let browser;
|
|
|
|
if (aEvent.target.ownerGlobal == window) {
|
|
|
|
browser = aEvent.target;
|
|
|
|
} else {
|
|
|
|
browser = aEvent.target.ownerGlobal.docShell.chromeEventHandler;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Addon installation should be cancelled when entering fullscreen for security and usability reasons.
|
|
|
|
// Installation prompts in fullscreen can trick the user into installing unwanted addons.
|
|
|
|
// In fullscreen the notification box does not have a clear visual association with its parent anymore.
|
2019-10-18 03:15:08 +03:00
|
|
|
if (window.gXPInstallObserver) {
|
|
|
|
window.gXPInstallObserver.removeAllNotifications(browser);
|
|
|
|
}
|
2019-10-11 17:30:28 +03:00
|
|
|
|
|
|
|
TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
|
|
|
|
window.FullScreen.enterDomFullscreen(browser, this);
|
2020-06-08 18:24:54 +03:00
|
|
|
this.updateFullscreenWindowReference(window);
|
2019-10-11 17:30:28 +03:00
|
|
|
break;
|
|
|
|
}
|
2019-10-18 03:15:08 +03:00
|
|
|
case "MozDOMFullscreen:Exited": {
|
2019-10-11 17:30:28 +03:00
|
|
|
TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
|
2019-11-06 02:29:26 +03:00
|
|
|
|
|
|
|
// Make sure that the actor has not been destroyed before
|
|
|
|
// accessing its browsing context. Otherwise, a error may
|
|
|
|
// occur and hence cleanupDomFullscreen not executed, resulting
|
|
|
|
// in the browser window being in an unstable state.
|
|
|
|
// (Bug 1590138).
|
|
|
|
if (!this.hasBeenDestroyed() && !this.requestOrigin) {
|
2019-10-11 17:30:28 +03:00
|
|
|
this.requestOrigin = this;
|
|
|
|
}
|
2021-12-17 12:15:10 +03:00
|
|
|
this.cleanupDomFullscreen(window);
|
2020-06-08 18:24:54 +03:00
|
|
|
this.updateFullscreenWindowReference(window);
|
2019-10-11 17:30:28 +03:00
|
|
|
this.removeListeners(window);
|
|
|
|
break;
|
2019-10-18 03:15:08 +03:00
|
|
|
}
|
2019-10-11 17:30:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addListeners(aWindow) {
|
|
|
|
aWindow.addEventListener(
|
|
|
|
"MozDOMFullscreen:Entered",
|
|
|
|
this,
|
|
|
|
/* useCapture */ true,
|
|
|
|
/* wantsUntrusted */
|
|
|
|
false
|
|
|
|
);
|
|
|
|
aWindow.addEventListener(
|
|
|
|
"MozDOMFullscreen:Exited",
|
|
|
|
this,
|
|
|
|
/* useCapture */ true,
|
|
|
|
/* wantsUntrusted */ false
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
removeListeners(aWindow) {
|
|
|
|
aWindow.removeEventListener("MozDOMFullscreen:Entered", this, true);
|
|
|
|
aWindow.removeEventListener("MozDOMFullscreen:Exited", this, true);
|
|
|
|
}
|
2019-10-11 17:30:28 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the actor where the original fullscreen
|
|
|
|
* enter or exit request comes from.
|
|
|
|
*/
|
|
|
|
get requestOrigin() {
|
|
|
|
let requestOrigin = this.browsingContext.top.fullscreenRequestOrigin;
|
|
|
|
return requestOrigin && requestOrigin.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store the actor where the original fullscreen
|
|
|
|
* enter or exit request comes from in the top level
|
|
|
|
* browsing context.
|
|
|
|
*/
|
|
|
|
set requestOrigin(aActor) {
|
|
|
|
if (aActor) {
|
|
|
|
this.browsingContext.top.fullscreenRequestOrigin = Cu.getWeakReference(
|
|
|
|
aActor
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
delete this.browsingContext.top.fullscreenRequestOrigin;
|
|
|
|
}
|
|
|
|
}
|
2019-11-06 02:29:26 +03:00
|
|
|
|
|
|
|
hasBeenDestroyed() {
|
2021-12-17 12:15:10 +03:00
|
|
|
if (this._didDestroy) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-11-06 02:29:26 +03:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2019-10-11 17:30:28 +03:00
|
|
|
}
|