Bug 457716 - XUL shouldn't always traverse all the prototypes, r+sr=peterv
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Wed, 08 Oct 2008 14:41:52 +0300
changeset 20136 ae1b32380e6501b4b63f2497a850088d2dcbc669
parent 20135 3447eeec927102e5204d71f7137c1de5b6b51c2e
child 20137 1fde919dfef3b5ee277e0512ff23bef61a16a0a6
push idunknown
push userunknown
push dateunknown
bugs457716
milestone1.9.1b2pre
Bug 457716 - XUL shouldn't always traverse all the prototypes, r+sr=peterv
content/xbl/src/nsXBLContentSink.cpp
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
content/xul/document/src/nsXULContentSink.cpp
content/xul/document/src/nsXULContentSink.h
content/xul/document/src/nsXULDocument.cpp
content/xul/document/src/nsXULPrototypeDocument.cpp
content/xul/document/src/nsXULPrototypeDocument.h
dom/src/base/nsJSEnvironment.cpp
--- a/content/xbl/src/nsXBLContentSink.cpp
+++ b/content/xbl/src/nsXBLContentSink.cpp
@@ -875,36 +875,28 @@ nsXBLContentSink::CreateElement(const PR
 #endif
     return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
                                            aLineNumber, aResult,
                                            aAppendContent, aFromParser);
 #ifdef MOZ_XUL
   }
 
   *aAppendContent = PR_TRUE;
-  nsXULPrototypeElement* prototype = new nsXULPrototypeElement();
+  nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
   if (!prototype)
     return NS_ERROR_OUT_OF_MEMORY;
 
   prototype->mNodeInfo = aNodeInfo;
   // XXX - we need to do exactly what the XUL content-sink does (eg,
   // look for 'type', 'version' etc attributes)
   prototype->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
 
   AddAttributesToXULPrototype(aAtts, aAttsCount, prototype);
 
-  nsresult rv = nsXULElement::Create(prototype, mDocument, PR_FALSE, aResult);
-
-  // XUL prototype elements start with a refcnt of 1 to represent
-  // ownership by the XUL prototype document.  In our case we have no
-  // prototype document, so release that reference.  The Create call
-  // above took a reference.
-  prototype->Release();
-
-  return rv;
+  return nsXULElement::Create(prototype, mDocument, PR_FALSE, aResult);
 #endif
 }
 
 nsresult 
 nsXBLContentSink::AddAttributes(const PRUnichar** aAtts,
                                 nsIContent* aContent)
 {
   if (aContent->IsNodeOfType(nsINode::eXUL))
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -149,16 +149,17 @@
 #include "nsIFrame.h"
 #include "nsNodeInfoManager.h"
 #include "nsXBLBinding.h"
 #include "nsEventDispatcher.h"
 #include "nsPresShellIterator.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIDOMXULCommandEvent.h"
 #include "nsIDOMNSEvent.h"
+#include "nsCCUncollectableMarker.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 \
@@ -368,18 +369,23 @@ NS_NewXULElement(nsIContent** aResult, n
 }
 
 //----------------------------------------------------------------------
 // nsISupports interface
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement,
                                                   nsGenericElement)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mPrototype,
-                                                  nsXULPrototypeElement)
+    nsIDocument* currentDoc = tmp->GetCurrentDoc();
+    if (currentDoc && nsCCUncollectableMarker::InGeneration(
+                          currentDoc->GetMarkedCCGeneration())) {
+        return NS_OK;
+    }
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mPrototype,
+                                                    nsXULPrototypeElement)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(nsXULElement, nsGenericElement)
 NS_IMPL_RELEASE_INHERITED(nsXULElement, nsGenericElement)
 
 NS_IMETHODIMP
 nsXULElement::QueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
@@ -2614,18 +2620,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE
             static_cast<nsXULPrototypeElement*>(tmp);
         cb.NoteXPCOMChild(elem->mNodeInfo);
         PRUint32 i;
         for (i = 0; i < elem->mNumAttributes; ++i) {
             const nsAttrName& name = elem->mAttributes[i].mName;
             if (!name.IsAtom())
                 cb.NoteXPCOMChild(name.NodeInfo());
         }
