普通文本  |  2156行  |  70.88 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/native_theme/native_theme_win.h"

#include <windows.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/win/scoped_gdi_object.h"
#include "base/win/scoped_hdc.h"
#include "base/win/scoped_select_object.h"
#include "base/win/windows_version.h"
#include "skia/ext/bitmap_platform_device.h"
#include "skia/ext/platform_canvas.h"
#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "third_party/skia/include/core/SkShader.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/gdi_util.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/rect_conversions.h"
#include "ui/gfx/win/dpi.h"
#include "ui/native_theme/common_theme.h"

// This was removed from Winvers.h but is still used.
#if !defined(COLOR_MENUHIGHLIGHT)
#define COLOR_MENUHIGHLIGHT 29
#endif

namespace {

// TODO: Obtain the correct colors using GetSysColor.
// Theme colors returned by GetSystemColor().
const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
// Dialogs:
const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
// FocusableBorder:
const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
// Button:
const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117);
const SkColor kButtonHoverBackgroundColor = SkColorSetRGB(0xEA, 0xEA, 0xEA);
// MenuItem:
const SkColor kEnabledMenuItemForegroundColor = SkColorSetRGB(6, 45, 117);
const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146);
const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253);
const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0);
// Table:
const SkColor kTreeSelectionBackgroundUnfocused = SkColorSetRGB(240, 240, 240);

// Windows system color IDs cached and updated by the native theme.
const int kSystemColors[] = {
  COLOR_3DFACE,
  COLOR_BTNTEXT,
  COLOR_GRAYTEXT,
  COLOR_HIGHLIGHT,
  COLOR_HIGHLIGHTTEXT,
  COLOR_SCROLLBAR,
  COLOR_WINDOW,
  COLOR_WINDOWTEXT,
  COLOR_BTNFACE,
  COLOR_MENUHIGHLIGHT,
};

void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
  // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
  const SkColor face = color_utils::GetSysSkColor(COLOR_3DFACE);
  const SkColor highlight = color_utils::GetSysSkColor(COLOR_3DHILIGHT);
  SkColor buffer[] = { face, highlight, highlight, face };
  // Confusing bit: we first create a temporary bitmap with our desired pattern,
  // then copy it to another bitmap.  The temporary bitmap doesn't take
  // ownership of the pixel data, and so will point to garbage when this
  // function returns.  The copy will copy the pixel data into a place owned by
  // the bitmap, which is in turn owned by the shader, etc., so it will live
  // until we're done using it.
  SkBitmap temp_bitmap;
  temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
  temp_bitmap.setPixels(buffer);
  SkBitmap bitmap;
  temp_bitmap.copyTo(&bitmap);

  // Align the pattern with the upper corner of |align_rect|.
  SkMatrix local_matrix;
  local_matrix.setTranslate(SkIntToScalar(align_rect.left),
                            SkIntToScalar(align_rect.top));
  skia::RefPtr<SkShader> shader =
      skia::AdoptRef(SkShader::CreateBitmapShader(bitmap,
                                                  SkShader::kRepeat_TileMode,
                                                  SkShader::kRepeat_TileMode,
                                                  &local_matrix));
  paint->setShader(shader.get());
}

//    <-a->
// [  *****             ]
//  ____ |              |
//  <-a-> <------b----->
// a: object_width
// b: frame_width
// *: animating object
//
// - the animation goes from "[" to "]" repeatedly.
// - the animation offset is at first "|"
//
int ComputeAnimationProgress(int frame_width,
                             int object_width,
                             int pixels_per_second,
                             double animated_seconds) {
  int animation_width = frame_width + object_width;
  double interval = static_cast<double>(animation_width) / pixels_per_second;
  double ratio = fmod(animated_seconds, interval) / interval;
  return static_cast<int>(animation_width * ratio) - object_width;
}

RECT InsetRect(const RECT* rect, int size) {
  gfx::Rect result(*rect);
  result.Inset(size, size);
  return result.ToRECT();
}

}  // namespace

namespace ui {

bool NativeThemeWin::IsThemingActive() const {
  if (is_theme_active_)
    return !!is_theme_active_();
  return false;
}

bool NativeThemeWin::IsUsingHighContrastTheme() const {
  if (is_using_high_contrast_valid_)
    return is_using_high_contrast_;
  HIGHCONTRAST result;
  result.cbSize = sizeof(HIGHCONTRAST);
  is_using_high_contrast_ =
      SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
      (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
  is_using_high_contrast_valid_ = true;
  return is_using_high_contrast_;
}

HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
                                      int part_id,
                                      int state_id,
                                      int prop_id,
                                      SkColor* color) const {
  HANDLE handle = GetThemeHandle(theme);
  if (handle && get_theme_color_) {
    COLORREF color_ref;
    if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
        S_OK) {
      *color = skia::COLORREFToSkColor(color_ref);
      return S_OK;
    }
  }
  return E_NOTIMPL;
}

SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
                                                 int part_id,
                                                 int state_id,
                                                 int prop_id,
                                                 int default_sys_color) const {
  SkColor color;
  if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
    color = color_utils::GetSysSkColor(default_sys_color);
  return color;
}

gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
  // For simplicity use the wildcard state==0, part==0, since it works
  // for the cases we currently depend on.
  int border;
  if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
    return gfx::Size(border, border);
  else
    return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
}

void NativeThemeWin::DisableTheming() const {
  if (!set_theme_properties_)
    return;
  set_theme_properties_(0);
}

void NativeThemeWin::CloseHandles() const {
  if (!close_theme_)
    return;

  for (int i = 0; i < LAST; ++i) {
    if (theme_handles_[i]) {
      close_theme_(theme_handles_[i]);
      theme_handles_[i] = NULL;
    }
  }
}

bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
  if (!theme_dll_)
    return true;

  return !GetThemeHandle(name);
}

// static
NativeThemeWin* NativeThemeWin::instance() {
  CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
  return &s_native_theme;
}

gfx::Size NativeThemeWin::GetPartSize(Part part,
                                      State state,
                                      const ExtraParams& extra) const {
  gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
  if (!part_size.IsEmpty())
    return part_size;

  // The GetThemePartSize call below returns the default size without
  // accounting for user customization (crbug/218291).
  switch (part) {
    case kScrollbarDownArrow:
    case kScrollbarLeftArrow:
    case kScrollbarRightArrow:
    case kScrollbarUpArrow:
    case kScrollbarHorizontalThumb:
    case kScrollbarVerticalThumb:
    case kScrollbarHorizontalTrack:
    case kScrollbarVerticalTrack: {
      int size = gfx::win::GetSystemMetricsInDIP(SM_CXVSCROLL);
      if (size == 0)
        size = 17;
      return gfx::Size(size, size);
    }
  }

  int part_id = GetWindowsPart(part, state, extra);
  int state_id = GetWindowsState(part, state, extra);

  SIZE size;
  HDC hdc = GetDC(NULL);
  HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id,
                                NULL, TS_TRUE, &size);
  ReleaseDC(NULL, hdc);

  if (FAILED(hr)) {
    // TODO(rogerta): For now, we need to support radio buttons and checkboxes
    // when theming is not enabled.  Support for other parts can be added
    // if/when needed.
    switch (part) {
      case kCheckbox:
      case kRadio:
        // TODO(rogerta): I was not able to find any API to get the default
        // size of these controls, so determined these values empirically.
        size.cx = 13;
        size.cy = 13;
        break;
      default:
        size.cx = 0;
        size.cy = 0;
        break;
    }
  }

  return gfx::Size(size.cx, size.cy);
}

