[PATCH 3/6] msxml3: implement SelectionNamespaces property

Adam Martinson amartinson at codeweavers.com
Tue Sep 28 14:35:48 CDT 2010


---
 dlls/msxml3/domdoc.c       |  211 ++++++++++++++++++++++++++++++++++++++++++--
 dlls/msxml3/queryresult.c  |    4 +
 dlls/msxml3/tests/domdoc.c |   38 +++++----
 3 files changed, 231 insertions(+), 22 deletions(-)

diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c
index 8f87a56..d44f81f 100644
--- a/dlls/msxml3/domdoc.c
+++ b/dlls/msxml3/domdoc.c
@@ -2,6 +2,7 @@
  *    DOM Document implementation
  *
  * Copyright 2005 Mike McCormack
+ * Copyright 2010 Adam Martinson for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -47,6 +48,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(msxml);
 
 #ifdef HAVE_LIBXML2
 
+#include <libxml/xpathInternals.h>
 #include <libxml/xmlsave.h>
 
 /* not defined in older versions */
@@ -117,6 +119,9 @@ typedef struct _xmldoc_priv {
     LONG refs;
     struct list orphans;
     BOOL XPath;
+    struct list selectNsList;
+    xmlChar const* selectNsStr;
+    LONG selectNsStr_len;
 } xmldoc_priv;
 
 typedef struct _orphan_entry {
@@ -124,6 +129,14 @@ typedef struct _orphan_entry {
     xmlNode * node;
 } orphan_entry;
 
+typedef struct _select_ns_entry {
+    struct list entry;
+    xmlChar const* prefix;
+    xmlChar prefix_end;
+    xmlChar const* href;
+    xmlChar href_end;
+} select_ns_entry;
+
 static inline xmldoc_priv * priv_from_xmlDocPtr(const xmlDocPtr doc)
 {
     return doc->_private;
@@ -144,6 +157,23 @@ static inline void reset_xpathmode(const xmlDocPtr doc)
     priv_from_xmlDocPtr(doc)->XPath = FALSE;
 }
 
+int registerNamespaces(xmlXPathContextPtr ctxt)
+{
+    int n = 0;
+    const select_ns_entry* ns = NULL;
+    const struct list* pNsList = &priv_from_xmlDocPtr(ctxt->doc)->selectNsList;
+
+    TRACE("(%p)\n", ctxt);
+
+    LIST_FOR_EACH_ENTRY( ns, pNsList, select_ns_entry, entry )
+    {
+        xmlXPathRegisterNs(ctxt, ns->prefix, ns->href);
+        ++n;
+    }
+
+    return n;
+}
+
 static xmldoc_priv * create_priv(void)
 {
     xmldoc_priv *priv;
@@ -154,6 +184,9 @@ static xmldoc_priv * create_priv(void)
         priv->refs = 0;
         priv->XPath = FALSE;
         list_init( &priv->orphans );
+        list_init( &priv->selectNsList );
+        priv->selectNsStr = heap_alloc_zero(sizeof(xmlChar));
+        priv->selectNsStr_len = 0;
     }
 
     return priv;
@@ -237,6 +270,16 @@ LONG xmldoc_add_ref(xmlDocPtr doc)
     return ref;
 }
 
+static inline void clear_selectNsList(struct list* pNsList)
+{
+    select_ns_entry *ns, *ns2;
+    LIST_FOR_EACH_ENTRY_SAFE( ns, ns2, pNsList, select_ns_entry, entry )
+    {
+        heap_free( ns );
+    }
+    list_init(pNsList);
+}
+
 LONG xmldoc_release(xmlDocPtr doc)
 {
     xmldoc_priv *priv = priv_from_xmlDocPtr(doc);
@@ -252,6 +295,8 @@ LONG xmldoc_release(xmlDocPtr doc)
             xmlFreeNode( orphan->node );
             heap_free( orphan );
         }
+        clear_selectNsList(&priv->selectNsList);
+        heap_free((xmlChar*)priv->selectNsStr);
         heap_free(doc->_private);
 
         xmlFreeDoc(doc);
@@ -2164,18 +2209,137 @@ static HRESULT WINAPI domdoc_setProperty(
         VariantClear(&varStr);
         return hr;
     }
