msxml3: Fix some test failures on Windows 8.
[wine] / dlls / msxml3 / nodelist.c
1 /*
2  *    Node list implementation
3  *
4  * Copyright 2005 Mike McCormack
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 "msxml2did.h"
37
38 #include "msxml_private.h"
39
40 #include "wine/debug.h"
41
42 /* This file implements the object returned by childNodes property. Note that this is
43  * not the IXMLDOMNodeList returned by XPath queries - it's implemented in selection.c.
44  * They are different because the list returned by childNodes:
45  *  - is "live" - changes to the XML tree are automatically reflected in the list
46  *  - doesn't supports IXMLDOMSelection
47  *  - note that an attribute node have a text child in DOM but not in the XPath data model
48  *    thus the child is inaccessible by an XPath query
49  */
50
51 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
52
53 #ifdef HAVE_LIBXML2
54
55 typedef struct
56 {
57     DispatchEx dispex;
58     IXMLDOMNodeList IXMLDOMNodeList_iface;
59     LONG ref;
60     xmlNodePtr parent;
61     xmlNodePtr current;
62     IEnumVARIANT *enumvariant;
63 } xmlnodelist;
64
65 static HRESULT nodelist_get_item(IUnknown *iface, LONG index, VARIANT *item)
66 {
67     V_VT(item) = VT_DISPATCH;
68     return IXMLDOMNodeList_get_item((IXMLDOMNodeList*)iface, index, (IXMLDOMNode**)&V_DISPATCH(item));
69 }
70
71 static const struct enumvariant_funcs nodelist_enumvariant = {
72     nodelist_get_item,
73     NULL
74 };
75
76 static inline xmlnodelist *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
77 {
78     return CONTAINING_RECORD(iface, xmlnodelist, IXMLDOMNodeList_iface);
79 }
80
81 static HRESULT WINAPI xmlnodelist_QueryInterface(
82     IXMLDOMNodeList *iface,
83     REFIID riid,
84     void** ppvObject )
85 {
86     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
87
88     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
89
90     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
91          IsEqualGUID( riid, &IID_IDispatch ) ||
92          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) )
93     {
94         *ppvObject = iface;
95     }
96     else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
97     {
98         if (!This->enumvariant)
99         {
100             HRESULT hr = create_enumvariant((IUnknown*)iface, FALSE, &nodelist_enumvariant, &This->enumvariant);
101             if (FAILED(hr)) return hr;
102         }
103
104         return IEnumVARIANT_QueryInterface(This->enumvariant, &IID_IEnumVARIANT, ppvObject);
105     }
106     else if (dispex_query_interface(&This->dispex, riid, ppvObject))
107     {
108         return *ppvObject ? S_OK : E_NOINTERFACE;
109     }
110     else
111     {
112         TRACE("interface %s not implemented\n", debugstr_guid(riid));
113         *ppvObject = NULL;
114         return E_NOINTERFACE;
115     }
116
117     IXMLDOMNodeList_AddRef( iface );
118
119     return S_OK;
120 }
121
122 static ULONG WINAPI xmlnodelist_AddRef(
123     IXMLDOMNodeList *iface )
124 {
125     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
126     ULONG ref = InterlockedIncrement( &This->ref );
127     TRACE("(%p)->(%d)\n", This, ref);
128     return ref;
129 }
130
131 static ULONG WINAPI xmlnodelist_Release(
132     IXMLDOMNodeList *iface )
133 {
134     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
135     ULONG ref = InterlockedDecrement( &This->ref );
136
137     TRACE("(%p)->(%d)\n", This, ref);
138     if ( ref == 0 )
139     {
140         xmldoc_release( This->parent->doc );
141         if (This->enumvariant) IEnumVARIANT_Release(This->enumvariant);
142         heap_free( This );
143     }
144
145     return ref;
146 }
147
148 static HRESULT WINAPI xmlnodelist_GetTypeInfoCount(
149     IXMLDOMNodeList *iface,
150     UINT* pctinfo )
151 {
152     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
153     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
154 }
155
156 static HRESULT WINAPI xmlnodelist_GetTypeInfo(
157     IXMLDOMNodeList *iface,
158     UINT iTInfo,
159     LCID lcid,
160     ITypeInfo** ppTInfo )
161 {
162     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
163     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
164         iTInfo, lcid, ppTInfo);
165 }
166
167 static HRESULT WINAPI xmlnodelist_GetIDsOfNames(
168     IXMLDOMNodeList *iface,
169     REFIID riid,
170     LPOLESTR* rgszNames,
171     UINT cNames,
172     LCID lcid,
173     DISPID* rgDispId )
174 {
175     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
176     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
177         riid, rgszNames, cNames, lcid, rgDispId);
178 }
179
180 static HRESULT WINAPI xmlnodelist_Invoke(
181     IXMLDOMNodeList *iface,
182     DISPID dispIdMember,
183     REFIID riid,
184     LCID lcid,
185     WORD wFlags,
186     DISPPARAMS* pDispParams,
187     VARIANT* pVarResult,
188     EXCEPINFO* pExcepInfo,
189     UINT* puArgErr )
190 {
191     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
192     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
193         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
194 }
195
196 static HRESULT WINAPI xmlnodelist_get_item(
197         IXMLDOMNodeList* iface,
198         LONG index,
199         IXMLDOMNode** listItem)
200 {
201     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
202     xmlNodePtr curr;
203     LONG nodeIndex = 0;
204
205     TRACE("(%p)->(%d %p)\n", This, index, listItem);
206
207     if(!listItem)
208         return E_INVALIDARG;
209
210     *listItem = NULL;
211
212     if (index < 0)
213         return S_FALSE;
214
215     curr = This->parent->children;
216     while(curr)
217     {
218         if(nodeIndex++ == index) break;
219         curr = curr->next;
220     }
221     if(!curr) return S_FALSE;
222
223     *listItem = create_node( curr );
224
225     return S_OK;
226 }
227
228 static HRESULT WINAPI xmlnodelist_get_length(
229         IXMLDOMNodeList* iface,
230         LONG* listLength)
231 {
232
233     xmlNodePtr curr;
234     LONG nodeCount = 0;
235
236     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
237
238     TRACE("(%p)->(%p)\n", This, listLength);
239
240     if(!listLength)
241         return E_INVALIDARG;
242
243     curr = This->parent->children;
244     while (curr)
245     {
246         nodeCount++;
247         curr = curr->next;
248     }
249
250     *listLength = nodeCount;
251     return S_OK;
252 }
253
254 static HRESULT WINAPI xmlnodelist_nextNode(
255         IXMLDOMNodeList* iface,
256         IXMLDOMNode** nextItem)
257 {
258     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
259
260     TRACE("(%p)->(%p)\n", This, nextItem );
261
262     if(!nextItem)
263         return E_INVALIDARG;
264
265     *nextItem = NULL;
266
267     if (!This->current)
268         return S_FALSE;
269
270     *nextItem = create_node( This->current );
271     This->current = This->current->next;
272     return S_OK;
273 }
274
275 static HRESULT WINAPI xmlnodelist_reset(
276         IXMLDOMNodeList* iface)
277 {
278     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
279
280     TRACE("%p\n", This);
281     This->current = This->parent->children;
282     return S_OK;
283 }
284
285 static HRESULT WINAPI xmlnodelist__newEnum(
286         IXMLDOMNodeList* iface,
287         IUnknown** enumv)
288 {
289     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
290     TRACE("(%p)->(%p)\n", This, enumv);
291     return create_enumvariant((IUnknown*)iface, TRUE, &nodelist_enumvariant, (IEnumVARIANT**)enumv);
292 }
293
294 static const struct IXMLDOMNodeListVtbl xmlnodelist_vtbl =
295 {
296     xmlnodelist_QueryInterface,
297     xmlnodelist_AddRef,
298     xmlnodelist_Release,
299     xmlnodelist_GetTypeInfoCount,
300     xmlnodelist_GetTypeInfo,
301     xmlnodelist_GetIDsOfNames,
302     xmlnodelist_Invoke,
303     xmlnodelist_get_item,
304     xmlnodelist_get_length,
305     xmlnodelist_nextNode,
306     xmlnodelist_reset,
307     xmlnodelist__newEnum,
308 };
309
310 static HRESULT xmlnodelist_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
311 {
312     WCHAR *ptr;
313     int idx = 0;
314
315     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
316         idx = idx*10 + (*ptr-'0');
317     if(*ptr)
318         return DISP_E_UNKNOWNNAME;
319
320     *dispid = DISPID_DOM_COLLECTION_BASE + idx;
321     TRACE("ret %x\n", *dispid);
322     return S_OK;
323 }
324
325 static HRESULT xmlnodelist_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
326         VARIANT *res, EXCEPINFO *ei)
327 {
328     xmlnodelist *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
329
330     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
331
332     if (id >= DISPID_DOM_COLLECTION_BASE && id <= DISPID_DOM_COLLECTION_MAX)
333     {
334         switch(flags)
335         {
336             case DISPATCH_PROPERTYGET:
337             {
338                 IXMLDOMNode *disp = NULL;
339
340                 V_VT(res) = VT_DISPATCH;
341                 IXMLDOMNodeList_get_item(&This->IXMLDOMNodeList_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
342                 V_DISPATCH(res) = (IDispatch*)disp;
343                 break;
344             }
345             default:
346             {
347                 FIXME("unimplemented flags %x\n", flags);
348                 break;
349             }
350         }
351     }
352     else if (id == DISPID_VALUE)
353     {
354         switch(flags)
355         {
356             case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
357             case DISPATCH_PROPERTYGET:
358             case DISPATCH_METHOD:
359             {
360                 IXMLDOMNode *item;
361                 VARIANT index;
362                 HRESULT hr;
363
364                 if (params->cArgs - params->cNamedArgs != 1) return DISP_E_BADPARAMCOUNT;
365
366                 VariantInit(&index);
367                 hr = VariantChangeType(&index, params->rgvarg, 0, VT_I4);
368                 if(FAILED(hr))
369                 {
370                     FIXME("failed to convert arg, %s\n", debugstr_variant(params->rgvarg));
371                     return hr;
372                 }
373
374                 IXMLDOMNodeList_get_item(&This->IXMLDOMNodeList_iface, V_I4(&index), &item);
375                 V_VT(res) = VT_DISPATCH;
376                 V_DISPATCH(res) = (IDispatch*)item;
377                 break;
378             }
379             default:
380             {
381                 FIXME("DISPID_VALUE: unimplemented flags %x\n", flags);
382                 break;
383             }
384         }
385     }
386     else
387         return DISP_E_UNKNOWNNAME;
388
389     TRACE("ret %p\n", V_DISPATCH(res));
390
391     return S_OK;
392 }
393
394 static const dispex_static_data_vtbl_t xmlnodelist_dispex_vtbl = {
395     xmlnodelist_get_dispid,
396     xmlnodelist_invoke
397 };
398
399 static const tid_t xmlnodelist_iface_tids[] = {
400     IXMLDOMNodeList_tid,
401     0
402 };
403 static dispex_static_data_t xmlnodelist_dispex = {
404     &xmlnodelist_dispex_vtbl,
405     IXMLDOMNodeList_tid,
406     NULL,
407     xmlnodelist_iface_tids
408 };
409
410 IXMLDOMNodeList* create_children_nodelist( xmlNodePtr node )
411 {
412     xmlnodelist *This;
413
414     This = heap_alloc( sizeof *This );
415     if ( !This )
416         return NULL;
417
418     This->IXMLDOMNodeList_iface.lpVtbl = &xmlnodelist_vtbl;
419     This->ref = 1;
420     This->parent = node;
421     This->current = node->children;
422     This->enumvariant = NULL;
423     xmldoc_add_ref( node->doc );
424
425     init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMNodeList_iface, &xmlnodelist_dispex);
426
427     return &This->IXMLDOMNodeList_iface;
428 }
429
430 #endif