Bug 1525427 - Part 1: Expose WindowGlobalParent & Element on BrowsingContext, r=farre
authorNika Layzell <nika@thelayzells.com>
Wed, 17 Apr 2019 00:51:36 +0000
changeset 469819 ccc41b77bdeb8a773f731a8ef85e1610675287f9
parent 469818 654741b9c784fb5f763ae500fb16865cdd167ccc
child 469820 db66b403cc95d1ec070441f6cff44aa02ee37b5d
push id35883
push userbtara@mozilla.com
push dateWed, 17 Apr 2019 21:47:29 +0000
treeherdermozilla-central@02b89c29412b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfarre
bugs1525427
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1525427 - Part 1: Expose WindowGlobalParent & Element on BrowsingContext, r=farre Differential Revision: https://phabricator.services.mozilla.com/D25181
docshell/base/BrowsingContext.cpp
docshell/base/BrowsingContext.h
docshell/base/CanonicalBrowsingContext.cpp
docshell/base/CanonicalBrowsingContext.h
dom/base/nsFrameLoader.cpp
dom/chrome-webidl/BrowsingContext.webidl
dom/ipc/ContentParent.cpp
dom/ipc/PWindowGlobal.ipdl
dom/ipc/TabParent.cpp
dom/ipc/WindowGlobalParent.cpp
dom/ipc/WindowGlobalParent.h
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -8,19 +8,22 @@
 
 #include "ipc/IPCMessageUtils.h"
 
 #include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/BrowsingContextGroup.h"
 #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"
 #include "mozilla/HashTable.h"
 #include "mozilla/Logging.h"
 #include "mozilla/StaticPtr.h"
 
 #include "nsDocShell.h"
@@ -196,16 +199,37 @@ BrowsingContext::BrowsingContext(Browsin
 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
   // XXX(nika): We should communicate that we are now an active BrowsingContext
   // process to the parent & do other validation here.
   MOZ_RELEASE_ASSERT(nsDocShell::Cast(aDocShell)->GetBrowsingContext() == this);
   mDocShell = 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<nsPIDOMWindowInner> embedderGlobal =
+        do_QueryInterface(mEmbedderElement->GetOwnerGlobal());
+    RefPtr<WindowGlobalChild> 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<WindowGlobalParent> 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,
            XRE_IsParentProcess() ? "Parent" : "Child",
            sCachedBrowsingContexts->has(Id()) ? "Re-connecting" : "Connecting",
            Id(), mParent ? mParent->Id() : 0));
 
   sCachedBrowsingContexts->remove(Id());
