Changeset 201471 in webkit
- Timestamp:
- May 27, 2016 3:31:43 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r201468 r201471 1 2016-05-26 Ryosuke Niwa <rniwa@webkit.org> 2 3 Crash in TreeScope::focusedElement 4 https://bugs.webkit.org/show_bug.cgi?id=158108 5 6 Reviewed by Enrica Casucci. 7 8 Added a regression test for accessing shadowRoot.activeElement after re-focusing an element 9 inside DOMNodeRemovedFromDocument event and unload events. 10 11 This patch also restores the expected result of fast/events/onblur-remove.html to that of when 12 the test was in r15720 and updated in r19014. The expected result was changed in r85495 as it was 13 converted to a eventSender test. 14 15 * fast/dom/Range/range-created-during-remove-children-expected.txt: 16 * fast/dom/Range/range-created-during-remove-children.html: Update the test to use unload event 17 of an iframe since we no longer fire blur event when removing a focused element. 18 * fast/dom/adopt-node-prevented-expected.txt: 19 * fast/dom/adopt-node-prevented.html: Ditto. 20 * fast/dom/remove-body-during-body-replacement2.html: Ditto. Use DOMNodeRemoved instead. 21 * fast/events/nested-event-remove-node-crash.html: Ditto. Use DOMNodeRemovedFromDocument instead. 22 * fast/events/onblur-remove-expected.txt: 23 * fast/events/onblur-remove.html: See above. 24 * fast/shadow-dom/shadow-root-active-element-crash-expected.txt: Added. 25 * fast/shadow-dom/shadow-root-active-element-crash.html: Added. 26 1 27 2016-05-27 Brent Fulgham <bfulgham@apple.com> 2 28 -
trunk/LayoutTests/fast/dom/Range/range-created-during-remove-children-expected.txt
r158739 r201471 1 PASS ranges[" blur"].startContainer is sample2 PASS ranges[" blur"].endContainer is sample3 PASS ranges[" blur"].startOffset is 04 PASS ranges[" blur"].endOffset is 01 PASS ranges["unload"].startContainer is sample 2 PASS ranges["unload"].endContainer is sample 3 PASS ranges["unload"].startOffset is 0 4 PASS ranges["unload"].endOffset is 0 5 5 PASS ranges["DOMNodeRemovedFromDocument"].startContainer is sample 6 6 PASS ranges["DOMNodeRemovedFromDocument"].endContainer is sample -
trunk/LayoutTests/fast/dom/Range/range-created-during-remove-children.html
r158739 r201471 1 1 <div id="container"> 2 2 <p id="description"></p> 3 <div id="sample"><span contenteditable="true">foobar< /span></div>3 <div id="sample"><span contenteditable="true">foobar<iframe id="target"></iframe></span></div> 4 4 </div> 5 5 <div id="console"></div> … … 17 17 } 18 18 19 document. addEventListener('blur', eventHandler, true);19 document.querySelector('iframe').contentWindow.addEventListener('unload', eventHandler, true); 20 20 document.addEventListener('DOMNodeRemovedFromDocument', eventHandler, true); 21 21 … … 23 23 sample.innerHTML = ''; 24 24 25 shouldBe('ranges[" blur"].startContainer', 'sample');26 shouldBe('ranges[" blur"].endContainer', 'sample');27 shouldBe('ranges[" blur"].startOffset', '0');28 shouldBe('ranges[" blur"].endOffset', '0');25 shouldBe('ranges["unload"].startContainer', 'sample'); 26 shouldBe('ranges["unload"].endContainer', 'sample'); 27 shouldBe('ranges["unload"].startOffset', '0'); 28 shouldBe('ranges["unload"].endOffset', '0'); 29 29 shouldBe('ranges["DOMNodeRemovedFromDocument"].startContainer', 'sample'); 30 30 shouldBe('ranges["DOMNodeRemovedFromDocument"].endContainer', 'sample'); -
trunk/LayoutTests/fast/dom/adopt-node-prevented-expected.txt
r129469 r201471 9 9 PASS target.ownerDocument.location is document.location 10 10 11 -
trunk/LayoutTests/fast/dom/adopt-node-prevented.html
r155265 r201471 6 6 <body> 7 7 <div id="newParent"></div> 8 < a href="#" id="target"></a>8 <iframe href="#" id="target"></iframe> 9 9 <script> 10 10 description("Test that adoptNode fails safely if prevented by a DOM mutation."); … … 13 13 newParent = document.getElementById("newParent"); 14 14 target = document.getElementById("target"); 15 target. addEventListener("blur", function () { newParent.appendChild(target); }, false);15 target.contentWindow.addEventListener("unload", function () { newParent.appendChild(target); }, false); 16 16 target.focus(); 17 17 var anotherDocument = document.implementation.createDocument("", "", null); -
trunk/LayoutTests/fast/dom/remove-body-during-body-replacement2.html
r120792 r201471 18 18 19 19 setTimeout(function () { 20 document.addEventListener('DOM FocusOut', function () { crash(); }, true);20 document.addEventListener('DOMNodeRemoved', function () { crash(); }, true); 21 21 document.addEventListener('DOMSubtreeModified', function () { /* noop */ }, false); 22 22 document.designMode = "on"; -
trunk/LayoutTests/fast/events/nested-event-remove-node-crash.html
r120792 r201471 35 35 4. the onblur event handler will send off an XHR who's handler will remove the node 36 36 */ 37 document.getElementById("theSelect").focus(); 37 var select = document.getElementById("theSelect"); 38 select.addEventListener('DOMNodeRemovedFromDocument', function () { sendXHR();GC(); }); 39 select.focus(); 38 40 sendXHR(); 39 41 -
trunk/LayoutTests/fast/events/onblur-remove-expected.txt
r85495 r201471 1 This tests that elements shouldn't emit any onblur events when they are being removed from the document. 2 Note, this test is expected to fail as of 04/25/2011. See bug #59379. 1 This tests that elements shouldn't emit any onblur events when they are being removed from the document. 3 2 4 3 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". 5 4 6 5 7 FAIL Onblur handler called. 6 PASS blurEventCount is 0 8 7 9 8 TEST COMPLETE -
trunk/LayoutTests/fast/events/onblur-remove.html
r155267 r201471 6 6 testRunner.waitUntilDone(); 7 7 8 var numBlurs= 0;8 var blurEventCount = 0; 9 9 10 10 window.onload = function() { document.getElementById("input").focus(); } … … 15 15 f.innerHTML = ''; 16 16 17 if (numBlurs) 18 testFailed('Onblur handler called.'); 19 else 20 testPassed('Onblur handler not called.'); 17 shouldBe("blurEventCount", "0"); 21 18 22 19 debug('<br /><span class="pass">TEST COMPLETE</span>'); … … 29 26 <p id="description"></p> 30 27 <form id='f'> 31 <input id="input" onblur=" numBlurs++" onfocus="setTimeout('finish()', 0)">28 <input id="input" onblur="blurEventCount++" onfocus="setTimeout('finish()', 0)"> 32 29 </form> 33 30 <div id="console"></div> 34 31 <script> 35 description("This tests that elements shouldn't emit any onblur events when they are being removed from the document. <br>" + 36 "Note, this test is expected to fail as of 04/25/2011. See bug #59379."); 32 description("This tests that elements shouldn't emit any onblur events when they are being removed from the document."); 37 33 </script> 38 34 </body> -
trunk/Source/WebCore/ChangeLog
r201468 r201471 1 2016-05-26 Ryosuke Niwa <rniwa@webkit.org> 2 3 Crash in TreeScope::focusedElement 4 https://bugs.webkit.org/show_bug.cgi?id=158108 5 6 Reviewed by Enrica Casucci. 7 8 The bug was caused by a flawed sequence of steps we took to remove an element. When an element is removed, 9 willRemoveChild and willRemoveChildren fire blur events on removed focused element and its ancestors and 10 unload event on any removed iframes. However, it was possible to focus an element on which we had fired blur 11 during an unload event, leaving m_focusedElement point to an element that's not in the document anymore. 12 13 Changing the order doesn't help because that would make it possible to insert the removed iframes back into 14 the document inside a event listener of the blur event, which was specifically fixed by r127534 four years ago. 15 16 Instead, fix the bug by not firing blur and change events on removed nodes. New behavior matches Firefox and HTML5 17 specification: https://html.spec.whatwg.org/multipage/interaction.html#focus-fixup-rule-one 18 19 Test: fast/shadow-dom/shadow-root-active-element-crash.html 20 21 * dom/ContainerNode.cpp: 22 (WebCore::willRemoveChild): Made this function static local since it didn't need to have access to any private 23 member variables. Call Document::nodeWillBeRemoved after disconnecting iframes since unload event handler could 24 allocate new Ranges just like mutation events. 25 (WebCore::willRemoveChildren): Ditto. 26 (WebCore::ContainerNode::removeChild): Removed the calls to removeFullScreenElementOfSubtree and 27 removeFocusedNodeOfSubtree as they're now called in Document::nodeWillBeRemoved. 28 (WebCore::ContainerNode::removeChildren): Ditto. 29 * dom/ContainerNode.h: 30 * dom/Document.cpp: 31 (WebCore::Document::removeFocusedNodeOfSubtree): Don't dispatch blur and change events when a node is removed. 32 (WebCore::Document::setFocusedElement): Added FocusRemovalEventsMode as the third argument. Avoid dispatching blur 33 and change events when FocusRemovalEventsMode::Dispatch is set. 34 (WebCore::Document::nodeChildrenWillBeRemoved): Added calls to removeFullScreenElementOfSubtree and 35 removeFocusedNodeOfSubtree. Also assert that no events are fired within this function. If we ever fire an event here, 36 "unloaded" iframes can be inserted back into a document before ContainerNode::removeChild actually removes them. 37 (WebCore::Document::nodeWillBeRemoved): Ditto. 38 * dom/Document.h: 39 * dom/TreeScope.cpp: 40 (WebCore::TreeScope::focusedElement): Added a release assertion to make sure the focused element is in the document 41 of the tree scope, and added an explicit type check just in case. 42 1 43 2016-05-27 Brent Fulgham <bfulgham@apple.com> 2 44 -
trunk/Source/WebCore/dom/ContainerNode.cpp
r201416 r201471 457 457 } 458 458 459 void ContainerNode::willRemoveChild(Node& child)459 static void willRemoveChild(ContainerNode& container, Node& child) 460 460 { 461 461 ASSERT(child.parentNode()); … … 465 465 dispatchChildRemovalEvents(child); 466 466 467 if (child.parentNode() != this)467 if (child.parentNode() != &container) 468 468 return; 469 469 470 child.document().nodeWillBeRemoved(child); // e.g. mutation event listener can create a new range.471 470 if (is<ContainerNode>(child)) 472 471 disconnectSubframesIfNeeded(downcast<ContainerNode>(child), RootAndDescendants); 472 473 if (child.parentNode() != &container) 474 return; 475 476 child.document().nodeWillBeRemoved(child); // e.g. mutation event listener can create a new range. 473 477 } 474 478 … … 487 491 } 488 492 493 disconnectSubframesIfNeeded(container, DescendantsOnly); 494 489 495 container.document().nodeChildrenWillBeRemoved(container); 490 496 491 disconnectSubframesIfNeeded(container, DescendantsOnly);492 497 } 493 498 … … 514 519 515 520 Ref<Node> child(oldChild); 516 517 document().removeFocusedNodeOfSubtree(child.ptr());518 519 #if ENABLE(FULLSCREEN_API)520 document().removeFullScreenElementOfSubtree(&child.get());521 #endif522 521 523 522 // Events fired when blurring currently focused node might have moved this … … 528 527 } 529 528 530 willRemoveChild( child);529 willRemoveChild(*this, child); 531 530 532 531 // Mutation events might have moved this child into a different parent. … … 611 610 // The container node can be removed from event handlers. 612 611 Ref<ContainerNode> protectedThis(*this); 613 614 // exclude this node when looking for removed focusedNode since only children will be removed615 document().removeFocusedNodeOfSubtree(this, true);616 617 #if ENABLE(FULLSCREEN_API)618 document().removeFullScreenElementOfSubtree(this, true);619 #endif620 612 621 613 // Do any prep work needed before actually starting to detach -
trunk/Source/WebCore/dom/ContainerNode.h
r200696 r201471 131 131 bool isContainerNode() const = delete; 132 132 133 void willRemoveChild(Node& child);134 135 133 Node* m_firstChild { nullptr }; 136 134 Node* m_lastChild { nullptr }; -
trunk/Source/WebCore/dom/Document.cpp
r201441 r201471 3701 3701 3702 3702 if (nodeInSubtree) 3703 setFocusedElement(nullptr );3703 setFocusedElement(nullptr, FocusDirectionNone, FocusRemovalEventsMode::DoNotDispatch); 3704 3704 } 3705 3705 … … 3739 3739 #endif 3740 3740 3741 bool Document::setFocusedElement(Element* element, FocusDirection direction )3741 bool Document::setFocusedElement(Element* element, FocusDirection direction, FocusRemovalEventsMode eventsMode) 3742 3742 { 3743 3743 RefPtr<Element> newFocusedElement = element; … … 3762 3762 oldFocusedElement->setFocus(false); 3763 3763 3764 // Dispatch a change event for form control elements that have been edited. 3765 if (is<HTMLFormControlElement>(*oldFocusedElement)) { 3766 HTMLFormControlElement& formControlElement = downcast<HTMLFormControlElement>(*oldFocusedElement); 3767 if (formControlElement.wasChangedSinceLastFormControlChangeEvent()) 3768 formControlElement.dispatchFormControlChangeEvent(); 3769 } 3770 3771 // Dispatch the blur event and let the node do any other blur related activities (important for text fields) 3772 oldFocusedElement->dispatchBlurEvent(newFocusedElement.copyRef()); 3773 3774 if (m_focusedElement) { 3775 // handler shifted focus 3776 focusChangeBlocked = true; 3777 newFocusedElement = nullptr; 3778 } 3779 3780 oldFocusedElement->dispatchFocusOutEvent(eventNames().focusoutEvent, newFocusedElement.copyRef()); // DOM level 3 name for the bubbling blur event. 3781 // FIXME: We should remove firing DOMFocusOutEvent event when we are sure no content depends 3782 // on it, probably when <rdar://problem/8503958> is resolved. 3783 oldFocusedElement->dispatchFocusOutEvent(eventNames().DOMFocusOutEvent, newFocusedElement.copyRef()); // DOM level 2 name for compatibility. 3784 3785 if (m_focusedElement) { 3786 // handler shifted focus 3787 focusChangeBlocked = true; 3788 newFocusedElement = nullptr; 3789 } 3790 3764 if (eventsMode == FocusRemovalEventsMode::Dispatch) { 3765 // Dispatch a change event for form control elements that have been edited. 3766 if (is<HTMLFormControlElement>(*oldFocusedElement)) { 3767 HTMLFormControlElement& formControlElement = downcast<HTMLFormControlElement>(*oldFocusedElement); 3768 if (formControlElement.wasChangedSinceLastFormControlChangeEvent()) 3769 formControlElement.dispatchFormControlChangeEvent(); 3770 } 3771 3772 // Dispatch the blur event and let the node do any other blur related activities (important for text fields) 3773 oldFocusedElement->dispatchBlurEvent(newFocusedElement.copyRef()); 3774 3775 if (m_focusedElement) { 3776 // handler shifted focus 3777 focusChangeBlocked = true; 3778 newFocusedElement = nullptr; 3779 } 3780 3781 oldFocusedElement->dispatchFocusOutEvent(eventNames().focusoutEvent, newFocusedElement.copyRef()); // DOM level 3 name for the bubbling blur event. 3782 // FIXME: We should remove firing DOMFocusOutEvent event when we are sure no content depends 3783 // on it, probably when <rdar://problem/8503958> is resolved. 3784 oldFocusedElement->dispatchFocusOutEvent(eventNames().DOMFocusOutEvent, newFocusedElement.copyRef()); // DOM level 2 name for compatibility. 3785 3786 if (m_focusedElement) { 3787 // handler shifted focus 3788 focusChangeBlocked = true; 3789 newFocusedElement = nullptr; 3790 } 3791 } else 3792 ASSERT(!m_focusedElement); 3793 3791 3794 if (oldFocusedElement->isRootEditableElement()) 3792 3795 frame()->editor().didEndEditing(); … … 3968 3971 void Document::nodeChildrenWillBeRemoved(ContainerNode& container) 3969 3972 { 3973 NoEventDispatchAssertion assertNoEventDispatch; 3974 3975 removeFocusedNodeOfSubtree(&container, true /* amongChildrenOnly */); 3976 3977 #if ENABLE(FULLSCREEN_API) 3978 removeFullScreenElementOfSubtree(&container, true /* amongChildrenOnly */); 3979 #endif 3980 3970 3981 for (auto* range : m_ranges) 3971 3982 range->nodeChildrenWillBeRemoved(container); … … 3992 4003 void Document::nodeWillBeRemoved(Node& n) 3993 4004 { 4005 NoEventDispatchAssertion assertNoEventDispatch; 4006 4007 removeFocusedNodeOfSubtree(&n); 4008 4009 #if ENABLE(FULLSCREEN_API) 4010 removeFullScreenElementOfSubtree(&n); 4011 #endif 4012 3994 4013 for (auto* it : m_nodeIterators) 3995 4014 it->nodeWillBeRemoved(n); -
trunk/Source/WebCore/dom/Document.h
r201104 r201471 723 723 void setSelectedStylesheetSet(const String&); 724 724 725 WEBCORE_EXPORT bool setFocusedElement(Element*, FocusDirection = FocusDirectionNone); 725 enum class FocusRemovalEventsMode { Dispatch, DoNotDispatch }; 726 WEBCORE_EXPORT bool setFocusedElement(Element*, FocusDirection = FocusDirectionNone, 727 FocusRemovalEventsMode = FocusRemovalEventsMode::Dispatch); 726 728 Element* focusedElement() const { return m_focusedElement.get(); } 727 729 UserActionElementSet& userActionElements() { return m_userActionElements; } -
trunk/Source/WebCore/dom/TreeScope.cpp
r196223 r201471 312 312 return nullptr; 313 313 TreeScope* treeScope = &element->treeScope(); 314 RELEASE_ASSERT(&document == &treeScope->documentScope()); 314 315 while (treeScope != this && treeScope != &document) { 315 element = downcast<ShadowRoot>(treeScope->rootNode()).host(); 316 auto& rootNode = treeScope->rootNode(); 317 if (is<ShadowRoot>(rootNode)) 318 element = downcast<ShadowRoot>(rootNode).host(); 319 else 320 return nullptr; 316 321 treeScope = &element->treeScope(); 317 322 }
Note: See TracChangeset
for help on using the changeset viewer.