1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.h"
6 
7 #include "cc/paint/paint_canvas.h"
8 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
9 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
10 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
11 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
12 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
13 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
14 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
15 #include "ui/gfx/skia_util.h"
16 
17 namespace blink {
18 
19 namespace {
20 
21 class ScopedScrollbarPainter {
22  public:
ScopedScrollbarPainter(cc::PaintCanvas & canvas,float device_scale_factor)23   ScopedScrollbarPainter(cc::PaintCanvas& canvas, float device_scale_factor)
24       : canvas_(canvas) {
25     builder_.Context().SetDeviceScaleFactor(device_scale_factor);
26   }
~ScopedScrollbarPainter()27   ~ScopedScrollbarPainter() { canvas_.drawPicture(builder_.EndRecording()); }
28 
Context()29   GraphicsContext& Context() { return builder_.Context(); }
30 
31  private:
32   cc::PaintCanvas& canvas_;
33   PaintRecordBuilder builder_;
34 };
35 
36 }  // namespace
37 
ScrollbarLayerDelegate(blink::Scrollbar & scrollbar,float device_scale_factor)38 ScrollbarLayerDelegate::ScrollbarLayerDelegate(blink::Scrollbar& scrollbar,
39                                                float device_scale_factor)
40     : scrollbar_(&scrollbar), device_scale_factor_(device_scale_factor) {
41   // Custom scrollbars are either non-composited or use cc::PictureLayers
42   // which don't need ScrollbarLayerDelegate.
43   DCHECK(!scrollbar.IsCustomScrollbar());
44 }
45 
46 ScrollbarLayerDelegate::~ScrollbarLayerDelegate() = default;
47 
IsSame(const cc::Scrollbar & other) const48 bool ScrollbarLayerDelegate::IsSame(const cc::Scrollbar& other) const {
49   return scrollbar_.Get() ==
50          static_cast<const ScrollbarLayerDelegate&>(other).scrollbar_.Get();
51 }
52 
Orientation() const53 cc::ScrollbarOrientation ScrollbarLayerDelegate::Orientation() const {
54   if (scrollbar_->Orientation() == kHorizontalScrollbar)
55     return cc::ScrollbarOrientation::HORIZONTAL;
56   return cc::ScrollbarOrientation::VERTICAL;
57 }
58 
IsLeftSideVerticalScrollbar() const59 bool ScrollbarLayerDelegate::IsLeftSideVerticalScrollbar() const {
60   return scrollbar_->IsLeftSideVerticalScrollbar();
61 }
62 
HasThumb() const63 bool ScrollbarLayerDelegate::HasThumb() const {
64   return scrollbar_->GetTheme().HasThumb(*scrollbar_);
65 }
66 
IsSolidColor() const67 bool ScrollbarLayerDelegate::IsSolidColor() const {
68   return scrollbar_->GetTheme().IsSolidColor();
69 }
70 
IsOverlay() const71 bool ScrollbarLayerDelegate::IsOverlay() const {
72   return scrollbar_->IsOverlayScrollbar();
73 }
74 
ThumbRect() const75 gfx::Rect ScrollbarLayerDelegate::ThumbRect() const {
76   IntRect track_rect = scrollbar_->GetTheme().ThumbRect(*scrollbar_);
77   track_rect.MoveBy(-scrollbar_->Location());
78   return track_rect;
79 }
80 
TrackRect() const81 gfx::Rect ScrollbarLayerDelegate::TrackRect() const {
82   IntRect track_rect = scrollbar_->GetTheme().TrackRect(*scrollbar_);
83   track_rect.MoveBy(-scrollbar_->Location());
84   return track_rect;
85 }
86 
SupportsDragSnapBack() const87 bool ScrollbarLayerDelegate::SupportsDragSnapBack() const {
88   return scrollbar_->GetTheme().SupportsDragSnapBack();
89 }
90 
JumpOnTrackClick() const91 bool ScrollbarLayerDelegate::JumpOnTrackClick() const {
92   return scrollbar_->GetTheme().JumpOnTrackClick();
93 }
94 
BackButtonRect() const95 gfx::Rect ScrollbarLayerDelegate::BackButtonRect() const {
96   IntRect back_button_rect = scrollbar_->GetTheme().BackButtonRect(*scrollbar_);
97   if (!back_button_rect.IsEmpty())
98     back_button_rect.MoveBy(-scrollbar_->Location());
99   return back_button_rect;
100 }
101 
ForwardButtonRect() const102 gfx::Rect ScrollbarLayerDelegate::ForwardButtonRect() const {
103   IntRect forward_button_rect =
104       scrollbar_->GetTheme().ForwardButtonRect(*scrollbar_);
105   if (!forward_button_rect.IsEmpty())
106     forward_button_rect.MoveBy(-scrollbar_->Location());
107   return forward_button_rect;
108 }
109 
Opacity() const110 float ScrollbarLayerDelegate::Opacity() const {
111   return scrollbar_->GetTheme().Opacity(*scrollbar_);
112 }
113 
NeedsRepaintPart(cc::ScrollbarPart part) const114 bool ScrollbarLayerDelegate::NeedsRepaintPart(cc::ScrollbarPart part) const {
115   if (part == cc::ScrollbarPart::THUMB)
116     return scrollbar_->ThumbNeedsRepaint();
117   return scrollbar_->TrackNeedsRepaint();
118 }
119 
UsesNinePatchThumbResource() const120 bool ScrollbarLayerDelegate::UsesNinePatchThumbResource() const {
121   return scrollbar_->GetTheme().UsesNinePatchThumbResource();
122 }
123 
NinePatchThumbCanvasSize() const124 gfx::Size ScrollbarLayerDelegate::NinePatchThumbCanvasSize() const {
125   DCHECK(scrollbar_->GetTheme().UsesNinePatchThumbResource());
126   return static_cast<gfx::Size>(
127       scrollbar_->GetTheme().NinePatchThumbCanvasSize(*scrollbar_));
128 }
129 
NinePatchThumbAperture() const130 gfx::Rect ScrollbarLayerDelegate::NinePatchThumbAperture() const {
131   DCHECK(scrollbar_->GetTheme().UsesNinePatchThumbResource());
132   return scrollbar_->GetTheme().NinePatchThumbAperture(*scrollbar_);
133 }
134 
ShouldPaint() const135 bool ScrollbarLayerDelegate::ShouldPaint() const {
136   // TODO(crbug.com/860499): Remove this condition, it should not occur.
137   // Layers may exist and be painted for a |scrollbar_| that has had its
138   // ScrollableArea detached. This seems weird because if the area is detached
139   // the layer should be destroyed but here we are. https://crbug.com/860499.
140   if (!scrollbar_->GetScrollableArea())
141     return false;
142   // When the frame is throttled, the scrollbar will not be painted because
143   // the frame has not had its lifecycle updated. Thus the actual value of
144   // HasTickmarks can't be known and may change once the frame is unthrottled.
145   if (scrollbar_->GetScrollableArea()->IsThrottled())
146     return false;
147   return true;
148 }
149 
HasTickmarks() const150 bool ScrollbarLayerDelegate::HasTickmarks() const {
151   return ShouldPaint() && scrollbar_->HasTickmarks();
152 }
153 
PaintPart(cc::PaintCanvas * canvas,cc::ScrollbarPart part,const gfx::Rect & rect)154 void ScrollbarLayerDelegate::PaintPart(cc::PaintCanvas* canvas,
155                                        cc::ScrollbarPart part,
156                                        const gfx::Rect& rect) {
157   if (!ShouldPaint())
158     return;
159 
160   auto& theme = scrollbar_->GetTheme();
161   ScopedScrollbarPainter painter(*canvas, device_scale_factor_);
162   // The canvas coordinate space is relative to the part's origin.
163   switch (part) {
164     case cc::ScrollbarPart::THUMB:
165       theme.PaintThumb(painter.Context(), *scrollbar_, IntRect(rect));
166       scrollbar_->ClearThumbNeedsRepaint();
167       break;
168     case cc::ScrollbarPart::TRACK_BUTTONS_TICKMARKS: {
169       DCHECK_EQ(IntSize(rect.size()), scrollbar_->FrameRect().Size());
170       IntPoint offset(IntPoint(rect.origin()) -
171                       scrollbar_->FrameRect().Location());
172       theme.PaintTrackButtonsTickmarks(painter.Context(), *scrollbar_, offset);
173       scrollbar_->ClearTrackNeedsRepaint();
174       break;
175     }
176     default:
177       NOTREACHED();
178   }
179 }
180 
181 }  // namespace blink
182