Remove LayerScrollOffsetDelegate, make all input paths go thru proxy.

This removes the cc::LayerScrollOffsetDelegate interface, moving it
into the InputHandlerClient interface.

It also removes the pointer to cc::LayerTreeHostImpl (as a
cc::InputHandler*) from the WebView embedder code. Instead make the
WebView code always go through the SynchronousInputHandlerProxy (which
is the InputHandlerProxy) to talk to the compositor about input-related
things.

R=boliu, [email protected]
BUG=531746
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

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

Cr-Commit-Position: refs/heads/master@{#350056}
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index b2fa8d2..51db013 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -52,6 +52,13 @@
   virtual void Animate(base::TimeTicks time) = 0;
   virtual void MainThreadHasStoppedFlinging() = 0;
   virtual void ReconcileElasticOverscrollAndRootScroll() = 0;
+  virtual void UpdateRootLayerStateForSynchronousInputHandler(
+      const gfx::ScrollOffset& total_scroll_offset,
+      const gfx::ScrollOffset& max_scroll_offset,
+      const gfx::SizeF& scrollable_size,
+      float page_scale_factor,
+      float min_page_scale_factor,
+      float max_page_scale_factor) = 0;
 
  protected:
   InputHandlerClient() {}
@@ -126,15 +133,13 @@
   // returned SCROLL_STARTED.
   virtual void ScrollEnd() = 0;
 
-  virtual void SetRootLayerScrollOffsetDelegate(
-      LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate) = 0;
+  // Requests a callback to UpdateRootLayerStateForSynchronousInputHandler()
+  // giving the current root scroll and page scale information.
+  virtual void RequestUpdateForSynchronousInputHandler() = 0;
 
-  // Called when the delegate's root scroll offset has changed for reasons
-  // other than a SetTotalScrollOffset call. This passes along the new value of
-  // the offset.
-  // NOTE: This should only called after a valid delegate was set via a call to
-  // SetRootLayerScrollOffsetDelegate.
-  virtual void OnRootLayerDelegatedScrollOffsetChanged(
+  // Called when the root scroll offset has been changed in the synchronous
+  // input handler by the application (outside of input event handling).
+  virtual void SetSynchronousInputHandlerRootScrollOffset(
       const gfx::ScrollOffset& root_offset) = 0;
 
   virtual void PinchGestureBegin() = 0;
diff --git a/cc/input/layer_scroll_offset_delegate.h b/cc/input/layer_scroll_offset_delegate.h
deleted file mode 100644
index 84a6f5b..0000000
--- a/cc/input/layer_scroll_offset_delegate.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2013 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_LAYER_SCROLL_OFFSET_DELEGATE_H_
-#define CC_INPUT_LAYER_SCROLL_OFFSET_DELEGATE_H_
-
-#include "base/basictypes.h"
-#include "base/callback_forward.h"
-#include "base/time/time.h"
-#include "ui/gfx/geometry/scroll_offset.h"
-#include "ui/gfx/geometry/size_f.h"
-
-namespace cc {
-
-// The LayerScrollOffsetDelegate allows for the embedder to take ownership of
-// the scroll offset of the root layer.
-//
-// The LayerScrollOffsetDelegate is only used on the impl thread.
-class LayerScrollOffsetDelegate {
- public:
-  // This is called by the compositor to notify the delegate of any change to
-  // the following parameters:
-  // |total_scroll_offset| current scroll offset of the root layer,
-  // |max_scroll_offset| total scroll offset upper bound for the root layer,
-  // |scrollable_size| root layer scrollable size,
-  // |page_scale_factor| current page scale,
-  // |min_page_scale_factor| page scale lower limit,
-  // |max_page_scale_factor| page scale upper limit.
-  virtual void UpdateRootLayerState(
-      const gfx::ScrollOffset& total_scroll_offset,
-      const gfx::ScrollOffset& max_scroll_offset,
-      const gfx::SizeF& scrollable_size,
-      float page_scale_factor,
-      float min_page_scale_factor,
-      float max_page_scale_factor) = 0;
-
- protected:
-  LayerScrollOffsetDelegate() {}
-  virtual ~LayerScrollOffsetDelegate() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LayerScrollOffsetDelegate);
-};
-
-}  // namespace cc
-
-#endif  // CC_INPUT_LAYER_SCROLL_OFFSET_DELEGATE_H_
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 0be5ac0..36e7bdd 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -185,7 +185,6 @@
       scroll_affects_scroll_handler_(false),
       scroll_layer_id_when_mouse_over_scrollbar_(0),
       tile_priorities_dirty_(false),
-      root_layer_scroll_offset_delegate_(nullptr),
       settings_(settings),
       visible_(true),
       cached_managed_memory_policy_(
@@ -425,8 +424,9 @@
   AnimateScrollbars(monotonic_time);
   AnimateTopControls(monotonic_time);
 
-  // Animating stuff can change the root scroll offset, so inform the delegate.
-  NotifyRootLayerScrollOffsetDelegate();
+  // Animating stuff can change the root scroll offset, so inform the
+  // synchronous input handler.
+  UpdateRootLayerStateForSynchronousInputHandler();
 }
 
 bool LayerTreeHostImpl::PrepareTiles() {
@@ -1929,8 +1929,9 @@
         pending_page_scale_animation->scale,
         pending_page_scale_animation->duration);
   }
-  // Activation can change the root scroll offset, so inform the delegate.
-  NotifyRootLayerScrollOffsetDelegate();
+  // Activation can change the root scroll offset, so inform the synchronous
+  // input handler.
+  UpdateRootLayerStateForSynchronousInputHandler();
 }
 
 void LayerTreeHostImpl::SetVisible(bool visible) {
@@ -2714,8 +2715,9 @@
   scroll_result.accumulated_root_overscroll = accumulated_root_overscroll_;
   scroll_result.unused_scroll_delta = unused_root_delta;
 
-  // Scrolling can change the root scroll offset, so inform the delegate.
-  NotifyRootLayerScrollOffsetDelegate();
+  // Scrolling can change the root scroll offset, so inform the synchronous
+  // input handler.
+  UpdateRootLayerStateForSynchronousInputHandler();
 
   return scroll_result;
 }
@@ -2762,20 +2764,17 @@
   return false;
 }
 
