Add histogram attributing cause of main thread scrolling.
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

BUG=569077

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

Cr-Commit-Position: refs/heads/master@{#370057}
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index 88601b5..56c2af9 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -345,9 +345,58 @@
   return layer_->have_scroll_event_handlers();
 }
 
-void WebLayerImpl::setShouldScrollOnMainThread(
-    bool should_scroll_on_main_thread) {
-  layer_->SetShouldScrollOnMainThread(should_scroll_on_main_thread);
+static_assert(static_cast<cc::InputHandler::MainThreadScrollingReason>(
+                  blink::WebMainThreadScrollingReason::NotScrollingOnMain) ==
+                  cc::InputHandler::NOT_SCROLLING_ON_MAIN,
+              "InputHandler::MainThreadScrollingReason and "
+              "WebMainThreadScrollingReason enums must match");
+static_assert(static_cast<cc::InputHandler::MainThreadScrollingReason>(
+                  blink::WebMainThreadScrollingReason::
+                      HasBackgroundAttachmentFixedObjects) ==
+                  cc::InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS,
+              "InputHandler::MainThreadScrollingReason and "
+              "WebMainThreadScrollingReason enums must match");
+static_assert(static_cast<cc::InputHandler::MainThreadScrollingReason>(
+                  blink::WebMainThreadScrollingReason::
+                      HasNonLayerViewportConstrainedObjects) ==
+                  cc::InputHandler::HAS_NON_LAYER_VIEWPORT_CONSTRAINED_OBJECTS,
+              "InputHandler::MainThreadScrollingReason and "
+              "WebMainThreadScrollingReason enums must match");
+static_assert(
+    static_cast<cc::InputHandler::MainThreadScrollingReason>(
+        blink::WebMainThreadScrollingReason::ThreadedScrollingDisabled) ==
+        cc::InputHandler::THREADED_SCROLLING_DISABLED,
+    "InputHandler::MainThreadScrollingReason and "
+    "WebMainThreadScrollingReason enums must match");
+static_assert(static_cast<cc::InputHandler::MainThreadScrollingReason>(
+                  blink::WebMainThreadScrollingReason::ScrollBarScrolling) ==
+                  cc::InputHandler::SCROLL_BAR_SCROLLING,
+              "InputHandler::MainThreadScrollingReason and "
+              "WebMainThreadScrollingReason enums must match");
+static_assert(static_cast<cc::InputHandler::MainThreadScrollingReason>(
+                  blink::WebMainThreadScrollingReason::PageOverlay) ==
+                  cc::InputHandler::PAGE_OVERLAY,
+              "InputHandler::MainThreadScrollingReason and "
+              "WebMainThreadScrollingReason enums must match");
+
+void WebLayerImpl::addMainThreadScrollingReasons(
+    blink::WebMainThreadScrollingReason::WebMainThreadScrollingReason
+        main_thread_scrolling_reasons) {
+  DCHECK(main_thread_scrolling_reasons);
+  // WebLayerImpl should only know about non-transient scrolling
+  // reasons. Transient scrolling reasons are computed per hit test.
+  DCHECK_LE(
+      main_thread_scrolling_reasons,
+      static_cast<
+          blink::WebMainThreadScrollingReason::WebMainThreadScrollingReason>(
+          cc::InputHandler::MaxNonTransientScrollingReason));
+  layer_->AddMainThreadScrollingReasons(
+      static_cast<cc::InputHandler::MainThreadScrollingReason>(
+          main_thread_scrolling_reasons));
+}
+
+void WebLayerImpl::clearMainThreadScrollingReasons() {
+  layer_->ClearMainThreadScrollingReasons();
 }
 
 bool WebLayerImpl::shouldScrollOnMainThread() const {
diff --git a/cc/blink/web_layer_impl.h b/cc/blink/web_layer_impl.h
index 3fd0bcc..482c633 100644
--- a/cc/blink/web_layer_impl.h
+++ b/cc/blink/web_layer_impl.h
@@ -22,6 +22,7 @@
 #include "third_party/WebKit/public/platform/WebDoublePoint.h"
 #include "third_party/WebKit/public/platform/WebFloatPoint.h"
 #include "third_party/WebKit/public/platform/WebLayer.h"
+#include "third_party/WebKit/public/platform/WebMainThreadScrollingReason.h"
 #include "third_party/WebKit/public/platform/WebPoint.h"
 #include "third_party/WebKit/public/platform/WebRect.h"
 #include "third_party/WebKit/public/platform/WebSize.h"
@@ -128,7 +129,10 @@
   bool haveWheelEventHandlers() const override;
   void setHaveScrollEventHandlers(bool have_scroll_event_handlers) override;
   bool haveScrollEventHandlers() const override;
-  void setShouldScrollOnMainThread(bool scroll_on_main) override;
+  void addMainThreadScrollingReasons(
+      blink::WebMainThreadScrollingReason::WebMainThreadScrollingReason
+          main_thread_scrolling_reasons) override;
+  void clearMainThreadScrollingReasons() override;
   bool shouldScrollOnMainThread() const override;
   void setNonFastScrollableRegion(
       const blink::WebVector<blink::WebRect>& region) override;
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index f65509f..d26e6a4b 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -77,14 +77,51 @@
  public:
   // Note these are used in a histogram. Do not reorder or delete existing
   // entries.
-  enum ScrollStatus {
+  enum ScrollThread {
     SCROLL_ON_MAIN_THREAD = 0,
-    SCROLL_STARTED,
+    SCROLL_ON_IMPL_THREAD,
     SCROLL_IGNORED,
     SCROLL_UNKNOWN,
     // This must be the last entry.
     ScrollStatusCount
   };
+
+  // Ensure this stays in sync with MainThreadScrollingReason in histograms.xml,
+  // and that this extends ScrollingCoordinator::MainThreadScrollingReason.
+  // ScrollingCoordinator::MainThreadScrollingReason contains the flags
+  // which are associated with a layer. The flags only contained in
+  // InputHandler::MainThreadScrollingReason are computed for each scroll
+  // begin.
+  enum MainThreadScrollingReason {
+    NOT_SCROLLING_ON_MAIN = 0,
+    HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS = 1 << 0,
+    HAS_NON_LAYER_VIEWPORT_CONSTRAINED_OBJECTS = 1 << 1,
+    THREADED_SCROLLING_DISABLED = 1 << 2,
+    SCROLL_BAR_SCROLLING = 1 << 3,
+    PAGE_OVERLAY = 1 << 4,
+    MaxNonTransientScrollingReason = PAGE_OVERLAY,
+    NON_FAST_SCROLLABLE_REGION = 1 << 5,
+    EVENT_HANDLERS = 1 << 6,
+    FAILED_HIT_TEST = 1 << 7,
+    NO_SCROLLING_LAYER = 1 << 8,
+    NOT_SCROLLABLE = 1 << 9,
+    CONTINUING_MAIN_THREAD_SCROLL = 1 << 10,
+    NON_INVERTIBLE_TRANSFORM = 1 << 11,
+    MainThreadScrollingReasonCount = 13
+  };
+
+  struct ScrollStatus {
+    ScrollStatus()
+        : thread(SCROLL_ON_IMPL_THREAD),
+          main_thread_scrolling_reasons(NOT_SCROLLING_ON_MAIN) {}
+    ScrollStatus(ScrollThread thread,
+                 MainThreadScrollingReason main_thread_scrolling_reasons)
+        : thread(thread),
+          main_thread_scrolling_reasons(main_thread_scrolling_reasons) {}
+    ScrollThread thread;
+    MainThreadScrollingReason main_thread_scrolling_reasons;
+  };
+
   enum ScrollInputType { GESTURE, WHEEL, ANIMATED_WHEEL, NON_BUBBLING_GESTURE };
 
   // Binds a client to this handler to receive notifications. Only one client
@@ -181,6 +218,13 @@
   DISALLOW_COPY_AND_ASSIGN(InputHandler);
 };
 
+inline const InputHandler::MainThreadScrollingReason& operator|=(
+    InputHandler::MainThreadScrollingReason& a,
+    InputHandler::MainThreadScrollingReason b) {
+  return a = static_cast<InputHandler::MainThreadScrollingReason>(
+             static_cast<unsigned>(a) | static_cast<unsigned>(b));
+}
+
 }  // namespace cc
 
 #endif  // CC_INPUT_INPUT_HANDLER_H_
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index cc2cbbb..88043a7 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -66,8 +66,8 @@
       property_tree_sequence_number_(-1),
       element_id_(0),
       mutable_properties_(kMutablePropertyNone),
+      main_thread_scrolling_reasons_(InputHandler::NOT_SCROLLING_ON_MAIN),
       should_flatten_transform_from_property_tree_(false),
-      should_scroll_on_main_thread_(false),
       have_wheel_event_handlers_(false),
       have_scroll_event_handlers_(false),
       user_scrollable_horizontal_(true),
@@ -947,11 +947,21 @@
   SetNeedsCommit();
 }
 
