From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/domdoc.c | 45 +++++++++++++++++++---- dlls/msxml3/msxml_private.h | 4 +- dlls/msxml3/node.c | 63 +++++++++++++++++++++++++------- dlls/msxml3/saxreader.c | 31 +++++++++++++--- dlls/msxml3/tests/domdoc.c | 1 - dlls/msxml6/tests/domdoc.c | 73 ++++++++++++++++++++++++++++++++++--- 6 files changed, 181 insertions(+), 36 deletions(-) diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c index 5159d717b04..252135b4f8d 100644 --- a/dlls/msxml3/domdoc.c +++ b/dlls/msxml3/domdoc.c @@ -1810,12 +1810,24 @@ static HRESULT WINAPI domdoc_validate(IXMLDOMDocument3 *iface, IXMLDOMParseError return IXMLDOMDocument3_validateNode(iface, (IXMLDOMNode *)iface, err); } +static HRESULT variant_get_bool_property(const VARIANT *v, VARIANT_BOOL *ret) +{ + VARIANT dest; + + VariantInit(&dest); + if (FAILED(VariantChangeType(&dest, v, 0, VT_BOOL))) + return E_FAIL; + + *ret = V_BOOL(&dest); + return S_OK; +} + static HRESULT WINAPI domdoc_setProperty(IXMLDOMDocument3 *iface, BSTR p, VARIANT value) { domdoc *doc = impl_from_IXMLDOMDocument3(iface); struct domdoc_properties *properties = doc->node->properties; + VARIANT_BOOL b; HRESULT hr; - VARIANT v; TRACE("%p, %s, %s.\n", iface, debugstr_w(p), debugstr_variant(&value)); @@ -1982,7 +1994,6 @@ static HRESULT WINAPI domdoc_setProperty(IXMLDOMDocument3 *iface, BSTR p, VARIAN else if (wcsicmp(p, L"NewParser") == 0 || wcsicmp(p, L"ResolveExternals") == 0 || wcsicmp(p, L"AllowXsltScript") == 0 || - wcsicmp(p, L"NormalizeAttributeValues") == 0 || wcsicmp(p, L"AllowDocumentFunction") == 0 || wcsicmp(p, L"UseInlineSchema") == 0) { @@ -1993,14 +2004,22 @@ static HRESULT WINAPI domdoc_setProperty(IXMLDOMDocument3 *iface, BSTR p, VARIAN if (!wcscmp(p, L"ProhibitDTD")) { - V_VT(&v) = VT_EMPTY; - if (FAILED(hr = VariantChangeType(&v, &value, 0, VT_BOOL))) - { - WARN("Failed to convert to boolean.\n"); + if (FAILED(hr = variant_get_bool_property(&value, &b))) return hr; - } - properties->prohibit_dtd = V_BOOL(&v) == VARIANT_TRUE; + properties->prohibit_dtd = b == VARIANT_TRUE; + return S_OK; + } + + if (!wcscmp(p, L"NormalizeAttributeValues")) + { + if (properties->version < MSXML6) + return E_FAIL; + + if (FAILED(hr = variant_get_bool_property(&value, &b))) + return hr; + + properties->normalize_attribute_values = b == VARIANT_TRUE; return S_OK; } @@ -2099,6 +2118,16 @@ static HRESULT WINAPI domdoc_getProperty(IXMLDOMDocument3 *iface, BSTR p, VARIAN return S_OK; } + if (!wcscmp(p, L"NormalizeAttributeValues")) + { + if (properties->version < MSXML6) + return E_FAIL; + + V_VT(var) = VT_BOOL; + V_BOOL(var) = properties->normalize_attribute_values ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; + } + FIXME("Unknown property %s\n", debugstr_w(p)); return E_FAIL; } diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index 4a2d9da7d32..8aff42fe51a 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -192,6 +192,7 @@ enum domnode_flags DOMNODE_READONLY_VALUE = 0x1, DOMNODE_IGNORED_WS_AFTER_STARTTAG = 0x2, DOMNODE_IGNORED_WS = 0x4, + DOMNODE_PARSED_VALUE = 0x8, }; typedef struct _select_ns_entry @@ -214,6 +215,7 @@ struct domdoc_properties LONG selectNsStr_len; bool XPath; bool prohibit_dtd; + bool normalize_attribute_values; int max_element_depth; IUri *uri; }; @@ -373,7 +375,7 @@ extern HRESULT node_get_namespaceURI(struct domnode*, BSTR *); extern HRESULT node_remove_child(struct domnode*,IXMLDOMNode*,IXMLDOMNode**); extern HRESULT node_has_childnodes(const struct domnode*,VARIANT_BOOL*); extern HRESULT node_get_owner_document(const struct domnode*,IXMLDOMDocument**); -extern HRESULT node_get_text(const struct domnode*,BSTR*); +extern HRESULT node_get_text(struct domnode*,BSTR*); extern HRESULT node_get_preserved_text(const struct domnode*,BSTR*); extern HRESULT node_get_value(struct domnode *node, VARIANT *value); extern HRESULT node_select_nodes(struct domnode*,BSTR,IXMLDOMNodeList**); diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index bec00576f2e..bd48c6d822d 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -468,7 +468,7 @@ static void domnode_set_owner(struct domnode *node, struct domnode *owner) struct domnode *n, *old_owner; /* Document node does not have the owner set. */ - owner = owner->owner ? owner->owner : owner; + owner = node_get_doc(owner); assert(!!owner); @@ -711,6 +711,8 @@ HRESULT node_put_data(struct domnode *node, const WCHAR *data) ++p; } + node->flags &= ~DOMNODE_PARSED_VALUE; + switch (node->type) { case NODE_ATTRIBUTE: @@ -1254,6 +1256,7 @@ struct domdoc_properties *domdoc_create_properties(MSXML_VERSION version) properties->version = version; properties->XPath = (version == MSXML4 || version == MSXML6); properties->prohibit_dtd = version == MSXML6; + properties->normalize_attribute_values = false; properties->max_element_depth = version > MSXML3 ? 256 : 5000; /* document uri */ @@ -1287,6 +1290,7 @@ static struct domdoc_properties* domdoc_properties_clone(struct domdoc_propertie pcopy->preserving = properties->preserving; pcopy->validating = properties->validating; pcopy->prohibit_dtd = properties->prohibit_dtd; + pcopy->normalize_attribute_values = properties->normalize_attribute_values; pcopy->schemaCache = properties->schemaCache; if (pcopy->schemaCache) IXMLDOMSchemaCollection2_AddRef(pcopy->schemaCache); @@ -1611,7 +1615,39 @@ static void domnode_get_text(const struct domnode *node, struct node_get_text_co } } -HRESULT node_get_text(const struct domnode *node, BSTR *text) +static void domnode_get_attribute_value(struct domnode *attr, struct string_buffer *buffer) +{ + struct domnode *doc = node_get_doc(attr); + const struct domdoc_properties *properties = doc->properties; + struct domnode *child; + const WCHAR *p; + + if (properties->normalize_attribute_values && (attr->flags & DOMNODE_PARSED_VALUE)) + { + LIST_FOR_EACH_ENTRY(child, &attr->children, struct domnode, entry) + { + p = child->data; + + while (*p) + { + if (*p == '\n' || *p == '\t') + string_append(buffer, L" ", 1); + else + string_append(buffer, p, 1); + ++p; + } + } + } + else + { + LIST_FOR_EACH_ENTRY(child, &attr->children, struct domnode, entry) + { + string_append(buffer, child->data, SysStringLen(child->data)); + } + } +} + +HRESULT node_get_text(struct domnode *node, BSTR *text) { struct node_get_text_context context = { 0 }; struct domnode *child; @@ -1644,10 +1680,7 @@ HRESULT node_get_text(const struct domnode *node, BSTR *text) return string_to_bstr(&context.buffer, text); case NODE_ATTRIBUTE: - LIST_FOR_EACH_ENTRY(child, &node->children, struct domnode, entry) - { - string_append(&context.buffer, child->data, SysStringLen(child->data)); - } + domnode_get_attribute_value(node, &context.buffer); return string_to_bstr(&context.buffer, text); case NODE_DOCUMENT: @@ -3368,8 +3401,8 @@ HRESULT node_get_attribute_by_index(const struct domnode *node, LONG index, IXML HRESULT node_get_attribute_value(struct domnode *node, const WCHAR *name, VARIANT *value) { - struct domnode *attr, *child; struct string_buffer buffer; + struct domnode *attr; if (!name || !value) return E_INVALIDARG; @@ -3386,10 +3419,7 @@ HRESULT node_get_attribute_value(struct domnode *node, const WCHAR *name, VARIAN } string_buffer_init(&buffer); - LIST_FOR_EACH_ENTRY(child, &attr->children, struct domnode, entry) - { - string_append(&buffer, child->data, SysStringLen(child->data)); - } + domnode_get_attribute_value(attr, &buffer); V_VT(value) = VT_BSTR; return string_to_bstr(&buffer, &V_BSTR(value)); @@ -3778,8 +3808,12 @@ static HRESULT WINAPI parse_content_handler_startElement(ISAXContentHandler *ifa ISAXAttributes_getValue(attrs, i, &str, &length); parse_context_node_put_data(c, attr, str, length); - if (attr && is_namespace_definition(attr)) - attr->flags |= DOMNODE_READONLY_VALUE; + if (attr) + { + attr->flags |= DOMNODE_PARSED_VALUE; + if (is_namespace_definition(attr)) + attr->flags |= DOMNODE_READONLY_VALUE; + } } } @@ -4067,6 +4101,7 @@ static HRESULT parse_context_init(struct parse_context *c, const struct domdoc_p V_I4(&v) = 0; ISAXXMLReader_putProperty(c->reader, L"max-element-depth", v); ISAXXMLReader_putFeature(c->reader, L"prohibit-dtd", properties->prohibit_dtd ? VARIANT_TRUE : VARIANT_FALSE); + ISAXXMLReader_putFeature(c->reader, L"normalize-attribute-values", VARIANT_FALSE); domnode_create(NODE_DOCUMENT, NULL, 0, NULL, 0, NULL, &c->root); c->root->properties = domdoc_create_properties(MSXML_DEFAULT); @@ -4312,7 +4347,7 @@ xmlDocPtr create_xmldoc_from_domdoc(struct domnode *node, xmlNodePtr *xmlnode) *xmlnode = NULL; - doc = node->owner ? node->owner : node; + doc = node_get_doc(node); xmldoc = xmlNewDoc(NULL); xmldoc->_private2 = doc; diff --git a/dlls/msxml3/saxreader.c b/dlls/msxml3/saxreader.c index 7bd6f5fa0d9..038e329f624 100644 --- a/dlls/msxml3/saxreader.c +++ b/dlls/msxml3/saxreader.c @@ -260,6 +260,7 @@ enum saxreader_feature UseSchemaLocation = 0x00002000, LexicalHandlerParEntities = 0x00004000, NormalizeLineBreaks = 0x00008000, + NormalizeAttributeValues = 0x00010000, FeatureForceDWord = 0xffffffff, }; @@ -276,6 +277,7 @@ static const struct saxreader_feature_pair saxreader_feature_map[] = { { LexicalHandlerParEntities, L"http://xml.org/sax/features/lexical-handler/parameter-entities" }, { NamespacePrefixes, L"http://xml.org/sax/features/namespace-prefixes" }, { Namespaces, L"http://xml.org/sax/features/namespaces" }, + { NormalizeAttributeValues, L"normalize-attribute-values" }, { NormalizeLineBreaks, L"normalize-line-breaks" }, { ProhibitDTD, L"prohibit-dtd" }, { SchemaValidation, L"schema-validation" }, @@ -3714,6 +3716,7 @@ static BSTR saxreader_parse_attvalue(struct saxlocator *locator) struct string_buffer buffer = { 0 }; struct text_position position; WCHAR quote[2] = { 0 }, ch; + bool normalized; BSTR name; if (!(quote[0] = saxreader_is_quote(locator))) @@ -3754,14 +3757,29 @@ static BSTR saxreader_parse_attvalue(struct saxlocator *locator) } else { - if (saxreader_cmp(locator, L"\r\n") - || saxreader_cmp(locator, L"\r") - || saxreader_cmp(locator, L"\n") - || saxreader_cmp(locator, L"\t")) + normalized = false; + if (locator->saxreader->features & NormalizeAttributeValues) { - saxreader_string_append(locator, &buffer, L" ", 1); + if (saxreader_cmp(locator, L"\r\n") + || saxreader_cmp(locator, L"\r") + || saxreader_cmp(locator, L"\n") + || saxreader_cmp(locator, L"\t")) + { + saxreader_string_append(locator, &buffer, L" ", 1); + normalized = true; + } } else + { + /* Potentially could happen on read */ + if (saxreader_cmp(locator, L"\r\n")) + { + saxreader_string_append(locator, &buffer, L"\n", 1); + normalized = true; + } + } + + if (!normalized) { ch = *saxreader_get_ptr_noread(locator); if (!ch) @@ -6199,6 +6217,7 @@ static HRESULT WINAPI isaxxmlreader_putFeature(ISAXXMLReader *iface, const WCHAR feature == Namespaces || feature == NamespacePrefixes || feature == NormalizeLineBreaks || + feature == NormalizeAttributeValues || feature == ProhibitDTD) { return set_feature_value(reader, feature, value); @@ -6406,7 +6425,7 @@ static HRESULT saxreader_create(MSXML_VERSION version, struct saxreader **reader object->ISAXXMLReader_iface.lpVtbl = &saxxmlreadervtbl; object->ISAXXMLReaderExtension_iface.lpVtbl = &saxxmlreaderextensionvtbl; object->refcount = 1; - object->features = Namespaces | NamespacePrefixes | NormalizeLineBreaks; + object->features = Namespaces | NamespacePrefixes | NormalizeLineBreaks | NormalizeAttributeValues; object->version = version; object->empty_bstr = SysAllocString(L""); object->max_element_depth = version > MSXML3 ? 256 : 5000; diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 15655a7b04e..7c8e28b56ae 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -16299,7 +16299,6 @@ static void test_attribute_value(void) hr = IXMLDOMElement_getAttribute(element, _bstr_("attr"), &value); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!wcscmp(V_BSTR(&value), L"v\na\nl\nu\te"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&value))); VariantClear(&value); diff --git a/dlls/msxml6/tests/domdoc.c b/dlls/msxml6/tests/domdoc.c index 91740990aa2..996b214ffe1 100644 --- a/dlls/msxml6/tests/domdoc.c +++ b/dlls/msxml6/tests/domdoc.c @@ -838,12 +838,18 @@ static void test_normalize_attribute_values(void) { IXMLDOMElement *element; IXMLDOMDocument2 *doc; + VARIANT_BOOL b; VARIANT var; HRESULT hr; hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (void **)&doc); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + b = 0x1; + hr = IXMLDOMDocument2_get_preserveWhiteSpace(doc, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %d.\n", b); + hr = IXMLDOMDocument2_loadXML(doc, _bstr_(L"<a attr=\"v\r\na\tl\r\" />"), NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -852,24 +858,39 @@ static void test_normalize_attribute_values(void) hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!wcscmp(V_BSTR(&var), L"v\na\tl\n"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); VariantClear(&var); + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!wcscmp(V_BSTR(&var), L"v\na\tl\n"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); + VariantClear(&var); + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + V_VT(&var) = VT_I2; V_I2(&var) = 10; hr = IXMLDOMDocument2_getProperty(doc, _bstr_(L"NormalizeAttributeValues"), &var); -todo_wine { ok(hr == S_OK, "Failed to get property value, hr %#lx.\n", hr); ok(V_VT(&var) == VT_BOOL, "Unexpected property value type, vt %d.\n", V_VT(&var)); ok(V_BOOL(&var) == VARIANT_FALSE, "Unexpected property value.\n"); -} + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!wcscmp(V_BSTR(&var), L"v\na\tl\n"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); VariantClear(&var); + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!wcscmp(V_BSTR(&var), L"v\na\tl\n"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); + VariantClear(&var); + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + V_VT(&var) = VT_BOOL; V_BOOL(&var) = VARIANT_TRUE; hr = IXMLDOMDocument2_setProperty(doc, _bstr_(L"NormalizeAttributeValues"), var); @@ -878,16 +899,56 @@ todo_wine { V_VT(&var) = VT_I2; V_I2(&var) = 10; hr = IXMLDOMDocument2_getProperty(doc, _bstr_(L"NormalizeAttributeValues"), &var); -todo_wine { ok(hr == S_OK, "Failed to get property value, hr %#lx.\n", hr); ok(V_VT(&var) == VT_BOOL, "Unexpected property value type, vt %d.\n", V_VT(&var)); ok(V_BOOL(&var) == VARIANT_TRUE, "Unexpected property value.\n"); -} + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!wcscmp(V_BSTR(&var), L"v a l "), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); VariantClear(&var); + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(L"v\r\na\tl\n\t"); + hr = IXMLDOMElement_setAttribute(element, _bstr_(L"attr"), var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + VariantClear(&var); + + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(!wcscmp(V_BSTR(&var), L"v\r\na\tl\n\t"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); + VariantClear(&var); + + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(!wcscmp(V_BSTR(&var), L"v\r\na\tl\n\t"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); + VariantClear(&var); + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&var) = VT_BOOL; + V_BOOL(&var) = VARIANT_FALSE; + hr = IXMLDOMDocument2_setProperty(doc, _bstr_(L"NormalizeAttributeValues"), var); + ok(hr == S_OK, "Failed to set property, hr %#lx.\n", hr); + + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!wcscmp(V_BSTR(&var), L"v\na\tl\n\t"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); + VariantClear(&var); + + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMElement_getAttribute(element, _bstr_(L"attr"), &var); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!wcscmp(V_BSTR(&var), L"v\na\tl\n\t"), "Unexpected value %s.\n", debugstr_w(V_BSTR(&var))); + VariantClear(&var); + hr = IXMLDOMDocument2_put_preserveWhiteSpace(doc, VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IXMLDOMElement_Release(element); IXMLDOMDocument2_Release(doc); free_bstrs(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10759