mshtml: Wine Gecko 1.4 release.
[wine] / dlls / msxml3 / nodemap.c
1 /*
2  *    Node map 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 "config.h"
22
23 #define COBJMACROS
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 "winnls.h"
35 #include "ole2.h"
36 #include "msxml6.h"
37 #include "msxml2did.h"
38
39 #include "msxml_private.h"
40
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
44
45 #ifdef HAVE_LIBXML2
46
47 typedef struct _xmlnodemap
48 {
49     DispatchEx dispex;
50     IXMLDOMNamedNodeMap IXMLDOMNamedNodeMap_iface;
51     ISupportErrorInfo ISupportErrorInfo_iface;
52     LONG ref;
53
54     xmlNodePtr node;
55     LONG iterator;
56 } xmlnodemap;
57
58 static inline xmlnodemap *impl_from_IXMLDOMNamedNodeMap( IXMLDOMNamedNodeMap *iface )
59 {
60     return CONTAINING_RECORD(iface, xmlnodemap, IXMLDOMNamedNodeMap_iface);
61 }
62
63 static inline xmlnodemap *impl_from_ISupportErrorInfo( ISupportErrorInfo *iface )
64 {
65     return CONTAINING_RECORD(iface, xmlnodemap, ISupportErrorInfo_iface);
66 }
67
68 static HRESULT WINAPI xmlnodemap_QueryInterface(
69     IXMLDOMNamedNodeMap *iface,
70     REFIID riid, void** ppvObject )
71 {
72     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
73     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
74
75     if( IsEqualGUID( riid, &IID_IUnknown ) ||
76         IsEqualGUID( riid, &IID_IDispatch ) ||
77         IsEqualGUID( riid, &IID_IXMLDOMNamedNodeMap ) )
78     {
79         *ppvObject = iface;
80     }
81     else if (dispex_query_interface(&This->dispex, riid, ppvObject))
82     {
83         return *ppvObject ? S_OK : E_NOINTERFACE;
84     }
85     else if( IsEqualGUID( riid, &IID_ISupportErrorInfo ))
86     {
87         *ppvObject = &This->ISupportErrorInfo_iface;
88     }
89     else
90     {
91         TRACE("interface %s not implemented\n", debugstr_guid(riid));
92         *ppvObject = NULL;
93         return E_NOINTERFACE;
94     }
95
96     IXMLDOMElement_AddRef( iface );
97
98     return S_OK;
99 }
100
101 static ULONG WINAPI xmlnodemap_AddRef(
102     IXMLDOMNamedNodeMap *iface )
103 {
104     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
105     ULONG ref = InterlockedIncrement( &This->ref );
106     TRACE("(%p)->(%d)\n", This, ref);
107     return ref;
108 }
109
110 static ULONG WINAPI xmlnodemap_Release(
111     IXMLDOMNamedNodeMap *iface )
112 {
113     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
114     ULONG ref = InterlockedDecrement( &This->ref );
115
116     TRACE("(%p)->(%d)\n", This, ref);
117     if ( ref == 0 )
118     {
119         xmldoc_release( This->node->doc );
120         release_dispex(&This->dispex);
121         heap_free( This );
122     }
123
124     return ref;
125 }
126
127 static HRESULT WINAPI xmlnodemap_GetTypeInfoCount(
128     IXMLDOMNamedNodeMap *iface,
129     UINT* pctinfo )
130 {
131     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
132
133     TRACE("(%p)->(%p)\n", This, pctinfo);
134
135     *pctinfo = 1;
136
137     return S_OK;
138 }
139
140 static HRESULT WINAPI xmlnodemap_GetTypeInfo(
141     IXMLDOMNamedNodeMap *iface,
142     UINT iTInfo, LCID lcid,
143     ITypeInfo** ppTInfo )
144 {
145     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
146     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
147     return get_typeinfo(IXMLDOMNamedNodeMap_tid, ppTInfo);
148 }
149
150 static HRESULT WINAPI xmlnodemap_GetIDsOfNames(
151     IXMLDOMNamedNodeMap *iface,
152     REFIID riid, LPOLESTR* rgszNames,
153     UINT cNames, LCID lcid, DISPID* rgDispId )
154 {
155     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
156     ITypeInfo *typeinfo;
157     HRESULT hr;
158
159     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
160           lcid, rgDispId);
161
162     if(!rgszNames || cNames == 0 || !rgDispId)
163         return E_INVALIDARG;
164
165     hr = get_typeinfo(IXMLDOMNamedNodeMap_tid, &typeinfo);
166     if(SUCCEEDED(hr))
167     {
168         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
169         ITypeInfo_Release(typeinfo);
170     }
171
172     return hr;
173 }
174
175 static HRESULT WINAPI xmlnodemap_Invoke(
176     IXMLDOMNamedNodeMap *iface,
177     DISPID dispIdMember, REFIID riid, LCID lcid,
178     WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
179     EXCEPINFO* pExcepInfo, UINT* puArgErr )
180 {
181     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
182     ITypeInfo *typeinfo;
183     HRESULT hr;
184
185     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
186           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
187
188     hr = get_typeinfo(IXMLDOMNamedNodeMap_tid, &typeinfo);
189     if(SUCCEEDED(hr))
190     {
191         hr = ITypeInfo_Invoke(typeinfo, &This->IXMLDOMNamedNodeMap_iface, dispIdMember, wFlags,
192                 pDispParams, pVarResult, pExcepInfo, puArgErr);
193         ITypeInfo_Release(typeinfo);
194     }
195
196     return hr;
197 }
198
199 static HRESULT WINAPI xmlnodemap_getNamedItem(
200     IXMLDOMNamedNodeMap *iface,
201     BSTR name,
202     IXMLDOMNode** namedItem)
203 {
204     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
205     TRACE("(%p)->(%s %p)\n", This, debugstr_w(name), namedItem );
206     return IXMLDOMNamedNodeMap_getQualifiedItem(iface, name, NULL, namedItem);
207 }
208
209 static HRESULT WINAPI xmlnodemap_setNamedItem(
210     IXMLDOMNamedNodeMap *iface,
211     IXMLDOMNode* newItem,
212     IXMLDOMNode** namedItem)
213 {
214     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
215     xmlNodePtr nodeNew;
216     xmlnode *ThisNew;
217
218     TRACE("(%p)->(%p %p)\n", This, newItem, namedItem );
219
220     if(!newItem)
221         return E_INVALIDARG;
222
223     if(namedItem) *namedItem = NULL;
224
225     /* Must be an Attribute */
226     ThisNew = get_node_obj( newItem );
227     if(!ThisNew) return E_FAIL;
228
229     if(ThisNew->node->type != XML_ATTRIBUTE_NODE)
230         return E_FAIL;
231
232     if(!ThisNew->node->parent)
233         if(xmldoc_remove_orphan(ThisNew->node->doc, ThisNew->node) != S_OK)
234             WARN("%p is not an orphan of %p\n", ThisNew->node, ThisNew->node->doc);
235
236     nodeNew = xmlAddChild(This->node, ThisNew->node);
237
238     if(namedItem)
239         *namedItem = create_node( nodeNew );
240     return S_OK;
241 }
242
243 static HRESULT WINAPI xmlnodemap_removeNamedItem(
244     IXMLDOMNamedNodeMap *iface,
245     BSTR name,
246     IXMLDOMNode** namedItem)
247 {
248     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
249     TRACE("(%p)->(%s %p)\n", This, debugstr_w(name), namedItem );
250     return IXMLDOMNamedNodeMap_removeQualifiedItem(iface, name, NULL, namedItem);
251 }
252
253 static HRESULT WINAPI xmlnodemap_get_item(
254     IXMLDOMNamedNodeMap *iface,
255     LONG index,
256     IXMLDOMNode** listItem)
257 {
258     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
259     xmlAttrPtr curr;
260     LONG attrIndex;
261
262     TRACE("(%p)->(%d %p)\n", This, index, listItem);
263
264     *listItem = NULL;
265
266     if (index < 0)
267         return S_FALSE;
268
269     curr = This->node->properties;
270
271     for (attrIndex = 0; attrIndex < index; attrIndex++) {
272         if (curr->next == NULL)
273             return S_FALSE;
274         else
275             curr = curr->next;
276     }
277     
278     *listItem = create_node( (xmlNodePtr) curr );
279
280     return S_OK;
281 }
282
283 static HRESULT WINAPI xmlnodemap_get_length(
284     IXMLDOMNamedNodeMap *iface,
285     LONG *listLength)
286 {
287     xmlAttrPtr first;
288     xmlAttrPtr curr;
289     LONG attrCount;
290
291     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
292
293     TRACE("(%p)->(%p)\n", This, listLength);
294
295     if( !listLength )
296         return E_INVALIDARG;
297
298     first = This->node->properties;
299     if (first == NULL) {
300         *listLength = 0;
301         return S_OK;
302     }
303
304     curr = first;
305     attrCount = 1;
306     while (curr->next) {
307         attrCount++;
308         curr = curr->next;
309     }
310     *listLength = attrCount;
311  
312     return S_OK;
313 }
314
315 static HRESULT WINAPI xmlnodemap_getQualifiedItem(
316     IXMLDOMNamedNodeMap *iface,
317     BSTR baseName,
318     BSTR namespaceURI,
319     IXMLDOMNode** qualifiedItem)
320 {
321     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
322     xmlAttrPtr attr;
323     xmlChar *href;
324     xmlChar *name;
325
326     TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(baseName), debugstr_w(namespaceURI), qualifiedItem);
327
328     if (!baseName || !qualifiedItem) return E_INVALIDARG;
329
330     if (namespaceURI && *namespaceURI)
331     {
332         href = xmlchar_from_wchar(namespaceURI);
333         if (!href) return E_OUTOFMEMORY;
334     }
335     else
336         href = NULL;
337
338     name = xmlchar_from_wchar(baseName);
339     if (!name)
340     {
341         heap_free(href);
342         return E_OUTOFMEMORY;
343     }
344
345     attr = xmlHasNsProp(This->node, name, href);
346
347     heap_free(name);
348     heap_free(href);
349
350     if (!attr)
351     {
352         *qualifiedItem = NULL;
353         return S_FALSE;
354     }
355
356     *qualifiedItem = create_node((xmlNodePtr)attr);
357
358     return S_OK;
359 }
360
361 static HRESULT WINAPI xmlnodemap_removeQualifiedItem(
362     IXMLDOMNamedNodeMap *iface,
363     BSTR baseName,
364     BSTR namespaceURI,
365     IXMLDOMNode** qualifiedItem)
366 {
367     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
368     xmlAttrPtr attr;
369     xmlChar *name;
370     xmlChar *href;
371
372     TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(baseName), debugstr_w(namespaceURI), qualifiedItem);
373
374     if (!baseName) return E_INVALIDARG;
375
376     if (namespaceURI && *namespaceURI)
377     {
378         href = xmlchar_from_wchar(namespaceURI);
379         if (!href) return E_OUTOFMEMORY;
380     }
381     else
382         href = NULL;
383
384     name = xmlchar_from_wchar(baseName);
385     if (!name)
386     {
387         heap_free(href);
388         return E_OUTOFMEMORY;
389     }
390
391     attr = xmlHasNsProp( This->node, name, href );
392
393     heap_free(name);
394     heap_free(href);
395
396     if ( !attr )
397     {
398         if (qualifiedItem) *qualifiedItem = NULL;
399         return S_FALSE;
400     }
401
402     if ( qualifiedItem )
403     {
404         xmlUnlinkNode( (xmlNodePtr) attr );
405         xmldoc_add_orphan( attr->doc, (xmlNodePtr) attr );
406         *qualifiedItem = create_node( (xmlNodePtr) attr );
407     }
408     else
409     {
410         if (xmlRemoveProp(attr) == -1)
411             ERR("xmlRemoveProp failed\n");
412     }
413
414     return S_OK;
415 }
416
417 static HRESULT WINAPI xmlnodemap_nextNode(
418     IXMLDOMNamedNodeMap *iface,
419     IXMLDOMNode** nextItem)
420 {
421     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
422     xmlAttrPtr curr;
423     LONG attrIndex;
424
425     TRACE("(%p)->(%p: %d)\n", This, nextItem, This->iterator);
426
427     *nextItem = NULL;
428
429     curr = This->node->properties;
430
431     for (attrIndex = 0; attrIndex < This->iterator; attrIndex++) {
432         if (curr->next == NULL)
433             return S_FALSE;
434         else
435             curr = curr->next;
436     }
437
438     This->iterator++;
439
440     *nextItem = create_node( (xmlNodePtr) curr );
441
442     return S_OK;
443 }
444
445 static HRESULT WINAPI xmlnodemap_reset(
446     IXMLDOMNamedNodeMap *iface )
447 {
448     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
449
450     TRACE("(%p: %d)\n", This, This->iterator);
451
452     This->iterator = 0;
453
454     return S_OK;
455 }
456
457 static HRESULT WINAPI xmlnodemap__newEnum(
458     IXMLDOMNamedNodeMap *iface,
459     IUnknown** ppUnk)
460 {
461     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( iface );
462     FIXME("(%p)->(%p)\n", This, ppUnk);
463     return E_NOTIMPL;
464 }
465
466 static const struct IXMLDOMNamedNodeMapVtbl xmlnodemap_vtbl =
467 {
468     xmlnodemap_QueryInterface,
469     xmlnodemap_AddRef,
470     xmlnodemap_Release,
471     xmlnodemap_GetTypeInfoCount,
472     xmlnodemap_GetTypeInfo,
473     xmlnodemap_GetIDsOfNames,
474     xmlnodemap_Invoke,
475     xmlnodemap_getNamedItem,
476     xmlnodemap_setNamedItem,
477     xmlnodemap_removeNamedItem,
478     xmlnodemap_get_item,
479     xmlnodemap_get_length,
480     xmlnodemap_getQualifiedItem,
481     xmlnodemap_removeQualifiedItem,
482     xmlnodemap_nextNode,
483     xmlnodemap_reset,
484     xmlnodemap__newEnum,
485 };
486
487 static HRESULT WINAPI support_error_QueryInterface(
488     ISupportErrorInfo *iface,
489     REFIID riid, void** ppvObject )
490 {
491     xmlnodemap *This = impl_from_ISupportErrorInfo( iface );
492     TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject);
493     return IXMLDOMNamedNodeMap_QueryInterface(&This->IXMLDOMNamedNodeMap_iface, riid, ppvObject);
494 }
495
496 static ULONG WINAPI support_error_AddRef(
497     ISupportErrorInfo *iface )
498 {
499     xmlnodemap *This = impl_from_ISupportErrorInfo( iface );
500     return IXMLDOMNamedNodeMap_AddRef(&This->IXMLDOMNamedNodeMap_iface);
501 }
502
503 static ULONG WINAPI support_error_Release(
504     ISupportErrorInfo *iface )
505 {
506     xmlnodemap *This = impl_from_ISupportErrorInfo( iface );
507     return IXMLDOMNamedNodeMap_Release(&This->IXMLDOMNamedNodeMap_iface);
508 }
509
510 static HRESULT WINAPI support_error_InterfaceSupportsErrorInfo(
511     ISupportErrorInfo *iface,
512     REFIID riid )
513 {
514     FIXME("(%p)->(%s)\n", iface, debugstr_guid(riid));
515     return S_FALSE;
516 }
517
518 static const struct ISupportErrorInfoVtbl support_error_vtbl =
519 {
520     support_error_QueryInterface,
521     support_error_AddRef,
522     support_error_Release,
523     support_error_InterfaceSupportsErrorInfo
524 };
525
526 static HRESULT xmlnodemap_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
527 {
528     WCHAR *ptr;
529     int idx = 0;
530
531     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
532         idx = idx*10 + (*ptr-'0');
533     if(*ptr)
534         return DISP_E_UNKNOWNNAME;
535
536     *dispid = DISPID_DOM_COLLECTION_BASE + idx;
537     TRACE("ret %x\n", *dispid);
538     return S_OK;
539 }
540
541 static HRESULT xmlnodemap_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
542         VARIANT *res, EXCEPINFO *ei)
543 {
544     xmlnodemap *This = impl_from_IXMLDOMNamedNodeMap( (IXMLDOMNamedNodeMap*)iface );
545
546     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
547
548     V_VT(res) = VT_DISPATCH;
549     V_DISPATCH(res) = NULL;
550
551     if (id < DISPID_DOM_COLLECTION_BASE || id > DISPID_DOM_COLLECTION_MAX)
552         return DISP_E_UNKNOWNNAME;
553
554     switch(flags)
555     {
556         case INVOKE_PROPERTYGET:
557         {
558             IXMLDOMNode *disp = NULL;
559
560             IXMLDOMNamedNodeMap_get_item(&This->IXMLDOMNamedNodeMap_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
561             V_DISPATCH(res) = (IDispatch*)disp;
562             break;
563         }
564         default:
565         {
566             FIXME("unimplemented flags %x\n", flags);
567             break;
568         }
569     }
570
571     TRACE("ret %p\n", V_DISPATCH(res));
572
573     return S_OK;
574 }
575
576 static const dispex_static_data_vtbl_t xmlnodemap_dispex_vtbl = {
577     xmlnodemap_get_dispid,
578     xmlnodemap_invoke
579 };
580
581 static const tid_t xmlnodemap_iface_tids[] = {
582     IXMLDOMNamedNodeMap_tid,
583     0
584 };
585
586 static dispex_static_data_t xmlnodemap_dispex = {
587     &xmlnodemap_dispex_vtbl,
588     IXMLDOMNamedNodeMap_tid,
589     NULL,
590     xmlnodemap_iface_tids
591 };
592
593 IXMLDOMNamedNodeMap *create_nodemap( const xmlNodePtr node )
594 {
595     xmlnodemap *This;
596
597     This = heap_alloc( sizeof *This );
598     if ( !This )
599         return NULL;
600
601     This->IXMLDOMNamedNodeMap_iface.lpVtbl = &xmlnodemap_vtbl;
602     This->ISupportErrorInfo_iface.lpVtbl = &support_error_vtbl;
603     This->node = node;
604     This->ref = 1;
605     This->iterator = 0;
606
607     init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMNamedNodeMap_iface, &xmlnodemap_dispex);
608
609     xmldoc_add_ref(node->doc);
610
611     return &This->IXMLDOMNamedNodeMap_iface;
612 }
613
614 #endif