From 3e6d2379f76cfd5e9775689712a99700a867fdfc Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 10 Feb 2022 11:06:41 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1191491 - Do not dispatch an audio-playback notification when swapping browsers; r=smaug (dacb2ff694) - Bug 1192252 - Add a helper RAII class for managing the cleanup in nsFrameLoader::SwapWithOtherLoader; r=smaug (362488db65) --- docshell/base/nsDocShell.cpp | 14 ++++++ docshell/base/nsDocShell.h | 7 +++ dom/base/nsFrameLoader.cpp | 82 +++++++++++++++++++++++++---------- dom/base/nsFrameLoader.h | 2 + dom/html/HTMLMediaElement.cpp | 10 ++++- dom/ipc/TabChild.cpp | 7 +++ 6 files changed, 97 insertions(+), 25 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index ff04e25362..6a4051571b 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -782,6 +782,7 @@ nsDocShell::nsDocShell() , mUseRemoteTabs(false) , mDeviceSizeIsPageSize(false) , mWindowDraggingAllowed(false) + , mInFrameSwap(false) , mCanExecuteScripts(false) , mFiredUnloadEvent(false) , mEODForCurrentDocument(false) @@ -14243,3 +14244,16 @@ nsDocShell::GetPaymentRequestId(nsAString& aPaymentRequestId) aPaymentRequestId = GetInheritedPaymentRequestId(); return NS_OK; } + +bool +nsDocShell::InFrameSwap() +{ + nsRefPtr shell = this; + do { + if (shell->mInFrameSwap) { + return true; + } + shell = shell->GetParentDocshell(); + } while (shell); + return false; +} diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index a9a73a1478..4b33c0ca43 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -262,6 +262,12 @@ public: // is no longer applied void NotifyAsyncPanZoomStopped(); + void SetInFrameSwap(bool aInSwap) + { + mInFrameSwap = aInSwap; + } + bool InFrameSwap(); + private: // An observed docshell wrapper is created when recording markers is enabled. mozilla::UniquePtr mObserved; @@ -910,6 +916,7 @@ protected: bool mUseRemoteTabs; bool mDeviceSizeIsPageSize; bool mWindowDraggingAllowed; + bool mInFrameSwap; // Because scriptability depends on the mAllowJavascript values of our // ancestors, we cache the effective scriptability and recompute it when diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index c7145c6772..a4ad730aa9 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -939,6 +939,60 @@ nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther, return NS_OK; } +class MOZ_STACK_CLASS AutoResetInFrameSwap final +{ +public: + AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader, + nsFrameLoader* aOtherFrameLoader, + nsDocShell* aThisDocShell, + nsDocShell* aOtherDocShell, + EventTarget* aThisEventTarget, + EventTarget* aOtherEventTarget + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mThisFrameLoader(aThisFrameLoader) + , mOtherFrameLoader(aOtherFrameLoader) + , mThisDocShell(aThisDocShell) + , mOtherDocShell(aOtherDocShell) + , mThisEventTarget(aThisEventTarget) + , mOtherEventTarget(aOtherEventTarget) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + + mThisFrameLoader->mInSwap = true; + mOtherFrameLoader->mInSwap = true; + mThisDocShell->SetInFrameSwap(true); + mOtherDocShell->SetInFrameSwap(true); + + // Fire pageshow events on still-loading pages, and then fire pagehide + // events. Note that we do NOT fire these in the normal way, but just fire + // them on the chrome event handlers. + nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, false); + nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, false); + nsContentUtils::FirePageHideEvent(mThisDocShell, mThisEventTarget); + nsContentUtils::FirePageHideEvent(mOtherDocShell, mOtherEventTarget); + } + + ~AutoResetInFrameSwap() + { + nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, true); + nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, true); + + mThisFrameLoader->mInSwap = false; + mOtherFrameLoader->mInSwap = false; + mThisDocShell->SetInFrameSwap(false); + mOtherDocShell->SetInFrameSwap(false); + } + +private: + nsRefPtr mThisFrameLoader; + nsRefPtr mOtherFrameLoader; + nsRefPtr mThisDocShell; + nsRefPtr mOtherDocShell; + nsCOMPtr mThisEventTarget; + nsCOMPtr mOtherEventTarget; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, nsRefPtr& aFirstToSwap, @@ -975,8 +1029,8 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, return NS_ERROR_DOM_SECURITY_ERR; } - nsCOMPtr ourDocshell = GetExistingDocShell(); - nsCOMPtr otherDocshell = aOther->GetExistingDocShell(); + nsRefPtr ourDocshell = static_cast(GetExistingDocShell()); + nsRefPtr otherDocshell = static_cast(aOther->GetExistingDocShell()); if (!ourDocshell || !otherDocshell) { // How odd return NS_ERROR_NOT_IMPLEMENTED; @@ -1107,39 +1161,23 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, if (mInSwap || aOther->mInSwap) { return NS_ERROR_NOT_IMPLEMENTED; } - mInSwap = aOther->mInSwap = true; + AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell, + ourEventTarget, otherEventTarget); - // Fire pageshow events on still-loading pages, and then fire pagehide - // events. Note that we do NOT fire these in the normal way, but just fire - // them on the chrome event handlers. - nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, false); - nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, false); - nsContentUtils::FirePageHideEvent(ourDocshell, ourEventTarget); - nsContentUtils::FirePageHideEvent(otherDocshell, otherEventTarget); - nsIFrame* ourFrame = ourContent->GetPrimaryFrame(); nsIFrame* otherFrame = otherContent->GetPrimaryFrame(); if (!ourFrame || !otherFrame) { - mInSwap = aOther->mInSwap = false; - nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, true); - nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true); return NS_ERROR_NOT_IMPLEMENTED; } nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); if (!ourFrameFrame) { - mInSwap = aOther->mInSwap = false; - nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, true); - nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true); return NS_ERROR_NOT_IMPLEMENTED; } // OK. First begin to swap the docshells in the two nsIFrames rv = ourFrameFrame->BeginSwapDocShells(otherFrame); if (NS_FAILED(rv)) { - mInSwap = aOther->mInSwap = false; - nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, true); - nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true); return rv; } @@ -1242,10 +1280,6 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, ourParentDocument->FlushPendingNotifications(Flush_Layout); otherParentDocument->FlushPendingNotifications(Flush_Layout); - nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, true); - nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true); - - mInSwap = aOther->mInSwap = false; return NS_OK; } diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h index 39c393d68d..1ffc1610fb 100644 --- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -31,6 +31,7 @@ class nsSubDocumentFrame; class nsView; class nsIInProcessContentFrameMessageManager; class AutoResetInShow; +class AutoResetInFrameSwap; class nsITabParent; class nsIDocShellTreeItem; class nsIDocShellTreeOwner; @@ -58,6 +59,7 @@ class nsFrameLoader final : public nsIFrameLoader, public mozilla::dom::ipc::MessageManagerCallback { friend class AutoResetInShow; + friend class AutoResetInFrameSwap; typedef mozilla::dom::PBrowserParent PBrowserParent; typedef mozilla::dom::TabParent TabParent; typedef mozilla::layout::RenderFrameParent RenderFrameParent; diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index b9fab96fad..3082a4df35 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -102,6 +102,7 @@ static PRLogModuleInfo* gMediaElementEventsLog; #include "nsIPermissionManager.h" #include "nsContentTypeParser.h" +#include "nsDocShell.h" #include "mozilla/EventStateManager.h" @@ -4016,7 +4017,14 @@ void HTMLMediaElement::NotifyOwnerDocumentActivityChanged() if (pauseElement && mAudioChannelAgent) { // If the element is being paused since we are navigating away from the // document, notify the audio channel agent. - NotifyAudioChannelAgent(false); + // Be careful to ignore this event during a docshell frame swap. + auto docShell = static_cast(OwnerDoc()->GetDocShell()); + if (!docShell) { + return; + } + if (!docShell->InFrameSwap()) { + NotifyAudioChannelAgent(false); + } } } diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 8c4e8d774e..18c91344de 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -2429,11 +2429,18 @@ TabChild::RecvSwappedWithOtherRemoteLoader() return true; } + nsRefPtr docShell = static_cast(ourDocShell.get()); + nsCOMPtr ourEventTarget = ourWindow->GetParentTarget(); + docShell->SetInFrameSwap(true); + nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, false); nsContentUtils::FirePageHideEvent(ourDocShell, ourEventTarget); nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, true); + + docShell->SetInFrameSwap(false); + return true; }