msxml3: Implement ::get_responseXML().
[wine] / dlls / msxml3 / queryresult.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 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "ole2.h"
32 #include "msxml6.h"
33
34 #include "msxml_private.h"
35
36 #include "wine/debug.h"
37
38 /* This file implements the object returned by a XPath query. Note that this is
39  * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
40  * They are different because the list returned by XPath queries:
41  *  - is static - gives the results for the XML tree as it existed during the
42  *    execution of the query
43  *  - supports IXMLDOMSelection (TODO)
44  *
45  */
46
47 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
48
49 #ifdef HAVE_LIBXML2
50
51 #include <libxml/xpath.h>
52 #include <libxml/xpathInternals.h>
53
54 int registerNamespaces(xmlXPathContextPtr ctxt);
55 BOOL is_xpathmode(const xmlDocPtr doc);
56 xmlChar* XSLPattern_to_XPath(xmlChar const* xslpat_str);
57
58 typedef struct _queryresult
59 {
60     DispatchEx dispex;
61     const struct IXMLDOMNodeListVtbl *lpVtbl;
62     LONG ref;
63     xmlNodePtr node;
64     xmlXPathObjectPtr result;
65     int resultPos;
66 } queryresult;
67
68 static inline queryresult *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
69 {
70     return (queryresult *)((char*)iface - FIELD_OFFSET(queryresult, lpVtbl));
71 }
72
73 #define XMLQUERYRES(x)  ((IXMLDOMNodeList*)&(x)->lpVtbl)
74
75 static HRESULT WINAPI queryresult_QueryInterface(
76     IXMLDOMNodeList *iface,
77     REFIID riid,
78     void** ppvObject )
79 {
80     queryresult *This = impl_from_IXMLDOMNodeList( iface );
81
82     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
83
84     if(!ppvObject)
85         return E_INVALIDARG;
86
87     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
88          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) )
89     {
90         *ppvObject = iface;
91     }
92     else if(dispex_query_interface(&This->dispex, riid, ppvObject))
93     {
94         return *ppvObject ? S_OK : E_NOINTERFACE;
95     }
96     else
97     {
98         FIXME("interface %s not implemented\n", debugstr_guid(riid));
99         *ppvObject = NULL;
100         return E_NOINTERFACE;
101     }
102
103     IXMLDOMNodeList_AddRef( iface );
104
105     return S_OK;
106 }
107
108 static ULONG WINAPI queryresult_AddRef(
109     IXMLDOMNodeList *iface )
110 {
111     queryresult *This = impl_from_IXMLDOMNodeList( iface );
112     return InterlockedIncrement( &This->ref );
113 }
114
115 static ULONG WINAPI queryresult_Release(
116     IXMLDOMNodeList *iface )
117 {
118     queryresult *This = impl_from_IXMLDOMNodeList( iface );
119     ULONG ref;
120
121     ref = InterlockedDecrement(&This->ref);
122     if ( ref == 0 )
123     {
124         xmlXPathFreeObject(This->result);
125         xmldoc_release(This->node->doc);
126         heap_free(This);
127     }
128
129     return ref;
130 }
131
132 static HRESULT WINAPI queryresult_GetTypeInfoCount(
133     IXMLDOMNodeList *iface,
134     UINT* pctinfo )
135 {
136     queryresult *This = impl_from_IXMLDOMNodeList( iface );
137
138     TRACE("(%p)->(%p)\n", This, pctinfo);
139
140     *pctinfo = 1;
141
142     return S_OK;
143 }
144
145 static HRESULT WINAPI queryresult_GetTypeInfo(
146     IXMLDOMNodeList *iface,
147     UINT iTInfo,
148     LCID lcid,
149     ITypeInfo** ppTInfo )
150 {
151     queryresult *This = impl_from_IXMLDOMNodeList( iface );
152     HRESULT hr;
153
154     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
155
156     hr = get_typeinfo(IXMLDOMNodeList_tid, ppTInfo);
157
158     return hr;
159 }
160
161 static HRESULT WINAPI queryresult_GetIDsOfNames(
162     IXMLDOMNodeList *iface,
163     REFIID riid,
164     LPOLESTR* rgszNames,
165     UINT cNames,
166     LCID lcid,
167     DISPID* rgDispId )
168 {
169     queryresult *This = impl_from_IXMLDOMNodeList( iface );
170     ITypeInfo *typeinfo;
171     HRESULT hr;
172
173     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
174           lcid, rgDispId);
175
176     if(!rgszNames || cNames == 0 || !rgDispId)
177         return E_INVALIDARG;
178
179     hr = get_typeinfo(IXMLDOMNodeList_tid, &typeinfo);
180     if(SUCCEEDED(hr))
181     {
182         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
183         ITypeInfo_Release(typeinfo);
184     }
185
186     return hr;
187 }
188
189 static HRESULT WINAPI queryresult_Invoke(
190     IXMLDOMNodeList *iface,
191     DISPID dispIdMember,
192     REFIID riid,
193     LCID lcid,
194     WORD wFlags,
195     DISPPARAMS* pDispParams,
196     VARIANT* pVarResult,
197     EXCEPINFO* pExcepInfo,
198     UINT* puArgErr )
199 {
200     queryresult *This = impl_from_IXMLDOMNodeList( iface );
201     ITypeInfo *typeinfo;
202     HRESULT hr;
203
204     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
205           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
206
207     hr = get_typeinfo(IXMLDOMNodeList_tid, &typeinfo);
208     if(SUCCEEDED(hr))
209     {
210         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
211                 pVarResult, pExcepInfo, puArgErr);
212         ITypeInfo_Release(typeinfo);
213     }
214
215     return hr;
216 }
217
218 static HRESULT WINAPI queryresult_get_item(
219         IXMLDOMNodeList* iface,
220         LONG index,
221         IXMLDOMNode** listItem)
222 {
223     queryresult *This = impl_from_IXMLDOMNodeList( iface );
224
225     TRACE("(%p)->(%d %p)\n", This, index, listItem);
226
227     if(!listItem)
228         return E_INVALIDARG;
229
230     *listItem = NULL;
231
232     if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
233         return S_FALSE;
234
235     *listItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, index));
236     This->resultPos = index + 1;
237
238     return S_OK;
239 }
240
241 static HRESULT WINAPI queryresult_get_length(
242         IXMLDOMNodeList* iface,
243         LONG* listLength)
244 {
245     queryresult *This = impl_from_IXMLDOMNodeList( iface );
246
247     TRACE("(%p)->(%p)\n", This, listLength);
248
249     if(!listLength)
250         return E_INVALIDARG;
251
252     *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
253     return S_OK;
254 }
255
256 static HRESULT WINAPI queryresult_nextNode(
257         IXMLDOMNodeList* iface,
258         IXMLDOMNode** nextItem)
259 {
260     queryresult *This = impl_from_IXMLDOMNodeList( iface );
261
262     TRACE("(%p)->(%p)\n", This, nextItem );
263
264     if(!nextItem)
265         return E_INVALIDARG;
266
267     *nextItem = NULL;
268
269     if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
270         return S_FALSE;
271
272     *nextItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, This->resultPos));
273     This->resultPos++;
274     return S_OK;
275 }
276
277 static HRESULT WINAPI queryresult_reset(
278         IXMLDOMNodeList* iface)
279 {
280     queryresult *This = impl_from_IXMLDOMNodeList( iface );
281
282     TRACE("%p\n", This);
283     This->resultPos = 0;
284     return S_OK;
285 }
286
287 static HRESULT WINAPI queryresult__newEnum(
288         IXMLDOMNodeList* iface,
289         IUnknown** ppUnk)
290 {
291     queryresult *This = impl_from_IXMLDOMNodeList( iface );
292     FIXME("(%p)->(%p)\n", This, ppUnk);
293     return E_NOTIMPL;
294 }
295
296
297 static const struct IXMLDOMNodeListVtbl queryresult_vtbl =
298 {
299     queryresult_QueryInterface,
300     queryresult_AddRef,
301     queryresult_Release,
302     queryresult_GetTypeInfoCount,
303     queryresult_GetTypeInfo,
304     queryresult_GetIDsOfNames,
305     queryresult_Invoke,
306     queryresult_get_item,
307     queryresult_get_length,
308     queryresult_nextNode,
309     queryresult_reset,
310     queryresult__newEnum,
311 };
312
313 static HRESULT queryresult_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
314 {
315     queryresult *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
316     WCHAR *ptr;
317     int idx = 0;
318
319     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
320         idx = idx*10 + (*ptr-'0');
321     if(*ptr)
322         return DISP_E_UNKNOWNNAME;
323
324     if(idx >= xmlXPathNodeSetGetLength(This->result->nodesetval))
325         return DISP_E_UNKNOWNNAME;
326
327     *dispid = MSXML_DISPID_CUSTOM_MIN + idx;
328     TRACE("ret %x\n", *dispid);
329     return S_OK;
330 }
331
332 static HRESULT queryresult_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
333         VARIANT *res, EXCEPINFO *ei)
334 {
335     queryresult *This = impl_from_IXMLDOMNodeList( (IXMLDOMNodeList*)iface );
336
337     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
338
339     V_VT(res) = VT_DISPATCH;
340     V_DISPATCH(res) = NULL;
341
342     switch(flags)
343     {
344         case INVOKE_PROPERTYGET:
345         {
346             IXMLDOMNode *disp = NULL;
347
348             queryresult_get_item(XMLQUERYRES(This), id - MSXML_DISPID_CUSTOM_MIN, &disp);
349             V_DISPATCH(res) = (IDispatch*)disp;
350             break;
351         }
352         default:
353         {
354             FIXME("unimplemented flags %x\n", flags);
355             break;
356         }
357     }
358
359     TRACE("ret %p\n", V_DISPATCH(res));
360
361     return S_OK;
362 }
363
364 static const dispex_static_data_vtbl_t queryresult_dispex_vtbl = {
365     queryresult_get_dispid,
366     queryresult_invoke
367 };
368
369 static const tid_t queryresult_iface_tids[] = {
370     IXMLDOMNodeList_tid,
371     0
372 };
373 static dispex_static_data_t queryresult_dispex = {
374     &queryresult_dispex_vtbl,
375     IXMLDOMSelection_tid,
376     NULL,
377     queryresult_iface_tids
378 };
379
380 void XSLPattern_invalid(xmlXPathParserContextPtr pctx, int nargs)
381 {
382     xmlXPathObjectPtr obj;
383     for (; nargs > 0; --nargs)
384     {
385         obj = valuePop(pctx);
386         xmlXPathFreeObject(obj);
387     }
388
389     obj = xmlMalloc(sizeof(xmlXPathObject));
390     obj->type = XPATH_UNDEFINED;
391     valuePush(pctx,obj);
392 }
393
394 #define XSLPATTERN_CHECK_ARGS(n) \
395     if (nargs != n) { \
396         FIXME("XSLPattern syntax error: Expected 0 arguments, got %i\n", nargs); \
397         XSLPattern_invalid(pctx, nargs); \
398         return; \
399     }
400
401
402 void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
403 {
404     XSLPATTERN_CHECK_ARGS(0);
405
406     xmlXPathPositionFunction(pctx, 0);
407     valuePush(pctx, xmlXPathNewFloat(xmlXPathPopNumber(pctx) - 1.0));
408 }
409
410 void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
411 {
412     double pos, last;
413     XSLPATTERN_CHECK_ARGS(0);
414
415     xmlXPathPositionFunction(pctx, 0);
416     pos = xmlXPathPopNumber(pctx);
417     xmlXPathLastFunction(pctx, 0);
418     last = xmlXPathPopNumber(pctx);
419     valuePush(pctx, xmlXPathNewBoolean(pos == last));
420 }
421
422 void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
423 {
424     xmlChar *arg1, *arg2;
425     XSLPATTERN_CHECK_ARGS(2);
426
427     arg2 = xmlXPathPopString(pctx);
428     arg1 = xmlXPathPopString(pctx);
429     valuePush(pctx, xmlXPathNewBoolean(xmlStrcasecmp(arg1, arg2) == 0));
430     xmlFree(arg1);
431     xmlFree(arg2);
432 }
433
434 void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
435 {
436     xmlChar *arg1, *arg2;
437     XSLPATTERN_CHECK_ARGS(2);
438
439     arg2 = xmlXPathPopString(pctx);
440     arg1 = xmlXPathPopString(pctx);
441     valuePush(pctx, xmlXPathNewBoolean(xmlStrcasecmp(arg1, arg2) != 0));
442     xmlFree(arg1);
443     xmlFree(arg2);
444 }
445
446 void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
447 {
448     xmlChar *arg1, *arg2;
449     XSLPATTERN_CHECK_ARGS(2);
450
451     arg2 = xmlXPathPopString(pctx);
452     arg1 = xmlXPathPopString(pctx);
453     valuePush(pctx, xmlXPathNewBoolean(xmlStrcasecmp(arg1, arg2) < 0));
454     xmlFree(arg1);
455     xmlFree(arg2);
456 }
457
458 void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
459 {
460     xmlChar *arg1, *arg2;
461     XSLPATTERN_CHECK_ARGS(2);
462
463     arg2 = xmlXPathPopString(pctx);
464     arg1 = xmlXPathPopString(pctx);
465     valuePush(pctx, xmlXPathNewBoolean(xmlStrcasecmp(arg1, arg2) <= 0));
466     xmlFree(arg1);
467     xmlFree(arg2);
468 }
469
470 void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
471 {
472     xmlChar *arg1, *arg2;
473     XSLPATTERN_CHECK_ARGS(2);
474
475     arg2 = xmlXPathPopString(pctx);
476     arg1 = xmlXPathPopString(pctx);
477     valuePush(pctx, xmlXPathNewBoolean(xmlStrcasecmp(arg1, arg2) > 0));
478     xmlFree(arg1);
479     xmlFree(arg2);
480 }
481
482 void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
483 {
484     xmlChar *arg1, *arg2;
485     XSLPATTERN_CHECK_ARGS(2);
486
487     arg2 = xmlXPathPopString(pctx);
488     arg1 = xmlXPathPopString(pctx);
489     valuePush(pctx, xmlXPathNewBoolean(xmlStrcasecmp(arg1, arg2) >= 0));
490     xmlFree(arg1);
491     xmlFree(arg2);
492 }
493
494 HRESULT queryresult_create(xmlNodePtr node, LPCWSTR szQuery, IXMLDOMNodeList **out)
495 {
496     queryresult *This = heap_alloc_zero(sizeof(queryresult));
497     xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
498     xmlChar *str = xmlChar_from_wchar(szQuery);
499     HRESULT hr;
500
501     TRACE("(%p, %s, %p)\n", node, wine_dbgstr_w(szQuery), out);
502
503     *out = NULL;
504     if (This == NULL || ctxt == NULL || str == NULL)
505     {
506         hr = E_OUTOFMEMORY;
507         goto cleanup;
508     }
509
510     This->lpVtbl = &queryresult_vtbl;
511     This->ref = 1;
512     This->resultPos = 0;
513     This->node = node;
514     xmldoc_add_ref(This->node->doc);
515
516     ctxt->node = node;
517     registerNamespaces(ctxt);
518
519     if (is_xpathmode(This->node->doc))
520     {
521         xmlXPathRegisterAllFunctions(ctxt);
522     }
523     else
524     {
525         xmlChar* tmp;
526         int len;
527         WARN("Attempting XSLPattern emulation (experimental).\n");
528         tmp = XSLPattern_to_XPath(str);
529         len = (xmlStrlen(tmp)+1)*sizeof(xmlChar);
530         str = heap_realloc(str, len);
531         memcpy(str, tmp, len);
532         xmlFree(tmp);
533
534         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
535         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
536
537         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
538         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
539
540         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
541         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
542         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
543         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
544         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
545         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
546     }
547
548     This->result = xmlXPathEval(str, ctxt);
549     if (!This->result || This->result->type != XPATH_NODESET)
550     {
551         hr = E_FAIL;
552         goto cleanup;
553     }
554
555     init_dispex(&This->dispex, (IUnknown*)&This->lpVtbl, &queryresult_dispex);
556
557     *out = (IXMLDOMNodeList *) &This->lpVtbl;
558     hr = S_OK;
559     TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
560
561 cleanup:
562     if (This != NULL && FAILED(hr))
563         IXMLDOMNodeList_Release( (IXMLDOMNodeList*) &This->lpVtbl );
564     xmlXPathFreeContext(ctxt);
565     heap_free(str);
566     return hr;
567 }
568
569 #endif