Bug 1405342 - Allow making chrome iframe be inactive. r=bz
MozReview-Commit-ID: BXEVAViAbWB
--- 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>