[go: nahoru, domu]

Hook ScrollElasticityController to InputHandlerProxy

Move ScrollElasticityControllerClient to ScrollElasticityHelper
in cc, where it interfaces with LayerTreeHostImpl. Rename the
ScrollElasticityController to InputScrollElasticityController.

Send events handled on the impl thread to the
InputScrollElasticityController, to update the scroll animation state.  Do
this asynchronously, since it will need to be asynchronous on the main
thread for non-impl-thread-handled events. (Note that it may be that
we will send events to the elasticity system after they bounce back
to the browser -- this is the more conservative version, and may be
changed as main thread events are added).

With this in place, the InputScrollElasticityController produces the
right over-scroll offsets in ScrollElasticityHelper most of the time
(according to printfs). The next step will be to add a layer transformed
by these offsets.

BUG=133097

Review URL: https://codereview.chromium.org/713413002

Cr-Commit-Position: refs/heads/master@{#304055}
diff --git a/android_webview/tools/third_party_files_whitelist.txt b/android_webview/tools/third_party_files_whitelist.txt
index 8c88957..f1d2776 100644
--- a/android_webview/tools/third_party_files_whitelist.txt
+++ b/android_webview/tools/third_party_files_whitelist.txt
@@ -18,6 +18,8 @@
 base/logging.h
 # Copyright Ron Rivest, public domain.
 base/md5.cc
+# Copyright Apple Inc; BSD license. Moved from third_party/WebKit/.
+cc/input/scroll_elasticity_helper.h
 # Copyright Netscape Communications Corporation; MPL, GPL v2 or LGPL v2
 # license. This third-party code is taken from Mozilla, the license for which
 # we already pick up from third_party/npapi/.
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 37e471d4..55c6c05d 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -119,6 +119,8 @@
     "input/page_scale_animation.h",
     "input/layer_selection_bound.cc",
     "input/layer_selection_bound.h",
+    "input/scroll_elasticity_helper.cc",
+    "input/scroll_elasticity_helper.h",
     "input/selection_bound_type.h",
     "input/top_controls_manager.cc",
     "input/top_controls_manager.h",
diff --git a/cc/cc.gyp b/cc/cc.gyp
index 1fea22f7..fac01a7 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -147,6 +147,8 @@
         'input/page_scale_animation.h',
         'input/layer_selection_bound.cc',
         'input/layer_selection_bound.h',
+        'input/scroll_elasticity_helper.cc',
+        'input/scroll_elasticity_helper.h',
         'input/selection_bound_type.h',
         'input/top_controls_manager.cc',
         'input/top_controls_manager.h',
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 7a9c991..7b83e4b2 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -24,6 +24,7 @@
 namespace cc {
 
 class LayerScrollOffsetDelegate;
+class ScrollElasticityHelper;
 
 struct CC_EXPORT InputHandlerScrollResult {
   InputHandlerScrollResult();
@@ -150,6 +151,8 @@
   virtual scoped_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
       ui::LatencyInfo* latency) = 0;
 
+  virtual ScrollElasticityHelper* CreateScrollElasticityHelper() = 0;
+
  protected:
   InputHandler() {}
   virtual ~InputHandler() {}
diff --git a/cc/input/scroll_elasticity_helper.cc b/cc/input/scroll_elasticity_helper.cc
new file mode 100644
index 0000000..54da888
--- /dev/null
+++ b/cc/input/scroll_elasticity_helper.cc
@@ -0,0 +1,101 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/input/scroll_elasticity_helper.h"
+
+#include "cc/layers/layer_impl.h"
+#include "cc/trees/layer_tree_host_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace cc {
+
+ScrollElasticityHelper::ScrollElasticityHelper(LayerTreeHostImpl* layer_tree)
+    : layer_tree_host_impl_(layer_tree), timer_active_(false) {
+}
+
+ScrollElasticityHelper::~ScrollElasticityHelper() {
+}
+
+bool ScrollElasticityHelper::AllowsHorizontalStretching() {
+  // The WebKit implementation has this interface because it is written in terms
+  // of overscrolling on a per-layer basis, not for the whole layer tree. In
+  // that implementation, this always returns true for the frame view's
+  // scrollable area.
+  // TODO(ccameron): This is function is redundant and may be removed.
+  return true;
+}
+
+bool ScrollElasticityHelper::AllowsVerticalStretching() {
+  // TODO(ccameron): This is function is redundant and may be removed.
+  return true;
+}
+
+gfx::Vector2dF ScrollElasticityHelper::StretchAmount() {
+  return stretch_offset_;
+}
+
+bool ScrollElasticityHelper::PinnedInDirection(
+    const gfx::Vector2dF& direction) {
+  gfx::ScrollOffset scroll_offset =
+      layer_tree_host_impl_->active_tree()->TotalScrollOffset();
+  gfx::ScrollOffset max_scroll_offset =
+      layer_tree_host_impl_->active_tree()->TotalMaxScrollOffset();
+  bool result = false;
+  if (direction.x() < 0)
+    result |= scroll_offset.x() <= 0;
+  if (direction.x() > 0)
+    result |= scroll_offset.x() >= max_scroll_offset.x();
+  if (direction.y() < 0)
+    result |= scroll_offset.y() <= 0;
+  if (direction.y() > 0)
+    result |= scroll_offset.y() >= max_scroll_offset.y();
+  return result;
+}
+
+bool ScrollElasticityHelper::CanScrollHorizontally() {
+  return layer_tree_host_impl_->active_tree()->TotalMaxScrollOffset().x() > 0;
+}
+
+bool ScrollElasticityHelper::CanScrollVertically() {
+  return layer_tree_host_impl_->active_tree()->TotalMaxScrollOffset().y() > 0;
+}
+
+gfx::Vector2dF ScrollElasticityHelper::AbsoluteScrollPosition() {
+  // TODO(ccameron): This is function is redundant and may be removed.
+  return stretch_offset_;
+}
+
+void ScrollElasticityHelper::ImmediateScrollBy(const gfx::Vector2dF& scroll) {
+  // TODO(ccameron): This is function is redundant and may be removed.
+}
+
+void ScrollElasticityHelper::ImmediateScrollByWithoutContentEdgeConstraints(
+    const gfx::Vector2dF& scroll) {
+  stretch_offset_ += scroll;
+
+  // TODO(ccameron): Update the transform of the appropriate layer in the
+  // LayerTreeHostImpl, and request that a frame be drawn.
+}
+
+void ScrollElasticityHelper::StartSnapRubberbandTimer() {
+  if (timer_active_)
+    return;
+  timer_active_ = true;
+  layer_tree_host_impl_->SetNeedsAnimate();
+}
+
+void ScrollElasticityHelper::StopSnapRubberbandTimer() {
+  timer_active_ = false;
+}
+
+void ScrollElasticityHelper::SnapRubberbandTimerFired() {
+  if (timer_active_)
+    layer_tree_host_impl_->SetNeedsAnimate();
+}
+
+void ScrollElasticityHelper::AdjustScrollPositionToBoundsIfNecessary() {
+  // TODO(ccameron): This is function is redundant and may be removed.
+}
+
+}  // namespace cc
diff --git a/cc/input/scroll_elasticity_helper.h b/cc/input/scroll_elasticity_helper.h
new file mode 100644
index 0000000..645b20c9
--- /dev/null
+++ b/cc/input/scroll_elasticity_helper.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_INPUT_SCROLL_ELASTICITY_HELPER_H_
+#define CC_INPUT_SCROLL_ELASTICITY_HELPER_H_
+
+#include "base/time/time.h"
+#include "cc/base/cc_export.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace cc {
+
+class LayerTreeHostImpl;
+
+// ScrollElasticityHelper is based on
+// WebKit/Source/platform/mac/ScrollElasticityController.h
+/*
+ * Copyright (C) 2011 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.
+ */
+
+// Interface between a LayerTreeHostImpl and the ScrollElasticityController. It
+// would be possible, in principle, for LayerTreeHostImpl to implement this
+// interface itself. This artificial boundary is introduced to reduce the amount
+// of logic and state held directly inside LayerTreeHostImpl.
+class CC_EXPORT ScrollElasticityHelper {
+ public:
+  explicit ScrollElasticityHelper(LayerTreeHostImpl* layer_tree);
+  ~ScrollElasticityHelper();
+
+  bool AllowsHorizontalStretching();
+  bool AllowsVerticalStretching();
+  // The amount that the view is stretched past the normal allowable bounds.
+  // The "overhang" amount.
+  gfx::Vector2dF StretchAmount();
+  bool PinnedInDirection(const gfx::Vector2dF& direction);
+  bool CanScrollHorizontally();
+  bool CanScrollVertically();
+
+  // Return the absolute scroll position, not relative to the scroll origin.
+  gfx::Vector2dF AbsoluteScrollPosition();
+
+  void ImmediateScrollBy(const gfx::Vector2dF& scroll);
+  void ImmediateScrollByWithoutContentEdgeConstraints(
+      const gfx::Vector2dF& scroll);
+  void StartSnapRubberbandTimer();
+  void StopSnapRubberbandTimer();
+  void SnapRubberbandTimerFired();
+
+  // If the current scroll position is within the overhang area, this function
+  // will cause
+  // the page to scroll to the nearest boundary point.
+  void AdjustScrollPositionToBoundsIfNecessary();
+
+ private:
+  LayerTreeHostImpl* layer_tree_host_impl_;
+  gfx::Vector2dF stretch_offset_;
+  bool timer_active_;
+};
+
+}  // namespace cc
+
+#endif  // CC_INPUT_SCROLL_ELASTICITY_HELPER_H_
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 4c312e7a9..696da7a 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -29,6 +29,7 @@
 #include "cc/debug/rendering_stats_instrumentation.h"
 #include "cc/debug/traced_value.h"
 #include "cc/input/page_scale_animation.h"
