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
nodelist_get_item(IUnknown * iface,LONG index,VARIANT * item)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
impl_from_IXMLDOMNodeList(IXMLDOMNodeList * iface)76 static inline xmlnodelist *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
77 {
78 return CONTAINING_RECORD(iface, xmlnodelist, IXMLDOMNodeList_iface);
79 }
80
xmlnodelist_QueryInterface(IXMLDOMNodeList * iface,REFIID riid,void ** ppvObject)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
xmlnodelist_AddRef(IXMLDOMNodeList * iface)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
xmlnodelist_Release(IXMLDOMNodeList * iface)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
xmlnodelist_GetTypeInfoCount(IXMLDOMNodeList * iface,UINT * pctinfo)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
xmlnodelist_GetTypeInfo(IXMLDOMNodeList * iface,UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)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
xmlnodelist_GetIDsOfNames(IXMLDOMNodeList * iface,REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)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
xmlnodelist_Invoke(IXMLDOMNodeList * iface,DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)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
xmlnodelist_get_item(IXMLDOMNodeList * iface,LONG index,IXMLDOMNode ** listItem)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
xmlnodelist_get_length(IXMLDOMNodeList * iface,LONG * listLength)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
xmlnodelist_nextNode(IXMLDOMNodeList * iface,IXMLDOMNode ** nextItem)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
xmlnodelist_reset(IXMLDOMNodeList * iface)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
xmlnodelist__newEnum(IXMLDOMNodeList * iface,IUnknown ** enumv)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
xmlnodelist_get_dispid(IUnknown * iface,BSTR name,DWORD flags,DISPID * dispid)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
xmlnodelist_invoke(IUnknown * iface,DISPID id,LCID lcid,WORD flags,DISPPARAMS * params,VARIANT * res,EXCEPINFO * ei)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
create_children_nodelist(xmlNodePtr node)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