| Index: third_party/WebKit/Source/core/input/EventHandler.cpp | 
| diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp | 
| index e142c70191dd7dc5ba452ec9985e361c8e69b634..c84317bf5523ee4dc4d7943e64cb1513f5f44cf7 100644 | 
| --- a/third_party/WebKit/Source/core/input/EventHandler.cpp | 
| +++ b/third_party/WebKit/Source/core/input/EventHandler.cpp | 
| @@ -164,6 +164,47 @@ bool shouldRefetchEventTarget(const MouseEventWithHitTestResults& mev) | 
| return targetNode->isShadowRoot() && isHTMLInputElement(*toShadowRoot(targetNode)->host()); | 
| } | 
|  | 
| +// TODO(bokan): This method can go away once all scrolls happen through the | 
| +// scroll customization path. | 
| +void computeScrollChainForSingleNode(Node& node, std::deque<int>& scrollChain) | 
| +{ | 
| +    scrollChain.clear(); | 
| + | 
| +    ASSERT(node.layoutObject()); | 
| +    Element* element = toElement(&node); | 
| + | 
| +    scrollChain.push_front(DOMNodeIds::idForNode(element)); | 
| +} | 
| + | 
| +void recomputeScrollChain(const LocalFrame& frame, const Node& startNode, | 
| +    std::deque<int>& 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()) { | 
| +            Element* curElement = toElement(curNode); | 
| +            if (curElement == frame.document()->scrollingElement()) | 
| +                break; | 
| +            scrollChain.push_front(DOMNodeIds::idForNode(curElement)); | 
| +        } | 
| +        curBox = curBox->containingBlock(); | 
| +    } | 
| +    // TODO(tdresser): this should sometimes be excluded, as part of crbug.com/410974. | 
| +    // We need to ensure that the scrollingElement is always part of | 
| +    // the scroll chain. In quirks mode, when the scrollingElement is | 
| +    // the body, some elements may use the documentElement as their | 
| +    // containingBlock, so we ensure the scrollingElement is added | 
| +    // here. | 
| +    scrollChain.push_front(DOMNodeIds::idForNode(frame.document()->scrollingElement())); | 
| +} | 
| + | 
| } // namespace | 
|  | 
| using namespace HTMLNames; | 
| @@ -222,35 +263,6 @@ private: | 
| Cursor m_cursor; | 
| }; | 
|  | 
| -void recomputeScrollChain(const LocalFrame& frame, const Node& startNode, | 
| -    std::deque<int>& 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()) { | 
| -            Element* curElement = toElement(curNode); | 
| -            if (curElement == frame.document()->scrollingElement()) | 
| -                break; | 
| -            scrollChain.push_front(DOMNodeIds::idForNode(curElement)); | 
| -        } | 
| -        curBox = curBox->containingBlock(); | 
| -    } | 
| -    // TODO(tdresser): this should sometimes be excluded, as part of crbug.com/410974. | 
| -    // We need to ensure that the scrollingElement is always part of | 
| -    // the scroll chain. In quirks mode, when the scrollingElement is | 
| -    // the body, some elements may use the documentElement as their | 
| -    // containingBlock, so we ensure the scrollingElement is added | 
| -    // here. | 
| -    scrollChain.push_front(DOMNodeIds::idForNode(frame.document()->scrollingElement())); | 
| -} | 
| - | 
| EventHandler::EventHandler(LocalFrame* frame) | 
| : m_frame(frame) | 
| , m_mousePressed(false) | 
| @@ -611,7 +623,83 @@ void EventHandler::stopAutoscroll() | 
| controller->stopAutoscroll(); | 
| } | 
|  | 
| -ScrollResult EventHandler::physicalScroll(ScrollGranularity granularity, const FloatSize& delta, Node* startNode, Node** stopNode, bool* consumed) | 
| +ScrollResult EventHandler::scrollBox(LayoutBox* box, | 
| +    ScrollGranularity granularity, const FloatSize& delta, | 
| +    const FloatPoint& position, const FloatSize& velocity, | 
| +    bool* wasRootScroller) | 
| +{ | 
| +    ASSERT(box); | 
| +    Node* node = box->node(); | 
| +    Document* document = m_frame->document(); | 
| +    Element* scrollingElement = document->scrollingElement(); | 
| + | 
| +    bool isRootFrame = !document->ownerElement(); | 
| + | 
| +    // TODO(bokan): If the ViewportScrollCallback is installed on the body, we | 
| +    // can still hit the HTML element for scrolling in which case it'll bubble | 
| +    // up to the document node and try to scroll the LayoutView directly. Make | 
| +    // sure we never scroll the LayoutView like this by manually resetting the | 
| +    // scroll to happen on the scrolling element. This can also happen in | 
| +    // QuirksMode when the body is scrollable and scrollingElement == nullptr. | 
| +    if (node && node->isDocumentNode() && isRootFrame) { | 
| +        node = scrollingElement | 
| +            ? scrollingElement | 
| +            : document->documentElement(); | 
| +    } | 
| + | 
| +    // If there's no ApplyScroll callback on the element, scroll as usuall in | 
| +    // the non-scroll-customization case. | 
| +    if (!node || !node->isElementNode() || !toElement(node)->getApplyScroll()) { | 
| +        ASSERT(!isRootFrame | 
| +            || node != scrollingElement | 
| +            || (!scrollingElement && node != document->documentElement())); | 
| +        *wasRootScroller = false; | 
| +        return box->scroll(granularity, delta); | 
| +    } | 
| + | 
| +    ASSERT(isRootFrame); | 
| + | 
| +    // If there is an ApplyScroll callback, its because we placed one on the | 
| +    // root scroller to control top controls and overscroll. Invoke a scroll | 
| +    // using parts of the scroll customization framework on just this element. | 
| +    computeScrollChainForSingleNode(*node, m_currentScrollChain); | 
| + | 
| +    OwnPtr<ScrollStateData> scrollStateData = adoptPtr(new ScrollStateData()); | 
| +    scrollStateData->delta_x = delta.width(); | 
| +    scrollStateData->delta_y = delta.height(); | 
| +    scrollStateData->position_x = position.x(); | 
| +    scrollStateData->position_y = position.y(); | 
| +    // TODO(bokan): delta_granularity is meant to be the number of pixels per | 
| +    // unit of delta but we can't determine that until we get to the area we'll | 
| +    // scroll. This is a hack, we stuff the enum into the double value for | 
| +    // now. | 
| +    scrollStateData->delta_granularity = static_cast<double>(granularity); | 
| +    scrollStateData->velocity_x = velocity.width(); | 
| +    scrollStateData->velocity_y = velocity.height(); | 
| +    scrollStateData->should_propagate = false; | 
| +    scrollStateData->is_in_inertial_phase = false; | 
| +    scrollStateData->from_user_input = true; | 
| +    scrollStateData->delta_consumed_for_scroll_sequence = false; | 
| +    ScrollState* scrollState = | 
| +        ScrollState::create(scrollStateData.release()); | 
| + | 
| +    customizedScroll(*node, *scrollState); | 
| + | 
| +    ScrollResult result( | 
| +        scrollState->deltaX() != delta.width(), | 
| +        scrollState->deltaY() != delta.height(), | 
| +        scrollState->deltaX(), | 
| +        scrollState->deltaY()); | 
| + | 
| +    *wasRootScroller = true; | 
| +    m_currentScrollChain.clear(); | 
| + | 
| +    return result; | 
| +} | 
| + | 
| +ScrollResult EventHandler::physicalScroll(ScrollGranularity granularity, | 
| +    const FloatSize& delta, const FloatPoint& position, | 
| +    const FloatSize& velocity, Node* startNode, Node** stopNode, bool* consumed) | 
| { | 
| if (consumed) | 
| *consumed = false; | 
| @@ -631,7 +719,15 @@ ScrollResult EventHandler::physicalScroll(ScrollGranularity granularity, const F | 
| // chain past it. | 
| bool shouldStopChaining = | 
| stopNode && *stopNode && curBox->node() == *stopNode; | 
| -        result = curBox->scroll(granularity, delta); | 
| +        bool wasRootScroller = false; | 
| + | 
| +        result = scrollBox( | 
| +            curBox, | 
| +            granularity, | 
| +            delta, | 
| +            position, | 
| +            velocity, | 
| +            &wasRootScroller); | 
|  | 
| if (result.didScroll() && stopNode) | 
| *stopNode = curBox->node(); | 
| @@ -641,6 +737,10 @@ ScrollResult EventHandler::physicalScroll(ScrollGranularity granularity, const F | 
| if (consumed) | 
| *consumed = true; | 
| return result; | 
| +        } else if (wasRootScroller) { | 
| +            // Don't try to chain past the root scroller, even if there's | 
| +            // eligible ancestors. | 
| +            break; | 
| } | 
|  | 
| curBox = curBox->containingBlock(); | 
| @@ -1894,13 +1994,18 @@ void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEv | 
|  | 
| // FIXME: enable scroll customization in this case. See crbug.com/410974. | 
| bool consumed = false; | 
| -    ScrollResult result = physicalScroll(granularity, delta, startNode, &node, &consumed); | 
| + | 
| +    physicalScroll( | 
| +        granularity, | 
| +        delta, | 
| +        FloatPoint(), | 
| +        FloatSize(), | 
| +        startNode, | 
| +        &node, | 
| +        &consumed); | 
|  | 
| if (consumed) | 
| wheelEvent->setDefaultHandled(); | 
| - | 
| -    if (m_frame->isMainFrame()) | 
| -        handleOverscroll(result); | 
| } | 
|  | 
| WebInputEventResult EventHandler::handleGestureShowPress() | 
| @@ -2393,6 +2498,20 @@ void EventHandler::handleOverscroll(const ScrollResult& scrollResult, const Floa | 
| } | 
| } | 
|  | 
| +bool EventHandler::isRootScroller(const Node& node) const | 
| +{ | 
| +    // The root scroller is the one Element on the page designated to perform | 
| +    // "viewport actions" like top controls movement and overscroll glow. | 
| + | 
| +    if (!node.isElementNode() || node.document().ownerElement()) | 
| +        return false; | 
| + | 
| +    Element* scrollingElement = node.document().scrollingElement(); | 
| +    return scrollingElement | 
| +        ? toElement(&node) == node.document().scrollingElement() | 
| +        : toElement(&node) == node.document().documentElement(); | 
| +} | 
| + | 
| WebInputEventResult EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gestureEvent) | 
| { | 
| ASSERT(gestureEvent.type() == PlatformEvent::GestureScrollUpdate); | 
| @@ -2435,6 +2554,7 @@ WebInputEventResult EventHandler::handleGestureScrollUpdate(const PlatformGestur | 
| OwnPtr<ScrollStateData> scrollStateData = adoptPtr(new ScrollStateData()); | 
| scrollStateData->delta_x = delta.width(); | 
| scrollStateData->delta_y = delta.height(); | 
| +            scrollStateData->delta_granularity = ScrollByPrecisePixel; | 
| scrollStateData->velocity_x = velocity.width(); | 
| scrollStateData->velocity_y = velocity.height(); | 
| scrollStateData->should_propagate = !gestureEvent.preventPropagation(); | 
| @@ -2464,17 +2584,20 @@ WebInputEventResult EventHandler::handleGestureScrollUpdate(const PlatformGestur | 
| stopNode = m_previousGestureScrolledNode.get(); | 
|  | 
| bool consumed = false; | 
| -            ScrollResult result = physicalScroll(granularity, delta, node, &stopNode, &consumed); | 
| +            ScrollResult result = physicalScroll( | 
| +                granularity, | 
| +                delta, | 
| +                FloatPoint(gestureEvent.position()), | 
| +                velocity, | 
| +                node, | 
| +                &stopNode, | 
| +                &consumed); | 
|  | 
| if (gestureEvent.preventPropagation()) | 
| m_previousGestureScrolledNode = stopNode; | 
|  | 
| -            if (m_frame->isMainFrame() && (!stopNode || stopNode->layoutObject() == m_frame->view()->layoutView())) { | 
| -                FloatPoint positionInRootFrame = FloatPoint(gestureEvent.position().x(), gestureEvent.position().y()); | 
| -                handleOverscroll(result, positionInRootFrame, velocity); | 
| -            } else { | 
| +            if (!stopNode || !isRootScroller(*stopNode)) | 
| resetOverscroll(result.didScrollX, result.didScrollY); | 
| -            } | 
|  | 
| if (consumed) | 
| return WebInputEventResult::HandledSystem; | 
|  |