+#include "cc/input/scroll_elasticity_helper.h"
 #include "cc/input/top_controls_manager.h"
 #include "cc/layers/append_quads_data.h"
 #include "cc/layers/heads_up_display_layer_impl.h"
@@ -280,6 +281,8 @@
     input_handler_client_->WillShutdown();
     input_handler_client_ = NULL;
   }
+  if (scroll_elasticity_helper_)
+    scroll_elasticity_helper_.reset();
 
   // The layer trees must be destroyed before the layer tree host. We've
   // made a contract with our animation controllers that the registrar
@@ -449,6 +452,12 @@
       new LatencyInfoSwapPromiseMonitor(latency, NULL, this));
 }
 
+ScrollElasticityHelper* LayerTreeHostImpl::CreateScrollElasticityHelper() {
+  DCHECK(!scroll_elasticity_helper_);
+  scroll_elasticity_helper_.reset(new ScrollElasticityHelper(this));
+  return scroll_elasticity_helper_.get();
+}
+
 void LayerTreeHostImpl::QueueSwapPromiseForMainThreadScrollUpdate(
     scoped_ptr<SwapPromise> swap_promise) {
   swap_promises_for_main_thread_scroll_update_.push_back(swap_promise.Pass());
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index f9fe8bb..24cb7301 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -54,6 +54,7 @@
 class RenderPassDrawQuad;
 class RenderingStatsInstrumentation;
 class ResourcePool;
+class ScrollElasticityHelper;
 class ScrollbarLayerImplBase;
 class TextureMailboxDeleter;
 class TopControlsManager;
@@ -152,6 +153,7 @@
   bool HaveTouchEventHandlersAt(const gfx::Point& viewport_port) override;
   scoped_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
       ui::LatencyInfo* latency) override;
