Bug 378776, Need safe way to iterate a document's presshells, r+sr=bz
authorOlli.Pettay@helsinki.fi
Tue, 22 May 2007 14:45:03 -0700
changeset 1722 9b4120ace2a0d29732d15a4a129ae0c2c44d3438
parent 1721 d5b2f6579962b0ae0ee182f93fb37e3dc2ba36e8
child 1723 e037baf5d4e3d0749fed9d088f4120ac0550a827
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs378776
milestone1.9a5pre
Bug 378776, Need safe way to iterate a document's presshells, r+sr=bz
chrome/src/nsChromeRegistry.cpp
content/base/public/Makefile.in
content/base/public/nsIDocument.h
content/base/public/nsPresShellIterator.h
content/base/src/nsContentSink.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsObjectLoadingContent.cpp
content/events/src/nsEventStateManager.cpp
content/html/document/src/nsHTMLDocument.cpp
content/html/document/src/nsMediaDocument.cpp
content/xml/document/src/nsXMLPrettyPrinter.cpp
content/xul/content/src/nsXULElement.cpp
content/xul/document/src/nsXULCommandDispatcher.cpp
content/xul/document/src/nsXULDocument.cpp
dom/src/base/nsFocusController.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
rdf/chrome/src/nsChromeRegistry.cpp
--- a/chrome/src/nsChromeRegistry.cpp
+++ b/chrome/src/nsChromeRegistry.cpp
@@ -95,16 +95,17 @@
 #include "nsISimpleEnumerator.h"
 #include "nsIStyleSheet.h"
 #include "nsISupportsArray.h"
 #include "nsIVersionComparator.h"
 #include "nsIWindowMediator.h"
 #include "nsIXPConnect.h"
 #include "nsIXULAppInfo.h"
 #include "nsIXULRuntime.h"
+#include "nsPresShellIterator.h"
 
 #ifdef MOZ_XUL
 // keep all the RDF stuff together, in case we can remove it in the far future
 #include "rdf.h"
 #include "nsRDFCID.h"
 #include "nsIRDFService.h"
 #include "nsIRDFDataSource.h"
 #include "nsIRDFObserver.h"
@@ -918,20 +919,19 @@ nsresult nsChromeRegistry::RefreshWindow
   if (!domDocument)
     return NS_OK;
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
   if (!document)
     return NS_OK;
 
   // Deal with the agent sheets first.  Have to do all the style sets by hand.
