msxml3[2/4]: improve the XPath queries handling

Mikołaj Zalewski mikolaj at zalewski.pl
Fri May 11 10:47:51 CDT 2007


  The current code uses xsltCompMatchList but that is not correct - that 
function find nodes for which there exist a context node that the 
pattern matches (as in <xsl:template match="...">). We want to find 
nodes for which the pattern matches with the given node as the context 
node. The xmlXPath* functions should be used for that. Also (probably to 
reduce the numer of false positives) the previous code matched only the 
children of the current node. The implementation with xmlXPathEval 
doesn't have this limitation.
  As the IXMLDOMNodeList object returned by the childNodes differs in 
some aspects from the object returned by queries we implement it as two 
classes - the nodelist.c handles now only listing the children and the 
new queryresult.c handles XPath query results. We currently don't 
support namespaces (that will require parsing of the 
"SelectionNamespaces" property and adding then to the XPath context). 
Also this code still handles the old XSLPattern queries as XPath queries 
what can give wrong results.
-------------- next part --------------
From c26a8e04f8cec47e37e4ca4e4a843473c2ae550e Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Miko=C5=82aj_Zalewski?= <mikolaj at zalewski.pl>
Date: Fri, 11 May 2007 17:03:13 +0200
Subject: [PATCH] msxml3: improve the XPath queries handling

---
 dlls/msxml3/Makefile.in       |    1 +
 dlls/msxml3/domdoc.c          |   15 ++-
 dlls/msxml3/msxml_private.h   |    5 +-
 dlls/msxml3/node.c            |   33 +----
 dlls/msxml3/nodelist.c        |  227 ++++----------------------------
 dlls/msxml3/queryresult.c     |  292 +++++++++++++++++++++++++++++++++++++++++
 dlls/msxml3/tests/Makefile.in |    2 +-
 dlls/msxml3/tests/domdoc.c    |  250 +++++++++++++++++++++++++++++++++++
 8 files changed, 584 insertions(+), 241 deletions(-)

diff --git a/dlls/msxml3/Makefile.in b/dlls/msxml3/Makefile.in
index 17f723f..c888e23 100644
--- a/dlls/msxml3/Makefile.in
+++ b/dlls/msxml3/Makefile.in
@@ -20,6 +20,7 @@ C_SRCS = \
 	nodemap.c \
 	parseerror.c \
 	pi.c \
+	queryresult.c \
 	regsvr.c \
 	schema.c \
 	text.c \
diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c
index 6954f81..347605b 100644
--- a/dlls/msxml3/domdoc.c
+++ b/dlls/msxml3/domdoc.c
@@ -823,15 +823,18 @@ static HRESULT WINAPI domdoc_getElementsByTagName(
     IXMLDOMNodeList** resultList )
 {
     domdoc *This = impl_from_IXMLDOMDocument2( iface );
-    xmlChar *name;
+    LPWSTR szPattern;
+    HRESULT hr;
     TRACE("(%p)->(%s, %p)\n", This, debugstr_w(tagName), resultList);
 
-    name = xmlChar_from_wchar((WCHAR*)tagName);
-    *resultList = create_filtered_nodelist((xmlNodePtr)get_doc(This), name, TRUE);
-    HeapFree(GetProcessHeap(), 0, name);
+    szPattern = HeapAlloc(GetProcessHeap(), 0, 2+lstrlenW(tagName)+1);
+    szPattern[0] = szPattern[1] = '/';
+    lstrcpyW(szPattern + 2, tagName);
 
-    if(!*resultList) return S_FALSE;
-    return S_OK;
+    hr = queryresult_create((xmlNodePtr)get_doc(This), szPattern, resultList);
+    HeapFree(GetProcessHeap(), 0, szPattern);
+
+    return hr;
 }
 
 static DOMNodeType get_node_type(VARIANT Type)
diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h
index 4f459b9..39aaa00 100644
--- a/dlls/msxml3/msxml_private.h
+++ b/dlls/msxml3/msxml_private.h
@@ -41,9 +41,10 @@ extern IUnknown         *create_attribute( xmlNodePtr attribute );
 extern IUnknown         *create_text( xmlNodePtr text );
 extern IUnknown         *create_pi( xmlNodePtr pi );
 extern IUnknown         *create_comment( xmlNodePtr comment );
-extern IXMLDOMNodeList  *create_nodelist( xmlNodePtr node );
+extern IXMLDOMNodeList  *create_children_nodelist( xmlNodePtr );
 extern IXMLDOMNamedNodeMap *create_nodemap( IXMLDOMNode *node );
