Bug 1669961: Return null from .contentWindow when inner window is inactive. r=nika

Differential Revision: https://phabricator.services.mozilla.com/D93853
This commit is contained in:
Kris Maglione 2020-11-10 20:35:04 +00:00
parent 61a922522e
commit cfebcd3fd2
11 changed files with 108 additions and 16 deletions

View file

@ -360,10 +360,14 @@ async function unregisterServiceWorker(tab, expectedPageUrl) {
async function waitForRegistrationReady(tab, expectedPageUrl) { async function waitForRegistrationReady(tab, expectedPageUrl) {
await asyncWaitUntil(() => await asyncWaitUntil(() =>
SpecialPowers.spawn(tab.linkedBrowser, [expectedPageUrl], function(_url) { SpecialPowers.spawn(tab.linkedBrowser, [expectedPageUrl], function(_url) {
const win = content.wrappedJSObject; try {
const isExpectedUrl = win.location.href === _url; const win = content.wrappedJSObject;
const hasRegistration = !!win.registration; const isExpectedUrl = win.location.href === _url;
return isExpectedUrl && hasRegistration; const hasRegistration = !!win.registration;
return isExpectedUrl && hasRegistration;
} catch (e) {
return false;
}
}) })
); );
} }

View file

@ -31,7 +31,6 @@ add_task(async function() {
"History listener got called after a content viewer was evicted" "History listener got called after a content viewer was evicted"
); );
legacySHistory.removeSHistoryListener(historyListener); legacySHistory.removeSHistoryListener(historyListener);
delete content._testListener;
// 6. Resolve the promise when we got our 'content viewer evicted' event // 6. Resolve the promise when we got our 'content viewer evicted' event
resolve(); resolve();
}, },

View file

@ -124,9 +124,17 @@ nsIDocShell* JSWindowActorChild::GetDocShell(ErrorResult& aRv) {
Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow( Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow(
ErrorResult& aRv) { ErrorResult& aRv) {
if (BrowsingContext* bc = GetBrowsingContext(aRv)) { if (!mManager) {
return WindowProxyHolder(bc); aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
} }
if (nsGlobalWindowInner* window = mManager->GetWindowGlobal()) {
if (window->IsCurrentInnerWindow()) {
return WindowProxyHolder(window->GetBrowsingContext());
}
}
return nullptr; return nullptr;
} }

View file

@ -2,6 +2,7 @@
support-files = support-files =
head.js head.js
[browser_contentWindow.js]
[browser_crash_report.js] [browser_crash_report.js]
[browser_destroy_callbacks.js] [browser_destroy_callbacks.js]
skip-if = !debug && (os == 'mac') #Bug 1604538 skip-if = !debug && (os == 'mac') #Bug 1604538

View file

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
declTest("contentWindow null when inner window inactive", {
matches: [TEST_URL + "*"],
url: TEST_URL + "?1",
async test(browser) {
{
let parent = browser.browsingContext.currentWindowGlobal;
let actorParent = parent.getActor("TestWindow");
await actorParent.sendQuery("storeActor");
}
{
let url = TEST_URL + "?2";
let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
await BrowserTestUtils.loadURI(browser, url);
await loaded;
}
let parent = browser.browsingContext.currentWindowGlobal;
let actorParent = parent.getActor("TestWindow");
let result = await actorParent.sendQuery("checkActor");
if (SpecialPowers.useRemoteSubframes) {
is(
result.status,
"error",
"Should get an error when bfcache is disabled for Fission"
);
is(
result.errorType,
"InvalidStateError",
"Should get an InvalidStateError without bfcache"
);
} else {
is(result.status, "success", "Should succeed when bfcache is enabled");
ok(
result.valueIsNull,
"Should get a null contentWindow when inner window is inactive"
);
}
},
});

View file

@ -35,7 +35,7 @@ declTest("sendQuery Error", {
is(error.name, "SyntaxError", "Error should have the correct name"); is(error.name, "SyntaxError", "Error should have the correct name");
is( is(
error.stack, error.stack,
"receiveMessage@resource://testing-common/TestWindowChild.jsm:33:31\n" + "receiveMessage@resource://testing-common/TestWindowChild.jsm:35:31\n" +
asyncStack, asyncStack,
"Error should have the correct stack" "Error should have the correct stack"
); );
@ -63,7 +63,7 @@ declTest("sendQuery Exception", {
); );
is( is(
error.stack, error.stack,
"receiveMessage@resource://testing-common/TestWindowChild.jsm:36:22\n" + "receiveMessage@resource://testing-common/TestWindowChild.jsm:38:22\n" +
asyncStack, asyncStack,
"Error should have the correct stack" "Error should have the correct stack"
); );

View file

@ -50,9 +50,10 @@ async function openPage(enableDialogs) {
const { Services } = ChromeUtils.import( const { Services } = ChromeUtils.import(
"resource://gre/modules/Services.jsm" "resource://gre/modules/Services.jsm"
); );
let win = content;
Services.obs.addObserver(doc => { Services.obs.addObserver(doc => {
if (content && doc == content.document) { if (doc == win.document) {
content.windowUtils[name](); win.windowUtils[name]();
} }
}, "document-element-inserted"); }, "document-element-inserted");
}); });