+    else if (lstrcmpiW(p, PropertySelectionNamespacesW) == 0)
+    {
+        VARIANT varStr;
+        HRESULT hr;
+        BSTR bstr;
+        xmlChar *pTokBegin, *pTokEnd, *pTokInner;
+        xmlChar *nsStr = (xmlChar*)priv_from_xmlDocPtr(This->node.node->doc)->selectNsStr;
+        xmlXPathContextPtr ctx;
+        struct list *pNsList;
+        select_ns_entry* pNsEntry = NULL;
+
+        V_VT(&varStr) = VT_EMPTY;
+        if (V_VT(&var) != VT_BSTR)
+        {
+            if (FAILED(hr = VariantChangeType(&varStr, &var, 0, VT_BSTR)))
+                return hr;
+            bstr = V_BSTR(&varStr);
+        }
+        else
+            bstr = V_BSTR(&var);
+
+        hr = S_OK;
+
+        pNsList = &(priv_from_xmlDocPtr(This->node.node->doc)->selectNsList);
+        clear_selectNsList(pNsList);
+        heap_free(nsStr);
+        nsStr = xmlChar_from_wchar(bstr);
+
+
+        TRACE("Setting SelectionNamespaces property to: %s\n", nsStr);
+
+        priv_from_xmlDocPtr(This->node.node->doc)->selectNsStr = nsStr;
+        priv_from_xmlDocPtr(This->node.node->doc)->selectNsStr_len = xmlStrlen(nsStr);
+        if (bstr && *bstr)
+        {
+            ctx = xmlXPathNewContext(This->node.node->doc);
+            pTokBegin = nsStr;
+            pTokEnd = nsStr;
+            for (; *pTokBegin; pTokBegin = pTokEnd)
+            {
+                if (pNsEntry != NULL)
+                    memset(pNsEntry, 0, sizeof(select_ns_entry));
+                else
+                    pNsEntry = heap_alloc_zero(sizeof(select_ns_entry));
+
+                while (*pTokBegin == ' ')
+                    ++pTokBegin;
+                pTokEnd = pTokBegin;
+                while (*pTokEnd != ' ' && *pTokEnd != 0)
+                    ++pTokEnd;
+
+                if (xmlStrncmp(pTokBegin, (xmlChar const*)"xmlns", 5) != 0)
+                {
+                    hr = E_FAIL;
+                    WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
+                          wine_dbgstr_w(bstr), wine_dbgstr_an((const char*)pTokBegin, pTokEnd-pTokBegin));
+                    continue;
+                }
+
+                pTokBegin += 5;
+                if (*pTokBegin == '=')
+                {
+                    /*valid for XSLPattern?*/
+                    FIXME("Setting default xmlns not supported - skipping.\n");
+                    pTokBegin = pTokEnd;
+                    continue;
+                }
+                else if (*pTokBegin == ':')
+                {
+                    pNsEntry->prefix = ++pTokBegin;
+                    for (pTokInner = pTokBegin; pTokInner != pTokEnd && *pTokInner != '='; ++pTokInner)
+                        ;
+
+                    if (pTokInner == pTokEnd)
+                    {
+                        hr = E_FAIL;
+                        WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
+                              wine_dbgstr_w(bstr), wine_dbgstr_an((const char*)pTokBegin, pTokEnd-pTokBegin));
+                        continue;
+                    }
+
+                    pNsEntry->prefix_end = *pTokInner;
+                    *pTokInner = 0;
+                    ++pTokInner;
+
+                    if (pTokEnd-pTokInner > 1 &&
+                        ((*pTokInner == '\'' && *(pTokEnd-1) == '\'') ||
+                         (*pTokInner == '"' && *(pTokEnd-1) == '"')))
+                    {
+                        pNsEntry->href = ++pTokInner;
+                        pNsEntry->href_end = *(pTokEnd-1);
+                        *(pTokEnd-1) = 0;
+                        list_add_tail(pNsList, &pNsEntry->entry);
+                        /*let libxml figure out if they're valid from here ;)*/
+                        if (xmlXPathRegisterNs(ctx, pNsEntry->prefix, pNsEntry->href) != 0)
+                        {
+                            hr = E_FAIL;
+                        }
+                        pNsEntry = NULL;
+                        continue;
+                    }
+                    else
+                    {
+                        WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
+                              wine_dbgstr_w(bstr), wine_dbgstr_an((const char*)pTokInner, pTokEnd-pTokInner));
+                        list_add_tail(pNsList, &pNsEntry->entry);
+
+                        pNsEntry = NULL;
+                        hr = E_FAIL;
+                        continue;
+                    }
+                }
+                else
+                {
+                    hr = E_FAIL;
+                    continue;
+                }
+            }
+            heap_free(pNsEntry);
+            xmlXPathFreeContext(ctx);
+        }
+
+        VariantClear(&varStr);
+        return hr;
+    }
     else if (lstrcmpiW(p, PropertyProhibitDTDW) == 0)
     {
         /* Ignore */
         FIXME("Ignoring property ProhibitDTD, value %d\n", V_BOOL(&var));
         return S_OK;
     }
-    else if (lstrcmpiW(p, PropertySelectionNamespacesW) == 0)
-    {
-        if (V_VT(&var) == VT_BSTR)
-            FIXME("Unsupported SelectionNamespaces: %s\n", wine_dbgstr_w(V_BSTR(&var)));
-        return E_FAIL;
-    }
 
     FIXME("Unknown property %s\n", wine_dbgstr_w(p));
     return E_FAIL;
