Bug 1353867 - Change WindowProxyHolder's native type to BrowsingContext. r=bzbarsky
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 02 Jan 2019 13:27:05 +0000
changeset 509382 18f95c6c1eb340c325e47b36223464625dd76a45
parent 509381 4f9080815cc8e2198230df3f1c7ca18ac97c47f1
child 509383 85ed8082eac91dcb2a33bad5a2f9fa8f7f52583f
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1353867
milestone66.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 1353867 - Change WindowProxyHolder's native type to BrowsingContext. r=bzbarsky Make the WindowProxyHolder hold a strong reference to a BrowsingContext, as in the future we might not have a nsPIDOMWindowOuter (if the document is loaded in a different process). Differential Revision: https://phabricator.services.mozilla.com/D12651
docshell/base/BrowsingContext.h
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/base/InProcessTabChildMessageManager.cpp
dom/base/InProcessTabChildMessageManager.h
dom/base/PostMessageEvent.cpp
dom/base/PostMessageEvent.h
dom/base/WindowNamedPropertiesHandler.cpp
dom/base/WindowProxyHolder.h
dom/base/nsDocument.cpp
dom/base/nsFrameLoader.cpp
dom/base/nsFrameLoader.h
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
dom/base/nsPIDOMWindow.h
dom/base/nsPIDOMWindowInlines.h
dom/base/nsWindowRoot.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/Bindings.conf
dom/bindings/ToJSValue.cpp
dom/bindings/ToJSValue.h
dom/browser-element/BrowserElementParent.cpp
dom/browser-element/BrowserElementParent.h
dom/events/EventTarget.cpp
dom/events/MessageEvent.cpp
dom/events/MessageEvent.h
dom/events/UIEvent.h
dom/html/HTMLObjectElement.cpp
dom/html/nsGenericHTMLFrameElement.cpp
dom/html/nsGenericHTMLFrameElement.h
dom/html/nsHTMLDocument.cpp
dom/ipc/TabChild.cpp
dom/smil/TimeEvent.h
dom/xul/XULFrameElement.cpp
dom/xul/XULFrameElement.h
js/xpconnect/wrappers/XrayWrapper.cpp
toolkit/components/extensions/ExtensionPolicyService.cpp
toolkit/components/extensions/WebExtensionContentScript.h
toolkit/components/extensions/WebExtensionPolicy.cpp
xpfe/appshell/nsContentTreeOwner.cpp
xpfe/appshell/nsXULWindow.cpp
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -7,21 +7,23 @@
 #ifndef mozilla_dom_BrowsingContext_h
 #define mozilla_dom_BrowsingContext_h
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsIDocShell.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
-class nsIDocShell;
+class nsGlobalWindowOuter;
+class nsOuterWindowProxy;
 
 namespace mozilla {
 
 class LogModule;
 
 namespace dom {
 
 class BrowsingContext;
@@ -75,16 +77,22 @@ class BrowsingContext : public nsWrapper
       BrowsingContext* aParent, BrowsingContext* aOpener,
       const nsAString& aName, uint64_t aId, ContentParent* aOriginProcess);
 
   // 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);
 
+  // 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
   // this method need only be called when restoring cached BrowsingContext
   // objects.
   void Attach();
 
   // Detach the current BrowsingContext from its parent, in both the
   // child and the parent process.
@@ -118,39 +126,67 @@ class BrowsingContext : public nsWrapper
 
   static void GetRootBrowsingContexts(
       nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);
 
   nsISupports* GetParentObject() const;
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
+  // Return the window proxy object that corresponds to this browsing context.
+  inline JSObject* GetWindowProxy() const { return mWindowProxy; }
+  // Set the window proxy object that corresponds to this browsing context.
+  void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) {
+    mWindowProxy = aWindowProxy;
+  }
+
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContext)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(BrowsingContext)
 
   using Children = nsTArray<RefPtr<BrowsingContext>>;
 
  protected:
   virtual ~BrowsingContext();
   BrowsingContext(BrowsingContext* aParent, BrowsingContext* aOpener,
                   const nsAString& aName, uint64_t aBrowsingContextId,
                   Type aType);
 
  private:
+  friend class ::nsOuterWindowProxy;
+  friend class ::nsGlobalWindowOuter;
+  // Update the window proxy object that corresponds to this browsing context.
+  // This should be called from the window proxy object's objectMoved hook, if
+  // the object mWindowProxy points to was moved by the JS GC.
+  void UpdateWindowProxy(JSObject* obj, JSObject* old) {
+    if (mWindowProxy) {
+      MOZ_ASSERT(mWindowProxy == old);
+      mWindowProxy = obj;
+    }
+  }
+  // Clear the window proxy object that corresponds to this browsing context.
+  // This should be called if the window proxy object is finalized, or it can't
+  // reach its browsing context anymore.
+  void ClearWindowProxy() { mWindowProxy = nullptr; }
+
   // Type of BrowsingContent
   const Type mType;
 
   // Unique id identifying BrowsingContext
   const uint64_t mBrowsingContextId;
 
   RefPtr<BrowsingContextGroup> mBrowsingContextGroup;
   RefPtr<BrowsingContext> mParent;
   Children mChildren;
   WeakPtr<BrowsingContext> mOpener;
   nsCOMPtr<nsIDocShell> mDocShell;
   nsString mName;
+  // 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;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // !defined(mozilla_dom_BrowsingContext_h)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13416,17 +13416,13 @@ nsDocShell::GetColorMatrix(uint32_t* aMa
     memcpy(*aMatrix, mColorMatrix->components, 20 * sizeof(float));
   }
 
   return NS_OK;
 }
 
 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
 
-BrowsingContext* nsDocShell::GetBrowsingContext() const {
-  return mBrowsingContext;
-}
-
 NS_IMETHODIMP
 nsDocShell::GetBrowsingContext(BrowsingContext** aBrowsingContext) {
   *aBrowsingContext = do_AddRef(mBrowsingContext).take();
   return NS_OK;
 }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -378,20 +378,23 @@ class nsDocShell final : public nsDocLoa
   static nsDocShell* Cast(nsIDocShell* aDocShell) {
     return static_cast<nsDocShell*>(aDocShell);
   }
 
   // Returns true if the current load is a force reload (started by holding
   // shift while triggering reload)
   bool IsForceReloading();
 
-  /**
-   * Native getter for a DocShell's BrowsingContext.
-   */
-  mozilla::dom::BrowsingContext* GetBrowsingContext() const;
+  mozilla::dom::BrowsingContext* GetBrowsingContext() const {
+    return mBrowsingContext;
+  }
+  mozilla::dom::BrowsingContext* GetWindowProxy() {
+    EnsureScriptEnvironment();
+    return mBrowsingContext;
+  }
 
   /**
    * Loads the given URI. See comments on nsDocShellLoadState members for more
    * information on information used. aDocShell and aRequest come from
    * onLinkClickSync, which is triggered during form submission.
    */
   nsresult InternalLoad(nsDocShellLoadState* aLoadState,
                         nsIDocShell** aDocShell, nsIRequest** aRequest);
--- a/dom/base/InProcessTabChildMessageManager.cpp
+++ b/dom/base/InProcessTabChildMessageManager.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InProcessTabChildMessageManager.h"
 #include "nsContentUtils.h"
+#include "nsDocShell.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsFrameLoader.h"
 #include "xpcpublic.h"
 #include "nsIMozBrowserFrame.h"
