xref: /reactos/dll/win32/msxml3/nodelist.c (revision 1734f297)
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 #ifdef HAVE_LIBXML2
52 
53 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
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 #ifdef __REACTOS__
91     if (!ppvObject)
92     {
93         /* NOTE: Interface documentation for IUnknown explicitly states
94          * this case should return E_POINTER. Empirical data proves
95          * MS violates this contract and instead return E_INVALIDARG.
96          */
97         return E_INVALIDARG;
98     }
99 #endif
100 
101     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
102          IsEqualGUID( riid, &IID_IDispatch ) ||
103          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) )
104     {
105         *ppvObject = iface;
106     }
107     else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
108     {
109         if (!This->enumvariant)
110         {
111             HRESULT hr = create_enumvariant((IUnknown*)iface, FALSE, &nodelist_enumvariant, &This->enumvariant);
112             if (FAILED(hr)) return hr;
113         }
114 
115         return IEnumVARIANT_QueryInterface(This->enumvariant, &IID_IEnumVARIANT, ppvObject);
116     }
117     else if (dispex_query_interface(&This->dispex, riid, ppvObject))
118     {
119         return *ppvObject ? S_OK : E_NOINTERFACE;
120     }
121     else
122     {
123         TRACE("interface %s not implemented\n", debugstr_guid(riid));
124         *ppvObject = NULL;
125         return E_NOINTERFACE;
126     }
127 
128     IXMLDOMNodeList_AddRef( iface );
129 
130     return S_OK;
131 }
132 
133 static ULONG WINAPI xmlnodelist_AddRef(
134     IXMLDOMNodeList *iface )
135 {
136     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
137     ULONG ref = InterlockedIncrement( &This->ref );
138     TRACE("(%p)->(%d)\n", This, ref);
139     return ref;
140 }
141 
142 static ULONG WINAPI xmlnodelist_Release(
143     IXMLDOMNodeList *iface )
144 {
145     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
146     ULONG ref = InterlockedDecrement( &This->ref );
147 
148     TRACE("(%p)->(%d)\n", This, ref);
149     if ( ref == 0 )
150     {
151         xmldoc_release( This->parent->doc );
152         if (This->enumvariant) IEnumVARIANT_Release(This->enumvariant);
153         heap_free( This );
154     }
155 
156     return ref;
157 }
158 
159 static HRESULT WINAPI xmlnodelist_GetTypeInfoCount(
160     IXMLDOMNodeList *iface,
161     UINT* pctinfo )
162 {
163     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
164     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
165 }
166 
167 static HRESULT WINAPI xmlnodelist_GetTypeInfo(
168     IXMLDOMNodeList *iface,
169     UINT iTInfo,
170     LCID lcid,
171     ITypeInfo** ppTInfo )
172 {
173     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
174     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
175         iTInfo, lcid, ppTInfo);
176 }
177 
178 static HRESULT WINAPI xmlnodelist_GetIDsOfNames(
179     IXMLDOMNodeList *iface,
180     REFIID riid,
181     LPOLESTR* rgszNames,
182     UINT cNames,
183     LCID lcid,
184     DISPID* rgDispId )
185 {
186     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
187     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
188         riid, rgszNames, cNames, lcid, rgDispId);
189 }
190 
191 static HRESULT WINAPI xmlnodelist_Invoke(
192     IXMLDOMNodeList *iface,
193     DISPID dispIdMember,
194     REFIID riid,
195     LCID lcid,
196     WORD wFlags,
197     DISPPARAMS* pDispParams,
198     VARIANT* pVarResult,
199     EXCEPINFO* pExcepInfo,
200     UINT* puArgErr )
201 {
202     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
203     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
204         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
205 }
206 
207 static HRESULT WINAPI xmlnodelist_get_item(
208         IXMLDOMNodeList* iface,
209         LONG index,
210         IXMLDOMNode** listItem)
211 {
212     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
213     xmlNodePtr curr;
214     LONG nodeIndex = 0;
215 
216     TRACE("(%p)->(%d %p)\n", This, index, listItem);
217 
218     if(!listItem)
219         return E_INVALIDARG;
220 
221     *listItem = NULL;
222 
223     if (index < 0)
224         return S_FALSE;
225 
226     curr = This->parent->children;
227     while(curr)
228     {
229         if(nodeIndex++ == index) break;
230         curr = curr->next;
231     }
232     if(!curr) return S_FALSE;
233 
234     *listItem = create_node( curr );
235 
236     return S_OK;
237 }
238 
239 static HRESULT WINAPI xmlnodelist_get_length(
240         IXMLDOMNodeList* iface,
241         LONG* listLength)
242 {
243 
244     xmlNodePtr curr;
245     LONG nodeCount = 0;
246 
247     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
248 
249     TRACE("(%p)->(%p)\n", This, listLength);
250 
251     if(!listLength)
252         return E_INVALIDARG;
253 
254     curr = This->parent->children;
255     while (curr)
256     {
257         nodeCount++;
258         curr = curr->next;
259     }
260 
261     *listLength = nodeCount;
262     return S_OK;
263 }
264 
265 static HRESULT WINAPI xmlnodelist_nextNode(
266         IXMLDOMNodeList* iface,
267         IXMLDOMNode** nextItem)
268 {
269     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
270 
271     TRACE("(%p)->(%p)\n", This, nextItem );
272 
273     if(!nextItem)
274         return E_INVALIDARG;
275 
276     *nextItem = NULL;
277 
278     if (!This->current)
279         return S_FALSE;
280 
281     *nextItem = create_node( This->current );
282     This->current = This->current->next;
283     return S_OK;
284 }
285 
286 static HRESULT WINAPI xmlnodelist_reset(
287         IXMLDOMNodeList* iface)
288 {
289     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
290 
291     TRACE("%p\n", This);
292     This->current = This->parent->children;
293     return S_OK;
294 }
295 
296 static HRESULT WINAPI xmlnodelist__newEnum(
297         IXMLDOMNodeList* iface,
298         IUnknown** enumv)
299 {
300     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
301     TRACE("(%p)->(%p)\n", This, enumv);
302     return create_enumvariant((IUnknown*)iface, TRUE, &nodelist_enumvariant, (IEnumVARIANT**)enumv);
303 }
304 
305 static const struct IXMLDOMNodeListVtbl xmlnodelist_vtbl =
306 {
307     xmlnodelist_QueryInterface,
308     xmlnodelist_AddRef,
309     xmlnodelist_Release,
310     xmlnodelist_GetTypeInfoCount,
311     xmlnodelist_GetTypeInfo,
312     xmlnodelist_GetIDsOfNames,
313     xmlnodelist_Invoke,
314     xmlnodelist_get_item,
315     xmlnodelist_get_length,
316     xmlnodelist_nextNode,
317     xmlnodelist_reset,
318     xmlnodelist__newEnum,
319 };
320 
321 static HRESULT xmlnodelist_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
322 {
323     WCHAR *ptr;
324     int idx = 0;
325 
326     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
327         idx = idx*10 + (*ptr-'0');
328     if(*ptr)
329         return DISP_E_UNKNOWNNAME;
330 
331     *dispid = DISPID_DOM_COLLECTION_BASE + idx;
332     TRACE("ret %x\n", *dispid);
333     return S_OK;
334 }
335 
336 static HRESULT xmlnodelist_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
337         VARIANT *res, EXCEPINFO *ei)
338 {
339     xmlnodelist *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
340 
341     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
342 
343     if (id >= DISPID_DOM_COLLECTION_BASE && id <= DISPID_DOM_COLLECTION_MAX)
344     {
345         switch(flags)
346         {
347             case DISPATCH_PROPERTYGET:
348             {
349                 IXMLDOMNode *disp = NULL;
350 
351                 V_VT(res) = VT_DISPATCH;
352                 IXMLDOMNodeList_get_item(&This->IXMLDOMNodeList_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
353                 V_DISPATCH(res) = (IDispatch*)disp;
354                 break;
355             }
356             default:
357             {
358                 FIXME("unimplemented flags %x\n", flags);
359                 break;
360             }
361         }
362     }
363     else if (id == DISPID_VALUE)
364     {
365         switch(flags)
366         {
367             case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
368             case DISPATCH_PROPERTYGET:
369             case DISPATCH_METHOD:
370             {
371                 IXMLDOMNode *item;
372                 VARIANT index;
373                 HRESULT hr;
374 
375                 if (params->cArgs - params->cNamedArgs != 1) return DISP_E_BADPARAMCOUNT;
376 
377                 VariantInit(&index);
378                 hr = VariantChangeType(&index, params->rgvarg, 0, VT_I4);
379                 if(FAILED(hr))
380                 {
381                     FIXME("failed to convert arg, %s\n", debugstr_variant(params->rgvarg));
382                     return hr;
383                 }
384 
385                 IXMLDOMNodeList_get_item(&This->IXMLDOMNodeList_iface, V_I4(&index), &item);
386                 V_VT(res) = VT_DISPATCH;
387                 V_DISPATCH(res) = (IDispatch*)item;
388                 break;
389             }
390             default:
391             {
392                 FIXME("DISPID_VALUE: unimplemented flags %x\n", flags);
393                 break;
394             }
395         }
396     }
397     else
398         return DISP_E_UNKNOWNNAME;
399 
400     TRACE("ret %p\n", V_DISPATCH(res));
401 
402     return S_OK;
403 }
404 
405 static const dispex_static_data_vtbl_t xmlnodelist_dispex_vtbl = {
406     xmlnodelist_get_dispid,
407     xmlnodelist_invoke
408 };
409 
410 static const tid_t xmlnodelist_iface_tids[] = {
411     IXMLDOMNodeList_tid,
412     0
413 };
414 static dispex_static_data_t xmlnodelist_dispex = {
415     &xmlnodelist_dispex_vtbl,
416     IXMLDOMNodeList_tid,
417     NULL,
418     xmlnodelist_iface_tids
419 };
420 
421 IXMLDOMNodeList* create_children_nodelist( xmlNodePtr node )
422 {
423     xmlnodelist *This;
424 
425     This = heap_alloc( sizeof *This );
426     if ( !This )
427         return NULL;
428 
429     This->IXMLDOMNodeList_iface.lpVtbl = &xmlnodelist_vtbl;
430     This->ref = 1;
431     This->parent = node;
432     This->current = node->children;
433     This->enumvariant = NULL;
434     xmldoc_add_ref( node->doc );
435 
436     init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMNodeList_iface, &xmlnodelist_dispex);
437 
438     return &This->IXMLDOMNodeList_iface;
439 }
440 
441 #endif
442