msxml3: Added IStream support to IMXWriter.

This commit is contained in:
Thomas Mullaly 2011-08-12 19:11:08 -04:00 committed by Alexandre Julliard
parent e4b7e96267
commit f1f57f2368
3 changed files with 206 additions and 39 deletions

View File

@ -194,6 +194,22 @@ static inline LPWSTR heap_strdupW(LPCWSTR str)
return ret;
}
static inline LPSTR heap_strdupWtoA(LPCWSTR str)
{
LPSTR ret = NULL;
if(str) {
DWORD len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
ret = heap_alloc(len+1);
if(!ret)
return NULL;
WideCharToMultiByte(CP_ACP, 0, str, -1, ret, len+1, NULL, NULL);
}
return ret;
}
#ifdef HAVE_LIBXML2
extern void schemasInit(void) DECLSPEC_HIDDEN;

View File

@ -2,6 +2,7 @@
* MXWriter implementation
*
* Copyright 2011 Nikolay Sivov for CodeWeaversы
* Copyright 2011 Thomas Mullaly
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -40,6 +41,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(msxml);
#ifdef HAVE_LIBXML2
static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
static const WCHAR utf8W[] = {'U','T','F','-','8',0};
static const char crlfA[] = "\r\n";
typedef enum
@ -60,14 +64,99 @@ typedef struct _mxwriter
LONG ref;
VARIANT_BOOL props[MXWriter_LastProp];
BSTR encoding;
BOOL prop_changed;
xmlCharEncoding encoding;
BSTR version;
IStream *dest;
ULONG dest_written;
xmlOutputBufferPtr buffer;
} mxwriter;
static HRESULT bstr_from_xmlCharEncoding(xmlCharEncoding enc, BSTR *encoding)
{
const char *encodingA;
if (enc != XML_CHAR_ENCODING_UTF16LE && enc != XML_CHAR_ENCODING_UTF8) {
FIXME("Unsupported xmlCharEncoding: %d\n", enc);
*encoding = NULL;
return E_NOTIMPL;
}
encodingA = xmlGetCharEncodingName(enc);
if (encodingA) {
DWORD len = MultiByteToWideChar(CP_ACP, 0, encodingA, -1, NULL, 0);
*encoding = SysAllocStringLen(NULL, len-1);
if(*encoding)
MultiByteToWideChar( CP_ACP, 0, encodingA, -1, *encoding, len);
} else
*encoding = SysAllocStringLen(NULL, 0);
return *encoding ? S_OK : E_OUTOFMEMORY;
}
/* Attempts to the write data from the mxwriter's buffer to
* the destination stream (if there is one).
*/
static HRESULT write_data_to_stream(mxwriter *This)
{
HRESULT hres;
ULONG written = 0;
xmlBufferPtr buffer = NULL;
if (!This->dest)
return S_OK;
/* The xmlOutputBuffer doesn't copy its contents from its 'buffer' to the
* 'conv' buffer when UTF8 encoding is used.
*/
if (This->encoding == XML_CHAR_ENCODING_UTF8)
buffer = This->buffer->buffer;
else
buffer = This->buffer->conv;
if (This->dest_written > buffer->use) {
ERR("Failed sanity check! Not sure what to do... (%d > %d)\n", This->dest_written, buffer->use);
return E_FAIL;
} else if (This->dest_written == buffer->use && This->encoding != XML_CHAR_ENCODING_UTF8)
/* Windows seems to make an empty write call when the encoding is UTF-8 and
* all the data has been written to the stream. It doesn't seem make this call
* for any other encodings.
*/
return S_OK;
/* Write the current content from the output buffer into 'dest'.
* TODO: Check what Windows does if the IStream doesn't write all of
* the data we give it at once.
*/
hres = IStream_Write(This->dest, buffer->content+This->dest_written,
buffer->use-This->dest_written, &written);
if (FAILED(hres)) {
WARN("Failed to write data to IStream (%08x)\n", hres);
return hres;
}
This->dest_written += written;
return hres;
}
static inline HRESULT flush_output_buffer(mxwriter *This)
{
xmlOutputBufferFlush(This->buffer);
return write_data_to_stream(This);
}
/* Resets the mxwriter's output buffer by closing it, then creating a new
* output buffer using the given encoding.
*/
static inline void reset_output_buffer(mxwriter *This)
{
xmlOutputBufferClose(This->buffer);
This->buffer = xmlAllocOutputBuffer(xmlGetCharEncodingHandler(This->encoding));
This->dest_written = 0;
}
static inline mxwriter *impl_from_IMXWriter(IMXWriter *iface)
{
return CONTAINING_RECORD(iface, mxwriter, IMXWriter_iface);
@ -124,8 +213,10 @@ static ULONG WINAPI mxwriter_Release(IMXWriter *iface)
if(!ref)
{
/* Windows flushes the buffer when the interface is destroyed. */
flush_output_buffer(This);
if (This->dest) IStream_Release(This->dest);
SysFreeString(This->encoding);
SysFreeString(This->version);
xmlOutputBufferClose(This->buffer);
@ -210,25 +301,38 @@ static HRESULT WINAPI mxwriter_Invoke(
static HRESULT WINAPI mxwriter_put_output(IMXWriter *iface, VARIANT dest)
{
mxwriter *This = impl_from_IMXWriter( iface );
HRESULT hr;
TRACE("(%p)->(%s)\n", This, debugstr_variant(&dest));
hr = flush_output_buffer(This);
if (FAILED(hr))
return hr;
switch (V_VT(&dest))
{
case VT_EMPTY:
{
if (This->dest) IStream_Release(This->dest);
This->dest = NULL;
/* We need to reset the output buffer to UTF-16, since the only way
* the content of the mxwriter can be accessed now is through a BSTR.
*/
This->encoding = xmlParseCharEncoding("UTF-16");
reset_output_buffer(This);
break;
}
case VT_UNKNOWN:
{
IStream *stream;
HRESULT hr;
hr = IUnknown_QueryInterface(V_UNKNOWN(&dest), &IID_IStream, (void**)&stream);
if (hr == S_OK)
{
/* Recreate the output buffer to make sure it's using the correct encoding. */
reset_output_buffer(This);
if (This->dest) IStream_Release(This->dest);
This->dest = stream;
break;
@ -253,8 +357,22 @@ static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
if (!This->dest)
{
HRESULT hr = flush_output_buffer(This);
if (FAILED(hr))
return hr;
/* TODO: Windows always seems to re-encode the XML to UTF-16 (this includes
* updating the XML decl so it says "UTF-16" instead of "UTF-8"). We don't
* support this yet...
*/
if (This->encoding == XML_CHAR_ENCODING_UTF8) {
FIXME("XML re-encoding not supported yet\n");
return E_NOTIMPL;
}
V_VT(dest) = VT_BSTR;
V_BSTR(dest) = bstr_from_xmlChar(This->buffer->buffer->content);
V_BSTR(dest) = SysAllocStringLen((const WCHAR*)This->buffer->conv->content,
This->buffer->conv->use/sizeof(WCHAR));
return S_OK;
}
@ -266,8 +384,6 @@ static HRESULT WINAPI mxwriter_get_output(IMXWriter *iface, VARIANT *dest)
static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
{
static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
static const WCHAR utf8W[] = {'U','T','F','-','8',0};
mxwriter *This = impl_from_IMXWriter( iface );
TRACE("(%p)->(%s)\n", This, debugstr_w(encoding));
@ -275,8 +391,21 @@ static HRESULT WINAPI mxwriter_put_encoding(IMXWriter *iface, BSTR encoding)
/* FIXME: filter all supported encodings */
if (!strcmpW(encoding, utf16W) || !strcmpW(encoding, utf8W))
{
SysFreeString(This->encoding);
This->encoding = SysAllocString(encoding);
HRESULT hr;
LPSTR enc;
hr = flush_output_buffer(This);
if (FAILED(hr))
return hr;
enc = heap_strdupWtoA(encoding);
if (!enc)
return E_OUTOFMEMORY;
This->encoding = xmlParseCharEncoding(enc);
heap_free(enc);
reset_output_buffer(This);
return S_OK;
}
else
@ -294,7 +423,7 @@ static HRESULT WINAPI mxwriter_get_encoding(IMXWriter *iface, BSTR *encoding)
if (!encoding) return E_POINTER;
return return_bstr(This->encoding, encoding);
return bstr_from_xmlCharEncoding(This->encoding, encoding);
}
static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL value)
@ -303,6 +432,7 @@ static HRESULT WINAPI mxwriter_put_byteOrderMark(IMXWriter *iface, VARIANT_BOOL
TRACE("(%p)->(%d)\n", This, value);
This->props[MXWriter_BOM] = value;
This->prop_changed = TRUE;
return S_OK;
}
@ -326,6 +456,7 @@ static HRESULT WINAPI mxwriter_put_indent(IMXWriter *iface, VARIANT_BOOL value)
TRACE("(%p)->(%d)\n", This, value);
This->props[MXWriter_Indent] = value;
This->prop_changed = TRUE;
return S_OK;
}
@ -349,6 +480,7 @@ static HRESULT WINAPI mxwriter_put_standalone(IMXWriter *iface, VARIANT_BOOL val
TRACE("(%p)->(%d)\n", This, value);
This->props[MXWriter_Standalone] = value;
This->prop_changed = TRUE;
return S_OK;
}
@ -372,6 +504,7 @@ static HRESULT WINAPI mxwriter_put_omitXMLDeclaration(IMXWriter *iface, VARIANT_
TRACE("(%p)->(%d)\n", This, value);
This->props[MXWriter_OmitXmlDecl] = value;
This->prop_changed = TRUE;
return S_OK;
}
@ -413,6 +546,7 @@ static HRESULT WINAPI mxwriter_put_disableOutputEscaping(IMXWriter *iface, VARIA
TRACE("(%p)->(%d)\n", This, value);
This->props[MXWriter_DisableEscaping] = value;
This->prop_changed = TRUE;
return S_OK;
}
@ -433,8 +567,8 @@ static HRESULT WINAPI mxwriter_get_disableOutputEscaping(IMXWriter *iface, VARIA
static HRESULT WINAPI mxwriter_flush(IMXWriter *iface)
{
mxwriter *This = impl_from_IMXWriter( iface );
FIXME("(%p)\n", This);
return E_NOTIMPL;
TRACE("(%p)\n", This);
return flush_output_buffer(This);
}
static const struct IMXWriterVtbl mxwriter_vtbl =
@ -503,6 +637,16 @@ static HRESULT WINAPI mxwriter_saxcontent_startDocument(ISAXContentHandler *ifac
TRACE("(%p)\n", This);
/* If properties have been changed since the last "endDocument" call
* we need to reset the output buffer. If we don't the output buffer
* could end up with multiple XML documents in it, plus this seems to
* be how Windows works.
*/
if (This->prop_changed) {
reset_output_buffer(This);
This->prop_changed = FALSE;
}
if (This->props[MXWriter_OmitXmlDecl] == VARIANT_TRUE) return S_OK;
/* version */
@ -514,9 +658,7 @@ static HRESULT WINAPI mxwriter_saxcontent_startDocument(ISAXContentHandler *ifac
/* encoding */
xmlOutputBufferWriteString(This->buffer, " encoding=\"");
s = xmlchar_from_wchar(This->encoding);
xmlOutputBufferWriteString(This->buffer, (char*)s);
heap_free(s);
xmlOutputBufferWriteString(This->buffer, xmlGetCharEncodingName(This->encoding));
xmlOutputBufferWriteString(This->buffer, "\"");
/* standalone */
@ -528,14 +670,25 @@ static HRESULT WINAPI mxwriter_saxcontent_startDocument(ISAXContentHandler *ifac
xmlOutputBufferWriteString(This->buffer, crlfA);
if (This->dest && This->encoding == XML_CHAR_ENCODING_UTF16LE) {
static const CHAR utf16BOM[] = {0xff,0xfe};
if (This->props[MXWriter_BOM] == VARIANT_TRUE)
/* Windows passes a NULL pointer as the pcbWritten parameter and
* ignores any error codes returned from this Write call.
*/
IStream_Write(This->dest, utf16BOM, sizeof(utf16BOM), NULL);
}
return S_OK;
}
static HRESULT WINAPI mxwriter_saxcontent_endDocument(ISAXContentHandler *iface)
{
mxwriter *This = impl_from_ISAXContentHandler( iface );
FIXME("(%p)\n", This);
return E_NOTIMPL;
TRACE("(%p)\n", This);
This->prop_changed = FALSE;
return flush_output_buffer(This);
}
static HRESULT WINAPI mxwriter_saxcontent_startPrefixMapping(
@ -723,7 +876,6 @@ static const struct ISAXContentHandlerVtbl mxwriter_saxcontent_vtbl =
HRESULT MXWriter_create(IUnknown *pUnkOuter, void **ppObj)
{
static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
static const WCHAR version10W[] = {'1','.','0',0};
mxwriter *This;
@ -744,13 +896,14 @@ HRESULT MXWriter_create(IUnknown *pUnkOuter, void **ppObj)
This->props[MXWriter_Indent] = VARIANT_FALSE;
This->props[MXWriter_OmitXmlDecl] = VARIANT_FALSE;
This->props[MXWriter_Standalone] = VARIANT_FALSE;
This->encoding = SysAllocString(utf16W);
This->prop_changed = FALSE;
This->encoding = xmlParseCharEncoding("UTF-16");
This->version = SysAllocString(version10W);
This->dest = NULL;
This->dest_written = 0;
/* set up a buffer, default encoding is UTF-16 */
This->buffer = xmlAllocOutputBuffer(xmlFindCharEncodingHandler("UTF-16"));
This->buffer = xmlAllocOutputBuffer(xmlGetCharEncodingHandler(This->encoding));
*ppObj = &This->IMXWriter_iface;

View File

@ -1238,15 +1238,13 @@ static void test_mxwriter_flush(void)
/* flush() doesn't detach a stream */
hr = IMXWriter_flush(writer);
todo_wine {
ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
EXPECT_REF(stream, 3);
}
todo_wine EXPECT_REF(stream, 3);
pos.QuadPart = 0;
hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
ok(pos2.QuadPart == 0, "expected stream beginning\n");
ok(pos2.QuadPart == 0, "expected stream beginning");
hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
ok(hr == S_OK, "got %08x\n", hr);
@ -1257,20 +1255,20 @@ todo_wine {
pos.QuadPart = 0;
hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
todo_wine ok(pos2.QuadPart != 0, "expected stream beginning\n");
ok(pos2.QuadPart != 0, "expected stream beginning\n");
/* already started */
hr = ISAXContentHandler_startDocument(content);
ok(hr == S_OK, "got %08x\n", hr);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "got %08x\n", hr);
ok(hr == S_OK, "got %08x\n", hr);
/* flushed on endDocument() */
pos.QuadPart = 0;
hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
todo_wine ok(pos2.QuadPart != 0, "expected stream position moved\n");
ok(pos2.QuadPart != 0, "expected stream position moved\n");
ISAXContentHandler_Release(content);
IStream_Release(stream);
@ -1295,7 +1293,7 @@ static void test_mxwriter_startenddocument(void)
ok(hr == S_OK, "got %08x\n", hr);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "got %08x\n", hr);
ok(hr == S_OK, "got %08x\n", hr);
V_VT(&dest) = VT_EMPTY;
hr = IMXWriter_get_output(writer, &dest);
@ -1336,7 +1334,7 @@ static void test_mxwriter_startenddocument(void)
ok(hr == S_OK, "got %08x\n", hr);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "got %08x\n", hr);
ok(hr == S_OK, "got %08x\n", hr);
V_VT(&dest) = VT_EMPTY;
hr = IMXWriter_get_output(writer, &dest);
@ -1458,7 +1456,7 @@ static void test_mxwriter_startendelement(void)
ok(hr == S_OK, "got %08x\n", hr);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "got %08x\n", hr);
ok(hr == S_OK, "got %08x\n", hr);
ISAXContentHandler_Release(content);
IMXWriter_Release(writer);
@ -1504,7 +1502,7 @@ static void test_mxwriter_characters(void)
VariantClear(&dest);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "got %08x\n", hr);
ok(hr == S_OK, "got %08x\n", hr);
ISAXContentHandler_Release(content);
IMXWriter_Release(writer);
@ -1596,12 +1594,12 @@ static void test_mxwriter_stream(void)
ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
ISAXContentHandler_Release(content);
IMXWriter_Release(writer);
todo_wine ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
current_write_test-test->expected_writes, current_stream_test_index);
}
@ -1636,19 +1634,19 @@ static void test_mxwriter_stream(void)
pos.QuadPart = 0;
hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
ok(hr == S_OK, "Seek failed: %08x\n", hr);
todo_wine ok(pos2.QuadPart != 0, "expected stream position moved\n");
ok(pos2.QuadPart != 0, "expected stream position moved\n");
hr = ISAXContentHandler_startDocument(content);
ok(hr == S_OK, "startDocument failed: %08x\n", hr);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "endDocument failed: %08x\n", hr);
ok(hr == S_OK, "endDocument failed: %08x\n", hr);
V_VT(&dest) = VT_EMPTY;
hr = IMXWriter_get_output(writer, &dest);
ok(hr == S_OK, "get_output failed: %08x\n", hr);
ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
todo_wine ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
"Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
VariantClear(&dest);
@ -1679,15 +1677,15 @@ static void test_mxwriter_encoding(void)
ok(hr == S_OK, "startDocument failed: %08x\n", hr);
hr = ISAXContentHandler_endDocument(content);
todo_wine ok(hr == S_OK, "endDocument failed: %08x\n", hr);
ok(hr == S_OK, "endDocument failed: %08x\n", hr);
/* The content is always re-encoded to UTF-16 when the output is
* retrieved as a BSTR.
*/
V_VT(&dest) = VT_EMPTY;
hr = IMXWriter_get_output(writer, &dest);
ok(hr == S_OK, "get_output failed: %08x\n", hr);
ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
todo_wine ok(hr == S_OK, "get_output failed: %08x\n", hr);
todo_wine ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
todo_wine ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
"got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
VariantClear(&dest);