Latching in iframes is not working as expected
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Sep 2014 01:10:50 +0000 (01:10 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Sep 2014 01:10:50 +0000 (01:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=136729
<rdar://problem/18370549>

Reviewed by Simon Fraser.

Source/WebCore:

Test: platform/mac/fast/scrolling/scrolling-iframe-100pct.html

Correct latching behavior by moving the concept of latching from the event handler to the main frame.
The event handlers are per-document, and can improperly latch to the iframe element (rather than the
scrollable content of the iframe) resulting in incorrect behavior.

Also move the wheel event delta tracking to the main frame, as this is similarly "top-level" in nature.

* WebCore.vcxproj/WebCore.vcxproj: Add new LatchedState class.
* WebCore.vcxproj/WebCore.vcxproj.filters: Ditto.
* WebCore.xcodeproj/project.pbxproj: Ditto.
* page/EventHandler.cpp:
(WebCore::EventHandler::EventHandler): Update constructor after moving some members to the new
LatchedState object.
(WebCore::EventHandler::clear): Call 'clear' on the LatchedState class.
(WebCore::EventHandler::platformRecordWheelEvent):  Update for new LatchedState class.
(WebCore::EventHandler::handleWheelEvent): Ditto.
(WebCore::EventHandler::clearLatchedState): Ditto.
(WebCore::EventHandler::defaultWheelEventHandler): Ditto.
* page/EventHandler.h:
* page/LatchedState.cpp: Added.
(WebCore::LatchedState::LatchedState):
(WebCore::LatchedState::~LatchedState):
(WebCore::LatchedState::clear):
(WebCore::LatchedState::setWheelEventElement):
(WebCore::LatchedState::setWidgetIsLatched):
(WebCore::LatchedState::setPreviousWheelScrolledElement):
(WebCore::LatchedState::setScrollableContainer):
* page/LatchedState.h: Added.
(WebCore::LatchedState::wheelEventElement):
(WebCore::LatchedState::frame):
(WebCore::LatchedState::setFrame):
(WebCore::LatchedState::widgetIsLatched):
(WebCore::LatchedState::previousWheelScrolledElement):
(WebCore::LatchedState::scrollableContainer):
(WebCore::LatchedState::startedGestureAtScrollLimit):
(WebCore::LatchedState::setStartedGestureAtScrollLimit):
* page/MainFrame.cpp:
(WebCore::MainFrame::MainFrame): Update for new members (LatchedState and WheelEventDeltaTracker)
* page/MainFrame.h:
* page/mac/EventHandlerMac.mm:
(WebCore::EventHandler::platformPrepareForWheelEvents): Use mainFrame-located latching information.
(WebCore::EventHandler::platformRecordWheelEvent): Use mainFrame-located wheel event delta tracking.
(WebCore::EventHandler::platformCompleteWheelEvent): Make sure to use the latched frame as well as
the latched element so that events get routed properly.
(WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent): Update for new mainFrame
location for latched state information.

LayoutTests:

* platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug-expected.txt: Added.
* platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@173784 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/page/EventHandler.cpp
Source/WebCore/page/EventHandler.h
Source/WebCore/page/MainFrame.cpp
Source/WebCore/page/MainFrame.h
Source/WebCore/page/mac/EventHandlerMac.mm
Source/WebCore/page/scrolling/ScrollLatchingState.cpp [new file with mode: 0644]
Source/WebCore/page/scrolling/ScrollLatchingState.h [new file with mode: 0644]

index 5b6a21c..79a2665 100644 (file)
@@ -1,3 +1,14 @@
+2014-09-19  Brent Fulgham  <bfulgham@apple.com>
+
+        Latching in iframes is not working as expected
+        https://bugs.webkit.org/show_bug.cgi?id=136729
+        <rdar://problem/18370549>
+
+        Reviewed by Simon Fraser.
+
+        * platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug-expected.txt: Added.
+        * platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug.html: Added.
+
 2014-09-19  Jer Noble  <jer.noble@apple.com>
 
         Videos with controls enabled never receive 'dragstart' events.
diff --git a/LayoutTests/platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug-expected.txt b/LayoutTests/platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug-expected.txt
new file mode 100644 (file)
index 0000000..9af1232
--- /dev/null
@@ -0,0 +1,13 @@
+
+Tests that iframe doesn't pass wheel events to main frame when scrolling at bottom
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+iframe display height = 150
+Mouse moved to (28, 116)
+PASS iframe received wheel events.
+
diff --git a/LayoutTests/platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug.html b/LayoutTests/platform/mac/fast/scrolling/scroll-iframe-webkit1-latching-bug.html
new file mode 100644 (file)
index 0000000..ef5839f
--- /dev/null
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <link rel="help" href="http://www.w3.org/TR/DOM-Level-3-Events/#events-WheelEvent">
+        <script src="../../../../resources/js-test-pre.js"></script>
+    </head>
+    <body id="parent" style="height: 2000px">
+        <script>
+        var iframeTarget;
+        var pageScrollPositionBefore;
+        var iframeScrollPositionBefore;
+        var continueCount = 5;
+
+        function checkForScroll() {
+
+            // The iframe should have scrolled, but not the main page.
+            var pageScrollPositionAfter = document.body.scrollTop;
+            var iframeScrollPositionAfter = window.frames['target'].document.body.scrollTop;
+
+            if (iframeScrollPositionBefore != iframeScrollPositionAfter)
+                testPassed("iframe received wheel events.");
+            else
+                testFailed("iframe did not receive wheel events.");
+
+            testRunner.notifyDone();
+        }
+
+        function scrollTest() {
+            // See where our iframe lives:
+            pageScrollPositionBefore = document.body.scrollTop;
+
+            iframeTarget = document.getElementById('target');
+
+            var iframeBody = window.frames['target'].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("iframe display height = " + iframeTarget.clientHeight);
+            var startPosY = iframeTarget.offsetTop + iframeTarget.clientHeight - 42; // One wheel turn before end.
+            eventSender.mouseMoveTo(startPosX, startPosY); // Make sure we are just outside the iframe
+            debug("Mouse moved to (" + startPosX + ", " + startPosY + ")");
+            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 = "<p>This test is better run under DumpRenderTree. To manually test it, place the mouse pointer<br/>"
+                    + "inside the iframe, then use the mouse wheel or a two-finger swipe to scroll the iframe to the bottom (and beyond).<br/>"
+                    + "<br/><br/>"
+                    + "The test passes if you scroll far enough to see the row of END labels but the main page does not scroll.</p>";
+                messageLocation.appendChild(message);
+            }
+        }
+        </script>
+        <iframe id="target" name="target" style="border:solid 1px green; height: 150px; width: 300px;" 
+            src= "data:text/html,
+                <html style='height: 100%'>
+                    <body style='height: 100%'>
+                        <div style='height: 100px; width: 200px'>
+                            <div style='overflow-y: auto; overflow-x: hidden;'>
+                            TOP TOP TOP TOP TOP TOP TOP TOP TOP TOP TOP TOP TOP TOP<br/><br/>
+                            This should still be visible inside the frame after you scroll down
+                            <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
+                            <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
+                            <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
+                            This should NOT be visible inside the frame after you scroll down<br/>
+                            <br/>
+                            END END END END END END END END END END END END END
+                            </div>
+                        </div>
+                    </body>
+                </html>"
+            onload="setupTopLevel();">
+        </iframe>
+        <div id="console"></div>
+        <script>
+        description("Tests that iframe doesn't pass wheel events to main frame when scrolling at bottom");
+        </script>
+        <script src="../../../../resources/js-test-post.js"></script>
+    </body>
+</html>
index b46cec3..392f343 100644 (file)
@@ -1972,6 +1972,7 @@ set(WebCore_SOURCES
     page/animation/ImplicitAnimation.cpp
     page/animation/KeyframeAnimation.cpp
 
+    page/scrolling/ScrollLatchingState.cpp
     page/scrolling/ScrollingConstraints.cpp
     page/scrolling/ScrollingCoordinator.cpp
     page/scrolling/ScrollingStateFixedNode.cpp
index a1fecab..44f6106 100644 (file)
@@ -1,3 +1,59 @@
+2014-09-19  Brent Fulgham  <bfulgham@apple.com>
+
+        Latching in iframes is not working as expected
+        https://bugs.webkit.org/show_bug.cgi?id=136729
+        <rdar://problem/18370549>
+
+        Reviewed by Simon Fraser.
+
+        Test: platform/mac/fast/scrolling/scrolling-iframe-100pct.html
+
+        Correct latching behavior by moving the concept of latching from the event handler to the main frame.
+        The event handlers are per-document, and can improperly latch to the iframe element (rather than the
+        scrollable content of the iframe) resulting in incorrect behavior.
+
+        Also move the wheel event delta tracking to the main frame, as this is similarly "top-level" in nature.
+
+        * WebCore.vcxproj/WebCore.vcxproj: Add new LatchedState class.
+        * WebCore.vcxproj/WebCore.vcxproj.filters: Ditto.
+        * WebCore.xcodeproj/project.pbxproj: Ditto.
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::EventHandler): Update constructor after moving some members to the new
+        LatchedState object.
+        (WebCore::EventHandler::clear): Call 'clear' on the LatchedState class.
+        (WebCore::EventHandler::platformRecordWheelEvent):  Update for new LatchedState class.
+        (WebCore::EventHandler::handleWheelEvent): Ditto.
+        (WebCore::EventHandler::clearLatchedState): Ditto.
+        (WebCore::EventHandler::defaultWheelEventHandler): Ditto.
+        * page/EventHandler.h:
+        * page/LatchedState.cpp: Added.
+        (WebCore::LatchedState::LatchedState):
+        (WebCore::LatchedState::~LatchedState):
+        (WebCore::LatchedState::clear):
+        (WebCore::LatchedState::setWheelEventElement):
+        (WebCore::LatchedState::setWidgetIsLatched):
+        (WebCore::LatchedState::setPreviousWheelScrolledElement):
+        (WebCore::LatchedState::setScrollableContainer):
+        * page/LatchedState.h: Added.
+        (WebCore::LatchedState::wheelEventElement):
+        (WebCore::LatchedState::frame):
+        (WebCore::LatchedState::setFrame):
+        (WebCore::LatchedState::widgetIsLatched):
+        (WebCore::LatchedState::previousWheelScrolledElement):
+        (WebCore::LatchedState::scrollableContainer):
+        (WebCore::LatchedState::startedGestureAtScrollLimit):
+        (WebCore::LatchedState::setStartedGestureAtScrollLimit):
+        * page/MainFrame.cpp:
+        (WebCore::MainFrame::MainFrame): Update for new members (LatchedState and WheelEventDeltaTracker)
+        * page/MainFrame.h:
+        * page/mac/EventHandlerMac.mm:
+        (WebCore::EventHandler::platformPrepareForWheelEvents): Use mainFrame-located latching information.
+        (WebCore::EventHandler::platformRecordWheelEvent): Use mainFrame-located wheel event delta tracking.
+        (WebCore::EventHandler::platformCompleteWheelEvent): Make sure to use the latched frame as well as
+        the latched element so that events get routed properly.
+        (WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent): Update for new mainFrame
+        location for latched state information.
+
 2014-09-19  Chris Dumez  <cdumez@apple.com>
 
         Minimize virtual function calls in MarkupAccumulator
index 917c688..7eb5cf0 100644 (file)
     <ClCompile Include="..\page\PerformanceTiming.cpp" />
     <ClCompile Include="..\page\PrintContext.cpp" />
     <ClCompile Include="..\page\Screen.cpp" />
+    <ClCompile Include="..\page\scrolling\ScrollLatchingState.cpp" />
     <ClCompile Include="..\page\scrolling\ScrollingConstraints.cpp" />
     <ClCompile Include="..\page\scrolling\ScrollingCoordinator.cpp" />
     <ClCompile Include="..\page\SecurityOrigin.cpp" />
     <ClInclude Include="..\page\PrintContext.h" />
     <ClInclude Include="..\page\Screen.h" />
     <ClInclude Include="..\page\scrolling\coordinatedgraphics\ScrollingCoordinatorCoordinatedGraphics.h" />
+    <ClInclude Include="..\page\scrolling\ScrollLatchingState.h" />
     <ClInclude Include="..\page\scrolling\ScrollingConstraints.h" />
     <ClInclude Include="..\page\scrolling\ScrollingCoordinator.h" />
     <ClInclude Include="..\page\scrolling\ScrollingStateFixedNode.h" />
index 6d14988..8681006 100644 (file)
     <ClCompile Include="..\page\Screen.cpp">
       <Filter>page</Filter>
     </ClCompile>
+    <ClCompile Include="..\page\scrolling\ScrollLatchingState.cpp">
+      <Filter>page</Filter>
+    </ClCompile>
     <ClCompile Include="..\page\scrolling\ScrollingConstraints.cpp">
       <Filter>page</Filter>
     </ClCompile>
     <ClInclude Include="..\page\Screen.h">
       <Filter>page</Filter>
     </ClInclude>
+    <ClInclude Include="..\page\scrolling\ScrollLatchingState.h">
+      <Filter>page</Filter>
+    </ClInclude>
     <ClInclude Include="..\page\scrolling\ScrollingConstraints.h">
       <Filter>page</Filter>
     </ClInclude>
index 5b4d38e..87d2bbc 100644 (file)
                7AA3A6A4194B5C22001CBD24 /* TileCoverageMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AA3A6A2194B5C22001CBD24 /* TileCoverageMap.h */; };
                7AABA25914BC613300AA9A11 /* DOMEditor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AABA25714BC613300AA9A11 /* DOMEditor.cpp */; };
                7AABA25A14BC613300AA9A11 /* DOMEditor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AABA25814BC613300AA9A11 /* DOMEditor.h */; };