-void Layer::SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) {
+void Layer::AddMainThreadScrollingReasons(
+    InputHandler::MainThreadScrollingReason main_thread_scrolling_reasons) {
   DCHECK(IsPropertyChangeAllowed());
-  if (should_scroll_on_main_thread_ == should_scroll_on_main_thread)
+  DCHECK(main_thread_scrolling_reasons);
+  if (main_thread_scrolling_reasons_ == main_thread_scrolling_reasons)
     return;
-  should_scroll_on_main_thread_ = should_scroll_on_main_thread;
+  main_thread_scrolling_reasons_ |= main_thread_scrolling_reasons;
+  SetNeedsCommit();
+}
+
+void Layer::ClearMainThreadScrollingReasons() {
+  DCHECK(IsPropertyChangeAllowed());
+  if (!main_thread_scrolling_reasons_)
+    return;
+  main_thread_scrolling_reasons_ = InputHandler::NOT_SCROLLING_ON_MAIN;
   SetNeedsCommit();
 }
 
@@ -1196,7 +1206,7 @@
   DCHECK(!(FilterIsAnimating() && layer->FilterIsAnimatingOnImplOnly()));
   layer->SetBackgroundFilters(background_filters());
   layer->SetMasksToBounds(masks_to_bounds_);
-  layer->SetShouldScrollOnMainThread(should_scroll_on_main_thread_);
+  layer->set_main_thread_scrolling_reasons(main_thread_scrolling_reasons_);
   layer->SetHaveWheelEventHandlers(have_wheel_event_handlers_);
   layer->SetHaveScrollEventHandlers(have_scroll_event_handlers_);
   layer->SetNonFastScrollableRegion(non_fast_scrollable_region_);
@@ -1467,7 +1477,7 @@
   // |filters_| and |background_filters_|. See crbug.com/541321.
 
   base->set_masks_to_bounds(masks_to_bounds_);
-  base->set_should_scroll_on_main_thread(should_scroll_on_main_thread_);
+  base->set_main_thread_scrolling_reasons(main_thread_scrolling_reasons_);
   base->set_have_wheel_event_handlers(have_wheel_event_handlers_);
   base->set_have_scroll_event_handlers(have_scroll_event_handlers_);
   RegionToProto(non_fast_scrollable_region_,
@@ -1553,7 +1563,9 @@
   hide_layer_and_subtree_ = base.hide_layer_and_subtree();
   has_render_surface_ = base.has_render_surface();
   masks_to_bounds_ = base.masks_to_bounds();
-  should_scroll_on_main_thread_ = base.should_scroll_on_main_thread();
+  main_thread_scrolling_reasons_ =
+      static_cast<InputHandler::MainThreadScrollingReason>(
+          base.main_thread_scrolling_reasons());
   have_wheel_event_handlers_ = base.have_wheel_event_handlers();
   have_scroll_event_handlers_ = base.have_scroll_event_handlers();
   non_fast_scrollable_region_ =
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index ef5da33..aff32fa 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -23,6 +23,7 @@
 #include "cc/base/region.h"
 #include "cc/debug/frame_timing_request.h"
 #include "cc/debug/micro_benchmark.h"
+#include "cc/input/input_handler.h"
 #include "cc/layers/layer_lists.h"
 #include "cc/layers/layer_position_constraint.h"
 #include "cc/layers/paint_properties.h"
@@ -276,9 +277,11 @@
   }
   bool user_scrollable_vertical() const { return user_scrollable_vertical_; }
 
-  void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread);
+  void AddMainThreadScrollingReasons(
+      InputHandler::MainThreadScrollingReason main_thread_scrolling_reasons);
+  void ClearMainThreadScrollingReasons();
   bool should_scroll_on_main_thread() const {
-    return should_scroll_on_main_thread_;
+    return !!main_thread_scrolling_reasons_;
   }
 
   void SetHaveWheelEventHandlers(bool have_wheel_event_handlers);
@@ -723,8 +726,8 @@
   uint64_t element_id_;
   uint32_t mutable_properties_;
   gfx::Vector2dF offset_to_transform_parent_;
+  InputHandler::MainThreadScrollingReason main_thread_scrolling_reasons_;
   bool should_flatten_transform_from_property_tree_ : 1;
-  bool should_scroll_on_main_thread_ : 1;
   bool have_wheel_event_handlers_ : 1;
   bool have_scroll_event_handlers_ : 1;
   bool user_scrollable_horizontal_ : 1;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index b845968..422de5c 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -57,7 +57,7 @@
       layer_tree_impl_(tree_impl),
       scroll_offset_(scroll_offset),
       scroll_clip_layer_id_(Layer::INVALID_ID),
-      should_scroll_on_main_thread_(false),
+      main_thread_scrolling_reasons_(InputHandler::NOT_SCROLLING_ON_MAIN),
       have_wheel_event_handlers_(false),
       have_scroll_event_handlers_(false),
       scroll_blocks_on_(SCROLL_BLOCKS_ON_NONE),
@@ -507,15 +507,24 @@
     const gfx::PointF& screen_space_point,
     InputHandler::ScrollInputType type,
     ScrollBlocksOn effective_block_mode) const {
+  InputHandler::ScrollStatus scroll_status;
+  scroll_status.main_thread_scrolling_reasons =
+      InputHandler::NOT_SCROLLING_ON_MAIN;
   if (should_scroll_on_main_thread()) {
     TRACE_EVENT0("cc", "LayerImpl::TryScroll: Failed ShouldScrollOnMainThread");
-    return InputHandler::SCROLL_ON_MAIN_THREAD;
+    scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD;
+    scroll_status.main_thread_scrolling_reasons =
+        main_thread_scrolling_reasons_;
+    return scroll_status;
   }
 
   gfx::Transform screen_space_transform = ScreenSpaceTransform();
   if (!screen_space_transform.IsInvertible()) {
     TRACE_EVENT0("cc", "LayerImpl::TryScroll: Ignored NonInvertibleTransform");
-    return InputHandler::SCROLL_IGNORED;
+    scroll_status.thread = InputHandler::SCROLL_IGNORED;
+    scroll_status.main_thread_scrolling_reasons =
+        InputHandler::NON_INVERTIBLE_TRANSFORM;
+    return scroll_status;
   }
 
   if (!non_fast_scrollable_region().IsEmpty()) {
@@ -535,26 +544,35 @@
             gfx::ToRoundedPoint(hit_test_point_in_layer_space))) {
       TRACE_EVENT0("cc",
                    "LayerImpl::tryScroll: Failed NonFastScrollableRegion");
-      return InputHandler::SCROLL_ON_MAIN_THREAD;
+      scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD;
+      scroll_status.main_thread_scrolling_reasons =
+          InputHandler::NON_FAST_SCROLLABLE_REGION;
+      return scroll_status;
     }
   }
 
   if (have_scroll_event_handlers() &&
       effective_block_mode & SCROLL_BLOCKS_ON_SCROLL_EVENT) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed ScrollEventHandlers");
-    return InputHandler::SCROLL_ON_MAIN_THREAD;
+    scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD;
+    scroll_status.main_thread_scrolling_reasons = InputHandler::EVENT_HANDLERS;
+    return scroll_status;
   }
 
   if ((type == InputHandler::WHEEL || type == InputHandler::ANIMATED_WHEEL) &&
       have_wheel_event_handlers() &&
       effective_block_mode & SCROLL_BLOCKS_ON_WHEEL_EVENT) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed WheelEventHandlers");
-    return InputHandler::SCROLL_ON_MAIN_THREAD;
+    scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD;
+    scroll_status.main_thread_scrolling_reasons = InputHandler::EVENT_HANDLERS;
+    return scroll_status;
   }
 
   if (!scrollable()) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable");
