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?