+               7AAFE8CF19CB8672000F56D8 /* ScrollLatchingState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AAFE8CD19CB8672000F56D8 /* ScrollLatchingState.cpp */; };
+               7AAFE8D019CB8672000F56D8 /* ScrollLatchingState.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AAFE8CE19CB8672000F56D8 /* ScrollLatchingState.h */; };
                7AB0B1C01211A62200A76940 /* InspectorDatabaseAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0B1BE1211A62200A76940 /* InspectorDatabaseAgent.cpp */; };
                7AB0B1C11211A62200A76940 /* InspectorDatabaseAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AB0B1BF1211A62200A76940 /* InspectorDatabaseAgent.h */; };
                7ACD88D314C08BD60084EDD2 /* InspectorIndexedDBAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD88D114C08BD60084EDD2 /* InspectorIndexedDBAgent.cpp */; };
                7AA3A6A2194B5C22001CBD24 /* TileCoverageMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TileCoverageMap.h; path = ca/TileCoverageMap.h; sourceTree = "<group>"; };
                7AABA25714BC613300AA9A11 /* DOMEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMEditor.cpp; sourceTree = "<group>"; };
                7AABA25814BC613300AA9A11 /* DOMEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMEditor.h; sourceTree = "<group>"; };
+               7AAFE8CD19CB8672000F56D8 /* ScrollLatchingState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollLatchingState.cpp; sourceTree = "<group>"; };
+               7AAFE8CE19CB8672000F56D8 /* ScrollLatchingState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollLatchingState.h; sourceTree = "<group>"; };
                7AB0B1BE1211A62200A76940 /* InspectorDatabaseAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorDatabaseAgent.cpp; sourceTree = "<group>"; };
                7AB0B1BF1211A62200A76940 /* InspectorDatabaseAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorDatabaseAgent.h; sourceTree = "<group>"; };
                7ACD88D114C08BD60084EDD2 /* InspectorIndexedDBAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorIndexedDBAgent.cpp; sourceTree = "<group>"; };
                                1AF62EE214DA22A70041556C /* mac */,
                                0FFD4D5E18651FA300512F6E /* AsyncScrollingCoordinator.cpp */,
                                0FFD4D5F18651FA300512F6E /* AsyncScrollingCoordinator.h */,
