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