diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index c594887e0e2f..38011fb50caa 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -1635,13 +1635,13 @@ nsDOMWindowUtils::DisableNonTestMouseEvents(bool aDisable) { NS_IMETHODIMP nsDOMWindowUtils::SuppressEventHandling(bool aSuppress) { - nsCOMPtr doc = GetDocument(); - NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + nsCOMPtr window = do_QueryReferent(mWindow); + NS_ENSURE_STATE(window); if (aSuppress) { - doc->SuppressEventHandling(); + window->SuppressEventHandling(); } else { - doc->UnsuppressEventHandlingAndFireEvents(true); + window->UnsuppressEventHandling(); } return NS_OK; diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index a6dbe2d7135e..0532d6bc3ceb 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -1492,7 +1492,7 @@ void nsGlobalWindowOuter::ShutDown() { void nsGlobalWindowOuter::DropOuterWindowDocs() { MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed()); mDoc = nullptr; - mSuspendedDoc = nullptr; + mSuspendedDocs.Clear(); } void nsGlobalWindowOuter::CleanUp() { @@ -1597,7 +1597,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowOuter) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDocs) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal) @@ -1629,7 +1629,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter) NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDocs) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal) @@ -2157,9 +2157,9 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, mDelayedCloseForPrinting = false; mDelayedPrintUntilAfterLoad = false; - // Take this opportunity to clear mSuspendedDoc. Our old inner window is now + // Take this opportunity to clear mSuspendedDocs. Our old inner window is now // responsible for unsuspending it. - mSuspendedDoc = nullptr; + mSuspendedDocs.Clear(); #ifdef DEBUG mLastOpenedURI = aDocument->GetDocumentURI(); @@ -6356,6 +6356,44 @@ void nsGlobalWindowOuter::ReallyCloseWindow() { CleanUp(); } +void nsGlobalWindowOuter::SuppressEventHandling() { + if (mSuppressEventHandlingDepth == 0) { + if (BrowsingContext* bc = GetBrowsingContext()) { + bc->PreOrderWalk([&](BrowsingContext* aBC) { + if (nsCOMPtr win = aBC->GetDOMWindow()) { + if (RefPtr doc = win->GetExtantDoc()) { + mSuspendedDocs.AppendElement(doc); + // 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(); + } + } + }); + } + } + mSuppressEventHandlingDepth++; +} + +void nsGlobalWindowOuter::UnsuppressEventHandling() { + MOZ_ASSERT(mSuppressEventHandlingDepth != 0); + mSuppressEventHandlingDepth--; + + if (mSuppressEventHandlingDepth == 0 && mSuspendedDocs.Length()) { + RefPtr currentDoc = GetExtantDoc(); + bool fireEvent = currentDoc == mSuspendedDocs[0]; + nsTArray> suspendedDocs = std::move(mSuspendedDocs); + for (const auto& doc : suspendedDocs) { + doc->UnsuppressEventHandlingAndFireEvents(fireEvent); + } + } +} + nsGlobalWindowOuter* nsGlobalWindowOuter::EnterModalState() { // GetInProcessScriptableTop, not GetInProcessTop, so that EnterModalState // works properly with + + diff --git a/dom/base/test/file_suppressed_events_top_modalstate.html b/dom/base/test/file_suppressed_events_top_modalstate.html new file mode 100644 index 000000000000..e20938bb2886 --- /dev/null +++ b/dom/base/test/file_suppressed_events_top_modalstate.html @@ -0,0 +1,79 @@ + + + +Test event suppression + + + + + + + +
Top
+ + + + diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 80c10ea38a14..413fba39ff08 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -778,6 +778,8 @@ support-files = skip-if = os == "android" support-files = file_suppressed_events_top_xhr.html + file_suppressed_events_top_modalstate.html + file_suppressed_events_top.html file_suppressed_events_middle.html file_suppressed_events_inner.html !/gfx/layers/apz/test/mochitest/apz_test_utils.js diff --git a/dom/base/test/test_suppressed_events_nested_iframe.html b/dom/base/test/test_suppressed_events_nested_iframe.html index b9865ec00fe7..1d31c7c5d60b 100644 --- a/dom/base/test/test_suppressed_events_nested_iframe.html +++ b/dom/base/test/test_suppressed_events_nested_iframe.html @@ -43,6 +43,28 @@ add_task(async function test_sync_xhr() { w.close(); }); +add_task(async function test_modalstate() { + await SpecialPowers.pushPrefEnv({"set": [ + ["test.events.async.enabled", false], + ["dom.events.coalesce.mousemove", false], + ]}); + + let w = window.open("file_suppressed_events_top_modalstate.html"); + await waitForMessage("done"); + w.close(); +}); + +add_task(async function test_suppress_event_handling() { + await SpecialPowers.pushPrefEnv({"set": [ + ["test.events.async.enabled", false], + ["dom.events.coalesce.mousemove", false], + ]}); + + let w = window.open("file_suppressed_events_top.html"); + await waitForMessage("done"); + w.close(); +}); +