msxml3: Fix node interface leak after nextNode().
[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 #include "msxml2did.h"
41
42 #include "msxml_private.h"
43
44 #include "wine/debug.h"
45
46 /* This file implements the object returned by a XPath query. Note that this is
47  * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
48  * They are different because the list returned by XPath queries:
49  *  - is static - gives the results for the XML tree as it existed during the
50  *    execution of the query
51  *  - supports IXMLDOMSelection
52  *
53  */
54
55 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
56
57 #ifdef HAVE_LIBXML2
58
59 int registerNamespaces(xmlXPathContextPtr ctxt);
60 xmlChar* XSLPattern_to_XPath(xmlXPathContextPtr ctxt, xmlChar const* xslpat_str);
61
62 typedef struct _enumvariant
63 {
64     IEnumVARIANT IEnumVARIANT_iface;
65     LONG ref;
66
67     IXMLDOMSelection *selection;
68     BOOL own;
69
70     LONG pos;
71 } enumvariant;
72
73 typedef struct _domselection
74 {
75     DispatchEx dispex;
76     IXMLDOMSelection IXMLDOMSelection_iface;
77     LONG ref;
78     xmlNodePtr node;
79     xmlXPathObjectPtr result;
80     int resultPos;
81     IEnumVARIANT *enumvariant;
82 } domselection;
83
84 static inline domselection *impl_from_IXMLDOMSelection( IXMLDOMSelection *iface )
85 {
86     return CONTAINING_RECORD(iface, domselection, IXMLDOMSelection_iface);
87 }
88
89 static inline enumvariant *impl_from_IEnumVARIANT( IEnumVARIANT *iface )
90 {
91     return CONTAINING_RECORD(iface, enumvariant, IEnumVARIANT_iface);
92 }
93
94 static HRESULT create_enumvariant(IXMLDOMSelection*, BOOL, IUnknown**);
95
96 static HRESULT WINAPI domselection_QueryInterface(
97     IXMLDOMSelection *iface,
98     REFIID riid,
99     void** ppvObject )
100 {
101     domselection *This = impl_from_IXMLDOMSelection( iface );
102
103     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
104
105     if(!ppvObject)
106         return E_INVALIDARG;
107
108     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
109          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) ||
110          IsEqualGUID( riid, &IID_IXMLDOMSelection ))
111     {
112         *ppvObject = &This->IXMLDOMSelection_iface;
113     }
114     else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
115     {
116         if (!This->enumvariant)
117         {
118             HRESULT hr = create_enumvariant(iface, FALSE, (IUnknown**)&This->enumvariant);
119             if (FAILED(hr)) return hr;
120         }
121
122         return IEnumVARIANT_QueryInterface(This->enumvariant, &IID_IEnumVARIANT, ppvObject);
123     }
124     else if (dispex_query_interface(&This->dispex, riid, ppvObject))
125     {
126         return *ppvObject ? S_OK : E_NOINTERFACE;
127     }
128     else
129     {
130         TRACE("interface %s not implemented\n", debugstr_guid(riid));
131         *ppvObject = NULL;
132         return E_NOINTERFACE;
133     }
134
135     IXMLDOMSelection_AddRef( iface );
136
137     return S_OK;
138 }
139
140 static ULONG WINAPI domselection_AddRef(
141     IXMLDOMSelection *iface )
142 {
143     domselection *This = impl_from_IXMLDOMSelection( iface );
144     ULONG ref = InterlockedIncrement( &This->ref );
145     TRACE("(%p)->(%d)\n", This, ref);
146     return ref;
147 }
148
149 static ULONG WINAPI domselection_Release(
150     IXMLDOMSelection *iface )
151 {
152     domselection *This = impl_from_IXMLDOMSelection( iface );
153     ULONG ref = InterlockedDecrement(&This->ref);
154
155     TRACE("(%p)->(%d)\n", This, ref);
156     if ( ref == 0 )
157     {
158         xmlXPathFreeObject(This->result);
159         xmldoc_release(This->node->doc);
160         if (This->enumvariant) IEnumVARIANT_Release(This->enumvariant);
161         release_dispex(&This->dispex);
162         heap_free(This);
163     }
164
165     return ref;
166 }
167
168 static HRESULT WINAPI domselection_GetTypeInfoCount(
169     IXMLDOMSelection *iface,
170     UINT* pctinfo )
171 {
172     domselection *This = impl_from_IXMLDOMSelection( iface );
173     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
174 }
175
176 static HRESULT WINAPI domselection_GetTypeInfo(
177     IXMLDOMSelection *iface,
178     UINT iTInfo,
179     LCID lcid,
180     ITypeInfo** ppTInfo )
181 {
182     domselection *This = impl_from_IXMLDOMSelection( iface );
183     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
184         iTInfo, lcid, ppTInfo);
185 }
186
187 static HRESULT WINAPI domselection_GetIDsOfNames(
188     IXMLDOMSelection *iface,
189     REFIID riid,
190     LPOLESTR* rgszNames,
191     UINT cNames,
192     LCID lcid,
193     DISPID* rgDispId )
194 {
195     domselection *This = impl_from_IXMLDOMSelection( iface );
196     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
197         riid, rgszNames, cNames, lcid, rgDispId);
198 }
199
200 static HRESULT WINAPI domselection_Invoke(
201     IXMLDOMSelection *iface,
202     DISPID dispIdMember,
203     REFIID riid,
204     LCID lcid,
205     WORD wFlags,
206     DISPPARAMS* pDispParams,
207     VARIANT* pVarResult,
208     EXCEPINFO* pExcepInfo,
209     UINT* puArgErr )
210 {
211     domselection *This = impl_from_IXMLDOMSelection( iface );
212     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
213         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
214 }
215
216 static HRESULT WINAPI domselection_get_item(
217         IXMLDOMSelection* iface,
218         LONG index,
219         IXMLDOMNode** listItem)
220 {
221     domselection *This = impl_from_IXMLDOMSelection( iface );
222
223     TRACE("(%p)->(%d %p)\n", This, index, listItem);
224
225     if(!listItem)
226         return E_INVALIDARG;
227
228     *listItem = NULL;
229
230     if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
231         return S_FALSE;
232
233     *listItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, index));
234     This->resultPos = index + 1;
235
236     return S_OK;
237 }
238
239 static HRESULT WINAPI domselection_get_length(
240         IXMLDOMSelection* iface,
241         LONG* listLength)
242 {
243     domselection *This = impl_from_IXMLDOMSelection( iface );
244
245     TRACE("(%p)->(%p)\n", This, listLength);
246
247     if(!listLength)
248         return E_INVALIDARG;
249
250     *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
251     return S_OK;
252 }
253
254 static HRESULT WINAPI domselection_nextNode(
255         IXMLDOMSelection* iface,
256         IXMLDOMNode** nextItem)
257 {
258     domselection *This = impl_from_IXMLDOMSelection( iface );
259
260     TRACE("(%p)->(%p)\n", This, nextItem );
261
262     if(!nextItem)
263         return E_INVALIDARG;
264
265     *nextItem = NULL;
266
267     if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
268         return S_FALSE;
269
270     *nextItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, This->resultPos));
271     This->resultPos++;
272     return S_OK;
273 }
274
275 static HRESULT WINAPI domselection_reset(
276         IXMLDOMSelection* iface)
277 {
278     domselection *This = impl_from_IXMLDOMSelection( iface );
279
280     TRACE("%p\n", This);
281     This->resultPos = 0;
282     return S_OK;
283 }
284
285 static HRESULT WINAPI domselection_get__newEnum(
286         IXMLDOMSelection* iface,
287         IUnknown** ppUnk)
288 {
289     domselection *This = impl_from_IXMLDOMSelection( iface );
290
291     TRACE("(%p)->(%p)\n", This, ppUnk);
292
293     return create_enumvariant(iface, TRUE, ppUnk);
294 }
295
296 static HRESULT WINAPI domselection_get_expr(
297         IXMLDOMSelection* iface,
298         BSTR *p)
299 {
300     domselection *This = impl_from_IXMLDOMSelection( iface );
301     FIXME("(%p)->(%p)\n", This, p);
302     return E_NOTIMPL;
303 }
304
305 static HRESULT WINAPI domselection_put_expr(
306         IXMLDOMSelection* iface,
307         BSTR p)
308 {
309     domselection *This = impl_from_IXMLDOMSelection( iface );
310     FIXME("(%p)->(%s)\n", This, debugstr_w(p));
311     return E_NOTIMPL;
312 }
313
314 static HRESULT WINAPI domselection_get_context(
315         IXMLDOMSelection* iface,
316         IXMLDOMNode **node)
317 {
318     domselection *This = impl_from_IXMLDOMSelection( iface );
319     FIXME("(%p)->(%p)\n", This, node);
320     return E_NOTIMPL;
321 }
322
323 static HRESULT WINAPI domselection_putref_context(
324         IXMLDOMSelection* iface,
325         IXMLDOMNode *node)
326 {
327     domselection *This = impl_from_IXMLDOMSelection( iface );
328     FIXME("(%p)->(%p)\n", This, node);
329     return E_NOTIMPL;
330 }
331
332 static HRESULT WINAPI domselection_peekNode(
333         IXMLDOMSelection* iface,
334         IXMLDOMNode **node)
335 {
336     domselection *This = impl_from_IXMLDOMSelection( iface );
337     FIXME("(%p)->(%p)\n", This, node);
338     return E_NOTIMPL;
339 }
340
341 static HRESULT WINAPI domselection_matches(
342         IXMLDOMSelection* iface,
343         IXMLDOMNode *node,
344         IXMLDOMNode **out_node)
345 {
346     domselection *This = impl_from_IXMLDOMSelection( iface );
347     FIXME("(%p)->(%p %p)\n", This, node, out_node);
348     return E_NOTIMPL;
349 }
350
351 static HRESULT WINAPI domselection_removeNext(
352         IXMLDOMSelection* iface,
353         IXMLDOMNode **node)
354 {
355     domselection *This = impl_from_IXMLDOMSelection( iface );
356     FIXME("(%p)->(%p)\n", This, node);
357     return E_NOTIMPL;
358 }
359
360 static HRESULT WINAPI domselection_removeAll(
361         IXMLDOMSelection* iface)
362 {
363     domselection *This = impl_from_IXMLDOMSelection( iface );
364     FIXME("(%p)\n", This);
365     return E_NOTIMPL;
366 }
367
368 static HRESULT WINAPI domselection_clone(
369         IXMLDOMSelection* iface,
370         IXMLDOMSelection **node)
371 {
372     domselection *This = impl_from_IXMLDOMSelection( iface );
373     FIXME("(%p)->(%p)\n", This, node);
374     return E_NOTIMPL;
375 }
376
377 static HRESULT WINAPI domselection_getProperty(
378         IXMLDOMSelection* iface,
379         BSTR p,
380         VARIANT *var)
381 {
382     domselection *This = impl_from_IXMLDOMSelection( iface );
383     FIXME("(%p)->(%s %p)\n", This, debugstr_w(p), var);
384     return E_NOTIMPL;
385 }
386
387 static HRESULT WINAPI domselection_setProperty(
388         IXMLDOMSelection* iface,
389         BSTR p,
390         VARIANT var)
391 {
392     domselection *This = impl_from_IXMLDOMSelection( iface );
393     FIXME("(%p)->(%s %s)\n", This, debugstr_w(p), debugstr_variant(&var));
394     return E_NOTIMPL;
395 }
396
397 static const struct IXMLDOMSelectionVtbl domselection_vtbl =
398 {
399     domselection_QueryInterface,
400     domselection_AddRef,
401     domselection_Release,
402     domselection_GetTypeInfoCount,
403     domselection_GetTypeInfo,
404     domselection_GetIDsOfNames,
405     domselection_Invoke,
406     domselection_get_item,
407     domselection_get_length,
408     domselection_nextNode,
409     domselection_reset,
410     domselection_get__newEnum,
411     domselection_get_expr,
412     domselection_put_expr,
413     domselection_get_context,
414     domselection_putref_context,
415     domselection_peekNode,
416     domselection_matches,
417     domselection_removeNext,
418     domselection_removeAll,
419     domselection_clone,
420     domselection_getProperty,
421     domselection_setProperty
422 };
423
424 /* IEnumVARIANT support */
425 static HRESULT WINAPI enumvariant_QueryInterface(
426     IEnumVARIANT *iface,
427     REFIID riid,
428     void** ppvObject )
429 {
430     enumvariant *This = impl_from_IEnumVARIANT( iface );
431
432     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
433
434     *ppvObject = NULL;
435
436     if (IsEqualGUID( riid, &IID_IUnknown ))
437     {
438         if (This->own)
439             *ppvObject = &This->IEnumVARIANT_iface;
440         else
441             return IXMLDOMSelection_QueryInterface(This->selection, riid, ppvObject);
442     }
443     else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
444     {
445         *ppvObject = &This->IEnumVARIANT_iface;
446     }
447     else
448         return IXMLDOMSelection_QueryInterface(This->selection, riid, ppvObject);
449
450     IEnumVARIANT_AddRef( iface );
451
452     return S_OK;
453 }
454
455 static ULONG WINAPI enumvariant_AddRef(IEnumVARIANT *iface )
456 {
457     enumvariant *This = impl_from_IEnumVARIANT( iface );
458     ULONG ref = InterlockedIncrement( &This->ref );
459     TRACE("(%p)->(%d)\n", This, ref);
460     return ref;
461 }
462
463 static ULONG WINAPI enumvariant_Release(IEnumVARIANT *iface )
464 {
465     enumvariant *This = impl_from_IEnumVARIANT( iface );
466     ULONG ref = InterlockedDecrement(&This->ref);
467
468     TRACE("(%p)->(%d)\n", This, ref);
469     if ( ref == 0 )
470     {
471         if (This->own) IXMLDOMSelection_Release(This->selection);
472         heap_free(This);
473     }
474
475     return ref;
476 }
477
478 static HRESULT WINAPI enumvariant_Next(
479     IEnumVARIANT *iface,
480     ULONG celt,
481     VARIANT *var,
482     ULONG *fetched)
483 {
484     enumvariant *This = impl_from_IEnumVARIANT( iface );
485     IXMLDOMNode *node;
486     ULONG ret_count = 0;
487
488     TRACE("(%p)->(%u %p %p)\n", This, celt, var, fetched);
489
490     if (fetched) *fetched = 0;
491
492     if (celt && !var) return E_INVALIDARG;
493
494     for (; celt > 0; celt--, var++, This->pos++)
495     {
496         IDispatch *disp = NULL;
497         HRESULT hr;
498
499         node = NULL;
500         hr = IXMLDOMSelection_get_item(This->selection, This->pos, &node);
501         if (hr != S_OK) break;
502
503         IXMLDOMNode_QueryInterface(node, &IID_IDispatch, (void**)&disp);
504         IXMLDOMNode_Release(node);
505
506         V_VT(var) = VT_DISPATCH;
507         V_DISPATCH(var) = disp;
508
509         ret_count++;
510     }
511
512     if (fetched) (*fetched)++;
513
514     /* we need to advance one step more for some reason */
515     if (ret_count)
516     {
517         node = NULL;
518         IXMLDOMSelection_nextNode(This->selection, &node);
519         if (node) IXMLDOMNode_Release(node);
520     }
521
522     return celt == 0 ? S_OK : S_FALSE;
523 }
524
525 static HRESULT WINAPI enumvariant_Skip(
526     IEnumVARIANT *iface,
527     ULONG celt)
528 {
529     enumvariant *This = impl_from_IEnumVARIANT( iface );
530     FIXME("(%p)->(%u): stub\n", This, celt);
531     return E_NOTIMPL;
532 }
533
534 static HRESULT WINAPI enumvariant_Reset(IEnumVARIANT *iface)
535 {
536     enumvariant *This = impl_from_IEnumVARIANT( iface );
537     FIXME("(%p): stub\n", This);
538     return E_NOTIMPL;
539 }
540
541 static HRESULT WINAPI enumvariant_Clone(
542     IEnumVARIANT *iface, IEnumVARIANT **ppenum)
543 {
544     enumvariant *This = impl_from_IEnumVARIANT( iface );
545     FIXME("(%p)->(%p): stub\n", This, ppenum);
546     return E_NOTIMPL;
547 }
548
549 static const struct IEnumVARIANTVtbl EnumVARIANTVtbl =
550 {
551     enumvariant_QueryInterface,
552     enumvariant_AddRef,
553     enumvariant_Release,
554     enumvariant_Next,
555     enumvariant_Skip,
556     enumvariant_Reset,
557     enumvariant_Clone
558 };
559
560 static HRESULT create_enumvariant(IXMLDOMSelection *selection, BOOL own, IUnknown **penum)
561 {
562     enumvariant *This;
563
564     This = heap_alloc(sizeof(enumvariant));
565     if (!This) return E_OUTOFMEMORY;
566
567     This->IEnumVARIANT_iface.lpVtbl = &EnumVARIANTVtbl;
568     This->ref = 0;
569     This->selection = selection;
570     This->own = own;
571     This->pos = 0;
572
573     if (This->own)
574         IXMLDOMSelection_AddRef(selection);
575
576     *penum = (IUnknown*)&This->IEnumVARIANT_iface;
577     IUnknown_AddRef(*penum);
578     return S_OK;
579 }
580
581 static HRESULT domselection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
582 {
583     WCHAR *ptr;
584     int idx = 0;
585
586     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
587         idx = idx*10 + (*ptr-'0');
588     if(*ptr)
589         return DISP_E_UNKNOWNNAME;
590
591     *dispid = DISPID_DOM_COLLECTION_BASE + idx;
592     TRACE("ret %x\n", *dispid);
593     return S_OK;
594 }
595
596 static HRESULT domselection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
597         VARIANT *res, EXCEPINFO *ei)
598 {
599     domselection *This = impl_from_IXMLDOMSelection( (IXMLDOMSelection*)iface );
600
601     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
602
603     V_VT(res) = VT_DISPATCH;
604     V_DISPATCH(res) = NULL;
605
606     if (id < DISPID_DOM_COLLECTION_BASE || id > DISPID_DOM_COLLECTION_MAX)
607         return DISP_E_UNKNOWNNAME;
608
609     switch(flags)
610     {
611         case INVOKE_PROPERTYGET:
612         {
613             IXMLDOMNode *disp = NULL;
614
615             IXMLDOMSelection_get_item(&This->IXMLDOMSelection_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
616             V_DISPATCH(res) = (IDispatch*)disp;
617             break;
618         }
619         default:
620         {
621             FIXME("unimplemented flags %x\n", flags);
622             break;
623         }
624     }
625
626     TRACE("ret %p\n", V_DISPATCH(res));
627
628     return S_OK;
629 }
630
631 static const dispex_static_data_vtbl_t domselection_dispex_vtbl = {
632     domselection_get_dispid,
633     domselection_invoke
634 };
635
636 static const tid_t domselection_iface_tids[] = {
637     IXMLDOMSelection_tid,
638     0
639 };
640 static dispex_static_data_t domselection_dispex = {
641     &domselection_dispex_vtbl,
642     IXMLDOMSelection_tid,
643     NULL,
644     domselection_iface_tids
645 };
646
647 #define XSLPATTERN_CHECK_ARGS(n) \
648     if (nargs != n) { \
649         FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
650         xmlXPathSetArityError(pctx); \
651         return; \
652     }
653
654
655 static void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
656 {
657     XSLPATTERN_CHECK_ARGS(0);
658
659     xmlXPathPositionFunction(pctx, 0);
660     xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
661 }
662
663 static void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
664 {
665     double pos, last;
666     XSLPATTERN_CHECK_ARGS(0);
667
668     xmlXPathPositionFunction(pctx, 0);
669     pos = xmlXPathPopNumber(pctx);
670     xmlXPathLastFunction(pctx, 0);
671     last = xmlXPathPopNumber(pctx);
672     xmlXPathReturnBoolean(pctx, pos == last);
673 }
674
675 static void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
676 {
677     XSLPATTERN_CHECK_ARGS(0);
678     xmlXPathReturnNumber(pctx, pctx->context->node->type);
679 }
680
681 static void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
682 {
683     xmlChar *arg1, *arg2;
684     XSLPATTERN_CHECK_ARGS(2);
685
686     arg2 = xmlXPathPopString(pctx);
687     arg1 = xmlXPathPopString(pctx);
688     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
689     xmlFree(arg1);
690     xmlFree(arg2);
691 }
692
693 static void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
694 {
695     xmlChar *arg1, *arg2;
696     XSLPATTERN_CHECK_ARGS(2);
697
698     arg2 = xmlXPathPopString(pctx);
699     arg1 = xmlXPathPopString(pctx);
700     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) != 0);
701     xmlFree(arg1);
702     xmlFree(arg2);
703 }
704
705 static void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
706 {
707     xmlChar *arg1, *arg2;
708     XSLPATTERN_CHECK_ARGS(2);
709
710     arg2 = xmlXPathPopString(pctx);
711     arg1 = xmlXPathPopString(pctx);
712     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) < 0);
713     xmlFree(arg1);
714     xmlFree(arg2);
715 }
716
717 static void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
718 {
719     xmlChar *arg1, *arg2;
720     XSLPATTERN_CHECK_ARGS(2);
721
722     arg2 = xmlXPathPopString(pctx);
723     arg1 = xmlXPathPopString(pctx);
724     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) <= 0);
725     xmlFree(arg1);
726     xmlFree(arg2);
727 }
728
729 static void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
730 {
731     xmlChar *arg1, *arg2;
732     XSLPATTERN_CHECK_ARGS(2);
733
734     arg2 = xmlXPathPopString(pctx);
735     arg1 = xmlXPathPopString(pctx);
736     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) > 0);
737     xmlFree(arg1);
738     xmlFree(arg2);
739 }
740
741 static void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
742 {
743     xmlChar *arg1, *arg2;
744     XSLPATTERN_CHECK_ARGS(2);
745
746     arg2 = xmlXPathPopString(pctx);
747     arg1 = xmlXPathPopString(pctx);
748     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) >= 0);
749     xmlFree(arg1);
750     xmlFree(arg2);
751 }
752
753 static void query_serror(void* ctx, xmlErrorPtr err)
754 {
755     LIBXML2_CALLBACK_SERROR(domselection_create, err);
756 }
757
758 HRESULT create_selection(xmlNodePtr node, xmlChar* query, IXMLDOMNodeList **out)
759 {
760     domselection *This = heap_alloc(sizeof(domselection));
761     xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
762     HRESULT hr;
763
764     TRACE("(%p, %s, %p)\n", node, debugstr_a((char const*)query), out);
765
766     *out = NULL;
767     if (!This || !ctxt || !query)
768     {
769         xmlXPathFreeContext(ctxt);
770         heap_free(This);
771         return E_OUTOFMEMORY;
772     }
773
774     This->IXMLDOMSelection_iface.lpVtbl = &domselection_vtbl;
775     This->ref = 1;
776     This->resultPos = 0;
777     This->node = node;
778     This->enumvariant = NULL;
779     init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSelection_iface, &domselection_dispex);
780     xmldoc_add_ref(This->node->doc);
781
782     ctxt->error = query_serror;
783     ctxt->node = node;
784     registerNamespaces(ctxt);
785
786     if (is_xpathmode(This->node->doc))
787     {
788         xmlXPathRegisterAllFunctions(ctxt);
789         This->result = xmlXPathEvalExpression(query, ctxt);
790     }
791     else
792     {
793         xmlChar* pattern_query = XSLPattern_to_XPath(ctxt, query);
794
795         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
796         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
797
798         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
799         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
800         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
801
802         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
803         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
804         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
805         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
806         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
807         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
808
809         This->result = xmlXPathEvalExpression(pattern_query, ctxt);
810         xmlFree(pattern_query);
811     }
812
813     if (!This->result || This->result->type != XPATH_NODESET)
814     {
815         hr = E_FAIL;
816         goto cleanup;
817     }
818
819     *out = (IXMLDOMNodeList*)&This->IXMLDOMSelection_iface;
820     hr = S_OK;
821     TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
822
823 cleanup:
824     if (This && FAILED(hr))
825         IXMLDOMSelection_Release( &This->IXMLDOMSelection_iface );
826     xmlXPathFreeContext(ctxt);
827     return hr;
828 }
829
830 #endif