+                               7AAFE8CD19CB8672000F56D8 /* ScrollLatchingState.cpp */,
+                               7AAFE8CE19CB8672000F56D8 /* ScrollLatchingState.h */,
                                0F605AEA15F94848004DF0C0 /* ScrollingConstraints.cpp */,
                                0F605AEB15F94848004DF0C0 /* ScrollingConstraints.h */,
                                1AF62EE414DA22A70041556C /* ScrollingCoordinator.cpp */,
                                BCEC01C30C274DDD009F4EC9 /* JSScreen.h in Headers */,
                                FDA15ECE12B03F61003A583A /* JSScriptProcessorNode.h in Headers */,
                                9FA37EFB1172FDA600C4CD55 /* JSScriptProfile.h in Headers */,
+                               7AAFE8D019CB8672000F56D8 /* ScrollLatchingState.h in Headers */,
                                9FA37EFD1172FDA600C4CD55 /* JSScriptProfileNode.h in Headers */,
                                41D07A7F0FF935CA0095EDCE /* JSSharedWorker.h in Headers */,
                                41D1690610238B66009BC827 /* JSSharedWorkerGlobalScope.h in Headers */,
                                B27535630B053814002CE64F /* PathCG.cpp in Sources */,
                                A88DD4890B4629B000C02990 /* PathTraversalState.cpp in Sources */,
                                A8FA6E5E0E4CFDED00D5CF49 /* Pattern.cpp in Sources */,
