<!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>[177912] 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/177912">177912</a></dd>
<dt>Author</dt> <dd>bfulgham@apple.com</dd>
<dt>Date</dt> <dd>2015-01-05 10:11:56 -0800 (Mon, 05 Jan 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Mac] Cannot scroll when a non-scrollable iframe is contained inside a scrollable iframe
https://bugs.webkit.org/show_bug.cgi?id=139914
&lt;rdar://problem/18750910&gt;

Reviewed by Darin Adler.

Source/WebCore:

Tests: platform/mac/fast/scrolling/scroll-nested-iframe.html

The latching logic was breaking down when a non-scrollable iframe was the closest target of
a wheel event. EventHandler would latch to the enclosing scrollable region (in this case, the
non-scrollable iframe) and would eat scroll events, preventing anything from working.
        
The fix is as follows:
1. Modify the logic to understand a stack of latched states, so that we can discared 'invalid'
   latched states as we discover them.
2. Revise the latching logic so that it understands the case where the 'latched' node for wheel
   events is in a parent frame of the current wheel event target. For example, when the mouse is over
   an element in an unscrollable iframe that is contained within a scrollable iframe. We should
   be latched to the scrollable iframe so events go to the right place.

* page/EventHandler.cpp:
(WebCore::EventHandler::handleWheelEvent): Update to call new 'stack' versions of latch
state methods.
(WebCore::EventHandler::clearLatchedState): Ditto.
(WebCore::EventHandler::defaultWheelEventHandler): Ditto.
* page/MainFrame.cpp: Update to store a stack of latched states. Provide methods to control
the lifetime of the stack and its elements.
(WebCore::MainFrame::MainFrame):
(WebCore::MainFrame::latchingState):
(WebCore::MainFrame::pushNewLatchingState):
(WebCore::MainFrame::resetLatchingState):
(WebCore::MainFrame::popLatchingState):
* page/MainFrame.h:
* page/mac/EventHandlerMac.mm:
(WebCore::latchingIsLockedToParentOfThisFrame): Added helper function.
(WebCore::EventHandler::platformPrepareForWheelEvents): Update to use new 'stack' style latch
methods. Also, if we are latched to a frame that contains the frame we are currently evaluating,
don't replace the current event target with the latched targets because (1) they will be processed
in the enclosing scope when we leave this routine, and (2) if we do change targets to the latched
elements we create an infinite loop.
(WebCore::EventHandler::platformCompleteWheelEvent): We want to mark the element as having started
at the scroll limit regardless of what the wheel event handler returns as its success state.
(WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent): Revise to handle the new
stack-based latching methods.

LayoutTests:

* platform/mac/fast/scrolling/scroll-nested-iframe-expected.txt: Added.
* platform/mac/fast/scrolling/scroll-nested-iframe.html: Added.
* platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_inner.html: Added.
* platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_outer.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="#trunkSourceWebCorepageEventHandlercpp">trunk/Source/WebCore/page/EventHandler.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>
<li><a href="#trunkSourceWebCorepagemacEventHandlerMacmm">trunk/Source/WebCore/page/mac/EventHandlerMac.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/LayoutTests/platform/mac/fast/scrolling/resources/</li>
<li><a href="#trunkLayoutTestsplatformmacfastscrollingresourcesscroll_nested_iframe_test_innerhtml">trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_inner.html</a></li>
<li><a href="#trunkLayoutTestsplatformmacfastscrollingresourcesscroll_nested_iframe_test_outerhtml">trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_outer.html</a></li>
<li><a href="#trunkLayoutTestsplatformmacfastscrollingscrollnestediframeexpectedtxt">trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe-expected.txt</a></li>
<li><a href="#trunkLayoutTestsplatformmacfastscrollingscrollnestediframehtml">trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (177911 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-01-05 17:30:16 UTC (rev 177911)
+++ trunk/LayoutTests/ChangeLog        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2014-12-23  Brent Fulgham  &lt;bfulgham@apple.com&gt;
+
+        [Mac] Cannot scroll when a non-scrollable iframe is contained inside a scrollable iframe
+        https://bugs.webkit.org/show_bug.cgi?id=139914
+        &lt;rdar://problem/18750910&gt;
+
+        Reviewed by Darin Adler.
+
+        * platform/mac/fast/scrolling/scroll-nested-iframe-expected.txt: Added.
+        * platform/mac/fast/scrolling/scroll-nested-iframe.html: Added.
+        * platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_inner.html: Added.
+        * platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_outer.html: Added.
+
</ins><span class="cx"> 2015-01-05  peavo@outlook.com  &lt;peavo@outlook.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [WinCairo] Crash when font data pointer is null.
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacfastscrollingresourcesscroll_nested_iframe_test_innerhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_inner.html (0 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_inner.html                                (rev 0)
+++ trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_inner.html        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -0,0 +1,21 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+
+&lt;html&gt;
+&lt;head&gt;
+    &lt;style&gt;
+        
+        body {
+            height: 2000px;
+        }
+    &lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+
+&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&lt;/p&gt;
+
+&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&lt;/p&gt;
+
+&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.&lt;/p&gt;
+
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformmacfastscrollingresourcesscroll_nested_iframe_test_outerhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_outer.html (0 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_outer.html                                (rev 0)
+++ trunk/LayoutTests/platform/mac/fast/scrolling/resources/scroll_nested_iframe_test_outer.html        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -0,0 +1,21 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+
+&lt;html&gt;
+&lt;head&gt;
+    &lt;style&gt;
+    body {
+        background-image: repeating-linear-gradient(to bottom, white, silver 400px);
+    }
+    iframe {
+        position: relative;
+        width: 500px;
+        height: 3000px;
+        padding: 0;
+        background-color: gray;
+    }
+    &lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;iframe  id=&quot;nonscrollable_iframe&quot; src=&quot;resources/scroll_nested_iframe_test_inner.html&quot;&gt;&lt;/iframe&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformmacfastscrollingscrollnestediframeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe-expected.txt (0 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe-expected.txt                                (rev 0)
+++ trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe-expected.txt        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -0,0 +1,14 @@
</span><ins>+Put mouse inside the iframe (below) and flick downwards
+
+Tests that iframe does scroll when inner iframe is NOT scrollable.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+IFrame display height = 1000
+Mouse moved to (28, 88)
+PASS iframe did scroll.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformmacfastscrollingscrollnestediframehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe.html (0 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe.html                                (rev 0)
+++ trunk/LayoutTests/platform/mac/fast/scrolling/scroll-nested-iframe.html        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -0,0 +1,96 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;link rel=&quot;help&quot; href=&quot;http://www.w3.org/TR/DOM-Level-3-Events/#events-WheelEvent&quot;&gt;
+&lt;script src=&quot;../../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+    &lt;style&gt;
+    iframe {
+        width: 800px;
+        height: 1000px;
+    }
+    &lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;script&gt;
+
+var iframeTarget;
+var pageScrollPositionBefore;
+var iFrameScrollPositionBefore;
+var continueCount = 5;
+
+function checkForScroll() {
+
+    // The IFrame should not have scrolled at all.
+    var pageScrollPositionAfter = document.body.scrollTop;
+    var iFrameScrollPositionAfter = window.frames['scrollable_iframe'].document.body.scrollTop;
+
+    if (iFrameScrollPositionBefore != iFrameScrollPositionAfter)
+        testPassed(&quot;iframe did scroll.&quot;);
+    else
+        testFailed(&quot;iframe did NOT scroll.&quot;);
+
+    testRunner.notifyDone();
+}
+
+function scrollTest() {
+    // See where our IFrame lives:
+    pageScrollPositionBefore = document.body.scrollTop;
+
+    iframeTarget = document.getElementById('scrollable_iframe');
+
+    var iFrameBody = window.frames['scrollable_iframe'].document.body;
+    iFrameBody.scrollTop = iFrameBody.scrollHeight - iframeTarget.clientHeight - 100;
+
+    iFrameScrollPositionBefore = iFrameBody.scrollTop;
+
+    // Scroll the #source until we reach the #target.
+    var startPosX = iframeTarget.offsetLeft + 20;
+    debug(&quot;IFrame display height = &quot; + iframeTarget.clientHeight);
+    var startPosY = iframeTarget.offsetTop + 60; // One wheel turn before end.
+    eventSender.mouseMoveTo(startPosX, startPosY); // Make sure we are just outside the iFrame
+    debug(&quot;Mouse moved to (&quot; + startPosX + &quot;, &quot; + startPosY + &quot;)&quot;);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'began', 'none', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'changed', 'none', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'changed', 'none', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'ended', 'none', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'begin', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue', true);
+    eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end', true);
+    setTimeout(checkForScroll, 100);
+}
+
+function setupTopLevel() {
+
+    if (window.eventSender) {
+        testRunner.waitUntilDone();
+
+        setTimeout(scrollTest, 1000);
+    } 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;inside the IFrame, then use the mouse wheel or a two-finger swipe to scroll the IFrame to the bottom (and beyond).&lt;br/&gt;&quot;
+            + &quot;&lt;br/&gt;&lt;br/&gt;&quot;
+            + &quot;The test passes if you scroll far enough to see the row of END labels but the main page does 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;source&quot; style=&quot;height: 20px&quot;&gt;
+        Put mouse inside the iframe (below) and flick downwards
+    &lt;/div&gt;
+    &lt;iframe id=&quot;scrollable_iframe&quot; src=&quot;resources/scroll_nested_iframe_test_outer.html&quot; onload=&quot;setupTopLevel();&quot;&gt;
+    &lt;/iframe&gt;
+&lt;/div&gt;
+&lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
+&lt;script&gt;
+description(&quot;Tests that iframe does scroll when inner iframe is NOT scrollable.&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></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (177911 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-01-05 17:30:16 UTC (rev 177911)
+++ trunk/Source/WebCore/ChangeLog        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -1,3 +1,50 @@
</span><ins>+2014-12-23  Brent Fulgham  &lt;bfulgham@apple.com&gt;
+
+        [Mac] Cannot scroll when a non-scrollable iframe is contained inside a scrollable iframe
+        https://bugs.webkit.org/show_bug.cgi?id=139914
+        &lt;rdar://problem/18750910&gt;
+
+        Reviewed by Darin Adler.
+
+        Tests: platform/mac/fast/scrolling/scroll-nested-iframe.html
+
+        The latching logic was breaking down when a non-scrollable iframe was the closest target of
+        a wheel event. EventHandler would latch to the enclosing scrollable region (in this case, the
+        non-scrollable iframe) and would eat scroll events, preventing anything from working.
+        
+        The fix is as follows:
+        1. Modify the logic to understand a stack of latched states, so that we can discared 'invalid'
+           latched states as we discover them.
+        2. Revise the latching logic so that it understands the case where the 'latched' node for wheel
+           events is in a parent frame of the current wheel event target. For example, when the mouse is over
+           an element in an unscrollable iframe that is contained within a scrollable iframe. We should
+           be latched to the scrollable iframe so events go to the right place.
+
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::handleWheelEvent): Update to call new 'stack' versions of latch
+        state methods.
+        (WebCore::EventHandler::clearLatchedState): Ditto.
+        (WebCore::EventHandler::defaultWheelEventHandler): Ditto.
+        * page/MainFrame.cpp: Update to store a stack of latched states. Provide methods to control
+        the lifetime of the stack and its elements.
+        (WebCore::MainFrame::MainFrame):
+        (WebCore::MainFrame::latchingState):
+        (WebCore::MainFrame::pushNewLatchingState):
+        (WebCore::MainFrame::resetLatchingState):
+        (WebCore::MainFrame::popLatchingState):
+        * page/MainFrame.h:
+        * page/mac/EventHandlerMac.mm:
+        (WebCore::latchingIsLockedToParentOfThisFrame): Added helper function.
+        (WebCore::EventHandler::platformPrepareForWheelEvents): Update to use new 'stack' style latch
+        methods. Also, if we are latched to a frame that contains the frame we are currently evaluating,
+        don't replace the current event target with the latched targets because (1) they will be processed
+        in the enclosing scope when we leave this routine, and (2) if we do change targets to the latched
+        elements we create an infinite loop.
+        (WebCore::EventHandler::platformCompleteWheelEvent): We want to mark the element as having started
+        at the scroll limit regardless of what the wheel event handler returns as its success state.
+        (WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent): Revise to handle the new
+        stack-based latching methods.
+
</ins><span class="cx"> 2015-01-05  Darin Adler  &lt;darin@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Quick follow-up to last check-in, addressing review comments.
</span></span></pre></div>
<a id="trunkSourceWebCorepageEventHandlercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/EventHandler.cpp (177911 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/EventHandler.cpp        2015-01-05 17:30:16 UTC (rev 177911)
+++ trunk/Source/WebCore/page/EventHandler.cpp        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -2687,7 +2687,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     if (event.phase() == PlatformWheelEventPhaseNone &amp;&amp; event.momentumPhase() == PlatformWheelEventPhaseNone)
</span><del>-        m_frame.mainFrame().latchingState()-&gt;clear();
</del><ins>+        m_frame.mainFrame().resetLatchingState();
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     // FIXME: It should not be necessary to do this mutation here.
</span><span class="lines">@@ -2734,7 +2734,7 @@
</span><span class="cx"> void EventHandler::clearLatchedState()
</span><span class="cx"> {
</span><span class="cx"> #if PLATFORM(MAC)
</span><del>-    m_frame.mainFrame().latchingState()-&gt;clear();
</del><ins>+    m_frame.mainFrame().resetLatchingState();
</ins><span class="cx"> #endif
</span><span class="cx">     m_frame.mainFrame().wheelEventDeltaTracker()-&gt;endTrackingDeltas();
</span><span class="cx"> }
</span><span class="lines">@@ -2748,8 +2748,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(MAC)
</span><span class="cx">     ScrollLatchingState* latchedState = m_frame.mainFrame().latchingState();
</span><del>-    ASSERT(latchedState);
-    Element* stopElement = latchedState-&gt;previousWheelScrolledElement();
</del><ins>+    Element* stopElement = latchedState ? latchedState-&gt;previousWheelScrolledElement() : nullptr;
</ins><span class="cx"> 
</span><span class="cx">     // Workaround for scrolling issues &lt;rdar://problem/14758615&gt;.
</span><span class="cx">     if (m_frame.mainFrame().wheelEventDeltaTracker()-&gt;isTrackingDeltas())
</span><span class="lines">@@ -2767,7 +2766,7 @@
</span><span class="cx">         wheelEvent-&gt;setDefaultHandled();
</span><span class="cx">     
</span><span class="cx"> #if PLATFORM(MAC)
</span><del>-    if (!latchedState-&gt;wheelEventElement())
</del><ins>+    if (latchedState &amp;&amp; !latchedState-&gt;wheelEventElement())
</ins><span class="cx">         latchedState-&gt;setPreviousWheelScrolledElement(stopElement);
</span><span class="cx"> #endif
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorepageMainFramecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/MainFrame.cpp (177911 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/MainFrame.cpp        2015-01-05 17:30:16 UTC (rev 177911)
+++ trunk/Source/WebCore/page/MainFrame.cpp        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;MainFrame.h&quot;
</span><span class="cx"> 
</span><ins>+#include &quot;Element.h&quot;
</ins><span class="cx"> #include &quot;PageConfiguration.h&quot;
</span><span class="cx"> #include &quot;PageOverlayController.h&quot;
</span><span class="cx"> #include &quot;ScrollLatchingState.h&quot;
</span><span class="lines">@@ -41,7 +42,6 @@
</span><span class="cx">     : Frame(page, nullptr, *configuration.loaderClientForMainFrame)
</span><span class="cx">     , m_selfOnlyRefCount(0)
</span><span class="cx"> #if PLATFORM(MAC)
</span><del>-    , m_latchingState(std::make_unique&lt;ScrollLatchingState&gt;())
</del><span class="cx"> #if ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION)
</span><span class="cx">     , m_servicesOverlayController(std::make_unique&lt;ServicesOverlayController&gt;(*this))
</span><span class="cx"> #endif
</span><span class="lines">@@ -88,13 +88,29 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(MAC)
</span><ins>+ScrollLatchingState* MainFrame::latchingState()
+{
+    if (m_latchingState.isEmpty())
+        return nullptr;
+
+    return &amp;m_latchingState.last();
+}
+
+void MainFrame::pushNewLatchingState()
+{
+    m_latchingState.append(ScrollLatchingState());
+}
+
</ins><span class="cx"> void MainFrame::resetLatchingState()
</span><span class="cx"> {
</span><del>-    if (!m_latchingState)
-        return;
</del><ins>+    m_latchingState.clear();
+}
+    
+void MainFrame::popLatchingState()
+{
+    m_latchingState.removeLast();
+}
</ins><span class="cx"> 
</span><del>-    m_latchingState-&gt;clear();
-}
</del><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 (177911 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/MainFrame.h        2015-01-05 17:30:16 UTC (rev 177911)
+++ trunk/Source/WebCore/page/MainFrame.h        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> #define MainFrame_h
</span><span class="cx"> 
</span><span class="cx"> #include &quot;Frame.h&quot;
</span><ins>+#include &lt;wtf/Vector.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -54,7 +55,9 @@
</span><span class="cx">     ServicesOverlayController&amp; servicesOverlayController() { return *m_servicesOverlayController; }
</span><span class="cx"> #endif // ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION)
</span><span class="cx"> 
</span><del>-    ScrollLatchingState* latchingState() { return m_latchingState.get(); }
</del><ins>+    ScrollLatchingState* latchingState();
+    void pushNewLatchingState();
+    void popLatchingState();
</ins><span class="cx">     void resetLatchingState();
</span><span class="cx"> #endif // PLATFORM(MAC)
</span><span class="cx"> 
</span><span class="lines">@@ -68,7 +71,7 @@
</span><span class="cx">     unsigned m_selfOnlyRefCount;
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(MAC)
</span><del>-    std::unique_ptr&lt;ScrollLatchingState&gt; m_latchingState;
</del><ins>+    Vector&lt;ScrollLatchingState&gt; m_latchingState;
</ins><span class="cx"> #if ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION)
</span><span class="cx">     std::unique_ptr&lt;ServicesOverlayController&gt; m_servicesOverlayController;
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCorepagemacEventHandlerMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/mac/EventHandlerMac.mm (177911 => 177912)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/mac/EventHandlerMac.mm        2015-01-05 17:30:16 UTC (rev 177911)
+++ trunk/Source/WebCore/page/mac/EventHandlerMac.mm        2015-01-05 18:11:56 UTC (rev 177912)
</span><span class="lines">@@ -824,7 +824,24 @@
</span><span class="cx"> 
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><ins>+
+static bool latchingIsLockedToAncestorOfThisFrame(const Frame&amp; frame)
+{
+    ScrollLatchingState* latchedState = frame.mainFrame().latchingState();
+    if (!latchedState || !latchedState-&gt;frame())
+        return false;
+
+    if (&amp;frame == latchedState-&gt;frame())
+        return false;
+
+    for (Frame* ancestor = frame.tree().parent(); ancestor; ancestor-&gt;tree().parent()) {
+        if (ancestor == latchedState-&gt;frame())
+            return true;
+    }
</ins><span class="cx">     
</span><ins>+    return false;
+}
+
</ins><span class="cx"> void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&amp; wheelEvent, const HitTestResult&amp; result, RefPtr&lt;Element&gt;&amp; wheelEventTarget, RefPtr&lt;ContainerNode&gt;&amp; scrollableContainer, ScrollableArea*&amp; scrollableArea, bool&amp; isOverWidget)
</span><span class="cx"> {
</span><span class="cx">     FrameView* view = m_frame.view();
</span><span class="lines">@@ -852,8 +869,9 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     ScrollLatchingState* latchingState = m_frame.mainFrame().latchingState();
</span><del>-    ASSERT(latchingState);
</del><span class="cx">     if (wheelEvent.shouldConsiderLatching()) {
</span><ins>+        m_frame.mainFrame().pushNewLatchingState();
+        latchingState = m_frame.mainFrame().latchingState();
</ins><span class="cx">         if (scrollableArea &amp;&amp; scrollableContainer)
</span><span class="cx">             latchingState-&gt;setStartedGestureAtScrollLimit(scrolledToEdgeInDominantDirection(*scrollableContainer, *scrollableArea, wheelEvent.deltaX(), wheelEvent.deltaY()));
</span><span class="cx">         else
</span><span class="lines">@@ -868,10 +886,13 @@
</span><span class="cx">     } else if (wheelEvent.shouldResetLatching())
</span><span class="cx">         clearLatchedState();
</span><span class="cx"> 
</span><del>-    if (!wheelEvent.shouldResetLatching() &amp;&amp; latchingState-&gt;wheelEventElement()) {
</del><ins>+    if (!wheelEvent.shouldResetLatching() &amp;&amp; latchingState &amp;&amp; latchingState-&gt;wheelEventElement()) {
</ins><span class="cx">         if (latchingIsLockedToPlatformFrame(m_frame))
</span><span class="cx">             return;
</span><span class="cx"> 
</span><ins>+        if (latchingIsLockedToAncestorOfThisFrame(m_frame))
+            return;
+
</ins><span class="cx">         wheelEventTarget = latchingState-&gt;wheelEventElement();
</span><span class="cx">         isOverWidget = latchingState-&gt;widgetIsLatched();
</span><span class="cx">     }
</span><span class="lines">@@ -907,17 +928,16 @@
</span><span class="cx">     FrameView* view = m_frame.view();
</span><span class="cx"> 
</span><span class="cx">     ScrollLatchingState* latchingState = m_frame.mainFrame().latchingState();
</span><del>-    ASSERT(latchingState);
-    if (wheelEvent.useLatchedEventElement() &amp;&amp; latchingState-&gt;scrollableContainer()) {
</del><ins>+    if (wheelEvent.useLatchedEventElement() &amp;&amp; latchingState &amp;&amp; latchingState-&gt;scrollableContainer()) {
</ins><span class="cx">         view = frameViewForLatchingState(m_frame, latchingState);
</span><span class="cx">         if (!view || !view-&gt;frame().isMainFrame()) {
</span><span class="cx">             bool didHandleWheelEvent = view &amp;&amp; view-&gt;wheelEvent(wheelEvent);
</span><del>-            if (!didHandleWheelEvent &amp;&amp; scrollableContainer == latchingState-&gt;scrollableContainer()) {
</del><ins>+            if (scrollableContainer == latchingState-&gt;scrollableContainer()) {
</ins><span class="cx">                 // If we are just starting a scroll event, and have nowhere left to scroll, allow
</span><span class="cx">                 // the enclosing frame to handle the scroll.
</span><span class="cx">                 didHandleWheelEvent = !latchingState-&gt;startedGestureAtScrollLimit();
</span><span class="cx">                 if (!didHandleWheelEvent)
</span><del>-                    latchingState-&gt;setFrame(nullptr);
</del><ins>+                    m_frame.mainFrame().popLatchingState();
</ins><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             // If the platform widget is handling the event, we always want to return false
</span><span class="lines">@@ -950,7 +970,9 @@
</span><span class="cx">         return true;
</span><span class="cx"> 
</span><span class="cx">     ScrollLatchingState* latchingState = m_frame.mainFrame().latchingState();
</span><del>-    ASSERT(latchingState);
</del><ins>+    if (!latchingState)
+        return false;
+
</ins><span class="cx">     if (wheelEvent.useLatchedEventElement() &amp;&amp; latchingState-&gt;scrollableContainer() &amp;&amp; scrollableContainer == latchingState-&gt;scrollableContainer())
</span><span class="cx">         return !latchingState-&gt;startedGestureAtScrollLimit();
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>