From: Gabriel Ivăncescu gabrielopcode@gmail.com
Example of non-contrived cyclic ref:
document.body.foobar = function() {} var enumerator = new Enumerator(document.getElementsByTagName("body"));
body element (BODY) has a prop with a function (FUNC) which holds link to the enclosing scope (SCOPE). The scope holds link to "enumerator" (ENUM) which holds a link to the IEnumVARIANT of the element collection (ELEMCOL), which holds a link to body element. So: BODY->FUNC->SCOPE->ENUM->ELEMCOL->BODY.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/dispex.c | 61 ++++++++++++++++++++++++++++++------ dlls/mshtml/mshtml_private.h | 3 +- dlls/mshtml/nsembed.c | 1 + 3 files changed, 55 insertions(+), 10 deletions(-)
diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index f5e6462bf54..8d414c3726b 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -2901,6 +2901,8 @@ static IWineJSDispatchHostVtbl JSDispatchHostVtbl = { JSDispatchHost_ToString, };
+static ExternalCycleCollectionParticipant enum_ccp; + static inline CollectionEnum *CollectionEnum_from_IEnumVARIANT(IEnumVARIANT *iface) { return CONTAINING_RECORD(iface, CollectionEnum, IEnumVARIANT_iface); @@ -2912,9 +2914,15 @@ HRESULT WINAPI CollectionEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, v
TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
- if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IEnumVARIANT)) + if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IEnumVARIANT)) { *ppv = &This->IEnumVARIANT_iface; - else { + }else if(IsEqualGUID(&IID_nsXPCOMCycleCollectionParticipant, riid)) { + *ppv = &enum_ccp; + return S_OK; + }else if(IsEqualGUID(&IID_nsCycleCollectionISupports, riid)) { + *ppv = &This->IEnumVARIANT_iface; + return S_OK; + }else { FIXME("Unsupported iface %s\n", debugstr_mshtml_guid(riid)); *ppv = NULL; return E_NOINTERFACE; @@ -2927,7 +2935,7 @@ HRESULT WINAPI CollectionEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, v ULONG WINAPI CollectionEnum_AddRef(IEnumVARIANT *iface) { CollectionEnum *This = CollectionEnum_from_IEnumVARIANT(iface); - LONG ref = InterlockedIncrement(&This->ref); + LONG ref = ccref_incr(&This->ccref, (nsISupports*)&This->IEnumVARIANT_iface);
TRACE("(%p) ref=%ld\n", This, ref);
@@ -2937,27 +2945,62 @@ ULONG WINAPI CollectionEnum_AddRef(IEnumVARIANT *iface) ULONG WINAPI CollectionEnum_Release(IEnumVARIANT *iface) { CollectionEnum *This = CollectionEnum_from_IEnumVARIANT(iface); - LONG ref = InterlockedDecrement(&This->ref); + LONG ref = ccref_decr(&This->ccref, (nsISupports*)&This->IEnumVARIANT_iface, &enum_ccp);
TRACE("(%p) ref=%ld\n", This, ref);
- if(!ref) { - DispatchEx_Release(&This->disp->IWineJSDispatchHost_iface); - free(This); + return ref; +} + +static nsresult NSAPI CollectionEnum_traverse(void *ccp, void *p, nsCycleCollectionTraversalCallback *cb) +{ + CollectionEnum *This = CollectionEnum_from_IEnumVARIANT(p); + + describe_cc_node(&This->ccref, "CollectionEnum", cb); + + if(This->disp) + note_cc_edge((nsISupports*)&This->disp->IWineJSDispatchHost_iface, "disp", cb); + return NS_OK; +} + +static nsresult NSAPI CollectionEnum_unlink(void *p) +{ + CollectionEnum *This = CollectionEnum_from_IEnumVARIANT(p); + + if(This->disp) { + DispatchEx *disp = This->disp; + This->disp = NULL; + DispatchEx_Release(&disp->IWineJSDispatchHost_iface); } + return NS_OK; +}
- return ref; +static void NSAPI CollectionEnum_delete_cycle_collectable(void *p) +{ + CollectionEnum *This = CollectionEnum_from_IEnumVARIANT(p); + CollectionEnum_unlink(p); + free(This); }
IUnknown *CollectionEnum_init(CollectionEnum *colenum, DispatchEx *disp, const IEnumVARIANTVtbl *vtbl) { colenum->IEnumVARIANT_iface.lpVtbl = vtbl; - colenum->ref = 1; colenum->disp = disp; + ccref_init(&colenum->ccref, 1); DispatchEx_AddRef(&disp->IWineJSDispatchHost_iface); return (IUnknown*)&colenum->IEnumVARIANT_iface; }
+void init_enum_cc(void) +{ + static const CCObjCallback ccp_callback = { + CollectionEnum_traverse, + CollectionEnum_unlink, + CollectionEnum_delete_cycle_collectable + }; + ccp_init(&enum_ccp, &ccp_callback); +} + HRESULT dispex_builtin_props_to_json(DispatchEx *dispex, HTMLInnerWindow *window, VARIANT *ret) { func_info_t *func, *end; diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 9b24bfd9a6e..bd16e3e35bf 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -611,7 +611,7 @@ struct DispatchEx {
typedef struct { IEnumVARIANT IEnumVARIANT_iface; - LONG ref; + nsCycleCollectingAutoRefCnt ccref; DispatchEx *disp; } CollectionEnum;
@@ -1265,6 +1265,7 @@ float get_viewer_zoom(GeckoBrowser*);
void init_dispex_cc(void); void init_window_cc(void); +void init_enum_cc(void);
HRESULT nsuri_to_url(LPCWSTR,BOOL,BSTR*);
diff --git a/dlls/mshtml/nsembed.c b/dlls/mshtml/nsembed.c index 62e1922cf6c..d6dba02551a 100644 --- a/dlls/mshtml/nsembed.c +++ b/dlls/mshtml/nsembed.c @@ -599,6 +599,7 @@ static BOOL init_xpcom(const PRUnichar *gre_path)
init_dispex_cc(); init_window_cc(); + init_enum_cc();
return TRUE; }