+                               7AAFE8CF19CB8672000F56D8 /* ScrollLatchingState.cpp in Sources */,
                                A80A38FE0E50CC8200A25EBC /* PatternCG.cpp in Sources */,
                                B27535640B053814002CE64F /* PDFDocumentImage.cpp in Sources */,
                                2D6E468417D660F500ECF8BB /* PDFDocumentImageMac.mm in Sources */,
index 248ab2b..a8417fd 100644 (file)
@@ -83,6 +83,7 @@
 #include "SVGNames.h"
 #include "SVGUseElement.h"
 #include "ScrollAnimator.h"
+#include "ScrollLatchingState.h"
 #include "Scrollbar.h"
 #include "Settings.h"
 #include "ShadowRoot.h"
@@ -401,12 +402,9 @@ EventHandler::EventHandler(Frame& frame)
 #endif
     , m_mousePositionIsUnknown(true)
     , m_mouseDownTimestamp(0)
-    , m_recentWheelEventDeltaTracker(std::make_unique<WheelEventDeltaTracker>())
-    , m_widgetIsLatched(false)
 #if PLATFORM(COCOA)
     , m_mouseDownView(nil)
     , m_sendingEventToSubview(false)
-    , m_startedGestureAtScrollLimit(false)
 #if !PLATFORM(IOS)
     , m_activationEventNumber(-1)
 #endif // !PLATFORM(IOS)
