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