[go: nahoru, domu]

Mac: Don't repaint scrollbars every frame

The core issue here is that Blink, when it sees that it needs to
change the scrollbar in any way (even just moving the thumb), uses the
signal blink::Scrollbar::setNeedsPaintInvalidation(). This will trigger
a call to cc::PaintedScrollbarLayer::Update(), which will re-paint all
controls into textures and then emit quads the quads for the controls.

We often only need to re-arrange the quads for the controls of the
scrollbar, not re-paint them.

The system that knows whether or not the controls need to be
repainted is blink::ScrollbarTheme (because that's the code that knows
the theme that will be used to do the painting).

Add blink::ScrollbarTheme::shouldRepaintAllPartsOnInvalidation() to
indicate if a call to blink::Scrollbar::setNeedsPaintInvalidation()
should cause re-painting of all of the controls. If this returns false
for a given theme, then methods blink::Scrollbar::setNeedsPaintTrack()
and blink::Scrollbar::setNeedsPaintThumb() may be used to specify more
granular control.

Back in cc::PaintedScrollbarLayer::Update(), use the methods
cc::Scrollbar::NeedsPaintPart() to check if re-paint is needed (it is
hooked up to the bit that is set by the blink::Scrollbar methods).

While we're in the neighborhood, it is worth noting that most of the
repainting of scrollbars on Mac is due to the alpha of the thumb or the
track changing. Add methods to blink::Scrollbar to query the opacity
of the controls, so that we can do the blending at compositing time
instead of requiring a repaint.

And, while we're in that neighborhood, fix cc::CALayerOverlay's
FromTextureQuad function to correctly take into account per-vertex
opacity.

BUG=549277
TEST=
- Set the system to only show scrollbars while scrolling
- Open a page with a vertical scrollbar
- Scroll to make the scroll thumb appear
- Hover the mouse somewhere below the thumb
- The thumb should become thicker and the track should appear
- After some time, the thumb and the track should fade away
TEST=
- Open a page with a scrollbar and search (command-F) for some text
- Ensure that the ticks on the vertical scrollbar appear
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

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

Cr-Commit-Position: refs/heads/master@{#361938}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 3e5b0a2..5afd8f7 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -764,6 +764,7 @@
     "layers/nine_patch_layer_impl_unittest.cc",
     "layers/nine_patch_layer_unittest.cc",
     "layers/painted_scrollbar_layer_impl_unittest.cc",
+    "layers/painted_scrollbar_layer_unittest.cc",
     "layers/picture_image_layer_impl_unittest.cc",
     "layers/picture_image_layer_unittest.cc",
     "layers/picture_layer_impl_unittest.cc",
diff --git a/cc/blink/scrollbar_impl.cc b/cc/blink/scrollbar_impl.cc
index 7efd513..6f5fe31 100644
--- a/cc/blink/scrollbar_impl.cc
+++ b/cc/blink/scrollbar_impl.cc
@@ -63,6 +63,16 @@
   return geometry_->trackRect(scrollbar_.get());
 }
 
+float ScrollbarImpl::ThumbOpacity() const {
+  return painter_.thumbOpacity();
+}
+
+bool ScrollbarImpl::NeedsPaintPart(cc::ScrollbarPart part) const {
+  if (part == cc::THUMB)
+    return painter_.thumbNeedsRepaint();
+  return painter_.trackNeedsRepaint();
+}
+
 void ScrollbarImpl::PaintPart(SkCanvas* canvas,
                               cc::ScrollbarPart part,
                               const gfx::Rect& content_rect) {
diff --git a/cc/blink/scrollbar_impl.h b/cc/blink/scrollbar_impl.h
index fa5f042..6179697 100644
--- a/cc/blink/scrollbar_impl.h
+++ b/cc/blink/scrollbar_impl.h
@@ -33,6 +33,8 @@
   int ThumbThickness() const override;
   int ThumbLength() const override;
   gfx::Rect TrackRect() const override;
+  float ThumbOpacity() const override;
+  bool NeedsPaintPart(cc::ScrollbarPart part) const override;
   void PaintPart(SkCanvas* canvas,
                  cc::ScrollbarPart part,
                  const gfx::Rect& content_rect) override;
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index b5fe7b8..81aca73 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -50,6 +50,7 @@
       'layers/nine_patch_layer_impl_unittest.cc',
       'layers/nine_patch_layer_unittest.cc',
       'layers/painted_scrollbar_layer_impl_unittest.cc',
+      'layers/painted_scrollbar_layer_unittest.cc',
       'layers/picture_image_layer_impl_unittest.cc',
       'layers/picture_image_layer_unittest.cc',
       'layers/picture_layer_impl_unittest.cc',
diff --git a/cc/input/scrollbar.h b/cc/input/scrollbar.h
index 19ac92a..3f2239b 100644
--- a/cc/input/scrollbar.h
+++ b/cc/input/scrollbar.h
@@ -31,6 +31,8 @@
   virtual int ThumbThickness() const = 0;
   virtual int ThumbLength() const = 0;
   virtual gfx::Rect TrackRect() const = 0;
+  virtual float ThumbOpacity() const = 0;
+  virtual bool NeedsPaintPart(ScrollbarPart part) const = 0;
   virtual void PaintPart(SkCanvas* canvas,
                          ScrollbarPart part,
                          const gfx::Rect& content_rect) = 0;
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index a074850..0d25dbcc 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -49,7 +49,8 @@
       thumb_thickness_(scrollbar_->ThumbThickness()),
       thumb_length_(scrollbar_->ThumbLength()),
       is_overlay_(scrollbar_->IsOverlay()),
-      has_thumb_(scrollbar_->HasThumb()) {
+      has_thumb_(scrollbar_->HasThumb()),
+      thumb_opacity_(scrollbar_->ThumbOpacity()) {
   if (!scrollbar_->IsOverlay())
     SetShouldScrollOnMainThread(true);
 }
@@ -127,6 +128,8 @@
   else
     scrollbar_layer->set_thumb_ui_resource_id(0);
 
+  scrollbar_layer->set_thumb_opacity(thumb_opacity_);
+
   scrollbar_layer->set_is_overlay_scrollbar(is_overlay_);
 }
 
@@ -244,17 +247,24 @@
   if (update_rect_.IsEmpty() && track_resource_)
     return updated;
 
-  track_resource_ = ScopedUIResource::Create(
-      layer_tree_host(),
-      RasterizeScrollbarPart(track_layer_rect, scaled_track_rect, TRACK));
+  if (!track_resource_ || scrollbar_->NeedsPaintPart(TRACK)) {
+    track_resource_ = ScopedUIResource::Create(
+        layer_tree_host(),
+        RasterizeScrollbarPart(track_layer_rect, scaled_track_rect, TRACK));
+  }
 
   gfx::Rect thumb_layer_rect = OriginThumbRect();
   gfx::Rect scaled_thumb_rect =
       ScrollbarLayerRectToContentRect(thumb_layer_rect);
   if (has_thumb_ && !scaled_thumb_rect.IsEmpty()) {
-    thumb_resource_ = ScopedUIResource::Create(
-        layer_tree_host(),
-        RasterizeScrollbarPart(thumb_layer_rect, scaled_thumb_rect, THUMB));
+    if (!thumb_resource_ || scrollbar_->NeedsPaintPart(THUMB) ||
+        scaled_thumb_rect.size() !=
+            thumb_resource_->GetBitmap(0, false).GetSize()) {
+      thumb_resource_ = ScopedUIResource::Create(
+          layer_tree_host(),
+          RasterizeScrollbarPart(thumb_layer_rect, scaled_thumb_rect, THUMB));
+    }
+    thumb_opacity_ = scrollbar_->ThumbOpacity();
   }
 
   // UI resources changed so push properties is needed.
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index b6170a64..8f016018 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -97,6 +97,8 @@
   scoped_ptr<ScopedUIResource> track_resource_;
   scoped_ptr<ScopedUIResource> thumb_resource_;
 
+  float thumb_opacity_;
+
   DISALLOW_COPY_AND_ASSIGN(PaintedScrollbarLayer);
 };
 
