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 
58 namespace 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.
62 static const int kStandardMenuListButtonWidth = 17;
63 
64 namespace {
65 // We must not create multiple ThemePainter instances.
66 class ThemePainter {
67 public:
ThemePainter(GraphicsContext * context,const IntRect & r)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 
~ThemePainter()90     ~ThemePainter()
91     {
92         m_helper.composite();
93 #ifndef NDEBUG
94         s_hasInstance = false;
95 #endif
96     }
97 
context()98     GraphicsContext* context() { return m_helper.context(); }
drawRect()99     const IntRect& drawRect() { return m_helper.drawRect(); }
100 
101 private:
102 
canvasHasMultipleLayers(const SkCanvas * canvas)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 
getLayerMode(GraphicsContext * context,TransparencyWin::TransformMode transformMode)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 
getTransformMode(const AffineTransform & matrix)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
137 bool ThemePainter::s_hasInstance = false;
138 #endif
139 
140 } // namespace
141 
getNonClientMetrics(NONCLIENTMETRICS * metrics)142 static 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 
151 static FontDescription smallSystemFont;
152 static FontDescription menuFont;
153 static 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 
supportsFocus(ControlPart appearance)158 static 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.
systemFontSize(const LOGFONT & font)174 static 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.
pointsToPixels(float points)207 static 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 
querySystemBlinkInterval(double defaultInterval)224 static 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 
create()234 PassRefPtr<RenderTheme> RenderThemeChromiumWin::create()
235 {
236     return adoptRef(new RenderThemeChromiumWin);
237 }
238 
themeForPage(Page * page)239 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
240 {
241     static RenderTheme* rt = RenderThemeChromiumWin::create().releaseRef();
242     return rt;
243 }
244 
supportsFocusRing(const RenderStyle * style) const245 bool 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 
platformActiveSelectionBackgroundColor() const254 Color 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 
platformInactiveSelectionBackgroundColor() const262 Color 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 
platformActiveSelectionForegroundColor() const270 Color 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 
platformInactiveSelectionForegroundColor() const278 Color RenderThemeChromiumWin::platformInactiveSelectionForegroundColor() const
279 {
280     return Color::white;
281 }
282 
platformActiveTextSearchHighlightColor() const283 Color RenderThemeChromiumWin::platformActiveTextSearchHighlightColor() const
284 {
285     return Color(0xff, 0x96, 0x32); // Orange.
286 }
287 
platformInactiveTextSearchHighlightColor() const288 Color RenderThemeChromiumWin::platformInactiveTextSearchHighlightColor() const
289 {
290     return Color(0xff, 0xff, 0x96); // Yellow.
291 }
292 
systemFont(int propId,FontDescription & fontDescription) const293 void 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().
cssValueIdToSysColorIndex(int cssValueId)355 static 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 
systemColor(int cssValueId) const390 Color 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 
adjustSliderThumbSize(RenderObject * o) const400 void 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 
paintCheckbox(RenderObject * o,const PaintInfo & i,const IntRect & r)415 bool RenderThemeChromiumWin::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
416 {
417     return paintButton(o, i, r);
418 }
paintRadio(RenderObject * o,const PaintInfo & i,const IntRect & r)419 bool RenderThemeChromiumWin::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
420 {
421     return paintButton(o, i, r);
422 }
423 
paintButton(RenderObject * o,const PaintInfo & i,const IntRect & r)424 bool 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 
paintTextField(RenderObject * o,const PaintInfo & i,const IntRect & r)437 bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
438 {
439     return paintTextFieldInternal(o, i, r, true);
440 }
441 
paintSliderTrack(RenderObject * o,const PaintInfo & i,const IntRect & r)442 bool 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 
paintSliderThumb(RenderObject * o,const PaintInfo & i,const IntRect & r)455 bool RenderThemeChromiumWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
456 {
457     return paintSliderTrack(o, i, r);
458 }
459 
menuListButtonWidth()460 static 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)
paintMenuList(RenderObject * o,const PaintInfo & i,const IntRect & r)467 bool 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
setDefaultFontSize(int fontSize)519 void RenderThemeChromiumWin::setDefaultFontSize(int fontSize)
520 {
521     RenderThemeChromiumSkia::setDefaultFontSize(fontSize);
522 
523     // Reset cached fonts.
524     smallSystemFont = menuFont = labelFont = FontDescription();
525 }
526 
caretBlinkIntervalInternal() const527 double 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 
determineState(RenderObject * o,ControlSubPart subPart)534 unsigned 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 
determineSliderThumbState(RenderObject * o)560 unsigned 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 
determineClassicState(RenderObject * o,ControlSubPart subPart)574 unsigned 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 
getThemeData(RenderObject * o,ControlSubPart subPart)612 ThemeData 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 
paintTextFieldInternal(RenderObject * o,const PaintInfo & i,const IntRect & r,bool drawEdges)669 bool 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 
adjustInnerSpinButtonStyle(CSSStyleSelector *,RenderStyle * style,Element *) const714 void 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 
paintInnerSpinButton(RenderObject * object,const PaintInfo & info,const IntRect & rect)721 bool 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
754 static const double progressAnimationFrameRate = 0.033;
755 
animationRepeatIntervalForProgressBar(RenderProgress *) const756 double RenderThemeChromiumWin::animationRepeatIntervalForProgressBar(RenderProgress*) const
757 {
758     return progressAnimationFrameRate;
759 }
760 
animationDurationForProgressBar(RenderProgress * renderProgress) const761 double 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 
adjustProgressBarStyle(CSSStyleSelector *,RenderStyle *,Element *) const768 void RenderThemeChromiumWin::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const
769 {
770 }
771 
paintProgressBar(RenderObject * o,const PaintInfo & i,const IntRect & r)772 bool 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