Bug 1505916 - [Fission] Part 2: Make the fullscreen code work with oop iframes. r=NeilDeakin,smaug

e10s scenario:
1. An DOM element request fulscreen mode.
2. The request is redirected to the parent.
3. Parent enters fullscreen.
4. Parent notifies child that it has finished entering fullscreen.
5. Child goes fullscreen.
6. Then, child notifies parent that it has finished transitioning to fullscreen.
4. Finally, parent notify observers that fullscreen paint has finished.

Let's go into the details of how step 5 works in the above scenario.
5.a The element that made the request is set to fullscreen.
5.b Then, the document where that element lives is set to fullscreen as well as all of its ancestors until we reach the top level document. (see Document::ApplyFulscreen method)

Now in Fission world, we may have a request comming from an oop iframe. And it that case since we won't have  to ancestor documents living in different content process(es), we will first notiy those content processes (one after another from bottom to top) to go fullscreen. Once they all do, the content process where the request originated will be told to enter fullscreen.

Differential Revision: https://phabricator.services.mozilla.com/D45972

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Abdoulaye Oumar Ly 2019-10-11 14:30:28 +00:00
parent a0898dc653
commit c558bed276
5 changed files with 135 additions and 25 deletions

View File

@ -19,22 +19,34 @@ class DOMFullscreenChild extends JSWindowActorChild {
switch (aMessage.name) {
case "DOMFullscreen:Entered": {
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", {});
let remoteFrameBC = aMessage.data.remoteFrameBC;
if (remoteFrameBC) {
let remoteFrame = remoteFrameBC.embedderElement;
this._isNotTheRequestSource = true;
windowUtils.remoteFrameFullscreenChanged(remoteFrame);
} else {
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 remoteFrameBC = aMessage.data.remoteFrameBC;
if (remoteFrameBC) {
this._isNotTheRequestSource = true;
}
// If we've exited fullscreen at this point, no need to record
// transaction id or call exit fullscreen. This is especially
// important for non-e10s, since in that case, it is possible
// 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._lastTransactionId = windowUtils.lastTransactionId;
@ -67,20 +79,29 @@ class DOMFullscreenChild extends JSWindowActorChild {
}
case "MozDOMFullscreen:Entered":
case "MozDOMFullscreen:Exited": {
let rootWindow = this.contentWindow.windowRoot;
rootWindow.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", {});
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", ""), {});
} else {
let rootWindow = this.contentWindow.windowRoot;
rootWindow.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 non-e10s
// Note that this._lastTransactionId is not set when in pre-e10s
// mode, so we need to check that explicitly.
if (
!this._lastTransactionId ||

View File

@ -15,6 +15,7 @@ class DOMFullscreenParent extends JSWindowActorParent {
let window = browser.ownerGlobal;
switch (aMessage.name) {
case "DOMFullscreen:Request": {
this.requestOrigin = this;
this.addListeners(window);
window.windowUtils.remoteFrameFullscreenChanged(browser);
break;
@ -78,6 +79,9 @@ class DOMFullscreenParent extends JSWindowActorParent {
}
case "MozDOMFullscreen:Exited":
TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
if (!this.requestOrigin) {
this.requestOrigin = this;
}
window.FullScreen.cleanupDomFullscreen(this);
this.removeListeners(window);
break;
@ -104,4 +108,28 @@ class DOMFullscreenParent extends JSWindowActorParent {
aWindow.removeEventListener("MozDOMFullscreen:Entered", this, true);
aWindow.removeEventListener("MozDOMFullscreen:Exited", this, true);
}
/**
* 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;
}
}
}

View File

@ -414,6 +414,7 @@ var FullScreen = {
enterDomFullscreen(aBrowser, aActor) {
if (!document.fullscreenElement) {
aActor.requestOrigin = null;
return;
}
@ -425,14 +426,21 @@ var FullScreen = {
// to enter fullscreen state. We don't need to do so if it is an
// in-process browser, since all related document should have
// entered fullscreen state at this point.
// Additionally, in Fission world, we may need to notify the
// frames in the middle (content frames that embbed the oop iframe where
// the element requesting fullscreen lives) to enter fullscreen
// first.
// This should be done before the active tab check below to ensure
// that the content document handles the pending request. Doing so
// before the check is fine since we also check the activeness of
// the requesting document in content-side handling code.
if (this._isRemoteBrowser(aBrowser)) {
aActor.sendAsyncMessage("DOMFullscreen:Entered", {});
if (
!this._sendMessageToTheRightContent(aActor, "DOMFullscreen:Entered")
) {
return;
}
}
// If we've received a fullscreen notification, we have to ensure that the
// element that's requesting fullscreen belongs to the browser that's currently
// active. If not, we exit fullscreen since the "full-screen document" isn't
@ -495,12 +503,15 @@ var FullScreen = {
},
cleanupDomFullscreen(aActor) {
if (!this._sendMessageToTheRightContent(aActor, "DOMFullscreen:CleanUp")) {
return;
}
PopupNotifications.panel.removeEventListener(
"popupshowing",
() => this._handlePermPromptShow(),
true
);
aActor.sendAsyncMessage("DOMFullscreen:CleanUp", {});
PointerlockFsWarning.close();
gBrowser.tabContainer.removeEventListener(
@ -511,6 +522,54 @@ var FullScreen = {
document.documentElement.removeAttribute("inDOMFullscreen");
},
/**
* Search for the first ancestor of aActor that lives in a different process.
* If found, that ancestor is sent the message. Otherwise, the recipient should
* be the actor of the request origin.
*
* @param {JSWindowActorParent} aActor
* The actor that called this function.
* @param {String} message
* Message to be sent.
*
* @return {boolean}
* Return true if the message is sent to the request source
* or false otherwise.
*/
_sendMessageToTheRightContent(aActor, aMessage) {
let childBC = aActor.browsingContext;
let parentBC = childBC.parent;
while (parentBC) {
let childPid = childBC.currentWindowGlobal.osPid;
let parentPid = parentBC.currentWindowGlobal.osPid;
if (childPid == parentPid) {
childBC = parentBC;
parentBC = childBC.parent;
} else {
break;
}
}
if (parentBC) {
let parentActor = parentBC.currentWindowGlobal.getActor("DOMFullscreen");
parentActor.sendAsyncMessage(aMessage, {
remoteFrameBC: childBC,
});
return false;
}
// All content frames living outside the process where
// the element requesting fullscreen lives should
// have entered or exited fullscreen at this point.
// So let's notify the process where the original request
// comes from.
aActor.requestOrigin.sendAsyncMessage(aMessage, {});
aActor.requestOrigin = null;
return true;
},
_isRemoteBrowser(aBrowser) {
return gMultiProcessBrowser && aBrowser.getAttribute("remote") == "true";
},

View File

@ -13463,7 +13463,7 @@ nsresult Document::RemoteFrameFullscreenChanged(Element* aFrameElement) {
// If the frame element is already the fullscreen element in this document,
// this has no effect.
auto request = FullscreenRequest::CreateForRemote(aFrameElement);
RequestFullscreen(std::move(request));
RequestFullscreen(std::move(request), XRE_IsContentProcess());
return NS_OK;
}
@ -13617,14 +13617,15 @@ static bool ShouldApplyFullscreenDirectly(Document* aDoc,
}
}
void Document::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest) {
void Document::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest,
bool applyFullScreenDirectly) {
nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this);
if (!rootWin) {
aRequest->MayRejectPromise();
return;
}
if (ShouldApplyFullscreenDirectly(this, rootWin)) {
if (applyFullScreenDirectly || ShouldApplyFullscreenDirectly(this, rootWin)) {
ApplyFullscreen(std::move(aRequest));
return;
}

View File

@ -2150,7 +2150,8 @@ class Document : public nsINode,
// This is called asynchronously by Document::AsyncRequestFullscreen()
// to move this document into fullscreen mode if allowed.
void RequestFullscreen(UniquePtr<FullscreenRequest> aRequest);
void RequestFullscreen(UniquePtr<FullscreenRequest> aRequest,
bool applyFullScreenDirectly = false);
// Removes all elements from the fullscreen stack, removing full-scren
// styles from the top element in the stack.