WebCore/rendering/RenderThemeWin.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/RenderThemeWin.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,997 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+#include "RenderThemeWin.h"
+
+#include "CSSValueKeywords.h"
+#include "Element.h"
+#include "Frame.h"
+#include "GraphicsContext.h"
+#include "RenderSlider.h"
+#include "Settings.h"
+#include "SoftLinking.h"
+#include "SystemInfo.h"
+#include "UserAgentStyleSheets.h"
+
+#if ENABLE(VIDEO)
+#include "RenderMediaControls.h"
+#endif
+
+#include <tchar.h>
+
+/* 
+ * The following constants are used to determine how a widget is drawn using
+ * Windows' Theme API. For more information on theme parts and states see
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
+ */
+
+// Generic state constants
+#define TS_NORMAL    1
+#define TS_HOVER     2
+#define TS_ACTIVE    3
+#define TS_DISABLED  4
+#define TS_FOCUSED   5
+
+// Button constants
+#define BP_BUTTON    1
+#define BP_RADIO     2
+#define BP_CHECKBOX  3
+
+// Textfield constants
+#define TFP_TEXTFIELD 1
+#define EP_EDITBORDER_NOSCROLL 6
+#define TFS_READONLY  6
+
+// ComboBox constants (from vsstyle.h)
+#define CP_DROPDOWNBUTTON 1
+#define CP_BORDER 4
+#define CP_READONLY 5
+#define CP_DROPDOWNBUTTONRIGHT 6
+
+// TrackBar (slider) parts
+#define TKP_TRACK       1
+#define TKP_TRACKVERT   2
+
+// TrackBar (slider) thumb parts
+#define TKP_THUMBBOTTOM 4
+#define TKP_THUMBTOP    5
+#define TKP_THUMBLEFT   7
+#define TKP_THUMBRIGHT  8
+
+// Trackbar (slider) thumb states
+#define TUS_NORMAL      1
+#define TUS_HOT         2
+#define TUS_PRESSED     3
+#define TUS_FOCUSED     4
+#define TUS_DISABLED    5
+
+// button states
+#define PBS_NORMAL      1
+#define PBS_HOT         2
+#define PBS_PRESSED     3
+#define PBS_DISABLED    4
+#define PBS_DEFAULTED   5
+
+SOFT_LINK_LIBRARY(uxtheme)
+SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList))
+SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme))
+SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect))
+SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ())
+SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId))
+
+static bool haveTheme;
+
+static const unsigned vistaMenuListButtonOutset = 1;
+
+using namespace std;
+
+namespace WebCore {
+
+// This is the fixed width IE and Firefox use for buttons on dropdown menus
+static const int dropDownButtonWidth = 17;
+
+static const int shell32MagnifierIconIndex = 22;
+
+// Default font size to match Firefox.
+static const float defaultControlFontPixelSize = 13;
+
+static const float defaultCancelButtonSize = 9;
+static const float minCancelButtonSize = 5;
+static const float maxCancelButtonSize = 21;
+static const float defaultSearchFieldResultsDecorationSize = 13;
+static const float minSearchFieldResultsDecorationSize = 9;
+static const float maxSearchFieldResultsDecorationSize = 30;
+static const float defaultSearchFieldResultsButtonWidth = 18;
+
+static bool gWebKitIsBeingUnloaded;
+
+static bool documentIsInApplicationChromeMode(const Document* document)
+{
+    Settings* settings = document->settings();
+    return settings && settings->inApplicationChromeMode();
+}
+
+void RenderThemeWin::setWebKitIsBeingUnloaded()
+{
+    gWebKitIsBeingUnloaded = true;
+}
+
+PassRefPtr<RenderTheme> RenderThemeWin::create()
+{
+    return adoptRef(new RenderThemeWin);
+}
+
+#if !USE(SAFARI_THEME)
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
+{
+    static RenderTheme* winTheme = RenderThemeWin::create().releaseRef();
+    return winTheme;
+}
+#endif
+
+RenderThemeWin::RenderThemeWin()
+    : m_buttonTheme(0)
+    , m_textFieldTheme(0)
+    , m_menuListTheme(0)
+    , m_sliderTheme(0)
+{
+    haveTheme = uxthemeLibrary() && IsThemeActive();
+}
+
+RenderThemeWin::~RenderThemeWin()
+{
+    // If WebKit is being unloaded, then uxtheme.dll is no longer available.
+    if (gWebKitIsBeingUnloaded || !uxthemeLibrary())
+        return;
+    close();
+}
+
+HANDLE RenderThemeWin::buttonTheme() const
+{
+    if (haveTheme && !m_buttonTheme)
+        m_buttonTheme = OpenThemeData(0, L"Button");
+    return m_buttonTheme;
+}
+
+HANDLE RenderThemeWin::textFieldTheme() const
+{
+    if (haveTheme && !m_textFieldTheme)
+        m_textFieldTheme = OpenThemeData(0, L"Edit");
+    return m_textFieldTheme;
+}
+
+HANDLE RenderThemeWin::menuListTheme() const
+{
+    if (haveTheme && !m_menuListTheme)
+        m_menuListTheme = OpenThemeData(0, L"ComboBox");
+    return m_menuListTheme;
+}
+
+HANDLE RenderThemeWin::sliderTheme() const
+{
+    if (haveTheme && !m_sliderTheme)
+        m_sliderTheme = OpenThemeData(0, L"TrackBar");
+    return m_sliderTheme;
+}
+
+void RenderThemeWin::close()
+{
+    // This method will need to be called when the OS theme changes to flush our cached themes.
+    if (m_buttonTheme)
+        CloseThemeData(m_buttonTheme);
+    if (m_textFieldTheme)
+        CloseThemeData(m_textFieldTheme);
+    if (m_menuListTheme)
+        CloseThemeData(m_menuListTheme);
+    if (m_sliderTheme)
+        CloseThemeData(m_sliderTheme);
+    m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = 0;
+
+    haveTheme = uxthemeLibrary() && IsThemeActive();
+}
+
+void RenderThemeWin::themeChanged()
+{
+    close();
+}
+
+String RenderThemeWin::extraDefaultStyleSheet()
+{
+    return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet));
+}
+
+String RenderThemeWin::extraQuirksStyleSheet()
+{
+    return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet));
+}
+
+bool RenderThemeWin::supportsHover(const RenderStyle*) const
+{
+    // The Classic/2k look has no hover effects.
+    return haveTheme;
+}
+
+Color RenderThemeWin::platformActiveSelectionBackgroundColor() const
+{
+    COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
+    return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const
+{
+    // This color matches Firefox.
+    return Color(176, 176, 176);
+}
+
+Color RenderThemeWin::platformActiveSelectionForegroundColor() const
+{
+    COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
+    return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+Color RenderThemeWin::platformInactiveSelectionForegroundColor() const
+{
+    return platformActiveSelectionForegroundColor();
+}
+
+static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize)
+{    
+    fontDescription.setIsAbsoluteSize(true);
+    fontDescription.setGenericFamily(FontDescription::NoFamily);
+    fontDescription.firstFamily().setFamily(String(logFont.lfFaceName));
+    fontDescription.setSpecifiedSize(fontSize);
+    fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight.
+    fontDescription.setItalic(logFont.lfItalic);
+}
+
+static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont)
+{   
+    fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight));
+}
+
+void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const
+{
+    static FontDescription captionFont;
+    static FontDescription controlFont;
+    static FontDescription smallCaptionFont;
+    static FontDescription menuFont;
+    static FontDescription iconFont;
+    static FontDescription messageBoxFont;
+    static FontDescription statusBarFont;
+    static FontDescription systemFont;
+    
+    static bool initialized;
+    static NONCLIENTMETRICS ncm;
+
+    if (!initialized) {
+        initialized = true;
+        ncm.cbSize = sizeof(NONCLIENTMETRICS);
+        ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
+    }
+ 
+    switch (propId) {
+        case CSSValueIcon: {
+            if (!iconFont.isAbsoluteSize()) {
+                LOGFONT logFont;
+                ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0);
+                fillFontDescription(iconFont, logFont);
+            }
+            fontDescription = iconFont;
+            break;
+        }
+        case CSSValueMenu:
+            if (!menuFont.isAbsoluteSize())
+                fillFontDescription(menuFont, ncm.lfMenuFont);
+            fontDescription = menuFont;
+            break;
+        case CSSValueMessageBox:
+            if (!messageBoxFont.isAbsoluteSize())
+                fillFontDescription(messageBoxFont, ncm.lfMessageFont);
+            fontDescription = messageBoxFont;
+            break;
+        case CSSValueStatusBar:
+            if (!statusBarFont.isAbsoluteSize())
+                fillFontDescription(statusBarFont, ncm.lfStatusFont);
+            fontDescription = statusBarFont;
+            break;
+        case CSSValueCaption:
+            if (!captionFont.isAbsoluteSize())
+                fillFontDescription(captionFont, ncm.lfCaptionFont);
+            fontDescription = captionFont;
+            break;
+        case CSSValueSmallCaption:
+            if (!smallCaptionFont.isAbsoluteSize())
+                fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont);
+            fontDescription = smallCaptionFont;
+            break;
+        case CSSValueWebkitSmallControl:
+        case CSSValueWebkitMiniControl: // Just map to small.
+        case CSSValueWebkitControl: // Just map to small.
+            if (!controlFont.isAbsoluteSize()) {
+                HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
+                if (hGDI) {
+                    LOGFONT logFont;
+                    if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
+                        fillFontDescription(controlFont, logFont, defaultControlFontPixelSize);
+                }
+            }
+            fontDescription = controlFont;
+            break;
+        default: { // Everything else uses the stock GUI font.
+            if (!systemFont.isAbsoluteSize()) {
+                HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
+                if (hGDI) {
+                    LOGFONT logFont;
+                    if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
+                        fillFontDescription(systemFont, logFont);
+                }
+            }
+            fontDescription = systemFont;
+        }
+    }
+}
+
+bool RenderThemeWin::supportsFocus(ControlPart appearance) const
+{
+    switch (appearance) {
+        case PushButtonPart:
+        case ButtonPart:
+        case DefaultButtonPart:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const
+{
+    return supportsFocus(style->appearance());
+}
+
+unsigned RenderThemeWin::determineClassicState(RenderObject* o)
+{
+    unsigned state = 0;
+    switch (o->style()->appearance()) {
+        case PushButtonPart:
+        case ButtonPart:
+        case DefaultButtonPart:
+            state = DFCS_BUTTONPUSH;
+            if (!isEnabled(o))
+                state |= DFCS_INACTIVE;
+            else if (isPressed(o))
+                state |= DFCS_PUSHED;
+            break;
+        case RadioPart:
+        case CheckboxPart:
+            state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK;
+            if (isChecked(o))
+                state |= DFCS_CHECKED;
+            if (!isEnabled(o))
+                state |= DFCS_INACTIVE;
+            else if (isPressed(o))
+                state |= DFCS_PUSHED;
+            break;
+        case MenulistPart:
+            state = DFCS_SCROLLCOMBOBOX;
+            if (!isEnabled(o))
+                state |= DFCS_INACTIVE;
+            else if (isPressed(o))
+                state |= DFCS_PUSHED;
+        default:
+            break;
+    }
+    return state;
+}
+
+unsigned RenderThemeWin::determineState(RenderObject* o)
+{
+    unsigned result = TS_NORMAL;
+    ControlPart appearance = o->style()->appearance();
+    if (!isEnabled(o))
+        result = TS_DISABLED;
+    else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance))
+        result = TFS_READONLY; // Readonly is supported on textfields.
+    else if (isPressed(o)) // Active overrides hover and focused.
+        result = TS_ACTIVE;
+    else if (supportsFocus(appearance) && isFocused(o))
+        result = TS_FOCUSED;
+    else if (isHovered(o))
+        result = TS_HOVER;
+    if (isChecked(o))
+        result += 4; // 4 unchecked states, 4 checked states.
+    return result;
+}
+
+unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o)
+{
+    unsigned result = TUS_NORMAL;
+    if (!isEnabled(o->parent()))
+        result = TUS_DISABLED;
+    else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
+        result = TUS_FOCUSED;
+    else if (toRenderSlider(o->parent())->inDragMode())
+        result = TUS_PRESSED;
+    else if (isHovered(o))
+        result = TUS_HOT;
+    return result;
+}
+
+unsigned RenderThemeWin::determineButtonState(RenderObject* o)
+{
+    unsigned result = PBS_NORMAL;
+    if (!isEnabled(o))
+        result = PBS_DISABLED;
+    else if (isPressed(o))
+        result = PBS_PRESSED;
+    else if (supportsFocus(o->style()->appearance()) && isFocused(o))
+        result = PBS_DEFAULTED;
+    else if (isHovered(o))
+        result = PBS_HOT;
+    else if (isDefault(o))
+        result = PBS_DEFAULTED;
+    return result;
+}
+
+ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o)
+{
+    ThemeData result;
+    switch (o->style()->appearance()) {
+        case PushButtonPart:
+        case ButtonPart:
+        case DefaultButtonPart:
+        case CheckboxPart:
+        case RadioPart:
+            result.m_part = DFC_BUTTON;
+            result.m_state = determineClassicState(o);
+            break;
+        case MenulistPart:
+            result.m_part = DFC_SCROLL;
+            result.m_state = determineClassicState(o);
+            break;
+        case SearchFieldPart:
+        case TextFieldPart:
+        case TextAreaPart:
+            result.m_part = TFP_TEXTFIELD;
+            result.m_state = determineState(o);
+            break;
+        case SliderHorizontalPart:
+            result.m_part = TKP_TRACK;
+            result.m_state = TS_NORMAL;
+            break;
+        case SliderVerticalPart:
+            result.m_part = TKP_TRACKVERT;
+            result.m_state = TS_NORMAL;
+            break;
+        case SliderThumbHorizontalPart:
+            result.m_part = TKP_THUMBBOTTOM;
+            result.m_state = determineSliderThumbState(o);
+            break;
+        case SliderThumbVerticalPart:
+            result.m_part = TKP_THUMBRIGHT;
+            result.m_state = determineSliderThumbState(o);
+            break;
+        default:
+            break;
+    }
+    return result;
+}
+
+ThemeData RenderThemeWin::getThemeData(RenderObject* o)
+{
+    if (!haveTheme)
+        return getClassicThemeData(o);
+
+    ThemeData result;
+    switch (o->style()->appearance()) {
+        case PushButtonPart:
+        case ButtonPart:
+        case DefaultButtonPart:
+            result.m_part = BP_BUTTON;
+            result.m_state = determineButtonState(o);
+            break;
+        case CheckboxPart:
+            result.m_part = BP_CHECKBOX;
+            result.m_state = determineState(o);
+            break;
+        case MenulistPart:
+        case MenulistButtonPart:
+            result.m_part = isRunningOnVistaOrLater() ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON;
+            if (isRunningOnVistaOrLater() && documentIsInApplicationChromeMode(o->document())) {
+                // The "readonly" look we use in application chrome mode
+                // only uses a "normal" look for the drop down button.
+                result.m_state = TS_NORMAL;
+            } else
+                result.m_state = determineState(o);
+            break;
+        case RadioPart:
+            result.m_part = BP_RADIO;
+            result.m_state = determineState(o);
+            break;
+        case SearchFieldPart:
+        case TextFieldPart:
+        case TextAreaPart:
+            result.m_part = isRunningOnVistaOrLater() ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD;
+            result.m_state = determineState(o);
+            break;
+        case SliderHorizontalPart:
+            result.m_part = TKP_TRACK;
+            result.m_state = TS_NORMAL;
+            break;
+        case SliderVerticalPart:
+            result.m_part = TKP_TRACKVERT;
+            result.m_state = TS_NORMAL;
+            break;
+        case SliderThumbHorizontalPart:
+            result.m_part = TKP_THUMBBOTTOM;
+            result.m_state = determineSliderThumbState(o);
+            break;
+        case SliderThumbVerticalPart:
+            result.m_part = TKP_THUMBRIGHT;
+            result.m_state = determineSliderThumbState(o);
+            break;
+    }
+
+    return result;
+}
+
+static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r)
+{
+    bool alphaBlend = false;
+    if (theme)
+        alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state);
+    HDC hdc = context->getWindowsContext(r, alphaBlend);
+    RECT widgetRect = r;
+    if (theme)
+        DrawThemeBackground(theme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL);
+    else {
+        if (themeData.m_part == TFP_TEXTFIELD) {
+            ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+            if (themeData.m_state == TS_DISABLED || themeData.m_state ==  TFS_READONLY)
+                ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1));
+            else
+                ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1));
+        } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) {
+            ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+            ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
+        } else if ((o->style()->appearance() == SliderThumbHorizontalPart ||
+                    o->style()->appearance() == SliderThumbVerticalPart) && 
+                   (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP || 
+                    themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) {
+            ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+            if (themeData.m_state == TUS_DISABLED) {
+                static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
+                HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits);
+                if (patternBmp) {
+                    HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp);
+                    COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE));
+                    COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT));
+                    POINT p;
+                    ::GetViewportOrgEx(hdc, &p);
+                    ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL);
+                    HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush);
+                    ::FillRect(hdc, &widgetRect, brush);
+                    ::SetTextColor(hdc, oldForeColor);
+                    ::SetBkColor(hdc, oldBackColor);
+                    ::SelectObject(hdc, oldBrush);
+                    ::DeleteObject(brush); 
+                } else
+                    ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT);
+                ::DeleteObject(patternBmp);
+            }
+        } else {
+            // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists.
+            if (o->style()->appearance() == DefaultButtonPart) {
+                HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
+                ::FrameRect(hdc, &widgetRect, brush);
+                ::InflateRect(&widgetRect, -1, -1);
+                ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE);
+            }
+            ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state);
+        }
+    }
+    context->releaseWindowsContext(hdc, r, alphaBlend);
+}
+
+bool RenderThemeWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{  
+    drawControl(i.context,  o, buttonTheme(), getThemeData(o), r);
+    return false;
+}
+
+void RenderThemeWin::setCheckboxSize(RenderStyle* style) const
+{
+    // If the width and height are both specified, then we have nothing to do.
+    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+        return;
+
+    // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary for now.  It matches Firefox.
+    // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
+    // the higher DPI.  Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
+    // metrics.
+    if (style->width().isIntrinsicOrAuto())
+        style->setWidth(Length(13, Fixed));
+    if (style->height().isAuto())
+        style->setHeight(Length(13, Fixed));
+}
+
+bool RenderThemeWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+    drawControl(i.context,  o, textFieldTheme(), getThemeData(o), r);
+    return false;
+}
+
+bool RenderThemeWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+    HANDLE theme;
+    int part;
+    if (haveTheme && isRunningOnVistaOrLater()) {
+        theme = menuListTheme();
+        if (documentIsInApplicationChromeMode(o->document()))
+            part = CP_READONLY;
+        else
+            part = CP_BORDER;
+    } else {
+        theme = textFieldTheme();
+        part = TFP_TEXTFIELD;
+    }
+
+    drawControl(i.context,  o, theme, ThemeData(part, determineState(o)), r);
+    
+    return paintMenuListButton(o, i, r);
+}
+
+void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+    style->resetBorder();
+    adjustMenuListButtonStyle(selector, style, e);
+}
+
+void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+    // These are the paddings needed to place the text correctly in the <select> box
+    const int dropDownBoxPaddingTop    = 2;
+    const int dropDownBoxPaddingRight  = style->direction() == LTR ? 4 + dropDownButtonWidth : 4;
+    const int dropDownBoxPaddingBottom = 2;
+    const int dropDownBoxPaddingLeft   = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth;
+    // The <select> box must be at least 12px high for the button to render nicely on Windows
+    const int dropDownBoxMinHeight = 12;
+    
+    // Position the text correctly within the select box and make the box wide enough to fit the dropdown button
+    style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed));
+    style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed));
+    style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed));
+    style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed));
+
+    // Height is locked to auto
+    style->setHeight(Length(Auto));
+
+    // Calculate our min-height
+    int minHeight = style->font().height();
+    minHeight = max(minHeight, dropDownBoxMinHeight);
+
+    style->setMinHeight(Length(minHeight, Fixed));
+    
+    // White-space is locked to pre
+    style->setWhiteSpace(PRE);
+}
+
+bool RenderThemeWin::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+    // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border.
+    int borderThickness = haveTheme ? 1 : 2;
+
+    // Paint the dropdown button on the inner edge of the text field,
+    // leaving space for the text field's 1px border
+    IntRect buttonRect(r);
+    buttonRect.inflate(-borderThickness);
+    if (o->style()->direction() == LTR)
+        buttonRect.setX(buttonRect.right() - dropDownButtonWidth);
+    buttonRect.setWidth(dropDownButtonWidth);
+
+    if (isRunningOnVistaOrLater()) {
+        // Outset the top, right, and bottom borders of the button so that they coincide with the <select>'s border.
+        buttonRect.setY(buttonRect.y() - vistaMenuListButtonOutset);
+        buttonRect.setHeight(buttonRect.height() + 2 * vistaMenuListButtonOutset);
+        buttonRect.setWidth(buttonRect.width() + vistaMenuListButtonOutset);
+    }
+
+    drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect);
+
+    return false;
+}
+
+const int trackWidth = 4;
+
+bool RenderThemeWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+    IntRect bounds = r;
+    
+    if (o->style()->appearance() ==  SliderHorizontalPart) {
+        bounds.setHeight(trackWidth);
+        bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
+    } else if (o->style()->appearance() == SliderVerticalPart) {
+        bounds.setWidth(trackWidth);
+        bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
+    }
+    
+    drawControl(i.context,  o, sliderTheme(), getThemeData(o), bounds);
+    return false;
+}
+
+bool RenderThemeWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{   
+    drawControl(i.context,  o, sliderTheme(), getThemeData(o), r);
+    return false;
+}
+
+const int sliderThumbWidth = 7;
+const int sliderThumbHeight = 15;
+
+void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const
+{
+    if (o->style()->appearance() == SliderThumbVerticalPart) {
+        o->style()->setWidth(Length(sliderThumbHeight, Fixed));
+        o->style()->setHeight(Length(sliderThumbWidth, Fixed));
+    } else if (o->style()->appearance() == SliderThumbHorizontalPart) {
+        o->style()->setWidth(Length(sliderThumbWidth, Fixed));
+        o->style()->setHeight(Length(sliderThumbHeight, Fixed));
+    }
+#if ENABLE(VIDEO)
+    else if (o->style()->appearance() == MediaSliderThumbPart) 
+        RenderMediaControls::adjustMediaSliderThumbSize(o);
+#endif
+}
+
+bool RenderThemeWin::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+    return paintTextField(o, i, r);
+}
+
+void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+    // Override paddingSize to match AppKit text positioning.
+    const int padding = 1;
+    style->setPaddingLeft(Length(padding, Fixed));
+    style->setPaddingRight(Length(padding, Fixed));
+    style->setPaddingTop(Length(padding, Fixed));
+    style->setPaddingBottom(Length(padding, Fixed));
+    if (e && e->focused() && e->document()->frame()->selection()->isFocusedAndActive())
+        style->setOutlineOffset(-2);
+}
+
+bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    IntRect bounds = r;
+    ASSERT(o->parent());
+    if (!o->parent() || !o->parent()->isBox())
+        return false;
+    
+    RenderBox* parentRenderBox = toRenderBox(o->parent());
+
+    IntRect parentBox = parentRenderBox->absoluteContentBox();
+    
+    // Make sure the scaled button stays square and will fit in its parent's box
+    bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
+    bounds.setWidth(bounds.height());
+
+    // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
+    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
+    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
+
+    static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef();
+    static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef();
+    paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), bounds);
+    return false;
+}
+
+void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+    // Scale the button size based on the font size
+    float fontScale = style->fontSize() / defaultControlFontPixelSize;
+    int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
+    style->setWidth(Length(cancelButtonSize, Fixed));
+    style->setHeight(Length(cancelButtonSize, Fixed));
+}
+
+void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+    IntSize emptySize(1, 11);
+    style->setWidth(Length(emptySize.width(), Fixed));
+    style->setHeight(Length(emptySize.height(), Fixed));
+}
+
+void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+    // Scale the decoration size based on the font size
+    float fontScale = style->fontSize() / defaultControlFontPixelSize;
+    int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 
+                                     maxSearchFieldResultsDecorationSize));
+    style->setWidth(Length(magnifierSize, Fixed));
+    style->setHeight(Length(magnifierSize, Fixed));
+}
+
+bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    IntRect bounds = r;
+    ASSERT(o->parent());
+    if (!o->parent() || !o->parent()->isBox())
+        return false;
+    
+    RenderBox* parentRenderBox = toRenderBox(o->parent());
+    IntRect parentBox = parentRenderBox->absoluteContentBox();
+    
+    // Make sure the scaled decoration stays square and will fit in its parent's box
+    bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
+    bounds.setWidth(bounds.height());
+
+    // Center the decoration vertically.  Round up though, so if it has to be one pixel off-center, it will
+    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
+    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
+    
+    static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef();
+    paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds);
+    return false;
+}
+
+void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+    // Scale the button size based on the font size
+    float fontScale = style->fontSize() / defaultControlFontPixelSize;
+    int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 
+                                   maxSearchFieldResultsDecorationSize));
+    int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize);
+    style->setWidth(Length(magnifierWidth, Fixed));
+    style->setHeight(Length(magnifierHeight, Fixed));
+}
+
+bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    IntRect bounds = r;
+    ASSERT(o->parent());
+    if (!o->parent())
+        return false;
+    if (!o->parent() || !o->parent()->isBox())
+        return false;
+    
+    RenderBox* parentRenderBox = toRenderBox(o->parent());
+    IntRect parentBox = parentRenderBox->absoluteContentBox();
+    
+    // Make sure the scaled decoration will fit in its parent's box
+    bounds.setHeight(min(parentBox.height(), bounds.height()));
+    bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize)));
+
+    // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
+    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
+    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
+
+    static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef();
+    paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds);
+    return false;
+}
+
+// Map a CSSValue* system color to an index understood by GetSysColor
+static int cssValueIdToSysColorIndex(int cssValueId)
+{
+    switch (cssValueId) {
+        case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
+        case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
+        case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
+        case CSSValueBackground: return COLOR_BACKGROUND;
+        case CSSValueButtonface: return COLOR_BTNFACE;
+        case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
+        case CSSValueButtonshadow: return COLOR_BTNSHADOW;
+        case CSSValueButtontext: return COLOR_BTNTEXT;
+        case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
+        case CSSValueGraytext: return COLOR_GRAYTEXT;
+        case CSSValueHighlight: return COLOR_HIGHLIGHT;
+        case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
+        case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
+        case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
+        case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
+        case CSSValueInfobackground: return COLOR_INFOBK;
+        case CSSValueInfotext: return COLOR_INFOTEXT;
+        case CSSValueMenu: return COLOR_MENU;
+        case CSSValueMenutext: return COLOR_MENUTEXT;
+        case CSSValueScrollbar: return COLOR_SCROLLBAR;
+        case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
+        case CSSValueThreedface: return COLOR_3DFACE;
+        case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
+        case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
+        case CSSValueThreedshadow: return COLOR_3DSHADOW;
+        case CSSValueWindow: return COLOR_WINDOW;
+        case CSSValueWindowframe: return COLOR_WINDOWFRAME;
+        case CSSValueWindowtext: return COLOR_WINDOWTEXT;
+        default: return -1; // Unsupported CSSValue
+    }
+}
+
+Color RenderThemeWin::systemColor(int cssValueId) const
+{
+    int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
+    if (sysColorIndex == -1)
+        return RenderTheme::systemColor(cssValueId);
+
+    COLORREF color = GetSysColor(sysColorIndex);
+    return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+#if ENABLE(VIDEO)
+
+bool RenderThemeWin::shouldRenderMediaControlPart(ControlPart part, Element* element)
+{
+    if (part == MediaToggleClosedCaptionsButtonPart) {
+        // We rely on QuickTime to render captions so only enable the button for a video element.
+#if SAFARI_THEME_VERSION >= 4
+        if (!element->hasTagName(videoTag))
+            return false;
+#else
+        return false;
+#endif
+    }
+
+    return RenderTheme::shouldRenderMediaControlPart(part, element);
+}
+
+
+bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+    return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r);
+}
+
+#endif
+
+}