1/*
2 * This file is part of the WebKit project.
3 *
4 * Copyright (C) 2006 Apple Computer, Inc.
5 * Copyright (C) 2008, 2009 Google, Inc.
6 * Copyright (C) 2009 Kenneth Rohde Christiansen
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
23 */
24
25#include "config.h"
26#include "RenderThemeChromiumWin.h"
27
28#include <windows.h>
29#include <uxtheme.h>
30#include <vssym32.h>
31
32#include "CSSValueKeywords.h"
33#include "CurrentTime.h"
34#include "FontSelector.h"
35#include "FontUtilsChromiumWin.h"
36#include "GraphicsContext.h"
37#include "HTMLMediaElement.h"
38#include "HTMLNames.h"
39#include "MediaControlElements.h"
40#include "PaintInfo.h"
41#include "PlatformBridge.h"
42#include "RenderBox.h"
43#include "RenderProgress.h"
44#include "RenderSlider.h"
45#include "ScrollbarTheme.h"
46#include "SystemInfo.h"
47#include "TransparencyWin.h"
48
49// FIXME: This dependency should eventually be removed.
50#include <skia/ext/skia_utils_win.h>
51
52#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(structName, member) \
53    offsetof(structName, member) + \
54    (sizeof static_cast<structName*>(0)->member)
55#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
56    SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
57
58namespace WebCore {
59
60// The standard width for the menu list drop-down button when run under
61// layout test mode. Use the value that's currently captured in most baselines.
62static const int kStandardMenuListButtonWidth = 17;
63
64namespace {
65// We must not create multiple ThemePainter instances.
66class ThemePainter {
67public:
68    ThemePainter(GraphicsContext* context, const IntRect& r)
69    {
70#ifndef NDEBUG
71        ASSERT(!s_hasInstance);
72        s_hasInstance = true;
73#endif
74        TransparencyWin::TransformMode transformMode = getTransformMode(context->getCTM());
75        m_helper.init(context, getLayerMode(context, transformMode), transformMode, r);
76
77        if (!m_helper.context()) {
78            // TransparencyWin doesn't have well-defined copy-ctor nor op=()
79            // so we re-initialize it instead of assigning a fresh istance.
80            // On the reinitialization, we fallback to use NoLayer mode.
81            // Note that the original initialization failure can be caused by
82            // a failure of an internal buffer allocation and NoLayer mode
83            // does not have such buffer allocations.
84            m_helper.~TransparencyWin();
85            new (&m_helper) TransparencyWin();
86            m_helper.init(context, TransparencyWin::NoLayer, transformMode, r);
87        }
88    }
89
90    ~ThemePainter()
91    {
92        m_helper.composite();
93#ifndef NDEBUG
94        s_hasInstance = false;
95#endif
96    }
97
98    GraphicsContext* context() { return m_helper.context(); }
99    const IntRect& drawRect() { return m_helper.drawRect(); }
100
101private:
102
103    static bool canvasHasMultipleLayers(const SkCanvas* canvas)
104    {
105        SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
106        iter.next(); // There is always at least one layer.
107        return !iter.done(); // There is > 1 layer if the the iterator can stil advance.
108    }
109
110    static TransparencyWin::LayerMode getLayerMode(GraphicsContext* context, TransparencyWin::TransformMode transformMode)
111    {
112        if (context->platformContext()->isDrawingToImageBuffer()) // Might have transparent background.
113            return TransparencyWin::WhiteLayer;
114        if (canvasHasMultipleLayers(context->platformContext()->canvas())) // Needs antialiasing help.
115            return TransparencyWin::OpaqueCompositeLayer;
116        // Nothing interesting.
117        return transformMode == TransparencyWin::KeepTransform ? TransparencyWin::NoLayer : TransparencyWin::OpaqueCompositeLayer;
118    }
119
120    static TransparencyWin::TransformMode getTransformMode(const AffineTransform& matrix)
121    {
122        if (matrix.b() || matrix.c()) // Skew.
123            return TransparencyWin::Untransform;
124        if (matrix.a() != 1.0 || matrix.d() != 1.0) // Scale.
125            return TransparencyWin::ScaleTransform;
126        // Nothing interesting.
127        return TransparencyWin::KeepTransform;
128    }
129
130    TransparencyWin m_helper;
131#ifndef NDEBUG
132    static bool s_hasInstance;
133#endif
134};
135
136#ifndef NDEBUG
137bool ThemePainter::s_hasInstance = false;
138#endif
139
140} // namespace
141
142static void getNonClientMetrics(NONCLIENTMETRICS* metrics)
143{
144    static UINT size = (windowsVersion() >= WindowsVista) ?
145        (sizeof NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
146    metrics->cbSize = size;
147    bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0);
148    ASSERT(success);
149}
150
151static FontDescription smallSystemFont;
152static FontDescription menuFont;
153static FontDescription labelFont;
154
155// Internal static helper functions.  We don't put them in an anonymous
156// namespace so they have easier access to the WebCore namespace.
157
158static bool supportsFocus(ControlPart appearance)
159{
160    switch (appearance) {
161    case PushButtonPart:
162    case ButtonPart:
163    case DefaultButtonPart:
164    case SearchFieldPart:
165    case TextFieldPart:
166    case TextAreaPart:
167        return true;
168    }
169    return false;
170}
171
172// Return the height of system font |font| in pixels.  We use this size by
173// default for some non-form-control elements.
174static float systemFontSize(const LOGFONT& font)
175{
176    float size = -font.lfHeight;
177    if (size < 0) {
178        HFONT hFont = CreateFontIndirect(&font);
179        if (hFont) {
180            HDC hdc = GetDC(0); // What about printing?  Is this the right DC?
181            if (hdc) {
182                HGDIOBJ hObject = SelectObject(hdc, hFont);
183                TEXTMETRIC tm;
184                GetTextMetrics(hdc, &tm);
185                SelectObject(hdc, hObject);
186                ReleaseDC(0, hdc);
187                size = tm.tmAscent;
188            }
189            DeleteObject(hFont);
190        }
191    }
192
193    // The "codepage 936" bit here is from Gecko; apparently this helps make
194    // fonts more legible in Simplified Chinese where the default font size is
195    // too small.
196    //
197    // FIXME: http://b/1119883 Since this is only used for "small caption",
198    // "menu", and "status bar" objects, I'm not sure how much this even
199    // matters.  Plus the Gecko patch went in back in 2002, and maybe this
200    // isn't even relevant anymore.  We should investigate whether this should
201    // be removed, or perhaps broadened to be "any CJK locale".
202    //
203    return ((size < 12.0f) && (GetACP() == 936)) ? 12.0f : size;
204}
205
206// Converts |points| to pixels.  One point is 1/72 of an inch.
207static float pointsToPixels(float points)
208{
209    static float pixelsPerInch = 0.0f;
210    if (!pixelsPerInch) {
211        HDC hdc = GetDC(0); // What about printing?  Is this the right DC?
212        if (hdc) { // Can this ever actually be NULL?
213            pixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSY);
214            ReleaseDC(0, hdc);
215        } else {
216            pixelsPerInch = 96.0f;
217        }
218    }
219
220    static const float pointsPerInch = 72.0f;
221    return points / pointsPerInch * pixelsPerInch;
222}
223
224static double querySystemBlinkInterval(double defaultInterval)
225{
226    UINT blinkTime = GetCaretBlinkTime();
227    if (!blinkTime)
228        return defaultInterval;
229    if (blinkTime == INFINITE)
230        return 0;
231    return blinkTime / 1000.0;
232}
233
234PassRefPtr<RenderTheme> RenderThemeChromiumWin::create()
235{
236    return adoptRef(new RenderThemeChromiumWin);
237}
238
239PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
240{
241    static RenderTheme* rt = RenderThemeChromiumWin::create().releaseRef();
242    return rt;
243}
244
245bool RenderThemeChromiumWin::supportsFocusRing(const RenderStyle* style) const
246{
247    // Let webkit draw one of its halo rings around any focused element,
248    // except push buttons. For buttons we use the windows PBS_DEFAULTED
249    // styling to give it a blue border.
250    return style->appearance() == ButtonPart
251            || style->appearance() == PushButtonPart;
252}
253
254Color RenderThemeChromiumWin::platformActiveSelectionBackgroundColor() const
255{
256    if (PlatformBridge::layoutTestMode())
257        return Color(0x00, 0x00, 0xff); // Royal blue.
258    COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
259    return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
260}
261
262Color RenderThemeChromiumWin::platformInactiveSelectionBackgroundColor() const
263{
264    if (PlatformBridge::layoutTestMode())
265        return Color(0x99, 0x99, 0x99); // Medium gray.
266    COLORREF color = GetSysColor(COLOR_GRAYTEXT);
267    return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
268}
269
270Color RenderThemeChromiumWin::platformActiveSelectionForegroundColor() const
271{
272    if (PlatformBridge::layoutTestMode())
273        return Color(0xff, 0xff, 0xcc); // Pale yellow.
274    COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
275    return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
276}
277
278Color RenderThemeChromiumWin::platformInactiveSelectionForegroundColor() const
279{
280    return Color::white;
281}
282
283Color RenderThemeChromiumWin::platformActiveTextSearchHighlightColor() const
284{
285    return Color(0xff, 0x96, 0x32); // Orange.
286}
287
288Color RenderThemeChromiumWin::platformInactiveTextSearchHighlightColor() const
289{
290    return Color(0xff, 0xff, 0x96); // Yellow.
291}
292
293void RenderThemeChromiumWin::systemFont(int propId, FontDescription& fontDescription) const
294{
295    // This logic owes much to RenderThemeSafari.cpp.
296    FontDescription* cachedDesc = 0;
297    AtomicString faceName;
298    float fontSize = 0;
299    switch (propId) {
300    case CSSValueSmallCaption:
301        cachedDesc = &smallSystemFont;
302        if (!smallSystemFont.isAbsoluteSize()) {
303            NONCLIENTMETRICS metrics;
304            getNonClientMetrics(&metrics);
305            faceName = AtomicString(metrics.lfSmCaptionFont.lfFaceName, wcslen(metrics.lfSmCaptionFont.lfFaceName));
306            fontSize = systemFontSize(metrics.lfSmCaptionFont);
307        }
308        break;
309    case CSSValueMenu:
310        cachedDesc = &menuFont;
311        if (!menuFont.isAbsoluteSize()) {
312            NONCLIENTMETRICS metrics;
313            getNonClientMetrics(&metrics);
314            faceName = AtomicString(metrics.lfMenuFont.lfFaceName, wcslen(metrics.lfMenuFont.lfFaceName));
315            fontSize = systemFontSize(metrics.lfMenuFont);
316        }
317        break;
318    case CSSValueStatusBar:
319        cachedDesc = &labelFont;
320        if (!labelFont.isAbsoluteSize()) {
321            NONCLIENTMETRICS metrics;
322            getNonClientMetrics(&metrics);
323            faceName = metrics.lfStatusFont.lfFaceName;
324            fontSize = systemFontSize(metrics.lfStatusFont);
325        }
326        break;
327    case CSSValueWebkitMiniControl:
328    case CSSValueWebkitSmallControl:
329    case CSSValueWebkitControl:
330        faceName = defaultGUIFont();
331        // Why 2 points smaller?  Because that's what Gecko does.
332        fontSize = defaultFontSize - pointsToPixels(2);
333        break;
334    default:
335        faceName = defaultGUIFont();
336        fontSize = defaultFontSize;
337        break;
338    }
339
340    if (!cachedDesc)
341        cachedDesc = &fontDescription;
342
343    if (fontSize) {
344        cachedDesc->firstFamily().setFamily(faceName);
345        cachedDesc->setIsAbsoluteSize(true);
346        cachedDesc->setGenericFamily(FontDescription::NoFamily);
347        cachedDesc->setSpecifiedSize(fontSize);
348        cachedDesc->setWeight(FontWeightNormal);
349        cachedDesc->setItalic(false);
350    }
351    fontDescription = *cachedDesc;
352}
353
354// Map a CSSValue* system color to an index understood by GetSysColor().
355static int cssValueIdToSysColorIndex(int cssValueId)
356{
357    switch (cssValueId) {
358    case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
359    case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
360    case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
361    case CSSValueBackground: return COLOR_BACKGROUND;
362    case CSSValueButtonface: return COLOR_BTNFACE;
363    case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
364    case CSSValueButtonshadow: return COLOR_BTNSHADOW;
365    case CSSValueButtontext: return COLOR_BTNTEXT;
366    case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
367    case CSSValueGraytext: return COLOR_GRAYTEXT;
368    case CSSValueHighlight: return COLOR_HIGHLIGHT;
369    case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
370    case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
371    case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
372    case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
373    case CSSValueInfobackground: return COLOR_INFOBK;
374    case CSSValueInfotext: return COLOR_INFOTEXT;
375    case CSSValueMenu: return COLOR_MENU;
376    case CSSValueMenutext: return COLOR_MENUTEXT;
377    case CSSValueScrollbar: return COLOR_SCROLLBAR;
378    case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
379    case CSSValueThreedface: return COLOR_3DFACE;
380    case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
381    case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
382    case CSSValueThreedshadow: return COLOR_3DSHADOW;
383    case CSSValueWindow: return COLOR_WINDOW;
384    case CSSValueWindowframe: return COLOR_WINDOWFRAME;
385    case CSSValueWindowtext: return COLOR_WINDOWTEXT;
386    default: return -1; // Unsupported CSSValue
387    }
388}
389
390Color RenderThemeChromiumWin::systemColor(int cssValueId) const
391{
392    int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
393    if (PlatformBridge::layoutTestMode() || (sysColorIndex == -1))
394        return RenderTheme::systemColor(cssValueId);
395
396    COLORREF color = GetSysColor(sysColorIndex);
397    return Color(GetRValue(color), GetGValue(color), GetBValue(color));
398}
399
400void RenderThemeChromiumWin::adjustSliderThumbSize(RenderObject* o) const
401{
402    // These sizes match what WinXP draws for various menus.
403    const int sliderThumbAlongAxis = 11;
404    const int sliderThumbAcrossAxis = 21;
405    if (o->style()->appearance() == SliderThumbHorizontalPart) {
406        o->style()->setWidth(Length(sliderThumbAlongAxis, Fixed));
407        o->style()->setHeight(Length(sliderThumbAcrossAxis, Fixed));
408    } else if (o->style()->appearance() == SliderThumbVerticalPart) {
409        o->style()->setWidth(Length(sliderThumbAcrossAxis, Fixed));
410        o->style()->setHeight(Length(sliderThumbAlongAxis, Fixed));
411    } else
412        RenderThemeChromiumSkia::adjustSliderThumbSize(o);
413}
414
415bool RenderThemeChromiumWin::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
416{
417    return paintButton(o, i, r);
418}
419bool RenderThemeChromiumWin::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
420{
421    return paintButton(o, i, r);
422}
423
424bool RenderThemeChromiumWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
425{
426    const ThemeData& themeData = getThemeData(o);
427
428    ThemePainter painter(i.context, r);
429    PlatformBridge::paintButton(painter.context(),
430                                themeData.m_part,
431                                themeData.m_state,
432                                themeData.m_classicState,
433                                painter.drawRect());
434    return false;
435}
436
437bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
438{
439    return paintTextFieldInternal(o, i, r, true);
440}
441
442bool RenderThemeChromiumWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r)
443{
444    const ThemeData& themeData = getThemeData(o);
445
446    ThemePainter painter(i.context, r);
447    PlatformBridge::paintTrackbar(painter.context(),
448                                  themeData.m_part,
449                                  themeData.m_state,
450                                  themeData.m_classicState,
451                                  painter.drawRect());
452    return false;
453}
454
455bool RenderThemeChromiumWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
456{
457    return paintSliderTrack(o, i, r);
458}
459
460static int menuListButtonWidth()
461{
462    static int width = PlatformBridge::layoutTestMode() ? kStandardMenuListButtonWidth : GetSystemMetrics(SM_CXVSCROLL);
463    return width;
464}
465
466// Used to paint unstyled menulists (i.e. with the default border)
467bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
468{
469    if (!o->isBox())
470        return false;
471
472    const RenderBox* box = toRenderBox(o);
473    int borderRight = box->borderRight();
474    int borderLeft = box->borderLeft();
475    int borderTop = box->borderTop();
476    int borderBottom = box->borderBottom();
477
478    // If all the borders are 0, then tell skia not to paint the border on the
479    // textfield.  FIXME: http://b/1210017 Figure out how to get Windows to not
480    // draw individual borders and then pass that to skia so we can avoid
481    // drawing any borders that are set to 0. For non-zero borders, we draw the
482    // border, but webkit just draws over it.
483    bool drawEdges = !(!borderRight && !borderLeft && !borderTop && !borderBottom);
484
485    paintTextFieldInternal(o, i, r, drawEdges);
486
487    // Take padding and border into account.  If the MenuList is smaller than
488    // the size of a button, make sure to shrink it appropriately and not put
489    // its x position to the left of the menulist.
490    const int buttonWidth = menuListButtonWidth();
491    int spacingLeft = borderLeft + box->paddingLeft();
492    int spacingRight = borderRight + box->paddingRight();
493    int spacingTop = borderTop + box->paddingTop();
494    int spacingBottom = borderBottom + box->paddingBottom();
495
496    int buttonX;
497    if (r.maxX() - r.x() < buttonWidth)
498        buttonX = r.x();
499    else
500        buttonX = o->style()->direction() == LTR ? r.maxX() - spacingRight - buttonWidth : r.x() + spacingLeft;
501
502    // Compute the rectangle of the button in the destination image.
503    IntRect rect(buttonX,
504                 r.y() + spacingTop,
505                 std::min(buttonWidth, r.maxX() - r.x()),
506                 r.height() - (spacingTop + spacingBottom));
507
508    // Get the correct theme data for a textfield and paint the menu.
509    ThemePainter painter(i.context, rect);
510    PlatformBridge::paintMenuList(painter.context(),
511                                  CP_DROPDOWNBUTTON,
512                                  determineState(o),
513                                  determineClassicState(o),
514                                  painter.drawRect());
515    return false;
516}
517
518// static
519void RenderThemeChromiumWin::setDefaultFontSize(int fontSize)
520{
521    RenderThemeChromiumSkia::setDefaultFontSize(fontSize);
522
523    // Reset cached fonts.
524    smallSystemFont = menuFont = labelFont = FontDescription();
525}
526
527double RenderThemeChromiumWin::caretBlinkIntervalInternal() const
528{
529    // This involves a system call, so we cache the result.
530    static double blinkInterval = querySystemBlinkInterval(RenderTheme::caretBlinkInterval());
531    return blinkInterval;
532}
533
534unsigned RenderThemeChromiumWin::determineState(RenderObject* o, ControlSubPart subPart)
535{
536    unsigned result = TS_NORMAL;
537    ControlPart appearance = o->style()->appearance();
538    if (!isEnabled(o))
539        result = TS_DISABLED;
540    else if (isReadOnlyControl(o))
541        result = (appearance == TextFieldPart || appearance == TextAreaPart || appearance == SearchFieldPart) ? ETS_READONLY : TS_DISABLED;
542    // Active overrides hover and focused.
543    else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o))
544        result = TS_PRESSED;
545    else if (supportsFocus(appearance) && isFocused(o))
546        result = ETS_FOCUSED;
547    else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o))
548        result = TS_HOT;
549
550    // CBS_UNCHECKED*: 1-4
551    // CBS_CHECKED*: 5-8
552    // CBS_MIXED*: 9-12
553    if (isIndeterminate(o))
554        result += 8;
555    else if (isChecked(o))
556        result += 4;
557    return result;
558}
559
560unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o)
561{
562    unsigned result = TUS_NORMAL;
563    if (!isEnabled(o->parent()))
564        result = TUS_DISABLED;
565    else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
566        result = TUS_FOCUSED;
567    else if (toRenderSlider(o->parent())->inDragMode())
568        result = TUS_PRESSED;
569    else if (isHovered(o))
570        result = TUS_HOT;
571    return result;
572}
573
574unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o, ControlSubPart subPart)
575{
576    unsigned result = 0;
577
578    ControlPart part = o->style()->appearance();
579
580    // Sliders are always in the normal state.
581    if (part == SliderHorizontalPart || part == SliderVerticalPart)
582        return result;
583
584    // So are readonly text fields.
585    if (isReadOnlyControl(o) && (part == TextFieldPart || part == TextAreaPart || part == SearchFieldPart))
586        return result;
587
588    if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
589        if (!isEnabled(o->parent()))
590            result = DFCS_INACTIVE;
591        else if (toRenderSlider(o->parent())->inDragMode()) // Active supersedes hover
592            result = DFCS_PUSHED;
593        else if (isHovered(o))
594            result = DFCS_HOT;
595    } else {
596        if (!isEnabled(o) || isReadOnlyControl(o))
597            result = DFCS_INACTIVE;
598        // Active supersedes hover
599        else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o))
600            result = DFCS_PUSHED;
601        else if (supportsFocus(part) && isFocused(o)) // So does focused
602            result = 0;
603        else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o))
604            result = DFCS_HOT;
605        // Classic theme can't represent indeterminate states. Use unchecked appearance.
606        if (isChecked(o) && !isIndeterminate(o))
607            result |= DFCS_CHECKED;
608    }
609    return result;
610}
611
612ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o, ControlSubPart subPart)
613{
614    ThemeData result;
615    switch (o->style()->appearance()) {
616    case CheckboxPart:
617        result.m_part = BP_CHECKBOX;
618        result.m_state = determineState(o);
619        result.m_classicState = DFCS_BUTTONCHECK;
620        break;
621    case RadioPart:
622        result.m_part = BP_RADIOBUTTON;
623        result.m_state = determineState(o);
624        result.m_classicState = DFCS_BUTTONRADIO;
625        break;
626    case PushButtonPart:
627    case ButtonPart:
628        result.m_part = BP_PUSHBUTTON;
629        result.m_state = determineState(o);
630        result.m_classicState = DFCS_BUTTONPUSH;
631        break;
632    case SliderHorizontalPart:
633        result.m_part = TKP_TRACK;
634        result.m_state = TRS_NORMAL;
635        break;
636    case SliderVerticalPart:
637        result.m_part = TKP_TRACKVERT;
638        result.m_state = TRVS_NORMAL;
639        break;
640    case SliderThumbHorizontalPart:
641        result.m_part = TKP_THUMBBOTTOM;
642        result.m_state = determineSliderThumbState(o);
643        break;
644    case SliderThumbVerticalPart:
645        result.m_part = TKP_THUMBVERT;
646        result.m_state = determineSliderThumbState(o);
647        break;
648    case ListboxPart:
649    case MenulistPart:
650    case MenulistButtonPart:
651    case SearchFieldPart:
652    case TextFieldPart:
653    case TextAreaPart:
654        result.m_part = EP_EDITTEXT;
655        result.m_state = determineState(o);
656        break;
657    case InnerSpinButtonPart:
658        result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN;
659        result.m_state = determineState(o, subPart);
660        result.m_classicState = subPart == SpinButtonUp ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
661        break;
662    }
663
664    result.m_classicState |= determineClassicState(o, subPart);
665
666    return result;
667}
668
669bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o,
670                                                    const PaintInfo& i,
671                                                    const IntRect& r,
672                                                    bool drawEdges)
673{
674    // Fallback to white if the specified color object is invalid.
675    // (Note PlatformBridge::paintTextField duplicates this check).
676    Color backgroundColor(Color::white);
677    if (o->style()->visitedDependentColor(CSSPropertyBackgroundColor).isValid())
678        backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor);
679
680    // If we have background-image, don't fill the content area to expose the
681    // parent's background. Also, we shouldn't fill the content area if the
682    // alpha of the color is 0. The API of Windows GDI ignores the alpha.
683    //
684    // Note that we should paint the content area white if we have neither the
685    // background color nor background image explicitly specified to keep the
686    // appearance of select element consistent with other browsers.
687    bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha();
688
689    if (o->style()->hasBorderRadius()) {
690        // If the style has rounded borders, setup the context to clip the
691        // background (themed or filled) appropriately.
692        // FIXME: make sure we do the right thing if css background-clip is set.
693        i.context->save();
694        i.context->addRoundedRectClip(o->style()->getRoundedBorderFor(r));
695    }
696    {
697        const ThemeData& themeData = getThemeData(o);
698        ThemePainter painter(i.context, r);
699        PlatformBridge::paintTextField(painter.context(),
700                                       themeData.m_part,
701                                       themeData.m_state,
702                                       themeData.m_classicState,
703                                       painter.drawRect(),
704                                       backgroundColor,
705                                       fillContentArea,
706                                       drawEdges);
707        // End of block commits the painter before restoring context.
708    }
709    if (o->style()->hasBorderRadius())
710        i.context->restore();
711    return false;
712}
713
714void RenderThemeChromiumWin::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
715{
716    int width = ScrollbarTheme::nativeTheme()->scrollbarThickness();
717    style->setWidth(Length(width, Fixed));
718    style->setMinWidth(Length(width, Fixed));
719}
720
721bool RenderThemeChromiumWin::paintInnerSpinButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
722{
723    IntRect half = rect;
724
725    // Need explicit blocks to avoid to create multiple ThemePainter instances.
726    {
727        half.setHeight(rect.height() / 2);
728        const ThemeData& upThemeData = getThemeData(object, SpinButtonUp);
729        ThemePainter upPainter(info.context, half);
730        PlatformBridge::paintSpinButton(upPainter.context(),
731                                        upThemeData.m_part,
732                                        upThemeData.m_state,
733                                        upThemeData.m_classicState,
734                                        upPainter.drawRect());
735    }
736
737    {
738        half.setY(rect.y() + rect.height() / 2);
739        const ThemeData& downThemeData = getThemeData(object, SpinButtonDown);
740        ThemePainter downPainter(info.context, half);
741        PlatformBridge::paintSpinButton(downPainter.context(),
742                                        downThemeData.m_part,
743                                        downThemeData.m_state,
744                                        downThemeData.m_classicState,
745                                        downPainter.drawRect());
746    }
747    return false;
748}
749
750#if ENABLE(PROGRESS_TAG)
751
752// MSDN says that update intervals for the bar is 30ms.
753// http://msdn.microsoft.com/en-us/library/bb760842(v=VS.85).aspx
754static const double progressAnimationFrameRate = 0.033;
755
756double RenderThemeChromiumWin::animationRepeatIntervalForProgressBar(RenderProgress*) const
757{
758    return progressAnimationFrameRate;
759}
760
761double RenderThemeChromiumWin::animationDurationForProgressBar(RenderProgress* renderProgress) const
762{
763    // On Chromium Windows port, animationProgress() and associated values aren't used.
764    // So here we can return arbitrary positive value.
765    return progressAnimationFrameRate;
766}
767
768void RenderThemeChromiumWin::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const
769{
770}
771
772bool RenderThemeChromiumWin::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& r)
773{
774    if (!o->isProgress())
775        return true;
776
777    RenderProgress* renderProgress = toRenderProgress(o);
778    // For indeterminate bar, valueRect is ignored and it is computed by the theme engine
779    // because the animation is a platform detail and WebKit doesn't need to know how.
780    IntRect valueRect = renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, r) : IntRect(0, 0, 0, 0);
781    double animatedSeconds = renderProgress->animationStartTime() ?  WTF::currentTime() - renderProgress->animationStartTime() : 0;
782    ThemePainter painter(i.context, r);
783    PlatformBridge::paintProgressBar(painter.context(), r, valueRect, renderProgress->isDeterminate(), animatedSeconds);
784    return false;
785}
786
787#endif
788
789} // namespace WebCore
790