msxml3: Better handle cross-tree node moves.
This commit is contained in:
parent
91cec0e82a
commit
cb9d787be1
|
@ -553,19 +553,28 @@ void xmldoc_init(xmlDocPtr doc, MSXML_VERSION version)
|
||||||
priv_from_xmlDocPtr(doc)->properties = create_properties(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);
|
TRACE("(%p)->(%d)\n", doc, ref);
|
||||||
return 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);
|
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);
|
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;
|
orphan_entry *orphan, *orphan2;
|
||||||
TRACE("freeing docptr %p\n", doc);
|
TRACE("freeing docptr %p\n", doc);
|
||||||
|
@ -584,6 +593,11 @@ LONG xmldoc_release(xmlDocPtr doc)
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LONG xmldoc_release(xmlDocPtr doc)
|
||||||
|
{
|
||||||
|
return xmldoc_release_refs(doc, 1);
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT xmldoc_add_orphan(xmlDocPtr doc, xmlNodePtr node)
|
HRESULT xmldoc_add_orphan(xmlDocPtr doc, xmlNodePtr node)
|
||||||
{
|
{
|
||||||
xmldoc_priv *priv = priv_from_xmlDocPtr(doc);
|
xmldoc_priv *priv = priv_from_xmlDocPtr(doc);
|
||||||
|
|
|
@ -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 void xmldoc_init( xmlDocPtr doc, MSXML_VERSION version ) DECLSPEC_HIDDEN;
|
||||||
extern LONG xmldoc_add_ref( xmlDocPtr doc ) DECLSPEC_HIDDEN;
|
extern LONG xmldoc_add_ref( xmlDocPtr doc ) DECLSPEC_HIDDEN;
|
||||||
extern LONG xmldoc_release( 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_add_orphan( xmlDocPtr doc, xmlNodePtr node ) DECLSPEC_HIDDEN;
|
||||||
extern HRESULT xmldoc_remove_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;
|
extern void xmldoc_link_xmldecl(xmlDocPtr doc, xmlNodePtr node) DECLSPEC_HIDDEN;
|
||||||
|
|
|
@ -374,11 +374,45 @@ HRESULT node_get_next_sibling(xmlnode *This, IXMLDOMNode **ret)
|
||||||
return get_node(This, "next", This->node->next, 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,
|
HRESULT node_insert_before(xmlnode *This, IXMLDOMNode *new_child, const VARIANT *ref_child,
|
||||||
IXMLDOMNode **ret)
|
IXMLDOMNode **ret)
|
||||||
{
|
{
|
||||||
IXMLDOMNode *before = NULL;
|
IXMLDOMNode *before = NULL;
|
||||||
xmlnode *node_obj;
|
xmlnode *node_obj;
|
||||||
|
int refcount = 0;
|
||||||
xmlDocPtr doc;
|
xmlDocPtr doc;
|
||||||
HRESULT hr;
|
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)
|
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);
|
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)
|
if(before)
|
||||||
{
|
{
|
||||||
xmlnode *before_node_obj = get_node_obj(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);
|
hr = IXMLDOMNode_removeChild(node_obj->parent, node_obj->iface, NULL);
|
||||||
if (hr == S_OK) xmldoc_remove_orphan(node_obj->node->doc, node_obj->node);
|
if (hr == S_OK) xmldoc_remove_orphan(node_obj->node->doc, node_obj->node);
|
||||||
}
|
}
|
||||||
|
|
||||||
doc = node_obj->node->doc;
|
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);
|
xmlAddPrevSibling(before_node_obj->node, node_obj->node);
|
||||||
xmldoc_release(doc);
|
if (refcount) xmldoc_release_refs(doc, refcount);
|
||||||
node_obj->parent = This->parent;
|
node_obj->parent = This->parent;
|
||||||
}
|
}
|
||||||
else
|
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);
|
if (hr == S_OK) xmldoc_remove_orphan(node_obj->node->doc, node_obj->node);
|
||||||
}
|
}
|
||||||
doc = node_obj->node->doc;
|
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 */
|
/* xmlAddChild doesn't unlink node from previous parent */
|
||||||
xmlUnlinkNode(node_obj->node);
|
xmlUnlinkNode(node_obj->node);
|
||||||
xmlAddChild(This->node, node_obj->node);
|
xmlAddChild(This->node, node_obj->node);
|
||||||
xmldoc_release(doc);
|
if (refcount) xmldoc_release_refs(doc, refcount);
|
||||||
node_obj->parent = This->iface;
|
node_obj->parent = This->iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1017,17 +1063,36 @@ HRESULT node_get_base_name(xmlnode *This, BSTR *name)
|
||||||
return S_OK;
|
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)
|
void destroy_xmlnode(xmlnode *This)
|
||||||
{
|
{
|
||||||
if(This->node)
|
if(This->node)
|
||||||
|
{
|
||||||
|
xmlnode_release(This->node);
|
||||||
xmldoc_release(This->node->doc);
|
xmldoc_release(This->node->doc);
|
||||||
|
}
|
||||||
release_dispex(&This->dispex);
|
release_dispex(&This->dispex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_xmlnode(xmlnode *This, xmlNodePtr node, IXMLDOMNode *node_iface, dispex_static_data_t *dispex_data)
|
void init_xmlnode(xmlnode *This, xmlNodePtr node, IXMLDOMNode *node_iface, dispex_static_data_t *dispex_data)
|
||||||
{
|
{
|
||||||
if(node)
|
if(node)
|
||||||
xmldoc_add_ref( node->doc );
|
{
|
||||||
|
xmlnode_add_ref(node);
|
||||||
|
xmldoc_add_ref(node->doc);
|
||||||
|
}
|
||||||
|
|
||||||
This->node = node;
|
This->node = node;
|
||||||
This->iface = node_iface;
|
This->iface = node_iface;
|
||||||
|
|
Loading…
Reference in New Issue