2 * Copyright (C) 2007 Apple Inc.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4 * Copyright (C) 2008 Collabora Ltd.
5 * Copyright (C) 2008, 2009 Google Inc.
6 * Copyright (C) 2009 Kenneth Rohde Christiansen
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.
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.
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., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
26 #include "core/rendering/RenderThemeChromiumDefault.h"
28 #include "CSSValueKeywords.h"
29 #include "UserAgentStyleSheets.h"
30 #include "core/rendering/PaintInfo.h"
31 #include "core/rendering/RenderObject.h"
32 #include "core/rendering/RenderProgress.h"
33 #include "platform/LayoutTestSupport.h"
34 #include "platform/graphics/Color.h"
35 #include "platform/graphics/GraphicsContext.h"
36 #include "platform/graphics/GraphicsContextStateSaver.h"
37 #include "public/platform/Platform.h"
38 #include "public/platform/WebRect.h"
39 #include "public/platform/WebThemeEngine.h"
40 #include "wtf/StdLibExtras.h"
44 static bool useMockTheme()
46 return isRunningLayoutTest();
49 unsigned RenderThemeChromiumDefault::m_activeSelectionBackgroundColor =
51 unsigned RenderThemeChromiumDefault::m_activeSelectionForegroundColor =
53 unsigned RenderThemeChromiumDefault::m_inactiveSelectionBackgroundColor =
55 unsigned RenderThemeChromiumDefault::m_inactiveSelectionForegroundColor =
58 double RenderThemeChromiumDefault::m_caretBlinkInterval;
60 static const unsigned defaultButtonBackgroundColor = 0xffdddddd;
62 static blink::WebThemeEngine::State getWebThemeState(const RenderTheme* theme, const RenderObject* o)
64 if (!theme->isEnabled(o))
65 return blink::WebThemeEngine::StateDisabled;
66 if (useMockTheme() && theme->isReadOnlyControl(o))
67 return blink::WebThemeEngine::StateReadonly;
68 if (theme->isPressed(o))
69 return blink::WebThemeEngine::StatePressed;
70 if (useMockTheme() && theme->isFocused(o))
71 return blink::WebThemeEngine::StateFocused;
72 if (theme->isHovered(o))
73 return blink::WebThemeEngine::StateHover;
75 return blink::WebThemeEngine::StateNormal;
78 PassRefPtr<RenderTheme> RenderThemeChromiumDefault::create()
80 return adoptRef(new RenderThemeChromiumDefault());
83 // RenderTheme::theme for Android is defined in RenderThemeChromiumAndroid.cpp.
85 RenderTheme& RenderTheme::theme()
87 DEFINE_STATIC_REF(RenderTheme, renderTheme, (RenderThemeChromiumDefault::create()));
92 RenderThemeChromiumDefault::RenderThemeChromiumDefault()
94 m_caretBlinkInterval = RenderTheme::caretBlinkInterval();
97 RenderThemeChromiumDefault::~RenderThemeChromiumDefault()
101 bool RenderThemeChromiumDefault::supportsFocusRing(const RenderStyle* style) const
103 if (useMockTheme()) {
104 // Don't use focus rings for buttons when mocking controls.
105 return style->appearance() == ButtonPart
106 || style->appearance() == PushButtonPart
107 || style->appearance() == SquareButtonPart;
110 return RenderThemeChromiumSkia::supportsFocusRing(style);
113 Color RenderThemeChromiumDefault::systemColor(CSSValueID cssValueId) const
115 static const Color defaultButtonGrayColor(0xffdddddd);
116 static const Color defaultMenuColor(0xfff7f7f7);
118 if (cssValueId == CSSValueButtonface) {
120 return Color(0xc0, 0xc0, 0xc0);
121 return defaultButtonGrayColor;
123 if (cssValueId == CSSValueMenu)
124 return defaultMenuColor;
125 return RenderTheme::systemColor(cssValueId);
128 String RenderThemeChromiumDefault::extraDefaultStyleSheet()
131 return RenderTheme::extraDefaultStyleSheet() +
132 RenderThemeChromiumSkia::extraDefaultStyleSheet() +
133 String(themeChromiumLinuxUserAgentStyleSheet, sizeof(themeChromiumLinuxUserAgentStyleSheet));
135 return RenderTheme::extraDefaultStyleSheet() +
136 RenderThemeChromiumSkia::extraDefaultStyleSheet();
140 bool RenderThemeChromiumDefault::controlSupportsTints(const RenderObject* o) const
145 Color RenderThemeChromiumDefault::activeListBoxSelectionBackgroundColor() const
147 return Color(0x28, 0x28, 0x28);
150 Color RenderThemeChromiumDefault::activeListBoxSelectionForegroundColor() const
155 Color RenderThemeChromiumDefault::inactiveListBoxSelectionBackgroundColor() const
157 return Color(0xc8, 0xc8, 0xc8);
160 Color RenderThemeChromiumDefault::inactiveListBoxSelectionForegroundColor() const
162 return Color(0x32, 0x32, 0x32);
165 Color RenderThemeChromiumDefault::platformActiveSelectionBackgroundColor() const
168 return Color(0x00, 0x00, 0xff); // Royal blue.
169 return m_activeSelectionBackgroundColor;
172 Color RenderThemeChromiumDefault::platformInactiveSelectionBackgroundColor() const
175 return Color(0x99, 0x99, 0x99); // Medium gray.
176 return m_inactiveSelectionBackgroundColor;
179 Color RenderThemeChromiumDefault::platformActiveSelectionForegroundColor() const
182 return Color(0xff, 0xff, 0xcc); // Pale yellow.
183 return m_activeSelectionForegroundColor;
186 Color RenderThemeChromiumDefault::platformInactiveSelectionForegroundColor() const
190 return m_inactiveSelectionForegroundColor;
193 IntSize RenderThemeChromiumDefault::sliderTickSize() const
196 return IntSize(1, 3);
197 return IntSize(1, 6);
200 int RenderThemeChromiumDefault::sliderTickOffsetFromTrackCenter() const
207 void RenderThemeChromiumDefault::adjustSliderThumbSize(RenderStyle* style, Element* element) const
209 IntSize size = blink::Platform::current()->themeEngine()->getSize(blink::WebThemeEngine::PartSliderThumb);
211 // FIXME: Mock theme doesn't handle zoomed sliders.
212 float zoomLevel = useMockTheme() ? 1 : style->effectiveZoom();
213 if (style->appearance() == SliderThumbHorizontalPart) {
214 style->setWidth(Length(size.width() * zoomLevel, Fixed));
215 style->setHeight(Length(size.height() * zoomLevel, Fixed));
216 } else if (style->appearance() == SliderThumbVerticalPart) {
217 style->setWidth(Length(size.height() * zoomLevel, Fixed));
218 style->setHeight(Length(size.width() * zoomLevel, Fixed));
220 RenderThemeChromiumSkia::adjustSliderThumbSize(style, element);
223 void RenderThemeChromiumDefault::setCaretBlinkInterval(double interval)
225 m_caretBlinkInterval = interval;
228 double RenderThemeChromiumDefault::caretBlinkIntervalInternal() const
230 return m_caretBlinkInterval;
233 void RenderThemeChromiumDefault::setSelectionColors(
234 unsigned activeBackgroundColor,
235 unsigned activeForegroundColor,
236 unsigned inactiveBackgroundColor,
237 unsigned inactiveForegroundColor)
239 m_activeSelectionBackgroundColor = activeBackgroundColor;
240 m_activeSelectionForegroundColor = activeForegroundColor;
241 m_inactiveSelectionBackgroundColor = inactiveBackgroundColor;
242 m_inactiveSelectionForegroundColor = inactiveForegroundColor;
245 bool RenderThemeChromiumDefault::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect)
247 if (i.context->paintingDisabled())
249 blink::WebThemeEngine::ExtraParams extraParams;
250 blink::WebCanvas* canvas = i.context->canvas();
251 extraParams.button.checked = isChecked(o);
252 extraParams.button.indeterminate = isIndeterminate(o);
254 float zoomLevel = o->style()->effectiveZoom();
255 GraphicsContextStateSaver stateSaver(*i.context, false);
256 IntRect unzoomedRect = rect;
257 if (zoomLevel != 1) {
259 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
260 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
261 i.context->translate(unzoomedRect.x(), unzoomedRect.y());
262 i.context->scale(FloatSize(zoomLevel, zoomLevel));
263 i.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
266 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartCheckbox, getWebThemeState(this, o), blink::WebRect(unzoomedRect), &extraParams);
270 void RenderThemeChromiumDefault::setCheckboxSize(RenderStyle* style) const
272 // If the width and height are both specified, then we have nothing to do.
273 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
276 IntSize size = blink::Platform::current()->themeEngine()->getSize(blink::WebThemeEngine::PartCheckbox);
277 float zoomLevel = style->effectiveZoom();
278 size.setWidth(size.width() * zoomLevel);
279 size.setHeight(size.height() * zoomLevel);
280 setSizeIfAuto(style, size);
283 bool RenderThemeChromiumDefault::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect)
285 if (i.context->paintingDisabled())
287 blink::WebThemeEngine::ExtraParams extraParams;
288 blink::WebCanvas* canvas = i.context->canvas();
289 extraParams.button.checked = isChecked(o);
291 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartRadio, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
295 void RenderThemeChromiumDefault::setRadioSize(RenderStyle* style) const
297 // If the width and height are both specified, then we have nothing to do.
298 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
301 IntSize size = blink::Platform::current()->themeEngine()->getSize(blink::WebThemeEngine::PartRadio);
302 float zoomLevel = style->effectiveZoom();
303 size.setWidth(size.width() * zoomLevel);
304 size.setHeight(size.height() * zoomLevel);
305 setSizeIfAuto(style, size);
308 bool RenderThemeChromiumDefault::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
310 if (i.context->paintingDisabled())
312 blink::WebThemeEngine::ExtraParams extraParams;
313 blink::WebCanvas* canvas = i.context->canvas();
314 extraParams.button.hasBorder = true;
315 extraParams.button.backgroundColor = useMockTheme() ? 0xffc0c0c0 : defaultButtonBackgroundColor;
316 if (o->hasBackground())
317 extraParams.button.backgroundColor = o->resolveColor(CSSPropertyBackgroundColor).rgb();
319 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartButton, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
323 bool RenderThemeChromiumDefault::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
325 // WebThemeEngine does not handle border rounded corner and background image
326 // so return true to draw CSS border and background.
327 if (o->style()->hasBorderRadius() || o->style()->hasBackgroundImage())
329 if (i.context->paintingDisabled())
332 ControlPart part = o->style()->appearance();
334 blink::WebThemeEngine::ExtraParams extraParams;
335 extraParams.textField.isTextArea = part == TextAreaPart;
336 extraParams.textField.isListbox = part == ListboxPart;
338 blink::WebCanvas* canvas = i.context->canvas();
340 Color backgroundColor = o->resolveColor(CSSPropertyBackgroundColor);
341 extraParams.textField.backgroundColor = backgroundColor.rgb();
343 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartTextField, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
347 bool RenderThemeChromiumDefault::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect)
349 if (!o->isBox() || i.context->paintingDisabled())
352 const int right = rect.x() + rect.width();
353 const int middle = rect.y() + rect.height() / 2;
355 blink::WebThemeEngine::ExtraParams extraParams;
356 extraParams.menuList.arrowY = middle;
357 const RenderBox* box = toRenderBox(o);
358 // Match Chromium Win behaviour of showing all borders if any are shown.
359 extraParams.menuList.hasBorder = box->borderRight() || box->borderLeft() || box->borderTop() || box->borderBottom();
360 extraParams.menuList.hasBorderRadius = o->style()->hasBorderRadius();
361 // Fallback to transparent if the specified color object is invalid.
362 Color backgroundColor(Color::transparent);
363 if (o->hasBackground())
364 backgroundColor = o->resolveColor(CSSPropertyBackgroundColor);
365 extraParams.menuList.backgroundColor = backgroundColor.rgb();
367 // If we have a background image, don't fill the content area to expose the
368 // parent's background. Also, we shouldn't fill the content area if the
369 // alpha of the color is 0. The API of Windows GDI ignores the alpha.
370 // FIXME: the normal Aura theme doesn't care about this, so we should
371 // investigate if we really need fillContentArea.
372 extraParams.menuList.fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha();
374 if (useMockTheme()) {
375 // The size and position of the drop-down button is different between
376 // the mock theme and the regular aura theme.
377 int spacingTop = box->borderTop() + box->paddingTop();
378 int spacingBottom = box->borderBottom() + box->paddingBottom();
379 int spacingRight = box->borderRight() + box->paddingRight();
380 extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 4 + spacingRight: right - 13 - spacingRight;
381 extraParams.menuList.arrowHeight = rect.height() - spacingBottom - spacingTop;
383 extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13;
386 blink::WebCanvas* canvas = i.context->canvas();
388 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartMenuList, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
392 bool RenderThemeChromiumDefault::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
394 if (!o->isBox() || i.context->paintingDisabled())
397 const int right = rect.x() + rect.width();
398 const int middle = rect.y() + rect.height() / 2;
400 blink::WebThemeEngine::ExtraParams extraParams;
401 extraParams.menuList.arrowY = middle;
402 extraParams.menuList.hasBorder = false;
403 extraParams.menuList.hasBorderRadius = o->style()->hasBorderRadius();
404 extraParams.menuList.backgroundColor = Color::transparent;
405 extraParams.menuList.fillContentArea = false;
407 if (useMockTheme()) {
408 const RenderBox* box = toRenderBox(o);
409 // The size and position of the drop-down button is different between
410 // the mock theme and the regular aura theme.
411 int spacingTop = box->borderTop() + box->paddingTop();
412 int spacingBottom = box->borderBottom() + box->paddingBottom();
413 int spacingRight = box->borderRight() + box->paddingRight();
414 extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 4 + spacingRight: right - 13 - spacingRight;
415 extraParams.menuList.arrowHeight = rect.height() - spacingBottom - spacingTop;
417 extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13;
420 blink::WebCanvas* canvas = i.context->canvas();
422 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartMenuList, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
426 bool RenderThemeChromiumDefault::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& rect)
428 if (i.context->paintingDisabled())
430 blink::WebThemeEngine::ExtraParams extraParams;
431 blink::WebCanvas* canvas = i.context->canvas();
432 extraParams.slider.vertical = o->style()->appearance() == SliderVerticalPart;
434 paintSliderTicks(o, i, rect);
436 // FIXME: Mock theme doesn't handle zoomed sliders.
437 float zoomLevel = useMockTheme() ? 1 : o->style()->effectiveZoom();
438 GraphicsContextStateSaver stateSaver(*i.context, false);
439 IntRect unzoomedRect = rect;
440 if (zoomLevel != 1) {
442 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
443 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
444 i.context->translate(unzoomedRect.x(), unzoomedRect.y());
445 i.context->scale(FloatSize(zoomLevel, zoomLevel));
446 i.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
449 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartSliderTrack, getWebThemeState(this, o), blink::WebRect(unzoomedRect), &extraParams);
454 bool RenderThemeChromiumDefault::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& rect)
456 if (i.context->paintingDisabled())
458 blink::WebThemeEngine::ExtraParams extraParams;
459 blink::WebCanvas* canvas = i.context->canvas();
460 extraParams.slider.vertical = o->style()->appearance() == SliderThumbVerticalPart;
461 extraParams.slider.inDrag = isPressed(o);
463 // FIXME: Mock theme doesn't handle zoomed sliders.
464 float zoomLevel = useMockTheme() ? 1 : o->style()->effectiveZoom();
465 GraphicsContextStateSaver stateSaver(*i.context, false);
466 IntRect unzoomedRect = rect;
467 if (zoomLevel != 1) {
469 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
470 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
471 i.context->translate(unzoomedRect.x(), unzoomedRect.y());
472 i.context->scale(FloatSize(zoomLevel, zoomLevel));
473 i.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
476 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartSliderThumb, getWebThemeState(this, o), blink::WebRect(unzoomedRect), &extraParams);
480 void RenderThemeChromiumDefault::adjustInnerSpinButtonStyle(RenderStyle* style, Element*) const
482 IntSize size = blink::Platform::current()->themeEngine()->getSize(blink::WebThemeEngine::PartInnerSpinButton);
484 style->setWidth(Length(size.width(), Fixed));
485 style->setMinWidth(Length(size.width(), Fixed));
488 bool RenderThemeChromiumDefault::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
490 if (i.context->paintingDisabled())
492 blink::WebThemeEngine::ExtraParams extraParams;
493 blink::WebCanvas* canvas = i.context->canvas();
494 extraParams.innerSpin.spinUp = (controlStatesForRenderer(o) & SpinUpState);
495 extraParams.innerSpin.readOnly = isReadOnlyControl(o);
497 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartInnerSpinButton, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
501 bool RenderThemeChromiumDefault::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& rect)
503 if (!o->isProgress())
505 if (i.context->paintingDisabled())
508 RenderProgress* renderProgress = toRenderProgress(o);
509 IntRect valueRect = progressValueRectFor(renderProgress, rect);
511 blink::WebThemeEngine::ExtraParams extraParams;
512 extraParams.progressBar.determinate = renderProgress->isDeterminate();
513 extraParams.progressBar.valueRectX = valueRect.x();
514 extraParams.progressBar.valueRectY = valueRect.y();
515 extraParams.progressBar.valueRectWidth = valueRect.width();
516 extraParams.progressBar.valueRectHeight = valueRect.height();
518 DirectionFlippingScope scope(o, i, rect);
519 blink::WebCanvas* canvas = i.context->canvas();
520 blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartProgressBar, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
524 bool RenderThemeChromiumDefault::shouldOpenPickerWithF4Key() const
529 bool RenderThemeChromiumDefault::shouldUseFallbackTheme(RenderStyle* style) const
531 if (useMockTheme()) {
532 // The mock theme can't handle zoomed controls, so we fall back to the "fallback" theme.
533 ControlPart part = style->appearance();
534 if (part == CheckboxPart || part == RadioPart)
535 return style->effectiveZoom() != 1;
537 return RenderTheme::shouldUseFallbackTheme(style);
540 } // namespace WebCore