@@ -72,17 +73,17 @@ nsresult InProcessTabChildMessageManager
     return rv;
   }
 
   queue->Push(ev);
   return NS_OK;
 }
 
 InProcessTabChildMessageManager::InProcessTabChildMessageManager(
-    nsIDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome)
+    nsDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome)
     : ContentFrameMessageManager(new nsFrameMessageManager(this)),
       mDocShell(aShell),
       mLoadingScript(false),
       mPreventEventsEscaping(false),
       mOwner(aOwner),
       mChromeMessageManager(aChrome) {
   mozilla::HoldJSObjects(this);
 
@@ -145,40 +146,35 @@ JSObject* InProcessTabChildMessageManage
 
 void InProcessTabChildMessageManager::CacheFrameLoader(
     nsFrameLoader* aFrameLoader) {
   mFrameLoader = aFrameLoader;
 }
 
 Nullable<WindowProxyHolder> InProcessTabChildMessageManager::GetContent(
     ErrorResult& aError) {
-  nsCOMPtr<nsPIDOMWindowOuter> content;
-  if (mDocShell) {
-    content = mDocShell->GetWindow();
-  }
-  if (!content) {
+  if (!mDocShell) {
     return nullptr;
   }
-  return WindowProxyHolder(content);
+  return WindowProxyHolder(mDocShell->GetBrowsingContext());
 }
 
 already_AddRefed<nsIEventTarget>
 InProcessTabChildMessageManager::GetTabEventTarget() {
   nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
   return target.forget();
 }
 
 uint64_t InProcessTabChildMessageManager::ChromeOuterWindowID() {
   if (!mDocShell) {
     return 0;
   }
 
-  nsCOMPtr<nsIDocShellTreeItem> item = mDocShell;
   nsCOMPtr<nsIDocShellTreeItem> root;
-  nsresult rv = item->GetRootTreeItem(getter_AddRefs(root));
+  nsresult rv = mDocShell->GetRootTreeItem(getter_AddRefs(root));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return 0;
   }
 
   nsPIDOMWindowOuter* topWin = root->GetWindow();
   if (!topWin) {
     return 0;
   }
--- a/dom/base/InProcessTabChildMessageManager.h
+++ b/dom/base/InProcessTabChildMessageManager.h
@@ -12,17 +12,17 @@
 #include "mozilla/RefPtr.h"
 #include "mozilla/dom/ContentFrameMessageManager.h"
 #include "nsCOMPtr.h"
 #include "nsFrameMessageManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIClassInfo.h"
-#include "nsIDocShell.h"
+#include "nsDocShell.h"
 #include "nsCOMArray.h"
 #include "nsIRunnable.h"
 #include "nsWeakReference.h"
 
 namespace mozilla {
 class EventChainPreVisitor;
 
 namespace dom {
@@ -37,22 +37,22 @@ class InProcessTabChildMessageManager fi
     : public ContentFrameMessageManager,
       public nsMessageManagerScriptExecutor,
       public nsIInProcessContentFrameMessageManager,
       public nsSupportsWeakReference,
       public mozilla::dom::ipc::MessageManagerCallback {
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
 
  private:
-  InProcessTabChildMessageManager(nsIDocShell* aShell, nsIContent* aOwner,
+  InProcessTabChildMessageManager(nsDocShell* aShell, nsIContent* aOwner,
                                   nsFrameMessageManager* aChrome);
 
  public:
   static already_AddRefed<InProcessTabChildMessageManager> Create(
-      nsIDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome) {
+      nsDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome) {
     RefPtr<InProcessTabChildMessageManager> mm =
         new InProcessTabChildMessageManager(aShell, aOwner, aChrome);
 
     NS_ENSURE_TRUE(mm->Init(), nullptr);
 
     return mm.forget();
   }
 
@@ -63,18 +63,17 @@ class InProcessTabChildMessageManager fi
   void MarkForCC();
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   Nullable<WindowProxyHolder> GetContent(ErrorResult& aError) override;
   virtual already_AddRefed<nsIDocShell> GetDocShell(
       ErrorResult& aError) override {
-    nsCOMPtr<nsIDocShell> docShell(mDocShell);
-    return docShell.forget();
+    return do_AddRef(mDocShell);
   }
   virtual already_AddRefed<nsIEventTarget> GetTabEventTarget() override;
   virtual uint64_t ChromeOuterWindowID() override;
 
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
 
   NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER
 
@@ -115,17 +114,17 @@ class InProcessTabChildMessageManager fi
     mChromeMessageManager = aParent;
   }
 
   already_AddRefed<nsFrameLoader> GetFrameLoader();
 
  protected:
   virtual ~InProcessTabChildMessageManager();
 
-  nsCOMPtr<nsIDocShell> mDocShell;
+  RefPtr<nsDocShell> mDocShell;
   bool mLoadingScript;
 
   // Is this the message manager for an in-process <iframe mozbrowser>? This
   // affects where events get sent, so it affects GetEventTargetParent.
   bool mIsBrowserFrame;
   bool mPreventEventsEscaping;
 
   // We keep a strong reference to the frameloader after we've started
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -14,27 +14,28 @@
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/UnionConversions.h"
 #include "mozilla/EventDispatcher.h"
 #include "nsContentUtils.h"
+#include "nsDocShell.h"
 #include "nsGlobalWindow.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptError.h"
 #include "nsPresContext.h"
 #include "nsQueryObject.h"
 
 namespace mozilla {
 namespace dom {
 
-PostMessageEvent::PostMessageEvent(nsGlobalWindowOuter* aSource,
+PostMessageEvent::PostMessageEvent(BrowsingContext* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindowOuter* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
                                    nsIDocument* aSourceDocument)
     : Runnable("dom::PostMessageEvent"),
       StructuredCloneHolder(CloningSupported, TransferringSupported,
                             StructuredCloneScope::SameProcessSameThread),
       mSource(aSource),
@@ -133,17 +134,19 @@ PostMessageEvent::Run() {
     DispatchError(cx, targetWindow, eventTarget);
     return NS_OK;
   }
 
   // Create the event
   RefPtr<MessageEvent> event = new MessageEvent(eventTarget, nullptr, nullptr);
 
   Nullable<WindowProxyOrMessagePortOrServiceWorker> source;
-  source.SetValue().SetAsWindowProxy() = mSource ? mSource->AsOuter() : nullptr;
+  if (mSource) {
+    source.SetValue().SetAsWindowProxy() = mSource;
+  }
 
   Sequence<OwningNonNull<MessagePort>> ports;
   if (!TakeTransferredPortsAsSequence(ports)) {
     DispatchError(cx, targetWindow, eventTarget);
     return NS_OK;
   }
 
   event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"), CanBubble::eNo,
@@ -158,17 +161,17 @@ void PostMessageEvent::DispatchError(JSC
                                      nsGlobalWindowInner* aTargetWindow,
                                      mozilla::dom::EventTarget* aEventTarget) {
   RootedDictionary<MessageEventInit> init(aCx);
   init.mBubbles = false;
   init.mCancelable = false;
   init.mOrigin = mCallerOrigin;
 
   if (mSource) {
-    init.mSource.SetValue().SetAsWindowProxy() = mSource->AsOuter();
+    init.mSource.SetValue().SetAsWindowProxy() = mSource;
   }
 
   RefPtr<Event> event = MessageEvent::Constructor(
       aEventTarget, NS_LITERAL_STRING("messageerror"), init);
   Dispatch(aTargetWindow, event);
 }
 
 void PostMessageEvent::Dispatch(nsGlobalWindowInner* aTargetWindow,
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -17,38 +17,40 @@
 class nsGlobalWindowOuter;
 class nsGlobalWindowInner;
 class nsIDocument;
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 
+class BrowsingContext;
+
 /**
  * Class used to represent events generated by calls to Window.postMessage,
  * which asynchronously creates and dispatches events.
  */
 class PostMessageEvent final : public Runnable, public StructuredCloneHolder {
  public:
   NS_DECL_NSIRUNNABLE
 
-  PostMessageEvent(nsGlobalWindowOuter* aSource, const nsAString& aCallerOrigin,
+  PostMessageEvent(BrowsingContext* aSource, const nsAString& aCallerOrigin,
                    nsGlobalWindowOuter* aTargetWindow,
                    nsIPrincipal* aProvidedPrincipal,
                    nsIDocument* aSourceDocument);
 
  private:
   ~PostMessageEvent();
 
   void Dispatch(nsGlobalWindowInner* aTargetWindow, Event* aEvent);
 
   void DispatchError(JSContext* aCx, nsGlobalWindowInner* aTargetWindow,
                      mozilla::dom::EventTarget* aEventTarget);
 
-  RefPtr<nsGlobalWindowOuter> mSource;
+  RefPtr<BrowsingContext> mSource;
   nsString mCallerOrigin;
   RefPtr<nsGlobalWindowOuter> mTargetWindow;
   nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
   nsCOMPtr<nsIDocument> mSourceDocument;
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -13,24 +13,25 @@
 #include "nsHTMLDocument.h"
 #include "nsJSUtils.h"
 #include "xpcprivate.h"
 
 namespace mozilla {
 namespace dom {
 
 static bool ShouldExposeChildWindow(nsString& aNameBeingResolved,
-                                    nsPIDOMWindowOuter* aChild) {
-  Element* e = aChild->GetFrameElementInternal();
+                                    BrowsingContext* aChild) {
+  nsPIDOMWindowOuter* child = aChild->GetDOMWindow();
+  Element* e = child->GetFrameElementInternal();
   if (e && e->IsInShadowTree()) {
     return false;
   }
 
   // If we're same-origin with the child, go ahead and expose it.
-  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aChild);
+  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(child);
   NS_ENSURE_TRUE(sop, false);
   if (nsContentUtils::SubjectPrincipal()->Equals(sop->GetPrincipal())) {
     return true;
   }
 
   // If we're not same-origin, expose it _only_ if the name of the browsing
   // context matches the 'name' attribute of the frame element in the parent.
   // The motivations behind this heuristic are worth explaining here.
@@ -95,23 +96,23 @@ bool WindowNamedPropertiesHandler::getOw
 
   if (str.IsEmpty()) {
     return true;
   }
 
   // Grab the DOM window.
   nsGlobalWindowInner* win = xpc::WindowGlobalOrNull(aProxy);
   if (win->Length() > 0) {
-    nsCOMPtr<nsPIDOMWindowOuter> childWin = win->GetChildWindow(str);
-    if (childWin && ShouldExposeChildWindow(str, childWin)) {
+    RefPtr<BrowsingContext> child = win->GetChildWindow(str);
+    if (child && ShouldExposeChildWindow(str, child)) {
       // We found a subframe of the right name. Shadowing via |var foo| in
       // global scope is still allowed, since |var| only looks up |own|
       // properties. But unqualified shadowing will fail, per-spec.
       JS::Rooted<JS::Value> v(aCx);
-      if (!ToJSValue(aCx, nsGlobalWindowOuter::Cast(childWin), &v)) {
+      if (!ToJSValue(aCx, WindowProxyHolder(child.forget()), &v)) {
         return false;
       }
       FillPropertyDescriptor(aDesc, aProxy, 0, v);
       return true;
     }
   }
 
   // The rest of this function is for HTML documents only.
@@ -177,18 +178,18 @@ bool WindowNamedPropertiesHandler::ownPr
         // item->GetWindow().  But it's not obvious whether this does the same
         // thing as GetChildWindow() with the item's name (due to the complexity
         // of FindChildWithName).  Since GetChildWindow is what we use in
         // getOwnPropDescriptor, let's try to be consistent.
         nsString name;
         item->GetName(name);
         if (!names.Contains(name)) {
           // Make sure we really would expose it from getOwnPropDescriptor.
-          nsCOMPtr<nsPIDOMWindowOuter> childWin = win->GetChildWindow(name);
-          if (childWin && ShouldExposeChildWindow(name, childWin)) {
+          RefPtr<BrowsingContext> child = win->GetChildWindow(name);
+          if (child && ShouldExposeChildWindow(name, child)) {
             names.AppendElement(name);
           }
         }
       }
     }
   }
   if (!AppendNamedPropertyIds(aCx, aProxy, names, false, aProps)) {
     return false;
--- a/dom/base/WindowProxyHolder.h
+++ b/dom/base/WindowProxyHolder.h
@@ -2,66 +2,66 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_WindowProxyHolder_h__
 #define mozilla_dom_WindowProxyHolder_h__
 
-#include "nsPIDOMWindow.h"
+#include "mozilla/dom/BrowsingContext.h"
 
 namespace mozilla {
 namespace dom {
 
 /**
  * This class is used for passing arguments and the return value for WebIDL
  * binding code that takes/returns a WindowProxy object and for WebIDL
  * unions/dictionaries that contain a WindowProxy member. It should never
  * contain null; if the value in WebIDL is nullable the binding code will use a
  * Nullable<WindowProxyHolder>.
  */
 class WindowProxyHolder {
  public:
   WindowProxyHolder() = default;
-  explicit WindowProxyHolder(nsPIDOMWindowOuter* aWin) : mWindow(aWin) {
-    MOZ_ASSERT(mWindow, "Don't set WindowProxyHolder to null.");
+  explicit WindowProxyHolder(BrowsingContext* aBC) : mBrowsingContext(aBC) {
+    MOZ_ASSERT(mBrowsingContext, "Don't set WindowProxyHolder to null.");
   }
-  explicit WindowProxyHolder(already_AddRefed<nsPIDOMWindowOuter>&& aWin)
-      : mWindow(std::move(aWin)) {
-    MOZ_ASSERT(mWindow, "Don't set WindowProxyHolder to null.");
+  explicit WindowProxyHolder(already_AddRefed<BrowsingContext>&& aBC)
+      : mBrowsingContext(std::move(aBC)) {
+    MOZ_ASSERT(mBrowsingContext, "Don't set WindowProxyHolder to null.");
   }
-  WindowProxyHolder& operator=(nsPIDOMWindowOuter* aWin) {
-    mWindow = aWin;
-    MOZ_ASSERT(mWindow, "Don't set WindowProxyHolder to null.");
+  WindowProxyHolder& operator=(BrowsingContext* aBC) {
+    mBrowsingContext = aBC;
+    MOZ_ASSERT(mBrowsingContext, "Don't set WindowProxyHolder to null.");
     return *this;
   }
-  WindowProxyHolder& operator=(already_AddRefed<nsPIDOMWindowOuter>&& aWin) {
-    mWindow = std::move(aWin);
-    MOZ_ASSERT(mWindow, "Don't set WindowProxyHolder to null.");
+  WindowProxyHolder& operator=(already_AddRefed<BrowsingContext>&& aBC) {
+    mBrowsingContext = std::move(aBC);
+    MOZ_ASSERT(mBrowsingContext, "Don't set WindowProxyHolder to null.");
     return *this;
   }
 
-  nsPIDOMWindowOuter* get() const {
-    MOZ_ASSERT(mWindow, "WindowProxyHolder hasn't been initialized.");
-    return mWindow;
+  BrowsingContext* get() const {
+    MOZ_ASSERT(mBrowsingContext, "WindowProxyHolder hasn't been initialized.");
+    return mBrowsingContext;
   }
 
  private:
   friend void ImplCycleCollectionUnlink(WindowProxyHolder& aProxy);
 
-  nsCOMPtr<nsPIDOMWindowOuter> mWindow;
+  RefPtr<BrowsingContext> mBrowsingContext;
 };
 
 inline void ImplCycleCollectionTraverse(
     nsCycleCollectionTraversalCallback& aCallback, WindowProxyHolder& aProxy,
     const char* aName, uint32_t aFlags = 0) {
-  CycleCollectionNoteChild(aCallback, aProxy.get(), "mWindow", aFlags);
+  CycleCollectionNoteChild(aCallback, aProxy.get(), "mBrowsingContext", aFlags);
 }
 
 inline void ImplCycleCollectionUnlink(WindowProxyHolder& aProxy) {
-  aProxy.mWindow = nullptr;
+  aProxy.mBrowsingContext = nullptr;
 }
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif /* mozilla_dom_WindowProxyHolder_h__ */
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3367,17 +3367,17 @@ nsresult nsIDocument::GetSrcdocData(nsAS
   return NS_OK;
 }
 
 Nullable<WindowProxyHolder> nsIDocument::GetDefaultView() const {
   nsPIDOMWindowOuter* win = GetWindow();
   if (!win) {
     return nullptr;
   }
-  return WindowProxyHolder(win);
+  return WindowProxyHolder(win->GetBrowsingContext());
 }
 
 Element* nsIDocument::GetActiveElement() {
   // Get the focused element.
   Element* focusedElement = GetRetargetedFocusedElement();
   if (focusedElement) {
     return focusedElement;
   }
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -498,17 +498,17 @@ nsresult nsFrameLoader::CheckURILoad(nsI
 
   // Bail out if this is an infinite recursion scenario
   if (IsRemoteFrame()) {
     return NS_OK;
   }
   return CheckForRecursiveLoad(aURI);
 }
 
-nsIDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
+nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
   if (IsRemoteFrame()) {
     return nullptr;
   }
 
   // If we have an owner, make sure we have a docshell and return
   // that. If not, we're most likely in the middle of being torn down,
   // then we just return null.
   if (mOwnerContent) {
@@ -670,41 +670,37 @@ bool nsFrameLoader::Show(int32_t marginW
   NS_ASSERTION(mDocShell, "MaybeCreateDocShell succeeded, but null mDocShell");
   if (!mDocShell) {
     return false;
   }
 
   mDocShell->SetMarginWidth(marginWidth);
   mDocShell->SetMarginHeight(marginHeight);
 
-  nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell);
-  if (sc) {
-    sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
-                                       scrollbarPrefX);
-    sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
-                                       scrollbarPrefY);
-  }
+  mDocShell->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
+                                            scrollbarPrefX);
+  mDocShell->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
+                                            scrollbarPrefY);
 
   nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
   if (presShell) {
     // Ensure root scroll frame is reflowed in case scroll preferences or
     // margins have changed
     nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
     if (rootScrollFrame) {
       presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize,
                                   NS_FRAME_IS_DIRTY);
     }
     return true;
   }
 
   nsView* view = frame->EnsureInnerView();
   if (!view) return false;
 
-  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mDocShell);
-  NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow.");
+  RefPtr<nsDocShell> baseWindow = mDocShell;
   baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0, size.width,
                          size.height);
   // This is kinda whacky, this "Create()" call doesn't really
   // create anything, one starts to wonder why this was named
   // "Create"...
   baseWindow->Create();
   baseWindow->SetVisibility(true);
   NS_ENSURE_TRUE(mDocShell, false);
@@ -853,19 +849,17 @@ void nsFrameLoader::Hide() {
   }
 
   if (!mDocShell) return;
 
   nsCOMPtr<nsIContentViewer> contentViewer;
   mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
   if (contentViewer) contentViewer->SetSticky(false);
 
-  nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
-  NS_ASSERTION(baseWin,
-               "Found an nsIDocShell which doesn't implement nsIBaseWindow.");
+  RefPtr<nsDocShell> baseWin = mDocShell;
   baseWin->SetVisibility(false);
   baseWin->SetParentWidget(nullptr);
 }
 
 void nsFrameLoader::ForceLayoutIfNecessary() {
   nsIFrame* frame = GetPrimaryFrameOfOwningContent();
   if (!frame) {
     return;
@@ -1672,19 +1666,18 @@ void nsFrameLoader::DestroyDocShell() {
   }
 
   // Fire the "unload" event if we're in-process.
   if (mChildMessageManager) {
     mChildMessageManager->FireUnloadEvent();
   }
 
   // Destroy the docshell.
-  nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
-  if (base_win) {
-    base_win->Destroy();
+  if (mDocShell) {
+    mDocShell->Destroy();
   }
   mDocShell = nullptr;
 
   if (mChildMessageManager) {
     // Stop handling events in the in-process frame script.
     mChildMessageManager->DisconnectEventListeners();
   }
 }
@@ -1972,18 +1965,18 @@ nsresult nsFrameLoader::MaybeCreateDocSh
                                  nsGkAtoms::allowscriptstoclose,
                                  nsGkAtoms::_true, eCaseMatters)) {
     nsGlobalWindowOuter::Cast(newWindow)->AllowScriptsToClose();
   }
 
   // This is kinda whacky, this call doesn't really create anything,
   // but it must be called to make sure things are properly
   // initialized.
-  nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
-  if (NS_FAILED(baseWin->Create())) {
+  RefPtr<nsDocShell> docShell = mDocShell;
+  if (NS_FAILED(docShell->Create())) {
     // Do not call Destroy() here. See bug 472312.
     NS_WARNING("Something wrong when creating the docshell for a frameloader!");
     return NS_ERROR_FAILURE;
   }
 
   // If we are an in-process browser, we want to set up our session history. We
   // do this by creating both the child SHistory (which is in the nsDocShell),
   // and creating the corresponding in-process ParentSHistory.
@@ -2292,18 +2285,17 @@ nsresult nsFrameLoader::UpdatePositionAn
     return NS_OK;
   }
   UpdateBaseWindowPositionAndSize(aIFrame);
   return NS_OK;
 }
 
 void nsFrameLoader::UpdateBaseWindowPositionAndSize(
     nsSubDocumentFrame* aIFrame) {
-  nsCOMPtr<nsIBaseWindow> baseWindow =
-      do_QueryInterface(GetDocShell(IgnoreErrors()));
+  nsCOMPtr<nsIBaseWindow> baseWindow = GetDocShell(IgnoreErrors());
 
   // resize the sub document
   if (baseWindow) {
     int32_t x = 0;
     int32_t y = 0;
 
     AutoWeakFrame weakFrame(aIFrame);
 
@@ -3012,17 +3004,17 @@ already_AddRefed<nsITabParent> nsFrameLo
   return do_AddRef(mRemoteBrowser);
 }
 
 already_AddRefed<nsILoadContext> nsFrameLoader::LoadContext() {
   nsCOMPtr<nsILoadContext> loadContext;
   if (IsRemoteFrame() && (mRemoteBrowser || TryRemoteBrowser())) {
     loadContext = mRemoteBrowser->GetLoadContext();
   } else {
-    loadContext = do_GetInterface(GetDocShell(IgnoreErrors()));
+    loadContext = do_GetInterface(ToSupports(GetDocShell(IgnoreErrors())));
   }
   return loadContext.forget();
 }
 
 already_AddRefed<BrowsingContext> nsFrameLoader::GetBrowsingContext() {
   RefPtr<BrowsingContext> browsingContext;
   if (IsRemoteFrame() && (mRemoteBrowser || TryRemoteBrowser())) {
     browsingContext = mRemoteBrowser->GetBrowsingContext();
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -7,17 +7,17 @@
 /*
  * Class for managing loading of a subframe (creation of the docshell,
  * handling of loads in it, recursion-checking).
  */
 
 #ifndef nsFrameLoader_h_
 #define nsFrameLoader_h_
 
-#include "nsIDocShell.h"
+#include "nsDocShell.h"
 #include "nsStringFwd.h"
 #include "nsIFrameLoaderOwner.h"
 #include "nsPoint.h"
 #include "nsSize.h"
 #include "nsWrapperCache.h"
 #include "nsIURI.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/dom/BindingUtils.h"
@@ -113,17 +113,17 @@ class nsFrameLoader final : public nsStu
       const {
     return mChildMessageManager;
   }
   nsresult CreateStaticClone(nsFrameLoader* aDest);
   nsresult UpdatePositionAndSize(nsSubDocumentFrame* aIFrame);
 
   // WebIDL methods
 
-  nsIDocShell* GetDocShell(mozilla::ErrorResult& aRv);
+  nsDocShell* GetDocShell(mozilla::ErrorResult& aRv);
 
   already_AddRefed<nsITabParent> GetTabParent();
 
   already_AddRefed<nsILoadContext> LoadContext();
 
   already_AddRefed<mozilla::dom::BrowsingContext> GetBrowsingContext();
 
   /**
@@ -424,17 +424,17 @@ class nsFrameLoader final : public nsStu
   nsresult GetNewTabContext(mozilla::dom::MutableTabContext* aTabContext,
                             nsIURI* aURI = nullptr);
 
   enum TabParentChange { eTabParentRemoved, eTabParentChanged };
   void MaybeUpdatePrimaryTabParent(TabParentChange aChange);
 
   nsresult PopulateUserContextIdFromAttribute(mozilla::OriginAttributes& aAttr);
 
-  nsCOMPtr<nsIDocShell> mDocShell;
+  RefPtr<nsDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURIToLoad;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   mozilla::dom::Element* mOwnerContent;  // WEAK
 
   // After the frameloader has been removed from the DOM but before all of the
   // messages from the frame have been received, we keep a strong reference to
   // our <browser> element.
   RefPtr<mozilla::dom::Element> mOwnerContentStrong;
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -3306,17 +3306,17 @@ double nsGlobalWindowInner::GetScrollY(E
 
 uint32_t nsGlobalWindowInner::Length() { FORWARD_TO_OUTER(Length, (), 0); }
 
 Nullable<WindowProxyHolder> nsGlobalWindowInner::GetTop(
     mozilla::ErrorResult& aError) {
   FORWARD_TO_OUTER_OR_THROW(GetTopOuter, (), aError, nullptr);
 }
 
-nsPIDOMWindowOuter* nsGlobalWindowInner::GetChildWindow(
+already_AddRefed<BrowsingContext> nsGlobalWindowInner::GetChildWindow(
     const nsAString& aName) {
   if (GetOuterWindowInternal()) {
     return GetOuterWindowInternal()->GetChildWindow(aName);
   }
   return nullptr;
 }
 
 void nsGlobalWindowInner::RefreshRealmPrincipal() {
@@ -3696,18 +3696,17 @@ Nullable<WindowProxyHolder> nsGlobalWind
     JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
     const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
     ErrorResult& aError) {
   FORWARD_TO_OUTER_OR_THROW(
       OpenDialogOuter, (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
       aError, nullptr);
 }
 
-already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowInner::GetFrames(
-    ErrorResult& aError) {
+BrowsingContext* nsGlobalWindowInner::GetFrames(ErrorResult& aError) {
   FORWARD_TO_OUTER_OR_THROW(GetFramesOuter, (), aError, nullptr);
 }
 
 void nsGlobalWindowInner::PostMessageMoz(JSContext* aCx,
                                          JS::Handle<JS::Value> aMessage,
                                          const nsAString& aTargetOrigin,
                                          JS::Handle<JS::Value> aTransfer,
                                          nsIPrincipal& aSubjectPrincipal,
@@ -6906,17 +6905,17 @@ already_AddRefed<External> nsGlobalWindo
   return nullptr;
 #endif
 }
 
 void nsGlobalWindowInner::GetSidebar(OwningExternalOrWindowProxy& aResult,
                                      ErrorResult& aRv) {
 #ifdef HAVE_SIDEBAR
   // First check for a named frame named "sidebar"
-  nsCOMPtr<nsPIDOMWindowOuter> domWindow =
+  RefPtr<BrowsingContext> domWindow =
       GetChildWindow(NS_LITERAL_STRING("sidebar"));
   if (domWindow) {
     aResult.SetAsWindowProxy() = domWindow.forget();
     return;
   }
 
   RefPtr<External> external = GetExternal(aRv);
   if (external) {
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -92,16 +92,17 @@ class IdleRequestExecutor;
 class DialogValueHolder;
 
 class PromiseDocumentFlushedResolver;
 
 namespace mozilla {
 class AbstractThread;
 namespace dom {
 class BarProp;
+class BrowsingContext;
 struct ChannelPixelLayout;
 class ClientSource;
 class Console;
 class Crypto;
 class CustomElementRegistry;
 class DocGroup;
 class External;
 class Function;
@@ -415,17 +416,18 @@ class nsGlobalWindowInner final : public
   void GetOwnPropertyNames(JSContext* aCx, JS::AutoIdVector& aNames,
                            bool aEnumerableOnly, mozilla::ErrorResult& aRv);
 
   nsPIDOMWindowOuter* GetScriptableTop() override;
   inline nsGlobalWindowOuter* GetTopInternal();
 
   inline nsGlobalWindowOuter* GetScriptableTopInternal();
 
-  nsPIDOMWindowOuter* GetChildWindow(const nsAString& aName);
+  already_AddRefed<mozilla::dom::BrowsingContext> GetChildWindow(
+      const nsAString& aName);
 
   // These return true if we've reached the state in this top level window
   // where we ask the user if further dialogs should be blocked.
   //
   // DialogsAreBeingAbused must be called on the scriptable top inner window.
   //
   // nsGlobalWindowOuter::ShouldPromptToBlockDialogs is implemented in terms of
   // nsGlobalWindowInner::DialogsAreBeingAbused, and will get the scriptable top
@@ -616,17 +618,17 @@ class nsGlobalWindowInner final : public
   void Close(mozilla::ErrorResult& aError);
   nsresult Close() override;
   bool GetClosed(mozilla::ErrorResult& aError);
   void Stop(mozilla::ErrorResult& aError);
   void Focus(mozilla::ErrorResult& aError);
   nsresult Focus() override;
   void Blur(mozilla::ErrorResult& aError);
   nsDOMWindowList* GetFrames() final;
-  already_AddRefed<nsPIDOMWindowOuter> GetFrames(mozilla::ErrorResult& aError);
+  mozilla::dom::BrowsingContext* GetFrames(mozilla::ErrorResult& aError);
   uint32_t Length();
   mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetTop(
       mozilla::ErrorResult& aError);
 
  protected:
   explicit nsGlobalWindowInner(nsGlobalWindowOuter* aOuterWindow);
   // Initializes the mWasOffline member variable
   void InitWasOffline();
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -440,16 +440,20 @@ const char* nsOuterWindowProxy::classNam
 
   return "Window";
 }
 
 void nsOuterWindowProxy::finalize(JSFreeOp* fop, JSObject* proxy) const {
   nsGlobalWindowOuter* outerWindow = GetOuterWindow(proxy);
   if (outerWindow) {
     outerWindow->ClearWrapper(proxy);
+    BrowsingContext* bc = outerWindow->GetBrowsingContext();
+    if (bc) {
+      bc->ClearWindowProxy();
+    }
 
     // Ideally we would use OnFinalize here, but it's possible that
     // EnsureScriptEnvironment will later be called on the window, and we don't
     // want to create a new script object in that case. Therefore, we need to
     // write a non-null value that will reliably crash when dereferenced.
     outerWindow->PoisonOuterWindowProxy(proxy);
   }
 }
@@ -798,16 +802,20 @@ bool nsOuterWindowProxy::AppendIndexedPr
 
   return true;
 }
 
 size_t nsOuterWindowProxy::objectMoved(JSObject* obj, JSObject* old) const {
   nsGlobalWindowOuter* outerWindow = GetOuterWindow(obj);
   if (outerWindow) {
     outerWindow->UpdateWrapper(obj, old);
+    BrowsingContext* bc = outerWindow->GetBrowsingContext();
+    if (bc) {
+      bc->UpdateWindowProxy(obj, old);
+    }
   }
   return 0;
 }
 
 const nsOuterWindowProxy nsOuterWindowProxy::singleton;
 
 class nsChromeOuterWindowProxy : public nsOuterWindowProxy {
  public:
@@ -976,16 +984,19 @@ nsGlobalWindowOuter::~nsGlobalWindowOute
   }
 #endif
 
   MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
           ("DOMWINDOW %p destroyed", this));
 
   JSObject* proxy = GetWrapperMaybeDead();
   if (proxy) {
+    if (mBrowsingContext) {
+      mBrowsingContext->ClearWindowProxy();
+    }
     js::SetProxyReservedSlot(proxy, 0, js::PrivateValue(nullptr));
   }
 
   // An outer window is destroyed with inner windows still possibly
   // alive, iterate through the inner windows and null out their
   // back pointer to this outer, and pull them out of the list of
   // inner windows.
   //
@@ -1176,16 +1187,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   // Traverse stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerForInitialContentBrowser)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
 
   tmp->TraverseHostObjectURIs(cb);
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
@@ -1203,16 +1215,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   // Unlink stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerForInitialContentBrowser)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
+  if (tmp->mBrowsingContext) {
+    tmp->mBrowsingContext->ClearWindowProxy();
+    tmp->mBrowsingContext = nullptr;
+  }
 
   tmp->UnlinkHostObjectURIs();
 
   if (tmp->IsChromeWindow()) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -1815,16 +1831,17 @@ nsresult nsGlobalWindowOuter::SetNewDocu
 
       JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
 
       js::SetProxyReservedSlot(obj, 0, js::PrivateValue(nullptr));
       js::SetProxyReservedSlot(outerObject, 0, js::PrivateValue(nullptr));
 
       outerObject = xpc::TransplantObject(cx, obj, outerObject);
       if (!outerObject) {
+        mBrowsingContext->ClearWindowProxy();
         NS_ERROR("unable to transplant wrappers, probably OOM");
         return NS_ERROR_FAILURE;
       }
 
       js::SetProxyReservedSlot(outerObject, 0,
                                js::PrivateValue(ToSupports(this)));
 
       SetWrapper(outerObject);
@@ -1836,16 +1853,17 @@ nsresult nsGlobalWindowOuter::SetNewDocu
     }
 
     // Enter the new global's realm.
     JSAutoRealm ar(cx, GetWrapperPreserveColor());
 
     {
       JS::Rooted<JSObject*> outer(cx, GetWrapperPreserveColor());
       js::SetWindowProxy(cx, newInnerGlobal, outer);
+      mBrowsingContext->SetWindowProxy(outer);
     }
 
     // Set scriptability based on the state of the docshell.
     bool allow = GetDocShell()->GetCanExecuteScripts();
     xpc::Scriptability::Get(GetWrapperPreserveColor())
         .SetDocShellAllowsScript(allow);
 
     if (!aState) {
@@ -2080,24 +2098,25 @@ void nsGlobalWindowOuter::DispatchDOMWin
             ? "chrome-document-global-created"
             : "content-document-global-created",
         origin.get());
   }
 }
 
 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(EmptyString()); }
 
-void nsGlobalWindowOuter::SetDocShell(nsIDocShell* aDocShell) {
+void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
   MOZ_ASSERT(aDocShell);
 
   if (aDocShell == mDocShell) {
     return;
   }
 
   mDocShell = aDocShell;  // Weak Reference
+  mBrowsingContext = aDocShell->GetBrowsingContext();
 
   nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetScriptableParentOrNull();
   MOZ_RELEASE_ASSERT(!parentWindow || !mTabGroup ||
                      mTabGroup ==
                          nsGlobalWindowOuter::Cast(parentWindow)->mTabGroup);
 
   mTopLevelOuterContentWindow =
       !mIsChrome && GetScriptableTopInternal() == this;
@@ -2622,21 +2641,22 @@ bool nsPIDOMWindowOuter::GetServiceWorke
   if (!topWindow) {
     return false;
   }
   return topWindow->mServiceWorkersTestingEnabled;
 }
 
 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetParentOuter() {
   nsPIDOMWindowOuter* parent = GetScriptableParent();
-  if (!parent) {
+  BrowsingContext* parentBC;
+  if (!parent || !(parentBC = parent->GetBrowsingContext())) {
     return nullptr;
   }
 
-  return WindowProxyHolder(parent);
+  return WindowProxyHolder(parentBC);
 }
 
 /**
  * GetScriptableParent is called when script reads window.parent.
  *
  * In contrast to GetRealParent, GetScriptableParent respects <iframe
  * mozbrowser> boundaries, so if |this| is contained by an <iframe
  * mozbrowser>, we will return |this| as its own parent.
@@ -2694,25 +2714,22 @@ static nsresult GetTopImpl(nsGlobalWindo
   nsCOMPtr<nsPIDOMWindowOuter> parent = aWin;
   do {
     if (!parent) {
       break;
     }
 
     prevParent = parent;
 
-    nsCOMPtr<nsPIDOMWindowOuter> newParent;
     if (aScriptable) {
-      newParent = parent->GetScriptableParent();
+      parent = parent->GetScriptableParent();
     } else {
-      newParent = parent->GetParent();
+      parent = parent->GetParent();
     }
 
-    parent = newParent;
-
   } while (parent != prevParent);
 
   if (parent) {
     parent.swap(*aTop);
   }
 
   return NS_OK;
 }
@@ -2758,26 +2775,26 @@ void nsGlobalWindowOuter::GetContentOute
   }
 
   aRetval.set(nullptr);
 }
 
 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetContentInternal(
     ErrorResult& aError, CallerType aCallerType) {
   // First check for a named frame named "content"
-  nsCOMPtr<nsPIDOMWindowOuter> domWindow =
-      GetChildWindow(NS_LITERAL_STRING("content"));
-  if (domWindow) {
-    return domWindow.forget();
+  RefPtr<BrowsingContext> bc = GetChildWindow(NS_LITERAL_STRING("content"));
+  if (bc) {
+    nsCOMPtr<nsPIDOMWindowOuter> content(bc->GetDOMWindow());
+    return content.forget();
   }
 
   // If we're contained in <iframe mozbrowser>, then GetContent is the same as
   // window.top.
   if (mDocShell && mDocShell->GetIsInMozBrowser()) {
-    domWindow = GetScriptableTop();
+    nsCOMPtr<nsPIDOMWindowOuter> domWindow(GetScriptableTop());
     return domWindow.forget();
   }
 
   nsCOMPtr<nsIDocShellTreeItem> primaryContent;
   if (aCallerType != CallerType::System) {
     if (mDoc) {
       mDoc->WarnOnceAbout(nsIDocument::eWindowContentUntrusted);
     }
@@ -2806,17 +2823,17 @@ already_AddRefed<nsPIDOMWindowOuter> nsG
 
     treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
   }
 
   if (!primaryContent) {
     return nullptr;
   }
 
-  domWindow = primaryContent->GetWindow();
+  nsCOMPtr<nsPIDOMWindowOuter> domWindow = primaryContent->GetWindow();
   return domWindow.forget();
 }
 
 nsresult nsGlobalWindowOuter::GetPrompter(nsIPrompt** aPrompt) {
   if (!mDocShell) return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
   NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
@@ -3551,32 +3568,35 @@ double nsGlobalWindowOuter::GetScrollYOu
 uint32_t nsGlobalWindowOuter::Length() {
   nsDOMWindowList* windows = GetFrames();
 
   return windows ? windows->GetLength() : 0;
 }
 
 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {
   nsCOMPtr<nsPIDOMWindowOuter> top = GetScriptableTop();
-  if (!top) {
+  BrowsingContext* topBC;
+  if (!top || !(topBC = top->GetBrowsingContext())) {
     return nullptr;
   }
-  return WindowProxyHolder(top.forget());
-}
-
-nsPIDOMWindowOuter* nsGlobalWindowOuter::GetChildWindow(
+  return WindowProxyHolder(topBC);
+}
+
+already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
     const nsAString& aName) {
   nsCOMPtr<nsIDocShell> docShell(GetDocShell());
   NS_ENSURE_TRUE(docShell, nullptr);
 
   nsCOMPtr<nsIDocShellTreeItem> child;
   docShell->FindChildWithName(aName, false, true, nullptr, nullptr,
                               getter_AddRefs(child));
 
-  return child ? child->GetWindow() : nullptr;
+  return child && child->GetWindow()
+             ? do_AddRef(child->GetWindow()->GetBrowsingContext())
+             : nullptr;
 }
 
 bool nsGlobalWindowOuter::DispatchCustomEvent(const nsAString& aEventName) {
   bool defaultActionEnabled = true;
   nsContentUtils::DispatchTrustedEvent(mDoc, ToSupports(this), aEventName,
                                        CanBubble::eYes, Cancelable::eYes,
                                        &defaultActionEnabled);
 
@@ -5234,20 +5254,21 @@ void nsGlobalWindowOuter::FireAbuseEvent
                         aPopupWindowFeatures);
 }
 
 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenOuter(
     const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
     ErrorResult& aError) {
   nsCOMPtr<nsPIDOMWindowOuter> window;
   aError = OpenJS(aUrl, aName, aOptions, getter_AddRefs(window));
-  if (!window) {
+  RefPtr<BrowsingContext> bc;
+  if (!window || !(bc = window->GetBrowsingContext())) {
     return nullptr;
   }
-  return WindowProxyHolder(window.forget());
+  return WindowProxyHolder(bc.forget());
 }
 
 nsresult nsGlobalWindowOuter::Open(const nsAString& aUrl,
                                    const nsAString& aName,
                                    const nsAString& aOptions,
                                    nsDocShellLoadState* aLoadState,
                                    bool aForceNoOpener,
                                    nsPIDOMWindowOuter** _retval) {
@@ -5330,26 +5351,27 @@ Nullable<WindowProxyHolder> nsGlobalWind
                         false,               // aContentModal
                         false,               // aCalledNoScript
                         false,               // aDoJSFixups
                         true,                // aNavigate
                         argvArray, nullptr,  // Arguments
                         nullptr,             // aLoadState
                         false,               // aForceNoOpener
                         getter_AddRefs(dialog));
-  if (!dialog) {
+  RefPtr<BrowsingContext> bc;
+  if (!dialog || !(bc = dialog->GetBrowsingContext())) {
     return nullptr;
   }
-  return WindowProxyHolder(dialog.forget());
-}
-
-already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetFramesOuter() {
+  return WindowProxyHolder(bc.forget());
+}
+
+BrowsingContext* nsGlobalWindowOuter::GetFramesOuter() {
   RefPtr<nsPIDOMWindowOuter> frames(this);
   FlushPendingNotifications(FlushType::ContentAndNotify);
-  return frames.forget();
+  return mBrowsingContext;
 }
 
 nsGlobalWindowInner* nsGlobalWindowOuter::CallerInnerWindow(JSContext* aCx) {
   nsIGlobalObject* global = GetIncumbentGlobal();
   NS_ENSURE_TRUE(global, nullptr);
   JS::Rooted<JSObject*> scope(aCx, global->GetGlobalJSObject());
   NS_ENSURE_TRUE(scope, nullptr);
 
@@ -5523,22 +5545,23 @@ void nsGlobalWindowOuter::PostMessageMoz
         !aSubjectPrincipal.GetIsSystemPrincipal() &&
         sourceAttrs.mFirstPartyDomain != targetAttrs.mFirstPartyDomain) {
       return;
     }
   }
 
   // Create and asynchronously dispatch a runnable which will handle actual DOM
   // event creation and dispatch.
-  RefPtr<PostMessageEvent> event =
-      new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
-                               ? nullptr
-                               : callerInnerWin->GetOuterWindowInternal(),
-                           origin, this, providedPrincipal,
-                           callerInnerWin ? callerInnerWin->GetDoc() : nullptr);
+  RefPtr<PostMessageEvent> event = new PostMessageEvent(
+      nsContentUtils::IsCallerChrome() || !callerInnerWin ||
+              !callerInnerWin->GetOuterWindowInternal()
+          ? nullptr
+          : callerInnerWin->GetOuterWindowInternal()->GetBrowsingContext(),
+      origin, this, providedPrincipal,
+      callerInnerWin ? callerInnerWin->GetDoc() : nullptr);
 
   JS::Rooted<JS::Value> message(aCx, aMessage);
   JS::Rooted<JS::Value> transfer(aCx, aTransfer);
 
   event->Write(aCx, message, transfer, JS::CloneDataPolicy(), aError);
   if (NS_WARN_IF(aError.Failed())) {
     return;
   }
@@ -7055,23 +7078,23 @@ ChromeMessageBroadcaster* nsGlobalWindow
   if (!mInnerWindow) {
     NS_WARNING("No inner window available!");
     return nullptr;
   }
   return GetCurrentInnerWindowInternal()->GetGroupMessageManager(aGroup);
 }
 
 void nsPIDOMWindowOuter::SetOpenerForInitialContentBrowser(
-    nsPIDOMWindowOuter* aOpenerWindow) {
+    BrowsingContext* aOpenerWindow) {
   MOZ_ASSERT(!mOpenerForInitialContentBrowser,
              "Don't set OpenerForInitialContentBrowser twice!");
   mOpenerForInitialContentBrowser = aOpenerWindow;
 }
 
-already_AddRefed<nsPIDOMWindowOuter>
+already_AddRefed<BrowsingContext>
 nsPIDOMWindowOuter::TakeOpenerForInitialContentBrowser() {
   // Intentionally forget our own member
   return mOpenerForInitialContentBrowser.forget();
 }
 
 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline = NS_IsOffline(); }
 
 #if defined(MOZ_WIDGET_ANDROID)
@@ -7262,17 +7285,17 @@ nsGlobalWindowOuter::TemporarilyDisableD
   }
 }
 
 mozilla::dom::TabGroup* nsPIDOMWindowOuter::TabGroup() {
   return nsGlobalWindowOuter::Cast(this)->TabGroupOuter();
 }
 
 /* static */ already_AddRefed<nsGlobalWindowOuter> nsGlobalWindowOuter::Create(
-    nsIDocShell* aDocShell, bool aIsChrome) {
+    nsDocShell* aDocShell, bool aIsChrome) {
   uint64_t outerWindowID = aDocShell->GetOuterWindowID();
   RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID);
   if (aIsChrome) {
     window->mIsChrome = true;
   }
   window->SetDocShell(aDocShell);
 
   window->InitWasOffline();
@@ -7405,13 +7428,8 @@ nsPIDOMWindowOuter::nsPIDOMWindowOuter(u
       mIsRootOuterWindow(false),
       mInnerWindow(nullptr),
       mWindowID(aWindowID),
       mMarkedCCGeneration(0),
       mServiceWorkersTestingEnabled(false),
       mLargeAllocStatus(LargeAllocStatus::NONE) {}
 
 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() {}
-
-mozilla::dom::BrowsingContext* nsPIDOMWindowOuter::GetBrowsingContext() const {
-  return mDocShell ? nsDocShell::Cast(mDocShell)->GetBrowsingContext()
-                   : nullptr;
-}
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -52,16 +52,17 @@
 #include "mozilla/dom/WindowBinding.h"
 #include "Units.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSize.h"
 #include "nsCheapSets.h"
 #include "mozilla/dom/ImageBitmapSource.h"
 #include "mozilla/UniquePtr.h"
 
+class nsDocShell;
 class nsIArray;
 class nsIBaseWindow;
 class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShellTreeOwner;
 class nsIDOMWindowUtils;
 class nsIScrollableFrame;
 class nsIControllers;
@@ -88,16 +89,17 @@ class IdleRequestExecutor;
 
 struct IdleObserverHolder;
 
 namespace mozilla {
 class AbstractThread;
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
+class BrowsingContext;
 struct ChannelPixelLayout;
 class Console;
 class Crypto;
 class CustomElementRegistry;
 class DocGroup;
 class External;
 class Function;
 class Gamepad;
@@ -211,17 +213,17 @@ class nsGlobalWindowOuter final : public
     return sOuterWindowsById;
   }
 
   static nsGlobalWindowOuter* FromSupports(nsISupports* supports) {
     // Make sure this matches the casts we do in QueryInterface().
     return (nsGlobalWindowOuter*)(mozilla::dom::EventTarget*)supports;
   }
 
-  static already_AddRefed<nsGlobalWindowOuter> Create(nsIDocShell* aDocShell,
+  static already_AddRefed<nsGlobalWindowOuter> Create(nsDocShell* aDocShell,
                                                       bool aIsChrome);
 
   // public methods
   nsPIDOMWindowOuter* GetPrivateParent();
 
   // callback for close event
   void ReallyCloseWindow();
 
@@ -357,17 +359,18 @@ class nsGlobalWindowOuter final : public
   already_AddRefed<nsPIDOMWindowOuter> IndexedGetterOuter(uint32_t aIndex);
 
   already_AddRefed<nsPIDOMWindowOuter> GetTop() override;
   nsPIDOMWindowOuter* GetScriptableTop() override;
   inline nsGlobalWindowOuter* GetTopInternal();
 
   inline nsGlobalWindowOuter* GetScriptableTopInternal();
 
-  nsPIDOMWindowOuter* GetChildWindow(const nsAString& aName);
+  already_AddRefed<mozilla::dom::BrowsingContext> GetChildWindow(
+      const nsAString& aName);
 
   // These return true if we've reached the state in this top level window
   // where we ask the user if further dialogs should be blocked.
   //
   // DialogsAreBeingAbused must be called on the scriptable top inner window.
   //
   // ShouldPromptToBlockDialogs is implemented in terms of
   // DialogsAreBeingAbused, and will get the scriptable top inner window
@@ -521,17 +524,17 @@ class nsGlobalWindowOuter final : public
   void CloseOuter(bool aTrustedCaller);
   nsresult Close() override;
   bool GetClosedOuter();
   bool Closed() override;
   void StopOuter(mozilla::ErrorResult& aError);
   void FocusOuter(mozilla::ErrorResult& aError);
   nsresult Focus() override;
   void BlurOuter();
-  already_AddRefed<nsPIDOMWindowOuter> GetFramesOuter();
+  mozilla::dom::BrowsingContext* GetFramesOuter();
   nsDOMWindowList* GetFrames() final;
   uint32_t Length();
   mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetTopOuter();
 
   nsresult GetPrompter(nsIPrompt** aPrompt) override;
 
  protected:
   nsPIDOMWindowOuter* GetOpenerWindowOuter();
@@ -976,17 +979,17 @@ class nsGlobalWindowOuter final : public
  private:
   enum class SecureContextFlags { eDefault, eIgnoreOpener };
   // Called only on outer windows to compute the value that will be returned by
   // IsSecureContext() for the inner window that corresponds to aDocument.
   bool ComputeIsSecureContext(
       nsIDocument* aDocument,
       SecureContextFlags aFlags = SecureContextFlags::eDefault);
 
-  void SetDocShell(nsIDocShell* aDocShell);
+  void SetDocShell(nsDocShell* aDocShell);
 
   // nsPIDOMWindow{Inner,Outer} should be able to see these helper methods.
   friend class nsPIDOMWindowInner;
   friend class nsPIDOMWindowOuter;
 
   mozilla::dom::TabGroup* TabGroupOuter();
 
   void SetIsBackgroundInternal(bool aIsBackground);
@@ -1094,16 +1097,17 @@ class nsGlobalWindowOuter final : public
     // A weak pointer to the nsPresShell that we are doing fullscreen for.
     // The pointer being set indicates we've set the IsInFullscreenChange
     // flag on this pres shell.
     nsWeakPtr mFullscreenPresShell;
   } mChromeFields;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
+  friend class mozilla::dom::BrowsingContext;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
   friend class mozilla::dom::TimeoutManager;
   friend class IdleRequestExecutor;
   friend class nsGlobalWindowInner;
 };
 
 // XXX: EWW - This is an awful hack - let's not do this
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -878,17 +878,20 @@ class nsPIDOMWindowOuter : public mozIDO
   // the window was frozen.
   virtual nsresult FireDelayedDOMEvents() = 0;
 
   /**
    * Get the docshell in this window.
    */
   inline nsIDocShell* GetDocShell() const;
 
-  mozilla::dom::BrowsingContext* GetBrowsingContext() const;
+  /**
+   * Get the browsing context in this window.
+   */
+  inline mozilla::dom::BrowsingContext* GetBrowsingContext() const;
 
   /**
    * Set a new document in the window. Calling this method will in most cases
    * create a new inner window. This may be called with a pointer to the current
    * document, in that case the document remains unchanged, but a new inner
    * window will be created.
    *
    * aDocument must not be null.
@@ -1098,18 +1101,20 @@ class nsPIDOMWindowOuter : public mozIDO
    * the window before the content itself is created. This is important in order
    * to set the DocGroup of a document, as the opener must be set before the
    * document is created.
    *
    * SetOpenerForInitialContentBrowser is used to set which opener will be used,
    * and TakeOpenerForInitialContentBrowser is used by nsXULElement in order to
    * take the value set earlier, and null out the value in the window.
    */
-  void SetOpenerForInitialContentBrowser(nsPIDOMWindowOuter* aOpener);
-  already_AddRefed<nsPIDOMWindowOuter> TakeOpenerForInitialContentBrowser();
+  void SetOpenerForInitialContentBrowser(
+      mozilla::dom::BrowsingContext* aOpener);
+  already_AddRefed<mozilla::dom::BrowsingContext>
+  TakeOpenerForInitialContentBrowser();
 
  protected:
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
   void SetChromeEventHandlerInternal(
       mozilla::dom::EventTarget* aChromeEventHandler);
@@ -1124,18 +1129,19 @@ class nsPIDOMWindowOuter : public mozIDO
   // Cache the URI when mDoc is cleared.
   nsCOMPtr<nsIURI> mDocumentURI;  // strong
 
   nsCOMPtr<mozilla::dom::EventTarget> mParentTarget;                 // strong
   RefPtr<mozilla::dom::ContentFrameMessageManager> mMessageManager;  // strong
 
   nsCOMPtr<mozilla::dom::Element> mFrameElement;
 
-  // This reference is used by nsGlobalWindow.
+  // These references are used by nsGlobalWindow.
   nsCOMPtr<nsIDocShell> mDocShell;
+  RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
 
   uint32_t mModalStateDepth;
 
   // Tracks activation state that's used for :-moz-window-inactive.
   bool mIsActive;
 
   // Tracks whether our docshell is active.  If it is, mIsBackground
   // is false.  Too bad we have so many different concepts of
@@ -1175,17 +1181,17 @@ class nsPIDOMWindowOuter : public mozIDO
   uint32_t mMarkedCCGeneration;
 
   // Let the service workers plumbing know that some feature are enabled while
   // testing.
   bool mServiceWorkersTestingEnabled;
 
   mozilla::dom::LargeAllocStatus mLargeAllocStatus;
 
-  nsCOMPtr<nsPIDOMWindowOuter> mOpenerForInitialContentBrowser;
+  RefPtr<mozilla::dom::BrowsingContext> mOpenerForInitialContentBrowser;
 
   using PermissionInfo = std::pair<bool, mozilla::TimeStamp>;
   nsDataHashtable<nsStringHashKey, PermissionInfo> mAutoplayTemporaryPermission;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowOuter, NS_PIDOMWINDOWOUTER_IID)
 
 #include "nsPIDOMWindowInlines.h"
--- a/dom/base/nsPIDOMWindowInlines.h
+++ b/dom/base/nsPIDOMWindowInlines.h
@@ -63,15 +63,19 @@ bool nsPIDOMWindowInner::IsTopInnerWindo
 }
 
 nsIDocShell* nsPIDOMWindowOuter::GetDocShell() const { return mDocShell; }
 
 nsIDocShell* nsPIDOMWindowInner::GetDocShell() const {
   return mOuterWindow ? mOuterWindow->GetDocShell() : nullptr;
 }
 
+mozilla::dom::BrowsingContext* nsPIDOMWindowOuter::GetBrowsingContext() const {
+  return mBrowsingContext;
+}
+
 mozilla::dom::Element* nsPIDOMWindowOuter::GetFocusedElement() const {
   return mInnerWindow ? mInnerWindow->GetFocusedElement() : nullptr;
 }
 
 mozilla::dom::Element* nsPIDOMWindowInner::GetFocusedElement() const {
   return mFocusedElement;
 }
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -102,17 +102,17 @@ void nsWindowRoot::GetEventTargetParent(
   aVisitor.SetParentTarget(mParent, false);
 }
 
 nsresult nsWindowRoot::PostHandleEvent(EventChainPostVisitor& aVisitor) {
   return NS_OK;
 }
 
 nsPIDOMWindowOuter* nsWindowRoot::GetOwnerGlobalForBindingsInternal() {
-  return GetWindow();
+  return mWindow;
 }
 
 nsIGlobalObject* nsWindowRoot::GetOwnerGlobal() const {
   nsCOMPtr<nsIGlobalObject> global =
       do_QueryInterface(mWindow->GetCurrentInnerWindow());
   // We're still holding a ref to it, so returning the raw pointer is ok...
   return global;
 }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1123,17 +1123,17 @@ bool VariantToJsval(JSContext* aCx, nsIV
     return false;
   }
 
   return true;
 }
 
 bool WrapObject(JSContext* cx, const WindowProxyHolder& p,
                 JS::MutableHandle<JS::Value> rval) {
-  return ToJSValue(cx, p.get(), rval);
+  return ToJSValue(cx, p, rval);
 }
 
 static int CompareIdsAtIndices(const void* aElement1, const void* aElement2,
                                void* aClosure) {
   const uint16_t index1 = *static_cast<const uint16_t*>(aElement1);
   const uint16_t index2 = *static_cast<const uint16_t*>(aElement2);
   const PropertyInfo* infos = static_cast<PropertyInfo*>(aClosure);
 
@@ -3230,17 +3230,18 @@ nsresult UnwrapArgImpl(JSContext* cx, JS
 
 nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle<JSObject*> src,
                               WindowProxyHolder& ppArg) {
   nsCOMPtr<nsPIDOMWindowInner> inner;
   nsresult rv = UnwrapArg<nsPIDOMWindowInner>(cx, src, getter_AddRefs(inner));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
-  ppArg = outer.forget();
+  RefPtr<BrowsingContext> bc = outer ? outer->GetBrowsingContext() : nullptr;
+  ppArg = bc.forget();
   return NS_OK;
 }
 
 template <decltype(JS::NewMapObject) Method>
 bool GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
                                     size_t aSlotIndex,
                                     JS::MutableHandle<JSObject*> aBackingObj,
                                     bool* aBackingObjCreated) {
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1482,17 +1482,17 @@ DOMInterfaces = {
     },
     'implicitJSContext': [
         'createImageBitmap',
         'requestIdleCallback'
     ],
 },
 
 'WindowProxy': {
-    'nativeType': 'mozilla::dom::WindowProxyHolder',
+    'headerFile': 'mozilla/dom/WindowProxyHolder.h',
     'concrete': False
 },
 
 'WindowRoot': {
     'nativeType': 'nsWindowRoot'
 },
 
 'WorkerDebuggerGlobalScope': {
--- a/dom/bindings/ToJSValue.cpp
+++ b/dom/bindings/ToJSValue.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/WindowProxyHolder.h"
 #include "nsAString.h"
 #include "nsContentUtils.h"
 #include "nsStringBuffer.h"
 #include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -55,10 +56,28 @@ bool ToJSValue(JSContext* aCx, ErrorResu
 }
 
 bool ToJSValue(JSContext* aCx, Promise& aArgument,
                JS::MutableHandle<JS::Value> aValue) {
   aValue.setObject(*aArgument.PromiseObj());
   return MaybeWrapObjectValue(aCx, aValue);
 }
 
+bool ToJSValue(JSContext* aCx, const WindowProxyHolder& aArgument,
+               JS::MutableHandle<JS::Value> aValue) {
+  BrowsingContext* bc = aArgument.get();
+  if (!bc) {
+    aValue.setNull();
+    return true;
+  }
+  JS::Rooted<JSObject*> windowProxy(aCx, bc->GetWindowProxy());
+  if (!windowProxy) {
+    nsPIDOMWindowOuter* window = bc->GetDOMWindow();
+    if (!window->EnsureInnerWindow()) {
+      return Throw(aCx, NS_ERROR_UNEXPECTED);
+    }
+    windowProxy = bc->GetWindowProxy();
+  }
+  return ToJSValue(aCx, windowProxy, aValue);
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -18,16 +18,17 @@
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
+class WindowProxyHolder;
 
 // If ToJSValue returns false, it must set an exception on the
 // JSContext.
 
 // Accept strings.
 MOZ_MUST_USE bool ToJSValue(JSContext* aCx, const nsAString& aArgument,
                             JS::MutableHandle<JS::Value> aValue);
 
@@ -216,16 +217,19 @@ ToJSValue(JSContext* aCx, T& aArgument, 
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
 
   xpcObjectHelper helper(ToSupports(&aArgument));
   JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
   return XPCOMObjectToJsval(aCx, scope, helper, nullptr, true, aValue);
 }
 
+MOZ_MUST_USE bool ToJSValue(JSContext* aCx, const WindowProxyHolder& aArgument,
+                            JS::MutableHandle<JS::Value> aValue);
+
 // Accept nsRefPtr/nsCOMPtr
 template <typename T>
 MOZ_MUST_USE bool ToJSValue(JSContext* aCx, const nsCOMPtr<T>& aArgument,
                             JS::MutableHandle<JS::Value> aValue) {
   return ToJSValue(aCx, *aArgument.get(), aValue);
 }
 
 template <typename T>
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -199,32 +199,33 @@ BrowserElementParent::OpenWindowResult B
   popupFrameElement->AllowCreateFrameLoader();
   popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent);
 
   return opened;
 }
 
 /* static */
 BrowserElementParent::OpenWindowResult
-BrowserElementParent::OpenWindowInProcess(nsPIDOMWindowOuter* aOpenerWindow,
+BrowserElementParent::OpenWindowInProcess(BrowsingContext* aOpenerWindow,
                                           nsIURI* aURI, const nsAString& aName,
                                           const nsACString& aFeatures,
                                           bool aForceNoOpener,
                                           mozIDOMWindowProxy** aReturnWindow) {
   *aReturnWindow = nullptr;
 
   // If we call window.open from an <iframe> inside an <iframe mozbrowser>,
   // it's as though the top-level document inside the <iframe mozbrowser>
   // called window.open.  (Indeed, in the OOP case, the inner <iframe> lives
   // out-of-process, so we couldn't touch it if we tried.)
   //
   // GetScriptableTop gets us the <iframe mozbrowser>'s window; we'll use its
   // frame element, rather than aOpenerWindow's frame element, as our "opener
   // frame element" below.
-  nsCOMPtr<nsPIDOMWindowOuter> win = aOpenerWindow->GetScriptableTop();
+  nsCOMPtr<nsPIDOMWindowOuter> win =
+      aOpenerWindow->GetDOMWindow()->GetScriptableTop();
 
   nsCOMPtr<Element> openerFrameElement = win->GetFrameElementInternal();
   NS_ENSURE_TRUE(openerFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
 
   RefPtr<HTMLIFrameElement> popupFrameElement =
       CreateIframe(openerFrameElement, aName, /* aRemote = */ false);
   NS_ENSURE_TRUE(popupFrameElement, BrowserElementParent::OPEN_WINDOW_IGNORED);
 
--- a/dom/browser-element/BrowserElementParent.h
+++ b/dom/browser-element/BrowserElementParent.h
@@ -14,16 +14,17 @@
 #include "mozilla/dom/Element.h"
 
 class nsIDOMWindow;
 class nsIURI;
 
 namespace mozilla {
 
 namespace dom {
+class BrowsingContext;
 class TabParent;
 }  // namespace dom
 
 /**
  * BrowserElementParent implements a portion of the parent-process side of
  * <iframe mozbrowser>.
  *
  * Most of the parent-process side of <iframe mozbrowser> is implemented in
@@ -98,18 +99,18 @@ class BrowserElementParent {
    * hand.  Feel free to add an override, if they are inconvenient to you.)
    *
    * @param aURI the URI the new window should load.  May be null.
    * @return an OpenWindowResult that describes whether the browser added the
    *         frame to a document or whether they called preventDefault to
    * prevent the platform from handling the open request
    */
   static OpenWindowResult OpenWindowInProcess(
-      nsPIDOMWindowOuter* aOpenerWindow, nsIURI* aURI, const nsAString& aName,
-      const nsACString& aFeatures, bool aForceNoOpener,
+      mozilla::dom::BrowsingContext* aOpenerWindow, nsIURI* aURI,
+      const nsAString& aName, const nsACString& aFeatures, bool aForceNoOpener,
       mozIDOMWindowProxy** aReturnWindow);
 
  private:
   static OpenWindowResult DispatchOpenWindowEvent(
       dom::Element* aOpenerFrameElement, dom::Element* aPopupFrameElement,
       const nsAString& aURL, const nsAString& aName,
       const nsAString& aFeatures);
 };
--- a/dom/events/EventTarget.cpp
+++ b/dom/events/EventTarget.cpp
@@ -185,13 +185,13 @@ void EventTarget::DispatchEvent(Event& a
 }
 
 Nullable<WindowProxyHolder> EventTarget::GetOwnerGlobalForBindings() {
   nsPIDOMWindowOuter* win = GetOwnerGlobalForBindingsInternal();
   if (!win) {
     return nullptr;
   }
 
-  return WindowProxyHolder(win);
+  return WindowProxyHolder(win->GetBrowsingContext());
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -1,23 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/MessageEvent.h"
+#include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/ServiceWorker.h"
 
 #include "mozilla/HoldDropJSObjects.h"
 #include "jsapi.h"
-#include "nsGlobalWindow.h"  // So we can assign an nsGlobalWindow* to mWindowSource
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MessageEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageEvent, Event)
   tmp->mData.setUndefined();
--- a/dom/events/MessageEvent.h
+++ b/dom/events/MessageEvent.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 namespace dom {
 
+class BrowsingContext;
 struct MessageEventInit;
 class MessagePort;
 class OwningWindowProxyOrMessagePortOrServiceWorker;
 class ServiceWorker;
 class WindowProxyOrMessagePortOrServiceWorker;
 
 /**
  * Implements the MessageEvent event, used for cross-document messaging and
@@ -75,17 +76,17 @@ class MessageEvent final : public Event 
 
  protected:
   ~MessageEvent();
 
  private:
   JS::Heap<JS::Value> mData;
   nsString mOrigin;
   nsString mLastEventId;
-  RefPtr<nsPIDOMWindowOuter> mWindowSource;
+  RefPtr<BrowsingContext> mWindowSource;
   RefPtr<MessagePort> mPortSource;
   RefPtr<ServiceWorker> mServiceWorkerSource;
 
   nsTArray<RefPtr<MessagePort>> mPorts;
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/events/UIEvent.h
+++ b/dom/events/UIEvent.h
@@ -48,17 +48,17 @@ class UIEvent : public Event {
   void InitUIEvent(const nsAString& typeArg, bool canBubbleArg,
                    bool cancelableArg, nsGlobalWindowInner* viewArg,
                    int32_t detailArg);
 
   Nullable<WindowProxyHolder> GetView() const {
     if (!mView) {
       return nullptr;
     }
-    return WindowProxyHolder(mView);
+    return WindowProxyHolder(mView->GetBrowsingContext());
   }
 
   int32_t Detail() const { return mDetail; }
 
   int32_t LayerX() const { return GetLayerPoint().x; }
 
   int32_t LayerY() const { return GetLayerPoint().y; }
 
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -375,17 +375,17 @@ int32_t HTMLObjectElement::TabIndexDefau
 }
 
 Nullable<WindowProxyHolder> HTMLObjectElement::GetContentWindow(
     nsIPrincipal& aSubjectPrincipal) {
   nsIDocument* doc = GetContentDocument(aSubjectPrincipal);
   if (doc) {
     nsPIDOMWindowOuter* win = doc->GetWindow();
     if (win) {
-      return WindowProxyHolder(win);
+      return WindowProxyHolder(win->GetBrowsingContext());
     }
   }
 
   return nullptr;
 }
 
 bool HTMLObjectElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
                                        const nsAString& aValue,
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -66,73 +66,72 @@ int32_t nsGenericHTMLFrameElement::TabIn
 nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement() {
   if (mFrameLoader) {
     mFrameLoader->Destroy();
   }
 }
 
 nsIDocument* nsGenericHTMLFrameElement::GetContentDocument(
     nsIPrincipal& aSubjectPrincipal) {
-  nsCOMPtr<nsPIDOMWindowOuter> win = GetContentWindowInternal();
-  if (!win) {
+  RefPtr<BrowsingContext> bc = GetContentWindowInternal();
+  if (!bc) {
     return nullptr;
   }
 
-  nsIDocument* doc = win->GetDoc();
+  nsIDocument* doc = bc->GetDOMWindow()->GetDoc();
   if (!doc) {
     return nullptr;
   }
 
   // Return null for cross-origin contentDocument.
   if (!aSubjectPrincipal.SubsumesConsideringDomain(doc->NodePrincipal())) {
     return nullptr;
   }
   return doc;
 }
 
-already_AddRefed<nsPIDOMWindowOuter>
-nsGenericHTMLFrameElement::GetContentWindowInternal() {
+BrowsingContext* nsGenericHTMLFrameElement::GetContentWindowInternal() {
   EnsureFrameLoader();
 
   if (!mFrameLoader) {
     return nullptr;
   }
 
   if (mFrameLoader->DepthTooGreat()) {
     // Claim to have no contentWindow
     return nullptr;
   }
 
-  nsCOMPtr<nsIDocShell> doc_shell = mFrameLoader->GetDocShell(IgnoreErrors());
+  RefPtr<nsDocShell> doc_shell = mFrameLoader->GetDocShell(IgnoreErrors());
   if (!doc_shell) {
     return nullptr;
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> win = doc_shell->GetWindow();
-  return win.forget();
+  return doc_shell->GetBrowsingContext();
 }
 
 Nullable<WindowProxyHolder> nsGenericHTMLFrameElement::GetContentWindow() {
-  nsCOMPtr<nsPIDOMWindowOuter> win = GetContentWindowInternal();
-  if (!win) {
+  RefPtr<BrowsingContext> bc = GetContentWindowInternal();
+  if (!bc) {
     return nullptr;
   }
-  return WindowProxyHolder(win.forget());
+  return WindowProxyHolder(bc);
 }
 
 void nsGenericHTMLFrameElement::EnsureFrameLoader() {
   if (!IsInComposedDoc() || mFrameLoader || mFrameLoaderCreationDisallowed) {
     // If frame loader is there, we just keep it around, cached
     return;
   }
 
   // Strangely enough, this method doesn't actually ensure that the
   // frameloader exists.  It's more of a best-effort kind of thing.
   mFrameLoader = nsFrameLoader::Create(
-      this, nsPIDOMWindowOuter::From(mOpenerWindow), mNetworkCreated);
+      this, mOpenerWindow ? mOpenerWindow->GetDOMWindow() : nullptr,
+      mNetworkCreated);
 }
 
 nsresult nsGenericHTMLFrameElement::CreateRemoteFrameLoader(
     nsITabParent* aTabParent) {
   MOZ_ASSERT(!mFrameLoader);
   EnsureFrameLoader();
   NS_ENSURE_STATE(mFrameLoader);
   mFrameLoader->SetRemoteBrowser(aTabParent);
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -123,17 +123,17 @@ class nsGenericHTMLFrameElement : public
                                 const nsAttrValue* aOldValue,
                                 nsIPrincipal* aSubjectPrincipal,
                                 bool aNotify) override;
   virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
                                           const nsAttrValueOrString& aValue,
                                           bool aNotify) override;
 
   RefPtr<nsFrameLoader> mFrameLoader;
-  nsCOMPtr<nsPIDOMWindowOuter> mOpenerWindow;
+  RefPtr<mozilla::dom::BrowsingContext> mOpenerWindow;
 
   nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal;
 
   /**
    * True if we have already loaded the frame's original src
    */
   bool mSrcLoadHappened;
 
@@ -165,15 +165,15 @@ class nsGenericHTMLFrameElement : public
    * @param aValue the value being set or null if the value is being unset
    * @param aNotify Whether we plan to notify document observers.
    */
   void AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName,
                             const nsAttrValueOrString* aValue,
                             nsIPrincipal* aMaybeScriptedPrincipal,
                             bool aNotify);
 
-  already_AddRefed<nsPIDOMWindowOuter> GetContentWindowInternal();
+  mozilla::dom::BrowsingContext* GetContentWindowInternal();
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsGenericHTMLFrameElement,
                               NS_GENERICHTMLFRAMEELEMENT_IID)
 
 #endif  // nsGenericHTMLFrameElement_h
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1157,17 +1157,17 @@ mozilla::dom::Nullable<mozilla::dom::Win
   }
   RefPtr<nsGlobalWindowOuter> win = nsGlobalWindowOuter::Cast(outer);
   nsCOMPtr<nsPIDOMWindowOuter> newWindow;
   // XXXbz We ignore aReplace for now.
   rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newWindow));
   if (!newWindow) {
     return nullptr;
   }
-  return WindowProxyHolder(newWindow.forget());
+  return WindowProxyHolder(newWindow->GetBrowsingContext());
 }
 
 already_AddRefed<nsIDocument> nsHTMLDocument::Open(
     JSContext* cx, const Optional<nsAString>& /* unused */,
     const nsAString& aReplace, ErrorResult& aError) {
   // Implements the "When called with two arguments (or fewer)" steps here:
   // https://html.spec.whatwg.org/multipage/webappapis.html#opening-the-input-stream
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3258,26 +3258,21 @@ void TabChildMessageManager::MarkForCC()
   if (elm) {
     elm->MarkForCC();
   }
   MessageManagerGlobal::MarkForCC();
 }
 
 Nullable<WindowProxyHolder> TabChildMessageManager::GetContent(
     ErrorResult& aError) {
-  if (!mTabChild) {
-    aError.Throw(NS_ERROR_NULL_POINTER);
+  nsCOMPtr<nsIDocShell> docShell = GetDocShell(aError);
+  if (!docShell) {
     return nullptr;
   }
-  nsCOMPtr<nsPIDOMWindowOuter> window =
-      do_GetInterface(mTabChild->WebNavigation());
-  if (!window) {
-    return nullptr;
-  }
-  return WindowProxyHolder(window.forget());
+  return WindowProxyHolder(nsDocShell::Cast(docShell)->GetBrowsingContext());
 }
 
 already_AddRefed<nsIDocShell> TabChildMessageManager::GetDocShell(
     ErrorResult& aError) {
   if (!mTabChild) {
     aError.Throw(NS_ERROR_NULL_POINTER);
     return nullptr;
   }
--- a/dom/smil/TimeEvent.h
+++ b/dom/smil/TimeEvent.h
@@ -35,17 +35,17 @@ class TimeEvent final : public Event {
                      int32_t aDetail);
 
   int32_t Detail() const { return mDetail; }
 
   Nullable<WindowProxyHolder> GetView() const {
     if (!mView) {
       return nullptr;
     }
-    return WindowProxyHolder(mView);
+    return WindowProxyHolder(mView->GetBrowsingContext());
   }
 
   TimeEvent* AsTimeEvent() final { return this; }
 
  private:
   ~TimeEvent() {}
 
   nsCOMPtr<nsPIDOMWindowOuter> mView;
--- a/dom/xul/XULFrameElement.cpp
+++ b/dom/xul/XULFrameElement.cpp
@@ -32,34 +32,31 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(XULFrameElement, nsXULElement,
                                              nsIFrameLoaderOwner)
 
 JSObject* XULFrameElement::WrapNode(JSContext* aCx,
                                     JS::Handle<JSObject*> aGivenProto) {
   return XULFrameElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-nsIDocShell* XULFrameElement::GetDocShell() {
+nsDocShell* XULFrameElement::GetDocShell() {
   RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
   return frameLoader ? frameLoader->GetDocShell(IgnoreErrors()) : nullptr;
 }
 
 already_AddRefed<nsIWebNavigation> XULFrameElement::GetWebNavigation() {
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(docShell);
   return webnav.forget();
 }
 
 Nullable<WindowProxyHolder> XULFrameElement::GetContentWindow() {
-  nsCOMPtr<nsIDocShell> docShell = GetDocShell();
+  RefPtr<nsDocShell> docShell = GetDocShell();
   if (docShell) {
-    nsCOMPtr<nsPIDOMWindowOuter> win = docShell->GetWindow();
-    if (win) {
-      return WindowProxyHolder(win.forget());
-    }
+    return WindowProxyHolder(docShell->GetWindowProxy());
   }
 
   return nullptr;
 }
 
 nsIDocument* XULFrameElement::GetContentDocument() {
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (docShell) {
@@ -73,33 +70,34 @@ nsIDocument* XULFrameElement::GetContent
 
 void XULFrameElement::LoadSrc() {
   if (!IsInUncomposedDoc() || !OwnerDoc()->GetRootElement()) {
     return;
   }
   RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
   if (!frameLoader) {
     // Check if we have an opener we need to be setting
-    nsCOMPtr<nsPIDOMWindowOuter> opener = mOpener;
+    RefPtr<BrowsingContext> opener = mOpener;
     if (!opener) {
       // If we are a primary xul-browser, we want to take the opener property!
       nsCOMPtr<nsPIDOMWindowOuter> window = OwnerDoc()->GetWindow();
       if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true,
                       eIgnoreCase) &&
           window) {
         opener = window->TakeOpenerForInitialContentBrowser();
       }
     }
     mOpener = nullptr;
 
     // false as the last parameter so that xul:iframe/browser/editor
     // session history handling works like dynamic html:iframes.
     // Usually xul elements are used in chrome, which doesn't have
     // session history at all.
-    mFrameLoader = nsFrameLoader::Create(this, opener, false);
+    mFrameLoader = nsFrameLoader::Create(
+        this, opener ? opener->GetDOMWindow() : nullptr, false);
     if (NS_WARN_IF(!mFrameLoader)) {
       return;
     }
 
     (new AsyncEventDispatcher(this, NS_LITERAL_STRING("XULFrameLoaderCreated"),
                               CanBubble::eYes))
         ->RunDOMEventWhenSafe();
   }
--- a/dom/xul/XULFrameElement.h
+++ b/dom/xul/XULFrameElement.h
@@ -18,26 +18,28 @@
 #include "nsXULElement.h"
 
 class nsIWebNavigation;
 class nsFrameLoader;
 
 namespace mozilla {
 namespace dom {
 
+class BrowsingContext;
+
 class XULFrameElement final : public nsXULElement, public nsIFrameLoaderOwner {
  public:
   explicit XULFrameElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
       : nsXULElement(std::move(aNodeInfo)) {}
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULFrameElement, nsXULElement)
 
   // XULFrameElement.webidl
-  nsIDocShell* GetDocShell();
+  nsDocShell* GetDocShell();
   already_AddRefed<nsIWebNavigation> GetWebNavigation();
   Nullable<WindowProxyHolder> GetContentWindow();
   nsIDocument* GetContentDocument();
 
   // nsIFrameLoaderOwner / MozFrameLoaderOwner
   NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() override {
     return do_AddRef(mFrameLoader);
   }
@@ -70,17 +72,17 @@ class XULFrameElement final : public nsX
                                 const nsAttrValue* aOldValue,
                                 nsIPrincipal* aSubjectPrincipal,
                                 bool aNotify) override;
 
  protected:
   virtual ~XULFrameElement() {}
 
   RefPtr<nsFrameLoader> mFrameLoader;
-  nsCOMPtr<nsPIDOMWindowOuter> mOpener;
+  RefPtr<BrowsingContext> mOpener;
 
   JSObject* WrapNode(JSContext* aCx,
                      JS::Handle<JSObject*> aGivenProto) override;
 
   void LoadSrc();
 };
 
 }  // namespace dom
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -17,16 +17,17 @@
 #include "xpcprivate.h"
 
 #include "jsapi.h"
 #include "nsJSUtils.h"
 #include "nsPrintfCString.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/XrayExpandoClass.h"
 #include "nsGlobalWindow.h"
 
 using namespace mozilla::dom;
 using namespace JS;
 using namespace mozilla;
 
@@ -1922,18 +1923,19 @@ bool XrayWrapper<Base, Traits>::getPrope
   // only relevant for CrossOriginXrayWrapper, which calls
   // getPropertyDescriptor from getOwnPropertyDescriptor.
   nsGlobalWindowInner* win = nullptr;
   if (!desc.object() && JSID_IS_STRING(id) && (win = AsWindow(cx, wrapper))) {
     nsAutoJSString name;
     if (!name.init(cx, JSID_TO_STRING(id))) {
       return false;
     }
-    if (nsCOMPtr<nsPIDOMWindowOuter> childDOMWin = win->GetChildWindow(name)) {
-      auto* cwin = nsGlobalWindowOuter::Cast(childDOMWin);
+    RefPtr<BrowsingContext> childDOMWin(win->GetChildWindow(name));
+    if (childDOMWin) {
+      auto* cwin = nsGlobalWindowOuter::Cast(childDOMWin->GetDOMWindow());
       JSObject* childObj = cwin->FastGetGlobalJSObject();
       if (MOZ_UNLIKELY(!childObj)) {
         return xpc::Throw(cx, NS_ERROR_FAILURE);
       }
       ExposeObjectToActiveJS(childObj);
       FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj),
                              /* readOnly = */ true);
       return JS_WrapPropertyDescriptor(cx, desc);
--- a/toolkit/components/extensions/ExtensionPolicyService.cpp
+++ b/toolkit/components/extensions/ExtensionPolicyService.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/Services.h"
 #include "mozilla/SimpleEnumerator.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentFrameMessageManager.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Promise-inl.h"
 #include "mozIExtensionProcessScript.h"
+#include "nsDocShell.h"
 #include "nsEscape.h"
 #include "nsGkAtoms.h"
 #include "nsIChannel.h"
 #include "nsIContentPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsGlobalWindowOuter.h"
 #include "nsILoadInfo.h"
--- a/toolkit/components/extensions/WebExtensionContentScript.h
+++ b/toolkit/components/extensions/WebExtensionContentScript.h
@@ -110,17 +110,17 @@ class MozDocumentMatcher : public nsISup
 
   bool Matches(const DocInfo& aDoc) const;
   bool MatchesURI(const URLInfo& aURL) const;
 
   bool MatchesLoadInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo) const {
     return Matches({aURL, aLoadInfo});
   }
   bool MatchesWindow(const dom::WindowProxyHolder& aWindow) const {
-    return Matches(aWindow.get());
+    return Matches(aWindow.get()->GetDOMWindow());
   }
 
   WebExtensionPolicy* GetExtension() { return mExtension; }
 
   WebExtensionPolicy* Extension() { return mExtension; }
   const WebExtensionPolicy* Extension() const { return mExtension; }
 
   bool AllFrames() const { return mAllFrames; }
--- a/toolkit/components/extensions/WebExtensionPolicy.cpp
+++ b/toolkit/components/extensions/WebExtensionPolicy.cpp
@@ -670,17 +670,18 @@ void DocumentObserver::Observe(
 
 void DocumentObserver::Disconnect() {
   Unused << EPS().UnregisterObserver(*this);
 }
 
 void DocumentObserver::NotifyMatch(MozDocumentMatcher& aMatcher,
                                    nsPIDOMWindowOuter* aWindow) {
   IgnoredErrorResult rv;
-  mCallbacks->OnNewDocument(aMatcher, dom::WindowProxyHolder(aWindow), rv);
+  mCallbacks->OnNewDocument(
+      aMatcher, WindowProxyHolder(aWindow->GetBrowsingContext()), rv);
 }
 
 void DocumentObserver::NotifyMatch(MozDocumentMatcher& aMatcher,
                                    nsILoadInfo* aLoadInfo) {
   IgnoredErrorResult rv;
   mCallbacks->OnPreloadDocument(aMatcher, aLoadInfo, rv);
 }
 
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -699,17 +699,19 @@ NS_IMETHODIMP
 nsContentTreeOwner::ProvideWindow(
     mozIDOMWindowProxy* aParent, uint32_t aChromeFlags, bool aCalledFromJS,
     bool aPositionSpecified, bool aSizeSpecified, nsIURI* aURI,
     const nsAString& aName, const nsACString& aFeatures, bool aForceNoOpener,
     nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
     mozIDOMWindowProxy** aReturn) {
   NS_ENSURE_ARG_POINTER(aParent);
 
-  auto* parent = nsPIDOMWindowOuter::From(aParent);
+  auto* parentWin = nsPIDOMWindowOuter::From(aParent);
+  dom::BrowsingContext* parent =
+      parentWin ? parentWin->GetBrowsingContext() : nullptr;
 
   *aReturn = nullptr;
 
   if (!mXULWindow) {
     // Nothing to do here
     return NS_OK;
   }
 
@@ -754,17 +756,18 @@ nsContentTreeOwner::ProvideWindow(
           info->LaunchWithURI(aURI, nullptr);
           return NS_ERROR_ABORT;
         }
       }
     }
   }
 
   int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
-      parent, aChromeFlags, aCalledFromJS, aPositionSpecified, aSizeSpecified);
+      parentWin, aChromeFlags, aCalledFromJS, aPositionSpecified,
+      aSizeSpecified);
 
   if (openLocation != nsIBrowserDOMWindow::OPEN_NEWTAB &&
       openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
     // Just open a window normally
     return NS_OK;
   }
 
   nsCOMPtr<mozIDOMWindowProxy> domWin;
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -2022,17 +2022,17 @@ NS_IMETHODIMP nsXULWindow::CreateNewCont
 
   if (aOpener) {
     nsCOMPtr<nsIDocShell> docShell;
     xulWin->GetDocShell(getter_AddRefs(docShell));
     MOZ_ASSERT(docShell);
     nsCOMPtr<nsPIDOMWindowOuter> window = docShell->GetWindow();
     MOZ_ASSERT(window);
     window->SetOpenerForInitialContentBrowser(
-        nsPIDOMWindowOuter::From(aOpener));
+        nsPIDOMWindowOuter::From(aOpener)->GetBrowsingContext());
   }
 
   xulWin->LockUntilChromeLoad();
 
   {
     AutoNoJSAPI nojsapi;
     SpinEventLoopUntil([&]() { return !xulWin->IsLocked(); });
   }