@@ -490,11 +488,9 @@ void EventHandler::clear()
     m_mousePressed = false;
     m_capturesDragging = false;
     m_capturingMouseEventsElement = nullptr;
-    m_latchedWheelEventElement = nullptr;
-#if PLATFORM(COCOA)
-    m_latchedScrollableContainer = nullptr;
+#if PLATFORM(MAC)
+    m_frame.mainFrame().resetLatchingState();
 #endif
-    m_previousWheelScrolledElement = nullptr;
 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
     m_originatingTouchPointTargets.clear();
     m_originatingTouchPointDocument.clear();
@@ -2644,7 +2640,7 @@ void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, cons
 
 void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event)
 {
-    m_recentWheelEventDeltaTracker->recordWheelEventDelta(event);
+    m_frame.mainFrame().wheelEventDeltaTracker()->recordWheelEventDelta(event);
 }
 
 bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, Element*, ContainerNode*, ScrollableArea*)
@@ -2689,13 +2685,12 @@ bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event)
     bool isOverWidget = result.isOverWidget();
     platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget);
 
-#if PLATFORM(COCOA)
+#if PLATFORM(MAC)
     if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone)
-#endif
     {
-        m_latchedWheelEventElement = nullptr;
-        m_previousWheelScrolledElement = nullptr;
+        m_frame.mainFrame().latchingState()->clear();
     }
+#endif
 
     // FIXME: It should not be necessary to do this mutation here.
     // Instead, the handlers should know convert vertical scrolls appropriately.
@@ -2740,12 +2735,10 @@ bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event)
 
 void EventHandler::clearLatchedState()
 {
-    m_latchedWheelEventElement = nullptr;
-#if PLATFORM(COCOA)
-    m_latchedScrollableContainer = nullptr;
+#if PLATFORM(MAC)
+    m_frame.mainFrame().latchingState()->clear();
 #endif
-    m_widgetIsLatched = false;
-    m_previousWheelScrolledElement = nullptr;
+    m_frame.mainFrame().wheelEventDeltaTracker()->endTrackingDeltas();
 }
 
 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent)
@@ -2753,13 +2746,18 @@ void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEv
     if (!startNode || !wheelEvent)
         return;
     
-    Element* stopElement = m_previousWheelScrolledElement.get();
     DominantScrollGestureDirection dominantDirection = DominantScrollGestureDirection::None;
 
+#if PLATFORM(MAC)
+    ScrollLatchingState* latchedState = m_frame.mainFrame().latchingState();
+    ASSERT(latchedState);
+    Element* stopElement = latchedState->previousWheelScrolledElement();
+
     // Workaround for scrolling issues <rdar://problem/14758615>.
-#if PLATFORM(COCOA)
-    if (m_recentWheelEventDeltaTracker->isTrackingDeltas())
-        dominantDirection = m_recentWheelEventDeltaTracker->dominantScrollGestureDirection();
+    if (m_frame.mainFrame().wheelEventDeltaTracker()->isTrackingDeltas())
+        dominantDirection = m_frame.mainFrame().wheelEventDeltaTracker()->dominantScrollGestureDirection();
+#else
+    Element* stopElement = nullptr;
 #endif
     
     // Break up into two scrolls if we need to.  Diagonal movement on 
@@ -2770,8 +2768,10 @@ void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEv
     if (dominantDirection != DominantScrollGestureDirection::Horizontal && handleWheelEventInAppropriateEnclosingBoxForSingleAxis(startNode, wheelEvent, &stopElement, ScrollEventAxis::Vertical))
         wheelEvent->setDefaultHandled();
     
-    if (!m_latchedWheelEventElement)
-        m_previousWheelScrolledElement = stopElement;
+#if PLATFORM(MAC)
+    if (!latchedState->wheelEventElement())
+        latchedState->setPreviousWheelScrolledElement(stopElement);
+#endif
 }
 
 #if ENABLE(CONTEXT_MENUS)
index 1b026e2..0086363 100644 (file)
@@ -535,15 +535,8 @@ private:
     double m_mouseDownTimestamp;
     PlatformMouseEvent m_mouseDown;
 
