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