Bug 1640766 - Make nsGlobalWindowInner::Suspend/Resume suspend/resume all in-process descendants; r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D107762
This commit is contained in:
Edgar Chen 2021-03-12 16:23:10 +00:00
Родитель f16dee35cf
Коммит c5688b603e
6 изменённых файлов: 158 добавлений и 40 удалений

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

@ -5452,10 +5452,11 @@ void nsGlobalWindowInner::Suspend(bool aIncludeSubWindows) {
return;
}
// All children are also suspended. This ensure mSuspendDepth is
// set properly and the timers are properly canceled for each child.
// All in-process descendants are also suspended. This ensure mSuspendDepth
// is set properly and the timers are properly canceled for each in-process
// descendant.
if (aIncludeSubWindows) {
CallOnInProcessChildren(&nsGlobalWindowInner::Suspend, aIncludeSubWindows);
CallOnInProcessDescendants(&nsGlobalWindowInner::Suspend, false);
}
mSuspendDepth += 1;
@ -5501,10 +5502,11 @@ void nsGlobalWindowInner::Resume(bool aIncludeSubWindows) {
return;
}
// Resume all children. This restores timers recursively canceled
// in Suspend() and ensures all children have the correct mSuspendDepth.
// Resume all in-process descendants. This restores timers recursively
// canceled in Suspend() and ensures all in-process descendants have the
// correct mSuspendDepth.
if (aIncludeSubWindows) {
CallOnInProcessChildren(&nsGlobalWindowInner::Resume, aIncludeSubWindows);
CallOnInProcessDescendants(&nsGlobalWindowInner::Resume, false);
}
if (mSuspendDepth == 0) {
@ -5683,43 +5685,36 @@ void nsGlobalWindowInner::SyncStateFromParentWindow() {
}
template <typename Method, typename... Args>
CallState nsGlobalWindowInner::CallOnInProcessChildren(Method aMethod,
Args&... aArgs) {
CallState nsGlobalWindowInner::CallOnInProcessDescendantsInternal(
BrowsingContext* aBrowsingContext, bool aChildOnly, Method aMethod,
Args&&... aArgs) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(IsCurrentInnerWindow());
MOZ_ASSERT(aBrowsingContext);
CallState state = CallState::Continue;
for (const RefPtr<BrowsingContext>& bc : aBrowsingContext->Children()) {
if (nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow()) {
auto* win = nsGlobalWindowOuter::Cast(pWin);
if (nsGlobalWindowInner* inner = win->GetCurrentInnerWindowInternal()) {
// Call the descendant method using our helper CallDescendant() template
// method. This allows us to handle both void returning methods and
// methods that return CallState explicitly. For void returning methods
// we assume CallState::Continue.
typedef decltype((inner->*aMethod)(aArgs...)) returnType;
state = CallDescendant<returnType>(inner, aMethod, aArgs...);
nsCOMPtr<nsIDocShell> docShell = GetDocShell();
if (!docShell) {
return state;
}
for (const RefPtr<BrowsingContext>& bc : GetBrowsingContext()->Children()) {
nsCOMPtr<nsPIDOMWindowOuter> pWin = bc->GetDOMWindow();
if (!pWin) {
continue;
if (state == CallState::Stop) {
return state;
}
}
}
auto* win = nsGlobalWindowOuter::Cast(pWin);
nsGlobalWindowInner* inner = win->GetCurrentInnerWindowInternal();
// This is a bit hackish. Only freeze/suspend windows which are truly our
// subwindows.
nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
continue;
}
// Call the child method using our helper CallChild() template method.
// This allows us to handle both void returning methods and methods
// that return CallState explicitly. For void returning methods we
// assume CallState::Continue.
typedef decltype((inner->*aMethod)(aArgs...)) returnType;
state = CallChild<returnType>(inner, aMethod, aArgs...);
if (state == CallState::Stop) {
return state;
if (!aChildOnly) {
state = CallOnInProcessDescendantsInternal(bc.get(), aChildOnly, aMethod,
aArgs...);
if (state == CallState::Stop) {
return state;
}
}
}

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

@ -1038,17 +1038,38 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
nsPIDOMWindowOuter* GetInProcessParentInternal();
private:
template <typename Method, typename... Args>
mozilla::CallState CallOnInProcessDescendantsInternal(
mozilla::dom::BrowsingContext* aBrowsingContext, bool aChildOnly,
Method aMethod, Args&&... aArgs);
// Call the given method on the immediate children of this window. The
// CallState returned by the last child method invocation is returned or
// CallState::Continue if the method returns void.
template <typename Method, typename... Args>
mozilla::CallState CallOnInProcessChildren(Method aMethod, Args&... aArgs);
mozilla::CallState CallOnInProcessChildren(Method aMethod, Args&&... aArgs) {
MOZ_ASSERT(IsCurrentInnerWindow());
return CallOnInProcessDescendantsInternal(GetBrowsingContext(), true,
aMethod, aArgs...);
}
// Call the given method on the descendant of this window. The CallState
// returned by the last descendant method invocation is returned or
// CallState::Continue if the method returns void.
template <typename Method, typename... Args>
mozilla::CallState CallOnInProcessDescendants(Method aMethod,
Args&&... aArgs) {
MOZ_ASSERT(IsCurrentInnerWindow());
return CallOnInProcessDescendantsInternal(GetBrowsingContext(), false,
aMethod, aArgs...);
}
// Helper to convert a void returning child method into an implicit
// CallState::Continue value.
template <typename Return, typename Method, typename... Args>
typename std::enable_if<std::is_void<Return>::value, mozilla::CallState>::type
CallChild(nsGlobalWindowInner* aWindow, Method aMethod, Args&... aArgs) {
CallDescendant(nsGlobalWindowInner* aWindow, Method aMethod,
Args&&... aArgs) {
(aWindow->*aMethod)(aArgs...);
return mozilla::CallState::Continue;
}
@ -1057,7 +1078,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
template <typename Return, typename Method, typename... Args>
typename std::enable_if<std::is_same<Return, mozilla::CallState>::value,
mozilla::CallState>::type
CallChild(nsGlobalWindowInner* aWindow, Method aMethod, Args&... aArgs) {
CallDescendant(nsGlobalWindowInner* aWindow, Method aMethod,
Args&&... aArgs) {
return (aWindow->*aMethod)(aArgs...);
}

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

@ -0,0 +1,20 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Iframe 1 for Bug 1640766</title>
</head>
<body>
<div>Iframe 1</div>
<script type="application/javascript">
if (parent == window) {
let iframe = document.createElement("iframe");
iframe.src = "http://example.org/tests/dom/base/test/iframe2_bug1640766.html";
document.body.appendChild(iframe);
} else {
window.onload = function() {
top.opener.postMessage("ready", "*");
};
}
</script>
</body>
</html>

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

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Iframe 2 for Bug 1640766</title>
</head>
<body>
<div>Iframe 2</div>
<iframe src="http://mochi.test:8888/tests/dom/base/test/iframe1_bug1640766.html"></iframe>
</body>
</html>

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

@ -603,6 +603,10 @@ support-files = file_bug1100912.html
[test_bug1499169.html]
skip-if = toolkit == 'android' # Timeouts on android due to page closing issues with embedded pdf
[test_bug1576154.html]
[test_bug1640766.html]
support-files =
iframe1_bug1640766.html
iframe2_bug1640766.html
[test_bug1648887.html]
[test_caretPositionFromPoint.html]
[test_change_policy.html]

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

@ -0,0 +1,67 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1640766
-->
<head>
<title>Test for Bug 1640766</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1640766">Mozilla Bug 1640766</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1640766 **/
function waitForMessage(aMsg) {
return new Promise((aResolve) => {
window.addEventListener("message", function handler(e) {
if (e.data != aMsg) {
return;
}
info(`receive: ${e.data}`);
window.removeEventListener("message", handler);
aResolve(e.source);
});
});
}
async function testSuspend(aWindow) {
let timerRan = false;
let timer = aWindow.setTimeout(function() {
timerRan = true;
}, 0);
return new Promise((aResolve) => {
setTimeout(function() {
ok(!timerRan, "timer should not run as the window is suspended");
clearTimeout(timer);
aResolve();
}, 0);
});
}
add_task(async function() {
let w = window.open("iframe1_bug1640766.html");
let inner = await waitForMessage("ready");
var utils = SpecialPowers.getDOMWindowUtils(w);
utils.enterModalState();
await testSuspend(w);
await testSuspend(inner);
utils.leaveModalState();
w.close();
});
</script>
</pre>
</body>
</html>