comctl32/tooltips: Remove redundant code, let handlers deal with A<->W conversions.
[wine] / dlls / 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 <stdarg.h>
20
21 #define COBJMACROS
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "ole2.h"
27
28 #include "wine/debug.h"
29
30 #include "mshtml_private.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
33
34 static const WCHAR objectW[] = {'[','o','b','j','e','c','t',']',0};
35
36 typedef struct {
37     DISPID id;
38     BSTR name;
39     tid_t tid;
40     int func_disp_idx;
41 } func_info_t;
42
43 struct dispex_data_t {
44     DWORD func_cnt;
45     func_info_t *funcs;
46     func_info_t **name_table;
47     DWORD func_disp_cnt;
48
49     struct list entry;
50 };
51
52 typedef struct {
53     VARIANT var;
54     LPWSTR name;
55 } dynamic_prop_t;
56
57 typedef struct {
58     DispatchEx dispex;
59     const IUnknownVtbl *lpIUnknownVtbl;
60     DispatchEx *obj;
61     func_info_t *info;
62 } func_disp_t;
63
64 #define FUNCUNKNOWN(x)  ((IUnknown*) &(x)->lpIUnknownVtbl)
65
66 struct dispex_dynamic_data_t {
67     DWORD buf_size;
68     DWORD prop_cnt;
69     dynamic_prop_t *props;
70     func_disp_t **func_disps;
71 };
72
73 #define DISPID_DYNPROP_0    0x50000000
74 #define DISPID_DYNPROP_MAX  0x5fffffff
75
76 static ITypeLib *typelib;
77 static ITypeInfo *typeinfos[LAST_tid];
78 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
79
80 static REFIID tid_ids[] = {
81     &IID_NULL,
82     &DIID_DispCEventObj,
83     &DIID_DispDOMChildrenCollection,
84     &DIID_DispHTMLAnchorElement,
85     &DIID_DispHTMLBody,
86     &DIID_DispHTMLCommentElement,
87     &DIID_DispHTMLCurrentStyle,
88     &DIID_DispHTMLDocument,
89     &DIID_DispHTMLDOMTextNode,
90     &DIID_DispHTMLElementCollection,
91     &DIID_DispHTMLGenericElement,
92     &DIID_DispHTMLIFrame,
93     &DIID_DispHTMLImg,
94     &DIID_DispHTMLInputElement,
95     &DIID_DispHTMLLocation,
96     &DIID_DispHTMLNavigator,
97     &DIID_DispHTMLOptionElement,
98     &DIID_DispHTMLSelectElement,
99     &DIID_DispHTMLStyle,
100     &DIID_DispHTMLTable,
101     &DIID_DispHTMLTableRow,
102     &DIID_DispHTMLUnknownElement,
103     &DIID_DispHTMLWindow2,
104     &IID_IHTMLAnchorElement,
105     &IID_IHTMLBodyElement,
106     &IID_IHTMLBodyElement2,
107     &IID_IHTMLCommentElement,
108     &IID_IHTMLCurrentStyle,
109     &IID_IHTMLCurrentStyle2,
110     &IID_IHTMLCurrentStyle3,
111     &IID_IHTMLCurrentStyle4,
112     &IID_IHTMLDocument2,
113     &IID_IHTMLDocument3,
114     &IID_IHTMLDocument4,
115     &IID_IHTMLDocument5,
116     &IID_IHTMLDOMChildrenCollection,
117     &IID_IHTMLDOMNode,
118     &IID_IHTMLDOMNode2,
119     &IID_IHTMLDOMTextNode,
120     &IID_IHTMLElement,
121     &IID_IHTMLElement2,
122     &IID_IHTMLElement3,
123     &IID_IHTMLElement4,
124     &IID_IHTMLElementCollection,
125     &IID_IHTMLEventObj,
126     &IID_IHTMLFrameBase,
127     &IID_IHTMLFrameBase2,
128     &IID_IHTMLGenericElement,
129     &IID_IHTMLImgElement,
130     &IID_IHTMLInputElement,
131     &IID_IHTMLLocation,
132     &IID_IHTMLOptionElement,
133     &IID_IHTMLSelectElement,
134     &IID_IHTMLStyle,
135     &IID_IHTMLStyle2,
136     &IID_IHTMLStyle3,
137     &IID_IHTMLStyle4,
138     &IID_IHTMLTable,
139     &IID_IHTMLTableRow,
140     &IID_IHTMLTextContainer,
141     &IID_IHTMLUniqueName,
142     &IID_IHTMLWindow2,
143     &IID_IHTMLWindow3,
144     &IID_IOmNavigator
145 };
146
147 static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
148 {
149     HRESULT hres;
150
151     if(!typelib) {
152         ITypeLib *tl;
153
154         hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
155         if(FAILED(hres)) {
156             ERR("LoadRegTypeLib failed: %08x\n", hres);
157             return hres;
158         }
159
160         if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
161             ITypeLib_Release(tl);
162     }
163
164     if(!typeinfos[tid]) {
165         ITypeInfo *typeinfo;
166
167         hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &typeinfo);
168         if(FAILED(hres)) {
169             ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres);
170             return hres;
171         }
172
173         if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), typeinfo, NULL))
174             ITypeInfo_Release(typeinfo);
175     }
176
177     *typeinfo = typeinfos[tid];
178     return S_OK;
179 }
180
181 void release_typelib(void)
182 {
183     dispex_data_t *iter;
184     unsigned i;
185
186     while(!list_empty(&dispex_data_list)) {
187         iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
188         list_remove(&iter->entry);
189
190         for(i=0; i < iter->func_cnt; i++)
191             SysFreeString(iter->funcs[i].name);
192
193         heap_free(iter->funcs);
194         heap_free(iter->name_table);
195         heap_free(iter);
196     }
197
198     if(!typelib)
199         return;
200
201     for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
202         if(typeinfos[i])
203             ITypeInfo_Release(typeinfos[i]);
204
205     ITypeLib_Release(typelib);
206 }
207
208 static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti)
209 {
210     HRESULT hres;
211
212     if(data->func_cnt && data->funcs[data->func_cnt-1].id == desc->memid)
213         return;
214
215     if(data->func_cnt == *size)
216         data->funcs = heap_realloc(data->funcs, (*size <<= 1)*sizeof(func_info_t));
217
218     hres = ITypeInfo_GetDocumentation(dti, desc->memid, &data->funcs[data->func_cnt].name, NULL, NULL, NULL);
219     if(FAILED(hres))
220         return;
221
222     data->funcs[data->func_cnt].id = desc->memid;
223     data->funcs[data->func_cnt].tid = tid;
224     data->funcs[data->func_cnt].func_disp_idx = (desc->invkind & DISPATCH_METHOD) ? data->func_disp_cnt++ : -1;
225
226     data->func_cnt++;
227 }
228
229 static int dispid_cmp(const void *p1, const void *p2)
230 {
231     return ((func_info_t*)p1)->id - ((func_info_t*)p2)->id;
232 }
233
234 static int func_name_cmp(const void *p1, const void *p2)
235 {
236     return strcmpiW((*(func_info_t**)p1)->name, (*(func_info_t**)p2)->name);
237 }
238
239 static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
240 {
241     const tid_t *tid = This->data->iface_tids;
242     FUNCDESC *funcdesc;
243     dispex_data_t *data;
244     DWORD size = 16, i;
245     ITypeInfo *ti, *dti;
246     HRESULT hres;
247
248     TRACE("(%p)\n", This);
249
250     if(This->data->disp_tid) {
251         hres = get_typeinfo(This->data->disp_tid, &dti);
252         if(FAILED(hres)) {
253             ERR("Could not get disp type info: %08x\n", hres);
254             return NULL;
255         }
256     }
257
258     data = heap_alloc(sizeof(dispex_data_t));
259     data->func_cnt = 0;
260     data->func_disp_cnt = 0;
261     data->funcs = heap_alloc(size*sizeof(func_info_t));
262     list_add_tail(&dispex_data_list, &data->entry);
263
264     while(*tid) {
265         hres = get_typeinfo(*tid, &ti);
266         if(FAILED(hres))
267             break;
268
269         i=7;
270         while(1) {
271             hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
272             if(FAILED(hres))
273                 break;
274
275             add_func_info(data, &size, *tid, funcdesc, dti);
276             ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
277         }
278
279         tid++;
280     }
281
282     if(!data->func_cnt) {
283         heap_free(data->funcs);
284         data->funcs = NULL;
285     }else if(data->func_cnt != size) {
286         data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
287     }
288
289     qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
290
291     if(data->funcs) {
292         data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
293         for(i=0; i < data->func_cnt; i++)
294             data->name_table[i] = data->funcs+i;
295         qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
296     }else {
297         data->name_table = NULL;
298     }
299
300     return data;
301 }
302
303 static CRITICAL_SECTION cs_dispex_static_data;
304 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
305 {
306     0, 0, &cs_dispex_static_data,
307     { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
308       0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
309 };
310 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
311
312
313 static dispex_data_t *get_dispex_data(DispatchEx *This)
314 {
315     if(This->data->data)
316         return This->data->data;
317
318     EnterCriticalSection(&cs_dispex_static_data);
319
320     if(!This->data->data)
321         This->data->data = preprocess_dispex_data(This);
322
323     LeaveCriticalSection(&cs_dispex_static_data);
324
325     return This->data->data;
326 }
327
328 HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp)
329 {
330     EXCEPINFO ei;
331     IDispatchEx *dispex;
332     VARIANT res;
333     HRESULT hres;
334
335     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
336     if(FAILED(hres)) {
337         FIXME("Could not get IDispatchEx interface: %08x\n", hres);
338         return hres;
339     }
340
341     VariantInit(&res);
342     memset(&ei, 0, sizeof(ei));
343
344     hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, dp, &res, &ei, NULL);
345
346     IDispatchEx_Release(dispex);
347     VariantClear(&res);
348     return hres;
349 }
350
351 static inline BOOL is_custom_dispid(DISPID id)
352 {
353     return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
354 }
355
356 static inline BOOL is_dynamic_dispid(DISPID id)
357 {
358     return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
359 }
360
361 static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This, BOOL alloc)
362 {
363     return !alloc || This->dynamic_data
364         ? This->dynamic_data
365         : (This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t)));
366 }
367
368 static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, BOOL alloc, dynamic_prop_t **ret)
369 {
370     dispex_dynamic_data_t *data = get_dynamic_data(This, alloc);
371     unsigned i;
372
373     if(!data) {
374         if(alloc)
375             return E_OUTOFMEMORY;
376
377         TRACE("not found %s\n", debugstr_w(name));
378         return DISP_E_UNKNOWNNAME;
379     }
380
381     for(i=0; i < data->prop_cnt; i++) {
382         if(!strcmpW(data->props[i].name, name)) {
383             *ret = data->props+i;
384             return S_OK;
385         }
386     }
387
388     TRACE("creating dynamic prop %s\n", debugstr_w(name));
389
390     if(!data->buf_size) {
391         data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
392         if(!data->props)
393             return E_OUTOFMEMORY;
394         data->buf_size = 4;
395     }else if(data->buf_size == data->prop_cnt) {
396         dynamic_prop_t *new_props;
397
398         new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1));
399         if(!new_props)
400             return E_OUTOFMEMORY;
401
402         data->props = new_props;
403         data->buf_size <<= 1;
404     }
405
406     data->props[data->prop_cnt].name = heap_strdupW(name);
407     VariantInit(&data->props[data->prop_cnt].var);
408     *ret = data->props + data->prop_cnt++;
409
410     return S_OK;
411 }
412
413 HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
414 {
415     dynamic_prop_t *prop;
416     HRESULT hres;
417
418     hres = get_dynamic_prop(This, name, alloc, &prop);
419     if(FAILED(hres))
420         return hres;
421
422     *ret = &prop->var;
423     return S_OK;
424 }
425
426 static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params,
427         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
428 {
429     if(This->data->vtbl && This->data->vtbl->value)
430         return This->data->vtbl->value(This->outer, lcid, flags, params, res, ei, caller);
431
432     switch(flags) {
433     case DISPATCH_PROPERTYGET:
434         V_VT(res) = VT_BSTR;
435         V_BSTR(res) = SysAllocString(objectW);
436         if(!V_BSTR(res))
437             return E_OUTOFMEMORY;
438         break;
439     default:
440         FIXME("Unimplemented flags %x\n", flags);
441         return E_NOTIMPL;
442     }
443
444     return S_OK;
445 }
446
447 static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
448         EXCEPINFO *ei)
449 {
450     ITypeInfo *ti;
451     IUnknown *unk;
452     UINT argerr=0;
453     HRESULT hres;
454
455     hres = get_typeinfo(func->tid, &ti);
456     if(FAILED(hres)) {
457         ERR("Could not get type info: %08x\n", hres);
458         return hres;
459     }
460
461     hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&unk);
462     if(FAILED(hres)) {
463         ERR("Could not get iface %s: %08x\n", debugstr_guid(tid_ids[func->tid]), hres);
464         return E_FAIL;
465     }
466
467     hres = ITypeInfo_Invoke(ti, unk, func->id, flags, dp, res, ei, &argerr);
468
469     IUnknown_Release(unk);
470     return hres;
471 }
472
473 #define FUNCTION_THIS(iface) DEFINE_THIS(func_disp_t, IUnknown, iface)
474
475 static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
476 {
477     func_disp_t *This = FUNCTION_THIS(iface);
478
479     if(IsEqualGUID(&IID_IUnknown, riid)) {
480         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
481         *ppv = FUNCUNKNOWN(This);
482     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
483         return *ppv ? S_OK : E_NOINTERFACE;
484     }else {
485         *ppv = NULL;
486         return E_NOINTERFACE;
487     }
488
489     IUnknown_AddRef((IUnknown*)*ppv);
490     return S_OK;
491 }
492
493 static ULONG WINAPI Function_AddRef(IUnknown *iface)
494 {
495     func_disp_t *This = FUNCTION_THIS(iface);
496
497     TRACE("(%p)\n", This);
498
499     return IDispatchEx_AddRef(DISPATCHEX(This->obj));
500 }
501
502 static ULONG WINAPI Function_Release(IUnknown *iface)
503 {
504     func_disp_t *This = FUNCTION_THIS(iface);
505
506     TRACE("(%p)\n", This);
507
508     return IDispatchEx_Release(DISPATCHEX(This->obj));
509 }
510
511 static HRESULT function_value(IUnknown *iface, LCID lcid, WORD flags, DISPPARAMS *params,
512         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
513 {
514     func_disp_t *This = FUNCTION_THIS(iface);
515     HRESULT hres;
516
517     switch(flags) {
518     case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
519         if(!res)
520             return E_INVALIDARG;
521     case DISPATCH_METHOD:
522         hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei);
523         break;
524     default:
525         FIXME("Unimplemented flags %x\n", flags);
526         hres = E_NOTIMPL;
527     }
528
529     return hres;
530 }
531
532 #undef FUNCTION_THIS
533
534 static const IUnknownVtbl FunctionUnkVtbl = {
535     Function_QueryInterface,
536     Function_AddRef,
537     Function_Release
538 };
539
540 static const dispex_static_data_vtbl_t function_dispex_vtbl = {
541     function_value,
542     NULL,
543     NULL
544 };
545
546 static const tid_t function_iface_tids[] = {0};
547
548 static dispex_static_data_t function_dispex = {
549     &function_dispex_vtbl,
550     NULL_tid,
551     NULL,
552     function_iface_tids
553 };
554
555 static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info)
556 {
557     func_disp_t *ret;
558
559     ret = heap_alloc_zero(sizeof(func_disp_t));
560     if(!ret)
561         return NULL;
562
563     ret->lpIUnknownVtbl = &FunctionUnkVtbl;
564     init_dispex(&ret->dispex, FUNCUNKNOWN(ret),  &function_dispex);
565     ret->obj = obj;
566     ret->info = info;
567
568     return ret;
569 }
570
571 static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
572         EXCEPINFO *ei)
573 {
574     HRESULT hres;
575
576     switch(flags) {
577     case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
578         if(!res)
579             return E_INVALIDARG;
580     case DISPATCH_METHOD:
581         hres = typeinfo_invoke(This, func, flags, dp, res, ei);
582         break;
583     case DISPATCH_PROPERTYGET: {
584         dispex_dynamic_data_t *dynamic_data;
585
586         if(func->id == DISPID_VALUE) {
587             BSTR ret;
588
589             ret = SysAllocString(objectW);
590             if(!ret)
591                 return E_OUTOFMEMORY;
592
593             V_VT(res) = VT_BSTR;
594             V_BSTR(res) = ret;
595             return S_OK;
596         }
597
598         dynamic_data = get_dynamic_data(This, TRUE);
599         if(!dynamic_data)
600             return E_OUTOFMEMORY;
601
602         if(!dynamic_data->func_disps) {
603             dynamic_data->func_disps = heap_alloc_zero(This->data->data->func_disp_cnt * sizeof(func_disp_t*));
604             if(!dynamic_data->func_disps)
605                 return E_OUTOFMEMORY;
606         }
607
608         if(!dynamic_data->func_disps[func->func_disp_idx]) {
609             dynamic_data->func_disps[func->func_disp_idx] = create_func_disp(This, func);
610             if(!dynamic_data->func_disps[func->func_disp_idx])
611                 return E_OUTOFMEMORY;
612         }
613
614         V_VT(res) = VT_DISPATCH;
615         V_DISPATCH(res) = (IDispatch*)DISPATCHEX(&dynamic_data->func_disps[func->func_disp_idx]->dispex);
616         IDispatch_AddRef(V_DISPATCH(res));
617         hres = S_OK;
618         break;
619     }
620     default:
621         FIXME("Unimplemented flags %x\n", flags);
622     case DISPATCH_PROPERTYPUT:
623         hres = E_NOTIMPL;
624     }
625
626     return hres;
627 }
628
629 static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret)
630 {
631     int min, max, n;
632
633     min = 0;
634     max = data->func_cnt-1;
635
636     while(min <= max) {
637         n = (min+max)/2;
638
639         if(data->funcs[n].id == id) {
640             *ret = data->funcs+n;
641             return S_OK;
642         }
643
644         if(data->funcs[n].id < id)
645             min = n+1;
646         else
647             max = n-1;
648     }
649
650     WARN("invalid id %x\n", id);
651     return DISP_E_UNKNOWNNAME;
652 }
653
654 #define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface)
655
656 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
657 {
658     DispatchEx *This = DISPATCHEX_THIS(iface);
659
660     return IUnknown_QueryInterface(This->outer, riid, ppv);
661 }
662
663 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
664 {
665     DispatchEx *This = DISPATCHEX_THIS(iface);
666
667     return IUnknown_AddRef(This->outer);
668 }
669
670 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
671 {
672     DispatchEx *This = DISPATCHEX_THIS(iface);
673
674     return IUnknown_Release(This->outer);
675 }
676
677 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
678 {
679     DispatchEx *This = DISPATCHEX_THIS(iface);
680
681     TRACE("(%p)->(%p)\n", This, pctinfo);
682
683     *pctinfo = 1;
684     return S_OK;
685 }
686
687 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
688                                               LCID lcid, ITypeInfo **ppTInfo)
689 {
690     DispatchEx *This = DISPATCHEX_THIS(iface);
691     HRESULT hres;
692
693     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
694
695     hres = get_typeinfo(This->data->disp_tid, ppTInfo);
696     if(FAILED(hres))
697         return hres;
698
699     ITypeInfo_AddRef(*ppTInfo);
700     return S_OK;
701 }
702
703 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
704                                                 LPOLESTR *rgszNames, UINT cNames,
705                                                 LCID lcid, DISPID *rgDispId)
706 {
707     DispatchEx *This = DISPATCHEX_THIS(iface);
708     UINT i;
709     HRESULT hres;
710
711     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
712           lcid, rgDispId);
713
714     for(i=0; i < cNames; i++) {
715         hres = IDispatchEx_GetDispID(DISPATCHEX(This), rgszNames[i], 0, rgDispId+i);
716         if(FAILED(hres))
717             return hres;
718     }
719
720     return S_OK;
721 }
722
723 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
724                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
725                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
726 {
727     DispatchEx *This = DISPATCHEX_THIS(iface);
728
729     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
730           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
731
732     return IDispatchEx_InvokeEx(DISPATCHEX(This), dispIdMember, lcid, wFlags,
733                                 pDispParams, pVarResult, pExcepInfo, NULL);
734 }
735
736 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
737 {
738     DispatchEx *This = DISPATCHEX_THIS(iface);
739     dynamic_prop_t *dprop;
740     dispex_data_t *data;
741     int min, max, n, c;
742     HRESULT hres;
743
744     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
745
746     if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit))
747         FIXME("Unsupported grfdex %x\n", grfdex);
748
749     data = get_dispex_data(This);
750     if(!data)
751         return E_FAIL;
752
753     min = 0;
754     max = data->func_cnt-1;
755
756     while(min <= max) {
757         n = (min+max)/2;
758
759         c = strcmpiW(data->name_table[n]->name, bstrName);
760         if(!c) {
761             if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, bstrName))
762                 break;
763
764             *pid = data->name_table[n]->id;
765             return S_OK;
766         }
767
768         if(c > 0)
769             max = n-1;
770         else
771             min = n+1;
772     }
773
774     if(This->data->vtbl && This->data->vtbl->get_dispid) {
775         HRESULT hres;
776
777         hres = This->data->vtbl->get_dispid(This->outer, bstrName, grfdex, pid);
778         if(hres != DISP_E_UNKNOWNNAME)
779             return hres;
780     }
781
782     hres = get_dynamic_prop(This, bstrName, grfdex&fdexNameEnsure, &dprop);
783     if(FAILED(hres))
784         return hres;
785
786     *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
787     return S_OK;
788 }
789
790 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
791         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
792 {
793     DispatchEx *This = DISPATCHEX_THIS(iface);
794     dispex_data_t *data;
795     func_info_t *func;
796     HRESULT hres;
797
798     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
799
800     if(is_custom_dispid(id) && This->data->vtbl && This->data->vtbl->invoke)
801         return This->data->vtbl->invoke(This->outer, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
802
803     if(wFlags == DISPATCH_CONSTRUCT) {
804         FIXME("DISPATCH_CONSTRUCT not implemented\n");
805         return E_NOTIMPL;
806     }
807
808     if(is_dynamic_dispid(id)) {
809         DWORD idx = id - DISPID_DYNPROP_0;
810         VARIANT *var;
811
812         if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
813             return DISP_E_UNKNOWNNAME;
814
815         var = &This->dynamic_data->props[idx].var;
816
817         switch(wFlags) {
818         case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
819             if(!pvarRes)
820                 return E_INVALIDARG;
821         case DISPATCH_METHOD: {
822             DISPID named_arg = DISPID_THIS;
823             DISPPARAMS dp = {NULL, &named_arg, 0, 1};
824             IDispatchEx *dispex;
825
826             if(V_VT(var) != VT_DISPATCH) {
827                 FIXME("invoke vt %d\n", V_VT(var));
828                 return E_NOTIMPL;
829             }
830
831             if(pdp->cNamedArgs) {
832                 FIXME("named args not supported\n");
833                 return E_NOTIMPL;
834             }
835
836             dp.rgvarg = heap_alloc((pdp->cArgs+1)*sizeof(VARIANTARG));
837             if(!dp.rgvarg)
838                 return E_OUTOFMEMORY;
839
840             dp.cArgs = pdp->cArgs+1;
841             memcpy(dp.rgvarg+1, pdp->rgvarg, pdp->cArgs*sizeof(VARIANTARG));
842
843             V_VT(dp.rgvarg) = VT_DISPATCH;
844             V_DISPATCH(dp.rgvarg) = (IDispatch*)DISPATCHEX(This);
845
846             hres = IDispatch_QueryInterface(V_DISPATCH(var), &IID_IDispatchEx, (void**)&dispex);
847             TRACE("%s call\n", debugstr_w(This->dynamic_data->props[idx].name));
848             if(SUCCEEDED(hres)) {
849                 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, wFlags, &dp, pvarRes, pei, pspCaller);
850                 IDispatchEx_Release(dispex);
851             }else {
852                 ULONG err = 0;
853                 hres = IDispatch_Invoke(V_DISPATCH(var), DISPID_VALUE, &IID_NULL, lcid, wFlags, pdp, pvarRes, pei, &err);
854             }
855             TRACE("%s ret %08x\n", debugstr_w(This->dynamic_data->props[idx].name), hres);
856
857             heap_free(dp.rgvarg);
858             return hres;
859         }
860         case DISPATCH_PROPERTYGET:
861             return VariantCopy(pvarRes, var);
862         case DISPATCH_PROPERTYPUT:
863             VariantClear(var);
864             return VariantCopy(var, pdp->rgvarg);
865         default:
866             FIXME("unhandled wFlags %x\n", wFlags);
867             return E_NOTIMPL;
868         }
869     }
870
871     data = get_dispex_data(This);
872     if(!data)
873         return E_FAIL;
874
875     hres = get_builtin_func(data, id, &func);
876     if(id == DISPID_VALUE && hres == DISP_E_UNKNOWNNAME)
877         return dispex_value(This, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
878     if(FAILED(hres))
879         return hres;
880
881     if(func->func_disp_idx == -1)
882         hres = typeinfo_invoke(This, func, wFlags, pdp, pvarRes, pei);
883     else
884         hres = function_invoke(This, func, wFlags, pdp, pvarRes, pei);
885
886     return hres;
887 }
888
889 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
890 {
891     DispatchEx *This = DISPATCHEX_THIS(iface);
892     FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
893     return E_NOTIMPL;
894 }
895
896 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
897 {
898     DispatchEx *This = DISPATCHEX_THIS(iface);
899     FIXME("(%p)->(%x)\n", This, id);
900     return E_NOTIMPL;
901 }
902
903 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
904 {
905     DispatchEx *This = DISPATCHEX_THIS(iface);
906     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
907     return E_NOTIMPL;
908 }
909
910 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
911 {
912     DispatchEx *This = DISPATCHEX_THIS(iface);
913     dispex_data_t *data;
914     func_info_t *func;
915     HRESULT hres;
916
917     TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
918
919     if(is_dynamic_dispid(id)) {
920         DWORD idx = id - DISPID_DYNPROP_0;
921
922         if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
923             return DISP_E_UNKNOWNNAME;
924
925         *pbstrName = SysAllocString(This->dynamic_data->props[idx].name);
926         if(!*pbstrName)
927             return E_OUTOFMEMORY;
928
929         return S_OK;
930     }
931
932     data = get_dispex_data(This);
933     if(!data)
934         return E_FAIL;
935
936     hres = get_builtin_func(data, id, &func);
937     if(FAILED(hres))
938         return hres;
939
940     *pbstrName = SysAllocString(func->name);
941     if(!*pbstrName)
942         return E_OUTOFMEMORY;
943     return S_OK;
944 }
945
946 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
947 {
948     DispatchEx *This = DISPATCHEX_THIS(iface);
949     dispex_data_t *data;
950     func_info_t *func;
951     HRESULT hres;
952
953     TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
954
955     if(is_dynamic_dispid(id)) {
956         DWORD idx = id - DISPID_DYNPROP_0;
957
958         if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
959             return DISP_E_UNKNOWNNAME;
960
961         if(idx+1 == This->dynamic_data->prop_cnt) {
962             *pid = DISPID_STARTENUM;
963             return S_FALSE;
964         }
965
966         *pid = id+1;
967         return S_OK;
968     }
969
970     data = get_dispex_data(This);
971     if(!data)
972         return E_FAIL;
973
974     if(id == DISPID_STARTENUM) {
975         func = data->funcs;
976     }else {
977         hres = get_builtin_func(data, id, &func);
978         if(FAILED(hres))
979             return hres;
980         func++;
981     }
982
983     while(func < data->funcs+data->func_cnt) {
984         /* FIXME: Skip hidden properties */
985         if(func->func_disp_idx == -1) {
986             *pid = func->id;
987             return S_OK;
988         }
989         func++;
990     }
991
992     if(This->dynamic_data && This->dynamic_data->prop_cnt) {
993         *pid = DISPID_DYNPROP_0;
994         return S_OK;
995     }
996
997     *pid = DISPID_STARTENUM;
998     return S_FALSE;
999 }
1000
1001 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
1002 {
1003     DispatchEx *This = DISPATCHEX_THIS(iface);
1004     FIXME("(%p)->(%p)\n", This, ppunk);
1005     return E_NOTIMPL;
1006 }
1007
1008 #undef DISPATCHEX_THIS
1009
1010 static IDispatchExVtbl DispatchExVtbl = {
1011     DispatchEx_QueryInterface,
1012     DispatchEx_AddRef,
1013     DispatchEx_Release,
1014     DispatchEx_GetTypeInfoCount,
1015     DispatchEx_GetTypeInfo,
1016     DispatchEx_GetIDsOfNames,
1017     DispatchEx_Invoke,
1018     DispatchEx_GetDispID,
1019     DispatchEx_InvokeEx,
1020     DispatchEx_DeleteMemberByName,
1021     DispatchEx_DeleteMemberByDispID,
1022     DispatchEx_GetMemberProperties,
1023     DispatchEx_GetMemberName,
1024     DispatchEx_GetNextDispID,
1025     DispatchEx_GetNameSpaceParent
1026 };
1027
1028 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
1029 {
1030     static const IID IID_UndocumentedScriptIface =
1031         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa0}};
1032     static const IID IID_IDispatchJS =
1033         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}};
1034
1035     if(IsEqualGUID(&IID_IDispatch, riid)) {
1036         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
1037         *ppv = DISPATCHEX(This);
1038     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
1039         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
1040         *ppv = DISPATCHEX(This);
1041     }else if(IsEqualGUID(&IID_IDispatchJS, riid)) {
1042         TRACE("(%p)->(IID_IDispatchJS %p) returning NULL\n", This, ppv);
1043         *ppv = NULL;
1044     }else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) {
1045         TRACE("(%p)->(IID_UndocumentedScriptIface %p) returning NULL\n", This, ppv);
1046         *ppv = NULL;
1047     }else {
1048         return FALSE;
1049     }
1050
1051     if(*ppv)
1052         IUnknown_AddRef((IUnknown*)*ppv);
1053     return TRUE;
1054 }
1055
1056 void release_dispex(DispatchEx *This)
1057 {
1058     dynamic_prop_t *prop;
1059
1060     if(!This->dynamic_data)
1061         return;
1062
1063     for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
1064         VariantClear(&prop->var);
1065         heap_free(prop->name);
1066     }
1067
1068     heap_free(This->dynamic_data->props);
1069
1070     if(This->dynamic_data->func_disps) {
1071         unsigned i;
1072
1073         for(i=0; i < This->data->data->func_disp_cnt; i++) {
1074             if(This->dynamic_data->func_disps[i]) {
1075                 release_dispex(&This->dynamic_data->func_disps[i]->dispex);
1076                 heap_free(This->dynamic_data->func_disps[i]);
1077             }
1078         }
1079
1080         heap_free(This->dynamic_data->func_disps);
1081     }
1082
1083     heap_free(This->dynamic_data);
1084 }
1085
1086 void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
1087 {
1088     dispex->lpIDispatchExVtbl = &DispatchExVtbl;
1089     dispex->outer = outer;
1090     dispex->data = data;
1091     dispex->dynamic_data = NULL;
1092 }