Changeset 147942 in webkit
- Timestamp:
- Apr 8, 2013 12:10:43 PM (11 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 2 deleted
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r147939 r147942 1 2013-04-08 Andrei Bucur <abucur@adobe.com> 2 3 Simplify ContainerNode::removeChildren 4 https://bugs.webkit.org/show_bug.cgi?id=113517 5 6 Reviewed by Darin Adler. 7 8 The patch is based on the work made by Elliott Sprehn. He kindly agreed 9 for me to finalize the last bits and pieces of the fix. 10 11 Remove containerNode.html test since it was checking for an infinite 12 loop when adding DOM nodes inside a DOMNodeRemoved mutation event 13 handler, but we actually do want to allow an infinite loop here for 14 correctness and compatability with other browsers. 15 16 Also added mutation-during-innerHTML which checks that all nodes 17 are notified of being removed even if they were added during the 18 DOMNodeRemoved notification. 19 20 There's a new test range-remove-children-event that verifies the 21 ranges modified inside a mutation event handler remain consistent. 22 23 * fast/dom/MutationObserver/added-out-of-order-expected.txt: 24 * fast/dom/MutationObserver/added-out-of-order.html: 25 * fast/dom/Range/range-remove-children-event-expected.txt: Added. 26 * fast/dom/Range/range-remove-children-event.html: Added. 27 * fast/dom/containerNode-expected.txt: Removed. 28 * fast/dom/containerNode.html: Removed. 29 * fast/events/mutation-during-innerHTML-expected.txt: Added. 30 * fast/events/mutation-during-innerHTML.html: Added. 31 1 32 2013-04-08 Robert Hogan <robert@webkit.org> 2 33 -
trunk/LayoutTests/fast/dom/MutationObserver/added-out-of-order-expected.txt
r137662 r147942 4 4 5 5 6 PASS mutations.length is 37 PASS mutations[0].addedNodes.length is 08 PASS mutations[0].removedNodes.length is 19 PASS mutations[0]. removedNodes[0].tagName is 'SPAN'6 PASS mutations.length is 2 7 PASS mutations[0].addedNodes.length is 1 8 PASS mutations[0].removedNodes.length is 0 9 PASS mutations[0].addedNodes[0].tagName is "DIV" 10 10 PASS mutations[1].addedNodes.length is 1 11 PASS mutations[1].removedNodes.length is 0 12 PASS mutations[1].addedNodes[0].tagName is 'DIV' 13 PASS mutations[2].addedNodes.length is 1 14 PASS mutations[2].removedNodes.length is 0 15 PASS mutations[2].addedNodes[0].nodeValue is 'hello world' 11 PASS mutations[1].removedNodes.length is 2 12 PASS mutations[1].addedNodes[0].nodeValue is "hello world" 13 PASS mutations[1].removedNodes[0].tagName is "SPAN" 14 PASS mutations[1].removedNodes[1].tagName is "DIV" 16 15 PASS successfullyParsed is true 17 16 -
trunk/LayoutTests/fast/dom/MutationObserver/added-out-of-order.html
r143386 r147942 17 17 18 18 var mutations = observer.takeRecords(); 19 shouldBe("mutations.length", " 3");20 shouldBe("mutations[0].addedNodes.length", " 0");21 shouldBe("mutations[0].removedNodes.length", " 1");22 shouldBe ("mutations[0].removedNodes[0].tagName", "'SPAN'");19 shouldBe("mutations.length", "2"); 20 shouldBe("mutations[0].addedNodes.length", "1"); 21 shouldBe("mutations[0].removedNodes.length", "0"); 22 shouldBeEqualToString("mutations[0].addedNodes[0].tagName", "DIV"); 23 23 shouldBe("mutations[1].addedNodes.length", "1"); 24 shouldBe("mutations[1].removedNodes.length", "0"); 25 shouldBe("mutations[1].addedNodes[0].tagName", "'DIV'"); 26 shouldBe("mutations[2].addedNodes.length", "1"); 27 shouldBe("mutations[2].removedNodes.length", "0"); 28 shouldBe("mutations[2].addedNodes[0].nodeValue", "'hello world'"); 24 shouldBe("mutations[1].removedNodes.length", "2"); 25 shouldBeEqualToString("mutations[1].addedNodes[0].nodeValue", "hello world"); 26 shouldBeEqualToString("mutations[1].removedNodes[0].tagName", "SPAN"); 27 shouldBeEqualToString("mutations[1].removedNodes[1].tagName", "DIV"); 29 28 </script> 30 29 <script src="../../js/resources/js-test-post.js"></script> -
trunk/Source/WebCore/ChangeLog
r147940 r147942 1 2013-04-08 Andrei Bucur <abucur@adobe.com> 2 3 Simplify ContainerNode::removeChildren 4 https://bugs.webkit.org/show_bug.cgi?id=113517 5 6 Reviewed by Darin Adler. 7 8 The patch is based on the work made by Elliott Sprehn. He kindly agreed 9 for me to finalize the last bits and pieces of the fix. 10 11 Simplify ContainerNode::removeChildren by merging the loops and removing 12 willRemoveChildren. This removes two traversals of the children, avoids 13 refing and derefing all the children once, avoids allocating a second 14 NodeVector of children, and means we detach() in the same order as 15 normal removal. 16 17 This does mean you can get into an infinite loop with DOMNodeRemoved 18 listeners by continously adding nodes but this is true in all other browsers 19 and the current behavior is bad because it means you don't get notified 20 of nodes added during removal (which other browsers do notify of). This 21 patch removes the containerNode.html test that originally tested for this 22 infinite loop and adds a new one that tests that all nodes get notified. 23 24 This makes PerformanceTests/Parser/innerHTML-setter.html 2-6% faster. 25 26 There's also a new test verifying ranges remain consistent if modified 27 inside an mutation event handler. Without the patch it's possible to create 28 a range with boundaries outside of the DOM tree. 29 30 Tests: fast/dom/Range/range-remove-children-event.html 31 fast/events/mutation-during-innerHTML.html 32 33 * dom/ContainerNode.cpp: 34 (WebCore::ContainerNode::removeChildren): 35 * dom/Document.cpp: 36 * dom/Document.h: nodeChildrenWillBeRemoved is not needed any more. 37 * dom/Range.cpp: 38 * dom/Range.h: nodeChildrenWillBeRemoved is not needed any more. 39 1 40 2013-04-06 Simon Fraser <simon.fraser@apple.com> 2 41 -
trunk/Source/WebCore/dom/ContainerNode.cpp
r147832 r147942 455 455 } 456 456 457 static void willRemoveChildren(ContainerNode* container)458 {459 NodeVector children;460 getChildNodes(container, children);461 462 container->document()->nodeChildrenWillBeRemoved(container);463 464 ChildListMutationScope mutation(container);465 for (NodeVector::const_iterator it = children.begin(); it != children.end(); ++it) {466 Node* child = it->get();467 mutation.willRemoveChild(child);468 child->notifyMutationObserversNodeWillDetach();469 470 // fire removed from document mutation events.471 dispatchChildRemovalEvents(child);472 }473 474 ChildFrameDisconnector(container).disconnect(ChildFrameDisconnector::DescendantsOnly);475 }476 477 457 void ContainerNode::disconnectDescendantFrames() 478 458 { … … 596 576 RefPtr<ContainerNode> protect(this); 597 577 598 // exclude this node when looking for removed focusedNode since only children will be removed578 // Exclude this node when looking for removed focusedNode since only children will be removed. 599 579 document()->removeFocusedNodeOfSubtree(this, true); 600 580 … … 603 583 #endif 604 584 605 // Do any prep work needed before actually starting to detach 606 // and remove... e.g. stop loading frames, fire unload events. 607 willRemoveChildren(protect.get()); 608 609 Vector<RefPtr<Node>, 10> removedChildren; 585 ChildListMutationScope mutation(this); 586 NodeVector removedChildren; 610 587 { 611 588 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates; 612 { 613 NoEventDispatchAssertion assertNoEventDispatch; 614 removedChildren.reserveInitialCapacity(childNodeCount()); 615 while (RefPtr<Node> n = m_firstChild) { 616 Node* next = n->nextSibling(); 617 618 // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744). 619 // removeChild() does this after calling detach(). There is no explanation for 620 // this discrepancy between removeChild() and its optimized version removeChildren(). 621 n->setPreviousSibling(0); 622 n->setNextSibling(0); 623 n->setParentOrShadowHostNode(0); 624 document()->adoptIfNeeded(n.get()); 625 626 m_firstChild = next; 627 if (n == m_lastChild) 628 m_lastChild = 0; 629 removedChildren.append(n.release()); 630 } 631 632 // Detach the nodes only after properly removed from the tree because 633 // a. detaching requires a proper DOM tree (for counters and quotes for 634 // example) and during the previous loop the next sibling still points to 635 // the node being removed while the node being removed does not point back 636 // and does not point to the same parent as its next sibling. 637 // b. destroying Renderers of standalone nodes is sometimes faster. 638 for (size_t i = 0; i < removedChildren.size(); ++i) { 639 Node* removedChild = removedChildren[i].get(); 640 if (removedChild->attached()) 641 removedChild->detach(); 642 } 589 590 while (RefPtr<Node> child = m_firstChild) { 591 // First dispatch the mutation events if any. 592 // Unfortunately it's possible for this to be called more than once for a node 593 // because of the nature of mutation events (if the node is moved further in the child list 594 // by an event handler). 595 dispatchChildRemovalEvents(child.get()); 596 ChildFrameDisconnector(child.get()).disconnect(); 597 598 // Mutation or unload events could have moved this child. 599 if (child != m_firstChild) 600 continue; 601 602 // We can't use a bulk version of document()->nodeWillBeRemoved() before the removal loop. 603 // We need to call document()->nodeWillBeRemoved() on each node in case the node was created 604 // by a mutation event handler. 605 // document()->nodeWillbeRemoved() may modify the children list so we may need to retry. 606 document()->nodeWillBeRemoved(child.get()); 607 if (child != m_firstChild) 608 continue; 609 610 // Notify the mutation observers. 611 mutation.willRemoveChild(child.get()); 612 child->notifyMutationObserversNodeWillDetach(); 613 614 removeBetween(0, child->nextSibling(), child.get()); 615 removedChildren.append(child.release()); 643 616 } 644 617 618 // FIXME: We could avoid walking all the children twice by calling 619 // notify inside the loop and childrenChanged after but that would mean 620 // calling childrenChanged in a different order than all other methods. 621 // Figure out if this is safe. 645 622 childrenChanged(false, 0, 0, -static_cast<int>(removedChildren.size())); 646 647 623 for (size_t i = 0; i < removedChildren.size(); ++i) 648 624 ChildNodeRemovalNotifier(this).notify(removedChildren[i].get()); -
trunk/Source/WebCore/dom/Document.cpp
r147920 r147942 3569 3569 } 3570 3570 3571 void Document::nodeChildrenWillBeRemoved(ContainerNode* container)3572 {3573 if (!m_ranges.isEmpty()) {3574 HashSet<Range*>::const_iterator end = m_ranges.end();3575 for (HashSet<Range*>::const_iterator it = m_ranges.begin(); it != end; ++it)3576 (*it)->nodeChildrenWillBeRemoved(container);3577 }3578 3579 HashSet<NodeIterator*>::const_iterator nodeIteratorsEnd = m_nodeIterators.end();3580 for (HashSet<NodeIterator*>::const_iterator it = m_nodeIterators.begin(); it != nodeIteratorsEnd; ++it) {3581 for (Node* n = container->firstChild(); n; n = n->nextSibling())3582 (*it)->nodeWillBeRemoved(n);3583 }3584 3585 if (Frame* frame = this->frame()) {3586 for (Node* n = container->firstChild(); n; n = n->nextSibling()) {3587 frame->eventHandler()->nodeWillBeRemoved(n);3588 frame->selection()->nodeWillBeRemoved(n);3589 frame->page()->dragCaretController()->nodeWillBeRemoved(n);3590 }3591 }3592 }3593 3594 3571 void Document::nodeWillBeRemoved(Node* n) 3595 3572 { -
trunk/Source/WebCore/dom/Document.h
r147920 r147942 725 725 726 726 void updateRangesAfterChildrenChanged(ContainerNode*); 727 // nodeChildrenWillBeRemoved is used when removing all node children at once.728 void nodeChildrenWillBeRemoved(ContainerNode*);729 727 // nodeWillBeRemoved is only safe when removing one node at a time. 730 728 void nodeWillBeRemoved(Node*); -
trunk/Source/WebCore/dom/Range.cpp
r147832 r147942 1733 1733 } 1734 1734 1735 static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundary, ContainerNode* container)1736 {1737 for (Node* nodeToBeRemoved = container->firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) {1738 if (boundary.childBefore() == nodeToBeRemoved) {1739 boundary.setToStartOfNode(container);1740 return;1741 }1742 1743 for (Node* n = boundary.container(); n; n = n->parentNode()) {1744 if (n == nodeToBeRemoved) {1745 boundary.setToStartOfNode(container);1746 return;1747 }1748 }1749 }1750 }1751 1752 void Range::nodeChildrenWillBeRemoved(ContainerNode* container)1753 {1754 ASSERT(container);1755 ASSERT(container->document() == m_ownerDocument);1756 boundaryNodeChildrenWillBeRemoved(m_start, container);1757 boundaryNodeChildrenWillBeRemoved(m_end, container);1758 }1759 1760 1735 static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node* nodeToBeRemoved) 1761 1736 { -
trunk/Source/WebCore/dom/Range.h
r142461 r147942 129 129 130 130 void nodeChildrenChanged(ContainerNode*); 131 void nodeChildrenWillBeRemoved(ContainerNode*);132 131 void nodeWillBeRemoved(Node*); 133 132
Note: See TracChangeset
for help on using the changeset viewer.