1 /*
2 * Copyright 2008-2009 Jacek Caban for CodeWeavers
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #include "mshtml_private.h"
20
21 #define MAX_ARGS 16
22
23 static CRITICAL_SECTION cs_dispex_static_data;
24 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
25 {
26 0, 0, &cs_dispex_static_data,
27 { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
28 0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
29 };
30 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
31
32
33 static const WCHAR objectW[] = {'[','o','b','j','e','c','t',']',0};
34
35 typedef struct {
36 DISPID id;
37 BSTR name;
38 tid_t tid;
39 SHORT call_vtbl_off;
40 SHORT put_vtbl_off;
41 SHORT get_vtbl_off;
42 SHORT func_disp_idx;
43 USHORT argc;
44 VARTYPE prop_vt;
45 VARTYPE *arg_types;
46 } func_info_t;
47
48 struct dispex_data_t {
49 DWORD func_cnt;
50 func_info_t *funcs;
51 func_info_t **name_table;
52 DWORD func_disp_cnt;
53
54 struct list entry;
55 };
56
57 typedef struct {
58 VARIANT var;
59 LPWSTR name;
60 DWORD flags;
61 } dynamic_prop_t;
62
63 #define DYNPROP_DELETED 0x01
64
65 typedef struct {
66 DispatchEx dispex;
67 IUnknown IUnknown_iface;
68 LONG ref;
69 DispatchEx *obj;
70 func_info_t *info;
71 } func_disp_t;
72
73 typedef struct {
74 func_disp_t *func_obj;
75 VARIANT val;
76 } func_obj_entry_t;
77
78 struct dispex_dynamic_data_t {
79 DWORD buf_size;
80 DWORD prop_cnt;
81 dynamic_prop_t *props;
82 func_obj_entry_t *func_disps;
83 };
84
85 #define DISPID_DYNPROP_0 0x50000000
86 #define DISPID_DYNPROP_MAX 0x5fffffff
87
88 #define FDEX_VERSION_MASK 0xf0000000
89
90 static ITypeLib *typelib;
91 static ITypeInfo *typeinfos[LAST_tid];
92 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
93
94 static REFIID tid_ids[] = {
95 #define XIID(iface) &IID_ ## iface,
96 #define XDIID(iface) &DIID_ ## iface,
97 TID_LIST
98 #undef XIID
99 #undef XDIID
100 };
101
load_typelib(void)102 static HRESULT load_typelib(void)
103 {
104 HRESULT hres;
105 ITypeLib *tl;
106
107 hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
108 if(FAILED(hres)) {
109 ERR("LoadRegTypeLib failed: %08x\n", hres);
110 return hres;
111 }
112
113 if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
114 ITypeLib_Release(tl);
115 return hres;
116 }
117
get_typeinfo(tid_t tid,ITypeInfo ** typeinfo)118 static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
119 {
120 HRESULT hres;
121
122 if (!typelib)
123 hres = load_typelib();
124 if (!typelib)
125 return hres;
126
127 if(!typeinfos[tid]) {
128 ITypeInfo *ti;
129
130 hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &ti);
131 if(FAILED(hres)) {
132 ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_mshtml_guid(tid_ids[tid]), hres);
133 return hres;
134 }
135
136 if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL))
137 ITypeInfo_Release(ti);
138 }
139
140 *typeinfo = typeinfos[tid];
141 return S_OK;
142 }
143
release_typelib(void)144 void release_typelib(void)
145 {
146 dispex_data_t *iter;
147 unsigned i;
148
149 while(!list_empty(&dispex_data_list)) {
150 iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
151 list_remove(&iter->entry);
152
153 for(i=0; i < iter->func_cnt; i++)
154 SysFreeString(iter->funcs[i].name);
155
156 heap_free(iter->funcs);
157 heap_free(iter->name_table);
158 heap_free(iter);
159 }
160
161 if(!typelib)
162 return;
163
164 for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
165 if(typeinfos[i])
166 ITypeInfo_Release(typeinfos[i]);
167
168 ITypeLib_Release(typelib);
169 DeleteCriticalSection(&cs_dispex_static_data);
170 }
171
get_htmldoc_classinfo(ITypeInfo ** typeinfo)172 HRESULT get_htmldoc_classinfo(ITypeInfo **typeinfo)
173 {
174 HRESULT hres;
175
176 if (!typelib)
177 hres = load_typelib();
178 if (!typelib)
179 return hres;
180
181 hres = ITypeLib_GetTypeInfoOfGuid(typelib, &CLSID_HTMLDocument, typeinfo);
182 if(FAILED(hres))
183 ERR("GetTypeInfoOfGuid failed: %08x\n", hres);
184 return hres;
185 }
186
187 /* Not all argument types are supported yet */
188 #define BUILTIN_ARG_TYPES_SWITCH \
189 CASE_VT(VT_I2, INT16, V_I2); \
190 CASE_VT(VT_I4, INT32, V_I4); \
191 CASE_VT(VT_R4, float, V_R4); \
192 CASE_VT(VT_BSTR, BSTR, V_BSTR); \
193 CASE_VT(VT_BOOL, VARIANT_BOOL, V_BOOL)
194
195 /* List all types used by IDispatchEx-based properties */
196 #define BUILTIN_TYPES_SWITCH \
197 BUILTIN_ARG_TYPES_SWITCH; \
198 CASE_VT(VT_VARIANT, VARIANT, *); \
199 CASE_VT(VT_PTR, void*, V_BYREF); \
200 CASE_VT(VT_UNKNOWN, IUnknown*, V_UNKNOWN); \
201 CASE_VT(VT_DISPATCH, IDispatch*, V_DISPATCH)
202
is_arg_type_supported(VARTYPE vt)203 static BOOL is_arg_type_supported(VARTYPE vt)
204 {
205 switch(vt) {
206 #define CASE_VT(x,a,b) case x: return TRUE
207 BUILTIN_ARG_TYPES_SWITCH;
208 #undef CASE_VT
209 }
210 return FALSE;
211 }
212
add_func_info(dispex_data_t * data,DWORD * size,tid_t tid,const FUNCDESC * desc,ITypeInfo * dti)213 static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti)
214 {
215 func_info_t *info;
216 HRESULT hres;
217
218 for(info = data->funcs; info < data->funcs+data->func_cnt; info++) {
219 if(info->id == desc->memid) {
220 if(info->tid != tid)
221 return; /* Duplicated in other interface */
222 break;
223 }
224 }
225
226 if(info == data->funcs+data->func_cnt) {
227 if(data->func_cnt == *size)
228 data->funcs = heap_realloc_zero(data->funcs, (*size <<= 1)*sizeof(func_info_t));
229
230 info = data->funcs+data->func_cnt;
231 hres = ITypeInfo_GetDocumentation(dti, desc->memid, &info->name, NULL, NULL, NULL);
232 if(FAILED(hres))
233 return;
234
235 data->func_cnt++;
236
237 info->id = desc->memid;
238 info->tid = tid;
239 info->func_disp_idx = -1;
240 info->prop_vt = VT_EMPTY;
241 }
242
243 if(desc->invkind & DISPATCH_METHOD) {
244 unsigned i;
245
246 info->func_disp_idx = data->func_disp_cnt++;
247 info->argc = desc->cParams;
248
249 assert(info->argc < MAX_ARGS);
250 assert(desc->funckind == FUNC_DISPATCH);
251
252 info->arg_types = heap_alloc(sizeof(*info->arg_types) * info->argc);
253 if(!info->arg_types)
254 return; /* FIXME: real error instead of fallback */
255
256 for(i=0; i < info->argc; i++)
257 info->arg_types[i] = desc->lprgelemdescParam[i].tdesc.vt;
258
259 info->prop_vt = desc->elemdescFunc.tdesc.vt;
260 if(info->prop_vt != VT_VOID && !is_arg_type_supported(info->prop_vt)) {
261 TRACE("%s: return type %d\n", debugstr_w(info->name), info->prop_vt);
262 return; /* Fallback to ITypeInfo::Invoke */
263 }
264
265 if(desc->cParamsOpt) {
266 TRACE("%s: optional params\n", debugstr_w(info->name));
267 return; /* Fallback to ITypeInfo::Invoke */
268 }
269
270 for(i=0; i < info->argc; i++) {
271 if(!is_arg_type_supported(info->arg_types[i])) {
272 return; /* Fallback to ITypeInfo for unsupported arg types */
273 }
274
275 if(desc->lprgelemdescParam[i].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) {
276 TRACE("%s param %d: default value\n", debugstr_w(info->name), i);
277 return; /* Fallback to ITypeInfo::Invoke */
278 }
279 }
280
281 assert(info->argc <= MAX_ARGS);
282 assert(desc->callconv == CC_STDCALL);
283
284 info->call_vtbl_off = desc->oVft/sizeof(void*);
285 }else if(desc->invkind & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET)) {
286 VARTYPE vt = VT_EMPTY;
287
288 if(desc->invkind & DISPATCH_PROPERTYGET) {
289 vt = desc->elemdescFunc.tdesc.vt;
290 info->get_vtbl_off = desc->oVft/sizeof(void*);
291 }
292 if(desc->invkind & DISPATCH_PROPERTYPUT) {
293 assert(desc->cParams == 1);
294 vt = desc->lprgelemdescParam->tdesc.vt;
295 info->put_vtbl_off = desc->oVft/sizeof(void*);
296 }
297
298 assert(info->prop_vt == VT_EMPTY || vt == info->prop_vt);
299 info->prop_vt = vt;
300 }
301 }
302
dispid_cmp(const void * p1,const void * p2)303 static int dispid_cmp(const void *p1, const void *p2)
304 {
305 return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id;
306 }
307
func_name_cmp(const void * p1,const void * p2)308 static int func_name_cmp(const void *p1, const void *p2)
309 {
310 return strcmpiW((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name);
311 }
312
preprocess_dispex_data(DispatchEx * This)313 static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
314 {
315 const tid_t *tid = This->data->iface_tids;
316 FUNCDESC *funcdesc;
317 dispex_data_t *data;
318 DWORD size = 16, i;
319 ITypeInfo *ti, *dti;
320 HRESULT hres;
321
322 TRACE("(%p)\n", This);
323
324 if(This->data->disp_tid) {
325 hres = get_typeinfo(This->data->disp_tid, &dti);
326 if(FAILED(hres)) {
327 ERR("Could not get disp type info: %08x\n", hres);
328 return NULL;
329 }
330 }
331
332 data = heap_alloc(sizeof(dispex_data_t));
333 if (!data) {
334 ERR("Out of memory\n");
335 return NULL;
336 }
337 data->func_cnt = 0;
338 data->func_disp_cnt = 0;
339 data->funcs = heap_alloc_zero(size*sizeof(func_info_t));
340 if (!data->funcs) {
341 heap_free (data);
342 ERR("Out of memory\n");
343 return NULL;
344 }
345 list_add_tail(&dispex_data_list, &data->entry);
346
347 while(*tid) {
348 hres = get_typeinfo(*tid, &ti);
349 if(FAILED(hres))
350 break;
351
352 i=7;
353 while(1) {
354 hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
355 if(FAILED(hres))
356 break;
357
358 add_func_info(data, &size, *tid, funcdesc, dti);
359 ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
360 }
361
362 tid++;
363 }
364
365 if(!data->func_cnt) {
366 heap_free(data->funcs);
367 data->name_table = NULL;
368 data->funcs = NULL;
369 return data;
370 }
371
372
373 data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
374 qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
375
376 data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
377 for(i=0; i < data->func_cnt; i++)
378 data->name_table[i] = data->funcs+i;
379 qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
380
381 return data;
382 }
383
id_cmp(const void * p1,const void * p2)384 static int id_cmp(const void *p1, const void *p2)
385 {
386 return *(const DISPID*)p1 - *(const DISPID*)p2;
387 }
388
get_dispids(tid_t tid,DWORD * ret_size,DISPID ** ret)389 HRESULT get_dispids(tid_t tid, DWORD *ret_size, DISPID **ret)
390 {
391 unsigned i, func_cnt;
392 FUNCDESC *funcdesc;
393 ITypeInfo *ti;
394 TYPEATTR *attr;
395 DISPID *ids;
396 HRESULT hres;
397
398 hres = get_typeinfo(tid, &ti);
399 if(FAILED(hres))
400 return hres;
401
402 hres = ITypeInfo_GetTypeAttr(ti, &attr);
403 if(FAILED(hres)) {
404 ITypeInfo_Release(ti);
405 return hres;
406 }
407
408 func_cnt = attr->cFuncs;
409 ITypeInfo_ReleaseTypeAttr(ti, attr);
410
411 ids = heap_alloc(func_cnt*sizeof(DISPID));
412 if(!ids) {
413 ITypeInfo_Release(ti);
414 return E_OUTOFMEMORY;
415 }
416
417 for(i=0; i < func_cnt; i++) {
418 hres = ITypeInfo_GetFuncDesc(ti, i, &funcdesc);
419 if(FAILED(hres))
420 break;
421
422 ids[i] = funcdesc->memid;
423 ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
424 }
425
426 ITypeInfo_Release(ti);
427 if(FAILED(hres)) {
428 heap_free(ids);
429 return hres;
430 }
431
432 qsort(ids, func_cnt, sizeof(DISPID), id_cmp);
433
434 *ret_size = func_cnt;
435 *ret = ids;
436 return S_OK;
437 }
438
get_dispex_data(DispatchEx * This)439 static dispex_data_t *get_dispex_data(DispatchEx *This)
440 {
441 if(This->data->data)
442 return This->data->data;
443
444 EnterCriticalSection(&cs_dispex_static_data);
445
446 if(!This->data->data)
447 This->data->data = preprocess_dispex_data(This);
448
449 LeaveCriticalSection(&cs_dispex_static_data);
450
451 return This->data->data;
452 }
453
is_custom_dispid(DISPID id)454 static inline BOOL is_custom_dispid(DISPID id)
455 {
456 return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
457 }
458
is_dynamic_dispid(DISPID id)459 static inline BOOL is_dynamic_dispid(DISPID id)
460 {
461 return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
462 }
463
get_dispid_type(DISPID id)464 dispex_prop_type_t get_dispid_type(DISPID id)
465 {
466 if(is_dynamic_dispid(id))
467 return DISPEXPROP_DYNAMIC;
468 if(is_custom_dispid(id))
469 return DISPEXPROP_CUSTOM;
470 return DISPEXPROP_BUILTIN;
471 }
472
variant_copy(VARIANT * dest,VARIANT * src)473 static HRESULT variant_copy(VARIANT *dest, VARIANT *src)
474 {
475 if(V_VT(src) == VT_BSTR && !V_BSTR(src)) {
476 V_VT(dest) = VT_BSTR;
477 V_BSTR(dest) = NULL;
478 return S_OK;
479 }
480
481 return VariantCopy(dest, src);
482 }
483
get_dynamic_data(DispatchEx * This)484 static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This)
485 {
486 if(This->dynamic_data)
487 return This->dynamic_data;
488
489 This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t));
490 if(!This->dynamic_data)
491 return NULL;
492
493 if(This->data->vtbl && This->data->vtbl->populate_props)
494 This->data->vtbl->populate_props(This);
495
496 return This->dynamic_data;
497 }
498
get_dynamic_prop(DispatchEx * This,const WCHAR * name,DWORD flags,dynamic_prop_t ** ret)499 static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, DWORD flags, dynamic_prop_t **ret)
500 {
501 const BOOL alloc = flags & fdexNameEnsure;
502 dispex_dynamic_data_t *data;
503 dynamic_prop_t *prop;
504
505 data = get_dynamic_data(This);
506 if(!data)
507 return E_OUTOFMEMORY;
508
509 for(prop = data->props; prop < data->props+data->prop_cnt; prop++) {
510 if(flags & fdexNameCaseInsensitive ? !strcmpiW(prop->name, name) : !strcmpW(prop->name, name)) {
511 if(prop->flags & DYNPROP_DELETED) {
512 if(!alloc)
513 return DISP_E_UNKNOWNNAME;
514 prop->flags &= ~DYNPROP_DELETED;
515 }
516 *ret = prop;
517 return S_OK;
518 }
519 }
520
521 if(!alloc)
522 return DISP_E_UNKNOWNNAME;
523
524 TRACE("creating dynamic prop %s\n", debugstr_w(name));
525
526 if(!data->buf_size) {
527 data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
528 if(!data->props)
529 return E_OUTOFMEMORY;
530 data->buf_size = 4;
531 }else if(data->buf_size == data->prop_cnt) {
532 dynamic_prop_t *new_props;
533
534 new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1));
535 if(!new_props)
536 return E_OUTOFMEMORY;
537
538 data->props = new_props;
539 data->buf_size <<= 1;
540 }
541
542 prop = data->props + data->prop_cnt;
543
544 prop->name = heap_strdupW(name);
545 if(!prop->name)
546 return E_OUTOFMEMORY;
547
548 VariantInit(&prop->var);
549 prop->flags = 0;
550 data->prop_cnt++;
551 *ret = prop;
552 return S_OK;
553 }
554
dispex_get_dprop_ref(DispatchEx * This,const WCHAR * name,BOOL alloc,VARIANT ** ret)555 HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
556 {
557 dynamic_prop_t *prop;
558 HRESULT hres;
559
560 hres = get_dynamic_prop(This, name, alloc ? fdexNameEnsure : 0, &prop);
561 if(FAILED(hres))
562 return hres;
563
564 *ret = &prop->var;
565 return S_OK;
566 }
567
dispex_get_dynid(DispatchEx * This,const WCHAR * name,DISPID * id)568 HRESULT dispex_get_dynid(DispatchEx *This, const WCHAR *name, DISPID *id)
569 {
570 dynamic_prop_t *prop;
571 HRESULT hres;
572
573 hres = get_dynamic_prop(This, name, fdexNameEnsure, &prop);
574 if(FAILED(hres))
575 return hres;
576
577 *id = DISPID_DYNPROP_0 + (prop - This->dynamic_data->props);
578 return S_OK;
579 }
580
dispex_value(DispatchEx * This,LCID lcid,WORD flags,DISPPARAMS * params,VARIANT * res,EXCEPINFO * ei,IServiceProvider * caller)581 static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params,
582 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
583 {
584 if(This->data->vtbl && This->data->vtbl->value)
585 return This->data->vtbl->value(This, lcid, flags, params, res, ei, caller);
586
587 switch(flags) {
588 case DISPATCH_PROPERTYGET:
589 V_VT(res) = VT_BSTR;
590 V_BSTR(res) = SysAllocString(objectW);
591 if(!V_BSTR(res))
592 return E_OUTOFMEMORY;
593 break;
594 default:
595 FIXME("Unimplemented flags %x\n", flags);
596 return E_NOTIMPL;
597 }
598
599 return S_OK;
600 }
601
typeinfo_invoke(DispatchEx * This,func_info_t * func,WORD flags,DISPPARAMS * dp,VARIANT * res,EXCEPINFO * ei)602 static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
603 EXCEPINFO *ei)
604 {
605 ITypeInfo *ti;
606 IUnknown *unk;
607 UINT argerr=0;
608 HRESULT hres;
609
610 hres = get_typeinfo(func->tid, &ti);
611 if(FAILED(hres)) {
612 ERR("Could not get type info: %08x\n", hres);
613 return hres;
614 }
615
616 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&unk);
617 if(FAILED(hres)) {
618 ERR("Could not get iface %s: %08x\n", debugstr_mshtml_guid(tid_ids[func->tid]), hres);
619 return E_FAIL;
620 }
621
622 hres = ITypeInfo_Invoke(ti, unk, func->id, flags, dp, res, ei, &argerr);
623
624 IUnknown_Release(unk);
625 return hres;
626 }
627
impl_from_IUnknown(IUnknown * iface)628 static inline func_disp_t *impl_from_IUnknown(IUnknown *iface)
629 {
630 return CONTAINING_RECORD(iface, func_disp_t, IUnknown_iface);
631 }
632
Function_QueryInterface(IUnknown * iface,REFIID riid,void ** ppv)633 static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
634 {
635 func_disp_t *This = impl_from_IUnknown(iface);
636
637 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
638
639 if(IsEqualGUID(&IID_IUnknown, riid)) {
640 *ppv = &This->IUnknown_iface;
641 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
642 return *ppv ? S_OK : E_NOINTERFACE;
643 }else {
644 *ppv = NULL;
645 return E_NOINTERFACE;
646 }
647
648 IUnknown_AddRef((IUnknown*)*ppv);
649 return S_OK;
650 }
651
Function_AddRef(IUnknown * iface)652 static ULONG WINAPI Function_AddRef(IUnknown *iface)
653 {
654 func_disp_t *This = impl_from_IUnknown(iface);
655 LONG ref = InterlockedIncrement(&This->ref);
656
657 TRACE("(%p) ref=%d\n", This, ref);
658
659 return ref;
660 }
661
Function_Release(IUnknown * iface)662 static ULONG WINAPI Function_Release(IUnknown *iface)
663 {
664 func_disp_t *This = impl_from_IUnknown(iface);
665 LONG ref = InterlockedDecrement(&This->ref);
666
667 TRACE("(%p) ref=%d\n", This, ref);
668
669 if(!ref) {
670 assert(!This->obj);
671 release_dispex(&This->dispex);
672 heap_free(This);
673 }
674
675 return ref;
676 }
677
678 static const IUnknownVtbl FunctionUnkVtbl = {
679 Function_QueryInterface,
680 Function_AddRef,
681 Function_Release
682 };
683
impl_from_DispatchEx(DispatchEx * iface)684 static inline func_disp_t *impl_from_DispatchEx(DispatchEx *iface)
685 {
686 return CONTAINING_RECORD(iface, func_disp_t, dispex);
687 }
688
function_value(DispatchEx * dispex,LCID lcid,WORD flags,DISPPARAMS * params,VARIANT * res,EXCEPINFO * ei,IServiceProvider * caller)689 static HRESULT function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *params,
690 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
691 {
692 func_disp_t *This = impl_from_DispatchEx(dispex);
693 HRESULT hres;
694
695 switch(flags) {
696 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
697 if(!res)
698 return E_INVALIDARG;
699 /* fall through */
700 case DISPATCH_METHOD:
701 if(!This->obj)
702 return E_UNEXPECTED;
703 hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei);
704 break;
705 case DISPATCH_PROPERTYGET: {
706 unsigned name_len;
707 WCHAR *ptr;
708 BSTR str;
709
710 static const WCHAR func_prefixW[] =
711 {'\n','f','u','n','c','t','i','o','n',' '};
712 static const WCHAR func_suffixW[] =
713 {'(',')',' ','{','\n',' ',' ',' ',' ','[','n','a','t','i','v','e',' ','c','o','d','e',']','\n','}','\n'};
714
715 /* FIXME: This probably should be more generic. Also we should try to get IID_IActiveScriptSite and SID_GetCaller. */
716 if(!caller)
717 return E_ACCESSDENIED;
718
719 name_len = SysStringLen(This->info->name);
720 ptr = str = SysAllocStringLen(NULL, name_len + (sizeof(func_prefixW)+sizeof(func_suffixW))/sizeof(WCHAR));
721 if(!str)
722 return E_OUTOFMEMORY;
723
724 memcpy(ptr, func_prefixW, sizeof(func_prefixW));
725 ptr += sizeof(func_prefixW)/sizeof(WCHAR);
726
727 memcpy(ptr, This->info->name, name_len*sizeof(WCHAR));
728 ptr += name_len;
729
730 memcpy(ptr, func_suffixW, sizeof(func_suffixW));
731
732 V_VT(res) = VT_BSTR;
733 V_BSTR(res) = str;
734 return S_OK;
735 }
736 default:
737 FIXME("Unimplemented flags %x\n", flags);
738 hres = E_NOTIMPL;
739 }
740
741 return hres;
742 }
743
744 static const dispex_static_data_vtbl_t function_dispex_vtbl = {
745 function_value,
746 NULL,
747 NULL,
748 NULL
749 };
750
751 static const tid_t function_iface_tids[] = {0};
752
753 static dispex_static_data_t function_dispex = {
754 &function_dispex_vtbl,
755 NULL_tid,
756 NULL,
757 function_iface_tids
758 };
759
create_func_disp(DispatchEx * obj,func_info_t * info)760 static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info)
761 {
762 func_disp_t *ret;
763
764 ret = heap_alloc_zero(sizeof(func_disp_t));
765 if(!ret)
766 return NULL;
767
768 ret->IUnknown_iface.lpVtbl = &FunctionUnkVtbl;
769 init_dispex(&ret->dispex, &ret->IUnknown_iface, &function_dispex);
770 ret->ref = 1;
771 ret->obj = obj;
772 ret->info = info;
773
774 return ret;
775 }
776
invoke_disp_value(DispatchEx * This,IDispatch * func_disp,LCID lcid,WORD flags,DISPPARAMS * dp,VARIANT * res,EXCEPINFO * ei,IServiceProvider * caller)777 static HRESULT invoke_disp_value(DispatchEx *This, IDispatch *func_disp, LCID lcid, WORD flags, DISPPARAMS *dp,
778 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
779 {
780 DISPID named_arg = DISPID_THIS;
781 DISPPARAMS new_dp = {NULL, &named_arg, 0, 1};
782 IDispatchEx *dispex;
783 HRESULT hres;
784
785 if(dp->cNamedArgs) {
786 FIXME("named args not supported\n");
787 return E_NOTIMPL;
788 }
789
790 new_dp.rgvarg = heap_alloc((dp->cArgs+1)*sizeof(VARIANTARG));
791 if(!new_dp.rgvarg)
792 return E_OUTOFMEMORY;
793
794 new_dp.cArgs = dp->cArgs+1;
795 memcpy(new_dp.rgvarg+1, dp->rgvarg, dp->cArgs*sizeof(VARIANTARG));
796
797 V_VT(new_dp.rgvarg) = VT_DISPATCH;
798 V_DISPATCH(new_dp.rgvarg) = (IDispatch*)&This->IDispatchEx_iface;
799
800 hres = IDispatch_QueryInterface(func_disp, &IID_IDispatchEx, (void**)&dispex);
801 TRACE(">>>\n");
802 if(SUCCEEDED(hres)) {
803 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, flags, &new_dp, res, ei, caller);
804 IDispatchEx_Release(dispex);
805 }else {
806 ULONG err = 0;
807 hres = IDispatch_Invoke(func_disp, DISPID_VALUE, &IID_NULL, lcid, flags, &new_dp, res, ei, &err);
808 }
809 if(SUCCEEDED(hres))
810 TRACE("<<< %s\n", debugstr_variant(res));
811 else
812 WARN("<<< %08x\n", hres);
813
814 heap_free(new_dp.rgvarg);
815 return hres;
816 }
817
get_func_obj_entry(DispatchEx * This,func_info_t * func,func_obj_entry_t ** ret)818 static HRESULT get_func_obj_entry(DispatchEx *This, func_info_t *func, func_obj_entry_t **ret)
819 {
820 dispex_dynamic_data_t *dynamic_data;
821 func_obj_entry_t *entry;
822
823 dynamic_data = get_dynamic_data(This);
824 if(!dynamic_data)
825 return E_OUTOFMEMORY;
826
827 if(!dynamic_data->func_disps) {
828 dynamic_data->func_disps = heap_alloc_zero(This->data->data->func_disp_cnt * sizeof(*dynamic_data->func_disps));
829 if(!dynamic_data->func_disps)
830 return E_OUTOFMEMORY;
831 }
832
833 entry = dynamic_data->func_disps + func->func_disp_idx;
834 if(!entry->func_obj) {
835 entry->func_obj = create_func_disp(This, func);
836 if(!entry->func_obj)
837 return E_OUTOFMEMORY;
838
839 IDispatchEx_AddRef(&entry->func_obj->dispex.IDispatchEx_iface);
840 V_VT(&entry->val) = VT_DISPATCH;
841 V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface;
842 }
843
844 *ret = entry;
845 return S_OK;
846 }
847
get_builtin_func(dispex_data_t * data,DISPID id,func_info_t ** ret)848 static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret)
849 {
850 int min, max, n;
851
852 min = 0;
853 max = data->func_cnt-1;
854
855 while(min <= max) {
856 n = (min+max)/2;
857
858 if(data->funcs[n].id == id) {
859 *ret = data->funcs+n;
860 return S_OK;
861 }
862
863 if(data->funcs[n].id < id)
864 min = n+1;
865 else
866 max = n-1;
867 }
868
869 WARN("invalid id %x\n", id);
870 return DISP_E_UNKNOWNNAME;
871 }
872
get_builtin_id(DispatchEx * This,BSTR name,DWORD grfdex,DISPID * ret)873 static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret)
874 {
875 dispex_data_t *data;
876 int min, max, n, c;
877
878 data = get_dispex_data(This);
879 if(!data)
880 return E_FAIL;
881
882 min = 0;
883 max = data->func_cnt-1;
884
885 while(min <= max) {
886 n = (min+max)/2;
887
888 c = strcmpiW(data->name_table[n]->name, name);
889 if(!c) {
890 if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, name))
891 break;
892
893 *ret = data->name_table[n]->id;
894 return S_OK;
895 }
896
897 if(c > 0)
898 max = n-1;
899 else
900 min = n+1;
901 }
902
903 if(This->data->vtbl && This->data->vtbl->get_dispid) {
904 HRESULT hres;
905
906 hres = This->data->vtbl->get_dispid(This, name, grfdex, ret);
907 if(hres != DISP_E_UNKNOWNNAME)
908 return hres;
909 }
910
911 return DISP_E_UNKNOWNNAME;
912 }
913
change_type(VARIANT * dst,VARIANT * src,VARTYPE vt,IServiceProvider * caller)914 static HRESULT change_type(VARIANT *dst, VARIANT *src, VARTYPE vt, IServiceProvider *caller)
915 {
916 V_VT(dst) = VT_EMPTY;
917
918 if(caller) {
919 IVariantChangeType *change_type = NULL;
920 HRESULT hres;
921
922 hres = IServiceProvider_QueryService(caller, &SID_VariantConversion, &IID_IVariantChangeType, (void**)&change_type);
923 if(SUCCEEDED(hres)) {
924 hres = IVariantChangeType_ChangeType(change_type, dst, src, LOCALE_NEUTRAL, vt);
925 IVariantChangeType_Release(change_type);
926 return hres;
927 }
928 }
929
930 switch(vt) {
931 case VT_BOOL:
932 if(V_VT(src) == VT_BSTR) {
933 V_VT(dst) = VT_BOOL;
934 V_BOOL(dst) = V_BSTR(src) && *V_BSTR(src) ? VARIANT_TRUE : VARIANT_FALSE;
935 return S_OK;
936 }
937 break;
938 }
939
940 return VariantChangeType(dst, src, 0, vt);
941 }
942
builtin_propget(DispatchEx * This,func_info_t * func,DISPPARAMS * dp,VARIANT * res)943 static HRESULT builtin_propget(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res)
944 {
945 IUnknown *iface;
946 HRESULT hres;
947
948 if(dp && dp->cArgs) {
949 FIXME("cArgs %d\n", dp->cArgs);
950 return E_NOTIMPL;
951 }
952
953 assert(func->get_vtbl_off);
954
955 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
956 if(SUCCEEDED(hres)) {
957 switch(func->prop_vt) {
958 #define CASE_VT(vt,type,access) \
959 case vt: { \
960 type val; \
961 hres = ((HRESULT (WINAPI*)(IUnknown*,type*))((void**)iface->lpVtbl)[func->get_vtbl_off])(iface,&val); \
962 if(SUCCEEDED(hres)) \
963 access(res) = val; \
964 } \
965 break
966 BUILTIN_TYPES_SWITCH;
967 #undef CASE_VT
968 default:
969 FIXME("Unhandled vt %d\n", func->prop_vt);
970 hres = E_NOTIMPL;
971 }
972 IUnknown_Release(iface);
973 }
974
975 if(FAILED(hres))
976 return hres;
977
978 if(func->prop_vt != VT_VARIANT)
979 V_VT(res) = func->prop_vt == VT_PTR ? VT_DISPATCH : func->prop_vt;
980 return S_OK;
981 }
982
builtin_propput(DispatchEx * This,func_info_t * func,DISPPARAMS * dp,IServiceProvider * caller)983 static HRESULT builtin_propput(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, IServiceProvider *caller)
984 {
985 VARIANT *v, tmpv;
986 IUnknown *iface;
987 HRESULT hres;
988
989 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
990 || dp->cNamedArgs > 1) {
991 FIXME("invalid args\n");
992 return E_INVALIDARG;
993 }
994
995 if(!func->put_vtbl_off) {
996 FIXME("No setter\n");
997 return E_FAIL;
998 }
999
1000 v = dp->rgvarg;
1001 if(func->prop_vt != VT_VARIANT && V_VT(v) != func->prop_vt) {
1002 hres = change_type(&tmpv, v, func->prop_vt, caller);
1003 if(FAILED(hres))
1004 return hres;
1005 v = &tmpv;
1006 }
1007
1008 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
1009 if(SUCCEEDED(hres)) {
1010 switch(func->prop_vt) {
1011 #define CASE_VT(vt,type,access) \
1012 case vt: \
1013 hres = ((HRESULT (WINAPI*)(IUnknown*,type))((void**)iface->lpVtbl)[func->put_vtbl_off])(iface,access(v)); \
1014 break
1015 BUILTIN_TYPES_SWITCH;
1016 #undef CASE_VT
1017 default:
1018 FIXME("Unimplemented vt %d\n", func->prop_vt);
1019 hres = E_NOTIMPL;
1020 }
1021
1022 IUnknown_Release(iface);
1023 }
1024
1025 if(v == &tmpv)
1026 VariantClear(v);
1027 return hres;
1028 }
1029
invoke_builtin_function(DispatchEx * This,func_info_t * func,DISPPARAMS * dp,VARIANT * res,IServiceProvider * caller)1030 static HRESULT invoke_builtin_function(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res, IServiceProvider *caller)
1031 {
1032 VARIANT arg_buf[MAX_ARGS], *arg_ptrs[MAX_ARGS], *arg, retv, ret_ref, vhres;
1033 unsigned i, nconv = 0;
1034 IUnknown *iface;
1035 HRESULT hres;
1036
1037 if(dp->cNamedArgs) {
1038 FIXME("Named arguments not supported\n");
1039 return E_NOTIMPL;
1040 }
1041
1042 if(dp->cArgs != func->argc) {
1043 FIXME("Invalid argument count (expected %u, got %u)\n", func->argc, dp->cArgs);
1044 return E_INVALIDARG;
1045 }
1046
1047 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
1048 if(FAILED(hres))
1049 return hres;
1050
1051 for(i=0; i < func->argc; i++) {
1052 arg = dp->rgvarg+dp->cArgs-i-1;
1053 if(func->arg_types[i] == V_VT(arg)) {
1054 arg_ptrs[i] = arg;
1055 }else {
1056 hres = change_type(arg_buf+nconv, arg, func->arg_types[i], caller);
1057 if(FAILED(hres))
1058 break;
1059 arg_ptrs[i] = arg_buf + nconv++;
1060 }
1061 }
1062
1063 if(SUCCEEDED(hres)) {
1064 if(func->prop_vt == VT_VOID) {
1065 V_VT(&retv) = VT_EMPTY;
1066 }else {
1067 V_VT(&retv) = func->prop_vt;
1068 arg_ptrs[func->argc] = &ret_ref;
1069 V_VT(&ret_ref) = VT_BYREF|func->prop_vt;
1070
1071 switch(func->prop_vt) {
1072 #define CASE_VT(vt,type,access) \
1073 case vt: \
1074 V_BYREF(&ret_ref) = &access(&retv); \
1075 break
1076 BUILTIN_TYPES_SWITCH;
1077 #undef CASE_VT
1078 default:
1079 assert(0);
1080 }
1081 }
1082
1083 V_VT(&vhres) = VT_ERROR;
1084 hres = DispCallFunc(iface, func->call_vtbl_off*sizeof(void*), CC_STDCALL, VT_ERROR,
1085 func->argc + (func->prop_vt == VT_VOID ? 0 : 1), func->arg_types, arg_ptrs, &vhres);
1086 }
1087
1088 while(nconv--)
1089 VariantClear(arg_buf+nconv);
1090 IUnknown_Release(iface);
1091 if(FAILED(hres))
1092 return hres;
1093 if(FAILED(V_ERROR(&vhres)))
1094 return V_ERROR(&vhres);
1095
1096 if(res)
1097 *res = retv;
1098 else
1099 VariantClear(&retv);
1100 return V_ERROR(&vhres);
1101 }
1102
function_invoke(DispatchEx * This,func_info_t * func,WORD flags,DISPPARAMS * dp,VARIANT * res,EXCEPINFO * ei,IServiceProvider * caller)1103 static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
1104 EXCEPINFO *ei, IServiceProvider *caller)
1105 {
1106 HRESULT hres;
1107
1108 switch(flags) {
1109 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1110 if(!res)
1111 return E_INVALIDARG;
1112 /* fall through */
1113 case DISPATCH_METHOD:
1114 if(This->dynamic_data && This->dynamic_data->func_disps
1115 && This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1116 func_obj_entry_t *entry = This->dynamic_data->func_disps + func->func_disp_idx;
1117
1118 if(V_VT(&entry->val) != VT_DISPATCH) {
1119 FIXME("calling %s not supported\n", debugstr_variant(&entry->val));
1120 return E_NOTIMPL;
1121 }
1122
1123 if((IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface != V_DISPATCH(&entry->val)) {
1124 if(!V_DISPATCH(&entry->val)) {
1125 FIXME("Calling null\n");
1126 return E_FAIL;
1127 }
1128
1129 hres = invoke_disp_value(This, V_DISPATCH(&entry->val), 0, flags, dp, res, ei, NULL);
1130 break;
1131 }
1132 }
1133
1134 if(func->call_vtbl_off)
1135 hres = invoke_builtin_function(This, func, dp, res, caller);
1136 else
1137 hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1138 break;
1139 case DISPATCH_PROPERTYGET: {
1140 func_obj_entry_t *entry;
1141
1142 if(func->id == DISPID_VALUE) {
1143 BSTR ret;
1144
1145 ret = SysAllocString(objectW);
1146 if(!ret)
1147 return E_OUTOFMEMORY;
1148
1149 V_VT(res) = VT_BSTR;
1150 V_BSTR(res) = ret;
1151 return S_OK;
1152 }
1153
1154 hres = get_func_obj_entry(This, func, &entry);
1155 if(FAILED(hres))
1156 return hres;
1157
1158 V_VT(res) = VT_EMPTY;
1159 return VariantCopy(res, &entry->val);
1160 }
1161 case DISPATCH_PROPERTYPUT: {
1162 func_obj_entry_t *entry;
1163
1164 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1165 || dp->cNamedArgs > 1) {
1166 FIXME("invalid args\n");
1167 return E_INVALIDARG;
1168 }
1169
1170 /*
1171 * NOTE: Although we have IDispatchEx tests showing, that it's not allowed to set
1172 * function property using InvokeEx, it's possible to do that from jscript.
1173 * Native probably uses some undocumented interface in this case, but it should
1174 * be fine for us to allow IDispatchEx handle that.
1175 */
1176 hres = get_func_obj_entry(This, func, &entry);
1177 if(FAILED(hres))
1178 return hres;
1179
1180 return VariantCopy(&entry->val, dp->rgvarg);
1181 }
1182 default:
1183 FIXME("Unimplemented flags %x\n", flags);
1184 hres = E_NOTIMPL;
1185 }
1186
1187 return hres;
1188 }
1189
invoke_builtin_prop(DispatchEx * This,DISPID id,LCID lcid,WORD flags,DISPPARAMS * dp,VARIANT * res,EXCEPINFO * ei,IServiceProvider * caller)1190 static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp,
1191 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
1192 {
1193 dispex_data_t *data;
1194 func_info_t *func;
1195 HRESULT hres;
1196
1197 data = get_dispex_data(This);
1198 if(!data)
1199 return E_FAIL;
1200
1201 hres = get_builtin_func(data, id, &func);
1202 if(id == DISPID_VALUE && hres == DISP_E_UNKNOWNNAME)
1203 return dispex_value(This, lcid, flags, dp, res, ei, caller);
1204 if(FAILED(hres))
1205 return hres;
1206
1207 if(func->func_disp_idx != -1)
1208 return function_invoke(This, func, flags, dp, res, ei, caller);
1209
1210 switch(flags) {
1211 case DISPATCH_PROPERTYPUT:
1212 if(res)
1213 V_VT(res) = VT_EMPTY;
1214 hres = builtin_propput(This, func, dp, caller);
1215 break;
1216 case DISPATCH_PROPERTYGET:
1217 hres = builtin_propget(This, func, dp, res);
1218 break;
1219 default:
1220 if(!func->get_vtbl_off) {
1221 hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1222 }else {
1223 VARIANT v;
1224
1225 hres = builtin_propget(This, func, NULL, &v);
1226 if(FAILED(hres))
1227 return hres;
1228
1229 if(flags != (DISPATCH_PROPERTYGET|DISPATCH_METHOD) || dp->cArgs) {
1230 if(V_VT(&v) != VT_DISPATCH) {
1231 FIXME("Not a function %s flags %08x\n", debugstr_variant(&v), flags);
1232 VariantClear(&v);
1233 return E_FAIL;
1234 }
1235
1236 hres = invoke_disp_value(This, V_DISPATCH(&v), lcid, flags, dp, res, ei, caller);
1237 IDispatch_Release(V_DISPATCH(&v));
1238 }else if(res) {
1239 *res = v;
1240 }else {
1241 VariantClear(&v);
1242 }
1243 }
1244 }
1245
1246 return hres;
1247 }
1248
remove_attribute(DispatchEx * This,DISPID id,VARIANT_BOOL * success)1249 HRESULT remove_attribute(DispatchEx *This, DISPID id, VARIANT_BOOL *success)
1250 {
1251 switch(get_dispid_type(id)) {
1252 case DISPEXPROP_CUSTOM:
1253 FIXME("DISPEXPROP_CUSTOM not supported\n");
1254 return E_NOTIMPL;
1255
1256 case DISPEXPROP_DYNAMIC: {
1257 DWORD idx = id - DISPID_DYNPROP_0;
1258 dynamic_prop_t *prop;
1259
1260 prop = This->dynamic_data->props+idx;
1261 VariantClear(&prop->var);
1262 prop->flags |= DYNPROP_DELETED;
1263 *success = VARIANT_TRUE;
1264 return S_OK;
1265 }
1266 case DISPEXPROP_BUILTIN: {
1267 VARIANT var;
1268 DISPPARAMS dp = {&var,NULL,1,0};
1269 dispex_data_t *data;
1270 func_info_t *func;
1271 HRESULT hres;
1272
1273 data = get_dispex_data(This);
1274 if(!data)
1275 return E_FAIL;
1276
1277 hres = get_builtin_func(data, id, &func);
1278 if(FAILED(hres))
1279 return hres;
1280
1281 /* For builtin functions, we set their value to the original function. */
1282 if(func->func_disp_idx != -1) {
1283 func_obj_entry_t *entry;
1284
1285 if(!This->dynamic_data || !This->dynamic_data->func_disps
1286 || !This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1287 *success = VARIANT_FALSE;
1288 return S_OK;
1289 }
1290
1291 entry = This->dynamic_data->func_disps + func->func_disp_idx;
1292 if(V_VT(&entry->val) == VT_DISPATCH
1293 && V_DISPATCH(&entry->val) == (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface) {
1294 *success = VARIANT_FALSE;
1295 return S_OK;
1296 }
1297
1298 VariantClear(&entry->val);
1299 V_VT(&entry->val) = VT_DISPATCH;
1300 V_DISPATCH(&entry->val) = (IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface;
1301 IDispatch_AddRef(V_DISPATCH(&entry->val));
1302 *success = VARIANT_TRUE;
1303 return S_OK;
1304 }
1305
1306 V_VT(&var) = VT_EMPTY;
1307 hres = builtin_propput(This, func, &dp, NULL);
1308 if(FAILED(hres))
1309 return hres;
1310
1311 *success = VARIANT_TRUE;
1312 return S_OK;
1313 }
1314 default:
1315 assert(0);
1316 return E_FAIL;
1317 }
1318 }
1319
impl_from_IDispatchEx(IDispatchEx * iface)1320 static inline DispatchEx *impl_from_IDispatchEx(IDispatchEx *iface)
1321 {
1322 return CONTAINING_RECORD(iface, DispatchEx, IDispatchEx_iface);
1323 }
1324
DispatchEx_QueryInterface(IDispatchEx * iface,REFIID riid,void ** ppv)1325 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1326 {
1327 DispatchEx *This = impl_from_IDispatchEx(iface);
1328
1329 return IUnknown_QueryInterface(This->outer, riid, ppv);
1330 }
1331
DispatchEx_AddRef(IDispatchEx * iface)1332 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1333 {
1334 DispatchEx *This = impl_from_IDispatchEx(iface);
1335
1336 return IUnknown_AddRef(This->outer);
1337 }
1338
DispatchEx_Release(IDispatchEx * iface)1339 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1340 {
1341 DispatchEx *This = impl_from_IDispatchEx(iface);
1342
1343 return IUnknown_Release(This->outer);
1344 }
1345
DispatchEx_GetTypeInfoCount(IDispatchEx * iface,UINT * pctinfo)1346 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1347 {
1348 DispatchEx *This = impl_from_IDispatchEx(iface);
1349
1350 TRACE("(%p)->(%p)\n", This, pctinfo);
1351
1352 *pctinfo = 1;
1353 return S_OK;
1354 }
1355
DispatchEx_GetTypeInfo(IDispatchEx * iface,UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)1356 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
1357 LCID lcid, ITypeInfo **ppTInfo)
1358 {
1359 DispatchEx *This = impl_from_IDispatchEx(iface);
1360 HRESULT hres;
1361
1362 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1363
1364 hres = get_typeinfo(This->data->disp_tid, ppTInfo);
1365 if(FAILED(hres))
1366 return hres;
1367
1368 ITypeInfo_AddRef(*ppTInfo);
1369 return S_OK;
1370 }
1371
DispatchEx_GetIDsOfNames(IDispatchEx * iface,REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)1372 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1373 LPOLESTR *rgszNames, UINT cNames,
1374 LCID lcid, DISPID *rgDispId)
1375 {
1376 DispatchEx *This = impl_from_IDispatchEx(iface);
1377 UINT i;
1378 HRESULT hres;
1379
1380 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1381 lcid, rgDispId);
1382
1383 for(i=0; i < cNames; i++) {
1384 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
1385 if(FAILED(hres))
1386 return hres;
1387 }
1388
1389 return S_OK;
1390 }
1391
DispatchEx_Invoke(IDispatchEx * iface,DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)1392 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1393 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1394 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1395 {
1396 DispatchEx *This = impl_from_IDispatchEx(iface);
1397
1398 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1399 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1400
1401 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, pDispParams,
1402 pVarResult, pExcepInfo, NULL);
1403 }
1404
DispatchEx_GetDispID(IDispatchEx * iface,BSTR bstrName,DWORD grfdex,DISPID * pid)1405 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1406 {
1407 DispatchEx *This = impl_from_IDispatchEx(iface);
1408 dynamic_prop_t *dprop;
1409 HRESULT hres;
1410
1411 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
1412
1413 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
1414 FIXME("Unsupported grfdex %x\n", grfdex);
1415
1416 hres = get_builtin_id(This, bstrName, grfdex, pid);
1417 if(hres != DISP_E_UNKNOWNNAME)
1418 return hres;
1419
1420 hres = get_dynamic_prop(This, bstrName, grfdex, &dprop);
1421 if(FAILED(hres))
1422 return hres;
1423
1424 *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
1425 return S_OK;
1426 }
1427
DispatchEx_InvokeEx(IDispatchEx * iface,DISPID id,LCID lcid,WORD wFlags,DISPPARAMS * pdp,VARIANT * pvarRes,EXCEPINFO * pei,IServiceProvider * pspCaller)1428 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1429 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1430 {
1431 DispatchEx *This = impl_from_IDispatchEx(iface);
1432 HRESULT hres;
1433
1434 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1435
1436 if(wFlags == (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF))
1437 wFlags = DISPATCH_PROPERTYPUT;
1438
1439 switch(get_dispid_type(id)) {
1440 case DISPEXPROP_CUSTOM:
1441 if(!This->data->vtbl || !This->data->vtbl->invoke)
1442 return DISP_E_UNKNOWNNAME;
1443 return This->data->vtbl->invoke(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1444
1445 case DISPEXPROP_DYNAMIC: {
1446 DWORD idx = id - DISPID_DYNPROP_0;
1447 dynamic_prop_t *prop;
1448
1449 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1450 return DISP_E_UNKNOWNNAME;
1451
1452 prop = This->dynamic_data->props+idx;
1453
1454 switch(wFlags) {
1455 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1456 if(!pvarRes)
1457 return E_INVALIDARG;
1458 /* fall through */
1459 case DISPATCH_METHOD:
1460 if(V_VT(&prop->var) != VT_DISPATCH) {
1461 FIXME("invoke %s\n", debugstr_variant(&prop->var));
1462 return E_NOTIMPL;
1463 }
1464
1465 return invoke_disp_value(This, V_DISPATCH(&prop->var), lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1466 case DISPATCH_PROPERTYGET:
1467 if(prop->flags & DYNPROP_DELETED)
1468 return DISP_E_UNKNOWNNAME;
1469 V_VT(pvarRes) = VT_EMPTY;
1470 return variant_copy(pvarRes, &prop->var);
1471 case DISPATCH_PROPERTYPUT:
1472 if(pdp->cArgs != 1 || (pdp->cNamedArgs == 1 && *pdp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1473 || pdp->cNamedArgs > 1) {
1474 FIXME("invalid args\n");
1475 return E_INVALIDARG;
1476 }
1477
1478 TRACE("put %s\n", debugstr_variant(pdp->rgvarg));
1479 VariantClear(&prop->var);
1480 hres = variant_copy(&prop->var, pdp->rgvarg);
1481 if(FAILED(hres))
1482 return hres;
1483
1484 prop->flags &= ~DYNPROP_DELETED;
1485 return S_OK;
1486 default:
1487 FIXME("unhandled wFlags %x\n", wFlags);
1488 return E_NOTIMPL;
1489 }
1490 }
1491 case DISPEXPROP_BUILTIN:
1492 if(wFlags == DISPATCH_CONSTRUCT) {
1493 if(id == DISPID_VALUE) {
1494 if(This->data->vtbl && This->data->vtbl->value) {
1495 return This->data->vtbl->value(This, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1496 }
1497 FIXME("DISPATCH_CONSTRUCT flag but missing value function\n");
1498 return E_FAIL;
1499 }
1500 FIXME("DISPATCH_CONSTRUCT flag without DISPID_VALUE\n");
1501 return E_FAIL;
1502 }
1503
1504 return invoke_builtin_prop(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1505 default:
1506 assert(0);
1507 return E_FAIL;
1508 }
1509 }
1510
DispatchEx_DeleteMemberByName(IDispatchEx * iface,BSTR bstrName,DWORD grfdex)1511 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
1512 {
1513 DispatchEx *This = impl_from_IDispatchEx(iface);
1514
1515 TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
1516
1517 /* Not implemented by IE */
1518 return E_NOTIMPL;
1519 }
1520
DispatchEx_DeleteMemberByDispID(IDispatchEx * iface,DISPID id)1521 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
1522 {
1523 DispatchEx *This = impl_from_IDispatchEx(iface);
1524
1525 TRACE("(%p)->(%x)\n", This, id);
1526
1527 /* Not implemented by IE */
1528 return E_NOTIMPL;
1529 }
1530
DispatchEx_GetMemberProperties(IDispatchEx * iface,DISPID id,DWORD grfdexFetch,DWORD * pgrfdex)1531 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
1532 {
1533 DispatchEx *This = impl_from_IDispatchEx(iface);
1534 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
1535 return E_NOTIMPL;
1536 }
1537
DispatchEx_GetMemberName(IDispatchEx * iface,DISPID id,BSTR * pbstrName)1538 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
1539 {
1540 DispatchEx *This = impl_from_IDispatchEx(iface);
1541 dispex_data_t *data;
1542 func_info_t *func;
1543 HRESULT hres;
1544
1545 TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
1546
1547 if(is_dynamic_dispid(id)) {
1548 DWORD idx = id - DISPID_DYNPROP_0;
1549
1550 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1551 return DISP_E_UNKNOWNNAME;
1552
1553 *pbstrName = SysAllocString(This->dynamic_data->props[idx].name);
1554 if(!*pbstrName)
1555 return E_OUTOFMEMORY;
1556
1557 return S_OK;
1558 }
1559
1560 data = get_dispex_data(This);
1561 if(!data)
1562 return E_FAIL;
1563
1564 hres = get_builtin_func(data, id, &func);
1565 if(FAILED(hres))
1566 return hres;
1567
1568 *pbstrName = SysAllocString(func->name);
1569 if(!*pbstrName)
1570 return E_OUTOFMEMORY;
1571 return S_OK;
1572 }
1573
DispatchEx_GetNextDispID(IDispatchEx * iface,DWORD grfdex,DISPID id,DISPID * pid)1574 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
1575 {
1576 DispatchEx *This = impl_from_IDispatchEx(iface);
1577 dispex_data_t *data;
1578 func_info_t *func;
1579 HRESULT hres;
1580
1581 TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
1582
1583 if(is_dynamic_dispid(id)) {
1584 DWORD idx = id - DISPID_DYNPROP_0;
1585
1586 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1587 return DISP_E_UNKNOWNNAME;
1588
1589 while(++idx < This->dynamic_data->prop_cnt && This->dynamic_data->props[idx].flags & DYNPROP_DELETED);
1590
1591 if(idx == This->dynamic_data->prop_cnt) {
1592 *pid = DISPID_STARTENUM;
1593 return S_FALSE;
1594 }
1595
1596 *pid = DISPID_DYNPROP_0+idx;
1597 return S_OK;
1598 }
1599
1600 data = get_dispex_data(This);
1601 if(!data)
1602 return E_FAIL;
1603
1604 if(id == DISPID_STARTENUM) {
1605 func = data->funcs;
1606 }else {
1607 hres = get_builtin_func(data, id, &func);
1608 if(FAILED(hres))
1609 return hres;
1610 func++;
1611 }
1612
1613 while(func < data->funcs+data->func_cnt) {
1614 /* FIXME: Skip hidden properties */
1615 if(func->func_disp_idx == -1) {
1616 *pid = func->id;
1617 return S_OK;
1618 }
1619 func++;
1620 }
1621
1622 if(get_dynamic_data(This) && This->dynamic_data->prop_cnt) {
1623 *pid = DISPID_DYNPROP_0;
1624 return S_OK;
1625 }
1626
1627 *pid = DISPID_STARTENUM;
1628 return S_FALSE;
1629 }
1630
DispatchEx_GetNameSpaceParent(IDispatchEx * iface,IUnknown ** ppunk)1631 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
1632 {
1633 DispatchEx *This = impl_from_IDispatchEx(iface);
1634 FIXME("(%p)->(%p)\n", This, ppunk);
1635 return E_NOTIMPL;
1636 }
1637
1638 static IDispatchExVtbl DispatchExVtbl = {
1639 DispatchEx_QueryInterface,
1640 DispatchEx_AddRef,
1641 DispatchEx_Release,
1642 DispatchEx_GetTypeInfoCount,
1643 DispatchEx_GetTypeInfo,
1644 DispatchEx_GetIDsOfNames,
1645 DispatchEx_Invoke,
1646 DispatchEx_GetDispID,
1647 DispatchEx_InvokeEx,
1648 DispatchEx_DeleteMemberByName,
1649 DispatchEx_DeleteMemberByDispID,
1650 DispatchEx_GetMemberProperties,
1651 DispatchEx_GetMemberName,
1652 DispatchEx_GetNextDispID,
1653 DispatchEx_GetNameSpaceParent
1654 };
1655
dispex_query_interface(DispatchEx * This,REFIID riid,void ** ppv)1656 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
1657 {
1658 if(IsEqualGUID(&IID_IDispatch, riid))
1659 *ppv = &This->IDispatchEx_iface;
1660 else if(IsEqualGUID(&IID_IDispatchEx, riid))
1661 *ppv = &This->IDispatchEx_iface;
1662 else if(IsEqualGUID(&IID_IDispatchJS, riid))
1663 *ppv = NULL;
1664 else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid))
1665 *ppv = NULL;
1666 else
1667 return FALSE;
1668
1669 if(*ppv)
1670 IUnknown_AddRef((IUnknown*)*ppv);
1671 return TRUE;
1672 }
1673
dispex_traverse(DispatchEx * This,nsCycleCollectionTraversalCallback * cb)1674 void dispex_traverse(DispatchEx *This, nsCycleCollectionTraversalCallback *cb)
1675 {
1676 dynamic_prop_t *prop;
1677
1678 if(!This->dynamic_data)
1679 return;
1680
1681 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1682 if(V_VT(&prop->var) == VT_DISPATCH)
1683 note_cc_edge((nsISupports*)V_DISPATCH(&prop->var), "dispex_data", cb);
1684 }
1685
1686 /* FIXME: Traverse func_disps */
1687 }
1688
dispex_unlink(DispatchEx * This)1689 void dispex_unlink(DispatchEx *This)
1690 {
1691 dynamic_prop_t *prop;
1692
1693 if(!This->dynamic_data)
1694 return;
1695
1696 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1697 if(V_VT(&prop->var) == VT_DISPATCH) {
1698 V_VT(&prop->var) = VT_EMPTY;
1699 IDispatch_Release(V_DISPATCH(&prop->var));
1700 }else {
1701 VariantClear(&prop->var);
1702 }
1703 }
1704 }
1705
release_dispex(DispatchEx * This)1706 void release_dispex(DispatchEx *This)
1707 {
1708 dynamic_prop_t *prop;
1709
1710 if(!This->dynamic_data)
1711 return;
1712
1713 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1714 VariantClear(&prop->var);
1715 heap_free(prop->name);
1716 }
1717
1718 heap_free(This->dynamic_data->props);
1719
1720 if(This->dynamic_data->func_disps) {
1721 func_obj_entry_t *iter;
1722
1723 for(iter = This->dynamic_data->func_disps; iter < This->dynamic_data->func_disps+This->data->data->func_disp_cnt; iter++) {
1724 if(iter->func_obj) {
1725 iter->func_obj->obj = NULL;
1726 IDispatchEx_Release(&iter->func_obj->dispex.IDispatchEx_iface);
1727 }
1728 VariantClear(&iter->val);
1729 }
1730
1731 heap_free(This->dynamic_data->func_disps);
1732 }
1733
1734 heap_free(This->dynamic_data);
1735 }
1736
init_dispex(DispatchEx * dispex,IUnknown * outer,dispex_static_data_t * data)1737 void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
1738 {
1739 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
1740 dispex->outer = outer;
1741 dispex->data = data;
1742 dispex->dynamic_data = NULL;
1743 }
1744