From cb9d787be150af96c75a652341622d086f7da1ff Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 25 Feb 2013 00:30:22 +0400 Subject: [PATCH] msxml3: Better handle cross-tree node moves. --- dlls/msxml3/domdoc.c | 24 +++++++++--- dlls/msxml3/msxml_private.h | 2 + dlls/msxml3/node.c | 75 ++++++++++++++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c index f1915a3d43b..fb96ba0c02c 100644 --- a/dlls/msxml3/domdoc.c +++ b/dlls/msxml3/domdoc.c @@ -553,19 +553,28 @@ void xmldoc_init(xmlDocPtr doc, MSXML_VERSION version) priv_from_xmlDocPtr(doc)->properties = create_properties(version); } -LONG xmldoc_add_ref(xmlDocPtr doc) +LONG xmldoc_add_refs(xmlDocPtr doc, LONG refs) { - LONG ref = InterlockedIncrement(&priv_from_xmlDocPtr(doc)->refs); + LONG ref = InterlockedExchangeAdd(&priv_from_xmlDocPtr(doc)->refs, refs) + refs; TRACE("(%p)->(%d)\n", doc, ref); return ref; } -LONG xmldoc_release(xmlDocPtr doc) +LONG xmldoc_add_ref(xmlDocPtr doc) +{ + return xmldoc_add_refs(doc, 1); +} + +LONG xmldoc_release_refs(xmlDocPtr doc, LONG refs) { xmldoc_priv *priv = priv_from_xmlDocPtr(doc); - LONG ref = InterlockedDecrement(&priv->refs); + LONG ref = InterlockedExchangeAdd(&priv->refs, -refs) - refs; TRACE("(%p)->(%d)\n", doc, ref); - if(ref == 0) + + if (ref < 0) + WARN("negative refcount, expect troubles\n"); + + if (ref == 0) { orphan_entry *orphan, *orphan2; TRACE("freeing docptr %p\n", doc); @@ -584,6 +593,11 @@ LONG xmldoc_release(xmlDocPtr doc) return ref; } +LONG xmldoc_release(xmlDocPtr doc) +{ + return xmldoc_release_refs(doc, 1); +} + HRESULT xmldoc_add_orphan(xmlDocPtr doc, xmlNodePtr node) { xmldoc_priv *priv = priv_from_xmlDocPtr(doc); diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index 66baea850e8..3109a427b04 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -292,6 +292,8 @@ extern xmlChar *xmlChar_from_wchar( LPCWSTR str ) DECLSPEC_HIDDEN; extern void xmldoc_init( xmlDocPtr doc, MSXML_VERSION version ) DECLSPEC_HIDDEN; extern LONG xmldoc_add_ref( xmlDocPtr doc ) DECLSPEC_HIDDEN; extern LONG xmldoc_release( xmlDocPtr doc ) DECLSPEC_HIDDEN; +extern LONG xmldoc_add_refs( xmlDocPtr doc, LONG refs ) DECLSPEC_HIDDEN; +extern LONG xmldoc_release_refs ( xmlDocPtr doc, LONG refs ) DECLSPEC_HIDDEN; extern HRESULT xmldoc_add_orphan( xmlDocPtr doc, xmlNodePtr node ) DECLSPEC_HIDDEN; extern HRESULT xmldoc_remove_orphan( xmlDocPtr doc, xmlNodePtr node ) DECLSPEC_HIDDEN; extern void xmldoc_link_xmldecl(xmlDocPtr doc, xmlNodePtr node) DECLSPEC_HIDDEN; diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 867b8907f07..8571955538e 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -374,11 +374,45 @@ HRESULT node_get_next_sibling(xmlnode *This, IXMLDOMNode **ret) return get_node(This, "next", This->node->next, ret); } +static int node_get_inst_cnt(xmlNodePtr node) +{ + int ret = *(LONG *)&node->_private; + xmlNodePtr child; + + /* add attribute counts */ + if (node->type == XML_ELEMENT_NODE) + { + xmlAttrPtr prop = node->properties; + + while (prop) + { + ret += node_get_inst_cnt((xmlNodePtr)prop); + prop = prop->next; + } + } + + /* add children counts */ + child = node->children; + while (child) + { + ret += node_get_inst_cnt(child); + child = child->next; + } + + return ret; +} + +static int xmlnode_get_inst_cnt(xmlnode *node) +{ + return node_get_inst_cnt(node->node); +} + HRESULT node_insert_before(xmlnode *This, IXMLDOMNode *new_child, const VARIANT *ref_child, IXMLDOMNode **ret) { IXMLDOMNode *before = NULL; xmlnode *node_obj; + int refcount = 0; xmlDocPtr doc; HRESULT hr; @@ -414,6 +448,8 @@ HRESULT node_insert_before(xmlnode *This, IXMLDOMNode *new_child, const VARIANT if(xmldoc_remove_orphan(node_obj->node->doc, node_obj->node) != S_OK) WARN("%p is not an orphan of %p\n", node_obj->node, node_obj->node->doc); + refcount = xmlnode_get_inst_cnt(node_obj); + if(before) { xmlnode *before_node_obj = get_node_obj(before); @@ -426,10 +462,16 @@ HRESULT node_insert_before(xmlnode *This, IXMLDOMNode *new_child, const VARIANT hr = IXMLDOMNode_removeChild(node_obj->parent, node_obj->iface, NULL); if (hr == S_OK) xmldoc_remove_orphan(node_obj->node->doc, node_obj->node); } + doc = node_obj->node->doc; - xmldoc_add_ref(before_node_obj->node->doc); + + /* refs count including subtree */ + if (doc != before_node_obj->node->doc) + refcount = xmlnode_get_inst_cnt(node_obj); + + if (refcount) xmldoc_add_refs(before_node_obj->node->doc, refcount); xmlAddPrevSibling(before_node_obj->node, node_obj->node); - xmldoc_release(doc); + if (refcount) xmldoc_release_refs(doc, refcount); node_obj->parent = This->parent; } else @@ -441,11 +483,15 @@ HRESULT node_insert_before(xmlnode *This, IXMLDOMNode *new_child, const VARIANT if (hr == S_OK) xmldoc_remove_orphan(node_obj->node->doc, node_obj->node); } doc = node_obj->node->doc; - xmldoc_add_ref(This->node->doc); + + if (doc != This->node->doc) + refcount = xmlnode_get_inst_cnt(node_obj); + + if (refcount) xmldoc_add_refs(This->node->doc, refcount); /* xmlAddChild doesn't unlink node from previous parent */ xmlUnlinkNode(node_obj->node); xmlAddChild(This->node, node_obj->node); - xmldoc_release(doc); + if (refcount) xmldoc_release_refs(doc, refcount); node_obj->parent = This->iface; } @@ -1017,17 +1063,36 @@ HRESULT node_get_base_name(xmlnode *This, BSTR *name) return S_OK; } +/* _private field holds a number of COM instances spawned from this libxml2 node */ +static void xmlnode_add_ref(xmlNodePtr node) +{ + if (node->type == XML_DOCUMENT_NODE) return; + InterlockedIncrement((LONG*)&node->_private); +} + +static void xmlnode_release(xmlNodePtr node) +{ + if (node->type == XML_DOCUMENT_NODE) return; + InterlockedDecrement((LONG*)&node->_private); +} + void destroy_xmlnode(xmlnode *This) { if(This->node) + { + xmlnode_release(This->node); xmldoc_release(This->node->doc); + } release_dispex(&This->dispex); } void init_xmlnode(xmlnode *This, xmlNodePtr node, IXMLDOMNode *node_iface, dispex_static_data_t *dispex_data) { if(node) - xmldoc_add_ref( node->doc ); + { + xmlnode_add_ref(node); + xmldoc_add_ref(node->doc); + } This->node = node; This->iface = node_iface;