msi: Fix a typo.
[wine] / dlls / mshtml / dispex.c
1 /*
2  * Copyright 2008 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 typedef struct {
35     DISPID id;
36     BSTR name;
37     tid_t tid;
38 } func_info_t;
39
40 struct dispex_data_t {
41     DWORD func_cnt;
42     func_info_t *funcs;
43     func_info_t **name_table;
44
45     struct list entry;
46 };
47
48 static ITypeLib *typelib;
49 static ITypeInfo *typeinfos[LAST_tid];
50 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
51
52 static REFIID tid_ids[] = {
53     &IID_NULL,
54     &DIID_DispDOMChildrenCollection,
55     &DIID_DispHTMLCommentElement,
56     &DIID_DispHTMLDocument,
57     &DIID_DispHTMLDOMTextNode,
58     &DIID_DispHTMLElementCollection,
59     &DIID_DispHTMLImg,
60     &DIID_DispHTMLInputElement,
61     &DIID_DispHTMLOptionElement,
62     &DIID_DispHTMLStyle,
63     &DIID_DispHTMLUnknownElement,
64     &DIID_DispHTMLWindow2,
65     &IID_IHTMLCommentElement,
66     &IID_IHTMLDocument2,
67     &IID_IHTMLDocument3,
68     &IID_IHTMLDocument4,
69     &IID_IHTMLDocument5,
70     &IID_IHTMLDOMChildrenCollection,
71     &IID_IHTMLDOMNode,
72     &IID_IHTMLDOMNode2,
73     &IID_IHTMLDOMTextNode,
74     &IID_IHTMLElement,
75     &IID_IHTMLElement2,
76     &IID_IHTMLElementCollection,
77     &IID_IHTMLImgElement,
78     &IID_IHTMLInputElement,
79     &IID_IHTMLOptionElement,
80     &IID_IHTMLStyle,
81     &IID_IHTMLWindow2,
82     &IID_IHTMLWindow3,
83     &IID_IOmNavigator
84 };
85
86 HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
87 {
88     HRESULT hres;
89
90     if(!typelib) {
91         ITypeLib *tl;
92
93         hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
94         if(FAILED(hres)) {
95             ERR("LoadRegTypeLib failed: %08x\n", hres);
96             return hres;
97         }
98
99         if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
100             ITypeLib_Release(tl);
101     }
102
103     if(!typeinfos[tid]) {
104         ITypeInfo *typeinfo;
105
106         hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &typeinfo);
107         if(FAILED(hres)) {
108             ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres);
109             return hres;
110         }
111
112         if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), typeinfo, NULL))
113             ITypeInfo_Release(typeinfo);
114     }
115
116     *typeinfo = typeinfos[tid];
117     return S_OK;
118 }
119
120 void release_typelib(void)
121 {
122     dispex_data_t *iter;
123     unsigned i;
124
125     while(!list_empty(&dispex_data_list)) {
126         iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
127         list_remove(&iter->entry);
128
129         for(i=0; i < iter->func_cnt; i++)
130             SysFreeString(iter->funcs[i].name);
131
132         heap_free(iter->funcs);
133         heap_free(iter->name_table);
134         heap_free(iter);
135     }
136
137     if(!typelib)
138         return;
139
140     for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
141         if(typeinfos[i])
142             ITypeInfo_Release(typeinfos[i]);
143
144     ITypeLib_Release(typelib);
145 }
146
147 static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, DISPID id, ITypeInfo *dti)
148 {
149     HRESULT hres;
150
151     if(data->func_cnt && data->funcs[data->func_cnt-1].id == id)
152         return;
153
154     if(data->func_cnt == *size)
155         data->funcs = heap_realloc(data->funcs, (*size <<= 1)*sizeof(func_info_t));
156
157     hres = ITypeInfo_GetDocumentation(dti, id, &data->funcs[data->func_cnt].name, NULL, NULL, NULL);
158     if(FAILED(hres))
159         return;
160
161     data->funcs[data->func_cnt].id = id;
162     data->funcs[data->func_cnt].tid = tid;
163
164     data->func_cnt++;
165 }
166
167 static int dispid_cmp(const void *p1, const void *p2)
168 {
169     return ((func_info_t*)p1)->id - ((func_info_t*)p2)->id;
170 }
171
172 static int func_name_cmp(const void *p1, const void *p2)
173 {
174     return strcmpiW((*(func_info_t**)p1)->name, (*(func_info_t**)p2)->name);
175 }
176
177 static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
178 {
179     const tid_t *tid = This->data->iface_tids;
180     FUNCDESC *funcdesc;
181     dispex_data_t *data;
182     DWORD size = 16, i;
183     ITypeInfo *ti, *dti;
184     HRESULT hres;
185
186     TRACE("(%p)\n", This);
187
188     hres = get_typeinfo(This->data->disp_tid, &dti);
189     if(FAILED(hres)) {
190         ERR("Could not get disp type info: %08x\n", hres);
191         return NULL;
192     }
193
194     data = heap_alloc(sizeof(dispex_data_t));
195     data->func_cnt = 0;
196     data->funcs = heap_alloc(size*sizeof(func_info_t));
197     list_add_tail(&dispex_data_list, &data->entry);
198
199     while(*tid) {
200         hres = get_typeinfo(*tid, &ti);
201         if(FAILED(hres))
202             break;
203
204         i=7;
205         while(1) {
206             hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
207             if(FAILED(hres))
208                 break;
209
210             add_func_info(data, &size, *tid, funcdesc->memid, dti);
211             ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
212         }
213
214         tid++;
215     }
216
217     if(!data->func_cnt) {
218         heap_free(data->funcs);
219         data->funcs = NULL;
220     }else if(data->func_cnt != size) {
221         data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
222     }
223
224     qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
225
226     if(data->funcs) {
227         data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
228         for(i=0; i < data->func_cnt; i++)
229             data->name_table[i] = data->funcs+i;
230         qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
231     }else {
232         data->name_table = NULL;
233     }
234
235     return data;
236 }
237
238 static CRITICAL_SECTION cs_dispex_static_data;
239 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
240 {
241     0, 0, &cs_dispex_static_data,
242     { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
243       0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
244 };
245 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
246
247
248 static dispex_data_t *get_dispex_data(DispatchEx *This)
249 {
250     if(This->data->data)
251         return This->data->data;
252
253     EnterCriticalSection(&cs_dispex_static_data);
254
255     if(!This->data->data)
256         This->data->data = preprocess_dispex_data(This);
257
258     LeaveCriticalSection(&cs_dispex_static_data);
259
260     return This->data->data;
261 }
262
263 void call_disp_func(HTMLDocument *doc, IDispatch *disp)
264 {
265     DISPID named_arg = DISPID_THIS;
266     VARIANTARG arg;
267     DISPPARAMS params = {&arg, &named_arg, 1, 1};
268     EXCEPINFO ei;
269     IDispatchEx *dispex;
270     VARIANT res;
271     HRESULT hres;
272
273     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
274     if(FAILED(hres)) {
275         FIXME("Could not get IDispatchEx interface: %08x\n", hres);
276         return;
277     }
278
279     V_VT(&arg) = VT_DISPATCH;
280     V_DISPATCH(&arg) = (IDispatch*)HTMLWINDOW2(doc->window);
281     VariantInit(&res);
282     memset(&ei, 0, sizeof(ei));
283
284     hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, &params, &res, &ei, NULL);
285     IDispatchEx_Release(dispex);
286
287     TRACE("%p returned %08x\n", disp, hres);
288
289     VariantClear(&res);
290 }
291
292 static inline BOOL is_custom_dispid(DISPID id)
293 {
294     return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
295 }
296
297 #define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface)
298
299 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
300 {
301     DispatchEx *This = DISPATCHEX_THIS(iface);
302
303     return IUnknown_QueryInterface(This->outer, riid, ppv);
304 }
305
306 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
307 {
308     DispatchEx *This = DISPATCHEX_THIS(iface);
309
310     return IUnknown_AddRef(This->outer);
311 }
312
313 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
314 {
315     DispatchEx *This = DISPATCHEX_THIS(iface);
316
317     return IUnknown_Release(This->outer);
318 }
319
320 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
321 {
322     DispatchEx *This = DISPATCHEX_THIS(iface);
323
324     TRACE("(%p)->(%p)\n", This, pctinfo);
325
326     *pctinfo = 1;
327     return S_OK;
328 }
329
330 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
331                                               LCID lcid, ITypeInfo **ppTInfo)
332 {
333     DispatchEx *This = DISPATCHEX_THIS(iface);
334     HRESULT hres;
335
336     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
337
338     hres = get_typeinfo(This->data->disp_tid, ppTInfo);
339     if(FAILED(hres))
340         return hres;
341
342     ITypeInfo_AddRef(*ppTInfo);
343     return S_OK;
344 }
345
346 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
347                                                 LPOLESTR *rgszNames, UINT cNames,
348                                                 LCID lcid, DISPID *rgDispId)
349 {
350     DispatchEx *This = DISPATCHEX_THIS(iface);
351     UINT i;
352     HRESULT hres;
353
354     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
355           lcid, rgDispId);
356
357     for(i=0; i < cNames; i++) {
358         hres = IDispatchEx_GetDispID(DISPATCHEX(This), rgszNames[i], 0, rgDispId+i);
359         if(FAILED(hres))
360             return hres;
361     }
362
363     return S_OK;
364 }
365
366 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
367                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
368                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
369 {
370     DispatchEx *This = DISPATCHEX_THIS(iface);
371
372     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
373           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
374
375     return IDispatchEx_InvokeEx(DISPATCHEX(This), dispIdMember, lcid, wFlags,
376                                 pDispParams, pVarResult, pExcepInfo, NULL);
377 }
378
379 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
380 {
381     DispatchEx *This = DISPATCHEX_THIS(iface);
382     dispex_data_t *data;
383     int min, max, n, c;
384
385     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
386
387     if(grfdex & (~fdexNameCaseSensitive))
388         FIXME("Unsupported grfdex %x\n", grfdex);
389
390     data = get_dispex_data(This);
391     if(!data)
392         return E_FAIL;
393
394     min = 0;
395     max = data->func_cnt-1;
396
397     while(min <= max) {
398         n = (min+max)/2;
399
400         c = strcmpiW(data->name_table[n]->name, bstrName);
401         if(!c) {
402             if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, bstrName))
403                 break;
404
405             *pid = data->name_table[n]->id;
406             return S_OK;
407         }
408
409         if(c > 0)
410             max = n-1;
411         else
412             min = n+1;
413     }
414
415     if(This->data->vtbl && This->data->vtbl->get_dispid) {
416         HRESULT hres;
417
418         hres = This->data->vtbl->get_dispid(This->outer, bstrName, grfdex, pid);
419         if(hres != DISP_E_UNKNOWNNAME)
420             return hres;
421     }
422
423     TRACE("not found %s\n", debugstr_w(bstrName));
424     return DISP_E_UNKNOWNNAME;
425 }
426
427 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
428         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
429 {
430     DispatchEx *This = DISPATCHEX_THIS(iface);
431     IUnknown *unk;
432     ITypeInfo *ti;
433     dispex_data_t *data;
434     UINT argerr=0;
435     int min, max, n;
436     HRESULT hres;
437
438     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
439
440     if(is_custom_dispid(id) && This->data->vtbl && This->data->vtbl->invoke)
441         return This->data->vtbl->invoke(This->outer, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
442
443     if(wFlags == DISPATCH_CONSTRUCT) {
444         FIXME("DISPATCH_CONSTRUCT not implemented\n");
445         return E_NOTIMPL;
446     }
447
448     data = get_dispex_data(This);
449     if(!data)
450         return E_FAIL;
451
452     min = 0;
453     max = data->func_cnt-1;
454
455     while(min <= max) {
456         n = (min+max)/2;
457
458         if(data->funcs[n].id == id)
459             break;
460
461         if(data->funcs[n].id < id)
462             min = n+1;
463         else
464             max = n-1;
465     }
466
467     if(min > max) {
468         WARN("invalid id %x\n", id);
469         return DISP_E_UNKNOWNNAME;
470     }
471
472     hres = get_typeinfo(data->funcs[n].tid, &ti);
473     if(FAILED(hres)) {
474         ERR("Could not get type info: %08x\n", hres);
475         return hres;
476     }
477
478     hres = IUnknown_QueryInterface(This->outer, tid_ids[data->funcs[n].tid], (void**)&unk);
479     if(FAILED(hres)) {
480         ERR("Could not get iface: %08x\n", hres);
481         return E_FAIL;
482     }
483
484     hres = ITypeInfo_Invoke(ti, unk, id, wFlags, pdp, pvarRes, pei, &argerr);
485
486     IUnknown_Release(unk);
487     return hres;
488 }
489
490 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
491 {
492     DispatchEx *This = DISPATCHEX_THIS(iface);
493     FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
494     return E_NOTIMPL;
495 }
496
497 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
498 {
499     DispatchEx *This = DISPATCHEX_THIS(iface);
500     FIXME("(%p)->(%x)\n", This, id);
501     return E_NOTIMPL;
502 }
503
504 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
505 {
506     DispatchEx *This = DISPATCHEX_THIS(iface);
507     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
508     return E_NOTIMPL;
509 }
510
511 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
512 {
513     DispatchEx *This = DISPATCHEX_THIS(iface);
514     FIXME("(%p)->(%x %p)\n", This, id, pbstrName);
515     return E_NOTIMPL;
516 }
517
518 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
519 {
520     DispatchEx *This = DISPATCHEX_THIS(iface);
521     FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
522     return E_NOTIMPL;
523 }
524
525 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
526 {
527     DispatchEx *This = DISPATCHEX_THIS(iface);
528     FIXME("(%p)->(%p)\n", This, ppunk);
529     return E_NOTIMPL;
530 }
531
532 #undef DISPATCHEX_THIS
533
534 static IDispatchExVtbl DispatchExVtbl = {
535     DispatchEx_QueryInterface,
536     DispatchEx_AddRef,
537     DispatchEx_Release,
538     DispatchEx_GetTypeInfoCount,
539     DispatchEx_GetTypeInfo,
540     DispatchEx_GetIDsOfNames,
541     DispatchEx_Invoke,
542     DispatchEx_GetDispID,
543     DispatchEx_InvokeEx,
544     DispatchEx_DeleteMemberByName,
545     DispatchEx_DeleteMemberByDispID,
546     DispatchEx_GetMemberProperties,
547     DispatchEx_GetMemberName,
548     DispatchEx_GetNextDispID,
549     DispatchEx_GetNameSpaceParent
550 };
551
552 void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
553 {
554     dispex->lpIDispatchExVtbl = &DispatchExVtbl;
555     dispex->outer = outer;
556     dispex->data = data;
557 }