xref: /reactos/dll/win32/mshtml/htmlelemcol.c (revision c2c66aff)
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 "mshtml_private.h"
20 
21 typedef struct {
22     DispatchEx dispex;
23     IHTMLElementCollection IHTMLElementCollection_iface;
24 
25     HTMLElement **elems;
26     DWORD len;
27 
28     LONG ref;
29 } HTMLElementCollection;
30 
31 typedef struct {
32     IEnumVARIANT IEnumVARIANT_iface;
33 
34     LONG ref;
35 
36     ULONG iter;
37     HTMLElementCollection *col;
38 } HTMLElementCollectionEnum;
39 
40 typedef struct {
41     HTMLElement **buf;
42     DWORD len;
43     DWORD size;
44 } elem_vector_t;
45 
46 /* FIXME: Handle it better way */
elem_from_HTMLDOMNode(HTMLDOMNode * iface)47 static inline HTMLElement *elem_from_HTMLDOMNode(HTMLDOMNode *iface)
48 {
49     return CONTAINING_RECORD(iface, HTMLElement, node);
50 }
51 
52 static IHTMLElementCollection *HTMLElementCollection_Create(HTMLElement **elems, DWORD len);
53 
elem_vector_add(elem_vector_t * buf,HTMLElement * elem)54 static void elem_vector_add(elem_vector_t *buf, HTMLElement *elem)
55 {
56     if(buf->len == buf->size) {
57         buf->size <<= 1;
58         buf->buf = heap_realloc(buf->buf, buf->size*sizeof(HTMLElement*));
59     }
60 
61     buf->buf[buf->len++] = elem;
62 }
63 
elem_vector_normalize(elem_vector_t * buf)64 static void elem_vector_normalize(elem_vector_t *buf)
65 {
66     if(!buf->len) {
67         heap_free(buf->buf);
68         buf->buf = NULL;
69     }else if(buf->size > buf->len) {
70         buf->buf = heap_realloc(buf->buf, buf->len*sizeof(HTMLElement*));
71     }
72 
73     buf->size = buf->len;
74 }
75 
is_elem_node(nsIDOMNode * node)76 static inline BOOL is_elem_node(nsIDOMNode *node)
77 {
78     UINT16 type=0;
79 
80     nsIDOMNode_GetNodeType(node, &type);
81 
82     return type == ELEMENT_NODE || type == COMMENT_NODE;
83 }
84 
impl_from_IEnumVARIANT(IEnumVARIANT * iface)85 static inline HTMLElementCollectionEnum *impl_from_IEnumVARIANT(IEnumVARIANT *iface)
86 {
87     return CONTAINING_RECORD(iface, HTMLElementCollectionEnum, IEnumVARIANT_iface);
88 }
89 
HTMLElementCollectionEnum_QueryInterface(IEnumVARIANT * iface,REFIID riid,void ** ppv)90 static HRESULT WINAPI HTMLElementCollectionEnum_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv)
91 {
92     HTMLElementCollectionEnum *This = impl_from_IEnumVARIANT(iface);
93 
94     TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
95 
96     if(IsEqualGUID(riid, &IID_IUnknown)) {
97         *ppv = &This->IEnumVARIANT_iface;
98     }else if(IsEqualGUID(riid, &IID_IEnumVARIANT)) {
99         *ppv = &This->IEnumVARIANT_iface;
100     }else {
101         FIXME("Unsupported iface %s\n", debugstr_mshtml_guid(riid));
102         *ppv = NULL;
103         return E_NOINTERFACE;
104     }
105 
106     IUnknown_AddRef((IUnknown*)*ppv);
107     return S_OK;
108 }
109 
HTMLElementCollectionEnum_AddRef(IEnumVARIANT * iface)110 static ULONG WINAPI HTMLElementCollectionEnum_AddRef(IEnumVARIANT *iface)
111 {
112     HTMLElementCollectionEnum *This = impl_from_IEnumVARIANT(iface);
113     LONG ref = InterlockedIncrement(&This->ref);
114 
115     TRACE("(%p) ref=%d\n", This, ref);
116 
117     return ref;
118 }
119 
HTMLElementCollectionEnum_Release(IEnumVARIANT * iface)120 static ULONG WINAPI HTMLElementCollectionEnum_Release(IEnumVARIANT *iface)
121 {
122     HTMLElementCollectionEnum *This = impl_from_IEnumVARIANT(iface);
123     LONG ref = InterlockedDecrement(&This->ref);
124 
125     TRACE("(%p) ref=%d\n", This, ref);
126 
127     if(!ref) {
128         IHTMLElementCollection_Release(&This->col->IHTMLElementCollection_iface);
129         heap_free(This);
130     }
131 
132     return ref;
133 }
134 
HTMLElementCollectionEnum_Next(IEnumVARIANT * iface,ULONG celt,VARIANT * rgVar,ULONG * pCeltFetched)135 static HRESULT WINAPI HTMLElementCollectionEnum_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
136 {
137     HTMLElementCollectionEnum *This = impl_from_IEnumVARIANT(iface);
138     ULONG fetched = 0;
139 
140     TRACE("(%p)->(%d %p %p)\n", This, celt, rgVar, pCeltFetched);
141 
142     while(This->iter+fetched < This->col->len && fetched < celt) {
143         V_VT(rgVar+fetched) = VT_DISPATCH;
144         V_DISPATCH(rgVar+fetched) = (IDispatch*)&This->col->elems[This->iter+fetched]->IHTMLElement_iface;
145         IDispatch_AddRef(V_DISPATCH(rgVar+fetched));
146         fetched++;
147     }
148 
149     This->iter += fetched;
150     if(pCeltFetched)
151         *pCeltFetched = fetched;
152     return fetched == celt ? S_OK : S_FALSE;
153 }
154 
HTMLElementCollectionEnum_Skip(IEnumVARIANT * iface,ULONG celt)155 static HRESULT WINAPI HTMLElementCollectionEnum_Skip(IEnumVARIANT *iface, ULONG celt)
156 {
157     HTMLElementCollectionEnum *This = impl_from_IEnumVARIANT(iface);
158 
159     TRACE("(%p)->(%d)\n", This, celt);
160 
161     if(This->iter + celt > This->col->len) {
162         This->iter = This->col->len;
163         return S_FALSE;
164     }
165 
166     This->iter += celt;
167     return S_OK;
168 }
169 
HTMLElementCollectionEnum_Reset(IEnumVARIANT * iface)170 static HRESULT WINAPI HTMLElementCollectionEnum_Reset(IEnumVARIANT *iface)
171 {
172     HTMLElementCollectionEnum *This = impl_from_IEnumVARIANT(iface);
173 
174     TRACE("(%p)->()\n", This);
175 
176     This->iter = 0;
177     return S_OK;
178 }
179 
HTMLElementCollectionEnum_Clone(IEnumVARIANT * iface,IEnumVARIANT ** ppEnum)180 static HRESULT WINAPI HTMLElementCollectionEnum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
181 {
182     HTMLElementCollectionEnum *This = impl_from_IEnumVARIANT(iface);
183     FIXME("(%p)->(%p)\n", This, ppEnum);
184     return E_NOTIMPL;
185 }
186 
187 static const IEnumVARIANTVtbl HTMLElementCollectionEnumVtbl = {
188     HTMLElementCollectionEnum_QueryInterface,
189     HTMLElementCollectionEnum_AddRef,
190     HTMLElementCollectionEnum_Release,
191     HTMLElementCollectionEnum_Next,
192     HTMLElementCollectionEnum_Skip,
193     HTMLElementCollectionEnum_Reset,
194     HTMLElementCollectionEnum_Clone
195 };
196 
impl_from_IHTMLElementCollection(IHTMLElementCollection * iface)197 static inline HTMLElementCollection *impl_from_IHTMLElementCollection(IHTMLElementCollection *iface)
198 {
199     return CONTAINING_RECORD(iface, HTMLElementCollection, IHTMLElementCollection_iface);
200 }
201 
HTMLElementCollection_QueryInterface(IHTMLElementCollection * iface,REFIID riid,void ** ppv)202 static HRESULT WINAPI HTMLElementCollection_QueryInterface(IHTMLElementCollection *iface,
203                                                            REFIID riid, void **ppv)
204 {
205     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
206 
207     TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
208 
209     if(IsEqualGUID(&IID_IUnknown, riid)) {
210         *ppv = &This->IHTMLElementCollection_iface;
211     }else if(IsEqualGUID(&IID_IHTMLElementCollection, riid)) {
212         *ppv = &This->IHTMLElementCollection_iface;
213     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
214         return *ppv ? S_OK : E_NOINTERFACE;
215     }else {
216         *ppv = NULL;
217         FIXME("Unsupported iface %s\n", debugstr_mshtml_guid(riid));
218         return E_NOINTERFACE;
219     }
220 
221     IHTMLElementCollection_AddRef(&This->IHTMLElementCollection_iface);
222     return S_OK;
223 }
224 
HTMLElementCollection_AddRef(IHTMLElementCollection * iface)225 static ULONG WINAPI HTMLElementCollection_AddRef(IHTMLElementCollection *iface)
226 {
227     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
228     LONG ref = InterlockedIncrement(&This->ref);
229 
230     TRACE("(%p) ref=%d\n", This, ref);
231 
232     return ref;
233 }
234 
HTMLElementCollection_Release(IHTMLElementCollection * iface)235 static ULONG WINAPI HTMLElementCollection_Release(IHTMLElementCollection *iface)
236 {
237     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
238     LONG ref = InterlockedDecrement(&This->ref);
239 
240     TRACE("(%p) ref=%d\n", This, ref);
241 
242     if(!ref) {
243         unsigned i;
244 
245         for(i=0; i < This->len; i++)
246             node_release(&This->elems[i]->node);
247         heap_free(This->elems);
248 
249         release_dispex(&This->dispex);
250         heap_free(This);
251     }
252 
253     return ref;
254 }
255 
HTMLElementCollection_GetTypeInfoCount(IHTMLElementCollection * iface,UINT * pctinfo)256 static HRESULT WINAPI HTMLElementCollection_GetTypeInfoCount(IHTMLElementCollection *iface,
257                                                              UINT *pctinfo)
258 {
259     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
260     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
261 }
262 
HTMLElementCollection_GetTypeInfo(IHTMLElementCollection * iface,UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)263 static HRESULT WINAPI HTMLElementCollection_GetTypeInfo(IHTMLElementCollection *iface,
264         UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
265 {
266     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
267     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
268 }
269 
HTMLElementCollection_GetIDsOfNames(IHTMLElementCollection * iface,REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)270 static HRESULT WINAPI HTMLElementCollection_GetIDsOfNames(IHTMLElementCollection *iface,
271         REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
272 {
273     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
274     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
275             lcid, rgDispId);
276 }
277 
HTMLElementCollection_Invoke(IHTMLElementCollection * iface,DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)278 static HRESULT WINAPI HTMLElementCollection_Invoke(IHTMLElementCollection *iface,
279         DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
280         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
281 {
282     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
283     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
284             wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
285 }
286 
HTMLElementCollection_toString(IHTMLElementCollection * iface,BSTR * String)287 static HRESULT WINAPI HTMLElementCollection_toString(IHTMLElementCollection *iface,
288                                                      BSTR *String)
289 {
290     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
291     FIXME("(%p)->(%p)\n", This, String);
292     return E_NOTIMPL;
293 }
294 
HTMLElementCollection_put_length(IHTMLElementCollection * iface,LONG v)295 static HRESULT WINAPI HTMLElementCollection_put_length(IHTMLElementCollection *iface,
296                                                        LONG v)
297 {
298     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
299     FIXME("(%p)->(%d)\n", This, v);
300     return E_NOTIMPL;
301 }
302 
HTMLElementCollection_get_length(IHTMLElementCollection * iface,LONG * p)303 static HRESULT WINAPI HTMLElementCollection_get_length(IHTMLElementCollection *iface,
304                                                        LONG *p)
305 {
306     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
307 
308     TRACE("(%p)->(%p)\n", This, p);
309 
310     *p = This->len;
311     return S_OK;
312 }
313 
HTMLElementCollection_get__newEnum(IHTMLElementCollection * iface,IUnknown ** p)314 static HRESULT WINAPI HTMLElementCollection_get__newEnum(IHTMLElementCollection *iface,
315                                                          IUnknown **p)
316 {
317     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
318     HTMLElementCollectionEnum *ret;
319 
320     TRACE("(%p)->(%p)\n", This, p);
321 
322     ret = heap_alloc(sizeof(*ret));
323     if(!ret)
324         return E_OUTOFMEMORY;
325 
326     ret->IEnumVARIANT_iface.lpVtbl = &HTMLElementCollectionEnumVtbl;
327     ret->ref = 1;
328     ret->iter = 0;
329 
330     IHTMLElementCollection_AddRef(&This->IHTMLElementCollection_iface);
331     ret->col = This;
332 
333     *p = (IUnknown*)&ret->IEnumVARIANT_iface;
334     return S_OK;
335 }
336 
is_elem_id(HTMLElement * elem,LPCWSTR name)337 static BOOL is_elem_id(HTMLElement *elem, LPCWSTR name)
338 {
339     BSTR elem_id;
340     HRESULT hres;
341 
342     hres = IHTMLElement_get_id(&elem->IHTMLElement_iface, &elem_id);
343     if(FAILED(hres)){
344         WARN("IHTMLElement_get_id failed: 0x%08x\n", hres);
345         return FALSE;
346     }
347 
348     if(elem_id && !strcmpW(elem_id, name)) {
349         SysFreeString(elem_id);
350         return TRUE;
351     }
352 
353     SysFreeString(elem_id);
354     return FALSE;
355 }
356 
is_elem_name(HTMLElement * elem,LPCWSTR name)357 static BOOL is_elem_name(HTMLElement *elem, LPCWSTR name)
358 {
359     const PRUnichar *str;
360     nsAString nsstr;
361     BOOL ret = FALSE;
362     nsresult nsres;
363 
364     static const PRUnichar nameW[] = {'n','a','m','e',0};
365 
366     if(!elem->nselem)
367         return FALSE;
368 
369     nsAString_Init(&nsstr, NULL);
370     nsIDOMHTMLElement_GetId(elem->nselem, &nsstr);
371     nsAString_GetData(&nsstr, &str);
372     if(!strcmpiW(str, name)) {
373         nsAString_Finish(&nsstr);
374         return TRUE;
375     }
376 
377     nsres = get_elem_attr_value(elem->nselem, nameW, &nsstr, &str);
378     if(NS_SUCCEEDED(nsres)) {
379         ret = !strcmpiW(str, name);
380         nsAString_Finish(&nsstr);
381     }
382 
383     return ret;
384 }
385 
get_item_idx(HTMLElementCollection * This,UINT idx,IDispatch ** ret)386 static HRESULT get_item_idx(HTMLElementCollection *This, UINT idx, IDispatch **ret)
387 {
388     if(idx < This->len) {
389         *ret = (IDispatch*)This->elems[idx];
390         IDispatch_AddRef(*ret);
391     }
392 
393     return S_OK;
394 }
395 
HTMLElementCollection_item(IHTMLElementCollection * iface,VARIANT name,VARIANT index,IDispatch ** pdisp)396 static HRESULT WINAPI HTMLElementCollection_item(IHTMLElementCollection *iface,
397         VARIANT name, VARIANT index, IDispatch **pdisp)
398 {
399     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
400     HRESULT hres = S_OK;
401 
402     TRACE("(%p)->(%s %s %p)\n", This, debugstr_variant(&name), debugstr_variant(&index), pdisp);
403 
404     *pdisp = NULL;
405 
406     switch(V_VT(&name)) {
407     case VT_I4:
408     case VT_INT:
409         if(V_I4(&name) < 0)
410             return E_INVALIDARG;
411         hres = get_item_idx(This, V_I4(&name), pdisp);
412         break;
413 
414     case VT_UI4:
415     case VT_UINT:
416         hres = get_item_idx(This, V_UINT(&name), pdisp);
417         break;
418 
419     case VT_BSTR: {
420         DWORD i;
421 
422         if(V_VT(&index) == VT_I4) {
423             LONG idx = V_I4(&index);
424 
425             if(idx < 0)
426                 return E_INVALIDARG;
427 
428             for(i=0; i<This->len; i++) {
429                 if(is_elem_name(This->elems[i], V_BSTR(&name)) && !idx--)
430                     break;
431             }
432 
433             if(i != This->len) {
434                 *pdisp = (IDispatch*)&This->elems[i]->IHTMLElement_iface;
435                 IDispatch_AddRef(*pdisp);
436             }
437         }else {
438             elem_vector_t buf = {NULL, 0, 8};
439 
440             buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
441 
442             for(i=0; i<This->len; i++) {
443                 if(is_elem_name(This->elems[i], V_BSTR(&name))) {
444                     node_addref(&This->elems[i]->node);
445                     elem_vector_add(&buf, This->elems[i]);
446                 }
447             }
448 
449             if(buf.len > 1) {
450                 elem_vector_normalize(&buf);
451                 *pdisp = (IDispatch*)HTMLElementCollection_Create(buf.buf, buf.len);
452             }else {
453                 if(buf.len == 1) {
454                     /* Already AddRef-ed */
455                     *pdisp = (IDispatch*)&buf.buf[0]->IHTMLElement_iface;
456                 }
457 
458                 heap_free(buf.buf);
459             }
460         }
461         break;
462     }
463 
464     default:
465         FIXME("Unsupported name %s\n", debugstr_variant(&name));
466         hres = E_NOTIMPL;
467     }
468 
469     if(SUCCEEDED(hres))
470         TRACE("returning %p\n", *pdisp);
471     return hres;
472 }
473 
HTMLElementCollection_tags(IHTMLElementCollection * iface,VARIANT tagName,IDispatch ** pdisp)474 static HRESULT WINAPI HTMLElementCollection_tags(IHTMLElementCollection *iface,
475                                                  VARIANT tagName, IDispatch **pdisp)
476 {
477     HTMLElementCollection *This = impl_from_IHTMLElementCollection(iface);
478     DWORD i;
479     nsAString tag_str;
480     const PRUnichar *tag;
481     elem_vector_t buf = {NULL, 0, 8};
482 
483     if(V_VT(&tagName) != VT_BSTR) {
484         WARN("Invalid arg\n");
485         return DISP_E_MEMBERNOTFOUND;
486     }
487 
488     TRACE("(%p)->(%s %p)\n", This, debugstr_w(V_BSTR(&tagName)), pdisp);
489 
490     buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
491 
492     nsAString_Init(&tag_str, NULL);
493 
494     for(i=0; i<This->len; i++) {
495         if(!This->elems[i]->nselem)
496             continue;
497 
498         nsIDOMHTMLElement_GetTagName(This->elems[i]->nselem, &tag_str);
499         nsAString_GetData(&tag_str, &tag);
500 
501         if(CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, tag, -1,
502                           V_BSTR(&tagName), -1) == CSTR_EQUAL) {
503             node_addref(&This->elems[i]->node);
504             elem_vector_add(&buf, This->elems[i]);
505         }
506     }
507 
508     nsAString_Finish(&tag_str);
509     elem_vector_normalize(&buf);
510 
511     TRACE("fount %d tags\n", buf.len);
512 
513     *pdisp = (IDispatch*)HTMLElementCollection_Create(buf.buf, buf.len);
514     return S_OK;
515 }
516 
517 static const IHTMLElementCollectionVtbl HTMLElementCollectionVtbl = {
518     HTMLElementCollection_QueryInterface,
519     HTMLElementCollection_AddRef,
520     HTMLElementCollection_Release,
521     HTMLElementCollection_GetTypeInfoCount,
522     HTMLElementCollection_GetTypeInfo,
523     HTMLElementCollection_GetIDsOfNames,
524     HTMLElementCollection_Invoke,
525     HTMLElementCollection_toString,
526     HTMLElementCollection_put_length,
527     HTMLElementCollection_get_length,
528     HTMLElementCollection_get__newEnum,
529     HTMLElementCollection_item,
530     HTMLElementCollection_tags
531 };
532 
impl_from_DispatchEx(DispatchEx * iface)533 static inline HTMLElementCollection *impl_from_DispatchEx(DispatchEx *iface)
534 {
535     return CONTAINING_RECORD(iface, HTMLElementCollection, dispex);
536 }
537 
538 #define DISPID_ELEMCOL_0 MSHTML_DISPID_CUSTOM_MIN
539 
HTMLElementCollection_get_dispid(DispatchEx * dispex,BSTR name,DWORD flags,DISPID * dispid)540 static HRESULT HTMLElementCollection_get_dispid(DispatchEx *dispex, BSTR name, DWORD flags, DISPID *dispid)
541 {
542     HTMLElementCollection *This = impl_from_DispatchEx(dispex);
543     WCHAR *ptr;
544     DWORD idx=0;
545 
546     if(!*name)
547         return DISP_E_UNKNOWNNAME;
548 
549     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
550         idx = idx*10 + (*ptr-'0');
551 
552     if(*ptr) {
553         /* the name contains alpha characters, so search by name & id */
554         for(idx = 0; idx < This->len; ++idx) {
555             if(is_elem_id(This->elems[idx], name) ||
556                     is_elem_name(This->elems[idx], name))
557                 break;
558         }
559     }
560 
561     if(idx >= This->len)
562         return DISP_E_UNKNOWNNAME;
563 
564     *dispid = DISPID_ELEMCOL_0 + idx;
565     TRACE("ret %x\n", *dispid);
566     return S_OK;
567 }
568 
HTMLElementCollection_invoke(DispatchEx * dispex,DISPID id,LCID lcid,WORD flags,DISPPARAMS * params,VARIANT * res,EXCEPINFO * ei,IServiceProvider * caller)569 static HRESULT HTMLElementCollection_invoke(DispatchEx *dispex, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
570         VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
571 {
572     HTMLElementCollection *This = impl_from_DispatchEx(dispex);
573     DWORD idx;
574 
575     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, flags, params, res, ei, caller);
576 
577     idx = id - DISPID_ELEMCOL_0;
578     if(idx >= This->len)
579         return DISP_E_UNKNOWNNAME;
580 
581     switch(flags) {
582     case DISPATCH_PROPERTYGET:
583         V_VT(res) = VT_DISPATCH;
584         V_DISPATCH(res) = (IDispatch*)&This->elems[idx]->IHTMLElement_iface;
585         IHTMLElement_AddRef(&This->elems[idx]->IHTMLElement_iface);
586         break;
587     default:
588         FIXME("unimplemented flags %x\n", flags);
589         return E_NOTIMPL;
590     }
591 
592     return S_OK;
593 }
594 
595 static const dispex_static_data_vtbl_t HTMLElementColection_dispex_vtbl = {
596     NULL,
597     HTMLElementCollection_get_dispid,
598     HTMLElementCollection_invoke,
599     NULL
600 };
601 
602 static const tid_t HTMLElementCollection_iface_tids[] = {
603     IHTMLElementCollection_tid,
604     0
605 };
606 
607 static dispex_static_data_t HTMLElementCollection_dispex = {
608     &HTMLElementColection_dispex_vtbl,
609     DispHTMLElementCollection_tid,
610     NULL,
611     HTMLElementCollection_iface_tids
612 };
613 
create_all_list(HTMLDocumentNode * doc,HTMLDOMNode * elem,elem_vector_t * buf)614 static void create_all_list(HTMLDocumentNode *doc, HTMLDOMNode *elem, elem_vector_t *buf)
615 {
616     nsIDOMNodeList *nsnode_list;
617     nsIDOMNode *iter;
618     UINT32 list_len = 0, i;
619     nsresult nsres;
620     HRESULT hres;
621 
622     nsres = nsIDOMNode_GetChildNodes(elem->nsnode, &nsnode_list);
623     if(NS_FAILED(nsres)) {
624         ERR("GetChildNodes failed: %08x\n", nsres);
625         return;
626     }
627 
628     nsIDOMNodeList_GetLength(nsnode_list, &list_len);
629     if(!list_len)
630         return;
631 
632     for(i=0; i<list_len; i++) {
633         nsres = nsIDOMNodeList_Item(nsnode_list, i, &iter);
634         if(NS_FAILED(nsres)) {
635             ERR("Item failed: %08x\n", nsres);
636             continue;
637         }
638 
639         if(is_elem_node(iter)) {
640             HTMLDOMNode *node;
641 
642             hres = get_node(doc, iter, TRUE, &node);
643             if(FAILED(hres)) {
644                 FIXME("get_node failed: %08x\n", hres);
645                 continue;
646             }
647 
648             elem_vector_add(buf, elem_from_HTMLDOMNode(node));
649             create_all_list(doc, node, buf);
650         }
651     }
652 }
653 
create_all_collection(HTMLDOMNode * node,BOOL include_root)654 IHTMLElementCollection *create_all_collection(HTMLDOMNode *node, BOOL include_root)
655 {
656     elem_vector_t buf = {NULL, 0, 8};
657 
658     buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
659 
660     if(include_root) {
661         node_addref(node);
662         elem_vector_add(&buf, elem_from_HTMLDOMNode(node));
663     }
664     create_all_list(node->doc, node, &buf);
665     elem_vector_normalize(&buf);
666 
667     return HTMLElementCollection_Create(buf.buf, buf.len);
668 }
669 
create_collection_from_nodelist(HTMLDocumentNode * doc,nsIDOMNodeList * nslist)670 IHTMLElementCollection *create_collection_from_nodelist(HTMLDocumentNode *doc, nsIDOMNodeList *nslist)
671 {
672     UINT32 length = 0, i;
673     HTMLDOMNode *node;
674     elem_vector_t buf;
675     HRESULT hres;
676 
677     nsIDOMNodeList_GetLength(nslist, &length);
678 
679     buf.len = 0;
680     buf.size = length;
681     if(length) {
682         nsIDOMNode *nsnode;
683 
684         buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
685 
686         for(i=0; i<length; i++) {
687             nsIDOMNodeList_Item(nslist, i, &nsnode);
688             if(is_elem_node(nsnode)) {
689                 hres = get_node(doc, nsnode, TRUE, &node);
690                 if(FAILED(hres))
691                     continue;
692                 buf.buf[buf.len++] = elem_from_HTMLDOMNode(node);
693             }
694             nsIDOMNode_Release(nsnode);
695         }
696 
697         elem_vector_normalize(&buf);
698     }else {
699         buf.buf = NULL;
700     }
701 
702     return HTMLElementCollection_Create(buf.buf, buf.len);
703 }
704 
create_collection_from_htmlcol(HTMLDocumentNode * doc,nsIDOMHTMLCollection * nscol)705 IHTMLElementCollection *create_collection_from_htmlcol(HTMLDocumentNode *doc, nsIDOMHTMLCollection *nscol)
706 {
707     UINT32 length = 0, i;
708     elem_vector_t buf;
709     HTMLDOMNode *node;
710     HRESULT hres = S_OK;
711 
712     nsIDOMHTMLCollection_GetLength(nscol, &length);
713 
714     buf.len = buf.size = length;
715     if(buf.len) {
716         nsIDOMNode *nsnode;
717 
718         buf.buf = heap_alloc(buf.size*sizeof(HTMLElement*));
719 
720         for(i=0; i<length; i++) {
721             nsIDOMHTMLCollection_Item(nscol, i, &nsnode);
722             hres = get_node(doc, nsnode, TRUE, &node);
723             nsIDOMNode_Release(nsnode);
724             if(FAILED(hres))
725                 break;
726             buf.buf[i] = elem_from_HTMLDOMNode(node);
727         }
728     }else {
729         buf.buf = NULL;
730     }
731 
732     if(FAILED(hres)) {
733         heap_free(buf.buf);
734         return NULL;
735     }
736 
737     return HTMLElementCollection_Create(buf.buf, buf.len);
738 }
739 
get_elem_source_index(HTMLElement * elem,LONG * ret)740 HRESULT get_elem_source_index(HTMLElement *elem, LONG *ret)
741 {
742     elem_vector_t buf = {NULL, 0, 8};
743     nsIDOMNode *parent_node, *iter;
744     UINT16 parent_type;
745     HTMLDOMNode *node;
746     int i;
747     nsresult nsres;
748     HRESULT hres;
749 
750     iter = elem->node.nsnode;
751     nsIDOMNode_AddRef(iter);
752 
753     /* Find document or document fragment parent. */
754     while(1) {
755         nsres = nsIDOMNode_GetParentNode(iter, &parent_node);
756         nsIDOMNode_Release(iter);
757         assert(nsres == NS_OK);
758         if(!parent_node)
759             break;
760 
761         nsres = nsIDOMNode_GetNodeType(parent_node, &parent_type);
762         assert(nsres == NS_OK);
763 
764         if(parent_type != ELEMENT_NODE) {
765             if(parent_type != DOCUMENT_NODE && parent_type != DOCUMENT_FRAGMENT_NODE)
766                 FIXME("Unexpected parent_type %d\n", parent_type);
767             break;
768         }
769 
770         iter = parent_node;
771     }
772 
773     if(!parent_node) {
774         *ret = -1;
775         return S_OK;
776     }
777 
778     hres = get_node(elem->node.doc, parent_node, TRUE, &node);
779     nsIDOMNode_Release(parent_node);
780     if(FAILED(hres))
781         return hres;
782 
783 
784     /* Create all children collection and find the element in it.
785      * This could be optimized if we ever find the reason. */
786     buf.buf = heap_alloc(buf.size*sizeof(*buf.buf));
787     if(!buf.buf) {
788         IHTMLDOMNode_Release(&node->IHTMLDOMNode_iface);
789         return E_OUTOFMEMORY;
790     }
791 
792     create_all_list(elem->node.doc, node, &buf);
793 
794     for(i=0; i < buf.len; i++) {
795         if(buf.buf[i] == elem)
796             break;
797     }
798     IHTMLDOMNode_Release(&node->IHTMLDOMNode_iface);
799     heap_free(buf.buf);
800     if(i == buf.len) {
801         FIXME("The element is not in parent's child list?\n");
802         return E_UNEXPECTED;
803     }
804 
805     *ret = i;
806     return S_OK;
807 }
808 
HTMLElementCollection_Create(HTMLElement ** elems,DWORD len)809 static IHTMLElementCollection *HTMLElementCollection_Create(HTMLElement **elems, DWORD len)
810 {
811     HTMLElementCollection *ret = heap_alloc_zero(sizeof(HTMLElementCollection));
812 
813     if (!ret)
814         return NULL;
815 
816     ret->IHTMLElementCollection_iface.lpVtbl = &HTMLElementCollectionVtbl;
817     ret->ref = 1;
818     ret->elems = elems;
819     ret->len = len;
820 
821     init_dispex(&ret->dispex, (IUnknown*)&ret->IHTMLElementCollection_iface,
822             &HTMLElementCollection_dispex);
823 
824     TRACE("ret=%p len=%d\n", ret, len);
825 
826     return &ret->IHTMLElementCollection_iface;
827 }
828