-    return InputHandler::SCROLL_IGNORED;
+    scroll_status.thread = InputHandler::SCROLL_IGNORED;
+    scroll_status.main_thread_scrolling_reasons = InputHandler::NOT_SCROLLABLE;
+    return scroll_status;
   }
 
   gfx::ScrollOffset max_scroll_offset = MaxScrollOffset();
@@ -562,10 +580,13 @@
     TRACE_EVENT0("cc",
                  "LayerImpl::tryScroll: Ignored. Technically scrollable,"
                  " but has no affordance in either direction.");
-    return InputHandler::SCROLL_IGNORED;
+    scroll_status.thread = InputHandler::SCROLL_IGNORED;
+    scroll_status.main_thread_scrolling_reasons = InputHandler::NOT_SCROLLABLE;
+    return scroll_status;
   }
 
-  return InputHandler::SCROLL_STARTED;
+  scroll_status.thread = InputHandler::SCROLL_ON_IMPL_THREAD;
+  return scroll_status;
 }
 
 skia::RefPtr<SkPicture> LayerImpl::GetPicture() {
@@ -594,7 +615,7 @@
   layer->SetFilters(filters());
   layer->SetBackgroundFilters(background_filters());
   layer->SetMasksToBounds(masks_to_bounds_);
-  layer->SetShouldScrollOnMainThread(should_scroll_on_main_thread_);
+  layer->set_main_thread_scrolling_reasons(main_thread_scrolling_reasons_);
   layer->SetHaveWheelEventHandlers(have_wheel_event_handlers_);
   layer->SetHaveScrollEventHandlers(have_scroll_event_handlers_);
   layer->SetScrollBlocksOn(scroll_blocks_on_);
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 4dca350..48e9529 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -492,11 +492,12 @@
 
   void ApplySentScrollDeltasFromAbortedCommit();
 
-  void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) {
-    should_scroll_on_main_thread_ = should_scroll_on_main_thread;
+  void set_main_thread_scrolling_reasons(
+      InputHandler::MainThreadScrollingReason main_thread_scrolling_reasons) {
+    main_thread_scrolling_reasons_ = main_thread_scrolling_reasons;
   }
   bool should_scroll_on_main_thread() const {
-    return should_scroll_on_main_thread_;
+    return !!main_thread_scrolling_reasons_;
   }
 
   void SetHaveWheelEventHandlers(bool have_wheel_event_handlers) {
@@ -773,8 +774,7 @@
   int scroll_clip_layer_id_;
 
   gfx::Vector2dF offset_to_transform_parent_;
-
-  bool should_scroll_on_main_thread_ : 1;
+  InputHandler::MainThreadScrollingReason main_thread_scrolling_reasons_;
   bool have_wheel_event_handlers_ : 1;
   bool have_scroll_event_handlers_ : 1;
 
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index f73cb25..13bcba1 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -131,8 +131,8 @@
     EXPECT_EQ(src->hide_layer_and_subtree_, dest->hide_layer_and_subtree_);
     EXPECT_EQ(src->has_render_surface_, dest->has_render_surface_);
     EXPECT_EQ(src->masks_to_bounds_, dest->masks_to_bounds_);
-    EXPECT_EQ(src->should_scroll_on_main_thread_,
-              dest->should_scroll_on_main_thread_);
+    EXPECT_EQ(src->main_thread_scrolling_reasons_,
+              dest->main_thread_scrolling_reasons_);
     EXPECT_EQ(src->have_wheel_event_handlers_,
               dest->have_wheel_event_handlers_);
     EXPECT_EQ(src->have_scroll_event_handlers_,
@@ -249,7 +249,7 @@
     layer->hide_layer_and_subtree_ = false;
     layer->has_render_surface_ = false;
     layer->masks_to_bounds_ = true;
-    layer->should_scroll_on_main_thread_ = false;
+    layer->main_thread_scrolling_reasons_ = InputHandler::NOT_SCROLLING_ON_MAIN;
     layer->have_wheel_event_handlers_ = true;
     layer->have_scroll_event_handlers_ = false;
     layer->non_fast_scrollable_region_ = Region(gfx::Rect(5, 1, 14, 3));
@@ -299,8 +299,8 @@
     layer->hide_layer_and_subtree_ = !layer->hide_layer_and_subtree_;
     layer->has_render_surface_ = !layer->has_render_surface_;
     layer->masks_to_bounds_ = !layer->masks_to_bounds_;
-    layer->should_scroll_on_main_thread_ =
-        !layer->should_scroll_on_main_thread_;
+    layer->main_thread_scrolling_reasons_ =
+        InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS;
     layer->have_wheel_event_handlers_ = !layer->have_wheel_event_handlers_;
     layer->have_scroll_event_handlers_ = !layer->have_scroll_event_handlers_;
     layer->non_fast_scrollable_region_ = Region(gfx::Rect(5, 1, 14, 3));
@@ -967,7 +967,8 @@
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetUserScrollable(true, false));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetScrollOffset(
       gfx::ScrollOffset(10, 10)));
-  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetShouldScrollOnMainThread(true));
+  EXPECT_SET_NEEDS_COMMIT(1, test_layer->AddMainThreadScrollingReasons(
+                                 InputHandler::EVENT_HANDLERS));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetNonFastScrollableRegion(
       Region(gfx::Rect(1, 1, 2, 2))));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetHaveWheelEventHandlers(true));
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index aabde35..2e822599 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -50,7 +50,7 @@
       has_thumb_(scrollbar_->HasThumb()),
       thumb_opacity_(scrollbar_->ThumbOpacity()) {
   if (!scrollbar_->IsOverlay())
-    SetShouldScrollOnMainThread(true);
+    AddMainThreadScrollingReasons(InputHandler::SCROLL_BAR_SCROLLING);
 }
 
 PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index b9fc9ae..e0d7a74b 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -160,9 +160,11 @@
   // When the scrollbar is not an overlay scrollbar, the scroll should be
   // responded to on the main thread as the compositor does not yet implement
   // scrollbar scrolling.
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            scrollbar_layer_impl->TryScroll(
-                gfx::PointF(), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE));
+  InputHandler::ScrollStatus status = scrollbar_layer_impl->TryScroll(
+      gfx::PointF(), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::SCROLL_BAR_SCROLLING,
+            status.main_thread_scrolling_reasons);
 
   // Create and attach an overlay scrollbar.
   scrollbar.reset(new FakeScrollbar(false, false, true));
