Index: Source/core/page/EventHandler.cpp |
diff --git a/Source/core/page/EventHandler.cpp b/Source/core/page/EventHandler.cpp |
index 80bf15b29e7cca3f4c5647b4e97b7e64199802e7..494da4af697b7b481ca8148ee268b9448c16f995 100644 |
--- a/Source/core/page/EventHandler.cpp |
+++ b/Source/core/page/EventHandler.cpp |
@@ -80,6 +80,7 @@ |
#include "core/page/Page.h" |
#include "core/page/SpatialNavigation.h" |
#include "core/page/TouchAdjustment.h" |
+#include "core/page/scrolling/ScrollState.h" |
#include "core/paint/DeprecatedPaintLayer.h" |
#include "core/svg/SVGDocumentExtensions.h" |
#include "platform/PlatformGestureEvent.h" |
@@ -196,6 +197,28 @@ static inline bool shouldRefetchEventTarget(const MouseEventWithHitTestResults& |
return targetNode->isShadowRoot() && isHTMLInputElement(*toShadowRoot(targetNode)->host()); |
} |
+void recomputeScrollChain(const LocalFrame& frame, const Node& startNode, |
+ WillBeHeapDeque<RefPtrWillBeMember<Element>>& scrollChain) |
+{ |
+ scrollChain.clear(); |
+ |
+ ASSERT(startNode.layoutObject()); |
+ LayoutBox* curBox = startNode.layoutObject()->enclosingBox(); |
+ |
+ // Scrolling propagates along the containing block chain. |
+ while (curBox && !curBox->isLayoutView()) { |
+ Node* curNode = curBox->node(); |
+ // FIXME: this should reject more elements, as part of crbug.com/410974. |
+ if (curNode && curNode->isElementNode()) |
+ scrollChain.prepend(toElement(curNode)); |
+ curBox = curBox->containingBlock(); |
+ } |
+ |
+ // FIXME: we should exclude the document in some cases, as part |
+ // of crbug.com/410974. |
+ scrollChain.prepend(frame.document()->documentElement()); |
+} |
+ |
EventHandler::EventHandler(LocalFrame* frame) |
: m_frame(frame) |
, m_mousePressed(false) |
@@ -223,6 +246,7 @@ EventHandler::EventHandler(LocalFrame* frame) |
, m_longTapShouldInvokeContextMenu(false) |
, m_activeIntervalTimer(this, &EventHandler::activeIntervalTimerFired) |
, m_lastShowPressTimestamp(0) |
+ , m_deltaConsumedForScrollSequence(false) |
{ |
} |
@@ -251,6 +275,7 @@ DEFINE_TRACE(EventHandler) |
visitor->trace(m_scrollGestureHandlingNode); |
visitor->trace(m_previousGestureScrolledNode); |
visitor->trace(m_lastDeferredTapElement); |
+ visitor->trace(m_currentScrollChain); |
#endif |
} |
@@ -940,11 +965,23 @@ bool EventHandler::scroll(ScrollDirection direction, ScrollGranularity granulari |
return false; |
} |
+void EventHandler::customizedScroll(const Node& startNode, ScrollState& scrollState) |
+{ |
+ if (scrollState.fullyConsumed()) |
+ return; |
+ |
+ if (m_currentScrollChain.isEmpty()) |
+ recomputeScrollChain(*m_frame, startNode, m_currentScrollChain); |
+ scrollState.setScrollChain(m_currentScrollChain); |
+ scrollState.distributeToScrollChainDescendant(); |
+} |
+ |
bool EventHandler::bubblingScroll(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode) |
{ |
// The layout needs to be up to date to determine if we can scroll. We may be |
// here because of an onLoad event, in which case the final layout hasn't been performed yet. |
m_frame->document()->updateLayoutIgnorePendingStylesheets(); |
+ // FIXME: enable scroll customization in this case. See crbug.com/410974. |
if (scroll(direction, granularity, startingNode)) |
return true; |
LocalFrame* frame = m_frame; |
@@ -2081,6 +2118,8 @@ void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEv |
// Break up into two scrolls if we need to. Diagonal movement on |
// a MacBook pro is an example of a 2-dimensional mouse wheel event (where both deltaX and deltaY can be set). |
+ |
+ // FIXME: enable scroll customization in this case. See crbug.com/410974. |
if (wheelEvent->railsMode() != Event::RailsModeVertical |
&& scroll(ScrollRight, granularity, startNode, &stopNode, wheelEvent->deltaX(), absolutePosition)) |
wheelEvent->setDefaultHandled(); |
@@ -2474,11 +2513,18 @@ bool EventHandler::passScrollGestureEventToWidget(const PlatformGestureEvent& ge |
bool EventHandler::handleGestureScrollEnd(const PlatformGestureEvent& gestureEvent) { |
RefPtrWillBeRawPtr<Node> node = m_scrollGestureHandlingNode; |
- clearGestureScrollNodes(); |
- if (node) |
+ if (node) { |
passScrollGestureEventToWidget(gestureEvent, node->layoutObject()); |
+ if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) { |
+ RefPtrWillBeRawPtr<ScrollState> scrollState = ScrollState::create( |
+ 0, 0, 0, 0, 0, gestureEvent.inertial(), /* isBeginning */ |
+ false, /* isEnding */ true, /* fromUserInput */ true); |
+ customizedScroll(*node.get(), *scrollState); |
+ } |
+ } |
+ clearGestureScrollNodes(); |
return false; |
} |
@@ -2497,24 +2543,28 @@ bool EventHandler::handleGestureScrollBegin(const PlatformGestureEvent& gestureE |
while (m_scrollGestureHandlingNode && !m_scrollGestureHandlingNode->layoutObject()) |
m_scrollGestureHandlingNode = m_scrollGestureHandlingNode->parentOrShadowHostNode(); |
- if (!m_scrollGestureHandlingNode) |
- return false; |
+ if (!m_scrollGestureHandlingNode) { |
+ if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) |
+ m_scrollGestureHandlingNode = m_frame->document()->documentElement(); |
+ else |
+ return false; |
+ } |
+ ASSERT(m_scrollGestureHandlingNode); |
passScrollGestureEventToWidget(gestureEvent, m_scrollGestureHandlingNode->layoutObject()); |
- |
- if (m_frame->isMainFrame()) |
- m_frame->host()->topControls().scrollBegin(); |
- |
+ if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) { |
+ m_currentScrollChain.clear(); |
+ RefPtrWillBeRawPtr<ScrollState> scrollState = ScrollState::create( |
+ 0, 0, 0, 0, 0, /* inInertialPhase */ false, /* isBeginning */ |
+ true, /* isEnding */ false, /* fromUserInput */ true); |
+ customizedScroll(*m_scrollGestureHandlingNode.get(), *scrollState); |
+ } else { |
+ if (m_frame->isMainFrame()) |
+ m_frame->host()->topControls().scrollBegin(); |
+ } |
return true; |
} |
-static bool scrollAreaOnBothAxes(const FloatSize& delta, ScrollableArea& view) |
-{ |
- bool scrolledHorizontal = view.scroll(ScrollLeft, ScrollByPrecisePixel, delta.width()); |
- bool scrolledVertical = view.scroll(ScrollUp, ScrollByPrecisePixel, delta.height()); |
- return scrolledHorizontal || scrolledVertical; |
-} |
- |
bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gestureEvent) |
{ |
ASSERT(gestureEvent.type() == PlatformEvent::GestureScrollUpdate); |
@@ -2535,68 +2585,78 @@ bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gesture |
// Try to send the event to the correct view. |
if (passScrollGestureEventToWidget(gestureEvent, renderer)) { |
- if (gestureEvent.preventPropagation()) |
+ if (gestureEvent.preventPropagation() |
+ && !RuntimeEnabledFeatures::scrollCustomizationEnabled()) { |
+ // This is an optimization which doesn't apply with |
+ // scroll customization enabled. |
m_previousGestureScrolledNode = m_scrollGestureHandlingNode; |
- |
+ } |
+ // FIXME: we should allow simultaneous scrolling of nested |
+ // iframes along perpendicular axes. See crbug.com/466991. |
+ m_deltaConsumedForScrollSequence = true; |
return true; |
} |
+ bool scrolled = false; |
+ if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) { |
+ RefPtrWillBeRawPtr<ScrollState> scrollState = ScrollState::create( |
+ gestureEvent.deltaX(), gestureEvent.deltaY(), |
+ 0, gestureEvent.velocityX(), gestureEvent.velocityY(), |
+ gestureEvent.inertial(), /* isBeginning */ |
+ false, /* isEnding */ false, /* fromUserInput */ true, |
+ !gestureEvent.preventPropagation(), m_deltaConsumedForScrollSequence); |
+ if (m_previousGestureScrolledNode) { |
+ // The ScrollState needs to know what the current |
+ // native scrolling element is, so that for an |
+ // inertial scroll that shouldn't propagate, only the |
+ // currently scrolling element responds. |
+ ASSERT(m_previousGestureScrolledNode->isElementNode()); |
+ scrollState->setCurrentNativeScrollingElement(toElement(m_previousGestureScrolledNode.get())); |
+ } |
+ customizedScroll(*node, *scrollState); |
+ m_previousGestureScrolledNode = scrollState->currentNativeScrollingElement(); |
+ m_deltaConsumedForScrollSequence = scrollState->deltaConsumedForScrollSequence(); |
+ scrolled = scrollState->deltaX() != gestureEvent.deltaX() |
+ || scrollState->deltaY() != gestureEvent.deltaY(); |
+ } else { |
+ if (gestureEvent.preventPropagation()) |
+ stopNode = m_previousGestureScrolledNode.get(); |
- if (gestureEvent.preventPropagation()) |
- stopNode = m_previousGestureScrolledNode.get(); |
- |
- // First try to scroll the closest scrollable LayoutBox ancestor of |node|. |
- ScrollGranularity granularity = ScrollByPrecisePixel; |
- bool horizontalScroll = scroll(ScrollLeft, granularity, node, &stopNode, delta.width()); |
- bool verticalScroll = scroll(ScrollUp, granularity, node, &stopNode, delta.height()); |
- |
- if (gestureEvent.preventPropagation()) |
- m_previousGestureScrolledNode = stopNode; |
+ // First try to scroll the closest scrollable LayoutBox ancestor of |node|. |
+ ScrollGranularity granularity = ScrollByPrecisePixel; |
+ bool horizontalScroll = scroll(ScrollLeft, granularity, node, &stopNode, delta.width()); |
+ if (!gestureEvent.preventPropagation()) |
+ stopNode = nullptr; |
+ bool verticalScroll = scroll(ScrollUp, granularity, node, &stopNode, delta.height()); |
+ scrolled = horizontalScroll || verticalScroll; |
- if (horizontalScroll || verticalScroll) { |
+ if (gestureEvent.preventPropagation()) |
+ m_previousGestureScrolledNode = stopNode; |
+ } |
+ if (scrolled) { |
setFrameWasScrolledByUser(); |
return true; |
} |
} |
- // If this is main frame, allow top controls to scroll first and update |
- // delta accordingly |
- bool consumed = false; |
- if (m_frame->isMainFrame() && shouldTopControlsConsumeScroll(delta)) { |
- FloatSize excessDelta = m_frame->host()->topControls().scrollBy(delta); |
- consumed = excessDelta != delta; |
- delta = excessDelta; |
- |
- if (delta.isZero()) |
- return consumed; |
- } |
+ if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) |
+ return false; |
// Try to scroll the frame view. |
- FrameView* view = m_frame->view(); |
- if (!view) |
- return consumed; |
- |
- if (scrollAreaOnBothAxes(delta, *view)) { |
+ if (m_frame->applyScrollDelta(delta, false)) { |
setFrameWasScrolledByUser(); |
return true; |
} |
- // If this is the main frame and it didn't scroll, propagate up to the pinch viewport. |
- if (!m_frame->isMainFrame()) |
- return consumed; |
- |
- if (scrollAreaOnBothAxes(delta, m_frame->host()->pinchViewport())) { |
- setFrameWasScrolledByUser(); |
- return true; |
- } |
- |
- return consumed; |
+ return false; |
} |
void EventHandler::clearGestureScrollNodes() |
{ |
m_scrollGestureHandlingNode = nullptr; |
m_previousGestureScrolledNode = nullptr; |
+ m_deltaConsumedForScrollSequence = false; |
+ m_currentScrollChain.clear(); |
} |
bool EventHandler::isScrollbarHandlingGestures() const |
@@ -3423,6 +3483,7 @@ void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) |
return; |
ScrollDirection direction = event->shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward; |
+ // FIXME: enable scroll customization in this case. See crbug.com/410974. |
if (scroll(direction, ScrollByPage)) { |
event->setDefaultHandled(); |
return; |
@@ -3939,18 +4000,4 @@ unsigned EventHandler::accessKeyModifiers() |
#endif |
} |
-bool EventHandler::shouldTopControlsConsumeScroll(FloatSize scrollDelta) const |
-{ |
- // Always consume if it's in the direction to show the top controls. |
- if (scrollDelta.height() > 0) |
- return true; |
- |
- // If it's in the direction to hide the top controls, only consume when the frame can also scroll. |
- if (m_frame->view()->scrollPosition().y() < m_frame->view()->maximumScrollPosition().y()) |
- return true; |
- |
- return false; |
-} |
- |
- |
} // namespace blink |