| Index: ui/events/blink/input_handler_proxy.cc | 
| diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc | 
| index 187c9ac85a13cb26f002f2224f96bb42a7fcd13d..1889a89e077137f8d2b9bf18fc658af6050d479d 100644 | 
| --- a/ui/events/blink/input_handler_proxy.cc | 
| +++ b/ui/events/blink/input_handler_proxy.cc | 
| @@ -208,6 +208,13 @@ void ReportInputEventLatencyUma(const WebInputEvent& event, | 
| } | 
| } | 
|  | 
| +cc::InputHandler::ScrollInputType GestureScrollInputType( | 
| +    blink::WebGestureDevice device) { | 
| +  return device == blink::WebGestureDeviceTouchpad | 
| +             ? cc::InputHandler::WHEEL | 
| +             : cc::InputHandler::TOUCHSCREEN; | 
| +} | 
| + | 
| }  // namespace | 
|  | 
| namespace ui { | 
| @@ -424,13 +431,12 @@ void RecordMainThreadScrollingReasons(WebInputEvent::Type type, | 
| } | 
| } | 
|  | 
| -bool InputHandlerProxy::ShouldAnimate( | 
| -    const blink::WebMouseWheelEvent& event) const { | 
| +bool InputHandlerProxy::ShouldAnimate(bool has_precise_scroll_deltas) const { | 
| #if defined(OS_MACOSX) | 
| // Mac does not smooth scroll wheel events (crbug.com/574283). | 
| return false; | 
| #else | 
| -  return smooth_scroll_enabled_ && !event.hasPreciseScrollingDeltas; | 
| +  return smooth_scroll_enabled_ && !has_precise_scroll_deltas; | 
| #endif | 
| } | 
|  | 
| @@ -487,7 +493,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::ScrollByMouseWheel( | 
| // Wheel events with |canScroll| == false will not trigger scrolling, | 
| // only event handlers.  Forward to the main thread. | 
| result = DID_NOT_HANDLE; | 
| -  } else if (ShouldAnimate(wheel_event)) { | 
| +  } else if (ShouldAnimate(wheel_event.hasPreciseScrollingDeltas)) { | 
| cc::InputHandler::ScrollStatus scroll_status = | 
| input_handler_->ScrollAnimated(gfx::Point(wheel_event.x, wheel_event.y), | 
| scroll_delta); | 
| @@ -592,26 +598,15 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin( | 
| scroll_status.main_thread_scrolling_reasons = | 
| cc::MainThreadScrollingReason::kContinuingMainThreadScroll; | 
| } else if (gesture_event.data.scrollBegin.targetViewport) { | 
| -    scroll_status = input_handler_->RootScrollBegin(&scroll_state, | 
| -                                                    cc::InputHandler::GESTURE); | 
| -  } else if (smooth_scroll_enabled_ && | 
| -             gesture_event.data.scrollBegin.deltaHintUnits == | 
| -                 blink::WebGestureEvent::ScrollUnits::Pixels) { | 
| -    // Generate a scroll begin/end combination to determine if | 
| -    // this can actually be handled by the impl thread or not. But | 
| -    // don't generate any scroll yet; GestureScrollUpdate will generate | 
| -    // the scroll animation. | 
| -    scroll_status = input_handler_->ScrollBegin( | 
| -        &scroll_state, cc::InputHandler::ANIMATED_WHEEL); | 
| -    if (scroll_status.thread == cc::InputHandler::SCROLL_ON_IMPL_THREAD) { | 
| -      cc::ScrollStateData scroll_state_end_data; | 
| -      scroll_state_end_data.is_ending = true; | 
| -      cc::ScrollState scroll_state_end(scroll_state_end_data); | 
| -      input_handler_->ScrollEnd(&scroll_state_end); | 
| -    } | 
| +    scroll_status = input_handler_->RootScrollBegin( | 
| +        &scroll_state, GestureScrollInputType(gesture_event.sourceDevice)); | 
| +  } else if (ShouldAnimate(gesture_event.data.scrollBegin.deltaHintUnits != | 
| +                           blink::WebGestureEvent::ScrollUnits::Pixels)) { | 
| +    gfx::Point scroll_point(gesture_event.x, gesture_event.y); | 
| +    scroll_status = input_handler_->ScrollAnimatedBegin(scroll_point); | 
| } else { | 
| -    scroll_status = | 
| -        input_handler_->ScrollBegin(&scroll_state, cc::InputHandler::GESTURE); | 
| +    scroll_status = input_handler_->ScrollBegin( | 
| +        &scroll_state, GestureScrollInputType(gesture_event.sourceDevice)); | 
| } | 
| UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult", | 
| scroll_status.thread, | 
| @@ -620,20 +615,28 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin( | 
| RecordMainThreadScrollingReasons(gesture_event.type, | 
| scroll_status.main_thread_scrolling_reasons); | 
|  | 
| +  InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE; | 
| switch (scroll_status.thread) { | 
| case cc::InputHandler::SCROLL_ON_IMPL_THREAD: | 
| TRACE_EVENT_INSTANT0("input", | 
| "InputHandlerProxy::handle_input gesture scroll", | 
| TRACE_EVENT_SCOPE_THREAD); | 
| gesture_scroll_on_impl_thread_ = true; | 
| -      return DID_HANDLE; | 
| +      result = DID_HANDLE; | 
| +      break; | 
| case cc::InputHandler::SCROLL_UNKNOWN: | 
| case cc::InputHandler::SCROLL_ON_MAIN_THREAD: | 
| -      return DID_NOT_HANDLE; | 
| +      result = DID_NOT_HANDLE; | 
| +      break; | 
| case cc::InputHandler::SCROLL_IGNORED: | 
| -      return DROP_EVENT; | 
| +      result = DROP_EVENT; | 
| +      break; | 
| } | 
| -  return DID_NOT_HANDLE; | 
| +  if (scroll_elasticity_controller_ && result != DID_NOT_HANDLE) | 
| +    HandleScrollElasticityOverscroll(gesture_event, | 
| +                                     cc::InputHandlerScrollResult()); | 
| + | 
| +  return result; | 
| } | 
|  | 
| InputHandlerProxy::EventDisposition | 
| @@ -650,9 +653,8 @@ InputHandlerProxy::HandleGestureScrollUpdate( | 
| gfx::Vector2dF scroll_delta(-gesture_event.data.scrollUpdate.deltaX, | 
| -gesture_event.data.scrollUpdate.deltaY); | 
|  | 
| -  if (smooth_scroll_enabled_ && | 
| -      gesture_event.data.scrollUpdate.deltaUnits == | 
| -          blink::WebGestureEvent::ScrollUnits::Pixels) { | 
| +  if (ShouldAnimate(gesture_event.data.scrollUpdate.deltaUnits != | 
| +                    blink::WebGestureEvent::ScrollUnits::Pixels)) { | 
| switch (input_handler_->ScrollAnimated(scroll_point, scroll_delta).thread) { | 
| case cc::InputHandler::SCROLL_ON_IMPL_THREAD: | 
| return DID_HANDLE; | 
| @@ -665,6 +667,10 @@ InputHandlerProxy::HandleGestureScrollUpdate( | 
| cc::InputHandlerScrollResult scroll_result = | 
| input_handler_->ScrollBy(&scroll_state); | 
| HandleOverscroll(scroll_point, scroll_result); | 
| + | 
| +  if (scroll_elasticity_controller_) | 
| +    HandleScrollElasticityOverscroll(gesture_event, scroll_result); | 
| + | 
| return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT; | 
| } | 
|  | 
| @@ -674,10 +680,21 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd( | 
| DCHECK(expect_scroll_update_end_); | 
| expect_scroll_update_end_ = false; | 
| #endif | 
| -  cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); | 
| -  input_handler_->ScrollEnd(&scroll_state); | 
| +  if (ShouldAnimate(gesture_event.data.scrollEnd.deltaUnits != | 
| +                    blink::WebGestureEvent::ScrollUnits::Pixels)) { | 
| +    // Do nothing if the scroll is being animated; the scroll animation will | 
| +    // generate the ScrollEnd when it is done. | 
| +  } else { | 
| +    cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); | 
| +    input_handler_->ScrollEnd(&scroll_state); | 
| +  } | 
| if (!gesture_scroll_on_impl_thread_) | 
| return DID_NOT_HANDLE; | 
| + | 
| +  if (scroll_elasticity_controller_) | 
| +    HandleScrollElasticityOverscroll(gesture_event, | 
| +                                     cc::InputHandlerScrollResult()); | 
| + | 
| gesture_scroll_on_impl_thread_ = false; | 
| return DID_HANDLE; | 
| } | 
| @@ -890,7 +907,7 @@ bool InputHandlerProxy::FilterInputEventForFlingBoosting( | 
| gfx::Point(gesture_event.x, gesture_event.y), | 
| fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad | 
| ? cc::InputHandler::NON_BUBBLING_GESTURE | 
| -                  : cc::InputHandler::GESTURE)) { | 
| +                  : cc::InputHandler::TOUCHSCREEN)) { | 
| CancelCurrentFling(); | 
| return false; | 
| } | 
| @@ -1310,4 +1327,19 @@ bool InputHandlerProxy::scrollBy(const WebFloatSize& increment, | 
| return did_scroll; | 
| } | 
|  | 
| +void InputHandlerProxy::HandleScrollElasticityOverscroll( | 
| +    const WebGestureEvent& gesture_event, | 
| +    const cc::InputHandlerScrollResult& scroll_result) { | 
| +  DCHECK(scroll_elasticity_controller_); | 
| +  // Send the event and its disposition to the elasticity controller to update | 
| +  // the over-scroll animation. Note that the call to the elasticity controller | 
| +  // is made asynchronously, to minimize divergence between main thread and | 
| +  // impl thread event handling paths. | 
| +  base::ThreadTaskRunnerHandle::Get()->PostTask( | 
| +      FROM_HERE, | 
| +      base::Bind(&InputScrollElasticityController::ObserveGestureEventAndResult, | 
| +                 scroll_elasticity_controller_->GetWeakPtr(), gesture_event, | 
| +                 scroll_result)); | 
| +} | 
| + | 
| }  // namespace ui | 
|  |