@@ -175,9 +177,10 @@
 
   // The user shouldn't be able to drag an overlay scrollbar and the scroll
   // may be handled in the compositor.
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
-            scrollbar_layer_impl->TryScroll(
-                gfx::PointF(), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE));
+  status = scrollbar_layer_impl->TryScroll(gfx::PointF(), InputHandler::GESTURE,
+                                           SCROLL_BLOCKS_ON_NONE);
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLABLE, status.main_thread_scrolling_reasons);
 }
 
 TEST_F(ScrollbarLayerTest, ScrollOffsetSynchronization) {
diff --git a/cc/proto/layer.proto b/cc/proto/layer.proto
index bc1255d..4c8ba319 100644
--- a/cc/proto/layer.proto
+++ b/cc/proto/layer.proto
@@ -85,7 +85,7 @@
   // repeated FilterOperation filters = 12;
   // repeated FilterOperation background_filters = 13;
   optional bool masks_to_bounds = 14;
-  optional bool should_scroll_on_main_thread = 15;
+  optional uint32 main_thread_scrolling_reasons = 15;
   optional bool have_wheel_event_handlers = 16;
   optional bool have_scroll_event_handlers = 17;
   optional Region non_fast_scrollable_region = 18;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 057228f0..2d1321e0 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -140,7 +140,7 @@
 
 void RecordCompositorSlowScrollMetric(InputHandler::ScrollInputType type,
                                       ScrollThread scroll_thread) {
-  bool scroll_on_main_thread = (scroll_thread == ScrollThread::MAIN_THREAD);
+  bool scroll_on_main_thread = (scroll_thread == MAIN_THREAD);
   if (type == InputHandler::WHEEL || type == InputHandler::ANIMATED_WHEEL) {
     UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorWheelScrollUpdateThread",
                           scroll_on_main_thread);
@@ -548,8 +548,10 @@
       active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
 
   bool scroll_on_main_thread = false;
+  InputHandler::MainThreadScrollingReason main_thread_scrolling_reasons;
   LayerImpl* test_layer_impl = FindScrollLayerForDeviceViewportPoint(
-      device_viewport_point, type, layer_impl, &scroll_on_main_thread, NULL);
+      device_viewport_point, type, layer_impl, &scroll_on_main_thread, nullptr,
+      &main_thread_scrolling_reasons);
 
   if (!test_layer_impl)
     return false;
@@ -2476,8 +2478,12 @@
     InputHandler::ScrollInputType type,
     LayerImpl* layer_impl,
     bool* scroll_on_main_thread,
-    bool* optional_has_ancestor_scroll_handler) const {
+    bool* optional_has_ancestor_scroll_handler,
+    InputHandler::MainThreadScrollingReason* main_thread_scrolling_reasons)
+    const {
   DCHECK(scroll_on_main_thread);
+  DCHECK(main_thread_scrolling_reasons);
+  *main_thread_scrolling_reasons = InputHandler::NOT_SCROLLING_ON_MAIN;
 
   ScrollBlocksOn block_mode = EffectiveScrollBlocksOn(layer_impl);
 
@@ -2488,8 +2494,17 @@
     // thread.
     ScrollStatus status =
         layer_impl->TryScroll(device_viewport_point, type, block_mode);
-    if (status == SCROLL_ON_MAIN_THREAD) {
+    if (status.thread == SCROLL_ON_MAIN_THREAD) {
+      if (layer_impl->should_scroll_on_main_thread()) {
+        DCHECK(status.main_thread_scrolling_reasons <=
+               InputHandler::MaxNonTransientScrollingReason);
+      } else {
+        DCHECK(status.main_thread_scrolling_reasons >
+               InputHandler::MaxNonTransientScrollingReason);
+      }
+
       *scroll_on_main_thread = true;
+      *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
       return NULL;
     }
 
@@ -2499,9 +2514,19 @@
 
     status =
         scroll_layer_impl->TryScroll(device_viewport_point, type, block_mode);
+
     // If any layer wants to divert the scroll event to the main thread, abort.
-    if (status == SCROLL_ON_MAIN_THREAD) {
+    if (status.thread == SCROLL_ON_MAIN_THREAD) {
+      if (layer_impl->should_scroll_on_main_thread()) {
+        DCHECK(status.main_thread_scrolling_reasons <=
+               InputHandler::MaxNonTransientScrollingReason);
+      } else {
+        DCHECK(status.main_thread_scrolling_reasons >
+               InputHandler::MaxNonTransientScrollingReason);
+      }
+
       *scroll_on_main_thread = true;
+      *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
       return NULL;
     }
 
@@ -2509,8 +2534,10 @@
         scroll_layer_impl->have_scroll_event_handlers())
       *optional_has_ancestor_scroll_handler = true;
 
-    if (status == SCROLL_STARTED && !potentially_scrolling_layer_impl)
+    if (status.thread == InputHandler::SCROLL_ON_IMPL_THREAD &&
+        !potentially_scrolling_layer_impl) {
       potentially_scrolling_layer_impl = scroll_layer_impl;
+    }
   }
 
   // Falling back to the root scroll layer ensures generation of root overscroll
@@ -2553,9 +2580,14 @@
   DCHECK(scroll_state);
   DCHECK(scroll_state->delta_x() == 0 && scroll_state->delta_y() == 0);
 
-  if (!scrolling_layer_impl)
-    return SCROLL_IGNORED;
-
+  InputHandler::ScrollStatus scroll_status;
+  scroll_status.main_thread_scrolling_reasons = NOT_SCROLLING_ON_MAIN;
+  if (!scrolling_layer_impl) {
+    scroll_status.thread = SCROLL_IGNORED;
+    scroll_status.main_thread_scrolling_reasons = NO_SCROLLING_LAYER;
+    return scroll_status;
+  }
+  scroll_status.thread = SCROLL_ON_IMPL_THREAD;
   ScrollAnimationAbort(scrolling_layer_impl);
 
   top_controls_manager_->ScrollBegin();
@@ -2570,12 +2602,12 @@
   DistributeScrollDelta(scroll_state);
 
   client_->RenewTreePriority();
-  RecordCompositorSlowScrollMetric(type, ScrollThread::CC_THREAD);
+  RecordCompositorSlowScrollMetric(type, CC_THREAD);
 
   // TODO(lanwei): Will remove this metric in M50 when we have used the new
   // metrics for one milestone, see https://crbug.com/557787.
   UMA_HISTOGRAM_BOOLEAN("TryScroll.SlowScroll", false);
-  return SCROLL_STARTED;
+  return scroll_status;
 }
 
 InputHandler::ScrollStatus LayerTreeHostImpl::RootScrollBegin(
@@ -2591,6 +2623,8 @@
 InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
     ScrollState* scroll_state,
     InputHandler::ScrollInputType type) {
+  ScrollStatus scroll_status;
+  scroll_status.main_thread_scrolling_reasons = NOT_SCROLLING_ON_MAIN;
   TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBegin");
 
   ClearCurrentlyScrollingLayer();
@@ -2607,22 +2641,29 @@
     LayerImpl* scroll_layer_impl =
         active_tree_->FindFirstScrollingLayerThatIsHitByPoint(
             device_viewport_point);
-    if (scroll_layer_impl && !HasScrollAncestor(layer_impl, scroll_layer_impl))
-      return SCROLL_UNKNOWN;
+    if (scroll_layer_impl &&
+        !HasScrollAncestor(layer_impl, scroll_layer_impl)) {
+      scroll_status.thread = SCROLL_UNKNOWN;
+      scroll_status.main_thread_scrolling_reasons =
+          InputHandler::FAILED_HIT_TEST;
+      return scroll_status;
+    }
   }
 
   bool scroll_on_main_thread = false;
   LayerImpl* scrolling_layer_impl = FindScrollLayerForDeviceViewportPoint(
       device_viewport_point, type, layer_impl, &scroll_on_main_thread,
-      &scroll_affects_scroll_handler_);
+      &scroll_affects_scroll_handler_,
+      &scroll_status.main_thread_scrolling_reasons);
 
   if (scroll_on_main_thread) {
-    RecordCompositorSlowScrollMetric(type, ScrollThread::MAIN_THREAD);
+    RecordCompositorSlowScrollMetric(type, MAIN_THREAD);
 
     // TODO(lanwei): Will remove this metric in M50 when we have used the new
     // metrics for one milestone, see https://crbug.com/557787.
     UMA_HISTOGRAM_BOOLEAN("TryScroll.SlowScroll", true);
-    return SCROLL_ON_MAIN_THREAD;
+    scroll_status.thread = SCROLL_ON_MAIN_THREAD;
+    return scroll_status;
   }
 
   return ScrollBeginImpl(scroll_state, scrolling_layer_impl, type);
