On 4/10/21 4:27 AM, Jefferson Carpenter wrote:
Moved some code from mxwriter ISAXContentHandler implementation into free functions that can be swapped out for DOMDocument functions.
For other mxwriter callback functions, immediately return E_NOTIMPL if the destination is a DOMDocument.
Added test_mxwriter_domdoc_start_end_document to help check the implementation of DOMDocument locking.
Added "BOOL locked" to XML documents and a private interface that allows this variable to be set and checked. (This variable is not used thread safely. `LONG refs` is thread safe, but `struct list orphans` is not.)
thanks, Jefferson @@ -125,6 +125,7 @@ struct domdoc IPersistStreamInit IPersistStreamInit_iface; IObjectWithSite IObjectWithSite_iface; IObjectSafety IObjectSafety_iface; + IWineXMLDOMDocumentLock IWineXMLDOMDocumentLock_iface; IConnectionPointContainer IConnectionPointContainer_iface; LONG ref; VARIANT_BOOL async; Maybe that could work, yes. @@ -209,6 +210,7 @@ typedef struct _xmldoc_priv { LONG refs; struct list orphans; domdoc_properties* properties; + BOOL locked; } xmldoc_priv; Why does it have to be in _priv?
diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 67f322eb0e5..9cbb2305830 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -457,6 +457,8 @@ HRESULT node_insert_before(xmlnode *This, IXMLDOMNode *new_child, const VARIANT if(!new_child) return E_INVALIDARG;
+ if (xmldoc_get_locked(This->node->doc)) return E_FAIL; + node_obj = get_node_obj(new_child); if(!node_obj) return E_FAIL;
@@ -569,6 +571,8 @@ HRESULT node_replace_child(xmlnode *This, IXMLDOMNode *newChild, IXMLDOMNode *ol if(!newChild || !oldChild) return E_INVALIDARG;
+ if (xmldoc_get_locked(This->node->doc)) return E_FAIL; + if(ret) *ret = NULL;
@@ -627,6 +631,8 @@ HRESULT node_remove_child(xmlnode *This, IXMLDOMNode* child, IXMLDOMNode** oldCh
if(!child) return E_INVALIDARG;
+ if (xmldoc_get_locked(This->node->doc)) return E_FAIL; + if(oldChild) *oldChild = NULL;
Maybe, but there's many more methods that modify the tree.
+static HRESULT writer_end_tag_domdoc(mxwriter *writer, const WCHAR *qname, int len) { + HRESULT hr; + IXMLDOMNode *parent_node; + + IWineXMLDOMDocumentLock_put_locked(writer->doc_lock, 0); + + hr = domdoc_maybe_write_chars(writer); + if (FAILED(hr)) return hr; + + IWineXMLDOMDocumentLock_put_locked(writer->doc_lock, 1); + + hr = IXMLDOMNode_get_parentNode(writer->current_node, &parent_node); + if (FAILED(hr)) return hr; + + IXMLDOMNode_Release(writer->current_node); + writer->current_node = parent_node; + return hr; +} I don't understand this pattern. Idea I had was to lock when output is set to a document, and unlock when everything is written. Is that wrong?
+static HRESULT writer_start_tag_string_len_stream(mxwriter *writer, const WCHAR *qname, int len) +{ + static const WCHAR ltW[] = {'<'}; + + close_element_starttag(writer); + set_element_name(writer, qname ? qname : emptyW, qname ? len : 0); + + write_node_indent(writer); + + write_output_buffer(writer, ltW, 1); + write_output_buffer(writer, qname ? qname : emptyW, qname ? len : 0); + writer_inc_indent(writer); + + return S_OK; +} + +static HRESULT writer_start_tag_bstr_stream(mxwriter *writer, BSTR name) +{ + return writer_start_tag_string_len_stream(writer, name, SysStringLen(name)); +} + Do you need both?