-    std::unique_ptr<WheelEventDeltaTracker> m_recentWheelEventDeltaTracker;
-    RefPtr<Element> m_latchedWheelEventElement;
-    bool m_widgetIsLatched;
-
-    RefPtr<Element> m_previousWheelScrolledElement;
-
 #if PLATFORM(COCOA)
     NSView *m_mouseDownView;
-    RefPtr<ContainerNode> m_latchedScrollableContainer;
     bool m_sendingEventToSubview;
     bool m_startedGestureAtScrollLimit;
 #if !PLATFORM(IOS)
index 054a062..828bd23 100644 (file)
@@ -1,37 +1,47 @@
 /*
-
-Copyright (C) 2013 Apple Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1.  Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-2.  Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in the
-    documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*/
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 #include "config.h"
 #include "MainFrame.h"
 
+#include "ScrollLatchingState.h"
+#include "WheelEventDeltaTracker.h"
+
 namespace WebCore {
 
 inline MainFrame::MainFrame(Page& page, FrameLoaderClient& client)
     : Frame(page, nullptr, client)
     , m_selfOnlyRefCount(0)
+#if PLATFORM(MAC)
+    , m_latchingState(std::make_unique<ScrollLatchingState>())
+#endif
+    , m_recentWheelEventDeltaTracker(std::make_unique<WheelEventDeltaTracker>())
+{
+}
+
+MainFrame::~MainFrame()
 {
 }
 
@@ -66,4 +76,14 @@ void MainFrame::dropChildren()
         tree().removeChild(child);
 }
 
+#if PLATFORM(MAC)
+void MainFrame::resetLatchingState()
+{
+    if (!m_latchingState)
+        return;
+
+    m_latchingState->clear();
+}
+#endif
+
 }
index 2e7828a..8a1476b 100644 (file)
@@ -1,28 +1,27 @@
 /*
-
-Copyright (C) 2013 Apple Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1.  Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-2.  Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in the
-    documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
-EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*/
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 #ifndef MainFrame_h
 #define MainFrame_h
@@ -31,19 +30,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace WebCore {
 
+class ScrollLatchingState;
+class WheelEventDeltaTracker;
+
 class MainFrame final : public Frame {
 public:
     static RefPtr<MainFrame> create(Page&, FrameLoaderClient&);
 
+    virtual ~MainFrame();
+
     void selfOnlyRef();
     void selfOnlyDeref();
 
+    WheelEventDeltaTracker* wheelEventDeltaTracker() { return m_recentWheelEventDeltaTracker.get(); }
+
+#if PLATFORM(MAC)
+    ScrollLatchingState* latchingState() { return m_latchingState.get(); }
+    void resetLatchingState();
+#endif
+
 private:
     MainFrame(Page&, FrameLoaderClient&);
 
     void dropChildren();
 
     unsigned m_selfOnlyRefCount;
+
+#if PLATFORM(MAC)
+    std::unique_ptr<ScrollLatchingState> m_latchingState;
+#endif
+    std::unique_ptr<WheelEventDeltaTracker> m_recentWheelEventDeltaTracker;
 };
 
 inline bool Frame::isMainFrame() const
index e7cf728..06e6e93 100644 (file)
@@ -47,6 +47,7 @@
 #include "RenderListBox.h"
 #include "RenderWidget.h"
 #include "RuntimeApplicationChecks.h"
+#include "ScrollLatchingState.h"
 #include "ScrollableArea.h"
 #include "Scrollbar.h"
 #include "Settings.h"
@@ -815,6 +816,18 @@ static bool eventTargetIsPlatformWidget(Element* eventTarget)
     return widget->platformWidget();
 }
 
+static bool latchingIsLockedToPlatformFrame(const Frame& frame)
+{
+    ScrollLatchingState* latchedState = frame.mainFrame().latchingState();
+    if (!latchedState)
+        return false;
+
+    if (frameHasPlatformWidget(frame) && &frame != latchedState->frame())
+        return true;
+
+    return false;
+}
+    
 void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent& wheelEvent, const HitTestResult& result, RefPtr<Element>& wheelEventTarget, RefPtr<ContainerNode>& scrollableContainer, ScrollableArea*& scrollableArea, bool& isOverWidget)
 {
     FrameView* view = m_frame.view();
@@ -841,25 +854,29 @@ void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent& wheel
         }
     }
     
