https://bugs.webkit.org/show_bug.cgi?id=93587
Node::replaceChild() can create bad DOM topology with MutationEvent, Part 2

Reviewed by Kent Tamura.

Source/WebCore:

This is a followup of r124156. replaceChild() has yet another hidden
MutationEvent trigger. This change added a guard for it.

Test: fast/events/mutation-during-replace-child-2.html

* dom/ContainerNode.cpp:
(WebCore::ContainerNode::replaceChild):

LayoutTests:

* fast/events/mutation-during-replace-child-2-expected.txt: Added.
* fast/events/mutation-during-replace-child-2.html: Added.

git-svn-id: svn://svn.chromium.org/blink/trunk@125237 bbb929c8-8fbe-4397-9dbb-9b2b20218538
bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach)
{
    ASSERT(refCount() || parentOrHostNode());

    RefPtr<Node> protect(this);

    ec = 0;

    if (oldChild == newChild) // nothing to do
        return true;

    checkReplaceChild(newChild.get(), oldChild, ec);
    if (ec)
        return false;

    if (!oldChild || oldChild->parentNode() != this) {
        ec = NOT_FOUND_ERR;
        return false;
    }

#if ENABLE(MUTATION_OBSERVERS)
    ChildListMutationScope mutation(this);
#endif

    RefPtr<Node> next = oldChild->nextSibling();

    RefPtr<Node> removedChild = oldChild;
    removeChild(oldChild, ec);
    if (ec)
        return false;

    if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do
        return true;

    checkReplaceChild(newChild.get(), oldChild, ec);
    if (ec)
        return false;

    NodeVector targets;
    collectChildrenAndRemoveFromOldParent(newChild.get(), targets, ec);
     if (ec)
         return false;
 
    // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
    checkReplaceChild(newChild.get(), oldChild, ec);
    if (ec)
        return false;

     InspectorInstrumentation::willInsertDOMNode(document(), this);
 
    for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
        Node* child = it->get();

        if (next && next->parentNode() != this)
            break;
        if (child->parentNode())
            break;

        treeScope()->adoptIfNeeded(child);

        forbidEventDispatch();
        if (next)
            insertBeforeCommon(next.get(), child);
        else
            appendChildToContainer(child, this);
        allowEventDispatch();

        updateTreeAfterInsertion(this, child, shouldLazyAttach);
    }

    dispatchSubtreeModifiedEvent();
    return true;
}

bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach)
{
    ASSERT(refCount() || parentOrHostNode());

    RefPtr<Node> protect(this);

    ec = 0;

    if (oldChild == newChild) // nothing to do
        return true;

    checkReplaceChild(newChild.get(), oldChild, ec);
    if (ec)
        return false;

    if (!oldChild || oldChild->parentNode() != this) {
        ec = NOT_FOUND_ERR;
        return false;
    }

#if ENABLE(MUTATION_OBSERVERS)
    ChildListMutationScope mutation(this);
#endif

    RefPtr<Node> next = oldChild->nextSibling();

    RefPtr<Node> removedChild = oldChild;
    removeChild(oldChild, ec);
    if (ec)
        return false;

    if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do
        return true;

    checkReplaceChild(newChild.get(), oldChild, ec);
    if (ec)
        return false;

    NodeVector targets;
    collectChildrenAndRemoveFromOldParent(newChild.get(), targets, ec);
     if (ec)
         return false;
 
     InspectorInstrumentation::willInsertDOMNode(document(), this);
 
    for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
        Node* child = it->get();

        if (next && next->parentNode() != this)
            break;
        if (child->parentNode())
            break;

        treeScope()->adoptIfNeeded(child);

        forbidEventDispatch();
        if (next)
            insertBeforeCommon(next.get(), child);
        else
            appendChildToContainer(child, this);
        allowEventDispatch();

        updateTreeAfterInsertion(this, child, shouldLazyAttach);
    }

    dispatchSubtreeModifiedEvent();
    return true;
}
