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