diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index 18ff941db547..f5f2c1f566bd 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -13,9 +13,12 @@ #include "mozilla/dom/BrowsingContextBinding.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/Location.h" #include "mozilla/dom/LocationBinding.h" #include "mozilla/dom/WindowBinding.h" +#include "mozilla/dom/WindowGlobalChild.h" +#include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/WindowProxyHolder.h" #include "mozilla/Assertions.h" #include "mozilla/ClearOnShutdown.h" @@ -201,6 +204,27 @@ void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) { mIsInProcess = true; } +void BrowsingContext::SetEmbedderElement(Element* aEmbedder) { + mEmbedderElement = aEmbedder; + + // Notify the parent process of the embedding status. We don't need to do + // this when clearing our embedder, as we're being destroyed either way. + if (mEmbedderElement) { + nsCOMPtr embedderGlobal = + do_QueryInterface(mEmbedderElement->GetOwnerGlobal()); + RefPtr wgc = embedderGlobal->GetWindowGlobalChild(); + + // If we're in-process, synchronously perform the update to ensure we don't + // get out of sync. + // XXX(nika): This is super gross, and I don't like it one bit. + if (RefPtr wgp = wgc->GetParentActor()) { + Canonical()->SetEmbedderWindowGlobal(wgp); + } else { + wgc->SendDidEmbedBrowsingContext(this); + } + } +} + void BrowsingContext::Attach(bool aFromIPC) { MOZ_LOG(GetLog(), LogLevel::Debug, ("%s: %s 0x%08" PRIx64 " to 0x%08" PRIx64, @@ -518,7 +542,8 @@ bool BrowsingContext::GetUserGestureActivation() { NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent, mGroup) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell, mChildren, mParent, mGroup, + mEmbedderElement) if (XRE_IsParentProcess()) { CanonicalBrowsingContext::Cast(tmp)->Unlink(); } @@ -526,7 +551,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell, mChildren, mParent, mGroup) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell, mChildren, mParent, mGroup, + mEmbedderElement) if (XRE_IsParentProcess()) { CanonicalBrowsingContext::Cast(tmp)->Traverse(cb); } diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index 0b8e8f5e65c5..cad6da80849c 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -46,6 +46,7 @@ class BrowsingContent; class BrowsingContextGroup; class CanonicalBrowsingContext; class ContentParent; +class Element; template struct Nullable; template @@ -123,6 +124,11 @@ class BrowsingContext : public nsWrapperCache, void SetDocShell(nsIDocShell* aDocShell); void ClearDocShell() { mDocShell = nullptr; } + // Get the embedder element for this BrowsingContext if the embedder is + // in-process, or null if it's not. + Element* GetEmbedderElement() const { return mEmbedderElement; } + void SetEmbedderElement(Element* aEmbedder); + // Get the outer window object for this BrowsingContext if it is in-process // and still has a docshell, or null otherwise. nsPIDOMWindowOuter* GetDOMWindow() const { @@ -439,6 +445,8 @@ class BrowsingContext : public nsWrapperCache, Children mChildren; nsCOMPtr mDocShell; + RefPtr mEmbedderElement; + // This is not a strong reference, but using a JS::Heap for that should be // fine. The JSObject stored in here should be a proxy with a // nsOuterWindowProxy handler, which will update the pointer from its diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index 5af235c4fb72..46619f67792b 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -116,6 +116,20 @@ void CanonicalBrowsingContext::SetCurrentWindowGlobal( mCurrentWindowGlobal = aGlobal; } +void CanonicalBrowsingContext::SetEmbedderWindowGlobal( + WindowGlobalParent* aGlobal) { + MOZ_RELEASE_ASSERT(aGlobal, "null embedder"); + if (RefPtr parent = GetParent()) { + MOZ_RELEASE_ASSERT(aGlobal->BrowsingContext() == parent, + "Embedder has incorrect browsing context"); + } else { + MOZ_RELEASE_ASSERT(aGlobal->IsInProcess(), + "Toplevel must have a parent-process embedder"); + } + + mEmbedderWindowGlobal = aGlobal; +} + bool CanonicalBrowsingContext::ValidateTransaction( const Transaction& aTransaction, ContentParent* aProcess) { // Check that the correct process is performing sets for transactions with @@ -137,12 +151,14 @@ JSObject* CanonicalBrowsingContext::WrapObject( void CanonicalBrowsingContext::Traverse( nsCycleCollectionTraversalCallback& cb) { CanonicalBrowsingContext* tmp = this; - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobals); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobals, mCurrentWindowGlobal, + mEmbedderWindowGlobal); } void CanonicalBrowsingContext::Unlink() { CanonicalBrowsingContext* tmp = this; - NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobals); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobals, mCurrentWindowGlobal, + mEmbedderWindowGlobal); } void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() { diff --git a/docshell/base/CanonicalBrowsingContext.h b/docshell/base/CanonicalBrowsingContext.h index 74de53dd05fa..cd639dab7009 100644 --- a/docshell/base/CanonicalBrowsingContext.h +++ b/docshell/base/CanonicalBrowsingContext.h @@ -52,6 +52,11 @@ class CanonicalBrowsingContext final : public BrowsingContext { } void SetCurrentWindowGlobal(WindowGlobalParent* aGlobal); + WindowGlobalParent* GetEmbedderWindowGlobal() const { + return mEmbedderWindowGlobal; + } + void SetEmbedderWindowGlobal(WindowGlobalParent* aGlobal); + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -97,6 +102,7 @@ class CanonicalBrowsingContext final : public BrowsingContext { // All live window globals within this browsing context. nsTHashtable> mWindowGlobals; RefPtr mCurrentWindowGlobal; + RefPtr mEmbedderWindowGlobal; // Generation information for each content process which has interacted with // this CanonicalBrowsingContext, by ChildID. diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 5f198e773a92..8c423dca25fa 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -1893,6 +1893,10 @@ void nsFrameLoader::SetOwnerContent(Element* aContent) { } mOwnerContent = aContent; + if (RefPtr browsingContext = GetBrowsingContext()) { + browsingContext->SetEmbedderElement(mOwnerContent); + } + AutoJSAPI jsapi; jsapi.Init(); @@ -2009,6 +2013,8 @@ nsresult nsFrameLoader::MaybeCreateDocShell() { RefPtr docShell = nsDocShell::Create(mBrowsingContext); NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); + mBrowsingContext->SetEmbedderElement(mOwnerContent); + mIsTopLevelContent = mBrowsingContext->IsContent() && !mBrowsingContext->GetParent(); if (!mNetworkCreated && !mIsTopLevelContent) { @@ -2604,6 +2610,8 @@ bool nsFrameLoader::TryRemoteBrowser() { // If we're in a content process, create a BrowserBridgeChild actor. if (XRE_IsContentProcess()) { + mBrowsingContext->SetEmbedderElement(mOwnerContent); + mBrowserBridgeChild = BrowserBridgeChild::Create( this, context, NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), mBrowsingContext); diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl index 918c7bb9571d..e093d19df5be 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl @@ -22,6 +22,8 @@ interface BrowsingContext { readonly attribute nsIDocShell? docShell; + readonly attribute Element? embedderElement; + readonly attribute unsigned long long id; readonly attribute BrowsingContext? opener; @@ -41,6 +43,8 @@ interface CanonicalBrowsingContext : BrowsingContext { [Throws] readonly attribute DOMString? currentRemoteType; + readonly attribute WindowGlobalParent? embedderWindowGlobal; + void notifyStartDelayedAutoplayMedia(); }; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 47eb5f9eaeff..bd234a7f4183 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1148,6 +1148,8 @@ TabParent* ContentParent::CreateBrowser(const TabContext& aContext, } } + aBrowsingContext->SetEmbedderElement(aFrameElement); + // Ensure that our content process is subscribed to our newly created // BrowsingContextGroup. aBrowsingContext->Group()->EnsureSubscribed(constructorSender); diff --git a/dom/ipc/PWindowGlobal.ipdl b/dom/ipc/PWindowGlobal.ipdl index f6b80cf95b26..db03c962de2a 100644 --- a/dom/ipc/PWindowGlobal.ipdl +++ b/dom/ipc/PWindowGlobal.ipdl @@ -36,6 +36,10 @@ parent: /// Notify the parent that this PWindowGlobal is now the current global. async BecomeCurrentWindowGlobal(); + /// Notify the parent that this PWindowGlobal has embedded the given + /// BrowsingContext. + async DidEmbedBrowsingContext(BrowsingContext aContext); + async Destroy(); }; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 4c802d4cebb4..507981c7b6fb 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -334,6 +334,21 @@ void TabParent::SetOwnerElement(Element* aElement) { if (mRenderFrame.IsInitialized()) { mRenderFrame.OwnerContentChanged(); } + + // Set our BrowsingContext's embedder if we're not embedded within a + // BrowserBridgeParent. + if (!GetBrowserBridgeParent() && mBrowsingContext) { + mBrowsingContext->SetEmbedderElement(mFrameElement); + } + + // Ensure all TabParent actors within BrowserBridges are also updated. + const auto& browserBridges = ManagedPBrowserBridgeParent(); + for (auto iter = browserBridges.ConstIter(); !iter.Done(); iter.Next()) { + BrowserBridgeParent* browserBridge = + static_cast(iter.Get()->GetKey()); + + browserBridge->GetTabParent()->SetOwnerElement(aElement); + } } NS_IMETHODIMP TabParent::GetOwnerElement(Element** aElement) { diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp index 3cb5c980f79f..892f155f48b5 100644 --- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -87,6 +87,12 @@ void WindowGlobalParent::Init(const WindowGlobalInit& aInit) { // Attach ourself to the browsing context. mBrowsingContext->RegisterWindowGlobal(this); + // If there is no current window global, assume we're about to become it + // optimistically. + if (!mBrowsingContext->GetCurrentWindowGlobal()) { + mBrowsingContext->SetCurrentWindowGlobal(this); + } + // Determine what toplevel frame element our WindowGlobalParent is being // embedded in. RefPtr frameElement; @@ -252,6 +258,13 @@ bool WindowGlobalParent::IsCurrentGlobal() { return !mIPCClosed && mBrowsingContext->GetCurrentWindowGlobal() == this; } +IPCResult WindowGlobalParent::RecvDidEmbedBrowsingContext( + dom::BrowsingContext* aContext) { + MOZ_ASSERT(aContext); + aContext->Canonical()->SetEmbedderWindowGlobal(this); + return IPC_OK(); +} + void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { mIPCClosed = true; gWindowGlobalParentsById->Remove(mInnerWindowId); diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h index ce546d0e5a90..2d61746d5fce 100644 --- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -111,6 +111,8 @@ class WindowGlobalParent final : public nsISupports, mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aActorName, const nsString& aMessageName, const ClonedMessageData& aData); + mozilla::ipc::IPCResult RecvDidEmbedBrowsingContext( + dom::BrowsingContext* aContext); void ActorDestroy(ActorDestroyReason aWhy) override;