diff --git a/cc/layers/painted_scrollbar_layer_impl.cc b/cc/layers/painted_scrollbar_layer_impl.cc
index 0b5b580..229e7f29 100644
--- a/cc/layers/painted_scrollbar_layer_impl.cc
+++ b/cc/layers/painted_scrollbar_layer_impl.cc
@@ -32,12 +32,12 @@
     : ScrollbarLayerImplBase(tree_impl, id, orientation, false, false),
       track_ui_resource_id_(0),
       thumb_ui_resource_id_(0),
+      thumb_opacity_(1.f),
       internal_contents_scale_(1.f),
       thumb_thickness_(0),
       thumb_length_(0),
       track_start_(0),
-      track_length_(0) {
-}
+      track_length_(0) {}
 
 PaintedScrollbarLayerImpl::~PaintedScrollbarLayerImpl() {}
 
@@ -62,6 +62,8 @@
 
   scrollbar_layer->set_track_ui_resource_id(track_ui_resource_id_);
   scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_);
+
+  scrollbar_layer->set_thumb_opacity(thumb_opacity_);
 }
 
 bool PaintedScrollbarLayerImpl::WillDraw(DrawMode draw_mode,
@@ -102,7 +104,8 @@
 
   if (thumb_resource_id && !visible_thumb_quad_rect.IsEmpty()) {
     gfx::Rect opaque_rect;
-    const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+    const float opacity[] = {thumb_opacity_, thumb_opacity_, thumb_opacity_,
+                             thumb_opacity_};
     TextureDrawQuad* quad =
         render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
     quad->SetNew(shared_quad_state, scaled_thumb_quad_rect, opaque_rect,
diff --git a/cc/layers/painted_scrollbar_layer_impl.h b/cc/layers/painted_scrollbar_layer_impl.h
index 65d28aea..190526f 100644
--- a/cc/layers/painted_scrollbar_layer_impl.h
+++ b/cc/layers/painted_scrollbar_layer_impl.h
@@ -45,6 +45,8 @@
     thumb_ui_resource_id_ = uid;
   }
 
+  void set_thumb_opacity(float opacity) { thumb_opacity_ = opacity; }
+
   void set_internal_contents_scale_and_bounds(float content_scale,
                                               const gfx::Size& content_bounds) {
     internal_contents_scale_ = content_scale;
@@ -69,6 +71,8 @@
   UIResourceId track_ui_resource_id_;
   UIResourceId thumb_ui_resource_id_;
 
+  float thumb_opacity_;
+
   float internal_contents_scale_;
   gfx::Size internal_content_bounds_;
 
diff --git a/cc/layers/painted_scrollbar_layer_impl_unittest.cc b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
index 68f24af..3edc450a 100644
--- a/cc/layers/painted_scrollbar_layer_impl_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "cc/layers/painted_scrollbar_layer_impl.h"
 
 #include "cc/quads/draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
 #include "cc/test/layer_test_common.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -16,6 +17,7 @@
   float scale = 2.f;
   gfx::Size scaled_layer_size(20, 2000);
   gfx::Size viewport_size(1000, 1000);
+  float thumb_opacity = 0.2f;
 
   LayerTestCommon::LayerImplTest impl;
 
@@ -50,6 +52,7 @@
   scrollbar_layer_impl->SetScrollLayerLength(200.f);
   scrollbar_layer_impl->set_track_ui_resource_id(track_uid);
   scrollbar_layer_impl->set_thumb_ui_resource_id(thumb_uid);
+  scrollbar_layer_impl->set_thumb_opacity(thumb_opacity);
 
   impl.CalcDrawProps(viewport_size);
 
@@ -71,8 +74,16 @@
     // Note: this is also testing that the thumb and track are both
     // scaled by the internal contents scale.  It's not occlusion-related
     // but is easy to verify here.
-    const DrawQuad* thumb_quad = impl.quad_list().ElementAt(0);
-    const DrawQuad* track_quad = impl.quad_list().ElementAt(1);
+    const DrawQuad* thumb_draw_quad = impl.quad_list().ElementAt(0);
+    const DrawQuad* track_draw_quad = impl.quad_list().ElementAt(1);
+
+    EXPECT_EQ(DrawQuad::TEXTURE_CONTENT, thumb_draw_quad->material);
+    EXPECT_EQ(DrawQuad::TEXTURE_CONTENT, track_draw_quad->material);
+
+    const TextureDrawQuad* thumb_quad =
+        TextureDrawQuad::MaterialCast(thumb_draw_quad);
+    const TextureDrawQuad* track_quad =
+        TextureDrawQuad::MaterialCast(track_draw_quad);
 
     gfx::Rect scaled_thumb_rect = gfx::ScaleToEnclosingRect(thumb_rect, scale);
     EXPECT_EQ(track_quad->rect.ToString(),
@@ -84,6 +95,12 @@
     EXPECT_EQ(thumb_quad->rect.ToString(), scaled_thumb_rect.ToString());
     EXPECT_EQ(thumb_quad->visible_rect.ToString(),
               scaled_thumb_rect.ToString());
+    EXPECT_EQ(thumb_quad->visible_rect.ToString(),
+              scaled_thumb_rect.ToString());
+    for (size_t i = 0; i < 4; ++i) {
+      EXPECT_EQ(thumb_opacity, thumb_quad->vertex_opacity[i]);
+      EXPECT_EQ(1.f, track_quad->vertex_opacity[i]);
+    }
   }
 
   {
diff --git a/cc/layers/painted_scrollbar_layer_unittest.cc b/cc/layers/painted_scrollbar_layer_unittest.cc
new file mode 100644
index 0000000..ac814ee
--- /dev/null
+++ b/cc/layers/painted_scrollbar_layer_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/painted_scrollbar_layer.h"
+
+#include "cc/layers/layer_settings.h"
+#include "cc/test/fake_layer_tree_host.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_scrollbar.h"
+#include "cc/test/test_task_graph_runner.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::Mock;
+using ::testing::_;
+
+namespace cc {
+
+namespace {
+
+class MockScrollbar : public FakeScrollbar {
+ public:
+  MockScrollbar() : FakeScrollbar(true, true, true) {}
+  MOCK_METHOD3(PaintPart,
+               void(SkCanvas* canvas,
+                    ScrollbarPart part,
+                    const gfx::Rect& content_rect));
+};
+
+TEST(PaintedScrollbarLayerTest, NeedsPaint) {
+  FakeLayerTreeHostClient fake_client_(FakeLayerTreeHostClient::DIRECT_3D);
+  TestTaskGraphRunner task_graph_runner_;
+  scoped_ptr<FakeLayerTreeHost> layer_tree_host_;
+  LayerSettings layer_settings_;
+
+  layer_tree_host_ =
+      FakeLayerTreeHost::Create(&fake_client_, &task_graph_runner_);
+  RendererCapabilities renderer_capabilities;
+  renderer_capabilities.max_texture_size = 2048;
+  layer_tree_host_->set_renderer_capabilities(renderer_capabilities);
+
+  MockScrollbar* scrollbar = new MockScrollbar();
+  scoped_refptr<PaintedScrollbarLayer> scrollbar_layer =
+      PaintedScrollbarLayer::Create(layer_settings_,
+                                    scoped_ptr<Scrollbar>(scrollbar).Pass(), 1);
+
+  scrollbar_layer->SetIsDrawable(true);
+  scrollbar_layer->SetBounds(gfx::Size(100, 100));
+
+  layer_tree_host_->SetRootLayer(scrollbar_layer);
+  EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
+  scrollbar_layer->SavePaintProperties();
+
+  // Request no paint, but expect them to be painted because they have not
+  // yet been initialized.
+  scrollbar->set_needs_paint_thumb(false);
+  scrollbar->set_needs_paint_track(false);
+  EXPECT_CALL(*scrollbar, PaintPart(_, THUMB, _)).Times(1);
+  EXPECT_CALL(*scrollbar, PaintPart(_, TRACK, _)).Times(1);
+  scrollbar_layer->Update();
+  Mock::VerifyAndClearExpectations(scrollbar);
+
+  // The next update will paint nothing because the first update caused a paint.
+  EXPECT_CALL(*scrollbar, PaintPart(_, THUMB, _)).Times(0);
+  EXPECT_CALL(*scrollbar, PaintPart(_, TRACK, _)).Times(0);
+  scrollbar_layer->Update();
+  Mock::VerifyAndClearExpectations(scrollbar);
+
+  // Enable the thumb.
+  EXPECT_CALL(*scrollbar, PaintPart(_, THUMB, _)).Times(1);
+  EXPECT_CALL(*scrollbar, PaintPart(_, TRACK, _)).Times(0);
+  scrollbar->set_needs_paint_thumb(true);
+  scrollbar->set_needs_paint_track(false);
+  scrollbar_layer->Update();
+  Mock::VerifyAndClearExpectations(scrollbar);
+
+  // Enable the track.
+  EXPECT_CALL(*scrollbar, PaintPart(_, THUMB, _)).Times(0);
+  EXPECT_CALL(*scrollbar, PaintPart(_, TRACK, _)).Times(1);
+  scrollbar->set_needs_paint_thumb(false);
+  scrollbar->set_needs_paint_track(true);
+  scrollbar_layer->Update();
+  Mock::VerifyAndClearExpectations(scrollbar);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/output/ca_layer_overlay.cc b/cc/output/ca_layer_overlay.cc
index c918852..a2d8d3e4 100644
--- a/cc/output/ca_layer_overlay.cc
+++ b/cc/output/ca_layer_overlay.cc
@@ -89,6 +89,11 @@
   ca_layer_overlay->contents_rect =
       BoundingRect(quad->uv_top_left, quad->uv_bottom_right);
   ca_layer_overlay->background_color = quad->background_color;
+  for (int i = 1; i < 4; ++i) {
+    if (quad->vertex_opacity[i] != quad->vertex_opacity[0])
+      return CA_LAYER_FAILED_UNKNOWN;
+  }
+  ca_layer_overlay->opacity *= quad->vertex_opacity[0];
   return CA_LAYER_SUCCESS;
 }
 
diff --git a/cc/test/fake_layer_tree_host.cc b/cc/test/fake_layer_tree_host.cc
index 0c4109e..741a1b4c 100644
--- a/cc/test/fake_layer_tree_host.cc
+++ b/cc/test/fake_layer_tree_host.cc
@@ -16,7 +16,8 @@
                  &task_runner_provider_,
                  &manager_,
                  params->task_graph_runner),
-      needs_commit_(false) {
+      needs_commit_(false),
+      renderer_capabilities_set(false) {
   client_->SetLayerTreeHost(this);
 }
 
@@ -43,6 +44,12 @@
   client_->SetLayerTreeHost(NULL);
 }
 
+const RendererCapabilities& FakeLayerTreeHost::GetRendererCapabilities() const {
+  if (renderer_capabilities_set)
+    return renderer_capabilities;
+  return LayerTreeHost::GetRendererCapabilities();
+}
+
 void FakeLayerTreeHost::SetNeedsCommit() { needs_commit_ = true; }
 
 LayerImpl* FakeLayerTreeHost::CommitAndCreateLayerImplTree() {
diff --git a/cc/test/fake_layer_tree_host.h b/cc/test/fake_layer_tree_host.h
index 5927dec6..5e6bb102 100644
--- a/cc/test/fake_layer_tree_host.h
+++ b/cc/test/fake_layer_tree_host.h
@@ -29,6 +29,7 @@
 
   ~FakeLayerTreeHost() override;
 
+  const RendererCapabilities& GetRendererCapabilities() const override;
   void SetNeedsCommit() override;
   void SetNeedsUpdateLayers() override {}
   void SetNeedsFullTreeSync() override {}
@@ -54,6 +55,11 @@
 
   bool needs_commit() { return needs_commit_; }
 
+  void set_renderer_capabilities(const RendererCapabilities& capabilities) {
+    renderer_capabilities_set = true;
+    renderer_capabilities = capabilities;
+  }
+
  protected:
   FakeLayerTreeHost(FakeLayerTreeHostClient* client,
                     LayerTreeHost::InitParams* params);
@@ -64,6 +70,9 @@
   TestSharedBitmapManager manager_;
   FakeLayerTreeHostImpl host_impl_;
   bool needs_commit_;
+
+  bool renderer_capabilities_set;
+  RendererCapabilities renderer_capabilities;
 };
 
 }  // namespace cc
diff --git a/cc/test/fake_scrollbar.cc b/cc/test/fake_scrollbar.cc
index 754b6f7..80bd444 100644
--- a/cc/test/fake_scrollbar.cc
+++ b/cc/test/fake_scrollbar.cc
@@ -15,6 +15,9 @@
       is_overlay_(false),
       thumb_thickness_(10),
       thumb_length_(5),
+      thumb_opacity_(1),
+      needs_paint_thumb_(true),
+      needs_paint_track_(true),
       track_rect_(0, 0, 100, 10),
       fill_color_(SK_ColorGREEN) {}
 
@@ -24,6 +27,9 @@
       is_overlay_(is_overlay),
       thumb_thickness_(10),
       thumb_length_(5),
+      thumb_opacity_(1),
+      needs_paint_thumb_(true),
+      needs_paint_track_(true),
       track_rect_(0, 0, 100, 10),
       fill_color_(SK_ColorGREEN) {}
 
@@ -55,6 +61,16 @@
   return track_rect_;
 }
 
+float FakeScrollbar::ThumbOpacity() const {
+  return thumb_opacity_;
+}
+
+bool FakeScrollbar::NeedsPaintPart(ScrollbarPart part) const {
+  if (part == THUMB)
+    return needs_paint_thumb_;
+  return needs_paint_track_;
+}
+
 void FakeScrollbar::PaintPart(SkCanvas* canvas,
                              ScrollbarPart part,
                              const gfx::Rect& content_rect) {
diff --git a/cc/test/fake_scrollbar.h b/cc/test/fake_scrollbar.h
index 2d14660..8ae49e4 100644
--- a/cc/test/fake_scrollbar.h
+++ b/cc/test/fake_scrollbar.h
@@ -26,6 +26,8 @@
   int ThumbThickness() const override;
   int ThumbLength() const override;
   gfx::Rect TrackRect() const override;
+  float ThumbOpacity() const override;
+  bool NeedsPaintPart(ScrollbarPart part) const override;
   void PaintPart(SkCanvas* canvas,
                  ScrollbarPart part,
                  const gfx::Rect& content_rect) override;
@@ -39,12 +41,23 @@
   void set_has_thumb(bool has_thumb) { has_thumb_ = has_thumb; }
   SkColor paint_fill_color() const { return SK_ColorBLACK | fill_color_; }
 
+  void set_thumb_opacity(float opacity) { thumb_opacity_ = opacity; }
+  void set_needs_paint_thumb(bool needs_paint) {
+    needs_paint_thumb_ = needs_paint;
+  }
+  void set_needs_paint_track(bool needs_paint) {
+    needs_paint_track_ = needs_paint;
+  }
+
  private:
   bool paint_;
   bool has_thumb_;
   bool is_overlay_;
   int thumb_thickness_;
   int thumb_length_;
+  float thumb_opacity_;
+  bool needs_paint_thumb_;
+  bool needs_paint_track_;
   gfx::Point location_;
   gfx::Rect track_rect_;
   SkColor fill_color_;
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index bb8944e..9c8e1ec 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -154,7 +154,7 @@
     return rendering_stats_instrumentation_.get();
   }
 
-  const RendererCapabilities& GetRendererCapabilities() const;
+  virtual const RendererCapabilities& GetRendererCapabilities() const;
 
   void SetNeedsAnimate();
   virtual void SetNeedsUpdateLayers();
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index d6cf526..4fa3a37 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -2075,8 +2075,10 @@
 
 void FrameView::invalidatePaintForTickmarks()
 {
-    if (Scrollbar* scrollbar = verticalScrollbar())
+    if (Scrollbar* scrollbar = verticalScrollbar()) {
+        scrollbar->setTrackNeedsRepaint(true);
         setScrollbarNeedsPaintInvalidation(scrollbar);
+    }
 }
 
 void FrameView::getTickmarks(Vector<IntRect>& tickmarks) const
diff --git a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.cpp b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.cpp
index b4138bc..cc078eb8 100644
--- a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.cpp
@@ -234,6 +234,26 @@
     return m_scrollbar->setElasticOverscroll(elasticOverscroll);
 }
 
+bool WebScrollbarThemeClientImpl::trackNeedsRepaint() const
+{
+    return true;
+}
+
+void WebScrollbarThemeClientImpl::setTrackNeedsRepaint(bool)
+{
+    ASSERT_NOT_REACHED();
+}
+
+bool WebScrollbarThemeClientImpl::thumbNeedsRepaint() const
+{
+    return true;
+}
+
+void WebScrollbarThemeClientImpl::setThumbNeedsRepaint(bool)
+{
+    ASSERT_NOT_REACHED();
+}
+
 DisplayItemClient WebScrollbarThemeClientImpl::displayItemClient() const
 {
     ASSERT_NOT_REACHED();
diff --git a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h
index 12b1500..d6ca2c0 100644
--- a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h
+++ b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeClientImpl.h
@@ -79,6 +79,10 @@
     void setIsAlphaLocked(bool) override;
     float elasticOverscroll() const override;
     void setElasticOverscroll(float) override;
+    bool trackNeedsRepaint() const override;
+    void setTrackNeedsRepaint(bool) override;
+    bool thumbNeedsRepaint() const override;
+    void setThumbNeedsRepaint(bool) override;
     DisplayItemClient displayItemClient() const override;
     String debugName() const override;
 
diff --git a/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp b/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp
index 8b63bbff..d4e9828 100644
--- a/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp
@@ -68,6 +68,8 @@
     pictureBuilder.context().setDeviceScaleFactor(m_deviceScaleFactor);
     m_theme->paintTrackBackground(&pictureBuilder.context(), m_scrollbar.get(), intRect);
     pictureBuilder.endRecording()->playback(canvas);
+    if (!m_theme->shouldRepaintAllPartsOnInvalidation())
+        m_scrollbar->setTrackNeedsRepaint(false);
 }
 
 void WebScrollbarThemePainter::paintBackTrackPart(WebCanvas* canvas, const WebRect& rect)
@@ -140,6 +142,8 @@
     pictureBuilder.context().setDeviceScaleFactor(m_deviceScaleFactor);
     m_theme->paintThumb(&pictureBuilder.context(), m_scrollbar.get(), intRect);
     pictureBuilder.endRecording()->playback(canvas);
+    if (!m_theme->shouldRepaintAllPartsOnInvalidation())
+        m_scrollbar->setThumbNeedsRepaint(false);
 }
 
 WebScrollbarThemePainter::WebScrollbarThemePainter(ScrollbarTheme* theme, Scrollbar* scrollbar, float deviceScaleFactor)
@@ -149,4 +153,19 @@
 {
 }
 
+float WebScrollbarThemePainter::thumbOpacity() const
+{
+    return m_theme->thumbOpacity(m_scrollbar.get());
+}
+
+bool WebScrollbarThemePainter::trackNeedsRepaint() const
+{
+    return m_scrollbar->trackNeedsRepaint();
+}
+
+bool WebScrollbarThemePainter::thumbNeedsRepaint() const
+{
+    return m_scrollbar->thumbNeedsRepaint();
+}
+
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm
index 30ed5ed5..02e5cd1 100644
--- a/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm
+++ b/third_party/WebKit/Source/platform/mac/ScrollAnimatorMac.mm
@@ -430,12 +430,16 @@
         break;
     case TrackAlpha:
         [_scrollbarPainter.get() setTrackAlpha:currentValue];
+        _scrollbar->setTrackNeedsRepaint(true);
         break;
     case UIStateTransition:
         [_scrollbarPainter.get() setUiStateTransitionProgress:currentValue];
+        _scrollbar->setThumbNeedsRepaint(true);
+        _scrollbar->setTrackNeedsRepaint(true);
         break;
     case ExpansionTransition:
         [_scrollbarPainter.get() setExpansionTransitionProgress:currentValue];
+        _scrollbar->setThumbNeedsRepaint(true);
         break;
     }
 
