Refactor cc painted scrollbar

- Remove unused cc/layers/scrollbar_theme_painter.h
- Add comments in cc::Scrollbar
- Remove rect parameter from cc::Scrollbar::PaintPart() because the
  rect can be easily calculated by blink, given that the coordinate
  space of the canvas is clearly defined
- Improve performance of blink::ScrollbarLayerDelegate::HasTickMarks
  (implementation of cc::Scrollbar::HasTickMarks()). The old
  implementation queried all tick marks and see if it was empty, which
  may be slow when there are a lot of tick marks (e.g. when searching
  in a huge document).
- Add blink::ScrollbarTheme::PaintTrackAndButtonsForCompositor() to
  avoid exposing too much details of ScrollbarTheme to outside.

This is a preparation for CompositeAfterPaint composited scrollbar
implementation.

Change-Id: Ibf8c7a90d80ddf972f215495d0fd89470fa2deb4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1834286
Reviewed-by: Philip Rogers <pdr@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#702201}
diff --git a/build/check_gn_headers_whitelist.txt b/build/check_gn_headers_whitelist.txt
index 0d8df0a..a6805c9 100644
--- a/build/check_gn_headers_whitelist.txt
+++ b/build/check_gn_headers_whitelist.txt
@@ -25,7 +25,6 @@
 cc/input/scrollbar.h
 cc/input/scroller_size_metrics.h
 cc/layers/performance_properties.h
-cc/layers/scrollbar_theme_painter.h
 cc/output/bsp_compare_result.h
 cc/resources/release_callback_impl.h
 cc/resources/return_callback.h