void NativeThemeWin::Paint(SkCanvas* canvas,
                           Part part,
                           State state,
                           const gfx::Rect& rect,
                           const ExtraParams& extra) const {
  if (rect.IsEmpty())
    return;

  switch (part) {
    case kComboboxArrow:
      CommonThemePaintComboboxArrow(canvas, rect);
      return;
    case kMenuPopupGutter:
      CommonThemePaintMenuGutter(canvas, rect);
      return;
    case kMenuPopupSeparator:
      CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
      return;
    case kMenuPopupBackground:
      CommonThemePaintMenuBackground(canvas, rect);
      return;
    case kMenuItemBackground:
      CommonThemePaintMenuItemBackground(canvas, state, rect);
      return;
  }

  bool needs_paint_indirect = false;
  if (!skia::SupportsPlatformPaint(canvas)) {
    // This block will only get hit with --enable-accelerated-drawing flag.
    needs_paint_indirect = true;
  } else {
    // Scrollbar components on Windows Classic theme (on all Windows versions)
    // have particularly problematic alpha values, so always draw them
    // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
    // theme (available only on Windows XP) also need their alpha values
    // fixed.
    switch (part) {
      case kScrollbarDownArrow:
      case kScrollbarUpArrow:
      case kScrollbarLeftArrow:
      case kScrollbarRightArrow:
        if (!GetThemeHandle(SCROLLBAR))
          needs_paint_indirect = true;
        break;
      case kScrollbarHorizontalThumb:
      case kScrollbarVerticalThumb:
      case kScrollbarHorizontalGripper:
      case kScrollbarVerticalGripper:
        if (!GetThemeHandle(SCROLLBAR) ||
            base::win::GetVersion() == base::win::VERSION_XP)
          needs_paint_indirect = true;
        break;
      default:
        break;
    }
  }

  if (needs_paint_indirect)
    PaintIndirect(canvas, part, state, rect, extra);
  else
    PaintDirect(canvas, part, state, rect, extra);
}

NativeThemeWin::NativeThemeWin()
    : theme_dll_(LoadLibrary(L"uxtheme.dll")),
      draw_theme_(NULL),
      draw_theme_ex_(NULL),
      get_theme_color_(NULL),
      get_theme_content_rect_(NULL),
      get_theme_part_size_(NULL),
      open_theme_(NULL),
      close_theme_(NULL),
      set_theme_properties_(NULL),
      is_theme_active_(NULL),
      get_theme_int_(NULL),
      color_change_listener_(this),
      is_using_high_contrast_(false),
      is_using_high_contrast_valid_(false) {
  if (theme_dll_) {
    draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
        GetProcAddress(theme_dll_, "DrawThemeBackground"));
    draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
        GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
    get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
        GetProcAddress(theme_dll_, "GetThemeColor"));
    get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
        GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
    get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
        GetProcAddress(theme_dll_, "GetThemePartSize"));
    open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
        GetProcAddress(theme_dll_, "OpenThemeData"));
    close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
        GetProcAddress(theme_dll_, "CloseThemeData"));
    set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
        GetProcAddress(theme_dll_, "SetThemeAppProperties"));
    is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
        GetProcAddress(theme_dll_, "IsThemeActive"));
    get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
        GetProcAddress(theme_dll_, "GetThemeInt"));
  }
  memset(theme_handles_, 0, sizeof(theme_handles_));

  // Initialize the cached system colors.
  UpdateSystemColors();
}

NativeThemeWin::~NativeThemeWin() {
  if (theme_dll_) {
    // todo (cpu): fix this soon.  Making a call to CloseHandles() here breaks
    // certain tests and the reliability bots.
    // CloseHandles();
    FreeLibrary(theme_dll_);
  }
}

void NativeThemeWin::OnSysColorChange() {
  UpdateSystemColors();
  is_using_high_contrast_valid_ = false;
  NotifyObservers();
}

void NativeThemeWin::UpdateSystemColors() {
  for (int i = 0; i < arraysize(kSystemColors); ++i) {
    system_colors_[kSystemColors[i]] =
        color_utils::GetSysSkColor(kSystemColors[i]);
  }
}

void NativeThemeWin::PaintDirect(SkCanvas* canvas,
                                 Part part,
                                 State state,
                                 const gfx::Rect& rect,
                                 const ExtraParams& extra) const {
  skia::ScopedPlatformPaint scoped_platform_paint(canvas);
  HDC hdc = scoped_platform_paint.GetPlatformSurface();

  switch (part) {
    case kCheckbox:
      PaintCheckbox(hdc, part, state, rect, extra.button);
      break;
    case kRadio:
      PaintRadioButton(hdc, part, state, rect, extra.button);
      break;
    case kPushButton:
      PaintPushButton(hdc, part, state, rect, extra.button);
      break;
    case kMenuPopupArrow:
      PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
      break;
    case kMenuPopupGutter:
      PaintMenuGutter(hdc, rect);
      break;
    case kMenuPopupSeparator:
      PaintMenuSeparator(hdc, rect, extra.menu_separator);
      break;
    case kMenuPopupBackground:
      PaintMenuBackground(hdc, rect);
      break;
    case kMenuCheck:
      PaintMenuCheck(hdc, state, rect, extra.menu_check);
      break;
    case kMenuCheckBackground:
      PaintMenuCheckBackground(hdc, state, rect);
      break;
    case kMenuItemBackground:
      PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
      break;
    case kMenuList:
      PaintMenuList(hdc, state, rect, extra.menu_list);
      break;
    case kScrollbarDownArrow:
    case kScrollbarUpArrow:
    case kScrollbarLeftArrow:
    case kScrollbarRightArrow:
      PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
      break;
    case kScrollbarHorizontalTrack:
    case kScrollbarVerticalTrack:
      PaintScrollbarTrack(canvas, hdc, part, state, rect,
                          extra.scrollbar_track);
      break;
    case kScrollbarCorner:
      canvas->drawColor(SK_ColorWHITE, SkXfermode::kSrc_Mode);
      break;
    case kScrollbarHorizontalThumb:
    case kScrollbarVerticalThumb:
    case kScrollbarHorizontalGripper:
    case kScrollbarVerticalGripper:
      PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
      break;
    case kInnerSpinButton:
      PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
      break;
    case kTrackbarThumb:
    case kTrackbarTrack:
      PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
      break;
    case kProgressBar:
      PaintProgressBar(hdc, rect, extra.progress_bar);
      break;
    case kWindowResizeGripper:
      PaintWindowResizeGripper(hdc, rect);
      break;
    case kTabPanelBackground:
      PaintTabPanelBackground(hdc, rect);
      break;
    case kTextField:
      PaintTextField(hdc, part, state, rect, extra.text_field);
      break;

    case kSliderTrack:
    case kSliderThumb:
    default:
      // While transitioning NativeThemeWin to the single Paint() entry point,
      // unsupported parts will DCHECK here.
      NOTREACHED();
  }
}

SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
  SkColor color;
  if (CommonThemeGetSystemColor(color_id, &color))
    return color;

  switch (color_id) {
    // Windows
    case kColorId_WindowBackground:
      return system_colors_[COLOR_WINDOW];

    // Dialogs
    case kColorId_DialogBackground:
      if (gfx::IsInvertedColorScheme())
        return color_utils::InvertColor(kDialogBackgroundColor);
      return kDialogBackgroundColor;

    // FocusableBorder
    case kColorId_FocusedBorderColor:
      return kFocusedBorderColor;
    case kColorId_UnfocusedBorderColor:
      return kUnfocusedBorderColor;

    // Button
    case kColorId_ButtonBackgroundColor:
      return kButtonBackgroundColor;
    case kColorId_ButtonEnabledColor:
      return system_colors_[COLOR_BTNTEXT];
    case kColorId_ButtonDisabledColor:
      return system_colors_[COLOR_GRAYTEXT];
    case kColorId_ButtonHighlightColor:
      return kButtonHighlightColor;
    case kColorId_ButtonHoverColor:
      return kButtonHoverColor;
    case kColorId_ButtonHoverBackgroundColor:
      return kButtonHoverBackgroundColor;

    // MenuItem
    case kColorId_EnabledMenuItemForegroundColor:
      return kEnabledMenuItemForegroundColor;
    case kColorId_DisabledMenuItemForegroundColor:
      return kDisabledMenuItemForegroundColor;
    case kColorId_DisabledEmphasizedMenuItemForegroundColor:
      return SK_ColorBLACK;
    case kColorId_FocusedMenuItemBackgroundColor:
      return kFocusedMenuItemBackgroundColor;
    case kColorId_MenuSeparatorColor:
      return kMenuSeparatorColor;

    // Label
    case kColorId_LabelEnabledColor:
      return system_colors_[COLOR_BTNTEXT];
    case kColorId_LabelDisabledColor:
      return system_colors_[COLOR_GRAYTEXT];
    case kColorId_LabelBackgroundColor:
      return system_colors_[COLOR_WINDOW];

    // Textfield
    case kColorId_TextfieldDefaultColor:
      return system_colors_[COLOR_WINDOWTEXT];
    case kColorId_TextfieldDefaultBackground:
      return system_colors_[COLOR_WINDOW];
    case kColorId_TextfieldReadOnlyColor:
      return system_colors_[COLOR_GRAYTEXT];
    case kColorId_TextfieldReadOnlyBackground:
      return system_colors_[COLOR_3DFACE];
    case kColorId_TextfieldSelectionColor:
      return system_colors_[COLOR_HIGHLIGHTTEXT];
    case kColorId_TextfieldSelectionBackgroundFocused:
      return system_colors_[COLOR_HIGHLIGHT];

    // Tree
    // NOTE: these aren't right for all themes, but as close as I could get.
    case kColorId_TreeBackground:
      return system_colors_[COLOR_WINDOW];
    case kColorId_TreeText:
      return system_colors_[COLOR_WINDOWTEXT];
    case kColorId_TreeSelectedText:
      return system_colors_[COLOR_HIGHLIGHTTEXT];
    case kColorId_TreeSelectedTextUnfocused:
      return system_colors_[COLOR_BTNTEXT];
    case kColorId_TreeSelectionBackgroundFocused:
      return system_colors_[COLOR_HIGHLIGHT];
    case kColorId_TreeSelectionBackgroundUnfocused:
      return system_colors_[IsUsingHighContrastTheme() ?
                              COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
    case kColorId_TreeArrow:
      return system_colors_[COLOR_WINDOWTEXT];

    // Table
    case kColorId_TableBackground:
      return system_colors_[COLOR_WINDOW];
    case kColorId_TableText:
      return system_colors_[COLOR_WINDOWTEXT];
    case kColorId_TableSelectedText:
      return system_colors_[COLOR_HIGHLIGHTTEXT];
    case kColorId_TableSelectedTextUnfocused:
      return system_colors_[COLOR_BTNTEXT];
    case kColorId_TableSelectionBackgroundFocused:
      return system_colors_[COLOR_HIGHLIGHT];
    case kColorId_TableSelectionBackgroundUnfocused:
      return system_colors_[IsUsingHighContrastTheme() ?
                              COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
    case kColorId_TableGroupingIndicatorColor:
      return system_colors_[COLOR_GRAYTEXT];

    // Results Tables
    case kColorId_ResultsTableNormalBackground:
      return system_colors_[COLOR_WINDOW];
    case kColorId_ResultsTableHoveredBackground:
      return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHT],
                                     system_colors_[COLOR_WINDOW], 0x40);
    case kColorId_ResultsTableSelectedBackground:
      return system_colors_[COLOR_HIGHLIGHT];
    case kColorId_ResultsTableNormalText:
    case kColorId_ResultsTableHoveredText:
      return system_colors_[COLOR_WINDOWTEXT];
    case kColorId_ResultsTableSelectedText:
      return system_colors_[COLOR_HIGHLIGHTTEXT];
    case kColorId_ResultsTableNormalDimmedText:
      return color_utils::AlphaBlend(system_colors_[COLOR_WINDOWTEXT],
                                     system_colors_[COLOR_WINDOW], 0x80);
    case kColorId_ResultsTableHoveredDimmedText:
      return color_utils::AlphaBlend(
          system_colors_[COLOR_WINDOWTEXT],
          GetSystemColor(kColorId_ResultsTableHoveredBackground), 0x80);
    case kColorId_ResultsTableSelectedDimmedText:
      return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHTTEXT],
                                     system_colors_[COLOR_HIGHLIGHT], 0x80);
    case kColorId_ResultsTableNormalUrl:
      return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
                                           system_colors_[COLOR_WINDOW]);
    case kColorId_ResultsTableHoveredUrl:
      return color_utils::GetReadableColor(
          SkColorSetRGB(0, 128, 0),
          GetSystemColor(kColorId_ResultsTableHoveredBackground));
    case kColorId_ResultsTableSelectedUrl:
      return color_utils::GetReadableColor(SkColorSetRGB(0, 128, 0),
                                           system_colors_[COLOR_HIGHLIGHT]);
    case kColorId_ResultsTableNormalDivider:
      return color_utils::AlphaBlend(system_colors_[COLOR_WINDOWTEXT],
                                     system_colors_[COLOR_WINDOW], 0x34);
    case kColorId_ResultsTableHoveredDivider:
      return color_utils::AlphaBlend(
          system_colors_[COLOR_WINDOWTEXT],
          GetSystemColor(kColorId_ResultsTableHoveredBackground), 0x34);
    case kColorId_ResultsTableSelectedDivider:
      return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHTTEXT],
                                     system_colors_[COLOR_HIGHLIGHT], 0x34);

    default:
      NOTREACHED();
      break;
  }
  return kInvalidColorIdColor;
}

