From 52c6c6a1d192324ee4b6f3910c8834d5f5bb2151 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 14 Apr 2014 09:00:51 +0400 Subject: [PATCH] msxml3: Fix transformation result output for stream and BSTR cases. --- dlls/msxml3/main.c | 3 + dlls/msxml3/msxml_private.h | 2 +- dlls/msxml3/node.c | 297 +++++++++++++++++++++++++++++++----- dlls/msxml3/stylesheet.c | 20 +-- dlls/msxml3/tests/domdoc.c | 14 +- 5 files changed, 270 insertions(+), 66 deletions(-) diff --git a/dlls/msxml3/main.c b/dlls/msxml3/main.c index d67e4e07fa8..49bfff26075 100644 --- a/dlls/msxml3/main.c +++ b/dlls/msxml3/main.c @@ -35,6 +35,7 @@ # ifdef HAVE_LIBXSLT_TRANSFORM_H # include # endif +# include # include # include # include @@ -171,6 +172,7 @@ DECL_FUNCPTR(xsltCleanupGlobals); DECL_FUNCPTR(xsltFreeStylesheet); DECL_FUNCPTR(xsltFreeTransformContext); DECL_FUNCPTR(xsltNewTransformContext); +DECL_FUNCPTR(xsltNextImport); DECL_FUNCPTR(xsltParseStylesheetDoc); DECL_FUNCPTR(xsltQuoteUserParams); DECL_FUNCPTR(xsltSaveResultTo); @@ -196,6 +198,7 @@ static void init_libxslt(void) LOAD_FUNCPTR(xsltFreeStylesheet, 1); LOAD_FUNCPTR(xsltFreeTransformContext, 1); LOAD_FUNCPTR(xsltNewTransformContext, 1); + LOAD_FUNCPTR(xsltNextImport, 1); LOAD_FUNCPTR(xsltParseStylesheetDoc, 1); LOAD_FUNCPTR(xsltQuoteUserParams, 1); LOAD_FUNCPTR(xsltSaveResultTo, 1); diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index fb7ddd1022a..be37e170b50 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -367,7 +367,7 @@ extern HRESULT node_get_text(const xmlnode*,BSTR*) DECLSPEC_HIDDEN; extern HRESULT node_select_nodes(const xmlnode*,BSTR,IXMLDOMNodeList**) DECLSPEC_HIDDEN; extern HRESULT node_select_singlenode(const xmlnode*,BSTR,IXMLDOMNode**) DECLSPEC_HIDDEN; extern HRESULT node_transform_node(const xmlnode*,IXMLDOMNode*,BSTR*) DECLSPEC_HIDDEN; -extern HRESULT node_transform_node_params(const xmlnode*,IXMLDOMNode*,BSTR*,const struct xslprocessor_params*) DECLSPEC_HIDDEN; +extern HRESULT node_transform_node_params(const xmlnode*,IXMLDOMNode*,BSTR*,IStream*,const struct xslprocessor_params*) DECLSPEC_HIDDEN; extern HRESULT node_create_supporterrorinfo(const tid_t*,void**) DECLSPEC_HIDDEN; extern HRESULT get_domdoc_from_xmldoc(xmlDocPtr xmldoc, IXMLDOMDocument3 **document) DECLSPEC_HIDDEN; diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 1319d7750d4..b49f46ca23a 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -35,6 +35,7 @@ # ifdef HAVE_LIBXSLT_TRANSFORM_H # include # endif +# include # include # include # include @@ -65,6 +66,7 @@ MAKE_FUNCPTR(xsltCleanupGlobals); MAKE_FUNCPTR(xsltFreeStylesheet); MAKE_FUNCPTR(xsltFreeTransformContext); MAKE_FUNCPTR(xsltNewTransformContext); +MAKE_FUNCPTR(xsltNextImport); MAKE_FUNCPTR(xsltParseStylesheetDoc); MAKE_FUNCPTR(xsltQuoteUserParams); MAKE_FUNCPTR(xsltSaveResultTo); @@ -905,6 +907,8 @@ HRESULT node_get_xml(xmlnode *This, BOOL ensure_eol, BSTR *ret) return *ret ? S_OK : E_OUTOFMEMORY; } +#ifdef SONAME_LIBXSLT + /* duplicates xmlBufferWriteQuotedString() logic */ static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string) { @@ -914,7 +918,7 @@ static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string { if (xmlStrchr(string, '\'')) { - xmlOutputBufferWrite(buf, 1, "\""); + xmlOutputBufferWrite(buf, 1, "\""); base = cur = string; while (*cur) @@ -932,13 +936,13 @@ static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string } if (base != cur) xmlOutputBufferWrite(buf, cur-base, (const char*)base); - xmlOutputBufferWrite(buf, 1, "\""); - } + xmlOutputBufferWrite(buf, 1, "\""); + } else { - xmlOutputBufferWrite(buf, 1, "\'"); + xmlOutputBufferWrite(buf, 1, "\'"); xmlOutputBufferWriteString(buf, (const char*)string); - xmlOutputBufferWrite(buf, 1, "\'"); + xmlOutputBufferWrite(buf, 1, "\'"); } } else @@ -949,6 +953,112 @@ static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string } } +static int XMLCALL transform_to_stream_write(void *context, const char *buffer, int len) +{ + DWORD written; + HRESULT hr = IStream_Write((IStream*)context, buffer, len, &written); + return hr == S_OK ? written : -1; +} + +/* Output for method "text" */ +static void transform_write_text(xmlDocPtr result, xsltStylesheetPtr style, xmlOutputBufferPtr output) +{ + xmlNodePtr cur = result->children; + while (cur) + { + if (cur->type == XML_TEXT_NODE) + xmlOutputBufferWriteString(output, (const char*)cur->content); + + /* skip to next node */ + if (cur->children) + { + if ((cur->children->type != XML_ENTITY_DECL) && + (cur->children->type != XML_ENTITY_REF_NODE) && + (cur->children->type != XML_ENTITY_NODE)) + { + cur = cur->children; + continue; + } + } + + if (cur->next) { + cur = cur->next; + continue; + } + + do + { + cur = cur->parent; + if (cur == NULL) + break; + if (cur == (xmlNodePtr) style->doc) { + cur = NULL; + break; + } + if (cur->next) { + cur = cur->next; + break; + } + } while (cur); + } +} + +#undef XSLT_GET_IMPORT_PTR +#define XSLT_GET_IMPORT_PTR(res, style, name) { \ + xsltStylesheetPtr st = style; \ + res = NULL; \ + while (st != NULL) { \ + if (st->name != NULL) { res = st->name; break; } \ + st = pxsltNextImport(st); \ + }} + +#undef XSLT_GET_IMPORT_INT +#define XSLT_GET_IMPORT_INT(res, style, name) { \ + xsltStylesheetPtr st = style; \ + res = -1; \ + while (st != NULL) { \ + if (st->name != -1) { res = st->name; break; } \ + st = pxsltNextImport(st); \ + }} + +static void transform_write_xmldecl(xmlDocPtr result, xsltStylesheetPtr style, BOOL omit_encoding, xmlOutputBufferPtr output) +{ + int omit_xmldecl, standalone; + + XSLT_GET_IMPORT_INT(omit_xmldecl, style, omitXmlDeclaration); + if (omit_xmldecl == 1) return; + + XSLT_GET_IMPORT_INT(standalone, style, standalone); + + xmlOutputBufferWriteString(output, "version) + { + xmlOutputBufferWriteString(output, "\""); + xmlOutputBufferWriteString(output, (const char *)result->version); + xmlOutputBufferWriteString(output, "\""); + } + else + xmlOutputBufferWriteString(output, "\"1.0\""); + + if (!omit_encoding) + { + const xmlChar *encoding; + + /* default encoding is UTF-16 */ + XSLT_GET_IMPORT_PTR(encoding, style, encoding); + xmlOutputBufferWriteString(output, " encoding="); + xmlOutputBufferWriteString(output, "\""); + xmlOutputBufferWriteString(output, encoding ? (const char *)encoding : "UTF-16"); + xmlOutputBufferWriteString(output, "\""); + } + + /* standalone attribute */ + if (standalone != -1) + xmlOutputBufferWriteString(output, standalone == 0 ? " standalone=\"no\"" : " standalone=\"yes\""); + + xmlOutputBufferWriteString(output, "?>"); +} + static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc) { xmlDtdPtr cur = doc->intSubset; @@ -963,7 +1073,7 @@ static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc) { xmlOutputBufferWriteString(buf, " "); xml_write_quotedstring(buf, cur->SystemID); - } + } } else if (cur->SystemID) { @@ -973,7 +1083,8 @@ static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc) xmlOutputBufferWriteString(buf, ">\n"); } -static void htmldoc_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc) +/* Duplicates htmlDocContentDumpFormatOutput() the way we need it - doesn't add trailing newline. */ +static void htmldoc_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc, const char *encoding, int format) { xmlElementType type; @@ -982,34 +1093,155 @@ static void htmldoc_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc) doc->type = XML_HTML_DOCUMENT_NODE; if (doc->intSubset) htmldtd_dumpcontent(buf, doc); - if (doc->children) - { + if (doc->children) { xmlNodePtr cur = doc->children; - - while (cur) - { - htmlNodeDumpFormatOutput(buf, doc, cur, NULL, 1); + while (cur) { + htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format); cur = cur->next; } - } doc->type = type; } -static const xmlChar *get_output_buffer_content(xmlOutputBufferPtr output) +static inline BOOL transform_is_empty_resultdoc(xmlDocPtr result) { -#ifdef LIBXML2_NEW_BUFFER - return xmlOutputBufferGetContent(output); -#else - return xmlBufferContent(output->buffer); -#endif + return !result->children || ((result->children->type == XML_DTD_NODE) && !result->children->next); } +static inline BOOL transform_is_valid_method(xsltStylesheetPtr style) +{ + return !style->methodURI || !(style->method && xmlStrEqual(style->method, (const xmlChar *)"xhtml")); +} + +/* Helper to write transformation result to specified output buffer. */ +static HRESULT node_transform_write(xsltStylesheetPtr style, xmlDocPtr result, BOOL omit_encoding, const char *encoding, xmlOutputBufferPtr output) +{ + const xmlChar *method; + int indent; + + if (!transform_is_valid_method(style)) + { + ERR("unknown output method\n"); + return E_FAIL; + } + + XSLT_GET_IMPORT_PTR(method, style, method) + XSLT_GET_IMPORT_INT(indent, style, indent); + + if (!method && (result->type == XML_HTML_DOCUMENT_NODE)) + method = (const xmlChar *) "html"; + + if (method && xmlStrEqual(method, (const xmlChar *)"html")) + { + htmlSetMetaEncoding(result, (const xmlChar *)encoding); + if (indent == -1) + indent = 1; + htmldoc_dumpcontent(output, result, (const char*)encoding, indent); + } + else if (method && xmlStrEqual(method, (const xmlChar *)"xhtml")) + { + htmlSetMetaEncoding(result, (const xmlChar *) encoding); + htmlDocContentDumpOutput(output, result, encoding); + } + else if (method && xmlStrEqual(method, (const xmlChar *)"text")) + transform_write_text(result, style, output); + else + { + transform_write_xmldecl(result, style, omit_encoding, output); + + if (result->children) + { + xmlNodePtr child = result->children; + + while (child) + { + xmlNodeDumpOutput(output, result, child, 0, indent == 1, encoding); + if (indent && ((child->type == XML_DTD_NODE) || ((child->type == XML_COMMENT_NODE) && child->next))) + xmlOutputBufferWriteString(output, "\r\n"); + child = child->next; + } + } + } + + xmlOutputBufferFlush(output); + return S_OK; +} + +/* For BSTR output is always UTF-16, without 'encoding' attribute */ +static HRESULT node_transform_write_to_bstr(xsltStylesheetPtr style, xmlDocPtr result, BSTR *str) +{ + HRESULT hr = S_OK; + + if (transform_is_empty_resultdoc(result)) + *str = SysAllocStringLen(NULL, 0); + else + { + xmlOutputBufferPtr output = xmlAllocOutputBuffer(xmlFindCharEncodingHandler("UTF-16")); + const xmlChar *content; + size_t len; + + *str = NULL; + if (!output) + return E_OUTOFMEMORY; + + hr = node_transform_write(style, result, TRUE, "UTF-16", output); +#ifdef LIBXML2_NEW_BUFFER + content = xmlBufContent(output->conv); + len = xmlBufUse(output->conv); +#else + content = xmlBufferContent(output->conv); + len = xmlBufferLength(output->conv); +#endif + /* UTF-16 encoder places UTF-16 bom, we don't need it for BSTR */ + content += sizeof(WCHAR); + *str = SysAllocStringLen((WCHAR*)content, len/sizeof(WCHAR) - 1); + xmlOutputBufferClose(output); + } + + return *str ? hr : E_OUTOFMEMORY; +} + +static HRESULT node_transform_write_to_stream(xsltStylesheetPtr style, xmlDocPtr result, IStream *stream) +{ + static const xmlChar *utf16 = (const xmlChar*)"UTF-16"; + xmlOutputBufferPtr output; + const xmlChar *encoding; + HRESULT hr; + + if (transform_is_empty_resultdoc(result)) + { + WARN("empty result document\n"); + return S_OK; + } + + if (style->methodURI && (!style->method || !xmlStrEqual(style->method, (const xmlChar *) "xhtml"))) + { + ERR("unknown output method\n"); + return E_FAIL; + } + + /* default encoding is UTF-16 */ + XSLT_GET_IMPORT_PTR(encoding, style, encoding); + if (!encoding) + encoding = utf16; + + output = xmlOutputBufferCreateIO(transform_to_stream_write, NULL, stream, xmlFindCharEncodingHandler((const char*)encoding)); + if (!output) + return E_OUTOFMEMORY; + + hr = node_transform_write(style, result, FALSE, (const char*)encoding, output); + xmlOutputBufferClose(output); + return hr; +} + +#endif + HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, BSTR *p, - const struct xslprocessor_params *params) + IStream *stream, const struct xslprocessor_params *params) { #ifdef SONAME_LIBXSLT xsltStylesheetPtr xsltSS; + HRESULT hr = S_OK; xmlnode *sheet; if (!libxslt_handle) return E_NOTIMPL; @@ -1058,21 +1290,12 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, else result = pxsltApplyStylesheet(xsltSS, This->node->doc, NULL); - if(result) + if (result) { - const xmlChar *content; - - xmlOutputBufferPtr output = xmlAllocOutputBuffer(NULL); - if (output) - { - if(result->type == XML_HTML_DOCUMENT_NODE) - htmldoc_dumpcontent(output, result->doc); - else - pxsltSaveResultTo(output, result->doc, xsltSS); - content = get_output_buffer_content(output); - *p = bstr_from_xmlChar(content); - xmlOutputBufferClose(output); - } + if (stream) + hr = node_transform_write_to_stream(xsltSS, result, stream); + else + hr = node_transform_write_to_bstr(xsltSS, result, p); xmlFreeDoc(result); } /* libxslt "helpfully" frees the XML document the stylesheet was @@ -1083,7 +1306,7 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, if(!*p) *p = SysAllocStringLen(NULL, 0); - return S_OK; + return hr; #else FIXME("libxslt headers were not found at compile time\n"); return E_NOTIMPL; @@ -1092,7 +1315,7 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, HRESULT node_transform_node(const xmlnode *node, IXMLDOMNode *stylesheet, BSTR *p) { - return node_transform_node_params(node, stylesheet, p, NULL); + return node_transform_node_params(node, stylesheet, p, NULL, NULL); } HRESULT node_select_nodes(const xmlnode *This, BSTR query, IXMLDOMNodeList **nodes) diff --git a/dlls/msxml3/stylesheet.c b/dlls/msxml3/stylesheet.c index b2816eeaac9..000199f2aaa 100644 --- a/dlls/msxml3/stylesheet.c +++ b/dlls/msxml3/stylesheet.c @@ -477,7 +477,7 @@ static HRESULT WINAPI xslprocessor_put_output( WARN("failed to get IStream from output, 0x%08x\n", hr); break; default: - FIXME("output type %d not handed\n", V_VT(&output)); + FIXME("output type %d not handled\n", V_VT(&output)); hr = E_FAIL; } @@ -530,22 +530,8 @@ static HRESULT WINAPI xslprocessor_transform( if (!ret) return E_INVALIDARG; SysFreeString(This->outstr); - - hr = node_transform_node_params(get_node_obj(This->input), This->stylesheet->node, &This->outstr, &This->params); - if (hr == S_OK) - { - if (This->output) - { - ULONG len = 0; - - /* output to stream */ - hr = IStream_Write(This->output, This->outstr, SysStringByteLen(This->outstr), &len); - *ret = len == SysStringByteLen(This->outstr) ? VARIANT_TRUE : VARIANT_FALSE; - } - } - else - *ret = VARIANT_FALSE; - + hr = node_transform_node_params(get_node_obj(This->input), This->stylesheet->node, &This->outstr, This->output, &This->params); + *ret = hr == S_OK ? VARIANT_TRUE : VARIANT_FALSE; return hr; #else FIXME("libxml2 is required but wasn't present at compile time\n"); diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 0c700c37855..50f0941a406 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -6121,7 +6121,7 @@ static void test_testTransforms(void) ok(hr == S_OK, "ret %08x\n", hr ); if(hr == S_OK) { - ok( compareIgnoreReturns( bOut, _bstr_(szTransformOutput)), "Stylesheet output not correct\n"); + ok( compareIgnoreReturns( bOut, _bstr_(szTransformOutput)), "got output %s\n", wine_dbgstr_w(bOut)); SysFreeString(bOut); } @@ -8471,7 +8471,7 @@ todo_wine { hr = IXSLProcessor_get_output(processor, &v); ok(hr == S_OK, "got 0x%08x\n", hr); ok(V_VT(&v) == VT_BSTR, "got type %d\n", V_VT(&v)); - ok(lstrcmpW(V_BSTR(&v), _bstr_("")) == 0, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); + ok(*V_BSTR(&v) == 0, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); IXMLDOMDocument_Release(doc2); VariantClear(&v); @@ -11695,12 +11695,6 @@ static const char omitxmldecl_doc[] = " " ""; -static const char omitxmldecl_result[] = -"item1item2"; - -static const char omitxmldecl_result2[] = -"item1item2\n"; - static void test_xsltext(void) { IXMLDOMDocument *doc, *doc2; @@ -11730,9 +11724,7 @@ static void test_xsltext(void) hr = IXMLDOMDocument_transformNode(doc2, (IXMLDOMNode*)doc, &ret); ok(hr == S_OK, "got 0x%08x\n", hr); - /* Old enough libxslt places extra '\n' at the end of the output. */ - ok(!lstrcmpW(ret, _bstr_(omitxmldecl_result)) || - !lstrcmpW(ret, _bstr_(omitxmldecl_result2)), "transform result %s\n", wine_dbgstr_w(ret)); + ok(!lstrcmpW(ret, _bstr_("item1item2")), "transform result %s\n", wine_dbgstr_w(ret)); SysFreeString(ret); IXMLDOMDocument_Release(doc2);