-void LayerTreeHostImpl::SetRootLayerScrollOffsetDelegate(
-      LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate) {
-  root_layer_scroll_offset_delegate_ = root_layer_scroll_offset_delegate;
-  // When first set, clobber the delegate's scroll offset with compositor's.
-  NotifyRootLayerScrollOffsetDelegate();
+void LayerTreeHostImpl::RequestUpdateForSynchronousInputHandler() {
+  UpdateRootLayerStateForSynchronousInputHandler();
 }
 
-void LayerTreeHostImpl::OnRootLayerDelegatedScrollOffsetChanged(
+void LayerTreeHostImpl::SetSynchronousInputHandlerRootScrollOffset(
     const gfx::ScrollOffset& root_offset) {
   active_tree_->DistributeRootScrollOffset(root_offset);
   client_->SetNeedsCommitOnImplThread();
-  // After applying the delegate's scroll offset, tell it what we ended up with.
-  DCHECK(root_layer_scroll_offset_delegate_);
-  NotifyRootLayerScrollOffsetDelegate();
+  // After applying the synchronous input handler's scroll offset, tell it what
+  // we ended up with.
+  UpdateRootLayerStateForSynchronousInputHandler();
   // No need to SetNeedsRedraw, this is for WebView and every frame has redraw
   // requested by the WebView embedder already.
 }
@@ -2914,8 +2913,9 @@
   client_->SetNeedsCommitOnImplThread();
   SetNeedsRedraw();
   client_->RenewTreePriority();
-  // Pinching can change the root scroll offset, so inform the delegate.
-  NotifyRootLayerScrollOffsetDelegate();
+  // Pinching can change the root scroll offset, so inform the synchronous input
+  // handler.
+  UpdateRootLayerStateForSynchronousInputHandler();
 }
 
 void LayerTreeHostImpl::PinchGestureEnd() {
@@ -3391,10 +3391,10 @@
     (*it)->OnForwardScrollUpdateToMainThreadOnImpl();
 }
 
-void LayerTreeHostImpl::NotifyRootLayerScrollOffsetDelegate() {
-  if (!root_layer_scroll_offset_delegate_)
+void LayerTreeHostImpl::UpdateRootLayerStateForSynchronousInputHandler() {
+  if (!input_handler_client_)
     return;
-  root_layer_scroll_offset_delegate_->UpdateRootLayerState(
+  input_handler_client_->UpdateRootLayerStateForSynchronousInputHandler(
       active_tree_->TotalScrollOffset(), active_tree_->TotalMaxScrollOffset(),
       active_tree_->ScrollableSize(), active_tree_->current_page_scale_factor(),
       active_tree_->min_page_scale_factor(),
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 358b88d..f3d4a67 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -21,7 +21,6 @@
 #include "cc/debug/frame_timing_tracker.h"
 #include "cc/debug/micro_benchmark_controller_impl.h"
 #include "cc/input/input_handler.h"
-#include "cc/input/layer_scroll_offset_delegate.h"
 #include "cc/input/top_controls_manager_client.h"
 #include "cc/layers/layer_lists.h"
 #include "cc/layers/render_pass_sink.h"
@@ -173,9 +172,8 @@
       const gfx::Vector2dF& scroll_delta) override;
   bool ScrollVerticallyByPage(const gfx::Point& viewport_point,
                               ScrollDirection direction) override;
-  void SetRootLayerScrollOffsetDelegate(
-      LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate) override;
-  void OnRootLayerDelegatedScrollOffsetChanged(
+  void RequestUpdateForSynchronousInputHandler() override;
+  void SetSynchronousInputHandlerRootScrollOffset(
       const gfx::ScrollOffset& root_offset) override;
   void ScrollEnd() override;
   InputHandler::ScrollStatus FlingScrollBegin() override;
@@ -674,7 +672,8 @@
 
   void NotifySwapPromiseMonitorsOfSetNeedsRedraw();
   void NotifySwapPromiseMonitorsOfForwardingToMainThread();
-  void NotifyRootLayerScrollOffsetDelegate();
+
+  void UpdateRootLayerStateForSynchronousInputHandler();
 
   void ScrollAnimationCreate(LayerImpl* layer_impl,
                              const gfx::ScrollOffset& target_offset,
@@ -737,9 +736,6 @@
 
   bool tile_priorities_dirty_;
 
-  // The optional delegate for the root layer scroll offset.
-  LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate_;
-
   const LayerTreeSettings settings_;
   LayerTreeDebugState debug_state_;
   bool visible_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index eeaf88f..9921731 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -4557,21 +4557,26 @@
             inner_viewport_scroll_layer->MaxScrollOffset());
 }
 
-class TestScrollOffsetDelegate : public LayerScrollOffsetDelegate {
+class TestInputHandlerClient : public InputHandlerClient {
  public:
-  TestScrollOffsetDelegate()
+  TestInputHandlerClient()
       : page_scale_factor_(0.f),
         min_page_scale_factor_(-1.f),
         max_page_scale_factor_(-1.f) {}
+  ~TestInputHandlerClient() override {}
 
-  ~TestScrollOffsetDelegate() override {}
-
-  void UpdateRootLayerState(const gfx::ScrollOffset& total_scroll_offset,
-                            const gfx::ScrollOffset& max_scroll_offset,
-                            const gfx::SizeF& scrollable_size,
-                            float page_scale_factor,
-                            float min_page_scale_factor,
-                            float max_page_scale_factor) override {
+  // InputHandlerClient implementation.
+  void WillShutdown() override {}
+  void Animate(base::TimeTicks time) override {}
+  void MainThreadHasStoppedFlinging() override {}
+  void ReconcileElasticOverscrollAndRootScroll() override {}
+  void UpdateRootLayerStateForSynchronousInputHandler(
+      const gfx::ScrollOffset& total_scroll_offset,
+      const gfx::ScrollOffset& max_scroll_offset,
+      const gfx::SizeF& scrollable_size,
+      float page_scale_factor,
+      float min_page_scale_factor,
+      float max_page_scale_factor) override {
     DCHECK(total_scroll_offset.x() <= max_scroll_offset.x());
     DCHECK(total_scroll_offset.y() <= max_scroll_offset.y());
     last_set_scroll_offset_ = total_scroll_offset;
@@ -4616,51 +4621,56 @@
 };
 
 TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
-  TestScrollOffsetDelegate scroll_delegate;
+  TestInputHandlerClient scroll_watcher;
   host_impl_->SetViewportSize(gfx::Size(10, 20));
   LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
   LayerImpl* clip_layer = scroll_layer->parent()->parent();
   clip_layer->SetBounds(gfx::Size(10, 20));
 
-  // Setting the delegate results in the current scroll offset being set.
+  host_impl_->BindToClient(&scroll_watcher);
+
   gfx::Vector2dF initial_scroll_delta(10.f, 10.f);
   scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset());
   scroll_layer->SetScrollDelta(initial_scroll_delta);
-  host_impl_->SetRootLayerScrollOffsetDelegate(&scroll_delegate);
-  EXPECT_EQ(initial_scroll_delta.ToString(),
-            scroll_delegate.last_set_scroll_offset().ToString());
+
+  EXPECT_EQ(gfx::ScrollOffset(), scroll_watcher.last_set_scroll_offset());
+
+  // Requesting an update results in the current scroll offset being set.
+  host_impl_->RequestUpdateForSynchronousInputHandler();
+  EXPECT_EQ(gfx::ScrollOffset(initial_scroll_delta),
+            scroll_watcher.last_set_scroll_offset());
 
   // Setting the delegate results in the scrollable_size, max_scroll_offset,
   // page_scale_factor and {min|max}_page_scale_factor being set.
-  EXPECT_EQ(gfx::SizeF(100, 100), scroll_delegate.scrollable_size());
-  EXPECT_EQ(gfx::ScrollOffset(90, 80), scroll_delegate.max_scroll_offset());
-  EXPECT_EQ(1.f, scroll_delegate.page_scale_factor());
-  EXPECT_EQ(1.f, scroll_delegate.min_page_scale_factor());
-  EXPECT_EQ(1.f, scroll_delegate.max_page_scale_factor());
+  EXPECT_EQ(gfx::SizeF(100, 100), scroll_watcher.scrollable_size());
+  EXPECT_EQ(gfx::ScrollOffset(90, 80), scroll_watcher.max_scroll_offset());
+  EXPECT_EQ(1.f, scroll_watcher.page_scale_factor());
+  EXPECT_EQ(1.f, scroll_watcher.min_page_scale_factor());
+  EXPECT_EQ(1.f, scroll_watcher.max_page_scale_factor());
 
   // Put a page scale on the tree.
   host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 0.5f, 4.f);
-  EXPECT_EQ(1.f, scroll_delegate.page_scale_factor());
-  EXPECT_EQ(1.f, scroll_delegate.min_page_scale_factor());
-  EXPECT_EQ(1.f, scroll_delegate.max_page_scale_factor());
+  EXPECT_EQ(1.f, scroll_watcher.page_scale_factor());
+  EXPECT_EQ(1.f, scroll_watcher.min_page_scale_factor());
+  EXPECT_EQ(1.f, scroll_watcher.max_page_scale_factor());
   // Activation will update the delegate.
   host_impl_->ActivateSyncTree();
-  EXPECT_EQ(2.f, scroll_delegate.page_scale_factor());
-  EXPECT_EQ(.5f, scroll_delegate.min_page_scale_factor());
-  EXPECT_EQ(4.f, scroll_delegate.max_page_scale_factor());
+  EXPECT_EQ(2.f, scroll_watcher.page_scale_factor());
+  EXPECT_EQ(.5f, scroll_watcher.min_page_scale_factor());
+  EXPECT_EQ(4.f, scroll_watcher.max_page_scale_factor());
 
   // Reset the page scale for the rest of the test.
   host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f);
-  EXPECT_EQ(2.f, scroll_delegate.page_scale_factor());
-  EXPECT_EQ(.5f, scroll_delegate.min_page_scale_factor());
-  EXPECT_EQ(4.f, scroll_delegate.max_page_scale_factor());
+  EXPECT_EQ(2.f, scroll_watcher.page_scale_factor());
+  EXPECT_EQ(.5f, scroll_watcher.min_page_scale_factor());
+  EXPECT_EQ(4.f, scroll_watcher.max_page_scale_factor());
 
   // Animating page scale can change the root offset, so it should update the
   // delegate.
   host_impl_->Animate();
-  EXPECT_EQ(1.f, scroll_delegate.page_scale_factor());
-  EXPECT_EQ(.5f, scroll_delegate.min_page_scale_factor());
-  EXPECT_EQ(4.f, scroll_delegate.max_page_scale_factor());
+  EXPECT_EQ(1.f, scroll_watcher.page_scale_factor());
+  EXPECT_EQ(.5f, scroll_watcher.min_page_scale_factor());
+  EXPECT_EQ(4.f, scroll_watcher.max_page_scale_factor());
 
   // The pinch gesture doesn't put the delegate into a state where the scroll
   // offset is outside of the scroll range.  (this is verified by DCHECKs in the
@@ -4678,19 +4688,19 @@
 
   EXPECT_EQ(InputHandler::SCROLL_STARTED,
             host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
-  host_impl_->OnRootLayerDelegatedScrollOffsetChanged(current_offset);
+  host_impl_->SetSynchronousInputHandlerRootScrollOffset(current_offset);
 
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   EXPECT_EQ(ScrollOffsetWithDelta(current_offset, scroll_delta),
-            scroll_delegate.last_set_scroll_offset());
+            scroll_watcher.last_set_scroll_offset());
 
   current_offset = gfx::ScrollOffset(42.f, 41.f);
-  host_impl_->OnRootLayerDelegatedScrollOffsetChanged(current_offset);
+  host_impl_->SetSynchronousInputHandlerRootScrollOffset(current_offset);
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   EXPECT_EQ(current_offset + gfx::ScrollOffset(scroll_delta),
-            scroll_delegate.last_set_scroll_offset());
+            scroll_watcher.last_set_scroll_offset());
   host_impl_->ScrollEnd();
-  host_impl_->OnRootLayerDelegatedScrollOffsetChanged(gfx::ScrollOffset());
+  host_impl_->SetSynchronousInputHandlerRootScrollOffset(gfx::ScrollOffset());
 
   // Forces a full tree synchronization and ensures that the scroll delegate
   // sees the correct size of the new tree.
@@ -4699,16 +4709,10 @@
   host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
   CreateScrollAndContentsLayers(host_impl_->pending_tree(), new_size);
   host_impl_->ActivateSyncTree();
-  EXPECT_EQ(new_size, scroll_delegate.scrollable_size());
+  EXPECT_EQ(new_size, scroll_watcher.scrollable_size());
 
-  // Un-setting the delegate should propagate the delegate's current offset to
-  // the root scrollable layer.
-  current_offset = gfx::ScrollOffset(13.f, 12.f);
-  host_impl_->OnRootLayerDelegatedScrollOffsetChanged(current_offset);
-  host_impl_->SetRootLayerScrollOffsetDelegate(NULL);
-
-  EXPECT_EQ(current_offset.ToString(),
-            scroll_layer->CurrentScrollOffset().ToString());
+  // Tear down the LayerTreeHostImpl before the InputHandlerClient.
+  host_impl_.reset();
 }
 
 void CheckLayerScrollDelta(LayerImpl* layer, gfx::Vector2dF scroll_delta) {
@@ -4723,12 +4727,10 @@
 
 TEST_F(LayerTreeHostImplTest,
        ExternalRootLayerScrollOffsetDelegationReflectedInNextDraw) {
-  TestScrollOffsetDelegate scroll_delegate;
   host_impl_->SetViewportSize(gfx::Size(10, 20));
   LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
   LayerImpl* clip_layer = scroll_layer->parent()->parent();
   clip_layer->SetBounds(gfx::Size(10, 20));
-  host_impl_->SetRootLayerScrollOffsetDelegate(&scroll_delegate);
 
   // Draw first frame to clear any pending draws and check scroll.
   DrawFrame();
@@ -4737,7 +4739,7 @@
 
   // Set external scroll delta on delegate and notify LayerTreeHost.
   gfx::ScrollOffset scroll_offset(10.f, 10.f);
-  host_impl_->OnRootLayerDelegatedScrollOffsetChanged(scroll_offset);
+  host_impl_->SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
 
   // Check scroll delta reflected in layer.
   LayerTreeHostImpl::FrameData frame;
@@ -4746,8 +4748,6 @@
   host_impl_->DidDrawAllLayers(frame);
   EXPECT_FALSE(frame.has_no_damage);
   CheckLayerScrollDelta(scroll_layer, ScrollOffsetToVector2dF(scroll_offset));
-
-  host_impl_->SetRootLayerScrollOffsetDelegate(NULL);
 }
 
 TEST_F(LayerTreeHostImplTest, OverscrollRoot) {
@@ -7698,9 +7698,6 @@
 
   SetupVirtualViewportLayers(content_size, outer_viewport, inner_viewport);
 
-  TestScrollOffsetDelegate scroll_delegate;
-  host_impl_->SetRootLayerScrollOffsetDelegate(&scroll_delegate);
-
   LayerImpl* outer_scroll = host_impl_->OuterViewportScrollLayer();
   LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer();
   DrawFrame();
@@ -7712,7 +7709,7 @@
 
     gfx::ScrollOffset current_offset(70.f, 100.f);
 
-    host_impl_->OnRootLayerDelegatedScrollOffsetChanged(current_offset);
+    host_impl_->SetSynchronousInputHandlerRootScrollOffset(current_offset);
     EXPECT_EQ(gfx::ScrollOffset(25.f, 40.f), inner_scroll->MaxScrollOffset());
     EXPECT_EQ(gfx::ScrollOffset(50.f, 80.f), outer_scroll->MaxScrollOffset());
 
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 484b4a2e..79fde4f2 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -1066,6 +1066,19 @@
     }
   }
 
+  void UpdateRootLayerStateForSynchronousInputHandler(
+      const gfx::ScrollOffset& total_scroll_offset,
+      const gfx::ScrollOffset& max_scroll_offset,
+      const gfx::SizeF& scrollable_size,
+      float page_scale_factor,
+      float min_page_scale_factor,
+      float max_page_scale_factor) override {
+    if (!task_runner_->BelongsToCurrentThread()) {
+      ADD_FAILURE() << "UpdateRootLayerStateForSynchronousInputHandler called "
+                    << " on wrong thread";
+    }
+  }
+
  private:
   base::SingleThreadTaskRunner* task_runner_;
   bool* received_stop_flinging_;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 00db0a64..61a64d634 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -19,7 +19,6 @@
 #include "cc/base/synced_property.h"
 #include "cc/debug/devtools_instrumentation.h"
 #include "cc/debug/traced_value.h"
-#include "cc/input/layer_scroll_offset_delegate.h"
 #include "cc/input/page_scale_animation.h"
 #include "cc/layers/heads_up_display_layer_impl.h"
 #include "cc/layers/layer.h"
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.cc b/content/browser/android/in_process/synchronous_compositor_impl.cc
index 06d622ce..98466724 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.cc
+++ b/content/browser/android/in_process/synchronous_compositor_impl.cc
@@ -8,7 +8,6 @@
 #include "base/bind.h"
 #include "base/lazy_instance.h"
 #include "base/message_loop/message_loop.h"
-#include "cc/input/input_handler.h"
 #include "content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.h"
 #include "content/browser/android/in_process/synchronous_compositor_factory_impl.h"
 #include "content/browser/android/in_process/synchronous_compositor_registry.h"
@@ -21,6 +20,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "ui/gfx/geometry/scroll_offset.h"
 #include "ui/gl/gl_surface.h"
 
 namespace content {
@@ -79,7 +79,7 @@
       begin_frame_source_(nullptr),
       contents_(contents),
       routing_id_(contents->GetRoutingID()),
-      input_handler_(nullptr),
+      synchronous_input_handler_proxy_(nullptr),
       registered_with_client_(false),
       is_active_(true),
       renderer_needs_begin_frames_(false),
@@ -92,7 +92,7 @@
 SynchronousCompositorImpl::~SynchronousCompositorImpl() {
   DCHECK(!output_surface_);
   DCHECK(!begin_frame_source_);
-  DCHECK(!input_handler_);
+  DCHECK(!synchronous_input_handler_proxy_);
 }
 
 void SynchronousCompositorImpl::SetClient(
@@ -120,7 +120,7 @@
   DCHECK(CalledOnValidThread());
   DCHECK(compositor_client_);
   DCHECK(output_surface_);
-  DCHECK(input_handler_);
+  DCHECK(synchronous_input_handler_proxy_);
   DCHECK(!registered_with_client_);
   registered_with_client_ = true;
 
@@ -130,12 +130,10 @@
     base::Bind(&SynchronousCompositorImpl::DidActivatePendingTree,
                weak_ptr_factory_.GetWeakPtr()));
 
-  // Setting the delegate causes UpdateRootLayerState immediately so do it
-  // after setting the client.
-  input_handler_->SetRootLayerScrollOffsetDelegate(this);
   // This disables the input system from animating inputs autonomously, instead
   // routing all input animations through the SynchronousInputHandler, which is
-  // |this| class.
+  // |this| class. Calling this causes an UpdateRootLayerState() immediately so,
+  // do it after setting the client.
   synchronous_input_handler_proxy_->SetOnlySynchronouslyAnimateRootFlings(this);
 }
 
@@ -155,19 +153,16 @@
 void SynchronousCompositorImpl::DidInitializeRendererObjects(
     SynchronousCompositorOutputSurface* output_surface,
     SynchronousCompositorExternalBeginFrameSource* begin_frame_source,
-    cc::InputHandler* input_handler,
     SynchronousInputHandlerProxy* synchronous_input_handler_proxy) {
   DCHECK(!output_surface_);
   DCHECK(!begin_frame_source_);
   DCHECK(output_surface);
   DCHECK(begin_frame_source);
   DCHECK(compositor_client_);
-  DCHECK(input_handler);
   DCHECK(synchronous_input_handler_proxy);
 
   output_surface_ = output_surface;
   begin_frame_source_ = begin_frame_source;
-  input_handler_ = input_handler;
   synchronous_input_handler_proxy_ = synchronous_input_handler_proxy;
 
   output_surface_->SetCompositor(this);
@@ -180,16 +175,18 @@
   DCHECK(compositor_client_);
 
   if (registered_with_client_) {
-    input_handler_->SetRootLayerScrollOffsetDelegate(nullptr);
     output_surface_->SetTreeActivationCallback(base::Closure());
     compositor_client_->DidDestroyCompositor(this);
     registered_with_client_ = false;
   }
 
+  // This object is being destroyed, so remove pointers to it.
   begin_frame_source_->SetCompositor(nullptr);
   output_surface_->SetCompositor(nullptr);
+  synchronous_input_handler_proxy_->SetOnlySynchronouslyAnimateRootFlings(
+      nullptr);
 
-  input_handler_ = nullptr;
+  synchronous_input_handler_proxy_ = nullptr;
   begin_frame_source_ = nullptr;
   output_surface_ = nullptr;
   // Don't propogate this signal from one renderer to the next.
@@ -276,9 +273,10 @@
 void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset(
     const gfx::ScrollOffset& root_offset) {
   DCHECK(CalledOnValidThread());
-  if (!input_handler_)
+  if (!synchronous_input_handler_proxy_)
     return;
-  input_handler_->OnRootLayerDelegatedScrollOffsetChanged(root_offset);
+  synchronous_input_handler_proxy_->SynchronouslySetRootScrollOffset(
+      root_offset);
 }
 
 void SynchronousCompositorImpl::SetIsActive(bool is_active) {
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.h b/content/browser/android/in_process/synchronous_compositor_impl.h
index 2f310bd..049fe78 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.h
+++ b/content/browser/android/in_process/synchronous_compositor_impl.h
@@ -10,7 +10,6 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
-#include "cc/input/layer_scroll_offset_delegate.h"
 #include "content/browser/android/in_process/synchronous_compositor_output_surface.h"
 #include "content/common/input/input_event_ack_state.h"
 #include "content/public/browser/android/synchronous_compositor.h"
@@ -38,8 +37,7 @@
 // This class is created on the main thread but most of the APIs are called
 // from the Compositor thread.
 class SynchronousCompositorImpl
-    : public cc::LayerScrollOffsetDelegate,
-      public SynchronousInputHandler,
+    : public SynchronousInputHandler,
       public SynchronousCompositor,
       public WebContentsUserData<SynchronousCompositorImpl> {
  public:
@@ -55,7 +53,6 @@
   void DidInitializeRendererObjects(
       SynchronousCompositorOutputSurface* output_surface,
       SynchronousCompositorExternalBeginFrameSource* begin_frame_source,
-      cc::InputHandler* input_handler,
       SynchronousInputHandlerProxy* synchronous_input_handler_proxy);
   void DidDestroyRendererObjects();
 
@@ -86,8 +83,6 @@
 
   // SynchronousInputHandler
   void SetNeedsSynchronousAnimateInput() override;
-
-  // LayerScrollOffsetDelegate
   void UpdateRootLayerState(const gfx::ScrollOffset& total_scroll_offset,
                             const gfx::ScrollOffset& max_scroll_offset,
                             const gfx::SizeF& scrollable_size,
@@ -117,7 +112,6 @@
   SynchronousCompositorExternalBeginFrameSource* begin_frame_source_;
   WebContents* contents_;
   const int routing_id_;
-  cc::InputHandler* input_handler_;
   SynchronousInputHandlerProxy* synchronous_input_handler_proxy_;
   bool registered_with_client_;
   bool is_active_;
diff --git a/content/browser/android/in_process/synchronous_compositor_registry.cc b/content/browser/android/in_process/synchronous_compositor_registry.cc
index 84c45a2..d101797 100644
--- a/content/browser/android/in_process/synchronous_compositor_registry.cc
+++ b/content/browser/android/in_process/synchronous_compositor_registry.cc
@@ -107,13 +107,11 @@
 
 void SynchronousCompositorRegistry::RegisterInputHandler(
     int routing_id,
-    cc::InputHandler* input_handler,
     SynchronousInputHandlerProxy* synchronous_input_handler_proxy) {
   DCHECK(CalledOnValidThread());
-  DCHECK(input_handler);
+  DCHECK(synchronous_input_handler_proxy);
   Entry& entry = entry_map_[routing_id];
-  DCHECK(!entry.input_handler);
-  entry.input_handler = input_handler;
+  DCHECK(!entry.synchronous_input_handler_proxy);
   entry.synchronous_input_handler_proxy = synchronous_input_handler_proxy;
   CheckIsReady(routing_id);
 }
@@ -125,7 +123,7 @@
 
   if (entry.IsReady())
     UnregisterObjects(routing_id);
-  entry.input_handler = nullptr;
+  entry.synchronous_input_handler_proxy = nullptr;
   RemoveEntryIfNeeded(routing_id);
 }
 
@@ -134,7 +132,7 @@
   Entry& entry = entry_map_[routing_id];
   if (entry.IsReady()) {
     entry.compositor->DidInitializeRendererObjects(
-        entry.output_surface, entry.begin_frame_source, entry.input_handler,
+        entry.output_surface, entry.begin_frame_source,
         entry.synchronous_input_handler_proxy);
   }
 }
@@ -150,7 +148,7 @@
   DCHECK(entry_map_.find(routing_id) != entry_map_.end());
   Entry& entry = entry_map_[routing_id];
   if (!entry.compositor && !entry.begin_frame_source && !entry.output_surface &&
-      !entry.input_handler) {
+      !entry.synchronous_input_handler_proxy) {
     entry_map_.erase(routing_id);
   }
 }
@@ -163,11 +161,10 @@
     : compositor(nullptr),
       begin_frame_source(nullptr),
       output_surface(nullptr),
-      input_handler(nullptr),
       synchronous_input_handler_proxy(nullptr) {}
 
 bool SynchronousCompositorRegistry::Entry::IsReady() {
-  return compositor && begin_frame_source && output_surface && input_handler &&
+  return compositor && begin_frame_source && output_surface &&
          synchronous_input_handler_proxy;
 }
 
diff --git a/content/browser/android/in_process/synchronous_compositor_registry.h b/content/browser/android/in_process/synchronous_compositor_registry.h
index f84bb0d..f7dc913 100644
--- a/content/browser/android/in_process/synchronous_compositor_registry.h
+++ b/content/browser/android/in_process/synchronous_compositor_registry.h
@@ -41,7 +41,6 @@
       SynchronousCompositorOutputSurface* output_surface);
   void RegisterInputHandler(
       int routing_id,
-      cc::InputHandler* input_handler,
       SynchronousInputHandlerProxy* synchronous_input_handler_proxy);
   void UnregisterInputHandler(int routing_id);
 
@@ -54,7 +53,6 @@
     SynchronousCompositorImpl* compositor;
     SynchronousCompositorExternalBeginFrameSource* begin_frame_source;
     SynchronousCompositorOutputSurface* output_surface;
-    cc::InputHandler* input_handler;
     SynchronousInputHandlerProxy* synchronous_input_handler_proxy;
 
     Entry();
diff --git a/content/browser/android/in_process/synchronous_input_event_filter.cc b/content/browser/android/in_process/synchronous_input_event_filter.cc
index c3669af..6645607 100644
--- a/content/browser/android/in_process/synchronous_input_event_filter.cc
+++ b/content/browser/android/in_process/synchronous_input_event_filter.cc
@@ -5,7 +5,6 @@
 #include "content/browser/android/in_process/synchronous_input_event_filter.h"
 
 #include "base/callback.h"
-#include "cc/input/input_handler.h"
 #include "content/browser/android/in_process/synchronous_compositor_impl.h"
 #include "content/browser/android/in_process/synchronous_compositor_registry.h"
 #include "content/public/browser/browser_thread.h"
@@ -48,11 +47,10 @@
 
 void SynchronousInputEventFilter::DidAddInputHandler(
     int routing_id,
-    cc::InputHandler* input_handler,
     SynchronousInputHandlerProxy* synchronous_input_handler_proxy) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   SynchronousCompositorRegistry::GetInstance()->RegisterInputHandler(
-      routing_id, input_handler, synchronous_input_handler_proxy);
+      routing_id, synchronous_input_handler_proxy);
 }
 
 void SynchronousInputEventFilter::DidRemoveInputHandler(int routing_id) {
diff --git a/content/browser/android/in_process/synchronous_input_event_filter.h b/content/browser/android/in_process/synchronous_input_event_filter.h
index 9174a4d..89feee1 100644
--- a/content/browser/android/in_process/synchronous_input_event_filter.h
+++ b/content/browser/android/in_process/synchronous_input_event_filter.h
@@ -35,7 +35,6 @@
   void SetBoundHandler(const Handler& handler) override;
   void DidAddInputHandler(
       int routing_id,
-      cc::InputHandler* input_handler,
       SynchronousInputHandlerProxy* synchronous_input_handler_proxy) override;
   void DidRemoveInputHandler(int routing_id) override;
   void DidOverscroll(int routing_id,
diff --git a/content/renderer/input/input_event_filter.cc b/content/renderer/input/input_event_filter.cc
index 36476a71..931b4e7 100644
--- a/content/renderer/input/input_event_filter.cc
+++ b/content/renderer/input/input_event_filter.cc
@@ -57,7 +57,6 @@
 
 void InputEventFilter::DidAddInputHandler(
     int routing_id,
-    cc::InputHandler* input_handler,
     SynchronousInputHandlerProxy* synchronous_input_handler_proxy) {
   base::AutoLock locked(routes_lock_);
   routes_.insert(routing_id);
diff --git a/content/renderer/input/input_event_filter.h b/content/renderer/input/input_event_filter.h
index 1678a79..2f87d57 100644
--- a/content/renderer/input/input_event_filter.h
+++ b/content/renderer/input/input_event_filter.h
@@ -62,7 +62,6 @@
   void SetBoundHandler(const Handler& handler) override;
   void DidAddInputHandler(
       int routing_id,
-      cc::InputHandler* input_handler,
       SynchronousInputHandlerProxy* synchronous_input_handler_proxy) override;
   void DidRemoveInputHandler(int routing_id) override;
   void DidOverscroll(int routing_id,
diff --git a/content/renderer/input/input_event_filter_unittest.cc b/content/renderer/input/input_event_filter_unittest.cc
index 98e6326..e96cd7a 100644
--- a/content/renderer/input/input_event_filter_unittest.cc
+++ b/content/renderer/input/input_event_filter_unittest.cc
@@ -165,7 +165,7 @@
   EXPECT_EQ(0U, event_recorder_.record_count());
   EXPECT_EQ(0U, message_recorder_.message_count());
 
-  filter_->DidAddInputHandler(kTestRoutingID, nullptr, nullptr);
+  filter_->DidAddInputHandler(kTestRoutingID, nullptr);
 
   AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
   ASSERT_EQ(arraysize(kEvents), ipc_sink_.message_count());
@@ -241,7 +241,7 @@
 }
 
 TEST_F(InputEventFilterTest, PreserveRelativeOrder) {
-  filter_->DidAddInputHandler(kTestRoutingID, nullptr, nullptr);
+  filter_->DidAddInputHandler(kTestRoutingID, nullptr);
   event_recorder_.set_send_to_widget(true);
 
 
diff --git a/content/renderer/input/input_handler_manager.cc b/content/renderer/input/input_handler_manager.cc
index 3a298e3..cbc9a15 100644
--- a/content/renderer/input/input_handler_manager.cc
+++ b/content/renderer/input/input_handler_manager.cc
@@ -93,8 +93,7 @@
       "result", "AddingRoute");
   scoped_ptr<InputHandlerWrapper> wrapper(new InputHandlerWrapper(
       this, routing_id, main_task_runner, input_handler, render_view_impl));
-  client_->DidAddInputHandler(routing_id, input_handler.get(),
-                              wrapper->input_handler_proxy());
+  client_->DidAddInputHandler(routing_id, wrapper->input_handler_proxy());
   input_handlers_.add(routing_id, wrapper.Pass());
 }
 
diff --git a/content/renderer/input/input_handler_manager_client.h b/content/renderer/input/input_handler_manager_client.h
index a098808c..834f253 100644
--- a/content/renderer/input/input_handler_manager_client.h
+++ b/content/renderer/input/input_handler_manager_client.h
@@ -45,7 +45,6 @@
   // Called from the compositor thread.
   virtual void DidAddInputHandler(
       int routing_id,
-      cc::InputHandler* input_handler,
       SynchronousInputHandlerProxy* synchronous_handler) = 0;
   virtual void DidRemoveInputHandler(int routing_id) = 0;
   virtual void DidOverscroll(int routing_id,
diff --git a/content/renderer/input/input_handler_proxy.cc b/content/renderer/input/input_handler_proxy.cc
index 302c66d..b5dcb18 100644
--- a/content/renderer/input/input_handler_proxy.cc
+++ b/content/renderer/input/input_handler_proxy.cc
@@ -816,21 +816,44 @@
     scroll_elasticity_controller_->ReconcileStretchAndScroll();
 }
 
+void InputHandlerProxy::UpdateRootLayerStateForSynchronousInputHandler(
+    const gfx::ScrollOffset& total_scroll_offset,
+    const gfx::ScrollOffset& max_scroll_offset,
+    const gfx::SizeF& scrollable_size,
+    float page_scale_factor,
+    float min_page_scale_factor,
+    float max_page_scale_factor) {
+  if (synchronous_input_handler_) {
+    synchronous_input_handler_->UpdateRootLayerState(
+        total_scroll_offset, max_scroll_offset, scrollable_size,
+        page_scale_factor, min_page_scale_factor, max_page_scale_factor);
+  }
+}
+
 void InputHandlerProxy::SetOnlySynchronouslyAnimateRootFlings(
     SynchronousInputHandler* synchronous_input_handler) {
-  allow_root_animate_ = false;
+  allow_root_animate_ = !synchronous_input_handler;
   synchronous_input_handler_ = synchronous_input_handler;
+  if (synchronous_input_handler_)
+    input_handler_->RequestUpdateForSynchronousInputHandler();
 }
 
 void InputHandlerProxy::SynchronouslyAnimate(base::TimeTicks time) {
   // When this function is used, SetOnlySynchronouslyAnimate() should have been
   // previously called. IOW you should either be entirely in synchronous mode or
   // not.
+  DCHECK(synchronous_input_handler_);
   DCHECK(!allow_root_animate_);
   base::AutoReset<bool> reset(&allow_root_animate_, true);
   Animate(time);
 }
 
+void InputHandlerProxy::SynchronouslySetRootScrollOffset(
+    const gfx::ScrollOffset& root_offset) {
+  DCHECK(synchronous_input_handler_);
+  input_handler_->SetSynchronousInputHandlerRootScrollOffset(root_offset);
+}
+
 void InputHandlerProxy::HandleOverscroll(
     const gfx::Point& causal_event_viewport_point,
     const cc::InputHandlerScrollResult& scroll_result) {
diff --git a/content/renderer/input/input_handler_proxy.h b/content/renderer/input/input_handler_proxy.h
index b61067c..6ebf073 100644
--- a/content/renderer/input/input_handler_proxy.h
+++ b/content/renderer/input/input_handler_proxy.h
@@ -54,11 +54,20 @@
   void Animate(base::TimeTicks time) override;
   void MainThreadHasStoppedFlinging() override;
   void ReconcileElasticOverscrollAndRootScroll() override;
+  void UpdateRootLayerStateForSynchronousInputHandler(
+      const gfx::ScrollOffset& total_scroll_offset,
+      const gfx::ScrollOffset& max_scroll_offset,
+      const gfx::SizeF& scrollable_size,
+      float page_scale_factor,
+      float min_page_scale_factor,
+      float max_page_scale_factor) override;
 
   // SynchronousInputHandlerProxy implementation.
   void SetOnlySynchronouslyAnimateRootFlings(
       SynchronousInputHandler* synchronous_input_handler) override;
   void SynchronouslyAnimate(base::TimeTicks time) override;
+  void SynchronouslySetRootScrollOffset(
+      const gfx::ScrollOffset& root_offset) override;
 
   // blink::WebGestureCurveTarget implementation.
   virtual bool scrollBy(const blink::WebFloatSize& offset,
diff --git a/content/renderer/input/input_handler_proxy_unittest.cc b/content/renderer/input/input_handler_proxy_unittest.cc
index d5b5df1..3cc7f43 100644
--- a/content/renderer/input/input_handler_proxy_unittest.cc
+++ b/content/renderer/input/input_handler_proxy_unittest.cc
@@ -17,6 +17,8 @@
 #include "third_party/WebKit/public/platform/WebPoint.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
 #include "ui/events/latency_info.h"
+#include "ui/gfx/geometry/scroll_offset.h"
+#include "ui/gfx/geometry/size_f.h"
 
 using blink::WebActiveWheelFlingParameters;
 using blink::WebFloatPoint;
@@ -132,12 +134,9 @@
   MOCK_METHOD1(HaveWheelEventHandlersAt, bool(const gfx::Point& point));
   MOCK_METHOD1(DoTouchEventsBlockScrollAt, bool(const gfx::Point& point));
 
-  void SetRootLayerScrollOffsetDelegate(
-      cc::LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate)
-      override {}
-
-  void OnRootLayerDelegatedScrollOffsetChanged(
-      const gfx::ScrollOffset& root_offset) override {}
+  MOCK_METHOD0(RequestUpdateForSynchronousInputHandler, void());
+  MOCK_METHOD1(SetSynchronousInputHandlerRootScrollOffset,
+               void(const gfx::ScrollOffset& root_offset));
 
   bool IsCurrentlyScrollingRoot() const override { return is_scrolling_root_; }
   void set_is_scrolling_root(bool is) { is_scrolling_root_ = is; }
@@ -150,6 +149,13 @@
 class MockSynchronousInputHandler : public content::SynchronousInputHandler {
  public:
   MOCK_METHOD0(SetNeedsSynchronousAnimateInput, void());
+  MOCK_METHOD6(UpdateRootLayerState,
+               void(const gfx::ScrollOffset& total_scroll_offset,
+                    const gfx::ScrollOffset& max_scroll_offset,
+                    const gfx::SizeF& scrollable_size,
+                    float page_scale_factor,
+                    float min_page_scale_factor,
+                    float max_page_scale_factor));
 };
 
 // A simple WebGestureCurve implementation that flings at a constant velocity
@@ -247,9 +253,13 @@
     scroll_result_did_not_scroll_.did_scroll = false;
 
     if (install_synchronous_handler_) {
+      EXPECT_CALL(mock_input_handler_,
+                  RequestUpdateForSynchronousInputHandler())
+          .Times(1);
       input_handler_->SetOnlySynchronouslyAnimateRootFlings(
           &mock_synchronous_input_handler_);
     }
+
     mock_input_handler_.set_is_scrolling_root(synchronous_root_scroll_);
   }
 
@@ -2311,6 +2321,8 @@
   input_handler_.reset(
         new content::InputHandlerProxy(&mock_input_handler_, &mock_client));
   if (install_synchronous_handler_) {
+    EXPECT_CALL(mock_input_handler_, RequestUpdateForSynchronousInputHandler())
+        .Times(1);
     input_handler_->SetOnlySynchronouslyAnimateRootFlings(
         &mock_synchronous_input_handler_);
   }
@@ -2336,6 +2348,75 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
+TEST(SynchronousInputHandlerProxyTest, StartupShutdown) {
+  testing::StrictMock<MockInputHandler> mock_input_handler;
+  testing::StrictMock<MockInputHandlerProxyClient> mock_client;
+  testing::StrictMock<MockSynchronousInputHandler>
+      mock_synchronous_input_handler;
+  content::InputHandlerProxy proxy(&mock_input_handler, &mock_client);
+
+  // When adding a SynchronousInputHandler, immediately request an
+  // UpdateRootLayerStateForSynchronousInputHandler() call.
+  EXPECT_CALL(mock_input_handler, RequestUpdateForSynchronousInputHandler())
+      .Times(1);
+  proxy.SetOnlySynchronouslyAnimateRootFlings(&mock_synchronous_input_handler);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler);
+  testing::Mock::VerifyAndClearExpectations(&mock_client);
+  testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler);
+
+  EXPECT_CALL(mock_input_handler, RequestUpdateForSynchronousInputHandler())
+      .Times(0);
+  proxy.SetOnlySynchronouslyAnimateRootFlings(nullptr);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler);
+  testing::Mock::VerifyAndClearExpectations(&mock_client);
+  testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler);
+}
+
+TEST(SynchronousInputHandlerProxyTest, UpdateRootLayerState) {
+  testing::NiceMock<MockInputHandler> mock_input_handler;
+  testing::StrictMock<MockInputHandlerProxyClient> mock_client;
+  testing::StrictMock<MockSynchronousInputHandler>
+      mock_synchronous_input_handler;
+  content::InputHandlerProxy proxy(&mock_input_handler, &mock_client);
+
+  proxy.SetOnlySynchronouslyAnimateRootFlings(&mock_synchronous_input_handler);
+
+  // When adding a SynchronousInputHandler, immediately request an
+  // UpdateRootLayerStateForSynchronousInputHandler() call.
+  EXPECT_CALL(
+      mock_synchronous_input_handler,
+      UpdateRootLayerState(gfx::ScrollOffset(1, 2), gfx::ScrollOffset(3, 4),
+                           gfx::SizeF(5, 6), 7, 8, 9))
+      .Times(1);
+  proxy.UpdateRootLayerStateForSynchronousInputHandler(
+      gfx::ScrollOffset(1, 2), gfx::ScrollOffset(3, 4), gfx::SizeF(5, 6), 7, 8,
+      9);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler);
+  testing::Mock::VerifyAndClearExpectations(&mock_client);
+  testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler);
+}
+
+TEST(SynchronousInputHandlerProxyTest, SetOffset) {
+  testing::NiceMock<MockInputHandler> mock_input_handler;
+  testing::StrictMock<MockInputHandlerProxyClient> mock_client;
+  testing::StrictMock<MockSynchronousInputHandler>
+      mock_synchronous_input_handler;
+  content::InputHandlerProxy proxy(&mock_input_handler, &mock_client);
+
+  proxy.SetOnlySynchronouslyAnimateRootFlings(&mock_synchronous_input_handler);
+
+  EXPECT_CALL(mock_input_handler, SetSynchronousInputHandlerRootScrollOffset(
+                                      gfx::ScrollOffset(5, 6)));
+  proxy.SynchronouslySetRootScrollOffset(gfx::ScrollOffset(5, 6));
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler);
+  testing::Mock::VerifyAndClearExpectations(&mock_client);
+  testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler);
+}
+
 INSTANTIATE_TEST_CASE_P(AnimateInput,
                         InputHandlerProxyTest,
                         testing::ValuesIn(test_types));