@@ -2631,10 +2672,16 @@
 InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated(
     const gfx::Point& viewport_point,
     const gfx::Vector2dF& scroll_delta) {
+  InputHandler::ScrollStatus scroll_status;
+  scroll_status.main_thread_scrolling_reasons = NOT_SCROLLING_ON_MAIN;
   if (LayerImpl* layer_impl = CurrentlyScrollingLayer()) {
-    return ScrollAnimationUpdateTarget(layer_impl, scroll_delta)
-               ? SCROLL_STARTED
-               : SCROLL_IGNORED;
+    if (ScrollAnimationUpdateTarget(layer_impl, scroll_delta)) {
+      scroll_status.thread = SCROLL_ON_IMPL_THREAD;
+    } else {
+      scroll_status.thread = SCROLL_IGNORED;
+      scroll_status.main_thread_scrolling_reasons = NOT_SCROLLABLE;
+    }
+    return scroll_status;
   }
 
   ScrollState scroll_state(0, 0, viewport_point.x(), viewport_point.y(), 0, 0,
@@ -2643,9 +2690,8 @@
   // that can scroll and set up an animation of its scroll offset. Note that
   // this does not currently go through the scroll customization and viewport
   // machinery that ScrollBy uses for non-animated wheel scrolls.
-  InputHandler::ScrollStatus scroll_status =
-      ScrollBegin(&scroll_state, ANIMATED_WHEEL);
-  if (scroll_status == SCROLL_STARTED) {
+  scroll_status = ScrollBegin(&scroll_state, ANIMATED_WHEEL);
+  if (scroll_status.thread == SCROLL_ON_IMPL_THREAD) {
     gfx::Vector2dF pending_delta = scroll_delta;
     for (LayerImpl* layer_impl = CurrentlyScrollingLayer(); layer_impl;
          layer_impl = NextLayerInScrollOrder(layer_impl)) {
@@ -2674,7 +2720,7 @@
       ScrollAnimationCreate(layer_impl, target_offset, current_offset);
 
       SetNeedsOneBeginImplFrame();
-      return SCROLL_STARTED;
+      return scroll_status;
     }
   }
   scroll_state.set_is_ending(true);
@@ -3006,9 +3052,16 @@
 }
 
 InputHandler::ScrollStatus LayerTreeHostImpl::FlingScrollBegin() {
-  if (!CurrentlyScrollingLayer())
-    return SCROLL_IGNORED;
-  return SCROLL_STARTED;
+  InputHandler::ScrollStatus scroll_status;
+  scroll_status.main_thread_scrolling_reasons =
+      InputHandler::NOT_SCROLLING_ON_MAIN;
+  if (!CurrentlyScrollingLayer()) {
+    scroll_status.thread = SCROLL_IGNORED;
+    scroll_status.main_thread_scrolling_reasons = NO_SCROLLING_LAYER;
+  } else {
+    scroll_status.thread = SCROLL_ON_IMPL_THREAD;
+  }
+  return scroll_status;
 }
 
 float LayerTreeHostImpl::DeviceSpaceDistanceToLayer(
@@ -3036,9 +3089,10 @@
     return;
 
   bool scroll_on_main_thread = false;
+  InputHandler::MainThreadScrollingReason main_thread_scrolling_reasons;
   LayerImpl* scroll_layer_impl = FindScrollLayerForDeviceViewportPoint(
       device_viewport_point, InputHandler::GESTURE, layer_impl,
-      &scroll_on_main_thread, NULL);
+      &scroll_on_main_thread, NULL, &main_thread_scrolling_reasons);
   if (scroll_layer_impl == InnerViewportScrollLayer())
     scroll_layer_impl = OuterViewportScrollLayer();
   if (scroll_on_main_thread || !scroll_layer_impl)
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 6d78c16..1e0f7f6 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -683,7 +683,9 @@
       InputHandler::ScrollInputType type,
       LayerImpl* layer_hit_by_point,
       bool* scroll_on_main_thread,
-      bool* optional_has_ancestor_scroll_handler) const;
+      bool* optional_has_ancestor_scroll_handler,
+      InputHandler::MainThreadScrollingReason* main_thread_scrolling_reason)
+      const;
   float DeviceSpaceDistanceToLayer(const gfx::PointF& device_viewport_point,
                                    LayerImpl* layer_impl);
   void StartScrollbarFadeRecursive(LayerImpl* layer);
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 814279e..72a3354 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -621,9 +621,12 @@
   host_impl_->SetViewportSize(gfx::Size(50, 50));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
                                                       InputHandler::WHEEL));
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
@@ -641,9 +644,12 @@
   host_impl_->SetViewportSize(gfx::Size(50, 50));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   EXPECT_FALSE(host_impl_->IsActivelyScrolling());
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   EXPECT_TRUE(host_impl_->IsActivelyScrolling());
@@ -653,9 +659,11 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
   // We should not crash when trying to scroll an empty layer tree.
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NO_SCROLLING_LAYER,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
@@ -672,9 +680,11 @@
 
   // We should not crash when trying to scroll after the renderer initialization
   // fails.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
@@ -683,9 +693,10 @@
   DrawFrame();
 
   // We should not crash if the tree is replaced while we are scrolling.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->active_tree()->DetachLayerTree();
 
   scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
@@ -709,30 +720,37 @@
   // With registered event handlers, wheel scrolls don't necessarily
   // have to go to the main thread.
   root->SetHaveWheelEventHandlers(true);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
   host_impl_->ScrollEnd(EndState().get());
 
   // But typically the scroll-blocks-on mode will require them to.
   root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT |
                           SCROLL_BLOCKS_ON_START_TOUCH);
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::EVENT_HANDLERS, status.main_thread_scrolling_reasons);
 
   // But gesture scrolls can still be handled.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
   host_impl_->ScrollEnd(EndState().get());
 
   // And if the handlers go away, wheel scrolls can again be processed
   // on impl (despite the scroll-blocks-on mode).
   root->SetHaveWheelEventHandlers(false);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
   host_impl_->ScrollEnd(EndState().get());
 }
 
@@ -762,9 +780,11 @@
   EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 10)));
 
   // But they don't influence the actual handling of the scroll gestures.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
   host_impl_->ScrollEnd(EndState().get());
 
   // It's the union of scroll-blocks-on mode bits across all layers in the
@@ -785,36 +805,47 @@
   // With registered scroll handlers, scrolls don't generally have to go
   // to the main thread.
   root->SetHaveScrollEventHandlers(true);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   host_impl_->ScrollEnd(EndState().get());
 
   // Even the default scroll blocks on mode doesn't require this.
   root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT |
                           SCROLL_BLOCKS_ON_START_TOUCH);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   host_impl_->ScrollEnd(EndState().get());
 
   // But the page can opt in to blocking on scroll event handlers.
   root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::EVENT_HANDLERS, status.main_thread_scrolling_reasons);
 
   // GESTURE and WHEEL scrolls behave identically in this regard.
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::EVENT_HANDLERS, status.main_thread_scrolling_reasons);
 
   // And if the handlers go away, scrolls can again be processed on impl
   // (despite the scroll-blocks-on mode).
   root->SetHaveScrollEventHandlers(false);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   host_impl_->ScrollEnd(EndState().get());
 }
 
@@ -858,46 +889,66 @@
     RebuildPropertyTrees();
   }
 
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point(10, 10)).get(), InputHandler::GESTURE);
   // Scroll-blocks-on on a layer affects scrolls that hit that layer.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
-                                    InputHandler::GESTURE));
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
   host_impl_->ScrollEnd(EndState().get());
+
   child1->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::EVENT_HANDLERS, status.main_thread_scrolling_reasons);
 
   // But not those that hit only other layers.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
   host_impl_->ScrollEnd(EndState().get());
 
   // It's the union of bits set across the scroll ancestor chain that matters.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   host_impl_->ScrollEnd(EndState().get());
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   host_impl_->ScrollEnd(EndState().get());
   root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   host_impl_->ScrollEnd(EndState().get());
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::EVENT_HANDLERS, status.main_thread_scrolling_reasons);
+
   child2->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
-                                    InputHandler::WHEEL));
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::EVENT_HANDLERS, status.main_thread_scrolling_reasons);
+
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 25)).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::EVENT_HANDLERS, status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchscreen) {
@@ -906,15 +957,23 @@
   DrawFrame();
 
   // Ignore the fling since no layer is being scrolled
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
+  InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin();
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NO_SCROLLING_LAYER,
+            status.main_thread_scrolling_reasons);
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
 
   // Now the fling should go ahead since we've started scrolling a layer
-  EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+  status = host_impl_->FlingScrollBegin();
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchpad) {
@@ -923,15 +982,23 @@
   DrawFrame();
 
   // Ignore the fling since no layer is being scrolled
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
+  InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin();
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NO_SCROLLING_LAYER,
+            status.main_thread_scrolling_reasons);
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
 
   // Now the fling should go ahead since we've started scrolling a layer
-  EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+  status = host_impl_->FlingScrollBegin();
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, NoFlingWhenScrollingOnMain) {
@@ -940,15 +1007,21 @@
   DrawFrame();
   LayerImpl* root = host_impl_->active_tree()->root_layer();
 
-  root->SetShouldScrollOnMainThread(true);
+  root->set_main_thread_scrolling_reasons(
+      InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS);
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS,
+            status.main_thread_scrolling_reasons);
 
   // The fling should be ignored since there's no layer being scrolled impl-side
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
+  status = host_impl_->FlingScrollBegin();
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NO_SCROLLING_LAYER,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
@@ -957,14 +1030,20 @@
   DrawFrame();
   LayerImpl* root = host_impl_->active_tree()->root_layer();
 