@@ -460,6 +464,7 @@
     RetainPtr<WebScrollbarPartAnimation> _trackAlphaAnimation;
     RetainPtr<WebScrollbarPartAnimation> _uiStateTransitionAnimation;
     RetainPtr<WebScrollbarPartAnimation> _expansionTransitionAnimation;
+    BOOL _hasExpandedSinceInvisible;
 }
 - (id)initWithScrollbar:(blink::Scrollbar*)scrollbar;
 - (void)updateVisibilityImmediately:(bool)show;
@@ -642,6 +647,21 @@
 
 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
 {
+    // The names of these states are based on their observed behavior, and are not based on documentation.
+    enum {
+        NSScrollerStateInvisible = 0,
+        NSScrollerStateKnob = 1,
+        NSScrollerStateExpanded = 2
+    };
+    // We do not receive notifications about the thumb un-expanding when the scrollbar fades away. Ensure
+    // that we re-paint the thumb the next time that we transition away from being invisible, so that
+    // the thumb doesn't stick in an expanded state.
+    if (newOverlayScrollerState == NSScrollerStateExpanded) {
+        _hasExpandedSinceInvisible = YES;
+    } else if (newOverlayScrollerState != NSScrollerStateInvisible && _hasExpandedSinceInvisible) {
+        _scrollbar->setThumbNeedsRepaint(true);
+        _hasExpandedSinceInvisible = NO;
+    }
 }
 
 - (void)invalidate
