RenderThemeChromiumWin.cpp revision 5f1ab04193ad0130ca8204aadaceae083aca9881
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 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 */
23
24#include "config.h"
25#include "RenderThemeChromiumWin.h"
26
27#include <windows.h>
28#include <uxtheme.h>
29#include <vssym32.h>
30
31#include "ChromiumBridge.h"
32#include "CSSStyleSheet.h"
33#include "CSSValueKeywords.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 "RenderBox.h"
41#include "RenderSlider.h"
42#include "ScrollbarTheme.h"
43#include "SkiaUtils.h"
44#include "TransparencyWin.h"
45#include "UserAgentStyleSheets.h"
46#include "WindowsVersion.h"
47
48// FIXME: This dependency should eventually be removed.
49#include <skia/ext/skia_utils_win.h>
50
51#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(structName, member) \
52    offsetof(structName, member) + \
53    (sizeof static_cast<structName*>(0)->member)
54#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
55    SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
56
57namespace WebCore {
58
59namespace {
60
61// The background for the media player controls should be a 60% opaque black rectangle. This
62// matches the UI mockups for the default UI theme.
63static const float defaultMediaControlOpacity = 0.6f;
64
65// These values all match Safari/Win.
66static const float defaultControlFontPixelSize = 13;
67static const float defaultCancelButtonSize = 9;
68static const float minCancelButtonSize = 5;
69static const float maxCancelButtonSize = 21;
70static const float defaultSearchFieldResultsDecorationSize = 13;
71static const float minSearchFieldResultsDecorationSize = 9;
72static const float maxSearchFieldResultsDecorationSize = 30;
73static const float defaultSearchFieldResultsButtonWidth = 18;
74
75bool canvasHasMultipleLayers(const SkCanvas* canvas)
76{
77    SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
78    iter.next();  // There is always at least one layer.
79    return !iter.done();  // There is > 1 layer if the the iterator can stil advance.
80}
81
82class ThemePainter : public TransparencyWin {
83public:
84    ThemePainter(GraphicsContext* context, const IntRect& r)
85    {
86        TransformMode transformMode = getTransformMode(context->getCTM());
87        init(context, getLayerMode(context, transformMode), transformMode, r);
88    }
89
90    ~ThemePainter()
91    {
92        composite();
93    }
94
95private:
96    static LayerMode getLayerMode(GraphicsContext* context, TransformMode transformMode)
97    {
98        if (context->platformContext()->isDrawingToImageBuffer())  // Might have transparent background.
99            return WhiteLayer;
100        else if (canvasHasMultipleLayers(context->platformContext()->canvas()))  // Needs antialiasing help.
101            return OpaqueCompositeLayer;
102        else  // Nothing interesting.
103            return transformMode == KeepTransform ? NoLayer : OpaqueCompositeLayer;
104    }
105
106    static TransformMode getTransformMode(const TransformationMatrix& matrix)
107    {
108        if (matrix.b() != 0 || matrix.c() != 0)  // Skew.
109            return Untransform;
110        else if (matrix.a() != 1.0 || matrix.d() != 1.0)  // Scale.
111            return ScaleTransform;
112        else  // Nothing interesting.
113            return KeepTransform;
114    }
115};
116
117}  // namespace
118
119static void getNonClientMetrics(NONCLIENTMETRICS* metrics) {
120    static UINT size = WebCore::isVistaOrNewer() ?
121        sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
122    metrics->cbSize = size;
123    bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0);
124    ASSERT(success);
125}
126
127enum PaddingType {
128    TopPadding,
129    RightPadding,
130    BottomPadding,
131    LeftPadding
132};
133
134static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 };
135
136// The default variable-width font size.  We use this as the default font
137// size for the "system font", and as a base size (which we then shrink) for
138// form control fonts.
139static float defaultFontSize = 16.0;
140
141static FontDescription smallSystemFont;
142static FontDescription menuFont;
143static FontDescription labelFont;
144
145// Internal static helper functions.  We don't put them in an anonymous
146// namespace so they have easier access to the WebCore namespace.
147
148static bool supportsFocus(ControlPart appearance)
149{
150    switch (appearance) {
151    case PushButtonPart:
152    case ButtonPart:
153    case DefaultButtonPart:
154    case SearchFieldPart:
155    case TextFieldPart:
156    case TextAreaPart:
157        return true;
158    }
159    return false;
160}
161
162static void setFixedPadding(RenderStyle* style, const int padding[4])
163{
164    style->setPaddingLeft(Length(padding[LeftPadding], Fixed));
165    style->setPaddingRight(Length(padding[RightPadding], Fixed));
166    style->setPaddingTop(Length(padding[TopPadding], Fixed));
167    style->setPaddingBottom(Length(padding[BottomPadding], Fixed));
168}
169
170// Return the height of system font |font| in pixels.  We use this size by
171// default for some non-form-control elements.
172static float systemFontSize(const LOGFONT& font)
173{
174    float size = -font.lfHeight;
175    if (size < 0) {
176        HFONT hFont = CreateFontIndirect(&font);
177        if (hFont) {
178            HDC hdc = GetDC(0);  // What about printing?  Is this the right DC?
179            if (hdc) {
180                HGDIOBJ hObject = SelectObject(hdc, hFont);
181                TEXTMETRIC tm;
182                GetTextMetrics(hdc, &tm);
183                SelectObject(hdc, hObject);
184                ReleaseDC(0, hdc);
185                size = tm.tmAscent;
186            }
187            DeleteObject(hFont);
188        }
189    }
190
191    // The "codepage 936" bit here is from Gecko; apparently this helps make
192    // fonts more legible in Simplified Chinese where the default font size is
193    // too small.
194    //
195    // FIXME: http://b/1119883 Since this is only used for "small caption",
196    // "menu", and "status bar" objects, I'm not sure how much this even
197    // matters.  Plus the Gecko patch went in back in 2002, and maybe this
198    // isn't even relevant anymore.  We should investigate whether this should
199    // be removed, or perhaps broadened to be "any CJK locale".
200    //
201    return ((size < 12.0f) && (GetACP() == 936)) ? 12.0f : size;
202}
203
204// We aim to match IE here.
205// -IE uses a font based on the encoding as the default font for form controls.
206// -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT),
207// which returns MS Shell Dlg)
208// -Safari uses Lucida Grande.
209//
210// FIXME: The only case where we know we don't match IE is for ANSI encodings.
211// IE uses MS Shell Dlg there, which we render incorrectly at certain pixel
212// sizes (e.g. 15px). So, for now we just use Arial.
213static wchar_t* defaultGUIFont()
214{
215    return L"Arial";
216}
217
218// Converts |points| to pixels.  One point is 1/72 of an inch.
219static float pointsToPixels(float points)
220{
221    static float pixelsPerInch = 0.0f;
222    if (!pixelsPerInch) {
223        HDC hdc = GetDC(0);  // What about printing?  Is this the right DC?
224        if (hdc) {  // Can this ever actually be NULL?
225            pixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSY);
226            ReleaseDC(0, hdc);
227        } else {
228            pixelsPerInch = 96.0f;
229        }
230    }
231
232    static const float pointsPerInch = 72.0f;
233    return points / pointsPerInch * pixelsPerInch;
234}
235
236static void setSizeIfAuto(RenderStyle* style, const IntSize& size)
237{
238    if (style->width().isIntrinsicOrAuto())
239        style->setWidth(Length(size.width(), Fixed));
240    if (style->height().isAuto())
241        style->setHeight(Length(size.height(), Fixed));
242}
243
244static double querySystemBlinkInterval(double defaultInterval)
245{
246    UINT blinkTime = GetCaretBlinkTime();
247    if (blinkTime == 0)
248        return defaultInterval;
249    if (blinkTime == INFINITE)
250        return 0;
251    return blinkTime / 1000.0;
252}
253
254#if ENABLE(VIDEO)
255// Attempt to retrieve a HTMLMediaElement from a Node. Returns NULL if one cannot be found.
256static HTMLMediaElement* mediaElementParent(Node* node)
257{
258    if (!node)
259        return 0;
260    Node* mediaNode = node->shadowAncestorNode();
261    if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag)))
262        return 0;
263
264    return static_cast<HTMLMediaElement*>(mediaNode);
265}
266#endif
267
268// Implement WebCore::theme() for getting the global RenderTheme.
269RenderTheme* theme()
270{
271    static RenderThemeChromiumWin winTheme;
272    return &winTheme;
273}
274
275String RenderThemeChromiumWin::extraDefaultStyleSheet()
276{
277    return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet));
278}
279
280String RenderThemeChromiumWin::extraQuirksStyleSheet()
281{
282    return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet));
283}
284
285#if ENABLE(VIDEO)
286String RenderThemeChromiumWin::extraMediaControlsStyleSheet()
287{
288    return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet));
289}
290#endif
291
292bool RenderThemeChromiumWin::supportsFocusRing(const RenderStyle* style) const
293{
294   // Let webkit draw one of its halo rings around any focused element,
295   // except push buttons. For buttons we use the windows PBS_DEFAULTED
296   // styling to give it a blue border.
297   return style->appearance() == ButtonPart
298       || style->appearance() == PushButtonPart;
299}
300
301Color RenderThemeChromiumWin::platformActiveSelectionBackgroundColor() const
302{
303    if (ChromiumBridge::layoutTestMode())
304        return Color(0x00, 0x00, 0xff);  // Royal blue.
305    COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
306    return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
307}
308
309Color RenderThemeChromiumWin::platformInactiveSelectionBackgroundColor() const
310{
311    if (ChromiumBridge::layoutTestMode())
312        return Color(0x99, 0x99, 0x99);  // Medium gray.
313    COLORREF color = GetSysColor(COLOR_GRAYTEXT);
314    return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
315}
316
317Color RenderThemeChromiumWin::platformActiveSelectionForegroundColor() const
318{
319    if (ChromiumBridge::layoutTestMode())
320        return Color(0xff, 0xff, 0xcc);  // Pale yellow.
321    COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
322    return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
323}
324
325Color RenderThemeChromiumWin::platformInactiveSelectionForegroundColor() const
326{
327    return Color::white;
328}
329
330Color RenderThemeChromiumWin::platformActiveTextSearchHighlightColor() const
331{
332    return Color(0xff, 0x96, 0x32);  // Orange.
333}
334
335Color RenderThemeChromiumWin::platformInactiveTextSearchHighlightColor() const
336{
337    return Color(0xff, 0xff, 0x96); // Yellow.
338}
339
340double RenderThemeChromiumWin::caretBlinkInterval() const
341{
342    // Disable the blinking caret in layout test mode, as it introduces
343    // a race condition for the pixel tests. http://b/1198440
344    if (ChromiumBridge::layoutTestMode())
345        return 0;
346
347    // This involves a system call, so we cache the result.
348    static double blinkInterval = querySystemBlinkInterval(RenderTheme::caretBlinkInterval());
349    return blinkInterval;
350}
351
352void RenderThemeChromiumWin::systemFont(int propId, FontDescription& fontDescription) const
353{
354    // This logic owes much to RenderThemeSafari.cpp.
355    FontDescription* cachedDesc = NULL;
356    wchar_t* faceName = 0;
357    float fontSize = 0;
358    switch (propId) {
359    case CSSValueSmallCaption:
360        cachedDesc = &smallSystemFont;
361        if (!smallSystemFont.isAbsoluteSize()) {
362            NONCLIENTMETRICS metrics;
363            getNonClientMetrics(&metrics);
364            faceName = metrics.lfSmCaptionFont.lfFaceName;
365            fontSize = systemFontSize(metrics.lfSmCaptionFont);
366        }
367        break;
368    case CSSValueMenu:
369        cachedDesc = &menuFont;
370        if (!menuFont.isAbsoluteSize()) {
371            NONCLIENTMETRICS metrics;
372            getNonClientMetrics(&metrics);
373            faceName = metrics.lfMenuFont.lfFaceName;
374            fontSize = systemFontSize(metrics.lfMenuFont);
375        }
376        break;
377    case CSSValueStatusBar:
378        cachedDesc = &labelFont;
379        if (!labelFont.isAbsoluteSize()) {
380            NONCLIENTMETRICS metrics;
381            getNonClientMetrics(&metrics);
382            faceName = metrics.lfStatusFont.lfFaceName;
383            fontSize = systemFontSize(metrics.lfStatusFont);
384        }
385        break;
386    case CSSValueWebkitMiniControl:
387    case CSSValueWebkitSmallControl:
388    case CSSValueWebkitControl:
389        faceName = defaultGUIFont();
390        // Why 2 points smaller?  Because that's what Gecko does.
391        fontSize = defaultFontSize - pointsToPixels(2);
392        break;
393    default:
394        faceName = defaultGUIFont();
395        fontSize = defaultFontSize;
396        break;
397    }
398
399    if (!cachedDesc)
400        cachedDesc = &fontDescription;
401
402    if (fontSize) {
403        ASSERT(faceName);
404        cachedDesc->firstFamily().setFamily(AtomicString(faceName,
405                                                         wcslen(faceName)));
406        cachedDesc->setIsAbsoluteSize(true);
407        cachedDesc->setGenericFamily(FontDescription::NoFamily);
408        cachedDesc->setSpecifiedSize(fontSize);
409        cachedDesc->setWeight(FontWeightNormal);
410        cachedDesc->setItalic(false);
411    }
412    fontDescription = *cachedDesc;
413}
414
415int RenderThemeChromiumWin::minimumMenuListSize(RenderStyle* style) const
416{
417    return 0;
418}
419
420void RenderThemeChromiumWin::adjustSliderThumbSize(RenderObject* o) const
421{
422    // These sizes match what WinXP draws for various menus.
423    const int sliderThumbAlongAxis = 11;
424    const int sliderThumbAcrossAxis = 21;
425    if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == MediaSliderThumbPart) {
426        o->style()->setWidth(Length(sliderThumbAlongAxis, Fixed));
427        o->style()->setHeight(Length(sliderThumbAcrossAxis, Fixed));
428    } else if (o->style()->appearance() == SliderThumbVerticalPart) {
429        o->style()->setWidth(Length(sliderThumbAcrossAxis, Fixed));
430        o->style()->setHeight(Length(sliderThumbAlongAxis, Fixed));
431    }
432}
433
434void RenderThemeChromiumWin::setCheckboxSize(RenderStyle* style) const
435{
436    // If the width and height are both specified, then we have nothing to do.
437    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
438        return;
439
440    // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary
441    // for now.  It matches Firefox.  At different DPI settings on Windows,
442    // querying the theme gives you a larger size that accounts for the higher
443    // DPI.  Until our entire engine honors a DPI setting other than 96, we
444    // can't rely on the theme's metrics.
445    const IntSize size(13, 13);
446    setSizeIfAuto(style, size);
447}
448
449void RenderThemeChromiumWin::setRadioSize(RenderStyle* style) const
450{
451    // Use same sizing for radio box as checkbox.
452    setCheckboxSize(style);
453}
454
455bool RenderThemeChromiumWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
456{
457    const ThemeData& themeData = getThemeData(o);
458
459    WebCore::ThemePainter painter(i.context, r);
460    ChromiumBridge::paintButton(painter.context(),
461                                themeData.m_part,
462                                themeData.m_state,
463                                themeData.m_classicState,
464                                painter.drawRect());
465    return false;
466}
467
468bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
469{
470    return paintTextFieldInternal(o, i, r, true);
471}
472
473bool RenderThemeChromiumWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
474{
475    const ThemeData& themeData = getThemeData(o);
476
477    WebCore::ThemePainter painter(i.context, r);
478    ChromiumBridge::paintTrackbar(painter.context(),
479                                  themeData.m_part,
480                                  themeData.m_state,
481                                  themeData.m_classicState,
482                                  painter.drawRect());
483    return false;
484}
485
486void RenderThemeChromiumWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
487{
488    // Scale the button size based on the font size
489    float fontScale = style->fontSize() / defaultControlFontPixelSize;
490    int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
491    style->setWidth(Length(cancelButtonSize, Fixed));
492    style->setHeight(Length(cancelButtonSize, Fixed));
493}
494
495bool RenderThemeChromiumWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
496{
497    IntRect bounds = r;
498    ASSERT(o->parent());
499    if (!o->parent() || !o->parent()->isBox())
500        return false;
501
502    RenderBox* parentRenderBox = toRenderBox(o->parent());
503
504    IntRect parentBox = parentRenderBox->absoluteContentBox();
505
506    // Make sure the scaled button stays square and will fit in its parent's box
507    bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height())));
508    bounds.setWidth(bounds.height());
509
510    // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
511    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
512    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
513
514    static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef();
515    static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef();
516    i.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds);
517    return false;
518}
519
520void RenderThemeChromiumWin::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
521{
522    IntSize emptySize(1, 11);
523    style->setWidth(Length(emptySize.width(), Fixed));
524    style->setHeight(Length(emptySize.height(), Fixed));
525}
526
527void RenderThemeChromiumWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
528{
529    // Scale the decoration size based on the font size
530    float fontScale = style->fontSize() / defaultControlFontPixelSize;
531    int magnifierSize = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
532                                         maxSearchFieldResultsDecorationSize));
533    style->setWidth(Length(magnifierSize, Fixed));
534    style->setHeight(Length(magnifierSize, Fixed));
535}
536
537bool RenderThemeChromiumWin::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
538{
539    IntRect bounds = r;
540    ASSERT(o->parent());
541    if (!o->parent() || !o->parent()->isBox())
542        return false;
543
544    RenderBox* parentRenderBox = toRenderBox(o->parent());
545    IntRect parentBox = parentRenderBox->absoluteContentBox();
546
547    // Make sure the scaled decoration stays square and will fit in its parent's box
548    bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height())));
549    bounds.setWidth(bounds.height());
550
551    // Center the decoration vertically.  Round up though, so if it has to be one pixel off-center, it will
552    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
553    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
554
555    static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef();
556    i.context->drawImage(magnifierImage, bounds);
557    return false;
558}
559
560void RenderThemeChromiumWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
561{
562    // Scale the button size based on the font size
563    float fontScale = style->fontSize() / defaultControlFontPixelSize;
564    int magnifierHeight = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
565                                           maxSearchFieldResultsDecorationSize));
566    int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize);
567    style->setWidth(Length(magnifierWidth, Fixed));
568    style->setHeight(Length(magnifierHeight, Fixed));
569}
570
571bool RenderThemeChromiumWin::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
572{
573    IntRect bounds = r;
574    ASSERT(o->parent());
575    if (!o->parent())
576        return false;
577    if (!o->parent() || !o->parent()->isBox())
578        return false;
579
580    RenderBox* parentRenderBox = toRenderBox(o->parent());
581    IntRect parentBox = parentRenderBox->absoluteContentBox();
582
583    // Make sure the scaled decoration will fit in its parent's box
584    bounds.setHeight(std::min(parentBox.height(), bounds.height()));
585    bounds.setWidth(std::min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize)));
586
587    // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
588    // be one pixel closer to the bottom of the field.  This tends to look better with the text.
589    bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
590
591    static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef();
592    i.context->drawImage(magnifierImage, bounds);
593    return false;
594}
595
596bool RenderThemeChromiumWin::paintMediaButtonInternal(GraphicsContext* context, const IntRect& rect, Image* image)
597{
598    context->beginTransparencyLayer(defaultMediaControlOpacity);
599
600    // Draw background.
601    Color oldFill = context->fillColor();
602    Color oldStroke = context->strokeColor();
603
604    context->setFillColor(Color::black);
605    context->setStrokeColor(Color::black);
606    context->drawRect(rect);
607
608    context->setFillColor(oldFill);
609    context->setStrokeColor(oldStroke);
610
611    // Create a destination rectangle for the image that is centered in the drawing rectangle, rounded left, and down.
612    IntRect imageRect = image->rect();
613    imageRect.setY(rect.y() + (rect.height() - image->height() + 1) / 2);
614    imageRect.setX(rect.x() + (rect.width() - image->width() + 1) / 2);
615
616    context->drawImage(image, imageRect, CompositeSourceAtop);
617    context->endTransparencyLayer();
618
619    return false;
620}
621
622bool RenderThemeChromiumWin::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
623{
624#if ENABLE(VIDEO)
625    HTMLMediaElement* mediaElement = mediaElementParent(o->node());
626    if (!mediaElement)
627        return false;
628
629    static Image* mediaPlay = Image::loadPlatformResource("mediaPlay").releaseRef();
630    static Image* mediaPause = Image::loadPlatformResource("mediaPause").releaseRef();
631
632    return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->paused() ? mediaPlay : mediaPause);
633#else
634    return false;
635#endif
636}
637
638bool RenderThemeChromiumWin::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
639{
640#if ENABLE(VIDEO)
641    HTMLMediaElement* mediaElement = mediaElementParent(o->node());
642    if (!mediaElement)
643        return false;
644
645    static Image* soundFull = Image::loadPlatformResource("mediaSoundFull").releaseRef();
646    static Image* soundNone = Image::loadPlatformResource("mediaSoundNone").releaseRef();
647
648    return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull);
649#else
650    return false;
651#endif
652}
653
654void RenderThemeChromiumWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
655{
656    // Height is locked to auto on all browsers.
657    style->setLineHeight(RenderStyle::initialLineHeight());
658}
659
660// Used to paint unstyled menulists (i.e. with the default border)
661bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
662{
663    if (!o->isBox())
664        return false;
665
666    const RenderBox* box = toRenderBox(o);
667    int borderRight = box->borderRight();
668    int borderLeft = box->borderLeft();
669    int borderTop = box->borderTop();
670    int borderBottom = box->borderBottom();
671
672    // If all the borders are 0, then tell skia not to paint the border on the
673    // textfield.  FIXME: http://b/1210017 Figure out how to get Windows to not
674    // draw individual borders and then pass that to skia so we can avoid
675    // drawing any borders that are set to 0. For non-zero borders, we draw the
676    // border, but webkit just draws over it.
677    bool drawEdges = !(borderRight == 0 && borderLeft == 0 && borderTop == 0 && borderBottom == 0);
678
679    paintTextFieldInternal(o, i, r, drawEdges);
680
681    // Take padding and border into account.  If the MenuList is smaller than
682    // the size of a button, make sure to shrink it appropriately and not put
683    // its x position to the left of the menulist.
684    const int buttonWidth = GetSystemMetrics(SM_CXVSCROLL);
685    int spacingLeft = borderLeft + box->paddingLeft();
686    int spacingRight = borderRight + box->paddingRight();
687    int spacingTop = borderTop + box->paddingTop();
688    int spacingBottom = borderBottom + box->paddingBottom();
689
690    int buttonX;
691    if (r.right() - r.x() < buttonWidth)
692        buttonX = r.x();
693    else
694        buttonX = o->style()->direction() == LTR ? r.right() - spacingRight - buttonWidth : r.x() + spacingLeft;
695
696    // Compute the rectangle of the button in the destination image.
697    IntRect rect(buttonX,
698                 r.y() + spacingTop,
699                 std::min(buttonWidth, r.right() - r.x()),
700                 r.height() - (spacingTop + spacingBottom));
701
702    // Get the correct theme data for a textfield and paint the menu.
703    WebCore::ThemePainter painter(i.context, rect);
704    ChromiumBridge::paintMenuList(painter.context(),
705                                  CP_DROPDOWNBUTTON,
706                                  determineState(o),
707                                  determineClassicState(o),
708                                  painter.drawRect());
709    return false;
710}
711
712void RenderThemeChromiumWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
713{
714    adjustMenuListStyle(selector, style, e);
715}
716
717// Used to paint styled menulists (i.e. with a non-default border)
718bool RenderThemeChromiumWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
719{
720    return paintMenuList(o, i, r);
721}
722
723int RenderThemeChromiumWin::popupInternalPaddingLeft(RenderStyle* style) const
724{
725    return menuListInternalPadding(style, LeftPadding);
726}
727
728int RenderThemeChromiumWin::popupInternalPaddingRight(RenderStyle* style) const
729{
730    return menuListInternalPadding(style, RightPadding);
731}
732
733int RenderThemeChromiumWin::popupInternalPaddingTop(RenderStyle* style) const
734{
735    return menuListInternalPadding(style, TopPadding);
736}
737
738int RenderThemeChromiumWin::popupInternalPaddingBottom(RenderStyle* style) const
739{
740    return menuListInternalPadding(style, BottomPadding);
741}
742
743int RenderThemeChromiumWin::buttonInternalPaddingLeft() const
744{
745    return 3;
746}
747
748int RenderThemeChromiumWin::buttonInternalPaddingRight() const
749{
750    return 3;
751}
752
753int RenderThemeChromiumWin::buttonInternalPaddingTop() const
754{
755    return 1;
756}
757
758int RenderThemeChromiumWin::buttonInternalPaddingBottom() const
759{
760    return 1;
761}
762
763// static
764void RenderThemeChromiumWin::setDefaultFontSize(int fontSize)
765{
766    defaultFontSize = static_cast<float>(fontSize);
767
768    // Reset cached fonts.
769    smallSystemFont = menuFont = labelFont = FontDescription();
770}
771
772unsigned RenderThemeChromiumWin::determineState(RenderObject* o)
773{
774    unsigned result = TS_NORMAL;
775    ControlPart appearance = o->style()->appearance();
776    if (!isEnabled(o))
777        result = TS_DISABLED;
778    else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance))
779        result = ETS_READONLY; // Readonly is supported on textfields.
780    else if (isPressed(o)) // Active overrides hover and focused.
781        result = TS_PRESSED;
782    else if (supportsFocus(appearance) && isFocused(o))
783        result = ETS_FOCUSED;
784    else if (isHovered(o))
785        result = TS_HOT;
786    if (isChecked(o))
787        result += 4; // 4 unchecked states, 4 checked states.
788    return result;
789}
790
791unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o)
792{
793    unsigned result = TUS_NORMAL;
794    if (!isEnabled(o->parent()))
795        result = TUS_DISABLED;
796    else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
797        result = TUS_FOCUSED;
798    else if (static_cast<RenderSlider*>(o->parent())->inDragMode())
799        result = TUS_PRESSED;
800    else if (isHovered(o))
801        result = TUS_HOT;
802    return result;
803}
804
805unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o)
806{
807    unsigned result = 0;
808    if (!isEnabled(o))
809        result = DFCS_INACTIVE;
810    else if (isPressed(o)) // Active supersedes hover
811        result = DFCS_PUSHED;
812    else if (isHovered(o))
813        result = DFCS_HOT;
814    if (isChecked(o))
815        result |= DFCS_CHECKED;
816    return result;
817}
818
819ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o)
820{
821    ThemeData result;
822    switch (o->style()->appearance()) {
823    case CheckboxPart:
824        result.m_part = BP_CHECKBOX;
825        result.m_state = determineState(o);
826        result.m_classicState = DFCS_BUTTONCHECK;
827        break;
828    case RadioPart:
829        result.m_part = BP_RADIOBUTTON;
830        result.m_state = determineState(o);
831        result.m_classicState = DFCS_BUTTONRADIO;
832        break;
833    case PushButtonPart:
834    case ButtonPart:
835        result.m_part = BP_PUSHBUTTON;
836        result.m_state = determineState(o);
837        result.m_classicState = DFCS_BUTTONPUSH;
838        break;
839    case SliderHorizontalPart:
840        result.m_part = TKP_TRACK;
841        result.m_state = TRS_NORMAL;
842        break;
843    case SliderVerticalPart:
844        result.m_part = TKP_TRACKVERT;
845        result.m_state = TRVS_NORMAL;
846        break;
847    case SliderThumbHorizontalPart:
848        result.m_part = TKP_THUMBBOTTOM;
849        result.m_state = determineSliderThumbState(o);
850        break;
851    case SliderThumbVerticalPart:
852        result.m_part = TKP_THUMBVERT;
853        result.m_state = determineSliderThumbState(o);
854        break;
855    case ListboxPart:
856    case MenulistPart:
857    case SearchFieldPart:
858    case TextFieldPart:
859    case TextAreaPart:
860        result.m_part = EP_EDITTEXT;
861        result.m_state = determineState(o);
862        break;
863    }
864
865    result.m_classicState |= determineClassicState(o);
866
867    return result;
868}
869
870bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o,
871                                                    const RenderObject::PaintInfo& i,
872                                                    const IntRect& r,
873                                                    bool drawEdges)
874{
875    // Nasty hack to make us not paint the border on text fields with a
876    // border-radius. Webkit paints elements with border-radius for us.
877    // FIXME: Get rid of this if-check once we can properly clip rounded
878    // borders: http://b/1112604 and http://b/1108635
879    // FIXME: make sure we do the right thing if css background-clip is set.
880    if (o->style()->hasBorderRadius())
881        return false;
882
883    const ThemeData& themeData = getThemeData(o);
884
885    // Fallback to white if the specified color object is invalid.
886    Color backgroundColor(Color::white);
887    if (o->style()->backgroundColor().isValid()) {
888        backgroundColor = o->style()->backgroundColor();
889    }
890
891    // If we have background-image, don't fill the content area to expose the
892    // parent's background. Also, we shouldn't fill the content area if the
893    // alpha of the color is 0. The API of Windows GDI ignores the alpha.
894    //
895    // Note that we should paint the content area white if we have neither the
896    // background color nor background image explicitly specified to keep the
897    // appearance of select element consistent with other browsers.
898    bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha() != 0;
899
900    WebCore::ThemePainter painter(i.context, r);
901    ChromiumBridge::paintTextField(painter.context(),
902                                   themeData.m_part,
903                                   themeData.m_state,
904                                   themeData.m_classicState,
905                                   painter.drawRect(),
906                                   backgroundColor,
907                                   fillContentArea,
908                                   drawEdges);
909    return false;
910}
911
912int RenderThemeChromiumWin::menuListInternalPadding(RenderStyle* style, int paddingType) const
913{
914    // This internal padding is in addition to the user-supplied padding.
915    // Matches the FF behavior.
916    int padding = styledMenuListInternalPadding[paddingType];
917
918    // Reserve the space for right arrow here. The rest of the padding is set
919    // by adjustMenuListStyle, since PopupMenuChromium.cpp uses the padding
920    // from RenderMenuList to lay out the individual items in the popup.  If
921    // the MenuList actually has appearance "NoAppearance", then that means we
922    // don't draw a button, so don't reserve space for it.
923    const int barType = style->direction() == LTR ? RightPadding : LeftPadding;
924    if (paddingType == barType && style->appearance() != NoControlPart)
925        padding += ScrollbarTheme::nativeTheme()->scrollbarThickness();
926
927    return padding;
928}
929
930} // namespace WebCore
931