-  root->SetShouldScrollOnMainThread(true);
+  root->set_main_thread_scrolling_reasons(
+      InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS);
 
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS,
+            status.main_thread_scrolling_reasons);
+
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
@@ -977,21 +1056,29 @@
   DrawFrame();
 
   // All scroll types inside the non-fast scrollable region should fail.
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(25, 25)).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point(25, 25)).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NON_FAST_SCROLLABLE_REGION,
+            status.main_thread_scrolling_reasons);
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
                                                        InputHandler::WHEEL));
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(25, 25)).get(),
-                                    InputHandler::GESTURE));
+
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(25, 25)).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NON_FAST_SCROLLABLE_REGION,
+            status.main_thread_scrolling_reasons);
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
                                                        InputHandler::GESTURE));
 
   // All scroll types outside this region should succeed.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
                                                       InputHandler::GESTURE));
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
@@ -1000,9 +1087,12 @@
   host_impl_->ScrollEnd(EndState().get());
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
                                                        InputHandler::GESTURE));
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(),
-                                    InputHandler::GESTURE));
+
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(),
+                                   InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
                                                       InputHandler::GESTURE));
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
@@ -1024,18 +1114,23 @@
 
   // This point would fall into the non-fast scrollable region except that we've
   // moved the layer down by 25 pixels.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(40, 10)).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point(40, 10)).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
+
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10),
                                                       InputHandler::WHEEL));
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 1)).get());
   host_impl_->ScrollEnd(EndState().get());
 
   // This point is still inside the non-fast region.
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
-                                    InputHandler::WHEEL));
+  status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
+                                   InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NON_FAST_SCROLLABLE_REGION,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
@@ -1072,9 +1167,11 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::GESTURE);
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+            status.main_thread_scrolling_reasons);
 
   // Trying to scroll to the left/top will not succeed.
   EXPECT_FALSE(
@@ -1145,9 +1242,10 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
 
   // Trying to scroll if not user_scrollable_vertical will fail.
   host_impl_->InnerViewportScrollLayer()->set_user_scrollable_vertical(false);
@@ -1180,9 +1278,10 @@
   DrawFrame();
   gfx::Point scroll_position(10, 10);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(scroll_position).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset());
 
@@ -1194,9 +1293,10 @@
 
   overflow->set_user_scrollable_horizontal(false);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(scroll_position).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset());
 
@@ -1207,9 +1307,10 @@
 
   overflow->set_user_scrollable_vertical(false);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(scroll_position).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset());
 
@@ -1556,9 +1657,10 @@
     host_impl_->ScrollEnd(EndState().get());
 
     gfx::Vector2d scroll_delta(0, 10);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
 
@@ -1907,9 +2009,10 @@
       new LatencyInfoSwapPromise(latency_info));
 
   SetupScrollAndContentsLayers(gfx::Size(100, 100));
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(
       std::move(swap_promise));
@@ -3036,9 +3139,10 @@
   }
 
   // Scrolling should update metadata immediately.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   {
     CompositorFrameMetadata metadata =
@@ -3669,9 +3773,11 @@
   DrawFrame();
 
   // Scroll event is ignored because layer is not scrollable.
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NO_SCROLLING_LAYER,
+            status.main_thread_scrolling_reasons);
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -3817,9 +3923,10 @@
 
   EXPECT_EQ(gfx::SizeF(50, 50), active_tree->ScrollableSize());
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
 
   host_impl_->top_controls_manager()->ScrollBegin();
 
@@ -3860,9 +3967,10 @@
       gfx::Size(10, 10), gfx::Size(10, 10), gfx::Size(10, 10));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
 
   // Make the test scroll delta a fractional amount, to verify that the
   // fixed container size delta is (1) non-zero, and (2) fractional, and
@@ -3904,9 +4012,10 @@
   outer_scroll->SetDrawsContent(true);
   host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(
       UpdateState(gfx::Point(), gfx::Vector2dF(0.f, 50.f)).get());
 
@@ -3926,9 +4035,10 @@
 
   host_impl_->ScrollEnd(EndState().get());
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), inner_scroll);
 
   host_impl_->ScrollBy(
@@ -3979,9 +4089,10 @@
   // not be scaled.
   host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1.f, 2.f);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
 
   // Scroll down, the top controls hiding should expand the viewport size so
   // the delta should be equal to the scroll distance.
@@ -4028,9 +4139,10 @@
 
   // Scroll 25px to hide top controls
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   EXPECT_FALSE(did_request_commit_);
 }
@@ -4072,9 +4184,10 @@
   gfx::Vector2dF scroll_delta(0.f, 25.f);
   host_impl_->active_tree()->property_trees()->needs_rebuild = true;
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -4232,9 +4345,10 @@
 
   // Hide the top controls by 25px.
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
 
   // scrolling down at the max extents no longer hides the top controls
@@ -4259,9 +4373,10 @@
 
   // Bring the top controls down by 25px.
   scroll_delta = gfx::Vector2dF(0.f, -25.f);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -4288,9 +4403,10 @@
                   host_impl_->top_controls_manager()->ContentTopOffset());
 
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -4329,9 +4445,10 @@
   // Send a gesture scroll that will scroll the outer viewport, make sure the
   // top controls get scrolled.
   gfx::Vector2dF scroll_delta(0.f, 15.f);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
 
   EXPECT_EQ(host_impl_->InnerViewportScrollLayer(),
@@ -4343,9 +4460,10 @@
                       host_impl_->top_controls_manager()->ContentTopOffset());
 
   scroll_delta = gfx::Vector2dF(0.f, 50.f);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
 
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ContentTopOffset());
@@ -4360,9 +4478,10 @@
   host_impl_->InnerViewportScrollLayer()->SetScrollDelta(inner_viewport_offset);
 
   scroll_delta = gfx::Vector2dF(0.f, -65.f);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
 
   EXPECT_EQ(top_controls_height_,
@@ -4382,9 +4501,10 @@
       layer_size_, layer_size_, layer_size_);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
 
   host_impl_->top_controls_manager()->ScrollBegin();
   host_impl_->top_controls_manager()->ScrollBy(gfx::Vector2dF(0.f, 50.f));
@@ -4397,9 +4517,10 @@
 
   host_impl_->ScrollEnd(EndState().get());
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
 
   float scroll_increment_y = -25.f;
   host_impl_->top_controls_manager()->ScrollBegin();
@@ -4427,9 +4548,10 @@
       gfx::ScrollOffset(),
       host_impl_->active_tree()->InnerViewportScrollLayer()->MaxScrollOffset());
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
@@ -4461,9 +4583,10 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   host_impl_->ScrollEnd(EndState().get());
   EXPECT_TRUE(did_request_redraw_);
@@ -4481,9 +4604,10 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   host_impl_->ScrollEnd(EndState().get());
   EXPECT_TRUE(did_request_redraw_);
@@ -4501,9 +4625,12 @@
 
   // Scroll event is ignored because the input coordinate is outside the layer
   // boundaries.
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(15, 5)).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point(15, 5)).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NO_SCROLLING_LAYER,
+            status.main_thread_scrolling_reasons);
+
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -4527,9 +4654,12 @@
 
   // Scroll event is ignored because the scrollable layer is not facing the
   // viewer and there is nothing scrollable behind it.
-  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+  EXPECT_EQ(InputHandler::NO_SCROLLING_LAYER,
+            status.main_thread_scrolling_reasons);
+
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -4540,7 +4670,8 @@
       LayerImpl::Create(host_impl_->active_tree(), 3);
   scoped_ptr<LayerImpl> content_layer =
       CreateScrollableLayer(1, surface_size, clip_layer.get());
-  content_layer->SetShouldScrollOnMainThread(true);
+  content_layer->set_main_thread_scrolling_reasons(
+      InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS);
   content_layer->SetScrollClipLayer(Layer::INVALID_ID);
 
   // Note: we can use the same clip layer for both since both calls to
@@ -4557,9 +4688,11 @@
 
   // Scrolling fails because the content layer is asking to be scrolled on the
   // main thread.
-  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
-            host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point(5, 5)).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+  EXPECT_EQ(InputHandler::HAS_BACKGROUND_ATTACHMENT_FIXED_OBJECTS,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
@@ -4585,9 +4718,10 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -4629,9 +4763,10 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -4726,9 +4861,10 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta(scroll_delta);
   gfx::ScrollOffset expected_max_scroll(outer_scroll->MaxScrollOffset());
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -4777,9 +4913,10 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(-8, -7);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
 
@@ -4837,9 +4974,10 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, -10);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
 