@@ -1069,6 +1089,8 @@
     NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle];
 
     if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
+        verticalScrollbar->setTrackNeedsRepaint(true);
+        verticalScrollbar->setThumbNeedsRepaint(true);
         verticalScrollbar->setNeedsPaintInvalidation();
 
         ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp];
@@ -1087,6 +1109,8 @@
     }
 
     if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
+        horizontalScrollbar->setTrackNeedsRepaint(true);
+        horizontalScrollbar->setThumbNeedsRepaint(true);
         horizontalScrollbar->setNeedsPaintInvalidation();
 
         ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp];
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp b/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp
index 13487d83..14084965 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableAreaTest.cpp
@@ -5,6 +5,8 @@
 #include "config.h"
 #include "platform/scroll/ScrollableArea.h"
 
+#include "platform/scroll/ScrollbarTheme.h"
+#include "platform/scroll/ScrollbarThemeMock.h"
 #include "platform/testing/TestingPlatformSupport.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebScheduler.h"
@@ -32,10 +34,10 @@
     MOCK_CONST_METHOD0(enclosingScrollableArea, ScrollableArea*());
     MOCK_CONST_METHOD1(visibleContentRect, IntRect(IncludeScrollbarsInRect));
     MOCK_CONST_METHOD0(contentsSize, IntSize());
