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