@@ -4862,9 +5000,10 @@
 
     // The next time we scroll we should only scroll the parent.
     scroll_delta = gfx::Vector2d(0, -3);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
@@ -4883,9 +5022,10 @@
     // After scrolling the parent, another scroll on the opposite direction
     // should still scroll the child.
     scroll_delta = gfx::Vector2d(0, 7);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
@@ -4906,9 +5046,10 @@
     host_impl_->active_tree()->SetPageScaleOnActiveTree(2.f);
 
     scroll_delta = gfx::Vector2d(0, -2);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(1, 1)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
     EXPECT_EQ(grand_child, host_impl_->CurrentlyScrollingLayer());
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
@@ -4952,9 +5093,10 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, 4);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
 
@@ -5007,9 +5149,10 @@
 
   // Scrolling should still work even though we did not draw yet.
   RebuildPropertyTrees();
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
@@ -5027,9 +5170,10 @@
 
   // Scroll to the right in screen coordinates with a gesture.
   gfx::Vector2d gesture_scroll_delta(10, 0);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), gesture_scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5041,9 +5185,10 @@
   // Reset and scroll down with the wheel.
   scroll_layer->SetScrollDelta(gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), wheel_scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5088,9 +5233,10 @@
   {
     // Scroll down in screen coordinates with a gesture.
     gfx::Vector2d gesture_scroll_delta(0, 10);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(1, 1)).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), gesture_scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
 
@@ -5112,9 +5258,10 @@
     // Now reset and scroll the same amount horizontally.
     child_ptr->SetScrollDelta(gfx::Vector2dF());
     gfx::Vector2d gesture_scroll_delta(10, 0);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(1, 1)).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), gesture_scroll_delta).get());
     host_impl_->ScrollEnd(EndState().get());
 
@@ -5191,9 +5338,10 @@
   for (int i = 0; i < 4; ++i) {
     child_ptr->SetScrollDelta(gfx::Vector2dF());
     DrawFrame();
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(viewport_point).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     host_impl_->ScrollBy(
         UpdateState(viewport_point, gesture_scroll_deltas[i]).get());
     viewport_point += gesture_scroll_deltas[i];
@@ -5225,9 +5373,10 @@
 
   // Scroll down in screen coordinates with a gesture.
   gfx::Vector2d scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5240,9 +5389,10 @@
   // Reset and scroll down with the wheel.
   scroll_layer->SetScrollDelta(gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   host_impl_->ScrollBy(UpdateState(gfx::Point(), wheel_scroll_delta).get());
   host_impl_->ScrollEnd(EndState().get());
 
@@ -5394,9 +5544,10 @@
   gfx::Vector2dF scroll_delta(0.f, 10.f);
   gfx::ScrollOffset current_offset(7.f, 8.f);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   host_impl_->SetSynchronousInputHandlerRootScrollOffset(current_offset);
 
   host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -5470,9 +5621,10 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // In-bounds scrolling does not affect overscroll.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   scroll_result = host_impl_->ScrollBy(
       UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   EXPECT_TRUE(scroll_result.did_scroll);
@@ -5619,9 +5771,10 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, -10);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
     scroll_result =
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     EXPECT_TRUE(scroll_result.did_scroll);
@@ -5632,9 +5785,10 @@
     // The next time we scroll we should only scroll the parent, but overscroll
     // should still not reach the root layer.
     scroll_delta = gfx::Vector2d(0, -30);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
     scroll_result =
@@ -5648,9 +5802,10 @@
     // After scrolling the parent, another scroll on the opposite direction
     // should scroll the child.
     scroll_delta = gfx::Vector2d(0, 70);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     scroll_result =
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -5671,9 +5826,10 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, 8);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
     scroll_result =
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
     EXPECT_TRUE(scroll_result.did_scroll);
@@ -5707,9 +5863,10 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // Even though the layer can't scroll the overscroll still happens.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   scroll_result = host_impl_->ScrollBy(
       UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
   EXPECT_FALSE(scroll_result.did_scroll);
@@ -5726,9 +5883,10 @@
     // Edge glow effect should be applicable only upon reaching Edges
     // of the content. unnecessary glow effect calls shouldn't be
     // called while scrolling up without reaching the edge of the content.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
     scroll_result = host_impl_->ScrollBy(
         UpdateState(gfx::Point(), gfx::Vector2dF(0, 100)).get());
     EXPECT_TRUE(scroll_result.did_scroll);
@@ -5744,10 +5902,12 @@
     host_impl_->ScrollEnd(EndState().get());
     // unusedrootDelta should be subtracted from applied delta so that
     // unwanted glow effect calls are not called.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
-              host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::NON_BUBBLING_GESTURE));
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
+                                      InputHandler::NON_BUBBLING_GESTURE)
+                  .thread);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
     scroll_result = host_impl_->ScrollBy(
         UpdateState(gfx::Point(), gfx::Vector2dF(0, 20)).get());
     EXPECT_TRUE(scroll_result.did_scroll);
@@ -5764,9 +5924,10 @@
     host_impl_->ScrollEnd(EndState().get());
     // TestCase to check  kEpsilon, which prevents minute values to trigger
     // gloweffect without reaching edge.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
     scroll_result = host_impl_->ScrollBy(
         UpdateState(gfx::Point(), gfx::Vector2dF(-0.12f, 0.1f)).get());
     EXPECT_FALSE(scroll_result.did_scroll);
@@ -7460,11 +7621,13 @@
   host_impl_->active_tree()->DidBecomeActive();
   DrawFrame();
   {
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
 
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
 
     gfx::Vector2d scroll_delta(0, 100);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -7513,9 +7676,10 @@
     LayerImpl* grand_child = child->children()[0].get();
 
     gfx::Vector2d scroll_delta(0, -2);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     EXPECT_TRUE(
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get())
             .did_scroll);
@@ -7538,7 +7702,8 @@
     ExpectNone(*scroll_info, child->id());
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
 
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
     EXPECT_FALSE(
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get())
             .did_scroll);
@@ -7581,11 +7746,13 @@
   host_impl_->active_tree()->DidBecomeActive();
   DrawFrame();
   {
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
 
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
 
     gfx::Vector2d scroll_delta(0, 100);
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -7629,9 +7796,11 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+  EXPECT_EQ(InputHandler::FAILED_HIT_TEST,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) {
@@ -7668,9 +7837,11 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN,
-            host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+  InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+      BeginState(gfx::Point()).get(), InputHandler::WHEEL);
+  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+  EXPECT_EQ(InputHandler::FAILED_HIT_TEST,
+            status.main_thread_scrolling_reasons);
 }
 
 TEST_F(LayerTreeHostImplTest, NotScrollInvisibleScroller) {
@@ -7698,9 +7869,10 @@
   //
   // Why SCROLL_STARTED? In this case, it's because we've bubbled out and
   // started scrolling the inner viewport.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
 
   EXPECT_EQ(2, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -7735,9 +7907,10 @@
 
   // We should have scrolled |invisible_scroll_layer| as it was hit and it has
   // a descendant which is a drawn RSLL member.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
 
   EXPECT_EQ(7, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -7789,9 +7962,10 @@
   // We should have scrolled |child_scroll| even though it is invisible.
   // The reason for this is that if the scrolling the scroll would move a layer
   // that is a drawn RSLL member, then we should accept this hit.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
 
   EXPECT_EQ(7, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -7981,9 +8155,10 @@
     LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
 
     // Scrolling normally should not trigger any forwarding.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     EXPECT_TRUE(
         host_impl_->ScrollBy(
                       UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
@@ -7997,9 +8172,10 @@
     // Scrolling with a scroll handler should defer the swap to the main
     // thread.
     scroll_layer->SetHaveScrollEventHandlers(true);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     EXPECT_TRUE(
         host_impl_->ScrollBy(
                       UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
@@ -8080,9 +8256,10 @@
       BOTH, SHOWN, false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF().ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -8154,9 +8331,10 @@
 
   LayerImpl* viewport_layer = host_impl_->InnerViewportScrollLayer();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), viewport_layer->CurrentScrollOffset());
 
@@ -8187,9 +8365,10 @@
       BOTH, SHOWN, false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF().ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -8264,9 +8443,10 @@
       gfx::ScrollOffset(0, initial_scroll_offset));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -8334,9 +8514,10 @@
       gfx::ScrollOffset(0, initial_scroll_offset));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -8402,9 +8583,10 @@
                                                              false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
 
   float offset = 50;
@@ -8577,11 +8759,13 @@
 
     // Scrolling the viewport always sets the outer scroll layer as the
     // currently scrolling layer.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     EXPECT_EQ(inner_scroll, host_impl_->CurrentlyScrollingLayer());
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
     EXPECT_EQ(inner_scroll, host_impl_->CurrentlyScrollingLayer());
 
     gfx::Vector2d scroll_delta(inner_viewport.width() / 2.f,
@@ -8597,11 +8781,13 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Fling past the inner viewport boundry, make sure outer viewport scrolls.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     EXPECT_EQ(inner_scroll, host_impl_->CurrentlyScrollingLayer());
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
     EXPECT_EQ(inner_scroll, host_impl_->CurrentlyScrollingLayer());
 
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
@@ -8638,10 +8824,12 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Make sure the scroll goes to the inner viewport first.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+                                      InputHandler::GESTURE)
+                  .thread);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
     EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
                                                         InputHandler::GESTURE));
 
