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

Mikołaj Zalewski mikolaj at zalewski.pl
Sat May 12 03:12:14 CDT 2007


The previous version was allocating too little memory in 
IXMLDOMDocument::getElementsByTagName.
-------------- 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, sizeof(WCHAR)*(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