@@ -513,25 +537,27 @@ void BrowsingContext::NotifyResetUserGes
 bool BrowsingContext::GetUserGestureActivation() {
   RefPtr<BrowsingContext> topLevelBC = Top();
   return topLevelBC->GetIsActivatedByUserGesture();
 }
 
 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();
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 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);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BrowsingContext)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef)
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -41,16 +41,17 @@ template <typename T>
 struct IPDLParamTraits;
 }  // namespace ipc
 
 namespace dom {
 class BrowsingContent;
 class BrowsingContextGroup;
 class CanonicalBrowsingContext;
 class ContentParent;
+class Element;
 template <typename>
 struct Nullable;
 template <typename T>
 class Sequence;
 struct WindowPostMessageOptions;
 class WindowProxyHolder;
 
 class BrowsingContextBase {
@@ -118,16 +119,21 @@ class BrowsingContext : public nsWrapper
   bool IsInProcess() const { return mIsInProcess; }
 
   // Get the DocShell for this BrowsingContext if it is in-process, or
   // null if it's not.
   nsIDocShell* GetDocShell() { return mDocShell; }
   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 {
     return mDocShell ? mDocShell->GetWindow() : nullptr;
   }
 
   // Attach the current BrowsingContext to its parent, in both the child and the
   // parent process. BrowsingContext objects are created attached by default, so
@@ -434,16 +440,18 @@ class BrowsingContext : public nsWrapper
   // Unique id identifying BrowsingContext
   const uint64_t mBrowsingContextId;
 
   RefPtr<BrowsingContextGroup> mGroup;
   RefPtr<BrowsingContext> mParent;
   Children mChildren;
   nsCOMPtr<nsIDocShell> mDocShell;
 
+  RefPtr<Element> 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
   // objectMoved hook and clear it from its finalize hook.
   JS::Heap<JSObject*> mWindowProxy;
   LocationProxy mLocation;
 
   FieldEpochs mFieldEpochs;
--- a/docshell/base/CanonicalBrowsingContext.cpp
+++ b/docshell/base/CanonicalBrowsingContext.cpp
@@ -111,16 +111,30 @@ void CanonicalBrowsingContext::Unregiste
 void CanonicalBrowsingContext::SetCurrentWindowGlobal(
     WindowGlobalParent* aGlobal) {
   MOZ_ASSERT(mWindowGlobals.Contains(aGlobal), "Global not registered!");
 
   // TODO: This should probably assert that the processes match.
   mCurrentWindowGlobal = aGlobal;
 }
 
+void CanonicalBrowsingContext::SetEmbedderWindowGlobal(
+    WindowGlobalParent* aGlobal) {
+  MOZ_RELEASE_ASSERT(aGlobal, "null embedder");
+  if (RefPtr<BrowsingContext> 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
   // non-racy fields.
   if (aTransaction.HasNonRacyField()) {
     if (NS_WARN_IF(aProcess && mProcessId != aProcess->ChildID())) {
       return false;
     }
@@ -132,22 +146,24 @@ bool CanonicalBrowsingContext::ValidateT
 JSObject* CanonicalBrowsingContext::WrapObject(
     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   return CanonicalBrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 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() {
   if (!mCurrentWindowGlobal) {
     return;
   }
 
   // As this function would only be called when user click the play icon on the
--- a/docshell/base/CanonicalBrowsingContext.h
+++ b/docshell/base/CanonicalBrowsingContext.h
@@ -47,16 +47,21 @@ class CanonicalBrowsingContext final : p
   void UnregisterWindowGlobal(WindowGlobalParent* aGlobal);
 
   // The current active WindowGlobal.
   WindowGlobalParent* GetCurrentWindowGlobal() const {
     return mCurrentWindowGlobal;
   }
   void SetCurrentWindowGlobal(WindowGlobalParent* aGlobal);
 
+  WindowGlobalParent* GetEmbedderWindowGlobal() const {
+    return mEmbedderWindowGlobal;
+  }
+  void SetEmbedderWindowGlobal(WindowGlobalParent* aGlobal);
+
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   // This functions would set/reset its user gesture activation flag and then
   // notify other browsing contexts which are not the one related with the
   // current window global to set/reset the flag. (the corresponding browsing
   // context of the current global window has been set/reset before calling this
   // function)
@@ -92,16 +97,17 @@ class CanonicalBrowsingContext final : p
 
   // XXX(farre): Store a ContentParent pointer here rather than mProcessId?
   // Indicates which process owns the docshell.
   uint64_t mProcessId;
 
   // All live window globals within this browsing context.
   nsTHashtable<nsRefPtrHashKey<WindowGlobalParent>> mWindowGlobals;
   RefPtr<WindowGlobalParent> mCurrentWindowGlobal;
+  RefPtr<WindowGlobalParent> mEmbedderWindowGlobal;
 
   // Generation information for each content process which has interacted with
   // this CanonicalBrowsingContext, by ChildID.
   nsDataHashtable<nsUint64HashKey, FieldEpochs> mChildFieldEpochs;
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1888,16 +1888,20 @@ void nsFrameLoader::DestroyComplete() {
 
 void nsFrameLoader::SetOwnerContent(Element* aContent) {
   if (mObservingOwnerContent) {
     mObservingOwnerContent = false;
     mOwnerContent->RemoveMutationObserver(this);
   }
   mOwnerContent = aContent;
 
+  if (RefPtr<BrowsingContext> browsingContext = GetBrowsingContext()) {
+    browsingContext->SetEmbedderElement(mOwnerContent);
+  }
+
   AutoJSAPI jsapi;
   jsapi.Init();
 
   JS::RootedObject wrapper(jsapi.cx(), GetWrapper());
   if (wrapper) {
     JSAutoRealm ar(jsapi.cx(), wrapper);
     IgnoredErrorResult rv;
     UpdateReflectorGlobal(jsapi.cx(), wrapper, rv);
@@ -2004,16 +2008,18 @@ nsresult nsFrameLoader::MaybeCreateDocSh
     return NS_ERROR_UNEXPECTED;
   }
 
   // nsDocShell::Create will attach itself to the passed browsing
   // context inside of nsDocShell::Create
   RefPtr<nsDocShell> docShell = nsDocShell::Create(mBrowsingContext);
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
+  mBrowsingContext->SetEmbedderElement(mOwnerContent);
+
   mIsTopLevelContent =
       mBrowsingContext->IsContent() && !mBrowsingContext->GetParent();
   if (!mNetworkCreated && !mIsTopLevelContent) {
     docShell->SetCreatedDynamically(true);
   }
 
   if (mIsTopLevelContent) {
     // Manually add ourselves to our parent's docshell, as BrowsingContext won't
@@ -2599,16 +2605,18 @@ bool nsFrameLoader::TryRemoteBrowser() {
       Unused << window->GetNextTabParentId(&nextTabParentId);
     }
   }
 
   nsCOMPtr<Element> ownerElement = mOwnerContent;
 
   // 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);
     return !!mBrowserBridgeChild;
   }
 
   mRemoteBrowser = ContentParent::CreateBrowser(
       context, ownerElement, mBrowsingContext, openerContentParent,
--- a/dom/chrome-webidl/BrowsingContext.webidl
+++ b/dom/chrome-webidl/BrowsingContext.webidl
@@ -17,16 +17,18 @@ interface BrowsingContext {
   readonly attribute BrowsingContext? parent;
 
   readonly attribute BrowsingContext top;
 
   sequence<BrowsingContext> getChildren();
 
   readonly attribute nsIDocShell? docShell;
 
+  readonly attribute Element? embedderElement;
+
   readonly attribute unsigned long long id;
 
   readonly attribute BrowsingContext? opener;
 
   readonly attribute BrowsingContextGroup group;
 };
 
 [Exposed=Window, ChromeOnly]
@@ -36,15 +38,17 @@ interface CanonicalBrowsingContext : Bro
   readonly attribute WindowGlobalParent? currentWindowGlobal;
 
   // XXX(nika): This feels kinda hacky, but will do for now while we don't
   // synchronously create WindowGlobalParent. It can throw if somehow the
   // content process has died.
   [Throws]
   readonly attribute DOMString? currentRemoteType;
 
+  readonly attribute WindowGlobalParent? embedderWindowGlobal;
+
   void notifyStartDelayedAutoplayMedia();
 };
 
 [Exposed=Window, ChromeOnly]
 interface BrowsingContextGroup {
   sequence<BrowsingContext> getToplevels();
 };
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1143,16 +1143,18 @@ TabParent* ContentParent::CreateBrowser(
           GetNewOrUsedBrowserProcess(aFrameElement, remoteType, initialPriority,
                                      nullptr, isPreloadBrowser);
     }
     if (!constructorSender) {
       return nullptr;
     }
   }
 
+  aBrowsingContext->SetEmbedderElement(aFrameElement);
+
   // Ensure that our content process is subscribed to our newly created
   // BrowsingContextGroup.
   aBrowsingContext->Group()->EnsureSubscribed(constructorSender);
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   cpm->RegisterRemoteFrame(tabId, ContentParentId(0), openerTabId,
                            aContext.AsIPCTabContext(),
                            constructorSender->ChildID());
--- a/dom/ipc/PWindowGlobal.ipdl
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -31,13 +31,17 @@ both:
 
 parent:
   /// Update the URI of the document in this WindowGlobal.
   async UpdateDocumentURI(nsIURI aUri);
 
   /// 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();
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -329,16 +329,31 @@ void TabParent::SetOwnerElement(Element*
     if (widgetNativeData) {
       Unused << SendSetWidgetNativeData(widgetNativeData);
     }
   }
 
   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<BrowserBridgeParent*>(iter.Get()->GetKey());
+
+    browserBridge->GetTabParent()->SetOwnerElement(aElement);
+  }
 }
 
 NS_IMETHODIMP TabParent::GetOwnerElement(Element** aElement) {
   *aElement = do_AddRef(GetOwnerElement()).take();
   return NS_OK;
 }
 
 void TabParent::AddWindowListeners() {
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -82,16 +82,22 @@ void WindowGlobalParent::Init(const Wind
 
   // XXX(nika): This won't be the case soon, but for now this is a good
   // assertion as we can't switch processes. We should relax this eventually.
   MOZ_ASSERT(mBrowsingContext->IsOwnedByProcess(processId));
 
   // 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<Element> frameElement;
   if (mInProcess) {
     // In the in-process case, we can get it from the other side's
     // WindowGlobalChild.
     MOZ_ASSERT(Manager()->GetProtocolTypeId() == PInProcessMsgStart);
     RefPtr<WindowGlobalChild> otherSide = GetChildActor();
@@ -247,16 +253,23 @@ already_AddRefed<JSWindowActorParent> Wi
   mWindowActors.Put(aName, actor);
   return actor.forget();
 }
 
 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);
   mBrowsingContext->UnregisterWindowGlobal(this);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(this, "window-global-destroyed", nullptr);
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -106,16 +106,18 @@ class WindowGlobalParent final : public 
  protected:
   // IPC messages
   mozilla::ipc::IPCResult RecvUpdateDocumentURI(nsIURI* aURI);
   mozilla::ipc::IPCResult RecvBecomeCurrentWindowGlobal();
   mozilla::ipc::IPCResult RecvDestroy();
   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;
 
  private:
   ~WindowGlobalParent();
 
   // NOTE: This document principal doesn't reflect possible |document.domain|
   // mutations which may have been made in the actual document.