+  ScrollElasticityHelper* CreateScrollElasticityHelper() override;
 
   // TopControlsManagerClient implementation.
   void SetControlsTopOffset(float offset) override;
@@ -621,6 +623,10 @@
   int scroll_layer_id_when_mouse_over_scrollbar_;
   ScopedPtrVector<SwapPromise> swap_promises_for_main_thread_scroll_update_;
 
+  // An object to implement the ScrollElasticityHelper interface and
+  // hold all state related to elasticity. May be NULL if never requested.
+  scoped_ptr<ScrollElasticityHelper> scroll_elasticity_helper_;
+
   bool tile_priorities_dirty_;
 
   // The optional delegate for the root layer scroll offset.
diff --git a/content/renderer/input/input_handler_proxy.cc b/content/renderer/input/input_handler_proxy.cc
index fe6de51..21f3359 100644
--- a/content/renderer/input/input_handler_proxy.cc
+++ b/content/renderer/input/input_handler_proxy.cc
@@ -13,6 +13,7 @@
 #include "content/common/input/web_input_event_traits.h"
 #include "content/public/common/content_switches.h"
 #include "content/renderer/input/input_handler_proxy_client.h"
+#include "content/renderer/input/input_scroll_elasticity_controller.h"
 #include "third_party/WebKit/public/platform/Platform.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
 #include "ui/events/latency_info.h"
@@ -161,11 +162,20 @@
   input_handler_->BindToClient(this);
   smooth_scroll_enabled_ = CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kEnableSmoothScrolling);
+
+#if defined(OS_MACOSX)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableThreadedEventHandlingMac)) {
+    scroll_elasticity_controller_.reset(new InputScrollElasticityController(
+        input_handler_->CreateScrollElasticityHelper()));
+  }
+#endif
 }
 
 InputHandlerProxy::~InputHandlerProxy() {}
 
 void InputHandlerProxy::WillShutdown() {
+  scroll_elasticity_controller_.reset();
   input_handler_ = NULL;
   client_->WillShutdown();
 }