View file

@ -138,7 +138,11 @@ class ReftestFissionChild extends JSWindowActorChild {
} }
} }
flushWindow(this.contentWindow); // `contentWindow` will be null if the inner window for this actor
// has been navigated away from.
if (this.contentWindow) {
flushWindow(this.contentWindow);
}
if (anyPendingPaintsGeneratedInDescendants && if (anyPendingPaintsGeneratedInDescendants &&
!this.contentWindow.windowUtils.isMozAfterPaintPending) { !this.contentWindow.windowUtils.isMozAfterPaintPending) {

View file

@ -8,6 +8,8 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var EXPORTED_SYMBOLS = ["TestWindowChild"]; var EXPORTED_SYMBOLS = ["TestWindowChild"];
var docShellThunks = new Map();
class TestWindowChild extends JSWindowActorChild { class TestWindowChild extends JSWindowActorChild {
constructor() { constructor() {
super(); super();
@ -41,6 +43,31 @@ class TestWindowChild extends JSWindowActorChild {
case "noncloneReply": case "noncloneReply":
// Return a value which is non-cloneable, like a WindowProxy. // Return a value which is non-cloneable, like a WindowProxy.
return this.contentWindow; return this.contentWindow;
case "storeActor":
docShellThunks.set(this.docShell, this);
break;
case "checkActor": {
let actor = docShellThunks.get(this.docShell);
docShellThunks.delete(this.docShell);
let contentWindow;
let error;
try {
contentWindow = actor.contentWindow;
} catch (e) {
error = e;
}
if (error) {
return {
status: "error",
errorType: error.name,
};
}
return {
status: "success",
valueIsNull: contentWindow === null,
};
}
} }
return undefined; return undefined;

View file

@ -267,10 +267,10 @@ async function submitForm(browser) {
await SpecialPowers.spawn(browser, [], async function() { await SpecialPowers.spawn(browser, [], async function() {
content.document.querySelector("form").submit(); content.document.querySelector("form").submit();
let win = content;
await ContentTaskUtils.waitForCondition(() => { await ContentTaskUtils.waitForCondition(() => {
return ( return (
content.location.pathname == "/" && win.location.pathname == "/" && win.document.readyState == "complete"
content.document.readyState == "complete"
); );
}, "Wait for form submission load"); }, "Wait for form submission load");
}); });

View file

@ -457,10 +457,11 @@ async function submitForm(browser, action = "") {
info("Submitting form to:" + form.action); info("Submitting form to:" + form.action);
form.submit(); form.submit();
let win = content;
await ContentTaskUtils.waitForCondition(() => { await ContentTaskUtils.waitForCondition(() => {
return ( return (
content.location.pathname == actionPathname && win.location.pathname == actionPathname &&
content.document.readyState == "complete" win.document.readyState == "complete"
); );
}, "Wait for form submission load"); }, "Wait for form submission load");
}); });