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();