[PATCH v2 0/6] MR11583: msxml3/dom: Add IStream support for the document object.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> -- v2: msxml3/dom: Add IStream support for the document object. https://gitlab.winehq.org/wine/wine/-/merge_requests/11583
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/tests/domdoc.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index bc950788e08..b76684b1ebc 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -18708,6 +18708,29 @@ static void test_document_stream(void) ok(!memcmp(buffer, "e></e>\r\n", 8), "%s\n", debugstr_an(buffer, 3)); IStream_Release(stream); + + /* Serialization happens on first read. */ + hr = IXMLDOMDocument_loadXML(doc, _bstr_("<f></f>"), NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMDocument_QueryInterface(doc, &IID_IStream, (void **)&stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMDocument_loadXML(doc, _bstr_("<g></g>"), NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + memset(buffer, 0, sizeof(buffer)); + hr = IStream_Read(stream, buffer, 9, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!memcmp(buffer, "<g></g>\r\n", 9), "%s\n", debugstr_an(buffer, 9)); + hr = IXMLDOMDocument_loadXML(doc, _bstr_("<h></h>"), NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + off.QuadPart = 0; + hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + memset(buffer, 0, sizeof(buffer)); + hr = IStream_Read(stream, buffer, 9, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!memcmp(buffer, "<g></g>\r\n", 9), "%s\n", debugstr_an(buffer, 9)); + IStream_Release(stream); + IPersistStreamInit_Release(streaminit); IXMLDOMDocument_Release(doc); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11583
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/node.c | 4 ++++ dlls/msxml3/tests/domdoc.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 70e0b02c077..7492a76865f 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -4678,6 +4678,10 @@ HRESULT node_save(struct domnode *doc, IStream *stream) node_dump_context_init(&context, codepage, stream); + /* UTF-16 BE BOM */ + if (codepage == ~0u) + node_dump_append(&context, L"\xfeff", 1); + LIST_FOR_EACH_ENTRY(node, &doc->children, struct domnode, entry) { node_dump(node, &context); diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index b76684b1ebc..7a3cfc45f12 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -6687,7 +6687,6 @@ static void test_save(void) hr = GetHGlobalFromStream(stream, &global); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ptr = GlobalLock(global); - todo_wine ok(!memcmp(ptr, "\xff\xfe<\x00", 4), "Unexpected content %s.\n", debugstr_an(ptr, 4)); GlobalUnlock(global); IStream_Release(stream); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11583
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/tests/domdoc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 7a3cfc45f12..c90ec116aac 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -18656,8 +18656,14 @@ static void test_document_stream(void) hr = IStream_Write(stream, "<d></d>", 7, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMDocument_get_xml(doc, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!wcscmp(str, L""), "Unexpected content %s.\n", debugstr_w(str)); + SysFreeString(str); hr = IStream_Write(stream2, "<e></e>", 7, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IStream_Write(stream, "\r", 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IStream_Release(stream); hr = IXMLDOMDocument_get_xml(doc, &str); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11583
From: Nikolay Sivov <nsivov@codeweavers.com> The issue here is in empty elements formatting - it doesn't quite match expected output, but it's not a goal of this test. Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/tests/domdoc.c | 75 ++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index c90ec116aac..653b8a698c6 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -18377,10 +18377,10 @@ static void test_document_stream(void) { IStream *stream, *stream2, *cloned_stream; IPersistStreamInit *streaminit; + ULARGE_INTEGER pos, size; IUnknown *unk, *unk2; IXMLDOMDocument *doc; IXMLDOMElement *root; - ULARGE_INTEGER pos; IXMLDOMNode *node; LARGE_INTEGER off; char buffer[64]; @@ -18432,6 +18432,9 @@ static void test_document_stream(void) hr = IStream_Clone(stream, &cloned_stream); ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + size.QuadPart = 10; + hr = IStream_SetSize(stream, size); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); hr = IStream_Stat(stream, &stat, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -18439,12 +18442,12 @@ static void test_document_stream(void) ok(stat.type == STGTY_STREAM, "Unexpected type %#lx.\n", stat.type); ok(!stat.cbSize.QuadPart, "Unexpected size %s.\n", wine_dbgstr_longlong(stat.cbSize.QuadPart)); - hr = IXMLDOMDocument_loadXML(doc, _bstr_("<a ></a >"), NULL); + hr = IXMLDOMDocument_loadXML(doc, _bstr_("<a >t</a >"), NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IStream_Stat(stream, &stat, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(stat.cbSize.QuadPart == 9, "Unexpected size %s.\n", wine_dbgstr_longlong(stat.cbSize.QuadPart)); + ok(stat.cbSize.QuadPart == 10, "Unexpected size %s.\n", wine_dbgstr_longlong(stat.cbSize.QuadPart)); off.QuadPart = 0; hr = IStream_Seek(stream, off, STREAM_SEEK_CUR, &pos); @@ -18457,7 +18460,12 @@ static void test_document_stream(void) memset(buffer, 0, sizeof(buffer)); hr = IStream_Read(stream, buffer, 4, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "<a><", 4), "%s\n", debugstr_an(buffer, 4)); + ok(!memcmp(buffer, "<a>t", 4), "%s\n", debugstr_an(buffer, 4)); + + off.QuadPart = 0; + hr = IStream_Seek(stream, off, STREAM_SEEK_CUR, &pos); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos.QuadPart == 4, "Unexpected position %s.\n", wine_dbgstr_longlong(pos.QuadPart)); /* Tree modifications are not reflected in the stream content. */ V_VT(&v) = VT_I1; @@ -18478,18 +18486,18 @@ static void test_document_stream(void) hr = IXMLDOMDocument_get_xml(doc, &str); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!wcscmp(str, L"<a><e/></a>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); + ok(!wcscmp(str, L"<a>t<e/></a>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); SysFreeString(str); memset(buffer, 0, sizeof(buffer)); hr = IStream_Read(stream, buffer, 4, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "/a>\r", 4), "%s\n", debugstr_an(buffer, 4)); + ok(!memcmp(buffer, "</a>", 4), "%s\n", debugstr_an(buffer, 4)); memset(buffer, 0, sizeof(buffer)); - hr = IStream_Read(stream, buffer, 1, NULL); + hr = IStream_Read(stream, buffer, 2, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "\n", 1), "%s\n", debugstr_an(buffer, 1)); + ok(!memcmp(buffer, "\r\n", 2), "%s\n", debugstr_an(buffer, 2)); memset(buffer, 0, sizeof(buffer)); hr = IStream_Read(stream, buffer, 1, &length); @@ -18502,7 +18510,7 @@ static void test_document_stream(void) memset(buffer, 0, sizeof(buffer)); hr = IStream_Read(stream, buffer, 4, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "a></", 4), "%s\n", debugstr_an(buffer, 4)); + ok(!memcmp(buffer, "a>t<", 4), "%s\n", debugstr_an(buffer, 4)); /* InitNew */ hr = IPersistStreamInit_InitNew(streaminit); @@ -18511,14 +18519,14 @@ static void test_document_stream(void) memset(buffer, 0, sizeof(buffer)); hr = IStream_Read(stream, buffer, 3, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "a>\r", 3), "%s\n", debugstr_an(buffer, 3)); + ok(!memcmp(buffer, "/a>", 3), "%s\n", debugstr_an(buffer, 3)); hr = IPersistStreamInit_IsDirty(streaminit); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IXMLDOMDocument_get_xml(doc, &str); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!wcscmp(str, L"<a><e/></a>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); + ok(!wcscmp(str, L"<a>t<e/></a>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); SysFreeString(str); /* Clear dirty state */ @@ -18547,9 +18555,9 @@ static void test_document_stream(void) hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(buffer, 0, sizeof(buffer)); - hr = IStream_Read(stream, buffer, 7, NULL); + hr = IStream_Read(stream, buffer, 8, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "<a></a>", 7), "%s\n", debugstr_an(buffer, 7)); + ok(!memcmp(buffer, "<a>t</a>", 8), "%s\n", debugstr_an(buffer, 8)); IStream_Release(stream2); @@ -18578,6 +18586,11 @@ static void test_document_stream(void) /* Releasing the stream triggers document parsing. */ hr = IStream_Write(stream, "<a>text</a>", 11, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IStream_Stat(stream, NULL, 0); + ok(hr == STG_E_INVALIDPOINTER, "Unexpected hr %#lx.\n", hr); + hr = IStream_Stat(stream, &stat, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!stat.cbSize.QuadPart, "Unexpected size %s.\n", wine_dbgstr_longlong(stat.cbSize.QuadPart)); hr = IPersistStreamInit_IsDirty(streaminit); ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); hr = IXMLDOMDocument_get_xml(doc, &str); @@ -18639,13 +18652,13 @@ static void test_document_stream(void) off.QuadPart = 0; hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IStream_Write(stream, "</c></b>", 8, &length); + hr = IStream_Write(stream, "t</c></b>", 9, &length); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IStream_Release(stream); hr = IXMLDOMDocument_get_xml(doc, &str); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!wcscmp(str, L"<b><c></c></b>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); + ok(!wcscmp(str, L"<b><c>t</c></b>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); SysFreeString(str); /* Open two streams for writing. */ @@ -18660,7 +18673,7 @@ static void test_document_stream(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!wcscmp(str, L""), "Unexpected content %s.\n", debugstr_w(str)); SysFreeString(str); - hr = IStream_Write(stream2, "<e></e>", 7, NULL); + hr = IStream_Write(stream2, "<e>t</e>", 8, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IStream_Write(stream, "\r", 1, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -18674,7 +18687,7 @@ static void test_document_stream(void) IStream_Release(stream2); hr = IXMLDOMDocument_get_xml(doc, &str); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!wcscmp(str, L"<e></e>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); + ok(!wcscmp(str, L"<e>t</e>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); SysFreeString(str); /* One stream for reading one for writing. */ @@ -18684,56 +18697,56 @@ static void test_document_stream(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(buffer, 0, sizeof(buffer)); - hr = IStream_Read(stream, buffer, 9, NULL); + hr = IStream_Read(stream, buffer, 10, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "<e></e>\r\n", 9), "%s\n", debugstr_an(buffer, 3)); + ok(!memcmp(buffer, "<e>t</e>\r\n", 10), "%s\n", debugstr_an(buffer, 10)); - hr = IStream_Write(stream2, "<f></f>", 7, NULL); + hr = IStream_Write(stream2, "<f>t</f>", 8, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IStream_Release(stream2); hr = IXMLDOMDocument_get_xml(doc, &str); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!wcscmp(str, L"<f></f>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); + ok(!wcscmp(str, L"<f>t</f>\r\n"), "Unexpected content %s.\n", debugstr_w(str)); SysFreeString(str); off.QuadPart = 0; hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(buffer, 0, sizeof(buffer)); - hr = IStream_Read(stream, buffer, 9, NULL); + hr = IStream_Read(stream, buffer, 10, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "<e></e>\r\n", 9), "%s\n", debugstr_an(buffer, 3)); + ok(!memcmp(buffer, "<e>t</e>\r\n", 10), "%s\n", debugstr_an(buffer, 10)); off.QuadPart = 1; hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(buffer, 0, sizeof(buffer)); hr = IStream_Read(stream, buffer, 9, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "e></e>\r\n", 8), "%s\n", debugstr_an(buffer, 3)); + ok(!memcmp(buffer, "e>t</e>\r\n", 9), "%s\n", debugstr_an(buffer, 9)); IStream_Release(stream); /* Serialization happens on first read. */ - hr = IXMLDOMDocument_loadXML(doc, _bstr_("<f></f>"), NULL); + hr = IXMLDOMDocument_loadXML(doc, _bstr_("<f>t</f>"), NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IXMLDOMDocument_QueryInterface(doc, &IID_IStream, (void **)&stream); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IXMLDOMDocument_loadXML(doc, _bstr_("<g></g>"), NULL); + hr = IXMLDOMDocument_loadXML(doc, _bstr_("<g>t</g>"), NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(buffer, 0, sizeof(buffer)); - hr = IStream_Read(stream, buffer, 9, NULL); + hr = IStream_Read(stream, buffer, 10, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "<g></g>\r\n", 9), "%s\n", debugstr_an(buffer, 9)); - hr = IXMLDOMDocument_loadXML(doc, _bstr_("<h></h>"), NULL); + ok(!memcmp(buffer, "<g>t</g>\r\n", 10), "%s\n", debugstr_an(buffer, 10)); + hr = IXMLDOMDocument_loadXML(doc, _bstr_("<h>t</h>"), NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); off.QuadPart = 0; hr = IStream_Seek(stream, off, STREAM_SEEK_SET, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); memset(buffer, 0, sizeof(buffer)); - hr = IStream_Read(stream, buffer, 9, NULL); + hr = IStream_Read(stream, buffer, 10, NULL); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!memcmp(buffer, "<g></g>\r\n", 9), "%s\n", debugstr_an(buffer, 9)); + ok(!memcmp(buffer, "<g>t</g>\r\n", 10), "%s\n", debugstr_an(buffer, 10)); IStream_Release(stream); IPersistStreamInit_Release(streaminit); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11583
From: Nikolay Sivov <nsivov@codeweavers.com> It's important to maintain QI(IID_IStream) + Release pattern on transform, that triggers document content parsing when outputing to a DOM document. Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/stylesheet.c | 144 +++++++++++++++++++++++++------------ dlls/msxml3/tests/domdoc.c | 4 +- 2 files changed, 101 insertions(+), 47 deletions(-) diff --git a/dlls/msxml3/stylesheet.c b/dlls/msxml3/stylesheet.c index ba445746868..3b02d75c343 100644 --- a/dlls/msxml3/stylesheet.c +++ b/dlls/msxml3/stylesheet.c @@ -63,14 +63,22 @@ typedef struct xsltemplate *stylesheet; IXMLDOMNode *input; - union + struct { - IUnknown *unk; - ISequentialStream *stream; - IPersistStream *persiststream; - IResponse *response; + /* Resolved output object type */ + union + { + IUnknown *obj; + ISequentialStream *stream; + IPersistStream *persiststream; + IResponse *response; + } object; + enum output_type type; + + /* Originally set output object */ + IUnknown *output; } output; - enum output_type output_type; + BSTR outstr; struct xslprocessor_params params; @@ -330,8 +338,10 @@ static ULONG WINAPI xslprocessor_Release( IXSLProcessor *iface ) struct xslprocessor_par *par, *par2; if (This->input) IXMLDOMNode_Release(This->input); - if (This->output.unk) - IUnknown_Release(This->output.unk); + if (This->output.object.obj) + IUnknown_Release(This->output.object.obj); + if (This->output.output) + IUnknown_Release(This->output.output); SysFreeString(This->outstr); LIST_FOR_EACH_ENTRY_SAFE(par, par2, &This->params.list, struct xslprocessor_par, entry) @@ -468,13 +478,50 @@ static HRESULT WINAPI xslprocessor_get_startModeURI( return E_NOTIMPL; } +static HRESULT xslprocessor_resolve_output(IUnknown *output, enum output_type *output_type, + IUnknown **typed_output) +{ + HRESULT hr; + + if (!output) + { + *output_type = PROCESSOR_OUTPUT_NOT_SET; + *typed_output = NULL; + return S_OK; + } + + *output_type = PROCESSOR_OUTPUT_STREAM; + hr = IUnknown_QueryInterface(output, &IID_IStream, (void **)typed_output); + if (FAILED(hr)) + hr = IUnknown_QueryInterface(output, &IID_ISequentialStream, (void **)typed_output); + if (FAILED(hr)) + { + *output_type = PROCESSOR_OUTPUT_RESPONSE; + hr = IUnknown_QueryInterface(output, &IID_IResponse, (void **)typed_output); + } + if (FAILED(hr)) + { + *output_type = PROCESSOR_OUTPUT_PERSISTSTREAM; + hr = IUnknown_QueryInterface(output, &IID_IPersistStream, (void **)typed_output); + } + if (FAILED(hr)) + hr = IUnknown_QueryInterface(output, &IID_IPersistStreamInit, (void **)typed_output); + if (FAILED(hr)) + { + *output_type = PROCESSOR_OUTPUT_NOT_SET; + WARN("failed to get output interface, hr %#lx.\n", hr); + } + + return hr; +} + static HRESULT WINAPI xslprocessor_put_output( IXSLProcessor *iface, VARIANT var) { xslprocessor *This = impl_from_IXSLProcessor( iface ); enum output_type output_type = PROCESSOR_OUTPUT_NOT_SET; - IUnknown *output = NULL; + IUnknown *output = NULL, *typed_output = NULL; HRESULT hr = S_OK; TRACE("(%p)->(%s)\n", This, debugstr_variant(&var)); @@ -488,27 +535,10 @@ static HRESULT WINAPI xslprocessor_put_output( if (!V_UNKNOWN(&var)) break; - output_type = PROCESSOR_OUTPUT_STREAM; - hr = IUnknown_QueryInterface(V_UNKNOWN(&var), &IID_IStream, (void **)&output); - if (FAILED(hr)) - hr = IUnknown_QueryInterface(V_UNKNOWN(&var), &IID_ISequentialStream, (void **)&output); - if (FAILED(hr)) - { - output_type = PROCESSOR_OUTPUT_RESPONSE; - hr = IUnknown_QueryInterface(V_UNKNOWN(&var), &IID_IResponse, (void **)&output); - } - if (FAILED(hr)) - { - output_type = PROCESSOR_OUTPUT_PERSISTSTREAM; - hr = IUnknown_QueryInterface(V_UNKNOWN(&var), &IID_IPersistStream, (void **)&output); - } - if (FAILED(hr)) - hr = IUnknown_QueryInterface(V_UNKNOWN(&var), &IID_IPersistStreamInit, (void **)&output); - if (FAILED(hr)) - { - output_type = PROCESSOR_OUTPUT_NOT_SET; - WARN("failed to get output interface, hr %#lx.\n", hr); - } + output = V_UNKNOWN(&var); + IUnknown_AddRef(output); + + hr = xslprocessor_resolve_output(output, &output_type, &typed_output); break; default: FIXME("output type %d not handled\n", V_VT(&var)); @@ -517,10 +547,20 @@ static HRESULT WINAPI xslprocessor_put_output( if (hr == S_OK) { - if (This->output.unk) - IUnknown_Release(This->output.unk); - This->output.unk = output; - This->output_type = output_type; + if (This->output.object.obj) + IUnknown_Release(This->output.object.obj); + if (This->output.output) + IUnknown_Release(This->output.output); + + This->output.output = output; + + This->output.object.obj = typed_output; + This->output.type = output_type; + } + else + { + if (output) + IUnknown_Release(output); } return hr; @@ -536,11 +576,11 @@ static HRESULT WINAPI xslprocessor_get_output( if (!output) return E_INVALIDARG; - if (This->output.unk) + if (This->output.output) { V_VT(output) = VT_UNKNOWN; - V_UNKNOWN(output) = This->output.unk; - IUnknown_AddRef(This->output.unk); + V_UNKNOWN(output) = This->output.output; + IUnknown_AddRef(This->output.output); } else if (This->outstr) { @@ -566,13 +606,21 @@ static HRESULT WINAPI xslprocessor_transform( if (!ret) return E_INVALIDARG; - if (This->output_type == PROCESSOR_OUTPUT_STREAM) + /* Typed output instance is released after each transformation */ + if (This->output.output && This->output.type == PROCESSOR_OUTPUT_NOT_SET) { - stream = This->output.stream; + hr = xslprocessor_resolve_output(This->output.output, &This->output.type, &This->output.object.obj); + if (FAILED(hr)) + return hr; + } + + if (This->output.type == PROCESSOR_OUTPUT_STREAM) + { + stream = This->output.object.stream; ISequentialStream_AddRef(stream); } - else if (This->output_type == PROCESSOR_OUTPUT_PERSISTSTREAM || - This->output_type == PROCESSOR_OUTPUT_RESPONSE) + else if (This->output.type == PROCESSOR_OUTPUT_PERSISTSTREAM || + This->output.type == PROCESSOR_OUTPUT_RESPONSE) { if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, (IStream **)&stream))) return hr; @@ -586,7 +634,7 @@ static HRESULT WINAPI xslprocessor_transform( { IStream *src = (IStream *)stream; - switch (This->output_type) + switch (This->output.type) { case PROCESSOR_OUTPUT_PERSISTSTREAM: { @@ -595,7 +643,7 @@ static HRESULT WINAPI xslprocessor_transform( /* for IPersistStream* output seekable stream is used */ zero.QuadPart = 0; IStream_Seek(src, zero, STREAM_SEEK_SET, NULL); - hr = IPersistStream_Load(This->output.persiststream, src); + hr = IPersistStream_Load(This->output.object.persiststream, src); break; } case PROCESSOR_OUTPUT_RESPONSE: @@ -627,7 +675,7 @@ static HRESULT WINAPI xslprocessor_transform( GlobalUnlock(hglobal); SafeArrayUnaccessData(array); - IResponse_BinaryWrite(This->output.response, bin); + IResponse_BinaryWrite(This->output.object.response, bin); } VariantClear(&bin); @@ -641,6 +689,12 @@ static HRESULT WINAPI xslprocessor_transform( if (stream) ISequentialStream_Release(stream); + /* Reset resolved typed output object. */ + if (This->output.object.obj) + IUnknown_Release(This->output.object.obj); + This->output.object.obj = NULL; + This->output.type = PROCESSOR_OUTPUT_NOT_SET; + *ret = hr == S_OK ? VARIANT_TRUE : VARIANT_FALSE; return hr; } @@ -815,7 +869,7 @@ HRESULT XSLProcessor_create(xsltemplate *template, IXSLProcessor **ret) object->IXSLProcessor_iface.lpVtbl = &XSLProcessorVtbl; object->ref = 1; - object->output_type = PROCESSOR_OUTPUT_NOT_SET; + object->output.type = PROCESSOR_OUTPUT_NOT_SET; list_init(&object->params.list); object->stylesheet = template; IXSLTemplate_AddRef(&template->IXSLTemplate_iface); diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 653b8a698c6..017aedb4916 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -9717,7 +9717,7 @@ todo_wine { ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); /* it seems processor grabs 2 references */ - todo_wine EXPECT_REF(stream, 3); + EXPECT_REF(stream, 3); V_VT(&v) = VT_EMPTY; hr = IXSLProcessor_get_output(processor, &v); @@ -9725,7 +9725,7 @@ todo_wine { ok(V_VT(&v) == VT_UNKNOWN, "got type %d\n", V_VT(&v)); ok(V_UNKNOWN(&v) == (IUnknown*)stream, "got %p\n", V_UNKNOWN(&v)); - todo_wine EXPECT_REF(stream, 4); + EXPECT_REF(stream, 4); VariantClear(&v); hr = IXSLProcessor_transform(processor, NULL); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11583
From: Nikolay Sivov <nsivov@codeweavers.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=4811 Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/msxml3/domdoc.c | 281 ++++++++++++++++++++++++++++++++++++ dlls/msxml3/msxml_private.h | 1 + dlls/msxml3/node.c | 6 +- dlls/msxml3/tests/domdoc.c | 11 +- dlls/msxml6/tests/domdoc.c | 2 +- 5 files changed, 290 insertions(+), 11 deletions(-) diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c index 4185229a097..bda18b0455e 100644 --- a/dlls/msxml3/domdoc.c +++ b/dlls/msxml3/domdoc.c @@ -74,6 +74,31 @@ typedef enum { EVENTID_LAST } eventid_t; +enum docstream_state +{ + DOCSTREAM_STATE_INITIAL = 0, + DOCSTREAM_STATE_READING, + DOCSTREAM_STATE_WRITING, +}; + +struct docstream +{ + IStream IStream_iface; + LONG refcount; + + enum docstream_state state; + IStream *stream; + struct domdoc *doc; + GUID id; +}; + +static HRESULT create_docstream(struct domdoc *doc, REFIID riid, void **obj); + +static inline struct docstream *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct docstream, IStream_iface); +} + struct domdoc { DispatchEx dispex; @@ -105,6 +130,8 @@ struct domdoc /* events */ IDispatch *events[EVENTID_LAST]; + GUID stream_id; + IXMLDOMSchemaCollection2 *namespaces; }; @@ -361,6 +388,10 @@ static HRESULT WINAPI domdoc_QueryInterface(IXMLDOMDocument3 *iface, REFIID riid { *obj = &doc->IPersistStreamInit_iface; } + else if (IsEqualGUID(&IID_IStream, riid)) + { + return create_docstream(doc, riid, obj); + } else if (IsEqualGUID(&IID_IObjectWithSite, riid)) { *obj = &doc->IObjectWithSite_iface; @@ -2471,3 +2502,253 @@ HRESULT create_domdoc(struct domnode *node, IUnknown **obj) return S_OK; } + +static HRESULT WINAPI docstream_QueryInterface(IStream *iface, REFIID riid, void **obj) +{ + struct docstream *stream = impl_from_IStream(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + *obj = NULL; + + if (IsEqualGUID(riid, &IID_ISequentialStream) || + IsEqualGUID(riid, &IID_IStream)) + { + *obj = iface; + } + else if (IsEqualGUID(riid, &IID_IUnknown)) + { + return IXMLDOMDocument3_QueryInterface(&stream->doc->IXMLDOMDocument3_iface, riid, obj); + } + else + { + TRACE("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI docstream_AddRef(IStream *iface) +{ + struct docstream *stream = impl_from_IStream(iface); + ULONG refcount = InterlockedIncrement(&stream->refcount); + + TRACE("%p, refcount %ld.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI docstream_Release(IStream *iface) +{ + struct docstream *stream = impl_from_IStream(iface); + ULONG refcount = InterlockedDecrement(&stream->refcount); + LARGE_INTEGER offset; + HRESULT hr; + + TRACE("%p, refcount %ld.\n", iface, refcount); + + if (!refcount) + { + if (stream->state == DOCSTREAM_STATE_WRITING && IsEqualGUID(&stream->id, &stream->doc->stream_id)) + { + offset.QuadPart = 0; + IStream_Seek(stream->stream, offset, STREAM_SEEK_SET, NULL); + if (FAILED(hr = domdoc_load_from_stream(stream->doc, (ISequentialStream *)stream->stream))) + WARN("Failed to parse stream, hr %#lx.\n", hr); + } + + IXMLDOMDocument3_Release(&stream->doc->IXMLDOMDocument3_iface); + IStream_Release(stream->stream); + free(stream); + } + + return refcount; +} + +static HRESULT docstream_save(struct docstream *stream) +{ + LARGE_INTEGER offset; + HRESULT hr = S_OK; + + /* Capture document contents once, and rewind. */ + if (stream->state == DOCSTREAM_STATE_INITIAL + && !list_empty(&stream->doc->node->children)) + { + hr = node_save(stream->doc->node, stream->stream); + + offset.QuadPart = 0; + IStream_Seek(stream->stream, offset, STREAM_SEEK_SET, NULL); + + stream->state = DOCSTREAM_STATE_READING; + } + + return hr; +} + +static HRESULT WINAPI docstream_Read(IStream *iface, void *buffer, ULONG size, ULONG *read_size) +{ + struct docstream *stream = impl_from_IStream(iface); + HRESULT hr; + + TRACE("%p, %p, %lu, %p.\n", iface, buffer, size, read_size); + + hr = docstream_save(stream); + if (hr == S_OK && stream->state == DOCSTREAM_STATE_READING) + return IStream_Read(stream->stream, buffer, size, read_size); + + return E_FAIL; +} + +static HRESULT WINAPI docstream_Write(IStream *iface, const void *data, ULONG size, ULONG *written) +{ + struct docstream *stream = impl_from_IStream(iface); + + TRACE("%p, %p, %lu, %p.\n", iface, data, size, written); + + if (stream->state == DOCSTREAM_STATE_INITIAL) + { + node_unlink_children(stream->doc->node); + stream->state = DOCSTREAM_STATE_WRITING; + } + + if (stream->state == DOCSTREAM_STATE_WRITING) + return IStream_Write(stream->stream, data, size, written); + + return S_OK; +} + +static HRESULT WINAPI docstream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD origin, ULARGE_INTEGER *pos) +{ + struct docstream *stream = impl_from_IStream(iface); + + TRACE("%p, %s, %lu, %p.\n", iface, wine_dbgstr_longlong(offset.QuadPart), origin, pos); + + if (stream->state == DOCSTREAM_STATE_WRITING) + { + if (offset.QuadPart && origin == STREAM_SEEK_SET) + return E_NOTIMPL; + return S_OK; + } + + return IStream_Seek(stream->stream, offset, origin, pos); +} + +static HRESULT WINAPI docstream_SetSize(IStream *iface, ULARGE_INTEGER size) +{ + TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(size.QuadPart)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI docstream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, ULARGE_INTEGER *count, + ULARGE_INTEGER *written) +{ + FIXME("%p, %p, %s, %p, %p stub\n", iface, dest, wine_dbgstr_longlong(size.QuadPart), count, written); + + return E_NOTIMPL; +} + +static HRESULT WINAPI docstream_Commit(IStream *iface, DWORD flags) +{ + FIXME("%p, %#lx stub\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI docstream_Revert(IStream *iface) +{ + FIXME("%p stub\n", iface); + + return E_NOTIMPL; +} + +static HRESULT WINAPI docstream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD lock_type) +{ + FIXME("%p, %s, %s, %#lx stub\n", iface, wine_dbgstr_longlong(offset.QuadPart), + wine_dbgstr_longlong(size.QuadPart), lock_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI docstream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD lock_type) +{ + FIXME("%p, %s, %s, %#lx stub\n", iface, wine_dbgstr_longlong(offset.QuadPart), + wine_dbgstr_longlong(size.QuadPart), lock_type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI docstream_Stat(IStream *iface, STATSTG *stat, DWORD flags) +{ + struct docstream *stream = impl_from_IStream(iface); + HRESULT hr; + + TRACE("%p, %p, %#lx.\n", iface, stat, flags); + + if (!stat) + return STG_E_INVALIDPOINTER; + + hr = docstream_save(stream); + if (hr == S_OK && stream->state == DOCSTREAM_STATE_READING) + return IStream_Stat(stream->stream, stat, flags); + + memset(stat, 0, sizeof(*stat)); + stat->type = STGTY_STREAM; + return hr; +} + +static HRESULT WINAPI docstream_Clone(IStream *iface, IStream **ppstm) +{ + TRACE("%p, %p.\n", iface, ppstm); + + return E_NOTIMPL; +} + +static const IStreamVtbl docstream_vtbl = +{ + docstream_QueryInterface, + docstream_AddRef, + docstream_Release, + docstream_Read, + docstream_Write, + docstream_Seek, + docstream_SetSize, + docstream_CopyTo, + docstream_Commit, + docstream_Revert, + docstream_LockRegion, + docstream_UnlockRegion, + docstream_Stat, + docstream_Clone, +}; + +static HRESULT create_docstream(struct domdoc *doc, REFIID riid, void **obj) +{ + struct docstream *object; + HRESULT hr; + + *obj = NULL; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IStream_iface.lpVtbl = &docstream_vtbl; + object->refcount = 1; + if (FAILED(hr = CreateStreamOnHGlobal(NULL, TRUE, &object->stream))) + { + free(object); + return hr; + } + object->doc = doc; + IXMLDOMDocument3_AddRef(&doc->IXMLDOMDocument3_iface); + CoCreateGuid(&object->id); + doc->stream_id = object->id; + + hr = IStream_QueryInterface(&object->IStream_iface, riid, obj); + IStream_Release(&object->IStream_iface); + + return hr; +} diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index f8c9e85900f..b27b9889f9d 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -409,6 +409,7 @@ extern HRESULT node_substring_data(struct domnode *, LONG, LONG, BSTR *); extern HRESULT node_get_data_length(struct domnode *, LONG *); extern HRESULT node_insert_data(struct domnode *, LONG, BSTR); extern HRESULT node_replace_data(struct domnode *, LONG, LONG, BSTR); +extern void node_unlink_children(struct domnode *); extern UINT get_codepage_for_encoding(const WCHAR *encoding); diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 7492a76865f..bc864271185 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -690,7 +690,7 @@ HRESULT node_replace_child(struct domnode *node, IXMLDOMNode *newChild, IXMLDOMN return node_remove_child(node, oldChild, ret); } -static void domnode_unlink_children(struct domnode *node) +void node_unlink_children(struct domnode *node) { struct domnode *child, *next; @@ -749,7 +749,7 @@ HRESULT node_put_data(struct domnode *node, const WCHAR *data) { case NODE_ATTRIBUTE: case NODE_ELEMENT: - domnode_unlink_children(node); + node_unlink_children(node); /* TODO: error handling */ domnode_create(NODE_TEXT, NULL, 0, NULL, 0, node->owner, &child); @@ -4915,7 +4915,7 @@ void node_move_children(struct domnode *dest, struct domnode *src) { struct domnode *child, *next; - domnode_unlink_children(dest); + node_unlink_children(dest); LIST_FOR_EACH_ENTRY_SAFE(child, next, &src->children, struct domnode, entry) { diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c index 017aedb4916..694ab77b926 100644 --- a/dlls/msxml3/tests/domdoc.c +++ b/dlls/msxml3/tests/domdoc.c @@ -17613,12 +17613,12 @@ static void test_interfaces(void) check_interface(doc, &IID_IPersistStream, TRUE); check_interface(doc, &IID_ISequentialStream, FALSE); check_interface(doc, &IID_IPersist, FALSE); + check_interface(doc, &IID_IStream, TRUE); todo_wine { check_interface(doc, &IID_IOleCommandTarget, TRUE); check_interface(doc, &IID_IPersistMoniker, TRUE); check_interface(doc, &IID_IProvideClassInfo, TRUE); - check_interface(doc, &IID_IStream, TRUE); } IXMLDOMDocument_Release(doc); } @@ -18393,13 +18393,7 @@ static void test_document_stream(void) doc = create_document(&IID_IXMLDOMDocument); hr = IXMLDOMDocument_QueryInterface(doc, &IID_IStream, (void **)&stream); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - if (FAILED(hr)) - { - IXMLDOMDocument_Release(doc); - return; - } check_interface(stream, &IID_IUnknown, TRUE); check_interface(stream, &IID_ISequentialStream, TRUE); @@ -18482,6 +18476,7 @@ static void test_document_stream(void) IXMLDOMNode_Release(node); hr = IPersistStreamInit_IsDirty(streaminit); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IXMLDOMDocument_get_xml(doc, &str); @@ -18522,6 +18517,7 @@ static void test_document_stream(void) ok(!memcmp(buffer, "/a>", 3), "%s\n", debugstr_an(buffer, 3)); hr = IPersistStreamInit_IsDirty(streaminit); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IXMLDOMDocument_get_xml(doc, &str); @@ -18536,6 +18532,7 @@ static void test_document_stream(void) hr = IPersistStreamInit_Save(streaminit, stream2, FALSE); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IPersistStreamInit_IsDirty(streaminit); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); off.QuadPart = 0; hr = IStream_Seek(stream, off, STREAM_SEEK_CUR, &pos); diff --git a/dlls/msxml6/tests/domdoc.c b/dlls/msxml6/tests/domdoc.c index 82b58a58843..0aee807ffc2 100644 --- a/dlls/msxml6/tests/domdoc.c +++ b/dlls/msxml6/tests/domdoc.c @@ -1027,12 +1027,12 @@ static void test_interfaces(void) check_interface(doc, &IID_IPersistStream, TRUE); check_interface(doc, &IID_ISequentialStream, FALSE); check_interface(doc, &IID_IPersist, FALSE); + check_interface(doc, &IID_IStream, TRUE); todo_wine { check_interface(doc, &IID_IOleCommandTarget, TRUE); check_interface(doc, &IID_IPersistMoniker, TRUE); check_interface(doc, &IID_IProvideClassInfo, TRUE); - check_interface(doc, &IID_IStream, TRUE); } IXMLDOMDocument_Release(doc); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11583
v2: fixed msxml6 test failure -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11583#note_147922
participants (2)
-
Nikolay Sivov -
Nikolay Sivov (@nsivov)