void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
                                   Part part,
                                   State state,
                                   const gfx::Rect& rect,
                                   const ExtraParams& extra) const {
  // TODO(asvitkine): This path is pretty inefficient - for each paint operation
  //                  it creates a new offscreen bitmap Skia canvas. This can
  //                  be sped up by doing it only once per part/state and
  //                  keeping a cache of the resulting bitmaps.

  // Create an offscreen canvas that is backed by an HDC.
  skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
      skia::BitmapPlatformDevice::Create(
          rect.width(), rect.height(), false, NULL));
  DCHECK(device);
  if (!device)
    return;
  SkCanvas offscreen_canvas(device.get());
  DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));

  // Some of the Windows theme drawing operations do not write correct alpha
  // values for fully-opaque pixels; instead the pixels get alpha 0. This is
  // especially a problem on Windows XP or when using the Classic theme.
  //
  // To work-around this, mark all pixels with a placeholder value, to detect
  // which pixels get touched by the paint operation. After paint, set any
  // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
  const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
  offscreen_canvas.clear(placeholder);

  // Offset destination rects to have origin (0,0).
  gfx::Rect adjusted_rect(rect.size());
  ExtraParams adjusted_extra(extra);
  switch (part) {
    case kProgressBar:
      adjusted_extra.progress_bar.value_rect_x = 0;
      adjusted_extra.progress_bar.value_rect_y = 0;
      break;
    case kScrollbarHorizontalTrack:
    case kScrollbarVerticalTrack:
      adjusted_extra.scrollbar_track.track_x = 0;
      adjusted_extra.scrollbar_track.track_y = 0;
      break;
    default: break;
  }
  // Draw the theme controls using existing HDC-drawing code.
  PaintDirect(&offscreen_canvas,
              part,
              state,
              adjusted_rect,
              adjusted_extra);

  // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
  // necessary to have when drawing to a SkPicture.
  const SkBitmap& hdc_bitmap =
      offscreen_canvas.getDevice()->accessBitmap(false);
  SkBitmap bitmap;
  hdc_bitmap.copyTo(&bitmap, kPMColor_SkColorType);

  // Post-process the pixels to fix up the alpha values (see big comment above).
  const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
  const int pixel_count = rect.width() * rect.height();
  SkPMColor* pixels = bitmap.getAddr32(0, 0);
  for (int i = 0; i < pixel_count; i++) {
    if (pixels[i] == placeholder_value) {
      // Pixel wasn't touched - make it fully transparent.
      pixels[i] = SkPackARGB32(0, 0, 0, 0);
    } else if (SkGetPackedA32(pixels[i]) == 0) {
      // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
      pixels[i] = SkPackARGB32(0xFF,
                               SkGetPackedR32(pixels[i]),
                               SkGetPackedG32(pixels[i]),
                               SkGetPackedB32(pixels[i]));
    }
  }

  // Draw the offscreen bitmap to the destination canvas.
  canvas->drawBitmap(bitmap, rect.x(), rect.y());
}

HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
                                         HDC hdc,
                                         int part_id,
                                         int state_id,
                                         RECT* rect,
                                         int ts,
                                         SIZE* size) const {
  HANDLE handle = GetThemeHandle(theme_name);
  if (handle && get_theme_part_size_)
    return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);

  return E_NOTIMPL;
}

HRESULT NativeThemeWin::PaintButton(HDC hdc,
                                    State state,
                                    const ButtonExtraParams& extra,
                                    int part_id,
                                    int state_id,
                                    RECT* rect) const {
  HANDLE handle = GetThemeHandle(BUTTON);
  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);

  // Adjust classic_state based on part, state, and extras.
  int classic_state = extra.classic_state;
  switch (part_id) {
    case BP_CHECKBOX:
      classic_state |= DFCS_BUTTONCHECK;
      break;
    case BP_RADIOBUTTON:
      classic_state |= DFCS_BUTTONRADIO;
      break;
    case BP_PUSHBUTTON:
      classic_state |= DFCS_BUTTONPUSH;
      break;
    default:
      NOTREACHED() << "Unknown part_id: " << part_id;
      break;
  }

  switch (state) {
    case kDisabled:
      classic_state |= DFCS_INACTIVE;
      break;
    case kPressed:
      classic_state |= DFCS_PUSHED;
      break;
    case kNormal:
    case kHovered:
      break;
    default:
      NOTREACHED() << "Unknown state: " << state;
      break;
  }

  if (extra.checked)
    classic_state |= DFCS_CHECKED;

  // Draw it manually.
  // All pressed states have both low bits set, and no other states do.
  const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
  const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
  if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
    // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
    // button itself is shrunk by 1 pixel.
    HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
    if (brush) {
      FrameRect(hdc, rect, brush);
      InflateRect(rect, -1, -1);
    }
  }
  DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);

  // Draw the focus rectangle (the dotted line box) only on buttons.  For radio
  // and checkboxes, we let webkit draw the focus rectangle (orange glow).
  if ((BP_PUSHBUTTON == part_id) && focused) {
    // The focus rect is inside the button.  The exact number of pixels depends
    // on whether we're in classic mode or using uxtheme.
    if (handle && get_theme_content_rect_) {
      get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
    } else {
      InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
                  -GetSystemMetrics(SM_CYEDGE));
    }
    DrawFocusRect(hdc, rect);
  }

  // Classic theme doesn't support indeterminate checkboxes.  We draw
  // a recangle inside a checkbox like IE10 does.
  if (part_id == BP_CHECKBOX && extra.indeterminate) {
    RECT inner_rect = *rect;
    // "4 / 13" is same as IE10 in classic theme.
    int padding = (inner_rect.right - inner_rect.left) * 4 / 13;
    InflateRect(&inner_rect, -padding, -padding);
    int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
    FillRect(hdc, &inner_rect, GetSysColorBrush(color_index));
  }

  return S_OK;
}

HRESULT NativeThemeWin::PaintMenuSeparator(
    HDC hdc,
    const gfx::Rect& rect,
    const MenuSeparatorExtraParams& extra) const {
  RECT rect_win = rect.ToRECT();

  HANDLE handle = GetThemeHandle(MENU);
  if (handle && draw_theme_) {
    // Delta is needed for non-classic to move separator up slightly.
    --rect_win.top;
    --rect_win.bottom;
    return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
                       NULL);
  }

  DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
  return S_OK;
}

HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
                                        const gfx::Rect& rect) const {
  RECT rect_win = rect.ToRECT();
  HANDLE handle = GetThemeHandle(MENU);
  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win,
                       NULL);
  return E_NOTIMPL;
}

HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
                                       State state,
                                       const gfx::Rect& rect,
                                       const MenuArrowExtraParams& extra)
    const {
  int state_id = MSM_NORMAL;
  if (state == kDisabled)
    state_id = MSM_DISABLED;

  HANDLE handle = GetThemeHandle(MENU);
  RECT rect_win = rect.ToRECT();
  if (handle && draw_theme_) {
    if (extra.pointing_right) {
      return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
                         NULL);
    } else {
      // There is no way to tell the uxtheme API to draw a left pointing arrow;
      // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT.  But they
      // are needed for RTL locales on Vista.  So use a memory DC and mirror
      // the region with GDI's StretchBlt.
      gfx::Rect r(rect);
      base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
      base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
                                                                r.height()));
      base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap);
      // Copy and horizontally mirror the background from hdc into mem_dc. Use
      // a negative-width source rect, starting at the rightmost pixel.
      StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
                 hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
      // Draw the arrow.
      RECT theme_rect = {0, 0, r.width(), r.height()};
      HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU,
                                   state_id, &theme_rect, NULL);
      // Copy and mirror the result back into mem_dc.
      StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
                 mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
      return result;
    }
  }

  // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
  // left pointing arrow. This makes the following 'if' statement slightly
  // counterintuitive.
  UINT pfc_state;
  if (extra.pointing_right)
    pfc_state = DFCS_MENUARROW;
  else
    pfc_state = DFCS_MENUARROWRIGHT;
  return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
                           state);
}

HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
                                            const gfx::Rect& rect) const {
  HANDLE handle = GetThemeHandle(MENU);
  RECT rect_win = rect.ToRECT();
  if (handle && draw_theme_) {
    HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
                                 &rect_win, NULL);
    FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
    return result;
  }

  FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
  DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
  return S_OK;
}

HRESULT NativeThemeWin::PaintMenuCheck(
    HDC hdc,
    State state,
    const gfx::Rect& rect,
    const MenuCheckExtraParams& extra) const {
  HANDLE handle = GetThemeHandle(MENU);
  int state_id;
  if (extra.is_radio) {
    state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
  } else {
    state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
  }

  RECT rect_win = rect.ToRECT();
  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);

  return PaintFrameControl(hdc, rect, DFC_MENU,
                           extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
                           extra.is_selected, state);
}

HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
                                                 State state,
                                                 const gfx::Rect& rect) const {
  HANDLE handle = GetThemeHandle(MENU);
  int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
  RECT rect_win = rect.ToRECT();
  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
                       &rect_win, NULL);
  // Nothing to do for background.
  return S_OK;
}

HRESULT NativeThemeWin::PaintMenuItemBackground(
    HDC hdc,
    State state,
    const gfx::Rect& rect,
    const MenuItemExtraParams& extra) const {
  HANDLE handle = GetThemeHandle(MENU);
  RECT rect_win = rect.ToRECT();
  int state_id;
  switch (state) {
    case kNormal:
      state_id = MPI_NORMAL;
      break;
    case kDisabled:
      state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
      break;
    case kHovered:
      state_id = MPI_HOT;
      break;
    default:
      NOTREACHED() << "Invalid state " << state;
      break;
  }

  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);

  if (extra.is_selected)
    FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
  return S_OK;
}

HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
                                        Part part,
                                        State state,
                                        const gfx::Rect& rect,
                                        const ButtonExtraParams& extra) const {
  int state_id;
  switch (state) {
    case kDisabled:
      state_id = PBS_DISABLED;
      break;
    case kHovered:
      state_id = PBS_HOT;
      break;
    case kNormal:
      state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
      break;
    case kPressed:
      state_id = PBS_PRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state: " << state;
      break;
  }

  RECT rect_win = rect.ToRECT();
  return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
}

HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
                                         Part part,
                                         State state,
                                         const gfx::Rect& rect,
                                         const ButtonExtraParams& extra) const {
  int state_id;
  switch (state) {
    case kDisabled:
      state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
      break;
    case kHovered:
      state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
      break;
    case kNormal:
      state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
      break;
    case kPressed:
      state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state: " << state;
      break;
  }

  RECT rect_win = rect.ToRECT();
  return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
}

HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
                                      Part part,
                                      State state,
                                      const gfx::Rect& rect,
                                      const ButtonExtraParams& extra) const {
  int state_id;
  switch (state) {
    case kDisabled:
      state_id = extra.checked ? CBS_CHECKEDDISABLED :
          extra.indeterminate ? CBS_MIXEDDISABLED :
              CBS_UNCHECKEDDISABLED;
      break;
    case kHovered:
      state_id = extra.checked ? CBS_CHECKEDHOT :
          extra.indeterminate ? CBS_MIXEDHOT :
              CBS_UNCHECKEDHOT;
      break;
    case kNormal:
      state_id = extra.checked ? CBS_CHECKEDNORMAL :
          extra.indeterminate ? CBS_MIXEDNORMAL :
              CBS_UNCHECKEDNORMAL;
      break;
    case kPressed:
      state_id = extra.checked ? CBS_CHECKEDPRESSED :
          extra.indeterminate ? CBS_MIXEDPRESSED :
              CBS_UNCHECKEDPRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state: " << state;
      break;
  }

  RECT rect_win = rect.ToRECT();
  return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
}

HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
                                      State state,
                                      const gfx::Rect& rect,
                                      const MenuListExtraParams& extra) const {
  HANDLE handle = GetThemeHandle(MENULIST);
  RECT rect_win = rect.ToRECT();
  int state_id;
  switch (state) {
    case kNormal:
      state_id = CBXS_NORMAL;
      break;
    case kDisabled:
      state_id = CBXS_DISABLED;
      break;
    case kHovered:
      state_id = CBXS_HOT;
      break;
    case kPressed:
      state_id = CBXS_PRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state " << state;
      break;
  }

  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
                       NULL);

  // Draw it manually.
  DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
                   DFCS_SCROLLCOMBOBOX | extra.classic_state);
  return S_OK;
}

HRESULT NativeThemeWin::PaintScrollbarArrow(
    HDC hdc,
    Part part,
    State state,
    const gfx::Rect& rect,
    const ScrollbarArrowExtraParams& extra) const {
  static const int state_id_matrix[4][kMaxState] = {
      ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
      ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
      ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
      ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
  };
  HANDLE handle = GetThemeHandle(SCROLLBAR);
  RECT rect_win = rect.ToRECT();
  if (handle && draw_theme_) {
    int index = part - kScrollbarDownArrow;
    DCHECK(index >=0 && index < 4);
    int state_id = state_id_matrix[index][state];

    // Hovering means that the cursor is over the scroolbar, but not over the
    // specific arrow itself.  We don't want to show it "hot" mode, but only
    // in "hover" mode.
    if (state == kHovered && extra.is_hovering) {
      switch (part) {
        case kScrollbarDownArrow:
          state_id = ABS_DOWNHOVER;
          break;
        case kScrollbarLeftArrow:
          state_id = ABS_LEFTHOVER;
          break;
        case kScrollbarRightArrow:
          state_id = ABS_RIGHTHOVER;
          break;
        case kScrollbarUpArrow:
          state_id = ABS_UPHOVER;
          break;
        default:
          NOTREACHED() << "Invalid part: " << part;
          break;
      }
    }
    return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
  }

  int classic_state = DFCS_SCROLLDOWN;
  switch (part) {
    case kScrollbarDownArrow:
      classic_state = DFCS_SCROLLDOWN;
      break;
    case kScrollbarLeftArrow:
      classic_state = DFCS_SCROLLLEFT;
      break;
    case kScrollbarRightArrow:
      classic_state = DFCS_SCROLLRIGHT;
      break;
    case kScrollbarUpArrow:
      classic_state = DFCS_SCROLLUP;
      break;
    default:
      NOTREACHED() << "Invalid part: " << part;
      break;
  }
  switch (state) {
    case kDisabled:
      classic_state |= DFCS_INACTIVE;
      break;
    case kHovered:
      classic_state |= DFCS_HOT;
      break;
    case kNormal:
      break;
    case kPressed:
      classic_state |= DFCS_PUSHED;
      break;
    default:
      NOTREACHED() << "Invalid state: " << state;
      break;
  }
  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
  return S_OK;
}

HRESULT NativeThemeWin::PaintScrollbarThumb(
    HDC hdc,
    Part part,
    State state,
    const gfx::Rect& rect,
    const ScrollbarThumbExtraParams& extra) const {
  HANDLE handle = GetThemeHandle(SCROLLBAR);
  RECT rect_win = rect.ToRECT();
  int part_id;
  int state_id;

  switch (part) {
    case NativeTheme::kScrollbarHorizontalThumb:
      part_id = SBP_THUMBBTNHORZ;
      break;
    case NativeTheme::kScrollbarVerticalThumb:
      part_id = SBP_THUMBBTNVERT;
      break;
    case NativeTheme::kScrollbarHorizontalGripper:
      part_id = SBP_GRIPPERHORZ;
      break;
    case NativeTheme::kScrollbarVerticalGripper:
      part_id = SBP_GRIPPERVERT;
      break;
    default:
      NOTREACHED() << "Invalid part: " << part;
      break;
  }

  switch (state) {
    case kDisabled:
      state_id = SCRBS_DISABLED;
      break;
    case kHovered:
      state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
      break;
    case kNormal:
      state_id = SCRBS_NORMAL;
      break;
    case kPressed:
      state_id = SCRBS_PRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state: " << state;
      break;
  }

  if (handle && draw_theme_)
    return PaintScaledTheme(handle, hdc, part_id, state_id, rect);

  // Draw it manually.
  if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
    DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
  // Classic mode doesn't have a gripper.
  return S_OK;
}

HRESULT NativeThemeWin::PaintScrollbarTrack(
    SkCanvas* canvas,
    HDC hdc,
    Part part,
    State state,
    const gfx::Rect& rect,
    const ScrollbarTrackExtraParams& extra) const {
  HANDLE handle = GetThemeHandle(SCROLLBAR);
  RECT rect_win = rect.ToRECT();
  int part_id;
  int state_id;

  switch (part) {
    case NativeTheme::kScrollbarHorizontalTrack:
      part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
      break;
    case NativeTheme::kScrollbarVerticalTrack:
      part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
      break;
    default:
      NOTREACHED() << "Invalid part: " << part;
      break;
  }

  switch (state) {
    case kDisabled:
      state_id = SCRBS_DISABLED;
      break;
    case kHovered:
      state_id = SCRBS_HOVER;
      break;
    case kNormal:
      state_id = SCRBS_NORMAL;
      break;
    case kPressed:
      state_id = SCRBS_PRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state: " << state;
      break;
  }

  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);

  // Draw it manually.
  if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
      (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
    FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
  } else {
    SkPaint paint;
    RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
                                extra.track_height).ToRECT();
    SetCheckerboardShader(&paint, align_rect);
    canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
  }
  if (extra.classic_state & DFCS_PUSHED)
    InvertRect(hdc, &rect_win);
  return S_OK;
}

HRESULT NativeThemeWin::PaintSpinButton(
    HDC hdc,
    Part part,
    State state,
    const gfx::Rect& rect,
    const InnerSpinButtonExtraParams& extra) const {
  HANDLE handle = GetThemeHandle(SPIN);
  RECT rect_win = rect.ToRECT();
  int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
  int state_id;
  switch (state) {
    case kDisabled:
      state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
      break;
    case kHovered:
      state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
      break;
    case kNormal:
      state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
      break;
    case kPressed:
      state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state " << state;
      break;
  }

  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
  return S_OK;
}

HRESULT NativeThemeWin::PaintTrackbar(
    SkCanvas* canvas,
    HDC hdc,
    Part part,
    State state,
    const gfx::Rect& rect,
    const TrackbarExtraParams& extra) const {
  int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
  if (extra.vertical)
    part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;

  int state_id = 0;
  switch (state) {
    case kDisabled:
      state_id = TUS_DISABLED;
      break;
    case kHovered:
      state_id = TUS_HOT;
      break;
    case kNormal:
      state_id = TUS_NORMAL;
      break;
    case kPressed:
      state_id = TUS_PRESSED;
      break;
    default:
      NOTREACHED() << "Invalid state " << state;
      break;
  }

  // Make the channel be 4 px thick in the center of the supplied rect.  (4 px
  // matches what XP does in various menus; GetThemePartSize() doesn't seem to
  // return good values here.)
  RECT rect_win = rect.ToRECT();
  RECT channel_rect = rect.ToRECT();
  const int channel_thickness = 4;
  if (part_id == TKP_TRACK) {
    channel_rect.top +=
        ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
    channel_rect.bottom = channel_rect.top + channel_thickness;
  } else if (part_id == TKP_TRACKVERT) {
    channel_rect.left +=
        ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
    channel_rect.right = channel_rect.left + channel_thickness;
  }  // else this isn't actually a channel, so |channel_rect| == |rect|.

  HANDLE handle = GetThemeHandle(TRACKBAR);
  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);

  // Classic mode, draw it manually.
  if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
    DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
  } else if (part_id == TKP_THUMBVERT) {
    DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
  } else {
    // Split rect into top and bottom pieces.
    RECT top_section = rect.ToRECT();
    RECT bottom_section = rect.ToRECT();
    top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
    bottom_section.top = top_section.bottom;
    DrawEdge(hdc, &top_section, EDGE_RAISED,
             BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);

    // Split triangular piece into two diagonals.
    RECT& left_half = bottom_section;
    RECT right_half = bottom_section;
    right_half.left += ((bottom_section.right - bottom_section.left) / 2);
    left_half.right = right_half.left;
    DrawEdge(hdc, &left_half, EDGE_RAISED,
             BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
    DrawEdge(hdc, &right_half, EDGE_RAISED,
             BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);

    // If the button is pressed, draw hatching.
    if (extra.classic_state & DFCS_PUSHED) {
      SkPaint paint;
      SetCheckerboardShader(&paint, rect_win);

      // Fill all three pieces with the pattern.
      canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);

      SkScalar left_triangle_top = SkIntToScalar(left_half.top);
      SkScalar left_triangle_right = SkIntToScalar(left_half.right);
      SkPath left_triangle;
      left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
      left_triangle.lineTo(left_triangle_right, left_triangle_top);
      left_triangle.lineTo(left_triangle_right,
                           SkIntToScalar(left_half.bottom));
      left_triangle.close();
      canvas->drawPath(left_triangle, paint);

      SkScalar right_triangle_left = SkIntToScalar(right_half.left);
      SkScalar right_triangle_top = SkIntToScalar(right_half.top);
      SkPath right_triangle;
      right_triangle.moveTo(right_triangle_left, right_triangle_top);
      right_triangle.lineTo(SkIntToScalar(right_half.right),
                            right_triangle_top);
      right_triangle.lineTo(right_triangle_left,
                            SkIntToScalar(right_half.bottom));
      right_triangle.close();
      canvas->drawPath(right_triangle, paint);
    }
  }
  return S_OK;
}

HRESULT NativeThemeWin::PaintProgressBar(
    HDC hdc,
    const gfx::Rect& rect,
    const ProgressBarExtraParams& extra) const {
  // There is no documentation about the animation speed, frame-rate, nor
  // size of moving overlay of the indeterminate progress bar.
  // So we just observed real-world programs and guessed following parameters.
  const int kDeteminateOverlayPixelsPerSecond = 300;
  const int kDeteminateOverlayWidth = 120;
  const int kIndeterminateOverlayPixelsPerSecond =  175;
  const int kVistaIndeterminateOverlayWidth = 120;
  const int kXPIndeterminateOverlayWidth = 55;
  // The thickness of the bar frame inside |value_rect|
  const int kXPBarPadding = 3;

  RECT bar_rect = rect.ToRECT();
  RECT value_rect = gfx::Rect(extra.value_rect_x,
                              extra.value_rect_y,
                              extra.value_rect_width,
                              extra.value_rect_height).ToRECT();

  bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
  HANDLE handle = GetThemeHandle(PROGRESS);
  if (handle && draw_theme_ && draw_theme_ex_) {
    draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);

    int bar_width = bar_rect.right - bar_rect.left;
    if (extra.determinate) {
      // TODO(morrita): this RTL guess can be wrong.
      // We should pass the direction from WebKit side.
      bool is_rtl = (bar_rect.right == value_rect.right &&
                     bar_rect.left != value_rect.left);
      // We should care the direction here because PP_CNUNK painting
      // is asymmetric.
      DTBGOPTS value_draw_options;
      value_draw_options.dwSize = sizeof(DTBGOPTS);
      value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
      value_draw_options.rcClip = bar_rect;

      if (pre_vista) {
        // On XP, progress bar is chunk-style and has no glossy effect.
        // We need to shrink destination rect to fit the part inside the bar
        // with an appropriate margin.
        RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
        draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
                       &shrunk_value_rect, &value_draw_options);
      } else  {
        // On Vista or later, the progress bar part has a
        // single-block value part. It also has glossy effect.
        // And the value part has exactly same height as the bar part
        // so we don't need to shrink the rect.
        draw_theme_ex_(handle, hdc, PP_FILL, 0,
                       &value_rect, &value_draw_options);

        int dx = ComputeAnimationProgress(bar_width,
                                          kDeteminateOverlayWidth,
                                          kDeteminateOverlayPixelsPerSecond,
                                          extra.animated_seconds);
        RECT overlay_rect = value_rect;
        overlay_rect.left += dx;
        overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
        draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
      }
    } else {
      // A glossy overlay for indeterminate progress bar has small pause
      // after each animation. We emulate this by adding an invisible margin
      // the animation has to traverse.
      int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
      int overlay_width = pre_vista ?
          kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
      int dx = ComputeAnimationProgress(width_with_margin,
                                        overlay_width,
                                        kIndeterminateOverlayPixelsPerSecond,
                                        extra.animated_seconds);
      RECT overlay_rect = bar_rect;
      overlay_rect.left += dx;
      overlay_rect.right = overlay_rect.left + overlay_width;
      if (pre_vista) {
        RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
        RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
        draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
      } else {
        draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
      }
    }

    return S_OK;
  }

  HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
  HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
  FillRect(hdc, &bar_rect, bg_brush);
  FillRect(hdc, &value_rect, fg_brush);
  DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
  return S_OK;
}

HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
                                                 const gfx::Rect& rect) const {
  HANDLE handle = GetThemeHandle(STATUS);
  RECT rect_win = rect.ToRECT();
  if (handle && draw_theme_) {
    // Paint the status bar gripper.  There doesn't seem to be a
    // standard gripper in Windows for the space between
    // scrollbars.  This is pretty close, but it's supposed to be
    // painted over a status bar.
    return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
  }

  // Draw a windows classic scrollbar gripper.
  DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
  return S_OK;
}

HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
                                                const gfx::Rect& rect) const {
  HANDLE handle = GetThemeHandle(TAB);
  RECT rect_win = rect.ToRECT();
  if (handle && draw_theme_)
    return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);

  // Classic just renders a flat color background.
  FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
  return S_OK;
}

HRESULT NativeThemeWin::PaintTextField(
    HDC hdc,
    Part part,
    State state,
    const gfx::Rect& rect,
    const TextFieldExtraParams& extra) const {
  int part_id = EP_EDITTEXT;
  int state_id = ETS_NORMAL;
  switch (state) {
    case kNormal:
      if (extra.is_read_only) {
        state_id = ETS_READONLY;
      } else if (extra.is_focused) {
        state_id = ETS_FOCUSED;
      } else {
        state_id = ETS_NORMAL;
      }
      break;
    case kHovered:
      state_id = ETS_HOT;
      break;
    case kPressed:
      state_id = ETS_SELECTED;
      break;
    case kDisabled:
      state_id = ETS_DISABLED;
      break;
    default:
      NOTREACHED() << "Invalid state: " << state;
      break;
  }

  RECT rect_win = rect.ToRECT();
  return PaintTextField(hdc, part_id, state_id, extra.classic_state,
                        &rect_win,
                        skia::SkColorToCOLORREF(extra.background_color),
                        extra.fill_content_area, extra.draw_edges);
}

HRESULT NativeThemeWin::PaintTextField(HDC hdc,
                                       int part_id,
                                       int state_id,
                                       int classic_state,
                                       RECT* rect,
                                       COLORREF color,
                                       bool fill_content_area,
                                       bool draw_edges) const {
  // TODO(ojan): http://b/1210017 Figure out how to give the ability to
  // exclude individual edges from being drawn.

  HANDLE handle = GetThemeHandle(TEXTFIELD);
  // TODO(mpcomplete): can we detect if the color is specified by the user,
  // and if not, just use the system color?
  // CreateSolidBrush() accepts a RGB value but alpha must be 0.
  HBRUSH bg_brush = CreateSolidBrush(color);
  HRESULT hr;
  // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
  // draw_theme_ex_ is NULL and draw_theme_ is non-null.
  if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
    if (draw_theme_ex_) {
      static const DTBGOPTS omit_border_options = {
        sizeof(DTBGOPTS),
        DTBG_OMITBORDER,
        { 0, 0, 0, 0 }
      };
      const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
      hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
    } else {
      hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
    }

    // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
    if (fill_content_area && get_theme_content_rect_) {
      RECT content_rect;
      hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
                                   &content_rect);
      FillRect(hdc, &content_rect, bg_brush);
    }
  } else {
    // Draw it manually.
    if (draw_edges)
      DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);

    if (fill_content_area) {
      FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
                   reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
    }
    hr = S_OK;
  }
  DeleteObject(bg_brush);
  return hr;
}

HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
                                         HDC hdc,
                                         int part_id,
                                         int state_id,
                                         const gfx::Rect& rect) const {
  // Correct the scaling and positioning of sub-components such as scrollbar
  // arrows and thumb grippers in the event that the world transform applies
  // scaling (e.g. in high-DPI mode).
  XFORM save_transform;
  if (GetWorldTransform(hdc, &save_transform)) {
    float scale = save_transform.eM11;
    if (scale != 1 && save_transform.eM12 == 0) {
      ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
      gfx::Rect scaled_rect = gfx::ToEnclosedRect(
          gfx::ScaleRect(rect, scale));
      RECT bounds = gfx::Rect(scaled_rect.x() + save_transform.eDx,
                              scaled_rect.y() + save_transform.eDy,
                              scaled_rect.width(),
                              scaled_rect.height()).ToRECT();
      HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds,
                                   NULL);
      SetWorldTransform(hdc, &save_transform);
      return result;
    }
  }
  RECT bounds = rect.ToRECT();
  return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
}

// static
NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
  ThemeName name;
  switch (part) {
    case kCheckbox:
    case kRadio:
    case kPushButton:
      name = BUTTON;
      break;
    case kInnerSpinButton:
      name = SPIN;
      break;
    case kMenuCheck:
    case kMenuPopupGutter:
    case kMenuList:
    case kMenuPopupArrow:
    case kMenuPopupSeparator:
      name = MENU;
      break;
    case kProgressBar:
      name = PROGRESS;
      break;
    case kScrollbarDownArrow:
    case kScrollbarLeftArrow:
    case kScrollbarRightArrow:
    case kScrollbarUpArrow:
    case kScrollbarHorizontalThumb:
    case kScrollbarVerticalThumb:
    case kScrollbarHorizontalTrack:
    case kScrollbarVerticalTrack:
      name = SCROLLBAR;
      break;
    case kSliderTrack:
    case kSliderThumb:
      name = TRACKBAR;
      break;
    case kTextField:
      name = TEXTFIELD;
      break;
    case kWindowResizeGripper:
      name = STATUS;
      break;
    default:
      NOTREACHED() << "Invalid part: " << part;
      break;
  }
  return name;
}

// static
int NativeThemeWin::GetWindowsPart(Part part,
                                   State state,
                                   const ExtraParams& extra) {
  int part_id;
  switch (part) {
    case kCheckbox:
      part_id = BP_CHECKBOX;
      break;
    case kMenuCheck:
      part_id = MENU_POPUPCHECK;
      break;
    case kMenuPopupArrow:
      part_id = MENU_POPUPSUBMENU;
      break;
    case kMenuPopupGutter:
      part_id = MENU_POPUPGUTTER;
      break;
    case kMenuPopupSeparator:
      part_id = MENU_POPUPSEPARATOR;
      break;
    case kPushButton:
      part_id = BP_PUSHBUTTON;
      break;
    case kRadio:
      part_id = BP_RADIOBUTTON;
      break;
    case kWindowResizeGripper:
      part_id = SP_GRIPPER;
      break;
    case kScrollbarDownArrow:
    case kScrollbarLeftArrow:
    case kScrollbarRightArrow:
    case kScrollbarUpArrow:
      part_id = SBP_ARROWBTN;
      break;
    case kScrollbarHorizontalThumb:
      part_id = SBP_THUMBBTNHORZ;
      break;
    case kScrollbarVerticalThumb:
      part_id = SBP_THUMBBTNVERT;
      break;
    default:
      NOTREACHED() << "Invalid part: " << part;
      break;
  }
  return part_id;
}