+    ScrollLatchingState* latchingState = m_frame.mainFrame().latchingState();
+    ASSERT(latchingState);
     if (wheelEvent.shouldConsiderLatching()) {
         if (scrollableArea && scrollableContainer)
-            m_startedGestureAtScrollLimit = scrolledToEdgeInDominantDirection(*scrollableContainer, *scrollableArea, wheelEvent.deltaX(), wheelEvent.deltaY());
+            latchingState->setStartedGestureAtScrollLimit(scrolledToEdgeInDominantDirection(*scrollableContainer, *scrollableArea, wheelEvent.deltaX(), wheelEvent.deltaY()));
         else
-            m_startedGestureAtScrollLimit = false;
-        m_latchedWheelEventElement = wheelEventTarget;
+            latchingState->setStartedGestureAtScrollLimit(false);
+        latchingState->setWheelEventElement(wheelEventTarget);
+        latchingState->setFrame(&m_frame);
         // FIXME: What prevents us from deleting this scrollable container while still holding a pointer to it?
-        m_latchedScrollableContainer = scrollableContainer;
-        m_widgetIsLatched = result.isOverWidget();
-        isOverWidget = m_widgetIsLatched;
-        m_recentWheelEventDeltaTracker->beginTrackingDeltas();
-    } else if (wheelEvent.shouldResetLatching()) {
+        latchingState->setScrollableContainer(scrollableContainer);
+        latchingState->setWidgetIsLatched(result.isOverWidget());
+        isOverWidget = latchingState->widgetIsLatched();
+        m_frame.mainFrame().wheelEventDeltaTracker()->beginTrackingDeltas();
+    } else if (wheelEvent.shouldResetLatching())
         clearLatchedState();
-        m_recentWheelEventDeltaTracker->endTrackingDeltas();
-    }
-    
-    if (!wheelEvent.shouldResetLatching() && m_latchedWheelEventElement) {
-        wheelEventTarget = m_latchedWheelEventElement.get();
-        isOverWidget = m_widgetIsLatched;
+
+    if (!wheelEvent.shouldResetLatching() && latchingState->wheelEventElement()) {
+        if (latchingIsLockedToPlatformFrame(m_frame))
+            return;
+
+        wheelEventTarget = latchingState->wheelEventElement();
+        isOverWidget = latchingState->widgetIsLatched();
     }
 }
 
@@ -867,16 +884,24 @@ void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& wheelEvent
 {
     switch (wheelEvent.phase()) {
         case PlatformWheelEventPhaseBegan:
-            m_recentWheelEventDeltaTracker->beginTrackingDeltas();
+            m_frame.mainFrame().wheelEventDeltaTracker()->beginTrackingDeltas();
             break;
         case PlatformWheelEventPhaseEnded:
-            m_recentWheelEventDeltaTracker->endTrackingDeltas();
+            m_frame.mainFrame().wheelEventDeltaTracker()->endTrackingDeltas();
             break;
         default:
             break;
     }
 
-    m_recentWheelEventDeltaTracker->recordWheelEventDelta(wheelEvent);
+    m_frame.mainFrame().wheelEventDeltaTracker()->recordWheelEventDelta(wheelEvent);
+}
+
+static FrameView* frameViewForLatchingState(Frame& frame, ScrollLatchingState* latchingState)
+{
+    if (latchingIsLockedToPlatformFrame(frame))
+        return frame.view();
+
+    return latchingState->frame() ? latchingState->frame()->view() : frame.view();
 }
 
 bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& wheelEvent, Element* wheelEventTarget, ContainerNode* scrollableContainer, ScrollableArea* scrollableArea)
@@ -884,13 +909,18 @@ bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& wheelEve
     // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
     FrameView* view = m_frame.view();
 
