From 2ba6c6183fd97ba28f3269ec38ce25d170428a56 Mon Sep 17 00:00:00 2001 From: "dbaron%dbaron.org" Date: Tue, 29 Mar 2005 23:26:56 +0000 Subject: [PATCH] Change how we preserve XPConnect wrappers on which JS properties have been set or that are otherwise needed by XBL: instead of rooting those wrappers until the document stops being displayed, mark them from GC hooks only if the nodes are reachable (via DOM node traversal, optimizing based on symmetry of reachability between DOM nodes) from the document or from nodes already marked. b=283129 r=jst sr=brendan --- content/base/public/nsIDocument.h | 9 +- content/base/src/nsContentUtils.cpp | 10 - content/base/src/nsDocument.cpp | 27 -- content/base/src/nsDocument.h | 4 - content/xbl/src/nsXBLBinding.cpp | 6 +- content/xbl/src/nsXBLProtoImpl.cpp | 10 +- dom/src/base/nsDOMClassInfo.cpp | 415 ++++++++++++++++++++++++++-- dom/src/base/nsDOMClassInfo.h | 35 +++ dom/src/base/nsJSEnvironment.cpp | 2 + dom/src/base/nsJSEnvironment.h | 2 - 10 files changed, 446 insertions(+), 74 deletions(-) diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 8da541bb929b..dbf9caa6416f 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -88,10 +88,10 @@ class nsHTMLStyleSheet; class nsIHTMLCSSStyleSheet; // IID for the nsIDocument interface -// 0924a06c-29df-4f3d-b053-4ebf099327c6 +// f01b47c6-6271-4c0d-b786-eb5eee222b45 #define NS_IDOCUMENT_IID \ -{ 0x0924a06c, 0x29df, 0x4f3d, \ - { 0xb0, 0x53, 0x4e, 0xbf, 0x09, 0x93, 0x27, 0xc6 } } +{ 0xf01b47c6, 0x6271, 0x4c0d, \ + { 0xb7, 0x86, 0xeb, 0x5e, 0xee, 0x22, 0x2b, 0x45 } } // The base value for the content ID counter. // This counter is used by the document to @@ -547,9 +547,6 @@ public: */ virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup) = 0; - virtual void AddReference(void *aKey, nsISupports *aReference) = 0; - virtual already_AddRefed RemoveReference(void *aKey) = 0; - /** * Set the container (docshell) for this document. */ diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index ada1ed938e3e..daabc1b73a68 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -740,16 +740,6 @@ nsContentUtils::doReparentContentWrapper(nsIContent *aChild, return NS_OK; } - if (aOldDocument) { - nsCOMPtr old_ref = aOldDocument->RemoveReference(aChild); - - if (old_ref) { - // Transfer the reference from aOldDocument to aNewDocument - - aNewDocument->AddReference(aChild, old_ref); - } - } - JSObject *old; rv = old_wrapper->GetJSObject(&old); NS_ENSURE_SUCCESS(rv, rv); diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index d93d0c201c13..841ffcd71266 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1835,13 +1835,6 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) shell->ReleaseAnonymousContent(); } - -#ifdef DEBUG_jst - printf ("Content wrapper hash had %d entries.\n", - mContentWrapperHash.Count()); -#endif - - mContentWrapperHash.Reset(); } mScriptGlobalObject = aScriptGlobalObject; @@ -4075,26 +4068,6 @@ nsDocument::FlushPendingNotifications(mozFlushType aType) } } -void -nsDocument::AddReference(void *aKey, nsISupports *aReference) -{ - nsVoidKey key(aKey); - - if (mScriptGlobalObject) { - mContentWrapperHash.Put(&key, aReference); - } -} - -already_AddRefed -nsDocument::RemoveReference(void *aKey) -{ - nsVoidKey key(aKey); - - nsISupports* oldReference; - mContentWrapperHash.Remove(&key, &oldReference); - return oldReference; -} - nsIScriptEventManager* nsDocument::GetScriptEventManager() { diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 0350a9601074..8e658461f418 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -405,8 +405,6 @@ public: nsIStyleRule* aStyleRule); virtual void FlushPendingNotifications(mozFlushType aType); - virtual void AddReference(void *aKey, nsISupports *aReference); - virtual already_AddRefed RemoveReference(void *aKey); virtual nsIScriptEventManager* GetScriptEventManager(); virtual void SetXMLDeclaration(const PRUnichar *aVersion, const PRUnichar *aEncoding, @@ -593,8 +591,6 @@ protected: nsSupportsHashtable* mBoxObjectTable; - nsSupportsHashtable mContentWrapperHash; - nsCOMPtr mCSSLoader; nsRefPtr mAttrStyleSheet; nsCOMPtr mStyleAttrStyleSheet; diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 9558d733a9ba..56fa171a9711 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -100,6 +100,9 @@ #include "prprf.h" +nsresult NS_DOMClassInfo_PreserveWrapper(nsIDOMNode *aDOMNode, + nsIXPConnectWrappedNative *aWrapper); + // Helper classes /***********************************************************************/ @@ -1125,7 +1128,8 @@ nsXBLBinding::InitClass(const nsCString& aClassName, do_QueryInterface(wrapper); if (native_wrapper) { - doc->AddReference(mBoundElement, native_wrapper); + nsCOMPtr node(do_QueryInterface(mBoundElement)); + NS_DOMClassInfo_PreserveWrapper(node, native_wrapper); } } diff --git a/content/xbl/src/nsXBLProtoImpl.cpp b/content/xbl/src/nsXBLProtoImpl.cpp index 3b34673cdb23..edd84ab6c65f 100644 --- a/content/xbl/src/nsXBLProtoImpl.cpp +++ b/content/xbl/src/nsXBLProtoImpl.cpp @@ -46,6 +46,10 @@ #include "nsIXPConnect.h" #include "nsIServiceManager.h" #include "nsIXBLDocumentInfo.h" +#include "nsIDOMNode.h" + +nsresult NS_DOMClassInfo_PreserveWrapper(nsIDOMNode *aDOMNode, + nsIXPConnectWrappedNative *aWrapper); nsresult nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement) @@ -133,8 +137,10 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding, nsIDocument* doc = aBoundElement->GetOwnerDoc(); if (doc) { nsCOMPtr nativeWrapper(do_QueryInterface(wrapper)); - if (nativeWrapper) - doc->AddReference(aBoundElement, nativeWrapper); + if (nativeWrapper) { + nsCOMPtr node(do_QueryInterface(aBoundElement)); + NS_DOMClassInfo_PreserveWrapper(node, nativeWrapper); + } } return rv; diff --git a/dom/src/base/nsDOMClassInfo.cpp b/dom/src/base/nsDOMClassInfo.cpp index 9cc40bde403f..e422462edf99 100644 --- a/dom/src/base/nsDOMClassInfo.cpp +++ b/dom/src/base/nsDOMClassInfo.cpp @@ -385,7 +385,9 @@ static const char kDOMStringBundleURL[] = ((DOM_DEFAULT_SCRIPTABLE_FLAGS | \ nsIXPCScriptable::WANT_PRECREATE | \ nsIXPCScriptable::WANT_ADDPROPERTY | \ - nsIXPCScriptable::WANT_SETPROPERTY) & \ + nsIXPCScriptable::WANT_SETPROPERTY | \ + nsIXPCScriptable::WANT_FINALIZE | \ + nsIXPCScriptable::WANT_MARK) & \ ~nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY) // We need to let JavaScript QI elements to interfaces that are not in @@ -3724,12 +3726,12 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // that's the case). If *vp is a window object (i.e. a child // frame), return without doing a security check. - nsCOMPtr wrapper; + nsCOMPtr vpwrapper; sXPConnect->GetWrappedNativeOfJSObject(cx, JSVAL_TO_OBJECT(*vp), - getter_AddRefs(wrapper)); + getter_AddRefs(vpwrapper)); - if (wrapper) { - nsCOMPtr window(do_QueryWrappedNative(wrapper)); + if (vpwrapper) { + nsCOMPtr window(do_QueryWrappedNative(vpwrapper)); if (window) { // Yup, *vp is a window object, return early (*vp is already @@ -4259,6 +4261,365 @@ nsDOMClassInfo::InitDOMJSClass(JSContext *cx, JSObject *obj) return NS_OK; } +/* + * The problem of how to deal with garbage collection in a system where: + * 1. the GC runtime contains lazily-created wrappers for other data + * structures not participating in the GC, + * 2. where those data structures are reachable from each other in a + * way that is reflected through the wrappers, and + * 3. where the wrappers can carry additional information that is not + * in the wrapped object + * is a difficult one. XPConnect is such a system. It creates wrappers + * around native objects that do not participate in the JS GC and which + * are reachable from each other outside of the knowledge of the JS + * engine (but through getters or functions that are accessible to JS + * users) and it allows these wrappers to be decorated with JS + * properties. It is worth noting one very important use case for our + * toolkit: XBL, which uses JS properties on the bound element for + * storing fields. + * + * For DOM nodes, compatibility with behavior in other browsers requires + * that we preserve JS properties on wrappers across garbage collection. + * To do this in a way that the garbage collector is still effective, we + * have to incorporate knowledge of reachability in the underlying + * (wrapped) data structures into the garbage collector's mark phase. + * + * See https://bugzilla.mozilla.org/show_bug.cgi?id=283129 for more details. + */ + +#if defined(DEBUG_dbaron) || defined(DEBUG_jst) +#define DEBUG_PRESERVE_WRAPPERS +#endif + +/** + * Every XPConnect wrapper that needs to be preserved (because it has JS + * properties set on it) as long as the element it wraps is reachable + * from script (via JS or via DOM APIs accessible from JS) gets an entry + * in this table. + */ +static PLDHashTable sPreservedWrapperTable; + +struct PreservedWrapperEntry : public PLDHashEntryHdr { + nsIDOMNode* key; // must be first to line up with PLDHashEntryStub + nsIXPConnectWrappedNative *wrapper; + + // See |WrapperSCCEntry::first|. Valid only during mark phase of GC. + PreservedWrapperEntry *next; +}; + +/** + * During the Mark phase of the GC, we need to mark all of the preserved + * wrappers that are reachable via DOM APIs. Since reachability for DOM + * nodes is symmetric, if one DOM node is reachable from another via DOM + * APIs, then they are in the same strongly connected component. + * (Strongly connected components are never reachable from each other + * via DOM APIs.) We can refer to each strongly connected component by + * walking up to the top of the parent chain. This function finds that + * root node for any DOM node. + */ +static nsIDOMNode* GetSCCRootFor(nsIDOMNode *aDOMNode) +{ + nsCOMPtr cur(aDOMNode), next; +#ifdef DEBUG_NOISY_PRESERVE_WRAPPERS + nsCOMArray stack; +#endif + for (;;) { +#ifdef DEBUG_NOISY_PRESERVE_WRAPPERS + stack.AppendObject(cur); +#endif + cur->GetParentNode(getter_AddRefs(next)); + if (!next) { +#ifdef DEBUG_NOISY_PRESERVE_WRAPPERS + PRUint16 nodeType; + cur->GetNodeType(&nodeType); + if (nodeType != nsIDOMNode::DOCUMENT_NODE) { + printf(" non-document root:"); + nsAutoString nodeName; + for (PRInt32 i = stack.Count() - 1; i >= 0; --i) { + stack[i]->GetNodeName(nodeName); + printf(" > %s", NS_ConvertUTF16toUTF8(nodeName).get()); + stack[i]->GetNodeType(&nodeType); + if (nodeType == nsIDOMNode::ELEMENT_NODE) { + nsCOMPtr content = do_QueryInterface(stack[i]); + for (PRInt32 j = 0, j_end = content->GetAttrCount(); j < j_end; ++j) { + PRInt32 namespaceID; + nsCOMPtr name, prefix; + content->GetAttrNameAt(j, &namespaceID, getter_AddRefs(name), + getter_AddRefs(prefix)); + nsAutoString val; + nsCAutoString atomStr; + content->GetAttr(namespaceID, name, val); + printf("["); + if (prefix) { + prefix->ToUTF8String(atomStr); + printf("%s:", atomStr.get()); + } + name->ToUTF8String(atomStr); + printf("%s=\"%s\"]", atomStr.get(), NS_ConvertUTF16toUTF8(val).get()); + } + } + } + printf("\n"); + } +#endif + return cur; + } + next.swap(cur); + } +} + +/** + * At the beginning of the mark phase of the GC, we sort all the + * wrappers into their strongly connected components. We maintain this + * information for the duration of the mark phase, during which time + * |sWrapperSCCTable| maps the SCC roots to a list of the wrappers in + * the SCC. This allows the mark callback for any wrapper (preserved or + * not) to quickly mark all of the preserved wrappers in its SCC. + * + * Many of these roots are documents. We know that a document will be + * marked if it is still being displayed since the document is reachable + * by a JS property on the global object. + */ +static PLDHashTable sWrapperSCCTable; + +// If we run out of memory constructing sWrapperSCCTable, its |ops| +// member holds this value for the remainder of that GC cycle. +#define WRAPPER_SCC_OPS_OOM_MARKER ((PLDHashTableOps*)1) + +struct WrapperSCCEntry : public PLDHashEntryHdr { + // This could probably be a weak pointer (which would avoid the + // need for hash table ops), but it seems safer this way. + nsCOMPtr key; // must be first to line up with PLDHashEntryStub + + // Linked list of preserved wrappers in the strongly connected + // component, to be traversed using |PreservedWrapperEntry::next|. + PreservedWrapperEntry *first; + + PRBool marked; + + WrapperSCCEntry(nsIDOMNode *aKey) + : key(aKey), first(nsnull), marked(PR_FALSE) {} +}; + +PR_STATIC_CALLBACK(void) +WrapperSCCsClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) +{ + WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*, hdr); + entry->~WrapperSCCEntry(); + memset(hdr, 0, table->entrySize); +} + +PR_STATIC_CALLBACK(PRBool) +WrapperSCCsInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr, + const void *key) +{ + WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*, hdr); + new (entry) WrapperSCCEntry(NS_STATIC_CAST(nsIDOMNode*, + NS_CONST_CAST(void*, key))); + return PR_TRUE; +} + +static const PLDHashTableOps sWrapperSCCTableOps = { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashGetKeyStub, + PL_DHashVoidPtrKeyStub, + PL_DHashMatchEntryStub, + PL_DHashMoveEntryStub, + WrapperSCCsClearEntry, + PL_DHashFinalizeStub, + WrapperSCCsInitEntry +}; + + +// static +nsresult +nsDOMClassInfo::PreserveWrapper(nsIDOMNode *aDOMNode, + nsIXPConnectWrappedNative *aWrapper) +{ + if (!sPreservedWrapperTable.ops && + !PL_DHashTableInit(&sPreservedWrapperTable, PL_DHashGetStubOps(), nsnull, + sizeof(PreservedWrapperEntry), 16)) { + sPreservedWrapperTable.ops = nsnull; + return NS_ERROR_OUT_OF_MEMORY; + } + + PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, + PL_DHashTableOperate(&sPreservedWrapperTable, aDOMNode, PL_DHASH_ADD)); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + + entry->key = aDOMNode; + entry->wrapper = aWrapper; + + return NS_OK; +} + +// static +void +nsDOMClassInfo::ReleaseWrapper(nsIDOMNode *aDOMNode) +{ + if (sPreservedWrapperTable.ops) { + PL_DHashTableOperate(&sPreservedWrapperTable, aDOMNode, PL_DHASH_REMOVE); + if (sPreservedWrapperTable.entryCount == 0) { + PL_DHashTableFinish(&sPreservedWrapperTable); + sPreservedWrapperTable.ops = nsnull; + } + } +} + +struct MarkAllWrappersData { + JSContext *cx; + void *arg; +}; + +PR_STATIC_CALLBACK(PLDHashOperator) +MarkAllWrappers(PLDHashTable *table, PLDHashEntryHdr *hdr, + PRUint32 number, void *arg) +{ + MarkAllWrappersData *data = NS_STATIC_CAST(MarkAllWrappersData*, arg); + PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, hdr); + + JSObject *wrapper_obj; + if (NS_SUCCEEDED(entry->wrapper->GetJSObject(&wrapper_obj))) + JS_MarkGCThing(data->cx, wrapper_obj, + "nsDOMClassInfo::sPreservedWrapperTable_OOM", data->arg); + + return PL_DHASH_NEXT; +} + +// static +void +nsDOMClassInfo::MarkReachablePreservedWrappers(nsIDOMNode *aDOMNode, + JSContext *cx, void *arg) +{ + // Magic value indicating we've hit out-of-memory earlier in this GC. + if (sWrapperSCCTable.ops == WRAPPER_SCC_OPS_OOM_MARKER) + return; + + // The JS engine doesn't have a notification for the beginning of + // the mark phase. However, we can determine that the mark phase + // has begun by detecting the first mark and lazily call + // BeginGCMark. + if (!sWrapperSCCTable.ops && !nsDOMClassInfo::BeginGCMark()) { + // We didn't have enough memory to create the temporary data + // structures we needed. + sWrapperSCCTable.ops = WRAPPER_SCC_OPS_OOM_MARKER; + + // Just mark all the preserved wrappers. It won't help the GC free + // up memory, but there's nothing better to do. + if (sPreservedWrapperTable.ops) { + MarkAllWrappersData data; + data.cx = cx; + data.arg = arg; + PL_DHashTableEnumerate(&sPreservedWrapperTable, MarkAllWrappers, &data); + } + return; + } + + WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*, + PL_DHashTableOperate(&sWrapperSCCTable, GetSCCRootFor(aDOMNode), + PL_DHASH_LOOKUP)); + if (!PL_DHASH_ENTRY_IS_BUSY(entry) || entry->marked) + return; + +#ifdef DEBUG_PRESERVE_WRAPPERS + { + nsAutoString nodeName; + entry->key->GetNodeName(nodeName); + printf(" marking entries for SCC root %p \"%s\"\n", + entry->key.get(), NS_ConvertUTF16toUTF8(nodeName).get()); + } +#endif + entry->marked = PR_TRUE; + + for (PreservedWrapperEntry *pwe = entry->first; pwe; pwe = pwe->next) { + JSObject *wrapper_obj; + if (NS_SUCCEEDED(pwe->wrapper->GetJSObject(&wrapper_obj))) + ::JS_MarkGCThing(cx, wrapper_obj, + "nsDOMClassInfo::sPreservedWrapperTable", arg); + } +} + +PR_STATIC_CALLBACK(PLDHashOperator) +ClassifyWrapper(PLDHashTable *table, PLDHashEntryHdr *hdr, + PRUint32 number, void *arg) +{ + PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, hdr); + + WrapperSCCEntry *SCCEntry = NS_STATIC_CAST(WrapperSCCEntry*, + PL_DHashTableOperate(&sWrapperSCCTable, GetSCCRootFor(entry->key), + PL_DHASH_ADD)); + if (!SCCEntry) { + *NS_STATIC_CAST(PRBool*, arg) = PR_TRUE; + return PL_DHASH_STOP; + } + +#ifdef DEBUG_PRESERVE_WRAPPERS + if (!SCCEntry->first) { + nsAutoString nodeName; + SCCEntry->key->GetNodeName(nodeName); + printf(" new SCC root %p \"%s\"\n", + SCCEntry->key.get(), NS_ConvertUTF16toUTF8(nodeName).get()); + } +#endif + + entry->next = SCCEntry->first; + SCCEntry->first = entry; + + return PL_DHASH_NEXT; +} + +// static +PRBool +nsDOMClassInfo::BeginGCMark() +{ + NS_PRECONDITION(!sWrapperSCCTable.ops, "table already initialized"); + +#ifdef DEBUG_PRESERVE_WRAPPERS + printf("\nClassifying preserved wrappers into SCCs:\n"); +#endif + + if (!PL_DHashTableInit(&sWrapperSCCTable, &sWrapperSCCTableOps, nsnull, + sizeof(WrapperSCCEntry), 16)) { + return PR_FALSE; + } + + PRBool failure = PR_FALSE; + if (sPreservedWrapperTable.ops) + PL_DHashTableEnumerate(&sPreservedWrapperTable, ClassifyWrapper, &failure); + if (failure) { + PL_DHashTableFinish(&sWrapperSCCTable); + // caller will reset table ops + return PR_FALSE; + } + +#ifdef DEBUG_PRESERVE_WRAPPERS + printf("Marking:\n"); +#endif + + return PR_TRUE; +} + +// static +void +nsDOMClassInfo::EndGCMark() +{ + if (sWrapperSCCTable.ops) { + if (sWrapperSCCTable.ops != WRAPPER_SCC_OPS_OOM_MARKER) + PL_DHashTableFinish(&sWrapperSCCTable); + sWrapperSCCTable.ops = nsnull; + } +} + +// hack to give XBL access to nsDOMClassInfo::PreserveWrapper +nsresult +NS_DOMClassInfo_PreserveWrapper(nsIDOMNode *aDOMNode, + nsIXPConnectWrappedNative *aWrapper) +{ + return nsDOMClassInfo::PreserveWrapper(aDOMNode, aWrapper); +} + // static nsresult nsWindowSH::GlobalResolve(nsIScriptGlobalObject *global, JSContext *cx, @@ -4492,7 +4853,7 @@ nsWindowSH::GlobalResolve(nsIScriptGlobalObject *global, JSContext *cx, nsCOMPtr proto_holder; - nsresult rv = + rv = sXPConnect->GetWrappedNativePrototype(cx, obj, ci, getter_AddRefs(proto_holder)); NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); @@ -4513,7 +4874,7 @@ nsWindowSH::GlobalResolve(nsIScriptGlobalObject *global, JSContext *cx, nsCOMPtr proto_holder; - nsresult rv = + rv = sXPConnect->GetWrappedNativePrototype(cx, obj, ci, getter_AddRefs(proto_holder)); NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); @@ -5069,24 +5430,34 @@ nsNodeSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, nsISupports *native = wrapper->Native(); nsCOMPtr node(do_QueryInterface(native)); - nsCOMPtr doc; + // This can fail on out-of-memory, which should end up throwing a JS + // exception. + return nsDOMClassInfo::PreserveWrapper(node, wrapper); +} - if (node) { - nsCOMPtr domdoc; - node->GetOwnerDocument(getter_AddRefs(domdoc)); - doc = do_QueryInterface(domdoc); - } +NS_IMETHODIMP +nsNodeSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsISupports *native = wrapper->Native(); + nsCOMPtr node(do_QueryInterface(native)); - if (!doc) { - doc = do_QueryInterface(native); - } + nsDOMClassInfo::ReleaseWrapper(node); - if (doc) { - nsCOMPtr content(do_QueryInterface(node)); - doc->AddReference(content, wrapper); - } + return NS_OK; +} - return nsEventReceiverSH::AddProperty(wrapper, cx, obj, id, vp, _retval); +NS_IMETHODIMP +nsNodeSH::Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, void *arg, PRUint32 *_retval) +{ + nsISupports *native = wrapper->Native(); + nsCOMPtr node(do_QueryInterface(native)); + + nsDOMClassInfo::MarkReachablePreservedWrappers(node, cx, arg); + + *_retval = 1; + return NS_OK; } NS_IMETHODIMP @@ -5807,7 +6178,7 @@ nsDocumentSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSString *val = ::JS_ValueToString(cx, *vp); NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); - nsresult rv = location->SetHref(nsDependentJSString(val)); + rv = location->SetHref(nsDependentJSString(val)); NS_ENSURE_SUCCESS(rv, rv); return WrapNative(cx, obj, location, NS_GET_IID(nsIDOMLocation), vp); diff --git a/dom/src/base/nsDOMClassInfo.h b/dom/src/base/nsDOMClassInfo.h index 07e3f71845bf..aa644692627b 100644 --- a/dom/src/base/nsDOMClassInfo.h +++ b/dom/src/base/nsDOMClassInfo.h @@ -48,6 +48,7 @@ class nsIDOMWindow; class nsIDOMNSHTMLOptionCollection; class nsIPluginInstance; class nsIForm; +class nsIDOMNode; class nsIDOMNodeList; class nsIDOMDocument; class nsIHTMLDocument; @@ -134,6 +135,36 @@ public: static JSClass sDOMJSClass; + /** + * Note that the XPConnect wrapper for |aDOMNode| should be preserved. + */ + static nsresult PreserveWrapper(nsIDOMNode *aDOMNode, + nsIXPConnectWrappedNative *aWrapper); + + /** + * Undoes the effects of any prior |PreserveWrapper| calls on + * |aDOMNode|. + */ + static void ReleaseWrapper(nsIDOMNode *aDOMNode); + + /** + * Mark all preserved wrappers reachable from |aDOMNode| via DOM APIs. + */ + static void MarkReachablePreservedWrappers(nsIDOMNode *aDOMNode, + JSContext *cx, void *arg); + + /** + * Classify the wrappers for use by |MarkReachablePreservedWrappers| + * during the GC. Returns false to indicate failure (out-of-memory). + */ + static PRBool BeginGCMark(); + + /** + * Clean up data structures (and strong references) created by + * |BeginGCMark|. + */ + static void EndGCMark(); + protected: const nsDOMClassInfoData* mData; @@ -431,6 +462,10 @@ public: JSObject *globalObj, JSObject **parentObj); NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, jsval *vp, PRBool *_retval); + NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); + NS_IMETHOD Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, void *arg, PRUint32 *_retval); NS_IMETHOD GetFlags(PRUint32 *aFlags); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index 29a0a580e391..3c706334b9ef 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -2022,6 +2022,8 @@ DOMGCCallback(JSContext *cx, JSGCStatus status) { if (status == JSGC_BEGIN && PR_GetCurrentThread() != gDOMThread) return JS_FALSE; + if (status == JSGC_MARK_END) + nsDOMClassInfo::EndGCMark(); return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; } diff --git a/dom/src/base/nsJSEnvironment.h b/dom/src/base/nsJSEnvironment.h index 8f40b7c1f645..91217b14a81c 100644 --- a/dom/src/base/nsJSEnvironment.h +++ b/dom/src/base/nsJSEnvironment.h @@ -185,8 +185,6 @@ public: static nsresult Init(); - static nsJSEnvironment *GetScriptingEnvironment(); - static nsresult CreateNewContext(nsIScriptContext **aContext); static void ShutDown();