Home | History | Annotate | Download | only in native_theme
      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 
      9 #include "base/command_line.h"
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "grit/ui_resources.h"
     13 #include "third_party/skia/include/effects/SkGradientShader.h"
     14 #include "ui/base/layout.h"
     15 #include "ui/base/resource/resource_bundle.h"
     16 #include "ui/base/ui_base_switches.h"
     17 #include "ui/gfx/canvas.h"
     18 #include "ui/gfx/color_utils.h"
     19 #include "ui/gfx/image/image_skia.h"
     20 #include "ui/gfx/rect.h"
     21 #include "ui/gfx/size.h"
     22 #include "ui/gfx/skia_util.h"
     23 
     24 namespace {
     25 
     26 // These are the default dimensions of radio buttons and checkboxes.
     27 const int kCheckboxAndRadioWidth = 13;
     28 const int kCheckboxAndRadioHeight = 13;
     29 
     30 // These sizes match the sizes in Chromium Win.
     31 const int kSliderThumbWidth = 11;
     32 const int kSliderThumbHeight = 21;
     33 
     34 const SkColor kSliderTrackBackgroundColor =
     35     SkColorSetRGB(0xe3, 0xdd, 0xd8);
     36 const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef);
     37 const SkColor kSliderThumbDarkGrey = SkColorSetRGB(0xea, 0xe5, 0xe0);
     38 const SkColor kSliderThumbBorderDarkGrey =
     39     SkColorSetRGB(0x9d, 0x96, 0x8e);
     40 
     41 const SkColor kMenuPopupBackgroundColor = SkColorSetRGB(210, 225, 246);
     42 
     43 const unsigned int kDefaultScrollbarWidth = 15;
     44 const unsigned int kDefaultScrollbarButtonLength = 14;
     45 
     46 const SkColor kCheckboxTinyColor = SK_ColorGRAY;
     47 const SkColor kCheckboxShadowColor = SkColorSetARGB(0x15, 0, 0, 0);
     48 const SkColor kCheckboxShadowHoveredColor = SkColorSetARGB(0x1F, 0, 0, 0);
     49 const SkColor kCheckboxShadowDisabledColor = SkColorSetARGB(0, 0, 0, 0);
     50 const SkColor kCheckboxGradientColors[] = {
     51     SkColorSetRGB(0xed, 0xed, 0xed),
     52     SkColorSetRGB(0xde, 0xde, 0xde) };
     53 const SkColor kCheckboxGradientPressedColors[] = {
     54     SkColorSetRGB(0xe7, 0xe7, 0xe7),
     55     SkColorSetRGB(0xd7, 0xd7, 0xd7) };
     56 const SkColor kCheckboxGradientHoveredColors[] = {
     57     SkColorSetRGB(0xf0, 0xf0, 0xf0),
     58     SkColorSetRGB(0xe0, 0xe0, 0xe0) };
     59 const SkColor kCheckboxGradientDisabledColors[] = {
     60     SkColorSetARGB(0x80, 0xed, 0xed, 0xed),
     61     SkColorSetARGB(0x80, 0xde, 0xde, 0xde) };
     62 const SkColor kCheckboxBorderColor = SkColorSetARGB(0x40, 0, 0, 0);
     63 const SkColor kCheckboxBorderHoveredColor = SkColorSetARGB(0x4D, 0, 0, 0);
     64 const SkColor kCheckboxBorderDisabledColor = SkColorSetARGB(0x20, 0, 0, 0);
     65 const SkColor kCheckboxStrokeColor = SkColorSetARGB(0xB3, 0, 0, 0);
     66 const SkColor kCheckboxStrokeDisabledColor = SkColorSetARGB(0x59, 0, 0, 0);
     67 const SkColor kRadioDotColor = SkColorSetRGB(0x66, 0x66, 0x66);
     68 const SkColor kRadioDotDisabledColor = SkColorSetARGB(0x80, 0x66, 0x66, 0x66);
     69 
     70 const SkColor kInputInvalidColor = SkColorSetRGB(0xde, 0x49, 0x32);
     71 
     72 // Get lightness adjusted color.
     73 SkColor BrightenColor(const color_utils::HSL& hsl, SkAlpha alpha,
     74     double lightness_amount) {
     75   color_utils::HSL adjusted = hsl;
     76   adjusted.l += lightness_amount;
     77   if (adjusted.l > 1.0)
     78     adjusted.l = 1.0;
     79   if (adjusted.l < 0.0)
     80     adjusted.l = 0.0;
     81 
     82   return color_utils::HSLToSkColor(adjusted, alpha);
     83 }
     84 
     85 }  // namespace
     86 
     87 namespace ui {
     88 
     89 gfx::Size NativeThemeBase::GetPartSize(Part part,
     90                                        State state,
     91                                        const ExtraParams& extra) const {
     92   switch (part) {
     93     // Please keep these in the order of NativeTheme::Part.
     94     case kCheckbox:
     95       return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
     96     case kInnerSpinButton:
     97       return gfx::Size(scrollbar_width_, 0);
     98     case kMenuList:
     99       return gfx::Size();  // No default size.
    100     case kMenuCheck:
    101     case kMenuCheckBackground:
    102     case kMenuPopupArrow:
    103       NOTIMPLEMENTED();
    104       break;
    105     case kMenuPopupBackground:
    106       return gfx::Size();  // No default size.
    107     case kMenuPopupGutter:
    108     case kMenuPopupSeparator:
    109       NOTIMPLEMENTED();
    110       break;
    111     case kMenuItemBackground:
    112     case kProgressBar:
    113     case kPushButton:
    114       return gfx::Size();  // No default size.
    115     case kRadio:
    116       return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
    117     case kScrollbarDownArrow:
    118     case kScrollbarUpArrow:
    119       return gfx::Size(scrollbar_width_, scrollbar_button_length_);
    120     case kScrollbarLeftArrow:
    121     case kScrollbarRightArrow:
    122       return gfx::Size(scrollbar_button_length_, scrollbar_width_);
    123     case kScrollbarHorizontalThumb:
    124       // This matches Firefox on Linux.
    125       return gfx::Size(2 * scrollbar_width_, scrollbar_width_);
    126     case kScrollbarVerticalThumb:
    127       // This matches Firefox on Linux.
    128       return gfx::Size(scrollbar_width_, 2 * scrollbar_width_);
    129     case kScrollbarHorizontalTrack:
    130       return gfx::Size(0, scrollbar_width_);
    131     case kScrollbarVerticalTrack:
    132       return gfx::Size(scrollbar_width_, 0);
    133     case kScrollbarHorizontalGripper:
    134     case kScrollbarVerticalGripper:
    135       NOTIMPLEMENTED();
    136       break;
    137     case kSliderTrack:
    138       return gfx::Size();  // No default size.
    139     case kSliderThumb:
    140       // These sizes match the sizes in Chromium Win.
    141       return gfx::Size(kSliderThumbWidth, kSliderThumbHeight);
    142     case kTabPanelBackground:
    143       NOTIMPLEMENTED();
    144       break;
    145     case kTextField:
    146       return gfx::Size();  // No default size.
    147     case kTrackbarThumb:
    148     case kTrackbarTrack:
    149     case kWindowResizeGripper:
    150       NOTIMPLEMENTED();
    151       break;
    152     default:
    153       NOTREACHED() << "Unknown theme part: " << part;
    154       break;
    155   }
    156   return gfx::Size();
    157 }
    158 
    159 void NativeThemeBase::Paint(SkCanvas* canvas,
    160                             Part part,
    161                             State state,
    162                             const gfx::Rect& rect,
    163                             const ExtraParams& extra) const {
    164   if (rect.IsEmpty())
    165     return;
    166 
    167   switch (part) {
    168     // Please keep these in the order of NativeTheme::Part.
    169     case kCheckbox:
    170       PaintCheckbox(canvas, state, rect, extra.button);
    171       break;
    172     case kInnerSpinButton:
    173       PaintInnerSpinButton(canvas, state, rect, extra.inner_spin);
    174       break;
    175     case kMenuList:
    176       PaintMenuList(canvas, state, rect, extra.menu_list);
    177       break;
    178     case kMenuCheck:
    179     case kMenuCheckBackground:
    180     case kMenuPopupArrow:
    181       NOTIMPLEMENTED();
    182       break;
    183     case kMenuPopupBackground:
    184       PaintMenuPopupBackground(canvas, rect.size(), extra.menu_background);
    185       break;
    186     case kMenuPopupGutter:
    187     case kMenuPopupSeparator:
    188       NOTIMPLEMENTED();
    189       break;
    190     case kMenuItemBackground:
    191       PaintMenuItemBackground(canvas, state, rect, extra.menu_list);
    192       break;
    193     case kProgressBar:
    194       PaintProgressBar(canvas, state, rect, extra.progress_bar);
    195       break;
    196     case kPushButton:
    197       PaintButton(canvas, state, rect, extra.button);
    198       break;
    199     case kRadio:
    200       PaintRadio(canvas, state, rect, extra.button);
    201       break;
    202     case kScrollbarDownArrow:
    203     case kScrollbarUpArrow:
    204     case kScrollbarLeftArrow:
    205     case kScrollbarRightArrow:
    206       PaintArrowButton(canvas, rect, part, state);
    207       break;
    208     case kScrollbarHorizontalThumb:
    209     case kScrollbarVerticalThumb:
    210       PaintScrollbarThumb(canvas, part, state, rect);
    211       break;
    212     case kScrollbarHorizontalTrack:
    213     case kScrollbarVerticalTrack:
    214       PaintScrollbarTrack(canvas, part, state, extra.scrollbar_track, rect);
    215       break;
    216     case kScrollbarHorizontalGripper:
    217     case kScrollbarVerticalGripper:
    218       // Invoked by views scrollbar code, don't care about for non-win
    219       // implementations, so no NOTIMPLEMENTED.
    220       break;
    221     case kSliderTrack:
    222       PaintSliderTrack(canvas, state, rect, extra.slider);
    223       break;
    224     case kSliderThumb:
    225       PaintSliderThumb(canvas, state, rect, extra.slider);
    226       break;
    227     case kTabPanelBackground:
    228       NOTIMPLEMENTED();
    229       break;
    230     case kTextField:
    231       PaintTextField(canvas, state, rect, extra.text_field);
    232       break;
    233     case kTrackbarThumb:
    234     case kTrackbarTrack:
    235     case kWindowResizeGripper:
    236       NOTIMPLEMENTED();
    237       break;
    238     default:
    239       NOTREACHED() << "Unknown theme part: " << part;
    240       break;
    241   }
    242 }
    243 
    244 NativeThemeBase::NativeThemeBase()
    245     : scrollbar_width_(kDefaultScrollbarWidth),
    246       scrollbar_button_length_(kDefaultScrollbarButtonLength) {
    247 }
    248 
    249 NativeThemeBase::~NativeThemeBase() {
    250 }
    251 
    252 void NativeThemeBase::PaintArrowButton(
    253     SkCanvas* canvas,
    254     const gfx::Rect& rect, Part direction, State state) const {
    255   int widthMiddle, lengthMiddle;
    256   SkPaint paint;
    257   if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) {
    258     widthMiddle = rect.width() / 2 + 1;
    259     lengthMiddle = rect.height() / 2 + 1;
    260   } else {
    261     lengthMiddle = rect.width() / 2 + 1;
    262     widthMiddle = rect.height() / 2 + 1;
    263   }
    264 
    265   // Calculate button color.
    266   SkScalar trackHSV[3];
    267   SkColorToHSV(track_color_, trackHSV);
    268   SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2f);
    269   SkColor backgroundColor = buttonColor;
    270   if (state == kPressed) {
    271     SkScalar buttonHSV[3];
    272     SkColorToHSV(buttonColor, buttonHSV);
    273     buttonColor = SaturateAndBrighten(buttonHSV, 0, -0.1f);
    274   } else if (state == kHovered) {
    275     SkScalar buttonHSV[3];
    276     SkColorToHSV(buttonColor, buttonHSV);
    277     buttonColor = SaturateAndBrighten(buttonHSV, 0, 0.05f);
    278   }
    279 
    280   SkIRect skrect;
    281   skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y()
    282       + rect.height());
    283   // Paint the background (the area visible behind the rounded corners).
    284   paint.setColor(backgroundColor);
    285   canvas->drawIRect(skrect, paint);
    286 
    287   // Paint the button's outline and fill the middle
    288   SkPath outline;
    289   switch (direction) {
    290     case kScrollbarUpArrow:
    291       outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5);
    292       outline.rLineTo(0, -(rect.height() - 2));
    293       outline.rLineTo(2, -2);
    294       outline.rLineTo(rect.width() - 5, 0);
    295       outline.rLineTo(2, 2);
    296       outline.rLineTo(0, rect.height() - 2);
    297       break;
    298     case kScrollbarDownArrow:
    299       outline.moveTo(rect.x() + 0.5, rect.y() - 0.5);
    300       outline.rLineTo(0, rect.height() - 2);
    301       outline.rLineTo(2, 2);
    302       outline.rLineTo(rect.width() - 5, 0);
    303       outline.rLineTo(2, -2);
    304       outline.rLineTo(0, -(rect.height() - 2));
    305       break;
    306     case kScrollbarRightArrow:
    307       outline.moveTo(rect.x() - 0.5, rect.y() + 0.5);
    308       outline.rLineTo(rect.width() - 2, 0);
    309       outline.rLineTo(2, 2);
    310       outline.rLineTo(0, rect.height() - 5);
    311       outline.rLineTo(-2, 2);
    312       outline.rLineTo(-(rect.width() - 2), 0);
    313       break;
    314     case kScrollbarLeftArrow:
    315       outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5);
    316       outline.rLineTo(-(rect.width() - 2), 0);
    317       outline.rLineTo(-2, 2);
    318       outline.rLineTo(0, rect.height() - 5);
    319       outline.rLineTo(2, 2);
    320       outline.rLineTo(rect.width() - 2, 0);
    321       break;
    322     default:
    323       break;
    324   }
    325   outline.close();
    326 
    327   paint.setStyle(SkPaint::kFill_Style);
    328   paint.setColor(buttonColor);
    329   canvas->drawPath(outline, paint);
    330 
    331   paint.setAntiAlias(true);
    332   paint.setStyle(SkPaint::kStroke_Style);
    333   SkScalar thumbHSV[3];
    334   SkColorToHSV(thumb_inactive_color_, thumbHSV);
    335   paint.setColor(OutlineColor(trackHSV, thumbHSV));
    336   canvas->drawPath(outline, paint);
    337 
    338   // If the button is disabled or read-only, the arrow is drawn with the
    339   // outline color.
    340   if (state != kDisabled)
    341     paint.setColor(SK_ColorBLACK);
    342 
    343   paint.setAntiAlias(false);
    344   paint.setStyle(SkPaint::kFill_Style);
    345 
    346   SkPath path;
    347   // The constants in this block of code are hand-tailored to produce good
    348   // looking arrows without anti-aliasing.
    349   switch (direction) {
    350     case kScrollbarUpArrow:
    351       path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle + 2);
    352       path.rLineTo(7, 0);
    353       path.rLineTo(-4, -4);
    354       break;
    355     case kScrollbarDownArrow:
    356       path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle - 3);
    357       path.rLineTo(7, 0);
    358       path.rLineTo(-4, 4);
    359       break;
    360     case kScrollbarRightArrow:
    361       path.moveTo(rect.x() + lengthMiddle - 3, rect.y() + widthMiddle - 4);
    362       path.rLineTo(0, 7);
    363       path.rLineTo(4, -4);
    364       break;
    365     case kScrollbarLeftArrow:
    366       path.moveTo(rect.x() + lengthMiddle + 1, rect.y() + widthMiddle - 5);
    367       path.rLineTo(0, 9);
    368       path.rLineTo(-4, -4);
    369       break;
    370     default:
    371       break;
    372   }
    373   path.close();
    374 
    375   canvas->drawPath(path, paint);
    376 }
    377 
    378 void NativeThemeBase::PaintScrollbarTrack(SkCanvas* canvas,
    379     Part part,
    380     State state,
    381     const ScrollbarTrackExtraParams& extra_params,
    382     const gfx::Rect& rect) const {
    383   SkPaint paint;
    384   SkIRect skrect;
    385 
    386   skrect.set(rect.x(), rect.y(), rect.right(), rect.bottom());
    387   SkScalar track_hsv[3];
    388   SkColorToHSV(track_color_, track_hsv);
    389   paint.setColor(SaturateAndBrighten(track_hsv, 0, 0));
    390   canvas->drawIRect(skrect, paint);
    391 
    392   SkScalar thumb_hsv[3];
    393   SkColorToHSV(thumb_inactive_color_, thumb_hsv);
    394 
    395   paint.setColor(OutlineColor(track_hsv, thumb_hsv));
    396   DrawBox(canvas, rect, paint);
    397 }
    398 
    399 void NativeThemeBase::PaintScrollbarThumb(SkCanvas* canvas,
    400                                            Part part,
    401                                            State state,
    402                                            const gfx::Rect& rect) const {
    403   const bool hovered = state == kHovered;
    404   const int midx = rect.x() + rect.width() / 2;
    405   const int midy = rect.y() + rect.height() / 2;
    406   const bool vertical = part == kScrollbarVerticalThumb;
    407 
    408   SkScalar thumb[3];
    409   SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb);
    410 
    411   SkPaint paint;
    412   paint.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
    413 
    414   SkIRect skrect;
    415   if (vertical)
    416     skrect.set(rect.x(), rect.y(), midx + 1, rect.y() + rect.height());
    417   else
    418     skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1);
    419 
    420   canvas->drawIRect(skrect, paint);
    421 
    422   paint.setColor(SaturateAndBrighten(thumb, 0, -0.02f));
    423 
    424   if (vertical) {
    425     skrect.set(
    426         midx + 1, rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
    427   } else {
    428     skrect.set(
    429         rect.x(), midy + 1, rect.x() + rect.width(), rect.y() + rect.height());
    430   }
    431 
    432   canvas->drawIRect(skrect, paint);
    433 
    434   SkScalar track[3];
    435   SkColorToHSV(track_color_, track);
    436   paint.setColor(OutlineColor(track, thumb));
    437   DrawBox(canvas, rect, paint);
    438 
    439   if (rect.height() > 10 && rect.width() > 10) {
    440     const int grippy_half_width = 2;
    441     const int inter_grippy_offset = 3;
    442     if (vertical) {
    443       DrawHorizLine(canvas,
    444                     midx - grippy_half_width,
    445                     midx + grippy_half_width,
    446                     midy - inter_grippy_offset,
    447                     paint);
    448       DrawHorizLine(canvas,
    449                     midx - grippy_half_width,
    450                     midx + grippy_half_width,
    451                     midy,
    452                     paint);
    453       DrawHorizLine(canvas,
    454                     midx - grippy_half_width,
    455                     midx + grippy_half_width,
    456                     midy + inter_grippy_offset,
    457                     paint);
    458     } else {
    459       DrawVertLine(canvas,
    460                    midx - inter_grippy_offset,
    461                    midy - grippy_half_width,
    462                    midy + grippy_half_width,
    463                    paint);
    464       DrawVertLine(canvas,
    465                    midx,
    466                    midy - grippy_half_width,
    467                    midy + grippy_half_width,
    468                    paint);
    469       DrawVertLine(canvas,
    470                    midx + inter_grippy_offset,
    471                    midy - grippy_half_width,
    472                    midy + grippy_half_width,
    473                    paint);
    474     }
    475   }
    476 }
    477 
    478 void NativeThemeBase::PaintCheckbox(SkCanvas* canvas,
    479                                     State state,
    480                                     const gfx::Rect& rect,
    481                                     const ButtonExtraParams& button) const {
    482   SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect,
    483                                            SkIntToScalar(2));
    484   if (!skrect.isEmpty()) {
    485     // Draw the checkmark / dash.
    486     SkPaint paint;
    487     paint.setAntiAlias(true);
    488     paint.setStyle(SkPaint::kStroke_Style);
    489     if (state == kDisabled)
    490       paint.setColor(kCheckboxStrokeDisabledColor);
    491     else
    492       paint.setColor(kCheckboxStrokeColor);
    493     if (button.indeterminate) {
    494       SkPath dash;
    495       dash.moveTo(skrect.x() + skrect.width() * 0.16,
    496                   (skrect.y() + skrect.bottom()) / 2);
    497       dash.rLineTo(skrect.width() * 0.68, 0);
    498       paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.2));
    499       canvas->drawPath(dash, paint);
    500     } else if (button.checked) {
    501       SkPath check;
    502       check.moveTo(skrect.x() + skrect.width() * 0.2,
    503                    skrect.y() + skrect.height() * 0.5);
    504       check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
    505       paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.23));
    506       check.lineTo(skrect.right() - skrect.width() * 0.2,
    507                    skrect.y() + skrect.height() * 0.2);
    508       canvas->drawPath(check, paint);
    509     }
    510   }
    511 }
    512 
    513 // Draws the common elements of checkboxes and radio buttons.
    514 // Returns the rectangle within which any additional decorations should be
    515 // drawn, or empty if none.
    516 SkRect NativeThemeBase::PaintCheckboxRadioCommon(
    517     SkCanvas* canvas,
    518     State state,
    519     const gfx::Rect& rect,
    520     const SkScalar borderRadius) const {
    521 
    522   SkRect skrect = gfx::RectToSkRect(rect);
    523 
    524   // Use the largest square that fits inside the provided rectangle.
    525   // No other browser seems to support non-square widget, so accidentally
    526   // having non-square sizes is common (eg. amazon and webkit dev tools).
    527   if (skrect.width() != skrect.height()) {
    528     SkScalar size = SkMinScalar(skrect.width(), skrect.height());
    529     skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
    530   }
    531 
    532   // If the rectangle is too small then paint only a rectangle.  We don't want
    533   // to have to worry about '- 1' and '+ 1' calculations below having overflow
    534   // or underflow.
    535   if (skrect.width() <= 2) {
    536     SkPaint paint;
    537     paint.setColor(kCheckboxTinyColor);
    538     paint.setStyle(SkPaint::kFill_Style);
    539     canvas->drawRect(skrect, paint);
    540     // Too small to draw anything more.
    541     return SkRect::MakeEmpty();
    542   }
    543 
    544   // Make room for the drop shadow.
    545   skrect.iset(skrect.x(), skrect.y(), skrect.right() - 1, skrect.bottom() - 1);
    546 
    547   // Draw the drop shadow below the widget.
    548   if (state != kPressed) {
    549     SkPaint paint;
    550     paint.setAntiAlias(true);
    551     SkRect shadowRect = skrect;
    552     shadowRect.offset(0, 1);
    553     if (state == kDisabled)
    554      paint.setColor(kCheckboxShadowDisabledColor);
    555     else if (state == kHovered)
    556       paint.setColor(kCheckboxShadowHoveredColor);
    557     else
    558       paint.setColor(kCheckboxShadowColor);
    559     paint.setStyle(SkPaint::kFill_Style);
    560     canvas->drawRoundRect(shadowRect, borderRadius, borderRadius, paint);
    561   }
    562 
    563   // Draw the gradient-filled rectangle
    564   SkPoint gradient_bounds[3];
    565   gradient_bounds[0].set(skrect.x(), skrect.y());
    566   gradient_bounds[1].set(skrect.x(), skrect.y() + skrect.height() * 0.38);
    567   gradient_bounds[2].set(skrect.x(), skrect.bottom());
    568   const SkColor* startEndColors;
    569   if (state == kPressed)
    570     startEndColors = kCheckboxGradientPressedColors;
    571   else if (state == kHovered)
    572     startEndColors = kCheckboxGradientHoveredColors;
    573   else if (state == kDisabled)
    574     startEndColors = kCheckboxGradientDisabledColors;
    575   else /* kNormal */
    576     startEndColors = kCheckboxGradientColors;
    577   SkColor colors[3] = {startEndColors[0], startEndColors[0], startEndColors[1]};
    578   skia::RefPtr<SkShader> shader = skia::AdoptRef(
    579       SkGradientShader::CreateLinear(
    580           gradient_bounds, colors, NULL, 3, SkShader::kClamp_TileMode, NULL));
    581   SkPaint paint;
    582   paint.setAntiAlias(true);
    583   paint.setShader(shader.get());
    584   paint.setStyle(SkPaint::kFill_Style);
    585   canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
    586   paint.setShader(NULL);
    587 
    588   // Draw the border.
    589   if (state == kHovered)
    590     paint.setColor(kCheckboxBorderHoveredColor);
    591   else if (state == kDisabled)
    592     paint.setColor(kCheckboxBorderDisabledColor);
    593   else
    594     paint.setColor(kCheckboxBorderColor);
    595   paint.setStyle(SkPaint::kStroke_Style);
    596   paint.setStrokeWidth(SkIntToScalar(1));
    597   skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
    598   canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
    599 
    600   // Return the rectangle excluding the drop shadow for drawing any additional
    601   // decorations.
    602   return skrect;
    603 }
    604 
    605 void NativeThemeBase::PaintRadio(SkCanvas* canvas,
    606                                   State state,
    607                                   const gfx::Rect& rect,
    608                                   const ButtonExtraParams& button) const {
    609 
    610   // Most of a radio button is the same as a checkbox, except the the rounded
    611   // square is a circle (i.e. border radius >= 100%).
    612   const SkScalar radius = SkFloatToScalar(
    613       static_cast<float>(std::max(rect.width(), rect.height())) / 2);
    614   SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, radius);
    615   if (!skrect.isEmpty() && button.checked) {
    616     // Draw the dot.
    617     SkPaint paint;
    618     paint.setAntiAlias(true);
    619     paint.setStyle(SkPaint::kFill_Style);
    620     if (state == kDisabled)
    621       paint.setColor(kRadioDotDisabledColor);
    622     else
    623       paint.setColor(kRadioDotColor);
    624     skrect.inset(skrect.width() * 0.25, skrect.height() * 0.25);
    625     // Use drawRoundedRect instead of drawOval to be completely consistent
    626     // with the border in PaintCheckboxRadioNewCommon.
    627     canvas->drawRoundRect(skrect, radius, radius, paint);
    628   }
    629 }
    630 
    631 void NativeThemeBase::PaintButton(SkCanvas* canvas,
    632                                   State state,
    633                                   const gfx::Rect& rect,
    634                                   const ButtonExtraParams& button) const {
    635   SkPaint paint;
    636   const int kRight = rect.right();
    637   const int kBottom = rect.bottom();
    638   SkRect skrect = SkRect::MakeLTRB(rect.x(), rect.y(), kRight, kBottom);
    639   SkColor base_color = button.background_color;
    640 
    641   color_utils::HSL base_hsl;
    642   color_utils::SkColorToHSL(base_color, &base_hsl);
    643 
    644   // Our standard gradient is from 0xdd to 0xf8. This is the amount of
    645   // increased luminance between those values.
    646   SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105));
    647 
    648   // If the button is too small, fallback to drawing a single, solid color
    649   if (rect.width() < 5 || rect.height() < 5) {
    650     paint.setColor(base_color);
    651     canvas->drawRect(skrect, paint);
    652     return;
    653   }
    654 
    655   paint.setColor(SK_ColorBLACK);
    656   const int kLightEnd = state == kPressed ? 1 : 0;
    657   const int kDarkEnd = !kLightEnd;
    658   SkPoint gradient_bounds[2];
    659   gradient_bounds[kLightEnd].iset(rect.x(), rect.y());
    660   gradient_bounds[kDarkEnd].iset(rect.x(), kBottom - 1);
    661   SkColor colors[2];
    662   colors[0] = light_color;
    663   colors[1] = base_color;
    664 
    665   skia::RefPtr<SkShader> shader = skia::AdoptRef(
    666       SkGradientShader::CreateLinear(
    667           gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode, NULL));
    668   paint.setStyle(SkPaint::kFill_Style);
    669   paint.setAntiAlias(true);
    670   paint.setShader(shader.get());
    671 
    672   canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
    673   paint.setShader(NULL);
    674 
    675   if (button.has_border) {
    676     int border_alpha = state == kHovered ? 0x80 : 0x55;
    677     if (button.is_focused) {
    678       border_alpha = 0xff;
    679       paint.setColor(GetSystemColor(kColorId_FocusedBorderColor));
    680     }
    681     paint.setStyle(SkPaint::kStroke_Style);
    682     paint.setStrokeWidth(SkIntToScalar(1));
    683     paint.setAlpha(border_alpha);
    684     skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
    685     canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
    686   }
    687 }
    688 
    689 void NativeThemeBase::PaintTextField(SkCanvas* canvas,
    690                                      State state,
    691                                      const gfx::Rect& rect,
    692                                      const TextFieldExtraParams& text) const {
    693   // The following drawing code simulates the user-agent css border for
    694   // text area and text input so that we do not break layout tests. Once we
    695   // have decided the desired looks, we should update the code here and
    696   // the layout test expectations.
    697   SkRect bounds;
    698   bounds.set(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1);
    699 
    700   SkPaint fill_paint;
    701   fill_paint.setStyle(SkPaint::kFill_Style);
    702   fill_paint.setColor(text.background_color);
    703   canvas->drawRect(bounds, fill_paint);
    704 
    705   if (text.is_text_area) {
    706     // Draw text area border: 1px solid black
    707     SkPaint stroke_paint;
    708     fill_paint.setStyle(SkPaint::kStroke_Style);
    709     fill_paint.setColor(SK_ColorBLACK);
    710     canvas->drawRect(bounds, fill_paint);
    711   } else {
    712     // Draw text input and listbox inset border
    713     //   Text Input: 2px inset #eee
    714     //   Listbox: 1px inset #808080
    715     const SkColor kLightColor = text.is_listbox ?
    716         SkColorSetRGB(0x80, 0x80, 0x80) : SkColorSetRGB(0xee, 0xee, 0xee);
    717     const SkColor kDarkColor = text.is_listbox ?
    718         SkColorSetRGB(0x2c, 0x2c, 0x2c) : SkColorSetRGB(0x9a, 0x9a, 0x9a);
    719     const int kBorderWidth = text.is_listbox ? 1 : 2;
    720 
    721     SkPaint dark_paint;
    722     dark_paint.setAntiAlias(true);
    723     dark_paint.setStyle(SkPaint::kFill_Style);
    724     dark_paint.setColor(kDarkColor);
    725 
    726     SkPaint light_paint;
    727     light_paint.setAntiAlias(true);
    728     light_paint.setStyle(SkPaint::kFill_Style);
    729     light_paint.setColor(kLightColor);
    730 
    731     int left = rect.x();
    732     int top = rect.y();
    733     int right = rect.right();
    734     int bottom = rect.bottom();
    735 
    736     SkPath path;
    737     path.incReserve(4);
    738 
    739     // Top
    740     path.moveTo(SkIntToScalar(left), SkIntToScalar(top));
    741     path.lineTo(SkIntToScalar(left + kBorderWidth),
    742                 SkIntToScalar(top + kBorderWidth));
    743     path.lineTo(SkIntToScalar(right - kBorderWidth),
    744                 SkIntToScalar(top + kBorderWidth));
    745     path.lineTo(SkIntToScalar(right), SkIntToScalar(top));
    746     canvas->drawPath(path, dark_paint);
    747 
    748     // Bottom
    749     path.reset();
    750     path.moveTo(SkIntToScalar(left + kBorderWidth),
    751                 SkIntToScalar(bottom - kBorderWidth));
    752     path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom));
    753     path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom));
    754     path.lineTo(SkIntToScalar(right - kBorderWidth),
    755                 SkIntToScalar(bottom - kBorderWidth));
    756     canvas->drawPath(path, light_paint);
    757 
    758     // Left
    759     path.reset();
    760     path.moveTo(SkIntToScalar(left), SkIntToScalar(top));
    761     path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom));
    762     path.lineTo(SkIntToScalar(left + kBorderWidth),
    763                 SkIntToScalar(bottom - kBorderWidth));
    764     path.lineTo(SkIntToScalar(left + kBorderWidth),
    765                 SkIntToScalar(top + kBorderWidth));
    766     canvas->drawPath(path, dark_paint);
    767 
    768     // Right
    769     path.reset();
    770     path.moveTo(SkIntToScalar(right - kBorderWidth),
    771                 SkIntToScalar(top + kBorderWidth));
    772     path.lineTo(SkIntToScalar(right - kBorderWidth), SkIntToScalar(bottom));
    773     path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom));
    774     path.lineTo(SkIntToScalar(right), SkIntToScalar(top));
    775     canvas->drawPath(path, light_paint);
    776   }
    777 }
    778 
    779 void NativeThemeBase::PaintMenuList(
    780     SkCanvas* canvas,
    781     State state,
    782     const gfx::Rect& rect,
    783     const MenuListExtraParams& menu_list) const {
    784   // If a border radius is specified, we let the WebCore paint the background
    785   // and the border of the control.
    786   if (!menu_list.has_border_radius) {
    787     ButtonExtraParams button = { 0 };
    788     button.background_color = menu_list.background_color;
    789     button.has_border = menu_list.has_border;
    790     PaintButton(canvas, state, rect, button);
    791   }
    792 
    793   SkPaint paint;
    794   paint.setColor(SK_ColorBLACK);
    795   paint.setAntiAlias(true);
    796   paint.setStyle(SkPaint::kFill_Style);
    797 
    798   SkPath path;
    799   path.moveTo(menu_list.arrow_x, menu_list.arrow_y - 3);
    800   path.rLineTo(6, 0);
    801   path.rLineTo(-3, 6);
    802   path.close();
    803   canvas->drawPath(path, paint);
    804 }
    805 
    806 void NativeThemeBase::PaintMenuPopupBackground(
    807     SkCanvas* canvas,
    808     const gfx::Size& size,
    809     const MenuBackgroundExtraParams& menu_background) const {
    810   canvas->drawColor(kMenuPopupBackgroundColor, SkXfermode::kSrc_Mode);
    811 }
    812 
    813 void NativeThemeBase::PaintMenuItemBackground(
    814     SkCanvas* canvas,
    815     State state,
    816     const gfx::Rect& rect,
    817     const MenuListExtraParams& menu_list) const {
    818   // By default don't draw anything over the normal background.
    819 }
    820 
    821 void NativeThemeBase::PaintSliderTrack(SkCanvas* canvas,
    822                                        State state,
    823                                        const gfx::Rect& rect,
    824                                        const SliderExtraParams& slider) const {
    825   const int kMidX = rect.x() + rect.width() / 2;
    826   const int kMidY = rect.y() + rect.height() / 2;
    827 
    828   SkPaint paint;
    829   paint.setColor(kSliderTrackBackgroundColor);
    830 
    831   SkRect skrect;
    832   if (slider.vertical) {
    833     skrect.set(std::max(rect.x(), kMidX - 2),
    834                rect.y(),
    835                std::min(rect.right(), kMidX + 2),
    836                rect.bottom());
    837   } else {
    838     skrect.set(rect.x(),
    839                std::max(rect.y(), kMidY - 2),
    840                rect.right(),
    841                std::min(rect.bottom(), kMidY + 2));
    842   }
    843   canvas->drawRect(skrect, paint);
    844 }
    845 
    846 void NativeThemeBase::PaintSliderThumb(SkCanvas* canvas,
    847                                        State state,
    848                                        const gfx::Rect& rect,
    849                                        const SliderExtraParams& slider) const {
    850   const bool hovered = (state == kHovered) || slider.in_drag;
    851   const int kMidX = rect.x() + rect.width() / 2;
    852   const int kMidY = rect.y() + rect.height() / 2;
    853 
    854   SkPaint paint;
    855   paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey);
    856 
    857   SkIRect skrect;
    858   if (slider.vertical)
    859     skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom());
    860   else
    861     skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1);
    862 
    863   canvas->drawIRect(skrect, paint);
    864 
    865   paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey);
    866 
    867   if (slider.vertical)
    868     skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom());
    869   else
    870     skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom());
    871 
    872   canvas->drawIRect(skrect, paint);
    873 
    874   paint.setColor(kSliderThumbBorderDarkGrey);
    875   DrawBox(canvas, rect, paint);
    876 
    877   if (rect.height() > 10 && rect.width() > 10) {
    878     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint);
    879     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint);
    880     DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint);
    881   }
    882 }
    883 
    884 void NativeThemeBase::PaintInnerSpinButton(SkCanvas* canvas,
    885     State state,
    886     const gfx::Rect& rect,
    887     const InnerSpinButtonExtraParams& spin_button) const {
    888   if (spin_button.read_only)
    889     state = kDisabled;
    890 
    891   State north_state = state;
    892   State south_state = state;
    893   if (spin_button.spin_up)
    894     south_state = south_state != kDisabled ? kNormal : kDisabled;
    895   else
    896     north_state = north_state != kDisabled ? kNormal : kDisabled;
    897 
    898   gfx::Rect half = rect;
    899   half.set_height(rect.height() / 2);
    900   PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state);
    901 
    902   half.set_y(rect.y() + rect.height() / 2);
    903   PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state);
    904 }
    905 
    906 void NativeThemeBase::PaintProgressBar(SkCanvas* canvas,
    907     State state,
    908     const gfx::Rect& rect,
    909     const ProgressBarExtraParams& progress_bar) const {
    910   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    911   gfx::ImageSkia* bar_image = rb.GetImageSkiaNamed(IDR_PROGRESS_BAR);
    912   gfx::ImageSkia* left_border_image = rb.GetImageSkiaNamed(
    913       IDR_PROGRESS_BORDER_LEFT);
    914   gfx::ImageSkia* right_border_image = rb.GetImageSkiaNamed(
    915       IDR_PROGRESS_BORDER_RIGHT);
    916 
    917   DCHECK(bar_image->width() > 0);
    918   DCHECK(rect.width() > 0);
    919 
    920   float tile_scale_y = static_cast<float>(rect.height()) / bar_image->height();
    921 
    922   int dest_left_border_width = left_border_image->width();
    923   int dest_right_border_width = right_border_image->width();
    924 
    925   // Since an implicit float -> int conversion will truncate, we want to make
    926   // sure that if a border is desired, it gets at least one pixel.
    927   if (dest_left_border_width > 0) {
    928     dest_left_border_width = dest_left_border_width * tile_scale_y;
    929     dest_left_border_width = std::max(dest_left_border_width, 1);
    930   }
    931   if (dest_right_border_width > 0) {
    932     dest_right_border_width = dest_right_border_width * tile_scale_y;
    933     dest_right_border_width = std::max(dest_right_border_width, 1);
    934   }
    935 
    936   // Since the width of the progress bar may not be evenly divisible by the
    937   // tile size, in order to make it look right we may need to draw some of the
    938   // with a width of 1 pixel smaller than the rest of the tiles.
    939   int new_tile_width = static_cast<int>(bar_image->width() * tile_scale_y);
    940   new_tile_width = std::max(new_tile_width, 1);
    941 
    942   float tile_scale_x = static_cast<float>(new_tile_width) / bar_image->width();
    943   if (rect.width() % new_tile_width == 0) {
    944     DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale_y,
    945         rect.x(), rect.y(),
    946         rect.width(), rect.height());
    947   } else {
    948     int num_tiles = 1 + rect.width() / new_tile_width;
    949     int overshoot = num_tiles * new_tile_width - rect.width();
    950     // Since |overshoot| represents the number of tiles that were too big, draw
    951     // |overshoot| tiles with their width reduced by 1.
    952     int num_big_tiles = num_tiles - overshoot;
    953     int num_small_tiles = overshoot;
    954     int small_width = new_tile_width - 1;
    955     float small_scale_x = static_cast<float>(small_width) / bar_image->width();
    956     float big_scale_x = tile_scale_x;
    957 
    958     gfx::Rect big_rect = rect;
    959     gfx::Rect small_rect = rect;
    960     big_rect.Inset(0, 0, num_small_tiles*small_width, 0);
    961     small_rect.Inset(num_big_tiles*new_tile_width, 0, 0, 0);
    962 
    963     DrawTiledImage(canvas, *bar_image, 0, 0, big_scale_x, tile_scale_y,
    964       big_rect.x(), big_rect.y(), big_rect.width(), big_rect.height());
    965     DrawTiledImage(canvas, *bar_image, 0, 0, small_scale_x, tile_scale_y,
    966       small_rect.x(), small_rect.y(), small_rect.width(), small_rect.height());
    967   }
    968   if (progress_bar.value_rect_width) {
    969     gfx::ImageSkia* value_image = rb.GetImageSkiaNamed(IDR_PROGRESS_VALUE);
    970 
    971     new_tile_width = static_cast<int>(value_image->width() * tile_scale_y);
    972     tile_scale_x = static_cast<float>(new_tile_width) /
    973         value_image->width();
    974 
    975     DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale_y,
    976         progress_bar.value_rect_x,
    977         progress_bar.value_rect_y,
    978         progress_bar.value_rect_width,
    979         progress_bar.value_rect_height);
    980   }
    981 
    982   DrawImageInt(canvas, *left_border_image, 0, 0, left_border_image->width(),
    983       left_border_image->height(), rect.x(), rect.y(), dest_left_border_width,
    984       rect.height());
    985 
    986   int dest_x = rect.right() - dest_right_border_width;
    987   DrawImageInt(canvas, *right_border_image, 0, 0, right_border_image->width(),
    988                right_border_image->height(), dest_x, rect.y(),
    989                dest_right_border_width, rect.height());
    990 }
    991 
    992 bool NativeThemeBase::IntersectsClipRectInt(SkCanvas* canvas,
    993                                             int x, int y, int w, int h) const {
    994   SkRect clip;
    995   return canvas->getClipBounds(&clip) &&
    996       clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
    997                      SkIntToScalar(y + h));
    998 }
    999 
   1000 void NativeThemeBase::DrawImageInt(
   1001     SkCanvas* sk_canvas, const gfx::ImageSkia& image,
   1002     int src_x, int src_y, int src_w, int src_h,
   1003     int dest_x, int dest_y, int dest_w, int dest_h) const {
   1004   // TODO(pkotwicz): Do something better and don't infer device
   1005   // scale factor from canvas scale.
   1006   SkMatrix m = sk_canvas->getTotalMatrix();
   1007   ui::ScaleFactor device_scale_factor = ui::GetScaleFactorFromScale(
   1008       SkScalarAbs(m.getScaleX()));
   1009   scoped_ptr<gfx::Canvas> canvas(gfx::Canvas::CreateCanvasWithoutScaling(
   1010       sk_canvas, device_scale_factor));
   1011   canvas->DrawImageInt(image, src_x, src_y, src_w, src_h,
   1012       dest_x, dest_y, dest_w, dest_h, true);
   1013 }
   1014 
   1015 void NativeThemeBase::DrawTiledImage(SkCanvas* sk_canvas,
   1016     const gfx::ImageSkia& image,
   1017     int src_x, int src_y, float tile_scale_x, float tile_scale_y,
   1018     int dest_x, int dest_y, int w, int h) const {
   1019   // TODO(pkotwicz): Do something better and don't infer device
   1020   // scale factor from canvas scale.
   1021   SkMatrix m = sk_canvas->getTotalMatrix();
   1022   ui::ScaleFactor device_scale_factor = ui::GetScaleFactorFromScale(
   1023       SkScalarAbs(m.getScaleX()));
   1024   scoped_ptr<gfx::Canvas> canvas(gfx::Canvas::CreateCanvasWithoutScaling(
   1025       sk_canvas, device_scale_factor));
   1026   canvas->TileImageInt(image, src_x, src_y, tile_scale_x,
   1027       tile_scale_y, dest_x, dest_y, w, h);
   1028 }
   1029 
   1030 SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
   1031                                              SkScalar saturate_amount,
   1032                                              SkScalar brighten_amount) const {
   1033   SkScalar color[3];
   1034   color[0] = hsv[0];
   1035   color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0);
   1036   color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0);
   1037   return SkHSVToColor(color);
   1038 }
   1039 
   1040 void NativeThemeBase::DrawVertLine(SkCanvas* canvas,
   1041                                    int x,
   1042                                    int y1,
   1043                                    int y2,
   1044                                    const SkPaint& paint) const {
   1045   SkIRect skrect;
   1046   skrect.set(x, y1, x + 1, y2 + 1);
   1047   canvas->drawIRect(skrect, paint);
   1048 }
   1049 
   1050 void NativeThemeBase::DrawHorizLine(SkCanvas* canvas,
   1051                                     int x1,
   1052                                     int x2,
   1053                                     int y,
   1054                                     const SkPaint& paint) const {
   1055   SkIRect skrect;
   1056   skrect.set(x1, y, x2 + 1, y + 1);
   1057   canvas->drawIRect(skrect, paint);
   1058 }
   1059 
   1060 void NativeThemeBase::DrawBox(SkCanvas* canvas,
   1061                               const gfx::Rect& rect,
   1062                               const SkPaint& paint) const {
   1063   const int right = rect.x() + rect.width() - 1;
   1064   const int bottom = rect.y() + rect.height() - 1;
   1065   DrawHorizLine(canvas, rect.x(), right, rect.y(), paint);
   1066   DrawVertLine(canvas, right, rect.y(), bottom, paint);
   1067   DrawHorizLine(canvas, rect.x(), right, bottom, paint);
   1068   DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
   1069 }
   1070 
   1071 SkScalar NativeThemeBase::Clamp(SkScalar value,
   1072                                 SkScalar min,
   1073                                 SkScalar max) const {
   1074   return std::min(std::max(value, min), max);
   1075 }
   1076 
   1077 SkColor NativeThemeBase::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
   1078   // GTK Theme engines have way too much control over the layout of
   1079   // the scrollbar. We might be able to more closely approximate its
   1080   // look-and-feel, if we sent whole images instead of just colors
   1081   // from the browser to the renderer. But even then, some themes
   1082   // would just break.
   1083   //
   1084   // So, instead, we don't even try to 100% replicate the look of
   1085   // the native scrollbar. We render our own version, but we make
   1086   // sure to pick colors that blend in nicely with the system GTK
   1087   // theme. In most cases, we can just sample a couple of pixels
   1088   // from the system scrollbar and use those colors to draw our
   1089   // scrollbar.
   1090   //
   1091   // This works fine for the track color and the overall thumb
   1092   // color. But it fails spectacularly for the outline color used
   1093   // around the thumb piece.  Not all themes have a clearly defined
   1094   // outline. For some of them it is partially transparent, and for
   1095   // others the thickness is very unpredictable.
   1096   //
   1097   // So, instead of trying to approximate the system theme, we
   1098   // instead try to compute a reasonable looking choice based on the
   1099   // known color of the track and the thumb piece. This is difficult
   1100   // when trying to deal both with high- and low-contrast themes,
   1101   // and both with positive and inverted themes.
   1102   //
   1103   // The following code has been tested to look OK with all of the
   1104   // default GTK themes.
   1105   SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2f, 0.28f, 0.5f);
   1106   SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5f);
   1107 
   1108   if (hsv1[2] + hsv2[2] > 1.0)
   1109     diff = -diff;
   1110 
   1111   return SaturateAndBrighten(hsv2, -0.2f, diff);
   1112 }
   1113 
   1114 }  // namespace ui
   1115