-    MOCK_CONST_METHOD0(scrollbarsCanBeActive, bool());
     MOCK_CONST_METHOD0(scrollableAreaBoundingBox, IntRect());
 
     bool userInputScrollable(ScrollbarOrientation) const override { return true; }
+    bool scrollbarsCanBeActive () const override { return true; }
     bool shouldPlaceVerticalScrollbarOnLeft() const override { return false; }
     void setScrollOffset(const IntPoint& offset, ScrollType) override { m_scrollPosition = offset.shrunkTo(m_maximumScrollPosition); }
     IntPoint scrollPosition() const override { return m_scrollPosition; }
@@ -127,4 +129,36 @@
     EXPECT_EQ(100.0, scrollableArea->scrollAnimator()->currentPosition().y());
 }
 
+TEST_F(ScrollableAreaTest, ScrollbarTrackAndThumbRepaint)
+{
+    ScrollbarTheme::setMockScrollbarsEnabled(true);
+    ScrollbarThemeMock::setShouldRepaintAllPartsOnInvalidation(true);
+
+    OwnPtrWillBeRawPtr<MockScrollableArea> scrollableArea = MockScrollableArea::create(IntPoint(0, 100));
+    RefPtrWillBeRawPtr<Scrollbar> scrollbar = Scrollbar::create(scrollableArea.get(), HorizontalScrollbar, RegularScrollbar);
+
+    EXPECT_TRUE(scrollbar->trackNeedsRepaint());
+    EXPECT_TRUE(scrollbar->thumbNeedsRepaint());
+    scrollbar->setNeedsPaintInvalidation();
+    EXPECT_TRUE(scrollbar->trackNeedsRepaint());
+    EXPECT_TRUE(scrollbar->thumbNeedsRepaint());
+
+    scrollbar->setTrackNeedsRepaint(false);
+    scrollbar->setThumbNeedsRepaint(false);
+    EXPECT_FALSE(scrollbar->trackNeedsRepaint());
+    EXPECT_FALSE(scrollbar->thumbNeedsRepaint());
+    scrollbar->setNeedsPaintInvalidation();
+    EXPECT_TRUE(scrollbar->trackNeedsRepaint());
+    EXPECT_TRUE(scrollbar->thumbNeedsRepaint());
+
+    ScrollbarThemeMock::setShouldRepaintAllPartsOnInvalidation(false);
+    scrollbar->setTrackNeedsRepaint(false);
+    scrollbar->setThumbNeedsRepaint(false);
+    EXPECT_FALSE(scrollbar->trackNeedsRepaint());
+    EXPECT_FALSE(scrollbar->thumbNeedsRepaint());
+    scrollbar->setNeedsPaintInvalidation();
+    EXPECT_FALSE(scrollbar->trackNeedsRepaint());
+    EXPECT_FALSE(scrollbar->thumbNeedsRepaint());
+}
+
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
index 19538b2..469b6394 100644
--- a/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
+++ b/third_party/WebKit/Source/platform/scroll/Scrollbar.cpp
@@ -61,6 +61,8 @@
     , m_overlapsResizer(false)
     , m_isAlphaLocked(false)
     , m_elasticOverscroll(0)
