Bug 1560400: Part 1 - Support remote frames in Window indexed getters. r=nika
☠☠ backed out by f9bf5e4b0b4f ☠ ☠
authorKris Maglione <maglione.k@gmail.com>
Thu, 20 Jun 2019 13:52:55 -0700
changeset 543736 84633034590f2d1a4c336f9653e6c542f3a09039
parent 543735 d5415970da5fd83eb870b397b8db7fdf6c57ad23
child 543737 bf0f0e95c61c2a57d176699f05e71e967a13d3e8
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1560400
milestone69.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 1560400: Part 1 - Support remote frames in Window indexed getters. r=nika Differential Revision: https://phabricator.services.mozilla.com/D35471
chrome/nsChromeRegistry.cpp
dom/base/WindowNamedPropertiesHandler.cpp
dom/base/moz.build
dom/base/nsDOMWindowList.cpp
dom/base/nsDOMWindowList.h
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsGlobalWindowOuter.h
dom/base/nsPIDOMWindow.h
dom/base/nsWindowMemoryReporter.cpp
dom/tests/mochitest/chrome/file_DOM_element_instanceof.xul
js/xpconnect/wrappers/AccessCheck.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
layout/base/tests/chrome/printpreview_bug396024_helper.xul
layout/base/tests/chrome/printpreview_bug482976_helper.xul
layout/base/tests/chrome/printpreview_helper.xul
toolkit/components/perfmonitoring/PerformanceUtils.cpp
--- a/chrome/nsChromeRegistry.cpp
+++ b/chrome/nsChromeRegistry.cpp
@@ -11,17 +11,16 @@
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsEscape.h"
 #include "nsNetUtil.h"
 #include "nsString.h"
 #include "nsQueryObject.h"
 
 #include "mozilla/dom/URL.h"
-#include "nsDOMWindowList.h"
 #include "nsIConsoleService.h"
 #include "mozilla/dom/Document.h"
 #include "nsIDOMWindow.h"
 #include "nsIObserverService.h"
 #include "nsIScriptError.h"
 #include "nsIWindowMediator.h"
 #include "nsIPrefService.h"
 #include "mozilla/Preferences.h"
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -4,26 +4,25 @@
  * 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 "WindowNamedPropertiesHandler.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/WindowProxyHolder.h"
 #include "nsContentUtils.h"
