From b7567012a26c6b2cb8ace937758fee0c547ae426 Mon Sep 17 00:00:00 2001 From: Adam Martinson Date: Tue, 21 Sep 2010 13:17:05 -0500 Subject: [PATCH] msxml3: Implement property SelectionNamespaces for XPath & move domdoc properties to their own struct so we can easily preserve them on reload. --- dlls/msxml3/domdoc.c | 344 ++++++++++++++++++++++++++++++++++++++------ dlls/msxml3/queryresult.c | 3 + dlls/msxml3/tests/domdoc.c | 46 ++++--- 3 files changed, 333 insertions(+), 60 deletions(-) diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c index 8f87a56..dbc71c4 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 #include /* not defined in older versions */ @@ -64,6 +66,16 @@ static const WCHAR PropertyProhibitDTDW[] = {'P','r','o','h','i','b','i','t','D' static const WCHAR PropValueXPathW[] = {'X','P','a','t','h',0}; static const WCHAR PropValueXSLPatternW[] = {'X','S','L','P','a','t','t','e','r','n',0}; +/* Data used by domdoc_getProperty()/domdoc_setProperty(). + * We need to preserve this when reloading a document, + * and also need access to it from the libxml backend. */ +typedef struct _domdoc_properties { + struct list selectNsList; + xmlChar const* selectNsStr; + LONG selectNsStr_len; + BOOL XPath; +} domdoc_properties; + typedef struct _domdoc { xmlnode node; @@ -77,6 +89,7 @@ typedef struct _domdoc VARIANT_BOOL validating; VARIANT_BOOL resolving; VARIANT_BOOL preserving; + domdoc_properties* properties; IXMLDOMSchemaCollection *schema; bsc_t *bsc; HRESULT error; @@ -116,7 +129,7 @@ typedef struct _domdoc typedef struct _xmldoc_priv { LONG refs; struct list orphans; - BOOL XPath; + domdoc_properties* properties; } xmldoc_priv; typedef struct _orphan_entry { @@ -124,24 +137,54 @@ 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; } -static inline BOOL is_xpathmode(const xmlDocPtr doc) +static inline domdoc_properties * properties_from_xmlDocPtr(const xmlDocPtr doc) { - return priv_from_xmlDocPtr(doc)->XPath; + return priv_from_xmlDocPtr(doc)->properties; } -static inline void set_xpathmode(const xmlDocPtr doc) +int registerNamespaces(xmlXPathContextPtr ctxt) { - priv_from_xmlDocPtr(doc)->XPath = TRUE; + int n = 0; + const select_ns_entry* ns = NULL; + const struct list* pNsList = &properties_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 inline void reset_xpathmode(const xmlDocPtr doc) +static inline void clear_selectNsList(struct list* pNsList) { - priv_from_xmlDocPtr(doc)->XPath = FALSE; + select_ns_entry *ns, *ns2; + LIST_FOR_EACH_ENTRY_SAFE( ns, ns2, pNsList, select_ns_entry, entry ) + { + heap_free( ns ); + } + list_init(pNsList); +} + +static inline BOOL is_xpathmode(const xmlDocPtr doc) +{ + return properties_from_xmlDocPtr(doc)->XPath; } static xmldoc_priv * create_priv(void) @@ -152,13 +195,73 @@ static xmldoc_priv * create_priv(void) if (priv) { priv->refs = 0; - priv->XPath = FALSE; list_init( &priv->orphans ); + priv->properties = NULL; } return priv; } +static domdoc_properties * create_properties(const GUID *clsid) +{ + domdoc_properties *properties = heap_alloc(sizeof(domdoc_properties)); + + list_init( &properties->selectNsList ); + properties->selectNsStr = heap_alloc_zero(sizeof(xmlChar)); + properties->selectNsStr_len = 0; + properties->XPath = FALSE; + + /* properties that are dependent on object versions */ + if (IsEqualCLSID( clsid, &CLSID_DOMDocument40 ) || + IsEqualCLSID( clsid, &CLSID_DOMDocument60 )) + { + properties->XPath = TRUE; + } + + return properties; +} + +static domdoc_properties* copy_properties(domdoc_properties const* properties) +{ + domdoc_properties* pcopy = heap_alloc(sizeof(domdoc_properties)); + select_ns_entry const* ns = NULL; + select_ns_entry* new_ns = NULL; + int len = (properties->selectNsStr_len+1)*sizeof(xmlChar); + ptrdiff_t offset; + + if (pcopy) + { + pcopy->XPath = properties->XPath; + pcopy->selectNsStr_len = properties->selectNsStr_len; + list_init( &pcopy->selectNsList ); + pcopy->selectNsStr = heap_alloc(len); + memcpy((xmlChar*)pcopy->selectNsStr, properties->selectNsStr, len); + offset = pcopy->selectNsStr - properties->selectNsStr; + + LIST_FOR_EACH_ENTRY( ns, (&properties->selectNsList), select_ns_entry, entry ) + { + new_ns = heap_alloc(sizeof(select_ns_entry)); + memcpy(new_ns, ns, sizeof(select_ns_entry)); + new_ns->href += offset; + new_ns->prefix += offset; + list_add_tail(&pcopy->selectNsList, &new_ns->entry); + } + + } + + return pcopy; +} + +static void free_properties(domdoc_properties* properties) +{ + if (properties) + { + clear_selectNsList(&properties->selectNsList); + heap_free((xmlChar*)properties->selectNsStr); + heap_free(properties); + } +} + /* links a "node ); heap_free( orphan ); } + free_properties(priv->properties); heap_free(doc->_private); xmlFreeDoc(doc); @@ -292,14 +396,28 @@ HRESULT xmldoc_remove_orphan(xmlDocPtr doc, xmlNodePtr node) return S_FALSE; } -static HRESULT attach_xmldoc( xmlnode *node, xmlDocPtr xml ) +static inline xmlDocPtr get_doc( domdoc *This ) { - if(node->node) - xmldoc_release(node->node->doc); + return (xmlDocPtr)This->node.node; +} - node->node = (xmlNodePtr) xml; - if(node->node) - xmldoc_add_ref(node->node->doc); +static HRESULT attach_xmldoc(domdoc *This, xmlDocPtr xml ) +{ + if(This->node.node) + { + priv_from_xmlDocPtr(get_doc(This))->properties = NULL; + if (xmldoc_release(get_doc(This)) != 0) + priv_from_xmlDocPtr(get_doc(This))->properties = + copy_properties(This->properties); + } + + This->node.node = (xmlNodePtr) xml; + + if(This->node.node) + { + xmldoc_add_ref(get_doc(This)); + priv_from_xmlDocPtr(get_doc(This))->properties = This->properties; + } return S_OK; } @@ -309,11 +427,6 @@ static inline domdoc *impl_from_IXMLDOMDocument3( IXMLDOMDocument3 *iface ) return (domdoc *)((char*)iface - FIELD_OFFSET(domdoc, lpVtbl)); } -static inline xmlDocPtr get_doc( domdoc *This ) -{ - return (xmlDocPtr)This->node.node; -} - static inline domdoc *impl_from_IPersistStreamInit(IPersistStreamInit *iface) { return (domdoc *)((char*)iface - FIELD_OFFSET(domdoc, lpvtblIPersistStreamInit)); @@ -429,7 +542,7 @@ static HRESULT WINAPI domdoc_IPersistStreamInit_Load( xmldoc->_private = create_priv(); - return attach_xmldoc( &This->node, xmldoc ); + return attach_xmldoc(This, xmldoc); } static HRESULT WINAPI domdoc_IPersistStreamInit_Save( @@ -1608,7 +1721,7 @@ static HRESULT domdoc_onDataAvailable(void *obj, char *ptr, DWORD len) xmldoc = doparse( ptr, len, NULL ); if(xmldoc) { xmldoc->_private = create_priv(); - return attach_xmldoc(&This->node, xmldoc); + return attach_xmldoc(This, xmldoc); } return S_OK; @@ -1661,7 +1774,7 @@ static HRESULT WINAPI domdoc_load( { domdoc *newDoc = impl_from_IXMLDOMDocument3( pNewDoc ); xmldoc = xmlCopyDoc(get_doc(newDoc), 1); - hr = attach_xmldoc(&This->node, xmldoc); + hr = attach_xmldoc(This, xmldoc); if(SUCCEEDED(hr)) *isSuccessful = VARIANT_TRUE; @@ -1723,7 +1836,7 @@ static HRESULT WINAPI domdoc_load( if(!filename || FAILED(hr)) { xmldoc = xmlNewDoc(NULL); xmldoc->_private = create_priv(); - hr = attach_xmldoc(&This->node, xmldoc); + hr = attach_xmldoc(This, xmldoc); if(SUCCEEDED(hr)) hr = S_FALSE; } @@ -1859,7 +1972,8 @@ static HRESULT WINAPI domdoc_loadXML( xmldoc = xmlNewDoc(NULL); xmldoc->_private = create_priv(); - hr2 = attach_xmldoc( &This->node, xmldoc ); + + hr2 = attach_xmldoc(This, xmldoc); if( FAILED(hr2) ) hr = hr2; @@ -2155,27 +2269,146 @@ static HRESULT WINAPI domdoc_setProperty( hr = S_OK; if (lstrcmpiW(bstr, PropValueXPathW) == 0) - set_xpathmode(get_doc(This)); + This->properties->XPath = TRUE; else if (lstrcmpiW(bstr, PropValueXSLPatternW) == 0) - reset_xpathmode(get_doc(This)); + This->properties->XPath = FALSE; else hr = E_FAIL; VariantClear(&varStr); return hr; } + else if (lstrcmpiW(p, PropertySelectionNamespacesW) == 0) + { + VARIANT varStr; + HRESULT hr; + BSTR bstr; + xmlChar *pTokBegin, *pTokEnd, *pTokInner; + xmlChar *nsStr = (xmlChar*)This->properties->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 = &(This->properties->selectNsList); + clear_selectNsList(pNsList); + heap_free(nsStr); + nsStr = xmlChar_from_wchar(bstr); + + + TRACE("Setting SelectionNamespaces property to: %s\n", nsStr); + + This->properties->selectNsStr = nsStr; + This->properties->selectNsStr_len = xmlStrlen(nsStr); + if (bstr && *bstr) + { + ctx = xmlXPathNewContext(get_doc(This)); + 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; @@ -2196,11 +2429,46 @@ static HRESULT WINAPI domdoc_getProperty( if (lstrcmpiW(p, PropertySelectionLanguageW) == 0) { V_VT(var) = VT_BSTR; - V_BSTR(var) = is_xpathmode(This->node.node->doc) ? + V_BSTR(var) = This->properties->XPath? SysAllocString(PropValueXPathW) : 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 = This->properties->selectNsStr; + pNsList = &This->properties->selectNsList; + lenA = This->properties->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; @@ -2476,6 +2744,7 @@ HRESULT DOMDocument_create_from_xmldoc(xmlDocPtr xmldoc, IXMLDOMDocument3 **docu doc->validating = 0; doc->resolving = 0; doc->preserving = 0; + doc->properties = properties_from_xmlDocPtr(xmldoc); doc->error = S_OK; doc->schema = NULL; doc->stream = NULL; @@ -2503,6 +2772,7 @@ HRESULT DOMDocument_create(const GUID *clsid, IUnknown *pUnkOuter, void **ppObj) return E_OUTOFMEMORY; xmldoc->_private = create_priv(); + priv_from_xmlDocPtr(xmldoc)->properties = create_properties(clsid); hr = DOMDocument_create_from_xmldoc(xmldoc, (IXMLDOMDocument3**)ppObj); if(FAILED(hr)) @@ -2511,14 +2781,6 @@ HRESULT DOMDocument_create(const GUID *clsid, IUnknown *pUnkOuter, void **ppObj) return hr; } - /* properties that are dependent on object versions */ - if (IsEqualCLSID( clsid, &CLSID_DOMDocument40 ) || - IsEqualCLSID( clsid, &CLSID_DOMDocument60 )) - { - domdoc *This = impl_from_IXMLDOMDocument3(*ppObj); - set_xpathmode(get_doc(This)); - } - return hr; } diff --git a/dlls/msxml3/queryresult.c b/dlls/msxml3/queryresult.c index 33a2f41..efaeb4b 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 @@ -395,6 +396,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 205716a..5ec0bc1 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,17 +3100,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 a description. " + * "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 */ @@ -3120,8 +3124,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=###"); @@ -5763,7 +5767,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); @@ -5771,13 +5775,13 @@ static void test_get_ownerDocument(void) hr = IXMLDOMNode_get_ownerDocument(node, &doc1); ok( hr == S_OK, "got 0x%08x\n", hr); - VariantClear(&var); hr = IXMLDOMDocument_QueryInterface(doc1, &IID_IXMLDOMDocument2, (void**)&doc_owner); 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); @@ -5799,11 +5803,15 @@ static void test_get_ownerDocument(void) ok( b == VARIANT_TRUE, "failed to load XML string\n"); SysFreeString( str ); - /* property retained even after reload */ - VariantClear(&var); + /* properties retained even after reload */ hr = IXMLDOMDocument2_getProperty(doc, _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, _bstr_("SelectionLanguage"), &var); + ok( hr == S_OK, "got 0x%08x\n", hr); + ok( lstrcmpW(V_BSTR(&var), _bstr_("XPath")) == 0, "expected previously set value\n"); VariantClear(&var); hr = IXMLDOMDocument2_get_firstChild(doc, &node); -- 1.7.2.3