-  PRUint32 shellCount = document->GetNumberOfShells();
-  for (PRUint32 k = 0; k < shellCount; k++) {
-    nsIPresShell *shell = document->GetShellAt(k);
-
+  nsPresShellIterator iter(document);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
     // Reload only the chrome URL agent style sheets.
     nsCOMArray<nsIStyleSheet> agentSheets;
     rv = shell->GetAgentStyleSheets(agentSheets);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMArray<nsIStyleSheet> newAgentSheets;
     for (PRInt32 l = 0; l < agentSheets.Count(); ++l) {
       nsIStyleSheet *sheet = agentSheets[l];
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -69,16 +69,17 @@ nsIHTMLToTextSink.h \
 nsIXPathEvaluatorInternal.h \
 mozISanitizingSerializer.h \
 nsCaseTreatment.h \
 nsContentCID.h \
 nsCopySupport.h \
 nsContentCreatorFunctions.h \
 nsLineBreaker.h \
 nsXMLNameSpaceMap.h \
+nsPresShellIterator.h \
 $(NULL)
 
 ifndef DISABLE_XFORMS_HOOKS
 EXPORTS += nsIXFormsUtilityService.h
 endif
 
 SDK_XPIDLSRCS   = \
 		nsISelection.idl  \
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -44,16 +44,17 @@
 #include "nsIURI.h"
 #include "nsWeakPtr.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsILoadGroup.h"
 #include "nsCRT.h"
 #include "mozFlushType.h"
 #include "nsIAtom.h"
 #include "nsCompatibility.h"
+#include "nsTObserverArray.h"
 
 class nsIContent;
 class nsPresContext;
 class nsIPresShell;
 
 class nsIStreamListener;
 class nsIStreamObserver;
 class nsStyleSet;
@@ -89,18 +90,18 @@ class nsIDOMUserDataHandler;
 template<class E> class nsCOMArray;
 class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-{ 0xb7f930df, 0x1c61, 0x410f, \
- { 0xab, 0x3c, 0xe2, 0x53, 0xca, 0x8d, 0x85, 0x49 } }
+{ 0x7dd5790f, 0x110d, 0x4bf6, \
+  { 0x83, 0x50, 0x4b, 0xe3, 0x5d, 0xdc, 0xe1, 0x1e } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -344,20 +345,19 @@ public:
    * presentation context (presentation contexts <b>must not</b> be
    * shared among multiple presentation shells).
    */
   virtual nsresult CreateShell(nsPresContext* aContext,
                                nsIViewManager* aViewManager,
                                nsStyleSet* aStyleSet,
                                nsIPresShell** aInstancePtrResult) = 0;
   virtual PRBool DeleteShell(nsIPresShell* aShell) = 0;
-  virtual PRUint32 GetNumberOfShells() const = 0;
-  virtual nsIPresShell *GetShellAt(PRUint32 aIndex) const = 0;
   virtual nsIPresShell *GetPrimaryShell() const = 0;
-  virtual void SetShellsHidden(PRBool aHide) = 0;
+  void SetShellsHidden(PRBool aHide) { mShellsAreHidden = aHide; }
+  PRBool ShellsAreHidden() const { return mShellsAreHidden; }
 
   /**
    * Return the parent document of this document. Will return null
    * unless this document is within a compound document and has a
    * parent. Note that this parent chain may cross chrome boundaries.
    */
   nsIDocument *GetParentDocument() const
   {
@@ -886,16 +886,17 @@ protected:
   /**
    * These methods should be called before and after dispatching
    * a mutation event.
    * To make this easy and painless, use the mozAutoSubtreeModified helper class.
    */
   virtual void WillDispatchMutationEvent(nsINode* aTarget) = 0;
   virtual void MutationEventDispatched(nsINode* aTarget) = 0;
   friend class mozAutoSubtreeModified;
+  friend class nsPresShellIterator;
 
   nsString mDocumentTitle;
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIURI> mDocumentBaseURI;
 
   nsWeakPtr mDocumentLoadGroup;
 
   nsWeakPtr mDocumentContainer;
@@ -927,16 +928,18 @@ protected:
   PRPackedBool mBidiEnabled;
 
   // True if this document is the initial document for a window.  This should
   // basically be true only for documents that exist in newly-opened windows or
   // documents created to satisfy a GetDocument() on a window when there's no
   // document in it.
   PRPackedBool mIsInitialDocumentInWindow;
 
+  PRPackedBool mShellsAreHidden;
+
   // The bidi options for this document.  What this bitfield means is
   // defined in nsBidiUtils.h
   PRUint32 mBidiOptions;
 
   nsCString mContentLanguage;
   nsCString mContentType;
 
   // The document's security info
@@ -944,16 +947,18 @@ protected:
 
   // if this document is part of a multipart document,
   // the ID can be used to distinguish it from the other parts.
   PRUint32 mPartID;
   
   // Cycle collector generation in which we're certain that this document
   // won't be collected
   PRUint32 mMarkedCCGeneration;
+
+  nsTObserverArray<nsIPresShell> mPresShells;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * Helper class to automatically handle batching of document updates.  This
  * class will call BeginUpdate on construction and EndUpdate on destruction on
  * the given document with the given update type.  The document could be null,
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsPresShellIterator.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Olli Pettay <Olli.Pettay@helsinki.fi> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsPresShellIterato_h___
+#define nsPresShellIterato_h___
+
+#include "nsIPresShell.h"
+#include "nsIDocument.h"
+
+class nsPresShellIterator :
+  private nsTObserverArray<nsIPresShell>::ForwardIterator
+{
+public:
+  nsPresShellIterator(nsIDocument* aDoc)
+  : nsTObserverArray<nsIPresShell>::ForwardIterator(aDoc->mPresShells),
+    mDoc(aDoc) {}
+
+  already_AddRefed<nsIPresShell> GetNextShell()
+  {
+    nsIPresShell* shell = nsnull;
+    if (!mDoc->ShellsAreHidden()) {
+      shell = GetNext();
+      NS_IF_ADDREF(shell);
+    }
+    return shell;
+  }
+private:
+  static void* operator new(size_t) CPP_THROW_NEW { return 0; }
+  static void operator delete(void*, size_t) {}
+
+  nsCOMPtr<nsIDocument> mDoc;
+};
+
+#endif
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -86,16 +86,17 @@
 #include "nsTimer.h"
 #include "nsIAppShell.h"
 #include "nsWidgetsCID.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIRequest.h"
 #include "nsNodeUtils.h"
 #include "nsIDOMNode.h"
 #include "nsThreadUtils.h"
+#include "nsPresShellIterator.h"
 
 PRLogModuleInfo* gContentSinkLogModuleInfo;
 
 class nsScriptLoaderObserverProxy : public nsIScriptLoaderObserver
 {
 public:
   nsScriptLoaderObserverProxy(nsIScriptLoaderObserver* aInner)
     : mInner(do_GetWeakReference(aInner))
@@ -944,42 +945,40 @@ nsContentSink::ScrollToRef()
   unescapedRef.Assign(tmpstr);
   nsMemory::Free(tmpstr);
 
   nsresult rv = NS_ERROR_FAILURE;
   // We assume that the bytes are in UTF-8, as it says in the spec:
   // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
   NS_ConvertUTF8toUTF16 ref(unescapedRef);
 
-  PRInt32 i, ns = mDocument->GetNumberOfShells();
-  for (i = 0; i < ns; i++) {
-    nsIPresShell* shell = mDocument->GetShellAt(i);
-    if (shell) {
-      // Check an empty string which might be caused by the UTF-8 conversion
-      if (!ref.IsEmpty()) {
-        // Note that GoToAnchor will handle flushing layout as needed.
-        rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
-      } else {
-        rv = NS_ERROR_FAILURE;
-      }
+  nsPresShellIterator iter(mDocument);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
+    // Check an empty string which might be caused by the UTF-8 conversion
+    if (!ref.IsEmpty()) {
+      // Note that GoToAnchor will handle flushing layout as needed.
+      rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
+    } else {
+      rv = NS_ERROR_FAILURE;
+    }
 
-      // If UTF-8 URI failed then try to assume the string as a
-      // document's charset.
+    // If UTF-8 URI failed then try to assume the string as a
+    // document's charset.
 
-      if (NS_FAILED(rv)) {
-        const nsACString &docCharset = mDocument->GetDocumentCharacterSet();
+    if (NS_FAILED(rv)) {
+      const nsACString &docCharset = mDocument->GetDocumentCharacterSet();
 
-        rv = nsContentUtils::ConvertStringFromCharset(docCharset, unescapedRef, ref);
+      rv = nsContentUtils::ConvertStringFromCharset(docCharset, unescapedRef, ref);
 
-        if (NS_SUCCEEDED(rv) && !ref.IsEmpty())
-          rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
-      }
-      if (NS_SUCCEEDED(rv)) {
-        mScrolledToRefAlready = PR_TRUE;
-      }
+      if (NS_SUCCEEDED(rv) && !ref.IsEmpty())
+        rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
+    }
+    if (NS_SUCCEEDED(rv)) {
+      mScrolledToRefAlready = PR_TRUE;
     }
   }
 }
 
 nsresult
 nsContentSink::RefreshIfEnabled(nsIViewManager* vm)
 {
   if (!vm) {
@@ -1024,56 +1023,50 @@ nsContentSink::StartLayout(PRBool aIgnor
   // UpdateChildCounts() (because we don't want to double-notify on whatever we
   // have right now).  If some of them _have_ started layout, we want to make
   // sure to flush tags instead of just calling UpdateChildCounts() after we
   // loop over the shells.
   FlushTags();
 
   mLayoutStarted = PR_TRUE;
   mLastNotificationTime = PR_Now();
-  
-  PRUint32 i;
 
-  // XXXbz Shells can get removed (or added!) as we iterate through this loop.
-  // We should try to use an nsTObserverArray for this.
-  for (i = 0; i < mDocument->GetNumberOfShells(); i++) {
-    nsIPresShell *shell = mDocument->GetShellAt(i);
-
-    if (shell) {
-      // Make sure we don't call InitialReflow() for a shell that has
-      // already called it. This can happen when the layout frame for
-      // an iframe is constructed *between* the Embed() call for the
-      // docshell in the iframe, and the content sink's call to OpenBody().
-      // (Bug 153815)
+  nsPresShellIterator iter(mDocument);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
+    // Make sure we don't call InitialReflow() for a shell that has
+    // already called it. This can happen when the layout frame for
+    // an iframe is constructed *between* the Embed() call for the
+    // docshell in the iframe, and the content sink's call to OpenBody().
+    // (Bug 153815)
 
-      PRBool didInitialReflow = PR_FALSE;
-      shell->GetDidInitialReflow(&didInitialReflow);
-      if (didInitialReflow) {
-        // XXX: The assumption here is that if something already
-        // called InitialReflow() on this shell, it also did some of
-        // the setup below, so we do nothing and just move on to the
-        // next shell in the list.
+    PRBool didInitialReflow = PR_FALSE;
+    shell->GetDidInitialReflow(&didInitialReflow);
+    if (didInitialReflow) {
+      // XXX: The assumption here is that if something already
+      // called InitialReflow() on this shell, it also did some of
+      // the setup below, so we do nothing and just move on to the
+      // next shell in the list.
 
-        continue;
-      }
+      continue;
+    }
 
-      // Make shell an observer for next time
-      shell->BeginObservingDocument();
+    // Make shell an observer for next time
+    shell->BeginObservingDocument();
 
-      // Resize-reflow this time
-      nsRect r = shell->GetPresContext()->GetVisibleArea();
-      nsCOMPtr<nsIPresShell> shellGrip = shell;
-      nsresult rv = shell->InitialReflow(r.width, r.height);
-      if (NS_FAILED(rv)) {
-        return;
-      }
+    // Resize-reflow this time
+    nsRect r = shell->GetPresContext()->GetVisibleArea();
+    nsCOMPtr<nsIPresShell> shellGrip = shell;
+    nsresult rv = shell->InitialReflow(r.width, r.height);
+    if (NS_FAILED(rv)) {
+      return;
+    }
 
-      // Now trigger a refresh
-      RefreshIfEnabled(shell->GetViewManager());
-    }
+    // Now trigger a refresh
+    RefreshIfEnabled(shell->GetViewManager());
   }
 
   // If the document we are loading has a reference or it is a
   // frameset document, disable the scroll bars on the views.
 
   if (mDocumentURI) {
     nsCAutoString ref;
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1289,19 +1289,20 @@ nsDocument::ResetStylesheetsToURI(nsIURI
   indx = mCatalogSheets.Count();
   while (--indx >= 0) {
     nsIStyleSheet* sheet = mCatalogSheets[indx];
     sheet->SetOwningDocument(nsnull);
 
     PRBool applicable;
     sheet->GetApplicable(applicable);
     if (applicable) {
-      for (PRInt32 i = 0, i_end = GetNumberOfShells(); i < i_end; ++i) {
-        GetShellAt(i)->StyleSet()->
-          RemoveStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      nsPresShellIterator iter(this);
+      nsCOMPtr<nsIPresShell> shell;
+      while ((shell = iter.GetNextShell())) {
+        shell->StyleSet()->RemoveStyleSheet(nsStyleSet::eAgentSheet, sheet);
       }
     }
 
     // XXX Tell observers?
   }
 
 
   // Release all the sheets
@@ -1310,53 +1311,55 @@ nsDocument::ResetStylesheetsToURI(nsIURI
   // now, but it could in the future -- in which case not releasing them
   // is probably the right thing to do.
 
   // Now reset our inline style and attribute sheets.
   nsresult rv;
   nsStyleSet::sheetType attrSheetType = GetAttrSheetType();
   if (mAttrStyleSheet) {
     // Remove this sheet from all style sets
-    PRInt32 count = GetNumberOfShells();
-    for (indx = 0; indx < count; ++indx) {
-      GetShellAt(indx)->StyleSet()->
-        RemoveStyleSheet(attrSheetType, mAttrStyleSheet);
+    nsPresShellIterator iter(this);
+    nsCOMPtr<nsIPresShell> shell;
+    while ((shell = iter.GetNextShell())) {
+      shell->StyleSet()->RemoveStyleSheet(attrSheetType, mAttrStyleSheet);
     }
     rv = mAttrStyleSheet->Reset(aURI);
   } else {
     rv = NS_NewHTMLStyleSheet(getter_AddRefs(mAttrStyleSheet), aURI, this);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Don't use AddStyleSheet, since it'll put the sheet into style
   // sets in the document level, which is not desirable here.
   mAttrStyleSheet->SetOwningDocument(this);
   
   if (mStyleAttrStyleSheet) {
     // Remove this sheet from all style sets
-    PRInt32 count = GetNumberOfShells();
-    for (indx = 0; indx < count; ++indx) {
-      GetShellAt(indx)->StyleSet()->
+    nsPresShellIterator iter(this);
+    nsCOMPtr<nsIPresShell> shell;
+    while ((shell = iter.GetNextShell())) {
+      shell->StyleSet()->
         RemoveStyleSheet(nsStyleSet::eStyleAttrSheet, mStyleAttrStyleSheet);
     }
     rv = mStyleAttrStyleSheet->Reset(aURI);
   } else {
     rv = NS_NewHTMLCSSStyleSheet(getter_AddRefs(mStyleAttrStyleSheet), aURI,
                                                 this);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   // The loop over style sets below will handle putting this sheet
   // into style sets as needed.
   mStyleAttrStyleSheet->SetOwningDocument(this);
 
   // Now set up our style sets
-  PRInt32 count = GetNumberOfShells();
-  for (indx = 0; indx < count; ++indx) {
-    FillStyleSet(GetShellAt(indx)->StyleSet());
+  nsPresShellIterator iter(this);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
+    FillStyleSet(shell->StyleSet());
   }
 
   return rv;
 }
 
 nsStyleSet::sheetType
 nsDocument::GetAttrSheetType()
 {
@@ -1945,52 +1948,33 @@ nsDocument::doCreateShell(nsPresContext*
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   rv = shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Note: we don't hold a ref to the shell (it holds a ref to us)
-  mPresShells.AppendElement(shell);
+  NS_ENSURE_TRUE(mPresShells.AppendObserver(shell), NS_ERROR_OUT_OF_MEMORY);
   shell.swap(*aInstancePtrResult);
 
   return NS_OK;
 }
 
 PRBool
 nsDocument::DeleteShell(nsIPresShell* aShell)
 {
-  return mPresShells.RemoveElement(aShell);
-}
-
-PRUint32
-nsDocument::GetNumberOfShells() const
-{
-  return mShellsAreHidden ? 0 : mPresShells.Count();
-}
-
-nsIPresShell *
-nsDocument::GetShellAt(PRUint32 aIndex) const
-{
-  return mShellsAreHidden ? nsnull :
-         NS_STATIC_CAST(nsIPresShell*, mPresShells.SafeElementAt(aIndex));
-}
+  return mPresShells.RemoveObserver(aShell);
+}
+
 
 nsIPresShell *
 nsDocument::GetPrimaryShell() const
 {
-  return mShellsAreHidden ? nsnull :
-         NS_STATIC_CAST(nsIPresShell*, mPresShells.SafeElementAt(0));
-}
-
-void
-nsDocument::SetShellsHidden(PRBool aHide)
-{
-  mShellsAreHidden = aHide;
+  return mShellsAreHidden ? nsnull : mPresShells.SafeObserverAt(0);
 }
 
 PR_STATIC_CALLBACK(void)
 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
 {
   SubDocMapEntry *e = NS_STATIC_CAST(SubDocMapEntry *, entry);
 
   NS_RELEASE(e->mKey);
@@ -2274,20 +2258,20 @@ PRInt32
 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
 {
   return mStyleSheets.IndexOf(aSheet);
 }
 
 void
 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
 {
-  PRInt32 count = GetNumberOfShells();
-  PRInt32 indx;
-  for (indx = 0; indx < count; ++indx) {
-    GetShellAt(indx)->StyleSet()->AddDocStyleSheet(aSheet, this);
+  nsPresShellIterator iter(this);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
+    shell->StyleSet()->AddDocStyleSheet(aSheet, this);
   }
 }
 
 void
 nsDocument::AddStyleSheet(nsIStyleSheet* aSheet)
 {
   NS_PRECONDITION(aSheet, "null arg");
   mStyleSheets.AppendObject(aSheet);
@@ -2301,19 +2285,21 @@ nsDocument::AddStyleSheet(nsIStyleSheet*
   }
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, PR_TRUE));
 }
 
 void
 nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet)
 {
-  for (PRInt32 i = 0, i_end = GetNumberOfShells(); i < i_end; ++i)
-    GetShellAt(i)->StyleSet()->
-      RemoveStyleSheet(nsStyleSet::eDocSheet, aSheet);
+  nsPresShellIterator iter(this);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
+    shell->StyleSet()->RemoveStyleSheet(nsStyleSet::eDocSheet, aSheet);
+  }
 }
 
 void
 nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet)
 {
   NS_PRECONDITION(aSheet, "null arg");
   nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
 
@@ -2438,19 +2424,21 @@ nsDocument::AddCatalogStyleSheet(nsIStyl
   mCatalogSheets.AppendObject(aSheet);
   aSheet->SetOwningDocument(this);
 
   PRBool applicable;
   aSheet->GetApplicable(applicable);
                                                                                 
   if (applicable) {
     // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
-    for (PRInt32 i = 0, i_end = GetNumberOfShells(); i < i_end; ++i)
-      GetShellAt(i)->StyleSet()->
-        AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet);
+    nsPresShellIterator iter(this);
+    nsCOMPtr<nsIPresShell> shell;
+    while ((shell = iter.GetNextShell())) {
+      shell->StyleSet()->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet);
+    }
   }
                                                                                 
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, PR_FALSE));
 }
 
 void
 nsDocument::EnsureCatalogStyleSheet(const char *aStyleSheetURI)
 {
@@ -3628,19 +3616,19 @@ nsDocument::GetTitle(nsAString& aTitle)
   aTitle.SetIsVoid(PR_FALSE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::SetTitle(const nsAString& aTitle)
 {
-  for (PRInt32 i = GetNumberOfShells() - 1; i >= 0; --i) {
-    nsCOMPtr<nsIPresShell> shell = GetShellAt(i);
-
+  nsPresShellIterator iter(this);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
     nsCOMPtr<nsISupports> container = shell->GetPresContext()->GetContainer();
     if (!container)
       continue;
 
     nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
     if (!docShellWin)
       continue;
 
@@ -4800,24 +4788,20 @@ nsDocument::FlushPendingNotifications(mo
       // things like resizes of our frame's widget, which we can't handle while
       // flushing is unsafe.
       if (doc && IsSafeToFlush()) {
         doc->FlushPendingNotifications(aType);
       }
     }
   }
 
-  PRInt32 i, count = GetNumberOfShells();
-
-  for (i = 0; i < count; i++) {
-    nsCOMPtr<nsIPresShell> shell = GetShellAt(i);
-
-    if (shell) {
-      shell->FlushPendingNotifications(aType);
-    }
+  nsPresShellIterator iter(this);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
+    shell->FlushPendingNotifications(aType);
   }
 }
 
 void
 nsDocument::AddReference(void *aKey, nsISupports *aReference)
 {
   if (mScriptGlobalObject) {
     if (!mContentWrapperHash) {
@@ -5256,24 +5240,21 @@ nsDocument::CreateElem(nsIAtom *aName, n
 
   return NS_NewElement(aResult, elementType, nodeInfo);
 }
 
 PRBool
 nsDocument::IsSafeToFlush() const
 {
   PRBool isSafeToFlush = PR_TRUE;
-  PRInt32 i = 0, n = GetNumberOfShells();
-  while (i < n && isSafeToFlush) {
-    nsIPresShell* shell = GetShellAt(i);
-
-    if (shell) {
-      shell->IsSafeToFlush(isSafeToFlush);
-    }
-    ++i;
+  nsPresShellIterator iter(NS_CONST_CAST(nsIDocument*,
+                           NS_STATIC_CAST(const nsIDocument*, this)));
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell()) && isSafeToFlush) {
+    shell->IsSafeToFlush(isSafeToFlush);
   }
   return isSafeToFlush;
 }
 
 nsresult
 nsDocument::Sanitize()
 {
   // Sanitize the document by resetting all password fields and any form
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -96,16 +96,17 @@
 #include "nsHTMLStyleSheet.h"
 #include "nsIHTMLCSSStyleSheet.h"
 
 #include "nsStyleSet.h"
 #include "nsXMLEventsManager.h"
 #include "pldhash.h"
 #include "nsAttrAndChildArray.h"
 #include "nsDOMAttributeMap.h"
+#include "nsPresShellIterator.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
 class nsIEventListenerManager;
@@ -369,20 +370,17 @@ public:
    * it's presentation context (presentation context's <b>must not</b> be
    * shared among multiple presentation shell's).
    */
   virtual nsresult CreateShell(nsPresContext* aContext,
                                nsIViewManager* aViewManager,
                                nsStyleSet* aStyleSet,
                                nsIPresShell** aInstancePtrResult);
   virtual PRBool DeleteShell(nsIPresShell* aShell);
-  virtual PRUint32 GetNumberOfShells() const;
-  virtual nsIPresShell *GetShellAt(PRUint32 aIndex) const;
   virtual nsIPresShell *GetPrimaryShell() const;
-  virtual void SetShellsHidden(PRBool aHide);
 
   virtual nsresult SetSubDocumentFor(nsIContent *aContent,
                                      nsIDocument* aSubDoc);
   virtual nsIDocument* GetSubDocumentFor(nsIContent *aContent) const;
   virtual nsIContent* FindContentForSubDocument(nsIDocument *aDocument) const;
 
   /**
    * Get the style sheets owned by this document.
@@ -760,18 +758,16 @@ protected:
 
   // True if the document has been detached from its content viewer.
   PRPackedBool mIsGoingAway:1;
   // True if the document is being destroyed.
   PRPackedBool mInDestructor:1;
   // True if the document "page" is not hidden
   PRPackedBool mVisible:1;
 
-  PRPackedBool mShellsAreHidden:1;
-
   PRUint8 mXMLDeclarationBits;
 
   PRUint8 mDefaultElementType;
 
   nsInterfaceHashtable<nsISupportsHashKey, nsPIBoxObject> *mBoxObjectTable;
   nsInterfaceHashtable<nsVoidPtrHashKey, nsISupports> *mContentWrapperHash;
 
   // The channel that got passed to StartDocumentLoad(), if any
@@ -802,18 +798,16 @@ private:
   // aSheetSet as the preferred set in the CSSLoader.
   void EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
                                        PRBool aUpdateCSSLoader);
 
   // These are not implemented and not supported.
   nsDocument(const nsDocument& aOther);
   nsDocument& operator=(const nsDocument& aOther);
 
-  nsSmallVoidArray mPresShells;
-
   nsCOMPtr<nsISupports> mXPathEvaluatorTearoff;
 
   // The layout history state that should be used by nodes in this
   // document.  We only actually store a pointer to it when:
   // 1)  We have no script global object.
   // 2)  We haven't had Destroy() called on us yet.
   nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
 
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -69,16 +69,17 @@
 #include "nsAutoPtr.h"
 #include "nsCURILoader.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsDocShellCID.h"
 #include "nsGkAtoms.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
+#include "nsPresShellIterator.h"
 
 // Concrete classes
 #include "nsFrameLoader.h"
 
 #include "nsObjectLoadingContent.h"
 
 static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID);
 
@@ -538,19 +539,19 @@ nsObjectLoadingContent::EnsureInstantiat
     NS_ASSERTION(thisContent, "must be a content");
 
     nsIDocument* doc = thisContent->GetCurrentDoc();
     if (!doc) {
       // Nothing we can do while plugin loading is done in layout...
       return NS_OK;
     }
 
-    PRUint32 numShells = doc->GetNumberOfShells();
-    for (PRUint32 i = 0; i < numShells; ++i) {
-      nsIPresShell* shell = doc->GetShellAt(i);
+    nsPresShellIterator iter(doc);
+    nsCOMPtr<nsIPresShell> shell;
+    while ((shell = iter.GetNextShell())) {
       shell->RecreateFramesFor(thisContent);
     }
 
     mInstantiating = PR_FALSE;
 
     frame = GetFrame();
     if (!frame) {
       return NS_OK;
@@ -1170,19 +1171,19 @@ nsObjectLoadingContent::NotifyStateChang
       // Make sure that frames are actually constructed, and do it after
       // EndUpdate was called.
       doc->FlushPendingNotifications(Flush_Frames);
     }
   } else if (aOldType != mType) {
     // If our state changed, then we already recreated frames
     // Otherwise, need to do that here
 
-    PRUint32 numShells = doc->GetNumberOfShells();
-    for (PRUint32 i = 0; i < numShells; ++i) {
-      nsIPresShell* shell = doc->GetShellAt(i);
+    nsPresShellIterator iter(doc);
+    nsCOMPtr<nsIPresShell> shell;
+    while ((shell = iter.GetNextShell())) {
       shell->RecreateFramesFor(thisContent);
     }
   }
 }
 
 /* static */ void
 nsObjectLoadingContent::FirePluginNotFound(nsIContent* thisContent)
 {
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -126,16 +126,17 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsContentUtils.h"
 
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsISupportsPrimitives.h"
 #include "nsEventDispatcher.h"
+#include "nsPresShellIterator.h"
 
 #ifdef XP_MACOSX
 #include <Events.h>
 #endif
 
 #if defined(DEBUG_rods) || defined(DEBUG_bryner)
 //#define DEBUG_DOCSHELL_FOCUS
 #endif
@@ -1982,19 +1983,19 @@ nsEventStateManager::GetParentScrollingV
 
   nsIDocument *parentDoc = doc->GetParentDocument();
 
   if (!parentDoc) {
     return NS_OK;
   }
 
   nsIPresShell *pPresShell = nsnull;
-  for (PRUint32 i = 0; i < parentDoc->GetNumberOfShells(); i++) {
-    nsIPresShell *tmpPresShell = parentDoc->GetShellAt(i);
-    NS_ENSURE_TRUE(tmpPresShell, NS_ERROR_FAILURE);
+  nsPresShellIterator iter(parentDoc);
+  nsCOMPtr<nsIPresShell> tmpPresShell;
+  while ((tmpPresShell = iter.GetNextShell())) {
     NS_ENSURE_TRUE(tmpPresShell->GetPresContext(), NS_ERROR_FAILURE);
     if (tmpPresShell->GetPresContext()->Type() == aPresContext->Type()) {
       pPresShell = tmpPresShell;
       break;
     }
   }
   if (!pPresShell)
     return NS_ERROR_FAILURE;
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2347,17 +2347,17 @@ nsHTMLDocument::Close()
     // handler of the frameset fires before the frames get reflowed
     // and loaded.  That is the long explanation for why we need this
     // one line of code here!
     // XXXbz as far as I can tell this may not be needed anymore; all
     // the testcases in bug 57636 pass without this line...  Leaving
     // it be for now, though.  In any case, there's no reason to do
     // this if we have no presshell, since in that case none of the
     // above about reusing frames applies.
-    if (GetNumberOfShells() != 0) {
+    if (GetPrimaryShell()) {
       FlushPendingNotifications(Flush_Layout);
     }
 
     // Remove the wyciwyg channel request from the document load group
     // that we added in OpenCommon().  If all other requests between
     // document.open() and document.close() have completed, then this
     // method should cause the firing of an onload event.
     NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::Close(): Trying to remove "
--- a/content/html/document/src/nsMediaDocument.cpp
+++ b/content/html/document/src/nsMediaDocument.cpp
@@ -261,22 +261,19 @@ nsMediaDocument::CreateSyntheticDocument
   root->AppendChildTo(body, PR_FALSE);
 
   return NS_OK;
 }
 
 nsresult
 nsMediaDocument::StartLayout()
 {
-  PRUint32 numberOfShells = GetNumberOfShells();
-  // XXXbz Shells can get removed (or added!) as we iterate through this loop.
-  // We should try to use an nsTObserverArray for this.
-  for (PRUint32 i = 0; i < numberOfShells; i++) {
-    nsIPresShell *shell = GetShellAt(i);
-
+  nsPresShellIterator iter(this);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
     // Make shell an observer for next time.
     shell->BeginObservingDocument();
 
     // Initial-reflow this time.
     nsRect visibleArea = shell->GetPresContext()->GetVisibleArea();
     nsCOMPtr<nsIPresShell> shellGrip = shell;
     nsresult rv = shell->InitialReflow(visibleArea.width, visibleArea.height);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/content/xml/document/src/nsXMLPrettyPrinter.cpp
+++ b/content/xml/document/src/nsXMLPrettyPrinter.cpp
@@ -69,17 +69,17 @@ nsXMLPrettyPrinter::~nsXMLPrettyPrinter(
 {
     NS_ASSERTION(!mDocument, "we shouldn't be referencing the document still");
 }
 
 nsresult
 nsXMLPrettyPrinter::PrettyPrint(nsIDocument* aDocument)
 {
     // Check for iframe with display:none. Such iframes don't have presshells
-    if (!aDocument->GetNumberOfShells()) {
+    if (!aDocument->GetPrimaryShell()) {
         return NS_OK;
     }
 
     // check if we're in an invisible iframe
     nsPIDOMWindow *internalWin = aDocument->GetWindow();
     nsCOMPtr<nsIDOMElement> frameElem;
     if (internalWin) {
         internalWin->GetFrameElement(getter_AddRefs(frameElem));
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -145,16 +145,17 @@
 #include "nsIDOMXULDocument.h"
 
 #include "nsReadableUtils.h"
 #include "nsITimelineService.h"
 #include "nsIFrame.h"
 #include "nsNodeInfoManager.h"
 #include "nsXBLBinding.h"
 #include "nsEventDispatcher.h"
+#include "nsPresShellIterator.h"
 
 /**
  * Three bits are used for XUL Element's lazy state.
  */
 #define XUL_ELEMENT_CHILDREN_MUST_BE_REBUILT \
   (nsXULElement::eChildrenMustBeRebuilt << XUL_ELEMENT_LAZY_STATE_OFFSET)
 
 #define XUL_ELEMENT_TEMPLATE_CONTENTS_BUILT \
@@ -1884,20 +1885,20 @@ nsXULElement::Focus()
     }
 
     nsIDocument* doc = GetCurrentDoc();
     // What kind of crazy tries to focus an element without a doc?
     if (!doc)
         return NS_OK;
 
     // Obtain a presentation context and then call SetFocus.
-    if (doc->GetNumberOfShells() == 0)
-        return NS_OK;
 
     nsIPresShell *shell = doc->GetPrimaryShell();
+    if (!shell)
+        return NS_OK;
 
     // Set focus
     nsCOMPtr<nsPresContext> context = shell->GetPresContext();
     SetFocus(context);
 
     return NS_OK;
 }
 
@@ -1905,44 +1906,41 @@ NS_IMETHODIMP
 nsXULElement::Blur()
 {
     nsIDocument* doc = GetCurrentDoc();
     // What kind of crazy tries to blur an element without a doc?
     if (!doc)
         return NS_OK;
 
     // Obtain a presentation context and then call SetFocus.
-    if (doc->GetNumberOfShells() == 0)
+    nsIPresShell *shell = doc->GetPrimaryShell();
+    if (!shell)
         return NS_OK;
 
-    nsIPresShell *shell = doc->GetPrimaryShell();
-
     // Set focus
     nsCOMPtr<nsPresContext> context = shell->GetPresContext();
     if (ShouldBlur(this))
       RemoveFocus(context);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXULElement::Click()
 {
     if (BoolAttrIsTrue(nsGkAtoms::disabled))
         return NS_OK;
 
     nsCOMPtr<nsIDocument> doc = GetCurrentDoc(); // Strong just in case
     if (doc) {
-        PRUint32 numShells = doc->GetNumberOfShells();
-        // strong ref to PresContext so events don't destroy it
-        nsCOMPtr<nsPresContext> context;
-
-        for (PRUint32 i = 0; i < numShells; ++i) {
-            nsIPresShell *shell = doc->GetShellAt(i);
-            context = shell->GetPresContext();
+        nsPresShellIterator iter(doc);
+        nsCOMPtr<nsIPresShell> shell;
+        while ((shell = iter.GetNextShell())) {
+            // strong ref to PresContext so events don't destroy it
+            nsCOMPtr<nsPresContext> context = shell->GetPresContext();
 
             PRBool isCallerChrome = nsContentUtils::IsCallerChrome();
 
             nsMouseEvent eventDown(isCallerChrome, NS_MOUSE_BUTTON_DOWN,
                                    nsnull, nsMouseEvent::eReal);
             nsMouseEvent eventUp(isCallerChrome, NS_MOUSE_BUTTON_UP,
                                  nsnull, nsMouseEvent::eReal);
             nsMouseEvent eventClick(isCallerChrome, NS_MOUSE_CLICK, nsnull,
@@ -1969,23 +1967,20 @@ nsXULElement::Click()
     return DoCommand();
 }
 
 NS_IMETHODIMP
 nsXULElement::DoCommand()
 {
     nsCOMPtr<nsIDocument> doc = GetCurrentDoc(); // strong just in case
     if (doc) {
-        PRUint32 numShells = doc->GetNumberOfShells();
-        nsCOMPtr<nsPresContext> context;
-
-        for (PRUint32 i = 0; i < numShells; ++i) {
-            nsIPresShell *shell = doc->GetShellAt(i);
-            context = shell->GetPresContext();
-
+        nsPresShellIterator iter(doc);
+        nsCOMPtr<nsIPresShell> shell;
+        while ((shell = iter.GetNextShell())) {
+            nsCOMPtr<nsPresContext> context = shell->GetPresContext();
             nsEventStatus status = nsEventStatus_eIgnore;
             nsXULCommandEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull);
             nsEventDispatcher::Dispatch(NS_STATIC_CAST(nsIContent*, this),
                                         context, &event, nsnull, &status);
         }
     }
 
     return NS_OK;
--- a/content/xul/document/src/nsXULCommandDispatcher.cpp
+++ b/content/xul/document/src/nsXULCommandDispatcher.cpp
@@ -64,16 +64,17 @@
 #include "prlog.h"
 #include "nsIDOMEventTarget.h"
 #include "nsGUIEvent.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 #include "nsDOMError.h"
 #include "nsEventDispatcher.h"
+#include "nsPresShellIterator.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gLog;
 #endif
 
 ////////////////////////////////////////////////////////////////////////
 
 nsXULCommandDispatcher::nsXULCommandDispatcher(nsIDocument* aDocument)
@@ -386,19 +387,19 @@ nsXULCommandDispatcher::UpdateCommands(c
       CopyUTF16toUTF8(aEventName, aeventnameC);
       PR_LOG(gLog, PR_LOG_NOTICE,
              ("xulcmd[%p] update %p event=%s",
               this, updater->mElement.get(),
               aeventnameC.get()));
     }
 #endif
 
-    PRUint32 count = document->GetNumberOfShells();
-    for (PRUint32 i = 0; i < count; i++) {
-      nsIPresShell *shell = document->GetShellAt(i);
+    nsPresShellIterator iter(document);
+    nsCOMPtr<nsIPresShell> shell;
+    while ((shell = iter.GetNextShell())) {
 
       // Retrieve the context in which our DOM event will fire.
       nsCOMPtr<nsPresContext> context = shell->GetPresContext();
 
       // Handle the DOM event
       nsEventStatus status = nsEventStatus_eIgnore;
 
       nsEvent event(PR_TRUE, NS_XUL_COMMAND_UPDATE);
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -896,19 +896,19 @@ nsXULDocument::ExecuteOnBroadcastHandler
             !listeningToAttribute.EqualsLiteral("*")) {
             continue;
         }
 
         // This is the right <observes> element. Execute the
         // |onbroadcast| event handler
         nsEvent event(PR_TRUE, NS_XUL_BROADCAST);
 
-        PRInt32 j = GetNumberOfShells();
-        while (--j >= 0) {
-            nsCOMPtr<nsIPresShell> shell = GetShellAt(j);
+        nsPresShellIterator iter(this);
+        nsCOMPtr<nsIPresShell> shell;
+        while ((shell = iter.GetNextShell())) {
 
             nsCOMPtr<nsPresContext> aPresContext = shell->GetPresContext();
 
             // Handle the DOM event
             nsEventStatus status = nsEventStatus_eIgnore;
             nsEventDispatcher::Dispatch(child, aPresContext, &event, nsnull,
                                         &status);
         }
@@ -1968,20 +1968,19 @@ nsXULDocument::StartLayout(void)
 
             PR_LOG(gXULLog, PR_LOG_WARNING,
                    ("xul: unable to layout '%s'; no root content", urlspec.get()));
         }
 #endif
         return NS_OK;
     }
 
-    // XXXbz Shells can get removed (or added!) as we iterate through this
-    // loop.  We should try to use an nsTObserverArray for this.
-    for (PRUint32 i = 0; i < GetNumberOfShells(); ++i) {
-        nsIPresShell *shell = GetShellAt(i);
+    nsPresShellIterator iter(this);
+    nsCOMPtr<nsIPresShell> shell;
+    while ((shell = iter.GetNextShell())) {
 
         // Resize-reflow this time
         nsPresContext *cx = shell->GetPresContext();
         NS_ASSERTION(cx != nsnull, "no pres context");
         if (! cx)
             return NS_ERROR_UNEXPECTED;
 
         nsCOMPtr<nsISupports> container = cx->GetContainer();
--- a/dom/src/base/nsFocusController.cpp
+++ b/dom/src/base/nsFocusController.cpp
@@ -206,17 +206,17 @@ nsFocusController::UpdateCommands()
     mCurrentElement->GetOwnerDocument(getter_AddRefs(domDoc));
     if (domDoc) {
       doc = do_QueryInterface(domDoc);
       window = do_QueryInterface(doc->GetScriptGlobalObject());
     }
   }
 
   // If there is no presshell, it's a zombie document which can't handle the command updates
-  if (window && doc && doc->GetNumberOfShells()) {
+  if (window && doc && doc->GetPrimaryShell()) {
     // Not a zombie document, so we can handle the command update
     window->UpdateCommands(NS_LITERAL_STRING("focus"));
     mNeedUpdateCommands = PR_FALSE;
   }
 }
 
   
 NS_IMETHODIMP
@@ -278,20 +278,16 @@ nsFocusController::MoveFocus(PRBool aFor
   }
 
   if (!doc)
     // No way to obtain an event state manager.  Give up.
     return NS_OK;
 
 
   // Obtain a presentation context
-  PRInt32 count = doc->GetNumberOfShells();
-  if (count == 0)
-    return NS_OK;
-
   nsIPresShell *shell = doc->GetPrimaryShell();
   if (!shell)
     return NS_OK;
 
   // Make sure frames have been constructed before shifting focus, bug 273092.
   shell->FlushPendingNotifications(Flush_Frames);
 
   // Retrieve the context
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -39,16 +39,17 @@
 #include "nsSVGOuterSVGFrame.h"
 #include "nsIDOMSVGSVGElement.h"
 #include "nsSVGSVGElement.h"
 #include "nsSVGTextFrame.h"
 #include "nsSVGRect.h"
 #include "nsDisplayList.h"
 #include "nsStubMutationObserver.h"
 #include "gfxContext.h"
+#include "nsPresShellIterator.h"
 
 #if defined(DEBUG) && defined(SVG_DEBUG_PRINTING)
 #include "nsIDeviceContext.h"
 #include "nsTransform2D.h"
 #endif
 
 class nsSVGMutationObserver : public nsStubMutationObserver
 {
@@ -83,19 +84,20 @@ nsSVGMutationObserver::AttributeChanged(
                                         PRInt32 aNameSpaceID,
                                         nsIAtom *aAttribute,
                                         PRInt32 aModType)
 {
   if (aNameSpaceID != kNameSpaceID_XML || aAttribute != nsGkAtoms::space) {
     return;
   }
 
-  PRUint32 count = aDocument->GetNumberOfShells();
-  for (PRUint32 i = 0; i < count; ++i) {
-    nsIFrame *frame = aDocument->GetShellAt(i)->GetPrimaryFrameFor(aContent);
+  nsPresShellIterator iter(aDocument);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
+    nsIFrame *frame = shell->GetPrimaryFrameFor(aContent);
     if (!frame) {
       continue;
     }
 
     // is the content a child of a text element
     nsISVGTextContentMetrics* metrics;
     CallQueryInterface(frame, &metrics);
     if (metrics) {
--- a/rdf/chrome/src/nsChromeRegistry.cpp
+++ b/rdf/chrome/src/nsChromeRegistry.cpp
@@ -89,16 +89,17 @@
 #include "nsIDOMElement.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsIAtom.h"
 #include "nsStaticAtom.h"
 #include "nsNetCID.h"
 #include "nsIJARURI.h"
 #include "nsIFileURL.h"
 #include "nsIXPConnect.h"
+#include "nsPresShellIterator.h"
 
 static char kChromePrefix[] = "chrome://";
 nsIAtom* nsChromeRegistry::sCPrefix; // atom for "c"
 
 #define kChromeFileName           NS_LITERAL_CSTRING("chrome.rdf")
 #define kInstalledChromeFileName  NS_LITERAL_CSTRING("installed-chrome.txt")
 
 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
@@ -1391,20 +1392,19 @@ nsresult nsChromeRegistry::RefreshWindow
   if (!domDocument)
     return NS_OK;
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
   if (!document)
     return NS_OK;
 
   // Deal with the agent sheets first.  Have to do all the style sets by hand.
-  PRUint32 shellCount = document->GetNumberOfShells();
-  for (PRUint32 k = 0; k < shellCount; k++) {
-    nsIPresShell *shell = document->GetShellAt(k);
-
+  nsPresShellIterator iter(document);
+  nsCOMPtr<nsIPresShell> shell;
+  while ((shell = iter.GetNextShell())) {
     // Reload only the chrome URL agent style sheets.
     nsCOMArray<nsIStyleSheet> agentSheets;
     rv = shell->GetAgentStyleSheets(agentSheets);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMArray<nsIStyleSheet> newAgentSheets;
     for (PRInt32 l = 0; l < agentSheets.Count(); ++l) {
       nsIStyleSheet *sheet = agentSheets[l];