diff --git a/cc/input/scrollbar.h b/cc/input/scrollbar.h
index 5a35ed8..4c739c6 100644
--- a/cc/input/scrollbar.h
+++ b/cc/input/scrollbar.h
@@ -30,7 +30,6 @@
 namespace cc {
 
 enum ScrollbarOrientation { HORIZONTAL, VERTICAL };
-enum ScrollDirection { SCROLL_BACKWARD, SCROLL_FORWARD };
 
 enum ScrollbarPart {
   THUMB,
@@ -55,15 +54,26 @@
   virtual bool SupportsDragSnapBack() const = 0;
   virtual int ThumbThickness() const = 0;
   virtual int ThumbLength() const = 0;
+
+  // Returns the track rect relative to the scrollbar's origin.
   virtual gfx::Rect TrackRect() const = 0;
+  // Returns the back button rect relative to the scrollbar's origin.
   virtual gfx::Rect BackButtonRect() const = 0;
+  // Returns the forward button rect relative to the scrollbar's origin.
   virtual gfx::Rect ForwardButtonRect() const = 0;
+
   virtual float ThumbOpacity() const = 0;
   virtual bool HasTickmarks() const = 0;
+
+  // Whether we need to repaint the part. Only THUMB and TRACK are supported.
+  // For TRACK, the return value means that the track, any buttons or tickmarks
+  // need repaint.
   virtual bool NeedsPaintPart(ScrollbarPart part) const = 0;
-  virtual void PaintPart(PaintCanvas* canvas,
-                         ScrollbarPart part,
-                         const gfx::Rect& content_rect) = 0;
+  // Paints the part. Only THUMB, TRACK and TICKMARKS are supported. When TRACK
+  // is specified, track, buttons and tickmarks will be painted. The canvas's
+  // coordinate space is relative to the part's origin.
+  virtual void PaintPart(PaintCanvas* canvas, ScrollbarPart part) = 0;
+
   virtual bool UsesNinePatchThumbResource() const = 0;
   virtual gfx::Size NinePatchThumbCanvasSize() const = 0;
   virtual gfx::Rect NinePatchThumbAperture() const = 0;
diff --git a/cc/layers/painted_overlay_scrollbar_layer.cc b/cc/layers/painted_overlay_scrollbar_layer.cc
index e6fcf5f..14faa69 100644
--- a/cc/layers/painted_overlay_scrollbar_layer.cc
+++ b/cc/layers/painted_overlay_scrollbar_layer.cc
@@ -44,6 +44,8 @@
       scroll_element_id_(scroll_element_id),
       thumb_thickness_(scrollbar_->ThumbThickness()),
       thumb_length_(scrollbar_->ThumbLength()) {
+  DCHECK(scrollbar_->HasThumb());
+  DCHECK(scrollbar_->IsOverlay());
   DCHECK(scrollbar_->UsesNinePatchThumbResource());
   SetIsScrollbar(true);
 }
@@ -117,9 +119,6 @@
   bool updated = false;
   updated |= Layer::Update();
 
-  DCHECK(scrollbar_->HasThumb());
-  DCHECK(scrollbar_->IsOverlay());
-  DCHECK(scrollbar_->UsesNinePatchThumbResource());
   updated |= UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
   updated |= UpdateProperty(scrollbar_->Location(), &location_);
   updated |= UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_);
@@ -143,15 +142,9 @@
   SkBitmap skbitmap;
   skbitmap.allocN32Pixels(paint_rect.width(), paint_rect.height());
   SkiaPaintCanvas canvas(skbitmap);
+  canvas.clear(SK_ColorTRANSPARENT);
 
-  SkRect content_skrect = RectToSkRect(paint_rect);
-  PaintFlags flags;
-  flags.setAntiAlias(false);
-  flags.setBlendMode(SkBlendMode::kClear);
-  canvas.drawRect(content_skrect, flags);
-  canvas.clipRect(content_skrect);
-
-  scrollbar_->PaintPart(&canvas, THUMB, paint_rect);
+  scrollbar_->PaintPart(&canvas, THUMB);
   // Make sure that the pixels are no longer mutable to unavoid unnecessary
   // allocation and copying.
   skbitmap.setImmutable();
@@ -183,15 +176,9 @@
   SkBitmap skbitmap;
   skbitmap.allocN32Pixels(paint_rect.width(), paint_rect.height());
   SkiaPaintCanvas canvas(skbitmap);
+  canvas.clear(SK_ColorTRANSPARENT);
 
-  SkRect content_skrect = RectToSkRect(paint_rect);
-  PaintFlags flags;
-  flags.setAntiAlias(false);
-  flags.setBlendMode(SkBlendMode::kClear);
-  canvas.drawRect(content_skrect, flags);
-  canvas.clipRect(content_skrect);
-
-  scrollbar_->PaintPart(&canvas, TICKMARKS, paint_rect);
+  scrollbar_->PaintPart(&canvas, TICKMARKS);
   // Make sure that the pixels are no longer mutable to unavoid unnecessary
   // allocation and copying.
   skbitmap.setImmutable();
diff --git a/cc/layers/painted_overlay_scrollbar_layer.h b/cc/layers/painted_overlay_scrollbar_layer.h
index 931d96c..0f7ee5e 100644
--- a/cc/layers/painted_overlay_scrollbar_layer.h
+++ b/cc/layers/painted_overlay_scrollbar_layer.h
@@ -9,7 +9,6 @@
 #include "cc/input/scrollbar.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/scrollbar_layer_interface.h"
-#include "cc/layers/scrollbar_theme_painter.h"
 #include "cc/resources/scoped_ui_resource.h"
 
 namespace cc {
diff --git a/cc/layers/painted_overlay_scrollbar_layer_unittest.cc b/cc/layers/painted_overlay_scrollbar_layer_unittest.cc
index db2867d..e1a77c3a 100644
--- a/cc/layers/painted_overlay_scrollbar_layer_unittest.cc
+++ b/cc/layers/painted_overlay_scrollbar_layer_unittest.cc
@@ -19,9 +19,7 @@
  public:
   MockScrollbar() : FakeScrollbar(true, true, true) {}
 
-  void PaintPart(PaintCanvas* canvas,
-                 ScrollbarPart part,
-                 const gfx::Rect& content_rect) override {
+  void PaintPart(PaintCanvas* canvas, ScrollbarPart part) override {
     if (part == TICKMARKS)
       paint_tickmarks_called_ = true;
   }
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index 96ece18..16e7b4f 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -263,13 +263,8 @@
   float scale_y =
       content_rect.height() / static_cast<float>(layer_rect.height());
   canvas.scale(SkFloatToScalar(scale_x), SkFloatToScalar(scale_y));
-  // TODO(pdr): Scrollbars are painted with an offset (see Scrollbar::PaintPart)
-  // and the canvas is translated so that scrollbars are drawn at the origin.
-  // Refactor this code to not use an offset at all so Scrollbar::PaintPart
-  // paints at the origin and no translation is needed below.
-  canvas.translate(-layer_rect.x(), -layer_rect.y());
 
-  scrollbar_->PaintPart(&canvas, part, layer_rect);
+  scrollbar_->PaintPart(&canvas, part);
   // Make sure that the pixels are no longer mutable to unavoid unnecessary
   // allocation and copying.
   skbitmap.setImmutable();
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index c2f21c8b..8ba7bc7c 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -9,7 +9,6 @@
 #include "cc/input/scrollbar.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/scrollbar_layer_interface.h"
-#include "cc/layers/scrollbar_theme_painter.h"
 #include "cc/resources/scoped_ui_resource.h"
 
 namespace cc {
diff --git a/cc/layers/painted_scrollbar_layer_unittest.cc b/cc/layers/painted_scrollbar_layer_unittest.cc
index ee0c030..1852f64 100644
--- a/cc/layers/painted_scrollbar_layer_unittest.cc
+++ b/cc/layers/painted_scrollbar_layer_unittest.cc
@@ -21,11 +21,11 @@
 
 class MockScrollbar : public FakeScrollbar {
  public:
-  MockScrollbar() : FakeScrollbar(true, true, true) {}
-  MOCK_METHOD3(PaintPart,
-               void(PaintCanvas* canvas,
-                    ScrollbarPart part,
-                    const gfx::Rect& content_rect));
+  MockScrollbar()
+      : FakeScrollbar(/*paint*/ true,
+                      /*has_thumb*/ true,
+                      /*is_overlay*/ false) {}
+  MOCK_METHOD2(PaintPart, void(PaintCanvas* canvas, ScrollbarPart part));
 };
 
 TEST(PaintedScrollbarLayerTest, NeedsPaint) {
@@ -52,28 +52,28 @@
   // 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);
+  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);
+  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);
+  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);
+  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();
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index 678551a..7f6c086f 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -254,6 +254,9 @@
 
 class FakeNinePatchScrollbar : public FakeScrollbar {
  public:
+  FakeNinePatchScrollbar()
+      : FakeScrollbar(/*paint*/ true, /*has_thumb*/ true, /*is_overlay*/ true) {
+  }
   bool UsesNinePatchThumbResource() const override { return true; }
 };
 
@@ -1354,7 +1357,8 @@
     scrollbar_layer->SetBounds(scrollbar_rect.size());
     scrollbar_layer->SetPosition(gfx::PointF(scrollbar_rect.origin()));
     scrollbar_layer->fake_scrollbar()->set_location(scrollbar_rect.origin());
-    scrollbar_layer->fake_scrollbar()->set_track_rect(scrollbar_rect);
+    scrollbar_layer->fake_scrollbar()->set_track_rect(
+        gfx::Rect(scrollbar_rect.size()));
 
     layer_tree_host_->SetViewportRectAndScale(
         layer_tree_host_->device_viewport_rect(), test_scale,
diff --git a/cc/layers/scrollbar_theme_painter.h b/cc/layers/scrollbar_theme_painter.h
deleted file mode 100644
index 2733f44..0000000
--- a/cc/layers/scrollbar_theme_painter.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_LAYERS_SCROLLBAR_THEME_PAINTER_H_
-#define CC_LAYERS_SCROLLBAR_THEME_PAINTER_H_
-
-#include "cc/cc_export.h"
-
-class SkCanvas;
-
-namespace gfx {
-class Rect;
-}
-
-namespace cc {
-
-class CC_EXPORT ScrollbarThemePainter {
- public:
-  virtual ~ScrollbarThemePainter() {}
-
-  virtual void PaintScrollbarBackground(SkCanvas* canvas,
-                                        const gfx::Rect& rect) = 0;
-  virtual void PaintTrackBackground(SkCanvas* canvas,
-                                    const gfx::Rect& rect) = 0;
-  virtual void PaintBackTrackPart(SkCanvas* canvas,
-                                  const gfx::Rect& rect) = 0;
-  virtual void PaintForwardTrackPart(SkCanvas* canvas,
-                                     const gfx::Rect& rect) = 0;
-  virtual void PaintBackButtonStart(SkCanvas* canvas,
-                                    const gfx::Rect& rect) = 0;
-  virtual void PaintBackButtonEnd(SkCanvas* canvas,
-                                  const gfx::Rect& rect) = 0;
-  virtual void PaintForwardButtonStart(SkCanvas* canvas,
-                                       const gfx::Rect& rect) = 0;
-  virtual void PaintForwardButtonEnd(SkCanvas* canvas,
-                                     const gfx::Rect& rect) = 0;
-  virtual void PaintTickmarks(SkCanvas* canvas, const gfx::Rect& rect) = 0;
-  virtual void PaintThumb(SkCanvas* canvas, const gfx::Rect& rect) = 0;
-};
-
-}  // namespace cc
-
-#endif  // CC_LAYERS_SCROLLBAR_THEME_PAINTER_H_
diff --git a/cc/test/fake_scrollbar.cc b/cc/test/fake_scrollbar.cc
index d8f0a47..6da534ef 100644
--- a/cc/test/fake_scrollbar.cc
+++ b/cc/test/fake_scrollbar.cc
@@ -88,9 +88,7 @@
   return has_tickmarks_;
 }
 
-void FakeScrollbar::PaintPart(PaintCanvas* canvas,
-                              ScrollbarPart part,
-                              const gfx::Rect& content_rect) {
+void FakeScrollbar::PaintPart(PaintCanvas* canvas, ScrollbarPart part) {
   if (!paint_)
     return;
 
@@ -100,12 +98,7 @@
   flags.setAntiAlias(false);
   flags.setColor(paint_fill_color());
   flags.setStyle(PaintFlags::kFill_Style);
-
-  // Emulate the how the real scrollbar works by using scrollbar's rect for
-  // TRACK and the given content_rect for the THUMB
-  SkRect rect = part == TRACK ? RectToSkRect(TrackRect())
-                              : RectToSkRect(content_rect);
-  canvas->drawRect(rect, flags);
+  canvas->drawRect(RectToSkRect(GetPartRect(part)), flags);
 }
 
 bool FakeScrollbar::UsesNinePatchThumbResource() const {
@@ -120,4 +113,23 @@
   return gfx::Rect();
 }
 
+gfx::Rect FakeScrollbar::GetPartRect(ScrollbarPart part) const {
+  switch (part) {
+    case THUMB:
+      if (UsesNinePatchThumbResource())
+        return gfx::Rect(NinePatchThumbCanvasSize());
+      if (Orientation() == VERTICAL)
+        return gfx::Rect(ThumbThickness(), ThumbLength());
+      return gfx::Rect(ThumbLength(), ThumbThickness());
+    case TRACK:
+      return gfx::UnionRects(
+          TrackRect(), gfx::UnionRects(BackButtonRect(), ForwardButtonRect()));
+    case TICKMARKS:
+      return gfx::Rect(TrackRect().size());
+    default:
+      NOTREACHED();
+      return gfx::Rect();
+  }
+}
+
 }  // namespace cc
diff --git a/cc/test/fake_scrollbar.h b/cc/test/fake_scrollbar.h
index 84648c80..86f6203 100644
--- a/cc/test/fake_scrollbar.h
+++ b/cc/test/fake_scrollbar.h
@@ -40,9 +40,7 @@
   float ThumbOpacity() const override;
   bool NeedsPaintPart(ScrollbarPart part) const override;
   bool HasTickmarks() const override;
-  void PaintPart(PaintCanvas* canvas,
-                 ScrollbarPart part,
-                 const gfx::Rect& content_rect) override;
+  void PaintPart(PaintCanvas* canvas, ScrollbarPart part) override;
   bool UsesNinePatchThumbResource() const override;
   gfx::Size NinePatchThumbCanvasSize() const override;
   gfx::Rect NinePatchThumbAperture() const override;
@@ -65,6 +63,8 @@
   }
   void set_has_tickmarks(bool has_tickmarks) { has_tickmarks_ = has_tickmarks; }
 
+  gfx::Rect GetPartRect(ScrollbarPart part) const;
+
  private:
   bool paint_;
   bool has_thumb_;
diff --git a/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
index 131dba0..4be54575 100644
--- a/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
+++ b/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
@@ -11,6 +11,7 @@
 #include "cc/layers/solid_color_layer.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_flags.h"
+#include "cc/test/fake_scrollbar.h"
 #include "cc/test/layer_tree_pixel_test.h"
 #include "cc/test/pixel_comparator.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -38,33 +39,23 @@
   float device_scale_factor_ = 1.f;
 };
 
