Bug 1592599 - Switch nsIDocShell.getDocShellEnumerator() away from using nsISimpleEnumerator; r=bzbarsky
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 18 Nov 2019 20:11:58 +0000
changeset 502495 383e90a53abebe31d0e8623c9447f013de22e9ac
parent 502494 f663e895f99599275d45eee3bc4525dfd4ad9d8e
child 502496 d9b0de6a3abc950bccbe20bb4eff14baffd5f830
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1592599
milestone72.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 1592599 - Switch nsIDocShell.getDocShellEnumerator() away from using nsISimpleEnumerator; r=bzbarsky Differential Revision: https://phabricator.services.mozilla.com/D51100
browser/components/uitour/test/browser_UITour_modalDialog.js
devtools/server/actors/targets/browsing-context.js
devtools/server/performance/timeline.js
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsDocShellEnumerator.cpp
docshell/base/nsDocShellEnumerator.h
docshell/base/nsIDocShell.idl
docshell/test/browser/file_bug1328501_framescript.js
dom/base/UIDirectionManager.cpp
layout/base/nsDocumentViewer.cpp
layout/base/nsRefreshDriver.cpp
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/ExtensionPolicyService.cpp
toolkit/components/extensions/WebNavigationFrames.jsm
toolkit/components/find/nsWebBrowserFind.cpp
toolkit/components/passwordmgr/test/browser/browser_private_window.js
toolkit/components/passwordmgr/test/browser/browser_username_select_dialog.js
toolkit/components/prompts/test/chromeScript.js
toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
uriloader/base/nsDocLoader.h
--- a/browser/components/uitour/test/browser_UITour_modalDialog.js
+++ b/browser/components/uitour/test/browser_UITour_modalDialog.js
@@ -51,17 +51,17 @@ var observer = SpecialPowers.wrapCallbac
 });
 
 function getDialogDoc() {
   // Find the <browser> which contains notifyWindow, by looking
   // through all the open windows and all the <browsers> in each.
 
   // var enumerator = wm.getEnumerator("navigator:browser");
   for (let { docShell } of Services.wm.getEnumerator(null)) {
-    var containedDocShells = docShell.getDocShellEnumerator(
+    var containedDocShells = docShell.getAllDocShellsInSubtree(
       docShell.typeChrome,
       docShell.ENUMERATE_FORWARDS
     );
     for (let childDocShell of containedDocShells) {
       // Get the corresponding document for this docshell
       // We don't want it if it's not done loading.
       if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
         continue;
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -89,23 +89,23 @@ function getDocShellChromeEventHandler(d
     } catch (e) {
       // ignore
     }
   }
   return handler;
 }
 
 function getChildDocShells(parentDocShell) {
-  const docShellsEnum = parentDocShell.getDocShellEnumerator(
+  const allDocShells = parentDocShell.getAllDocShellsInSubtree(
     Ci.nsIDocShellTreeItem.typeAll,
     Ci.nsIDocShell.ENUMERATE_FORWARDS
   );
 
   const docShells = [];
-  for (const docShell of docShellsEnum) {
+  for (const docShell of allDocShells) {
     docShell
       .QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIWebProgress);
     docShells.push(docShell);
   }
   return docShells;
 }
 
--- a/devtools/server/performance/timeline.js
+++ b/devtools/server/performance/timeline.js
@@ -96,22 +96,22 @@ Timeline.prototype = {
     } else {
       originalDocShell = this.targetActor.originalDocShell;
     }
 
     if (!originalDocShell) {
       return [];
     }
 
-    const docShellsEnum = originalDocShell.getDocShellEnumerator(
+    const docShells = originalDocShell.getAllDocShellsInSubtree(
       Ci.nsIDocShellTreeItem.typeAll,
       Ci.nsIDocShell.ENUMERATE_FORWARDS
     );
 
-    return Array.from(docShellsEnum);
+    return docShells;
   },
 
   /**
    * At regular intervals, pop the markers from the docshell, and forward
    * markers, memory, tick and frames events, if any.
    */
   _pullTimelineData: function() {
     const docShells = this.docShells;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1935,48 +1935,33 @@ nsDocShell::GetCharsetAutodetected(bool*
       source == kCharsetFromUserForcedAutoDetection) {
     *aCharsetAutodetected = true;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetDocShellEnumerator(int32_t aItemType,
-                                  DocShellEnumeratorDirection aDirection,
-                                  nsISimpleEnumerator** aResult) {
-  NS_ENSURE_ARG_POINTER(aResult);
-  *aResult = nullptr;
-
-  RefPtr<nsDocShellEnumerator> docShellEnum;
-  if (aDirection == ENUMERATE_FORWARDS) {
-    docShellEnum = new nsDocShellForwardsEnumerator;
-  } else {
-    docShellEnum = new nsDocShellBackwardsEnumerator;
-  }
-
-  nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
+nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
+                                     DocShellEnumeratorDirection aDirection,
+                                     nsTArray<RefPtr<nsIDocShell>>& aResult) {
+  aResult.Clear();
+
+  nsDocShellEnumerator docShellEnum(
+      (aDirection == ENUMERATE_FORWARDS)
+          ? nsDocShellEnumerator::EnumerationDirection::Forwards
+          : nsDocShellEnumerator::EnumerationDirection::Backwards,
+      aItemType, *this);
+
+  nsresult rv = docShellEnum.BuildDocShellArray(aResult);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem*)this);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  rv = docShellEnum->First();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator),
-                                    (void**)aResult);
-
-  return rv;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetAppType(AppType* aAppType) {
   *aAppType = mAppType;
   return NS_OK;
 }
 
@@ -3200,28 +3185,37 @@ nsDocShell::RemoveChild(nsIDocShellTreeI
 
   return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
 }
 
 NS_IMETHODIMP
 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
   NS_ENSURE_ARG_POINTER(aChild);
 
+  RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
+  NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
+
+  child.forget(aChild);
+
+  return NS_OK;
+}
+
+nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
 #ifdef DEBUG
   if (aIndex < 0) {
     NS_WARNING("Negative index passed to GetChildAt");
   } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
     NS_WARNING("Too large an index passed to GetChildAt");
   }
 #endif
 
   nsIDocumentLoader* child = ChildAt(aIndex);
