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