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