-        for (i = 0; i < elem->mNumChildren; ++i) {
-            NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i],
+        for (i = 0; i < elem->mChildren.Length(); ++i) {
+            NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i].get(),
                                                          nsXULPrototypeNode,
                                                          "mChildren[i]")
         }
     }
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BEGIN(nsXULPrototypeNode)
     if (tmp->mType == nsXULPrototypeNode::eType_Element) {
@@ -2713,19 +2719,19 @@ nsXULPrototypeElement::Serialize(nsIObje
         NS_ASSERTION(index >= 0, "unknown nsINodeInfo index");
         rv |= aStream->Write32(index);
 
         mAttributes[i].mValue.ToString(attributeValue);
         rv |= aStream->WriteWStringZ(attributeValue.get());
     }
 
     // Now write children
-    rv |= aStream->Write32(PRUint32(mNumChildren));
-    for (i = 0; i < mNumChildren; i++) {
-        nsXULPrototypeNode* child = mChildren[i];
+    rv |= aStream->Write32(PRUint32(mChildren.Length()));
+    for (i = 0; i < mChildren.Length(); i++) {
+        nsXULPrototypeNode* child = mChildren[i].get();
         switch (child->mType) {
         case eType_Element:
         case eType_Text:
         case eType_PI:
             rv |= child->Serialize(aStream, aGlobal, aNodeInfos);
             break;
         case eType_Script:
             rv |= aStream->Write32(child->mType);
@@ -2798,30 +2804,27 @@ nsXULPrototypeElement::Deserialize(nsIOb
             mAttributes[i].mName.SetTo(ni);
 
             rv |= aStream->ReadString(attributeValue);
             rv |= SetAttrAt(i, attributeValue, aDocumentURI);
         }
     }
 
     rv |= aStream->Read32(&number);
-    mNumChildren = PRInt32(number);
-
-    if (mNumChildren > 0) {
-        mChildren = new nsXULPrototypeNode*[mNumChildren];
-        if (! mChildren)
+    PRUint32 numChildren = PRInt32(number);
+
+    if (numChildren > 0) {
+        if (!mChildren.SetCapacity(numChildren))
             return NS_ERROR_OUT_OF_MEMORY;
 
-        memset(mChildren, 0, sizeof(nsXULPrototypeNode*) * mNumChildren);
-
-        for (i = 0; i < mNumChildren; i++) {
+        for (i = 0; i < numChildren; i++) {
             rv |= aStream->Read32(&number);
             Type childType = (Type)number;
 
-            nsXULPrototypeNode* child = nsnull;
+            nsRefPtr<nsXULPrototypeNode> child;
 
             switch (childType) {
             case eType_Element:
                 child = new nsXULPrototypeElement();
                 if (! child)
                     return NS_ERROR_OUT_OF_MEMORY;
                 child->mType = childType;
 
@@ -2869,17 +2872,17 @@ nsXULPrototypeElement::Deserialize(nsIOb
                 // If we failed to deserialize, consider deleting 'script'?
                 break;
             }
             default:
                 NS_NOTREACHED("Unexpected child type!");
                 rv = NS_ERROR_UNEXPECTED;
             }
 
-            mChildren[i] = child;
+            mChildren.AppendElement(child);
 
             // Oh dear. Something failed during the deserialization.
             // We don't know what.  But likely consequences of failed
             // deserializations included calls to |AbortFastLoads| which
             // shuts down the FastLoadService and closes our streams.
             // If that happens, next time through this loop, we die a messy
             // death. So, let's just fail now, and propagate that failure
             // upward so that the ChromeProtocolHandler knows it can't use
@@ -2979,17 +2982,16 @@ nsXULPrototypeScript::nsXULPrototypeScri
     : nsXULPrototypeNode(eType_Script),
       mLineNo(aLineNo),
       mSrcLoading(PR_FALSE),
       mOutOfLine(PR_TRUE),
       mSrcLoadWaiters(nsnull),
       mLangVersion(aVersion),
       mScriptObject(aLangID)
 {
-    NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
     NS_ASSERTION(aLangID != nsIProgrammingLanguage::UNKNOWN,
                  "The language ID must be known and constant");
 }
 
 
 nsXULPrototypeScript::~nsXULPrototypeScript()
 {
     UnlinkJSObjects();
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -82,16 +82,18 @@
 class nsIDocument;
 class nsString;
 class nsIDocShell;
 class nsICSSStyleRule;
 
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 class nsIScriptGlobalObjectOwner;
+class nsXULPrototypeNode;
+typedef nsTArray<nsRefPtr<nsXULPrototypeNode> > nsPrototypeArray;
 
 static NS_DEFINE_CID(kCSSParserCID, NS_CSSPARSER_CID);
 
 ////////////////////////////////////////////////////////////////////////
 
 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
 #define XUL_PROTOTYPE_ATTRIBUTE_METER(counter) (nsXULPrototypeAttribute::counter++)
 #else
@@ -221,86 +223,75 @@ public:
     /**
      * The prototype document must call ReleaseSubtree when it is going
      * away.  This makes the parents through the tree stop owning their
      * children, whether or not the parent's reference count is zero.
      * Individual elements may still own individual prototypes, but
      * those prototypes no longer remember their children to allow them
      * to be constructed.
      */
-    virtual void ReleaseSubtree() { Release(); }
+    virtual void ReleaseSubtree() { }
 
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsXULPrototypeNode)
 
 protected:
     nsXULPrototypeNode(Type aType)
-        : mType(aType), mRefCnt(1) {}
+        : mType(aType) {}
 };
 
 class nsXULPrototypeElement : public nsXULPrototypeNode
 {
 public:
     nsXULPrototypeElement()
         : nsXULPrototypeNode(eType_Element),
-          mNumChildren(0),
-          mChildren(nsnull),
           mNumAttributes(0),
           mAttributes(nsnull),
           mHasIdAttribute(PR_FALSE),
           mHasClassAttribute(PR_FALSE),
           mHasStyleAttribute(PR_FALSE),
           mHoldsScriptObject(PR_FALSE),
           mScriptTypeID(nsIProgrammingLanguage::UNKNOWN)
     {
-        NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
     }
 
     virtual ~nsXULPrototypeElement()
     {
         UnlinkJSObjects();
         Unlink();
-        NS_ASSERTION(!mChildren && mNumChildren == 0,
-                     "ReleaseSubtree not called");
     }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() { return "nsXULPrototypeElement"; }
     virtual PRUint32 ClassSize() { return sizeof(*this); }
 #endif
 
     virtual void ReleaseSubtree()
     {
-      if (mChildren) {
-        for (PRInt32 i = mNumChildren-1; i >= 0; i--) {
-          if (mChildren[i])
-            mChildren[i]->ReleaseSubtree();
+        for (PRInt32 i = mChildren.Length() - 1; i >= 0; i--) {
+            if (mChildren[i].get())
+                mChildren[i]->ReleaseSubtree();
         }
-        mNumChildren = 0;
-        delete[] mChildren;
-        mChildren = nsnull;
-      }
-
-      nsXULPrototypeNode::ReleaseSubtree();
+        mChildren.Clear();
+        nsXULPrototypeNode::ReleaseSubtree();
     }
 
     virtual nsresult Serialize(nsIObjectOutputStream* aStream,
                                nsIScriptGlobalObject* aGlobal,
                                const nsCOMArray<nsINodeInfo> *aNodeInfos);
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
                                  nsIScriptGlobalObject* aGlobal,
                                  nsIURI* aDocumentURI,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos);
 
     nsresult SetAttrAt(PRUint32 aPos, const nsAString& aValue, nsIURI* aDocumentURI);
 
     void UnlinkJSObjects();
     void Unlink();
 
-    PRUint32                 mNumChildren;
-    nsXULPrototypeNode**     mChildren;           // [OWNER]
+    nsPrototypeArray         mChildren;
 
     nsCOMPtr<nsINodeInfo>    mNodeInfo;           // [OWNER]
 
     PRUint32                 mNumAttributes;
     nsXULPrototypeAttribute* mAttributes;         // [OWNER]
     
     PRPackedBool             mHasIdAttribute:1;
     PRPackedBool             mHasClassAttribute:1;
@@ -411,17 +402,16 @@ public:
 };
 
 class nsXULPrototypeText : public nsXULPrototypeNode
 {
 public:
     nsXULPrototypeText()
         : nsXULPrototypeNode(eType_Text)
     {
-        NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
     }
 
     virtual ~nsXULPrototypeText()
     {
     }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() { return "nsXULPrototypeText"; }
@@ -440,17 +430,16 @@ public:
 };
 
 class nsXULPrototypePI : public nsXULPrototypeNode
 {
 public:
     nsXULPrototypePI()
         : nsXULPrototypeNode(eType_PI)
     {
-        NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
     }
 
     virtual ~nsXULPrototypePI()
     {
     }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() { return "nsXULPrototypePI"; }
--- a/content/xul/document/src/nsXULContentSink.cpp
+++ b/content/xul/document/src/nsXULContentSink.cpp
@@ -143,28 +143,28 @@ XULContentSinkImpl::ContextStack::Pop(St
     *aState = entry->mState;
     delete entry;
 
     return NS_OK;
 }
 
 
 nsresult
-XULContentSinkImpl::ContextStack::GetTopNode(nsXULPrototypeNode** aNode)
+XULContentSinkImpl::ContextStack::GetTopNode(nsRefPtr<nsXULPrototypeNode>& aNode)
 {
     if (mDepth == 0)
         return NS_ERROR_UNEXPECTED;
 
-    *aNode = mTop->mNode;
+    aNode = mTop->mNode;
     return NS_OK;
 }
 
 
 nsresult
-XULContentSinkImpl::ContextStack::GetTopChildren(nsVoidArray** aChildren)
+XULContentSinkImpl::ContextStack::GetTopChildren(nsPrototypeArray** aChildren)
 {
     if (mDepth == 0)
         return NS_ERROR_UNEXPECTED;
 
     *aChildren = &(mTop->mChildren);
     return NS_OK;
 }
 
@@ -172,59 +172,47 @@ nsresult
 XULContentSinkImpl::ContextStack::GetTopNodeScriptType(PRUint32 *aScriptType)
 {
     if (mDepth == 0)
         return NS_ERROR_UNEXPECTED;
 
     // This would be much simpler if nsXULPrototypeNode itself
     // stored the language ID - but text elements don't need it!
     nsresult rv = NS_OK;
-    nsXULPrototypeNode* node;
-    rv = GetTopNode(&node);
+    nsRefPtr<nsXULPrototypeNode> node;
+    rv = GetTopNode(node);
     if (NS_FAILED(rv)) return rv;
     switch (node->mType) {
         case nsXULPrototypeNode::eType_Element: {
-            nsXULPrototypeElement *parent = \
-                reinterpret_cast<nsXULPrototypeElement*>(node);
+            nsXULPrototypeElement *parent =
+                reinterpret_cast<nsXULPrototypeElement*>(node.get());
             *aScriptType = parent->mScriptTypeID;
             break;
         }
         case nsXULPrototypeNode::eType_Script: {
-            nsXULPrototypeScript *parent = \
-                reinterpret_cast<nsXULPrototypeScript*>(node);
+            nsXULPrototypeScript *parent =
+                reinterpret_cast<nsXULPrototypeScript*>(node.get());
             *aScriptType = parent->mScriptObject.mLangID;
             break;
         }
         default: {
             NS_WARNING("Unexpected parent node type");
             rv = NS_ERROR_UNEXPECTED;
         }
     }
     return rv;
 }
 
 void
 XULContentSinkImpl::ContextStack::Clear()
 {
   Entry *cur = mTop;
   while (cur) {
-    // Release all children (with their descendants) that haven't been added to
-    // their parents.
-    for (PRInt32 i = cur->mChildren.Count() - 1; i >= 0; --i) {
-      nsXULPrototypeNode* child =
-          reinterpret_cast<nsXULPrototypeNode*>(cur->mChildren.ElementAt(i));
-
-      child->ReleaseSubtree();
-    }
-
     // Release the root element (and its descendants).
     Entry *next = cur->mNext;
-    if (!next)
-      cur->mNode->ReleaseSubtree();
-
     delete cur;
     cur = next;
   }
 
   mTop = nsnull;
   mDepth = 0;
 }
 
@@ -411,24 +399,24 @@ XULContentSinkImpl::FlushText(PRBool aCr
         // Don't do anything if there's no text to create a node from, or
         // if they've told us not to create a text node
         if (! mTextLength)
             break;
 
         if (! aCreateTextNode)
             break;
 
-        nsXULPrototypeNode* node;
-        rv = mContextStack.GetTopNode(&node);
+        nsRefPtr<nsXULPrototypeNode> node;
+        rv = mContextStack.GetTopNode(node);
         if (NS_FAILED(rv)) return rv;
 
         PRBool stripWhitespace = PR_FALSE;
         if (node->mType == nsXULPrototypeNode::eType_Element) {
             nsINodeInfo *nodeInfo =
-                static_cast<nsXULPrototypeElement*>(node)->mNodeInfo;
+                static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo;
 
             if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL))
                 stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) &&
                                   !nodeInfo->Equals(nsGkAtoms::description);
         }
 
         // Don't bother if there's nothing but whitespace.
         if (stripWhitespace && ! IsDataInBuffer(mText, mTextLength))
@@ -442,17 +430,17 @@ XULContentSinkImpl::FlushText(PRBool aCr
         if (! text)
             return NS_ERROR_OUT_OF_MEMORY;
 
         text->mValue.Assign(mText, mTextLength);
         if (stripWhitespace)
             text->mValue.Trim(" \t\n\r");
 
         // hook it up
-        nsVoidArray* children;
+        nsPrototypeArray* children = nsnull;
         rv = mContextStack.GetTopChildren(&children);
         if (NS_FAILED(rv)) return rv;
 
         // transfer ownership of 'text' to the children array
         children->AppendElement(text);
     } while (0);
 
     // Reset our text buffer
@@ -570,55 +558,52 @@ XULContentSinkImpl::HandleStartElement(c
 NS_IMETHODIMP 
 XULContentSinkImpl::HandleEndElement(const PRUnichar *aName)
 {
     // Never EVER return anything but NS_OK or
     // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow
     // the parser's little mind all over the planet.
     nsresult rv;
 
-    nsXULPrototypeNode* node;
-    rv = mContextStack.GetTopNode(&node);
+    nsRefPtr<nsXULPrototypeNode> node;
+    rv = mContextStack.GetTopNode(node);
 
     if (NS_FAILED(rv)) {
       return NS_OK;
     }
 
     switch (node->mType) {
     case nsXULPrototypeNode::eType_Element: {
         // Flush any text _now_, so that we'll get text nodes created
         // before popping the stack.
         FlushText();
 
         // Pop the context stack and do prototype hookup.
-        nsVoidArray* children;
+        nsPrototypeArray* children = nsnull;
         rv = mContextStack.GetTopChildren(&children);
         if (NS_FAILED(rv)) return rv;
 
         nsXULPrototypeElement* element =
-            reinterpret_cast<nsXULPrototypeElement*>(node);
+          static_cast<nsXULPrototypeElement*>(node.get());
 
-        PRInt32 count = children->Count();
+        PRInt32 count = children->Length();
         if (count) {
-            element->mChildren = new nsXULPrototypeNode*[count];
-            if (! element->mChildren)
+            if (!element->mChildren.SetCapacity(count))
                 return NS_ERROR_OUT_OF_MEMORY;
 
-            for (PRInt32 i = count - 1; i >= 0; --i)
-                element->mChildren[i] =
-                    reinterpret_cast<nsXULPrototypeNode*>(children->ElementAt(i));
+            for (PRInt32 i = 0; i < count; ++i)
+                element->mChildren.AppendElement(children->ElementAt(i));
 
-            element->mNumChildren = count;
         }
     }
     break;
 
     case nsXULPrototypeNode::eType_Script: {
         nsXULPrototypeScript* script =
-            static_cast<nsXULPrototypeScript*>(node);
+            static_cast<nsXULPrototypeScript*>(node.get());
 
         // If given a src= attribute, we must ignore script tag content.
         if (! script->mSrcURI && ! script->mScriptObject.mObject) {
             nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
 
             script->mOutOfLine = PR_FALSE;
             if (doc)
                 script->Compile(mText, mTextLength, mDocumentURL,
@@ -644,17 +629,17 @@ XULContentSinkImpl::HandleEndElement(con
         NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element, "root is not an element");
         if (node->mType != nsXULPrototypeNode::eType_Element)
             return NS_ERROR_UNEXPECTED;
 
         // Now that we're done parsing, set the prototype document's
         // root element. This transfers ownership of the prototype
         // element tree to the prototype document.
         nsXULPrototypeElement* element =
-            static_cast<nsXULPrototypeElement*>(node);
+            static_cast<nsXULPrototypeElement*>(node.get());
 
         mPrototype->SetRootElement(element);
         mState = eInEpilog;
     }
 
     return NS_OK;
 }
 
@@ -697,38 +682,36 @@ XULContentSinkImpl::HandleProcessingInst
                                                 const PRUnichar *aData)
 {
     FlushText();
 
     const nsDependentString target(aTarget);
     const nsDependentString data(aData);
 
     // Note: the created nsXULPrototypePI has mRefCnt == 1
-    nsXULPrototypePI* pi = new nsXULPrototypePI();
+    nsRefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
     if (!pi)
         return NS_ERROR_OUT_OF_MEMORY;
 
     pi->mTarget = target;
     pi->mData = data;
 
     if (mState == eInProlog) {
         // Note: passing in already addrefed pi
         return mPrototype->AddProcessingInstruction(pi);
     }
 
     nsresult rv;
-    nsVoidArray* children;
+    nsPrototypeArray* children = nsnull;
     rv = mContextStack.GetTopChildren(&children);
     if (NS_FAILED(rv)) {
-        pi->Release();
         return rv;
     }
 
     if (!children->AppendElement(pi)) {
-        pi->Release();
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -934,17 +917,17 @@ XULContentSinkImpl::OpenTag(const PRUnic
                     aLineNumber));
         }
 #endif
 
         return rv;
     }
 
     // Link this element to its parent.
-    nsVoidArray* children;
+    nsPrototypeArray* children = nsnull;
     rv = mContextStack.GetTopChildren(&children);
     if (NS_FAILED(rv)) {
         delete element;
         return rv;
     }
 
     // Add the attributes
     rv = AddAttributes(aAttributes, aAttrLen, element);
@@ -1130,17 +1113,17 @@ XULContentSinkImpl::OpenScript(const PRU
       langID = nsIProgrammingLanguage::UNKNOWN;
       NS_WARNING("Non JS language called from non chrome - ignored");
   }
   // Don't process scripts that aren't known
   if (langID != nsIProgrammingLanguage::UNKNOWN) {
       nsIScriptGlobalObject* globalObject = nsnull; // borrowed reference
       if (doc)
           globalObject = doc->GetScriptGlobalObject();
-      nsXULPrototypeScript* script =
+      nsRefPtr<nsXULPrototypeScript> script =
           new nsXULPrototypeScript(langID, aLineNumber, version);
       if (! script)
           return NS_ERROR_OUT_OF_MEMORY;
 
       // If there is a SRC attribute...
       if (! src.IsEmpty()) {
           // Use the SRC attribute value to load the URL
           rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nsnull, mDocumentURL);
@@ -1159,32 +1142,30 @@ XULContentSinkImpl::OpenScript(const PRU
                           CheckLoadURIWithPrincipal(doc->NodePrincipal(),
                                                     script->mSrcURI,
                                                     nsIScriptSecurityManager::ALLOW_CHROME);
                   }
               }
           }
 
           if (NS_FAILED(rv)) {
-              delete script;
               return rv;
           }
 
           // Attempt to deserialize an out-of-line script from the FastLoad
           // file right away.  Otherwise we'll end up reloading the script and
           // corrupting the FastLoad file trying to serialize it, in the case
           // where it's already there.
           if (globalObject)
                 script->DeserializeOutOfLine(nsnull, globalObject);
       }
 
-      nsVoidArray* children;
+      nsPrototypeArray* children = nsnull;
       rv = mContextStack.GetTopChildren(&children);
       if (NS_FAILED(rv)) {
-          delete script;
           return rv;
       }
 
       children->AppendElement(script);
 
       mConstrainSize = PR_FALSE;
 
       mContextStack.Push(script, mState);
--- a/content/xul/document/src/nsXULContentSink.h
+++ b/content/xul/document/src/nsXULContentSink.h
@@ -43,16 +43,17 @@
 #define nsXULContentSink_h__
 
 #include "nsIExpatSink.h"
 #include "nsIXMLContentSink.h"
 #include "nsAutoPtr.h"
 #include "nsNodeInfoManager.h"
 #include "nsVoidArray.h"
 #include "nsWeakPtr.h"
+#include "nsXULElement.h"
 
 class nsIDocument;
 class nsIScriptSecurityManager;
 class nsAttrName;
 class nsXULPrototypeDocument;
 class nsXULPrototypeElement;
 class nsXULPrototypeNode;
 
@@ -138,37 +139,38 @@ protected:
     protected:
 
     State mState;
 
     // content stack management
     class ContextStack {
     protected:
         struct Entry {
-            nsXULPrototypeNode* mNode;
+            nsRefPtr<nsXULPrototypeNode> mNode;
             // a LOT of nodes have children; preallocate for 8
-            nsAutoVoidArray     mChildren;
+            nsPrototypeArray    mChildren;
             State               mState;
             Entry*              mNext;
+            Entry() : mChildren(8) {}
         };
 
         Entry* mTop;
         PRInt32 mDepth;
 
     public:
         ContextStack();
         ~ContextStack();
 
         PRInt32 Depth() { return mDepth; }
 
         nsresult Push(nsXULPrototypeNode* aNode, State aState);
         nsresult Pop(State* aState);
 
-        nsresult GetTopNode(nsXULPrototypeNode** aNode);
-        nsresult GetTopChildren(nsVoidArray** aChildren);
+        nsresult GetTopNode(nsRefPtr<nsXULPrototypeNode>& aNode);
+        nsresult GetTopChildren(nsPrototypeArray** aChildren);
         nsresult GetTopNodeScriptType(PRUint32 *aScriptType);
 
         void Clear();
     };
 
     friend class ContextStack;
     ContextStack mContextStack;
 
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -119,16 +119,17 @@
 #include "nsEventDispatcher.h"
 #include "nsContentErrors.h"
 #include "nsIObserverService.h"
 #include "nsNodeUtils.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIXULWindow.h"
 #include "nsXULPopupManager.h"
+#include "nsCCUncollectableMarker.h"
 
 //----------------------------------------------------------------------
 //
 // CIDs
 //
 
 static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
 
@@ -328,16 +329,19 @@ TraverseObservers(nsIURI* aKey, nsIObser
 
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mOverlayLoadObservers/mPendingOverlayLoadNotifications value");
     cb->NoteXPCOMChild(aData);
 
     return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULDocument, nsXMLDocument)
+    if (nsCCUncollectableMarker::InGeneration(tmp->GetMarkedCCGeneration())) {
+        return NS_OK;
+    }
     // XXX tmp->mForwardReferences?
     // XXX tmp->mContextStack?
 
     // An element will only have a template builder as long as it's in the
     // document, so we'll traverse the table here instead of from the element.
     if (tmp->mTemplateBuilderTable)
         tmp->mTemplateBuilderTable->EnumerateRead(TraverseTemplateBuilders, &cb);
         
@@ -2384,17 +2388,17 @@ nsXULDocument::PrepareToWalk()
 
     PRUint32 piInsertionPoint = 0;
     if (mState != eState_Master) {
         piInsertionPoint = IndexOf(GetRootContent());
         NS_ASSERTION(piInsertionPoint >= 0,
                      "No root content when preparing to walk overlay!");
     }
 
-    const nsTArray<nsXULPrototypePI*>& processingInstructions =
+    const nsTArray<nsRefPtr<nsXULPrototypePI> >& processingInstructions =
         mCurrentPrototype->GetProcessingInstructions();
 
     PRUint32 total = processingInstructions.Length();
     for (PRUint32 i = 0; i < total; ++i) {
         rv = CreateAndInsertPI(processingInstructions[i],
                                this, piInsertionPoint + i);
         if (NS_FAILED(rv)) return rv;
     }
@@ -2824,17 +2828,17 @@ nsXULDocument::ResumeWalk()
             // inserted to the actual document.
             nsXULPrototypeElement* proto;
             nsCOMPtr<nsIContent> element;
             PRInt32 indx; // all children of proto before indx (not
                           // inclusive) have already been constructed
             rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
             if (NS_FAILED(rv)) return rv;
 
-            if (indx >= (PRInt32)proto->mNumChildren) {
+            if (indx >= (PRInt32)proto->mChildren.Length()) {
                 if (element) {
                     // We've processed all of the prototype's children. If
                     // we're in the master prototype, do post-order
                     // document-level hookup. (An overlay will get its
                     // document hookup done when it's successfully
                     // resolved.)
                     if (mState == eState_Master) {
                         AddElementToDocumentPost(element);
@@ -2915,17 +2919,17 @@ nsXULDocument::ResumeWalk()
                     // enter a forward reference so we can hook it up
                     // later.
                     rv = CreateOverlayElement(protoele, getter_AddRefs(child));
                     if (NS_FAILED(rv)) return rv;
                 }
 
                 // If it has children, push the element onto the context
                 // stack and begin to process them.
-                if (protoele->mNumChildren > 0) {
+                if (protoele->mChildren.Length() > 0) {
                     rv = mContextStack.Push(protoele, child);
                     if (NS_FAILED(rv)) return rv;
                 }
                 else {
                     if (mState == eState_Master) {
                         // If there are no children, and we're in the
                         // master document, do post-order document hookup
                         // immediately.
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -57,17 +57,17 @@
 #include "jsapi.h"
 #include "nsString.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsDOMCID.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentUtils.h"
-
+#include "nsCCUncollectableMarker.h"
 #include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
 
 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
                      NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 
 class nsXULPDGlobalObject : public nsIScriptGlobalObject,
                             public nsIScriptObjectPrincipal
@@ -174,22 +174,16 @@ nsXULPrototypeDocument::Init()
 
 nsXULPrototypeDocument::~nsXULPrototypeDocument()
 {
     if (mGlobalObject) {
         // cleaup cycles etc.
         mGlobalObject->ClearGlobalObjectOwner();
     }
 
-    PRUint32 count = mProcessingInstructions.Length();
-    for (PRUint32 i = 0; i < count; i++)
-    {
-        mProcessingInstructions[i]->Release();
-    }
-
     if (mRoot)
         mRoot->ReleaseSubtree();
 
     if (--gRefCnt == 0) {
         NS_IF_RELEASE(gSystemPrincipal);
         NS_IF_RELEASE(gSystemGlobal);
     }
 }
@@ -329,17 +323,17 @@ nsXULPrototypeDocument::Read(nsIObjectIn
     }
 
     // Document contents
     PRUint32 type;
     while (NS_SUCCEEDED(rv)) {
         rv |= aStream->Read32(&type);
 
         if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) {
-            nsXULPrototypePI* pi = new nsXULPrototypePI();
+            nsRefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
             if (! pi) {
                rv |= NS_ERROR_OUT_OF_MEMORY;
                break;
             }
 
             rv |= pi->Deserialize(aStream, mGlobalObject, mURI, &nodeInfos);
             rv |= AddProcessingInstruction(pi);
         } else if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_Element) {
@@ -384,17 +378,17 @@ GetNodeInfos(nsXULPrototypeElement* aPro
         if (aArray.IndexOf(ni) < 0) {
             if (!aArray.AppendObject(ni)) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
         }
     }
 
     // Search children
-    for (i = 0; i < aPrototype->mNumChildren; ++i) {
+    for (i = 0; i < aPrototype->mChildren.Length(); ++i) {
         nsXULPrototypeNode* child = aPrototype->mChildren[i];
         if (child->mType == nsXULPrototypeNode::eType_Element) {
             rv = GetNodeInfos(static_cast<nsXULPrototypeElement*>(child),
                               aArray);
             NS_ENSURE_SUCCESS(rv, rv);
         }
     }
 
@@ -508,17 +502,17 @@ nsXULPrototypeDocument::AddProcessingIns
 {
     NS_PRECONDITION(aPI, "null ptr");
     if (!mProcessingInstructions.AppendElement(aPI)) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
     return NS_OK;
 }
 
-const nsTArray<nsXULPrototypePI*>&
+const nsTArray<nsRefPtr<nsXULPrototypePI> >&
 nsXULPrototypeDocument::GetProcessingInstructions() const
 {
     return mProcessingInstructions;
 }
 
 void
 nsXULPrototypeDocument::AddStyleSheetReference(nsIURI* aURI)
 {
--- a/content/xul/document/src/nsXULPrototypeDocument.h
+++ b/content/xul/document/src/nsXULPrototypeDocument.h
@@ -43,16 +43,17 @@
 #define nsXULPrototypeDocument_h__
 
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsIScriptGlobalObjectOwner.h"
 #include "nsISerializable.h"
+#include "nsIDocument.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsIAtom;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeInfoManager;
 class nsXULDocument;
 class nsXULPrototypeElement;
@@ -97,17 +98,17 @@ public:
      * @param aPI an already adrefed PI proto to add. This method takes
      *            ownership of the passed PI.
      */
     nsresult AddProcessingInstruction(nsXULPrototypePI* aPI);
     /**
      * @note GetProcessingInstructions retains the ownership (the PI
      *       protos only get deleted when the proto document is deleted)
      */
-    const nsTArray<nsXULPrototypePI*>& GetProcessingInstructions() const;
+    const nsTArray<nsRefPtr<nsXULPrototypePI> >& GetProcessingInstructions() const;
 
     /**
      * Access the array of style overlays for this document.
      *
      * Style overlays are stylesheets that need to be applied to the
      * document, but are not referenced from within the document. They
      * are currently obtained from the chrome registry via
      * nsIXULOverlayProvider::getStyleOverlays.)
@@ -146,18 +147,18 @@ public:
     // nsIScriptGlobalObjectOwner methods
     virtual nsIScriptGlobalObject* GetScriptGlobalObject();
 
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULPrototypeDocument,
                                              nsIScriptGlobalObjectOwner)
 
 protected:
     nsCOMPtr<nsIURI> mURI;
-    nsXULPrototypeElement* mRoot;
-    nsTArray<nsXULPrototypePI*> mProcessingInstructions;
+    nsRefPtr<nsXULPrototypeElement> mRoot;
+    nsTArray<nsRefPtr<nsXULPrototypePI> > mProcessingInstructions;
     nsCOMArray<nsIURI> mStyleSheetReferences;
 
     nsRefPtr<nsXULPDGlobalObject> mGlobalObject;
 
     PRPackedBool mLoaded;
     nsTArray< nsRefPtr<nsXULDocument> > mPrototypeWaiters;
 
     nsRefPtr<nsNodeInfoManager> mNodeInfoManager;
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -3406,18 +3406,19 @@ nsJSContext::CC()
   sDelayedCCollectCount = 0;
   sGCCount = 0;
   sCCSuspectChanges = 0;
   // nsCycleCollector_collect() will run a ::JS_GC() indirectly, so
   // we do not explicitly call ::JS_GC() here.
   sCollectedObjectsCounts = nsCycleCollector_collect();
   sCCSuspectedCount = nsCycleCollector_suspectedCount();
 #ifdef DEBUG_smaug
-  printf("Collected %u objects, %u suspected objects\n",
-         sCollectedObjectsCounts, sCCSuspectedCount);
+  printf("Collected %u objects, %u suspected objects, took %lldms\n",
+         sCollectedObjectsCounts, sCCSuspectedCount,
+         (PR_Now() - sPreviousCCTime) / PR_USEC_PER_MSEC);
 #endif
 }
 
 //static
 PRBool
 nsJSContext::MaybeCC(PRBool aHigherProbability)
 {
   ++sDelayedCCollectCount;