зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1588259: Part 1 - Suspend windows when spinning event loop for window.open. r=smaug
This doesn't solve all problems with potential reentrancy during window.open nested event loops, but it does improve the situation somewhat. Since any window in the same BrowsingContextGroup can target any window in the same group, we need to suspend all windows in the group, not just the root of the new window's parent. We also need to make sure we suspend all in-process windows, even if we have out-of-process frames somewhere in the parent chain. This patch takes care of suspending timeouts and input event handling in all of these cases. It doesn't block all potential paths for running code in the suspended windows, though, so the next patch explicitly prevents the problematic reentrancy. Differential Revision: https://phabricator.services.mozilla.com/D57666 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
d98bab47bb
Коммит
29c31af3ee
|
@ -20,7 +20,7 @@
|
|||
#include "imgRequestProxy.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/Array.h" // JS::NewArrayObject
|
||||
#include "js/Array.h" // JS::NewArrayObject
|
||||
#include "js/ArrayBuffer.h" // JS::{GetArrayBufferData,IsArrayBufferObject,NewArrayBuffer}
|
||||
#include "js/JSON.h"
|
||||
#include "js/RegExp.h" // JS::ExecuteRegExpNoStatics, JS::NewUCRegExpObject, JS::RegExpFlags
|
||||
|
@ -533,6 +533,47 @@ class SameOriginCheckerImpl final : public nsIChannelEventSink,
|
|||
|
||||
} // namespace
|
||||
|
||||
AutoSuppressEventHandlingAndSuspend::AutoSuppressEventHandlingAndSuspend(
|
||||
BrowsingContextGroup* aGroup) {
|
||||
for (const auto& bc : aGroup->Toplevels()) {
|
||||
SuppressBrowsingContext(bc);
|
||||
}
|
||||
}
|
||||
|
||||
void AutoSuppressEventHandlingAndSuspend::SuppressBrowsingContext(
|
||||
BrowsingContext* aBC) {
|
||||
if (nsCOMPtr<nsPIDOMWindowOuter> win = aBC->GetDOMWindow()) {
|
||||
if (RefPtr<Document> doc = win->GetExtantDoc()) {
|
||||
mDocuments.AppendElement(doc);
|
||||
mWindows.AppendElement(win->GetCurrentInnerWindow());
|
||||
// Note: Document::SuppressEventHandling will also automatically suppress
|
||||
// event handling for any in-process sub-documents. However, since we need
|
||||
// to deal with cases where remote BrowsingContexts may be interleaved
|
||||
// with in-process ones, we still need to walk the entire tree ourselves.
|
||||
// This may be slightly redundant in some cases, but since event handling
|
||||
// suppressions maintain a count of current blockers, it does not cause
|
||||
// any problems.
|
||||
doc->SuppressEventHandling();
|
||||
win->GetCurrentInnerWindow()->Suspend();
|
||||
}
|
||||
}
|
||||
|
||||
BrowsingContext::Children children;
|
||||
aBC->GetChildren(children);
|
||||
for (const auto& bc : children) {
|
||||
SuppressBrowsingContext(bc);
|
||||
}
|
||||
}
|
||||
|
||||
AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
|
||||
for (const auto& win : mWindows) {
|
||||
win->Resume();
|
||||
}
|
||||
for (const auto& doc : mDocuments) {
|
||||
doc->UnsuppressEventHandlingAndFireEvents(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to determine whether or not the user is currently
|
||||
* interacting with the browser. It listens to observer events to toggle the
|
||||
|
|
|
@ -133,6 +133,8 @@ class TextEditor;
|
|||
enum class StorageAccess;
|
||||
|
||||
namespace dom {
|
||||
class BrowsingContext;
|
||||
class BrowsingContextGroup;
|
||||
class ContentFrameMessageManager;
|
||||
struct CustomElementDefinition;
|
||||
class DataTransfer;
|
||||
|
@ -3415,6 +3417,26 @@ class MOZ_STACK_CLASS nsAutoScriptBlockerSuppressNodeRemoved
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* Suppresses event handling and suspends the active inner window for all
|
||||
* in-process documents in a BrowsingContextGroup. This should be used while
|
||||
* spinning the event loop for a synchronous operation (like `window.open()`)
|
||||
* which affects operations in any other window in the same BrowsingContext
|
||||
* group.
|
||||
*/
|
||||
|
||||
class MOZ_RAII AutoSuppressEventHandlingAndSuspend {
|
||||
public:
|
||||
explicit AutoSuppressEventHandlingAndSuspend(BrowsingContextGroup* aGroup);
|
||||
~AutoSuppressEventHandlingAndSuspend();
|
||||
|
||||
private:
|
||||
void SuppressBrowsingContext(BrowsingContext* aBC);
|
||||
|
||||
AutoTArray<RefPtr<Document>, 16> mDocuments;
|
||||
AutoTArray<nsCOMPtr<nsPIDOMWindowInner>, 16> mWindows;
|
||||
};
|
||||
|
||||
class TreeOrderComparator {
|
||||
public:
|
||||
bool Equals(nsINode* aElem1, nsINode* aElem2) const {
|
||||
|
|
|
@ -1059,15 +1059,6 @@ nsresult ContentChild::ProvideWindowCommon(
|
|||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
|
||||
if (aParent) {
|
||||
nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow =
|
||||
nsPIDOMWindowOuter::From(aParent)->GetInProcessTop();
|
||||
if (parentTopWindow) {
|
||||
parentTopInnerWindow = parentTopWindow->GetCurrentInnerWindow();
|
||||
}
|
||||
}
|
||||
|
||||
// Set to true when we're ready to return from this function.
|
||||
bool ready = false;
|
||||
|
||||
|
@ -1236,12 +1227,15 @@ nsresult ContentChild::ProvideWindowCommon(
|
|||
}
|
||||
});
|
||||
|
||||
// Suspend our window if we have one to make sure we don't re-enter it.
|
||||
if (parentTopInnerWindow) {
|
||||
parentTopInnerWindow->Suspend();
|
||||
}
|
||||
|
||||
{
|
||||
// Suppress event handling for all contexts in our BrowsingContextGroup so
|
||||
// that event handlers cannot target our new window while it's still being
|
||||
// opened. Note that pending events that were suppressed while our blocker
|
||||
// was active will be dispatched asynchronously from a runnable dispatched
|
||||
// to the main event loop after this function returns, not immediately when
|
||||
// we leave this scope.
|
||||
AutoSuppressEventHandlingAndSuspend seh(browsingContext->Group());
|
||||
|
||||
AutoNoJSAPI nojsapi;
|
||||
|
||||
// Spin the event loop until we get a response. Callers of this function
|
||||
|
@ -1254,10 +1248,6 @@ nsresult ContentChild::ProvideWindowCommon(
|
|||
"loop without ready being true.");
|
||||
}
|
||||
|
||||
if (parentTopInnerWindow) {
|
||||
parentTopInnerWindow->Resume();
|
||||
}
|
||||
|
||||
// =====================
|
||||
// End Nested Event Loop
|
||||
// =====================
|
||||
|
|
Загрузка…
Ссылка в новой задаче