msxml3: COM cleanup in domdoc.c.
[wine] / dlls / msxml3 / selection.c
1 /*
2  *    XPath/XSLPattern query result node list implementation
3  *
4  * Copyright 2005 Mike McCormack
5  * Copyright 2007 Mikolaj Zalewski
6  * Copyright 2010 Adam Martinson for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #define COBJMACROS
24
25 #include "config.h"
26
27 #include <stdarg.h>
28 #ifdef HAVE_LIBXML2
29 # include <libxml/parser.h>
30 # include <libxml/xmlerror.h>
31 # include <libxml/xpath.h>
32 # include <libxml/xpathInternals.h>
33 #endif
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"
38 #include "ole2.h"
39 #include "msxml6.h"
40
41 #include "msxml_private.h"
42
43 #include "wine/debug.h"
44
45 /* This file implements the object returned by a XPath query. Note that this is
46  * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
47  * They are different because the list returned by XPath queries:
48  *  - is static - gives the results for the XML tree as it existed during the
49  *    execution of the query
50  *  - supports IXMLDOMSelection
51  *
52  */
53
54 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
55
56 #ifdef HAVE_LIBXML2
57
58 int registerNamespaces(xmlXPathContextPtr ctxt);
59 xmlChar* XSLPattern_to_XPath(xmlXPathContextPtr ctxt, xmlChar const* xslpat_str);
60
61 typedef struct _domselection
62 {
63     DispatchEx dispex;
64     IXMLDOMSelection IXMLDOMSelection_iface;
65     LONG ref;
66     xmlNodePtr node;
67     xmlXPathObjectPtr result;
68     int resultPos;
69 } domselection;
70
71 static inline domselection *impl_from_IXMLDOMSelection( IXMLDOMSelection *iface )
72 {
73     return CONTAINING_RECORD(iface, domselection, IXMLDOMSelection_iface);
74 }
75
76 static HRESULT WINAPI domselection_QueryInterface(
77     IXMLDOMSelection *iface,
78     REFIID riid,
79     void** ppvObject )
80 {
81     domselection *This = impl_from_IXMLDOMSelection( iface );
82
83     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
84
85     if(!ppvObject)
86         return E_INVALIDARG;
87
88     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
89          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) ||
90          IsEqualGUID( riid, &IID_IXMLDOMSelection ))
91     {
92         *ppvObject = &This->IXMLDOMSelection_iface;
93     }
94     else if(dispex_query_interface(&This->dispex, riid, ppvObject))
95     {
96         return *ppvObject ? S_OK : E_NOINTERFACE;
97     }
98     else
99     {
100         TRACE("interface %s not implemented\n", debugstr_guid(riid));
101         *ppvObject = NULL;
102         return E_NOINTERFACE;
103     }
104
105     IXMLDOMSelection_AddRef( iface );
106
107     return S_OK;
108 }
109
110 static ULONG WINAPI domselection_AddRef(
111     IXMLDOMSelection *iface )
112 {
113     domselection *This = impl_from_IXMLDOMSelection( iface );
114     ULONG ref = InterlockedIncrement( &This->ref );
115     TRACE("(%p)->(%d)\n", This, ref);
116     return ref;
117 }
118
119 static ULONG WINAPI domselection_Release(
120     IXMLDOMSelection *iface )
121 {
122     domselection *This = impl_from_IXMLDOMSelection( iface );
123     ULONG ref = InterlockedDecrement(&This->ref);
124
125     TRACE("(%p)->(%d)\n", This, ref);
126     if ( ref == 0 )
127     {
128         xmlXPathFreeObject(This->result);
129         xmldoc_release(This->node->doc);
130         heap_free(This);
131     }
132
133     return ref;
134 }
135
136 static HRESULT WINAPI domselection_GetTypeInfoCount(
137     IXMLDOMSelection *iface,
138     UINT* pctinfo )
139 {
140     domselection *This = impl_from_IXMLDOMSelection( iface );
141
142     TRACE("(%p)->(%p)\n", This, pctinfo);
143
144     *pctinfo = 1;
145
146     return S_OK;
147 }
148
149 static HRESULT WINAPI domselection_GetTypeInfo(
150     IXMLDOMSelection *iface,
151     UINT iTInfo,
152     LCID lcid,
153     ITypeInfo** ppTInfo )
154 {
155     domselection *This = impl_from_IXMLDOMSelection( iface );
156     HRESULT hr;
157
158     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
159
160     hr = get_typeinfo(IXMLDOMSelection_tid, ppTInfo);
161
162     return hr;
163 }
164
165 static HRESULT WINAPI domselection_GetIDsOfNames(
166     IXMLDOMSelection *iface,
167     REFIID riid,
168     LPOLESTR* rgszNames,
169     UINT cNames,
170     LCID lcid,
171     DISPID* rgDispId )
172 {
173     domselection *This = impl_from_IXMLDOMSelection( iface );
174     ITypeInfo *typeinfo;
175     HRESULT hr;
176
177     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
178           lcid, rgDispId);
179
180     if(!rgszNames || cNames == 0 || !rgDispId)
181         return E_INVALIDARG;
182
183     hr = get_typeinfo(IXMLDOMSelection_tid, &typeinfo);
184     if(SUCCEEDED(hr))
185     {
186         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
187         ITypeInfo_Release(typeinfo);
188     }
189
190     return hr;
191 }
192
193 static HRESULT WINAPI domselection_Invoke(
194     IXMLDOMSelection *iface,
195     DISPID dispIdMember,
196     REFIID riid,
197     LCID lcid,
198     WORD wFlags,
199     DISPPARAMS* pDispParams,
200     VARIANT* pVarResult,
201     EXCEPINFO* pExcepInfo,
202     UINT* puArgErr )
203 {
204     domselection *This = impl_from_IXMLDOMSelection( iface );
205     ITypeInfo *typeinfo;
206     HRESULT hr;
207
208     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
209           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
210
211     hr = get_typeinfo(IXMLDOMSelection_tid, &typeinfo);
212     if(SUCCEEDED(hr))
213     {
214         hr = ITypeInfo_Invoke(typeinfo, &This->IXMLDOMSelection_iface, dispIdMember, wFlags, pDispParams,
215                 pVarResult, pExcepInfo, puArgErr);
216         ITypeInfo_Release(typeinfo);
217     }
218
219     return hr;
220 }
221
222 static HRESULT WINAPI domselection_get_item(
223         IXMLDOMSelection* iface,
224         LONG index,
225         IXMLDOMNode** listItem)
226 {
227     domselection *This = impl_from_IXMLDOMSelection( iface );
228
229     TRACE("(%p)->(%d %p)\n", This, index, listItem);
230
231     if(!listItem)
232         return E_INVALIDARG;
233
234     *listItem = NULL;
235
236     if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
237         return S_FALSE;
238
239     *listItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, index));
240     This->resultPos = index + 1;
241
242     return S_OK;
243 }
244
245 static HRESULT WINAPI domselection_get_length(
246         IXMLDOMSelection* iface,
247         LONG* listLength)
248 {
249     domselection *This = impl_from_IXMLDOMSelection( iface );
250
251     TRACE("(%p)->(%p)\n", This, listLength);
252
253     if(!listLength)
254         return E_INVALIDARG;
255
256     *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
257     return S_OK;
258 }
259
260 static HRESULT WINAPI domselection_nextNode(
261         IXMLDOMSelection* iface,
262         IXMLDOMNode** nextItem)
263 {
264     domselection *This = impl_from_IXMLDOMSelection( iface );
265
266     TRACE("(%p)->(%p)\n", This, nextItem );
267
268     if(!nextItem)
269         return E_INVALIDARG;
270
271     *nextItem = NULL;
272
273     if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
274         return S_FALSE;
275
276     *nextItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, This->resultPos));
277     This->resultPos++;
278     return S_OK;
279 }
280
281 static HRESULT WINAPI domselection_reset(
282         IXMLDOMSelection* iface)
283 {
284     domselection *This = impl_from_IXMLDOMSelection( iface );
285
286     TRACE("%p\n", This);
287     This->resultPos = 0;
288     return S_OK;
289 }
290
291 static HRESULT WINAPI domselection_get__newEnum(
292         IXMLDOMSelection* iface,
293         IUnknown** ppUnk)
294 {
295     domselection *This = impl_from_IXMLDOMSelection( iface );
296     FIXME("(%p)->(%p)\n", This, ppUnk);
297     return E_NOTIMPL;
298 }
299
300 static HRESULT WINAPI domselection_get_expr(
301         IXMLDOMSelection* iface,
302         BSTR *p)
303 {
304     domselection *This = impl_from_IXMLDOMSelection( iface );
305     FIXME("(%p)->(%p)\n", This, p);
306     return E_NOTIMPL;
307 }
308
309 static HRESULT WINAPI domselection_put_expr(
310         IXMLDOMSelection* iface,
311         BSTR p)
312 {
313     domselection *This = impl_from_IXMLDOMSelection( iface );
314     FIXME("(%p)->(%s)\n", This, debugstr_w(p));
315     return E_NOTIMPL;
316 }
317
318 static HRESULT WINAPI domselection_get_context(
319         IXMLDOMSelection* iface,
320         IXMLDOMNode **node)
321 {
322     domselection *This = impl_from_IXMLDOMSelection( iface );
323     FIXME("(%p)->(%p)\n", This, node);
324     return E_NOTIMPL;
325 }
326
327 static HRESULT WINAPI domselection_putref_context(
328         IXMLDOMSelection* iface,
329         IXMLDOMNode *node)
330 {
331     domselection *This = impl_from_IXMLDOMSelection( iface );
332     FIXME("(%p)->(%p)\n", This, node);
333     return E_NOTIMPL;
334 }
335
336 static HRESULT WINAPI domselection_peekNode(
337         IXMLDOMSelection* iface,
338         IXMLDOMNode **node)
339 {
340     domselection *This = impl_from_IXMLDOMSelection( iface );
341     FIXME("(%p)->(%p)\n", This, node);
342     return E_NOTIMPL;
343 }
344
345 static HRESULT WINAPI domselection_matches(
346         IXMLDOMSelection* iface,
347         IXMLDOMNode *node,
348         IXMLDOMNode **out_node)
349 {
350     domselection *This = impl_from_IXMLDOMSelection( iface );
351     FIXME("(%p)->(%p %p)\n", This, node, out_node);
352     return E_NOTIMPL;
353 }
354
355 static HRESULT WINAPI domselection_removeNext(
356         IXMLDOMSelection* iface,
357         IXMLDOMNode **node)
358 {
359     domselection *This = impl_from_IXMLDOMSelection( iface );
360     FIXME("(%p)->(%p)\n", This, node);
361     return E_NOTIMPL;
362 }
363
364 static HRESULT WINAPI domselection_removeAll(
365         IXMLDOMSelection* iface)
366 {
367     domselection *This = impl_from_IXMLDOMSelection( iface );
368     FIXME("(%p)\n", This);
369     return E_NOTIMPL;
370 }
371
372 static HRESULT WINAPI domselection_clone(
373         IXMLDOMSelection* iface,
374         IXMLDOMSelection **node)
375 {
376     domselection *This = impl_from_IXMLDOMSelection( iface );
377     FIXME("(%p)->(%p)\n", This, node);
378     return E_NOTIMPL;
379 }
380
381 static HRESULT WINAPI domselection_getProperty(
382         IXMLDOMSelection* iface,
383         BSTR p,
384         VARIANT *var)
385 {
386     domselection *This = impl_from_IXMLDOMSelection( iface );
387     FIXME("(%p)->(%s %p)\n", This, debugstr_w(p), var);
388     return E_NOTIMPL;
389 }
390
391 static HRESULT WINAPI domselection_setProperty(
392         IXMLDOMSelection* iface,
393         BSTR p,
394         VARIANT var)
395 {
396     domselection *This = impl_from_IXMLDOMSelection( iface );
397     FIXME("(%p)->(%s %s)\n", This, debugstr_w(p), debugstr_variant(&var));
398     return E_NOTIMPL;
399 }
400
401 static const struct IXMLDOMSelectionVtbl domselection_vtbl =
402 {
403     domselection_QueryInterface,
404     domselection_AddRef,
405     domselection_Release,
406     domselection_GetTypeInfoCount,
407     domselection_GetTypeInfo,
408     domselection_GetIDsOfNames,
409     domselection_Invoke,
410     domselection_get_item,
411     domselection_get_length,
412     domselection_nextNode,
413     domselection_reset,
414     domselection_get__newEnum,
415     domselection_get_expr,
416     domselection_put_expr,
417     domselection_get_context,
418     domselection_putref_context,
419     domselection_peekNode,
420     domselection_matches,
421     domselection_removeNext,
422     domselection_removeAll,
423     domselection_clone,
424     domselection_getProperty,
425     domselection_setProperty
426 };
427
428 static HRESULT domselection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
429 {
430     domselection *This = impl_from_IXMLDOMSelection( (IXMLDOMSelection*)iface );
431     WCHAR *ptr;
432     int idx = 0;
433
434     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
435         idx = idx*10 + (*ptr-'0');
436     if(*ptr)
437         return DISP_E_UNKNOWNNAME;
438
439     if(idx >= xmlXPathNodeSetGetLength(This->result->nodesetval))
440         return DISP_E_UNKNOWNNAME;
441
442     *dispid = MSXML_DISPID_CUSTOM_MIN + idx;
443     TRACE("ret %x\n", *dispid);
444     return S_OK;
445 }
446
447 static HRESULT domselection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
448         VARIANT *res, EXCEPINFO *ei)
449 {
450     domselection *This = impl_from_IXMLDOMSelection( (IXMLDOMSelection*)iface );
451
452     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
453
454     V_VT(res) = VT_DISPATCH;
455     V_DISPATCH(res) = NULL;
456
457     switch(flags)
458     {
459         case INVOKE_PROPERTYGET:
460         {
461             IXMLDOMNode *disp = NULL;
462
463             domselection_get_item(&This->IXMLDOMSelection_iface, id - MSXML_DISPID_CUSTOM_MIN, &disp);
464             V_DISPATCH(res) = (IDispatch*)disp;
465             break;
466         }
467         default:
468         {
469             FIXME("unimplemented flags %x\n", flags);
470             break;
471         }
472     }
473
474     TRACE("ret %p\n", V_DISPATCH(res));
475
476     return S_OK;
477 }
478
479 static const dispex_static_data_vtbl_t domselection_dispex_vtbl = {
480     domselection_get_dispid,
481     domselection_invoke
482 };
483
484 static const tid_t domselection_iface_tids[] = {
485     IXMLDOMSelection_tid,
486     0
487 };
488 static dispex_static_data_t domselection_dispex = {
489     &domselection_dispex_vtbl,
490     IXMLDOMSelection_tid,
491     NULL,
492     domselection_iface_tids
493 };
494
495 #define XSLPATTERN_CHECK_ARGS(n) \
496     if (nargs != n) { \
497         FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
498         xmlXPathSetArityError(pctx); \
499         return; \
500     }
501
502
503 void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
504 {
505     XSLPATTERN_CHECK_ARGS(0);
506
507     xmlXPathPositionFunction(pctx, 0);
508     xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
509 }
510
511 void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
512 {
513     double pos, last;
514     XSLPATTERN_CHECK_ARGS(0);
515
516     xmlXPathPositionFunction(pctx, 0);
517     pos = xmlXPathPopNumber(pctx);
518     xmlXPathLastFunction(pctx, 0);
519     last = xmlXPathPopNumber(pctx);
520     xmlXPathReturnBoolean(pctx, pos == last);
521 }
522
523 void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
524 {
525     XSLPATTERN_CHECK_ARGS(0);
526     xmlXPathReturnNumber(pctx, pctx->context->node->type);
527 }
528
529 void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
530 {
531     xmlChar *arg1, *arg2;
532     XSLPATTERN_CHECK_ARGS(2);
533
534     arg2 = xmlXPathPopString(pctx);
535     arg1 = xmlXPathPopString(pctx);
536     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
537     xmlFree(arg1);
538     xmlFree(arg2);
539 }
540
541 void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
542 {
543     xmlChar *arg1, *arg2;
544     XSLPATTERN_CHECK_ARGS(2);
545
546     arg2 = xmlXPathPopString(pctx);
547     arg1 = xmlXPathPopString(pctx);
548     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) != 0);
549     xmlFree(arg1);
550     xmlFree(arg2);
551 }
552
553 void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
554 {
555     xmlChar *arg1, *arg2;
556     XSLPATTERN_CHECK_ARGS(2);
557
558     arg2 = xmlXPathPopString(pctx);
559     arg1 = xmlXPathPopString(pctx);
560     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) < 0);
561     xmlFree(arg1);
562     xmlFree(arg2);
563 }
564
565 void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
566 {
567     xmlChar *arg1, *arg2;
568     XSLPATTERN_CHECK_ARGS(2);
569
570     arg2 = xmlXPathPopString(pctx);
571     arg1 = xmlXPathPopString(pctx);
572     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) <= 0);
573     xmlFree(arg1);
574     xmlFree(arg2);
575 }
576
577 void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
578 {
579     xmlChar *arg1, *arg2;
580     XSLPATTERN_CHECK_ARGS(2);
581
582     arg2 = xmlXPathPopString(pctx);
583     arg1 = xmlXPathPopString(pctx);
584     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) > 0);
585     xmlFree(arg1);
586     xmlFree(arg2);
587 }
588
589 void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
590 {
591     xmlChar *arg1, *arg2;
592     XSLPATTERN_CHECK_ARGS(2);
593
594     arg2 = xmlXPathPopString(pctx);
595     arg1 = xmlXPathPopString(pctx);
596     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) >= 0);
597     xmlFree(arg1);
598     xmlFree(arg2);
599 }
600
601 static void query_serror(void* ctx, xmlErrorPtr err)
602 {
603     LIBXML2_CALLBACK_SERROR(domselection_create, err);
604 }
605
606 HRESULT create_selection(xmlNodePtr node, xmlChar* query, IXMLDOMNodeList **out)
607 {
608     domselection *This = heap_alloc(sizeof(domselection));
609     xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
610     HRESULT hr;
611
612     TRACE("(%p, %s, %p)\n", node, wine_dbgstr_a((char const*)query), out);
613
614     *out = NULL;
615     if (!This || !ctxt || !query)
616     {
617         hr = E_OUTOFMEMORY;
618         goto cleanup;
619     }
620
621     This->IXMLDOMSelection_iface.lpVtbl = &domselection_vtbl;
622     This->ref = 1;
623     This->resultPos = 0;
624     This->node = node;
625     xmldoc_add_ref(This->node->doc);
626
627     ctxt->error = query_serror;
628     ctxt->node = node;
629     registerNamespaces(ctxt);
630
631     if (is_xpathmode(This->node->doc))
632     {
633         xmlXPathRegisterAllFunctions(ctxt);
634         This->result = xmlXPathEvalExpression(query, ctxt);
635     }
636     else
637     {
638         xmlChar* pattern_query = XSLPattern_to_XPath(ctxt, query);
639
640         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
641         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
642
643         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
644         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
645         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
646
647         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
648         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
649         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
650         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
651         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
652         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
653
654         This->result = xmlXPathEvalExpression(pattern_query, ctxt);
655         xmlFree(pattern_query);
656     }
657
658     if (!This->result || This->result->type != XPATH_NODESET)
659     {
660         hr = E_FAIL;
661         goto cleanup;
662     }
663
664     init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSelection_iface, &domselection_dispex);
665
666     *out = (IXMLDOMNodeList*)&This->IXMLDOMSelection_iface;
667     hr = S_OK;
668     TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
669
670 cleanup:
671     if (This && FAILED(hr))
672         IXMLDOMSelection_Release( &This->IXMLDOMSelection_iface );
673     xmlXPathFreeContext(ctxt);
674     return hr;
675 }
676
677 #endif