Bug 366200: Fix problems with mRootContent being out of sync with mChildren by removing mRootContent. r/sr=bz
authorjonas@sicking.cc
Tue, 11 Dec 2007 18:26:09 -0800
changeset 8938 f3e07ee375932ec4da295ceeb79093031526c012
parent 8937 3c8e96943d126ef1893b2091ce622f530867f97e
child 8939 4d4a0113bf56bf7b1c81f6e2d2b075ba939bb31e
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs366200
milestone1.9b3pre
Bug 366200: Fix problems with mRootContent being out of sync with mChildren by removing mRootContent. r/sr=bz
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/html/document/src/nsHTMLDocument.cpp
content/html/document/src/nsHTMLDocument.h
content/html/document/src/nsImageDocument.cpp
content/html/document/src/nsMediaDocument.cpp
content/html/document/src/nsPluginDocument.cpp
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/src/nsSVGSVGElement.h
content/svg/document/src/nsSVGDocument.cpp
content/xml/document/src/nsXMLDocument.cpp
content/xul/document/src/nsXULDocument.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -391,18 +391,22 @@ public:
    */
   virtual nsIContent *FindContentForSubDocument(nsIDocument *aDocument) const = 0;
 
   /**
    * Return the root content object for this document.
    */
   nsIContent *GetRootContent() const
   {
-    return mRootContent;
+    return (mCachedRootContent &&
+            mCachedRootContent->GetNodeParent() == this) ?
+           reinterpret_cast<nsIContent*>(mCachedRootContent.get()) :
+           GetRootContentInternal();
   }