-    if (wheelEvent.useLatchedEventElement() && m_latchedScrollableContainer) {
+    ScrollLatchingState* latchingState = m_frame.mainFrame().latchingState();
+    ASSERT(latchingState);
+    if (wheelEvent.useLatchedEventElement() && latchingState->scrollableContainer()) {
+        view = frameViewForLatchingState(m_frame, latchingState);
         if (!view || !view->frame().isMainFrame()) {
             bool didHandleWheelEvent = view && view->wheelEvent(wheelEvent);
-            if (!didHandleWheelEvent && scrollableContainer == m_latchedScrollableContainer) {
+            if (!didHandleWheelEvent && scrollableContainer == latchingState->scrollableContainer()) {
                 // If we are just starting a scroll event, and have nowhere left to scroll, allow
                 // the enclosing frame to handle the scroll.
-                didHandleWheelEvent = !m_startedGestureAtScrollLimit;
+                didHandleWheelEvent = !latchingState->startedGestureAtScrollLimit();
+                if (!didHandleWheelEvent)
+                    latchingState->setFrame(nullptr);
             }
 
             // If the platform widget is handling the event, we always want to return false
@@ -901,11 +931,11 @@ bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& wheelEve
             return didHandleWheelEvent;
         }
         
-        if (scrollableArea && !m_startedGestureAtScrollLimit && scrollableContainer == m_latchedScrollableContainer) {
+        if (scrollableArea && !latchingState->startedGestureAtScrollLimit() && scrollableContainer == latchingState->scrollableContainer()) {
             m_isHandlingWheelEvent = false;
 
             if (eventTargetIsPlatformWidget(wheelEventTarget))
-                return !m_startedGestureAtScrollLimit;
+                return !latchingState->startedGestureAtScrollLimit();
 
             return true;
         }
@@ -922,8 +952,10 @@ bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelE
     if (frameHasPlatformWidget(m_frame) && widget.isFrameView())
         return true;
 
-    if (wheelEvent.useLatchedEventElement() && m_latchedScrollableContainer && scrollableContainer == m_latchedScrollableContainer)
-        return !m_startedGestureAtScrollLimit;
+    ScrollLatchingState* latchingState = m_frame.mainFrame().latchingState();
+    ASSERT(latchingState);
+    if (wheelEvent.useLatchedEventElement() && latchingState->scrollableContainer() && scrollableContainer == latchingState->scrollableContainer())
+        return !latchingState->startedGestureAtScrollLimit();
 
     return false;
 }
diff --git a/Source/WebCore/page/scrolling/ScrollLatchingState.cpp b/Source/WebCore/page/scrolling/ScrollLatchingState.cpp
new file mode 100644 (file)
index 0000000..cbd3d6c
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ScrollLatchingState.h"
+
+#include "Element.h"
+
+namespace WebCore {
+
+ScrollLatchingState::ScrollLatchingState()
+    : m_frame(nullptr)
+    , m_widgetIsLatched(false)
+    , m_startedGestureAtScrollLimit(false)
+{
+}
+    
+ScrollLatchingState::~ScrollLatchingState()
+{
+}
+
+void ScrollLatchingState::clear()
+{
+    m_wheelEventElement = nullptr;
+    m_frame = nullptr;
+    m_scrollableContainer = nullptr;
+    m_widgetIsLatched = false;
+    m_previousWheelScrolledElement = nullptr;
+}
+
+void ScrollLatchingState::setWheelEventElement(PassRefPtr<Element> element)
+{
+    m_wheelEventElement = element;
+}
+
+void ScrollLatchingState::setWidgetIsLatched(bool isOverWidget)
+{
+    m_widgetIsLatched = isOverWidget;
+}
+
+void ScrollLatchingState::setPreviousWheelScrolledElement(PassRefPtr<Element> element)
+{
+    m_previousWheelScrolledElement = element;
+}
+
+void ScrollLatchingState::setScrollableContainer(PassRefPtr<ContainerNode> node)
+{
+    m_scrollableContainer = node;
+}
+
+}
diff --git a/Source/WebCore/page/scrolling/ScrollLatchingState.h b/Source/WebCore/page/scrolling/ScrollLatchingState.h
new file mode 100644 (file)
index 0000000..68e8ce8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ScrollLatchingState_h
+#define ScrollLatchingState_h
+
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class ContainerNode;
+class Element;
+class Frame;
+
+class ScrollLatchingState {
+public:
+    ScrollLatchingState();
+    virtual ~ScrollLatchingState();
+
+    void clear();
+
+    Element* wheelEventElement() { return m_wheelEventElement.get(); }
+    void setWheelEventElement(PassRefPtr<Element>);
+    Frame* frame() { return m_frame; }
+    void setFrame(Frame* frame) { m_frame = frame; }
+
+    bool widgetIsLatched() const { return m_widgetIsLatched; }
+    void setWidgetIsLatched(bool isOverWidget);
+
+    Element* previousWheelScrolledElement() { return m_previousWheelScrolledElement.get(); }
+    void setPreviousWheelScrolledElement(PassRefPtr<Element>);
+    
+    ContainerNode* scrollableContainer() { return m_scrollableContainer.get(); }
+    void setScrollableContainer(PassRefPtr<ContainerNode>);
+    bool startedGestureAtScrollLimit() const { return m_startedGestureAtScrollLimit; }
+    void setStartedGestureAtScrollLimit(bool startedAtLimit) { m_startedGestureAtScrollLimit = startedAtLimit; }
+
+private:
+    RefPtr<Element> m_wheelEventElement;
+    RefPtr<Element> m_previousWheelScrolledElement;
+    RefPtr<ContainerNode> m_scrollableContainer;
+
+    Frame* m_frame;
+    
+    bool m_widgetIsLatched;
+    bool m_startedGestureAtScrollLimit;
+};
+    
+}
+
+#endif