msxml3: Fix transformation result output for stream and BSTR cases.
This commit is contained in:
parent
7367c68cc9
commit
52c6c6a1d1
|
@ -35,6 +35,7 @@
|
|||
# ifdef HAVE_LIBXSLT_TRANSFORM_H
|
||||
# include <libxslt/transform.h>
|
||||
# endif
|
||||
# include <libxslt/imports.h>
|
||||
# include <libxslt/xsltutils.h>
|
||||
# include <libxslt/variables.h>
|
||||
# include <libxslt/xsltInternals.h>
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
# ifdef HAVE_LIBXSLT_TRANSFORM_H
|
||||
# include <libxslt/transform.h>
|
||||
# endif
|
||||
# include <libxslt/imports.h>
|
||||
# include <libxslt/variables.h>
|
||||
# include <libxslt/xsltutils.h>
|
||||
# include <libxslt/xsltInternals.h>
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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, "<?xml version=");
|
||||
if (result->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;
|
||||
|
@ -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);
|
||||
if (stream)
|
||||
hr = node_transform_write_to_stream(xsltSS, result, stream);
|
||||
else
|
||||
pxsltSaveResultTo(output, result->doc, xsltSS);
|
||||
content = get_output_buffer_content(output);
|
||||
*p = bstr_from_xmlChar(content);
|
||||
xmlOutputBufferClose(output);
|
||||
}
|
||||
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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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[] =
|
|||
" <item name=\"item2\"/>"
|
||||
"</a>";
|
||||
|
||||
static const char omitxmldecl_result[] =
|
||||
"<node>item1</node><node>item2</node>";
|
||||
|
||||
static const char omitxmldecl_result2[] =
|
||||
"<node>item1</node><node>item2</node>\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_("<node>item1</node><node>item2</node>")), "transform result %s\n", wine_dbgstr_w(ret));
|
||||
SysFreeString(ret);
|
||||
|
||||
IXMLDOMDocument_Release(doc2);
|
||||
|
|
Loading…
Reference in New Issue