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