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