-extern IXMLDOMNodeList  *create_filtered_nodelist( xmlNodePtr, const xmlChar *, BOOL );
+
+extern HRESULT queryresult_create( xmlNodePtr, LPWSTR, IXMLDOMNodeList ** );
 
 extern void attach_xmlnode( IXMLDOMNode *node, xmlNodePtr xmlnode );
 
diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c
index 512e4e9..8c48ecf 100644
--- a/dlls/msxml3/node.c
+++ b/dlls/msxml3/node.c
@@ -311,23 +311,10 @@ static HRESULT WINAPI xmlnode_get_childNodes(
     if ( !childList )
         return E_INVALIDARG;
 
-    switch(This->node->type)
-    {
-    case XML_ELEMENT_NODE:
-        *childList = create_filtered_nodelist( This->node->children, (const xmlChar *)"*", FALSE );
-        break;
+    *childList = create_children_nodelist(This->node);
+    if (*childList == NULL)
+        return E_OUTOFMEMORY;
 
-    case XML_ATTRIBUTE_NODE:
-        *childList = create_filtered_nodelist( This->node->children, (const xmlChar *)"node()", FALSE );
-        break;
-
-    default:
-        FIXME("unhandled node type %d\n", This->node->type);
-        break;
-    }
-
-    if (!*childList)
-        return S_FALSE;
     return S_OK;
 }
 