@@ -8695,9 +8883,10 @@
     scoped_ptr<ScrollAndScaleSet> scroll_info;
 
     gfx::Vector2d scroll_delta(0, inner_viewport.height());
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::GESTURE));
+                                      InputHandler::GESTURE)
+                  .thread);
     EXPECT_TRUE(
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get())
             .did_scroll);
@@ -8712,7 +8901,8 @@
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child_scroll);
 
     // The fling have no effect on the currently scrolling layer.
-    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+              host_impl_->FlingScrollBegin().thread);
     EXPECT_FALSE(
         host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get())
             .did_scroll);
@@ -8756,14 +8946,16 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->RootScrollBegin(BeginState(gfx::Point()).get(),
-                                        InputHandler::GESTURE));
+                                        InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), inner_scroll);
   host_impl_->ScrollEnd(EndState().get());
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child_scroll);
   host_impl_->ScrollEnd(EndState().get());
 }
@@ -8784,9 +8976,10 @@
 
   // Ensure inner viewport doesn't react to scrolls (test it's unscrollable).
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), inner_scroll->CurrentScrollOffset());
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                    InputHandler::GESTURE));
+                                    InputHandler::GESTURE)
+                .thread);
   scroll_result = host_impl_->ScrollBy(
       UpdateState(gfx::Point(), gfx::Vector2dF(0, 100)).get());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), inner_scroll->CurrentScrollOffset());
@@ -9089,8 +9282,9 @@
   BeginFrameArgs begin_frame_args =
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)).thread);
 
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
   EXPECT_EQ(host_impl_->OuterViewportScrollLayer(), scrolling_layer);
@@ -9113,8 +9307,9 @@
   EXPECT_TRUE(y > 1 && y < 49);
 
   // Update target.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)).thread);
   host_impl_->DidFinishImplFrame();
 
   begin_frame_args.frame_time =
@@ -9153,8 +9348,9 @@
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
 
   // Perform animated scroll.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)).thread);
 
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
 
@@ -9179,9 +9375,10 @@
   EXPECT_TRUE(y > 1 && y < 49);
 
   // Perform instant scroll.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_->ScrollBegin(BeginState(gfx::Point(0, y)).get(),
-                                    InputHandler::WHEEL));
+                                    InputHandler::WHEEL)
+                .thread);
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y),
                                                       InputHandler::WHEEL));
   host_impl_->ScrollBy(
@@ -9217,8 +9414,9 @@
   BeginFrameArgs begin_frame_args =
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
 
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)).thread);
 
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
 
@@ -9240,8 +9438,9 @@
   EXPECT_TRUE(y > 1 && y < 49);
 
   // Update target.
-  EXPECT_EQ(InputHandler::SCROLL_STARTED,
-            host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_IMPL_THREAD,
+      host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)).thread);
   host_impl_->DidFinishImplFrame();
 
   begin_frame_args.frame_time =
@@ -9379,9 +9578,10 @@
     host_impl_->ScrollEnd(EndState().get());
 
     gfx::Vector2dF scroll_delta(0, 5);
-    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
               host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
-                                      InputHandler::WHEEL));
+                                      InputHandler::WHEEL)
+                  .thread);
     EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
 
     host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index ac20d47f..6d34140 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -608,7 +608,7 @@
                                gfx::Vector2dF(0.5f, 0.5f));
         InputHandler::ScrollStatus status = impl->ScrollBegin(
             BeginState(scroll_point).get(), InputHandler::GESTURE);
-        EXPECT_EQ(InputHandler::SCROLL_STARTED, status);
+        EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
         impl->ScrollBy(UpdateState(gfx::Point(), scroll_amount_).get());
         LayerImpl* scrolling_layer = impl->CurrentlyScrollingLayer();
         CHECK(scrolling_layer);
@@ -631,7 +631,7 @@
                                gfx::Vector2dF(0.5f, 0.5f));
         InputHandler::ScrollStatus status = impl->ScrollBegin(
             BeginState(scroll_point).get(), InputHandler::WHEEL);
-        EXPECT_EQ(InputHandler::SCROLL_STARTED, status);
+        EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
         impl->ScrollBy(UpdateState(gfx::Point(), scroll_amount_).get());
         impl->ScrollEnd(EndState().get());
 
@@ -1034,24 +1034,29 @@
     // Set max_scroll_offset = (100, 100).
     scroll_layer->SetBounds(
         gfx::Size(root->bounds().width() + 100, root->bounds().height() + 100));
-    EXPECT_EQ(
-        InputHandler::SCROLL_STARTED,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
-                                SCROLL_BLOCKS_ON_NONE));
+
+    InputHandler::ScrollStatus status = scroll_layer->TryScroll(
+        gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE);
+
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+    EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+              status.main_thread_scrolling_reasons);
 
     // Set max_scroll_offset = (0, 0).
     scroll_layer->SetBounds(root->bounds());
-    EXPECT_EQ(
-        InputHandler::SCROLL_IGNORED,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
-                                SCROLL_BLOCKS_ON_NONE));
+    status = scroll_layer->TryScroll(
+        gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE);
+    EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+    EXPECT_EQ(InputHandler::NOT_SCROLLABLE,
+              status.main_thread_scrolling_reasons);
 
     // Set max_scroll_offset = (-100, -100).
     scroll_layer->SetBounds(gfx::Size());
-    EXPECT_EQ(
-        InputHandler::SCROLL_IGNORED,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
-                                SCROLL_BLOCKS_ON_NONE));
+    status = scroll_layer->TryScroll(
+        gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE);
+    EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+    EXPECT_EQ(InputHandler::NOT_SCROLLABLE,
+              status.main_thread_scrolling_reasons);
 
     EndTest();
   }
@@ -1085,14 +1090,18 @@
     // Verify that the scroll layer's scroll offset is taken into account when
     // checking whether the screen space point is inside the non-fast
     // scrollable region.
-    EXPECT_EQ(
-        InputHandler::SCROLL_ON_MAIN_THREAD,
-        scroll_layer->TryScroll(gfx::PointF(1.f, 1.f), InputHandler::GESTURE,
-                                SCROLL_BLOCKS_ON_NONE));
-    EXPECT_EQ(
-        InputHandler::SCROLL_STARTED,
-        scroll_layer->TryScroll(gfx::PointF(21.f, 21.f), InputHandler::GESTURE,
-                                SCROLL_BLOCKS_ON_NONE));
+
+    InputHandler::ScrollStatus status = scroll_layer->TryScroll(
+        gfx::PointF(1.f, 1.f), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE);
+    EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+    EXPECT_EQ(InputHandler::NON_FAST_SCROLLABLE_REGION,
+              status.main_thread_scrolling_reasons);
+
+    status = scroll_layer->TryScroll(
+        gfx::PointF(21.f, 21.f), InputHandler::GESTURE, SCROLL_BLOCKS_ON_NONE);
+    EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+    EXPECT_EQ(InputHandler::NOT_SCROLLING_ON_MAIN,
+              status.main_thread_scrolling_reasons);
 
     EndTest();
   }