diff --git a/content/renderer/input/synchronous_input_handler_proxy.h b/content/renderer/input/synchronous_input_handler_proxy.h
index 8f84aad7..438f41ce 100644
--- a/content/renderer/input/synchronous_input_handler_proxy.h
+++ b/content/renderer/input/synchronous_input_handler_proxy.h
@@ -7,6 +7,11 @@
 
 #include "base/time/time.h"
 
+namespace gfx {
+class ScrollOffset;
+class SizeF;
+}
+
 namespace content {
 
 class CONTENT_EXPORT SynchronousInputHandler {
@@ -16,6 +21,16 @@
   // animation. The embedder/app may choose to override and ignore the
   // request for animation.
   virtual void SetNeedsSynchronousAnimateInput() = 0;
+
+  // Informs the Android WebView embedder of the current root scroll and page
+  // scale state.
+  virtual void UpdateRootLayerState(
+      const gfx::ScrollOffset& total_scroll_offset,
+      const gfx::ScrollOffset& max_scroll_offset,
+      const gfx::SizeF& scrollable_size,
+      float page_scale_factor,
+      float min_page_scale_factor,
+      float max_page_scale_factor) = 0;
 };
 
 // Android WebView requires synchronous scrolling from the WebView application.
@@ -38,6 +53,14 @@
   // it returns, it expects the animation scroll offsets to be visible to the
   // application.
   virtual void SynchronouslyAnimate(base::TimeTicks time) = 0;
+
+  // Called when the synchronous input handler wants to change the root scroll
+  // offset. Since it has the final say, this overrides values from compositor-
+  // controlled behaviour. After the offset is applied, the
+  // SynchronousInputHandler should be given back the result in case it differs
+  // from what was sent.
+  virtual void SynchronouslySetRootScrollOffset(
+      const gfx::ScrollOffset& root_offset) = 0;
 };
 
 }  // namespace content