msxml3: Add support for accessing document node.
[wine] / dlls / msxml3 / xmlelem.c
1 /*
2  * XML Element implementation
3  *
4  * Copyright 2007 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include "config.h"
24
25 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "ole2.h"
30 #include "msxml2.h"
31 #include "ocidl.h"
32
33 #include "wine/debug.h"
34
35 #include "msxml_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
38
39 #ifdef HAVE_LIBXML2
40
41 /**********************************************************************
42  * IXMLElement
43  */
44 typedef struct _xmlelem
45 {
46     const IXMLElementVtbl *lpVtbl;
47     LONG ref;
48     xmlNodePtr node;
49 } xmlelem;
50
51 static inline xmlelem *impl_from_IXMLElement(IXMLElement *iface)
52 {
53     return (xmlelem *)((char*)iface - FIELD_OFFSET(xmlelem, lpVtbl));
54 }
55
56 static HRESULT WINAPI xmlelem_QueryInterface(IXMLElement *iface, REFIID riid, void** ppvObject)
57 {
58     xmlelem *This = impl_from_IXMLElement(iface);
59
60     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
61
62     if (IsEqualGUID(riid, &IID_IUnknown) ||
63         IsEqualGUID(riid, &IID_IXMLElement))
64     {
65         *ppvObject = iface;
66     }
67     else
68     {
69         FIXME("interface %s not implemented\n", debugstr_guid(riid));
70         return E_NOINTERFACE;
71     }
72
73     IXMLElement_AddRef(iface);
74
75     return S_OK;
76 }
77
78 static ULONG WINAPI xmlelem_AddRef(IXMLElement *iface)
79 {
80     xmlelem *This = impl_from_IXMLElement(iface);
81     TRACE("%p\n", This);
82     return InterlockedIncrement(&This->ref);
83 }
84
85 static ULONG WINAPI xmlelem_Release(IXMLElement *iface)
86 {
87     xmlelem *This = impl_from_IXMLElement(iface);
88     LONG ref;
89
90     TRACE("%p\n", This);
91
92     ref = InterlockedDecrement(&This->ref);
93     if (ref == 0)
94     {
95         HeapFree(GetProcessHeap(), 0, This);
96     }
97
98     return ref;
99 }
100
101 static HRESULT WINAPI xmlelem_GetTypeInfoCount(IXMLElement *iface, UINT* pctinfo)
102 {
103     xmlelem *This = impl_from_IXMLElement(iface);
104
105     TRACE("(%p)->(%p)\n", This, pctinfo);
106
107     *pctinfo = 1;
108
109     return S_OK;
110 }
111
112 static HRESULT WINAPI xmlelem_GetTypeInfo(IXMLElement *iface, UINT iTInfo,
113                                           LCID lcid, ITypeInfo** ppTInfo)
114 {
115     xmlelem *This = impl_from_IXMLElement(iface);
116     HRESULT hr;
117
118     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
119
120     hr = get_typeinfo(IXMLElement_tid, ppTInfo);
121
122     return hr;
123 }
124
125 static HRESULT WINAPI xmlelem_GetIDsOfNames(IXMLElement *iface, REFIID riid,
126                                             LPOLESTR* rgszNames, UINT cNames,
127                                             LCID lcid, DISPID* rgDispId)
128 {
129     xmlelem *This = impl_from_IXMLElement(iface);
130     ITypeInfo *typeinfo;
131     HRESULT hr;
132
133     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
134           lcid, rgDispId);
135
136     if(!rgszNames || cNames == 0 || !rgDispId)
137         return E_INVALIDARG;
138
139     hr = get_typeinfo(IXMLElement_tid, &typeinfo);
140     if(SUCCEEDED(hr))
141     {
142         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
143         ITypeInfo_Release(typeinfo);
144     }
145
146     return hr;
147 }
148
149 static HRESULT WINAPI xmlelem_Invoke(IXMLElement *iface, DISPID dispIdMember,
150                                      REFIID riid, LCID lcid, WORD wFlags,
151                                      DISPPARAMS* pDispParams, VARIANT* pVarResult,
152                                      EXCEPINFO* pExcepInfo, UINT* puArgErr)
153 {
154     xmlelem *This = impl_from_IXMLElement(iface);
155     ITypeInfo *typeinfo;
156     HRESULT hr;
157
158     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
159           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
160
161     hr = get_typeinfo(IXMLElement_tid, &typeinfo);
162     if(SUCCEEDED(hr))
163     {
164         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
165                 pVarResult, pExcepInfo, puArgErr);
166         ITypeInfo_Release(typeinfo);
167     }
168
169     return hr;
170 }
171
172 static inline BSTR str_dup_upper(BSTR str)
173 {
174     INT len = (lstrlenW(str) + 1) * sizeof(WCHAR);
175     BSTR p = SysAllocStringLen(NULL, len);
176     if (p)
177     {
178         memcpy(p, str, len);
179         CharUpperW(p);
180     }
181     return p;
182 }
183
184 static HRESULT WINAPI xmlelem_get_tagName(IXMLElement *iface, BSTR *p)
185 {
186     xmlelem *This = impl_from_IXMLElement(iface);
187     BSTR temp;
188
189     TRACE("(%p, %p)\n", iface, p);
190
191     if (!p)
192         return E_INVALIDARG;
193
194     temp = bstr_from_xmlChar(This->node->name);
195     *p = str_dup_upper(temp);
196     SysFreeString(temp);
197
198     TRACE("returning %s\n", debugstr_w(*p));
199
200     return S_OK;
201 }
202
203 static HRESULT WINAPI xmlelem_put_tagName(IXMLElement *iface, BSTR p)
204 {
205     FIXME("(%p, %p): stub\n", iface, p);
206
207     if (!p)
208         return E_INVALIDARG;
209
210     return E_NOTIMPL;
211 }
212
213 static HRESULT WINAPI xmlelem_get_parent(IXMLElement *iface, IXMLElement **parent)
214 {
215     xmlelem *This = impl_from_IXMLElement(iface);
216
217     TRACE("(%p, %p)\n", iface, parent);
218
219     if (!parent)
220         return E_INVALIDARG;
221
222     *parent = NULL;
223
224     if (!This->node->parent)
225         return S_FALSE;
226
227     return XMLElement_create((IUnknown *)iface, This->node->parent, (LPVOID *)parent);
228 }
229
230 static HRESULT WINAPI xmlelem_setAttribute(IXMLElement *iface, BSTR strPropertyName,
231                                             VARIANT PropertyValue)
232 {
233     xmlelem *This = impl_from_IXMLElement(iface);
234     xmlChar *name, *value;
235     xmlAttrPtr attr;
236
237     TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName));
238
239     if (!strPropertyName || V_VT(&PropertyValue) != VT_BSTR)
240         return E_INVALIDARG;
241
242     name = xmlChar_from_wchar(strPropertyName);
243     value = xmlChar_from_wchar(V_BSTR(&PropertyValue));
244     attr = xmlSetProp(This->node, name, value);
245
246     HeapFree(GetProcessHeap(), 0, name);
247     HeapFree(GetProcessHeap(), 0, value);
248     return (attr) ? S_OK : S_FALSE;
249 }
250
251 static HRESULT WINAPI xmlelem_getAttribute(IXMLElement *iface, BSTR strPropertyName,
252                                            VARIANT *PropertyValue)
253 {
254     xmlelem *This = impl_from_IXMLElement(iface);
255     xmlChar *val = NULL, *name;
256     xmlAttrPtr ptr;
257
258     TRACE("(%p, %s, %p)\n", iface, debugstr_w(strPropertyName), PropertyValue);
259
260     if (!PropertyValue)
261         return E_INVALIDARG;
262
263     VariantInit(PropertyValue);
264     V_BSTR(PropertyValue) = NULL;
265
266     if (!strPropertyName)
267         return E_INVALIDARG;
268
269     name = xmlChar_from_wchar(strPropertyName);
270     ptr = This->node->properties;
271     while (ptr)
272     {
273         if (!lstrcmpiA((LPSTR)name, (LPCSTR)ptr->name))
274         {
275             val = xmlNodeListGetString(ptr->doc, ptr->children, 1);
276             break;
277         }
278
279         ptr = ptr->next;
280     }
281
282     if (val)
283     {
284         V_VT(PropertyValue) = VT_BSTR;
285         V_BSTR(PropertyValue) = bstr_from_xmlChar(val);
286     }
287
288     HeapFree(GetProcessHeap(), 0, name);
289     xmlFree(val);
290     TRACE("returning %s\n", debugstr_w(V_BSTR(PropertyValue)));
291     return (val) ? S_OK : S_FALSE;
292 }
293
294 static HRESULT WINAPI xmlelem_removeAttribute(IXMLElement *iface, BSTR strPropertyName)
295 {
296     xmlelem *This = impl_from_IXMLElement(iface);
297     xmlChar *name;
298     xmlAttrPtr attr;
299     int res;
300     HRESULT hr = S_FALSE;
301
302     TRACE("(%p, %s)\n", iface, debugstr_w(strPropertyName));
303
304     if (!strPropertyName)
305         return E_INVALIDARG;
306
307     name = xmlChar_from_wchar(strPropertyName);
308     attr = xmlHasProp(This->node, name);
309     if (!attr)
310         goto done;
311
312     res = xmlRemoveProp(attr);
313
314     if (res == 0)
315         hr = S_OK;
316
317 done:
318     HeapFree(GetProcessHeap(), 0, name);
319     return hr;
320 }
321
322 static HRESULT WINAPI xmlelem_get_children(IXMLElement *iface, IXMLElementCollection **p)
323 {
324     xmlelem *This = impl_from_IXMLElement(iface);
325
326     TRACE("(%p, %p)\n", iface, p);
327
328     if (!p)
329         return E_INVALIDARG;
330
331     return XMLElementCollection_create((IUnknown *)iface, This->node->children, (LPVOID *)p);
332 }
333
334 static long type_libxml_to_msxml(xmlElementType type)
335 {
336     switch (type)
337     {
338         case XML_ELEMENT_NODE:
339             return XMLELEMTYPE_ELEMENT;
340         case XML_TEXT_NODE:
341             return XMLELEMTYPE_TEXT;
342         case XML_COMMENT_NODE:
343             return XMLELEMTYPE_COMMENT;
344         case XML_DOCUMENT_NODE:
345             return XMLELEMTYPE_DOCUMENT;
346         case XML_DTD_NODE:
347             return XMLELEMTYPE_DTD;
348         case XML_PI_NODE:
349             return XMLELEMTYPE_PI;
350         default:
351             break;
352     }
353
354     return XMLELEMTYPE_OTHER;
355 }
356
357 static HRESULT WINAPI xmlelem_get_type(IXMLElement *iface, long *p)
358 {
359     xmlelem *This = impl_from_IXMLElement(iface);
360
361     TRACE("(%p, %p)\n", This, p);
362
363     if (!p)
364         return E_INVALIDARG;
365
366     *p = type_libxml_to_msxml(This->node->type);
367     TRACE("returning %ld\n", *p);
368     return S_OK;
369 }
370
371 static HRESULT WINAPI xmlelem_get_text(IXMLElement *iface, BSTR *p)
372 {
373     xmlelem *This = impl_from_IXMLElement(iface);
374     xmlChar *content;
375
376     TRACE("(%p, %p)\n", iface, p);
377
378     if (!p)
379         return E_INVALIDARG;
380
381     content = xmlNodeGetContent(This->node);
382     *p = bstr_from_xmlChar(content);
383     TRACE("returning %s\n", debugstr_w(*p));
384
385     xmlFree(content);
386     return S_OK;
387 }
388
389 static HRESULT WINAPI xmlelem_put_text(IXMLElement *iface, BSTR p)
390 {
391     xmlelem *This = impl_from_IXMLElement(iface);
392     xmlChar *content;
393
394     TRACE("(%p, %s)\n", iface, debugstr_w(p));
395
396     /* FIXME: test which types can be used */
397     if (This->node->type == XML_ELEMENT_NODE)
398         return E_NOTIMPL;
399
400     content = xmlChar_from_wchar(p);
401     xmlNodeSetContent(This->node, content);
402
403     HeapFree( GetProcessHeap(), 0, content);
404
405     return S_OK;
406 }
407
408 static HRESULT WINAPI xmlelem_addChild(IXMLElement *iface, IXMLElement *pChildElem,
409                                         long lIndex, long lreserved)
410 {
411     xmlelem *This = impl_from_IXMLElement(iface);
412     xmlelem *childElem = impl_from_IXMLElement(pChildElem);
413     xmlNodePtr child;
414
415     TRACE("(%p, %p, %ld, %ld)\n", iface, pChildElem, lIndex, lreserved);
416
417     if (lIndex == 0)
418         child = xmlAddChild(This->node, childElem->node);
419     else
420         child = xmlAddNextSibling(This->node, childElem->node->last);
421
422     return (child) ? S_OK : S_FALSE;
423 }
424
425 static HRESULT WINAPI xmlelem_removeChild(IXMLElement *iface, IXMLElement *pChildElem)
426 {
427     FIXME("(%p, %p): stub\n", iface, pChildElem);
428     return E_NOTIMPL;
429 }
430
431 static const struct IXMLElementVtbl xmlelem_vtbl =
432 {
433     xmlelem_QueryInterface,
434     xmlelem_AddRef,
435     xmlelem_Release,
436     xmlelem_GetTypeInfoCount,
437     xmlelem_GetTypeInfo,
438     xmlelem_GetIDsOfNames,
439     xmlelem_Invoke,
440     xmlelem_get_tagName,
441     xmlelem_put_tagName,
442     xmlelem_get_parent,
443     xmlelem_setAttribute,
444     xmlelem_getAttribute,
445     xmlelem_removeAttribute,
446     xmlelem_get_children,
447     xmlelem_get_type,
448     xmlelem_get_text,
449     xmlelem_put_text,
450     xmlelem_addChild,
451     xmlelem_removeChild
452 };
453
454 HRESULT XMLElement_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj)
455 {
456     xmlelem *elem;
457
458     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
459
460     if (!ppObj)
461         return E_INVALIDARG;
462
463     *ppObj = NULL;
464
465     elem = HeapAlloc(GetProcessHeap(), 0, sizeof (*elem));
466     if(!elem)
467         return E_OUTOFMEMORY;
468
469     elem->lpVtbl = &xmlelem_vtbl;
470     elem->ref = 1;
471     elem->node = node;
472
473     *ppObj = &elem->lpVtbl;
474
475     TRACE("returning iface %p\n", *ppObj);
476     return S_OK;
477 }
478
479 /************************************************************************
480  * IXMLElementCollection
481  */
482 typedef struct _xmlelem_collection
483 {
484     const IXMLElementCollectionVtbl *lpVtbl;
485     const IEnumVARIANTVtbl          *lpvtblIEnumVARIANT;
486     LONG ref;
487     LONG length;
488     xmlNodePtr node;
489
490     /* IEnumVARIANT members */
491     xmlNodePtr current;
492 } xmlelem_collection;
493
494 static inline xmlelem_collection *impl_from_IXMLElementCollection(IXMLElementCollection *iface)
495 {
496     return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpVtbl));
497 }
498
499 static inline xmlelem_collection *impl_from_IEnumVARIANT(IEnumVARIANT *iface)
500 {
501     return (xmlelem_collection *)((char*)iface - FIELD_OFFSET(xmlelem_collection, lpvtblIEnumVARIANT));
502 }
503
504 static HRESULT WINAPI xmlelem_collection_QueryInterface(IXMLElementCollection *iface, REFIID riid, void** ppvObject)
505 {
506     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
507
508     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
509
510     if (IsEqualGUID(riid, &IID_IUnknown) ||
511         IsEqualGUID(riid, &IID_IXMLElementCollection))
512     {
513         *ppvObject = iface;
514     }
515     else if (IsEqualGUID(riid, &IID_IEnumVARIANT))
516     {
517         *ppvObject = (IEnumVARIANT *)&(This->lpvtblIEnumVARIANT);
518     }
519     else
520     {
521         FIXME("interface %s not implemented\n", debugstr_guid(riid));
522         return E_NOINTERFACE;
523     }
524
525     IXMLElementCollection_AddRef(iface);
526
527     return S_OK;
528 }
529
530 static ULONG WINAPI xmlelem_collection_AddRef(IXMLElementCollection *iface)
531 {
532     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
533     TRACE("%p\n", This);
534     return InterlockedIncrement(&This->ref);
535 }
536
537 static ULONG WINAPI xmlelem_collection_Release(IXMLElementCollection *iface)
538 {
539     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
540     LONG ref;
541
542     TRACE("%p\n", This);
543
544     ref = InterlockedDecrement(&This->ref);
545     if (ref == 0)
546     {
547         HeapFree(GetProcessHeap(), 0, This);
548     }
549
550     return ref;
551 }
552
553 static HRESULT WINAPI xmlelem_collection_GetTypeInfoCount(IXMLElementCollection *iface, UINT* pctinfo)
554 {
555     FIXME("\n");
556     return E_NOTIMPL;
557 }
558
559 static HRESULT WINAPI xmlelem_collection_GetTypeInfo(IXMLElementCollection *iface, UINT iTInfo,
560                                                      LCID lcid, ITypeInfo** ppTInfo)
561 {
562     FIXME("\n");
563     return E_NOTIMPL;
564 }
565
566 static HRESULT WINAPI xmlelem_collection_GetIDsOfNames(IXMLElementCollection *iface, REFIID riid,
567                                                        LPOLESTR* rgszNames, UINT cNames,
568                                                        LCID lcid, DISPID* rgDispId)
569 {
570     FIXME("\n");
571     return E_NOTIMPL;
572 }
573
574 static HRESULT WINAPI xmlelem_collection_Invoke(IXMLElementCollection *iface, DISPID dispIdMember,
575                                                 REFIID riid, LCID lcid, WORD wFlags,
576                                                 DISPPARAMS* pDispParams, VARIANT* pVarResult,
577                                                 EXCEPINFO* pExcepInfo, UINT* puArgErr)
578 {
579     FIXME("\n");
580     return E_NOTIMPL;
581 }
582
583 static HRESULT WINAPI xmlelem_collection_put_length(IXMLElementCollection *iface, long v)
584 {
585     TRACE("(%p, %ld)\n", iface, v);
586     return E_FAIL;
587 }
588
589 static HRESULT WINAPI xmlelem_collection_get_length(IXMLElementCollection *iface, long *p)
590 {
591     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
592
593     TRACE("(%p, %p)\n", iface, p);
594
595     if (!p)
596         return E_INVALIDARG;
597
598     *p = This->length;
599     return S_OK;
600 }
601
602 static HRESULT WINAPI xmlelem_collection_get__newEnum(IXMLElementCollection *iface, IUnknown **ppUnk)
603 {
604     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
605
606     TRACE("(%p, %p)\n", iface, ppUnk);
607
608     if (!ppUnk)
609         return E_INVALIDARG;
610
611     *ppUnk = (IUnknown *)This;
612     IUnknown_AddRef(*ppUnk);
613     return S_OK;
614 }
615
616 static HRESULT WINAPI xmlelem_collection_item(IXMLElementCollection *iface, VARIANT var1,
617                                               VARIANT var2, IDispatch **ppDisp)
618 {
619     xmlelem_collection *This = impl_from_IXMLElementCollection(iface);
620     xmlNodePtr ptr = This->node;
621     int index, i;
622
623     TRACE("(%p, %p)\n", iface, ppDisp);
624
625     if (!ppDisp)
626         return E_INVALIDARG;
627
628     *ppDisp = NULL;
629
630     index = V_I4(&var1);
631     if (index < 0)
632         return E_INVALIDARG;
633     if (index >= This->length)
634         return E_FAIL;
635
636     for (i = 0; i < index; i++)
637         ptr = ptr->next;
638
639     return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)ppDisp);
640 }
641
642 static const struct IXMLElementCollectionVtbl xmlelem_collection_vtbl =
643 {
644     xmlelem_collection_QueryInterface,
645     xmlelem_collection_AddRef,
646     xmlelem_collection_Release,
647     xmlelem_collection_GetTypeInfoCount,
648     xmlelem_collection_GetTypeInfo,
649     xmlelem_collection_GetIDsOfNames,
650     xmlelem_collection_Invoke,
651     xmlelem_collection_put_length,
652     xmlelem_collection_get_length,
653     xmlelem_collection_get__newEnum,
654     xmlelem_collection_item
655 };
656
657 /************************************************************************
658  * xmlelem_collection implementation of IEnumVARIANT.
659  */
660 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_QueryInterface(
661     IEnumVARIANT *iface, REFIID riid, LPVOID *ppvObj)
662 {
663     xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
664     return IXMLDocument_QueryInterface((IXMLDocument *)this, riid, ppvObj);
665 }
666
667 static ULONG WINAPI xmlelem_collection_IEnumVARIANT_AddRef(
668     IEnumVARIANT *iface)
669 {
670     xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
671     return IXMLDocument_AddRef((IXMLDocument *)this);
672 }
673
674 static ULONG WINAPI xmlelem_collection_IEnumVARIANT_Release(
675     IEnumVARIANT *iface)
676 {
677     xmlelem_collection *this = impl_from_IEnumVARIANT(iface);
678     return IXMLDocument_Release((IXMLDocument *)this);
679 }
680
681 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Next(
682     IEnumVARIANT *iface, ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
683 {
684     xmlelem_collection *This = impl_from_IEnumVARIANT(iface);
685     xmlNodePtr ptr = This->current;
686
687     TRACE("(%p, %d, %p, %p)\n", iface, celt, rgVar, pCeltFetched);
688
689     if (!rgVar)
690         return E_INVALIDARG;
691
692     /* FIXME: handle celt */
693     if (pCeltFetched)
694         *pCeltFetched = 1;
695
696     This->current = This->current->next;
697
698     V_VT(rgVar) = VT_DISPATCH;
699     return XMLElement_create((IUnknown *)iface, ptr, (LPVOID *)&V_DISPATCH(rgVar));
700 }
701
702 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Skip(
703     IEnumVARIANT *iface, ULONG celt)
704 {
705     FIXME("(%p, %d): stub\n", iface, celt);
706     return E_NOTIMPL;
707 }
708
709 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Reset(
710     IEnumVARIANT *iface)
711 {
712     xmlelem_collection *This = impl_from_IEnumVARIANT(iface);
713     This->current = This->node;
714     return S_OK;
715 }
716
717 static HRESULT WINAPI xmlelem_collection_IEnumVARIANT_Clone(
718     IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
719 {
720     FIXME("(%p, %p): stub\n", iface, ppEnum);
721     return E_NOTIMPL;
722 }
723
724 static const struct IEnumVARIANTVtbl xmlelem_collection_IEnumVARIANTvtbl =
725 {
726     xmlelem_collection_IEnumVARIANT_QueryInterface,
727     xmlelem_collection_IEnumVARIANT_AddRef,
728     xmlelem_collection_IEnumVARIANT_Release,
729     xmlelem_collection_IEnumVARIANT_Next,
730     xmlelem_collection_IEnumVARIANT_Skip,
731     xmlelem_collection_IEnumVARIANT_Reset,
732     xmlelem_collection_IEnumVARIANT_Clone
733 };
734
735 HRESULT XMLElementCollection_create(IUnknown *pUnkOuter, xmlNodePtr node, LPVOID *ppObj)
736 {
737     xmlelem_collection *collection;
738     xmlNodePtr ptr;
739
740     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
741
742     *ppObj = NULL;
743
744     if (!node)
745         return S_FALSE;
746
747     collection = HeapAlloc(GetProcessHeap(), 0, sizeof (*collection));
748     if(!collection)
749         return E_OUTOFMEMORY;
750
751     collection->lpVtbl = &xmlelem_collection_vtbl;
752     collection->lpvtblIEnumVARIANT = &xmlelem_collection_IEnumVARIANTvtbl;
753     collection->ref = 1;
754     collection->length = 0;
755     collection->node = node;
756     collection->current = node;
757
758     ptr = node;
759     while (ptr)
760     {
761         collection->length++;
762         ptr = ptr->next;
763     }
764
765     *ppObj = &collection->lpVtbl;
766
767     TRACE("returning iface %p\n", *ppObj);
768     return S_OK;
769 }
770
771 #endif