-  NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
-
-  return CallQueryInterface(child, aChild);
+
+  // child may be nullptr here.
+  return static_cast<nsDocShell*>(child);
 }
 
 NS_IMETHODIMP
 nsDocShell::FindChildWithName(const nsAString& aName, bool aRecurse,
                               bool aSameType, nsIDocShellTreeItem* aRequestor,
                               nsIDocShellTreeItem* aOriginalRequestor,
                               nsIDocShellTreeItem** aResult) {
   NS_ENSURE_ARG_POINTER(aResult);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -488,16 +488,18 @@ class nsDocShell final : public nsDocLoa
                                    const nsString* aInitiatorType,
                                    uint32_t aLoadType, uint32_t aCacheKey,
                                    bool aHasNonEmptySandboxingFlags);
 
   // Notify consumers of a search being loaded through the observer service:
   static void MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
                                               const nsString& aKeyword);
 
+  nsDocShell* GetInProcessChildAt(int32_t aIndex);
+
  private:  // member functions
   friend class nsDSURIContentListener;
   friend class FramingChecker;
   friend class OnLinkClickEvent;
   friend class nsIDocShell;
   friend class mozilla::dom::BrowsingContext;
 
   // It is necessary to allow adding a timeline marker wherever a docshell
--- a/docshell/base/nsDocShellEnumerator.cpp
+++ b/docshell/base/nsDocShellEnumerator.cpp
@@ -1,178 +1,83 @@
 /* -*- 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 "nsDocShellEnumerator.h"
 
-#include "nsIDocShellTreeItem.h"
-
-nsDocShellEnumerator::nsDocShellEnumerator(int32_t aEnumerationDirection)
-    : mRootItem(nullptr),
-      mCurIndex(0),
-      mDocShellType(nsIDocShellTreeItem::typeAll),
-      mArrayValid(false),
-      mEnumerationDirection(aEnumerationDirection) {}
-
-nsDocShellEnumerator::~nsDocShellEnumerator() {}
+#include "nsDocShell.h"
 
-NS_IMETHODIMP
-nsDocShellEnumerator::GetNext(nsISupports** aResult) {
-  NS_ENSURE_ARG_POINTER(aResult);
-  *aResult = nullptr;
-
-  nsresult rv = EnsureDocShellArray();
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (mCurIndex >= mItemArray.Length()) {
-    return NS_ERROR_FAILURE;
-  }
+nsDocShellEnumerator::nsDocShellEnumerator(
+    nsDocShellEnumerator::EnumerationDirection aDirection,
+    int32_t aDocShellType, nsDocShell& aRootItem)
+    : mRootItem(&aRootItem),
+      mDocShellType(aDocShellType),
+      mDirection(aDirection) {}
 
-  // post-increment is important here
-  nsCOMPtr<nsISupports> item = do_QueryReferent(mItemArray[mCurIndex++], &rv);
-  item.forget(aResult);
-  return rv;
-}
+nsresult nsDocShellEnumerator::BuildDocShellArray(
+    nsTArray<RefPtr<nsIDocShell>>& aItemArray) {
+  MOZ_ASSERT(mRootItem);
 
-NS_IMETHODIMP
-nsDocShellEnumerator::HasMoreElements(bool* aResult) {
-  NS_ENSURE_ARG_POINTER(aResult);
-  *aResult = false;
+  aItemArray.Clear();
 
-  nsresult rv = EnsureDocShellArray();
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (mDirection == EnumerationDirection::Forwards) {
+    return BuildArrayRecursiveForwards(mRootItem, aItemArray);
   }
-
-  *aResult = (mCurIndex < mItemArray.Length());
-  return NS_OK;
-}
-
-nsresult nsDocShellEnumerator::GetEnumerationRootItem(
-    nsIDocShellTreeItem** aEnumerationRootItem) {
-  NS_ENSURE_ARG_POINTER(aEnumerationRootItem);
-  nsCOMPtr<nsIDocShellTreeItem> item = do_QueryReferent(mRootItem);
-  item.forget(aEnumerationRootItem);
-  return NS_OK;
+  MOZ_ASSERT(mDirection == EnumerationDirection::Backwards);
+  return BuildArrayRecursiveBackwards(mRootItem, aItemArray);
 }
 
-nsresult nsDocShellEnumerator::SetEnumerationRootItem(
-    nsIDocShellTreeItem* aEnumerationRootItem) {
-  mRootItem = do_GetWeakReference(aEnumerationRootItem);
-  ClearState();
-  return NS_OK;
-}
-
-nsresult nsDocShellEnumerator::GetEnumDocShellType(
-    int32_t* aEnumerationItemType) {
-  NS_ENSURE_ARG_POINTER(aEnumerationItemType);
-  *aEnumerationItemType = mDocShellType;
-  return NS_OK;
-}
-
-nsresult nsDocShellEnumerator::SetEnumDocShellType(
-    int32_t aEnumerationItemType) {
-  mDocShellType = aEnumerationItemType;
-  ClearState();
-  return NS_OK;
-}
-
-nsresult nsDocShellEnumerator::First() {
-  mCurIndex = 0;
-  return EnsureDocShellArray();
-}
-
-nsresult nsDocShellEnumerator::EnsureDocShellArray() {
-  if (!mArrayValid) {
-    mArrayValid = true;
-    return BuildDocShellArray(mItemArray);
-  }
-
-  return NS_OK;
-}
-
-nsresult nsDocShellEnumerator::ClearState() {
-  mItemArray.Clear();
-  mArrayValid = false;
-  mCurIndex = 0;
-  return NS_OK;
-}
-
-nsresult nsDocShellEnumerator::BuildDocShellArray(
-    nsTArray<nsWeakPtr>& aItemArray) {
-  NS_ENSURE_TRUE(mRootItem, NS_ERROR_NOT_INITIALIZED);
-  aItemArray.Clear();
-  nsCOMPtr<nsIDocShellTreeItem> item = do_QueryReferent(mRootItem);
-  return BuildArrayRecursive(item, aItemArray);
-}
-
-nsresult nsDocShellForwardsEnumerator::BuildArrayRecursive(
-    nsIDocShellTreeItem* aItem, nsTArray<nsWeakPtr>& aItemArray) {
+nsresult nsDocShellEnumerator::BuildArrayRecursiveForwards(
+    nsDocShell* aItem, nsTArray<RefPtr<nsIDocShell>>& aItemArray) {
   nsresult rv;
 
   // add this item to the array
   if (mDocShellType == nsIDocShellTreeItem::typeAll ||
       aItem->ItemType() == mDocShellType) {
-    if (!aItemArray.AppendElement(do_GetWeakReference(aItem))) {
+    if (!aItemArray.AppendElement(aItem, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
-  int32_t numChildren;
-  rv = aItem->GetInProcessChildCount(&numChildren);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  int32_t numChildren = aItem->ChildCount();
 
   for (int32_t i = 0; i < numChildren; ++i) {
-    nsCOMPtr<nsIDocShellTreeItem> curChild;
-    rv = aItem->GetInProcessChildAt(i, getter_AddRefs(curChild));
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
+    RefPtr<nsDocShell> curChild = aItem->GetInProcessChildAt(i);
+    MOZ_ASSERT(curChild);
 
-    rv = BuildArrayRecursive(curChild, aItemArray);
+    rv = BuildArrayRecursiveForwards(curChild, aItemArray);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
-nsresult nsDocShellBackwardsEnumerator::BuildArrayRecursive(
-    nsIDocShellTreeItem* aItem, nsTArray<nsWeakPtr>& aItemArray) {
+nsresult nsDocShellEnumerator::BuildArrayRecursiveBackwards(
+    nsDocShell* aItem, nsTArray<RefPtr<nsIDocShell>>& aItemArray) {
   nsresult rv;
 
-  int32_t numChildren;
-  rv = aItem->GetInProcessChildCount(&numChildren);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  uint32_t numChildren = aItem->ChildCount();
 
   for (int32_t i = numChildren - 1; i >= 0; --i) {
-    nsCOMPtr<nsIDocShellTreeItem> curChild;
-    rv = aItem->GetInProcessChildAt(i, getter_AddRefs(curChild));
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
+    RefPtr<nsDocShell> curChild = aItem->GetInProcessChildAt(i);
+    MOZ_ASSERT(curChild);
 
-    rv = BuildArrayRecursive(curChild, aItemArray);
+    rv = BuildArrayRecursiveBackwards(curChild, aItemArray);
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
   // add this item to the array
   if (mDocShellType == nsIDocShellTreeItem::typeAll ||
       aItem->ItemType() == mDocShellType) {
-    if (!aItemArray.AppendElement(do_GetWeakReference(aItem))) {
+    if (!aItemArray.AppendElement(aItem)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   return NS_OK;
 }
--- a/docshell/base/nsDocShellEnumerator.h
+++ b/docshell/base/nsDocShellEnumerator.h
@@ -2,94 +2,38 @@
 /* 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 nsDocShellEnumerator_h___
 #define nsDocShellEnumerator_h___
 
-#include "nsSimpleEnumerator.h"
 #include "nsTArray.h"
-#include "nsIWeakReferenceUtils.h"
-
-class nsIDocShellTreeItem;
 
-/*
-// {13cbc281-35ae-11d5-be5b-bde0edece43c}
-#define NS_DOCSHELL_FORWARDS_ENUMERATOR_CID  \
-{ 0x13cbc281, 0x35ae, 0x11d5, { 0xbe, 0x5b, 0xbd, 0xe0, 0xed, 0xec, 0xe4, 0x3c }
-}
-
-#define NS_DOCSHELL_FORWARDS_ENUMERATOR_CONTRACTID \
-"@mozilla.org/docshell/enumerator-forwards;1"
+class nsDocShell;
+class nsIDocShell;
 
-// {13cbc282-35ae-11d5-be5b-bde0edece43c}
-#define NS_DOCSHELL_BACKWARDS_ENUMERATOR_CID  \
-{ 0x13cbc282, 0x35ae, 0x11d5, { 0xbe, 0x5b, 0xbd, 0xe0, 0xed, 0xec, 0xe4, 0x3c }
-}
-
-#define NS_DOCSHELL_BACKWARDS_ENUMERATOR_CONTRACTID \
-"@mozilla.org/docshell/enumerator-backwards;1"
-*/
+class MOZ_STACK_CLASS nsDocShellEnumerator {
+ public:
+  enum class EnumerationDirection : uint8_t { Forwards, Backwards };
 
-class nsDocShellEnumerator : public nsSimpleEnumerator {
- protected:
-  enum { enumerateForwards, enumerateBackwards };
-
-  virtual ~nsDocShellEnumerator();
-
- public:
-  explicit nsDocShellEnumerator(int32_t aEnumerationDirection);
-
-  // nsISimpleEnumerator
-  NS_DECL_NSISIMPLEENUMERATOR
-
-  const nsID& DefaultInterface() override { return NS_GET_IID(nsIDocShell); }
+  nsDocShellEnumerator(EnumerationDirection aDirection, int32_t aDocShellType,
+                       nsDocShell& aRootItem);
 
  public:
-  nsresult GetEnumerationRootItem(nsIDocShellTreeItem** aEnumerationRootItem);
-  nsresult SetEnumerationRootItem(nsIDocShellTreeItem* aEnumerationRootItem);
-
-  nsresult GetEnumDocShellType(int32_t* aEnumerationItemType);
-  nsresult SetEnumDocShellType(int32_t aEnumerationItemType);
-
-  nsresult First();
-
- protected:
-  nsresult EnsureDocShellArray();
-  nsresult ClearState();
-
-  nsresult BuildDocShellArray(nsTArray<nsWeakPtr>& aItemArray);
-  virtual nsresult BuildArrayRecursive(nsIDocShellTreeItem* aItem,
-                                       nsTArray<nsWeakPtr>& aItemArray) = 0;
-
- protected:
-  nsWeakPtr mRootItem;  // weak ref!
-
-  nsTArray<nsWeakPtr> mItemArray;  // flattened list of items with matching type
-  uint32_t mCurIndex;
+  nsresult BuildDocShellArray(nsTArray<RefPtr<nsIDocShell>>& aItemArray);
 
-  int32_t mDocShellType;  // only want shells of this type
-  bool mArrayValid;       // is mItemArray up to date?
-
-  const int8_t mEnumerationDirection;
-};
-
-class nsDocShellForwardsEnumerator : public nsDocShellEnumerator {
- public:
-  nsDocShellForwardsEnumerator() : nsDocShellEnumerator(enumerateForwards) {}
+ private:
+  nsresult BuildArrayRecursiveForwards(
+      nsDocShell* aItem, nsTArray<RefPtr<nsIDocShell>>& aItemArray);
+  nsresult BuildArrayRecursiveBackwards(
+      nsDocShell* aItem, nsTArray<RefPtr<nsIDocShell>>& aItemArray);
 
- protected:
-  virtual nsresult BuildArrayRecursive(
-      nsIDocShellTreeItem* aItem, nsTArray<nsWeakPtr>& aItemArray) override;
-};
+ private:
+  const RefPtr<nsDocShell> mRootItem;
 
-class nsDocShellBackwardsEnumerator : public nsDocShellEnumerator {
- public:
-  nsDocShellBackwardsEnumerator() : nsDocShellEnumerator(enumerateBackwards) {}
+  const int32_t mDocShellType;  // only want shells of this type
 
- protected:
-  virtual nsresult BuildArrayRecursive(
-      nsIDocShellTreeItem* aItem, nsTArray<nsWeakPtr>& aItemArray) override;
+  const EnumerationDirection mDirection;
 };
 
 #endif  // nsDocShellEnumerator_h___
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -42,17 +42,16 @@ class ClientSource;
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
 interface nsIContentSecurityPolicy;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
 interface nsIEditingSession;
-interface nsISimpleEnumerator;
 interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIScriptGlobalObject;
 interface nsIStructuredCloneContainer;
 interface nsIDOMStorage;
@@ -249,31 +248,31 @@ interface nsIDocShell : nsIDocShellTreeI
    * its parent when reparented.
    *
    * NOTE: This should *not* be set false in new code, or for docShells
    * inserted anywhere other than as children of panels.
    */
   [infallible] attribute boolean inheritPrivateBrowsingId;
 
   /**
-   * Get an enumerator over this docShell and its children.
+   * Get an array of this docShell and its children.
    *
    * @param aItemType  - Only include docShells of this type, or if typeAll,
    *                     include all child shells.
    *                     Uses types from nsIDocShellTreeItem.
    * @param aDirection - Whether to enumerate forwards or backwards.
    */
 
   cenum DocShellEnumeratorDirection : 8 {
     ENUMERATE_FORWARDS  = 0,
     ENUMERATE_BACKWARDS = 1
   };
 
-  nsISimpleEnumerator getDocShellEnumerator(in long aItemType,
-                                            in nsIDocShell_DocShellEnumeratorDirection aDirection);
+  Array<nsIDocShell> getAllDocShellsInSubtree(in long aItemType,
+                                              in nsIDocShell_DocShellEnumeratorDirection aDirection);
 
   /**
    * The type of application that created this window.
    *
    * DO NOT DELETE, see bug 176166. For firefox, this value will always be
    * UNKNOWN. However, it is used heavily in Thunderbird/comm-central and we
    * don't really have a great replacement at the moment, so we'll just leave it
    * here.
--- a/docshell/test/browser/file_bug1328501_framescript.js
+++ b/docshell/test/browser/file_bug1328501_framescript.js
@@ -26,15 +26,13 @@ let requestObserver = {
 Services.obs.addObserver(requestObserver, "http-on-opening-request");
 addEventListener("unload", e => {
   if (e.target == this) {
     Services.obs.removeObserver(requestObserver, "http-on-opening-request");
   }
 });
 
 function getChildDocShells() {
-  let docShellsEnum = docShell.getDocShellEnumerator(
+  return docShell.getAllDocShellsInSubtree(
     Ci.nsIDocShellTreeItem.typeAll,
     Ci.nsIDocShell.ENUMERATE_FORWARDS
   );
-
-  return Array.from(docShellsEnum);
 }
--- a/dom/base/UIDirectionManager.cpp
+++ b/dom/base/UIDirectionManager.cpp
@@ -27,22 +27,20 @@ void OnPrefChange(const char* aPrefName,
 
   for (auto& elements : SimpleEnumerator<nsISupports>(windowEnumerator)) {
     nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(elements);
     if (window->Closed()) {
       continue;
     }
 
     nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
-    nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
-    rootDocShell->GetDocShellEnumerator(nsIDocShell::typeAll,
-                                        nsIDocShell::ENUMERATE_FORWARDS,
-                                        getter_AddRefs(docShellEnumerator));
-    NS_ENSURE_TRUE_VOID(docShellEnumerator);
-    for (auto& docShell : SimpleEnumerator<nsIDocShell>(docShellEnumerator)) {
+    nsTArray<RefPtr<nsIDocShell>> docShells;
+    rootDocShell->GetAllDocShellsInSubtree(
+        nsIDocShell::typeAll, nsIDocShell::ENUMERATE_FORWARDS, docShells);
+    for (auto& docShell : docShells) {
       if (nsCOMPtr<nsPIDOMWindowOuter> win = do_GetInterface(docShell)) {
         if (dom::Document* doc = win->GetExtantDoc()) {
           doc->ResetDocumentDirection();
         }
       }
     }
   }
 }
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3771,26 +3771,22 @@ nsDocumentViewer::Cancel() {
 #  if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
 // Reset ESM focus for all descendent doc shells.
 static void ResetFocusState(nsIDocShell* aDocShell) {
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   if (!fm) {
     return;
   }
 
-  nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
-  aDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
-                                   nsIDocShell::ENUMERATE_FORWARDS,
-                                   getter_AddRefs(docShellEnumerator));
-
-  nsCOMPtr<nsISupports> currentContainer;
-  bool hasMoreDocShells;
-  while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) &&
-         hasMoreDocShells) {
-    docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
+  nsTArray<RefPtr<nsIDocShell>> docShells;
+  aDocShell->GetAllDocShellsInSubtree(nsIDocShellTreeItem::typeContent,
+                                      nsIDocShell::ENUMERATE_FORWARDS,
+                                      docShells);
+
+  for (const auto& currentContainer : docShells) {
     nsCOMPtr<nsPIDOMWindowOuter> win = do_GetInterface(currentContainer);
     if (win) {
       fm->ClearFocus(win);
     }
   }
 }
 #  endif  // NS_PRINTING && NS_PRINT_PREVIEW
 
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1545,33 +1545,27 @@ static void GetProfileTimelineSubDocShel
     return;
   }
 
   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
   if (!timelines || timelines->IsEmpty()) {
     return;
   }
 
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  nsresult rv = aRootDocShell->GetDocShellEnumerator(
+  nsTArray<RefPtr<nsIDocShell>> docShells;
+  nsresult rv = aRootDocShell->GetAllDocShellsInSubtree(
       nsIDocShellTreeItem::typeAll, nsIDocShell::ENUMERATE_BACKWARDS,
-      getter_AddRefs(enumerator));
+      docShells);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
-  nsCOMPtr<nsIDocShell> curItem;
-  bool hasMore = false;
-  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
-    nsCOMPtr<nsISupports> curSupports;
-    enumerator->GetNext(getter_AddRefs(curSupports));
-    curItem = do_QueryInterface(curSupports);
-
-    if (!curItem || !curItem->GetRecordProfileTimelineMarkers()) {
+  for (const auto& curItem : docShells) {
+    if (!curItem->GetRecordProfileTimelineMarkers()) {
       continue;
     }
 
     nsDocShell* shell = static_cast<nsDocShell*>(curItem.get());
     bool isVisible = false;
     shell->GetVisibility(&isVisible);
     if (!isVisible) {
       continue;
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -1289,22 +1289,22 @@ var ExtensionContent = {
         return this.handleWebNavigationGetAllFrames(global);
     }
     return null;
   },
 
   // Helpers
 
   *enumerateWindows(docShell) {
-    let enum_ = docShell.getDocShellEnumerator(
+    let docShells = docShell.getAllDocShellsInSubtree(
       docShell.typeContent,
       docShell.ENUMERATE_FORWARDS
     );
 
-    for (let docShell of enum_) {
+    for (let docShell of docShells) {
       try {
         yield docShell.domWindow;
       } catch (e) {
         // This can fail if the docShell is being destroyed, so just
         // ignore the error.
       }
     }
   },
--- a/toolkit/components/extensions/ExtensionPolicyService.cpp
+++ b/toolkit/components/extensions/ExtensionPolicyService.cpp
@@ -287,22 +287,21 @@ nsresult ExtensionPolicyService::HandleE
     mMessageManagers.RemoveEntry(mm);
   }
   return NS_OK;
 }
 
 nsresult ForEachDocShell(
     nsIDocShell* aDocShell,
     const std::function<nsresult(nsIDocShell*)>& aCallback) {
-  nsCOMPtr<nsISimpleEnumerator> iter;
-  MOZ_TRY(aDocShell->GetDocShellEnumerator(nsIDocShell::typeContent,
-                                           nsIDocShell::ENUMERATE_FORWARDS,
-                                           getter_AddRefs(iter)));
+  nsTArray<RefPtr<nsIDocShell>> docShells;
+  MOZ_TRY(aDocShell->GetAllDocShellsInSubtree(
+      nsIDocShell::typeContent, nsIDocShell::ENUMERATE_FORWARDS, docShells));
 
-  for (auto& docShell : SimpleEnumerator<nsIDocShell>(iter)) {
+  for (auto& docShell : docShells) {
     MOZ_TRY(aCallback(docShell));
   }
   return NS_OK;
 }
 
 already_AddRefed<Promise> ExtensionPolicyService::ExecuteContentScript(
     nsPIDOMWindowInner* aWindow, WebExtensionContentScript& aScript) {
   if (!aWindow->IsCurrentInnerWindow()) {
--- a/toolkit/components/extensions/WebNavigationFrames.jsm
+++ b/toolkit/components/extensions/WebNavigationFrames.jsm
@@ -22,17 +22,17 @@ const EXPORTED_SYMBOLS = ["WebNavigation
 
 /**
  * A generator function which iterates over a docShell tree, given a root docShell.
  *
  * @param   {nsIDocShell} docShell - the root docShell object
  * @returns {Iterator<nsIDocShell>}
  */
 function iterateDocShellTree(docShell) {
-  return docShell.getDocShellEnumerator(
+  return docShell.getAllDocShellsInSubtree(
     docShell.typeContent,
     docShell.ENUMERATE_FORWARDS
   );
 }
 
 /**
  * Returns the frame ID of the given window. If the window is the
  * top-level content window, its frame ID is 0. Otherwise, its frame ID
--- a/toolkit/components/find/nsWebBrowserFind.cpp
+++ b/toolkit/components/find/nsWebBrowserFind.cpp
@@ -122,44 +122,30 @@ nsWebBrowserFind::FindNext(bool* aResult
   nsIDocShell* rootDocShell = rootFrame->GetDocShell();
   if (!rootDocShell) {
     return NS_ERROR_FAILURE;
   }
 
   auto enumDirection = mFindBackwards ? nsIDocShell::ENUMERATE_BACKWARDS
                                       : nsIDocShell::ENUMERATE_FORWARDS;
 
-  nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
-  rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
-                                           enumDirection,
-                                           getter_AddRefs(docShellEnumerator));
+  nsTArray<RefPtr<nsIDocShell>> docShells;
+  rv = rootDocShell->GetAllDocShellsInSubtree(nsIDocShellTreeItem::typeAll,
+                                              enumDirection, docShells);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // remember where we started
   nsCOMPtr<nsIDocShellTreeItem> startingItem = searchFrame->GetDocShell();
 
-  nsCOMPtr<nsIDocShellTreeItem> curItem;
-
   // XXX We should avoid searching in frameset documents here.
   // We also need to honour mSearchSubFrames and mSearchParentFrames.
-  bool hasMore, doFind = false;
-  while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) &&
-         hasMore) {
-    nsCOMPtr<nsISupports> curSupports;
-    rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports));
-    if (NS_FAILED(rv)) {
-      break;
-    }
-    curItem = do_QueryInterface(curSupports, &rv);
-    if (NS_FAILED(rv)) {
-      break;
-    }
-
+  bool doFind = false;
+  for (const auto& curItem : docShells) {
     if (doFind) {
       searchFrame = curItem->GetWindow();
       if (!searchFrame) {
         break;
       }
 
       OnStartSearchFrame(searchFrame);
 
@@ -187,36 +173,23 @@ nsWebBrowserFind::FindNext(bool* aResult
     return NS_OK;
   }
 
   // From here on, we're wrapping, first through the other frames, then finally
   // from the beginning of the starting frame back to the starting point.
 
   // because nsISimpleEnumerator is bad and isn't resettable, I have to
   // make a new one
-  docShellEnumerator = nullptr;
-  rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
-                                           enumDirection,
-                                           getter_AddRefs(docShellEnumerator));
+  rv = rootDocShell->GetAllDocShellsInSubtree(nsIDocShellTreeItem::typeAll,
+                                              enumDirection, docShells);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) &&
-         hasMore) {
-    nsCOMPtr<nsISupports> curSupports;
-    rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports));
-    if (NS_FAILED(rv)) {
-      break;
-    }
-    curItem = do_QueryInterface(curSupports, &rv);
-    if (NS_FAILED(rv)) {
-      break;
-    }
-
+  for (const auto& curItem : docShells) {
     searchFrame = curItem->GetWindow();
     if (!searchFrame) {
       rv = NS_ERROR_FAILURE;
       break;
     }
 
     if (curItem.get() == startingItem.get()) {
       // Beware! This may flush notifications via synchronous
--- a/toolkit/components/passwordmgr/test/browser/browser_private_window.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_private_window.js
@@ -17,17 +17,17 @@ async function focusWindow(win) {
   await promise;
 }
 
 function getDialogDoc() {
   // Trudge through all the open windows, until we find the one
   // that has either commonDialog.xul or selectDialog.xul loaded.
   // var enumerator = Services.wm.getEnumerator("navigator:browser");
   for (let { docShell } of Services.wm.getEnumerator(null)) {
-    var containedDocShells = docShell.getDocShellEnumerator(
+    var containedDocShells = docShell.getAllDocShellsInSubtree(
       docShell.typeChrome,
       docShell.ENUMERATE_FORWARDS
     );
     for (let childDocShell of containedDocShells) {
       // Get the corresponding document for this docshell
       // We don't want it if it's not done loading.
       if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
         continue;
--- a/toolkit/components/passwordmgr/test/browser/browser_username_select_dialog.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_username_select_dialog.js
@@ -4,17 +4,17 @@
  */
 
 // Copied from prompt_common.js. TODO: share the code.
 function getSelectDialogDoc() {
   // Trudge through all the open windows, until we find the one
   // that has selectDialog.xul loaded.
   // var enumerator = Services.wm.getEnumerator("navigator:browser");
   for (let { docShell } of Services.wm.getEnumerator(null)) {
-    var containedDocShells = docShell.getDocShellEnumerator(
+    var containedDocShells = docShell.getAllDocShellsInSubtree(
       docShell.typeChrome,
       docShell.ENUMERATE_FORWARDS
     );
     for (let childDocShell of containedDocShells) {
       // We don't want it if it's not done loading.
       if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
         continue;
       }
--- a/toolkit/components/prompts/test/chromeScript.js
+++ b/toolkit/components/prompts/test/chromeScript.js
@@ -217,17 +217,17 @@ function dismissPrompt(ui, action) {
   }
 }
 
 function getDialogDoc() {
   // Trudge through all the open windows, until we find the one
   // that has either commonDialog.xul or selectDialog.xul loaded.
   // var enumerator = Services.wm.getEnumerator("navigator:browser");
   for (let { docShell } of Services.wm.getEnumerator(null)) {
-    var containedDocShells = docShell.getDocShellEnumerator(
+    var containedDocShells = docShell.getAllDocShellsInSubtree(
       docShell.typeChrome,
       docShell.ENUMERATE_FORWARDS
     );
     for (let childDocShell of containedDocShells) {
       // Get the corresponding document for this docshell
       // We don't want it if it's not done loading.
       if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) {
         continue;
--- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
+++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
@@ -366,46 +366,43 @@ nsresult nsTypeAheadFind::FindItNow(uint
   nsCOMPtr<nsIDocShell> startingDocShell(presContext->GetDocShell());
   NS_ASSERTION(
       startingDocShell,
       "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]");
   if (!startingDocShell) return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIDocShell> currentDocShell;
   nsCOMPtr<nsISupports> currentContainer;
-  nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
   nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
   nsCOMPtr<nsIDocShell> rootContentDocShell;
+  typedef nsTArray<RefPtr<nsIDocShell>> DocShells;
+  DocShells docShells;
+  DocShells::const_iterator it, it_end;
   if (!aDontIterateFrames) {
     // The use of GetInProcessSameTypeRootTreeItem (and later in this method) is
     // OK here as out-of-process frames are handled externally by
     // FinderParent.jsm, which will end up only calling this method with
     // aDontIterateFrames set to true.
     startingDocShell->GetInProcessSameTypeRootTreeItem(
         getter_AddRefs(rootContentTreeItem));
     rootContentDocShell = do_QueryInterface(rootContentTreeItem);
 
     if (!rootContentDocShell) return NS_ERROR_FAILURE;
 
-    rootContentDocShell->GetDocShellEnumerator(
+    rootContentDocShell->GetAllDocShellsInSubtree(
         nsIDocShellTreeItem::typeContent, nsIDocShell::ENUMERATE_FORWARDS,
-        getter_AddRefs(docShellEnumerator));
+        docShells);
 
     // Default: can start at the current document
     currentContainer = do_QueryInterface(rootContentDocShell);
 
     // Iterate up to current shell, if there's more than 1 that we're
     // dealing with
-    bool hasMoreDocShells;
-
-    while (
-        NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) &&
-        hasMoreDocShells) {
-      docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
-      currentDocShell = do_QueryInterface(currentContainer);
+    for (it = docShells.begin(), it_end = docShells.end(); it != it_end; ++it) {
+      currentDocShell = *it;
       if (!currentDocShell || currentDocShell == startingDocShell ||
           aIsFirstVisiblePreferred)
         break;
     }
   } else {
     currentContainer = currentDocShell = startingDocShell;
   }
 
@@ -645,34 +642,34 @@ nsresult nsTypeAheadFind::FindItNow(uint
     if (aDontIterateFrames) {
       return NS_OK;
     }
 
     // ---------- Nothing found yet, try next document  -------------
     bool hasTriedFirstDoc = false;
     do {
       // ==== Second inner loop - get another while  ====
-      bool hasMoreDocShells;
-      if (NS_SUCCEEDED(
-              docShellEnumerator->HasMoreElements(&hasMoreDocShells)) &&
-          hasMoreDocShells) {
-        docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
-        NS_ASSERTION(currentContainer, "HasMoreElements lied to us!");
+      if (it != it_end) {
+        currentContainer = *it;
+        ++it;
+        NS_ASSERTION(currentContainer, "We're not at the end yet!");
         currentDocShell = do_QueryInterface(currentContainer);
 
         if (currentDocShell) break;
       } else if (hasTriedFirstDoc)  // Avoid potential infinite loop
         return NS_ERROR_FAILURE;    // No content doc shells
 
       // Reached last doc shell, loop around back to first doc shell
-      rootContentDocShell->GetDocShellEnumerator(
+      rootContentDocShell->GetAllDocShellsInSubtree(
           nsIDocShellTreeItem::typeContent, nsIDocShell::ENUMERATE_FORWARDS,
-          getter_AddRefs(docShellEnumerator));
+          docShells);
+      it = docShells.begin();
+      it_end = docShells.end();
       hasTriedFirstDoc = true;
-    } while (docShellEnumerator);  // ==== end second inner while  ===
+    } while (it != it_end);  // ==== end second inner while  ===
 
     bool continueLoop = false;
     if (currentDocShell != startingDocShell)
       continueLoop = true;  // Try next document
     else if (!hasWrapped || aIsFirstVisiblePreferred) {
       // Finished searching through docshells:
       // If aFirstVisiblePreferred == true, we may need to go through all
       // docshells twice -once to look for visible matches, the second time
--- a/uriloader/base/nsDocLoader.h
+++ b/uriloader/base/nsDocLoader.h
@@ -158,16 +158,18 @@ class nsDocLoader : public nsIDocumentLo
     // aChild will not be in the list if nsDocLoader::Stop() was called, since
     // that clears mOOPChildrenLoading.  It also dispatches the 'load' event,
     // so we don't need to call DocLoaderIsEmpty in that case.
     if (mOOPChildrenLoading.RemoveElement(aChild)) {
       DocLoaderIsEmpty(true);
     }
   }
 
+  uint32_t ChildCount() const { return mChildList.Length(); }
+
  protected:
   virtual ~nsDocLoader();
 
   virtual MOZ_MUST_USE nsresult SetDocLoaderParent(nsDocLoader* aLoader);
 
   bool IsBusy();
 
   void SetBackgroundLoadIframe();