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_win.h"
6
7#include <windows.h>
8#include <uxtheme.h>
9#include <vsstyle.h>
10#include <vssym32.h>
11
12#include "base/basictypes.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/win/scoped_gdi_object.h"
16#include "base/win/scoped_hdc.h"
17#include "base/win/scoped_select_object.h"
18#include "base/win/windows_version.h"
19#include "skia/ext/bitmap_platform_device.h"
20#include "skia/ext/platform_canvas.h"
21#include "skia/ext/skia_utils_win.h"
22#include "third_party/skia/include/core/SkCanvas.h"
23#include "third_party/skia/include/core/SkColorPriv.h"
24#include "third_party/skia/include/core/SkShader.h"
25#include "ui/gfx/color_utils.h"
26#include "ui/gfx/gdi_util.h"
27#include "ui/gfx/rect.h"
28#include "ui/gfx/rect_conversions.h"
29#include "ui/gfx/win/dpi.h"
30#include "ui/native_theme/common_theme.h"
31
32// This was removed from Winvers.h but is still used.
33#if !defined(COLOR_MENUHIGHLIGHT)
34#define COLOR_MENUHIGHLIGHT 29
35#endif
36
37namespace {
38
39// TODO: Obtain the correct colors using GetSysColor.
40// Theme colors returned by GetSystemColor().
41const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
42// Dialogs:
43const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
44// FocusableBorder:
45const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
46const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
47// Button:
48const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
49const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
50const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117);
51const SkColor kButtonHoverBackgroundColor = SkColorSetRGB(0xEA, 0xEA, 0xEA);
52// MenuItem:
53const SkColor kEnabledMenuItemForegroundColor = SkColorSetRGB(6, 45, 117);
54const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146);
55const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253);
56const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0);
57// Table:
58const SkColor kTreeSelectionBackgroundUnfocused = SkColorSetRGB(240, 240, 240);
59
60// Windows system color IDs cached and updated by the native theme.
61const int kSystemColors[] = {
62  COLOR_3DFACE,
63  COLOR_BTNTEXT,
64  COLOR_GRAYTEXT,
65  COLOR_HIGHLIGHT,
66  COLOR_HIGHLIGHTTEXT,
67  COLOR_SCROLLBAR,
68  COLOR_WINDOW,
69  COLOR_WINDOWTEXT,
70  COLOR_BTNFACE,
71  COLOR_MENUHIGHLIGHT,
72};
73
74void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
75  // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
76  const SkColor face = color_utils::GetSysSkColor(COLOR_3DFACE);
77  const SkColor highlight = color_utils::GetSysSkColor(COLOR_3DHILIGHT);
78  SkColor buffer[] = { face, highlight, highlight, face };
79  // Confusing bit: we first create a temporary bitmap with our desired pattern,
80  // then copy it to another bitmap.  The temporary bitmap doesn't take
81  // ownership of the pixel data, and so will point to garbage when this
82  // function returns.  The copy will copy the pixel data into a place owned by
83  // the bitmap, which is in turn owned by the shader, etc., so it will live
84  // until we're done using it.
85  SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
86  SkBitmap temp_bitmap;
87  temp_bitmap.installPixels(info, buffer, info.minRowBytes());
88  SkBitmap bitmap;
89  temp_bitmap.copyTo(&bitmap);
90
91  // Align the pattern with the upper corner of |align_rect|.
92  SkMatrix local_matrix;
93  local_matrix.setTranslate(SkIntToScalar(align_rect.left),
94                            SkIntToScalar(align_rect.top));
95  skia::RefPtr<SkShader> shader =
96      skia::AdoptRef(SkShader::CreateBitmapShader(bitmap,
97                                                  SkShader::kRepeat_TileMode,
98                                                  SkShader::kRepeat_TileMode,
99                                                  &local_matrix));
100  paint->setShader(shader.get());
101}
102
103//    <-a->
104// [  *****             ]
105//  ____ |              |
106//  <-a-> <------b----->
107// a: object_width
108// b: frame_width
109// *: animating object
110//
111// - the animation goes from "[" to "]" repeatedly.
112// - the animation offset is at first "|"
113//
114int ComputeAnimationProgress(int frame_width,
115                             int object_width,
116                             int pixels_per_second,
117                             double animated_seconds) {
118  int animation_width = frame_width + object_width;
119  double interval = static_cast<double>(animation_width) / pixels_per_second;
120  double ratio = fmod(animated_seconds, interval) / interval;
121  return static_cast<int>(animation_width * ratio) - object_width;
122}
123
124RECT InsetRect(const RECT* rect, int size) {
125  gfx::Rect result(*rect);
126  result.Inset(size, size);
127  return result.ToRECT();
128}
129
130}  // namespace
131
132namespace ui {
133
134bool NativeThemeWin::IsThemingActive() const {
135  return is_theme_active_ && is_theme_active_();
136}
137
138bool NativeThemeWin::IsUsingHighContrastTheme() const {
139  if (is_using_high_contrast_valid_)
140    return is_using_high_contrast_;
141  HIGHCONTRAST result;
142  result.cbSize = sizeof(HIGHCONTRAST);
143  is_using_high_contrast_ =
144      SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
145      (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
146  is_using_high_contrast_valid_ = true;
147  return is_using_high_contrast_;
148}
149
150HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
151                                      int part_id,
152                                      int state_id,
153                                      int prop_id,
154                                      SkColor* color) const {
155  HANDLE handle = GetThemeHandle(theme);
156  if (!handle || !get_theme_color_)
157    return E_NOTIMPL;
158  COLORREF color_ref;
159  if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) != S_OK)
160    return E_NOTIMPL;
161  *color = skia::COLORREFToSkColor(color_ref);
162  return S_OK;
163}
164
165SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
166                                                 int part_id,
167                                                 int state_id,
168                                                 int prop_id,
169                                                 int default_sys_color) const {
170  SkColor color;
171  return (GetThemeColor(theme, part_id, state_id, prop_id, &color) == S_OK) ?
172      color : color_utils::GetSysSkColor(default_sys_color);
173}
174
175gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
176  // For simplicity use the wildcard state==0, part==0, since it works
177  // for the cases we currently depend on.
178  int border;
179  return (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK) ?
180      gfx::Size(border, border) :
181      gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
182}
183
184void NativeThemeWin::DisableTheming() const {
185  if (set_theme_properties_)
186    set_theme_properties_(0);
187}
188
189void NativeThemeWin::CloseHandles() const {
190  if (!close_theme_)
191    return;
192
193  for (int i = 0; i < LAST; ++i) {
194    if (theme_handles_[i]) {
195      close_theme_(theme_handles_[i]);
196      theme_handles_[i] = NULL;
197    }
198  }
199}
200
201bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
202  return !theme_dll_ || !GetThemeHandle(name);
203}
204
205// static
206NativeThemeWin* NativeThemeWin::instance() {
207  CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
208  return &s_native_theme;
209}
210
211gfx::Size NativeThemeWin::GetPartSize(Part part,
212                                      State state,
213                                      const ExtraParams& extra) const {
214  gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
215  if (!part_size.IsEmpty())
216    return part_size;
217
218  // The GetThemePartSize call below returns the default size without
219  // accounting for user customization (crbug/218291).
220  switch (part) {
221    case kScrollbarDownArrow:
222    case kScrollbarLeftArrow:
223    case kScrollbarRightArrow:
224    case kScrollbarUpArrow:
225    case kScrollbarHorizontalThumb:
226    case kScrollbarVerticalThumb:
227    case kScrollbarHorizontalTrack:
228    case kScrollbarVerticalTrack: {
229      int size = gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL);
230      if (size == 0)
231        size = 17;
232      return gfx::Size(size, size);
233    }
234    default:
235      break;
236  }
237
238  int part_id = GetWindowsPart(part, state, extra);
239  int state_id = GetWindowsState(part, state, extra);
240
241  base::win::ScopedGetDC screen_dc(NULL);
242  SIZE size;
243  if (SUCCEEDED(GetThemePartSize(GetThemeName(part), screen_dc, part_id,
244                                 state_id, NULL, TS_TRUE, &size)))
245    return gfx::Size(size.cx, size.cy);
246
247  // TODO(rogerta): For now, we need to support radio buttons and checkboxes
248  // when theming is not enabled.  Support for other parts can be added
249  // if/when needed.
250  return (part == kCheckbox || part == kRadio) ?
251      gfx::Size(13, 13) : gfx::Size();
252}
253
254void NativeThemeWin::Paint(SkCanvas* canvas,
255                           Part part,
256                           State state,
257                           const gfx::Rect& rect,
258                           const ExtraParams& extra) const {
259  if (rect.IsEmpty())
260    return;
261
262  switch (part) {
263    case kComboboxArrow:
264      CommonThemePaintComboboxArrow(canvas, rect);
265      return;
266    case kMenuPopupGutter:
267      CommonThemePaintMenuGutter(canvas, rect);
268      return;
269    case kMenuPopupSeparator:
270      CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
271      return;
272    case kMenuPopupBackground:
273      CommonThemePaintMenuBackground(canvas, rect);
274      return;
275    case kMenuItemBackground:
276      CommonThemePaintMenuItemBackground(canvas, state, rect);
277      return;
278    default:
279      break;
280  }
281
282  bool needs_paint_indirect = false;
283  if (!skia::SupportsPlatformPaint(canvas)) {
284    // This block will only get hit with --enable-accelerated-drawing flag.
285    needs_paint_indirect = true;
286  } else {
287    // Scrollbar components on Windows Classic theme (on all Windows versions)
288    // have particularly problematic alpha values, so always draw them
289    // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
290    // theme (available only on Windows XP) also need their alpha values
291    // fixed.
292    switch (part) {
293      case kScrollbarDownArrow:
294      case kScrollbarUpArrow:
295      case kScrollbarLeftArrow:
296      case kScrollbarRightArrow:
297        needs_paint_indirect = !GetThemeHandle(SCROLLBAR);
298        break;
299      case kScrollbarHorizontalThumb:
300      case kScrollbarVerticalThumb:
301      case kScrollbarHorizontalGripper:
302      case kScrollbarVerticalGripper:
303        needs_paint_indirect = !GetThemeHandle(SCROLLBAR) ||
304            base::win::GetVersion() == base::win::VERSION_XP;
305        break;
306      default:
307        break;
308    }
309  }
310
311  if (needs_paint_indirect)
312    PaintIndirect(canvas, part, state, rect, extra);
313  else
314    PaintDirect(canvas, part, state, rect, extra);
315}
316
317NativeThemeWin::NativeThemeWin()
318    : theme_dll_(LoadLibrary(L"uxtheme.dll")),
319      draw_theme_(NULL),
320      draw_theme_ex_(NULL),
321      get_theme_color_(NULL),
322      get_theme_content_rect_(NULL),
323      get_theme_part_size_(NULL),
324      open_theme_(NULL),
325      close_theme_(NULL),
326      set_theme_properties_(NULL),
327      is_theme_active_(NULL),
328      get_theme_int_(NULL),
329      color_change_listener_(this),
330      is_using_high_contrast_(false),
331      is_using_high_contrast_valid_(false) {
332  if (theme_dll_) {
333    draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
334        GetProcAddress(theme_dll_, "DrawThemeBackground"));
335    draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
336        GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
337    get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
338        GetProcAddress(theme_dll_, "GetThemeColor"));
339    get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
340        GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
341    get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
342        GetProcAddress(theme_dll_, "GetThemePartSize"));
343    open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
344        GetProcAddress(theme_dll_, "OpenThemeData"));
345    close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
346        GetProcAddress(theme_dll_, "CloseThemeData"));
347    set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
348        GetProcAddress(theme_dll_, "SetThemeAppProperties"));
349    is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
350        GetProcAddress(theme_dll_, "IsThemeActive"));
351    get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
352        GetProcAddress(theme_dll_, "GetThemeInt"));
353  }
354  memset(theme_handles_, 0, sizeof(theme_handles_));
355
356  // Initialize the cached system colors.
357  UpdateSystemColors();
358}
359
360NativeThemeWin::~NativeThemeWin() {
361  if (theme_dll_) {
362    // todo (cpu): fix this soon.  Making a call to CloseHandles() here breaks
363    // certain tests and the reliability bots.
364    // CloseHandles();
365    FreeLibrary(theme_dll_);
366  }
367}
368
369void NativeThemeWin::OnSysColorChange() {
370  UpdateSystemColors();
371  is_using_high_contrast_valid_ = false;
372  NotifyObservers();
373}
374
375void NativeThemeWin::UpdateSystemColors() {
376  for (int i = 0; i < arraysize(kSystemColors); ++i) {
377    system_colors_[kSystemColors[i]] =
378        color_utils::GetSysSkColor(kSystemColors[i]);
379  }
380}
381
382void NativeThemeWin::PaintDirect(SkCanvas* canvas,
383                                 Part part,
384                                 State state,
385                                 const gfx::Rect& rect,
386                                 const ExtraParams& extra) const {
387  skia::ScopedPlatformPaint scoped_platform_paint(canvas);
388  HDC hdc = scoped_platform_paint.GetPlatformSurface();
389
390  switch (part) {
391    case kCheckbox:
392      PaintCheckbox(hdc, part, state, rect, extra.button);
393      return;
394    case kInnerSpinButton:
395      PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
396      return;
397    case kMenuList:
398      PaintMenuList(hdc, state, rect, extra.menu_list);
399      return;
400    case kMenuCheck:
401      PaintMenuCheck(hdc, state, rect, extra.menu_check);
402      return;
403    case kMenuCheckBackground:
404      PaintMenuCheckBackground(hdc, state, rect);
405      return;
406    case kMenuPopupArrow:
407      PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
408      return;
409    case kMenuPopupBackground:
410      PaintMenuBackground(hdc, rect);
411      return;
412    case kMenuPopupGutter:
413      PaintMenuGutter(hdc, rect);
414      return;
415    case kMenuPopupSeparator:
416      PaintMenuSeparator(hdc, rect, extra.menu_separator);
417      return;
418    case kMenuItemBackground:
419      PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
420      return;
421    case kProgressBar:
422      PaintProgressBar(hdc, rect, extra.progress_bar);
423      return;
424    case kPushButton:
425      PaintPushButton(hdc, part, state, rect, extra.button);
426      return;
427    case kRadio:
428      PaintRadioButton(hdc, part, state, rect, extra.button);
429      return;
430    case kScrollbarDownArrow:
431    case kScrollbarUpArrow:
432    case kScrollbarLeftArrow:
433    case kScrollbarRightArrow:
434      PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
435      return;
436    case kScrollbarHorizontalThumb:
437    case kScrollbarVerticalThumb:
438    case kScrollbarHorizontalGripper:
439    case kScrollbarVerticalGripper:
440      PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
441      return;
442    case kScrollbarHorizontalTrack:
443    case kScrollbarVerticalTrack:
444      PaintScrollbarTrack(canvas, hdc, part, state, rect,
445                          extra.scrollbar_track);
446      return;
447    case kScrollbarCorner:
448      canvas->drawColor(SK_ColorWHITE, SkXfermode::kSrc_Mode);
449      return;
450    case kTabPanelBackground:
451      PaintTabPanelBackground(hdc, rect);
452      return;
453    case kTextField:
454      PaintTextField(hdc, part, state, rect, extra.text_field);
455      return;
456    case kTrackbarThumb:
457    case kTrackbarTrack:
458      PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
459      return;
460    case kWindowResizeGripper:
461      PaintWindowResizeGripper(hdc, rect);
462      return;
463    case kComboboxArrow:
464    case kSliderTrack:
465    case kSliderThumb:
466    case kMaxPart:
467      NOTREACHED();
468  }
469}
470
471SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
472  SkColor color;
473  if (CommonThemeGetSystemColor(color_id, &color))
474    return color;
475
476  switch (color_id) {
477    // Windows
478    case kColorId_WindowBackground:
479      return system_colors_[COLOR_WINDOW];
480
481    // Dialogs
482    case kColorId_DialogBackground:
483      return gfx::IsInvertedColorScheme() ?
484          color_utils::InvertColor(kDialogBackgroundColor) :
485          kDialogBackgroundColor;
486
487    // FocusableBorder
488    case kColorId_FocusedBorderColor:
489      return kFocusedBorderColor;
490    case kColorId_UnfocusedBorderColor:
491      return kUnfocusedBorderColor;
492
493    // Button
494    case kColorId_ButtonBackgroundColor:
495      return kButtonBackgroundColor;
496    case kColorId_ButtonEnabledColor:
497      return system_colors_[COLOR_BTNTEXT];
498    case kColorId_ButtonDisabledColor:
499      return system_colors_[COLOR_GRAYTEXT];
500    case kColorId_ButtonHighlightColor:
501      return kButtonHighlightColor;
502    case kColorId_ButtonHoverColor:
503      return kButtonHoverColor;
504    case kColorId_ButtonHoverBackgroundColor:
505      return kButtonHoverBackgroundColor;
506    case kColorId_BlueButtonEnabledColor:
507    case kColorId_BlueButtonDisabledColor:
508    case kColorId_BlueButtonPressedColor:
509    case kColorId_BlueButtonHoverColor:
510      NOTREACHED();
511      return kInvalidColorIdColor;
512
513    // MenuItem
514    case kColorId_EnabledMenuItemForegroundColor:
515      return kEnabledMenuItemForegroundColor;
516    case kColorId_DisabledMenuItemForegroundColor:
517      return kDisabledMenuItemForegroundColor;
518    case kColorId_DisabledEmphasizedMenuItemForegroundColor:
519      return SK_ColorBLACK;
520    case kColorId_FocusedMenuItemBackgroundColor:
521      return kFocusedMenuItemBackgroundColor;
522    case kColorId_MenuSeparatorColor:
523      return kMenuSeparatorColor;
524    case kColorId_SelectedMenuItemForegroundColor:
525    case kColorId_HoverMenuItemBackgroundColor:
526    case kColorId_MenuBackgroundColor:
527    case kColorId_MenuBorderColor:
528      NOTREACHED();
529      return kInvalidColorIdColor;
530
531    // MenuButton
532    case kColorId_EnabledMenuButtonBorderColor:
533    case kColorId_FocusedMenuButtonBorderColor:
534    case kColorId_HoverMenuButtonBorderColor:
535      NOTREACHED();
536      return kInvalidColorIdColor;
537
538    // Label
539    case kColorId_LabelEnabledColor:
540      return system_colors_[COLOR_BTNTEXT];
541    case kColorId_LabelDisabledColor:
542      return system_colors_[COLOR_GRAYTEXT];
543    case kColorId_LabelBackgroundColor:
544      return system_colors_[COLOR_WINDOW];
545
546    // Textfield
547    case kColorId_TextfieldDefaultColor:
548      return system_colors_[COLOR_WINDOWTEXT];
549    case kColorId_TextfieldDefaultBackground:
550      return system_colors_[COLOR_WINDOW];
551    case kColorId_TextfieldReadOnlyColor:
552      return system_colors_[COLOR_GRAYTEXT];
553    case kColorId_TextfieldReadOnlyBackground:
554      return system_colors_[COLOR_3DFACE];
555    case kColorId_TextfieldSelectionColor:
556      return system_colors_[COLOR_HIGHLIGHTTEXT];
557    case kColorId_TextfieldSelectionBackgroundFocused:
558      return system_colors_[COLOR_HIGHLIGHT];
559
560    // Tooltip
561    case kColorId_TooltipBackground:
562    case kColorId_TooltipText:
563      NOTREACHED();
564      return kInvalidColorIdColor;
565
566    // Tree
567    // NOTE: these aren't right for all themes, but as close as I could get.
568    case kColorId_TreeBackground:
569      return system_colors_[COLOR_WINDOW];
570    case kColorId_TreeText:
571      return system_colors_[COLOR_WINDOWTEXT];
572    case kColorId_TreeSelectedText:
573      return system_colors_[COLOR_HIGHLIGHTTEXT];
574    case kColorId_TreeSelectedTextUnfocused:
575      return system_colors_[COLOR_BTNTEXT];
576    case kColorId_TreeSelectionBackgroundFocused:
577      return system_colors_[COLOR_HIGHLIGHT];
578    case kColorId_TreeSelectionBackgroundUnfocused:
579      return system_colors_[IsUsingHighContrastTheme() ?
580                              COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
581    case kColorId_TreeArrow:
582      return system_colors_[COLOR_WINDOWTEXT];
583
584    // Table
585    case kColorId_TableBackground:
586      return system_colors_[COLOR_WINDOW];
587    case kColorId_TableText:
588      return system_colors_[COLOR_WINDOWTEXT];
589    case kColorId_TableSelectedText:
590      return system_colors_[COLOR_HIGHLIGHTTEXT];
591    case kColorId_TableSelectedTextUnfocused:
592      return system_colors_[COLOR_BTNTEXT];
593    case kColorId_TableSelectionBackgroundFocused:
594      return system_colors_[COLOR_HIGHLIGHT];
595    case kColorId_TableSelectionBackgroundUnfocused:
596      return system_colors_[IsUsingHighContrastTheme() ?
597                              COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
598    case kColorId_TableGroupingIndicatorColor:
599      return system_colors_[COLOR_GRAYTEXT];
600
601    // Results Tables
602    case kColorId_ResultsTableNormalBackground:
603      return system_colors_[COLOR_WINDOW];
604    case kColorId_ResultsTableHoveredBackground:
605      return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHT],
606                                     system_colors_[COLOR_WINDOW], 0x40);
607    case kColorId_ResultsTableSelectedBackground:
608      return system_colors_[COLOR_HIGHLIGHT];
609    case kColorId_ResultsTableNormalText:
610    case kColorId_ResultsTableHoveredText:
611      return system_colors_[COLOR_WINDOWTEXT];
612    case kColorId_ResultsTableSelectedText:
613      return system_colors_[COLOR_HIGHLIGHTTEXT];
614    case kColorId_ResultsTableNormalDimmedText:
615      return color_utils::AlphaBlend(system_colors_[COLOR_WINDOWTEXT],
616                                     system_colors_[COLOR_WINDOW], 0x80);
617    case kColorId_ResultsTableHoveredDimmedText:
618      return color_utils::AlphaBlend(
619          system_colors_[COLOR_WINDOWTEXT],
620          GetSystemColor(kColorId_ResultsTableHoveredBackground), 0x80);
621    case kColorId_ResultsTableSelectedDimmedText:
622      return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHTTEXT],
623                                     system_colors_[COLOR_HIGHLIGHT], 0x80);
624    case kColorId_ResultsTableNormalUrl:
625      return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
626                                           system_colors_[COLOR_WINDOW]);
627    case kColorId_ResultsTableHoveredUrl:
628      return color_utils::GetReadableColor(
629          SkColorSetRGB(0, 128, 0),
630          GetSystemColor(kColorId_ResultsTableHoveredBackground));
631    case kColorId_ResultsTableSelectedUrl:
632      return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
633                                           system_colors_[COLOR_HIGHLIGHT]);
634    case kColorId_ResultsTableNormalDivider:
635      return color_utils::AlphaBlend(system_colors_[COLOR_WINDOWTEXT],
636                                     system_colors_[COLOR_WINDOW], 0x34);
637    case kColorId_ResultsTableHoveredDivider:
638      return color_utils::AlphaBlend(
639          system_colors_[COLOR_WINDOWTEXT],
640          GetSystemColor(kColorId_ResultsTableHoveredBackground), 0x34);
641    case kColorId_ResultsTableSelectedDivider:
642      return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHTTEXT],
643                                     system_colors_[COLOR_HIGHLIGHT], 0x34);
644  }
645  NOTREACHED();
646  return kInvalidColorIdColor;
647}
648
649void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
650                                   Part part,
651                                   State state,
652                                   const gfx::Rect& rect,
653                                   const ExtraParams& extra) const {
654  // TODO(asvitkine): This path is pretty inefficient - for each paint operation
655  //                  it creates a new offscreen bitmap Skia canvas. This can
656  //                  be sped up by doing it only once per part/state and
657  //                  keeping a cache of the resulting bitmaps.
658
659  // Create an offscreen canvas that is backed by an HDC.
660  skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
661      skia::BitmapPlatformDevice::Create(
662          rect.width(), rect.height(), false, NULL));
663  DCHECK(device);
664  SkCanvas offscreen_canvas(device.get());
665  DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
666
667  // Some of the Windows theme drawing operations do not write correct alpha
668  // values for fully-opaque pixels; instead the pixels get alpha 0. This is
669  // especially a problem on Windows XP or when using the Classic theme.
670  //
671  // To work-around this, mark all pixels with a placeholder value, to detect
672  // which pixels get touched by the paint operation. After paint, set any
673  // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
674  const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
675  offscreen_canvas.clear(placeholder);
676
677  // Offset destination rects to have origin (0,0).
678  gfx::Rect adjusted_rect(rect.size());
679  ExtraParams adjusted_extra(extra);
680  switch (part) {
681    case kProgressBar:
682      adjusted_extra.progress_bar.value_rect_x = 0;
683      adjusted_extra.progress_bar.value_rect_y = 0;
684      break;
685    case kScrollbarHorizontalTrack:
686    case kScrollbarVerticalTrack:
687      adjusted_extra.scrollbar_track.track_x = 0;
688      adjusted_extra.scrollbar_track.track_y = 0;
689      break;
690    default:
691      break;
692  }
693  // Draw the theme controls using existing HDC-drawing code.
694  PaintDirect(&offscreen_canvas, part, state, adjusted_rect, adjusted_extra);
695
696  // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
697  // necessary to have when drawing to a SkPicture.
698  const SkBitmap& hdc_bitmap =
699      offscreen_canvas.getDevice()->accessBitmap(false);
700  SkBitmap bitmap;
701  hdc_bitmap.copyTo(&bitmap, kN32_SkColorType);
702
703  // Post-process the pixels to fix up the alpha values (see big comment above).
704  const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
705  const int pixel_count = rect.width() * rect.height();
706  SkPMColor* pixels = bitmap.getAddr32(0, 0);
707  for (int i = 0; i < pixel_count; i++) {
708    if (pixels[i] == placeholder_value) {
709      // Pixel wasn't touched - make it fully transparent.
710      pixels[i] = SkPackARGB32(0, 0, 0, 0);
711    } else if (SkGetPackedA32(pixels[i]) == 0) {
712      // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
713      pixels[i] = SkPackARGB32(0xFF,
714                               SkGetPackedR32(pixels[i]),
715                               SkGetPackedG32(pixels[i]),
716                               SkGetPackedB32(pixels[i]));
717    }
718  }
719
720  // Draw the offscreen bitmap to the destination canvas.
721  canvas->drawBitmap(bitmap, rect.x(), rect.y());
722}
723
724HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
725                                         HDC hdc,
726                                         int part_id,
727                                         int state_id,
728                                         RECT* rect,
729                                         int ts,
730                                         SIZE* size) const {
731  HANDLE handle = GetThemeHandle(theme_name);
732  return (handle && get_theme_part_size_) ?
733      get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size) :
734      E_NOTIMPL;
735}
736
737HRESULT NativeThemeWin::PaintButton(HDC hdc,
738                                    State state,
739                                    const ButtonExtraParams& extra,
740                                    int part_id,
741                                    int state_id,
742                                    RECT* rect) const {
743  HANDLE handle = GetThemeHandle(BUTTON);
744  if (handle && draw_theme_)
745    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
746
747  // Adjust classic_state based on part, state, and extras.
748  int classic_state = extra.classic_state;
749  switch (part_id) {
750    case BP_CHECKBOX:
751      classic_state |= DFCS_BUTTONCHECK;
752      break;
753    case BP_RADIOBUTTON:
754      classic_state |= DFCS_BUTTONRADIO;
755      break;
756    case BP_PUSHBUTTON:
757      classic_state |= DFCS_BUTTONPUSH;
758      break;
759    default:
760      NOTREACHED();
761      break;
762  }
763
764  switch (state) {
765    case kDisabled:
766      classic_state |= DFCS_INACTIVE;
767      break;
768    case kHovered:
769    case kNormal:
770      break;
771    case kPressed:
772      classic_state |= DFCS_PUSHED;
773      break;
774    case kNumStates:
775      NOTREACHED();
776      break;
777  }
778
779  if (extra.checked)
780    classic_state |= DFCS_CHECKED;
781
782  // Draw it manually.
783  // All pressed states have both low bits set, and no other states do.
784  const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
785  const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
786  if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
787    // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
788    // button itself is shrunk by 1 pixel.
789    HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
790    if (brush) {
791      FrameRect(hdc, rect, brush);
792      InflateRect(rect, -1, -1);
793    }
794  }
795  DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
796
797  // Draw the focus rectangle (the dotted line box) only on buttons.  For radio
798  // and checkboxes, we let webkit draw the focus rectangle (orange glow).
799  if ((BP_PUSHBUTTON == part_id) && focused) {
800    // The focus rect is inside the button.  The exact number of pixels depends
801    // on whether we're in classic mode or using uxtheme.
802    if (handle && get_theme_content_rect_) {
803      get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
804    } else {
805      InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
806                  -GetSystemMetrics(SM_CYEDGE));
807    }
808    DrawFocusRect(hdc, rect);
809  }
810
811  // Classic theme doesn't support indeterminate checkboxes.  We draw
812  // a recangle inside a checkbox like IE10 does.
813  if (part_id == BP_CHECKBOX && extra.indeterminate) {
814    RECT inner_rect = *rect;
815    // "4 / 13" is same as IE10 in classic theme.
816    int padding = (inner_rect.right - inner_rect.left) * 4 / 13;
817    InflateRect(&inner_rect, -padding, -padding);
818    int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
819    FillRect(hdc, &inner_rect, GetSysColorBrush(color_index));
820  }
821
822  return S_OK;
823}
824
825HRESULT NativeThemeWin::PaintMenuSeparator(
826    HDC hdc,
827    const gfx::Rect& rect,
828    const MenuSeparatorExtraParams& extra) const {
829  RECT rect_win = rect.ToRECT();
830
831  HANDLE handle = GetThemeHandle(MENU);
832  if (handle && draw_theme_) {
833    // Delta is needed for non-classic to move separator up slightly.
834    --rect_win.top;
835    --rect_win.bottom;
836    return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
837                       NULL);
838  }
839
840  DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
841  return S_OK;
842}
843
844HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
845                                        const gfx::Rect& rect) const {
846  RECT rect_win = rect.ToRECT();
847  HANDLE handle = GetThemeHandle(MENU);
848  return (handle && draw_theme_) ?
849      draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win, NULL) :
850      E_NOTIMPL;
851}
852
853HRESULT NativeThemeWin::PaintMenuArrow(
854    HDC hdc,
855    State state,
856    const gfx::Rect& rect,
857    const MenuArrowExtraParams& extra) const {
858  int state_id = MSM_NORMAL;
859  if (state == kDisabled)
860    state_id = MSM_DISABLED;
861
862  HANDLE handle = GetThemeHandle(MENU);
863  RECT rect_win = rect.ToRECT();
864  if (handle && draw_theme_) {
865    if (extra.pointing_right) {
866      return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
867                         NULL);
868    }
869    // There is no way to tell the uxtheme API to draw a left pointing arrow; it
870    // doesn't have a flag equivalent to DFCS_MENUARROWRIGHT.  But they are
871    // needed for RTL locales on Vista.  So use a memory DC and mirror the
872    // region with GDI's StretchBlt.
873    gfx::Rect r(rect);
874    base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
875    base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
876                                                              r.height()));
877    base::win::ScopedSelectObject select_bitmap(mem_dc.Get(), mem_bitmap);
878    // Copy and horizontally mirror the background from hdc into mem_dc. Use
879    // a negative-width source rect, starting at the rightmost pixel.
880    StretchBlt(mem_dc.Get(), 0, 0, r.width(), r.height(),
881               hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
882    // Draw the arrow.
883    RECT theme_rect = {0, 0, r.width(), r.height()};
884    HRESULT result = draw_theme_(handle, mem_dc.Get(), MENU_POPUPSUBMENU,
885                                  state_id, &theme_rect, NULL);
886    // Copy and mirror the result back into mem_dc.
887    StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
888               mem_dc.Get(), r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
889    return result;
890  }
891
892  // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
893  // left pointing arrow. This makes the following statement counterintuitive.
894  UINT pfc_state = extra.pointing_right ? DFCS_MENUARROW : DFCS_MENUARROWRIGHT;
895  return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
896                           state);
897}
898
899HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
900                                            const gfx::Rect& rect) const {
901  HANDLE handle = GetThemeHandle(MENU);
902  RECT rect_win = rect.ToRECT();
903  if (handle && draw_theme_) {
904    HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
905                                 &rect_win, NULL);
906    FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
907    return result;
908  }
909
910  FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
911  DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
912  return S_OK;
913}
914
915HRESULT NativeThemeWin::PaintMenuCheck(
916    HDC hdc,
917    State state,
918    const gfx::Rect& rect,
919    const MenuCheckExtraParams& extra) const {
920  HANDLE handle = GetThemeHandle(MENU);
921  if (handle && draw_theme_) {
922    const int state_id = extra.is_radio ?
923        ((state == kDisabled) ? MC_BULLETDISABLED : MC_BULLETNORMAL) :
924        ((state == kDisabled) ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL);
925    RECT rect_win = rect.ToRECT();
926    return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
927  }
928
929  return PaintFrameControl(hdc, rect, DFC_MENU,
930                           extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
931                           extra.is_selected, state);
932}
933
934HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
935                                                 State state,
936                                                 const gfx::Rect& rect) const {
937  HANDLE handle = GetThemeHandle(MENU);
938  if (!handle || !draw_theme_)
939    return S_OK;  // Nothing to do for background.
940
941  int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
942  RECT rect_win = rect.ToRECT();
943  return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
944                     &rect_win, NULL);
945}
946
947HRESULT NativeThemeWin::PaintMenuItemBackground(
948    HDC hdc,
949    State state,
950    const gfx::Rect& rect,
951    const MenuItemExtraParams& extra) const {
952  HANDLE handle = GetThemeHandle(MENU);
953  RECT rect_win = rect.ToRECT();
954  int state_id = MPI_NORMAL;
955  switch (state) {
956    case kDisabled:
957      state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
958      break;
959    case kHovered:
960      state_id = MPI_HOT;
961      break;
962    case kNormal:
963      break;
964    case kPressed:
965    case kNumStates:
966      NOTREACHED();
967      break;
968  }
969
970  if (handle && draw_theme_)
971    return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
972
973  if (extra.is_selected)
974    FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
975  return S_OK;
976}
977
978HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
979                                        Part part,
980                                        State state,
981                                        const gfx::Rect& rect,
982                                        const ButtonExtraParams& extra) const {
983  int state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
984  switch (state) {
985    case kDisabled:
986      state_id = PBS_DISABLED;
987      break;
988    case kHovered:
989      state_id = PBS_HOT;
990      break;
991    case kNormal:
992      break;
993    case kPressed:
994      state_id = PBS_PRESSED;
995      break;
996    case kNumStates:
997      NOTREACHED();
998      break;
999  }
1000
1001  RECT rect_win = rect.ToRECT();
1002  return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
1003}
1004
1005HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
1006                                         Part part,
1007                                         State state,
1008                                         const gfx::Rect& rect,
1009                                         const ButtonExtraParams& extra) const {
1010  int state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
1011  switch (state) {
1012    case kDisabled:
1013      state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
1014      break;
1015    case kHovered:
1016      state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
1017      break;
1018    case kNormal:
1019      break;
1020    case kPressed:
1021      state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
1022      break;
1023    case kNumStates:
1024      NOTREACHED();
1025      break;
1026  }
1027
1028  RECT rect_win = rect.ToRECT();
1029  return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
1030}
1031
1032HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
1033                                      Part part,
1034                                      State state,
1035                                      const gfx::Rect& rect,
1036                                      const ButtonExtraParams& extra) const {
1037  int state_id = extra.checked ?
1038      CBS_CHECKEDNORMAL :
1039      (extra.indeterminate ? CBS_MIXEDNORMAL : CBS_UNCHECKEDNORMAL);
1040  switch (state) {
1041    case kDisabled:
1042      state_id = extra.checked ?
1043          CBS_CHECKEDDISABLED :
1044          (extra.indeterminate ? CBS_MIXEDDISABLED : CBS_UNCHECKEDDISABLED);
1045      break;
1046    case kHovered:
1047      state_id = extra.checked ?
1048          CBS_CHECKEDHOT :
1049          (extra.indeterminate ? CBS_MIXEDHOT : CBS_UNCHECKEDHOT);
1050      break;
1051    case kNormal:
1052      break;
1053    case kPressed:
1054      state_id = extra.checked ?
1055          CBS_CHECKEDPRESSED :
1056          (extra.indeterminate ? CBS_MIXEDPRESSED : CBS_UNCHECKEDPRESSED);
1057      break;
1058    case kNumStates:
1059      NOTREACHED();
1060      break;
1061  }
1062
1063  RECT rect_win = rect.ToRECT();
1064  return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
1065}
1066
1067HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
1068                                      State state,
1069                                      const gfx::Rect& rect,
1070                                      const MenuListExtraParams& extra) const {
1071  HANDLE handle = GetThemeHandle(MENULIST);
1072  RECT rect_win = rect.ToRECT();
1073  int state_id = CBXS_NORMAL;
1074  switch (state) {
1075    case kDisabled:
1076      state_id = CBXS_DISABLED;
1077      break;
1078    case kHovered:
1079      state_id = CBXS_HOT;
1080      break;
1081    case kNormal:
1082      break;
1083    case kPressed:
1084      state_id = CBXS_PRESSED;
1085      break;
1086    case kNumStates:
1087      NOTREACHED();
1088      break;
1089  }
1090
1091  if (handle && draw_theme_)
1092    return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
1093                       NULL);
1094
1095  // Draw it manually.
1096  DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
1097                   DFCS_SCROLLCOMBOBOX | extra.classic_state);
1098  return S_OK;
1099}
1100
1101HRESULT NativeThemeWin::PaintScrollbarArrow(
1102    HDC hdc,
1103    Part part,
1104    State state,
1105    const gfx::Rect& rect,
1106    const ScrollbarArrowExtraParams& extra) const {
1107  static const int state_id_matrix[4][kNumStates] = {
1108      ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
1109      ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
1110      ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
1111      ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
1112  };
1113  HANDLE handle = GetThemeHandle(SCROLLBAR);
1114  RECT rect_win = rect.ToRECT();
1115  if (handle && draw_theme_) {
1116    int index = part - kScrollbarDownArrow;
1117    DCHECK_GE(index, 0);
1118    DCHECK_LT(static_cast<size_t>(index), arraysize(state_id_matrix));
1119    int state_id = state_id_matrix[index][state];
1120
1121    // Hovering means that the cursor is over the scroolbar, but not over the
1122    // specific arrow itself.  We don't want to show it "hot" mode, but only
1123    // in "hover" mode.
1124    if (state == kHovered && extra.is_hovering) {
1125      switch (part) {
1126        case kScrollbarDownArrow:
1127          state_id = ABS_DOWNHOVER;
1128          break;
1129        case kScrollbarLeftArrow:
1130          state_id = ABS_LEFTHOVER;
1131          break;
1132        case kScrollbarRightArrow:
1133          state_id = ABS_RIGHTHOVER;
1134          break;
1135        case kScrollbarUpArrow:
1136          state_id = ABS_UPHOVER;
1137          break;
1138        default:
1139          NOTREACHED();
1140          break;
1141      }
1142    }
1143    return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
1144  }
1145
1146  int classic_state = DFCS_SCROLLDOWN;
1147  switch (part) {
1148    case kScrollbarDownArrow:
1149      break;
1150    case kScrollbarLeftArrow:
1151      classic_state = DFCS_SCROLLLEFT;
1152      break;
1153    case kScrollbarRightArrow:
1154      classic_state = DFCS_SCROLLRIGHT;
1155      break;
1156    case kScrollbarUpArrow:
1157      classic_state = DFCS_SCROLLUP;
1158      break;
1159    default:
1160      NOTREACHED();
1161      break;
1162  }
1163  switch (state) {
1164    case kDisabled:
1165      classic_state |= DFCS_INACTIVE;
1166      break;
1167    case kHovered:
1168      classic_state |= DFCS_HOT;
1169      break;
1170    case kNormal:
1171      break;
1172    case kPressed:
1173      classic_state |= DFCS_PUSHED;
1174      break;
1175    case kNumStates:
1176      NOTREACHED();
1177      break;
1178  }
1179  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
1180  return S_OK;
1181}
1182
1183HRESULT NativeThemeWin::PaintScrollbarThumb(
1184    HDC hdc,
1185    Part part,
1186    State state,
1187    const gfx::Rect& rect,
1188    const ScrollbarThumbExtraParams& extra) const {
1189  HANDLE handle = GetThemeHandle(SCROLLBAR);
1190  RECT rect_win = rect.ToRECT();
1191
1192  int part_id = SBP_THUMBBTNVERT;
1193  switch (part) {
1194    case kScrollbarHorizontalThumb:
1195      part_id = SBP_THUMBBTNHORZ;
1196      break;
1197    case kScrollbarVerticalThumb:
1198      break;
1199    case kScrollbarHorizontalGripper:
1200      part_id = SBP_GRIPPERHORZ;
1201      break;
1202    case kScrollbarVerticalGripper:
1203      part_id = SBP_GRIPPERVERT;
1204      break;
1205    default:
1206      NOTREACHED();
1207      break;
1208  }
1209
1210  int state_id = SCRBS_NORMAL;
1211  switch (state) {
1212    case kDisabled:
1213      state_id = SCRBS_DISABLED;
1214      break;
1215    case kHovered:
1216      state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
1217      break;
1218    case kNormal:
1219      break;
1220    case kPressed:
1221      state_id = SCRBS_PRESSED;
1222      break;
1223    case kNumStates:
1224      NOTREACHED();
1225      break;
1226  }
1227
1228  if (handle && draw_theme_)
1229    return PaintScaledTheme(handle, hdc, part_id, state_id, rect);
1230
1231  // Draw it manually.
1232  if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
1233    DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
1234  // Classic mode doesn't have a gripper.
1235  return S_OK;
1236}
1237
1238HRESULT NativeThemeWin::PaintScrollbarTrack(
1239    SkCanvas* canvas,
1240    HDC hdc,
1241    Part part,
1242    State state,
1243    const gfx::Rect& rect,
1244    const ScrollbarTrackExtraParams& extra) const {
1245  HANDLE handle = GetThemeHandle(SCROLLBAR);
1246  RECT rect_win = rect.ToRECT();
1247
1248  const int part_id = extra.is_upper ?
1249      ((part == kScrollbarHorizontalTrack) ?
1250          SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT) :
1251      ((part == kScrollbarHorizontalTrack) ?
1252          SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT);
1253
1254  int state_id = SCRBS_NORMAL;
1255  switch (state) {
1256    case kDisabled:
1257      state_id = SCRBS_DISABLED;
1258      break;
1259    case kHovered:
1260      state_id = SCRBS_HOVER;
1261      break;
1262    case kNormal:
1263      break;
1264    case kPressed:
1265      state_id = SCRBS_PRESSED;
1266      break;
1267    case kNumStates:
1268      NOTREACHED();
1269      break;
1270  }
1271
1272  if (handle && draw_theme_)
1273    return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1274
1275  // Draw it manually.
1276  if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
1277      (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
1278    FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
1279  } else {
1280    SkPaint paint;
1281    RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
1282                                extra.track_height).ToRECT();
1283    SetCheckerboardShader(&paint, align_rect);
1284    canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
1285  }
1286  if (extra.classic_state & DFCS_PUSHED)
1287    InvertRect(hdc, &rect_win);
1288  return S_OK;
1289}
1290
1291HRESULT NativeThemeWin::PaintSpinButton(
1292    HDC hdc,
1293    Part part,
1294    State state,
1295    const gfx::Rect& rect,
1296    const InnerSpinButtonExtraParams& extra) const {
1297  HANDLE handle = GetThemeHandle(SPIN);
1298  RECT rect_win = rect.ToRECT();
1299  int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
1300  int state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
1301  switch (state) {
1302    case kDisabled:
1303      state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
1304      break;
1305    case kHovered:
1306      state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
1307      break;
1308    case kNormal:
1309      break;
1310    case kPressed:
1311      state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
1312      break;
1313    case kNumStates:
1314      NOTREACHED();
1315      break;
1316  }
1317
1318  if (handle && draw_theme_)
1319    return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
1320  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
1321  return S_OK;
1322}
1323
1324HRESULT NativeThemeWin::PaintTrackbar(
1325    SkCanvas* canvas,
1326    HDC hdc,
1327    Part part,
1328    State state,
1329    const gfx::Rect& rect,
1330    const TrackbarExtraParams& extra) const {
1331  const int part_id = extra.vertical ?
1332      ((part == kTrackbarTrack) ? TKP_TRACKVERT : TKP_THUMBVERT) :
1333      ((part == kTrackbarTrack) ? TKP_TRACK : TKP_THUMBBOTTOM);
1334
1335  int state_id = TUS_NORMAL;
1336  switch (state) {
1337    case kDisabled:
1338      state_id = TUS_DISABLED;
1339      break;
1340    case kHovered:
1341      state_id = TUS_HOT;
1342      break;
1343    case kNormal:
1344      break;
1345    case kPressed:
1346      state_id = TUS_PRESSED;
1347      break;
1348    case kNumStates:
1349      NOTREACHED();
1350      break;
1351  }
1352
1353  // Make the channel be 4 px thick in the center of the supplied rect.  (4 px
1354  // matches what XP does in various menus; GetThemePartSize() doesn't seem to
1355  // return good values here.)
1356  RECT rect_win = rect.ToRECT();
1357  RECT channel_rect = rect.ToRECT();
1358  const int channel_thickness = 4;
1359  if (part_id == TKP_TRACK) {
1360    channel_rect.top +=
1361        ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
1362    channel_rect.bottom = channel_rect.top + channel_thickness;
1363  } else if (part_id == TKP_TRACKVERT) {
1364    channel_rect.left +=
1365        ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
1366    channel_rect.right = channel_rect.left + channel_thickness;
1367  }  // else this isn't actually a channel, so |channel_rect| == |rect|.
1368
1369  HANDLE handle = GetThemeHandle(TRACKBAR);
1370  if (handle && draw_theme_)
1371    return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
1372
1373  // Classic mode, draw it manually.
1374  if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
1375    DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
1376  } else if (part_id == TKP_THUMBVERT) {
1377    DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
1378  } else {
1379    // Split rect into top and bottom pieces.
1380    RECT top_section = rect.ToRECT();
1381    RECT bottom_section = rect.ToRECT();
1382    top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
1383    bottom_section.top = top_section.bottom;
1384    DrawEdge(hdc, &top_section, EDGE_RAISED,
1385             BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1386
1387    // Split triangular piece into two diagonals.
1388    RECT& left_half = bottom_section;
1389    RECT right_half = bottom_section;
1390    right_half.left += ((bottom_section.right - bottom_section.left) / 2);
1391    left_half.right = right_half.left;
1392    DrawEdge(hdc, &left_half, EDGE_RAISED,
1393             BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1394    DrawEdge(hdc, &right_half, EDGE_RAISED,
1395             BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
1396
1397    // If the button is pressed, draw hatching.
1398    if (extra.classic_state & DFCS_PUSHED) {
1399      SkPaint paint;
1400      SetCheckerboardShader(&paint, rect_win);
1401
1402      // Fill all three pieces with the pattern.
1403      canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
1404
1405      SkScalar left_triangle_top = SkIntToScalar(left_half.top);
1406      SkScalar left_triangle_right = SkIntToScalar(left_half.right);
1407      SkPath left_triangle;
1408      left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
1409      left_triangle.lineTo(left_triangle_right, left_triangle_top);
1410      left_triangle.lineTo(left_triangle_right,
1411                           SkIntToScalar(left_half.bottom));
1412      left_triangle.close();
1413      canvas->drawPath(left_triangle, paint);
1414
1415      SkScalar right_triangle_left = SkIntToScalar(right_half.left);
1416      SkScalar right_triangle_top = SkIntToScalar(right_half.top);
1417      SkPath right_triangle;
1418      right_triangle.moveTo(right_triangle_left, right_triangle_top);
1419      right_triangle.lineTo(SkIntToScalar(right_half.right),
1420                            right_triangle_top);
1421      right_triangle.lineTo(right_triangle_left,
1422                            SkIntToScalar(right_half.bottom));
1423      right_triangle.close();
1424      canvas->drawPath(right_triangle, paint);
1425    }
1426  }
1427  return S_OK;
1428}
1429
1430HRESULT NativeThemeWin::PaintProgressBar(
1431    HDC hdc,
1432    const gfx::Rect& rect,
1433    const ProgressBarExtraParams& extra) const {
1434  // There is no documentation about the animation speed, frame-rate, nor
1435  // size of moving overlay of the indeterminate progress bar.
1436  // So we just observed real-world programs and guessed following parameters.
1437  const int kDeterminateOverlayPixelsPerSecond = 300;
1438  const int kDeterminateOverlayWidth = 120;
1439  const int kIndeterminateOverlayPixelsPerSecond =  175;
1440  const int kVistaIndeterminateOverlayWidth = 120;
1441  const int kXPIndeterminateOverlayWidth = 55;
1442  // The thickness of the bar frame inside |value_rect|
1443  const int kXPBarPadding = 3;
1444
1445  RECT bar_rect = rect.ToRECT();
1446  RECT value_rect = gfx::Rect(extra.value_rect_x,
1447                              extra.value_rect_y,
1448                              extra.value_rect_width,
1449                              extra.value_rect_height).ToRECT();
1450
1451  HANDLE handle = GetThemeHandle(PROGRESS);
1452  if (!handle || !draw_theme_ || !draw_theme_ex_) {
1453    FillRect(hdc, &bar_rect, GetSysColorBrush(COLOR_BTNFACE));
1454    FillRect(hdc, &value_rect, GetSysColorBrush(COLOR_BTNSHADOW));
1455    DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1456    return S_OK;
1457  }
1458
1459  draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
1460
1461  bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
1462  int bar_width = bar_rect.right - bar_rect.left;
1463  if (!extra.determinate) {
1464    // The glossy overlay for the indeterminate progress bar has a small pause
1465    // after each animation. We emulate this by adding an invisible margin the
1466    // animation has to traverse.
1467    int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
1468    int overlay_width = pre_vista ?
1469        kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
1470    RECT overlay_rect = bar_rect;
1471    overlay_rect.left += ComputeAnimationProgress(
1472        width_with_margin, overlay_width, kIndeterminateOverlayPixelsPerSecond,
1473        extra.animated_seconds);
1474    overlay_rect.right = overlay_rect.left + overlay_width;
1475    if (pre_vista) {
1476      RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
1477      RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
1478      draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
1479    } else {
1480      draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
1481    }
1482    return S_OK;
1483  }
1484
1485  // We care about the direction here because PP_CHUNK painting is asymmetric.
1486  // TODO(morrita): This RTL guess can be wrong.  We should pass in the
1487  // direction from WebKit.
1488  const DTBGOPTS value_draw_options = {
1489    sizeof(DTBGOPTS),
1490    (bar_rect.right == value_rect.right && bar_rect.left != value_rect.left) ?
1491        DTBG_MIRRORDC : 0,
1492    bar_rect
1493  };
1494  if (pre_vista) {
1495    // On XP, the progress bar is chunk-style and has no glossy effect.  We need
1496    // to shrink the destination rect to fit the part inside the bar with an
1497    // appropriate margin.
1498    RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
1499    draw_theme_ex_(handle, hdc, PP_CHUNK, 0, &shrunk_value_rect,
1500                   &value_draw_options);
1501  } else  {
1502    // On Vista or later, the progress bar part has a single-block value part
1503    // and a glossy effect.  The value part has exactly same height as the bar
1504    // part, so we don't need to shrink the rect.
1505    draw_theme_ex_(handle, hdc, PP_FILL, 0, &value_rect, &value_draw_options);
1506
1507    RECT overlay_rect = value_rect;
1508    overlay_rect.left += ComputeAnimationProgress(
1509        bar_width, kDeterminateOverlayWidth, kDeterminateOverlayPixelsPerSecond,
1510        extra.animated_seconds);
1511    overlay_rect.right = overlay_rect.left + kDeterminateOverlayWidth;
1512    draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
1513  }
1514  return S_OK;
1515}
1516
1517HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
1518                                                 const gfx::Rect& rect) const {
1519  HANDLE handle = GetThemeHandle(STATUS);
1520  RECT rect_win = rect.ToRECT();
1521  if (handle && draw_theme_) {
1522    // Paint the status bar gripper.  There doesn't seem to be a standard
1523    // gripper in Windows for the space between scrollbars.  This is pretty
1524    // close, but it's supposed to be painted over a status bar.
1525    return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
1526  }
1527
1528  // Draw a windows classic scrollbar gripper.
1529  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
1530  return S_OK;
1531}
1532
1533HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
1534                                                const gfx::Rect& rect) const {
1535  HANDLE handle = GetThemeHandle(TAB);
1536  RECT rect_win = rect.ToRECT();
1537  if (handle && draw_theme_)
1538    return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
1539
1540  // Classic just renders a flat color background.
1541  FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
1542  return S_OK;
1543}
1544
1545HRESULT NativeThemeWin::PaintTextField(
1546    HDC hdc,
1547    Part part,
1548    State state,
1549    const gfx::Rect& rect,
1550    const TextFieldExtraParams& extra) const {
1551  int state_id = ETS_NORMAL;
1552  switch (state) {
1553    case kDisabled:
1554      state_id = ETS_DISABLED;
1555      break;
1556    case kHovered:
1557      state_id = ETS_HOT;
1558      break;
1559    case kNormal:
1560      if (extra.is_read_only)
1561        state_id = ETS_READONLY;
1562      else if (extra.is_focused)
1563        state_id = ETS_FOCUSED;
1564      break;
1565    case kPressed:
1566      state_id = ETS_SELECTED;
1567      break;
1568    case kNumStates:
1569      NOTREACHED();
1570      break;
1571  }
1572
1573  RECT rect_win = rect.ToRECT();
1574  return PaintTextField(hdc, EP_EDITTEXT, state_id, extra.classic_state,
1575                        &rect_win,
1576                        skia::SkColorToCOLORREF(extra.background_color),
1577                        extra.fill_content_area, extra.draw_edges);
1578}
1579
1580HRESULT NativeThemeWin::PaintTextField(HDC hdc,
1581                                       int part_id,
1582                                       int state_id,
1583                                       int classic_state,
1584                                       RECT* rect,
1585                                       COLORREF color,
1586                                       bool fill_content_area,
1587                                       bool draw_edges) const {
1588  // TODO(ojan): http://b/1210017 Figure out how to give the ability to
1589  // exclude individual edges from being drawn.
1590
1591  HANDLE handle = GetThemeHandle(TEXTFIELD);
1592  // TODO(mpcomplete): can we detect if the color is specified by the user,
1593  // and if not, just use the system color?
1594  // CreateSolidBrush() accepts a RGB value but alpha must be 0.
1595  base::win::ScopedGDIObject<HBRUSH> bg_brush(CreateSolidBrush(color));
1596  // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
1597  // draw_theme_ex_ is NULL and draw_theme_ is non-null.
1598  if (!handle || (!draw_theme_ex_ && (!draw_theme_ || !draw_edges))) {
1599    // Draw it manually.
1600    if (draw_edges)
1601      DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1602
1603    if (fill_content_area) {
1604      FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
1605                   reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
1606    }
1607    return S_OK;
1608  }
1609
1610  static const DTBGOPTS omit_border_options = {
1611    sizeof(DTBGOPTS),
1612    DTBG_OMITBORDER,
1613    { 0, 0, 0, 0 }
1614  };
1615  HRESULT hr = draw_theme_ex_ ?
1616    draw_theme_ex_(handle, hdc, part_id, state_id, rect,
1617                   draw_edges ? NULL : &omit_border_options) :
1618    draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
1619
1620  // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
1621  if (fill_content_area && get_theme_content_rect_) {
1622    RECT content_rect;
1623    hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
1624                                  &content_rect);
1625    FillRect(hdc, &content_rect, bg_brush);
1626  }
1627  return hr;
1628}
1629
1630HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
1631                                         HDC hdc,
1632                                         int part_id,
1633                                         int state_id,
1634                                         const gfx::Rect& rect) const {
1635  // Correct the scaling and positioning of sub-components such as scrollbar
1636  // arrows and thumb grippers in the event that the world transform applies
1637  // scaling (e.g. in high-DPI mode).
1638  XFORM save_transform;
1639  if (GetWorldTransform(hdc, &save_transform)) {
1640    float scale = save_transform.eM11;
1641    if (scale != 1 && save_transform.eM12 == 0) {
1642      ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
1643      gfx::Rect scaled_rect(gfx::ToEnclosedRect(gfx::ScaleRect(rect, scale)));
1644      scaled_rect.Offset(save_transform.eDx, save_transform.eDy);
1645      RECT bounds = scaled_rect.ToRECT();
1646      HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds,
1647                                   NULL);
1648      SetWorldTransform(hdc, &save_transform);
1649      return result;
1650    }
1651  }
1652  RECT bounds = rect.ToRECT();
1653  return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
1654}
1655
1656// static
1657NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
1658  switch (part) {
1659    case kCheckbox:
1660    case kPushButton:
1661    case kRadio:
1662      return BUTTON;
1663    case kInnerSpinButton:
1664      return SPIN;
1665    case kMenuList:
1666    case kMenuCheck:
1667    case kMenuPopupArrow:
1668    case kMenuPopupGutter:
1669    case kMenuPopupSeparator:
1670      return MENU;
1671    case kProgressBar:
1672      return PROGRESS;
1673    case kScrollbarDownArrow:
1674    case kScrollbarLeftArrow:
1675    case kScrollbarRightArrow:
1676    case kScrollbarUpArrow:
1677    case kScrollbarHorizontalThumb:
1678    case kScrollbarVerticalThumb:
1679    case kScrollbarHorizontalTrack:
1680    case kScrollbarVerticalTrack:
1681      return SCROLLBAR;
1682    case kSliderTrack:
1683    case kSliderThumb:
1684      return TRACKBAR;
1685    case kTextField:
1686      return TEXTFIELD;
1687    case kWindowResizeGripper:
1688      return STATUS;
1689    case kComboboxArrow:
1690    case kMenuCheckBackground:
1691    case kMenuPopupBackground:
1692    case kMenuItemBackground:
1693    case kScrollbarHorizontalGripper:
1694    case kScrollbarVerticalGripper:
1695    case kScrollbarCorner:
1696    case kTabPanelBackground:
1697    case kTrackbarThumb:
1698    case kTrackbarTrack:
1699    case kMaxPart:
1700      NOTREACHED();
1701  }
1702  return LAST;
1703}
1704
1705// static
1706int NativeThemeWin::GetWindowsPart(Part part,
1707                                   State state,
1708                                   const ExtraParams& extra) {
1709  switch (part) {
1710    case kCheckbox:
1711      return BP_CHECKBOX;
1712    case kMenuCheck:
1713      return MENU_POPUPCHECK;
1714    case kMenuPopupArrow:
1715      return MENU_POPUPSUBMENU;
1716    case kMenuPopupGutter:
1717      return MENU_POPUPGUTTER;
1718    case kMenuPopupSeparator:
1719      return MENU_POPUPSEPARATOR;
1720    case kPushButton:
1721      return BP_PUSHBUTTON;
1722    case kRadio:
1723      return BP_RADIOBUTTON;
1724    case kScrollbarDownArrow:
1725    case kScrollbarLeftArrow:
1726    case kScrollbarRightArrow:
1727    case kScrollbarUpArrow:
1728      return SBP_ARROWBTN;
1729    case kScrollbarHorizontalThumb:
1730      return SBP_THUMBBTNHORZ;
1731    case kScrollbarVerticalThumb:
1732      return SBP_THUMBBTNVERT;
1733    case kWindowResizeGripper:
1734      return SP_GRIPPER;
1735    case kComboboxArrow:
1736    case kInnerSpinButton:
1737    case kMenuList:
1738    case kMenuCheckBackground:
1739    case kMenuPopupBackground:
1740    case kMenuItemBackground:
1741    case kProgressBar:
1742    case kScrollbarHorizontalTrack:
1743    case kScrollbarVerticalTrack:
1744    case kScrollbarHorizontalGripper:
1745    case kScrollbarVerticalGripper:
1746    case kScrollbarCorner:
1747    case kSliderTrack:
1748    case kSliderThumb:
1749    case kTabPanelBackground:
1750    case kTextField:
1751    case kTrackbarThumb:
1752    case kTrackbarTrack:
1753    case kMaxPart:
1754      NOTREACHED();
1755  }
1756  return 0;
1757}
1758
1759int NativeThemeWin::GetWindowsState(Part part,
1760                                    State state,
1761                                    const ExtraParams& extra) {
1762  switch (part) {
1763    case kCheckbox:
1764      switch (state) {
1765        case kDisabled:
1766          return CBS_UNCHECKEDDISABLED;
1767        case kHovered:
1768          return CBS_UNCHECKEDHOT;
1769        case kNormal:
1770          return CBS_UNCHECKEDNORMAL;
1771        case kPressed:
1772          return CBS_UNCHECKEDPRESSED;
1773        case kNumStates:
1774          NOTREACHED();
1775          return 0;
1776      }
1777    case kMenuCheck:
1778      switch (state) {
1779        case kDisabled:
1780          return extra.menu_check.is_radio ?
1781              MC_BULLETDISABLED : MC_CHECKMARKDISABLED;
1782        case kHovered:
1783        case kNormal:
1784        case kPressed:
1785          return extra.menu_check.is_radio ?
1786              MC_BULLETNORMAL : MC_CHECKMARKNORMAL;
1787        case kNumStates:
1788          NOTREACHED();
1789          return 0;
1790      }
1791    case kMenuPopupArrow:
1792    case kMenuPopupGutter:
1793    case kMenuPopupSeparator:
1794      switch (state) {
1795        case kDisabled:
1796          return MBI_DISABLED;
1797        case kHovered:
1798          return MBI_HOT;
1799        case kNormal:
1800          return MBI_NORMAL;
1801        case kPressed:
1802          return MBI_PUSHED;
1803        case kNumStates:
1804          NOTREACHED();
1805          return 0;
1806      }
1807    case kPushButton:
1808      switch (state) {
1809        case kDisabled:
1810          return PBS_DISABLED;
1811        case kHovered:
1812          return PBS_HOT;
1813        case kNormal:
1814          return PBS_NORMAL;
1815        case kPressed:
1816          return PBS_PRESSED;
1817        case kNumStates:
1818          NOTREACHED();
1819          return 0;
1820      }
1821    case kRadio:
1822      switch (state) {
1823        case kDisabled:
1824          return RBS_UNCHECKEDDISABLED;
1825        case kHovered:
1826          return RBS_UNCHECKEDHOT;
1827        case kNormal:
1828          return RBS_UNCHECKEDNORMAL;
1829        case kPressed:
1830          return RBS_UNCHECKEDPRESSED;
1831        case kNumStates:
1832          NOTREACHED();
1833          return 0;
1834      }
1835    case kScrollbarDownArrow:
1836      switch (state) {
1837        case kDisabled:
1838          return ABS_DOWNDISABLED;
1839        case kHovered:
1840          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1841          return base::win::GetVersion() < base::win::VERSION_VISTA ?
1842              ABS_DOWNHOT : ABS_DOWNHOVER;
1843        case kNormal:
1844          return ABS_DOWNNORMAL;
1845        case kPressed:
1846          return ABS_DOWNPRESSED;
1847        case kNumStates:
1848          NOTREACHED();
1849          return 0;
1850      }
1851    case kScrollbarLeftArrow:
1852      switch (state) {
1853        case kDisabled:
1854          return ABS_LEFTDISABLED;
1855        case kHovered:
1856          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1857          return base::win::GetVersion() < base::win::VERSION_VISTA ?
1858              ABS_LEFTHOT : ABS_LEFTHOVER;
1859        case kNormal:
1860          return ABS_LEFTNORMAL;
1861        case kPressed:
1862          return ABS_LEFTPRESSED;
1863        case kNumStates:
1864          NOTREACHED();
1865          return 0;
1866      }
1867    case kScrollbarRightArrow:
1868      switch (state) {
1869        case kDisabled:
1870          return ABS_RIGHTDISABLED;
1871        case kHovered:
1872          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1873          return base::win::GetVersion() < base::win::VERSION_VISTA ?
1874              ABS_RIGHTHOT : ABS_RIGHTHOVER;
1875        case kNormal:
1876          return ABS_RIGHTNORMAL;
1877        case kPressed:
1878          return ABS_RIGHTPRESSED;
1879        case kNumStates:
1880          NOTREACHED();
1881          return 0;
1882      }
1883      break;
1884    case kScrollbarUpArrow:
1885      switch (state) {
1886        case kDisabled:
1887          return ABS_UPDISABLED;
1888        case kHovered:
1889          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
1890          return base::win::GetVersion() < base::win::VERSION_VISTA ?
1891              ABS_UPHOT : ABS_UPHOVER;
1892        case kNormal:
1893          return ABS_UPNORMAL;
1894        case kPressed:
1895          return ABS_UPPRESSED;
1896        case kNumStates:
1897          NOTREACHED();
1898          return 0;
1899      }
1900      break;
1901    case kScrollbarHorizontalThumb:
1902    case kScrollbarVerticalThumb:
1903      switch (state) {
1904        case kDisabled:
1905          return SCRBS_DISABLED;
1906        case kHovered:
1907          // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
1908          return base::win::GetVersion() < base::win::VERSION_VISTA ?
1909              SCRBS_HOT : SCRBS_HOVER;
1910        case kNormal:
1911          return SCRBS_NORMAL;
1912        case kPressed:
1913          return SCRBS_PRESSED;
1914        case kNumStates:
1915          NOTREACHED();
1916          return 0;
1917      }
1918    case kWindowResizeGripper:
1919      switch (state) {
1920        case kDisabled:
1921        case kHovered:
1922        case kNormal:
1923        case kPressed:
1924          return 1;  // gripper has no windows state
1925        case kNumStates:
1926          NOTREACHED();
1927          return 0;
1928      }
1929    case kComboboxArrow:
1930    case kInnerSpinButton:
1931    case kMenuList:
1932    case kMenuCheckBackground:
1933    case kMenuPopupBackground:
1934    case kMenuItemBackground:
1935    case kProgressBar:
1936    case kScrollbarHorizontalTrack:
1937    case kScrollbarVerticalTrack:
1938    case kScrollbarHorizontalGripper:
1939    case kScrollbarVerticalGripper:
1940    case kScrollbarCorner:
1941    case kSliderTrack:
1942    case kSliderThumb:
1943    case kTabPanelBackground:
1944    case kTextField:
1945    case kTrackbarThumb:
1946    case kTrackbarTrack:
1947    case kMaxPart:
1948      NOTREACHED();
1949  }
1950  return 0;
1951}
1952
1953HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
1954                                    int part_id,
1955                                    int state_id,
1956                                    int prop_id,
1957                                    int *value) const {
1958  HANDLE handle = GetThemeHandle(theme);
1959  return (handle && get_theme_int_) ?
1960      get_theme_int_(handle, part_id, state_id, prop_id, value) : E_NOTIMPL;
1961}
1962
1963HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
1964                                          const gfx::Rect& rect,
1965                                          UINT type,
1966                                          UINT state,
1967                                          bool is_selected,
1968                                          State control_state) const {
1969  const int width = rect.width();
1970  const int height = rect.height();
1971
1972  // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
1973  base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
1974
1975  if (mask_bitmap == NULL)
1976    return E_OUTOFMEMORY;
1977
1978  base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
1979  base::win::ScopedSelectObject select_bitmap(bitmap_dc.Get(), mask_bitmap);
1980  RECT local_rect = { 0, 0, width, height };
1981  DrawFrameControl(bitmap_dc.Get(), &local_rect, type, state);
1982
1983  // We're going to use BitBlt with a b&w mask. This results in using the dest
1984  // dc's text color for the black bits in the mask, and the dest dc's
1985  // background color for the white bits in the mask. DrawFrameControl draws the
1986  // check in black, and the background in white.
1987  int bg_color_key = COLOR_MENU;
1988  int text_color_key = COLOR_MENUTEXT;
1989  switch (control_state) {
1990    case kDisabled:
1991      bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
1992      text_color_key = COLOR_GRAYTEXT;
1993      break;
1994    case kHovered:
1995      bg_color_key = COLOR_HIGHLIGHT;
1996      text_color_key = COLOR_HIGHLIGHTTEXT;
1997      break;
1998    case kNormal:
1999      break;
2000    case kPressed:
2001    case kNumStates:
2002      NOTREACHED();
2003      break;
2004  }
2005  COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
2006  COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
2007  BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc.Get(), 0, 0,
2008         SRCCOPY);
2009  SetBkColor(hdc, old_bg_color);
2010  SetTextColor(hdc, old_text_color);
2011
2012  return S_OK;
2013}
2014
2015HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
2016  if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
2017    return 0;
2018
2019  if (theme_handles_[theme_name])
2020    return theme_handles_[theme_name];
2021
2022  // Not found, try to load it.
2023  HANDLE handle = 0;
2024  switch (theme_name) {
2025  case BUTTON:
2026    handle = open_theme_(NULL, L"Button");
2027    break;
2028  case LIST:
2029    handle = open_theme_(NULL, L"Listview");
2030    break;
2031  case MENU:
2032    handle = open_theme_(NULL, L"Menu");
2033    break;
2034  case MENULIST:
2035    handle = open_theme_(NULL, L"Combobox");
2036    break;
2037  case SCROLLBAR:
2038    handle = open_theme_(NULL, L"Scrollbar");
2039    break;
2040  case STATUS:
2041    handle = open_theme_(NULL, L"Status");
2042    break;
2043  case TAB:
2044    handle = open_theme_(NULL, L"Tab");
2045    break;
2046  case TEXTFIELD:
2047    handle = open_theme_(NULL, L"Edit");
2048    break;
2049  case TRACKBAR:
2050    handle = open_theme_(NULL, L"Trackbar");
2051    break;
2052  case WINDOW:
2053    handle = open_theme_(NULL, L"Window");
2054    break;
2055  case PROGRESS:
2056    handle = open_theme_(NULL, L"Progress");
2057    break;
2058  case SPIN:
2059    handle = open_theme_(NULL, L"Spin");
2060    break;
2061  case LAST:
2062    NOTREACHED();
2063    break;
2064  }
2065  theme_handles_[theme_name] = handle;
2066  return handle;
2067}
2068
2069}  // namespace ui
2070