<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[188920] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/188920">188920</a></dd>
<dt>Author</dt> <dd>bfulgham@apple.com</dd>
<dt>Date</dt> <dd>2015-08-25 11:03:00 -0700 (Tue, 25 Aug 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Wheel events stop propagating when target element is removed from DOM
https://bugs.webkit.org/show_bug.cgi?id=148384
&lt;rdar://problem/19732211&gt;

Reviewed by David Hyatt.

Source/WebCore:

Tested by tiled-drawing/scrolling/latched-to-deleted-node.html

We need to reset our latching state if the targeted node is removed from the DOM.
Add a check in 'platformPrepareForWheelEvents' that checks if the expected latching
target node was already removed from the DOM. If it was, we should not send events
to it, and should reset latching state so we can attach to the next relevant node.

* dom/Element.cpp:
(WebCore::Element::removedFrom): Remove any latched wheel event state objects that
match the current element.
* page/MainFrame.cpp:
(WebCore::MainFrame::removeLatchingStateForTarget): Remove any latched wheel event
state structures that match the passed wheel event target.
* page/MainFrame.h:

LayoutTests:

* tiled-drawing/scrolling/latched-to-deleted-node-expected.txt: Added.
* tiled-drawing/scrolling/latched-to-deleted-node.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomElementcpp">trunk/Source/WebCore/dom/Element.cpp</a></li>
<li><a href="#trunkSourceWebCorepageMainFramecpp">trunk/Source/WebCore/page/MainFrame.cpp</a></li>
<li><a href="#trunkSourceWebCorepageMainFrameh">trunk/Source/WebCore/page/MainFrame.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTeststileddrawingscrollinglatchedtodeletednodeexpectedtxt">trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node-expected.txt</a></li>
<li><a href="#trunkLayoutTeststileddrawingscrollinglatchedtodeletednodehtml">trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (188919 => 188920)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/LayoutTests/ChangeLog        2015-08-25 18:03:00 UTC (rev 188920)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2015-08-24  Brent Fulgham  &lt;bfulgham@apple.com&gt;
+
+        Wheel events stop propagating when target element is removed from DOM
+        https://bugs.webkit.org/show_bug.cgi?id=148384
+        &lt;rdar://problem/19732211&gt;
+
+        Reviewed by David Hyatt.
+
+        * tiled-drawing/scrolling/latched-to-deleted-node-expected.txt: Added.
+        * tiled-drawing/scrolling/latched-to-deleted-node.html: Added.
+
</ins><span class="cx"> 2015-08-25  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         compareDocumentPosition() should report PRECEDING or FOLLOWING information even if nodes are disconnected
</span></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollinglatchedtodeletednodeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node-expected.txt (0 => 188920)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node-expected.txt                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node-expected.txt        2015-08-25 18:03:00 UTC (rev 188920)
</span><span class="lines">@@ -0,0 +1,18 @@
</span><ins>+Container
+
+After moving 40px up or down we remove the initial target element of wheel event from
+the dom. In safari this causes the wheel event to stop firing on the current frame, but it
+then starts firing on the parent frame.
+
+Tests that iframe doesn't consume wheel events when scrolling a select in an iframe.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS Removed the target element
+PASS Page did not scroll
+PASS div continued scrolling after target element was deleted.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTeststileddrawingscrollinglatchedtodeletednodehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node.html (0 => 188920)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node.html                                (rev 0)
+++ trunk/LayoutTests/tiled-drawing/scrolling/latched-to-deleted-node.html        2015-08-25 18:03:00 UTC (rev 188920)
</span><span class="lines">@@ -0,0 +1,181 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+        &lt;style&gt;
+        #pane {
+      display:inline-block;
+          width:200px;
+          height:500px;
+        }
+        #boxup {
+          position: absolute;
+          top:0px;
+          width:200px;
+          height:230px;
+          background-color: #000;
+          opacity:.4;
+        }
+        #boxdown {
+          position: absolute;
+          top:270px;
+          width:200px;
+          height:230px;
+          background-color: #000;
+          opacity:.4;
+        }
+        #objectBack {
+          position: absolute;
+          top:200px;
+          width:200px;
+          height:100px;
+          background-color: #6c6;
+        }
+        #targetElement {
+          position: absolute;
+          top:30px;
+          width:200px;
+          height:40px;
+          background-color: #c66;
+        }
+        &lt;/style&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;setupTopLevel();&quot;&gt;
+&lt;script&gt;
+window.jsTestIsAsync = true;
+
+var pageScrollPositionBeforeGesture;
+var divElement;
+var divScrollPositionBeforeGesture;
+
+var deleteThreshhold = 40;
+
+function locationInWindowCoordinates(element)
+{
+    var position = {};
+    position.x = element.offsetLeft;
+    position.y = element.offsetTop;
+
+    while (element.offsetParent) {
+        position.x = position.x + element.offsetParent.offsetLeft;
+        position.y = position.y + element.offsetParent.offsetTop;
+        if (element == document.getElementsByTagName(&quot;body&quot;)[0])
+            break;
+
+        element = element.offsetParent;
+    }
+
+    return position;
+}
+
+document.addEventListener(&quot;DOMContentLoaded&quot;, function() {
+        var objectBack = document.getElementById(&quot;objectBack&quot;);
+        var targetElement = document.getElementById(&quot;targetElement&quot;);
+        var inIFrame = window.parent !== window;
+        var pos = 0;
+        
+        
+        document.addEventListener(&quot;wheel&quot;, function(event) {
+                pos = pos - event.deltaY ;
+                move(objectBack, pos);
+                if (Math.abs(pos) &gt; deleteThreshhold) {
+                        // don't remove it twice
+                        if (targetElement.parentNode) {
+                                var parent = targetElement.parentNode;
+                                parent.removeChild(targetElement);
+                testPassed(&quot;Removed the target element&quot;);
+                        }
+                }
+                event.preventDefault();
+        });
+        
+        // Setup
+    targetElement.innerHTML = &quot;Put cursor here. Scroll up/down&quot;;
+
+        function move(element, pos) {
+                element.style.transform = &quot;translateY(&quot; + pos + &quot;px)&quot;;
+    }
+});        
+
+function checkForScroll()
+{
+    var pageScrollPositionAfterGesture = document.body.scrollTop;
+
+    // The page should not have scrolled
+    if (pageScrollPositionBeforeGesture != pageScrollPositionAfterGesture)
+        testFailed(&quot;Page received scroll events, when it should not have. Expected &quot; + pageScrollPositionBeforeGesture + &quot;, but got &quot; + pageScrollPositionAfterGesture);
+    else
+        testPassed(&quot;Page did not scroll&quot;);
+
+    var divScrollPositionAfterGesture = divElement.getBoundingClientRect().top;
+
+    // The div should have continued moving after the element was deleted
+    if (divScrollPositionAfterGesture &gt; (divScrollPositionBeforeGesture - deleteThreshhold - 10))
+        testFailed(&quot;div did not scroll to proper location. Expected &quot; + divScrollPositionBeforeGesture + &quot;, but got &quot; + divScrollPositionAfterGesture);
+    else
+        testPassed(&quot;div continued scrolling after target element was deleted.&quot;);
+
+    finishJSTest();
+}
+
+function scrollTest()
+{
+    pageScrollPositionBeforeGesture = document.body.scrollTop;
+
+    divElement = document.getElementById('objectBack');
+
+    divScrollPositionBeforeGesture = divElement.getBoundingClientRect().top;
+
+    var windowPosition = locationInWindowCoordinates(divElement);
+
+    var startPosX = windowPosition.x + 0.5 * divElement.clientWidth;
+    var startPosY = windowPosition.y + 0.5 * divElement.clientHeight;
+    
+    eventSender.mouseMoveTo(startPosX, startPosY);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'began', 'none');
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, 'changed', 'none');
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, 'changed', 'none');
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none');
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'begin');
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue');
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end');
+    eventSender.callAfterScrollingCompletes(checkForScroll);
+}
+
+function setupTopLevel()
+{
+    if (window.eventSender) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+
+        eventSender.monitorWheelEvents();
+        setTimeout(scrollTest, 0);
+    } else {
+        var messageLocation = document.getElementById('parent');
+        var message = document.createElement('div');
+        message.innerHTML = &quot;&lt;p&gt;This test is better run under DumpRenderTree. To manually test it, place the mouse pointer&lt;br/&gt;&quot;
+            + &quot;at the top of the page, and then use the mouse wheel or a two-finger swipe to scroll the&lt;br/&gt;&quot;
+            + &quot;down past the iframe.&lt;br/&gt;&lt;br/&gt;&quot;
+            + &quot;The iframe should not scroll.&lt;/p&gt;&quot;;
+        messageLocation.appendChild(message);
+    }
+}
+&lt;/script&gt;
+&lt;div id=&quot;parent&quot; style=&quot;height: 2000px;&quot;&gt;
+    &lt;div id=&quot;pane&quot;&gt;
+            &lt;h3&gt;Container&lt;/h3&gt;
+            &lt;div id=&quot;objectBack&quot;&gt;&lt;div id=&quot;targetElement&quot;&gt;&lt;/div&gt;&lt;/div&gt;
+            &lt;div id=&quot;boxup&quot;&gt;&lt;/div&gt;
+            &lt;div id=&quot;boxdown&quot;&gt;&lt;/div&gt;
+    &lt;/div&gt;
+    &lt;p class=&quot;description&quot;&gt;After moving 40px up or down we remove the initial target element of wheel event from&lt;br/&gt;
+        the dom. In safari this causes the wheel event to stop firing on the current frame, but it&lt;br/&gt;
+        then starts firing on the parent frame.&lt;/p&gt;
+&lt;/div&gt;
+&lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
+&lt;script&gt;
+description(&quot;Tests that iframe doesn't consume wheel events when scrolling a select in an iframe.&quot;);
+&lt;/script&gt;
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (188919 => 188920)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/ChangeLog        2015-08-25 18:03:00 UTC (rev 188920)
</span><span class="lines">@@ -1,3 +1,26 @@
</span><ins>+2015-08-24  Brent Fulgham  &lt;bfulgham@apple.com&gt;
+
+        Wheel events stop propagating when target element is removed from DOM
+        https://bugs.webkit.org/show_bug.cgi?id=148384
+        &lt;rdar://problem/19732211&gt;
+
+        Reviewed by David Hyatt.
+
+        Tested by tiled-drawing/scrolling/latched-to-deleted-node.html
+
+        We need to reset our latching state if the targeted node is removed from the DOM.
+        Add a check in 'platformPrepareForWheelEvents' that checks if the expected latching
+        target node was already removed from the DOM. If it was, we should not send events
+        to it, and should reset latching state so we can attach to the next relevant node.
+
+        * dom/Element.cpp:
+        (WebCore::Element::removedFrom): Remove any latched wheel event state objects that
+        match the current element.
+        * page/MainFrame.cpp:
+        (WebCore::MainFrame::removeLatchingStateForTarget): Remove any latched wheel event
+        state structures that match the passed wheel event target.
+        * page/MainFrame.h:
+
</ins><span class="cx"> 2015-08-25  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix crash due to search field disappearing when showing results menu
</span></span></pre></div>
<a id="trunkSourceWebCoredomElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Element.cpp (188919 => 188920)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Element.cpp        2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/dom/Element.cpp        2015-08-25 18:03:00 UTC (rev 188920)
</span><span class="lines">@@ -56,6 +56,7 @@
</span><span class="cx"> #include &quot;IdTargetObserverRegistry.h&quot;
</span><span class="cx"> #include &quot;InsertionPoint.h&quot;
</span><span class="cx"> #include &quot;KeyboardEvent.h&quot;
</span><ins>+#include &quot;MainFrame.h&quot;
</ins><span class="cx"> #include &quot;MutationObserverInterestGroup.h&quot;
</span><span class="cx"> #include &quot;MutationRecord.h&quot;
</span><span class="cx"> #include &quot;NodeRenderStyle.h&quot;
</span><span class="lines">@@ -71,6 +72,7 @@
</span><span class="cx"> #include &quot;SVGDocumentExtensions.h&quot;
</span><span class="cx"> #include &quot;SVGElement.h&quot;
</span><span class="cx"> #include &quot;SVGNames.h&quot;
</span><ins>+#include &quot;ScrollLatchingState.h&quot;
</ins><span class="cx"> #include &quot;SelectorQuery.h&quot;
</span><span class="cx"> #include &quot;Settings.h&quot;
</span><span class="cx"> #include &quot;StyleProperties.h&quot;
</span><span class="lines">@@ -1591,6 +1593,12 @@
</span><span class="cx"> 
</span><span class="cx">     if (hasPendingResources())
</span><span class="cx">         document().accessSVGExtensions().removeElementFromPendingResources(this);
</span><ins>+
+
+#if PLATFORM(MAC)
+    if (Frame* frame = document().frame())
+        frame-&gt;mainFrame().removeLatchingStateForTarget(*this);
+#endif
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void Element::unregisterNamedFlowContentElement()
</span></span></pre></div>
<a id="trunkSourceWebCorepageMainFramecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/MainFrame.cpp (188919 => 188920)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/MainFrame.cpp        2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/page/MainFrame.cpp        2015-08-25 18:03:00 UTC (rev 188920)
</span><span class="lines">@@ -124,6 +124,20 @@
</span><span class="cx"> {
</span><span class="cx">     m_latchingState.removeLast();
</span><span class="cx"> }
</span><ins>+
+void MainFrame::removeLatchingStateForTarget(Element&amp; targetNode)
+{
+    if (m_latchingState.isEmpty())
+        return;
+
+    m_latchingState.removeAllMatching([&amp;targetNode] (ScrollLatchingState&amp; state) {
+        auto* wheelElement = state.wheelEventElement();
+        if (!wheelElement)
+            return false;
+
+        return targetNode.isEqualNode(wheelElement);
+    });
+}
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorepageMainFrameh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/MainFrame.h (188919 => 188920)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/MainFrame.h        2015-08-25 18:01:58 UTC (rev 188919)
+++ trunk/Source/WebCore/page/MainFrame.h        2015-08-25 18:03:00 UTC (rev 188920)
</span><span class="lines">@@ -59,6 +59,7 @@
</span><span class="cx">     void pushNewLatchingState();
</span><span class="cx">     void popLatchingState();
</span><span class="cx">     void resetLatchingState();
</span><ins>+    void removeLatchingStateForTarget(Element&amp;);
</ins><span class="cx"> #endif // PLATFORM(MAC)
</span><span class="cx"> 
</span><span class="cx">     WEBCORE_EXPORT DiagnosticLoggingClient&amp; diagnosticLoggingClient() const;
</span></span></pre>
</div>
</div>

</body>
</html>