Bug 1405342 - Allow making chrome iframe be inactive. r=bz draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 10 Oct 2017 14:51:17 +0200
changeset 687555 a913295691c6454b6da9af211203cf9d1c604525
parent 687532 ecef003d836730c07ecfcfa25df8e71a88aec99f
child 737683 667379ffb4d96298de3670607f75bdf9963a8157
push id86535
push userbmo:poirot.alex@gmail.com
push dateFri, 27 Oct 2017 11:15:34 +0000
reviewersbz
bugs1405342
milestone58.0a1
Bug 1405342 - Allow making chrome iframe be inactive. r=bz MozReview-Commit-ID: BXEVAViAbWB
docshell/base/nsDocShell.cpp
docshell/test/navigation/browser.ini
docshell/test/navigation/browser_bug1405342.js
docshell/test/navigation/bug1405342.xul
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3464,19 +3464,26 @@ nsDocShell::SetDocLoaderParent(nsDocLoad
     if (mAllowWindowControl && NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
       SetAllowWindowControl(value);
     }
     SetAllowContentRetargeting(mAllowContentRetargeting &&
       parentAsDocShell->GetAllowContentRetargetingOnChildren());
     if (parentAsDocShell->GetIsPrerendered()) {
       SetIsPrerendered();
     }
-    if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
+    // To match SetIsActive propagation behavior, do not inherit active state
+    // between chrome parent and content children
+    if (parentAsDocShell->ItemType() == mItemType) {
+      if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
+        // a prerendered docshell is not active yet
+        SetIsActive(value && !mIsPrerendered);
+      }
+    } else if (mIsPrerendered) {
       // a prerendered docshell is not active yet
-      SetIsActive(value && !mIsPrerendered);
+      SetIsActive(false);
     }
     if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
         !customUserAgent.IsEmpty()) {
       SetCustomUserAgent(customUserAgent);
     }
     if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
       value = false;
     }
