Nikolay Sivov : msxml3: Better handle cross-tree node moves.

Alexandre Julliard julliard at winehq.org
Wed Feb 27 14:41:58 CST 2013


Module: wine
Branch: master
Commit: cb9d787be150af96c75a652341622d086f7da1ff
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=cb9d787be150af96c75a652341622d086f7da1ff

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Mon Feb 25 00:30:22 2013 +0400

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 f1915a3..fb96ba0 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 66baea8..3109a42 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 867b890..8571955 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;




More information about the wine-cvs mailing list