Revert r66580 and r66579.
[reactos.git] / reactos / dll / win32 / mshtml / dispex.c
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 IDispatch *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
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
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
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
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
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
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 if(data->func_cnt && data->funcs[data->func_cnt-1].id == desc->memid) {
219 info = data->funcs+data->func_cnt-1;
220 }else {
221 if(data->func_cnt == *size)
222 data->funcs = heap_realloc_zero(data->funcs, (*size <<= 1)*sizeof(func_info_t));
223
224 info = data->funcs+data->func_cnt;
225 hres = ITypeInfo_GetDocumentation(dti, desc->memid, &info->name, NULL, NULL, NULL);
226 if(FAILED(hres))
227 return;
228
229 data->func_cnt++;
230
231 info->id = desc->memid;
232 info->tid = tid;
233 info->func_disp_idx = -1;
234 info->prop_vt = VT_EMPTY;
235 }
236
237 if(desc->invkind & DISPATCH_METHOD) {
238 unsigned i;
239
240 info->func_disp_idx = data->func_disp_cnt++;
241 info->argc = desc->cParams;
242
243 assert(info->argc < MAX_ARGS);
244 assert(desc->funckind == FUNC_DISPATCH);
245
246 info->arg_types = heap_alloc(sizeof(*info->arg_types) * info->argc);
247 if(!info->arg_types)
248 return; /* FIXME: real error instead of fallback */
249
250 for(i=0; i < info->argc; i++)
251 info->arg_types[i] = desc->lprgelemdescParam[i].tdesc.vt;
252
253 info->prop_vt = desc->elemdescFunc.tdesc.vt;
254 if(info->prop_vt != VT_VOID && !is_arg_type_supported(info->prop_vt)) {
255 TRACE("%s: return type %d\n", debugstr_w(info->name), info->prop_vt);
256 return; /* Fallback to ITypeInfo::Invoke */
257 }
258
259 if(desc->cParamsOpt) {
260 TRACE("%s: optional params\n", debugstr_w(info->name));
261 return; /* Fallback to ITypeInfo::Invoke */
262 }
263
264 for(i=0; i < info->argc; i++) {
265 if(!is_arg_type_supported(info->arg_types[i])) {
266 return; /* Fallback to ITypeInfo for unsupported arg types */
267 }
268
269 if(desc->lprgelemdescParam[i].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) {
270 TRACE("%s param %d: default value\n", debugstr_w(info->name), i);
271 return; /* Fallback to ITypeInfo::Invoke */
272 }
273 }
274
275 assert(info->argc <= MAX_ARGS);
276 assert(desc->callconv == CC_STDCALL);
277
278 info->call_vtbl_off = desc->oVft/sizeof(void*);
279 }else if(desc->invkind & (DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYGET)) {
280 VARTYPE vt = VT_EMPTY;
281
282 if(desc->invkind & DISPATCH_PROPERTYGET) {
283 vt = desc->elemdescFunc.tdesc.vt;
284 info->get_vtbl_off = desc->oVft/sizeof(void*);
285 }
286 if(desc->invkind & DISPATCH_PROPERTYPUT) {
287 assert(desc->cParams == 1);
288 vt = desc->lprgelemdescParam->tdesc.vt;
289 info->put_vtbl_off = desc->oVft/sizeof(void*);
290 }
291
292 assert(info->prop_vt == VT_EMPTY || vt == info->prop_vt);
293 info->prop_vt = vt;
294 }
295 }
296
297 static int dispid_cmp(const void *p1, const void *p2)
298 {
299 return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id;
300 }
301
302 static int func_name_cmp(const void *p1, const void *p2)
303 {
304 return strcmpiW((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name);
305 }
306
307 static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
308 {
309 const tid_t *tid = This->data->iface_tids;
310 FUNCDESC *funcdesc;
311 dispex_data_t *data;
312 DWORD size = 16, i;
313 ITypeInfo *ti, *dti;
314 HRESULT hres;
315
316 TRACE("(%p)\n", This);
317
318 if(This->data->disp_tid) {
319 hres = get_typeinfo(This->data->disp_tid, &dti);
320 if(FAILED(hres)) {
321 ERR("Could not get disp type info: %08x\n", hres);
322 return NULL;
323 }
324 }
325
326 data = heap_alloc(sizeof(dispex_data_t));
327 data->func_cnt = 0;
328 data->func_disp_cnt = 0;
329 data->funcs = heap_alloc_zero(size*sizeof(func_info_t));
330 list_add_tail(&dispex_data_list, &data->entry);
331
332 while(*tid) {
333 hres = get_typeinfo(*tid, &ti);
334 if(FAILED(hres))
335 break;
336
337 i=7;
338 while(1) {
339 hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
340 if(FAILED(hres))
341 break;
342
343 add_func_info(data, &size, *tid, funcdesc, dti);
344 ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
345 }
346
347 tid++;
348 }
349
350 if(!data->func_cnt) {
351 heap_free(data->funcs);
352 data->name_table = NULL;
353 data->funcs = NULL;
354 return data;
355 }
356
357
358 data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
359 qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
360
361 for(i = 1; i < data->func_cnt && data->funcs[i-1].id != data->funcs[i].id; i++);
362 if(i < data->func_cnt) {
363 unsigned j = i--;
364
365 /* We have at least one duplicated property. This may happen if more than one
366 * interface implements the same property. We have to remove these duplicated
367 * entries. */
368
369 while(j < data->func_cnt) {
370 while(j+1 < data->func_cnt && data->funcs[j+1].id == data->funcs[j].id)
371 j++;
372 data->funcs[i++] = data->funcs[j++];
373 }
374 data->func_cnt = i;
375 }
376
377 data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
378 for(i=0; i < data->func_cnt; i++)
379 data->name_table[i] = data->funcs+i;
380 qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
381
382 return data;
383 }
384
385 static int id_cmp(const void *p1, const void *p2)
386 {
387 return *(const DISPID*)p1 - *(const DISPID*)p2;
388 }
389
390 HRESULT get_dispids(tid_t tid, DWORD *ret_size, DISPID **ret)
391 {
392 unsigned i, func_cnt;
393 FUNCDESC *funcdesc;
394 ITypeInfo *ti;
395 TYPEATTR *attr;
396 DISPID *ids;
397 HRESULT hres;
398
399 hres = get_typeinfo(tid, &ti);
400 if(FAILED(hres))
401 return hres;
402
403 hres = ITypeInfo_GetTypeAttr(ti, &attr);
404 if(FAILED(hres)) {
405 ITypeInfo_Release(ti);
406 return hres;
407 }
408
409 func_cnt = attr->cFuncs;
410 ITypeInfo_ReleaseTypeAttr(ti, attr);
411
412 ids = heap_alloc(func_cnt*sizeof(DISPID));
413 if(!ids) {
414 ITypeInfo_Release(ti);
415 return E_OUTOFMEMORY;
416 }
417
418 for(i=0; i < func_cnt; i++) {
419 hres = ITypeInfo_GetFuncDesc(ti, i, &funcdesc);
420 if(FAILED(hres))
421 break;
422
423 ids[i] = funcdesc->memid;
424 ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
425 }
426
427 ITypeInfo_Release(ti);
428 if(FAILED(hres)) {
429 heap_free(ids);
430 return hres;
431 }
432
433 qsort(ids, func_cnt, sizeof(DISPID), id_cmp);
434
435 *ret_size = func_cnt;
436 *ret = ids;
437 return S_OK;
438 }
439
440 static dispex_data_t *get_dispex_data(DispatchEx *This)
441 {
442 if(This->data->data)
443 return This->data->data;
444
445 EnterCriticalSection(&cs_dispex_static_data);
446
447 if(!This->data->data)
448 This->data->data = preprocess_dispex_data(This);
449
450 LeaveCriticalSection(&cs_dispex_static_data);
451
452 return This->data->data;
453 }
454
455 static inline BOOL is_custom_dispid(DISPID id)
456 {
457 return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
458 }
459
460 static inline BOOL is_dynamic_dispid(DISPID id)
461 {
462 return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
463 }
464
465 dispex_prop_type_t get_dispid_type(DISPID id)
466 {
467 if(is_dynamic_dispid(id))
468 return DISPEXPROP_DYNAMIC;
469 if(is_custom_dispid(id))
470 return DISPEXPROP_CUSTOM;
471 return DISPEXPROP_BUILTIN;
472 }
473
474 static HRESULT variant_copy(VARIANT *dest, VARIANT *src)
475 {
476 if(V_VT(src) == VT_BSTR && !V_BSTR(src)) {
477 V_VT(dest) = VT_BSTR;
478 V_BSTR(dest) = NULL;
479 return S_OK;
480 }
481
482 return VariantCopy(dest, src);
483 }
484
485 static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This)
486 {
487 if(This->dynamic_data)
488 return This->dynamic_data;
489
490 This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t));
491 if(!This->dynamic_data)
492 return NULL;
493
494 if(This->data->vtbl && This->data->vtbl->populate_props)
495 This->data->vtbl->populate_props(This);
496
497 return This->dynamic_data;
498 }
499
500 static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, DWORD flags, dynamic_prop_t **ret)
501 {
502 const BOOL alloc = flags & fdexNameEnsure;
503 dispex_dynamic_data_t *data;
504 dynamic_prop_t *prop;
505
506 data = get_dynamic_data(This);
507 if(!data)
508 return E_OUTOFMEMORY;
509
510 for(prop = data->props; prop < data->props+data->prop_cnt; prop++) {
511 if(flags & fdexNameCaseInsensitive ? !strcmpiW(prop->name, name) : !strcmpW(prop->name, name)) {
512 if(prop->flags & DYNPROP_DELETED) {
513 if(!alloc)
514 return DISP_E_UNKNOWNNAME;
515 prop->flags &= ~DYNPROP_DELETED;
516 }
517 *ret = prop;
518 return S_OK;
519 }
520 }
521
522 if(!alloc)
523 return DISP_E_UNKNOWNNAME;
524
525 TRACE("creating dynamic prop %s\n", debugstr_w(name));
526
527 if(!data->buf_size) {
528 data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
529 if(!data->props)
530 return E_OUTOFMEMORY;
531 data->buf_size = 4;
532 }else if(data->buf_size == data->prop_cnt) {
533 dynamic_prop_t *new_props;
534
535 new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1));
536 if(!new_props)
537 return E_OUTOFMEMORY;
538
539 data->props = new_props;
540 data->buf_size <<= 1;
541 }
542
543 prop = data->props + data->prop_cnt;
544
545 prop->name = heap_strdupW(name);
546 if(!prop->name)
547 return E_OUTOFMEMORY;
548
549 VariantInit(&prop->var);
550 prop->flags = 0;
551 data->prop_cnt++;
552 *ret = prop;
553 return S_OK;
554 }
555
556 HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
557 {
558 dynamic_prop_t *prop;
559 HRESULT hres;
560
561 hres = get_dynamic_prop(This, name, alloc ? fdexNameEnsure : 0, &prop);
562 if(FAILED(hres))
563 return hres;
564
565 *ret = &prop->var;
566 return S_OK;
567 }
568
569 HRESULT dispex_get_dynid(DispatchEx *This, const WCHAR *name, DISPID *id)
570 {
571 dynamic_prop_t *prop;
572 HRESULT hres;
573
574 hres = get_dynamic_prop(This, name, fdexNameEnsure, &prop);
575 if(FAILED(hres))
576 return hres;
577
578 *id = DISPID_DYNPROP_0 + (prop - This->dynamic_data->props);
579 return S_OK;
580 }
581
582 static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params,
583 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
584 {
585 if(This->data->vtbl && This->data->vtbl->value)
586 return This->data->vtbl->value(This, lcid, flags, params, res, ei, caller);
587
588 switch(flags) {
589 case DISPATCH_PROPERTYGET:
590 V_VT(res) = VT_BSTR;
591 V_BSTR(res) = SysAllocString(objectW);
592 if(!V_BSTR(res))
593 return E_OUTOFMEMORY;
594 break;
595 default:
596 FIXME("Unimplemented flags %x\n", flags);
597 return E_NOTIMPL;
598 }
599
600 return S_OK;
601 }
602
603 static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
604 EXCEPINFO *ei)
605 {
606 ITypeInfo *ti;
607 IUnknown *unk;
608 UINT argerr=0;
609 HRESULT hres;
610
611 hres = get_typeinfo(func->tid, &ti);
612 if(FAILED(hres)) {
613 ERR("Could not get type info: %08x\n", hres);
614 return hres;
615 }
616
617 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&unk);
618 if(FAILED(hres)) {
619 ERR("Could not get iface %s: %08x\n", debugstr_mshtml_guid(tid_ids[func->tid]), hres);
620 return E_FAIL;
621 }
622
623 hres = ITypeInfo_Invoke(ti, unk, func->id, flags, dp, res, ei, &argerr);
624
625 IUnknown_Release(unk);
626 return hres;
627 }
628
629 static inline func_disp_t *impl_from_IUnknown(IUnknown *iface)
630 {
631 return CONTAINING_RECORD(iface, func_disp_t, IUnknown_iface);
632 }
633
634 static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
635 {
636 func_disp_t *This = impl_from_IUnknown(iface);
637
638 if(IsEqualGUID(&IID_IUnknown, riid)) {
639 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
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
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
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
684 static inline func_disp_t *impl_from_DispatchEx(DispatchEx *iface)
685 {
686 return CONTAINING_RECORD(iface, func_disp_t, dispex);
687 }
688
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
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
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
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
822 dynamic_data = get_dynamic_data(This);
823 if(!dynamic_data)
824 return E_OUTOFMEMORY;
825
826 if(!dynamic_data->func_disps) {
827 dynamic_data->func_disps = heap_alloc_zero(This->data->data->func_disp_cnt * sizeof(*dynamic_data->func_disps));
828 if(!dynamic_data->func_disps)
829 return E_OUTOFMEMORY;
830 }
831
832 if(!dynamic_data->func_disps[func->func_disp_idx].func_obj) {
833 func_disp_t *func_obj;
834
835 func_obj = create_func_disp(This, func);
836 if(!func_obj)
837 return E_OUTOFMEMORY;
838
839 dynamic_data->func_disps[func->func_disp_idx].func_obj = func_obj;
840
841 IDispatchEx_AddRef(&func_obj->dispex.IDispatchEx_iface);
842 dynamic_data->func_disps[func->func_disp_idx].val = (IDispatch*)&func_obj->dispex.IDispatchEx_iface;
843 }
844
845 *ret = dynamic_data->func_disps+func->func_disp_idx;
846 return S_OK;
847 }
848
849 static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret)
850 {
851 int min, max, n;
852
853 min = 0;
854 max = data->func_cnt-1;
855
856 while(min <= max) {
857 n = (min+max)/2;
858
859 if(data->funcs[n].id == id) {
860 *ret = data->funcs+n;
861 return S_OK;
862 }
863
864 if(data->funcs[n].id < id)
865 min = n+1;
866 else
867 max = n-1;
868 }
869
870 WARN("invalid id %x\n", id);
871 return DISP_E_UNKNOWNNAME;
872 }
873
874 static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret)
875 {
876 dispex_data_t *data;
877 int min, max, n, c;
878
879 data = get_dispex_data(This);
880 if(!data)
881 return E_FAIL;
882
883 min = 0;
884 max = data->func_cnt-1;
885
886 while(min <= max) {
887 n = (min+max)/2;
888
889 c = strcmpiW(data->name_table[n]->name, name);
890 if(!c) {
891 if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, name))
892 break;
893
894 *ret = data->name_table[n]->id;
895 return S_OK;
896 }
897
898 if(c > 0)
899 max = n-1;
900 else
901 min = n+1;
902 }
903
904 if(This->data->vtbl && This->data->vtbl->get_dispid) {
905 HRESULT hres;
906
907 hres = This->data->vtbl->get_dispid(This, name, grfdex, ret);
908 if(hres != DISP_E_UNKNOWNNAME)
909 return hres;
910 }
911
912 return DISP_E_UNKNOWNNAME;
913 }
914
915 static HRESULT change_type(VARIANT *dst, VARIANT *src, VARTYPE vt, IServiceProvider *caller)
916 {
917 V_VT(dst) = VT_EMPTY;
918
919 if(caller) {
920 IVariantChangeType *change_type = NULL;
921 HRESULT hres;
922
923 hres = IServiceProvider_QueryService(caller, &SID_VariantConversion, &IID_IVariantChangeType, (void**)&change_type);
924 if(SUCCEEDED(hres)) {
925 hres = IVariantChangeType_ChangeType(change_type, dst, src, LOCALE_NEUTRAL, vt);
926 IVariantChangeType_Release(change_type);
927 return hres;
928 }
929 }
930
931 switch(vt) {
932 case VT_BOOL:
933 if(V_VT(src) == VT_BSTR) {
934 V_VT(dst) = VT_BOOL;
935 V_BOOL(dst) = V_BSTR(src) && *V_BSTR(src) ? VARIANT_TRUE : VARIANT_FALSE;
936 return S_OK;
937 }
938 break;
939 }
940
941 return VariantChangeType(dst, src, 0, vt);
942 }
943
944 static HRESULT builtin_propget(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res)
945 {
946 IUnknown *iface;
947 HRESULT hres;
948
949 if(dp && dp->cArgs) {
950 FIXME("cArgs %d\n", dp->cArgs);
951 return E_NOTIMPL;
952 }
953
954 assert(func->get_vtbl_off);
955
956 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
957 if(SUCCEEDED(hres)) {
958 switch(func->prop_vt) {
959 #define CASE_VT(vt,type,access) \
960 case vt: { \
961 type val; \
962 hres = ((HRESULT (WINAPI*)(IUnknown*,type*))((void**)iface->lpVtbl)[func->get_vtbl_off])(iface,&val); \
963 if(SUCCEEDED(hres)) \
964 access(res) = val; \
965 } \
966 break
967 BUILTIN_TYPES_SWITCH;
968 #undef CASE_VT
969 default:
970 FIXME("Unhandled vt %d\n", func->prop_vt);
971 hres = E_NOTIMPL;
972 }
973 IUnknown_Release(iface);
974 }
975
976 if(FAILED(hres))
977 return hres;
978
979 if(func->prop_vt != VT_VARIANT)
980 V_VT(res) = func->prop_vt == VT_PTR ? VT_DISPATCH : func->prop_vt;
981 return S_OK;
982 }
983
984 static HRESULT builtin_propput(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, IServiceProvider *caller)
985 {
986 VARIANT *v, tmpv;
987 IUnknown *iface;
988 HRESULT hres;
989
990 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
991 || dp->cNamedArgs > 1) {
992 FIXME("invalid args\n");
993 return E_INVALIDARG;
994 }
995
996 if(!func->put_vtbl_off) {
997 FIXME("No setter\n");
998 return E_FAIL;
999 }
1000
1001 v = dp->rgvarg;
1002 if(func->prop_vt != VT_VARIANT && V_VT(v) != func->prop_vt) {
1003 hres = change_type(&tmpv, v, func->prop_vt, caller);
1004 if(FAILED(hres))
1005 return hres;
1006 v = &tmpv;
1007 }
1008
1009 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
1010 if(SUCCEEDED(hres)) {
1011 switch(func->prop_vt) {
1012 #define CASE_VT(vt,type,access) \
1013 case vt: \
1014 hres = ((HRESULT (WINAPI*)(IUnknown*,type))((void**)iface->lpVtbl)[func->put_vtbl_off])(iface,access(v)); \
1015 break
1016 BUILTIN_TYPES_SWITCH;
1017 #undef CASE_VT
1018 default:
1019 FIXME("Unimplemented vt %d\n", func->prop_vt);
1020 hres = E_NOTIMPL;
1021 }
1022
1023 IUnknown_Release(iface);
1024 }
1025
1026 if(v == &tmpv)
1027 VariantClear(v);
1028 return hres;
1029 }
1030
1031 static HRESULT invoke_builtin_function(DispatchEx *This, func_info_t *func, DISPPARAMS *dp, VARIANT *res, IServiceProvider *caller)
1032 {
1033 VARIANT arg_buf[MAX_ARGS], *arg_ptrs[MAX_ARGS], *arg, retv, ret_ref, vhres;
1034 unsigned i, nconv = 0;
1035 IUnknown *iface;
1036 HRESULT hres;
1037
1038 if(dp->cNamedArgs) {
1039 FIXME("Named arguments not supported\n");
1040 return E_NOTIMPL;
1041 }
1042
1043 if(dp->cArgs != func->argc) {
1044 FIXME("Invalid argument count (expected %u, got %u)\n", func->argc, dp->cArgs);
1045 return E_INVALIDARG;
1046 }
1047
1048 hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&iface);
1049 if(FAILED(hres))
1050 return hres;
1051
1052 for(i=0; i < func->argc; i++) {
1053 arg = dp->rgvarg+dp->cArgs-i-1;
1054 if(func->arg_types[i] == V_VT(arg)) {
1055 arg_ptrs[i] = arg;
1056 }else {
1057 hres = change_type(arg_buf+nconv, arg, func->arg_types[i], caller);
1058 if(FAILED(hres))
1059 break;
1060 arg_ptrs[i] = arg_buf + nconv++;
1061 }
1062 }
1063
1064 if(SUCCEEDED(hres)) {
1065 if(func->prop_vt == VT_VOID) {
1066 V_VT(&retv) = VT_EMPTY;
1067 }else {
1068 V_VT(&retv) = func->prop_vt;
1069 arg_ptrs[func->argc] = &ret_ref;
1070 V_VT(&ret_ref) = VT_BYREF|func->prop_vt;
1071
1072 switch(func->prop_vt) {
1073 #define CASE_VT(vt,type,access) \
1074 case vt: \
1075 V_BYREF(&ret_ref) = &access(&retv); \
1076 break
1077 BUILTIN_TYPES_SWITCH;
1078 #undef CASE_VT
1079 default:
1080 assert(0);
1081 }
1082 }
1083
1084 V_VT(&vhres) = VT_ERROR;
1085 hres = DispCallFunc(iface, func->call_vtbl_off*sizeof(void*), CC_STDCALL, VT_ERROR,
1086 func->argc + (func->prop_vt == VT_VOID ? 0 : 1), func->arg_types, arg_ptrs, &vhres);
1087 }
1088
1089 while(nconv--)
1090 VariantClear(arg_buf+nconv);
1091 IUnknown_Release(iface);
1092 if(FAILED(hres))
1093 return hres;
1094 if(FAILED(V_ERROR(&vhres)))
1095 return V_ERROR(&vhres);
1096
1097 if(res)
1098 *res = retv;
1099 else
1100 VariantClear(&retv);
1101 return V_ERROR(&vhres);
1102 }
1103
1104 static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
1105 EXCEPINFO *ei, IServiceProvider *caller)
1106 {
1107 HRESULT hres;
1108
1109 switch(flags) {
1110 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1111 if(!res)
1112 return E_INVALIDARG;
1113 /* fall through */
1114 case DISPATCH_METHOD:
1115 if(This->dynamic_data && This->dynamic_data->func_disps
1116 && This->dynamic_data->func_disps[func->func_disp_idx].func_obj) {
1117 func_obj_entry_t *entry = This->dynamic_data->func_disps + func->func_disp_idx;
1118
1119 if((IDispatch*)&entry->func_obj->dispex.IDispatchEx_iface != entry->val) {
1120 if(!entry->val) {
1121 FIXME("Calling null\n");
1122 return E_FAIL;
1123 }
1124
1125 hres = invoke_disp_value(This, entry->val, 0, flags, dp, res, ei, NULL);
1126 break;
1127 }
1128 }
1129
1130 if(func->call_vtbl_off)
1131 hres = invoke_builtin_function(This, func, dp, res, caller);
1132 else
1133 hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1134 break;
1135 case DISPATCH_PROPERTYGET: {
1136 func_obj_entry_t *entry;
1137
1138 if(func->id == DISPID_VALUE) {
1139 BSTR ret;
1140
1141 ret = SysAllocString(objectW);
1142 if(!ret)
1143 return E_OUTOFMEMORY;
1144
1145 V_VT(res) = VT_BSTR;
1146 V_BSTR(res) = ret;
1147 return S_OK;
1148 }
1149
1150 hres = get_func_obj_entry(This, func, &entry);
1151 if(FAILED(hres))
1152 return hres;
1153
1154 V_VT(res) = VT_DISPATCH;
1155 V_DISPATCH(res) = entry->val;
1156 if(V_DISPATCH(res))
1157 IDispatch_AddRef(V_DISPATCH(res));
1158 hres = S_OK;
1159 break;
1160 }
1161 case DISPATCH_PROPERTYPUT: {
1162 func_obj_entry_t *entry;
1163 VARIANT *v;
1164
1165 if(dp->cArgs != 1 || (dp->cNamedArgs == 1 && *dp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1166 || dp->cNamedArgs > 1) {
1167 FIXME("invalid args\n");
1168 return E_INVALIDARG;
1169 }
1170
1171 v = dp->rgvarg;
1172 /* FIXME: not exactly right */
1173 if(V_VT(v) != VT_DISPATCH)
1174 return E_NOTIMPL;
1175
1176 hres = get_func_obj_entry(This, func, &entry);
1177 if(FAILED(hres))
1178 return hres;
1179
1180 if(entry->val)
1181 IDispatch_Release(entry->val);
1182 entry->val = V_DISPATCH(v);
1183 if(entry->val)
1184 IDispatch_AddRef(entry->val);
1185 hres = S_OK;
1186 break;
1187 }
1188 default:
1189 FIXME("Unimplemented flags %x\n", flags);
1190 hres = E_NOTIMPL;
1191 }
1192
1193 return hres;
1194 }
1195
1196 static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp,
1197 VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
1198 {
1199 dispex_data_t *data;
1200 func_info_t *func;
1201 HRESULT hres;
1202
1203 data = get_dispex_data(This);
1204 if(!data)
1205 return E_FAIL;
1206
1207 hres = get_builtin_func(data, id, &func);
1208 if(id == DISPID_VALUE && hres == DISP_E_UNKNOWNNAME)
1209 return dispex_value(This, lcid, flags, dp, res, ei, caller);
1210 if(FAILED(hres))
1211 return hres;
1212
1213 if(func->func_disp_idx != -1)
1214 return function_invoke(This, func, flags, dp, res, ei, caller);
1215
1216 switch(flags) {
1217 case DISPATCH_PROPERTYPUT:
1218 case DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF:
1219 if(res)
1220 V_VT(res) = VT_EMPTY;
1221 hres = builtin_propput(This, func, dp, caller);
1222 break;
1223 case DISPATCH_PROPERTYGET:
1224 hres = builtin_propget(This, func, dp, res);
1225 break;
1226 default:
1227 if(!func->get_vtbl_off) {
1228 hres = typeinfo_invoke(This, func, flags, dp, res, ei);
1229 }else {
1230 VARIANT v;
1231
1232 hres = builtin_propget(This, func, NULL, &v);
1233 if(FAILED(hres))
1234 return hres;
1235
1236 if(flags != (DISPATCH_PROPERTYGET|DISPATCH_METHOD) || dp->cArgs) {
1237 if(V_VT(&v) != VT_DISPATCH) {
1238 FIXME("Not a function %s flags %08x\n", debugstr_variant(&v), flags);
1239 VariantClear(&v);
1240 return E_FAIL;
1241 }
1242
1243 hres = invoke_disp_value(This, V_DISPATCH(&v), lcid, flags, dp, res, ei, caller);
1244 IDispatch_Release(V_DISPATCH(&v));
1245 }else if(res) {
1246 *res = v;
1247 }else {
1248 VariantClear(&v);
1249 }
1250 }
1251 }
1252
1253 return hres;
1254 }
1255
1256 HRESULT remove_prop(DispatchEx *This, BSTR name, VARIANT_BOOL *success)
1257 {
1258 dynamic_prop_t *prop;
1259 DISPID id;
1260 HRESULT hres;
1261
1262 hres = get_builtin_id(This, name, 0, &id);
1263 if(hres == S_OK) {
1264 DISPID named_id = DISPID_PROPERTYPUT;
1265 VARIANT var;
1266 DISPPARAMS dp = {&var,&named_id,1,1};
1267 EXCEPINFO ei;
1268
1269 V_VT(&var) = VT_EMPTY;
1270 memset(&ei, 0, sizeof(ei));
1271 hres = invoke_builtin_prop(This, id, 0, DISPATCH_PROPERTYPUT, &dp, NULL, &ei, NULL);
1272 if(FAILED(hres))
1273 return hres;
1274
1275 *success = VARIANT_TRUE;
1276 return S_OK;
1277 }
1278
1279 hres = get_dynamic_prop(This, name, 0, &prop);
1280 if(FAILED(hres)) {
1281 if(hres != DISP_E_UNKNOWNNAME)
1282 return hres;
1283 *success = VARIANT_FALSE;
1284 return S_OK;
1285 }
1286
1287 VariantClear(&prop->var);
1288 prop->flags |= DYNPROP_DELETED;
1289 *success = VARIANT_TRUE;
1290 return S_OK;
1291 }
1292
1293 static inline DispatchEx *impl_from_IDispatchEx(IDispatchEx *iface)
1294 {
1295 return CONTAINING_RECORD(iface, DispatchEx, IDispatchEx_iface);
1296 }
1297
1298 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1299 {
1300 DispatchEx *This = impl_from_IDispatchEx(iface);
1301
1302 return IUnknown_QueryInterface(This->outer, riid, ppv);
1303 }
1304
1305 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1306 {
1307 DispatchEx *This = impl_from_IDispatchEx(iface);
1308
1309 return IUnknown_AddRef(This->outer);
1310 }
1311
1312 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1313 {
1314 DispatchEx *This = impl_from_IDispatchEx(iface);
1315
1316 return IUnknown_Release(This->outer);
1317 }
1318
1319 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1320 {
1321 DispatchEx *This = impl_from_IDispatchEx(iface);
1322
1323 TRACE("(%p)->(%p)\n", This, pctinfo);
1324
1325 *pctinfo = 1;
1326 return S_OK;
1327 }
1328
1329 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
1330 LCID lcid, ITypeInfo **ppTInfo)
1331 {
1332 DispatchEx *This = impl_from_IDispatchEx(iface);
1333 HRESULT hres;
1334
1335 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1336
1337 hres = get_typeinfo(This->data->disp_tid, ppTInfo);
1338 if(FAILED(hres))
1339 return hres;
1340
1341 ITypeInfo_AddRef(*ppTInfo);
1342 return S_OK;
1343 }
1344
1345 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1346 LPOLESTR *rgszNames, UINT cNames,
1347 LCID lcid, DISPID *rgDispId)
1348 {
1349 DispatchEx *This = impl_from_IDispatchEx(iface);
1350 UINT i;
1351 HRESULT hres;
1352
1353 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1354 lcid, rgDispId);
1355
1356 for(i=0; i < cNames; i++) {
1357 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
1358 if(FAILED(hres))
1359 return hres;
1360 }
1361
1362 return S_OK;
1363 }
1364
1365 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1366 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1367 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1368 {
1369 DispatchEx *This = impl_from_IDispatchEx(iface);
1370
1371 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1372 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1373
1374 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, pDispParams,
1375 pVarResult, pExcepInfo, NULL);
1376 }
1377
1378 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1379 {
1380 DispatchEx *This = impl_from_IDispatchEx(iface);
1381 dynamic_prop_t *dprop;
1382 HRESULT hres;
1383
1384 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
1385
1386 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
1387 FIXME("Unsupported grfdex %x\n", grfdex);
1388
1389 hres = get_builtin_id(This, bstrName, grfdex, pid);
1390 if(hres != DISP_E_UNKNOWNNAME)
1391 return hres;
1392
1393 hres = get_dynamic_prop(This, bstrName, grfdex, &dprop);
1394 if(FAILED(hres))
1395 return hres;
1396
1397 *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
1398 return S_OK;
1399 }
1400
1401 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1402 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1403 {
1404 DispatchEx *This = impl_from_IDispatchEx(iface);
1405 HRESULT hres;
1406
1407 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1408
1409 switch(get_dispid_type(id)) {
1410 case DISPEXPROP_CUSTOM:
1411 if(!This->data->vtbl || !This->data->vtbl->invoke)
1412 return DISP_E_UNKNOWNNAME;
1413 return This->data->vtbl->invoke(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1414
1415 case DISPEXPROP_DYNAMIC: {
1416 DWORD idx = id - DISPID_DYNPROP_0;
1417 dynamic_prop_t *prop;
1418
1419 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1420 return DISP_E_UNKNOWNNAME;
1421
1422 prop = This->dynamic_data->props+idx;
1423
1424 switch(wFlags) {
1425 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1426 if(!pvarRes)
1427 return E_INVALIDARG;
1428 /* fall through */
1429 case DISPATCH_METHOD:
1430 if(V_VT(&prop->var) != VT_DISPATCH) {
1431 FIXME("invoke %s\n", debugstr_variant(&prop->var));
1432 return E_NOTIMPL;
1433 }
1434
1435 return invoke_disp_value(This, V_DISPATCH(&prop->var), lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1436 case DISPATCH_PROPERTYGET:
1437 if(prop->flags & DYNPROP_DELETED)
1438 return DISP_E_UNKNOWNNAME;
1439 V_VT(pvarRes) = VT_EMPTY;
1440 return variant_copy(pvarRes, &prop->var);
1441 case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF:
1442 case DISPATCH_PROPERTYPUT:
1443 if(pdp->cArgs != 1 || (pdp->cNamedArgs == 1 && *pdp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
1444 || pdp->cNamedArgs > 1) {
1445 FIXME("invalid args\n");
1446 return E_INVALIDARG;
1447 }
1448
1449 TRACE("put %s\n", debugstr_variant(pdp->rgvarg));
1450 VariantClear(&prop->var);
1451 hres = variant_copy(&prop->var, pdp->rgvarg);
1452 if(FAILED(hres))
1453 return hres;
1454
1455 prop->flags &= ~DYNPROP_DELETED;
1456 return S_OK;
1457 default:
1458 FIXME("unhandled wFlags %x\n", wFlags);
1459 return E_NOTIMPL;
1460 }
1461 }
1462 case DISPEXPROP_BUILTIN:
1463 if(wFlags == DISPATCH_CONSTRUCT) {
1464 if(id == DISPID_VALUE) {
1465 if(This->data->vtbl && This->data->vtbl->value) {
1466 return This->data->vtbl->value(This, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1467 }
1468 FIXME("DISPATCH_CONSTRUCT flag but missing value function\n");
1469 return E_FAIL;
1470 }
1471 FIXME("DISPATCH_CONSTRUCT flag without DISPID_VALUE\n");
1472 return E_FAIL;
1473 }
1474
1475 return invoke_builtin_prop(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1476 default:
1477 assert(0);
1478 return E_FAIL;
1479 }
1480 }
1481
1482 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
1483 {
1484 DispatchEx *This = impl_from_IDispatchEx(iface);
1485
1486 TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
1487
1488 /* Not implemented by IE */
1489 return E_NOTIMPL;
1490 }
1491
1492 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
1493 {
1494 DispatchEx *This = impl_from_IDispatchEx(iface);
1495
1496 TRACE("(%p)->(%x)\n", This, id);
1497
1498 /* Not implemented by IE */
1499 return E_NOTIMPL;
1500 }
1501
1502 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
1503 {
1504 DispatchEx *This = impl_from_IDispatchEx(iface);
1505 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
1506 return E_NOTIMPL;
1507 }
1508
1509 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
1510 {
1511 DispatchEx *This = impl_from_IDispatchEx(iface);
1512 dispex_data_t *data;
1513 func_info_t *func;
1514 HRESULT hres;
1515
1516 TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
1517
1518 if(is_dynamic_dispid(id)) {
1519 DWORD idx = id - DISPID_DYNPROP_0;
1520
1521 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1522 return DISP_E_UNKNOWNNAME;
1523
1524 *pbstrName = SysAllocString(This->dynamic_data->props[idx].name);
1525 if(!*pbstrName)
1526 return E_OUTOFMEMORY;
1527
1528 return S_OK;
1529 }
1530
1531 data = get_dispex_data(This);
1532 if(!data)
1533 return E_FAIL;
1534
1535 hres = get_builtin_func(data, id, &func);
1536 if(FAILED(hres))
1537 return hres;
1538
1539 *pbstrName = SysAllocString(func->name);
1540 if(!*pbstrName)
1541 return E_OUTOFMEMORY;
1542 return S_OK;
1543 }
1544
1545 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
1546 {
1547 DispatchEx *This = impl_from_IDispatchEx(iface);
1548 dispex_data_t *data;
1549 func_info_t *func;
1550 HRESULT hres;
1551
1552 TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
1553
1554 if(is_dynamic_dispid(id)) {
1555 DWORD idx = id - DISPID_DYNPROP_0;
1556
1557 if(!get_dynamic_data(This) || This->dynamic_data->prop_cnt <= idx)
1558 return DISP_E_UNKNOWNNAME;
1559
1560 while(++idx < This->dynamic_data->prop_cnt && This->dynamic_data->props[idx].flags & DYNPROP_DELETED);
1561
1562 if(idx == This->dynamic_data->prop_cnt) {
1563 *pid = DISPID_STARTENUM;
1564 return S_FALSE;
1565 }
1566
1567 *pid = DISPID_DYNPROP_0+idx;
1568 return S_OK;
1569 }
1570
1571 data = get_dispex_data(This);
1572 if(!data)
1573 return E_FAIL;
1574
1575 if(id == DISPID_STARTENUM) {
1576 func = data->funcs;
1577 }else {
1578 hres = get_builtin_func(data, id, &func);
1579 if(FAILED(hres))
1580 return hres;
1581 func++;
1582 }
1583
1584 while(func < data->funcs+data->func_cnt) {
1585 /* FIXME: Skip hidden properties */
1586 if(func->func_disp_idx == -1) {
1587 *pid = func->id;
1588 return S_OK;
1589 }
1590 func++;
1591 }
1592
1593 if(get_dynamic_data(This) && This->dynamic_data->prop_cnt) {
1594 *pid = DISPID_DYNPROP_0;
1595 return S_OK;
1596 }
1597
1598 *pid = DISPID_STARTENUM;
1599 return S_FALSE;
1600 }
1601
1602 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
1603 {
1604 DispatchEx *This = impl_from_IDispatchEx(iface);
1605 FIXME("(%p)->(%p)\n", This, ppunk);
1606 return E_NOTIMPL;
1607 }
1608
1609 static IDispatchExVtbl DispatchExVtbl = {
1610 DispatchEx_QueryInterface,
1611 DispatchEx_AddRef,
1612 DispatchEx_Release,
1613 DispatchEx_GetTypeInfoCount,
1614 DispatchEx_GetTypeInfo,
1615 DispatchEx_GetIDsOfNames,
1616 DispatchEx_Invoke,
1617 DispatchEx_GetDispID,
1618 DispatchEx_InvokeEx,
1619 DispatchEx_DeleteMemberByName,
1620 DispatchEx_DeleteMemberByDispID,
1621 DispatchEx_GetMemberProperties,
1622 DispatchEx_GetMemberName,
1623 DispatchEx_GetNextDispID,
1624 DispatchEx_GetNameSpaceParent
1625 };
1626
1627 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
1628 {
1629 static const IID IID_UndocumentedScriptIface =
1630 {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa0}};
1631 static const IID IID_IDispatchJS =
1632 {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}};
1633
1634 if(IsEqualGUID(&IID_IDispatch, riid)) {
1635 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
1636 *ppv = &This->IDispatchEx_iface;
1637 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
1638 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
1639 *ppv = &This->IDispatchEx_iface;
1640 }else if(IsEqualGUID(&IID_IDispatchJS, riid)) {
1641 TRACE("(%p)->(IID_IDispatchJS %p) returning NULL\n", This, ppv);
1642 *ppv = NULL;
1643 }else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) {
1644 TRACE("(%p)->(IID_UndocumentedScriptIface %p) returning NULL\n", This, ppv);
1645 *ppv = NULL;
1646 }else {
1647 return FALSE;
1648 }
1649
1650 if(*ppv)
1651 IUnknown_AddRef((IUnknown*)*ppv);
1652 return TRUE;
1653 }
1654
1655 void dispex_traverse(DispatchEx *This, nsCycleCollectionTraversalCallback *cb)
1656 {
1657 dynamic_prop_t *prop;
1658
1659 if(!This->dynamic_data)
1660 return;
1661
1662 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1663 if(V_VT(&prop->var) == VT_DISPATCH)
1664 note_cc_edge((nsISupports*)V_DISPATCH(&prop->var), "dispex_data", cb);
1665 }
1666
1667 /* FIXME: Traverse func_disps */
1668 }
1669
1670 void dispex_unlink(DispatchEx *This)
1671 {
1672 dynamic_prop_t *prop;
1673
1674 if(!This->dynamic_data)
1675 return;
1676
1677 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1678 if(V_VT(&prop->var) == VT_DISPATCH) {
1679 V_VT(&prop->var) = VT_EMPTY;
1680 IDispatch_Release(V_DISPATCH(&prop->var));
1681 }else {
1682 VariantClear(&prop->var);
1683 }
1684 }
1685 }
1686
1687 void release_dispex(DispatchEx *This)
1688 {
1689 dynamic_prop_t *prop;
1690
1691 if(!This->dynamic_data)
1692 return;
1693
1694 for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1695 VariantClear(&prop->var);
1696 heap_free(prop->name);
1697 }
1698
1699 heap_free(This->dynamic_data->props);
1700
1701 if(This->dynamic_data->func_disps) {
1702 func_obj_entry_t *iter;
1703
1704 for(iter = This->dynamic_data->func_disps; iter < This->dynamic_data->func_disps+This->data->data->func_disp_cnt; iter++) {
1705 if(iter->func_obj) {
1706 iter->func_obj->obj = NULL;
1707 IDispatchEx_Release(&iter->func_obj->dispex.IDispatchEx_iface);
1708 }
1709 if(iter->val)
1710 IDispatch_Release(iter->val);
1711 }
1712
1713 heap_free(This->dynamic_data->func_disps);
1714 }
1715
1716 heap_free(This->dynamic_data);
1717 }
1718
1719 void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
1720 {
1721 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
1722 dispex->outer = outer;
1723 dispex->data = data;
1724 dispex->dynamic_data = NULL;
1725 }