@@ -2201,6 +2365,41 @@ static HRESULT WINAPI domdoc_getProperty(
                       SysAllocString(PropValueXSLPatternW);
         return V_BSTR(var) ? S_OK : E_OUTOFMEMORY;
     }
+    else if (lstrcmpiW(p, PropertySelectionNamespacesW) == 0)
+    {
+        int lenA, lenW;
+        BSTR rebuiltStr, cur;
+        const xmlChar *nsStr;
+        struct list *pNsList;
+        select_ns_entry* pNsEntry;
+
+        V_VT(var) = VT_BSTR;
+        nsStr = priv_from_xmlDocPtr(This->node.node->doc)->selectNsStr;
+        pNsList = &priv_from_xmlDocPtr(This->node.node->doc)->selectNsList;
+        lenA = priv_from_xmlDocPtr(This->node.node->doc)->selectNsStr_len;
+        lenW = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)nsStr, lenA+1, NULL, 0);
+        rebuiltStr = heap_alloc(lenW*sizeof(WCHAR));
+        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)nsStr, lenA+1, rebuiltStr, lenW);
+        cur = rebuiltStr;
+        /* this is fine because all of the chars that end tokens are ASCII*/
+        LIST_FOR_EACH_ENTRY(pNsEntry, pNsList, select_ns_entry, entry)
+        {
+            while (*cur != 0) ++cur;
+            if (pNsEntry->prefix_end)
+            {
+                *cur = pNsEntry->prefix_end;
+                while (*cur != 0) ++cur;
+            }
+
+            if (pNsEntry->href_end)
+            {
+                *cur = pNsEntry->href_end;
+            }
+        }
+        V_BSTR(var) = SysAllocString(rebuiltStr);
+        heap_free(rebuiltStr);
+        return S_OK;
+    }
 
     FIXME("Unknown property %s\n", wine_dbgstr_w(p));
     return E_FAIL;
diff --git a/dlls/msxml3/queryresult.c b/dlls/msxml3/queryresult.c
index 33a2f41..739faae 100644
--- a/dlls/msxml3/queryresult.c
+++ b/dlls/msxml3/queryresult.c
@@ -50,6 +50,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(msxml);
 
 #include <libxml/xpath.h>
 
