msctf: Implement ITfClientId.
[wine] / dlls / mshtml / htmlelemcol.c
1 /*
2  * Copyright 2006-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     DispatchEx dispex;
36     const IHTMLElementCollectionVtbl *lpHTMLElementCollectionVtbl;
37
38     IUnknown *ref_unk;
39     HTMLElement **elems;
40     DWORD len;
41
42     LONG ref;
43 } HTMLElementCollection;
44
45 #define HTMLELEMCOL(x)  ((IHTMLElementCollection*) &(x)->lpHTMLElementCollectionVtbl)
46
47 typedef struct {
48     HTMLElement **buf;
49     DWORD len;
50     DWORD size;
51 } elem_vector_t;
52
53 static IHTMLElementCollection *HTMLElementCollection_Create(IUnknown *ref_unk,
54                                                             HTMLElement **elems, DWORD len);
55
56 static void elem_vector_add(elem_vector_t *buf, HTMLElement *elem)
57 {
58     if(buf->len == buf->size) {
59         buf->size <<= 1;
60         buf->buf = heap_realloc(buf->buf, buf->size*sizeof(HTMLElement**));
61     }
62
63     buf->buf[buf->len++] = elem;
64 }
65
66 static void elem_vector_normalize(elem_vector_t *buf)
67 {
68     if(!buf->len) {
69         heap_free(buf->buf);
70         buf->buf = NULL;
71     }else if(buf->size > buf->len) {
72         buf->buf = heap_realloc(buf->buf, buf->len*sizeof(HTMLElement**));
73     }
74
75     buf->size = buf->len;
76 }
77
78 static inline BOOL is_elem_node(nsIDOMNode *node)
79 {
80     PRUint16 type=0;
81
82     nsIDOMNode_GetNodeType(node, &type);
83
84     return type == ELEMENT_NODE || type == COMMENT_NODE;
85 }
86
87 #define ELEMCOL_THIS(iface) DEFINE_THIS(HTMLElementCollection, HTMLElementCollection, iface)
88 #define HTMLELEM_NODE_THIS(iface) DEFINE_THIS2(HTMLElement, node, iface)
89
90 static HRESULT WINAPI HTMLElementCollection_QueryInterface(IHTMLElementCollection *iface,
91                                                            REFIID riid, void **ppv)
92 {
93     HTMLElementCollection *This = ELEMCOL_THIS(iface);
94
95     *ppv = NULL;
96
97     if(IsEqualGUID(&IID_IUnknown, riid)) {
98         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
99         *ppv = HTMLELEMCOL(This);
100     }else if(IsEqualGUID(&IID_IHTMLElementCollection, riid)) {
101         TRACE("(%p)->(IID_IHTMLElementCollection %p)\n", This, ppv);
102         *ppv = HTMLELEMCOL(This);
103     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
104         return *ppv ? S_OK : E_NOINTERFACE;
105     }
106
107     if(*ppv) {
108         IHTMLElementCollection_AddRef(HTMLELEMCOL(This));
109         return S_OK;
110     }
111
112     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
113     return E_NOINTERFACE;
114 }
115
116 static ULONG WINAPI HTMLElementCollection_AddRef(IHTMLElementCollection *iface)
117 {
118     HTMLElementCollection *This = ELEMCOL_THIS(iface);
119     LONG ref = InterlockedIncrement(&This->ref);
120
121     TRACE("(%p) ref=%d\n", This, ref);
122
123     return ref;
124 }
125
126 static ULONG WINAPI HTMLElementCollection_Release(IHTMLElementCollection *iface)
127 {
128     HTMLElementCollection *This = ELEMCOL_THIS(iface);
129     LONG ref = InterlockedDecrement(&This->ref);
130
131     TRACE("(%p) ref=%d\n", This, ref);
132
133     if(!ref) {
134         IUnknown_Release(This->ref_unk);
135         heap_free(This->elems);
136         heap_free(This);
137     }
138
139     return ref;
140 }
141
142 static HRESULT WINAPI HTMLElementCollection_GetTypeInfoCount(IHTMLElementCollection *iface,
143                                                              UINT *pctinfo)
144 {
145     HTMLElementCollection *This = ELEMCOL_THIS(iface);
146     return IDispatchEx_GetTypeInfoCount(DISPATCHEX(&This->dispex), pctinfo);
147 }
148
149 static HRESULT WINAPI HTMLElementCollection_GetTypeInfo(IHTMLElementCollection *iface,
150         UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
151 {
152     HTMLElementCollection *This = ELEMCOL_THIS(iface);
153     return IDispatchEx_GetTypeInfo(DISPATCHEX(&This->dispex), iTInfo, lcid, ppTInfo);
154 }
155
156 static HRESULT WINAPI HTMLElementCollection_GetIDsOfNames(IHTMLElementCollection *iface,
157         REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
158 {
159     HTMLElementCollection *This = ELEMCOL_THIS(iface);
160     return IDispatchEx_GetIDsOfNames(DISPATCHEX(&This->dispex), riid, rgszNames, cNames, lcid, rgDispId);
161 }
162
163 static HRESULT WINAPI HTMLElementCollection_Invoke(IHTMLElementCollection *iface,
164         DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
165         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
166 {
167     HTMLElementCollection *This = ELEMCOL_THIS(iface);
168     return IDispatchEx_Invoke(DISPATCHEX(&This->dispex), dispIdMember, riid, lcid,
169             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
170 }
171
172 static HRESULT WINAPI HTMLElementCollection_toString(IHTMLElementCollection *iface,
173                                                      BSTR *String)
174 {
175     HTMLElementCollection *This = ELEMCOL_THIS(iface);
176     FIXME("(%p)->(%p)\n", This, String);
177     return E_NOTIMPL;
178 }
179
180 static HRESULT WINAPI HTMLElementCollection_put_length(IHTMLElementCollection *iface,
181                                                        LONG v)
182 {
183     HTMLElementCollection *This = ELEMCOL_THIS(iface);
184     FIXME("(%p)->(%d)\n", This, v);
185     return E_NOTIMPL;
186 }
187
188 static HRESULT WINAPI HTMLElementCollection_get_length(IHTMLElementCollection *iface,
189                                                        LONG *p)
190 {
191     HTMLElementCollection *This = ELEMCOL_THIS(iface);
192
193     TRACE("(%p)->(%p)\n", This, p);
194
195     *p = This->len;
196     return S_OK;
197 }
198
199 static HRESULT WINAPI HTMLElementCollection_get__newEnum(IHTMLElementCollection *iface,
200                                                          IUnknown **p)
201 {
202     HTMLElementCollection *This = ELEMCOL_THIS(iface);
203     FIXME("(%p)->(%p)\n", This, p);
204     return E_NOTIMPL;
205 }
206
207 static BOOL is_elem_name(HTMLElement *elem, LPCWSTR name)
208 {
209     const PRUnichar *str;
210     nsAString nsstr, nsname;
211     BOOL ret = FALSE;
212     nsresult nsres;
213
214     static const PRUnichar nameW[] = {'n','a','m','e',0};
215
216     if(!elem->nselem)
217         return FALSE;
218
219     nsAString_Init(&nsstr, NULL);
220     nsIDOMHTMLElement_GetId(elem->nselem, &nsstr);
221     nsAString_GetData(&nsstr, &str);
222     if(!strcmpiW(str, name)) {
223         nsAString_Finish(&nsstr);
224         return TRUE;
225     }
226
227     nsAString_Init(&nsname, nameW);
228     nsres =  nsIDOMHTMLElement_GetAttribute(elem->nselem, &nsname, &nsstr);
229     nsAString_Finish(&nsname);
230     if(NS_SUCCEEDED(nsres)) {
231         nsAString_GetData(&nsstr, &str);
232         ret = !strcmpiW(str, name);
233     }
234
235     nsAString_Finish(&nsstr);
236     return ret;
237 }
238
239 static HRESULT WINAPI HTMLElementCollection_item(IHTMLElementCollection *iface,
240         VARIANT name, VARIANT index, IDispatch **pdisp)
241 {
242     HTMLElementCollection *This = ELEMCOL_THIS(iface);
243
244     TRACE("(%p)->(v(%d) v(%d) %p)\n", This, V_VT(&name), V_VT(&index), pdisp);
245
246     *pdisp = NULL;
247
248     if(V_VT(&name) == VT_I4) {
249         TRACE("name is VT_I4: %d\n", V_I4(&name));
250
251         if(V_I4(&name) < 0)
252             return E_INVALIDARG;
253         if(V_I4(&name) >= This->len)
254             return S_OK;
255
256         *pdisp = (IDispatch*)This->elems[V_I4(&name)];
257         IDispatch_AddRef(*pdisp);
258         TRACE("Returning pdisp=%p\n", pdisp);
259         return S_OK;
260     }
261
262     if(V_VT(&name) == VT_BSTR) {
263         DWORD i;
264
265         TRACE("name is VT_BSTR: %s\n", debugstr_w(V_BSTR(&name)));
266
267         if(V_VT(&index) == VT_I4) {
268             LONG idx = V_I4(&index);
269
270             TRACE("index = %d\n", idx);
271
272             if(idx < 0)
273                 return E_INVALIDARG;
274
275             for(i=0; i<This->len; i++) {
276                 if(is_elem_name(This->elems[i], V_BSTR(&name)) && !idx--)
277                     break;
278             }
279
280             if(i != This->len) {
281                 *pdisp = (IDispatch*)HTMLELEM(This->elems[i]);
282                 IDispatch_AddRef(*pdisp);
283             }
284
285             return S_OK;
286         }else {
287             elem_vector_t buf = {NULL, 0, 8};
288
289             buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
290
291             for(i=0; i<This->len; i++) {
292                 if(is_elem_name(This->elems[i], V_BSTR(&name)))
293                     elem_vector_add(&buf, This->elems[i]);
294             }
295
296             if(buf.len > 1) {
297                 elem_vector_normalize(&buf);
298                 *pdisp = (IDispatch*)HTMLElementCollection_Create(This->ref_unk, buf.buf, buf.len);
299             }else {
300                 if(buf.len == 1) {
301                     *pdisp = (IDispatch*)HTMLELEM(buf.buf[0]);
302                     IDispatch_AddRef(*pdisp);
303                 }
304
305                 heap_free(buf.buf);
306             }
307
308             return S_OK;
309         }
310     }
311
312     FIXME("unsupported arguments\n");
313     return E_INVALIDARG;
314 }
315
316 static HRESULT WINAPI HTMLElementCollection_tags(IHTMLElementCollection *iface,
317                                                  VARIANT tagName, IDispatch **pdisp)
318 {
319     HTMLElementCollection *This = ELEMCOL_THIS(iface);
320     DWORD i;
321     nsAString tag_str;
322     const PRUnichar *tag;
323     elem_vector_t buf = {NULL, 0, 8};
324
325     if(V_VT(&tagName) != VT_BSTR) {
326         WARN("Invalid arg\n");
327         return DISP_E_MEMBERNOTFOUND;
328     }
329
330     TRACE("(%p)->(%s %p)\n", This, debugstr_w(V_BSTR(&tagName)), pdisp);
331
332     buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
333
334     nsAString_Init(&tag_str, NULL);
335
336     for(i=0; i<This->len; i++) {
337         if(!This->elems[i]->nselem)
338             continue;
339
340         nsIDOMElement_GetTagName(This->elems[i]->nselem, &tag_str);
341         nsAString_GetData(&tag_str, &tag);
342
343         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, tag, -1,
344                           V_BSTR(&tagName), -1) == CSTR_EQUAL)
345             elem_vector_add(&buf, This->elems[i]);
346     }
347
348     nsAString_Finish(&tag_str);
349     elem_vector_normalize(&buf);
350
351     TRACE("fount %d tags\n", buf.len);
352
353     *pdisp = (IDispatch*)HTMLElementCollection_Create(This->ref_unk, buf.buf, buf.len);
354     return S_OK;
355 }
356
357 #define DISPID_ELEMCOL_0 MSHTML_DISPID_CUSTOM_MIN
358
359 static HRESULT HTMLElementCollection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
360 {
361     HTMLElementCollection *This = ELEMCOL_THIS(iface);
362     WCHAR *ptr;
363     DWORD idx=0;
364
365     if(!*name)
366         return DISP_E_UNKNOWNNAME;
367
368     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
369         idx = idx*10 + (*ptr-'0');
370
371     if(*ptr || idx >= This->len)
372         return DISP_E_UNKNOWNNAME;
373
374     *dispid = DISPID_ELEMCOL_0 + idx;
375     TRACE("ret %x\n", *dispid);
376     return S_OK;
377 }
378
379 static HRESULT HTMLElementCollection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
380         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
381 {
382     HTMLElementCollection *This = ELEMCOL_THIS(iface);
383     DWORD idx;
384
385     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, flags, params, res, ei, caller);
386
387     idx = id - DISPID_ELEMCOL_0;
388     if(idx >= This->len)
389         return DISP_E_UNKNOWNNAME;
390
391     switch(flags) {
392     case INVOKE_PROPERTYGET:
393         V_VT(res) = VT_DISPATCH;
394         V_DISPATCH(res) = (IDispatch*)HTMLELEM(This->elems[idx]);
395         IHTMLElement_AddRef(HTMLELEM(This->elems[idx]));
396         break;
397     default:
398         FIXME("unimplemented flags %x\n", flags);
399         return E_NOTIMPL;
400     }
401
402     return S_OK;
403 }
404
405 #undef ELEMCOL_THIS
406
407 static const IHTMLElementCollectionVtbl HTMLElementCollectionVtbl = {
408     HTMLElementCollection_QueryInterface,
409     HTMLElementCollection_AddRef,
410     HTMLElementCollection_Release,
411     HTMLElementCollection_GetTypeInfoCount,
412     HTMLElementCollection_GetTypeInfo,
413     HTMLElementCollection_GetIDsOfNames,
414     HTMLElementCollection_Invoke,
415     HTMLElementCollection_toString,
416     HTMLElementCollection_put_length,
417     HTMLElementCollection_get_length,
418     HTMLElementCollection_get__newEnum,
419     HTMLElementCollection_item,
420     HTMLElementCollection_tags
421 };
422
423 static const dispex_static_data_vtbl_t HTMLElementColection_dispex_vtbl = {
424     HTMLElementCollection_get_dispid,
425     HTMLElementCollection_invoke
426 };
427
428 static const tid_t HTMLElementCollection_iface_tids[] = {
429     IHTMLElementCollection_tid,
430     0
431 };
432 static dispex_static_data_t HTMLElementCollection_dispex = {
433     &HTMLElementColection_dispex_vtbl,
434     DispHTMLElementCollection_tid,
435     NULL,
436     HTMLElementCollection_iface_tids
437 };
438
439 static void create_all_list(HTMLDocument *doc, HTMLDOMNode *elem, elem_vector_t *buf)
440 {
441     nsIDOMNodeList *nsnode_list;
442     nsIDOMNode *iter;
443     PRUint32 list_len = 0, i;
444     nsresult nsres;
445
446     nsres = nsIDOMNode_GetChildNodes(elem->nsnode, &nsnode_list);
447     if(NS_FAILED(nsres)) {
448         ERR("GetChildNodes failed: %08x\n", nsres);
449         return;
450     }
451
452     nsIDOMNodeList_GetLength(nsnode_list, &list_len);
453     if(!list_len)
454         return;
455
456     for(i=0; i<list_len; i++) {
457         nsres = nsIDOMNodeList_Item(nsnode_list, i, &iter);
458         if(NS_FAILED(nsres)) {
459             ERR("Item failed: %08x\n", nsres);
460             continue;
461         }
462
463         if(is_elem_node(iter)) {
464             HTMLDOMNode *node = get_node(doc, iter, TRUE);
465
466             elem_vector_add(buf, HTMLELEM_NODE_THIS(node));
467             create_all_list(doc, node, buf);
468         }
469     }
470 }
471
472 IHTMLElementCollection *create_all_collection(HTMLDOMNode *node, BOOL include_root)
473 {
474     elem_vector_t buf = {NULL, 0, 8};
475
476     buf.buf = heap_alloc(buf.size*sizeof(HTMLElement**));
477
478     if(include_root)
479         elem_vector_add(&buf, HTMLELEM_NODE_THIS(node));
480     create_all_list(node->doc, node, &buf);
481     elem_vector_normalize(&buf);
482
483     return HTMLElementCollection_Create((IUnknown*)HTMLDOMNODE(node), buf.buf, buf.len);
484 }
485
486 IHTMLElementCollection *create_collection_from_nodelist(HTMLDocument *doc, IUnknown *unk, nsIDOMNodeList *nslist)
487 {
488     PRUint32 length = 0, i;
489     elem_vector_t buf;
490
491     nsIDOMNodeList_GetLength(nslist, &length);
492
493     buf.len = 0;
494     buf.size = length;
495     if(length) {
496         nsIDOMNode *nsnode;
497
498         buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
499
500         for(i=0; i<length; i++) {
501             nsIDOMNodeList_Item(nslist, i, &nsnode);
502             if(is_elem_node(nsnode))
503                 buf.buf[buf.len++] = HTMLELEM_NODE_THIS(get_node(doc, nsnode, TRUE));
504             nsIDOMNode_Release(nsnode);
505         }
506
507         elem_vector_normalize(&buf);
508     }else {
509         buf.buf = NULL;
510     }
511
512     return HTMLElementCollection_Create(unk, buf.buf, buf.len);
513 }
514
515 IHTMLElementCollection *create_collection_from_htmlcol(HTMLDocument *doc, IUnknown *unk, nsIDOMHTMLCollection *nscol)
516 {
517     PRUint32 length = 0, i;
518     elem_vector_t buf;
519
520     nsIDOMHTMLCollection_GetLength(nscol, &length);
521
522     buf.len = buf.size = length;
523     if(buf.len) {
524         nsIDOMNode *nsnode;
525
526         buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
527
528         for(i=0; i<length; i++) {
529             nsIDOMHTMLCollection_Item(nscol, i, &nsnode);
530             buf.buf[i] = HTMLELEM_NODE_THIS(get_node(doc, nsnode, TRUE));
531             nsIDOMNode_Release(nsnode);
532         }
533     }else {
534         buf.buf = NULL;
535     }
536
537     return HTMLElementCollection_Create(unk, buf.buf, buf.len);
538 }
539
540 static IHTMLElementCollection *HTMLElementCollection_Create(IUnknown *ref_unk,
541             HTMLElement **elems, DWORD len)
542 {
543     HTMLElementCollection *ret = heap_alloc_zero(sizeof(HTMLElementCollection));
544
545     ret->lpHTMLElementCollectionVtbl = &HTMLElementCollectionVtbl;
546     ret->ref = 1;
547     ret->elems = elems;
548     ret->len = len;
549
550     init_dispex(&ret->dispex, (IUnknown*)HTMLELEMCOL(ret), &HTMLElementCollection_dispex);
551
552     IUnknown_AddRef(ref_unk);
553     ret->ref_unk = ref_unk;
554
555     TRACE("ret=%p len=%d\n", ret, len);
556
557     return HTMLELEMCOL(ret);
558 }