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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue