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