Bug 1702678: Handle corner case when opener window is closed from a nested event loop during open. r=nika

Differential Revision: https://phabricator.services.mozilla.com/D111179
This commit is contained in:
Kris Maglione 2021-05-07 03:00:13 +00:00
Родитель 21a6efd6a3
Коммит 08a972cffb
4 изменённых файлов: 88 добавлений и 4 удалений

Просмотреть файл

@ -84,6 +84,11 @@ skip-if = true # Bug 1026815,Bug 1546159
[test_bug89419.xhtml]
[test_bug909218.html]
[test_bug92598.xhtml]
[test_open_and_immediately_close_opener.html]
# This bug only manifests in the non-e10s window open codepath. The test
# should be updated to make sure it still opens a new window in the parent
# process if and when we start running chrome mochitests with e10s enabled.
skip-if = e10s
[test_mozFrameType.xhtml]
[test_viewsource_forbidden_in_iframe.xhtml]
skip-if = true # bug 1019315

Просмотреть файл

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>Test for Bug 1702678</title>
<meta charset="utf-8">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1702678">Mozilla Bug 1702678</a>
<script type="application/javascript">
"use strict";
const HTML = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script>
// We need to queue a promise reaction job whilch will close the window
// during the nested event loop spun up by the window opening code.
Promise.resolve().then(() => {
window.close();
});
window.open("data:text/html,Hello");
<\/script>
</head>
</html>
`;
add_task(async function() {
// This bug only manifests when opening tabs in new windows.
await SpecialPowers.pushPrefEnv({
set: [["browser.link.open_newwindow", 2]],
});
// Create a window in a new BrowsingContextGroup so that it will be the last
// window in the group when it closes, and the group will be destroyed.
window.open(`data:text/html,${encodeURIComponent(HTML)}`, "", "noopener");
// Make a few trips through the event loop to ensure we've had a chance to
// open and close the relevant windows.
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, 0));
}
ok(true, "We didn't crash");
});
</script>
</body>
</html>

Просмотреть файл

@ -288,8 +288,23 @@ static already_AddRefed<BrowsingContext> CreateBrowsingContext(
RefPtr<BrowsingContext> opener;
if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) {
opener = aOpenWindowInfo->GetParent();
// Must create BrowsingContext with opener in-process.
MOZ_ASSERT_IF(opener, opener->IsInProcess());
if (opener) {
// Must create BrowsingContext with opener in-process.
MOZ_ASSERT(opener->IsInProcess());
// This can only happen when the opener was closed from a nested event
// loop in the window provider code, and only when the open was triggered
// by a non-e10s tab, and the new tab is being opened in a new browser
// window. Since it is a corner case among corner cases, and the opener
// window will appear to be null to consumers after it is discarded
// anyway, just drop the opener entirely.
if (opener->IsDiscarded()) {
NS_WARNING(
"Opener was closed from a nested event loop in the parent process. "
"Please fix this.");
opener = nullptr;
}
}
}
RefPtr<nsGlobalWindowInner> parentInner =

Просмотреть файл

@ -986,8 +986,18 @@ nsresult nsWindowWatcher::OpenWindowInternal(
// when its BrowsingContext was created, in order to ensure that the context
// is loaded within the correct BrowsingContextGroup.
if (windowIsNew && newBC->IsContent()) {
MOZ_RELEASE_ASSERT(newBC->GetOpenerId() == parentBC->Id());
MOZ_RELEASE_ASSERT(!!parentBC == newBC->HadOriginalOpener());
if (parentBC->IsDiscarded()) {
// If the parent BC was discarded in a nested event loop before we got
// to this point, we can't set it as the opener. Ideally we would still
// set `HadOriginalOpener()` in that case, but that's somewhat
// nontrivial, and not worth the effort given the nature of the corner
// case (see comment in `nsFrameLoader::CreateBrowsingContext`.
MOZ_RELEASE_ASSERT(newBC->GetOpenerId() == parentBC->Id() ||
newBC->GetOpenerId() == 0);
} else {
MOZ_RELEASE_ASSERT(newBC->GetOpenerId() == parentBC->Id());
MOZ_RELEASE_ASSERT(newBC->HadOriginalOpener());
}
} else {
// Update the opener for an existing or chrome BC.
newBC->SetOpener(parentBC);