1/*
2 * Copyright (C) 2006, 2007 Apple Inc.
3 * Copyright (C) 2009 Kenneth Rohde Christiansen
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 */
21
22#include "config.h"
23#include "RenderThemeWin.h"
24
25#include "CSSValueKeywords.h"
26#include "Element.h"
27#include "Frame.h"
28#include "GraphicsContext.h"
29#include "RenderSlider.h"
30#include "Settings.h"
31#include "SoftLinking.h"
32#include "SystemInfo.h"
33#include "UserAgentStyleSheets.h"
34
35#if ENABLE(VIDEO)
36#include "RenderMediaControls.h"
37#endif
38
39#include <tchar.h>
40
41/*
42 * The following constants are used to determine how a widget is drawn using
43 * Windows' Theme API. For more information on theme parts and states see
44 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
45 */
46
47// Generic state constants
48#define TS_NORMAL    1
49#define TS_HOVER     2
50#define TS_ACTIVE    3
51#define TS_DISABLED  4
52#define TS_FOCUSED   5
53
54// Button constants
55#define BP_BUTTON    1
56#define BP_RADIO     2
57#define BP_CHECKBOX  3
58
59// Textfield constants
60#define TFP_TEXTFIELD 1
61#define EP_EDITBORDER_NOSCROLL 6
62#define TFS_READONLY  6
63
64// ComboBox constants (from vsstyle.h)
65#define CP_DROPDOWNBUTTON 1
66#define CP_BORDER 4
67#define CP_READONLY 5
68#define CP_DROPDOWNBUTTONRIGHT 6
69
70// TrackBar (slider) parts
71#define TKP_TRACK       1
72#define TKP_TRACKVERT   2
73
74// TrackBar (slider) thumb parts
75#define TKP_THUMBBOTTOM 4
76#define TKP_THUMBTOP    5
77#define TKP_THUMBLEFT   7
78#define TKP_THUMBRIGHT  8
79
80// Trackbar (slider) thumb states
81#define TUS_NORMAL      1
82#define TUS_HOT         2
83#define TUS_PRESSED     3
84#define TUS_FOCUSED     4
85#define TUS_DISABLED    5
86
87// button states
88#define PBS_NORMAL      1
89#define PBS_HOT         2
90#define PBS_PRESSED     3
91#define PBS_DISABLED    4
92#define PBS_DEFAULTED   5
93
94SOFT_LINK_LIBRARY(uxtheme)
95SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList))
96SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme))
97SOFT_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))
98SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ())
99SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId))
100
101static bool haveTheme;
102
103static const unsigned vistaMenuListButtonOutset = 1;
104
105using namespace std;
106
107namespace WebCore {
108
109// This is the fixed width IE and Firefox use for buttons on dropdown menus
110static const int dropDownButtonWidth = 17;
111
112static const int shell32MagnifierIconIndex = 22;
113
114// Default font size to match Firefox.
115static const float defaultControlFontPixelSize = 13;
116
117static const float defaultCancelButtonSize = 9;
118static const float minCancelButtonSize = 5;
119static const float maxCancelButtonSize = 21;
120static const float defaultSearchFieldResultsDecorationSize = 13;
121static const float minSearchFieldResultsDecorationSize = 9;
122static const float maxSearchFieldResultsDecorationSize = 30;
123static const float defaultSearchFieldResultsButtonWidth = 18;
124
125static bool gWebKitIsBeingUnloaded;
126
127static bool documentIsInApplicationChromeMode(const Document* document)
128{
129    Settings* settings = document->settings();
130    return settings && settings->inApplicationChromeMode();
131}
132
133void RenderThemeWin::setWebKitIsBeingUnloaded()
134{
135    gWebKitIsBeingUnloaded = true;
136}
137
138PassRefPtr<RenderTheme> RenderThemeWin::create()
139{
140    return adoptRef(new RenderThemeWin);
141}
142
143#if !USE(SAFARI_THEME)
144PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
145{
146    static RenderTheme* winTheme = RenderThemeWin::create().releaseRef();
147    return winTheme;
148}
149#endif
150
151RenderThemeWin::RenderThemeWin()
152    : m_buttonTheme(0)
153    , m_textFieldTheme(0)
154    , m_menuListTheme(0)
155    , m_sliderTheme(0)
156{
157    haveTheme = uxthemeLibrary() && IsThemeActive();
158}
159
160RenderThemeWin::~RenderThemeWin()
161{
162    // If WebKit is being unloaded, then uxtheme.dll is no longer available.
163    if (gWebKitIsBeingUnloaded || !uxthemeLibrary())
164        return;
165    close();
166}
167
168HANDLE RenderThemeWin::buttonTheme() const
169{
170    if (haveTheme && !m_buttonTheme)
171        m_buttonTheme = OpenThemeData(0, L"Button");
172    return m_buttonTheme;
173}
174
175HANDLE RenderThemeWin::textFieldTheme() const
176{
177    if (haveTheme && !m_textFieldTheme)
178        m_textFieldTheme = OpenThemeData(0, L"Edit");
179    return m_textFieldTheme;
180}
181
182HANDLE RenderThemeWin::menuListTheme() const
183{
184    if (haveTheme && !m_menuListTheme)
185        m_menuListTheme = OpenThemeData(0, L"ComboBox");
186    return m_menuListTheme;
187}
188
189HANDLE RenderThemeWin::sliderTheme() const
190{
191    if (haveTheme && !m_sliderTheme)
192        m_sliderTheme = OpenThemeData(0, L"TrackBar");
193    return m_sliderTheme;
194}
195
196void RenderThemeWin::close()
197{
198    // This method will need to be called when the OS theme changes to flush our cached themes.
199    if (m_buttonTheme)
200        CloseThemeData(m_buttonTheme);
201    if (m_textFieldTheme)
202        CloseThemeData(m_textFieldTheme);
203    if (m_menuListTheme)
204        CloseThemeData(m_menuListTheme);
205    if (m_sliderTheme)
206        CloseThemeData(m_sliderTheme);
207    m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = 0;
208
209    haveTheme = uxthemeLibrary() && IsThemeActive();
210}
211
212void RenderThemeWin::themeChanged()
213{
214    close();
215}
216
217String RenderThemeWin::extraDefaultStyleSheet()
218{
219    return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet));
220}
221
222String RenderThemeWin::extraQuirksStyleSheet()
223{
224    return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet));
225}
226
227bool RenderThemeWin::supportsHover(const RenderStyle*) const
228{
229    // The Classic/2k look has no hover effects.
230    return haveTheme;
231}
232
233Color RenderThemeWin::platformActiveSelectionBackgroundColor() const
234{
235    COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
236    return Color(GetRValue(color), GetGValue(color), GetBValue(color));
237}
238
239Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const
240{
241    // This color matches Firefox.
242    return Color(176, 176, 176);
243}
244
245Color RenderThemeWin::platformActiveSelectionForegroundColor() const
246{
247    COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
248    return Color(GetRValue(color), GetGValue(color), GetBValue(color));
249}
250
251Color RenderThemeWin::platformInactiveSelectionForegroundColor() const
252{
253    return platformActiveSelectionForegroundColor();
254}
255
256static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize)
257{
258    fontDescription.setIsAbsoluteSize(true);
259    fontDescription.setGenericFamily(FontDescription::NoFamily);
260    fontDescription.firstFamily().setFamily(String(logFont.lfFaceName));
261    fontDescription.setSpecifiedSize(fontSize);
262    fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight.
263    fontDescription.setItalic(logFont.lfItalic);
264}
265
266static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont)
267{
268    fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight));
269}
270
271void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const
272{
273    static FontDescription captionFont;
274    static FontDescription controlFont;
275    static FontDescription smallCaptionFont;
276    static FontDescription menuFont;
277    static FontDescription iconFont;
278    static FontDescription messageBoxFont;
279    static FontDescription statusBarFont;
280    static FontDescription systemFont;
281
282    static bool initialized;
283    static NONCLIENTMETRICS ncm;
284
285    if (!initialized) {
286        initialized = true;
287        ncm.cbSize = sizeof(NONCLIENTMETRICS);
288        ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
289    }
290
291    switch (propId) {
292        case CSSValueIcon: {
293            if (!iconFont.isAbsoluteSize()) {
294                LOGFONT logFont;
295                ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0);
296                fillFontDescription(iconFont, logFont);
297            }
298            fontDescription = iconFont;
299            break;
300        }
301        case CSSValueMenu:
302            if (!menuFont.isAbsoluteSize())
303                fillFontDescription(menuFont, ncm.lfMenuFont);
304            fontDescription = menuFont;
305            break;
306        case CSSValueMessageBox:
307            if (!messageBoxFont.isAbsoluteSize())
308                fillFontDescription(messageBoxFont, ncm.lfMessageFont);
309            fontDescription = messageBoxFont;
310            break;
311        case CSSValueStatusBar:
312            if (!statusBarFont.isAbsoluteSize())
313                fillFontDescription(statusBarFont, ncm.lfStatusFont);
314            fontDescription = statusBarFont;
315            break;
316        case CSSValueCaption:
317            if (!captionFont.isAbsoluteSize())
318                fillFontDescription(captionFont, ncm.lfCaptionFont);
319            fontDescription = captionFont;
320            break;
321        case CSSValueSmallCaption:
322            if (!smallCaptionFont.isAbsoluteSize())
323                fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont);
324            fontDescription = smallCaptionFont;
325            break;
326        case CSSValueWebkitSmallControl:
327        case CSSValueWebkitMiniControl: // Just map to small.
328        case CSSValueWebkitControl: // Just map to small.
329            if (!controlFont.isAbsoluteSize()) {
330                HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
331                if (hGDI) {
332                    LOGFONT logFont;
333                    if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
334                        fillFontDescription(controlFont, logFont, defaultControlFontPixelSize);
335                }
336            }
337            fontDescription = controlFont;
338            break;
339        default: { // Everything else uses the stock GUI font.
340            if (!systemFont.isAbsoluteSize()) {
341                HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
342                if (hGDI) {
343                    LOGFONT logFont;
344                    if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
345                        fillFontDescription(systemFont, logFont);
346                }
347            }
348            fontDescription = systemFont;
349        }
350    }
351}
352
353bool RenderThemeWin::supportsFocus(ControlPart appearance) const
354{
355    switch (appearance) {
356        case PushButtonPart:
357        case ButtonPart:
358        case DefaultButtonPart:
359            return true;
360        default:
361            return false;
362    }
363}
364
365bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const
366{
367    return supportsFocus(style->appearance());
368}
369
370unsigned RenderThemeWin::determineClassicState(RenderObject* o)
371{
372    unsigned state = 0;
373    switch (o->style()->appearance()) {
374        case PushButtonPart:
375        case ButtonPart:
376        case DefaultButtonPart:
377            state = DFCS_BUTTONPUSH;
378            if (!isEnabled(o))
379                state |= DFCS_INACTIVE;
380            else if (isPressed(o))
381                state |= DFCS_PUSHED;
382            break;
383        case RadioPart:
384        case CheckboxPart:
385            state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK;
386            if (isChecked(o))
387                state |= DFCS_CHECKED;
388            if (!isEnabled(o))
389                state |= DFCS_INACTIVE;
390            else if (isPressed(o))
391                state |= DFCS_PUSHED;
392            break;
393        case MenulistPart:
394            state = DFCS_SCROLLCOMBOBOX;
395            if (!isEnabled(o))
396                state |= DFCS_INACTIVE;
397            else if (isPressed(o))
398                state |= DFCS_PUSHED;
399        default:
400            break;
401    }
402    return state;
403}
404
405unsigned RenderThemeWin::determineState(RenderObject* o)
406{
407    unsigned result = TS_NORMAL;
408    ControlPart appearance = o->style()->appearance();
409    if (!isEnabled(o))
410        result = TS_DISABLED;
411    else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance))
412        result = TFS_READONLY; // Readonly is supported on textfields.
413    else if (isPressed(o)) // Active overrides hover and focused.
414        result = TS_ACTIVE;
415    else if (supportsFocus(appearance) && isFocused(o))
416        result = TS_FOCUSED;
417    else if (isHovered(o))
418        result = TS_HOVER;
419    if (isChecked(o))
420        result += 4; // 4 unchecked states, 4 checked states.
421    return result;
422}
423
424unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o)
425{
426    unsigned result = TUS_NORMAL;
427    if (!isEnabled(o->parent()))
428        result = TUS_DISABLED;
429    else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
430        result = TUS_FOCUSED;
431    else if (toRenderSlider(o->parent())->inDragMode())
432        result = TUS_PRESSED;
433    else if (isHovered(o))
434        result = TUS_HOT;
435    return result;
436}
437
438unsigned RenderThemeWin::determineButtonState(RenderObject* o)
439{
440    unsigned result = PBS_NORMAL;
441    if (!isEnabled(o))
442        result = PBS_DISABLED;
443    else if (isPressed(o))
444        result = PBS_PRESSED;
445    else if (supportsFocus(o->style()->appearance()) && isFocused(o))
446        result = PBS_DEFAULTED;
447    else if (isHovered(o))
448        result = PBS_HOT;
449    else if (isDefault(o))
450        result = PBS_DEFAULTED;
451    return result;
452}
453
454ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o)
455{
456    ThemeData result;
457    switch (o->style()->appearance()) {
458        case PushButtonPart:
459        case ButtonPart:
460        case DefaultButtonPart:
461        case CheckboxPart:
462        case RadioPart:
463            result.m_part = DFC_BUTTON;
464            result.m_state = determineClassicState(o);
465            break;
466        case MenulistPart:
467            result.m_part = DFC_SCROLL;
468            result.m_state = determineClassicState(o);
469            break;
470        case SearchFieldPart:
471        case TextFieldPart:
472        case TextAreaPart:
473            result.m_part = TFP_TEXTFIELD;
474            result.m_state = determineState(o);
475            break;
476        case SliderHorizontalPart:
477            result.m_part = TKP_TRACK;
478            result.m_state = TS_NORMAL;
479            break;
480        case SliderVerticalPart:
481            result.m_part = TKP_TRACKVERT;
482            result.m_state = TS_NORMAL;
483            break;
484        case SliderThumbHorizontalPart:
485            result.m_part = TKP_THUMBBOTTOM;
486            result.m_state = determineSliderThumbState(o);
487            break;
488        case SliderThumbVerticalPart:
489            result.m_part = TKP_THUMBRIGHT;
490            result.m_state = determineSliderThumbState(o);
491            break;
492        default:
493            break;
494    }
495    return result;
496}
497
498ThemeData RenderThemeWin::getThemeData(RenderObject* o)
499{
500    if (!haveTheme)
501        return getClassicThemeData(o);
502
503    ThemeData result;
504    switch (o->style()->appearance()) {
505        case PushButtonPart:
506        case ButtonPart:
507        case DefaultButtonPart:
508            result.m_part = BP_BUTTON;
509            result.m_state = determineButtonState(o);
510            break;
511        case CheckboxPart:
512            result.m_part = BP_CHECKBOX;
513            result.m_state = determineState(o);
514            break;
515        case MenulistPart:
516        case MenulistButtonPart:
517            result.m_part = isRunningOnVistaOrLater() ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON;
518            if (isRunningOnVistaOrLater() && documentIsInApplicationChromeMode(o->document())) {
519                // The "readonly" look we use in application chrome mode
520                // only uses a "normal" look for the drop down button.
521                result.m_state = TS_NORMAL;
522            } else
523                result.m_state = determineState(o);
524            break;
525        case RadioPart:
526            result.m_part = BP_RADIO;
527            result.m_state = determineState(o);
528            break;
529        case SearchFieldPart:
530        case TextFieldPart:
531        case TextAreaPart:
532            result.m_part = isRunningOnVistaOrLater() ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD;
533            result.m_state = determineState(o);
534            break;
535        case SliderHorizontalPart:
536            result.m_part = TKP_TRACK;
537            result.m_state = TS_NORMAL;
538            break;
539        case SliderVerticalPart:
540            result.m_part = TKP_TRACKVERT;
541            result.m_state = TS_NORMAL;
542            break;
543        case SliderThumbHorizontalPart:
544            result.m_part = TKP_THUMBBOTTOM;
545            result.m_state = determineSliderThumbState(o);
546            break;
547        case SliderThumbVerticalPart:
548            result.m_part = TKP_THUMBRIGHT;
549            result.m_state = determineSliderThumbState(o);
550            break;
551    }
552
553    return result;
554}
555
556static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r)
557{
558    bool alphaBlend = false;
559    if (theme)
560        alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state);
561    HDC hdc = context->getWindowsContext(r, alphaBlend);
562    RECT widgetRect = r;
563    if (theme)
564        DrawThemeBackground(theme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL);
565    else {
566        if (themeData.m_part == TFP_TEXTFIELD) {
567            ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
568            if (themeData.m_state == TS_DISABLED || themeData.m_state ==  TFS_READONLY)
569                ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1));
570            else
571                ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1));
572        } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) {
573            ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
574            ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
575        } else if ((o->style()->appearance() == SliderThumbHorizontalPart ||
576                    o->style()->appearance() == SliderThumbVerticalPart) &&
577                   (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP ||
578                    themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) {
579            ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
580            if (themeData.m_state == TUS_DISABLED) {
581                static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
582                HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits);
583                if (patternBmp) {
584                    HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp);
585                    COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE));
586                    COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT));
587                    POINT p;
588                    ::GetViewportOrgEx(hdc, &p);
589                    ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL);
590                    HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush);
591                    ::FillRect(hdc, &widgetRect, brush);
592                    ::SetTextColor(hdc, oldForeColor);
593                    ::SetBkColor(hdc, oldBackColor);
594                    ::SelectObject(hdc, oldBrush);
595                    ::DeleteObject(brush);
596                } else
597                    ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT);
598                ::DeleteObject(patternBmp);
599            }
600        } else {
601            // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists.
602            if (o->style()->appearance() == DefaultButtonPart) {
603                HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
604                ::FrameRect(hdc, &widgetRect, brush);
605                ::InflateRect(&widgetRect, -1, -1);
606                ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE);
607            }
608            ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state);
609        }
610    }
611    context->releaseWindowsContext(hdc, r, alphaBlend);
612}
613
614bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
615{
616    drawControl(i.context,  o, buttonTheme(), getThemeData(o), r);
617    return false;
618}
619
620void RenderThemeWin::setCheckboxSize(RenderStyle* style) const
621{
622    // If the width and height are both specified, then we have nothing to do.
623    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
624        return;
625
626    // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary for now.  It matches Firefox.
627    // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
628    // the higher DPI.  Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
629    // metrics.
630    if (style->width().isIntrinsicOrAuto())
631        style->setWidth(Length(13, Fixed));
632    if (style->height().isAuto())
633        style->setHeight(Length(13, Fixed));
634}
635
636bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
637{
638    drawControl(i.context,  o, textFieldTheme(), getThemeData(o), r);
639    return false;
640}
641
642bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
643{
644    HANDLE theme;
645    int part;
646    if (haveTheme && isRunningOnVistaOrLater()) {
647        theme = menuListTheme();
648        if (documentIsInApplicationChromeMode(o->document()))
649            part = CP_READONLY;
650        else
651            part = CP_BORDER;
652    } else {
653        theme = textFieldTheme();
654        part = TFP_TEXTFIELD;
655    }
656
657    drawControl(i.context,  o, theme, ThemeData(part, determineState(o)), r);
658
659    return paintMenuListButton(o, i, r);
660}
661
662void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
663{
664    style->resetBorder();
665    adjustMenuListButtonStyle(selector, style, e);
666}
667
668void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
669{
670    // These are the paddings needed to place the text correctly in the <select> box
671    const int dropDownBoxPaddingTop    = 2;
672    const int dropDownBoxPaddingRight  = style->direction() == LTR ? 4 + dropDownButtonWidth : 4;
673    const int dropDownBoxPaddingBottom = 2;
674    const int dropDownBoxPaddingLeft   = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth;
675    // The <select> box must be at least 12px high for the button to render nicely on Windows
676    const int dropDownBoxMinHeight = 12;
677
678    // Position the text correctly within the select box and make the box wide enough to fit the dropdown button
679    style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed));
680    style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed));
681    style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed));
682    style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed));
683
684    // Height is locked to auto
685    style->setHeight(Length(Auto));
686
687    // Calculate our min-height
688    int minHeight = style->font().height();
689    minHeight = max(minHeight, dropDownBoxMinHeight);
690
691    style->setMinHeight(Length(minHeight, Fixed));
692
693    // White-space is locked to pre
694    style->setWhiteSpace(PRE);
695}
696
697bool RenderThemeWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
698{
699    // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border.
700    int borderThickness = haveTheme ? 1 : 2;
701
702    // Paint the dropdown button on the inner edge of the text field,
703    // leaving space for the text field's 1px border
704    IntRect buttonRect(r);
705    buttonRect.inflate(-borderThickness);
706    if (o->style()->direction() == LTR)
707        buttonRect.setX(buttonRect.right() - dropDownButtonWidth);
708    buttonRect.setWidth(dropDownButtonWidth);
709
710    if (isRunningOnVistaOrLater()) {
711        // Outset the top, right, and bottom borders of the button so that they coincide with the <select>'s border.
712        buttonRect.setY(buttonRect.y() - vistaMenuListButtonOutset);
713        buttonRect.setHeight(buttonRect.height() + 2 * vistaMenuListButtonOutset);
714        buttonRect.setWidth(buttonRect.width() + vistaMenuListButtonOutset);
715    }
716
717    drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect);
718
719    return false;
720}
721
722const int trackWidth = 4;
723
724bool RenderThemeWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
725{
726    IntRect bounds = r;
727
728    if (o->style()->appearance() ==  SliderHorizontalPart) {
729        bounds.setHeight(trackWidth);
730        bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
731    } else if (o->style()->appearance() == SliderVerticalPart) {
732        bounds.setWidth(trackWidth);
733        bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
734    }
735
736    drawControl(i.context,  o, sliderTheme(), getThemeData(o), bounds);
737    return false;
738}
739
740bool RenderThemeWin::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
741{
742    drawControl(i.context,  o, sliderTheme(), getThemeData(o), r);
743    return false;
744}
745
746const int sliderThumbWidth = 7;
747const int sliderThumbHeight = 15;
748
749void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const
750{
751    if (o->style()->appearance() == SliderThumbVerticalPart) {
752        o->style()->setWidth(Length(sliderThumbHeight, Fixed));
753        o->style()->setHeight(Length(sliderThumbWidth, Fixed));
754    } else if (o->style()->appearance() == SliderThumbHorizontalPart) {
755        o->style()->setWidth(Length(sliderThumbWidth, Fixed));
756        o->style()->setHeight(Length(sliderThumbHeight, Fixed));
757    }
758#if ENABLE(VIDEO)
759    else if (o->style()->appearance() == MediaSliderThumbPart)
760        RenderMediaControls::adjustMediaSliderThumbSize(o);
761#endif
762}
763
764int RenderThemeWin::buttonInternalPaddingLeft() const
765{
766    return 3;
767}
768
769int RenderThemeWin::buttonInternalPaddingRight() const
770{
771    return 3;
772}
773
774int RenderThemeWin::buttonInternalPaddingTop() const
775{
776    return 1;
777}
778
779int RenderThemeWin::buttonInternalPaddingBottom() const
780{
781    return 1;
782}
783
784bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
785{
786    return paintTextField(o, i, r);
787}
788
789void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
790{
791    // Override paddingSize to match AppKit text positioning.
792    const int padding = 1;
793    style->setPaddingLeft(Length(padding, Fixed));
794    style->setPaddingRight(Length(padding, Fixed));
795    style->setPaddingTop(Length(padding, Fixed));
796    style->setPaddingBottom(Length(padding, Fixed));
797    if (e && e->focused() && e->document()->frame()->selection()->isFocusedAndActive())
798        style->setOutlineOffset(-2);
799}
800
801bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
802{
803    IntRect bounds = r;
804    ASSERT(o->parent());
805    if (!o->parent() || !o->parent()->isBox())
806        return false;
807
808    RenderBox* parentRenderBox = toRenderBox(o->parent());
809
810    IntRect parentBox = parentRenderBox->absoluteContentBox();
811
812    // Make sure the scaled button stays square and will fit in its parent's box
813    bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
814    bounds.setWidth(bounds.height());
815
816    // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
817    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
818    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
819
820    static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef();
821    static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef();
822    paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), bounds);
823    return false;
824}
825
826void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
827{
828    // Scale the button size based on the font size
829    float fontScale = style->fontSize() / defaultControlFontPixelSize;
830    int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
831    style->setWidth(Length(cancelButtonSize, Fixed));
832    style->setHeight(Length(cancelButtonSize, Fixed));
833}
834
835void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
836{
837    IntSize emptySize(1, 11);
838    style->setWidth(Length(emptySize.width(), Fixed));
839    style->setHeight(Length(emptySize.height(), Fixed));
840}
841
842void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
843{
844    // Scale the decoration size based on the font size
845    float fontScale = style->fontSize() / defaultControlFontPixelSize;
846    int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
847                                     maxSearchFieldResultsDecorationSize));
848    style->setWidth(Length(magnifierSize, Fixed));
849    style->setHeight(Length(magnifierSize, Fixed));
850}
851
852bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
853{
854    IntRect bounds = r;
855    ASSERT(o->parent());
856    if (!o->parent() || !o->parent()->isBox())
857        return false;
858
859    RenderBox* parentRenderBox = toRenderBox(o->parent());
860    IntRect parentBox = parentRenderBox->absoluteContentBox();
861
862    // Make sure the scaled decoration stays square and will fit in its parent's box
863    bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
864    bounds.setWidth(bounds.height());
865
866    // Center the decoration vertically.  Round up though, so if it has to be one pixel off-center, it will
867    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
868    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
869
870    static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef();
871    paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds);
872    return false;
873}
874
875void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
876{
877    // Scale the button size based on the font size
878    float fontScale = style->fontSize() / defaultControlFontPixelSize;
879    int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
880                                   maxSearchFieldResultsDecorationSize));
881    int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize);
882    style->setWidth(Length(magnifierWidth, Fixed));
883    style->setHeight(Length(magnifierHeight, Fixed));
884}
885
886bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
887{
888    IntRect bounds = r;
889    ASSERT(o->parent());
890    if (!o->parent())
891        return false;
892    if (!o->parent() || !o->parent()->isBox())
893        return false;
894
895    RenderBox* parentRenderBox = toRenderBox(o->parent());
896    IntRect parentBox = parentRenderBox->absoluteContentBox();
897
898    // Make sure the scaled decoration will fit in its parent's box
899    bounds.setHeight(min(parentBox.height(), bounds.height()));
900    bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize)));
901
902    // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
903    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
904    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
905
906    static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef();
907    paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds);
908    return false;
909}
910
911// Map a CSSValue* system color to an index understood by GetSysColor
912static int cssValueIdToSysColorIndex(int cssValueId)
913{
914    switch (cssValueId) {
915        case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
916        case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
917        case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
918        case CSSValueBackground: return COLOR_BACKGROUND;
919        case CSSValueButtonface: return COLOR_BTNFACE;
920        case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
921        case CSSValueButtonshadow: return COLOR_BTNSHADOW;
922        case CSSValueButtontext: return COLOR_BTNTEXT;
923        case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
924        case CSSValueGraytext: return COLOR_GRAYTEXT;
925        case CSSValueHighlight: return COLOR_HIGHLIGHT;
926        case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
927        case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
928        case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
929        case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
930        case CSSValueInfobackground: return COLOR_INFOBK;
931        case CSSValueInfotext: return COLOR_INFOTEXT;
932        case CSSValueMenu: return COLOR_MENU;
933        case CSSValueMenutext: return COLOR_MENUTEXT;
934        case CSSValueScrollbar: return COLOR_SCROLLBAR;
935        case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
936        case CSSValueThreedface: return COLOR_3DFACE;
937        case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
938        case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
939        case CSSValueThreedshadow: return COLOR_3DSHADOW;
940        case CSSValueWindow: return COLOR_WINDOW;
941        case CSSValueWindowframe: return COLOR_WINDOWFRAME;
942        case CSSValueWindowtext: return COLOR_WINDOWTEXT;
943        default: return -1; // Unsupported CSSValue
944    }
945}
946
947Color RenderThemeWin::systemColor(int cssValueId) const
948{
949    int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
950    if (sysColorIndex == -1)
951        return RenderTheme::systemColor(cssValueId);
952
953    COLORREF color = GetSysColor(sysColorIndex);
954    return Color(GetRValue(color), GetGValue(color), GetBValue(color));
955}
956
957#if ENABLE(VIDEO)
958
959bool RenderThemeWin::shouldRenderMediaControlPart(ControlPart part, Element* element)
960{
961    if (part == MediaToggleClosedCaptionsButtonPart) {
962        // We rely on QuickTime to render captions so only enable the button for a video element.
963#if SAFARI_THEME_VERSION >= 4
964        if (!element->hasTagName(videoTag))
965            return false;
966#else
967        return false;
968#endif
969    }
970
971    return RenderTheme::shouldRenderMediaControlPart(part, element);
972}
973
974
975bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
976{
977    return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r);
978}
979
980bool RenderThemeWin::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
981{
982    return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r);
983}
984
985bool RenderThemeWin::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
986{
987    return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r);
988}
989
990bool RenderThemeWin::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
991{
992    return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r);
993}
994
995bool RenderThemeWin::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
996{
997    return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r);
998}
999
1000bool RenderThemeWin::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1001{
1002    return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r);
1003}
1004
1005bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1006{
1007    return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r);
1008}
1009
1010bool RenderThemeWin::paintMediaToggleClosedCaptionsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1011{
1012    return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r);
1013}
1014
1015#endif
1016
1017}
1018