+    , m_trackNeedsRepaint(true)
+    , m_thumbNeedsRepaint(true)
 {
     if (!m_theme)
         m_theme = ScrollbarTheme::theme();
@@ -541,6 +543,10 @@
 
 void Scrollbar::setNeedsPaintInvalidation()
 {
+    if (m_theme->shouldRepaintAllPartsOnInvalidation()) {
+        m_trackNeedsRepaint = true;
+        m_thumbNeedsRepaint = true;
+    }
     if (m_scrollableArea)
         m_scrollableArea->setScrollbarNeedsPaintInvalidation(this);
 }
diff --git a/third_party/WebKit/Source/platform/scroll/Scrollbar.h b/third_party/WebKit/Source/platform/scroll/Scrollbar.h
index 0ec1b88..0ed1694 100644
--- a/third_party/WebKit/Source/platform/scroll/Scrollbar.h
+++ b/third_party/WebKit/Source/platform/scroll/Scrollbar.h
@@ -140,6 +140,11 @@
     float elasticOverscroll() const override { return m_elasticOverscroll; }
     void setElasticOverscroll(float elasticOverscroll) override { m_elasticOverscroll = elasticOverscroll; }
 
+    bool trackNeedsRepaint() const override { return m_trackNeedsRepaint; }
+    void setTrackNeedsRepaint(bool trackNeedsRepaint) override { m_trackNeedsRepaint = trackNeedsRepaint; }
+    bool thumbNeedsRepaint() const override { return m_thumbNeedsRepaint; }
+    void setThumbNeedsRepaint(bool thumbNeedsRepaint) override { m_thumbNeedsRepaint = thumbNeedsRepaint; }
+
     bool overlapsResizer() const { return m_overlapsResizer; }
     void setOverlapsResizer(bool overlapsResizer) { m_overlapsResizer = overlapsResizer; }
 
@@ -202,6 +207,9 @@
     void invalidateRect(const IntRect&) override { setNeedsPaintInvalidation(); }
 
     float scrollableAreaCurrentPos() const;
+
+    bool m_trackNeedsRepaint;
+    bool m_thumbNeedsRepaint;
 };
 
 DEFINE_TYPE_CASTS(Scrollbar, Widget, widget, widget->isScrollbar(), widget.isScrollbar());
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h
index f078c02a..11a38e5 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h
@@ -38,6 +38,7 @@
 class GraphicsContext;
 class PlatformMouseEvent;
 class ScrollbarThemeClient;