+  virtual nsIContent *GetRootContentInternal() const = 0;
 
   /**
    * Accessors to the collection of stylesheets owned by this document.
    * Style sheets are ordered, most significant last.
    */
 
   /**
    * Get the number of stylesheets
@@ -936,19 +940,20 @@ protected:
   nsWeakPtr mDocumentContainer;
 
   nsCString mCharacterSet;
   PRInt32 mCharacterSetSource;
 
   // This is just a weak pointer; the parent document owns its children.
   nsIDocument* mParentDocument;
 
-  // A weak reference to the only child element, or null if no
-  // such element exists.
-  nsIContent* mRootContent;
+  // A reference to the content last returned from GetRootContent().
+  // This should be an nsIContent, but that would force us to pull in
+  // nsIContent.h
+  nsCOMPtr<nsINode> mCachedRootContent;
 
   // We'd like these to be nsRefPtrs, but that'd require us to include
   // additional headers that we don't want to expose.
   // The cleanup is handled by the nsDocument destructor.
   nsBindingManager* mBindingManager; // [STRONG]
   nsNodeInfoManager* mNodeInfoManager; // [STRONG]
   nsICSSLoader* mCSSLoader; // [STRONG]
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -813,37 +813,27 @@ nsDocument::~nsDocument()
   // Kill the subdocument map, doing this will release its strong
   // references, if any.
   if (mSubDocuments) {
     PL_DHashTableDestroy(mSubDocuments);
 
     mSubDocuments = nsnull;
   }
 
-  PRInt32 indx;
-  if (mRootContent) {
-    if (mRootContent->GetCurrentDoc()) {
-      NS_ASSERTION(mRootContent->GetCurrentDoc() == this,
-                   "Unexpected current doc in root content");
-      // The root content still has a pointer back to the document,
-      // clear the document pointer in all children.
-      
-      // Destroy link map now so we don't waste time removing
-      // links one by one
-      DestroyLinkMap();
-
-      PRUint32 count = mChildren.ChildCount();
-      for (indx = PRInt32(count) - 1; indx >= 0; --indx) {
-        mChildren.ChildAt(indx)->UnbindFromTree();
-        mChildren.RemoveChildAt(indx);
-      }
-    }
-  }
-
-  mRootContent = nsnull;
+  // Destroy link map now so we don't waste time removing
+  // links one by one
+  DestroyLinkMap();
+
+  PRInt32 indx; // must be signed
+  PRUint32 count = mChildren.ChildCount();
+  for (indx = PRInt32(count) - 1; indx >= 0; --indx) {
+    mChildren.ChildAt(indx)->UnbindFromTree();
+    mChildren.RemoveChildAt(indx);
+  }
+  mCachedRootContent = nsnull;
 
   // Let the stylesheets know we're going away
   indx = mStyleSheets.Count();
   while (--indx >= 0) {
     mStyleSheets[indx]->SetOwningDocument(nsnull);
   }
   indx = mCatalogSheets.Count();
   while (--indx >= 0) {
@@ -1015,16 +1005,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   // Traverse the mChildren nsAttrAndChildArray.
   for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()); indx > 0; --indx) {
     cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_USERDATA
 
   // Traverse all nsIDocument pointer members.
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedRootContent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mBindingManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSecurityInfo)
 
   // Traverse all nsDocument nsCOMPtrs.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParser)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptGlobalObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMStyleSheets)
@@ -1073,23 +1064,24 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   // Unlink the mChildren nsAttrAndChildArray.
   for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()) - 1; 
        indx >= 0; --indx) {
     tmp->mChildren.ChildAt(indx)->UnbindFromTree();
     tmp->mChildren.RemoveChildAt(indx);
   }
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedRootContent)
+
   NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
 
   // Unlink any associated preserved wrapper.
   tmp->RemoveReference(tmp);
 
   tmp->mParentDocument = nsnull;
-  tmp->mRootContent = nsnull;
 
   // nsDocument has a pretty complex destructor, so we're going to
   // assume that *most* cycles you actually want to break somewhere
   // else, and not unlink an awful lot here.
   //
   // In rare cases where you think an unlink will help here, add one
   // manually.
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -1213,25 +1205,25 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
 
     mSubDocuments = nsnull;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyLinkMap();
 
-  mRootContent = nsnull;
   PRUint32 count = mChildren.ChildCount();
   for (PRInt32 i = PRInt32(count) - 1; i >= 0; i--) {
     nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
 
     nsNodeUtils::ContentRemoved(this, content, i);
     content->UnbindFromTree();
     mChildren.RemoveChildAt(i);
   }
+  mCachedRootContent = nsnull;
 
   // Reset our stylesheets
   ResetStylesheetsToURI(aURI);
   
   // Release the listener manager
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nsnull;
@@ -2202,16 +2194,34 @@ nsDocument::FindContentForSubDocument(ns
 }
 
 PRBool
 nsDocument::IsNodeOfType(PRUint32 aFlags) const
 {
     return !(aFlags & ~eDOCUMENT);
 }
 
+nsIContent*
+nsDocument::GetRootContentInternal() const
+{
+  // Loop backwards because any non-elements, such as doctypes and PIs
+  // are likely to appear before the root element.
+  PRUint32 i;
+  for (i = mChildren.ChildCount(); i > 0; --i) {
+    nsIContent* child = mChildren.ChildAt(i - 1);
+    if (child->IsNodeOfType(nsINode::eELEMENT)) {
+      const_cast<nsDocument*>(this)->mCachedRootContent = child;
+      return child;
+    }
+  }
+  
+  const_cast<nsDocument*>(this)->mCachedRootContent = nsnull;
+  return nsnull;
+}
+
 nsIContent *
 nsDocument::GetChildAt(PRUint32 aIndex) const
 {
   return mChildren.GetSafeChildAt(aIndex);
 }
 
 PRInt32
 nsDocument::IndexOf(nsINode* aPossibleChild) const
@@ -2224,47 +2234,24 @@ nsDocument::GetChildCount() const
 {
   return mChildren.ChildCount();
 }
 
 nsresult
 nsDocument::InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
                           PRBool aNotify)
 {
-  if (aKid->IsNodeOfType(nsINode::eELEMENT)) {
-    if (mRootContent) {
-      NS_ERROR("Inserting element child when we already have one");
-      return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-    }
-
-    mRootContent = aKid;
-  }
-  
-  nsresult rv = nsGenericElement::doInsertChildAt(aKid, aIndex, aNotify,
-                                                  nsnull, this, mChildren);
-
-  if (NS_FAILED(rv) && mRootContent == aKid) {
-    PRInt32 kidIndex = mChildren.IndexOfChild(aKid);
-    NS_ASSERTION(kidIndex == -1,
-                 "Error result and still have same root content but it's in "
-                 "our child list?");
-    // Check to make sure that we're keeping mRootContent in sync with our
-    // child list... but really, if kidIndex != -1 we have major problems
-    // coming up; hence the assert above.  This check is just a feeble attempt
-    // to not die due to mRootContent being bogus.
-    if (kidIndex == -1) {
-      mRootContent = nsnull;
-    }
-  }
-
-#ifdef DEBUG
-  VerifyRootContentState();
-#endif
-
-  return rv;
+  if (aKid->IsNodeOfType(nsINode::eELEMENT) &&
+      GetRootContent()) {
+    NS_ERROR("Inserting element child when we already have one");
+    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
+  }
+
+  return nsGenericElement::doInsertChildAt(aKid, aIndex, aNotify,
+                                           nsnull, this, mChildren);
 }
 
 nsresult
 nsDocument::AppendChildTo(nsIContent* aKid, PRBool aNotify)
 {
   // Make sure to _not_ call the subclass InsertChildAt here.  If
   // subclasses wanted to hook into this stuff, they would have
   // overridden AppendChildTo.
@@ -2272,68 +2259,31 @@ nsDocument::AppendChildTo(nsIContent* aK
   // Feels that way to me...
   return nsDocument::InsertChildAt(aKid, GetChildCount(), aNotify);
 }
 
 nsresult
 nsDocument::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
 {
   nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
-  nsresult rv = NS_OK;
-  if (oldKid) {
-    if (oldKid == mRootContent) {
-      NS_ASSERTION(oldKid->IsNodeOfType(nsINode::eELEMENT),
-                   "Non-element root content?");
-      // Destroy the link map up front and null out mRootContent before we mess
-      // with the child list.  Hopefully no one in doRemoveChildAt will compare
-      // the content being removed to GetRootContent()....  Need to do this
-      // before calling doRemoveChildAt because DOM events might fire while
-      // we're inside the doInsertChildAt call and want to set a new
-      // mRootContent; if they do that, setting mRootContent after the
-      // doRemoveChildAt call would clobber state.  If we ever fix the issue of
-      // DOM events firing at inconvenient times, consider changing the order
-      // here.  Just make sure we DestroyLinkMap() before unbinding the
-      // content.
-      DestroyLinkMap();
-      mRootContent = nsnull;
-    }
-    
-    rv = nsGenericElement::doRemoveChildAt(aIndex, aNotify, oldKid,
-                                           nsnull, this, mChildren);
-    if (NS_FAILED(rv) && mChildren.IndexOfChild(oldKid) != -1) {
-      mRootContent = oldKid;
-    }
-  }
-
-#ifdef DEBUG
-  VerifyRootContentState();
-#endif
-
+  if (!oldKid) {
+    return NS_OK;
+  }
+
+  if (oldKid->IsNodeOfType(nsINode::eELEMENT)) {
+    // Destroy the link map up front before we mess with the child list.
+    DestroyLinkMap();
+  }
+
+  nsresult rv = nsGenericElement::doRemoveChildAt(aIndex, aNotify, oldKid,
+                                                  nsnull, this, mChildren);
+  mCachedRootContent = nsnull;
   return rv;
 }
 
-#ifdef DEBUG
-void
-nsDocument::VerifyRootContentState()
-{
-  nsIContent* elementChild = nsnull;
-  for (PRUint32 i = 0; i < GetChildCount(); ++i) {
-    nsIContent* kid = GetChildAt(i);
-    NS_ASSERTION(kid, "Must have kid here");
-
-    if (kid->IsNodeOfType(nsINode::eELEMENT)) {
-      NS_ASSERTION(!elementChild, "Multiple element kids?");
-      elementChild = kid;
-    }
-  }
-
-  NS_ASSERTION(mRootContent == elementChild, "Incorrect mRootContent");
-}
-#endif // DEBUG
-
 PRInt32
 nsDocument::GetNumberOfStyleSheets() const
 {
   return mStyleSheets.Count();
 }
 
 nsIStyleSheet*
 nsDocument::GetStyleSheetAt(PRInt32 aIndex) const
@@ -2954,38 +2904,21 @@ nsDocument::StyleRuleRemoved(nsIStyleShe
 NS_IMETHODIMP
 nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype)
 {
   NS_ENSURE_ARG_POINTER(aDoctype);
 
   *aDoctype = nsnull;
   PRInt32 i, count;
   count = mChildren.ChildCount();
-  nsCOMPtr<nsIDOMNode> rootContentNode(do_QueryInterface(mRootContent) );
-  nsCOMPtr<nsIDOMNode> node;
-
   for (i = 0; i < count; i++) {
-    node = do_QueryInterface(mChildren.ChildAt(i));
-
-    NS_ASSERTION(node, "null element of mChildren");
-
-    // doctype can't be after the root
-    // XXX Do we really want to enforce this when we don't enforce
-    // anything else?
-    if (node == rootContentNode)
+    CallQueryInterface(mChildren.ChildAt(i), aDoctype);
+
+    if (*aDoctype) {
       return NS_OK;
-
-    if (node) {
-      PRUint16 nodeType;
-
-      node->GetNodeType(&nodeType);
-
-      if (nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE) {
-        return CallQueryInterface(node, aDoctype);
-      }
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
@@ -3010,26 +2943,24 @@ nsDocument::GetImplementation(nsIDOMDOMI
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
 {
   NS_ENSURE_ARG_POINTER(aDocumentElement);
 
-  nsresult rv = NS_OK;
-
-  if (mRootContent) {
-    rv = CallQueryInterface(mRootContent, aDocumentElement);
-    NS_ASSERTION(NS_OK == rv, "Must be a DOM Element");
-  } else {
-    *aDocumentElement = nsnull;
-  }
-
-  return rv;
+  nsIContent* root = GetRootContent();
+  if (root) {
+    return CallQueryInterface(root, aDocumentElement);
+  }
+
+  *aDocumentElement = nsnull;
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::CreateElement(const nsAString& aTagName,
                           nsIDOMElement** aReturn)
 {
   *aReturn = nsnull;
 
@@ -4299,30 +4230,30 @@ nsDocument::GetUserData(const nsAString 
 {
   return nsNodeUtils::GetUserData(this, aKey, aResult);
 }
 
 NS_IMETHODIMP
 nsDocument::LookupPrefix(const nsAString& aNamespaceURI,
                          nsAString& aPrefix)
 {
-  nsCOMPtr<nsIDOM3Node> root(do_QueryInterface(mRootContent));
+  nsCOMPtr<nsIDOM3Node> root(do_QueryInterface(GetRootContent()));
   if (root) {
     return root->LookupPrefix(aNamespaceURI, aPrefix);
   }
 
   SetDOMStringToNull(aPrefix);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::LookupNamespaceURI(const nsAString& aNamespacePrefix,
                                nsAString& aNamespaceURI)
 {
-  if (NS_FAILED(nsContentUtils::LookupNamespaceURI(mRootContent,
+  if (NS_FAILED(nsContentUtils::LookupNamespaceURI(GetRootContent(),
                                                    aNamespacePrefix,
                                                    aNamespaceURI))) {
     SetDOMStringToNull(aNamespaceURI);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -5748,19 +5679,20 @@ nsDocument::DispatchEventToWindow(nsEven
 }
 
 void
 nsDocument::OnPageShow(PRBool aPersisted)
 {
   mVisible = PR_TRUE;
   UpdateLinkMap();
   
-  if (aPersisted && mRootContent) {
+  nsIContent* root = GetRootContent();
+  if (aPersisted && root) {
     // Send out notifications that our <link> elements are attached.
-    nsRefPtr<nsContentList> links = NS_GetContentList(mRootContent,
+    nsRefPtr<nsContentList> links = NS_GetContentList(root,
                                                       nsGkAtoms::link,
                                                       kNameSpaceID_Unknown);
 
     if (links) {
       PRUint32 linkCount = links->Length(PR_TRUE);
       for (PRUint32 i = 0; i < linkCount; ++i) {
         nsCOMPtr<nsILink> link = do_QueryInterface(links->Item(i, PR_FALSE));
         if (link) {
@@ -5774,18 +5706,19 @@ nsDocument::OnPageShow(PRBool aPersisted
   DispatchEventToWindow(&event);
 }
 
 void
 nsDocument::OnPageHide(PRBool aPersisted)
 {
   // Send out notifications that our <link> elements are detached,
   // but only if this is not a full unload.
-  if (aPersisted && mRootContent) {
-    nsRefPtr<nsContentList> links = NS_GetContentList(mRootContent,
+  nsIContent* root = GetRootContent();
+  if (aPersisted && root) {
+    nsRefPtr<nsContentList> links = NS_GetContentList(root,
                                                       nsGkAtoms::link,
                                                       kNameSpaceID_Unknown);
 
     if (links) {
       PRUint32 linkCount = links->Length(PR_TRUE);
       for (PRUint32 i = 0; i < linkCount; ++i) {
         nsCOMPtr<nsILink> link = do_QueryInterface(links->Item(i, PR_FALSE));
         if (link) {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -375,16 +375,17 @@ public:
                                nsIPresShell** aInstancePtrResult);
   virtual PRBool DeleteShell(nsIPresShell* aShell);
   virtual nsIPresShell *GetPrimaryShell() const;
 
   virtual nsresult SetSubDocumentFor(nsIContent *aContent,
                                      nsIDocument* aSubDoc);
   virtual nsIDocument* GetSubDocumentFor(nsIContent *aContent) const;
   virtual nsIContent* FindContentForSubDocument(nsIDocument *aDocument) const;
+  virtual nsIContent* GetRootContentInternal() const;
 
   /**
    * Get the style sheets owned by this document.
    * These are ordered, highest priority last
    */
   virtual PRInt32 GetNumberOfStyleSheets() const;
   virtual nsIStyleSheet* GetStyleSheetAt(PRInt32 aIndex) const;
   virtual PRInt32 GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const;
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -377,17 +377,16 @@ IdAndNameMapEntryTraverse(PLDHashTable *
     cb->NoteXPCOMChild(entry->mNameContentList);
 
   cb->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(entry->mDocAllList));
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLDocument, nsDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBodyContent)
   if (tmp->mIdAndNameHashTable.ops) {
     PL_DHashTableEnumerate(&tmp->mIdAndNameHashTable,
                            IdAndNameMapEntryTraverse,
                            &cb);
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mImageMaps)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mImages)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplets)
@@ -474,18 +473,16 @@ nsHTMLDocument::ResetToURI(nsIURI *aURI,
   PrePopulateHashTables();
 
   mImages = nsnull;
   mApplets = nsnull;
   mEmbeds = nsnull;
   mLinks = nsnull;
   mAnchors = nsnull;
 
-  mBodyContent = nsnull;
-
   mImageMaps.Clear();
   mForms = nsnull;
 
   NS_ASSERTION(!mWyciwygChannel,
                "nsHTMLDocument::Reset() - Wyciwyg Channel  still exists!");
 
   mWyciwygChannel = nsnull;
 
@@ -1368,22 +1365,16 @@ nsHTMLDocument::ContentRemoved(nsIDocume
                                nsIContent* aContainer,
                                nsIContent* aChild,
                                PRInt32 aIndexInContainer)
 {
   NS_ASSERTION(aDocument == this, "unexpected doc");
 
   NS_ABORT_IF_FALSE(aChild, "Null content!");
 
-  if (aContainer == mRootContent) {
-    // Reset mBodyContent in case we got a new body.
-
-    mBodyContent = nsnull;
-  }
-
   UnregisterNamedItems(aChild);
 }
 
 void
 nsHTMLDocument::AttributeWillChange(nsIContent* aContent, PRInt32 aNameSpaceID,
                                     nsIAtom* aAttribute)
 {
   NS_ABORT_IF_FALSE(aContent, "Null content!");
@@ -1786,76 +1777,72 @@ nsHTMLDocument::GetURL(nsAString& aURL)
   CopyUTF8toUTF16(str, aURL);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody)
 {
-  NS_ENSURE_ARG_POINTER(aBody);
   *aBody = nsnull;
 
-  nsISupports* element = nsnull;
-  nsCOMPtr<nsIDOMNode> node;
-
-  if (mBodyContent || GetBodyContent()) {
+  nsIContent* body = GetBodyContent();
+
+  if (body) {
     // There is a body element, return that as the body.
-    element = mBodyContent;
+    return CallQueryInterface(body, aBody);
+  }
+
+  // The document is most likely a frameset document so look for the
+  // outer most frameset element
+  nsCOMPtr<nsIDOMNodeList> nodeList;
+
+  nsresult rv;
+  if (IsXHTML()) {
+    rv = GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"),
+                                NS_LITERAL_STRING("frameset"),
+                                getter_AddRefs(nodeList));
   } else {
-    // The document is most likely a frameset document so look for the
-    // outer most frameset element
-
-    nsCOMPtr<nsIDOMNodeList> nodeList;
-
-    nsresult rv;
-    if (IsXHTML()) {
-      rv = GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"),
-                                  NS_LITERAL_STRING("frameset"),
-                                  getter_AddRefs(nodeList));
-    } else {
-      rv = GetElementsByTagName(NS_LITERAL_STRING("frameset"),
-                                getter_AddRefs(nodeList));
-    }
-
-    if (nodeList) {
-      rv |= nodeList->Item(0, getter_AddRefs(node));
-
-      element = node;
-    }
-
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = GetElementsByTagName(NS_LITERAL_STRING("frameset"),
+                              getter_AddRefs(nodeList));
   }
-
-  return element ? CallQueryInterface(element, aBody) : NS_OK;
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMNode> node;
+  nodeList->Item(0, getter_AddRefs(node));
+
+  return node ? CallQueryInterface(node, aBody) : NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetBody(nsIDOMHTMLElement* aBody)
 {
-  nsCOMPtr<nsIContent> body(do_QueryInterface(aBody));
-  nsCOMPtr<nsIDOMElement> root(do_QueryInterface(mRootContent));
-
-  // The body element must be either a body tag or a frameset tag.
-  if (!body || !root || !(body->Tag() == nsGkAtoms::body ||
-                          body->Tag() == nsGkAtoms::frameset)) {
+  nsCOMPtr<nsIContent> newBody = do_QueryInterface(aBody);
+  nsIContent* root = GetRootContent();
+
+  // The body element must be either a body tag or a frameset tag. And we must
+  // have a html root tag, otherwise GetBody will not return the newly set
+  // body.
+  if (!newBody || !(newBody->Tag() == nsGkAtoms::body ||
+                    newBody->Tag() == nsGkAtoms::frameset) ||
+      !root || !root->IsNodeOfType(nsINode::eHTML) ||
+      root->Tag() != nsGkAtoms::html) {
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
+  nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(root);
   nsCOMPtr<nsIDOMNode> tmp;
 
-  if (mBodyContent || GetBodyContent()) {
-    root->ReplaceChild(aBody, mBodyContent, getter_AddRefs(tmp));
-  } else {
-    root->AppendChild(aBody, getter_AddRefs(tmp));
+  // Use DOM methods so that we pass through the appropriate security checks.
+  nsCOMPtr<nsIDOMNode> currentBody = do_QueryInterface(GetBodyContent());
+  if (currentBody) {
+    return rootElem->ReplaceChild(aBody, currentBody, getter_AddRefs(tmp));
   }
 
-  mBodyContent = aBody;
-
-  return PR_FALSE;
+  return rootElem->AppendChild(aBody, getter_AddRefs(tmp));
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetImages(nsIDOMHTMLCollection** aImages)
 {
   if (!mImages) {
     mImages = new nsContentList(this, nsGkAtoms::img, mDefaultNamespaceID);
     if (!mImages) {
@@ -2183,41 +2170,40 @@ nsHTMLDocument::OpenCommon(const nsACStr
       nsContentUtils::ReparentContentWrappersInScope(oldScope, newScope);
     }
   }
 
   // XXX This is a nasty workaround for a scrollbar code bug
   // (http://bugzilla.mozilla.org/show_bug.cgi?id=55334).
 
   // Hold on to our root element
-  nsCOMPtr<nsIContent> root(mRootContent);
+  nsCOMPtr<nsIContent> root = GetRootContent();
 
   if (root) {
-    PRInt32 rootIndex = mChildren.IndexOfChild(mRootContent);
+    PRInt32 rootIndex = mChildren.IndexOfChild(root);
     NS_ASSERTION(rootIndex >= 0, "Root must be in list!");
     
     PRUint32 count = root->GetChildCount();
 
     // Remove all the children from the root.
     while (count-- > 0) {
       root->RemoveChildAt(count, PR_TRUE);
     }
 
-    count = mRootContent->GetAttrCount();
+    count = root->GetAttrCount();
 
     // Remove all attributes from the root element
     while (count-- > 0) {
       const nsAttrName* name = root->GetAttrNameAt(count);
       root->UnsetAttr(name->NamespaceID(), name->LocalName(), PR_FALSE);
     }
 
     // Remove the root from the childlist
     mChildren.RemoveChildAt(rootIndex);
-
-    mRootContent = nsnull;
+    mCachedRootContent = nsnull;
   }
 
   // Call Reset(), this will now do the full reset, except removing
   // the root from the document, doing that confuses the scrollbar
   // code in mozilla since the document in the root element and all
   // the anonymous content (i.e. scrollbar elements) is set to
   // null.
 
@@ -2230,17 +2216,16 @@ nsHTMLDocument::OpenCommon(const nsACStr
 
     // Put the root element back into the document, we don't notify
     // the document about this insertion since the sink will do that
     // for us and that'll create frames for the root element and the
     // scrollbars work as expected (since the document in the root
     // element was never set to null)
 
     mChildren.AppendChild(root);
-    mRootContent = root;
   }
 
   if (IsEditingOn()) {
     // Reset() blows away all event listeners in the document, and our
     // editor relies heavily on those. Midas is turned on, to make it
     // work, re-initialize it to give it a chance to add its event
     // listeners again.
 
@@ -2610,38 +2595,39 @@ nsHTMLDocument::GetElementById(const nsA
     // mIdAndNameHashTable is live for entries in the table)
 
     return NS_OK;
   }
 
   if (!e) {
     // If IdTableIsLive(), no need to look for the element in the document,
     // since we're fully maintaining our table's state as the DOM mutates.
+    nsIContent* root = GetRootContent();
     if (!IdTableIsLive()) {
       if (IdTableShouldBecomeLive()) {
         // Just make sure our table is up to date and call this method again
         // to look up in the hashtable.
-        if (mRootContent) {
-          RegisterNamedItems(mRootContent);
+        if (root) {
+          RegisterNamedItems(root);
         }
         return GetElementById(aElementId, aReturn);
       }
 
-      if (mRootContent && CheckGetElementByIdArg(aElementId)) {
-        e = nsContentUtils::MatchElementId(mRootContent, idAtom);
+      if (root && CheckGetElementByIdArg(aElementId)) {
+        e = nsContentUtils::MatchElementId(root, idAtom);
       }
     }
 
     if (!e) {
 #ifdef DEBUG
       // No reason to call MatchElementId if !IdTableIsLive, since
       // we'd have done just that already
-      if (IdTableIsLive() && mRootContent && !aElementId.IsEmpty()) {
+      if (IdTableIsLive() && root && !aElementId.IsEmpty()) {
         nsIContent* eDebug =
-          nsContentUtils::MatchElementId(mRootContent, idAtom);
+          nsContentUtils::MatchElementId(root, idAtom);
         NS_ASSERTION(!eDebug,
                      "We got null for |e| but MatchElementId found something?");
       }
 #endif
       // There is no element with the given id in the document, cache
       // the fact that it's not in the document
       entry->FlagIDNotInDocument();
 
@@ -2768,22 +2754,21 @@ nsHTMLDocument::GetBodySize(PRInt32* aWi
 
   nsCOMPtr<nsIPresShell> shell = GetPrimaryShell();
   
   if (!shell)
     return NS_OK;
 
   // Find the <body> element: this is what we'll want to use for the
   // document's width and height values.
-  if (!mBodyContent && !GetBodyContent()) {
+  nsIContent* body = GetBodyContent();
+  if (!body) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIContent> body = do_QueryInterface(mBodyContent);
-
   // Now grab its frame
   nsIFrame* frame = shell->GetPrimaryFrameFor(body);
   if (!frame)
     return NS_OK;
   
   nsSize size = frame->GetSize();
 
   *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width);
@@ -2810,18 +2795,17 @@ nsHTMLDocument::GetHeight(PRInt32* aHeig
   return GetBodySize(&width, aHeight);
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetAlinkColor(nsAString& aAlinkColor)
 {
   aAlinkColor.Truncate();
 
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->GetALink(aAlinkColor);
   } else if (mAttrStyleSheet) {
     nscolor color;
     nsresult rv = mAttrStyleSheet->GetActiveLinkColor(color);
     if (NS_SUCCEEDED(rv)) {
       NS_RGBToHex(color, aAlinkColor);
@@ -2829,18 +2813,17 @@ nsHTMLDocument::GetAlinkColor(nsAString&
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetAlinkColor(const nsAString& aAlinkColor)
 {
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->SetALink(aAlinkColor);
   } else if (mAttrStyleSheet) {
     nsAttrValue value;
     if (value.ParseColor(aAlinkColor, this)) {
       nscolor color;
       value.GetColorValue(color);
@@ -2851,18 +2834,17 @@ nsHTMLDocument::SetAlinkColor(const nsAS
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetLinkColor(nsAString& aLinkColor)
 {
   aLinkColor.Truncate();
 
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->GetLink(aLinkColor);
   } else if (mAttrStyleSheet) {
     nscolor color;
     nsresult rv = mAttrStyleSheet->GetLinkColor(color);
     if (NS_SUCCEEDED(rv)) {
       NS_RGBToHex(color, aLinkColor);
@@ -2870,18 +2852,17 @@ nsHTMLDocument::GetLinkColor(nsAString& 
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetLinkColor(const nsAString& aLinkColor)
 {
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->SetLink(aLinkColor);
   } else if (mAttrStyleSheet) {
     nsAttrValue value;
     if (value.ParseColor(aLinkColor, this)) {
       nscolor color;
       value.GetColorValue(color);
@@ -2892,18 +2873,17 @@ nsHTMLDocument::SetLinkColor(const nsASt
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetVlinkColor(nsAString& aVlinkColor)
 {
   aVlinkColor.Truncate();
 
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->GetVLink(aVlinkColor);
   } else if (mAttrStyleSheet) {
     nscolor color;
     nsresult rv = mAttrStyleSheet->GetVisitedLinkColor(color);
     if (NS_SUCCEEDED(rv)) {
       NS_RGBToHex(color, aVlinkColor);
@@ -2911,18 +2891,17 @@ nsHTMLDocument::GetVlinkColor(nsAString&
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetVlinkColor(const nsAString& aVlinkColor)
 {
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->SetVLink(aVlinkColor);
   } else if (mAttrStyleSheet) {
     nsAttrValue value;
     if (value.ParseColor(aVlinkColor, this)) {
       nscolor color;
       value.GetColorValue(color);
@@ -2933,60 +2912,56 @@ nsHTMLDocument::SetVlinkColor(const nsAS
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetBgColor(nsAString& aBgColor)
 {
   aBgColor.Truncate();
 
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->GetBgColor(aBgColor);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetBgColor(const nsAString& aBgColor)
 {
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->SetBgColor(aBgColor);
   }
   // XXXldb And otherwise?
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetFgColor(nsAString& aFgColor)
 {
   aFgColor.Truncate();
 
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->GetText(aFgColor);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetFgColor(const nsAString& aFgColor)
 {
-  nsCOMPtr<nsIDOMHTMLBodyElement> body;
-  GetBodyElement(getter_AddRefs(body));
+  nsCOMPtr<nsIDOMHTMLBodyElement> body = do_QueryInterface(GetBodyContent());
 
   if (body) {
     body->SetText(aFgColor);
   }
   // XXXldb And otherwise?
 
   return NS_OK;
 }
@@ -3469,20 +3444,21 @@ nsHTMLDocument::ResolveName(const nsAStr
 #endif
 
     list = new nsBaseContentList();
     NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
 
     entry->mNameContentList = list;
     NS_ADDREF(entry->mNameContentList);
 
-    if (mRootContent && !aName.IsEmpty()) {
+    nsIContent* root = GetRootContent();
+    if (root && !aName.IsEmpty()) {
       // We'll never get here if !IsXHTML(), so we can just pass
       // PR_FALSE to FindNamedItems().
-      FindNamedItems(name, mRootContent, *entry, PR_FALSE);
+      FindNamedItems(name, root, *entry, PR_FALSE);
     }
   }
 
   PRUint32 length;
   list->GetLength(&length);
 
   if (length > 0) {
     if (length == 1) {
@@ -3561,52 +3537,41 @@ nsHTMLDocument::ResolveName(const nsAStr
     }
   }
 
   return NS_OK;
 }
 
 //----------------------------
 
-PRBool
+nsIContent*
 nsHTMLDocument::GetBodyContent()
 {
-  if (!mRootContent) {
-    return PR_FALSE;
-  }
-
-  PRUint32 i, child_count = mRootContent->GetChildCount();
-
-  for (i = 0; i < child_count; ++i) {
-    nsIContent *child = mRootContent->GetChildAt(i);
-    NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
-
-    if (child->NodeInfo()->Equals(nsGkAtoms::body, mDefaultNamespaceID) &&
-        child->IsNodeOfType(nsINode::eHTML)) {
-      mBodyContent = do_QueryInterface(child);
-
-      return PR_TRUE;
+  // Loop backwards because any non-elements, such as doctypes and PIs
+  // are likely to appear before the root element.
+  PRUint32 i;
+  for (i = mChildren.ChildCount(); i > 0; --i) {
+    nsIContent* html = mChildren.ChildAt(i - 1);
+    if (html->Tag() == nsGkAtoms::html &&
+        html->IsNodeOfType(nsINode::eHTML)) {
+
+      // Look for body inside html
+      for (i = html->GetChildCount(); i > 0; --i) {
+        nsIContent* body = html->GetChildAt(i - 1);
+        if (body->Tag() == nsGkAtoms::body &&
+            body->IsNodeOfType(nsINode::eHTML)) {
+          return body;
+        }
+      }
+
+      break;
     }
   }
 
-  return PR_FALSE;
-}
-
-void
-nsHTMLDocument::GetBodyElement(nsIDOMHTMLBodyElement** aBody)
-{
-  *aBody = nsnull;
-
-  if (!mBodyContent && !GetBodyContent()) {
-    // No body in this document.
-
-    return;
-  }
-
-  CallQueryInterface(mBodyContent, aBody);
+  return nsnull;
 }
 
 // forms related stuff
 
 NS_IMETHODIMP
 nsHTMLDocument::GetForms(nsIDOMHTMLCollection** aForms)
 {
   nsContentList *forms = nsHTMLDocument::GetForms();
@@ -3863,22 +3828,23 @@ nsHTMLDocument::GetDocumentAllResult(con
 
   // If we did a lookup and it failed, there are no items with this id
   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
     NS_ASSERTION(IdTableIsLive(), "should have gotten a busy entry");
 
     return NS_OK;
   }
 
-  if (!mRootContent) {
+  nsIContent* root = GetRootContent();
+  if (!root) {
     return NS_OK;
   }
 
   if (!entry->mDocAllList) {
-    entry->mDocAllList = new nsContentList(mRootContent, DocAllResultMatch,
+    entry->mDocAllList = new nsContentList(root, DocAllResultMatch,
                                            nsnull, nsnull, PR_TRUE, id);
     NS_ENSURE_TRUE(entry->mDocAllList, NS_ERROR_OUT_OF_MEMORY);
   }
 
   // Check if there are more than 1 entries. Do this by getting the second one
   // rather than the length since getting the length always requires walking
   // the entire document.
 
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -236,18 +236,17 @@ protected:
                            nsIAtom* aAtom, void* aData);
   static PRBool MatchAnchors(nsIContent *aContent, PRInt32 aNamespaceID,
                              nsIAtom* aAtom, void* aData);
   static PRBool MatchNameAttribute(nsIContent* aContent, PRInt32 aNamespaceID,
                                    nsIAtom* aAtom, void* aData);
 
   static void DocumentWriteTerminationFunc(nsISupports *aRef);
 
-  PRBool GetBodyContent();
-  void GetBodyElement(nsIDOMHTMLBodyElement** aBody);
+  nsIContent* GetBodyContent();
 
   void GetDomainURI(nsIURI **uri);
 
   nsresult WriteCommon(const nsAString& aText,
                        PRBool aNewlineTerminate);
   nsresult ScriptWriteCommon(PRBool aNewlineTerminate);
   nsresult OpenCommon(const nsACString& aContentType, PRBool aReplace);
 
@@ -327,18 +326,16 @@ protected:
   // finishes processing that script.
   PRUint32 mWriteLevel;
 
   nsSmallVoidArray mPendingScripts;
 
   // Load flags of the document's channel
   PRUint32 mLoadFlags;
 
-  nsCOMPtr<nsIDOMNode> mBodyContent;
-
   PRPackedBool mIsFrameset;
 
   PRPackedBool mTooDeepWriteRecursion;
 
   PRBool IdTableIsLive() const {
     // live if we've had over 63 misses
     return (mIdMissCount & 0x40) != 0;
   }
--- a/content/html/document/src/nsImageDocument.cpp
+++ b/content/html/document/src/nsImageDocument.cpp
@@ -349,17 +349,17 @@ nsImageDocument::SetScriptGlobalObject(n
                                 PR_FALSE);
   }
 
   // Set the script global object on the superclass before doing
   // anything that might require it....
   nsHTMLDocument::SetScriptGlobalObject(aScriptGlobalObject);
 
   if (aScriptGlobalObject) {
-    if (!mRootContent) {
+    if (!GetRootContent()) {
       // Create synthetic document
       nsresult rv = CreateSyntheticDocument();
       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document");
 
       target = do_QueryInterface(mImageContent);
       target->AddEventListener(NS_LITERAL_STRING("click"), this, PR_FALSE);
     }
 
@@ -563,17 +563,17 @@ nsImageDocument::HandleEvent(nsIDOMEvent
 
 nsresult
 nsImageDocument::CreateSyntheticDocument()
 {
   // Synthesize an html document that refers to the image
   nsresult rv = nsMediaDocument::CreateSyntheticDocument();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIContent> body = do_QueryInterface(mBodyContent);
+  nsIContent* body = GetBodyContent();
   if (!body) {
     NS_WARNING("no body on image document!");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsINodeInfo> nodeInfo;
   rv = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nsnull,
                                      kNameSpaceID_None,
@@ -613,17 +613,17 @@ nsImageDocument::CheckOverflowing(PRBool
     nsIPresShell *shell = GetPrimaryShell();
     if (!shell) {
       return NS_OK;
     }
 
     nsPresContext *context = shell->GetPresContext();
     nsRect visibleArea = context->GetVisibleArea();
 
-    nsCOMPtr<nsIContent> content = do_QueryInterface(mBodyContent);
+    nsIContent* content = GetBodyContent();
     if (!content) {
       NS_WARNING("no body on image document!");
       return NS_ERROR_FAILURE;
     }
 
     nsRefPtr<nsStyleContext> styleContext =
       context->StyleSet()->ResolveStyleFor(content, nsnull);
 
--- a/content/html/document/src/nsMediaDocument.cpp
+++ b/content/html/document/src/nsMediaDocument.cpp
@@ -251,17 +251,16 @@ nsMediaDocument::CreateSyntheticDocument
                                      kNameSpaceID_None,
                                      getter_AddRefs(nodeInfo));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<nsGenericHTMLElement> body = NS_NewHTMLBodyElement(nodeInfo);
   if (!body) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
-  mBodyContent = do_QueryInterface(body);
 
   root->AppendChildTo(body, PR_FALSE);
 
   return NS_OK;
 }
 
 nsresult
 nsMediaDocument::StartLayout()
--- a/content/html/document/src/nsPluginDocument.cpp
+++ b/content/html/document/src/nsPluginDocument.cpp
@@ -222,17 +222,17 @@ nsPluginDocument::CreateSyntheticPluginD
     }
   }
 
   // make our generic document
   nsresult rv = nsMediaDocument::CreateSyntheticDocument();
   NS_ENSURE_SUCCESS(rv, rv);
   // then attach our plugin
 
-  nsCOMPtr<nsIContent> body = do_QueryInterface(mBodyContent);
+  nsIContent* body = GetBodyContent();
   if (!body) {
     NS_WARNING("no body on plugin document!");
     return NS_ERROR_FAILURE;
   }
 
   // remove margins from body
   NS_NAMED_LITERAL_STRING(zero, "0");
   body->SetAttr(kNameSpaceID_None, nsGkAtoms::marginwidth, zero, PR_FALSE);
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -752,18 +752,17 @@ nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix 
     ancestorCount++;
   }
 
   // now account for our offset
 
   if (!ancestorCTM) {
     // we didn't find an SVG ancestor
     float s=1, x=0, y=0;
-    if (ownerDoc &&
-        ownerDoc->GetRootContent() == static_cast<nsIContent*>(this)) {
+    if (IsRoot()) {
       // we're the root element. get our currentScale and currentTranslate vals
       mCurrentScale->GetValue(&s);
       mCurrentTranslate->GetX(&x);
       mCurrentTranslate->GetY(&y);
     }
     else {
       // we're inline in some non-SVG content. get our offset from the root
       GetOffsetToAncestor(nsnull, x, y);
@@ -862,18 +861,17 @@ nsSVGSVGElement::GetScreenCTM(nsIDOMSVGM
     ancestorCount++;
   }
 
   // now account for our offset
 
   if (!ancestorScreenCTM) {
     // we didn't find an SVG ancestor
     float s=1, x=0, y=0;
-    if (ownerDoc &&
-        ownerDoc->GetRootContent() == static_cast<nsIContent*>(this)) {
+    if (IsRoot()) {
       // we're the root element. get our currentScale and currentTranslate vals
       mCurrentScale->GetValue(&s);
       mCurrentTranslate->GetX(&x);
       mCurrentTranslate->GetY(&y);
     }
     else {
       // we're inline in some non-SVG content. get our offset from the root
       GetOffsetToAncestor(nsnull, x, y);
@@ -991,18 +989,17 @@ nsSVGSVGElement::SetCurrentScaleTranslat
   mCurrentTranslate->SetY(y);
   mDispatchEvent = PR_TRUE;
 
   // now dispatch an SVGZoom event if we are the root element
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
     NS_ASSERTION(presShell, "no presShell");
-    if (presShell &&
-        doc->GetRootContent() == static_cast<nsIContent*>(this)) {
+    if (presShell && IsRoot()) {
       nsEventStatus status = nsEventStatus_eIgnore;
       nsGUIEvent event(PR_TRUE, NS_SVG_ZOOM, 0);
       event.eventStructType = NS_SVGZOOM_EVENT;
       presShell->HandleDOMEventWithTarget(this, &event, &status);
     }
   }
   return NS_OK;
 }
@@ -1016,18 +1013,17 @@ nsSVGSVGElement::SetCurrentTranslate(flo
   mCurrentTranslate->SetY(y);
   mDispatchEvent = PR_TRUE;
 
   // now dispatch an SVGScroll event if we are the root element
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
     NS_ASSERTION(presShell, "no presShell");
-    if (presShell &&
-        doc->GetRootContent() == static_cast<nsIContent*>(this)) {
+    if (presShell && IsRoot()) {
       nsEventStatus status = nsEventStatus_eIgnore;
       nsEvent event(PR_TRUE, NS_SVG_SCROLL);
       event.eventStructType = NS_SVG_EVENT;
       presShell->HandleDOMEventWithTarget(this, &event, &status);
     }
   }
   return NS_OK;
 }
@@ -1155,32 +1151,30 @@ nsSVGSVGElement::DidModifySVGObservable 
   NS_ASSERTION(presShell, "no presShell");
   if (!presShell) return NS_ERROR_FAILURE;
 
   // If currentScale or currentTranslate has changed, we are the root element,
   // and the changes wasn't caused by SetCurrent[Scale]Translate then we must
   // dispatch an SVGZoom or SVGScroll DOM event before repainting
   nsCOMPtr<nsIDOMSVGNumber> n = do_QueryInterface(observable);
   if (n && n==mCurrentScale) {
-    if (mDispatchEvent &&
-        doc->GetRootContent() == static_cast<nsIContent*>(this)) {
+    if (mDispatchEvent && IsRoot()) {
       nsEventStatus status = nsEventStatus_eIgnore;
       nsGUIEvent event(PR_TRUE, NS_SVG_ZOOM, 0);
       event.eventStructType = NS_SVGZOOM_EVENT;
       presShell->HandleDOMEventWithTarget(this, &event, &status);
     }
     else {
       return NS_OK;  // we don't care about currentScale changes on non-root
     }
   }
   else {
     nsCOMPtr<nsIDOMSVGPoint> p = do_QueryInterface(observable);
     if (p && p==mCurrentTranslate) {
-      if (mDispatchEvent &&
-          doc->GetRootContent() == static_cast<nsIContent*>(this)) {
+      if (mDispatchEvent && IsRoot()) {
         nsEventStatus status = nsEventStatus_eIgnore;
         nsEvent event(PR_TRUE, NS_SVG_SCROLL);
         event.eventStructType = NS_SVG_EVENT;
         presShell->HandleDOMEventWithTarget(this, &event, &status);
       }
       else {
         return NS_OK;  // we don't care about currentScale changes on non-root
       }
--- a/content/svg/content/src/nsSVGSVGElement.h
+++ b/content/svg/content/src/nsSVGSVGElement.h
@@ -167,16 +167,22 @@ public:
   }
 
 protected:
   // nsSVGElement overrides
   PRBool IsEventName(nsIAtom* aName);
 
   // implementation helpers:
   void GetOffsetToAncestor(nsIContent* ancestor, float &x, float &y);
+  PRBool IsRoot() {
+    NS_ASSERTION((IsInDoc() && !GetParent()) ==
+                 (GetOwnerDoc() && (GetOwnerDoc()->GetRootContent() == this)),
+                 "Can't determine if we're root");
+    return IsInDoc() && !GetParent();
+  }
 
   // invalidate viewbox -> viewport xform & inform frames
   void InvalidateTransformNotifyFrame();
 
   virtual LengthAttributesInfo GetLengthInfo();
 
   enum { X, Y, WIDTH, HEIGHT };
   nsSVGLength2 mLengthAttributes[4];
--- a/content/svg/document/src/nsSVGDocument.cpp
+++ b/content/svg/document/src/nsSVGDocument.cpp
@@ -116,23 +116,20 @@ nsSVGDocument::GetURL(nsAString& aURL)
 
   return NS_OK;
 }
 
 /* readonly attribute SVGSVGElement rootElement; */
 NS_IMETHODIMP
 nsSVGDocument::GetRootElement(nsIDOMSVGSVGElement** aRootElement)
 {
-  NS_ENSURE_ARG_POINTER(aRootElement);
+  *aRootElement = nsnull;
+  nsIContent* root = GetRootContent();
 
-  if (mRootContent)
-    return CallQueryInterface(mRootContent, aRootElement);
-
-  *aRootElement = nsnull;
-  return NS_OK;
+  return root ? CallQueryInterface(root, aRootElement) : NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////
 // Exported creation functions
 
 nsresult
 NS_NewSVGDocument(nsIDocument** aInstancePtrResult)
 {
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -530,17 +530,17 @@ nsXMLDocument::Load(const nsAString& aUr
 
     mLoopingForSyncLoad = PR_TRUE;
     while (mLoopingForSyncLoad) {
       if (!NS_ProcessNextEvent(thread))
         break;
     }
 
     // We set return to true unless there was a parsing error
-    nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mRootContent);
+    nsCOMPtr<nsIDOMNode> node = do_QueryInterface(GetRootContent());
     if (node) {
       nsAutoString name, ns;      
       if (NS_SUCCEEDED(node->GetLocalName(name)) &&
           name.EqualsLiteral("parsererror") &&
           NS_SUCCEEDED(node->GetNamespaceURI(ns)) &&
           ns.EqualsLiteral("http://www.mozilla.org/newlayout/xml/parsererror.xml")) {
         //return is already false
       } else {
@@ -655,24 +655,25 @@ nsXMLDocument::GetElementById(const nsAS
   *aReturn = nsnull;
 
   if (!CheckGetElementByIdArg(aElementId))
     return NS_OK;
 
   // If we tried to load a document and something went wrong, we might not have
   // root content. This can happen when you do document.load() and the document
   // to load is not XML, for example.
-  if (!mRootContent)
+  nsIContent* root = GetRootContent();
+  if (!root)
     return NS_OK;
 
   // XXX For now, we do a brute force search of the content tree.
   // We should come up with a more efficient solution.
   // Note that content is *not* refcounted here, so do *not* release it!
   nsIContent *content =
-    nsContentUtils::MatchElementId(mRootContent, aElementId);
+    nsContentUtils::MatchElementId(root, aElementId);
 
   if (!content) {
     return NS_OK;
   }
 
   return CallQueryInterface(content, aReturn);
 }
 
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -1870,17 +1870,17 @@ nsXULDocument::Init()
 
     return NS_OK;
 }
 
 
 nsresult
 nsXULDocument::StartLayout(void)
 {
-    if (!mRootContent) {
+    if (!GetRootContent()) {
 #ifdef PR_LOGGING
         if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) {
             nsCAutoString urlspec;
             mDocumentURI->GetSpec(urlspec);
 
             PR_LOG(gXULLog, PR_LOG_WARNING,
                    ("xul: unable to layout '%s'; no root content", urlspec.get()));
         }
@@ -3041,19 +3041,19 @@ nsXULDocument::DoneWalking()
         // causes ResumeWalk() to be reentered, we'll take the other branch of
         // the |if (!mDocumentLoaded)| check above and since
         // mInitialLayoutComplete will be false will follow the else branch
         // there too.  See the big comment there for how such reentry can
         // happen.
         mDocumentLoaded = PR_TRUE;
 
         nsAutoString title;
-        if (mRootContent) {
-            mRootContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title,
-                                  title);
+        nsIContent* root = GetRootContent();
+        if (root) {
+            root->GetAttr(kNameSpaceID_None, nsGkAtoms::title, title);
         }
         SetTitle(title);
 
         // Before starting layout, check whether we're a toplevel chrome
         // window.  If we are, set our chrome flags now, so that we don't have
         // to restyle the whole frame tree after StartLayout.
         nsCOMPtr<nsISupports> container = GetContainer();
         nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(container);
@@ -3724,21 +3724,22 @@ nsXULDocument::OverlayForwardReference::
     if (shell)
         shell->GetDidInitialReflow(&notify);
 
     nsAutoString id;
     mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
     if (id.IsEmpty()) {
         // mOverlay is a direct child of <overlay> and has no id.
         // Insert it under the root element in the base document.
-        if (!mDocument->mRootContent) {
+        nsIContent* root = mDocument->GetRootContent();
+        if (!root) {
             return eResolve_Error;
         }
 
-        rv = mDocument->InsertElement(mDocument->mRootContent, mOverlay, notify);
+        rv = mDocument->InsertElement(root, mOverlay, notify);
         if (NS_FAILED(rv)) return eResolve_Error;
 
         target = mOverlay;
     }
     else {
         // The hook-up element has an id, try to match it with an element
         // with the same id in the base document.
         nsCOMPtr<nsIDOMElement> domtarget;