@@ -276,64 +286,81 @@
 
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel(
     const WebMouseWheelEvent& wheel_event) {
+  InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE;
+  cc::InputHandlerScrollResult scroll_result;
+
   if (wheel_event.scrollByPage) {
     // TODO(jamesr): We don't properly handle scroll by page in the compositor
     // thread, so punt it to the main thread. http://crbug.com/236639
-    return DID_NOT_HANDLE;
-  }
-  if (wheel_event.modifiers & WebInputEvent::ControlKey) {
+    result = DID_NOT_HANDLE;
+  } else if (wheel_event.modifiers & WebInputEvent::ControlKey) {
     // Wheel events involving the control key never trigger scrolling, only
     // event handlers.  Forward to the main thread.
-    return DID_NOT_HANDLE;
-  }
-  if (smooth_scroll_enabled_) {
+    result = DID_NOT_HANDLE;
+  } else if (smooth_scroll_enabled_) {
     cc::InputHandler::ScrollStatus scroll_status =
         input_handler_->ScrollAnimated(
             gfx::Point(wheel_event.x, wheel_event.y),
             gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
     switch (scroll_status) {
       case cc::InputHandler::ScrollStarted:
-        return DID_HANDLE;
+        result = DID_HANDLE;
+        break;
       case cc::InputHandler::ScrollIgnored:
-        return DROP_EVENT;
+        result = DROP_EVENT;
       default:
-        return DID_NOT_HANDLE;
+        result = DID_NOT_HANDLE;
+        break;
+    }
+  } else {
+    cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
+        gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel);
+    switch (scroll_status) {
+      case cc::InputHandler::ScrollStarted: {
+        TRACE_EVENT_INSTANT2(
+            "input", "InputHandlerProxy::handle_input wheel scroll",
+            TRACE_EVENT_SCOPE_THREAD, "deltaX", -wheel_event.deltaX, "deltaY",
+            -wheel_event.deltaY);
+        gfx::Point scroll_point(wheel_event.x, wheel_event.y);
+        gfx::Vector2dF scroll_delta(-wheel_event.deltaX, -wheel_event.deltaY);
+        scroll_result = input_handler_->ScrollBy(scroll_point, scroll_delta);
+        HandleOverscroll(scroll_point, scroll_result);
+        input_handler_->ScrollEnd();
+        result = scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
+        break;
+      }
+      case cc::InputHandler::ScrollIgnored:
+        // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
+        // to properly sync scrollability it's safer to send the event to the
+        // main thread. Change back to DROP_EVENT once we have synchronization
+        // bugs sorted out.
+        result = DID_NOT_HANDLE;
+        break;
+      case cc::InputHandler::ScrollUnknown:
+      case cc::InputHandler::ScrollOnMainThread:
+        result = DID_NOT_HANDLE;
+        break;
+      case cc::InputHandler::ScrollStatusCount:
+        NOTREACHED();
+        break;
     }
   }
-  cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
-      gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel);
-  switch (scroll_status) {
-    case cc::InputHandler::ScrollStarted: {
-      TRACE_EVENT_INSTANT2(
-          "input",
-          "InputHandlerProxy::handle_input wheel scroll",
-          TRACE_EVENT_SCOPE_THREAD,
-          "deltaX",
-          -wheel_event.deltaX,
-          "deltaY",
-          -wheel_event.deltaY);
-      gfx::Point scroll_point(wheel_event.x, wheel_event.y);
-      gfx::Vector2dF scroll_delta(-wheel_event.deltaX, -wheel_event.deltaY);
-      cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(
-          scroll_point, scroll_delta);
-      HandleOverscroll(scroll_point, scroll_result);
-      input_handler_->ScrollEnd();
-      return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
-    }
-    case cc::InputHandler::ScrollIgnored:
-      // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
-      // to properly sync scrollability it's safer to send the event to the
-      // main thread. Change back to DROP_EVENT once we have synchronization
-      // bugs sorted out.
-      return DID_NOT_HANDLE;
-    case cc::InputHandler::ScrollUnknown:
-    case cc::InputHandler::ScrollOnMainThread:
-      return DID_NOT_HANDLE;
-    case cc::InputHandler::ScrollStatusCount:
-      NOTREACHED();
-      break;
+
+  // Send the event and its disposition to the elasticity controller to update
+  // the over-scroll animation. If the event is to be handled on the main
+  // thread, the event and its disposition will be sent to the elasticity
+  // controller after being handled on the main thread.
+  if (scroll_elasticity_controller_ && result != DID_NOT_HANDLE) {
+    // Note that the call to the elasticity controller is made asynchronously,
+    // to minimize divergence between main thread and impl thread event
+    // handling paths.
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&InputScrollElasticityController::ObserveWheelEventAndResult,
+                   scroll_elasticity_controller_->GetWeakPtr(), wheel_event,
+                   scroll_result));
   }
