gdi32: Fix leak in GdiDeleteSpoolFileHandle.
[wine.git] / dlls / mshtml / dispex.c
blobe47e4d2dc4538e0a47d2ce5b2e69fac200724c4c
1 /*
2 * Copyright 2008-2009 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
21 #define COBJMACROS
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "ole2.h"
26 #include "mscoree.h"
28 #include "wine/debug.h"
30 #include "mshtml_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
34 #define MAX_ARGS 16
36 static ExternalCycleCollectionParticipant dispex_ccp;
38 static CRITICAL_SECTION cs_dispex_static_data;
39 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
41 0, 0, &cs_dispex_static_data,
42 { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
43 0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
45 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
47 typedef struct {
48 IID iid;
49 VARIANT default_value;
50 } func_arg_info_t;
52 typedef struct {
53 DISPID id;
54 BSTR name;
55 tid_t tid;
56 dispex_hook_invoke_t hook;
57 SHORT call_vtbl_off;
58 SHORT put_vtbl_off;
59 SHORT get_vtbl_off;
60 SHORT func_disp_idx;
61 USHORT argc;
62 USHORT default_value_cnt;
63 VARTYPE prop_vt;
64 VARTYPE *arg_types;
65 func_arg_info_t *arg_info;
66 } func_info_t;
68 struct dispex_data_t {
69 dispex_static_data_t *desc;
70 compat_mode_t compat_mode;
72 DWORD func_cnt;
73 DWORD func_size;
74 func_info_t *funcs;
75 func_info_t **name_table;
76 DWORD func_disp_cnt;
78 struct list entry;
81 typedef struct {
82 VARIANT var;
83 LPWSTR name;
84 DWORD flags;
85 } dynamic_prop_t;
87 #define DYNPROP_DELETED 0x01
88 #define DYNPROP_HIDDEN 0x02
90 typedef struct {
91 DispatchEx dispex;
92 DispatchEx *obj;
93 func_info_t *info;
94 } func_disp_t;
96 typedef struct {
97 func_disp_t *func_obj;
98 VARIANT val;
99 } func_obj_entry_t;
101 struct dispex_dynamic_data_t {
102 DWORD buf_size;
103 DWORD prop_cnt;
104 dynamic_prop_t *props;
105 func_obj_entry_t *func_disps;
108 #define DISPID_DYNPROP_0 0x50000000
109 #define DISPID_DYNPROP_MAX 0x5fffffff
111 #define FDEX_VERSION_MASK 0xf0000000
113 static ITypeLib *typelib, *typelib_private;
114 static ITypeInfo *typeinfos[LAST_tid];
115 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
117 static REFIID tid_ids[] = {
118 #define XIID(iface) &IID_ ## iface,
119 #define XDIID(iface) &DIID_ ## iface,
120 TID_LIST
121 NULL,
122 PRIVATE_TID_LIST
123 #undef XIID
124 #undef XDIID
127 static HRESULT load_typelib(void)
129 WCHAR module_path[MAX_PATH + 3];
130 HRESULT hres;
131 ITypeLib *tl;
132 DWORD len;
134 hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
135 if(FAILED(hres)) {
136 ERR("LoadRegTypeLib failed: %08lx\n", hres);
137 return hres;
140 if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
141 ITypeLib_Release(tl);
143 len = GetModuleFileNameW(hInst, module_path, MAX_PATH + 1);
144 if (!len || len == MAX_PATH + 1)
146 ERR("Could not get module file name, len %lu.\n", len);
147 return E_FAIL;
149 lstrcatW(module_path, L"\\1");
151 hres = LoadTypeLibEx(module_path, REGKIND_NONE, &tl);
152 if(FAILED(hres)) {
153 ERR("LoadTypeLibEx failed for private typelib: %08lx\n", hres);
154 return hres;
157 if(InterlockedCompareExchangePointer((void**)&typelib_private, tl, NULL))
158 ITypeLib_Release(tl);
160 return S_OK;
163 static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
165 HRESULT hres;
167 if (!typelib)
168 hres = load_typelib();
169 if (!typelib)
170 return hres;
172 if(!typeinfos[tid]) {
173 ITypeInfo *ti;
175 hres = ITypeLib_GetTypeInfoOfGuid(tid > LAST_public_tid ? typelib_private : typelib, tid_ids[tid], &ti);
176 if(FAILED(hres)) {
177 ERR("GetTypeInfoOfGuid(%s) failed: %08lx\n", debugstr_mshtml_guid(tid_ids[tid]), hres);
178 return hres;
181 if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL))
182 ITypeInfo_Release(ti);
185 *typeinfo = typeinfos[tid];
186 return S_OK;
189 void release_typelib(void)
191 dispex_data_t *iter;
192 unsigned i, j;
194 while(!list_empty(&dispex_data_list)) {
195 iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
196 list_remove(&iter->entry);
198 for(i = 0; i < iter->func_cnt; i++) {
199 if(iter->funcs[i].default_value_cnt && iter->funcs[i].arg_info) {
200 for(j = 0; j < iter->funcs[i].argc; j++)
201 VariantClear(&iter->funcs[i].arg_info[j].default_value);
203 free(iter->funcs[i].arg_types);
204 free(iter->funcs[i].arg_info);
205 SysFreeString(iter->funcs[i].name);
208 free(iter->funcs);
209 free(iter->name_table);
210 free(iter);
213 if(!typelib)
214 return;
216 for(i=0; i < ARRAY_SIZE(typeinfos); i++)
217 if(typeinfos[i])
218 ITypeInfo_Release(typeinfos[i]);
220 ITypeLib_Release(typelib);
221 ITypeLib_Release(typelib_private);
222 DeleteCriticalSection(&cs_dispex_static_data);
225 HRESULT get_class_typeinfo(const CLSID *clsid, ITypeInfo **typeinfo)
227 HRESULT hres;
229 if (!typelib)
230 hres = load_typelib();
231 if (!typelib)
232 return hres;
234 hres = ITypeLib_GetTypeInfoOfGuid(typelib, clsid, typeinfo);
235 if (FAILED(hres))
236 hres = ITypeLib_GetTypeInfoOfGuid(typelib_private, clsid, typeinfo);
237 if(FAILED(hres))
238 ERR("GetTypeInfoOfGuid failed: %08lx\n", hres);
239 return hres;
242 /* Not all argument types are supported yet */
243 #define BUILTIN_ARG_TYPES_SWITCH \
244 CASE_VT(VT_I2, INT16, V_I2); \
245 CASE_VT(VT_UI2, UINT16, V_UI2); \
246 CASE_VT(VT_I4, INT32, V_I4); \
247 CASE_VT(VT_UI4, UINT32, V_UI4); \
248 CASE_VT(VT_R4, float, V_R4); \
249 CASE_VT(VT_BSTR, BSTR, V_BSTR); \
250 CASE_VT(VT_DISPATCH, IDispatch*, V_DISPATCH); \
251 CASE_VT(VT_BOOL, VARIANT_BOOL, V_BOOL)
253 /* List all types used by IDispatchEx-based properties */
254 #define BUILTIN_TYPES_SWITCH \
255 BUILTIN_ARG_TYPES_SWITCH; \
256 CASE_VT(VT_VARIANT, VARIANT, *); \
257 CASE_VT(VT_PTR, void*, V_BYREF); \
258 CASE_VT(VT_UNKNOWN, IUnknown*, V_UNKNOWN); \
259 CASE_VT(VT_UI8, ULONGLONG, V_UI8)
261 static BOOL is_arg_type_supported(VARTYPE vt)
263 switch(vt) {
264 #define CASE_VT(x,a,b) case x: return TRUE
265 BUILTIN_ARG_TYPES_SWITCH;
266 #undef CASE_VT
268 return FALSE;
271 static void add_func_info(dispex_data_t *data, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti,
272 dispex_hook_invoke_t hook, const WCHAR *name_override)
274 func_info_t *info;
275 BSTR name;
276 HRESULT hres;
278 if(name_override)
279 name = SysAllocString(name_override);
280 else {
281 hres = ITypeInfo_GetDocumentation(dti, desc->memid, &name, NULL, NULL, NULL);
282 if(FAILED(hres)) {
283 WARN("GetDocumentation failed: %08lx\n", hres);
284 return;
288 for(info = data->funcs; info < data->funcs+data->func_cnt; info++) {
289 if(info->id == desc->memid || !wcscmp(info->name, name)) {
290 if(info->tid != tid) {
291 SysFreeString(name);
292 return; /* Duplicated in other interface */
294 break;
298 TRACE("adding %s...\n", debugstr_w(name));
300 if(info == data->funcs+data->func_cnt) {
301 if(data->func_cnt == data->func_size) {
302 info = realloc(data->funcs, data->func_size * 2 * sizeof(func_info_t));
303 if(!info) {
304 SysFreeString(name);
305 return;
307 memset(info + data->func_size, 0, data->func_size * sizeof(func_info_t));
308 data->funcs = info;
309 data->func_size *= 2;
311 info = data->funcs+data->func_cnt;
313 data->func_cnt++;
315 info->id = desc->memid;
316 info->name = name;
317 info->tid = tid;
318 info->func_disp_idx = -1;
319 info->prop_vt = VT_EMPTY;
320 info->hook = hook;
321 }else {
322 SysFreeString(name);
325 if(desc->invkind & DISPATCH_METHOD) {
326 unsigned i;
328 info->func_disp_idx = data->func_disp_cnt++;
329 info->argc = desc->cParams;
331 assert(info->argc < MAX_ARGS);
332 assert(desc->funckind == FUNC_DISPATCH);
334 info->arg_info = calloc(info->argc, sizeof(*info->arg_info));
335 if(!info->arg_info)
336 return;
338 info->prop_vt = desc->elemdescFunc.tdesc.vt;
339 if(info->prop_vt != VT_VOID && info->prop_vt != VT_PTR && !is_arg_type_supported(info->prop_vt)) {
340 TRACE("%s: return type %d\n", debugstr_w(info->name), info->prop_vt);
341 return; /* Fallback to ITypeInfo::Invoke */
344 info->arg_types = malloc(sizeof(*info->arg_types) * (info->argc + (info->prop_vt == VT_VOID ? 0 : 1)));
345 if(!info->arg_types)
346 return;
348 for(i=0; i < info->argc; i++)
349 info->arg_types[i] = desc->lprgelemdescParam[i].tdesc.vt;
351 if(info->prop_vt == VT_PTR)
352 info->arg_types[info->argc] = VT_BYREF | VT_DISPATCH;
353 else if(info->prop_vt != VT_VOID)
354 info->arg_types[info->argc] = VT_BYREF | info->prop_vt;
356 if(desc->cParamsOpt) {
357 TRACE("%s: optional params\n", debugstr_w(info->name));
358 return; /* Fallback to ITypeInfo::Invoke */
361 for(i=0; i < info->argc; i++) {
362 TYPEDESC *tdesc = &desc->lprgelemdescParam[i].tdesc;
363 if(tdesc->vt == VT_PTR && tdesc->lptdesc->vt == VT_USERDEFINED) {
364 ITypeInfo *ref_type_info;
365 TYPEATTR *attr;
367 hres = ITypeInfo_GetRefTypeInfo(dti, tdesc->lptdesc->hreftype, &ref_type_info);
368 if(FAILED(hres)) {
369 ERR("Could not get referenced type info: %08lx\n", hres);
370 return;
373 hres = ITypeInfo_GetTypeAttr(ref_type_info, &attr);
374 if(SUCCEEDED(hres)) {
375 assert(attr->typekind == TKIND_DISPATCH);
376 info->arg_info[i].iid = attr->guid;
377 ITypeInfo_ReleaseTypeAttr(ref_type_info, attr);
378 }else {
379 ERR("GetTypeAttr failed: %08lx\n", hres);
381 ITypeInfo_Release(ref_type_info);
382 if(FAILED(hres))
383 return;
384 info->arg_types[i] = VT_DISPATCH;
385 }else if(!is_arg_type_supported(info->arg_types[i])) {
386 TRACE("%s: unsupported arg type %s\n", debugstr_w(info->name), debugstr_vt(info->arg_types[i]));
387 return; /* Fallback to ITypeInfo for unsupported arg types */
390 if(desc->lprgelemdescParam[i].paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) {
391 hres = VariantCopy(&info->arg_info[i].default_value,
392 &desc->lprgelemdescParam[i].paramdesc.pparamdescex->varDefaultValue);
393 if(FAILED(hres)) {
394 ERR("Could not copy default value: %08lx\n", hres);
395 return;
397 TRACE("%s param %d: default value %s\n", debugstr_w(info->name),
398 i, debugstr_variant(&info->arg_info[i].default_value));
399 info->default_value_cnt++;
403 assert(info->argc <= MAX_ARGS);
404 assert(desc->callconv == CC_STDCALL);
406 info->call_vtbl_off = desc->oVft/sizeof(void*);
407 }else if(desc->invkind & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET)) {
408 VARTYPE vt = VT_EMPTY;
410 if(desc->wFuncFlags & FUNCFLAG_FHIDDEN)
411 info->func_disp_idx = -2;
413 if(desc->invkind & DISPATCH_PROPERTYGET) {
414 vt = desc->elemdescFunc.tdesc.vt;
415 info->get_vtbl_off = desc->oVft/sizeof(void*);
417 if(desc->invkind & DISPATCH_PROPERTYPUT) {
418 assert(desc->cParams == 1);
419 vt = desc->lprgelemdescParam->tdesc.vt;
420 info->put_vtbl_off = desc->oVft/sizeof(void*);
423 assert(info->prop_vt == VT_EMPTY || vt == info->prop_vt);
424 info->prop_vt = vt;
428 static HRESULT process_interface(dispex_data_t *data, tid_t tid, ITypeInfo *disp_typeinfo, const dispex_hook_t *hooks)
430 unsigned i = 7; /* skip IDispatch functions */
431 ITypeInfo *typeinfo;
432 FUNCDESC *funcdesc;
433 HRESULT hres;
435 hres = get_typeinfo(tid, &typeinfo);
436 if(FAILED(hres))
437 return hres;
439 while(1) {
440 const dispex_hook_t *hook = NULL;
442 hres = ITypeInfo_GetFuncDesc(typeinfo, i++, &funcdesc);
443 if(FAILED(hres))
444 break;
446 if(hooks) {
447 for(hook = hooks; hook->dispid != DISPID_UNKNOWN; hook++) {
448 if(hook->dispid == funcdesc->memid)
449 break;
451 if(hook->dispid == DISPID_UNKNOWN)
452 hook = NULL;
455 if(!hook || hook->invoke || hook->name) {
456 add_func_info(data, tid, funcdesc, disp_typeinfo ? disp_typeinfo : typeinfo,
457 hook ? hook->invoke : NULL, hook ? hook->name : NULL);
460 ITypeInfo_ReleaseFuncDesc(typeinfo, funcdesc);
463 return S_OK;
466 void dispex_info_add_interface(dispex_data_t *info, tid_t tid, const dispex_hook_t *hooks)
468 HRESULT hres;
470 hres = process_interface(info, tid, NULL, hooks);
471 if(FAILED(hres))
472 ERR("process_interface failed: %08lx\n", hres);
475 static int __cdecl dispid_cmp(const void *p1, const void *p2)
477 return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id;
480 static int __cdecl func_name_cmp(const void *p1, const void *p2)
482 return wcsicmp((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name);
485 static dispex_data_t *preprocess_dispex_data(dispex_static_data_t *desc, compat_mode_t compat_mode)
487 const tid_t *tid;
488 dispex_data_t *data;
489 DWORD i;
490 ITypeInfo *dti;
491 HRESULT hres;
493 if(desc->disp_tid) {
494 hres = get_typeinfo(desc->disp_tid, &dti);
495 if(FAILED(hres)) {
496 ERR("Could not get disp type info: %08lx\n", hres);
497 return NULL;
501 data = malloc(sizeof(dispex_data_t));
502 if (!data) {
503 ERR("Out of memory\n");
504 return NULL;
506 data->desc = desc;
507 data->compat_mode = compat_mode;
508 data->func_cnt = 0;
509 data->func_disp_cnt = 0;
510 data->func_size = 16;
511 data->funcs = calloc(data->func_size, sizeof(func_info_t));
512 if (!data->funcs) {
513 free(data);
514 ERR("Out of memory\n");
515 return NULL;
517 list_add_tail(&dispex_data_list, &data->entry);
519 if(desc->init_info)
520 desc->init_info(data, compat_mode);
522 for(tid = desc->iface_tids; *tid; tid++) {
523 hres = process_interface(data, *tid, dti, NULL);
524 if(FAILED(hres))
525 break;
528 if(!data->func_cnt) {
529 free(data->funcs);
530 data->name_table = NULL;
531 data->funcs = NULL;
532 data->func_size = 0;
533 return data;
537 data->funcs = realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
538 qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
540 data->name_table = malloc(data->func_cnt * sizeof(func_info_t*));
541 for(i=0; i < data->func_cnt; i++)
542 data->name_table[i] = data->funcs+i;
543 qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
544 return data;
547 static int __cdecl id_cmp(const void *p1, const void *p2)
549 return *(const DISPID*)p1 - *(const DISPID*)p2;
552 HRESULT get_dispids(tid_t tid, DWORD *ret_size, DISPID **ret)
554 unsigned i, func_cnt;
555 FUNCDESC *funcdesc;
556 ITypeInfo *ti;
557 TYPEATTR *attr;
558 DISPID *ids;
559 HRESULT hres;
561 hres = get_typeinfo(tid, &ti);
562 if(FAILED(hres))
563 return hres;
565 hres = ITypeInfo_GetTypeAttr(ti, &attr);
566 if(FAILED(hres)) {
567 ITypeInfo_Release(ti);
568 return hres;
571 func_cnt = attr->cFuncs;
572 ITypeInfo_ReleaseTypeAttr(ti, attr);
574 ids = malloc(func_cnt * sizeof(DISPID));
575 if(!ids) {
576 ITypeInfo_Release(ti);
577 return E_OUTOFMEMORY;
580 for(i=0; i < func_cnt; i++) {
581 hres = ITypeInfo_GetFuncDesc(ti, i, &funcdesc);
582 if(FAILED(hres))
583 break;
585 ids[i] = funcdesc->memid;
586 ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
589 ITypeInfo_Release(ti);
590 if(FAILED(hres)) {
591 free(ids);
592 return hres;
595 qsort(ids, func_cnt, sizeof(DISPID), id_cmp);
597 *ret_size = func_cnt;
598 *ret = ids;
599 return S_OK;
602 static inline BOOL is_custom_dispid(DISPID id)
604 return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
607 static inline BOOL is_dynamic_dispid(DISPID id)
609 return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
612 dispex_prop_type_t get_dispid_type(DISPID id)
614 if(is_dynamic_dispid(id))
615 return DISPEXPROP_DYNAMIC;
616 if(is_custom_dispid(id))
617 return DISPEXPROP_CUSTOM;
618 return DISPEXPROP_BUILTIN;
621 static HRESULT variant_copy(VARIANT *dest, VARIANT *src)
623 if(V_VT(src) == VT_BSTR && !V_BSTR(src)) {
624 V_VT(dest) = VT_BSTR;
625 V_BSTR(dest) = NULL;
626 return S_OK;
629 return VariantCopy(dest, src);
632 static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This)
634 if(This->dynamic_data)
635 return This->dynamic_data;
637 This->dynamic_data = calloc(1, sizeof(dispex_dynamic_data_t));
638 if(!This->dynamic_data)
639 return NULL;
641 if(This->info->desc->vtbl->populate_props)
642 This->info->desc->vtbl->populate_props(This);
644 return This->dynamic_data;
647 static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, DWORD flags, dynamic_prop_t **ret)
649 const BOOL alloc = flags & fdexNameEnsure;
650 dispex_dynamic_data_t *data;
651 dynamic_prop_t *prop;
653 data = get_dynamic_data(This);
654 if(!data)
655 return E_OUTOFMEMORY;
657 for(prop = data->props; prop < data->props+data->prop_cnt; prop++) {
658 if(flags & fdexNameCaseInsensitive ? !wcsicmp(prop->name, name) : !wcscmp(prop->name, name)) {
659 if(prop->flags & DYNPROP_DELETED) {
660 if(!alloc)
661 return DISP_E_UNKNOWNNAME;
662 prop->flags &= ~DYNPROP_DELETED;
664 *ret = prop;
665 return S_OK;
669 if(!alloc)
670 return DISP_E_UNKNOWNNAME;
672 TRACE("creating dynamic prop %s\n", debugstr_w(name));
674 if(!data->buf_size) {
675 data->props = malloc(sizeof(dynamic_prop_t) * 4);
676 if(!data->props)
677 return E_OUTOFMEMORY;
678 data->buf_size = 4;
679 }else if(data->buf_size == data->prop_cnt) {
680 dynamic_prop_t *new_props;
682 new_props = realloc(data->props, sizeof(dynamic_prop_t) * (data->buf_size << 1));
683 if(!new_props)
684 return E_OUTOFMEMORY;
686 data->props = new_props;
687 data->buf_size <<= 1;
690 prop = data->props + data->prop_cnt;
692 prop->name = wcsdup(name);
693 if(!prop->name)
694 return E_OUTOFMEMORY;
696 VariantInit(&prop->var);
697 prop->flags = 0;
698 data->prop_cnt++;
699 *ret = prop;
700 return S_OK;
703 HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
705 dynamic_prop_t *prop;
706 HRESULT hres;
708 hres = get_dynamic_prop(This, name, alloc ? fdexNameEnsure : 0, &prop);
709 if(FAILED(hres))
710 return hres;
712 if(alloc)
713 prop->flags |= DYNPROP_HIDDEN;
714 *ret = &prop->var;
715 return S_OK;
718 HRESULT dispex_get_dynid(DispatchEx *This, const WCHAR *name, BOOL hidden, DISPID *id)
720 dynamic_prop_t *prop;
721 HRESULT hres;
723 hres = get_dynamic_prop(This, name, fdexNameEnsure, &prop);
724 if(FAILED(hres))
725 return hres;
727 if(hidden)
728 prop->flags |= DYNPROP_HIDDEN;
729 *id = DISPID_DYNPROP_0 + (prop - This->dynamic_data->props);
730 return S_OK;
733 static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params,
734 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
736 HRESULT hres;
738 if(This->info->desc->vtbl->value)
739 return This->info->desc->vtbl->value(This, lcid, flags, params, res, ei, caller);
741 switch(flags) {
742 case DISPATCH_PROPERTYGET:
743 V_VT(res) = VT_BSTR;
744 hres = dispex_to_string(This, &V_BSTR(res));
745 if(FAILED(hres))
746 return hres;
747 break;
748 default:
749 FIXME("Unimplemented flags %x\n", flags);
750 return E_NOTIMPL;
753 return S_OK;
756 static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
757 EXCEPINFO *ei)
759 DISPPARAMS params = {dp->rgvarg, NULL, dp->cArgs, 0};
760 ITypeInfo *ti;
761 IUnknown *unk;
762 UINT argerr=0;
763 HRESULT hres;
765 if(params.cArgs > func->argc) {
766 params.rgvarg += params.cArgs - func->argc;
767 params.cArgs = func->argc;
770 hres = get_typeinfo(func->tid, &ti);
771 if(FAILED(hres)) {
772 ERR("Could not get type info: %08lx\n", hres);
773 return hres;
776 hres = IDispatchEx_QueryInterface(&This->IDispatchEx_iface, tid_ids[func->tid], (void**)&unk);
777 if(FAILED(hres)) {
778 ERR("Could not get iface %s: %08lx\n", debugstr_mshtml_guid(tid_ids[func->tid]), hres);
779 return E_FAIL;
782 hres = ITypeInfo_Invoke(ti, unk, func->id, flags, &params, res, ei, &argerr);
784 IUnknown_Release(unk);
785 return hres;
788 static inline func_disp_t *impl_from_DispatchEx(DispatchEx *iface)
790 return CONTAINING_RECORD(iface, func_disp_t, dispex);
793 static void function_destructor(DispatchEx *dispex)
795 func_disp_t *This = impl_from_DispatchEx(dispex);
796 assert(!This->obj);
797 free(This);
800 static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *params,
801 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
803 func_disp_t *This = impl_from_DispatchEx(dispex);
804 HRESULT hres;
806 switch(flags) {
807 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
808 if(!res)
809 return E_INVALIDARG;
810 /* fall through */
811 case DISPATCH_METHOD:
812 if(!This->obj)
813 return E_UNEXPECTED;
814 hres = dispex_call_builtin(This->obj, This->info->id, params, res, ei, caller);
815 break;
816 case DISPATCH_PROPERTYGET: {
817 unsigned name_len;
818 WCHAR *ptr;
819 BSTR str;
821 static const WCHAR func_prefixW[] =
822 {'\n','f','u','n','c','t','i','o','n',' '};
823 static const WCHAR func_suffixW[] =
824 {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};
826 /* FIXME: This probably should be more generic. Also we should try to get IID_IActiveScriptSite and SID_GetCaller. */
827 if(!caller)
828 return E_ACCESSDENIED;
830 name_len = SysStringLen(This->info->name);
831 ptr = str = SysAllocStringLen(NULL, name_len + ARRAY_SIZE(func_prefixW) + ARRAY_SIZE(func_suffixW));
832 if(!str)
833 return E_OUTOFMEMORY;
835 memcpy(ptr, func_prefixW, sizeof(func_prefixW));
836 ptr += ARRAY_SIZE(func_prefixW);
838 memcpy(ptr, This->info->name, name_len*sizeof(WCHAR));
839 ptr += name_len;
841 memcpy(ptr, func_suffixW, sizeof(func_suffixW));
843 V_VT(res) = VT_BSTR;
844 V_BSTR(res) = str;
845 return S_OK;
847 default:
848 FIXME("Unimplemented flags %x\n", flags);
849 hres = E_NOTIMPL;
852 return hres;
855 static const dispex_static_data_vtbl_t function_dispex_vtbl = {
856 .destructor = function_destructor,
857 .value = function_value,
860 static const tid_t function_iface_tids[] = {0};
862 static dispex_static_data_t function_dispex = {
863 "Function",
864 &function_dispex_vtbl,
865 NULL_tid,
866 function_iface_tids
869 static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info)
871 func_disp_t *ret;
873 ret = calloc(1, sizeof(func_disp_t));
874 if(!ret)
875 return NULL;
877 init_dispatch(&ret->dispex, &function_dispex, dispex_compat_mode(obj));
878 ret->obj = obj;
879 ret->info = info;
881 return ret;
884 static HRESULT invoke_disp_value(DispatchEx *This, IDispatch *func_disp, LCID lcid, WORD flags, DISPPARAMS *dp,
885 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
887 DISPID named_arg = DISPID_THIS;
888 DISPPARAMS new_dp = {NULL, &named_arg, 0, 1};
889 IDispatchEx *dispex;
890 HRESULT hres;
892 if(dp->cNamedArgs) {
893 FIXME("named args not supported\n");
894 return E_NOTIMPL;
897 new_dp.rgvarg = malloc((dp->cArgs + 1) * sizeof(VARIANTARG));
898 if(!new_dp.rgvarg)
899 return E_OUTOFMEMORY;
901 new_dp.cArgs = dp->cArgs+1;
902 memcpy(new_dp.rgvarg+1, dp->rgvarg, dp->cArgs*sizeof(VARIANTARG));
904 V_VT(new_dp.rgvarg) = VT_DISPATCH;
905 V_DISPATCH(new_dp.rgvarg) = (IDispatch*)&This->IDispatchEx_iface;
907 hres = IDispatch_QueryInterface(func_disp, &IID_IDispatchEx, (void**)&dispex);
908 TRACE(">>>\n");
909 if(SUCCEEDED(hres)) {
910 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, flags, &new_dp, res, ei, caller);
911 IDispatchEx_Release(dispex);
912 }else {
913 UINT err = 0;
914 hres = IDispatch_Invoke(func_disp, DISPID_VALUE, &IID_NULL, lcid, flags, &new_dp, res, ei, &err);
916 if(SUCCEEDED(hres))
917 TRACE("<<< %s\n", debugstr_variant(res));
918 else
919 WARN("<<< %08lx\n", hres);
921 free(new_dp.rgvarg);
922 return hres;
925 static HRESULT get_func_obj_entry(DispatchEx *This, func_info_t *func, func_obj_entry_t **ret)
927 dispex_dynamic_data_t *dynamic_data;
928 func_obj_entry_t *entry;
930 dynamic_data = get_dynamic_data(This);
931 if(!dynamic_data)
932 return E_OUTOFMEMORY;
934 if(!dynamic_data->func_disps) {
935 dynamic_data->func_disps = calloc(This->info->func_disp_cnt, sizeof(*dynamic_data->func_disps));
936 if(!dynamic_data->func_disps)
937 return E_OUTOFMEMORY;
940 entry = dynamic_data->func_disps + func->func_disp_idx;
941 if(!entry->func_obj) {
942 entry->func_obj = create_func_disp(This, func);
943 if(!entry->func_obj)
944 return E_OUTOFMEMORY;
946 IDispatchEx_AddRef(&entry->func_obj->dispex.IDispatchEx_iface);
947 V_VT(&entry->val) = VT_DISPATCH;
948 V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface;
951 *ret = entry;
952 return S_OK;
955 static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret)
957 int min, max, n;
959 min = 0;
960 max = data->func_cnt-1;
962 while(min <= max) {
963 n = (min+max)/2;
965 if(data->funcs[n].id == id) {
966 *ret = data->funcs+n;
967 return S_OK;
970 if(data->funcs[n].id < id)
971 min = n+1;
972 else
973 max = n-1;
976 WARN("invalid id %lx\n", id);
977 return DISP_E_MEMBERNOTFOUND;
980 static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret)
982 int min, max, n, c;
984 min = 0;
985 max = This->info->func_cnt-1;
987 while(min <= max) {
988 n = (min+max)/2;
990 c = wcsicmp(This->info->name_table[n]->name, name);
991 if(!c) {
992 if((grfdex & fdexNameCaseSensitive) && wcscmp(This->info->name_table[n]->name, name))
993 break;
995 *ret = This->info->name_table[n]->id;
996 return S_OK;
999 if(c > 0)
1000 max = n-1;
1001 else
1002 min = n+1;
1005 if(This->info->desc->vtbl->get_dispid) {
1006 HRESULT hres;
1008 hres = This->info->desc->vtbl->get_dispid(This, name, grfdex, ret);
1009 if(hres != DISP_E_UNKNOWNNAME)
1010 return hres;
1013 return DISP_E_UNKNOWNNAME;
1016 HRESULT change_type(VARIANT *dst, VARIANT *src, VARTYPE vt, IServiceProvider *caller)
1018 V_VT(dst) = VT_EMPTY;
1020 if(caller) {
1021 IVariantChangeType *change_type = NULL;
1022 HRESULT hres;
1024 hres = IServiceProvider_QueryService(caller, &SID_VariantConversion, &IID_IVariantChangeType, (void**)&change_type);
1025 if(SUCCEEDED(hres)) {
1026 hres = IVariantChangeType_ChangeType(change_type, dst, src, LOCALE_NEUTRAL, vt);
1027 IVariantChangeType_Release(change_type);
1028 if(SUCCEEDED(hres))
1029 return S_OK;
1033 switch(vt) {
1034 case VT_BOOL:
1035 if(V_VT(src) == VT_BSTR) {
1036 V_VT(dst) = VT_BOOL;
1037 V_BOOL(dst) = variant_bool(V_BSTR(src) && *V_BSTR(src));
1038 return S_OK;
1040 break;
1041 case VT_UNKNOWN:
1042 case VT_DISPATCH:
1043 if(V_VT(src) == VT_EMPTY || V_VT(src) == VT_NULL) {
1044 V_VT(dst) = vt;
1045 V_DISPATCH(dst) = NULL;
1046 return S_OK;
1048 break;
1051 return VariantChangeType(dst, src, 0, vt);
1054 static HRESULT builtin_propget(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res)
1056 IUnknown *iface;
1057 HRESULT hres;
1059 if(dp && dp->cArgs) {
1060 FIXME("cArgs %d\n", dp->cArgs);
1061 return E_NOTIMPL;
1064 assert(func->get_vtbl_off);
1066 hres = IDispatchEx_QueryInterface(&This->IDispatchEx_iface, tid_ids[func->tid], (void**)&iface);
1067 if(SUCCEEDED(hres)) {
1068 switch(func->prop_vt) {
1069 #define CASE_VT(vt,type,access) \
1070 case vt: { \
1071 type val; \
1072 hres = ((HRESULT (WINAPI*)(IUnknown*,type*))((void**)iface->lpVtbl)[func->get_vtbl_off])(iface,&val); \
1073 if(SUCCEEDED(hres)) \
1074 access(res) = val; \
1076 break
1077 BUILTIN_TYPES_SWITCH;
1078 #undef CASE_VT
1079 default:
1080 FIXME("Unhandled vt %d\n", func->prop_vt);
1081 hres = E_NOTIMPL;
1083 IUnknown_Release(iface);
1086 if(FAILED(hres))
1087 return hres;
1089 if(func->prop_vt != VT_VARIANT)
1090 V_VT(res) = func->prop_vt == VT_PTR ? VT_DISPATCH : func->prop_vt;
1091 return S_OK;
1094 static HRESULT builtin_propput(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, IServiceProvider *caller)
1096 VARIANT *v, tmpv;
1097 IUnknown *iface;
1098 HRESULT hres;
1100 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1101 || dp->cNamedArgs > 1) {
1102 FIXME("invalid args\n");
1103 return E_INVALIDARG;
1106 if(!func->put_vtbl_off) {
1107 if(dispex_compat_mode(This) >= COMPAT_MODE_IE9) {
1108 WARN("No setter\n");
1109 return S_OK;
1111 FIXME("No setter\n");
1112 return E_FAIL;
1115 v = dp->rgvarg;
1116 if(func->prop_vt != VT_VARIANT && V_VT(v) != func->prop_vt) {
1117 hres = change_type(&tmpv, v, func->prop_vt, caller);
1118 if(FAILED(hres))
1119 return hres;
1120 v = &tmpv;
1123 hres = IDispatchEx_QueryInterface(&This->IDispatchEx_iface, tid_ids[func->tid], (void**)&iface);
1124 if(SUCCEEDED(hres)) {
1125 switch(func->prop_vt) {
1126 #define CASE_VT(vt,type,access) \
1127 case vt: \
1128 hres = ((HRESULT (WINAPI*)(IUnknown*,type))((void**)iface->lpVtbl)[func->put_vtbl_off])(iface,access(v)); \
1129 break
1130 BUILTIN_TYPES_SWITCH;
1131 #undef CASE_VT
1132 default:
1133 FIXME("Unimplemented vt %d\n", func->prop_vt);
1134 hres = E_NOTIMPL;
1137 IUnknown_Release(iface);
1140 if(v == &tmpv)
1141 VariantClear(v);
1142 return hres;
1145 static HRESULT invoke_builtin_function(DispatchEx *This, func_info_t *func, DISPPARAMS *dp,
1146 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
1148 VARIANT arg_buf[MAX_ARGS], *arg_ptrs[MAX_ARGS], *arg, retv, ret_ref, vhres;
1149 unsigned i, nconv = 0;
1150 IUnknown *iface;
1151 HRESULT hres;
1153 if(func->hook) {
1154 hres = func->hook(This, DISPATCH_METHOD, dp, res, ei, caller);
1155 if(hres != S_FALSE)
1156 return hres;
1159 if(!func->call_vtbl_off)
1160 return typeinfo_invoke(This, func, DISPATCH_METHOD, dp, res, ei);
1162 if(dp->cArgs + func->default_value_cnt < func->argc) {
1163 FIXME("Invalid argument count (expected %u, got %u)\n", func->argc, dp->cArgs);
1164 return E_INVALIDARG;
1167 hres = IDispatchEx_QueryInterface(&This->IDispatchEx_iface, tid_ids[func->tid], (void**)&iface);
1168 if(FAILED(hres))
1169 return hres;
1171 for(i=0; i < func->argc; i++) {
1172 BOOL own_value = FALSE;
1173 if(i >= dp->cArgs) {
1174 /* use default value */
1175 arg_ptrs[i] = &func->arg_info[i].default_value;
1176 continue;
1178 arg = dp->rgvarg+dp->cArgs-i-1;
1179 if(func->arg_types[i] == V_VT(arg)) {
1180 arg_ptrs[i] = arg;
1181 }else {
1182 hres = change_type(arg_buf+nconv, arg, func->arg_types[i], caller);
1183 if(FAILED(hres))
1184 break;
1185 arg_ptrs[i] = arg_buf + nconv++;
1186 own_value = TRUE;
1189 if(func->arg_types[i] == VT_DISPATCH && !IsEqualGUID(&func->arg_info[i].iid, &IID_NULL)
1190 && V_DISPATCH(arg_ptrs[i])) {
1191 IDispatch *iface;
1192 if(!own_value) {
1193 arg_buf[nconv] = *arg_ptrs[i];
1194 arg_ptrs[i] = arg_buf + nconv++;
1196 hres = IDispatch_QueryInterface(V_DISPATCH(arg_ptrs[i]), &func->arg_info[i].iid, (void**)&iface);
1197 if(FAILED(hres)) {
1198 WARN("Could not get %s iface: %08lx\n", debugstr_guid(&func->arg_info[i].iid), hres);
1199 break;
1201 if(own_value)
1202 IDispatch_Release(V_DISPATCH(arg_ptrs[i]));
1203 V_DISPATCH(arg_ptrs[i]) = iface;
1207 if(SUCCEEDED(hres)) {
1208 if(func->prop_vt == VT_VOID) {
1209 V_VT(&retv) = VT_EMPTY;
1210 }else {
1211 V_VT(&retv) = func->prop_vt;
1212 arg_ptrs[func->argc] = &ret_ref;
1213 V_VT(&ret_ref) = VT_BYREF|func->prop_vt;
1215 switch(func->prop_vt) {
1216 #define CASE_VT(vt,type,access) \
1217 case vt: \
1218 V_BYREF(&ret_ref) = &access(&retv); \
1219 break
1220 BUILTIN_ARG_TYPES_SWITCH;
1221 #undef CASE_VT
1222 case VT_PTR:
1223 V_VT(&retv) = VT_DISPATCH;
1224 V_VT(&ret_ref) = VT_BYREF | VT_DISPATCH;
1225 V_BYREF(&ret_ref) = &V_DISPATCH(&retv);
1226 break;
1227 default:
1228 assert(0);
1232 V_VT(&vhres) = VT_ERROR;
1233 hres = DispCallFunc(iface, func->call_vtbl_off*sizeof(void*), CC_STDCALL, VT_ERROR,
1234 func->argc + (func->prop_vt == VT_VOID ? 0 : 1), func->arg_types, arg_ptrs, &vhres);
1237 while(nconv--)
1238 VariantClear(arg_buf+nconv);
1239 IUnknown_Release(iface);
1240 if(FAILED(hres))
1241 return hres;
1242 if(FAILED(V_ERROR(&vhres)))
1243 return V_ERROR(&vhres);
1245 if(res)
1246 *res = retv;
1247 else
1248 VariantClear(&retv);
1249 return V_ERROR(&vhres);
1252 static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
1253 EXCEPINFO *ei, IServiceProvider *caller)
1255 HRESULT hres;
1257 switch(flags) {
1258 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1259 if(!res)
1260 return E_INVALIDARG;
1261 /* fall through */
1262 case DISPATCH_METHOD:
1263 if(This->dynamic_data && This->dynamic_data->func_disps
1264 && This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1265 func_obj_entry_t *entry = This->dynamic_data->func_disps + func->func_disp_idx;
1267 if(V_VT(&entry->val) != VT_DISPATCH) {
1268 FIXME("calling %s not supported\n", debugstr_variant(&entry->val));
1269 return E_NOTIMPL;
1272 if((IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface != V_DISPATCH(&entry->val)) {
1273 if(!V_DISPATCH(&entry->val)) {
1274 FIXME("Calling null\n");
1275 return E_FAIL;
1278 hres = invoke_disp_value(This, V_DISPATCH(&entry->val), 0, flags, dp, res, ei, NULL);
1279 break;
1283 hres = invoke_builtin_function(This, func, dp, res, ei, caller);
1284 break;
1285 case DISPATCH_PROPERTYGET: {
1286 func_obj_entry_t *entry;
1288 if(func->id == DISPID_VALUE) {
1289 BSTR ret;
1291 hres = dispex_to_string(This, &ret);
1292 if(FAILED(hres))
1293 return hres;
1295 V_VT(res) = VT_BSTR;
1296 V_BSTR(res) = ret;
1297 return S_OK;
1300 hres = get_func_obj_entry(This, func, &entry);
1301 if(FAILED(hres))
1302 return hres;
1304 V_VT(res) = VT_EMPTY;
1305 return VariantCopy(res, &entry->val);
1307 case DISPATCH_PROPERTYPUT: {
1308 func_obj_entry_t *entry;
1310 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1311 || dp->cNamedArgs > 1) {
1312 FIXME("invalid args\n");
1313 return E_INVALIDARG;
1317 * NOTE: Although we have IDispatchEx tests showing, that it's not allowed to set
1318 * function property using InvokeEx, it's possible to do that from jscript.
1319 * Native probably uses some undocumented interface in this case, but it should
1320 * be fine for us to allow IDispatchEx handle that.
1322 hres = get_func_obj_entry(This, func, &entry);
1323 if(FAILED(hres))
1324 return hres;
1326 return VariantCopy(&entry->val, dp->rgvarg);
1328 default:
1329 FIXME("Unimplemented flags %x\n", flags);
1330 hres = E_NOTIMPL;
1333 return hres;
1336 static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp,
1337 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
1339 func_info_t *func;
1340 HRESULT hres;
1342 hres = get_builtin_func(This->info, id, &func);
1343 if(id == DISPID_VALUE && hres == DISP_E_MEMBERNOTFOUND)
1344 return dispex_value(This, lcid, flags, dp, res, ei, caller);
1345 if(FAILED(hres))
1346 return hres;
1348 if(func->func_disp_idx >= 0)
1349 return function_invoke(This, func, flags, dp, res, ei, caller);
1351 if(func->hook) {
1352 hres = func->hook(This, flags, dp, res, ei, caller);
1353 if(hres != S_FALSE)
1354 return hres;
1357 switch(flags) {
1358 case DISPATCH_PROPERTYPUT:
1359 if(res)
1360 V_VT(res) = VT_EMPTY;
1361 hres = builtin_propput(This, func, dp, caller);
1362 break;
1363 case DISPATCH_PROPERTYGET:
1364 hres = builtin_propget(This, func, dp, res);
1365 break;
1366 default:
1367 if(!func->get_vtbl_off) {
1368 hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1369 }else {
1370 VARIANT v;
1372 hres = builtin_propget(This, func, NULL, &v);
1373 if(FAILED(hres))
1374 return hres;
1376 if(flags != (DISPATCH_PROPERTYGET|DISPATCH_METHOD) || dp->cArgs) {
1377 if(V_VT(&v) != VT_DISPATCH) {
1378 FIXME("Not a function %s flags %08x\n", debugstr_variant(&v), flags);
1379 VariantClear(&v);
1380 return E_FAIL;
1383 hres = invoke_disp_value(This, V_DISPATCH(&v), lcid, flags, dp, res, ei, caller);
1384 IDispatch_Release(V_DISPATCH(&v));
1385 }else if(res) {
1386 *res = v;
1387 }else {
1388 VariantClear(&v);
1393 return hres;
1396 HRESULT dispex_call_builtin(DispatchEx *dispex, DISPID id, DISPPARAMS *dp,
1397 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
1399 func_info_t *func;
1400 HRESULT hres;
1402 hres = get_builtin_func(dispex->info, id, &func);
1403 if(FAILED(hres))
1404 return hres;
1406 return invoke_builtin_function(dispex, func, dp, res, ei, caller);
1409 HRESULT remove_attribute(DispatchEx *This, DISPID id, VARIANT_BOOL *success)
1411 switch(get_dispid_type(id)) {
1412 case DISPEXPROP_CUSTOM:
1413 FIXME("DISPEXPROP_CUSTOM not supported\n");
1414 return E_NOTIMPL;
1416 case DISPEXPROP_DYNAMIC: {
1417 DWORD idx = id - DISPID_DYNPROP_0;
1418 dynamic_prop_t *prop;
1420 prop = This->dynamic_data->props+idx;
1421 VariantClear(&prop->var);
1422 prop->flags |= DYNPROP_DELETED;
1423 *success = VARIANT_TRUE;
1424 return S_OK;
1426 case DISPEXPROP_BUILTIN: {
1427 VARIANT var;
1428 DISPPARAMS dp = {&var,NULL,1,0};
1429 func_info_t *func;
1430 HRESULT hres;
1432 hres = get_builtin_func(This->info, id, &func);
1433 if(FAILED(hres))
1434 return hres;
1436 /* For builtin functions, we set their value to the original function. */
1437 if(func->func_disp_idx >= 0) {
1438 func_obj_entry_t *entry;
1440 if(!This->dynamic_data || !This->dynamic_data->func_disps
1441 || !This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1442 *success = VARIANT_FALSE;
1443 return S_OK;
1446 entry = This->dynamic_data->func_disps + func->func_disp_idx;
1447 if(V_VT(&entry->val) == VT_DISPATCH
1448 && V_DISPATCH(&entry->val) == (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface) {
1449 *success = VARIANT_FALSE;
1450 return S_OK;
1453 VariantClear(&entry->val);
1454 V_VT(&entry->val) = VT_DISPATCH;
1455 V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface;
1456 IDispatch_AddRef(V_DISPATCH(&entry->val));
1457 *success = VARIANT_TRUE;
1458 return S_OK;
1460 *success = VARIANT_TRUE;
1462 V_VT(&var) = VT_EMPTY;
1463 hres = builtin_propput(This, func, &dp, NULL);
1464 if(FAILED(hres)) {
1465 VARIANT *ref;
1466 hres = dispex_get_dprop_ref(This, func->name, FALSE, &ref);
1467 if(FAILED(hres) || V_VT(ref) != VT_BSTR)
1468 *success = VARIANT_FALSE;
1469 else
1470 VariantClear(ref);
1472 return S_OK;
1474 default:
1475 assert(0);
1476 return E_FAIL;
1480 compat_mode_t dispex_compat_mode(DispatchEx *dispex)
1482 return dispex->info != dispex->info->desc->delayed_init_info
1483 ? dispex->info->compat_mode
1484 : dispex->info->desc->vtbl->get_compat_mode(dispex);
1487 HRESULT dispex_to_string(DispatchEx *dispex, BSTR *ret)
1489 static const WCHAR prefix[8] = L"[object ";
1490 static const WCHAR suffix[] = L"]";
1491 WCHAR buf[ARRAY_SIZE(prefix) + 28 + ARRAY_SIZE(suffix)], *p = buf;
1492 compat_mode_t compat_mode = dispex_compat_mode(dispex);
1493 const char *name = dispex->info->desc->name;
1495 if(!ret)
1496 return E_INVALIDARG;
1498 memcpy(p, prefix, sizeof(prefix));
1499 p += ARRAY_SIZE(prefix);
1500 if(compat_mode < COMPAT_MODE_IE9)
1501 p--;
1502 else {
1503 while(*name)
1504 *p++ = *name++;
1505 assert(p + ARRAY_SIZE(suffix) - buf <= ARRAY_SIZE(buf));
1507 memcpy(p, suffix, sizeof(suffix));
1509 *ret = SysAllocString(buf);
1510 return *ret ? S_OK : E_OUTOFMEMORY;
1513 static dispex_data_t *ensure_dispex_info(dispex_static_data_t *desc, compat_mode_t compat_mode)
1515 if(!desc->info_cache[compat_mode]) {
1516 EnterCriticalSection(&cs_dispex_static_data);
1517 if(!desc->info_cache[compat_mode])
1518 desc->info_cache[compat_mode] = preprocess_dispex_data(desc, compat_mode);
1519 LeaveCriticalSection(&cs_dispex_static_data);
1521 return desc->info_cache[compat_mode];
1524 static BOOL ensure_real_info(DispatchEx *dispex)
1526 if(dispex->info != dispex->info->desc->delayed_init_info)
1527 return TRUE;
1529 dispex->info = ensure_dispex_info(dispex->info->desc, dispex_compat_mode(dispex));
1530 return dispex->info != NULL;
1533 static inline DispatchEx *impl_from_IDispatchEx(IDispatchEx *iface)
1535 return CONTAINING_RECORD(iface, DispatchEx, IDispatchEx_iface);
1538 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1540 DispatchEx *This = impl_from_IDispatchEx(iface);
1542 TRACE("%s (%p)->(%s %p)\n", This->info->desc->name, This, debugstr_mshtml_guid(riid), ppv);
1544 if(This->info->desc->vtbl->query_interface) {
1545 *ppv = This->info->desc->vtbl->query_interface(This, riid);
1546 if(*ppv)
1547 goto ret;
1550 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IDispatch, riid) || IsEqualGUID(&IID_IDispatchEx, riid))
1551 *ppv = &This->IDispatchEx_iface;
1552 else if(IsEqualGUID(&IID_nsXPCOMCycleCollectionParticipant, riid)) {
1553 *ppv = &dispex_ccp;
1554 return S_OK;
1555 }else if(IsEqualGUID(&IID_nsCycleCollectionISupports, riid)) {
1556 *ppv = &This->IDispatchEx_iface;
1557 return S_OK;
1558 }else if(IsEqualGUID(&IID_IDispatchJS, riid) ||
1559 IsEqualGUID(&IID_UndocumentedScriptIface, riid) ||
1560 IsEqualGUID(&IID_IMarshal, riid) ||
1561 IsEqualGUID(&IID_IManagedObject, riid)) {
1562 *ppv = NULL;
1563 return E_NOINTERFACE;
1564 }else {
1565 *ppv = NULL;
1566 WARN("%s (%p)->(%s %p)\n", This->info->desc->name, This, debugstr_mshtml_guid(riid), ppv);
1567 return E_NOINTERFACE;
1570 ret:
1571 IDispatchEx_AddRef(&This->IDispatchEx_iface);
1572 return S_OK;
1575 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1577 DispatchEx *This = impl_from_IDispatchEx(iface);
1578 LONG ref = ccref_incr(&This->ccref, (nsISupports*)&This->IDispatchEx_iface);
1580 TRACE("%s (%p) ref=%ld\n", This->info->desc->name, This, ref);
1582 return ref;
1585 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1587 DispatchEx *This = impl_from_IDispatchEx(iface);
1588 LONG ref = ccref_decr(&This->ccref, (nsISupports*)&This->IDispatchEx_iface, &dispex_ccp);
1590 TRACE("%s (%p) ref=%ld\n", This->info->desc->name, This, ref);
1592 /* Gecko ccref may not free the object immediately when ref count reaches 0, so we need
1593 * an extra care for objects that need an immediate clean up. See Gecko's
1594 * NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE_WITH_LAST_RELEASE for details. */
1595 if(!ref && This->info->desc->vtbl->last_release) {
1596 ccref_incr(&This->ccref, (nsISupports*)&This->IDispatchEx_iface);
1597 This->info->desc->vtbl->last_release(This);
1598 ccref_decr(&This->ccref, (nsISupports*)&This->IDispatchEx_iface, &dispex_ccp);
1601 return ref;
1604 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1606 DispatchEx *This = impl_from_IDispatchEx(iface);
1608 TRACE("%s (%p)->(%p)\n", This->info->desc->name, This, pctinfo);
1610 *pctinfo = 1;
1611 return S_OK;
1614 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
1615 LCID lcid, ITypeInfo **ppTInfo)
1617 DispatchEx *This = impl_from_IDispatchEx(iface);
1618 HRESULT hres;
1620 TRACE("%s (%p)->(%u %lu %p)\n", This->info->desc->name, This, iTInfo, lcid, ppTInfo);
1622 hres = get_typeinfo(This->info->desc->disp_tid, ppTInfo);
1623 if(FAILED(hres))
1624 return hres;
1626 ITypeInfo_AddRef(*ppTInfo);
1627 return S_OK;
1630 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1631 LPOLESTR *rgszNames, UINT cNames,
1632 LCID lcid, DISPID *rgDispId)
1634 DispatchEx *This = impl_from_IDispatchEx(iface);
1635 HRESULT hres = S_OK;
1637 TRACE("%s (%p)->(%s %p %u %lu %p)\n", This->info->desc->name, This, debugstr_guid(riid), rgszNames,
1638 cNames, lcid, rgDispId);
1640 /* Native ignores all cNames > 1, and doesn't even fill them */
1641 if(cNames)
1642 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[0], 0, rgDispId);
1644 return hres;
1647 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1648 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1649 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1651 DispatchEx *This = impl_from_IDispatchEx(iface);
1653 TRACE("%s (%p)->(%ld %s %ld %d %p %p %p %p)\n", This->info->desc->name, This, dispIdMember,
1654 debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1656 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, pDispParams,
1657 pVarResult, pExcepInfo, NULL);
1660 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1662 DispatchEx *This = impl_from_IDispatchEx(iface);
1663 dynamic_prop_t *dprop;
1664 HRESULT hres;
1666 TRACE("%s (%p)->(%s %lx %p)\n", This->info->desc->name, This, debugstr_w(bstrName), grfdex, pid);
1668 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
1669 FIXME("Unsupported grfdex %lx\n", grfdex);
1671 if(!ensure_real_info(This))
1672 return E_OUTOFMEMORY;
1674 hres = get_builtin_id(This, bstrName, grfdex, pid);
1675 if(hres != DISP_E_UNKNOWNNAME)
1676 return hres;
1678 hres = get_dynamic_prop(This, bstrName, grfdex, &dprop);
1679 if(FAILED(hres))
1680 return hres;
1682 *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
1683 return S_OK;
1686 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1687 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1689 DispatchEx *This = impl_from_IDispatchEx(iface);
1690 HRESULT hres;
1692 TRACE("%s (%p)->(%lx %lx %x %p %p %p %p)\n", This->info->desc->name, This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1694 if(!ensure_real_info(This))
1695 return E_OUTOFMEMORY;
1697 if(wFlags == (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF))
1698 wFlags = DISPATCH_PROPERTYPUT;
1700 switch(get_dispid_type(id)) {
1701 case DISPEXPROP_CUSTOM:
1702 if(!This->info->desc->vtbl->invoke)
1703 return DISP_E_MEMBERNOTFOUND;
1704 return This->info->desc->vtbl->invoke(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1706 case DISPEXPROP_DYNAMIC: {
1707 DWORD idx = id - DISPID_DYNPROP_0;
1708 dynamic_prop_t *prop;
1710 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1711 return DISP_E_MEMBERNOTFOUND;
1713 prop = This->dynamic_data->props+idx;
1715 switch(wFlags) {
1716 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1717 if(!pvarRes)
1718 return E_INVALIDARG;
1719 /* fall through */
1720 case DISPATCH_METHOD:
1721 if(V_VT(&prop->var) != VT_DISPATCH) {
1722 FIXME("invoke %s\n", debugstr_variant(&prop->var));
1723 return E_NOTIMPL;
1726 return invoke_disp_value(This, V_DISPATCH(&prop->var), lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1727 case DISPATCH_PROPERTYGET:
1728 if(prop->flags & DYNPROP_DELETED)
1729 return DISP_E_MEMBERNOTFOUND;
1730 V_VT(pvarRes) = VT_EMPTY;
1731 return variant_copy(pvarRes, &prop->var);
1732 case DISPATCH_PROPERTYPUT:
1733 if(pdp->cArgs != 1 || (pdp->cNamedArgs == 1 && *pdp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1734 || pdp->cNamedArgs > 1) {
1735 FIXME("invalid args\n");
1736 return E_INVALIDARG;
1739 TRACE("put %s\n", debugstr_variant(pdp->rgvarg));
1740 VariantClear(&prop->var);
1741 hres = variant_copy(&prop->var, pdp->rgvarg);
1742 if(FAILED(hres))
1743 return hres;
1745 prop->flags &= ~DYNPROP_DELETED;
1746 return S_OK;
1747 default:
1748 FIXME("unhandled wFlags %x\n", wFlags);
1749 return E_NOTIMPL;
1752 case DISPEXPROP_BUILTIN:
1753 if(wFlags == DISPATCH_CONSTRUCT) {
1754 if(id == DISPID_VALUE) {
1755 if(This->info->desc->vtbl->value) {
1756 return This->info->desc->vtbl->value(This, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1758 FIXME("DISPATCH_CONSTRUCT flag but missing value function\n");
1759 return E_FAIL;
1761 FIXME("DISPATCH_CONSTRUCT flag without DISPID_VALUE\n");
1762 return E_FAIL;
1765 return invoke_builtin_prop(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1766 default:
1767 assert(0);
1768 return E_FAIL;
1772 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR name, DWORD grfdex)
1774 DispatchEx *This = impl_from_IDispatchEx(iface);
1775 DISPID id;
1776 HRESULT hres;
1778 TRACE("%s (%p)->(%s %lx)\n", This->info->desc->name, This, debugstr_w(name), grfdex);
1780 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, name, grfdex & ~fdexNameEnsure, &id);
1781 if(FAILED(hres)) {
1782 compat_mode_t compat_mode = dispex_compat_mode(This);
1783 TRACE("property %s not found\n", debugstr_w(name));
1784 return compat_mode < COMPAT_MODE_IE8 ? E_NOTIMPL :
1785 compat_mode < COMPAT_MODE_IE9 ? hres : S_OK;
1788 return IDispatchEx_DeleteMemberByDispID(&This->IDispatchEx_iface, id);
1791 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
1793 DispatchEx *This = impl_from_IDispatchEx(iface);
1795 TRACE("%s (%p)->(%lx)\n", This->info->desc->name, This, id);
1797 if(is_custom_dispid(id) && This->info->desc->vtbl->delete)
1798 return This->info->desc->vtbl->delete(This, id);
1800 if(dispex_compat_mode(This) < COMPAT_MODE_IE8) {
1801 /* Not implemented by IE */
1802 return E_NOTIMPL;
1805 if(is_dynamic_dispid(id)) {
1806 DWORD idx = id - DISPID_DYNPROP_0;
1807 dynamic_prop_t *prop;
1809 if(!get_dynamic_data(This) || idx >= This->dynamic_data->prop_cnt)
1810 return S_OK;
1812 prop = This->dynamic_data->props + idx;
1813 VariantClear(&prop->var);
1814 prop->flags |= DYNPROP_DELETED;
1815 return S_OK;
1818 return S_OK;
1821 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
1823 DispatchEx *This = impl_from_IDispatchEx(iface);
1824 FIXME("%s (%p)->(%lx %lx %p)\n", This->info->desc->name, This, id, grfdexFetch, pgrfdex);
1825 return E_NOTIMPL;
1828 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
1830 DispatchEx *This = impl_from_IDispatchEx(iface);
1831 func_info_t *func;
1832 HRESULT hres;
1834 TRACE("%s (%p)->(%lx %p)\n", This->info->desc->name, This, id, pbstrName);
1836 if(!ensure_real_info(This))
1837 return E_OUTOFMEMORY;
1839 if(is_custom_dispid(id)) {
1840 if(This->info->desc->vtbl->get_name)
1841 return This->info->desc->vtbl->get_name(This, id, pbstrName);
1842 return DISP_E_MEMBERNOTFOUND;
1845 if(is_dynamic_dispid(id)) {
1846 DWORD idx = id - DISPID_DYNPROP_0;
1848 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1849 return DISP_E_MEMBERNOTFOUND;
1851 *pbstrName = SysAllocString(This->dynamic_data->props[idx].name);
1852 if(!*pbstrName)
1853 return E_OUTOFMEMORY;
1855 return S_OK;
1858 hres = get_builtin_func(This->info, id, &func);
1859 if(FAILED(hres))
1860 return hres;
1862 *pbstrName = SysAllocString(func->name);
1863 if(!*pbstrName)
1864 return E_OUTOFMEMORY;
1865 return S_OK;
1868 static HRESULT next_dynamic_id(DispatchEx *dispex, DWORD idx, DISPID *ret_id)
1870 while(idx < dispex->dynamic_data->prop_cnt &&
1871 (dispex->dynamic_data->props[idx].flags & (DYNPROP_DELETED | DYNPROP_HIDDEN)))
1872 idx++;
1874 if(idx == dispex->dynamic_data->prop_cnt) {
1875 *ret_id = DISPID_STARTENUM;
1876 return S_FALSE;
1879 *ret_id = DISPID_DYNPROP_0+idx;
1880 return S_OK;
1883 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
1885 DispatchEx *This = impl_from_IDispatchEx(iface);
1886 func_info_t *func;
1887 HRESULT hres;
1889 TRACE("%s (%p)->(%lx %lx %p)\n", This->info->desc->name, This, grfdex, id, pid);
1891 if(!ensure_real_info(This))
1892 return E_OUTOFMEMORY;
1894 if(is_dynamic_dispid(id)) {
1895 DWORD idx = id - DISPID_DYNPROP_0;
1897 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1898 return DISP_E_MEMBERNOTFOUND;
1900 return next_dynamic_id(This, idx+1, pid);
1903 if(!is_custom_dispid(id)) {
1904 if(id == DISPID_STARTENUM) {
1905 func = This->info->funcs;
1906 }else {
1907 hres = get_builtin_func(This->info, id, &func);
1908 if(FAILED(hres))
1909 return hres;
1910 func++;
1913 while(func < This->info->funcs + This->info->func_cnt) {
1914 if(func->func_disp_idx == -1) {
1915 *pid = func->id;
1916 return S_OK;
1918 func++;
1921 id = DISPID_STARTENUM;
1924 if(This->info->desc->vtbl->next_dispid) {
1925 hres = This->info->desc->vtbl->next_dispid(This, id, pid);
1926 if(hres != S_FALSE)
1927 return hres;
1930 if(get_dynamic_data(This) && This->dynamic_data->prop_cnt)
1931 return next_dynamic_id(This, 0, pid);
1933 *pid = DISPID_STARTENUM;
1934 return S_FALSE;
1937 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
1939 DispatchEx *This = impl_from_IDispatchEx(iface);
1940 FIXME("%s (%p)->(%p)\n", This->info->desc->name, This, ppunk);
1941 return E_NOTIMPL;
1944 static IDispatchExVtbl DispatchExVtbl = {
1945 DispatchEx_QueryInterface,
1946 DispatchEx_AddRef,
1947 DispatchEx_Release,
1948 DispatchEx_GetTypeInfoCount,
1949 DispatchEx_GetTypeInfo,
1950 DispatchEx_GetIDsOfNames,
1951 DispatchEx_Invoke,
1952 DispatchEx_GetDispID,
1953 DispatchEx_InvokeEx,
1954 DispatchEx_DeleteMemberByName,
1955 DispatchEx_DeleteMemberByDispID,
1956 DispatchEx_GetMemberProperties,
1957 DispatchEx_GetMemberName,
1958 DispatchEx_GetNextDispID,
1959 DispatchEx_GetNameSpaceParent
1962 static nsresult NSAPI dispex_traverse(void *ccp, void *p, nsCycleCollectionTraversalCallback *cb)
1964 DispatchEx *This = impl_from_IDispatchEx(p);
1965 dynamic_prop_t *prop;
1967 describe_cc_node(&This->ccref, This->info->desc->name, cb);
1969 if(This->info->desc->vtbl->traverse)
1970 This->info->desc->vtbl->traverse(This, cb);
1972 if(!This->dynamic_data)
1973 return NS_OK;
1975 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++)
1976 traverse_variant(&prop->var, "dispex_data", cb);
1978 if(This->dynamic_data->func_disps) {
1979 func_obj_entry_t *iter = This->dynamic_data->func_disps, *end = iter + This->info->func_disp_cnt;
1981 for(iter = This->dynamic_data->func_disps; iter < end; iter++) {
1982 if(!iter->func_obj)
1983 continue;
1984 note_cc_edge((nsISupports*)&iter->func_obj->dispex.IDispatchEx_iface, "func_obj", cb);
1985 traverse_variant(&iter->val, "func_val", cb);
1989 return NS_OK;
1992 void dispex_props_unlink(DispatchEx *This)
1994 dynamic_prop_t *prop;
1996 if(!This->dynamic_data)
1997 return;
1999 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
2000 prop->flags |= DYNPROP_DELETED;
2001 unlink_variant(&prop->var);
2004 if(This->dynamic_data->func_disps) {
2005 func_obj_entry_t *iter = This->dynamic_data->func_disps, *end = iter + This->info->func_disp_cnt;
2007 for(iter = This->dynamic_data->func_disps; iter < end; iter++) {
2008 if(!iter->func_obj)
2009 continue;
2010 iter->func_obj->obj = NULL;
2011 IDispatchEx_Release(&iter->func_obj->dispex.IDispatchEx_iface);
2012 VariantClear(&iter->val);
2015 free(This->dynamic_data->func_disps);
2016 This->dynamic_data->func_disps = NULL;
2020 static nsresult NSAPI dispex_unlink(void *p)
2022 DispatchEx *This = impl_from_IDispatchEx(p);
2024 if(This->info->desc->vtbl->unlink)
2025 This->info->desc->vtbl->unlink(This);
2027 dispex_props_unlink(This);
2028 return NS_OK;
2031 static void NSAPI dispex_delete_cycle_collectable(void *p)
2033 DispatchEx *This = impl_from_IDispatchEx(p);
2034 dynamic_prop_t *prop;
2036 if(This->info->desc->vtbl->unlink)
2037 This->info->desc->vtbl->unlink(This);
2039 if(!This->dynamic_data)
2040 goto destructor;
2042 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
2043 VariantClear(&prop->var);
2044 free(prop->name);
2047 free(This->dynamic_data->props);
2049 if(This->dynamic_data->func_disps) {
2050 func_obj_entry_t *iter;
2052 for(iter = This->dynamic_data->func_disps; iter < This->dynamic_data->func_disps + This->info->func_disp_cnt; iter++) {
2053 if(iter->func_obj) {
2054 iter->func_obj->obj = NULL;
2055 IDispatchEx_Release(&iter->func_obj->dispex.IDispatchEx_iface);
2057 VariantClear(&iter->val);
2060 free(This->dynamic_data->func_disps);
2063 free(This->dynamic_data);
2065 destructor:
2066 This->info->desc->vtbl->destructor(This);
2069 void init_dispex_cc(void)
2071 static const CCObjCallback dispex_ccp_callback = {
2072 dispex_traverse,
2073 dispex_unlink,
2074 dispex_delete_cycle_collectable
2076 ccp_init(&dispex_ccp, &dispex_ccp_callback);
2079 const void *dispex_get_vtbl(DispatchEx *dispex)
2081 return dispex->info->desc->vtbl;
2084 void init_dispatch(DispatchEx *dispex, dispex_static_data_t *data, compat_mode_t compat_mode)
2086 assert(compat_mode < COMPAT_MODE_CNT);
2088 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
2089 dispex->dynamic_data = NULL;
2090 ccref_init(&dispex->ccref, 1);
2092 if(data->vtbl->get_compat_mode) {
2093 /* delayed init */
2094 if(!data->delayed_init_info) {
2095 EnterCriticalSection(&cs_dispex_static_data);
2096 if(!data->delayed_init_info) {
2097 dispex_data_t *info = calloc(1, sizeof(*data->delayed_init_info));
2098 if(info) {
2099 info->desc = data;
2100 data->delayed_init_info = info;
2103 LeaveCriticalSection(&cs_dispex_static_data);
2105 dispex->info = data->delayed_init_info;
2106 }else {
2107 dispex->info = ensure_dispex_info(data, compat_mode);