-class PaintedScrollbar : public Scrollbar {
+class PaintedScrollbar : public FakeScrollbar {
  public:
-  ~PaintedScrollbar() override = default;
+  explicit PaintedScrollbar(const gfx::Size& size)
+      : FakeScrollbar(/*paint*/ true,
+                      /*has_thumb*/ false,
+                      HORIZONTAL,
+                      /*is_left_side_vertical_scrollbar*/ false,
+                      /*is_overlay*/ false) {
+    set_track_rect(gfx::Rect(size));
+  }
 
-  ScrollbarOrientation Orientation() const override { return VERTICAL; }
-  bool IsLeftSideVerticalScrollbar() const override { return false; }
-  gfx::Point Location() const override { return gfx::Point(); }
-  bool IsOverlay() const override { return false; }
-  bool HasThumb() const override { return thumb_; }
-  int ThumbThickness() const override { return rect_.width(); }
-  int ThumbLength() const override { return rect_.height(); }
-  gfx::Rect TrackRect() const override { return rect_; }
-  gfx::Rect BackButtonRect() const override { return rect_; }
-  gfx::Rect ForwardButtonRect() const override { return rect_; }
-  bool SupportsDragSnapBack() const override { return false; }
-  float ThumbOpacity() const override { return 1.f; }
-  bool NeedsPaintPart(ScrollbarPart part) const override { return true; }
-  bool HasTickmarks() const override { return false; }
-  void PaintPart(PaintCanvas* canvas,
-                 ScrollbarPart part,
-                 const gfx::Rect& content_rect) override {
+  void PaintPart(PaintCanvas* canvas, ScrollbarPart part) override {
     PaintFlags flags;
     flags.setStyle(PaintFlags::kStroke_Style);
     flags.setStrokeWidth(SkIntToScalar(paint_scale_));
     flags.setColor(color_);
-
-    gfx::Rect inset_rect = content_rect;
+    gfx::Rect inset_rect = GetPartRect(part);
     while (!inset_rect.IsEmpty()) {
       int big = paint_scale_ + 2;
       int small = paint_scale_;
@@ -73,17 +64,12 @@
       inset_rect.Inset(big, big, small, small);
     }
   }
-  bool UsesNinePatchThumbResource() const override { return false; }
-  gfx::Size NinePatchThumbCanvasSize() const override { return gfx::Size(); }
-  gfx::Rect NinePatchThumbAperture() const override { return gfx::Rect(); }
 
   void set_paint_scale(int scale) { paint_scale_ = scale; }
 
  private:
   int paint_scale_ = 4;
-  bool thumb_ = false;
   SkColor color_ = SK_ColorGREEN;
-  gfx::Rect rect_;
 };
 
 LayerTreeTest::RendererType const kRendererTypes[] = {
@@ -102,7 +88,7 @@
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
 
-  auto scrollbar = std::make_unique<PaintedScrollbar>();
+  auto scrollbar = std::make_unique<PaintedScrollbar>(gfx::Size(200, 200));
   scoped_refptr<PaintedScrollbarLayer> layer =
       PaintedScrollbarLayer::Create(std::move(scrollbar));
   layer->SetIsDrawable(true);
@@ -121,7 +107,7 @@
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE);
 
-  auto scrollbar = std::make_unique<PaintedScrollbar>();
+  auto scrollbar = std::make_unique<PaintedScrollbar>(gfx::Size(100, 100));
   scoped_refptr<PaintedScrollbarLayer> layer =
       PaintedScrollbarLayer::Create(std::move(scrollbar));
   layer->SetIsDrawable(true);
@@ -136,7 +122,7 @@
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE);
 
-  auto scrollbar = std::make_unique<PaintedScrollbar>();
+  auto scrollbar = std::make_unique<PaintedScrollbar>(gfx::Size(100, 100));
   scoped_refptr<PaintedScrollbarLayer> layer =
       PaintedScrollbarLayer::Create(std::move(scrollbar));
   layer->SetIsDrawable(true);
@@ -163,7 +149,7 @@
   scoped_refptr<SolidColorLayer> background =
       CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorWHITE);
 
