1 // Copyright (c) 2012 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 "ui/native_theme/native_theme_base.h"
6 
7 #include <limits>
8 #include <memory>
9 
10 #include "base/check.h"
11 #include "base/command_line.h"
12 #include "base/notreached.h"
13 #include "base/numerics/ranges.h"
14 #include "build/build_config.h"
15 #include "cc/paint/paint_flags.h"
16 #include "cc/paint/paint_shader.h"
17 #include "third_party/skia/include/core/SkPath.h"
18 #include "third_party/skia/include/effects/SkGradientShader.h"
19 #include "ui/base/layout.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/base/ui_base_features.h"
22 #include "ui/base/ui_base_switches.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/color_palette.h"
25 #include "ui/gfx/color_utils.h"
26 #include "ui/gfx/geometry/rect.h"
27 #include "ui/gfx/geometry/rect_f.h"
28 #include "ui/gfx/geometry/size.h"
29 #include "ui/gfx/image/image_skia.h"
30 #include "ui/gfx/skia_util.h"
31 #include "ui/native_theme/common_theme.h"
32 
33 namespace {
34 
35 // These are the default dimensions of radio buttons and checkboxes.
36 const int kCheckboxAndRadioWidth = 13;
37 const int kCheckboxAndRadioHeight = 13;
38 
39 // These sizes match the sizes in Chromium Win.
40 const int kSliderThumbWidth = 11;
41 const int kSliderThumbHeight = 21;
42 
43 // Color constant pairs for light/default and dark color-schemes below.
44 constexpr SkColor kThumbActiveColor[2] = {SkColorSetRGB(0xF4, 0xF4, 0xF4),
45                                           gfx::kPlaceholderColor};
46 constexpr SkColor kThumbInactiveColor[2] = {SkColorSetRGB(0xEA, 0xEA, 0xEA),
47                                             SK_ColorWHITE};
48 constexpr SkColor kTrackColor[2] = {SkColorSetRGB(0xD3, 0xD3, 0xD3),
49                                     gfx::kPlaceholderColor};
50 constexpr SkColor kSliderTrackBackgroundColor[2] = {
51     SkColorSetRGB(0xE3, 0xDD, 0xD8), SkColorSetRGB(0x44, 0x44, 0x44)};
52 constexpr SkColor kSliderThumbBrightColor[2] = {
53     SkColorSetRGB(0xF4, 0xF2, 0xEF), SkColorSetRGB(0xD0, 0xD0, 0xD0)};
54 constexpr SkColor kSliderThumbShadedColor[2] = {
55     SkColorSetRGB(0xEA, 0xE5, 0xE0), SkColorSetRGB(0xC4, 0xC4, 0xC4)};
56 constexpr SkColor kSliderThumbHoveredBrightColor[2] = {
57     SK_ColorWHITE, SkColorSetRGB(0xDD, 0xDD, 0xDD)};
58 constexpr SkColor kSliderThumbHoveredShadedColor[2] = {
59     SkColorSetRGB(0xF4, 0xF2, 0xEF), SkColorSetRGB(0xD0, 0xD0, 0xD0)};
60 constexpr SkColor kSliderThumbBorder[2] = {SkColorSetRGB(0x9D, 0x96, 0x8E),
61                                            SkColorSetRGB(0x63, 0x6C, 0x72)};
62 constexpr SkColor kTextBorderColor[2] = {SkColorSetRGB(0xA9, 0xA9, 0xA9),
63                                          SkColorSetRGB(0x60, 0x60, 0x60)};
64 constexpr SkColor kProgressBorderColor[2] = {SkColorSetRGB(0xA9, 0xA9, 0xA9),
65                                              SkColorSetRGB(0x60, 0x60, 0x60)};
66 constexpr SkColor kProgressTickColor[2] = {SkColorSetRGB(0xED, 0xED, 0xED),
67                                            SkColorSetRGB(0x20, 0x20, 0x20)};
68 constexpr SkColor kProgressValueColor[2] = {gfx::kGoogleBlue300,
69                                             gfx::kGoogleBlue700};
70 // We are currently only painting kMenuPopupBackground with the kDefault
71 // scheme. If that changes, we need to replace gfx::kPlaceholderColor with an
72 // appropriate dark scheme color. See the DCHECK in PaintMenuPopupBackground().
73 constexpr SkColor kMenuPopupBackgroundColor[2] = {SkColorSetRGB(210, 225, 246),
74                                                   gfx::kPlaceholderColor};
75 constexpr SkColor kCheckboxTinyColor[2] = {SK_ColorGRAY, SK_ColorDKGRAY};
76 constexpr SkColor kCheckboxShadowColor[2] = {SkColorSetA(SK_ColorBLACK, 0x15),
77                                              SkColorSetA(SK_ColorWHITE, 0x15)};
78 constexpr SkColor kCheckboxShadowHoveredColor[2] = {
79     SkColorSetA(SK_ColorBLACK, 0x1F), SkColorSetA(SK_ColorWHITE, 0x1F)};
80 constexpr SkColor kCheckboxShadowDisabledColor[2] = {
81     SK_ColorTRANSPARENT, SkColorSetA(SK_ColorWHITE, 0x1F)};
82 constexpr SkColor kCheckboxGradientStartColor[2] = {
83     SkColorSetRGB(0xED, 0xED, 0xED), SkColorSetRGB(0x13, 0x13, 0x13)};
84 constexpr SkColor kCheckboxGradientEndColor[2] = {
85     SkColorSetRGB(0xDE, 0xDE, 0xDE), SkColorSetRGB(0x20, 0x20, 0x20)};
86 constexpr U8CPU kCheckboxDisabledGradientAlpha = 0x80;
87 constexpr SkColor kCheckboxPressedGradientStartColor[2] = {
88     SkColorSetRGB(0xE7, 0xE7, 0xE7), SkColorSetRGB(0x19, 0x19, 0x19)};
89 constexpr SkColor kCheckboxPressedGradientEndColor[2] = {
90     SkColorSetRGB(0xD7, 0xD7, 0xD7), SkColorSetRGB(0x27, 0x27, 0x27)};
91 const SkColor kCheckboxHoveredGradientStartColor[2] = {
92     SkColorSetRGB(0xF0, 0xF0, 0xF0), SkColorSetRGB(0x16, 0x16, 0x16)};
93 const SkColor kCheckboxHoveredGradientEndColor[2] = {
94     SkColorSetRGB(0xE0, 0xE0, 0xE0), SkColorSetRGB(0x20, 0x20, 0x20)};
95 constexpr SkColor kCheckboxBorderColor[2] = {SkColorSetA(SK_ColorBLACK, 0x40),
96                                              SkColorSetA(SK_ColorWHITE, 0x40)};
97 constexpr SkColor kCheckboxBorderHoveredColor[2] = {
98     SkColorSetA(SK_ColorBLACK, 0x4D), SkColorSetA(SK_ColorWHITE, 0x4D)};
99 constexpr SkColor kCheckboxBorderDisabledColor[2] = {
100     SkColorSetA(SK_ColorBLACK, 0x20), SkColorSetA(SK_ColorWHITE, 0x20)};
101 constexpr SkColor kCheckboxStrokeColor[2] = {SkColorSetA(SK_ColorBLACK, 0xB3),
102                                              SkColorSetA(SK_ColorWHITE, 0xB3)};
103 constexpr SkColor kCheckboxStrokeDisabledColor[2] = {
104     SkColorSetA(SK_ColorBLACK, 0x59), SkColorSetA(SK_ColorWHITE, 0x59)};
105 constexpr SkColor kRadioDotColor[2] = {SkColorSetRGB(0x66, 0x66, 0x66),
106                                        SkColorSetRGB(0xDD, 0xDD, 0xDD)};
107 constexpr SkColor kRadioDotDisabledColor[2] = {
108     SkColorSetARGB(0x80, 0x66, 0x66, 0x66),
109     SkColorSetARGB(0x80, 0xDD, 0xDD, 0xDD)};
110 constexpr SkColor kArrowDisabledColor[2] = {SK_ColorBLACK, SK_ColorWHITE};
111 constexpr SkColor kButtonBorderColor[2] = {SK_ColorBLACK, SK_ColorWHITE};
112 constexpr SkColor kProgressBackgroundColor[2] = {SK_ColorWHITE, SK_ColorBLACK};
113 
114 // The "dash" is 8x2 px by default (the checkbox is 13x13 px).
115 const SkScalar kIndeterminateInsetWidthRatio = (13 - 8) / 2.0f / 13;
116 const SkScalar kIndeterminateInsetHeightRatio = (13 - 2) / 2.0f / 13;
117 const SkScalar kBorderWidth = 1.f;
118 const SkScalar kSliderTrackHeight = 8.f;
119 const SkScalar kSliderThumbBorderWidth = 1.f;
120 const SkScalar kSliderThumbBorderHoveredWidth = 1.f;
121 // Default height for progress is 16px and the track is 8px.
122 const SkScalar kTrackHeightRatio = 8.0f / 16;
123 const SkScalar kMenuListArrowStrokeWidth = 2.f;
124 const int kSliderThumbSize = 16;
125 
126 // Get a color constant based on color-scheme
GetColor(const SkColor colors[2],ui::NativeTheme::ColorScheme color_scheme)127 SkColor GetColor(const SkColor colors[2],
128                  ui::NativeTheme::ColorScheme color_scheme) {
129   return colors[color_scheme == ui::NativeTheme::ColorScheme::kDark ? 1 : 0];
130 }
131 
132 // Get lightness adjusted color.
BrightenColor(const color_utils::HSL & hsl,SkAlpha alpha,double lightness_amount)133 SkColor BrightenColor(const color_utils::HSL& hsl, SkAlpha alpha,
134     double lightness_amount) {
135   color_utils::HSL adjusted = hsl;
136   adjusted.l += lightness_amount;
137   if (adjusted.l > 1.0)
138     adjusted.l = 1.0;
139   if (adjusted.l < 0.0)
140     adjusted.l = 0.0;
141 
142   return color_utils::HSLToSkColor(adjusted, alpha);
143 }
144 
145 }  // namespace
146 
147 namespace ui {
148 
GetPartSize(Part part,State state,const ExtraParams & extra) const149 gfx::Size NativeThemeBase::GetPartSize(Part part,
150                                        State state,
151                                        const ExtraParams& extra) const {
152   switch (part) {
153     // Please keep these in the order of NativeTheme::Part.
154     case kCheckbox:
155       return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
156     case kInnerSpinButton:
157       return gfx::Size(scrollbar_width_, 0);
158     case kMenuList:
159       return gfx::Size();  // No default size.
160     case kMenuPopupBackground:
161       return gfx::Size();  // No default size.
162     case kMenuItemBackground:
163     case kProgressBar:
164     case kPushButton:
165       return gfx::Size();  // No default size.
166     case kRadio:
167       return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
168     case kScrollbarDownArrow:
169     case kScrollbarUpArrow:
170       return gfx::Size(scrollbar_width_, scrollbar_button_length_);
171     case kScrollbarLeftArrow:
172     case kScrollbarRightArrow:
173       return gfx::Size(scrollbar_button_length_, scrollbar_width_);
174     case kScrollbarHorizontalThumb:
175       // This matches Firefox on Linux.
176       return gfx::Size(2 * scrollbar_width_, scrollbar_width_);
177     case kScrollbarVerticalThumb:
178       // This matches Firefox on Linux.
179       return gfx::Size(scrollbar_width_, 2 * scrollbar_width_);
180     case kScrollbarHorizontalTrack:
181       return gfx::Size(0, scrollbar_width_);
182     case kScrollbarVerticalTrack:
183       return gfx::Size(scrollbar_width_, 0);
184     case kScrollbarHorizontalGripper:
185     case kScrollbarVerticalGripper:
186       NOTIMPLEMENTED();
187       break;
188     case kSliderTrack:
189       return gfx::Size();  // No default size.
190     case kSliderThumb:
191       // These sizes match the sizes in Chromium Win.
192       if (features::IsFormControlsRefreshEnabled())
193         return gfx::Size(kSliderThumbSize, kSliderThumbSize);
194       return gfx::Size(kSliderThumbWidth, kSliderThumbHeight);
195     case kTabPanelBackground:
196       NOTIMPLEMENTED();
197       break;
198     case kTextField:
199       return gfx::Size();  // No default size.
200     case kTrackbarThumb:
201     case kTrackbarTrack:
202     case kWindowResizeGripper:
203       NOTIMPLEMENTED();
204       break;
205     default:
206       NOTREACHED() << "Unknown theme part: " << part;
207       break;
208   }
209   return gfx::Size();
210 }
211 
GetBorderRadiusForPart(Part part,float width,float height,float zoom) const212 float NativeThemeBase::GetBorderRadiusForPart(Part part,
213                                               float width,
214                                               float height,
215                                               float zoom) const {
216   if (!features::IsFormControlsRefreshEnabled()) {
217     NOTREACHED() << "GetBorderRadiusForPart only supports FormControlsRefresh.";
218     return 0;
219   }
220 
221   switch (part) {
222     case kCheckbox:
223       return 2.f * zoom;
224     case kPushButton:
225     case kTextField:
226       return 2.f;
227     case kRadio:
228       return std::max(width, height) * 0.5;
229     case kProgressBar:
230     case kSliderTrack:
231       // default border radius for progress and range is 40px.
232       return 40.f;
233     case kSliderThumb:
234       return std::max(width, height) * 0.5;
235     default:
236       break;
237   }
238   return 0;
239 }
240 
Paint(cc::PaintCanvas * canvas,Part part,State state,const gfx::Rect & rect,const ExtraParams & extra,ColorScheme color_scheme) const241 void NativeThemeBase::Paint(cc::PaintCanvas* canvas,
242                             Part part,
243                             State state,
244                             const gfx::Rect& rect,
245                             const ExtraParams& extra,
246                             ColorScheme color_scheme) const {
247   if (rect.IsEmpty())
248     return;
249 
250   canvas->save();
251   canvas->clipRect(gfx::RectToSkRect(rect));
252 
253   switch (part) {
254     // Please keep these in the order of NativeTheme::Part.
255     case kCheckbox:
256       PaintCheckbox(canvas, state, rect, extra.button, color_scheme);
257       break;
258 #if (defined(OS_LINUX) || defined(OS_BSD)) && !defined(OS_CHROMEOS)
259     case kFrameTopArea:
260       PaintFrameTopArea(canvas, state, rect, extra.frame_top_area,
261                         color_scheme);
262       break;
263 #endif
264     case kInnerSpinButton:
265       PaintInnerSpinButton(canvas, state, rect, extra.inner_spin, color_scheme);
266       break;
267     case kMenuList:
268       PaintMenuList(canvas, state, rect, extra.menu_list, color_scheme);
269       break;
270     case kMenuPopupBackground:
271       PaintMenuPopupBackground(canvas, rect.size(), extra.menu_background,
272                                color_scheme);
273       break;
274     case kMenuPopupSeparator:
275       PaintMenuSeparator(canvas, state, rect, extra.menu_separator,
276                          color_scheme);
277       break;
278     case kMenuItemBackground:
279       PaintMenuItemBackground(canvas, state, rect, extra.menu_item,
280                               color_scheme);
281       break;
282     case kProgressBar:
283       PaintProgressBar(canvas, state, rect, extra.progress_bar, color_scheme);
284       break;
285     case kPushButton:
286       PaintButton(canvas, state, rect, extra.button, color_scheme);
287       break;
288     case kRadio:
289       PaintRadio(canvas, state, rect, extra.button, color_scheme);
290       break;
291     case kScrollbarDownArrow:
292     case kScrollbarUpArrow:
293     case kScrollbarLeftArrow:
294     case kScrollbarRightArrow:
295       if (scrollbar_button_length_ > 0)
296         PaintArrowButton(canvas, rect, part, state, color_scheme,
297                          extra.scrollbar_arrow);
298       break;
299     case kScrollbarHorizontalThumb:
300     case kScrollbarVerticalThumb:
301       PaintScrollbarThumb(canvas, part, state, rect,
302                           extra.scrollbar_thumb.scrollbar_theme, color_scheme);
303       break;
304     case kScrollbarHorizontalTrack:
305     case kScrollbarVerticalTrack:
306       PaintScrollbarTrack(canvas, part, state, extra.scrollbar_track, rect,
307                           color_scheme);
308       break;
309     case kScrollbarHorizontalGripper:
310     case kScrollbarVerticalGripper:
311       // Invoked by views scrollbar code, don't care about for non-win
312       // implementations, so no NOTIMPLEMENTED.
313       break;
314     case kScrollbarCorner:
315       PaintScrollbarCorner(canvas, state, rect, color_scheme);
316       break;
317     case kSliderTrack:
318       PaintSliderTrack(canvas, state, rect, extra.slider, color_scheme);
319       break;
320     case kSliderThumb:
321       PaintSliderThumb(canvas, state, rect, extra.slider, color_scheme);
322       break;
323     case kTabPanelBackground:
324       NOTIMPLEMENTED();
325       break;
326     case kTextField:
327       PaintTextField(canvas, state, rect, extra.text_field, color_scheme);
328       break;
329     case kTrackbarThumb:
330     case kTrackbarTrack:
331     case kWindowResizeGripper:
332       NOTIMPLEMENTED();
333       break;
334     default:
335       NOTREACHED() << "Unknown theme part: " << part;
336       break;
337   }
338 
339   canvas->restore();
340 }
341 
SupportsNinePatch(Part part) const342 bool NativeThemeBase::SupportsNinePatch(Part part) const {
343   return false;
344 }
345 
GetNinePatchCanvasSize(Part part) const346 gfx::Size NativeThemeBase::GetNinePatchCanvasSize(Part part) const {
347   NOTREACHED() << "NativeThemeBase doesn't support nine-patch resources.";
348   return gfx::Size();
349 }
350 
GetNinePatchAperture(Part part) const351 gfx::Rect NativeThemeBase::GetNinePatchAperture(Part part) const {
352   NOTREACHED() << "NativeThemeBase doesn't support nine-patch resources.";
353   return gfx::Rect();
354 }
355 
NativeThemeBase()356 NativeThemeBase::NativeThemeBase() : NativeThemeBase(false) {}
357 
NativeThemeBase(bool should_only_use_dark_colors)358 NativeThemeBase::NativeThemeBase(bool should_only_use_dark_colors)
359     : NativeTheme(should_only_use_dark_colors) {}
360 
361 NativeThemeBase::~NativeThemeBase() = default;
362 
PaintArrowButton(cc::PaintCanvas * canvas,const gfx::Rect & rect,Part direction,State state,ColorScheme color_scheme,const ScrollbarArrowExtraParams & arrow) const363 void NativeThemeBase::PaintArrowButton(
364     cc::PaintCanvas* canvas,
365     const gfx::Rect& rect,
366     Part direction,
367     State state,
368     ColorScheme color_scheme,
369     const ScrollbarArrowExtraParams& arrow) const {
370   cc::PaintFlags flags;
371 
372   // Calculate button color.
373   SkScalar track_hsv[3];
374   SkColorToHSV(GetColor(kTrackColor, color_scheme), track_hsv);
375   SkColor button_color = SaturateAndBrighten(track_hsv, 0, 0.2f);
376   SkColor background_color = button_color;
377   if (state == kPressed) {
378     SkScalar button_hsv[3];
379     SkColorToHSV(button_color, button_hsv);
380     button_color = SaturateAndBrighten(button_hsv, 0, -0.1f);
381   } else if (state == kHovered) {
382     SkScalar button_hsv[3];
383     SkColorToHSV(button_color, button_hsv);
384     button_color = SaturateAndBrighten(button_hsv, 0, 0.05f);
385   }
386 
387   SkIRect skrect;
388   skrect.setXYWH(rect.x(), rect.y(), rect.width(), rect.height());
389   // Paint the background (the area visible behind the rounded corners).
390   flags.setColor(background_color);
391   canvas->drawIRect(skrect, flags);
392 
393   // Paint the button's outline and fill the middle
394   SkPath outline;
395   switch (direction) {
396     case kScrollbarUpArrow:
397       outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5);
398       outline.rLineTo(0, -(rect.height() - 2));
399       outline.rLineTo(2, -2);
400       outline.rLineTo(rect.width() - 5, 0);
401       outline.rLineTo(2, 2);
402       outline.rLineTo(0, rect.height() - 2);
403       break;
404     case kScrollbarDownArrow:
405       outline.moveTo(rect.x() + 0.5, rect.y() - 0.5);
406       outline.rLineTo(0, rect.height() - 2);
407       outline.rLineTo(2, 2);
408       outline.rLineTo(rect.width() - 5, 0);
409       outline.rLineTo(2, -2);
410       outline.rLineTo(0, -(rect.height() - 2));
411       break;
412     case kScrollbarRightArrow:
413       outline.moveTo(rect.x() - 0.5, rect.y() + 0.5);
414       outline.rLineTo(rect.width() - 2, 0);
415       outline.rLineTo(2, 2);
416       outline.rLineTo(0, rect.height() - 5);
417       outline.rLineTo(-2, 2);
418       outline.rLineTo(-(rect.width() - 2), 0);
419       break;
420     case kScrollbarLeftArrow:
421       outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5);
422       outline.rLineTo(-(rect.width() - 2), 0);
423       outline.rLineTo(-2, 2);
424       outline.rLineTo(0, rect.height() - 5);
425       outline.rLineTo(2, 2);
426       outline.rLineTo(rect.width() - 2, 0);
427       break;
428     default:
429       break;
430   }
431   outline.close();
432 
433   flags.setStyle(cc::PaintFlags::kFill_Style);
434   flags.setColor(button_color);
435   canvas->drawPath(outline, flags);
436 
437   flags.setAntiAlias(true);
438   flags.setStyle(cc::PaintFlags::kStroke_Style);
439   SkScalar thumb_hsv[3];
440   SkColorToHSV(GetColor(kThumbInactiveColor, color_scheme), thumb_hsv);
441   flags.setColor(OutlineColor(track_hsv, thumb_hsv));
442   canvas->drawPath(outline, flags);
443 
444   PaintArrow(canvas, rect, direction, GetArrowColor(state, color_scheme));
445 }
446 
PaintArrow(cc::PaintCanvas * gc,const gfx::Rect & rect,Part direction,SkColor color) const447 void NativeThemeBase::PaintArrow(cc::PaintCanvas* gc,
448                                  const gfx::Rect& rect,
449                                  Part direction,
450                                  SkColor color) const {
451   cc::PaintFlags flags;
452   flags.setColor(color);
453 
454   SkPath path = PathForArrow(rect, direction);
455 
456   gc->drawPath(path, flags);
457 }
458 
PathForArrow(const gfx::Rect & rect,Part direction) const459 SkPath NativeThemeBase::PathForArrow(const gfx::Rect& rect,
460                                      Part direction) const {
461   gfx::Rect bounding_rect = BoundingRectForArrow(rect);
462   const gfx::PointF center = gfx::RectF(bounding_rect).CenterPoint();
463   SkPath path;
464   SkMatrix transform;
465   transform.setIdentity();
466   if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) {
467     int arrow_altitude = bounding_rect.height() / 2 + 1;
468     path.moveTo(bounding_rect.x(), bounding_rect.bottom());
469     path.rLineTo(bounding_rect.width(), 0);
470     path.rLineTo(-bounding_rect.width() / 2.0f, -arrow_altitude);
471     path.close();
472     path.offset(0, -arrow_altitude / 2 + 1);
473     if (direction == kScrollbarDownArrow) {
474       transform.setScale(1, -1, center.x(), center.y());
475     }
476   } else {
477     int arrow_altitude = bounding_rect.width() / 2 + 1;
478     path.moveTo(bounding_rect.x(), bounding_rect.y());
479     path.rLineTo(0, bounding_rect.height());
480     path.rLineTo(arrow_altitude, -bounding_rect.height() / 2.0f);
481     path.close();
482     path.offset(arrow_altitude / 2, 0);
483     if (direction == kScrollbarLeftArrow) {
484       transform.setScale(-1, 1, center.x(), center.y());
485     }
486   }
487   path.transform(transform);
488 
489   return path;
490 }
491 
BoundingRectForArrow(const gfx::Rect & rect) const492 gfx::Rect NativeThemeBase::BoundingRectForArrow(const gfx::Rect& rect) const {
493   const std::pair<int, int> rect_sides =
494       std::minmax(rect.width(), rect.height());
495   const int side_length_inset = 2 * std::ceil(rect_sides.second / 4.f);
496   const int side_length =
497       std::min(rect_sides.first, rect_sides.second - side_length_inset);
498   // When there are an odd number of pixels, put the extra on the top/left.
499   return gfx::Rect(rect.x() + (rect.width() - side_length + 1) / 2,
500                    rect.y() + (rect.height() - side_length + 1) / 2,
501                    side_length, side_length);
502 }
503 
PaintScrollbarTrack(cc::PaintCanvas * canvas,Part part,State state,const ScrollbarTrackExtraParams & extra_params,const gfx::Rect & rect,ColorScheme color_scheme) const504 void NativeThemeBase::PaintScrollbarTrack(
505     cc::PaintCanvas* canvas,
506     Part part,
507     State state,
508     const ScrollbarTrackExtraParams& extra_params,
509     const gfx::Rect& rect,
510     ColorScheme color_scheme) const {
511   cc::PaintFlags flags;
512   SkIRect skrect;
513 
514   skrect.setLTRB(rect.x(), rect.y(), rect.right(), rect.bottom());
515   SkScalar track_hsv[3];
516   SkColorToHSV(GetColor(kTrackColor, color_scheme), track_hsv);
517   flags.setColor(SaturateAndBrighten(track_hsv, 0, 0));
518   canvas->drawIRect(skrect, flags);
519 
520   SkScalar thumb_hsv[3];
521   SkColorToHSV(GetColor(kThumbInactiveColor, color_scheme), thumb_hsv);
522 
523   flags.setColor(OutlineColor(track_hsv, thumb_hsv));
524   DrawBox(canvas, rect, flags);
525 }
526 
PaintScrollbarThumb(cc::PaintCanvas * canvas,Part part,State state,const gfx::Rect & rect,ScrollbarOverlayColorTheme,ColorScheme color_scheme) const527 void NativeThemeBase::PaintScrollbarThumb(cc::PaintCanvas* canvas,
528                                           Part part,
529                                           State state,
530                                           const gfx::Rect& rect,
531                                           ScrollbarOverlayColorTheme,
532                                           ColorScheme color_scheme) const {
533   const bool hovered = state == kHovered;
534   const int midx = rect.x() + rect.width() / 2;
535   const int midy = rect.y() + rect.height() / 2;
536   const bool vertical = part == kScrollbarVerticalThumb;
537 
538   SkScalar thumb[3];
539   SkColorToHSV(
540       GetColor(hovered ? kThumbActiveColor : kThumbInactiveColor, color_scheme),
541       thumb);
542 
543   cc::PaintFlags flags;
544   flags.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
545 
546   SkIRect skrect;
547   if (vertical)
548     skrect.setLTRB(rect.x(), rect.y(), midx + 1, rect.y() + rect.height());
549   else
550     skrect.setLTRB(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1);
551 
552   canvas->drawIRect(skrect, flags);
553 
554   flags.setColor(SaturateAndBrighten(thumb, 0, -0.02f));
555 
556   if (vertical) {
557     skrect.setLTRB(midx + 1, rect.y(), rect.x() + rect.width(),
558                    rect.y() + rect.height());
559   } else {
560     skrect.setLTRB(rect.x(), midy + 1, rect.x() + rect.width(),
561                    rect.y() + rect.height());
562   }
563 
564   canvas->drawIRect(skrect, flags);
565 
566   SkScalar track[3];
567   SkColorToHSV(GetColor(kTrackColor, color_scheme), track);
568   flags.setColor(OutlineColor(track, thumb));
569   DrawBox(canvas, rect, flags);
570 
571   if (rect.height() > 10 && rect.width() > 10) {
572     const int grippy_half_width = 2;
573     const int inter_grippy_offset = 3;
574     if (vertical) {
575       DrawHorizLine(canvas, midx - grippy_half_width, midx + grippy_half_width,
576                     midy - inter_grippy_offset, flags);
577       DrawHorizLine(canvas, midx - grippy_half_width, midx + grippy_half_width,
578                     midy, flags);
579       DrawHorizLine(canvas, midx - grippy_half_width, midx + grippy_half_width,
580                     midy + inter_grippy_offset, flags);
581     } else {
582       DrawVertLine(canvas, midx - inter_grippy_offset, midy - grippy_half_width,
583                    midy + grippy_half_width, flags);
584       DrawVertLine(canvas, midx, midy - grippy_half_width,
585                    midy + grippy_half_width, flags);
586       DrawVertLine(canvas, midx + inter_grippy_offset, midy - grippy_half_width,
587                    midy + grippy_half_width, flags);
588     }
589   }
590 }
591 
PaintScrollbarCorner(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,ColorScheme color_scheme) const592 void NativeThemeBase::PaintScrollbarCorner(cc::PaintCanvas* canvas,
593                                            State state,
594                                            const gfx::Rect& rect,
595                                            ColorScheme color_scheme) const {}
596 
PaintCheckbox(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,ColorScheme color_scheme) const597 void NativeThemeBase::PaintCheckbox(cc::PaintCanvas* canvas,
598                                     State state,
599                                     const gfx::Rect& rect,
600                                     const ButtonExtraParams& button,
601                                     ColorScheme color_scheme) const {
602   if (features::IsFormControlsRefreshEnabled()) {
603     const float border_radius = GetBorderRadiusForPart(
604         kCheckbox, rect.width(), rect.height(), button.zoom);
605     SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, true,
606                                              border_radius, color_scheme);
607 
608     if (!skrect.isEmpty()) {
609       cc::PaintFlags flags;
610       flags.setAntiAlias(true);
611 
612       if (button.indeterminate) {
613         // Draw the dash.
614         flags.setColor(ControlsBorderColorForState(state, color_scheme));
615         const auto indeterminate =
616             skrect.makeInset(skrect.width() * kIndeterminateInsetWidthRatio,
617                              skrect.height() * kIndeterminateInsetHeightRatio);
618         flags.setStyle(cc::PaintFlags::kFill_Style);
619         canvas->drawRoundRect(indeterminate, border_radius, border_radius,
620                               flags);
621       } else if (button.checked) {
622         // Draw the accent background.
623         flags.setStyle(cc::PaintFlags::kFill_Style);
624         flags.setColor(ControlsAccentColorForState(state, color_scheme));
625         canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
626 
627         // Draw the checkmark.
628         SkPath check;
629         check.moveTo(skrect.x() + skrect.width() * 0.2, skrect.centerY());
630         check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
631         check.lineTo(skrect.right() - skrect.width() * 0.2,
632                      skrect.y() + skrect.height() * 0.2);
633         flags.setStyle(cc::PaintFlags::kStroke_Style);
634         flags.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.16));
635         SkColor checkmark_color =
636             ControlsBackgroundColorForState(state, color_scheme);
637         flags.setColor(checkmark_color);
638         canvas->drawPath(check, flags);
639       }
640     }
641     return;
642   }
643 
644   SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, true,
645                                            SkIntToScalar(2), color_scheme);
646   if (!skrect.isEmpty()) {
647     // Draw the checkmark / dash.
648     cc::PaintFlags flags;
649     flags.setAntiAlias(true);
650     flags.setStyle(cc::PaintFlags::kStroke_Style);
651     flags.setColor(GetColor(state == kDisabled ? kCheckboxStrokeDisabledColor
652                                                : kCheckboxStrokeColor,
653                             color_scheme));
654     if (button.indeterminate) {
655       SkPath dash;
656       dash.moveTo(skrect.x() + skrect.width() * 0.16,
657                   (skrect.y() + skrect.bottom()) / 2);
658       dash.rLineTo(skrect.width() * 0.68, 0);
659       flags.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.2));
660       canvas->drawPath(dash, flags);
661     } else if (button.checked) {
662       SkPath check;
663       check.moveTo(skrect.x() + skrect.width() * 0.2,
664                    skrect.y() + skrect.height() * 0.5);
665       check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
666       flags.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.23));
667       check.lineTo(skrect.right() - skrect.width() * 0.2,
668                    skrect.y() + skrect.height() * 0.2);
669       canvas->drawPath(check, flags);
670     }
671   }
672 }
673 
674 // Draws the common elements of checkboxes and radio buttons.
675 // Returns the rectangle within which any additional decorations should be
676 // drawn, or empty if none.
PaintCheckboxRadioCommon(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,bool is_checkbox,const SkScalar border_radius,ColorScheme color_scheme) const677 SkRect NativeThemeBase::PaintCheckboxRadioCommon(
678     cc::PaintCanvas* canvas,
679     State state,
680     const gfx::Rect& rect,
681     const ButtonExtraParams& button,
682     bool is_checkbox,
683     const SkScalar border_radius,
684     ColorScheme color_scheme) const {
685   if (features::IsFormControlsRefreshEnabled()) {
686     SkRect skrect = gfx::RectToSkRect(rect);
687 
688     // Use the largest square that fits inside the provided rectangle.
689     // No other browser seems to support non-square widget, so accidentally
690     // having non-square sizes is common (eg. amazon and webkit dev tools).
691     if (skrect.width() != skrect.height()) {
692       SkScalar size = std::min(skrect.width(), skrect.height());
693       skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
694     }
695 
696     // If the rectangle is too small then paint only a rectangle. We don't want
697     // to have to worry about '- 1' and '+ 1' calculations below having overflow
698     // or underflow.
699     if (skrect.width() <= 2) {
700       cc::PaintFlags flags;
701       flags.setColor(GetControlColor(kBorder, color_scheme));
702       flags.setStyle(cc::PaintFlags::kFill_Style);
703       canvas->drawRect(skrect, flags);
704       // Too small to draw anything more.
705       return SkRect::MakeEmpty();
706     }
707 
708     cc::PaintFlags flags;
709     flags.setAntiAlias(true);
710 
711     // Paint the background (is not visible behind the rounded corners).
712     // Note we need to shrink the rect for background a little bit so we don't
713     // see artifacts introduced by antialiasing between the border and the
714     // background near the rounded corners of checkbox.
715     const auto background_rect =
716         skrect.makeInset(kBorderWidth * 0.2f, kBorderWidth * 0.2f);
717     PaintLightenLayer(canvas, background_rect, state, border_radius,
718                       color_scheme);
719     flags.setColor(ControlsBackgroundColorForState(state, color_scheme));
720     flags.setStyle(cc::PaintFlags::kFill_Style);
721     canvas->drawRoundRect(background_rect, border_radius, border_radius, flags);
722 
723     // For checkbox the border is drawn only when it is unchecked or
724     // indeterminate. For radio the border is always drawn.
725     if (!(is_checkbox && button.checked && !button.indeterminate)) {
726       // Shrink half border width so the final pixels of the border will be
727       // within the rectangle.
728       const auto border_rect =
729           skrect.makeInset(kBorderWidth / 2, kBorderWidth / 2);
730       SkColor border_color =
731           (button.checked && !button.indeterminate)
732               ? ControlsAccentColorForState(state, color_scheme)
733               : ControlsBorderColorForState(state, color_scheme);
734       flags.setColor(border_color);
735       flags.setStyle(cc::PaintFlags::kStroke_Style);
736       flags.setStrokeWidth(kBorderWidth);
737       canvas->drawRoundRect(border_rect, border_radius, border_radius, flags);
738     }
739 
740     // Return the rectangle for drawing any additional decorations.
741     return skrect;
742   }
743 
744   SkRect skrect = gfx::RectToSkRect(rect);
745 
746   // Use the largest square that fits inside the provided rectangle.
747   // No other browser seems to support non-square widget, so accidentally
748   // having non-square sizes is common (eg. amazon and webkit dev tools).
749   if (skrect.width() != skrect.height()) {
750     SkScalar size = std::min(skrect.width(), skrect.height());
751     skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
752   }
753 
754   // If the rectangle is too small then paint only a rectangle.  We don't want
755   // to have to worry about '- 1' and '+ 1' calculations below having overflow
756   // or underflow.
757   if (skrect.width() <= 2) {
758     cc::PaintFlags flags;
759     flags.setColor(GetColor(kCheckboxTinyColor, color_scheme));
760     flags.setStyle(cc::PaintFlags::kFill_Style);
761     canvas->drawRect(skrect, flags);
762     // Too small to draw anything more.
763     return SkRect::MakeEmpty();
764   }
765 
766   // Make room for padding/drop shadow.
767   AdjustCheckboxRadioRectForPadding(&skrect);
768 
769   // Draw the drop shadow below the widget.
770   if (state != kPressed) {
771     cc::PaintFlags flags;
772     flags.setAntiAlias(true);
773     SkRect shadow_rect = skrect;
774     shadow_rect.offset(0, 1);
775     if (state == kDisabled)
776       flags.setColor(GetColor(kCheckboxShadowDisabledColor, color_scheme));
777     else if (state == kHovered)
778       flags.setColor(GetColor(kCheckboxShadowHoveredColor, color_scheme));
779     else
780       flags.setColor(GetColor(kCheckboxShadowColor, color_scheme));
781     flags.setStyle(cc::PaintFlags::kFill_Style);
782     canvas->drawRoundRect(shadow_rect, border_radius, border_radius, flags);
783   }
784 
785   // Draw the gradient-filled rectangle
786   SkPoint gradient_bounds[3];
787   gradient_bounds[0].set(skrect.x(), skrect.y());
788   gradient_bounds[1].set(skrect.x(), skrect.y() + skrect.height() * 0.38);
789   gradient_bounds[2].set(skrect.x(), skrect.bottom());
790   SkColor start_color;
791   SkColor end_color;
792   if (state == kPressed) {
793     start_color = GetColor(kCheckboxPressedGradientStartColor, color_scheme);
794     end_color = GetColor(kCheckboxPressedGradientEndColor, color_scheme);
795   } else if (state == kHovered) {
796     start_color = GetColor(kCheckboxHoveredGradientStartColor, color_scheme);
797     end_color = GetColor(kCheckboxHoveredGradientEndColor, color_scheme);
798   } else /* kNormal or kDisabled */ {
799     start_color = GetColor(kCheckboxGradientStartColor, color_scheme);
800     end_color = GetColor(kCheckboxGradientEndColor, color_scheme);
801     if (state == kDisabled) {
802       start_color = SkColorSetA(start_color, kCheckboxDisabledGradientAlpha);
803       end_color = SkColorSetA(end_color, kCheckboxDisabledGradientAlpha);
804     }
805   }
806   SkColor colors[3] = {start_color, start_color, end_color};
807   cc::PaintFlags flags;
808   flags.setAntiAlias(true);
809   flags.setShader(cc::PaintShader::MakeLinearGradient(
810       gradient_bounds, colors, nullptr, 3, SkTileMode::kClamp));
811   flags.setStyle(cc::PaintFlags::kFill_Style);
812   canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
813   flags.setShader(nullptr);
814 
815   // Draw the border.
816   if (state == kHovered)
817     flags.setColor(GetColor(kCheckboxBorderHoveredColor, color_scheme));
818   else if (state == kDisabled)
819     flags.setColor(GetColor(kCheckboxBorderDisabledColor, color_scheme));
820   else
821     flags.setColor(GetColor(kCheckboxBorderColor, color_scheme));
822   flags.setStyle(cc::PaintFlags::kStroke_Style);
823   flags.setStrokeWidth(SkIntToScalar(1));
824   skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
825   canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
826 
827   // Return the rectangle excluding the drop shadow for drawing any additional
828   // decorations.
829   return skrect;
830 }
831 
PaintRadio(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,ColorScheme color_scheme) const832 void NativeThemeBase::PaintRadio(cc::PaintCanvas* canvas,
833                                  State state,
834                                  const gfx::Rect& rect,
835                                  const ButtonExtraParams& button,
836                                  ColorScheme color_scheme) const {
837   if (features::IsFormControlsRefreshEnabled()) {
838     // Most of a radio button is the same as a checkbox, except the the rounded
839     // square is a circle (i.e. border radius >= 100%).
840     const float border_radius = GetBorderRadiusForPart(
841         kRadio, rect.width(), rect.height(), button.zoom);
842     SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, false,
843                                              border_radius, color_scheme);
844     if (!skrect.isEmpty() && button.checked) {
845       // Draw the dot.
846       cc::PaintFlags flags;
847       flags.setAntiAlias(true);
848       flags.setStyle(cc::PaintFlags::kFill_Style);
849       flags.setColor(ControlsAccentColorForState(state, color_scheme));
850 
851       skrect.inset(skrect.width() * 0.2, skrect.height() * 0.2);
852       // Use drawRoundedRect instead of drawOval to be completely consistent
853       // with the border in PaintCheckboxRadioNewCommon.
854       canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
855     }
856     return;
857   }
858 
859   // Most of a radio button is the same as a checkbox, except the the rounded
860   // square is a circle (i.e. border radius >= 100%).
861   const SkScalar radius = SkFloatToScalar(
862       static_cast<float>(std::max(rect.width(), rect.height())) / 2);
863   SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, button, false,
864                                            radius, color_scheme);
865   if (!skrect.isEmpty() && button.checked) {
866     // Draw the dot.
867     cc::PaintFlags flags;
868     flags.setAntiAlias(true);
869     flags.setStyle(cc::PaintFlags::kFill_Style);
870     flags.setColor(
871         GetColor(state == kDisabled ? kRadioDotDisabledColor : kRadioDotColor,
872                  color_scheme));
873     skrect.inset(skrect.width() * 0.25, skrect.height() * 0.25);
874     // Use drawRoundedRect instead of drawOval to be completely consistent
875     // with the border in PaintCheckboxRadioNewCommon.
876     canvas->drawRoundRect(skrect, radius, radius, flags);
877   }
878 }
879 
PaintButton(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ButtonExtraParams & button,ColorScheme color_scheme) const880 void NativeThemeBase::PaintButton(cc::PaintCanvas* canvas,
881                                   State state,
882                                   const gfx::Rect& rect,
883                                   const ButtonExtraParams& button,
884                                   ColorScheme color_scheme) const {
885   if (features::IsFormControlsRefreshEnabled()) {
886     cc::PaintFlags flags;
887     SkRect skrect = gfx::RectToSkRect(rect);
888 
889     flags.setAntiAlias(true);
890     flags.setStyle(cc::PaintFlags::kFill_Style);
891 
892     // If the button is too small, fallback to drawing a single, solid color.
893     if (rect.width() < 5 || rect.height() < 5) {
894       flags.setColor(ButtonFillColorForState(state, color_scheme));
895       canvas->drawRect(skrect, flags);
896       return;
897     }
898 
899     float border_radius = GetBorderRadiusForPart(kPushButton, rect.width(),
900                                                  rect.height(), button.zoom);
901     // Paint the background (is not visible behind the rounded corners).
902     skrect.inset(kBorderWidth / 2, kBorderWidth / 2);
903     PaintLightenLayer(canvas, skrect, state, border_radius, color_scheme);
904     flags.setColor(ButtonFillColorForState(state, color_scheme));
905     canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
906 
907     // Paint the border: 1px solid.
908     if (button.has_border) {
909       flags.setStyle(cc::PaintFlags::kStroke_Style);
910       flags.setStrokeWidth(kBorderWidth);
911       flags.setColor(ButtonBorderColorForState(state, color_scheme));
912       canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
913     }
914     return;
915   }
916 
917   cc::PaintFlags flags;
918   SkRect skrect = gfx::RectToSkRect(rect);
919   SkColor base_color = button.background_color;
920 
921   color_utils::HSL base_hsl;
922   color_utils::SkColorToHSL(base_color, &base_hsl);
923 
924   // Our standard gradient is from 0xDD to 0xF8. This is the amount of
925   // increased luminance between those values.
926   SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105));
927 
928   // If the button is too small, fallback to drawing a single, solid color
929   if (rect.width() < 5 || rect.height() < 5) {
930     flags.setColor(base_color);
931     canvas->drawRect(skrect, flags);
932     return;
933   }
934 
935   flags.setColor(GetColor(kButtonBorderColor, color_scheme));
936   SkPoint gradient_bounds[2] = {
937     gfx::PointToSkPoint(rect.origin()),
938     gfx::PointToSkPoint(rect.bottom_left() - gfx::Vector2d(0, 1))
939   };
940   if (state == kPressed)
941     std::swap(gradient_bounds[0], gradient_bounds[1]);
942   SkColor colors[2] = { light_color, base_color };
943 
944   flags.setStyle(cc::PaintFlags::kFill_Style);
945   flags.setAntiAlias(true);
946   flags.setShader(cc::PaintShader::MakeLinearGradient(
947       gradient_bounds, colors, nullptr, 2, SkTileMode::kClamp));
948 
949   canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), flags);
950   flags.setShader(nullptr);
951 
952   if (button.has_border) {
953     int border_alpha = state == kHovered ? 0x80 : 0x55;
954     if (button.is_focused) {
955       border_alpha = 0xFF;
956       flags.setColor(GetSystemColor(kColorId_FocusedBorderColor, color_scheme));
957     }
958     flags.setStyle(cc::PaintFlags::kStroke_Style);
959     flags.setStrokeWidth(SkIntToScalar(1));
960     flags.setAlpha(border_alpha);
961     skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
962     canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), flags);
963   }
964 }
965 
PaintTextField(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const TextFieldExtraParams & text,ColorScheme color_scheme) const966 void NativeThemeBase::PaintTextField(cc::PaintCanvas* canvas,
967                                      State state,
968                                      const gfx::Rect& rect,
969                                      const TextFieldExtraParams& text,
970                                      ColorScheme color_scheme) const {
971   if (features::IsFormControlsRefreshEnabled()) {
972     SkRect bounds = gfx::RectToSkRect(rect);
973     const SkScalar border_radius = GetBorderRadiusForPart(
974         kTextField, rect.width(), rect.height(), /*zoom level=*/1);
975 
976     // Paint the background (is not visible behind the rounded corners).
977     bounds.inset(kBorderWidth / 2, kBorderWidth / 2);
978     cc::PaintFlags fill_flags;
979     fill_flags.setStyle(cc::PaintFlags::kFill_Style);
980     if (text.background_color != 0) {
981       PaintLightenLayer(canvas, bounds, state, border_radius, color_scheme);
982       SkColor text_field_background_color =
983           ControlsBackgroundColorForState(state, color_scheme);
984       if (text.auto_complete_active && state != kDisabled) {
985         text_field_background_color =
986             GetControlColor(kAutoCompleteBackground, color_scheme);
987       }
988       fill_flags.setColor(text_field_background_color);
989       canvas->drawRoundRect(bounds, border_radius, border_radius, fill_flags);
990     }
991 
992     // Paint the border: 1px solid.
993     if (text.has_border) {
994       cc::PaintFlags stroke_flags;
995       stroke_flags.setColor(ControlsBorderColorForState(state, color_scheme));
996       stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
997       stroke_flags.setStrokeWidth(kBorderWidth);
998       canvas->drawRoundRect(bounds, border_radius, border_radius, stroke_flags);
999     }
1000 
1001     return;
1002   }
1003 
1004   SkRect bounds;
1005   bounds.setLTRB(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1);
1006 
1007   cc::PaintFlags fill_flags;
1008   fill_flags.setStyle(cc::PaintFlags::kFill_Style);
1009   fill_flags.setColor(text.background_color);
1010   canvas->drawRect(bounds, fill_flags);
1011 
1012   // Text INPUT, listbox SELECT, and TEXTAREA have consistent borders.
1013   // border: 1px solid #a9a9a9
1014   cc::PaintFlags stroke_flags;
1015   stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
1016   stroke_flags.setColor(GetColor(kTextBorderColor, color_scheme));
1017   canvas->drawRect(bounds, stroke_flags);
1018 }
1019 
PaintMenuList(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const MenuListExtraParams & menu_list,ColorScheme color_scheme) const1020 void NativeThemeBase::PaintMenuList(cc::PaintCanvas* canvas,
1021                                     State state,
1022                                     const gfx::Rect& rect,
1023                                     const MenuListExtraParams& menu_list,
1024                                     ColorScheme color_scheme) const {
1025   if (features::IsFormControlsRefreshEnabled()) {
1026     // If a border radius is specified paint the background and the border of
1027     // the menulist, otherwise let the non-theming code paint the background
1028     // and the border of the control. The arrow (menulist button) is always
1029     // painted by the theming code.
1030     if (!menu_list.has_border_radius) {
1031       TextFieldExtraParams text_field = {0};
1032       text_field.background_color = menu_list.background_color;
1033       text_field.has_border = menu_list.has_border;
1034       PaintTextField(canvas, state, rect, text_field, color_scheme);
1035     }
1036 
1037     // Paint the arrow.
1038     cc::PaintFlags flags;
1039     flags.setColor(menu_list.arrow_color);
1040     flags.setAntiAlias(true);
1041     flags.setStyle(cc::PaintFlags::kStroke_Style);
1042     flags.setStrokeWidth(kMenuListArrowStrokeWidth);
1043 
1044     float arrow_width = menu_list.arrow_size;
1045     int arrow_height = arrow_width * 0.5;
1046     gfx::Rect arrow(menu_list.arrow_x, menu_list.arrow_y - (arrow_height / 2),
1047                     arrow_width, arrow_height);
1048     arrow.Intersect(rect);
1049 
1050     if (arrow_width != arrow.width() || arrow_height != arrow.height()) {
1051       // The arrow is clipped after being constrained to the paint rect so we
1052       // need to recalculate its size.
1053       int height_clip = arrow_height - arrow.height();
1054       int width_clip = arrow_width - arrow.width();
1055       if (height_clip > width_clip) {
1056         arrow.set_width(arrow.height() * 1.6);
1057       } else {
1058         arrow.set_height(arrow.width() * 0.6);
1059       }
1060       arrow.set_y(menu_list.arrow_y - (arrow.height() / 2));
1061     }
1062 
1063     SkPath path;
1064     path.moveTo(arrow.x(), arrow.y());
1065     path.lineTo(arrow.x() + arrow.width() / 2, arrow.y() + arrow.height());
1066     path.lineTo(arrow.x() + arrow.width(), arrow.y());
1067     canvas->drawPath(path, flags);
1068     return;
1069   }
1070 
1071   // If a border radius is specified, we let the WebCore paint the background
1072   // and the border of the control.
1073   if (!menu_list.has_border_radius) {
1074     ButtonExtraParams button = { 0 };
1075     button.background_color = menu_list.background_color;
1076     button.has_border = menu_list.has_border;
1077     PaintButton(canvas, state, rect, button, color_scheme);
1078   }
1079 
1080   cc::PaintFlags flags;
1081   flags.setColor(menu_list.arrow_color);
1082   flags.setAntiAlias(true);
1083   flags.setStyle(cc::PaintFlags::kFill_Style);
1084 
1085   int arrow_size = menu_list.arrow_size;
1086   gfx::Rect arrow(
1087     menu_list.arrow_x,
1088     menu_list.arrow_y - (arrow_size / 2),
1089     arrow_size,
1090     arrow_size);
1091 
1092   // Constrain to the paint rect.
1093   arrow.Intersect(rect);
1094 
1095   SkPath path;
1096   path.moveTo(arrow.x(), arrow.y());
1097   path.lineTo(arrow.right(), arrow.y());
1098   path.lineTo(arrow.x() + arrow.width() / 2, arrow.bottom());
1099   path.close();
1100   canvas->drawPath(path, flags);
1101 }
1102 
PaintMenuPopupBackground(cc::PaintCanvas * canvas,const gfx::Size & size,const MenuBackgroundExtraParams & menu_background,ColorScheme color_scheme) const1103 void NativeThemeBase::PaintMenuPopupBackground(
1104     cc::PaintCanvas* canvas,
1105     const gfx::Size& size,
1106     const MenuBackgroundExtraParams& menu_background,
1107     ColorScheme color_scheme) const {
1108   // We are currently only painting kMenuPopupBackground with the kDefault
1109   // scheme. If that changes, we need to add an appropriate dark scheme color to
1110   // kMenuPopupBackgroundColor.
1111   DCHECK(color_scheme == ColorScheme::kDefault);
1112   canvas->drawColor(GetColor(kMenuPopupBackgroundColor, color_scheme),
1113                     SkBlendMode::kSrc);
1114 }
1115 
PaintMenuItemBackground(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const MenuItemExtraParams & menu_item,ColorScheme color_scheme) const1116 void NativeThemeBase::PaintMenuItemBackground(
1117     cc::PaintCanvas* canvas,
1118     State state,
1119     const gfx::Rect& rect,
1120     const MenuItemExtraParams& menu_item,
1121     ColorScheme color_scheme) const {
1122   // By default don't draw anything over the normal background.
1123 }
1124 
PaintMenuSeparator(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const MenuSeparatorExtraParams & menu_separator,ColorScheme color_scheme) const1125 void NativeThemeBase::PaintMenuSeparator(
1126     cc::PaintCanvas* canvas,
1127     State state,
1128     const gfx::Rect& rect,
1129     const MenuSeparatorExtraParams& menu_separator,
1130     ColorScheme color_scheme) const {
1131   cc::PaintFlags flags;
1132   flags.setColor(GetSystemColor(ui::NativeTheme::kColorId_MenuSeparatorColor,
1133                                 color_scheme));
1134   canvas->drawRect(gfx::RectToSkRect(*menu_separator.paint_rect), flags);
1135 }
1136 
PaintSliderTrack(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const SliderExtraParams & slider,ColorScheme color_scheme) const1137 void NativeThemeBase::PaintSliderTrack(cc::PaintCanvas* canvas,
1138                                        State state,
1139                                        const gfx::Rect& rect,
1140                                        const SliderExtraParams& slider,
1141                                        ColorScheme color_scheme) const {
1142   if (features::IsFormControlsRefreshEnabled()) {
1143     // Paint the entire slider track.
1144     cc::PaintFlags flags;
1145     flags.setAntiAlias(true);
1146     flags.setColor(ControlsFillColorForState(state, color_scheme));
1147     const float track_height = kSliderTrackHeight * slider.zoom;
1148     SkRect track_rect = AlignSliderTrack(rect, slider, false, track_height);
1149     // Shrink the track by 1 pixel so the thumb can completely cover the track
1150     // on both ends.
1151     if (slider.vertical)
1152       track_rect.inset(0, 1);
1153     else
1154       track_rect.inset(1, 0);
1155     float border_radius = GetBorderRadiusForPart(kSliderTrack, rect.width(),
1156                                                  rect.height(), slider.zoom);
1157     canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1158 
1159     // Clip the track to create rounded corners for the value bar.
1160     SkRRect rounded_rect;
1161     rounded_rect.setRectXY(track_rect, border_radius, border_radius);
1162     canvas->clipRRect(rounded_rect, SkClipOp::kIntersect, true);
1163 
1164     // Paint the value slider track.
1165     flags.setColor(ControlsSliderColorForState(state, color_scheme));
1166     SkRect value_rect = AlignSliderTrack(rect, slider, true, track_height);
1167     canvas->drawRect(value_rect, flags);
1168 
1169     // Paint the border.
1170     flags.setStyle(cc::PaintFlags::kStroke_Style);
1171     flags.setStrokeWidth(kBorderWidth);
1172     SkColor border_color = ControlsBorderColorForState(state, color_scheme);
1173     if (!UsesHighContrastColors() && state != kDisabled &&
1174         color_scheme != ColorScheme::kDark)
1175       border_color = SkColorSetA(border_color, 0x80);
1176     flags.setColor(border_color);
1177     track_rect.inset(kBorderWidth / 2, kBorderWidth / 2);
1178     canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1179     return;
1180   }
1181 
1182   const int kMidX = rect.x() + rect.width() / 2;
1183   const int kMidY = rect.y() + rect.height() / 2;
1184 
1185   cc::PaintFlags flags;
1186   flags.setColor(GetColor(kSliderTrackBackgroundColor, color_scheme));
1187 
1188   SkRect skrect;
1189   if (slider.vertical) {
1190     skrect.setLTRB(std::max(rect.x(), kMidX - 2), rect.y(),
1191                    std::min(rect.right(), kMidX + 2), rect.bottom());
1192   } else {
1193     skrect.setLTRB(rect.x(), std::max(rect.y(), kMidY - 2), rect.right(),
1194                    std::min(rect.bottom(), kMidY + 2));
1195   }
1196   canvas->drawRect(skrect, flags);
1197 }
1198 
PaintSliderThumb(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const SliderExtraParams & slider,ColorScheme color_scheme) const1199 void NativeThemeBase::PaintSliderThumb(cc::PaintCanvas* canvas,
1200                                        State state,
1201                                        const gfx::Rect& rect,
1202                                        const SliderExtraParams& slider,
1203                                        ColorScheme color_scheme) const {
1204   if (features::IsFormControlsRefreshEnabled()) {
1205     const float radius = GetBorderRadiusForPart(kSliderThumb, rect.width(),
1206                                                 rect.height(), slider.zoom);
1207     SkRect thumb_rect = gfx::RectToSkRect(rect);
1208 
1209     cc::PaintFlags flags;
1210     flags.setAntiAlias(true);
1211     SkScalar border_width = kSliderThumbBorderWidth;
1212     if (state == kHovered || state == kPressed) {
1213       border_width = kSliderThumbBorderHoveredWidth;
1214     }
1215 
1216     // Paint the background (is not visible behind the rounded corners).
1217     thumb_rect.inset(border_width / 2, border_width / 2);
1218     flags.setColor(ControlsSliderColorForState(state, color_scheme));
1219     flags.setStyle(cc::PaintFlags::kFill_Style);
1220     canvas->drawRoundRect(thumb_rect, radius, radius, flags);
1221     return;
1222   }
1223 
1224   const bool hovered = (state == kHovered) || slider.in_drag;
1225   const int kMidX = rect.x() + rect.width() / 2;
1226   const int kMidY = rect.y() + rect.height() / 2;
1227 
1228   cc::PaintFlags flags;
1229   flags.setColor(GetColor(
1230       hovered ? kSliderThumbHoveredBrightColor : kSliderThumbBrightColor,
1231       color_scheme));
1232 
1233   SkIRect skrect;
1234   if (slider.vertical)
1235     skrect.setLTRB(rect.x(), rect.y(), kMidX + 1, rect.bottom());
1236   else
1237     skrect.setLTRB(rect.x(), rect.y(), rect.right(), kMidY + 1);
1238 
1239   canvas->drawIRect(skrect, flags);
1240 
1241   flags.setColor(GetColor(
1242       hovered ? kSliderThumbHoveredShadedColor : kSliderThumbShadedColor,
1243       color_scheme));
1244 
1245   if (slider.vertical)
1246     skrect.setLTRB(kMidX + 1, rect.y(), rect.right(), rect.bottom());
1247   else
1248     skrect.setLTRB(rect.x(), kMidY + 1, rect.right(), rect.bottom());
1249 
1250   canvas->drawIRect(skrect, flags);
1251 
1252   flags.setColor(GetColor(kSliderThumbBorder, color_scheme));
1253   DrawBox(canvas, rect, flags);
1254 
1255   if (rect.height() > 10 && rect.width() > 10) {
1256     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, flags);
1257     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, flags);
1258     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, flags);
1259   }
1260 }
1261 
PaintInnerSpinButton(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const InnerSpinButtonExtraParams & spin_button,ColorScheme color_scheme) const1262 void NativeThemeBase::PaintInnerSpinButton(
1263     cc::PaintCanvas* canvas,
1264     State state,
1265     const gfx::Rect& rect,
1266     const InnerSpinButtonExtraParams& spin_button,
1267     ColorScheme color_scheme) const {
1268   if (spin_button.read_only)
1269     state = kDisabled;
1270 
1271   State north_state = state;
1272   State south_state = state;
1273   if (spin_button.spin_up)
1274     south_state = south_state != kDisabled ? kNormal : kDisabled;
1275   else
1276     north_state = north_state != kDisabled ? kNormal : kDisabled;
1277 
1278   gfx::Rect half = rect;
1279   half.set_height(rect.height() / 2);
1280   ScrollbarArrowExtraParams arrow = ScrollbarArrowExtraParams();
1281   arrow.zoom = 1.0;
1282   PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state, color_scheme,
1283                    arrow);
1284 
1285   half.set_y(rect.y() + rect.height() / 2);
1286   PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state, color_scheme,
1287                    arrow);
1288 }
1289 
PaintProgressBar(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const ProgressBarExtraParams & progress_bar,ColorScheme color_scheme) const1290 void NativeThemeBase::PaintProgressBar(
1291     cc::PaintCanvas* canvas,
1292     State state,
1293     const gfx::Rect& rect,
1294     const ProgressBarExtraParams& progress_bar,
1295     ColorScheme color_scheme) const {
1296   if (features::IsFormControlsRefreshEnabled()) {
1297     DCHECK(!rect.IsEmpty());
1298 
1299     // Paint the track.
1300     cc::PaintFlags flags;
1301     flags.setAntiAlias(true);
1302     flags.setStyle(cc::PaintFlags::kFill_Style);
1303     flags.setColor(GetControlColor(kFill, color_scheme));
1304     SliderExtraParams slider;
1305     slider.vertical = false;
1306     float track_height = rect.height() * kTrackHeightRatio;
1307     SkRect track_rect = AlignSliderTrack(rect, slider, false, track_height);
1308     float border_radius = GetBorderRadiusForPart(
1309         kProgressBar, rect.width(), rect.height(), /*zoom level=*/1);
1310     canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1311 
1312     // Clip the track to create rounded corners for the value bar.
1313     SkRRect rounded_rect;
1314     rounded_rect.setRectXY(track_rect, border_radius, border_radius);
1315     canvas->clipRRect(rounded_rect, SkClipOp::kIntersect, true);
1316 
1317     // Paint the progress value bar.
1318     const SkScalar kMinimumProgressValueWidth = 2;
1319     SkScalar adjusted_width = progress_bar.value_rect_width;
1320     if (adjusted_width > 0 && adjusted_width < kMinimumProgressValueWidth)
1321       adjusted_width = kMinimumProgressValueWidth;
1322     gfx::Rect original_value_rect(progress_bar.value_rect_x,
1323                                   progress_bar.value_rect_y, adjusted_width,
1324                                   progress_bar.value_rect_height);
1325     SkRect value_rect =
1326         AlignSliderTrack(original_value_rect, slider, false, track_height);
1327     flags.setColor(GetControlColor(kAccent, color_scheme));
1328     if (progress_bar.determinate) {
1329       canvas->drawRect(value_rect, flags);
1330     } else {
1331       canvas->drawRoundRect(value_rect, border_radius, border_radius, flags);
1332     }
1333 
1334     // Paint the border.
1335     flags.setStyle(cc::PaintFlags::kStroke_Style);
1336     flags.setStrokeWidth(kBorderWidth);
1337     SkColor border_color = GetControlColor(kBorder, color_scheme);
1338     if (!UsesHighContrastColors() && color_scheme != ColorScheme::kDark)
1339       border_color = SkColorSetA(border_color, 0x80);
1340     flags.setColor(border_color);
1341     track_rect.inset(kBorderWidth / 2, kBorderWidth / 2);
1342     canvas->drawRoundRect(track_rect, border_radius, border_radius, flags);
1343     return;
1344   }
1345 
1346   DCHECK(!rect.IsEmpty());
1347 
1348   canvas->drawColor(GetColor(kProgressBackgroundColor, color_scheme));
1349 
1350   // Draw the tick marks. The spacing between the tick marks is adjusted to
1351   // evenly divide into the width.
1352   SkPath path;
1353   int stroke_width = std::max(1, rect.height() / 18);
1354   int tick_width = 16 * stroke_width;
1355   int ticks = rect.width() / tick_width + (rect.width() % tick_width ? 1 : 0);
1356   SkScalar tick_spacing = SkIntToScalar(rect.width()) / ticks;
1357   for (int i = 1; i < ticks; ++i) {
1358     path.moveTo(rect.x() + i * tick_spacing, rect.y());
1359     path.rLineTo(0, rect.height());
1360   }
1361   cc::PaintFlags stroke_flags;
1362   stroke_flags.setColor(GetColor(kProgressTickColor, color_scheme));
1363   stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
1364   stroke_flags.setStrokeWidth(stroke_width);
1365   canvas->drawPath(path, stroke_flags);
1366 
1367   // Draw progress.
1368   gfx::Rect progress_rect(progress_bar.value_rect_x, progress_bar.value_rect_y,
1369                           progress_bar.value_rect_width,
1370                           progress_bar.value_rect_height);
1371   cc::PaintFlags progress_flags;
1372   progress_flags.setColor(GetColor(kProgressValueColor, color_scheme));
1373   progress_flags.setStyle(cc::PaintFlags::kFill_Style);
1374   canvas->drawRect(gfx::RectToSkRect(progress_rect), progress_flags);
1375 
1376   // Draw the border.
1377   gfx::RectF border_rect(rect);
1378   border_rect.Inset(stroke_width / 2.0f, stroke_width / 2.0f);
1379   stroke_flags.setColor(GetColor(kProgressBorderColor, color_scheme));
1380   canvas->drawRect(gfx::RectFToSkRect(border_rect), stroke_flags);
1381 }
1382 
PaintFrameTopArea(cc::PaintCanvas * canvas,State state,const gfx::Rect & rect,const FrameTopAreaExtraParams & frame_top_area,ColorScheme color_scheme) const1383 void NativeThemeBase::PaintFrameTopArea(
1384     cc::PaintCanvas* canvas,
1385     State state,
1386     const gfx::Rect& rect,
1387     const FrameTopAreaExtraParams& frame_top_area,
1388     ColorScheme color_scheme) const {
1389   cc::PaintFlags flags;
1390   flags.setColor(frame_top_area.default_background_color);
1391   canvas->drawRect(gfx::RectToSkRect(rect), flags);
1392 }
1393 
AdjustCheckboxRadioRectForPadding(SkRect * rect) const1394 void NativeThemeBase::AdjustCheckboxRadioRectForPadding(SkRect* rect) const {
1395   // By default we only take 1px from right and bottom for the drop shadow.
1396   rect->setLTRB(static_cast<int>(rect->x()), static_cast<int>(rect->y()),
1397                 static_cast<int>(rect->right()) - 1,
1398                 static_cast<int>(rect->bottom()) - 1);
1399 }
1400 
SaturateAndBrighten(SkScalar * hsv,SkScalar saturate_amount,SkScalar brighten_amount) const1401 SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
1402                                              SkScalar saturate_amount,
1403                                              SkScalar brighten_amount) const {
1404   SkScalar color[3];
1405   color[0] = hsv[0];
1406   color[1] =
1407       base::ClampToRange(hsv[1] + saturate_amount, SkScalar{0}, SK_Scalar1);
1408   color[2] =
1409       base::ClampToRange(hsv[2] + brighten_amount, SkScalar{0}, SK_Scalar1);
1410   return SkHSVToColor(color);
1411 }
1412 
GetArrowColor(State state,ColorScheme color_scheme) const1413 SkColor NativeThemeBase::GetArrowColor(State state,
1414                                        ColorScheme color_scheme) const {
1415   if (state != kDisabled)
1416     return GetColor(kArrowDisabledColor, color_scheme);
1417 
1418   SkScalar track_hsv[3];
1419   SkColorToHSV(GetColor(kTrackColor, color_scheme), track_hsv);
1420 
1421   SkScalar thumb_hsv[3];
1422   SkColorToHSV(GetControlColor(kScrollbarThumbInactive, color_scheme),
1423                thumb_hsv);
1424   return OutlineColor(track_hsv, thumb_hsv);
1425 }
1426 
DrawVertLine(cc::PaintCanvas * canvas,int x,int y1,int y2,const cc::PaintFlags & flags) const1427 void NativeThemeBase::DrawVertLine(cc::PaintCanvas* canvas,
1428                                    int x,
1429                                    int y1,
1430                                    int y2,
1431                                    const cc::PaintFlags& flags) const {
1432   SkIRect skrect;
1433   skrect.setLTRB(x, y1, x + 1, y2 + 1);
1434   canvas->drawIRect(skrect, flags);
1435 }
1436 
DrawHorizLine(cc::PaintCanvas * canvas,int x1,int x2,int y,const cc::PaintFlags & flags) const1437 void NativeThemeBase::DrawHorizLine(cc::PaintCanvas* canvas,
1438                                     int x1,
1439                                     int x2,
1440                                     int y,
1441                                     const cc::PaintFlags& flags) const {
1442   SkIRect skrect;
1443   skrect.setLTRB(x1, y, x2 + 1, y + 1);
1444   canvas->drawIRect(skrect, flags);
1445 }
1446 
DrawBox(cc::PaintCanvas * canvas,const gfx::Rect & rect,const cc::PaintFlags & flags) const1447 void NativeThemeBase::DrawBox(cc::PaintCanvas* canvas,
1448                               const gfx::Rect& rect,
1449                               const cc::PaintFlags& flags) const {
1450   const int right = rect.x() + rect.width() - 1;
1451   const int bottom = rect.y() + rect.height() - 1;
1452   DrawHorizLine(canvas, rect.x(), right, rect.y(), flags);
1453   DrawVertLine(canvas, right, rect.y(), bottom, flags);
1454   DrawHorizLine(canvas, rect.x(), right, bottom, flags);
1455   DrawVertLine(canvas, rect.x(), rect.y(), bottom, flags);
1456 }
1457 
OutlineColor(SkScalar * hsv1,SkScalar * hsv2) const1458 SkColor NativeThemeBase::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
1459   // GTK Theme engines have way too much control over the layout of
1460   // the scrollbar. We might be able to more closely approximate its
1461   // look-and-feel, if we sent whole images instead of just colors
1462   // from the browser to the renderer. But even then, some themes
1463   // would just break.
1464   //
1465   // So, instead, we don't even try to 100% replicate the look of
1466   // the native scrollbar. We render our own version, but we make
1467   // sure to pick colors that blend in nicely with the system GTK
1468   // theme. In most cases, we can just sample a couple of pixels
1469   // from the system scrollbar and use those colors to draw our
1470   // scrollbar.
1471   //
1472   // This works fine for the track color and the overall thumb
1473   // color. But it fails spectacularly for the outline color used
1474   // around the thumb piece.  Not all themes have a clearly defined
1475   // outline. For some of them it is partially transparent, and for
1476   // others the thickness is very unpredictable.
1477   //
1478   // So, instead of trying to approximate the system theme, we
1479   // instead try to compute a reasonable looking choice based on the
1480   // known color of the track and the thumb piece. This is difficult
1481   // when trying to deal both with high- and low-contrast themes,
1482   // and both with positive and inverted themes.
1483   //
1484   // The following code has been tested to look OK with all of the
1485   // default GTK themes.
1486   SkScalar min_diff =
1487       base::ClampToRange((hsv1[1] + hsv2[1]) * 1.2f, 0.28f, 0.5f);
1488   SkScalar diff =
1489       base::ClampToRange(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5f);
1490 
1491   if (hsv1[2] + hsv2[2] > 1.0)
1492     diff = -diff;
1493 
1494   return SaturateAndBrighten(hsv2, -0.2f, diff);
1495 }
1496 
ControlsAccentColorForState(State state,ColorScheme color_scheme) const1497 SkColor NativeThemeBase::ControlsAccentColorForState(
1498     State state,
1499     ColorScheme color_scheme) const {
1500   ControlColorId color_id;
1501   if (state == kHovered) {
1502     color_id = kHoveredAccent;
1503   } else if (state == kPressed) {
1504     color_id = kPressedAccent;
1505   } else if (state == kDisabled) {
1506     color_id = kDisabledAccent;
1507   } else {
1508     color_id = kAccent;
1509   }
1510   return GetControlColor(color_id, color_scheme);
1511 }
1512 
ControlsSliderColorForState(State state,ColorScheme color_scheme) const1513 SkColor NativeThemeBase::ControlsSliderColorForState(
1514     State state,
1515     ColorScheme color_scheme) const {
1516   ControlColorId color_id;
1517   if (state == kHovered) {
1518     color_id = kHoveredSlider;
1519   } else if (state == kPressed) {
1520     color_id = kPressedSlider;
1521   } else if (state == kDisabled) {
1522     color_id = kDisabledSlider;
1523   } else {
1524     color_id = kSlider;
1525   }
1526   return GetControlColor(color_id, color_scheme);
1527 }
1528 
ControlsBorderColorForState(State state,ColorScheme color_scheme) const1529 SkColor NativeThemeBase::ControlsBorderColorForState(
1530     State state,
1531     ColorScheme color_scheme) const {
1532   ControlColorId color_id;
1533   if (state == kHovered) {
1534     color_id = kHoveredBorder;
1535   } else if (state == kPressed) {
1536     color_id = kPressedBorder;
1537   } else if (state == kDisabled) {
1538     color_id = kDisabledBorder;
1539   } else {
1540     color_id = kBorder;
1541   }
1542   return GetControlColor(color_id, color_scheme);
1543 }
1544 
ButtonBorderColorForState(State state,ColorScheme color_scheme) const1545 SkColor NativeThemeBase::ButtonBorderColorForState(
1546     State state,
1547     ColorScheme color_scheme) const {
1548   ControlColorId color_id;
1549   if (state == kHovered) {
1550     color_id = kButtonHoveredBorder;
1551   } else if (state == kPressed) {
1552     color_id = kButtonPressedBorder;
1553   } else if (state == kDisabled) {
1554     color_id = kButtonDisabledBorder;
1555   } else {
1556     color_id = kButtonBorder;
1557   }
1558   return GetControlColor(color_id, color_scheme);
1559 }
1560 
ControlsFillColorForState(State state,ColorScheme color_scheme) const1561 SkColor NativeThemeBase::ControlsFillColorForState(
1562     State state,
1563     ColorScheme color_scheme) const {
1564   ControlColorId color_id;
1565   if (state == kHovered) {
1566     color_id = kHoveredFill;
1567   } else if (state == kPressed) {
1568     color_id = kPressedFill;
1569   } else if (state == kDisabled) {
1570     color_id = kDisabledFill;
1571   } else {
1572     color_id = kFill;
1573   }
1574   return GetControlColor(color_id, color_scheme);
1575 }
1576 
ButtonFillColorForState(State state,ColorScheme color_scheme) const1577 SkColor NativeThemeBase::ButtonFillColorForState(
1578     State state,
1579     ColorScheme color_scheme) const {
1580   ControlColorId color_id;
1581   if (state == kHovered) {
1582     color_id = kButtonHoveredFill;
1583   } else if (state == kPressed) {
1584     color_id = kButtonPressedFill;
1585   } else if (state == kDisabled) {
1586     color_id = kButtonDisabledFill;
1587   } else {
1588     color_id = kButtonFill;
1589   }
1590   return GetControlColor(color_id, color_scheme);
1591 }
1592 
ControlsBackgroundColorForState(State state,ColorScheme color_scheme) const1593 SkColor NativeThemeBase::ControlsBackgroundColorForState(
1594     State state,
1595     ColorScheme color_scheme) const {
1596   ControlColorId color_id;
1597   if (state == kDisabled) {
1598     color_id = kDisabledBackground;
1599   } else {
1600     color_id = kBackground;
1601   }
1602   return GetControlColor(color_id, color_scheme);
1603 }
1604 
GetControlColor(ControlColorId color_id,ColorScheme color_scheme) const1605 SkColor NativeThemeBase::GetControlColor(ControlColorId color_id,
1606                                          ColorScheme color_scheme) const {
1607 #if defined(OS_WIN)
1608   if (UsesHighContrastColors() && features::IsForcedColorsEnabled())
1609     return GetHighContrastControlColor(color_id, color_scheme);
1610 #endif
1611 
1612   if(color_scheme == ColorScheme::kDark)
1613     return GetDarkModeControlColor(color_id);
1614 
1615   switch (color_id) {
1616     case kBorder:
1617     case kButtonBorder:
1618       return SkColorSetRGB(0x76, 0x76, 0x76);
1619     case kHoveredBorder:
1620     case kButtonHoveredBorder:
1621       return SkColorSetRGB(0x4F, 0x4F, 0x4F);
1622     case kPressedBorder:
1623     case kButtonPressedBorder:
1624       return SkColorSetRGB(0x8D, 0x8D, 0x8D);
1625     case kDisabledBorder:
1626     case kButtonDisabledBorder:
1627       return SkColorSetARGB(0x4D, 0x76, 0x76, 0x76);
1628     case kAccent:
1629       return SkColorSetRGB(0x00, 0x75, 0xFF);
1630     case kHoveredAccent:
1631       return SkColorSetRGB(0x00, 0x5C, 0xC8);
1632     case kPressedAccent:
1633       return SkColorSetRGB(0x37, 0x93, 0xFF);
1634     case kDisabledAccent:
1635       return SkColorSetARGB(0x4D, 0x76, 0x76, 0x76);
1636     case kBackground:
1637       return SK_ColorWHITE;
1638     case kDisabledBackground:
1639       return SkColorSetA(SK_ColorWHITE, 0x99);
1640     case kFill:
1641     case kButtonFill:
1642       return SkColorSetRGB(0xEF, 0xEF, 0xEF);
1643     case kHoveredFill:
1644     case kButtonHoveredFill:
1645       return SkColorSetRGB(0xE5, 0xE5, 0xE5);
1646     case kPressedFill:
1647     case kButtonPressedFill:
1648       return SkColorSetRGB(0xF5, 0xF5, 0xF5);
1649     case kDisabledFill:
1650     case kButtonDisabledFill:
1651       return SkColorSetARGB(0x4D, 0xEF, 0xEF, 0xEF);
1652     case kLightenLayer:
1653       return SkColorSetARGB(0x33, 0xA9, 0xA9, 0xA9);
1654     case kProgressValue:
1655       return SkColorSetRGB(0x00, 0x75, 0xFF);
1656     case kSlider:
1657       return SkColorSetRGB(0x00, 0x75, 0xFF);
1658     case kHoveredSlider:
1659       return SkColorSetRGB(0x00, 0x5C, 0xC8);
1660     case kPressedSlider:
1661       return SkColorSetRGB(0x37, 0x93, 0xFF);
1662     case kDisabledSlider:
1663       return SkColorSetRGB(0xCB, 0xCB, 0xCB);
1664     case kAutoCompleteBackground:
1665       return SkColorSetRGB(0xE8, 0xF0, 0xFE);
1666     case kScrollbarArrowBackground:
1667     case kScrollbarTrack:
1668       return SkColorSetRGB(0xF1, 0xF1, 0xF1);
1669     case kScrollbarArrowBackgroundHovered:
1670       return SkColorSetRGB(0xD2, 0xD2, 0xD2);
1671     case kScrollbarArrowBackgroundPressed:
1672       return SkColorSetRGB(0x78, 0x78, 0x78);
1673     case kScrollbarArrowHovered:
1674     case kScrollbarArrow:
1675       return SkColorSetRGB(0x50, 0x50, 0x50);
1676     case kScrollbarArrowPressed:
1677       return SK_ColorWHITE;
1678     case kScrollbarThumbInactive:
1679       return SkColorSetRGB(0xEA, 0xEA, 0xEA);
1680     case kScrollbarThumbHovered:
1681     case kScrollbarThumbPressed:
1682     case kScrollbarThumb:
1683       return SK_ColorBLACK;
1684   }
1685   NOTREACHED();
1686   return gfx::kPlaceholderColor;
1687 }
1688 
GetDarkModeControlColor(ControlColorId color_id) const1689 SkColor NativeThemeBase::GetDarkModeControlColor(
1690     ControlColorId color_id) const {
1691   switch (color_id) {
1692     case kAccent:
1693       return SkColorSetRGB(0x99, 0xC8, 0xFF);
1694     case kHoveredAccent:
1695       return SkColorSetRGB(0xD1, 0xE6, 0xFF);
1696     case kPressedAccent:
1697       return SkColorSetRGB(0x61, 0xA9, 0xFF);
1698     case kDisabledAccent:
1699       return SkColorSetRGB(0x75, 0x75, 0x75);
1700     case kProgressValue:
1701       return SkColorSetRGB(0x63, 0xAD, 0xE5);
1702     case kFill:
1703       return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1704     case kButtonBorder:
1705     case kButtonFill:
1706       return SkColorSetRGB(0x6B, 0x6B, 0x6B);
1707     case kLightenLayer:
1708     case kAutoCompleteBackground:
1709     case kBackground:
1710       return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1711     case kBorder:
1712       return SkColorSetRGB(0x85, 0x85, 0x85);
1713     case kSlider:
1714       return SkColorSetRGB(0x99, 0xC8, 0xFF);
1715     case kHoveredSlider:
1716       return SkColorSetRGB(0xD1, 0xE6, 0xFF);
1717     case kPressedSlider:
1718       return SkColorSetRGB(0x61, 0xA9, 0xFF);
1719     case kDisabledSlider:
1720       return SkColorSetRGB(0x75, 0x75, 0x75);
1721     case kDisabledBackground:
1722       return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1723     case kHoveredBorder:
1724       return SkColorSetRGB(0xAC, 0xAC, 0xAC);
1725     case kPressedBorder:
1726       return SkColorSetRGB(0x6E, 0x6E, 0x6E);
1727     case kDisabledBorder:
1728       return SkColorSetRGB(0x62, 0x62, 0x62);
1729     case kHoveredFill:
1730       return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1731     case kButtonHoveredBorder:
1732     case kButtonHoveredFill:
1733       return SkColorSetRGB(0x7B, 0x7B, 0x7B);
1734     case kPressedFill:
1735       return SkColorSetRGB(0x3B, 0x3B, 0x3B);
1736     case kButtonPressedBorder:
1737     case kButtonPressedFill:
1738       return SkColorSetRGB(0x61, 0x61, 0x61);
1739     case kDisabledFill:
1740     case kButtonDisabledBorder:
1741     case kButtonDisabledFill:
1742       return SkColorSetRGB(0x36, 0x36, 0x36);
1743     case kScrollbarArrowBackground:
1744       return SkColorSetRGB(0x42, 0x42, 0x42);
1745     case kScrollbarArrowBackgroundHovered:
1746       return SkColorSetRGB(0x4F, 0x4F, 0x4F);
1747     case kScrollbarArrowBackgroundPressed:
1748       return SkColorSetRGB(0xB1, 0xB1, 0xB1);
1749     case kScrollbarArrowHovered:
1750     case kScrollbarArrow:
1751       return SK_ColorWHITE;
1752     case kScrollbarArrowPressed:
1753       return SK_ColorBLACK;
1754     case kScrollbarTrack:
1755       return SkColorSetRGB(0x42, 0x42, 0x42);
1756     case kScrollbarThumbInactive:
1757     case kScrollbarThumbHovered:
1758     case kScrollbarThumbPressed:
1759     case kScrollbarThumb:
1760       return SK_ColorWHITE;
1761   }
1762   NOTREACHED();
1763   return gfx::kPlaceholderColor;
1764 }
1765 
GetHighContrastControlColor(ControlColorId color_id,ColorScheme color_scheme) const1766 SkColor NativeThemeBase::GetHighContrastControlColor(
1767     ControlColorId color_id,
1768     ColorScheme color_scheme) const {
1769   if (!system_colors_.empty()) {
1770     switch (color_id) {
1771       case kDisabledBorder:
1772       case kDisabledAccent:
1773       case kDisabledSlider:
1774       case kButtonDisabledBorder:
1775         return system_colors_[SystemThemeColor::kGrayText];
1776       case kBorder:
1777       case kHoveredBorder:
1778       case kPressedBorder:
1779       case kButtonBorder:
1780       case kButtonHoveredBorder:
1781       case kButtonPressedBorder:
1782         return system_colors_[SystemThemeColor::kButtonText];
1783       case kAccent:
1784       case kHoveredAccent:
1785       case kPressedAccent:
1786       case kProgressValue:
1787       case kSlider:
1788       case kHoveredSlider:
1789       case kPressedSlider:
1790       case kScrollbarThumbHovered:
1791       case kScrollbarThumbPressed:
1792       case kScrollbarArrowBackgroundHovered:
1793       case kScrollbarArrowBackgroundPressed:
1794         return system_colors_[SystemThemeColor::kHighlight];
1795       case kBackground:
1796       case kDisabledBackground:
1797       case kFill:
1798       case kHoveredFill:
1799       case kPressedFill:
1800       case kDisabledFill:
1801       case kButtonFill:
1802       case kButtonHoveredFill:
1803       case kButtonPressedFill:
1804       case kButtonDisabledFill:
1805       case kAutoCompleteBackground:
1806       case kLightenLayer:
1807       case kScrollbarArrowBackground:
1808       case kScrollbarTrack:
1809         return system_colors_[SystemThemeColor::kWindow];
1810       case kScrollbarArrow:
1811       case kScrollbarThumb:
1812       case kScrollbarThumbInactive:
1813         return system_colors_[SystemThemeColor::kWindowText];
1814       case kScrollbarArrowHovered:
1815       case kScrollbarArrowPressed:
1816         return system_colors_[SystemThemeColor::kButtonFace];
1817     }
1818   } else {
1819     //   // Default high contrast colors (used in web test mode)
1820     switch (color_id) {
1821       case kDisabledBorder:
1822       case kDisabledAccent:
1823       case kDisabledSlider:
1824       case kButtonDisabledBorder:
1825         return SK_ColorGREEN;
1826       case kBorder:
1827       case kHoveredBorder:
1828       case kPressedBorder:
1829       case kButtonBorder:
1830       case kButtonHoveredBorder:
1831       case kButtonPressedBorder:
1832       case kScrollbarThumbInactive:
1833       case kScrollbarArrowBackground:
1834       case kScrollbarTrack:
1835         return SK_ColorWHITE;
1836       case kAccent:
1837       case kHoveredAccent:
1838       case kPressedAccent:
1839       case kProgressValue:
1840       case kSlider:
1841       case kHoveredSlider:
1842       case kPressedSlider:
1843         return SK_ColorCYAN;
1844       case kBackground:
1845       case kDisabledBackground:
1846       case kFill:
1847       case kHoveredFill:
1848       case kPressedFill:
1849       case kDisabledFill:
1850       case kButtonFill:
1851       case kButtonHoveredFill:
1852       case kButtonPressedFill:
1853       case kButtonDisabledFill:
1854       case kAutoCompleteBackground:
1855       case kLightenLayer:
1856       case kScrollbarThumb:
1857       case kScrollbarArrow:
1858       case kScrollbarArrowHovered:
1859       case kScrollbarArrowPressed:
1860         return SK_ColorBLACK;
1861       case kScrollbarThumbHovered:
1862       case kScrollbarThumbPressed:
1863       case kScrollbarArrowBackgroundHovered:
1864       case kScrollbarArrowBackgroundPressed:
1865         return SkColorSetRGB(0x1A, 0xEB, 0xFF);
1866     }
1867   }
1868   NOTREACHED();
1869   return gfx::kPlaceholderColor;
1870 }
1871 
PaintLightenLayer(cc::PaintCanvas * canvas,SkRect skrect,State state,SkScalar border_radius,ColorScheme color_scheme) const1872 void NativeThemeBase::PaintLightenLayer(cc::PaintCanvas* canvas,
1873                                         SkRect skrect,
1874                                         State state,
1875                                         SkScalar border_radius,
1876                                         ColorScheme color_scheme) const {
1877   if (state == kDisabled) {
1878     cc::PaintFlags flags;
1879     flags.setAntiAlias(true);
1880     flags.setStyle(cc::PaintFlags::kFill_Style);
1881     // Draw the lighten layer to lighten the background so the translucent
1882     // disabled color works regardless of what it's over.
1883     flags.setColor(GetControlColor(kLightenLayer, color_scheme));
1884     canvas->drawRoundRect(skrect, border_radius, border_radius, flags);
1885   }
1886 }
1887 
AlignSliderTrack(const gfx::Rect & slider_rect,const NativeTheme::SliderExtraParams & slider,bool is_value,float track_height) const1888 SkRect NativeThemeBase::AlignSliderTrack(
1889     const gfx::Rect& slider_rect,
1890     const NativeTheme::SliderExtraParams& slider,
1891     bool is_value,
1892     float track_height) const {
1893   const float kAlignment = track_height / 2;
1894   const float mid_x = slider_rect.x() + slider_rect.width() / 2.0f;
1895   const float mid_y = slider_rect.y() + slider_rect.height() / 2.0f;
1896   SkRect aligned_rect;
1897 
1898   if (slider.vertical) {
1899     const float top = is_value ? slider_rect.y() + slider.thumb_y + kAlignment
1900                                : slider_rect.y();
1901     aligned_rect.setLTRB(
1902         std::max(float(slider_rect.x()), mid_x - kAlignment), top,
1903         std::min(float(slider_rect.right()), mid_x + kAlignment),
1904         slider_rect.bottom());
1905   } else {
1906     const float right = is_value && !slider.right_to_left
1907                             ? slider_rect.x() + slider.thumb_x + kAlignment
1908                             : slider_rect.right();
1909     const float left = is_value && slider.right_to_left
1910                            ? slider_rect.x() + slider.thumb_x + kAlignment
1911                            : slider_rect.x();
1912 
1913     aligned_rect.setLTRB(
1914         left, std::max(float(slider_rect.y()), mid_y - kAlignment), right,
1915         std::min(float(slider_rect.bottom()), mid_y + kAlignment));
1916   }
1917 
1918   return aligned_rect;
1919 }
1920 
1921 }  // namespace ui
1922