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