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