diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index c6770279709..2edbf495dd2 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -855,6 +855,24 @@ public: */ virtual PRBool MutationEventBeingDispatched() = 0; + /** + * Marks as not-going-to-be-collected for the given generation of + * cycle collection. + */ + void MarkUncollectableForCCGeneration(PRUint32 aGeneration) + { + mMarkedCCGeneration = aGeneration; + } + + /** + * Gets the cycle collector generation this document is marked for. + */ + PRUint32 GetMarkedCCGeneration() + { + return mMarkedCCGeneration; + } + + protected: ~nsIDocument() { @@ -927,6 +945,10 @@ 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; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID) diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index 66c4833c3b2..d95b4dd8ac8 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -82,11 +82,16 @@ class nsNodeSupportsWeakRefTearoff; // NOTE: Should only be used on nsIContent nodes #define NODE_MAY_HAVE_FRAME 0x00000020U +// Whether the 'in doc' flag is faked to true for +// this node. This should only ever be set on XUL +// elements. +#define NODE_HAS_FAKED_INDOC 0x00000040U + // Four bits for the script-type ID -#define NODE_SCRIPT_TYPE_OFFSET 6 +#define NODE_SCRIPT_TYPE_OFFSET 7 // Remaining bits are node type specific. -#define NODE_TYPE_SPECIFIC_BITS_OFFSET 0x0a +#define NODE_TYPE_SPECIFIC_BITS_OFFSET 0x0b // Useful macro for getting a node given an nsIContent and an nsIDocument // Returns the first argument cast to nsINode if it is non-null, otherwise diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index a88535f5f72..96189689c82 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -76,7 +76,9 @@ REQUIRES = xpcom \ uriloader \ rdf \ xultmpl \ - util \ + util \ + appshell \ + shistory \ $(NULL) EXPORTS = \ @@ -100,6 +102,7 @@ CPPSRCS = \ nsAtomListUtils.cpp \ nsAttrAndChildArray.cpp \ nsAttrValue.cpp \ + nsCCUncollectableMarker.cpp \ nsCommentNode.cpp \ nsContentAreaDragDrop.cpp \ nsContentIterator.cpp \ diff --git a/content/base/src/nsCCUncollectableMarker.cpp b/content/base/src/nsCCUncollectableMarker.cpp index 63303f810f0..f8008d47e19 100644 --- a/content/base/src/nsCCUncollectableMarker.cpp +++ b/content/base/src/nsCCUncollectableMarker.cpp @@ -73,6 +73,9 @@ nsCCUncollectableMarker::Init() NS_ENSURE_SUCCESS(rv, rv); // This makes the observer service hold an owning reference to the marker + rv = obs->AddObserver(marker, "xpcom-shutdown", PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + rv = obs->AddObserver(marker, "cycle-collector-begin", PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); @@ -183,11 +186,30 @@ nsresult nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { + nsresult rv; + + if (!strcmp(aTopic, "xpcom-shutdown")) { + nsCOMPtr obs = + do_GetService("@mozilla.org/observer-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // No need for kungFuDeathGrip here, yay observerservice! + obs->RemoveObserver(this, "xpcom-shutdown"); + obs->RemoveObserver(this, "cycle-collector-begin"); + + sGeneration = 0; + + return NS_OK; + } + + NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin"), "wrong topic"); + // Increase generation to effectivly unmark all current objects - ++sGeneration; + if (!++sGeneration) { + ++sGeneration; + } // Iterate all toplevel windows - nsresult rv; nsCOMPtr windowList; nsCOMPtr med = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); @@ -206,5 +228,7 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic, MarkWindowList(windowList); } + + return NS_OK; } diff --git a/content/base/src/nsCCUncollectableMarker.h b/content/base/src/nsCCUncollectableMarker.h index 822ff75ea68..bc14d6cbecf 100644 --- a/content/base/src/nsCCUncollectableMarker.h +++ b/content/base/src/nsCCUncollectableMarker.h @@ -51,7 +51,7 @@ class nsCCUncollectableMarker : public nsIObserver * Checks if we're collecting during a given generation */ static PRBool InGeneration(PRUint32 aGeneration) { - return aGeneration == sGeneration; + return aGeneration && aGeneration == sGeneration; } static PRUint32 sGeneration; diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index bd271cb88a2..efc3bc91fdc 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -153,6 +153,7 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID); #include "nsIJSContextStack.h" #include "nsIXPConnect.h" #include "nsCycleCollector.h" +#include "nsCCUncollectableMarker.h" #ifdef MOZ_LOGGING // so we can get logging even in release builds @@ -1010,6 +1011,10 @@ LinkMapTraverser(nsUint32ToContentHashEntry* aEntry, void* userArg) } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument) + if (nsCCUncollectableMarker::InGeneration(tmp->GetMarkedCCGeneration())) { + return NS_OK; + } + // Traverse the mChildren nsAttrAndChildArray. for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()); indx > 0; --indx) { cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1)); diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index d80dfd5deb8..7cfdfa8acfb 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -117,6 +117,7 @@ #include "nsCycleCollectionParticipant.h" +#include "nsCCUncollectableMarker.h" #ifdef MOZ_SVG PRBool NS_SVG_TestFeature(const nsAString &fstr); @@ -3007,6 +3008,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericElement) + nsIDocument* currentDoc = tmp->GetCurrentDoc(); + if (currentDoc && !tmp->HasFlag(NODE_HAS_FAKED_INDOC) && + nsCCUncollectableMarker::InGeneration( + currentDoc->GetMarkedCCGeneration())) { + return NS_OK; + } + nsIDocument* ownerDoc = tmp->GetOwnerDoc(); if (ownerDoc) { ownerDoc->BindingManager()->Traverse(tmp, cb); diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index c38e8f6b885..76d499dd620 100755 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -547,6 +547,7 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep, nsXULElement *xulElem = NS_STATIC_CAST(nsXULElement*, elem); if (!xulElem->mPrototype || xulElem->IsInDoc()) { clone->mParentPtrBits |= nsINode::PARENT_BIT_INDOCUMENT; + clone->SetFlags(NODE_HAS_FAKED_INDOC); } } #endif diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index 02101ec97af..38a4700d900 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -867,6 +867,9 @@ nsXULElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // Being added to a document. mParentPtrBits |= PARENT_BIT_INDOCUMENT; + + // Unset this flag since we now really are in a document. + UnsetFlags(NODE_HAS_FAKED_INDOC); } // Now recurse into our kids diff --git a/dom/src/base/Makefile.in b/dom/src/base/Makefile.in index 3c13bdb0463..6cbe78e8483 100644 --- a/dom/src/base/Makefile.in +++ b/dom/src/base/Makefile.in @@ -123,6 +123,7 @@ LOCAL_INCLUDES = \ -I$(topsrcdir)/content/xbl/src \ -I$(topsrcdir)/content/xul/document/src \ -I$(topsrcdir)/content/events/src \ + -I$(topsrcdir)/content/base/src \ $(NULL) DEFINES += -D_IMPL_NS_LAYOUT diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 74234aa9da4..883675352b1 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -75,6 +75,7 @@ #include "nsContentCID.h" #include "nsLayoutStatics.h" #include "nsCycleCollector.h" +#include "nsCCUncollectableMarker.h" // Interfaces Needed #include "nsIWidget.h" @@ -718,6 +719,11 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsGlobalWindow, NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow) + if (tmp->mDoc && nsCCUncollectableMarker::InGeneration( + tmp->mDoc->GetMarkedCCGeneration())) { + return NS_OK; + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOpener) diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index b2848d61509..883474b40b0 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -79,6 +79,7 @@ #include "nsDOMStorage.h" #include "nsCellMap.h" #include "nsTextFrameTextRunCache.h" +#include "nsCCUncollectableMarker.h" #ifdef MOZ_XUL #include "nsXULContentUtils.h" @@ -216,6 +217,12 @@ nsLayoutStatics::Initialize() return rv; } + rv = nsCCUncollectableMarker::Init(); + if (NS_FAILED(rv)) { + NS_ERROR("Could not initialize nsCCUncollectableMarker"); + return rv; + } + return NS_OK; } diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 7a6b4766ed2..83c7b7f0b1f 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -138,6 +138,8 @@ #include "prtime.h" #include "nsPrintfCString.h" #include "nsTArray.h" +#include "nsIObserverService.h" +#include "nsServiceManagerUtils.h" #include #ifdef WIN32 @@ -1917,6 +1919,12 @@ nsCycleCollector::Collect(PRUint32 aTryCollections) PRTime start = PR_Now(), now; #endif + nsCOMPtr obs = + do_GetService("@mozilla.org/observer-service;1"); + if (obs) { + obs->NotifyObservers(nsnull, "cycle-collector-begin", nsnull); + } + while (aTryCollections > 0) { // This triggers a JS GC. Our caller assumes we always trigger at // least one JS GC -- they rely on this fact to avoid redundant JS