Relanding
bug 378987. Don't try to collect windows, documents or elements of actively viewed pages. r=bz/jst sr=jst
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -850,16 +850,34 @@ public:
*/
virtual void FlushSkinBindings() = 0;
/**
* Returns PR_TRUE if one or more mutation events are being dispatched.
*/
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()
{
// XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
// releasing it) happens in the nsDocument destructor. We'd prefer to
// do it here but nsNodeInfoManager is a concrete class that we don't
// want to expose to users of the nsIDocument API outside of Gecko.
// XXX Same thing applies to mBindingManager
@@ -922,16 +940,20 @@ protected:
nsCString mContentType;
// The document's security info
nsCOMPtr<nsISupports> mSecurityInfo;
// 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)
/**
* Helper class to automatically handle batching of document updates. This
* class will call BeginUpdate on construction and EndUpdate on destruction on
* the given document with the given update type. The document could be null,
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -77,21 +77,26 @@ class nsNodeSupportsWeakRefTearoff;
// Whether this node is anonymous for events
// NOTE: Should only be used on nsIContent nodes
#define NODE_IS_ANONYMOUS_FOR_EVENTS 0x00000010U
// Whether this node may have a frame
// 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
// returns the second (which may be null)
#define NODE_FROM(content_, document_) \
((content_) ? NS_STATIC_CAST(nsINode*, (content_)) : \
NS_STATIC_CAST(nsINode*, (document_)))
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -71,17 +71,19 @@ REQUIRES = xpcom \
webbrowserpersist \
imglib2 \
plugin \
prefetch \
xuldoc \
uriloader \
rdf \
xultmpl \
- util \
+ util \
+ appshell \
+ shistory \
$(NULL)
EXPORTS = \
nsAtomListUtils.h \
nsAttrName.h \
nsContentList.h \
nsGkAtomList.h \
nsGkAtoms.h \
@@ -95,16 +97,17 @@ EXPORTS = \
nsTObserverArray.h \
$(NULL)
CPPSRCS = \
mozSanitizingSerializer.cpp \
nsAtomListUtils.cpp \
nsAttrAndChildArray.cpp \
nsAttrValue.cpp \
+ nsCCUncollectableMarker.cpp \
nsCommentNode.cpp \
nsContentAreaDragDrop.cpp \
nsContentIterator.cpp \
nsContentList.cpp \
nsContentPolicy.cpp \
nsContentSink.cpp \
nsContentUtils.cpp \
nsCopySupport.cpp \
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -68,16 +68,19 @@ nsCCUncollectableMarker::Init()
NS_ENSURE_TRUE(marker, NS_ERROR_OUT_OF_MEMORY);
nsresult rv;
nsCOMPtr<nsIObserverService> obs =
do_GetService("@mozilla.org/observer-service;1", &rv);
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);
sInited = PR_TRUE;
return NS_OK;
}
@@ -178,21 +181,40 @@ MarkWindowList(nsISimpleEnumerator* aWin
}
}
}
nsresult
nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData)
{
+ nsresult rv;
+
+ if (!strcmp(aTopic, "xpcom-shutdown")) {
+ nsCOMPtr<nsIObserverService> 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<nsISimpleEnumerator> windowList;
nsCOMPtr<nsIWindowMediator> med =
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
if (med) {
rv = med->GetEnumerator(nsnull, getter_AddRefs(windowList));
NS_ENSURE_SUCCESS(rv, rv);
MarkWindowList(windowList);
@@ -201,10 +223,12 @@ nsCCUncollectableMarker::Observe(nsISupp
nsCOMPtr<nsIWindowWatcher> ww =
do_GetService(NS_WINDOWWATCHER_CONTRACTID);
if (ww) {
rv = ww->GetWindowEnumerator(getter_AddRefs(windowList));
NS_ENSURE_SUCCESS(rv, rv);
MarkWindowList(windowList);
}
+
+ return NS_OK;
}
--- a/content/base/src/nsCCUncollectableMarker.h
+++ b/content/base/src/nsCCUncollectableMarker.h
@@ -46,17 +46,17 @@ class nsCCUncollectableMarker : public n
* Inits a global nsCCUncollectableMarker. Should only be called once.
*/
static nsresult Init();
/**
* Checks if we're collecting during a given generation
*/
static PRBool InGeneration(PRUint32 aGeneration) {
- return aGeneration == sGeneration;
+ return aGeneration && aGeneration == sGeneration;
}
static PRUint32 sGeneration;
private:
nsCCUncollectableMarker() {};
};
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -148,16 +148,17 @@ static NS_DEFINE_CID(kDOMEventGroupCID,
#include "nsMutationEvent.h"
#include "nsIDOMXPathEvaluator.h"
#include "nsDOMCID.h"
#include "nsLayoutStatics.h"
#include "nsIJSContextStack.h"
#include "nsIXPConnect.h"
#include "nsCycleCollector.h"
+#include "nsCCUncollectableMarker.h"
#ifdef MOZ_LOGGING
// so we can get logging even in release builds
#define FORCE_PR_LOG 1
#endif
#include "prlog.h"
#ifdef PR_LOGGING
@@ -1005,16 +1006,20 @@ LinkMapTraverser(nsUint32ToContentHashEn
{
LinkMapTraversalVisitor visitor;
visitor.mCb = NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, userArg);
aEntry->VisitContent(&visitor);
return PL_DHASH_NEXT;
}
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));
}
// Traverse all nsIDocument pointer members.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mBindingManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSecurityInfo)
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -112,16 +112,17 @@
#include "nsIDOMUserDataHandler.h"
#include "nsEventDispatcher.h"
#include "nsContentCreatorFunctions.h"
#include "nsIFocusController.h"
#include "nsIControllers.h"
#include "nsCycleCollectionParticipant.h"
+#include "nsCCUncollectableMarker.h"
#ifdef MOZ_SVG
PRBool NS_SVG_TestFeature(const nsAString &fstr);
#endif /* MOZ_SVG */
#ifdef DEBUG_waterson
/**
@@ -3002,16 +3003,23 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
{
nsDOMSlots *slots = tmp->GetExistingDOMSlots();
if (slots)
slots->mAttributeMap = nsnull;
}
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);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_LISTENERMANAGER
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -542,16 +542,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
// Note: Make sure to do this witchery _after_ we've done any deep
// cloning, so kids of the new node aren't confused about whether they're
// in a document.
#ifdef MOZ_XUL
if (aClone && !aParent && aNode->IsNodeOfType(nsINode::eXUL)) {
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
if (aNode->HasProperties()) {
PRBool ok = aNodesWithProperties.AppendObject(aNode);
if (aClone) {
ok = ok && aNodesWithProperties.AppendObject(clone);
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -862,16 +862,19 @@ nsXULElement::BindToTree(nsIDocument* aD
// XXXbz XBL doesn't handle this (asserts), and we don't really want
// to be doing this during parsing anyway... sort this out.
// aDocument->BindingManager()->ChangeDocumentFor(this, nsnull,
// aDocument);
// 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
nsresult rv;
PRUint32 i;
for (i = 0; i < GetChildCount(); ++i) {
// The child can remove itself from the parent in BindToTree.
nsCOMPtr<nsIContent> child = mAttrsAndChildren.ChildAt(i);
--- a/dom/src/base/Makefile.in
+++ b/dom/src/base/Makefile.in
@@ -118,16 +118,17 @@ CPPSRCS = \
FORCE_STATIC_LIB = 1
LOCAL_INCLUDES = \
-I$(srcdir)/../events \
-I$(srcdir)/../storage \
-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
ifdef MOZ_JSDEBUGGER
DEFINES += -DMOZ_JSDEBUGGER
endif
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -70,16 +70,17 @@
#include "nsPluginArray.h"
#include "nsIPluginHost.h"
#ifdef OJI
#include "nsIJVMManager.h"
#endif
#include "nsContentCID.h"
#include "nsLayoutStatics.h"
#include "nsCycleCollector.h"
+#include "nsCCUncollectableMarker.h"
// Interfaces Needed
#include "nsIWidget.h"
#include "nsIBaseWindow.h"
#include "nsICharsetConverterManager.h"
#include "nsIContent.h"
#include "nsIContentViewerEdit.h"
#include "nsIDocShell.h"
@@ -713,16 +714,21 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsGlobalWindow, nsIScriptGlobalObject)
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsGlobalWindow,
nsIScriptGlobalObject)
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)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArguments)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgumentsLast)
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -73,16 +73,17 @@
#include "nsStackLayout.h"
#include "nsStyleSet.h"
#include "nsTextControlFrame.h"
#include "nsTextTransformer.h"
#include "nsXBLWindowKeyHandler.h"
#include "txMozillaXSLTProcessor.h"
#include "nsDOMStorage.h"
#include "nsCellMap.h"
+#include "nsCCUncollectableMarker.h"
#include "nsTextFrameTextRunCache.h"
#ifdef MOZ_XUL
#include "nsXULContentUtils.h"
#include "nsXULElement.h"
#include "nsXULPrototypeCache.h"
#include "nsXULTooltipListener.h"
#endif
@@ -211,16 +212,22 @@ nsLayoutStatics::Initialize()
}
rv = nsDOMStorageManager::Initialize();
if (NS_FAILED(rv)) {
NS_ERROR("Could not initialize nsDOMStorageManager");
return rv;
}
+ rv = nsCCUncollectableMarker::Init();
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Could not initialize nsCCUncollectableMarker");
+ return rv;
+ }
+
return NS_OK;
}
void
nsLayoutStatics::Shutdown()
{
nsDOMStorageManager::Shutdown();
txMozillaXSLTProcessor::Shutdown();
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -133,16 +133,18 @@
#include "nsCycleCollector.h"
#include "nsThreadUtils.h"
#include "prenv.h"
#include "prprf.h"
#include "plstr.h"
#include "prtime.h"
#include "nsPrintfCString.h"
#include "nsTArray.h"
+#include "nsIObserverService.h"
+#include "nsServiceManagerUtils.h"
#include <stdio.h>
#ifdef WIN32
#include <io.h>
#include <process.h>
#endif
#define DEFAULT_SHUTDOWN_COLLECTIONS 5
@@ -1912,16 +1914,22 @@ nsCycleCollector::Collect(PRUint32 aTryC
InitMemHook();
#endif
#ifdef COLLECT_TIME_DEBUG
printf("cc: Starting nsCycleCollector::Collect(%d)\n", aTryCollections);
PRTime start = PR_Now(), now;
#endif
+ nsCOMPtr<nsIObserverService> 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
// GC calls -- so it's essential that we actually execute this
// step!
//
// It is also essential to empty mBufs[0] here because starting up
// collection in language runtimes may force some "current" suspects