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