+int registerNamespaces(xmlXPathContextPtr ctxt);
+
 typedef struct _queryresult
 {
     DispatchEx dispex;
@@ -395,6 +397,8 @@ HRESULT queryresult_create(xmlNodePtr node, LPCWSTR szQuery, IXMLDOMNodeList **o
     xmldoc_add_ref(This->node->doc);
 
     ctxt->node = node;
+    registerNamespaces(ctxt);
+
     This->result = xmlXPathEval(str, ctxt);
     if (!This->result || This->result->type != XPATH_NODESET)
     {
diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c
index eb057c4..34774b2 100644
--- a/dlls/msxml3/tests/domdoc.c
+++ b/dlls/msxml3/tests/domdoc.c
@@ -2998,7 +2998,7 @@ static void test_IXMLDOMDocument2(void)
     V_VT(&var) = VT_BSTR;
     V_BSTR(&var) = SysAllocString(emptyW);
     r = IXMLDOMDocument2_setProperty(doc2, _bstr_("SelectionNamespaces"), var);
-    todo_wine ok(r == S_OK, "got 0x%08x\n", r);
+    ok(r == S_OK, "got 0x%08x\n", r);
     VariantClear(&var);
 
     V_VT(&var) = VT_I2;
@@ -3100,18 +3100,24 @@ static void test_XPath(void)
     expect_list_and_release(list, "E3.E4.E2.D1");
 
     /* it has to be declared in SelectionNamespaces */
-    todo_wine ole_check(IXMLDOMDocument2_setProperty(doc, _bstr_("SelectionNamespaces"),
+    ole_check(IXMLDOMDocument2_setProperty(doc, _bstr_("SelectionNamespaces"),
         _variantbstr_("xmlns:test='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29'")));
 
     /* now the namespace can be used */
-    todo_wine ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("root//test:c"), &list));
-    todo_wine expect_list_and_release(list, "E3.E3.E2.D1 E3.E4.E2.D1");
-    todo_wine ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_(".//test:c"), &list));
-    todo_wine expect_list_and_release(list, "E3.E3.E2.D1 E3.E4.E2.D1");
-    todo_wine ole_check(IXMLDOMNode_selectNodes(elem1Node, _bstr_("//test:c"), &list));
-    todo_wine expect_list_and_release(list, "E3.E3.E2.D1 E3.E4.E2.D1");
-    todo_wine ole_check(IXMLDOMNode_selectNodes(elem1Node, _bstr_(".//test:x"), &list));
-    todo_wine expect_list_and_release(list, "E5.E1.E4.E1.E2.D1");
+    ole_check(IXMLDOMDocument_selectNodes(doc, _bstr_("root//test:c"), &list));
+    expect_list_and_release(list, "E3.E3.E2.D1 E3.E4.E2.D1");
+    ole_check(IXMLDOMNode_selectNodes(rootNode, _bstr_(".//test:c"), &list));
+    expect_list_and_release(list, "E3.E3.E2.D1 E3.E4.E2.D1");
+    ole_check(IXMLDOMNode_selectNodes(elem1Node, _bstr_("//test:c"), &list));
+    expect_list_and_release(list, "E3.E3.E2.D1 E3.E4.E2.D1");
+    ole_check(IXMLDOMNode_selectNodes(elem1Node, _bstr_(".//test:x"), &list));
+    {
+        char *str = list_to_string(list);
+        /* it's the correct node, just the wrong index due to whitespace-only nodes */
+        todo_wine ok(strcmp(str, "E5.E1.E4.E1.E2.D1")==0, "Invalid node list: %s, expected %s\n", str, "E5.E1.E4.E1.E2.D1");
+        if (list)
+            IXMLDOMNodeList_Release(list);
+    }
 
     /* SelectionNamespaces syntax error - the namespaces doesn't work anymore but the value is stored */
     ole_expect(IXMLDOMDocument2_setProperty(doc, _bstr_("SelectionNamespaces"),
@@ -3120,8 +3126,8 @@ static void test_XPath(void)
     ole_expect(IXMLDOMDocument_selectNodes(doc, _bstr_("root//foo:c"), &list), E_FAIL);
 
     VariantInit(&var);
-    todo_wine ole_check(IXMLDOMDocument2_getProperty(doc, _bstr_("SelectionNamespaces"), &var));
-    todo_wine expect_eq(V_VT(&var), VT_BSTR, int, "%x");
+    ole_check(IXMLDOMDocument2_getProperty(doc, _bstr_("SelectionNamespaces"), &var));
+    expect_eq(V_VT(&var), VT_BSTR, int, "%x");
     if (V_VT(&var) == VT_BSTR)
         expect_bstr_eq_and_free(V_BSTR(&var), "xmlns:test='urn:uuid:86B2F87F-ACB6-45cd-8B77-9BDB92A01A29' xmlns:foo=###");
 
@@ -5759,7 +5765,7 @@ static void test_get_ownerDocument(void)
     ok( hr == S_OK, "got 0x%08x\n", hr);
 
     hr = IXMLDOMDocument2_setProperty(doc, _bstr_("SelectionNamespaces"), _variantbstr_("xmlns:wi=\'www.winehq.org\'"));
-    todo_wine ok( hr == S_OK, "got 0x%08x\n", hr);
+    ok( hr == S_OK, "got 0x%08x\n", hr);
 
     hr = IXMLDOMDocument2_get_firstChild(doc, &node);
     ok( hr == S_OK, "got 0x%08x\n", hr);
@@ -5771,8 +5777,8 @@ static void test_get_ownerDocument(void)
     ok( hr == S_OK, "got 0x%08x\n", hr);
     ok( doc_owner != doc, "got %p, doc %p\n", doc_owner, doc);
     hr = IXMLDOMDocument2_getProperty(doc_owner, _bstr_("SelectionNamespaces"), &var);
-    todo_wine ok( hr == S_OK, "got 0x%08x\n", hr);
-    todo_wine ok( lstrcmpW(V_BSTR(&var), _bstr_("xmlns:wi=\'www.winehq.org\'")) == 0, "expected previously set value\n");
+    ok( hr == S_OK, "got 0x%08x\n", hr);
+    ok( lstrcmpW(V_BSTR(&var), _bstr_("xmlns:wi=\'www.winehq.org\'")) == 0, "expected previously set value\n");
     VariantClear(&var);
 
     hr = IXMLDOMDocument2_getProperty(doc_owner, _bstr_("SelectionLanguage"), &var);
@@ -5797,7 +5803,7 @@ static void test_get_ownerDocument(void)
     /* property retained even after reload */
     VariantClear(&var);
     hr = IXMLDOMDocument2_getProperty(doc, _bstr_("SelectionNamespaces"), &var);
-    todo_wine ok( hr == S_OK, "got 0x%08x\n", hr);
+    ok( hr == S_OK, "got 0x%08x\n", hr);
     todo_wine ok( lstrcmpW(V_BSTR(&var), _bstr_("xmlns:wi=\'www.winehq.org\'")) == 0, "expected previously set value\n");
     VariantClear(&var);
 
-- 
1.7.2.3


--------------020200070204040501080904--



More information about the wine-patches mailing list