@@ -6306,21 +6313,16 @@ nsDocShell::GetIsOffScreenBrowser(bool* 
 {
   *aIsOffScreen = mIsOffScreenBrowser;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetIsActive(bool aIsActive)
 {
-  // We disallow setting active on chrome docshells.
-  if (mItemType == nsIDocShellTreeItem::typeChrome) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
   // Keep track ourselves.
   mIsActive = aIsActive;
 
   // Clear prerender flag if necessary.
   if (mIsPrerendered && aIsActive) {
     MOZ_ASSERT(mPrerenderGlobalHistory.get());
     mIsPrerendered = false;
     nsCOMPtr<IHistory> history = services::GetHistoryService();
@@ -6368,26 +6370,28 @@ nsDocShell::SetIsActive(bool aIsActive)
     }
   }
   if (timing) {
     timing->NotifyDocShellStateChanged(
       aIsActive ? nsDOMNavigationTiming::DocShellState::eActive
                 : nsDOMNavigationTiming::DocShellState::eInactive);
   }
 
-  // Recursively tell all of our children, but don't tell <iframe mozbrowser>
-  // children; they handle their state separately.
+  // Recursively tell all of our children, but don't tell:
+  // * <iframe mozbrowser> children; they handle their state separately,
+  // * content children if we are making a chrome docshell inactive.
   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
   while (iter.HasMore()) {
     nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
     if (!docshell) {
       continue;
     }
 
-    if (!docshell->GetIsMozBrowser()) {
+    if (!docshell->GetIsMozBrowser() &&
+        mItemType == docshell->ItemType()) {
       docshell->SetIsActive(aIsActive);
     }
   }
 
   // Restart or stop meta refresh timers if necessary
   if (mDisableMetaRefreshWhenInactive) {
     if (mIsActive) {
       ResumeRefreshURIs();
--- a/docshell/test/navigation/browser.ini
+++ b/docshell/test/navigation/browser.ini
@@ -1,12 +1,14 @@
 [DEFAULT]
 support-files =
   bug343515_pg1.html
   bug343515_pg2.html
   bug343515_pg3.html
   bug343515_pg3_1.html
   bug343515_pg3_1_1.html
   bug343515_pg3_2.html
+  bug1405342.xul
 
 [browser_bug343515.js]
 [browser_test-content-chromeflags.js]
-tags = openwindow
\ No newline at end of file
+tags = openwindow
+[browser_bug1405342.js]
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/browser_bug1405342.js
@@ -0,0 +1,75 @@
+// Test for bug 1405342 - Allow making chrome iframes be active/inactive.
+
+const TEST_URI = "chrome://mochitests/content/browser/docshell/test/navigation/bug1405342.xul";
+
+function oneShotListener(element, type) {
+  return new Promise(done => {
+    element.addEventListener(type, done, { once: true, capture: true });
+  });
+}
+
+function waitForVisibilityChange(iframe) {
+  return oneShotListener(iframe.contentWindow, "visibilitychange");
+}
+
+add_task(async function() {
+  info("Create privileged iframe in main-process");
+  let iframe = document.createElement("iframe");
+  document.documentElement.appendChild(iframe);
+  ok(iframe.frameLoader.docShell.isActive, "Iframe is active by default");
+
+  info("Waiting for iframe page load");
+  let onLoad = oneShotListener(iframe, "load");
+  iframe.setAttribute("src", TEST_URI);
+  await onLoad;
+  ok(iframe.frameLoader.docShell.isActive, "Iframe is still active after load");
+
+  let content = iframe.contentWindow.document.getElementById("content");
+  let chrome = iframe.contentWindow.document.getElementById("chrome");
+
+  is(content.contentWindow.top, content.contentWindow, "#content is really a content iframe");
+  is(chrome.contentWindow.top, window, "#chrome is really a chrome iframe");
+
+  ok(chrome.frameLoader.docShell.isActive, "Chrome child iframe is active after load");
+  ok(content.frameLoader.docShell.isActive, "Content child iframe is active after load");
+
+  info("Switching iframe to inactive");
+  let onVisibilityChange = waitForVisibilityChange(iframe);
+  iframe.frameLoader.docShell.isActive = false;
+  ok(!iframe.frameLoader.docShell.isActive, "Iframe is now reported as inactive");
+  ok(content.frameLoader.docShell.isActive, "Content child iframe stays active");
+  ok(!chrome.frameLoader.docShell.isActive, "Chrome child iframe is now reported as inactive");
+
+  info("Attach two iframes to the inactive docshell");
+  let iframeDoc = iframe.contentWindow.document;
+  let contentInherit = iframeDoc.createElement("iframe");
+  contentInherit.setAttribute("type", "content");
+  iframeDoc.documentElement.appendChild(contentInherit);
+  let chromeInherit = iframeDoc.createElement("iframe");
+  iframeDoc.documentElement.appendChild(chromeInherit);
+  is(chromeInherit.contentWindow.top, window, "chromeInherit refers to a chrome iframe");
+  is(contentInherit.contentWindow.top, contentInherit.contentWindow, "contentInherit refers to a content iframe");
+  ok(!chromeInherit.frameLoader.docShell.isActive, "Chrome iframe inherits active state and is still inactive");
+  ok(contentInherit.frameLoader.docShell.isActive, "Content iframe doesn't inherit active state and is active");
+
+  info("Waiting for visibilitychange event");
+  await onVisibilityChange;
+  ok(!iframe.frameLoader.docShell.isActive, "Iframe is still inactive after visibilitychange event");
+  ok(content.frameLoader.docShell.isActive, "Content child iframe still stays active");
+  ok(!chrome.frameLoader.docShell.isActive, "Chrome child iframe is still inactive after visibilitychange event");
+
+  info("Switching iframe back to active");
+  onVisibilityChange = waitForVisibilityChange(iframe);
+  iframe.frameLoader.docShell.isActive = true;
+  ok(iframe.frameLoader.docShell.isActive, "Iframe is now reported as active");
+  ok(content.frameLoader.docShell.isActive, "Content child iframe is always active");
+  ok(chrome.frameLoader.docShell.isActive, "Chrome child iframe is now reported as active");
+
+  info("Waiting for visibiliychange event");
+  await onVisibilityChange;
+  ok(iframe.frameLoader.docShell.isActive, "Iframe is back to active after visibilitychange event");
+  ok(content.frameLoader.docShell.isActive, "Content child iframe is active");
+  ok(chrome.frameLoader.docShell.isActive, "Chrome child iframe is back to active after visibilitychange event");
+
+  iframe.remove();
+});
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/bug1405342.xul
@@ -0,0 +1,5 @@
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<label value="Chrome document" />
+<iframe id="content" type="content" src="data:text/html;charset=utf-8,content"></iframe>
+<iframe id="chrome" src="data:text/html;charset=utf-8,chrome"></iframe>
+</window>