-  auto scrollbar = std::make_unique<PaintedScrollbar>();
+  auto scrollbar = std::make_unique<PaintedScrollbar>(gfx::Size(10, 400));
   scrollbar->set_paint_scale(1);
   scoped_refptr<PaintedScrollbarLayer> layer =
       PaintedScrollbarLayer::Create(std::move(scrollbar));
@@ -216,18 +202,20 @@
   float thickness_scale_;
 };
 
-class PaintedOverlayScrollbar : public PaintedScrollbar {
+class PaintedOverlayScrollbar : public FakeScrollbar {
  public:
-  ~PaintedOverlayScrollbar() override = default;
+  PaintedOverlayScrollbar()
+      : FakeScrollbar(/*paint*/ true,
+                      /*has_thumb*/ true,
+                      VERTICAL,
+                      /*is_left_side_vertical_scrollbar*/ false,
+                      /*is_overlay*/ true) {
+    set_thumb_thickness(15);
+    set_thumb_length(50);
+    set_track_rect(gfx::Rect(0, 0, 15, 400));
+  }
 
-  int ThumbThickness() const override { return 15; }
-  int ThumbLength() const override { return 50; }
-  gfx::Rect TrackRect() const override { return gfx::Rect(0, 0, 15, 400); }
-  bool HasThumb() const override { return true; }
-  bool IsOverlay() const override { return true; }
-  void PaintPart(PaintCanvas* canvas,
-                 ScrollbarPart part,
-                 const gfx::Rect& content_rect) override {
+  void PaintPart(PaintCanvas* canvas, ScrollbarPart part) override {
     // The outside of the rect will be painted with a 1 pixel black, red, then
     // blue border. The inside will be solid blue. This will allow the test to
     // ensure that scaling the thumb doesn't scale the border at all.  Note
@@ -238,8 +226,7 @@
     flags.setStrokeWidth(SkIntToScalar(1));
     flags.setColor(SK_ColorBLACK);
 
-    gfx::Rect inset_rect = content_rect;
-
+    gfx::Rect inset_rect = GetPartRect(part);
     canvas->drawRect(RectToSkRect(inset_rect), flags);
 
     flags.setColor(SK_ColorRED);
@@ -250,6 +237,7 @@
     inset_rect.Inset(1, 1);
     canvas->drawRect(RectToSkRect(inset_rect), flags);
   }
+
   bool UsesNinePatchThumbResource() const override { return true; }
   gfx::Size NinePatchThumbCanvasSize() const override {
     return gfx::Size(7, 7);
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
index 023cfc5..aa9a8dc 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
@@ -679,6 +679,10 @@
   return markers_to_paint;
 }
 
+bool DocumentMarkerController::PossiblyHasTextMatchMarkers() const {
+  return PossiblyHasMarkers(DocumentMarker::kTextMatch);
+}
+
 Vector<IntRect> DocumentMarkerController::LayoutRectsForTextMatchMarkers() {
   DCHECK(!document_->View()->NeedsLayout());
   DCHECK(!document_->NeedsLayoutTreeUpdate());
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.h b/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
index 20d0a08..f9d2b4f 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
@@ -143,6 +143,7 @@
   DocumentMarkerVector Markers() const;
   DocumentMarkerVector ComputeMarkersToPaint(const Text&) const;
 
+  bool PossiblyHasTextMatchMarkers() const;
   Vector<IntRect> LayoutRectsForTextMatchMarkers();
   void InvalidateRectsForAllTextMatchMarkers();
   void InvalidateRectsForTextMatchMarkersInNode(const Text&);
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index ad99877..b3c8bf5 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -5220,8 +5220,7 @@
   // Get the tickmarks for the original find request.
   LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
   ScrollableArea* layout_viewport = frame_view->LayoutViewport();
-  Vector<IntRect> original_tickmarks;
-  layout_viewport->GetTickmarks(original_tickmarks);
+  Vector<IntRect> original_tickmarks = layout_viewport->GetTickmarks();
   EXPECT_EQ(4u, original_tickmarks.size());
 
   // Override the tickmarks.
@@ -5232,8 +5231,7 @@
   main_frame->SetTickmarks(overriding_tickmarks_expected);
 
   // Check the tickmarks are overriden correctly.
-  Vector<IntRect> overriding_tickmarks_actual;
-  layout_viewport->GetTickmarks(overriding_tickmarks_actual);
+  Vector<IntRect> overriding_tickmarks_actual = layout_viewport->GetTickmarks();
   EXPECT_EQ(overriding_tickmarks_expected, overriding_tickmarks_actual);
 
   // Reset the tickmark behavior.
@@ -5241,8 +5239,8 @@
   main_frame->SetTickmarks(reset_tickmarks);
 
   // Check that the original tickmarks are returned
-  Vector<IntRect> original_tickmarks_after_reset;
-  layout_viewport->GetTickmarks(original_tickmarks_after_reset);
+  Vector<IntRect> original_tickmarks_after_reset =
+      layout_viewport->GetTickmarks();
   EXPECT_EQ(original_tickmarks, original_tickmarks_after_reset);
 }
 
@@ -11379,8 +11377,8 @@
                                                       search_text, *options);
 
   // Get the tickmarks for the original find request.
-  Vector<IntRect> original_tickmarks;
-  frame_view->LayoutViewport()->GetTickmarks(original_tickmarks);
+  Vector<IntRect> original_tickmarks =
+      frame_view->LayoutViewport()->GetTickmarks();
   EXPECT_EQ(1u, original_tickmarks.size());
 
   EXPECT_EQ(IntPoint(800, 2000), original_tickmarks[0].Location());
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index f709c11..30ebb63 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -900,6 +900,11 @@
   }
 }
 