-#include "nsDOMWindowList.h"
 #include "nsGlobalWindow.h"
 #include "nsHTMLDocument.h"
 #include "nsJSUtils.h"
 #include "xpcprivate.h"
 
 namespace mozilla {
 namespace dom {
 
-static bool ShouldExposeChildWindow(nsString& aNameBeingResolved,
+static bool ShouldExposeChildWindow(const nsString& aNameBeingResolved,
                                     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.
@@ -164,33 +163,22 @@ bool WindowNamedPropertiesHandler::ownPr
   }
 
   // Grab the DOM window.
   nsGlobalWindowInner* win = xpc::WindowGlobalOrNull(aProxy);
   nsTArray<nsString> names;
   // The names live on the outer window, which might be null
   nsGlobalWindowOuter* outer = win->GetOuterWindowInternal();
   if (outer) {
-    nsDOMWindowList* childWindows = outer->GetFrames();
-    if (childWindows) {
-      uint32_t length = childWindows->GetLength();
-      for (uint32_t i = 0; i < length; ++i) {
-        nsCOMPtr<nsIDocShellTreeItem> item =
-            childWindows->GetDocShellTreeItemAt(i);
-        // This is a bit silly, since we could presumably just do
-        // 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)) {
+    if (BrowsingContext* bc = outer->GetBrowsingContext()) {
+      for (const auto& child : bc->GetChildren()) {
+        const nsString& name = child->Name();
+        if (!name.IsEmpty() && !names.Contains(name)) {
           // Make sure we really would expose it from getOwnPropDescriptor.
-          RefPtr<BrowsingContext> child = win->GetChildWindow(name);
-          if (child && ShouldExposeChildWindow(name, child)) {
+          if (ShouldExposeChildWindow(name, child)) {
             names.AppendElement(name);
           }
         }
       }
     }
   }
   if (!AppendNamedPropertyIds(aCx, aProxy, names, false, aProps)) {
     return false;
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -332,17 +332,16 @@ UNIFIED_SOURCES += [
     'nsDataDocumentContentPolicy.cpp',
     'nsDocumentEncoder.cpp',
     'nsDOMAttributeMap.cpp',
     'nsDOMCaretPosition.cpp',
     'nsDOMMutationObserver.cpp',
     'nsDOMNavigationTiming.cpp',
     'nsDOMSerializer.cpp',
     'nsDOMTokenList.cpp',
-    'nsDOMWindowList.cpp',
     'nsFocusManager.cpp',
     'nsFrameLoader.cpp',
     'nsFrameLoaderOwner.cpp',
     'nsGlobalWindowCommands.cpp',
     'nsHistory.cpp',
     'nsHTMLContentSerializer.cpp',
     'nsIGlobalObject.cpp',
     'nsINode.cpp',
deleted file mode 100644
--- a/dom/base/nsDOMWindowList.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- 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 "nsDOMWindowList.h"
-
-#include "FlushType.h"
-#include "nsCOMPtr.h"
-#include "mozilla/dom/Document.h"
-#include "nsIDOMWindow.h"
-#include "nsIDocShell.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsIWebNavigation.h"
-
-using namespace mozilla;
-
-nsDOMWindowList::nsDOMWindowList(nsIDocShell* aDocShell) {
-  SetDocShell(aDocShell);
-}
-
-nsDOMWindowList::~nsDOMWindowList() {}
-
-void nsDOMWindowList::SetDocShell(nsIDocShell* aDocShell) {
-  mDocShellNode = aDocShell;  // Weak Reference
-}
-
-void nsDOMWindowList::EnsureFresh() {
-  nsCOMPtr<nsIWebNavigation> shellAsNav = do_QueryInterface(mDocShellNode);
-
-  if (shellAsNav) {
-    nsCOMPtr<dom::Document> doc;
-    shellAsNav->GetDocument(getter_AddRefs(doc));
-
-    if (doc) {
-      doc->FlushPendingNotifications(FlushType::ContentAndNotify);
-    }
-  }
-}
-
-uint32_t nsDOMWindowList::GetLength() {
-  EnsureFresh();
-
-  NS_ENSURE_TRUE(mDocShellNode, 0);
-
-  int32_t length;
-  nsresult rv = mDocShellNode->GetChildCount(&length);
-  NS_ENSURE_SUCCESS(rv, 0);
-
-  return uint32_t(length);
-}
-
-already_AddRefed<nsPIDOMWindowOuter> nsDOMWindowList::IndexedGetter(
-    uint32_t aIndex) {
-  nsCOMPtr<nsIDocShellTreeItem> item = GetDocShellTreeItemAt(aIndex);
-  if (!item) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsPIDOMWindowOuter> window = item->GetWindow();
-  MOZ_ASSERT(window);
-
-  return window.forget();
-}
-
-already_AddRefed<nsPIDOMWindowOuter> nsDOMWindowList::NamedItem(
-    const nsAString& aName) {
-  EnsureFresh();
-
-  if (!mDocShellNode) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIDocShellTreeItem> item;
-  mDocShellNode->FindChildWithName(aName, false, false, nullptr, nullptr,
-                                   getter_AddRefs(item));
-
-  nsCOMPtr<nsPIDOMWindowOuter> childWindow(do_GetInterface(item));
-  return childWindow.forget();
-}
deleted file mode 100644
--- a/dom/base/nsDOMWindowList.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- 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/. */
-#ifndef nsDOMWindowList_h___
-#define nsDOMWindowList_h___
-
-#include "nsCOMPtr.h"
-#include <stdint.h>
-#include "nsIDocShell.h"
-
-class nsIDocShell;
-class nsIDOMWindow;
-
-class nsDOMWindowList final {
- public:
-  explicit nsDOMWindowList(nsIDocShell* aDocShell);
-
-  NS_INLINE_DECL_REFCOUNTING(nsDOMWindowList)
-
-  uint32_t GetLength();
-  already_AddRefed<nsPIDOMWindowOuter> IndexedGetter(uint32_t aIndex);
-  already_AddRefed<nsPIDOMWindowOuter> NamedItem(const nsAString& aName);
-
-  // local methods
-  void SetDocShell(nsIDocShell* aDocShell);
-  already_AddRefed<nsIDocShellTreeItem> GetDocShellTreeItemAt(uint32_t aIndex) {
-    EnsureFresh();
-    nsCOMPtr<nsIDocShellTreeItem> item;
-    if (mDocShellNode) {
-      mDocShellNode->GetChildAt(aIndex, getter_AddRefs(item));
-    }
-    return item.forget();
-  }
-
- protected:
-  ~nsDOMWindowList();
-
-  // Note: this function may flush and cause mDocShellNode to become null.
-  void EnsureFresh();
-
-  nsIDocShell* mDocShellNode;  // Weak Reference
-};
-
-#endif  // nsDOMWindowList_h___
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -45,17 +45,16 @@
 #if defined(MOZ_WIDGET_ANDROID)
 #  include "mozilla/dom/WindowOrientationObserver.h"
 #endif
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsISizeOfEventTarget.h"
 #include "nsDOMJSUtils.h"
 #include "nsArrayUtils.h"
-#include "nsDOMWindowList.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocumentLoader.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPermission.h"
 #include "nsIPermissionManager.h"
@@ -2703,21 +2702,17 @@ bool nsGlobalWindowInner::GetClosed(Erro
   // If we're called from JS (which is the only way we should be getting called
   // here) and we reach this point, that means our JS global is the current
   // target of the WindowProxy, which means that we are the "current inner"
   // of our outer. So if FORWARD_TO_OUTER fails to forward, that means the
   // outer is already torn down, which corresponds to the closed state.
   FORWARD_TO_OUTER(GetClosedOuter, (), true);
 }
 
-nsDOMWindowList* nsGlobalWindowInner::GetFrames() {
-  FORWARD_TO_OUTER(GetFrames, (), nullptr);
-}
-
-already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowInner::IndexedGetter(
+Nullable<WindowProxyHolder> nsGlobalWindowInner::IndexedGetter(
     uint32_t aIndex) {
   FORWARD_TO_OUTER(IndexedGetterOuter, (aIndex), nullptr);
 }
 
 namespace {
 
 struct InterfaceShimEntry {
   const char* geckoName;
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -72,17 +72,16 @@ class nsIScrollableFrame;
 class nsIControllers;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
 class nsIBrowserChild;
 class nsITimeoutHandler;
 class nsIWebBrowserChrome;
 class mozIDOMWindowProxy;
 
-class nsDOMWindowList;
 class nsScreen;
 class nsHistory;
 class nsGlobalWindowObserver;
 class nsGlobalWindowOuter;
 class nsDOMWindowUtils;
 class nsIIdleService;
 struct nsRect;
 
@@ -376,17 +375,18 @@ class nsGlobalWindowInner final : public
   virtual void EventListenerAdded(nsAtom* aType) override;
   using EventTarget::EventListenerRemoved;
   virtual void EventListenerRemoved(nsAtom* aType) override;
 
   // nsIInterfaceRequestor
   NS_DECL_NSIINTERFACEREQUESTOR
 
   // WebIDL interface.
-  already_AddRefed<nsPIDOMWindowOuter> IndexedGetter(uint32_t aIndex);
+  mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> IndexedGetter(
+      uint32_t aIndex);
 
   static bool IsPrivilegedChromeWindow(JSContext* /* unused */, JSObject* aObj);
 
   static bool OfflineCacheAllowedForContext(JSContext* /* unused */,
                                             JSObject* aObj);
 
   static bool IsRequestIdleCallbackEnabled(JSContext* aCx,
                                            JSObject* /* unused */);
@@ -607,17 +607,16 @@ class nsGlobalWindowInner final : public
   void Close(mozilla::dom::CallerType aCallerType,
              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;
   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
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -43,17 +43,16 @@
 #if defined(MOZ_WIDGET_ANDROID)
 #  include "mozilla/dom/WindowOrientationObserver.h"
 #endif
 #include "nsBaseCommandController.h"
 #include "nsError.h"
 #include "nsISizeOfEventTarget.h"
 #include "nsDOMJSUtils.h"
 #include "nsArrayUtils.h"
-#include "nsDOMWindowList.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptContext.h"
 #include "nsISlowScriptDebug.h"
 #include "nsWindowMemoryReporter.h"
@@ -505,18 +504,19 @@ class nsOuterWindowProxy : public MaybeC
   // False return value means we threw an exception.  True return value
   // but false "found" means we didn't have a subframe at that index.
   bool GetSubframeWindow(JSContext* cx, JS::Handle<JSObject*> proxy,
                          JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp,
                          bool& found) const;
 
   // Returns a non-null window only if id is an index and we have a
   // window at that index.
-  already_AddRefed<nsPIDOMWindowOuter> GetSubframeWindow(
-      JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const;
+  Nullable<WindowProxyHolder> GetSubframeWindow(JSContext* cx,
+                                                JS::Handle<JSObject*> proxy,
+                                                JS::Handle<jsid> id) const;
 
   bool AppendIndexedPropertyNames(JSObject* proxy,
                                   JS::MutableHandleVector<jsid> props) const;
 
   using MaybeCrossOriginObjectMixins::EnsureHolder;
   bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> proxy,
                     JS::MutableHandle<JSObject*> holder) const override;
 };
@@ -779,17 +779,17 @@ bool nsOuterWindowProxy::ownPropertyKeys
 
 bool nsOuterWindowProxy::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
                                  JS::Handle<jsid> id,
                                  JS::ObjectOpResult& result) const {
   if (!IsPlatformObjectSameOrigin(cx, proxy)) {
     return ReportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("delete"));
   }
 
-  if (nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id)) {
+  if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
     // Fail (which means throw if strict, else return false).
     return result.failCantDeleteWindowElement();
   }
 
   if (IsArrayIndex(GetArrayIndexFromId(id))) {
     // Indexed, but not supported.  Spec says return true.
     return result.succeed();
   }
@@ -813,17 +813,17 @@ bool nsOuterWindowProxy::has(JSContext* 
   // work than we have to do for has() on the Window.
 
   if (!IsPlatformObjectSameOrigin(cx, proxy)) {
     // In the cross-origin case we only have own properties.  Just call hasOwn
     // directly.
     return hasOwn(cx, proxy, id, bp);
   }
 
-  if (nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id)) {
+  if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
     *bp = true;
     return true;
   }
 
   // Just to be safe in terms of compartment asserts, enter the Realm of
   // "proxy".  We're same-origin with it, so this should be safe.
   JSAutoRealm ar(cx, proxy);
   JS_MarkCrossZoneId(cx, id);
@@ -846,17 +846,17 @@ bool nsOuterWindowProxy::hasOwn(JSContex
     // not do the right security and cross-origin checks and will pass through
     // the call to the Window.
     //
     // The BaseProxyHandler code is OK with this happening without entering the
     // compartment of "proxy".
     return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
   }
 
-  if (nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id)) {
+  if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
     *bp = true;
     return true;
   }
 
   // Just to be safe in terms of compartment asserts, enter the Realm of
   // "proxy".  We're same-origin with it, so this should be safe.
   JSAutoRealm ar(cx, proxy);
   JS_MarkCrossZoneId(cx, id);
@@ -977,38 +977,27 @@ bool nsOuterWindowProxy::getOwnEnumerabl
   return js::AppendUnique(cx, props, innerProps);
 }
 
 bool nsOuterWindowProxy::GetSubframeWindow(JSContext* cx,
                                            JS::Handle<JSObject*> proxy,
                                            JS::Handle<jsid> id,
                                            JS::MutableHandle<JS::Value> vp,
                                            bool& found) const {
-  nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id);
-  if (!frame) {
+  Nullable<WindowProxyHolder> frame = GetSubframeWindow(cx, proxy, id);
+  if (frame.IsNull()) {
     found = false;
     return true;
   }
 
   found = true;
-  // Just return the window's global
-  nsGlobalWindowOuter* global = nsGlobalWindowOuter::Cast(frame);
-  frame->EnsureInnerWindow();
-  JSObject* obj = global->GetGlobalJSObject();
-  // This null check fixes a hard-to-reproduce crash that occurs when we
-  // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
-  // comment 105.
-  if (MOZ_UNLIKELY(!obj)) {
-    return xpc::Throw(cx, NS_ERROR_FAILURE);
-  }
-  vp.setObject(*obj);
-  return JS_WrapValue(cx, vp);
-}
-
-already_AddRefed<nsPIDOMWindowOuter> nsOuterWindowProxy::GetSubframeWindow(
+  return WrapObject(cx, frame.Value(), vp);
+}
+
+Nullable<WindowProxyHolder> nsOuterWindowProxy::GetSubframeWindow(
     JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const {
   uint32_t index = GetArrayIndexFromId(id);
   if (!IsArrayIndex(index)) {
     return nullptr;
   }
 
   nsGlobalWindowOuter* win = GetOuterWindow(proxy);
   return win->IndexedGetterOuter(index);
@@ -1295,17 +1284,16 @@ void nsGlobalWindowOuter::DropOuterWindo
 
 void nsGlobalWindowOuter::CleanUp() {
   // Guarantee idempotence.
   if (mCleanedUp) return;
   mCleanedUp = true;
 
   StartDying();
 
-  mFrames = nullptr;
   mWindowUtils = nullptr;
 
   ClearControllers();
 
   mOpener = nullptr;  // Forces Release
   if (mContext) {
     mContext = nullptr;  // Forces Release
   }
@@ -2375,20 +2363,16 @@ void nsGlobalWindowOuter::SetDocShell(ns
   nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetScriptableParentOrNull();
   MOZ_RELEASE_ASSERT(!parentWindow || !mTabGroup ||
                      mTabGroup ==
                          nsGlobalWindowOuter::Cast(parentWindow)->mTabGroup);
 
   mTopLevelOuterContentWindow =
       !mIsChrome && GetScriptableTopInternal() == this;
 
-  if (mFrames) {
-    mFrames->SetDocShell(aDocShell);
-  }
-
   // Get our enclosing chrome shell and retrieve its global window impl, so
   // that we can do some forwarding to the chrome document.
   RefPtr<EventTarget> chromeEventHandler;
   mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
   mChromeEventHandler = chromeEventHandler;
   if (!mChromeEventHandler) {
     // We have no chrome event handler. If we have a parent,
     // get our chrome event handler from the parent. If
@@ -2470,20 +2454,16 @@ void nsGlobalWindowOuter::DetachFromDocS
                             ? nullptr
                             : GetWrapperPreserveColor());
     mContext = nullptr;
   }
 
   mDocShell = nullptr;
   mBrowsingContext->ClearDocShell();
 
-  if (mFrames) {
-    mFrames->SetDocShell(nullptr);
-  }
-
   MaybeForgiveSpamCount();
   CleanUp();
 }
 
 void nsGlobalWindowOuter::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
                                           bool aOriginalOpener) {
   nsWeakPtr opener = do_GetWeakReference(aOpener);
   if (opener == mOpener) {
@@ -3191,30 +3171,26 @@ nsresult nsGlobalWindowOuter::GetPrompte
 
 bool nsGlobalWindowOuter::GetClosedOuter() {
   // If someone called close(), or if we don't have a docshell, we're closed.
   return mIsClosed || !mDocShell;
 }
 
 bool nsGlobalWindowOuter::Closed() { return GetClosedOuter(); }
 
-nsDOMWindowList* nsGlobalWindowOuter::GetFrames() {
-  if (!mFrames && mDocShell) {
-    mFrames = new nsDOMWindowList(mDocShell);
-  }
-
-  return mFrames;
-}
-
-already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::IndexedGetterOuter(
+Nullable<WindowProxyHolder> nsGlobalWindowOuter::IndexedGetterOuter(
     uint32_t aIndex) {
-  nsDOMWindowList* windows = GetFrames();
-  NS_ENSURE_TRUE(windows, nullptr);
-
-  return windows->IndexedGetter(aIndex);
+  BrowsingContext* bc = GetBrowsingContext();
+  NS_ENSURE_TRUE(bc, nullptr);
+
+  const BrowsingContext::Children& children = bc->GetChildren();
+  if (aIndex < children.Length()) {
+    return WindowProxyHolder(children[aIndex]);
+  }
+  return nullptr;
 }
 
 nsIControllers* nsGlobalWindowOuter::GetControllersOuter(ErrorResult& aError) {
   if (!mControllers) {
     mControllers = new nsXULControllers();
     if (!mControllers) {
       aError.Throw(NS_ERROR_FAILURE);
       return nullptr;
@@ -3957,19 +3933,18 @@ CSSPoint nsGlobalWindowOuter::GetScrollX
   return CSSPoint::FromAppUnits(scrollPos);
 }
 
 double nsGlobalWindowOuter::GetScrollXOuter() { return GetScrollXY(false).x; }
 
 double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; }
 
 uint32_t nsGlobalWindowOuter::Length() {
-  nsDOMWindowList* windows = GetFrames();
-
-  return windows ? windows->GetLength() : 0;
+  BrowsingContext* bc = GetBrowsingContext();
+  return bc ? bc->GetChildren().Length() : 0;
 }
 
 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {
   BrowsingContext* bc = GetBrowsingContext();
   return bc ? bc->GetTop(IgnoreErrors()) : nullptr;
 }
 
 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -69,17 +69,16 @@ class nsIControllers;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
 class nsIBrowserChild;
 class nsITimeoutHandler;
 class nsIWebBrowserChrome;
 class mozIDOMWindowProxy;
 
 class nsDocShellLoadState;
-class nsDOMWindowList;
 class nsScreen;
 class nsHistory;
 class nsGlobalWindowObserver;
 class nsGlobalWindowInner;
 class nsDOMWindowUtils;
 struct nsRect;
 
 class nsWindowSizes;
@@ -349,17 +348,18 @@ class nsGlobalWindowOuter final : public
   bool Fullscreen() const;
 
   // nsIInterfaceRequestor
   NS_DECL_NSIINTERFACEREQUESTOR
 
   // nsIObserver
   NS_DECL_NSIOBSERVER
 
-  already_AddRefed<nsPIDOMWindowOuter> IndexedGetterOuter(uint32_t aIndex);
+  mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> IndexedGetterOuter(
+      uint32_t aIndex);
 
   already_AddRefed<nsPIDOMWindowOuter> GetTop() override;
   // Similar to GetTop() except that it stops at content frames that an
   // extension has permission to access.  This is used by the third-party util
   // service in order to determine the top window for a channel which is used
   // in third-partiness checks.
   already_AddRefed<nsPIDOMWindowOuter>
   GetTopExcludingExtensionAccessibleContentFrames(nsIURI* aURIBeingLoaded);
@@ -530,17 +530,16 @@ class nsGlobalWindowOuter final : public
   nsresult Close() override;
   bool GetClosedOuter();
   bool Closed() override;
   void StopOuter(mozilla::ErrorResult& aError);
   void FocusOuter();
   nsresult Focus() override;
   void BlurOuter();
   mozilla::dom::BrowsingContext* GetFramesOuter();
-  nsDOMWindowList* GetFrames() final;
   uint32_t Length();
   mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> GetTopOuter();
 
   nsresult GetPrompter(nsIPrompt** aPrompt) override;
 
   RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
  protected:
   nsPIDOMWindowOuter* GetOpenerWindowOuter();
@@ -913,18 +912,18 @@ class nsGlobalWindowOuter final : public
   virtual void SetFocusedElement(mozilla::dom::Element* aElement,
                                  uint32_t aFocusMethod = 0,
                                  bool aNeedsFocus = false) override;
 
   virtual uint32_t GetFocusMethod() override;
 
   virtual bool ShouldShowFocusRing() override;
 
-  virtual void SetKeyboardIndicators(UIStateChangeType aShowFocusRings)
-      override;
+  virtual void SetKeyboardIndicators(
+      UIStateChangeType aShowFocusRings) override;
 
  public:
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() override;
 
  protected:
   void NotifyWindowIDDestroyed(const char* aTopic);
 
   void ClearStatus();
@@ -1094,17 +1093,16 @@ class nsGlobalWindowOuter final : public
 
   nsCOMPtr<nsIScriptContext> mContext;
   nsWeakPtr mOpener;
   nsCOMPtr<nsIControllers> mControllers;
 
   // For |window.arguments|, via |openDialog|.
   nsCOMPtr<nsIArray> mArguments;
 
-  RefPtr<nsDOMWindowList> mFrames;
   RefPtr<nsDOMWindowUtils> mWindowUtils;
   nsString mStatus;
 
   RefPtr<mozilla::dom::Storage> mLocalStorage;
 
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIPrincipal> mDocumentStoragePrincipal;
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -20,17 +20,16 @@
 #include "nsRefPtrHashtable.h"
 
 // Only fired for inner windows.
 #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed"
 #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen"
 #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed"
 
 class nsDOMOfflineResourceList;
-class nsDOMWindowList;
 class nsGlobalWindowInner;
 class nsGlobalWindowOuter;
 class nsIArray;
 class nsIChannel;
 class nsIContent;
 class nsIContentSecurityPolicy;
 class nsICSSDeclaration;
 class nsIDocShell;
@@ -541,18 +540,16 @@ class nsPIDOMWindowInner : public mozIDO
 
   uint32_t GetMarkedCCGeneration() { return mMarkedCCGeneration; }
 
   mozilla::dom::Navigator* Navigator();
   virtual mozilla::dom::Location* Location() = 0;
 
   virtual nsresult GetControllers(nsIControllers** aControllers) = 0;
 
-  virtual nsDOMWindowList* GetFrames() = 0;
-
   virtual nsresult GetInnerWidth(int32_t* aWidth) = 0;
   virtual nsresult GetInnerHeight(int32_t* aHeight) = 0;
 
   virtual already_AddRefed<nsICSSDeclaration> GetComputedStyle(
       mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
       mozilla::ErrorResult& aError) = 0;
 
   virtual mozilla::dom::Element* GetFrameElement() = 0;
@@ -1040,18 +1037,16 @@ class nsPIDOMWindowOuter : public mozIDO
   virtual mozilla::dom::Navigator* GetNavigator() = 0;
   virtual mozilla::dom::Location* GetLocation() = 0;
 
   virtual nsresult GetPrompter(nsIPrompt** aPrompt) = 0;
   virtual nsresult GetControllers(nsIControllers** aControllers) = 0;
   virtual already_AddRefed<mozilla::dom::Selection> GetSelection() = 0;
   virtual already_AddRefed<nsPIDOMWindowOuter> GetOpener() = 0;
 
-  virtual nsDOMWindowList* GetFrames() = 0;
-
   // aLoadState will be passed on through to the windowwatcher.
   // aForceNoOpener will act just like a "noopener" feature in aOptions except
   //                will not affect any other window features.
   virtual nsresult Open(const nsAString& aUrl, const nsAString& aName,
                         const nsAString& aOptions,
                         nsDocShellLoadState* aLoadState, bool aForceNoOpener,
                         nsPIDOMWindowOuter** _retval) = 0;
   virtual nsresult OpenDialog(const nsAString& aUrl, const nsAString& aName,
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -2,23 +2,24 @@
 /* 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 "nsWindowMemoryReporter.h"
 #include "nsWindowSizes.h"
 #include "nsGlobalWindow.h"
+#include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/Document.h"
-#include "nsDOMWindowList.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/ResultExtensions.h"
 #include "nsNetCID.h"
 #include "nsPrintfCString.h"
 #include "XPCJSMemoryReporter.h"
 #include "js/MemoryMetrics.h"
 #include "nsQueryObject.h"
 #include "nsServiceManagerUtils.h"
 #ifdef MOZ_XUL
 #  include "nsXULPrototypeCache.h"
@@ -56,29 +57,26 @@ static nsresult AddNonJSSizeOfWindowAndI
   // Measure the inner window, if there is one.
   nsGlobalWindowInner* inner = aWindow->GetCurrentInnerWindowInternal();
   if (inner) {
     inner->AddSizeOfIncludingThis(windowSizes);
   }
 
   windowSizes.addToTabSizes(aSizes);
 
-  nsDOMWindowList* frames = aWindow->GetFrames();
-
-  uint32_t length = frames->GetLength();
+  BrowsingContext* bc = aWindow->GetBrowsingContext();
+  if (!bc) {
+    return NS_OK;
+  }
 
   // Measure this window's descendents.
-  for (uint32_t i = 0; i < length; i++) {
-    nsCOMPtr<nsPIDOMWindowOuter> child = frames->IndexedGetter(i);
-    NS_ENSURE_STATE(child);
-
-    nsGlobalWindowOuter* childWin = nsGlobalWindowOuter::Cast(child);
-
-    nsresult rv = AddNonJSSizeOfWindowAndItsDescendents(childWin, aSizes);
-    NS_ENSURE_SUCCESS(rv, rv);
+  for (const auto& frame : bc->GetChildren()) {
+    if (auto* childWin = nsGlobalWindowOuter::Cast(frame->GetDOMWindow())) {
+      MOZ_TRY(AddNonJSSizeOfWindowAndItsDescendents(childWin, aSizes));
+    }
   }
   return NS_OK;
 }
 
 static nsresult NonJSSizeOfTab(nsPIDOMWindowOuter* aWindow, size_t* aDomSize,
                                size_t* aStyleSize, size_t* aOtherSize) {
   nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
 
--- a/dom/tests/mochitest/chrome/file_DOM_element_instanceof.xul
+++ b/dom/tests/mochitest/chrome/file_DOM_element_instanceof.xul
@@ -6,17 +6,19 @@
   <iframe type="content"></iframe>
 
   <script type="application/javascript">
   <![CDATA[
   /** Test for Bug 799299 **/
   var SimpleTest = opener.wrappedJSObject.SimpleTest;
   var ok = opener.wrappedJSObject.ok;
 
-  var doc = frames[0].document;
+  // Note: We can't use frames[0] here because the type="content" attribute
+  // isolates it into a separate browsing context hierarchy.
+  var doc = document.querySelector("iframe").contentDocument;
   ok(doc.createElement("body") instanceof HTMLBodyElement,
      "Should be instance of HTMLBodyElement");
   ok(doc.createElement("div") instanceof HTMLDivElement,
      "Should be instance of HTMLDivElement");
   ok(doc.createElement("frameset") instanceof HTMLFrameSetElement,
      "Should be instance of HTMLFrameSetElement");
   ok(doc.createElement("h1") instanceof HTMLHeadingElement,
      "Should be instance of HTMLHeadingElement");
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -2,17 +2,16 @@
 /* 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 "AccessCheck.h"
 
 #include "nsJSPrincipals.h"
-#include "nsDOMWindowList.h"
 #include "nsGlobalWindow.h"
 
 #include "XPCWrapper.h"
 #include "XrayWrapper.h"
 #include "FilteringWrapper.h"
 
 #include "jsfriendapi.h"
 #include "mozilla/BasePrincipal.h"
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1643,26 +1643,22 @@ bool DOMXrayTraits::resolveOwnProperty(J
   }
 
   // Check for indexed access on a window.
   uint32_t index = GetArrayIndexFromId(id);
   if (IsArrayIndex(index)) {
     nsGlobalWindowInner* win = AsWindow(cx, wrapper);
     // Note: As() unwraps outer windows to get to the inner window.
     if (win) {
-      nsCOMPtr<nsPIDOMWindowOuter> subframe = win->IndexedGetter(index);
-      if (subframe) {
-        subframe->EnsureInnerWindow();
-        nsGlobalWindowOuter* global = nsGlobalWindowOuter::Cast(subframe);
-        JSObject* obj = global->GetGlobalJSObject();
-        if (MOZ_UNLIKELY(!obj)) {
+      Nullable<WindowProxyHolder> subframe = win->IndexedGetter(index);
+      if (!subframe.IsNull()) {
+        if (MOZ_UNLIKELY(!WrapObject(cx, subframe.Value(), desc.value()))) {
           // It's gone?
           return xpc::Throw(cx, NS_ERROR_FAILURE);
         }
-        desc.value().setObject(*obj);
         FillPropertyDescriptor(desc, wrapper, true);
         return JS_WrapPropertyDescriptor(cx, desc);
       }
     }
   }
 
   if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc)) {
     return false;
--- a/layout/base/tests/chrome/printpreview_bug396024_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug396024_helper.xul
@@ -6,23 +6,27 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=396024
 -->
 <window title="Mozilla Bug 396024" onload="run()"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <iframe id="i" src="about:blank" type="content"></iframe>
 <iframe src="about:blank" type="content"></iframe>
 <script type="application/javascript">
 <![CDATA[
+// Note: We can't use window.frames directly here because the type="content"
+// attributes isolate the frames into their own BrowsingContext hierarchies.
+let frameElts = document.getElementsByTagName("iframe");
+
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
+  gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer();
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -34,22 +38,22 @@ function printpreview() {
       throw Cr.NS_NOINTERFACE;
     }
   }
   var prefs = Cc["@mozilla.org/preferences-service;1"]
                 .getService(Ci.nsIPrefBranch);
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
-  gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
+  gWbp.printPreview(gWbp.globalPrintSettings, frameElts[0].contentWindow, listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].docShell.exitPrintPreview();
+  frameElts[1].contentWindow.docShell.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run()
@@ -81,21 +85,21 @@ function run()
 }
 
 function run2() {
   var loadhandler = function() {
     document.getElementById("i").removeEventListener("load", arguments.callee, true);
     setTimeout(run3, 0);
   };
   document.getElementById("i").addEventListener("load", loadhandler, true);
-  window.frames[0].location.reload();
+  frameElts[0].contentWindow.location.reload();
 }
 
 function run3() {
-  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
+  gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer();
   ok(gWbp.doingPrintPreview, "Should be doing print preview");
   exitprintpreview();
   setTimeout(run4, 0);
 }
 
 function run4() {
   var i = document.getElementById("i");
   i.remove();
@@ -104,17 +108,17 @@ function run4() {
     setTimeout(run5, 0);
   };
   i.addEventListener("load", loadhandler, true);
   document.documentElement.getBoundingClientRect();
   document.documentElement.prepend(i);
 }
 
 function run5() {
-  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
+  gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer();
   ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore2");
 
   //XXX this shouldn't be necessary, see bug 405555
   printpreview();
   exitprintpreview();
   finish(); //should not have crashed after all of this
 }
 ]]></script>
--- a/layout/base/tests/chrome/printpreview_bug482976_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xul
@@ -6,23 +6,27 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=482976
 -->
 <window title="Mozilla Bug 482976" onload="run1()"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <iframe src="about:blank" type="content"></iframe>
 <iframe src="about:blank" type="content"></iframe>
 <script type="application/javascript">
 <![CDATA[
+// Note: We can't use window.frames directly here because the type="content"
+// attributes isolate the frames into their own BrowsingContext hierarchies.
+let frameElts = document.getElementsByTagName("iframe");
+
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
+  gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer();
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -34,22 +38,22 @@ function printpreview() {
       throw Cr.NS_NOINTERFACE;
     }
   }
   var prefs = Cc["@mozilla.org/preferences-service;1"]
                 .getService(Ci.nsIPrefBranch);
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
-  gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
+  gWbp.printPreview(gWbp.globalPrintSettings, frameElts[0].contentWindow, listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].docShell.exitPrintPreview();
+  frameElts[1].contentWindow.docShell.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run1()
--- a/layout/base/tests/chrome/printpreview_helper.xul
+++ b/layout/base/tests/chrome/printpreview_helper.xul
@@ -3,33 +3,37 @@
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 <window onload="runTests()"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <iframe height="200" width="600" type="content"></iframe>
 <iframe height="200" width="600" type="content"></iframe>
 <script type="application/javascript">
 <![CDATA[
+// Note: We can't use window.frames directly here because the type="content"
+// attributes isolate the frames into their own BrowsingContext hierarchies.
+let frameElts = document.getElementsByTagName("iframe");
+
 var is = window.opener.wrappedJSObject.is;
 var isnot = window.opener.wrappedJSObject.isnot;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 var ctx1;
 var ctx2;
 var counter = 0;
 
 var file = Cc["@mozilla.org/file/directory_service;1"]
              .getService(Ci.nsIProperties)
              .get("TmpD", Ci.nsIFile);
 filePath = file.path;
 
 function printpreview(hasMozPrintCallback) {
-  gWbp = window.frames[1].docShell.initOrReusePrintPreviewViewer();
+  gWbp = frameElts[1].docShell.initOrReusePrintPreviewViewer();
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -45,32 +49,32 @@ function printpreview(hasMozPrintCallbac
                 .getService(Ci.nsIPrefBranch);
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false;
   var before = 0;
   var after = 0;
   function beforeprint() { ++before; }
   function afterprint() { ++after; }
-  window.frames[0].addEventListener("beforeprint", beforeprint, true);
-  window.frames[0].addEventListener("afterprint", afterprint, true);
-  gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
+  frameElts[0].contentWindow.addEventListener("beforeprint", beforeprint, true);
+  frameElts[0].contentWindow.addEventListener("afterprint", afterprint, true);
+  gWbp.printPreview(gWbp.globalPrintSettings, frameElts[0].contentWindow, listener);
   is(before, 1, "Should have called beforeprint listener!");
   if (!hasMozPrintCallback) {
     // If there's a mozPrintCallback the after print event won't fire until
     // later.
     is(after, 1, "Should have called afterprint listener!");
   }
-  window.frames[0].removeEventListener("beforeprint", beforeprint, true);
-  window.frames[0].removeEventListener("afterprint", afterprint, true);
+  frameElts[0].contentWindow.removeEventListener("beforeprint", beforeprint, true);
+  frameElts[0].contentWindow.removeEventListener("afterprint", afterprint, true);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].docShell.exitPrintPreview();
+  frameElts[1].contentWindow.docShell.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function runTests()
@@ -112,55 +116,55 @@ function addHTMLContent(parent) {
   }
   s += "</table>";
   n.innerHTML = s;
 }
 
 function startTest1() {
   ctx1 = document.getElementsByTagName("canvas")[0].getContext("2d");
   ctx2 = document.getElementsByTagName("canvas")[1].getContext("2d");
-  window.frames[0].document.body.innerHTML = "<div> </div><div>" + counter + " timers</div><div> </div>";
+  frameElts[0].contentDocument.body.innerHTML = "<div> </div><div>" + counter + " timers</div><div> </div>";
 
   // Note this timeout is needed so that we can check that timers run
   // after print preview, but not during it.
-  window.frames[0].wrappedJSObject.counter = counter;
-  window.frames[0].counterTimeout = "document.body.firstChild.nextSibling.innerHTML = ++counter + ' timers';" +
+  frameElts[0].contentWindow.wrappedJSObject.counter = counter;
+  frameElts[0].contentWindow.counterTimeout = "document.body.firstChild.nextSibling.innerHTML = ++counter + ' timers';" +
                                     "window.setTimeout(counterTimeout, 0);";
-  window.frames[0].setTimeout(window.frames[0].counterTimeout, 0);
-  window.frames[0].document.body.firstChild.innerHTML = "Print preview";
+  frameElts[0].contentWindow.setTimeout(frameElts[0].contentWindow.counterTimeout, 0);
+  frameElts[0].contentDocument.body.firstChild.innerHTML = "Print preview";
 
   printpreview();
-  ctx1.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(256,256,256)");
-  window.frames[0].document.body.firstChild.innerHTML = "Galley presentation";
+  ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)");
+  frameElts[0].contentDocument.body.firstChild.innerHTML = "Galley presentation";
 
   // Add some elements.
-  addHTMLContent(window.frames[0].document.body.lastChild);
+  addHTMLContent(frameElts[0].contentDocument.body.lastChild);
   // Delete them.
-  window.frames[0].document.body.lastChild.innerHTML = "";
+  frameElts[0].contentDocument.body.lastChild.innerHTML = "";
   // And readd.
-  addHTMLContent(window.frames[0].document.body.lastChild);
+  addHTMLContent(frameElts[0].contentDocument.body.lastChild);
 
   setTimeout(finalizeTest1, 1000);
 }
 
 function finalizeTest1() {
-  ctx2.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(256,256,256)");
+  ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)");
   exitprintpreview();
   ok(compareCanvases(), "Canvas should be the same!");
-  counter = window.frames[0].counter;
+  counter = frameElts[0].contentWindow.counter;
   // This timeout is needed so that we can check that timers do run after
   // print preview.
   setTimeout(runTest2, 1000);
 }
 
 function runTest2() {
-  isnot(window.frames[0].document.body.firstChild.nextSibling.textContent, "0 timers", "Timers should have run!");
-  isnot(window.frames[0].counter, 0, "Timers should have run!");
-  counter = window.frames[0].counter;
-  window.frames[0].counterTimeout = "";
+  isnot(frameElts[0].contentDocument.body.firstChild.nextSibling.textContent, "0 timers", "Timers should have run!");
+  isnot(frameElts[0].contentWindow.counter, 0, "Timers should have run!");
+  counter = frameElts[0].contentWindow.counter;
+  frameElts[0].contentWindow.counterTimeout = "";
   setTimeout(runTest3, 0);
 }
 
 var elementIndex = 0;
 var compareEmptyElement = true;
 var emptyFormElements =
   ["<input type='text'>",
    "<input type='password'>",
@@ -203,154 +207,154 @@ function runTest3() {
     compareFormElementPrint(emptyFormElements[currentIndex], formElements[currentIndex], false);
     return;
   }
 
   setTimeout(runTest4, 0)
 }
 
 function compareFormElementPrint(el1, el2, equals) {
-  window.frames[0].document.body.innerHTML = el1;
-  window.frames[0].document.body.firstChild.value =
-    window.frames[0].document.body.firstChild.getAttribute('value');
+  frameElts[0].contentDocument.body.innerHTML = el1;
+  frameElts[0].contentDocument.body.firstChild.value =
+    frameElts[0].contentDocument.body.firstChild.getAttribute('value');
   printpreview();
-  ctx1.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(256,256,256)");
+  ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)");
   exitprintpreview();
-  window.frames[0].document.body.innerHTML = el2;
-  window.frames[0].document.body.firstChild.value =
-    window.frames[0].document.body.firstChild.getAttribute('value');
+  frameElts[0].contentDocument.body.innerHTML = el2;
+  frameElts[0].contentDocument.body.firstChild.value =
+    frameElts[0].contentDocument.body.firstChild.getAttribute('value');
   printpreview();
-  ctx2.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(256,256,256)");
+  ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(256,256,256)");
   exitprintpreview();
   is(compareCanvases(), equals,
      "Comparing print preview didn't succeed [" + el1 + " : " + el2 + "]");
   setTimeout(runTest3, 100);
 }
 
 // This is a crash test for bug 539060.
 function runTest4() {
-  window.frames[0].document.body.innerHTML =
+  frameElts[0].contentDocument.body.innerHTML =
     "<iframe style='display: none;' src='data:text/html,<iframe>'></iframe>";
   setTimeout(runTest4end, 500);
 }
 
 function runTest4end() {
   printpreview();
   exitprintpreview();
 
   runTest5();
 }
 
 // This is a crash test for bug 595337
 function runTest5() {
-  window.frames[0].document.body.innerHTML =
+  frameElts[0].contentDocument.body.innerHTML =
     '<iframe style="position: fixed; visibility: hidden; bottom: 10em;"></iframe>' +
     '<input contenteditable="true" style="display: table; page-break-before: left; width: 10000px;">';
   printpreview();
   exitprintpreview();
 
   setTimeout(runTest6, 0);
 }
 
 // Crash test for bug 878037
 function runTest6() {
-  window.frames[0].document.body.innerHTML =
+  frameElts[0].contentDocument.body.innerHTML =
     '<style> li { list-style-image: url("animated.gif"); } </style>' +
     '<li>Firefox will crash if you try and print this page</li>';
 
   setTimeout(runTest6end, 500);
 }
 
 function runTest6end() {
   printpreview();
   exitprintpreview();
 
   requestAnimationFrame(function() { setTimeout(runTest7); } );
 }
 
 function runTest7() {
   var contentText = "<a href='#'>mozilla</a><input>test<select><option>option1</option></select>";
   // Create normal content
-  window.frames[0].document.body.innerHTML =
+  frameElts[0].contentDocument.body.innerHTML =
     "<div>" + contentText + "</div>";
-  window.frames[0].document.body.firstChild.value =
-    window.frames[0].document.body.firstChild.getAttribute('value');
+  frameElts[0].contentDocument.body.firstChild.value =
+    frameElts[0].contentDocument.body.firstChild.getAttribute('value');
   printpreview();
-  ctx1.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
   exitprintpreview();
 
-  window.frames[0].document.body.innerHTML = "<div></div>";
-  var sr = window.frames[0].document.body.firstChild.attachShadow({mode: "open"});
+  frameElts[0].contentDocument.body.innerHTML = "<div></div>";
+  var sr = frameElts[0].contentDocument.body.firstChild.attachShadow({mode: "open"});
   sr.innerHTML = contentText;
   printpreview();
-  ctx2.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
   exitprintpreview();
   ok(compareCanvases(), "Printing light DOM and shadow DOM should create same output");
 
   requestAnimationFrame(function() { setTimeout(runTest8); } );
 }
 
 async function runTest8() {
   // Test that fonts loaded with CSS and JS are printed the same.
   const iframeElement = document.getElementsByTagName("iframe")[0];
 
   // First, snapshot the page with font defined in CSS.
   await new Promise((resolve) => {
     iframeElement.addEventListener("load", resolve, { capture: true, once: true });
     iframeElement.setAttribute("src", "printpreview_font_api_ref.html");
   });
   printpreview();
-  ctx1.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
   exitprintpreview();
 
   // Second, snapshot the page with font loaded in JS.
   await new Promise((resolve) => {
     iframeElement.addEventListener("message", resolve, { capture: true, once: true });
     iframeElement.setAttribute("src", "printpreview_font_api.html");
   });
   printpreview();
-  ctx2.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
   exitprintpreview();
   ok(compareCanvases(), "Printing pages with fonts loaded from CSS and JS should be the same.");
 
   requestAnimationFrame(function() { setTimeout(runTest9); } );
 }
 
 // Test for bug 1487649
 async function runTest9() {
-  window.frames[0].document.body.innerHTML = `
+  frameElts[0].contentDocument.body.innerHTML = `
     <svg width="100" height="100">
       <rect width='100' height='100' fill='lime'/>
     </svg>
   `;
 
   printpreview();
-  ctx1.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
   exitprintpreview();
 
-  window.frames[0].document.body.innerHTML = `
+  frameElts[0].contentDocument.body.innerHTML = `
     <svg width="100" height="100">
       <defs>
         <g id="useme">
           <rect width='100' height='100' fill='lime'/>
         </g>
       </defs>
       <use />
     </svg>
   `;
 
   // Set the attribute explicitly because this is a chrome document, and the
   // href attribute would get sanitized.
-  window.frames[0].document.querySelector("use").setAttribute("href", "#useme");
+  frameElts[0].contentDocument.querySelector("use").setAttribute("href", "#useme");
 
   // Ensure the <use> shadow tree is created so we test what we want to test.
-  window.frames[0].document.body.offsetTop;
+  frameElts[0].contentDocument.body.offsetTop;
 
   printpreview();
-  ctx2.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
   exitprintpreview();
   ok(compareCanvases(), "Printing <use> subtrees should create same output");
 
   requestAnimationFrame(function() { setTimeout(runTest10); } );
 }
 
 // Test for bug 1524640
 async function runTest10() {
@@ -363,31 +367,31 @@ async function runTest10() {
     iframeElement.addEventListener("load", resolve, { capture: true, once: true });
     iframeElement.setAttribute("src", "printpreview_font_mozprintcallback_ref.html");
   });
   let mozPrintCallbackDone = new Promise((resolve) => {
     iframeElement.addEventListener("message", resolve, { capture: true, once: true });
   });
   printpreview(true);
   await mozPrintCallbackDone;
-  ctx1.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx1.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
   exitprintpreview();
 
   // Second, snapshot the page with font loaded in JS.
   await new Promise((resolve) => {
     iframeElement.addEventListener("load", resolve, { capture: true, once: true });
     iframeElement.setAttribute("src", "printpreview_font_mozprintcallback.html");
   });
   mozPrintCallbackDone = new Promise((resolve) => {
     iframeElement.addEventListener("message", resolve, { capture: true, once: true });
   });
   printpreview(true);
   // Wait for the mozprintcallback to finish.
   await mozPrintCallbackDone;
-  ctx2.drawWindow(window.frames[1], 0, 0, 400, 400, "rgb(255,255,255)");
+  ctx2.drawWindow(frameElts[1].contentWindow, 0, 0, 400, 400, "rgb(255,255,255)");
 
   exitprintpreview();
   ok(compareCanvases(), "Printing pages with fonts loaded from a mozPrintCallback should be the same.");
 
   finish();
 }
 
 ]]></script>
--- a/toolkit/components/perfmonitoring/PerformanceUtils.cpp
+++ b/toolkit/components/perfmonitoring/PerformanceUtils.cpp
@@ -1,27 +1,27 @@
 /* -*- 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 "nsThreadUtils.h"
 #include "mozilla/PerformanceUtils.h"
+#include "mozilla/ResultExtensions.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/WorkerDebugger.h"
 #include "mozilla/dom/WorkerDebuggerManager.h"
 
 #include "MediaDecoder.h"
 #include "XPCJSMemoryReporter.h"
 #include "jsfriendapi.h"
 #include "js/MemoryMetrics.h"
 #include "nsWindowMemoryReporter.h"
-#include "nsDOMWindowList.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 nsTArray<RefPtr<PerformanceInfoPromise>> CollectPerformanceInfo() {
   nsTArray<RefPtr<PerformanceInfoPromise>> promises;
@@ -88,25 +88,26 @@ void AddWindowTabSizes(nsGlobalWindowOut
   aSizes->mStyle += sizes.mStyle;
   aSizes->mOther += sizes.mOther;
 }
 
 nsresult GetTabSizes(nsGlobalWindowOuter* aWindow, nsTabSizes* aSizes) {
   // Add the window (and inner window) sizes. Might be cached.
   AddWindowTabSizes(aWindow, aSizes);
 
-  nsDOMWindowList* frames = aWindow->GetFrames();
-  uint32_t length = frames->GetLength();
+  BrowsingContext* bc = aWindow->GetBrowsingContext();
+  if (!bc) {
+    return NS_OK;
+  }
+
   // Measure this window's descendents.
-  for (uint32_t i = 0; i < length; i++) {
-    nsCOMPtr<nsPIDOMWindowOuter> child = frames->IndexedGetter(i);
-    NS_ENSURE_STATE(child);
-    nsGlobalWindowOuter* childWin = nsGlobalWindowOuter::Cast(child);
-    nsresult rv = GetTabSizes(childWin, aSizes);
-    NS_ENSURE_SUCCESS(rv, rv);
+  for (const auto& frame : bc->GetChildren()) {
+    if (auto* childWin = nsGlobalWindowOuter::Cast(frame->GetDOMWindow())) {
+      MOZ_TRY(GetTabSizes(childWin, aSizes));
+    }
   }
   return NS_OK;
 }
 
 RefPtr<MemoryPromise> CollectMemoryInfo(
     const nsCOMPtr<nsPIDOMWindowOuter>& aWindow,
     const RefPtr<AbstractThread>& aEventTarget) {
   // Getting Dom sizes. -- XXX should we reimplement GetTabSizes to async here ?