+class ScrollbarThemePaintParams;
 
 class PLATFORM_EXPORT ScrollbarTheme {
     WTF_MAKE_NONCOPYABLE(ScrollbarTheme); USING_FAST_MALLOC(ScrollbarTheme);
@@ -45,6 +46,13 @@
     ScrollbarTheme() { }
     virtual ~ScrollbarTheme() { }
 
+    // If true, then scrollbars with this theme will be painted every time
+    // Scrollbar::setNeedsPaintInvalidation is called. If false, then scrollbar
+    // thumb and track part painting results will be cached and not repainted
+    // unless requested by Scrollbar::setThumbNeedsRepaint or
+    // Scrollbar::setTrackNeedsRepaint.
+    virtual bool shouldRepaintAllPartsOnInvalidation() const { return true; }
+
     virtual void updateEnabledState(const ScrollbarThemeClient*) { }
 
     virtual bool paint(const ScrollbarThemeClient*, GraphicsContext*, const CullRect&);
@@ -77,6 +85,8 @@
     virtual int trackPosition(const ScrollbarThemeClient*);
     // The length of the track along the axis of the scrollbar.
     virtual int trackLength(const ScrollbarThemeClient*);
+    // The opacity to be applied to the thumb.
+    virtual float thumbOpacity(const ScrollbarThemeClient*) const { return 1.0f; }
 
     virtual bool hasButtons(const ScrollbarThemeClient*) = 0;
     virtual bool hasThumb(const ScrollbarThemeClient*) = 0;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeClient.h b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeClient.h
index f08f8b6..a097df4 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeClient.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeClient.h
@@ -91,6 +91,11 @@
     virtual float elasticOverscroll() const = 0;
     virtual void setElasticOverscroll(float) = 0;
 
+    virtual bool trackNeedsRepaint() const = 0;
+    virtual void setTrackNeedsRepaint(bool) = 0;
+    virtual bool thumbNeedsRepaint() const = 0;
+    virtual void setThumbNeedsRepaint(bool) = 0;
+
     virtual DisplayItemClient displayItemClient() const = 0;
     virtual String debugName() const = 0;
 
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.h b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.h
index fd5d323..d74d840 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.h
@@ -39,6 +39,7 @@
 
 class PLATFORM_EXPORT ScrollbarThemeMacOverlayAPI : public ScrollbarThemeMacCommon {
 public:
+    bool shouldRepaintAllPartsOnInvalidation() const override { return false; }
     void updateEnabledState(const ScrollbarThemeClient*) override;
     int scrollbarThickness(ScrollbarControlSize = RegularScrollbar) override;
     bool usesOverlayScrollbars() const override;
@@ -49,11 +50,13 @@
     void unregisterScrollbar(ScrollbarThemeClient*) override;
 
     void setNewPainterForScrollbar(ScrollbarThemeClient*, ScrollbarPainter);
-    ScrollbarPainter painterForScrollbar(const ScrollbarThemeClient*);
+    ScrollbarPainter painterForScrollbar(const ScrollbarThemeClient*) const;
 
     void paintTrackBackground(GraphicsContext*, const ScrollbarThemeClient*, const IntRect&) override;
     void paintThumb(GraphicsContext*, const ScrollbarThemeClient*, const IntRect&) override;
 
+    float thumbOpacity(const ScrollbarThemeClient*) const override;
+
 protected:
     IntRect trackRect(const ScrollbarThemeClient*, bool painting = false) override;
     IntRect backButtonRect(const ScrollbarThemeClient*, ScrollbarPart, bool painting = false) override;
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.mm b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.mm
index eed11375..91d5131 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.mm
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMacOverlayAPI.mm
@@ -133,7 +133,7 @@
     updateScrollbarOverlayStyle(scrollbar);
 }
 
-ScrollbarPainter ScrollbarThemeMacOverlayAPI::painterForScrollbar(const ScrollbarThemeClient* scrollbar)
+ScrollbarPainter ScrollbarThemeMacOverlayAPI::painterForScrollbar(const ScrollbarThemeClient* scrollbar) const
 {
     return [scrollbarPainterMap()->get(const_cast<ScrollbarThemeClient*>(scrollbar)).get() painter];
 }
@@ -154,7 +154,6 @@
     ScrollbarPainter scrollbarPainter = painterForScrollbar(scrollbar);
     [scrollbarPainter setEnabled:scrollbar->enabled()];
     [scrollbarPainter setBoundsSize: NSSizeFromCGSize(frameRect.size)];
-
     NSRect trackRect = NSMakeRect(0, 0, frameRect.size.width, frameRect.size.height);
     [scrollbarPainter drawKnobSlotInRect:trackRect highlight:NO];
 }