@@ -658,21 +645,10 @@ static HRESULT WINAPI xmlnode_selectNodes(
     IXMLDOMNodeList** resultList)
 {
     xmlnode *This = impl_from_IXMLDOMNode( iface );
-    xmlChar *str = NULL;
-    HRESULT r = E_FAIL;
 
     TRACE("%p %s %p\n", This, debugstr_w(queryString), resultList );
 
-    str = xmlChar_from_wchar( queryString );
-    if (!str)
-        return r;
-
-    if( !This->node->children )
-        return S_FALSE;
-
-    *resultList = create_filtered_nodelist( This->node->children, str, FALSE );
-    HeapFree( GetProcessHeap(), 0, str );
-    return S_OK;
+    return queryresult_create( This->node, queryString, resultList );
 }
 
 static HRESULT WINAPI xmlnode_selectSingleNode(
@@ -686,6 +662,7 @@ static HRESULT WINAPI xmlnode_selectSingleNode(
 
     TRACE("%p %s %p\n", This, debugstr_w(queryString), resultNode );
 
+    *resultNode = NULL;
     r = IXMLDOMNode_selectNodes(iface, queryString, &list);
     if(r == S_OK)
     {
diff --git a/dlls/msxml3/nodelist.c b/dlls/msxml3/nodelist.c
index a1b4306..d2346f4 100644
--- a/dlls/msxml3/nodelist.c
+++ b/dlls/msxml3/nodelist.c
@@ -33,147 +33,25 @@
 
 #include "wine/debug.h"
 
+/* This file implements the object returned by childNodes property. Note that this is
+ * not the IXMLDOMNodeList returned by XPath querites - it's implemented in xpathresult.c.
+ * They are different because the list returned by childNodes:
+ *  - is "live" - changes to the XML tree are automatically reflected in the list
+ *  - doesn't supports IXMLDOMSelection
+ *  - note that an attribute node have a text child in DOM but not in the XPath data model
+ *    thus the child is inaccessible by an XPath query
+ */
+
 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
 
 #ifdef HAVE_LIBXML2
 
-#ifdef HAVE_LIBXSLT
-
-#ifdef HAVE_LIBXSLT_PATTERN_H
-#include <libxslt/pattern.h>
-#endif
-#ifdef HAVE_LIBXSLT_TRANSFORM_H
-#include <libxslt/transform.h>
-#endif
-
-struct xslt_info {
-    xsltTransformContextPtr ctxt;
-    xsltCompMatchPtr pattern;
-    xsltStylesheetPtr sheet;
-};
-
-static void xslt_info_init( struct xslt_info *info )
-{
-    info->ctxt = NULL;
-    info->pattern = NULL;
-    info->sheet = NULL;
-}
-
-static int create_xslt_parser( struct xslt_info *info, xmlNodePtr node, const xmlChar *str )
-{
-    if(!node) return 1;
-
-    info->sheet = xsltNewStylesheet();
-    if (!info->sheet)
-        return 0;
-
-    info->ctxt = xsltNewTransformContext( info->sheet, node->doc );
-    if (!info->ctxt)
-        return 0;
-
-    info->pattern = xsltCompilePattern( str, node->doc,
-                                        node, info->sheet, info->ctxt );
-    if (!info->pattern)
-        return 0;
-    return 1;
-}
-
-static void free_xslt_info( struct xslt_info *info )
-{
-    if (info->pattern)
-        xsltFreeCompMatchList( info->pattern );
-    if (info->sheet)
-        xsltFreeStylesheet( info->sheet );
-    if (info->ctxt)
-        xsltFreeTransformContext( info->ctxt );
-}
-
-
-static xmlNodePtr get_next_node( struct xslt_info *info, xmlNodePtr node, xmlNodePtr *top_level_node );
-
-static HRESULT xslt_next_match( struct xslt_info *info, xmlNodePtr *node, xmlNodePtr *top_level_node )
-{
-    if (!info->ctxt)
-        return S_FALSE;
- 
-    /* make sure that the current element matches the pattern */
-    while ( *node )
-    {
-        int r;
-
-        r = xsltTestCompMatchList( info->ctxt, *node, info->pattern );
-        if ( 1 == r )
-        {
-            TRACE("Matched %p (%s)\n", *node, (*node)->name );
-            return S_OK;
-        }
-        if (r != 0)
-        {
-            ERR("Pattern match failed\n");
-            return E_FAIL;
-        }
-        *node = get_next_node(info, *node, top_level_node);
-    }
-    return S_OK;
-}
-
-#else
-
-struct xslt_info {
-    /* empty */
-};
-
-static void xslt_info_init( struct xslt_info *info )
-{
-}
-
-void free_xslt_info( struct xslt_info *info )
-{
-}
-
-static int create_xslt_parser( struct xslt_info *info, xmlNodePtr node, const xmlChar *str )
-{
-    MESSAGE("libxslt was missing at compile time\n");
-    return 0;
-}
-
-static HRESULT xslt_next_match( struct xslt_info *info, xmlNodePtr *node, xmlNodePtr *top_level_node )
-{
-    return S_FALSE;
-}
-
-#endif
-
-static xmlNodePtr get_next_node( struct xslt_info *info, xmlNodePtr node, xmlNodePtr *top_level_node )
-{
-    if(!top_level_node) return node->next;
-
-    if(node->children) return node->children;
-    if(node->next)
-    {
-        if(node == *top_level_node)
-            *top_level_node = node->next;
-        return node->next;
-    }
-
-    if(node != *top_level_node && node->parent)
-    {
-        if(node->parent == *top_level_node)
-            *top_level_node = node->parent->next;
-        return node->parent->next;
-    }
-    return NULL;
-}
-
 typedef struct _xmlnodelist
 {
     const struct IXMLDOMNodeListVtbl *lpVtbl;
     LONG ref;
-    xmlNodePtr node;
+    xmlNodePtr parent;
     xmlNodePtr current;
-    xmlNodePtr top_level_node;
-    BOOL enum_children;
-    struct xslt_info xinfo;
 } xmlnodelist;
 
 static inline xmlnodelist *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
@@ -222,8 +100,7 @@ static ULONG WINAPI xmlnodelist_Release(
     ref = InterlockedDecrement( &This->ref );
     if ( ref == 0 )
     {
-        free_xslt_info( &This->xinfo );
-        if(This->node) xmldoc_release( This->node->doc );
+        xmldoc_release( This->parent->doc );
         HeapFree( GetProcessHeap(), 0, This );
     }
 
@@ -281,10 +158,8 @@ static HRESULT WINAPI xmlnodelist_get_item(
         IXMLDOMNode** listItem)
 {
     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
-    xmlNodePtr curr, tmp;
-    xmlNodePtr *top_level_node = NULL;
+    xmlNodePtr curr;
     long nodeIndex = 0;
-    HRESULT r;
 
     TRACE("%p %ld\n", This, index);
  
@@ -293,20 +168,11 @@ static HRESULT WINAPI xmlnodelist_get_item(
     if (index < 0)
         return S_FALSE;
 
-    curr = This->node;
-
-    if(This->enum_children)
-    {
-        tmp = curr;
-        top_level_node = &tmp;
-    }
-
+    curr = This->parent->children;
     while(curr)
     {
-        r = xslt_next_match( &This->xinfo, &curr, top_level_node);
-        if(FAILED(r) || !curr) return S_FALSE;
         if(nodeIndex++ == index) break;
-        curr = get_next_node(&This->xinfo, curr, top_level_node);
+        curr = curr->next;
     }
     if(!curr) return S_FALSE;
 
@@ -320,34 +186,18 @@ static HRESULT WINAPI xmlnodelist_get_length(
         long* listLength)
 {
 
-    xmlNodePtr curr, tmp;
-    xmlNodePtr *top_level_node = NULL;
+    xmlNodePtr curr;
     long nodeCount = 0;
-    HRESULT r;
 
     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
 
     TRACE("%p\n", This);
 
-    if (This->node == NULL) {
-        *listLength = 0;
-	return S_OK;
-    }
-        
-    curr = This->node;
-
-    if(This->enum_children)
-    {
-        tmp = curr;
-        top_level_node = &tmp;
-    }
-
+    curr = This->parent->children;
     while (curr)
     {
-        r = xslt_next_match( &This->xinfo, &curr, top_level_node );
-        if(FAILED(r) || !curr) break;
         nodeCount++;
-        curr = get_next_node(&This->xinfo, curr, top_level_node);
+        curr = curr->next;
     }
 
     *listLength = nodeCount;
@@ -359,25 +209,16 @@ static HRESULT WINAPI xmlnodelist_nextNode(
         IXMLDOMNode** nextItem)
 {
     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
-    HRESULT r;
-    xmlNodePtr *top_level_node = NULL;
 
     TRACE("%p %p\n", This, nextItem );
 
     *nextItem = NULL;
 
-    if(This->enum_children)
-        top_level_node = &This->top_level_node;
-
-    r = xslt_next_match( &This->xinfo, &This->current, top_level_node );
-    if (FAILED(r) )
-        return r;
-
     if (!This->current)
         return S_FALSE;
 
     *nextItem = create_node( This->current );
-    This->current = get_next_node(&This->xinfo, This->current, top_level_node);
+    This->current = This->current->next;
     return S_OK;
 }
 
@@ -387,7 +228,7 @@ static HRESULT WINAPI xmlnodelist_reset(
     xmlnodelist *This = impl_from_IXMLDOMNodeList( iface );
 
     TRACE("%p\n", This);
-    This->current = This->node;
+    This->current = This->parent->children;
     return S_OK;
 }
 
@@ -416,7 +257,7 @@ static const struct IXMLDOMNodeListVtbl xmlnodelist_vtbl =
     xmlnodelist__newEnum,
 };
 
-static xmlnodelist *new_nodelist( xmlNodePtr node )
+IXMLDOMNodeList* create_children_nodelist( xmlNodePtr node )
 {
     xmlnodelist *nodelist;
 
@@ -426,34 +267,12 @@ static xmlnodelist *new_nodelist( xmlNodePtr node )
 
     nodelist->lpVtbl = &xmlnodelist_vtbl;
     nodelist->ref = 1;
-    nodelist->node = node;
-    nodelist->current = node; 
-    nodelist->top_level_node = node;
-    nodelist->enum_children = FALSE;
-    xslt_info_init( &nodelist->xinfo );
+    nodelist->parent = node;
+    nodelist->current = node->children;
 
-    if(node) xmldoc_add_ref( node->doc );
+    xmldoc_add_ref( node->doc );
 
-    return nodelist;
-}
-
-IXMLDOMNodeList* create_nodelist( xmlNodePtr node )
-{
-    xmlnodelist *nodelist = new_nodelist( node );
     return (IXMLDOMNodeList*) &nodelist->lpVtbl;
 }
 
-IXMLDOMNodeList* create_filtered_nodelist( xmlNodePtr node, const xmlChar *str, BOOL enum_children )
-{
-    xmlnodelist *This = new_nodelist( node );
-    if (create_xslt_parser( &This->xinfo, node, str ))
-    {
-        This->enum_children = enum_children;
-        return (IXMLDOMNodeList*) &This->lpVtbl;
-    }
-
-    IXMLDOMNodeList_Release( (IXMLDOMNodeList*) &This->lpVtbl );
-    return NULL;
-}
-
 #endif
diff --git a/dlls/msxml3/queryresult.c b/dlls/msxml3/queryresult.c
new file mode 100644
index 0000000..75ca2c8
--- /dev/null
+++ b/dlls/msxml3/queryresult.c
@@ -0,0 +1,292 @@
+/*
+ *    XPath result node list implementation
+ *
+ * Copyright 2005 Mike McCormack
+ * Copyright 2007 Mikolaj Zalewski
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include "config.h"
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "ole2.h"
+#include "msxml2.h"
+
+#include "msxml_private.h"
+
+#include "wine/debug.h"
+
+/* This file implements the object returned by a XPath query. Note that this is
+ * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
+ * They are different because the list returned by XPath queries:
+ *  - is static - gives the results for the XML tree as it existed during the
+ *    execution of the query
+ *  - supports IXMLDOMSelection (TODO)
+ *
+ * TODO: XSLPattern support
+ */
+
+WINE_DEFAULT_DEBUG_CHANNEL(msxml);
+
+#ifdef HAVE_LIBXML2
+
+#include <libxml/xpath.h>
+
+static const struct IXMLDOMNodeListVtbl queryresult_vtbl;
+
+typedef struct _queryresult
+{
+    const struct IXMLDOMNodeListVtbl *lpVtbl;
+    LONG ref;
+    xmlNodePtr node;
+    xmlXPathObjectPtr result;
+    int resultPos;
+} queryresult;
+
+static inline queryresult *impl_from_IXMLDOMNodeList( IXMLDOMNodeList *iface )
+{
+    return (queryresult *)((char*)iface - FIELD_OFFSET(queryresult, lpVtbl));
+}
+
+HRESULT queryresult_create(xmlNodePtr node, LPWSTR szQuery, IXMLDOMNodeList **out)
+{
+    queryresult *This = CoTaskMemAlloc(sizeof(queryresult));
+    xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
+    xmlChar *str = xmlChar_from_wchar(szQuery);
+    HRESULT hr;
+    
+    
+    TRACE("(%p, %s, %p)\n", node, wine_dbgstr_w(szQuery), out);
+
+    *out = NULL;
+    if (This == NULL || ctxt == NULL || str == NULL)
+    {
+        hr = E_OUTOFMEMORY;
+        goto cleanup;
+    }
+
+    This->lpVtbl = &queryresult_vtbl;
+    This->ref = 1;
+    This->resultPos = 0;
+    This->node = node;
+    xmldoc_add_ref(This->node->doc);
+
+    ctxt->node = node;
+    This->result = xmlXPathEval(str, ctxt);
+    if (!This->result || This->result->type != XPATH_NODESET)
+    {
+        hr = E_FAIL;
+        goto cleanup;
+    }
+
+    *out = (IXMLDOMNodeList *)This;
+    hr = S_OK;
+    TRACE("found %d matches\n", This->result->nodesetval->nodeNr);
+
+cleanup:
+    if (This != NULL && FAILED(hr))
+        IXMLDOMNodeList_Release( (IXMLDOMNodeList*) &This->lpVtbl );
+    if (ctxt != NULL)
+        xmlXPathFreeContext(ctxt);
+    HeapFree(GetProcessHeap(), 0, str);
+    return hr;
+}
+
+
+static HRESULT WINAPI queryresult_QueryInterface(
+    IXMLDOMNodeList *iface,
+    REFIID riid,
+    void** ppvObject )
+{
+    TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject);
+
+    if ( IsEqualGUID( riid, &IID_IUnknown ) ||
+         IsEqualGUID( riid, &IID_IDispatch ) ||
+         IsEqualGUID( riid, &IID_IXMLDOMNodeList ) )
+    {
+        *ppvObject = iface;
+    }
+    else
+    {
+        FIXME("interface %s not implemented\n", debugstr_guid(riid));
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IXMLDOMNodeList_AddRef( iface );
+
+    return S_OK;
+}
+
+static ULONG WINAPI queryresult_AddRef(
+    IXMLDOMNodeList *iface )
+{
+    queryresult *This = impl_from_IXMLDOMNodeList( iface );
+    return InterlockedIncrement( &This->ref );
+}
+
+static ULONG WINAPI queryresult_Release(
+    IXMLDOMNodeList *iface )
+{
+    queryresult *This = impl_from_IXMLDOMNodeList( iface );
+    ULONG ref;
+
+    ref = InterlockedDecrement(&This->ref);
+    if ( ref == 0 )
+    {
+        xmlXPathFreeObject(This->result);
+        xmldoc_release(This->node->doc);
+        CoTaskMemFree(This);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI queryresult_GetTypeInfoCount(
+    IXMLDOMNodeList *iface,
+    UINT* pctinfo )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI queryresult_GetTypeInfo(
+    IXMLDOMNodeList *iface,
+    UINT iTInfo,
+    LCID lcid,
+    ITypeInfo** ppTInfo )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI queryresult_GetIDsOfNames(
+    IXMLDOMNodeList *iface,
+    REFIID riid,
+    LPOLESTR* rgszNames,
+    UINT cNames,
+    LCID lcid,
+    DISPID* rgDispId )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI queryresult_Invoke(
+    IXMLDOMNodeList *iface,
+    DISPID dispIdMember,
+    REFIID riid,
+    LCID lcid,
+    WORD wFlags,
+    DISPPARAMS* pDispParams,
+    VARIANT* pVarResult,
+    EXCEPINFO* pExcepInfo,
+    UINT* puArgErr )
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI queryresult_get_item(
+        IXMLDOMNodeList* iface,
+        long index,
+        IXMLDOMNode** listItem)
+{
+    queryresult *This = impl_from_IXMLDOMNodeList( iface );
+
+    TRACE("%p %ld\n", This, index);
+ 
+    *listItem = NULL;
+
+    if (index < 0 || index >= This->result->nodesetval->nodeNr)
+        return S_FALSE;
+
+    *listItem = create_node(This->result->nodesetval->nodeTab[index]);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI queryresult_get_length(
+        IXMLDOMNodeList* iface,
+        long* listLength)
+{
+    queryresult *This = impl_from_IXMLDOMNodeList( iface );
+
+    TRACE("%p\n", This);
+
+    *listLength = This->result->nodesetval->nodeNr;
+    return S_OK;
+}
+
+static HRESULT WINAPI queryresult_nextNode(
+        IXMLDOMNodeList* iface,
+        IXMLDOMNode** nextItem)
+{
+    queryresult *This = impl_from_IXMLDOMNodeList( iface );
+
+    TRACE("%p %p\n", This, nextItem );
+
+    *nextItem = NULL;
+
+    if (This->resultPos >= This->result->nodesetval->nodeNr)
+        return S_FALSE;
+
+    *nextItem = create_node(This->result->nodesetval->nodeTab[This->resultPos]);
+    This->resultPos++;
+    return S_OK;
+}
+
+static HRESULT WINAPI queryresult_reset(
+        IXMLDOMNodeList* iface)
+{
+    queryresult *This = impl_from_IXMLDOMNodeList( iface );
+
+    TRACE("%p\n", This);
+    This->resultPos = 0;
+    return S_OK;
+}
+
+static HRESULT WINAPI queryresult__newEnum(
+        IXMLDOMNodeList* iface,
+        IUnknown** ppUnk)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+
+static const struct IXMLDOMNodeListVtbl queryresult_vtbl =
+{
+    queryresult_QueryInterface,
+    queryresult_AddRef,
+    queryresult_Release,
+    queryresult_GetTypeInfoCount,
+    queryresult_GetTypeInfo,
+    queryresult_GetIDsOfNames,
+    queryresult_Invoke,
+    queryresult_get_item,
+    queryresult_get_length,
+    queryresult_nextNode,
+    queryresult_reset,
+    queryresult__newEnum,
+};
+
+#endif
diff --git a/dlls/msxml3/tests/Makefile.in b/dlls/msxml3/tests/Makefile.in
index 2aabdb6..e1131b8 100644
--- a/dlls/msxml3/tests/Makefile.in
+++ b/dlls/msxml3/tests/Makefile.in
@@ -3,7 +3,7 @@ TOPOBJDIR = ../../..
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 TESTDLL   = msxml3.dll
-IMPORTS   = oleaut32 ole32 kernel32
+IMPORTS   = oleaut32 ole32 user32 kernel32
 EXTRALIBS = -luuid
 
 CTESTS = \
diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c
index 02e8bf9..2cee379 100644
--- a/dlls/msxml3/tests/domdoc.c
+++ b/dlls/msxml3/tests/domdoc.c
@@ -85,6 +85,39 @@ static const WCHAR szComplete5[] = {
     '<','/','S',':','s','e','a','r','c','h','>',0
 };
 
+static const CHAR szExampleXML[] =
+"<?xml version='1.0' encoding='utf-8'?>\n"
+"<root xmlns:foo='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29'>\n"
+"    <elem>\n"
+"        <a>A1 field</a>\n"
+"        <b>B1 field</b>\n"
+"        <c>C1 field</c>\n"
+"        <description xmlns:foo='http://www.winehq.org' xmlns:bar='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29'>\n"
+"            <html xmlns='http://www.w3.org/1999/xhtml'>\n"
+"                This is <strong>a</strong> <i>description</i>. <bar:x/>\n"
+"            </html>\n"
+"        </description>\n"
+"    </elem>\n"
+"\n"
+"    <elem>\n"
+"        <a>A2 field</a>\n"
+"        <b>B2 field</b>\n"
+"        <c type=\"old\">C2 field</c>\n"
+"    </elem>\n"
+"\n"
+"    <elem xmlns='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29'>\n"
+"        <a>A3 field</a>\n"
+"        <b>B3 field</b>\n"
+"        <c>C3 field</c>\n"
+"    </elem>\n"
+"\n"
+"    <elem>\n"
+"        <a>A4 field</a>\n"
+"        <b>B4 field</b>\n"
+"        <foo:c>C4 field</foo:c>\n"
+"    </elem>\n"
+"</root>\n";
+
 static const WCHAR szNonExistentFile[] = {
     'c', ':', '\\', 'N', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', '.', 'x', 'm', 'l', 0
 };
@@ -155,6 +188,142 @@ static VARIANT _variantbstr_(const char *str)
     return v;
 }
 
+static void get_str_for_type(DOMNodeType type, char *buf)
+{
+    switch (type)
+    {
+        case NODE_ATTRIBUTE:
+            strcpy(buf, "A");
+            break;
+        case NODE_ELEMENT:
+            strcpy(buf, "E");
+            break;
+        case NODE_DOCUMENT:
+            strcpy(buf, "D");
+            break;
+        default:
+            wsprintfA(buf, "[%d]", type);
+    }
+}
+
+static int get_node_position(IXMLDOMNode *node)
+{
+    HRESULT r;
+    int pos = 0;
+
+    IXMLDOMNode_AddRef(node);
+    do
+    {
+        IXMLDOMNode *new_node;
+
+        pos++;
+        r = IXMLDOMNode_get_previousSibling(node, &new_node);
+        ok(!FAILED(r), "get_previousSibling failed\n");
+        IXMLDOMNode_Release(node);
+        node = new_node;
+    } while (r == S_OK);
+    return pos;
+}
+
+static void node_to_string(IXMLDOMNode *node, char *buf)
+{
+    HRESULT r = S_OK;
+    DOMNodeType type;
+
+    if (node == NULL)
+    {
+        lstrcpyA(buf, "(null)");
+        return;
+    }
+
+    IXMLDOMNode_AddRef(node);
+    while (r == S_OK)
+    {
+        IXMLDOMNode *new_node;
+
+        ole_check(IXMLDOMNode_get_nodeType(node, &type));
+        get_str_for_type(type, buf);
+        buf+=strlen(buf);
+
+        if (type == NODE_ATTRIBUTE)
+        {
+            BSTR bstr;
+            ole_check(IXMLDOMNode_get_nodeName(node, &bstr));
+            *(buf++) = '\'';
+            wsprintfA(buf, "%ws", bstr);
+            buf += strlen(buf);
+            *(buf++) = '\'';
+            SysFreeString(bstr);
+
+            r = IXMLDOMNode_selectSingleNode(node, _bstr_(".."), &new_node);
+        }
+        else
+        {
+            int pos = get_node_position(node);
+            DOMNodeType parent_type = NODE_INVALID;
+            r = IXMLDOMNode_get_parentNode(node, &new_node);
+            
+            /* currently wine doesn't create a node for the <?xml ... ?>. To be able to test query
+             * results we "fix" it */
+            if (r == S_OK)
+                ole_check(IXMLDOMNode_get_nodeType(node, &parent_type));
+            /* we need also to workaround the no document node problem - see below */
+            if (((r == S_FALSE && type != NODE_DOCUMENT) || parent_type == NODE_DOCUMENT) && type != NODE_PROCESSING_INSTRUCTION && pos==1)
+            {
+                todo_wine ok(FALSE, "The first child of the document node in MSXML is the <?xml ... ?> processing instruction\n");
+                pos++;
+            }
+            wsprintf(buf, "%d", pos);
+            buf += strlen(buf);
+        }
+
+        ok(!FAILED(r), "get_parentNode failed (%08x)\n", r);
+        IXMLDOMNode_Release(node);
+        node = new_node;
+        if (r == S_OK)
+            *(buf++) = '.';
+    }
+    
+    /* currently we can't access document node in wine. All our examples this is the
+     * root node so to be able to test query results we add it */
+    if (type != NODE_DOCUMENT)
+    {
+        todo_wine ok(FALSE, "Document node is not the last returned node!\n");
+        *(buf++) = '.';
+        *(buf++) = 'D';
+        *(buf++) = '1';
+    }
+    *buf = 0;
+}
+
+static char *list_to_string(IXMLDOMNodeList *list)
+{
+    static char buf[4096];
+    char *pos = buf;
+    long len = 0;
+    int i;
+    
+    if (list == NULL)
+    {
+        lstrcpyA(buf, "(null)");
+        return buf;
+    }
+    ole_check(IXMLDOMNodeList_get_length(list, &len));
+    for (i = 0; i < len; i++)
+    {
+        IXMLDOMNode *node;
+        if (i > 0)
+            *(pos++) = ' ';
+        ole_check(IXMLDOMNodeList_nextNode(list, &node));
+        node_to_string(node, pos);
+        pos += strlen(pos);
+        IXMLDOMNode_Release(node);
+    }
+    *pos = 0;
+    return buf;
+}
+#define expect_list_and_release(list, expstr) { char *str = list_to_string(list); ok(strcmp(str, expstr)==0, "Invalid node list: %s, exptected %s\n", str, expstr); if (list) IXMLDOMNodeList_Release(list); }
+
 static void test_domdoc( void )
 {
     HRESULT r;
@@ -1272,6 +1441,86 @@ static void test_IXMLDOMDocument2(void)
     free_bstrs();
 }
 
+static void test_XPath()
+{
+    HRESULT r;
+    VARIANT_BOOL b;
+    IXMLDOMDocument2 *doc;
+    IXMLDOMNode *rootNode;
+    IXMLDOMNode *elem1Node;
+    IXMLDOMNodeList *list;
+
+    r = CoCreateInstance( &CLSID_DOMDocument, NULL,
+        CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (LPVOID*)&doc );
+    if( r != S_OK )
+        return;
+
+    ole_check(IXMLDOMDocument_loadXML(doc, _bstr_(szExampleXML), &b));
+    ok(b == VARIANT_TRUE, "failed to load XML string\n");
+
+    /* switch to XPath */
+    ole_check(IXMLDOMDocument2_setProperty(doc, _bstr_("SelectionLanguage"), _variantbstr_("XPath")));
+
+    /* some simple queries*/
+    ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("root"), &list));
+    ole_check(IXMLDOMNodeList_get_item(list, 0, &rootNode));
+    ole_check(IXMLDOMNodeList_reset(list));
+    expect_list_and_release(list, "E2.D1");
+    if (rootNode == NULL)
+        return;
+
+    ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("root//c"), &list));
+    expect_list_and_release(list, "E3.E1.E2.D1 E3.E2.E2.D1");
+
+    ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("//c[@type]"), &list));
+    expect_list_and_release(list, "E3.E2.E2.D1");
+
+    ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("elem"), &list));
+    expect_list_and_release(list, "E1.E2.D1 E2.E2.D1 E4.E2.D1");
+
+    ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("."), &list));
+    expect_list_and_release(list, "E2.D1");
+
+    ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("elem[3]/preceding-sibling::*"), &list));
+    ole_check(IXMLDOMNodeList_get_item(list, 0, &elem1Node));
+    ole_check(IXMLDOMNodeList_reset(list));
+    expect_list_and_release(list, "E1.E2.D1 E2.E2.D1 E3.E2.D1");
+
+    /* select an attribute */
+    ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_(".//@type"), &list));
+    expect_list_and_release(list, "A'type'.E3.E2.E2.D1");
+
+    /* would evaluate to a number */
+    ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_("count(*)"), &list), E_FAIL);
+    /* would evaluate to a boolean */
+    ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_("position()>0"), &list), E_FAIL);
+    /* would evaluate to a string */
+    ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_("name()"), &list), E_FAIL);
+
+    /* no results */
+    ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_("c"), &list));
+    expect_list_and_release(list, "");
+    ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("elem//c"), &list));
+    expect_list_and_release(list, "");
+    ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("//elem[4]"), &list));
+    expect_list_and_release(list, "");
+
+    /* foo undeclared in document node */
+    ole_expect(IXMLDOMDocument_selectNodes(doc, _bstr_("root//foo:c"), &list), E_FAIL);
+    /* undeclared in <root> node */
+    ole_expect(IXMLDOMNode_selectNodes(rootNode, _bstr_(".//foo:c"), &list), E_FAIL);
+    /* undeclared in <elem> node */
+    ole_expect(IXMLDOMNode_selectNodes(elem1Node, _bstr_("//foo:c"), &list), E_FAIL);
+    /* but this trick can be used */
+    ole_check(IXMLDOMNode_selectNodes(elem1Node, _bstr_("//*[name()='foo:c']"), &list));
+    expect_list_and_release(list, "E3.E4.E2.D1");
+
+    IXMLDOMNode_Release(rootNode);
+    IXMLDOMNode_Release(elem1Node);
+    IXMLDOMDocument_Release(doc);
+    free_bstrs();
+}
+
 START_TEST(domdoc)
 {
     HRESULT r;
@@ -1289,6 +1538,7 @@ START_TEST(domdoc)
     test_removeChild();
     test_XMLHTTP();
     test_IXMLDOMDocument2();
+    test_XPath();
 
     CoUninitialize();
 }
-- 
1.4.4.2


More information about the wine-patches mailing list