-  return DID_NOT_HANDLE;
+  return result;
 }
 
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
@@ -639,6 +666,9 @@
 }
 
 void InputHandlerProxy::Animate(base::TimeTicks time) {
+  if (scroll_elasticity_controller_)
+    scroll_elasticity_controller_->Animate(time);
+
   if (!fling_curve_)
     return;
 
diff --git a/content/renderer/input/input_handler_proxy.h b/content/renderer/input/input_handler_proxy.h
index d3b59111..ab3b109 100644
--- a/content/renderer/input/input_handler_proxy.h
+++ b/content/renderer/input/input_handler_proxy.h
@@ -19,6 +19,7 @@
 namespace content {
 
 class InputHandlerProxyClient;
+class InputScrollElasticityController;
 
 // This class is a proxy between the content input event filtering and the
 // compositor's input handling logic. InputHandlerProxy instances live entirely
@@ -134,6 +135,9 @@
   // Non-zero only within the scope of |scrollBy|.
   gfx::Vector2dF current_fling_velocity_;
 
+  // Used to animate rubber-band over-scroll effect on Mac.
+  scoped_ptr<InputScrollElasticityController> scroll_elasticity_controller_;
+
   bool smooth_scroll_enabled_;
 
   DISALLOW_COPY_AND_ASSIGN(InputHandlerProxy);
diff --git a/content/renderer/input/input_handler_proxy_unittest.cc b/content/renderer/input/input_handler_proxy_unittest.cc
index 4de5469..aafe9187 100644
--- a/content/renderer/input/input_handler_proxy_unittest.cc
+++ b/content/renderer/input/input_handler_proxy_unittest.cc
@@ -104,6 +104,10 @@
       return scoped_ptr<cc::SwapPromiseMonitor>();
   }
 
+  cc::ScrollElasticityHelper* CreateScrollElasticityHelper() override {
+    return NULL;
+  }
+
   virtual void BindToClient(cc::InputHandlerClient* client) override {}
 
   virtual void MouseMoveAt(const gfx::Point& mouse_position) override {}
diff --git a/content/renderer/input/input_scroll_elasticity_controller.cc b/content/renderer/input/input_scroll_elasticity_controller.cc
index 059bc9d..ca0258e 100644
--- a/content/renderer/input/input_scroll_elasticity_controller.cc
+++ b/content/renderer/input/input_scroll_elasticity_controller.cc
@@ -6,8 +6,8 @@
 
 #include <math.h>
 
-// ScrollElasticityController and ScrollElasticityControllerClient are based on
-// WebKit/Source/platform/mac/ScrollElasticityController.mm
+// InputScrollElasticityController is based on
+// WebKit/Source/platform/mac/InputScrollElasticityController.mm
 /*
  * Copyright (C) 2011 Apple Inc. All rights reserved.
  *
@@ -88,18 +88,47 @@
 
 }  // namespace
 
-ScrollElasticityController::ScrollElasticityController(
-    ScrollElasticityControllerClient* client)
-    : client_(client),
+InputScrollElasticityController::InputScrollElasticityController(
+    cc::ScrollElasticityHelper* helper)
+    : helper_(helper),
       in_scroll_gesture_(false),
       has_scrolled_(false),
       momentum_scroll_in_progress_(false),
       ignore_momentum_scrolls_(false),
       last_momentum_scroll_timestamp_(0),
-      snap_rubberband_timer_is_active_(false) {
+      snap_rubberband_timer_is_active_(false),
+      weak_factory_(this) {
 }
 
-bool ScrollElasticityController::HandleWheelEvent(
+InputScrollElasticityController::~InputScrollElasticityController() {
+}
+
+base::WeakPtr<InputScrollElasticityController>
+InputScrollElasticityController::GetWeakPtr() {
+  if (helper_)
+    return weak_factory_.GetWeakPtr();
+  return base::WeakPtr<InputScrollElasticityController>();
+}
+
+void InputScrollElasticityController::Animate(base::TimeTicks time) {
+  if (!snap_rubberband_timer_is_active_)
+    return;
+
+  // TODO(ccameron): This should send the time parameter to the snap function,
+  // so that animation can be based on frame time, not arbitrarily-sampled
+  // clock time.
+  SnapRubberbandTimerFired();
+}
+
+void InputScrollElasticityController::ObserveWheelEventAndResult(
+    const blink::WebMouseWheelEvent& wheel_event,
+    const cc::InputHandlerScrollResult& scroll_result) {
+  // TODO(ccameron): Pull non-over-scrolling scroll logic out of
+  // HandleWheelEvent, and read the scroll result instead.
+  HandleWheelEvent(wheel_event);
+}
+
+bool InputScrollElasticityController::HandleWheelEvent(
     const blink::WebMouseWheelEvent& wheel_event) {
   if (wheel_event.phase == blink::WebMouseWheelEvent::PhaseMayBegin)
     return false;
@@ -112,7 +141,7 @@
     last_momentum_scroll_timestamp_ = 0;
     momentum_velocity_ = gfx::Vector2dF();
 
-    gfx::Vector2dF stretch_amount = client_->StretchAmount();
+    gfx::Vector2dF stretch_amount = helper_->StretchAmount();
     stretch_scroll_force_.set_x(
         ReboundDeltaForElasticDelta(stretch_amount.x()));
     stretch_scroll_force_.set_y(
@@ -127,7 +156,7 @@
     // return ShouldHandleEvent(wheel_event);
 
     // This logic is incorrect, since diagonal wheel events are not consumed.
-    if (client_->PinnedInDirection(gfx::Vector2dF(-wheel_event.deltaX, 0))) {
+    if (helper_->PinnedInDirection(gfx::Vector2dF(-wheel_event.deltaX, 0))) {
       if (wheel_event.deltaX > 0 && !wheel_event.canRubberbandLeft)
         return false;
       if (wheel_event.deltaX < 0 && !wheel_event.canRubberbandRight)
@@ -166,7 +195,7 @@
   // points and put it into overflow.
   overflow_scroll_delta_ = gfx::Vector2dF();
 
-  gfx::Vector2dF stretch_amount = client_->StretchAmount();
+  gfx::Vector2dF stretch_amount = helper_->StretchAmount();
   bool is_vertically_stretched = stretch_amount.y() != 0.f;
   bool is_horizontally_stretched = stretch_amount.x() != 0.f;
 
@@ -206,7 +235,7 @@
 
     if (is_vertically_stretched) {
       if (!is_horizontally_stretched &&
-          client_->PinnedInDirection(gfx::Vector2dF(delta_x, 0))) {
+          helper_->PinnedInDirection(gfx::Vector2dF(delta_x, 0))) {
         // Stretching only in the vertical.
         if (delta_y != 0 &&
             (fabsf(delta_x / delta_y) < kRubberbandDirectionLockStretchRatio))
@@ -220,7 +249,7 @@
       }
     } else if (is_horizontally_stretched) {
       // Stretching only in the horizontal.
-      if (client_->PinnedInDirection(gfx::Vector2dF(0, delta_y))) {
+      if (helper_->PinnedInDirection(gfx::Vector2dF(0, delta_y))) {
         if (delta_x != 0 &&
             (fabsf(delta_y / delta_x) < kRubberbandDirectionLockStretchRatio))
           delta_y = 0;
@@ -233,7 +262,7 @@
       }
     } else {
       // Not stretching at all yet.
-      if (client_->PinnedInDirection(gfx::Vector2dF(delta_x, delta_y))) {
+      if (helper_->PinnedInDirection(gfx::Vector2dF(delta_x, delta_y))) {
         if (fabsf(delta_y) >= fabsf(delta_x)) {
           if (fabsf(delta_x) < kRubberbandMinimumRequiredDeltaBeforeStretch) {
             overflow_scroll_delta_.set_x(overflow_scroll_delta_.x() + delta_x);
@@ -252,41 +281,41 @@
           is_horizontally_stretched)) {
       if (delta_y != 0) {
         delta_y *= ScrollWheelMultiplier();
-        client_->ImmediateScrollBy(gfx::Vector2dF(0, delta_y));
+        helper_->ImmediateScrollBy(gfx::Vector2dF(0, delta_y));
       }
       if (delta_x != 0) {
         delta_x *= ScrollWheelMultiplier();
-        client_->ImmediateScrollBy(gfx::Vector2dF(delta_x, 0));
+        helper_->ImmediateScrollBy(gfx::Vector2dF(delta_x, 0));
       }
     } else {
-      if (!client_->AllowsHorizontalStretching()) {
+      if (!helper_->AllowsHorizontalStretching()) {
         delta_x = 0;
         event_coalesced_delta_x = 0;
       } else if ((delta_x != 0) && !is_horizontally_stretched &&
-                 !client_->PinnedInDirection(gfx::Vector2dF(delta_x, 0))) {
+                 !helper_->PinnedInDirection(gfx::Vector2dF(delta_x, 0))) {
         delta_x *= ScrollWheelMultiplier();
 
-        client_->ImmediateScrollByWithoutContentEdgeConstraints(
+        helper_->ImmediateScrollByWithoutContentEdgeConstraints(
             gfx::Vector2dF(delta_x, 0));
         delta_x = 0;
       }
 
-      if (!client_->AllowsVerticalStretching()) {
+      if (!helper_->AllowsVerticalStretching()) {
         delta_y = 0;
         event_coalesced_delta_y = 0;
       } else if ((delta_y != 0) && !is_vertically_stretched &&
-                 !client_->PinnedInDirection(gfx::Vector2dF(0, delta_y))) {
+                 !helper_->PinnedInDirection(gfx::Vector2dF(0, delta_y))) {
         delta_y *= ScrollWheelMultiplier();
 
-        client_->ImmediateScrollByWithoutContentEdgeConstraints(
+        helper_->ImmediateScrollByWithoutContentEdgeConstraints(
             gfx::Vector2dF(0, delta_y));
         delta_y = 0;
       }
 
-      gfx::Vector2dF stretch_amount = client_->StretchAmount();
+      gfx::Vector2dF stretch_amount = helper_->StretchAmount();
 
       if (momentum_scroll_in_progress_) {
-        if ((client_->PinnedInDirection(gfx::Vector2dF(
+        if ((helper_->PinnedInDirection(gfx::Vector2dF(
                  event_coalesced_delta_x, event_coalesced_delta_y)) ||
              (fabsf(event_coalesced_delta_x) + fabsf(event_coalesced_delta_y) <=
               0)) &&
@@ -304,7 +333,7 @@
           ceilf(ElasticDeltaForReboundDelta(stretch_scroll_force_.x())),
           ceilf(ElasticDeltaForReboundDelta(stretch_scroll_force_.y())));
 
-      client_->ImmediateScrollByWithoutContentEdgeConstraints(damped_delta -
+      helper_->ImmediateScrollByWithoutContentEdgeConstraints(damped_delta -
                                                               stretch_amount);
     }
   }
@@ -335,12 +364,12 @@
 
 }  // namespace
 
-void ScrollElasticityController::SnapRubberbandTimerFired() {
+void InputScrollElasticityController::SnapRubberbandTimerFired() {
   if (!momentum_scroll_in_progress_ || ignore_momentum_scrolls_) {
     float time_delta = (base::Time::Now() - start_time_).InSecondsF();
 
     if (start_stretch_ == gfx::Vector2dF()) {
-      start_stretch_ = client_->StretchAmount();
+      start_stretch_ = helper_->StretchAmount();
       if (start_stretch_ == gfx::Vector2dF()) {
         StopSnapRubberbandTimer();
 
@@ -351,7 +380,7 @@
         return;
       }
 
-      orig_origin_ = client_->AbsoluteScrollPosition() - start_stretch_;
+      orig_origin_ = helper_->AbsoluteScrollPosition() - start_stretch_;
       orig_velocity_ = momentum_velocity_;
 
       // Just like normal scrolling, prefer vertical rubberbanding
@@ -360,12 +389,12 @@
 
       // Don't rubber-band horizontally if it's not possible to scroll
       // horizontally
-      if (!client_->CanScrollHorizontally())
+      if (!helper_->CanScrollHorizontally())
         orig_velocity_.set_x(0);
 
       // Don't rubber-band vertically if it's not possible to scroll
       // vertically
-      if (!client_->CanScrollVertically())
+      if (!helper_->CanScrollVertically())
         orig_velocity_.set_y(0);
     }
 
@@ -376,15 +405,15 @@
             start_stretch_.y(), -orig_velocity_.y(), time_delta)));
 
     if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) {
-      client_->ImmediateScrollByWithoutContentEdgeConstraints(
-          gfx::Vector2dF(delta.x(), delta.y()) - client_->StretchAmount());
+      helper_->ImmediateScrollByWithoutContentEdgeConstraints(
+          gfx::Vector2dF(delta.x(), delta.y()) - helper_->StretchAmount());
 
-      gfx::Vector2dF new_stretch = client_->StretchAmount();
+      gfx::Vector2dF new_stretch = helper_->StretchAmount();
 
       stretch_scroll_force_.set_x(ReboundDeltaForElasticDelta(new_stretch.x()));
       stretch_scroll_force_.set_y(ReboundDeltaForElasticDelta(new_stretch.y()));
     } else {
-      client_->AdjustScrollPositionToBoundsIfNecessary();
+      helper_->AdjustScrollPositionToBoundsIfNecessary();
 
       StopSnapRubberbandTimer();
       stretch_scroll_force_ = gfx::Vector2dF();
@@ -397,22 +426,24 @@
     start_time_ = base::Time::Now();
     start_stretch_ = gfx::Vector2dF();
   }
+
+  helper_->SnapRubberbandTimerFired();
 }
 
-bool ScrollElasticityController::IsRubberbandInProgress() const {
+bool InputScrollElasticityController::IsRubberbandInProgress() const {
   if (!in_scroll_gesture_ && !momentum_scroll_in_progress_ &&
       !snap_rubberband_timer_is_active_)
     return false;
 
-  return !client_->StretchAmount().IsZero();
+  return !helper_->StretchAmount().IsZero();
 }
 
-void ScrollElasticityController::StopSnapRubberbandTimer() {
-  client_->StopSnapRubberbandTimer();
+void InputScrollElasticityController::StopSnapRubberbandTimer() {
+  helper_->StopSnapRubberbandTimer();
   snap_rubberband_timer_is_active_ = false;
 }
 
-void ScrollElasticityController::SnapRubberband() {
+void InputScrollElasticityController::SnapRubberband() {
   double time_delta = SystemUptime() - last_momentum_scroll_timestamp_;
   if (last_momentum_scroll_timestamp_ &&
       time_delta >= kScrollVelocityZeroingTimeout)
@@ -430,18 +461,18 @@
   // If there's no momentum scroll or stretch amount, no need to start the
   // timer.
   if (!momentum_scroll_in_progress_ &&
-      client_->StretchAmount() == gfx::Vector2dF()) {
+      helper_->StretchAmount() == gfx::Vector2dF()) {
     start_time_ = base::Time();
     stretch_scroll_force_ = gfx::Vector2dF();
     return;
   }
 
   start_time_ = base::Time::Now();
-  client_->StartSnapRubberbandTimer();
+  helper_->StartSnapRubberbandTimer();
   snap_rubberband_timer_is_active_ = true;
 }
 
-bool ScrollElasticityController::ShouldHandleEvent(
+bool InputScrollElasticityController::ShouldHandleEvent(
     const blink::WebMouseWheelEvent& wheel_event) {
   // Once any scrolling has happened, all future events should be handled.
   if (has_scrolled_)
@@ -451,12 +482,12 @@
   if (wheel_event.deltaX == 0 && wheel_event.deltaY == 0)
     return false;
 
-  // If the client isn't pinned, then the event is guaranteed to cause
+  // If the helper isn't pinned, then the event is guaranteed to cause
   // scrolling.
-  if (!client_->PinnedInDirection(gfx::Vector2dF(-wheel_event.deltaX, 0)))
+  if (!helper_->PinnedInDirection(gfx::Vector2dF(-wheel_event.deltaX, 0)))
     return true;
 
-  // If the event is pinned, then the client can't scroll, but it might rubber
+  // If the event is pinned, then the helper can't scroll, but it might rubber
   // band.
   // Check if the event allows rubber banding.
   if (wheel_event.deltaY == 0) {
diff --git a/content/renderer/input/input_scroll_elasticity_controller.h b/content/renderer/input/input_scroll_elasticity_controller.h
index 79cb4ed..d9d6a47 100644
--- a/content/renderer/input/input_scroll_elasticity_controller.h
+++ b/content/renderer/input/input_scroll_elasticity_controller.h
@@ -6,11 +6,11 @@
 #define CONTENT_RENDERER_INPUT_INPUT_SCROLL_ELASTICITY_CONTROLLER_H_
 
 #include "base/macros.h"
-#include "base/time/time.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/input/scroll_elasticity_helper.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "ui/gfx/geometry/vector2d_f.h"
 
-// ScrollElasticityController and ScrollElasticityControllerClient are based on
+// InputScrollElasticityController is based on
 // WebKit/Source/platform/mac/ScrollElasticityController.h
 /*
  * Copyright (C) 2011 Apple Inc. All rights reserved.
@@ -37,41 +37,25 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+namespace cc {
+struct InputHandlerScrollResult;
+}  // namespace cc
+
 namespace content {
 
-class ScrollElasticityControllerClient {
- protected:
-  virtual ~ScrollElasticityControllerClient() {}
-
+class InputScrollElasticityController {
  public:
-  virtual bool AllowsHorizontalStretching() = 0;
-  virtual bool AllowsVerticalStretching() = 0;
-  // The amount that the view is stretched past the normal allowable bounds.
-  // The "overhang" amount.
-  virtual gfx::Vector2dF StretchAmount() = 0;
-  virtual bool PinnedInDirection(const gfx::Vector2dF& direction) = 0;
-  virtual bool CanScrollHorizontally() = 0;
-  virtual bool CanScrollVertically() = 0;
+  explicit InputScrollElasticityController(cc::ScrollElasticityHelper* helper);
+  virtual ~InputScrollElasticityController();
 
-  // Return the absolute scroll position, not relative to the scroll origin.
-  virtual gfx::Vector2dF AbsoluteScrollPosition() = 0;
+  base::WeakPtr<InputScrollElasticityController> GetWeakPtr();
+  void ObserveWheelEventAndResult(
+      const blink::WebMouseWheelEvent& wheel_event,
+      const cc::InputHandlerScrollResult& scroll_result);
 
-  virtual void ImmediateScrollBy(const gfx::Vector2dF& scroll) = 0;
-  virtual void ImmediateScrollByWithoutContentEdgeConstraints(
-      const gfx::Vector2dF& scroll) = 0;
-  virtual void StartSnapRubberbandTimer() = 0;
-  virtual void StopSnapRubberbandTimer() = 0;
+  void Animate(base::TimeTicks time);
 
-  // If the current scroll position is within the overhang area, this function
-  // will cause
-  // the page to scroll to the nearest boundary point.
-  virtual void AdjustScrollPositionToBoundsIfNecessary() = 0;
-};
-
-class ScrollElasticityController {
- public:
-  explicit ScrollElasticityController(ScrollElasticityControllerClient*);
-
+ private:
   // This method is responsible for both scrolling and rubber-banding.
   //
   // Events are passed by IPC from the embedder. Events on Mac are grouped
@@ -89,7 +73,6 @@
 
   bool IsRubberbandInProgress() const;
 
- private:
   void StopSnapRubberbandTimer();
   void SnapRubberband();
 
@@ -102,12 +85,12 @@
   // + No previous events in this gesture have caused any scrolling or rubber
   // banding.
   // + The event contains a horizontal component.
-  // + The client's view is pinned in the horizontal direction of the event.
+  // + The helper's view is pinned in the horizontal direction of the event.
   // + The wheel event disallows rubber banding in the horizontal direction
   // of the event.
   bool ShouldHandleEvent(const blink::WebMouseWheelEvent& wheel_event);
 
-  ScrollElasticityControllerClient* client_;
+  cc::ScrollElasticityHelper* helper_;
 
   // There is an active scroll gesture event. This parameter only gets set to
   // false after the rubber band has been snapped, and before a new gesture
@@ -135,7 +118,8 @@
 
   bool snap_rubberband_timer_is_active_;
 
-  DISALLOW_COPY_AND_ASSIGN(ScrollElasticityController);
+  base::WeakPtrFactory<InputScrollElasticityController> weak_factory_;
+  DISALLOW_COPY_AND_ASSIGN(InputScrollElasticityController);
 };
 
 }  // namespace content