+bool LayoutView::HasTickmarks() const {
+  return !tickmarks_override_.IsEmpty() ||
+         GetDocument().Markers().PossiblyHasTextMatchMarkers();
+}
+
 Vector<IntRect> LayoutView::GetTickmarks() const {
   if (!tickmarks_override_.IsEmpty())
     return tickmarks_override_;
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index ca4d4bb..4b32f408 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -243,8 +243,9 @@
   PhysicalRect DebugRect() const override;
 
   // Returns the coordinates of find-in-page scrollbar tickmarks.  These come
-  // from DocumentMarkerController, unless overridden by SetTickmarks.
+  // from DocumentMarkerController, unless overridden by OverrideTickmarks().
   Vector<IntRect> GetTickmarks() const;
+  bool HasTickmarks() const;
 
   // Sets the coordinates of find-in-page scrollbar tickmarks, bypassing
   // DocumentMarkerController.  This is used by the PDF plugin.
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 985bb9b..d46dfac5 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2332,9 +2332,14 @@
       ->GetCompositorAnimationTimeline();
 }
 
-void PaintLayerScrollableArea::GetTickmarks(Vector<IntRect>& tickmarks) const {
+bool PaintLayerScrollableArea::HasTickmarks() const {
+  return layer_->IsRootLayer() && ToLayoutView(GetLayoutBox())->HasTickmarks();
+}
+
+Vector<IntRect> PaintLayerScrollableArea::GetTickmarks() const {
   if (layer_->IsRootLayer())
-    tickmarks = ToLayoutView(GetLayoutBox())->GetTickmarks();
+    return ToLayoutView(GetLayoutBox())->GetTickmarks();
+  return Vector<IntRect>();
 }
 
 void PaintLayerScrollableArea::ScrollbarManager::SetHasHorizontalScrollbar(
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 302a5da..60af30c4 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -337,7 +337,8 @@
   WebColorScheme UsedColorScheme() const override;
   cc::AnimationHost* GetCompositorAnimationHost() const override;
   CompositorAnimationTimeline* GetCompositorAnimationTimeline() const override;
-  void GetTickmarks(Vector<IntRect>&) const override;
+  bool HasTickmarks() const override;
+  Vector<IntRect> GetTickmarks() const override;
 
   void VisibleSizeChanged();
 
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index 095da3a..e24c8bb 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -199,7 +199,8 @@
   virtual bool IsScrollCornerVisible() const = 0;
   virtual IntRect ScrollCornerRect() const = 0;
   void SetScrollCornerNeedsPaintInvalidation();
-  virtual void GetTickmarks(Vector<IntRect>&) const {}
+  virtual bool HasTickmarks() const { return false; }
+  virtual Vector<IntRect> GetTickmarks() const { return Vector<IntRect>(); }
 
   // Convert points and rects between the scrollbar and its containing
   // EmbeddedContentView. The client needs to implement these in order to be
diff --git a/third_party/blink/renderer/core/scroll/scrollbar.cc b/third_party/blink/renderer/core/scroll/scrollbar.cc
index 7aecdaf..5f1b857 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar.cc
@@ -111,9 +111,15 @@
                           : kScrollbarOverlayColorThemeDark;
 }
 
-void Scrollbar::GetTickmarks(Vector<IntRect>& tickmarks) const {
+bool Scrollbar::HasTickmarks() const {
+  return orientation_ == kVerticalScrollbar && scrollable_area_ &&
+         scrollable_area_->HasTickmarks();
+}
+
+Vector<IntRect> Scrollbar::GetTickmarks() const {
   if (scrollable_area_)
-    scrollable_area_->GetTickmarks(tickmarks);
+    return scrollable_area_->GetTickmarks();
+  return Vector<IntRect>();
 }
 
 bool Scrollbar::IsScrollableAreaActive() const {
diff --git a/third_party/blink/renderer/core/scroll/scrollbar.h b/third_party/blink/renderer/core/scroll/scrollbar.h
index b4a27d2f..363f3256 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar.h
+++ b/third_party/blink/renderer/core/scroll/scrollbar.h
@@ -79,7 +79,8 @@
   const IntRect& FrameRect() const { return frame_rect_; }
 
   ScrollbarOverlayColorTheme GetScrollbarOverlayColorTheme() const;
-  void GetTickmarks(Vector<IntRect>&) const;
+  bool HasTickmarks() const;
+  Vector<IntRect> GetTickmarks() const;
   bool IsScrollableAreaActive() const;
 
   IntPoint ConvertFromRootFrame(const IntPoint&) const;
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.cc b/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.cc
index 6557dadb..0088012 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.cc
@@ -128,7 +128,7 @@
   return theme_.NinePatchThumbAperture(*scrollbar_);
 }
 
-bool ScrollbarLayerDelegate::HasTickmarks() const {
+bool ScrollbarLayerDelegate::ShouldPaint() const {
   // TODO(crbug.com/860499): Remove this condition, it should not occur.
   // Layers may exist and be painted for a |scrollbar_| that has had its
   // ScrollableArea detached. This seems weird because if the area is detached
@@ -140,80 +140,45 @@
   // HasTickmarks can't be known and may change once the frame is unthrottled.
   if (scrollbar_->GetScrollableArea()->IsThrottled())
     return false;
+  return true;
+}
 
-  Vector<IntRect> tickmarks;
-  scrollbar_->GetTickmarks(tickmarks);
-  return !tickmarks.IsEmpty();
+bool ScrollbarLayerDelegate::HasTickmarks() const {
+  return ShouldPaint() && scrollbar_->HasTickmarks();
 }
 
 void ScrollbarLayerDelegate::PaintPart(cc::PaintCanvas* canvas,
-                                       cc::ScrollbarPart part,
-                                       const gfx::Rect& content_rect) {
-  PaintCanvasAutoRestore auto_restore(canvas, true);
-  blink::Scrollbar& scrollbar = *scrollbar_;
-
-  // TODO(crbug.com/860499): Remove this condition, it should not occur.
-  // Layers may exist and be painted for a |scrollbar_| that has had its
-  // ScrollableArea detached. This seems weird because if the area is detached
-  // the layer should be destroyed but here we are.
-  if (!scrollbar_->GetScrollableArea())
-    return;
-  // When the frame is throttled, the scrollbar will not be painted because
-  // the frame has not had its lifecycle updated.
-  if (scrollbar.GetScrollableArea()->IsThrottled())
+                                       cc::ScrollbarPart part) {
+  if (!ShouldPaint())
     return;
 
-  if (part == cc::THUMB) {
-    ScopedScrollbarPainter painter(*canvas, device_scale_factor_);
-    theme_.PaintThumb(painter.Context(), scrollbar, IntRect(content_rect));
-    if (!theme_.ShouldRepaintAllPartsOnInvalidation())
-      scrollbar.ClearThumbNeedsRepaint();
-    return;
-  }
-
-  if (part == cc::TICKMARKS) {
-    ScopedScrollbarPainter painter(*canvas, device_scale_factor_);
-    theme_.PaintTickmarks(painter.Context(), scrollbar, IntRect(content_rect));
-    return;
-  }
-
-  canvas->clipRect(gfx::RectToSkRect(content_rect));
   ScopedScrollbarPainter painter(*canvas, device_scale_factor_);
-  GraphicsContext& context = painter.Context();
-
-  theme_.PaintScrollbarBackground(context, scrollbar);
-
-  if (theme_.HasButtons(scrollbar)) {
-    theme_.PaintButton(context, scrollbar,
-                       theme_.BackButtonRect(scrollbar, kBackButtonStartPart),
-                       kBackButtonStartPart);
-    theme_.PaintButton(context, scrollbar,
-                       theme_.BackButtonRect(scrollbar, kBackButtonEndPart),
-                       kBackButtonEndPart);
-    theme_.PaintButton(
-        context, scrollbar,
-        theme_.ForwardButtonRect(scrollbar, kForwardButtonStartPart),
-        kForwardButtonStartPart);
-    theme_.PaintButton(
-        context, scrollbar,
-        theme_.ForwardButtonRect(scrollbar, kForwardButtonEndPart),
-        kForwardButtonEndPart);
+  // The canvas coordinate space is relative to the part's origin.
+  switch (part) {
+    case cc::THUMB: {
+      IntRect rect(IntPoint(),
+                   UsesNinePatchThumbResource()
+                       ? theme_.NinePatchThumbCanvasSize(*scrollbar_)
+                       : theme_.ThumbRect(*scrollbar_).Size());
+      theme_.PaintThumb(painter.Context(), *scrollbar_, rect);
+      scrollbar_->ClearThumbNeedsRepaint();
+      break;
+    }
+    case cc::TRACK: {
+      theme_.PaintTrackAndButtonsForCompositor(painter.Context(), *scrollbar_);
+      theme_.PaintTickmarks(painter.Context(), *scrollbar_,
+                            IntRect(TrackRect()));
+      scrollbar_->ClearTrackNeedsRepaint();
+      break;
+    }
+    case cc::TICKMARKS: {
+      IntRect rect(IntPoint(), theme_.TrackRect(*scrollbar_).Size());
+      theme_.PaintTickmarks(painter.Context(), *scrollbar_, rect);
+      break;
+    }
+    default:
+      NOTREACHED();
   }
-
-  IntRect track_paint_rect = theme_.TrackRect(scrollbar);
-  theme_.PaintTrackBackground(context, scrollbar, track_paint_rect);
-
-  if (theme_.HasThumb(scrollbar)) {
-    theme_.PaintTrackPiece(painter.Context(), scrollbar, track_paint_rect,
-                           kForwardTrackPart);
-    theme_.PaintTrackPiece(painter.Context(), scrollbar, track_paint_rect,
-                           kBackTrackPart);
-  }
-
-  theme_.PaintTickmarks(painter.Context(), scrollbar, track_paint_rect);
-
-  if (!theme_.ShouldRepaintAllPartsOnInvalidation())
-    scrollbar.ClearTrackNeedsRepaint();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.h b/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.h
index 6c2d8ec6..c0136ed9 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.h
+++ b/third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.h
@@ -52,15 +52,15 @@
   float ThumbOpacity() const override;
   bool NeedsPaintPart(cc::ScrollbarPart part) const override;
   bool HasTickmarks() const override;
-  void PaintPart(cc::PaintCanvas* canvas,
-                 cc::ScrollbarPart part,
-                 const gfx::Rect& content_rect) override;
+  void PaintPart(cc::PaintCanvas* canvas, cc::ScrollbarPart part) override;
 
   bool UsesNinePatchThumbResource() const override;
   gfx::Size NinePatchThumbCanvasSize() const override;
   gfx::Rect NinePatchThumbAperture() const override;
 
  private:
+  bool ShouldPaint() const;
+
   // Accessed by main and compositor threads, e.g., the compositor thread
   // checks |Orientation()|.
   CrossThreadPersistent<blink::Scrollbar> scrollbar_;
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_theme.cc b/third_party/blink/renderer/core/scroll/scrollbar_theme.cc
index f71b889..f5a2d8aa 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_theme.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar_theme.cc
@@ -223,8 +223,7 @@
     return;
 
   // Get the tickmarks for the frameview.
-  Vector<IntRect> tickmarks;
-  scrollbar.GetTickmarks(tickmarks);
+  Vector<IntRect> tickmarks = scrollbar.GetTickmarks();
   if (!tickmarks.size())
     return;
 
@@ -394,6 +393,43 @@
   return g_mock_scrollbars_enabled_;
 }
 
+void ScrollbarTheme::PaintTrackAndButtonsForCompositor(
+    GraphicsContext& context,
+    const Scrollbar& scrollbar) {
+  // We paint compositor scrollbars in the space relative to the scrollbar's
+  // origin.
+  IntPoint offset = -scrollbar.Location();
+
+  if (HasButtons(scrollbar)) {
+    IntRect back_button_rect = BackButtonRect(scrollbar, kBackButtonStartPart);
+    back_button_rect.MoveBy(offset);
+    PaintButton(context, scrollbar, back_button_rect, kBackButtonStartPart);
+
+    IntRect forward_button_rect =
+        ForwardButtonRect(scrollbar, kForwardButtonEndPart);
+    forward_button_rect.MoveBy(offset);
+    PaintButton(context, scrollbar, forward_button_rect, kForwardButtonEndPart);
+
+    // Composited scrollbars don't have kBackButtonEndPart and
+    // kForwardButtonStartPart.
+    DCHECK(BackButtonRect(scrollbar, kBackButtonEndPart).IsEmpty());
+    DCHECK(ForwardButtonRect(scrollbar, kForwardButtonStartPart).IsEmpty());
+  }
+
+  IntRect track_rect = TrackRect(scrollbar);
+  track_rect.MoveBy(offset);
+  PaintTrackBackground(context, scrollbar, track_rect);
+
+  if (HasThumb(scrollbar)) {
+    // We don't repaint composited scrollbars on thumb position change, so
+    // the back track and forward track can't depend on thumb position. Just
+    // paint them on the whole track. All scrollbar themes for composited
+    // scrollbars know how to handle this case.
+    PaintTrackPiece(context, scrollbar, track_rect, kBackTrackPart);
+    PaintTrackPiece(context, scrollbar, track_rect, kForwardTrackPart);
+  }
+}
+
 DisplayItem::Type ScrollbarTheme::ButtonPartToDisplayItemType(
     ScrollbarPart part) {
   switch (part) {
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_theme.h b/third_party/blink/renderer/core/scroll/scrollbar_theme.h
index 531d146..e2787208 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_theme.h
+++ b/third_party/blink/renderer/core/scroll/scrollbar_theme.h
@@ -156,29 +156,9 @@
                           IntRect& thumb,
                           IntRect& end_track);
 
-  virtual void PaintScrollbarBackground(GraphicsContext&, const Scrollbar&) {}
-  virtual void PaintTrackBackground(GraphicsContext&,
-                                    const Scrollbar&,
-                                    const IntRect&) {}
-  virtual void PaintTrackPiece(GraphicsContext&,
-                               const Scrollbar&,
-                               const IntRect&,
-                               ScrollbarPart) {}
-  virtual void PaintButton(GraphicsContext&,
-                           const Scrollbar&,
-                           const IntRect&,
-                           ScrollbarPart) {}
   virtual void PaintThumb(GraphicsContext&, const Scrollbar&, const IntRect&) {}
 
-  // Paint the thumb with ThumbOpacity() applied.
-  virtual void PaintThumbWithOpacity(GraphicsContext& context,
-                                     const Scrollbar& scrollbar,
-                                     const IntRect& rect) {
-    // By default this method just calls PaintThumb(). A theme with custom
-    // ThumbOpacity() should override this method to apply the opacity.
-    DCHECK_EQ(1.0f, ThumbOpacity(scrollbar));
-    PaintThumb(context, scrollbar, rect);
-  }
+  void PaintTrackAndButtonsForCompositor(GraphicsContext&, const Scrollbar&);
 
   virtual int MaxOverlapBetweenPages() {
     return std::numeric_limits<int>::max();
@@ -226,6 +206,29 @@
 
  protected:
   virtual int TickmarkBorderWidth() { return 0; }
+  virtual void PaintScrollbarBackground(GraphicsContext&, const Scrollbar&) {}
+  virtual void PaintTrackBackground(GraphicsContext&,
+                                    const Scrollbar&,
+                                    const IntRect&) {}
+  virtual void PaintTrackPiece(GraphicsContext&,
+                               const Scrollbar&,
+                               const IntRect&,
+                               ScrollbarPart) {}
+  virtual void PaintButton(GraphicsContext&,
+                           const Scrollbar&,
+                           const IntRect&,
+                           ScrollbarPart) {}
+
+  // Paint the thumb with ThumbOpacity() applied.
+  virtual void PaintThumbWithOpacity(GraphicsContext& context,
+                                     const Scrollbar& scrollbar,
+                                     const IntRect& rect) {
+    // By default this method just calls PaintThumb(). A theme with custom
+    // ThumbOpacity() should override this method to apply the opacity.
+    DCHECK_EQ(1.0f, ThumbOpacity(scrollbar));
+    PaintThumb(context, scrollbar, rect);
+  }
+
   static DisplayItem::Type ButtonPartToDisplayItemType(ScrollbarPart);
   static DisplayItem::Type TrackPiecePartToDisplayItemType(ScrollbarPart);
 
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 55dcbfa..6e0b01d6 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -267,6 +267,7 @@
             'cc::HORIZONTAL',
             'cc::VERTICAL',
             'cc::THUMB',
+            'cc::TRACK',
             'cc::TICKMARKS',
             'cc::BrowserControlsState',
             'cc::EventListenerClass',