msxml3: Implement SelectionNamespaces property for XPath.
Adam Martinson (none)
Loki at X200t.
Tue Sep 14 09:56:43 CDT 2010
---
dlls/msxml3/domdoc.c | 205 ++++++++++++++++++++++++++++++++++++++++++++
dlls/msxml3/queryresult.c | 5 +
dlls/msxml3/tests/domdoc.c | 34 ++++---
3 files changed, 229 insertions(+), 15 deletions(-)
diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c
index 35a8f7c..526a31a 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 _domdoc
typedef struct _xmldoc_priv {
LONG refs;
struct list orphans;
+ struct list selectNsList;
+ xmlChar const* selectNsStr;
+ LONG selectNsStr_len;
} xmldoc_priv;
typedef struct _orphan_entry {
@@ -124,11 +129,36 @@ 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(xmlDocPtr doc)
{
return doc->_private;
}
+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;
@@ -138,6 +168,9 @@ static xmldoc_priv * create_priv(void)
{
priv->refs = 0;
list_init( &priv->orphans );
+ list_init( &priv->selectNsList );
+ priv->selectNsStr = heap_alloc_zero(sizeof(xmlChar));
+ priv->selectNsStr_len = 0;
}
return priv;
@@ -221,6 +254,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);
@@ -236,6 +279,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);
@@ -2116,6 +2161,131 @@ 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 */
@@ -2154,6 +2324,41 @@ static HRESULT WINAPI domdoc_getProperty(
V_BSTR(var) = SysAllocString(PropValueXSLPatternW);
return S_OK;
}
+ 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..2defb78 100644
--- a/dlls/msxml3/queryresult.c
+++ b/dlls/msxml3/queryresult.c
@@ -3,6 +3,7 @@
*
* Copyright 2005 Mike McCormack
* Copyright 2007 Mikolaj Zalewski
+ * 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
@@ -50,6 +51,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(msxml);
#include <libxml/xpath.h>
+int registerNamespaces(xmlXPathContextPtr ctxt);
+
typedef struct _queryresult
{
DispatchEx dispex;
@@ -395,6 +398,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 4317c52..2bd1f02 100644
--- a/dlls/msxml3/tests/domdoc.c
+++ b/dlls/msxml3/tests/domdoc.c
@@ -2958,7 +2958,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;
@@ -3060,17 +3060,21 @@ 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));
+ 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");
+ /* This one is failing due to not merging adjacent text nodes.
+ * At the line: "This is <strong>a</strong> <i>description</i>. <bar:x/>"
+ * "This is" is parsed as 1 node by native msxml3, and 2 nodes by our version.
+ * It selects the correct node, it just shows up as E6 instead of E5. */
+ ole_check(IXMLDOMNode_selectNodes(elem1Node, _bstr_(".//test:x"), &list));
todo_wine expect_list_and_release(list, "E5.E1.E4.E1.E2.D1");
/* SelectionNamespaces syntax error - the namespaces doesn't work anymore but the value is stored */
@@ -3080,8 +3084,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=###");
@@ -5876,7 +5880,7 @@ static void test_get_ownerDocument(void)
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = _bstr_("xmlns:wi=\'www.winehq.org\'");
hr = IXMLDOMDocument2_setProperty(doc, _bstr_("SelectionNamespaces"), var);
- 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);
@@ -5889,8 +5893,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");
IXMLDOMDocument2_Release(doc_owner);
VariantClear(&var);
@@ -5910,7 +5914,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.2
--------------070105000503020109040202--
More information about the wine-patches
mailing list