@@ -179,12 +178,17 @@
     [scrollbarPainter setBoundsSize:NSSizeFromCGSize(rect.size())];
     [scrollbarPainter setDoubleValue:0];
     [scrollbarPainter setKnobProportion:1];
+
+    CGFloat oldKnobAlpha = [scrollbarPainter knobAlpha];
+    [scrollbarPainter setKnobAlpha:1];
+
     if (scrollbar->enabled())
         [scrollbarPainter drawKnob];
 
     // If this state is not set, then moving the cursor over the scrollbar area will only cause the
     // scrollbar to engorge when moved over the top of the scrollbar area.
     [scrollbarPainter setBoundsSize: NSSizeFromCGSize(scrollbar->frameRect().size())];
+    [scrollbarPainter setKnobAlpha:oldKnobAlpha];
 }
 
 int ScrollbarThemeMacOverlayAPI::scrollbarThickness(ScrollbarControlSize controlSize)
@@ -266,5 +270,10 @@
     [painterForScrollbar(scrollbar) setEnabled:scrollbar->enabled()];
 }
 
+float ScrollbarThemeMacOverlayAPI::thumbOpacity(const ScrollbarThemeClient* scrollbar) const {
+    ScrollbarPainter scrollbarPainter = painterForScrollbar(scrollbar);
+    return [scrollbarPainter knobAlpha];
+}
+
 } // namespace blink
 
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.cpp
index 61d91d4..4007cbb6 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.cpp
@@ -33,13 +33,15 @@
 
 namespace blink {
 
-static int cScrollbarThickness[] = { 15, 11 };
+static bool gShouldRepaintAllPartsOnInvalidation = true;
 
-IntRect ScrollbarThemeMock::trackRect(const ScrollbarThemeClient* scrollbar, bool)
+void ScrollbarThemeMock::setShouldRepaintAllPartsOnInvalidation(bool shouldRepaint)
 {
-    return scrollbar->frameRect();
+    gShouldRepaintAllPartsOnInvalidation = shouldRepaint;
 }
 
+static int cScrollbarThickness[] = { 15, 11 };
+
 int ScrollbarThemeMock::scrollbarThickness(ScrollbarControlSize controlSize)
 {
     return cScrollbarThickness[controlSize];
@@ -50,6 +52,17 @@
     return RuntimeEnabledFeatures::overlayScrollbarsEnabled();
 }
 
+bool ScrollbarThemeMock::shouldRepaintAllPartsOnInvalidation() const
+{
+    return gShouldRepaintAllPartsOnInvalidation;
+}
+
+IntRect ScrollbarThemeMock::trackRect(const ScrollbarThemeClient* scrollbar, bool)
+{
+    return scrollbar->frameRect();
+}
+
+
 void ScrollbarThemeMock::paintTrackBackground(GraphicsContext* context, const ScrollbarThemeClient* scrollbar, const IntRect& trackRect)
 {
     if (DrawingRecorder::useCachedDrawingIfPossible(*context, *scrollbar, DisplayItem::ScrollbarTrackBackground))
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.h b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.h
index b6512102..f14f4b5 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeMock.h
@@ -33,10 +33,13 @@
 // Scrollbar theme used in image snapshots, to eliminate appearance differences between platforms.
 class PLATFORM_EXPORT ScrollbarThemeMock : public ScrollbarTheme {
 public:
+    static void setShouldRepaintAllPartsOnInvalidation(bool);
+
     int scrollbarThickness(ScrollbarControlSize = RegularScrollbar) override;
     bool usesOverlayScrollbars() const override;
 
 protected:
+    bool shouldRepaintAllPartsOnInvalidation() const override;
     bool hasButtons(const ScrollbarThemeClient*) override { return false; }
     bool hasThumb(const ScrollbarThemeClient*) override { return true; }
 
diff --git a/third_party/WebKit/public/platform/WebScrollbarThemePainter.h b/third_party/WebKit/public/platform/WebScrollbarThemePainter.h
index 6563a6e6..fcbfe14b7 100644
--- a/third_party/WebKit/public/platform/WebScrollbarThemePainter.h
+++ b/third_party/WebKit/public/platform/WebScrollbarThemePainter.h
@@ -64,6 +64,12 @@
     BLINK_PLATFORM_EXPORT void paintTickmarks(WebCanvas*, const WebRect&);
     BLINK_PLATFORM_EXPORT void paintThumb(WebCanvas*, const WebRect&);
 
+    // This opacity is applied on top of the content that is painted for the thumb.
+    BLINK_PLATFORM_EXPORT float thumbOpacity() const;
+
+    BLINK_PLATFORM_EXPORT bool trackNeedsRepaint() const;
+    BLINK_PLATFORM_EXPORT bool thumbNeedsRepaint() const;
+
 #if INSIDE_BLINK
     BLINK_PLATFORM_EXPORT WebScrollbarThemePainter(ScrollbarTheme*, Scrollbar*, float deviceScaleFactor);
 #endif