int NativeThemeWin::GetWindowsState(Part part,
                                    State state,
                                    const ExtraParams& extra) {
  int state_id;
  switch (part) {
    case kCheckbox:
      switch (state) {
        case kNormal:
          state_id = CBS_UNCHECKEDNORMAL;
          break;
        case kHovered:
          state_id = CBS_UNCHECKEDHOT;
          break;
        case kPressed:
          state_id = CBS_UNCHECKEDPRESSED;
          break;
        case kDisabled:
          state_id = CBS_UNCHECKEDDISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kMenuCheck:
      switch (state) {
        case kNormal:
        case kHovered:
        case kPressed:
          state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
                                               : MC_CHECKMARKNORMAL;
          break;
        case kDisabled:
          state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
                                               : MC_CHECKMARKDISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kMenuPopupArrow:
    case kMenuPopupGutter:
    case kMenuPopupSeparator:
      switch (state) {
        case kNormal:
          state_id = MBI_NORMAL;
          break;
        case kHovered:
          state_id = MBI_HOT;
          break;
        case kPressed:
          state_id = MBI_PUSHED;
          break;
        case kDisabled:
          state_id = MBI_DISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kPushButton:
      switch (state) {
        case kNormal:
          state_id = PBS_NORMAL;
          break;
        case kHovered:
          state_id = PBS_HOT;
          break;
        case kPressed:
          state_id = PBS_PRESSED;
          break;
        case kDisabled:
          state_id = PBS_DISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kRadio:
      switch (state) {
        case kNormal:
          state_id = RBS_UNCHECKEDNORMAL;
          break;
        case kHovered:
          state_id = RBS_UNCHECKEDHOT;
          break;
        case kPressed:
          state_id = RBS_UNCHECKEDPRESSED;
          break;
        case kDisabled:
          state_id = RBS_UNCHECKEDDISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kWindowResizeGripper:
      switch (state) {
        case kNormal:
        case kHovered:
        case kPressed:
        case kDisabled:
          state_id = 1;  // gripper has no windows state
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kScrollbarDownArrow:
      switch (state) {
        case kNormal:
          state_id = ABS_DOWNNORMAL;
          break;
        case kHovered:
          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
              ABS_DOWNHOT : ABS_DOWNHOVER;
          break;
        case kPressed:
          state_id = ABS_DOWNPRESSED;
          break;
        case kDisabled:
          state_id = ABS_DOWNDISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kScrollbarLeftArrow:
      switch (state) {
        case kNormal:
          state_id = ABS_LEFTNORMAL;
          break;
        case kHovered:
          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
              ABS_LEFTHOT : ABS_LEFTHOVER;
          break;
        case kPressed:
          state_id = ABS_LEFTPRESSED;
          break;
        case kDisabled:
          state_id = ABS_LEFTDISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kScrollbarRightArrow:
      switch (state) {
        case kNormal:
          state_id = ABS_RIGHTNORMAL;
          break;
        case kHovered:
          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
              ABS_RIGHTHOT : ABS_RIGHTHOVER;
          break;
        case kPressed:
          state_id = ABS_RIGHTPRESSED;
          break;
        case kDisabled:
          state_id = ABS_RIGHTDISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kScrollbarUpArrow:
      switch (state) {
        case kNormal:
          state_id = ABS_UPNORMAL;
          break;
        case kHovered:
          // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
              ABS_UPHOT : ABS_UPHOVER;
          break;
        case kPressed:
          state_id = ABS_UPPRESSED;
          break;
        case kDisabled:
          state_id = ABS_UPDISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    case kScrollbarHorizontalThumb:
    case kScrollbarVerticalThumb:
      switch (state) {
        case kNormal:
          state_id = SCRBS_NORMAL;
          break;
        case kHovered:
          // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
          state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
              SCRBS_HOT : SCRBS_HOVER;
          break;
        case kPressed:
          state_id = SCRBS_PRESSED;
          break;
        case kDisabled:
          state_id = SCRBS_DISABLED;
          break;
        default:
          NOTREACHED() << "Invalid state: " << state;
          break;
      }
      break;
    default:
      NOTREACHED() << "Invalid part: " << part;
      break;
  }
  return state_id;
}

HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
                                    int part_id,
                                    int state_id,
                                    int prop_id,
                                    int *value) const {
  HANDLE handle = GetThemeHandle(theme);
  if (handle && get_theme_int_)
    return get_theme_int_(handle, part_id, state_id, prop_id, value);
  return E_NOTIMPL;
}

HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
                                          const gfx::Rect& rect,
                                          UINT type,
                                          UINT state,
                                          bool is_selected,
                                          State control_state) const {
  const int width = rect.width();
  const int height = rect.height();

  // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
  base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));

  if (mask_bitmap == NULL)
    return E_OUTOFMEMORY;

  base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
  base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap);
  RECT local_rect = { 0, 0, width, height };
  DrawFrameControl(bitmap_dc, &local_rect, type, state);

  // We're going to use BitBlt with a b&w mask. This results in using the dest
  // dc's text color for the black bits in the mask, and the dest dc's
  // background color for the white bits in the mask. DrawFrameControl draws the
  // check in black, and the background in white.
  int bg_color_key;
  int text_color_key;
  switch (control_state) {
    case NativeTheme::kHovered:
      bg_color_key = COLOR_HIGHLIGHT;
      text_color_key = COLOR_HIGHLIGHTTEXT;
      break;
    case NativeTheme::kNormal:
      bg_color_key = COLOR_MENU;
      text_color_key = COLOR_MENUTEXT;
      break;
    case NativeTheme::kDisabled:
      bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
      text_color_key = COLOR_GRAYTEXT;
      break;
    default:
      NOTREACHED();
      bg_color_key = COLOR_MENU;
      text_color_key = COLOR_MENUTEXT;
      break;
  }
  COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
  COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
  BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY);
  SetBkColor(hdc, old_bg_color);
  SetTextColor(hdc, old_text_color);

  return S_OK;
}

HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
  if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
    return 0;

  if (theme_handles_[theme_name])
    return theme_handles_[theme_name];

  // Not found, try to load it.
  HANDLE handle = 0;
  switch (theme_name) {
  case BUTTON:
    handle = open_theme_(NULL, L"Button");
    break;
  case LIST:
    handle = open_theme_(NULL, L"Listview");
    break;
  case MENU:
    handle = open_theme_(NULL, L"Menu");
    break;
  case MENULIST:
    handle = open_theme_(NULL, L"Combobox");
    break;
  case SCROLLBAR:
    handle = open_theme_(NULL, L"Scrollbar");
    break;
  case STATUS:
    handle = open_theme_(NULL, L"Status");
    break;
  case TAB:
    handle = open_theme_(NULL, L"Tab");
    break;
  case TEXTFIELD:
    handle = open_theme_(NULL, L"Edit");
    break;
  case TRACKBAR:
    handle = open_theme_(NULL, L"Trackbar");
    break;
  case WINDOW:
    handle = open_theme_(NULL, L"Window");
    break;
  case PROGRESS:
    handle = open_theme_(NULL, L"Progress");
    break;
  case SPIN:
    handle = open_theme_(NULL, L"Spin");
    break;
  default:
    NOTREACHED();
  }
  theme_handles_[theme_name] = handle;
  return handle;
}

}  // namespace ui