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