a3278f5d3b127bce719164fd11ea51c7aa369767
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderThemeChromiumDefault.cpp
1 /*
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
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., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "core/rendering/RenderThemeChromiumDefault.h"
27
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"
41
42 namespace WebCore {
43
44 static bool useMockTheme()
45 {
46     return isRunningLayoutTest();
47 }
48
49 unsigned RenderThemeChromiumDefault::m_activeSelectionBackgroundColor =
50     0xff1e90ff;
51 unsigned RenderThemeChromiumDefault::m_activeSelectionForegroundColor =
52     Color::black;
53 unsigned RenderThemeChromiumDefault::m_inactiveSelectionBackgroundColor =
54     0xffc8c8c8;
55 unsigned RenderThemeChromiumDefault::m_inactiveSelectionForegroundColor =
56     0xff323232;
57
58 double RenderThemeChromiumDefault::m_caretBlinkInterval;
59
60 static const unsigned defaultButtonBackgroundColor = 0xffdddddd;
61
62 static blink::WebThemeEngine::State getWebThemeState(const RenderTheme* theme, const RenderObject* o)
63 {
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;
74
75     return blink::WebThemeEngine::StateNormal;
76 }
77
78 PassRefPtr<RenderTheme> RenderThemeChromiumDefault::create()
79 {
80     return adoptRef(new RenderThemeChromiumDefault());
81 }
82
83 // RenderTheme::theme for Android is defined in RenderThemeChromiumAndroid.cpp.
84 #if !OS(ANDROID)
85 RenderTheme& RenderTheme::theme()
86 {
87     DEFINE_STATIC_REF(RenderTheme, renderTheme, (RenderThemeChromiumDefault::create()));
88     return *renderTheme;
89 }
90 #endif
91
92 RenderThemeChromiumDefault::RenderThemeChromiumDefault()
93 {
94     m_caretBlinkInterval = RenderTheme::caretBlinkInterval();
95 }
96
97 RenderThemeChromiumDefault::~RenderThemeChromiumDefault()
98 {
99 }
100
101 bool RenderThemeChromiumDefault::supportsFocusRing(const RenderStyle* style) const
102 {
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;
108     }
109
110     return RenderThemeChromiumSkia::supportsFocusRing(style);
111 }
112
113 Color RenderThemeChromiumDefault::systemColor(CSSValueID cssValueId) const
114 {
115     static const Color defaultButtonGrayColor(0xffdddddd);
116     static const Color defaultMenuColor(0xfff7f7f7);
117
118     if (cssValueId == CSSValueButtonface) {
119         if (useMockTheme())
120             return Color(0xc0, 0xc0, 0xc0);
121         return defaultButtonGrayColor;
122     }
123     if (cssValueId == CSSValueMenu)
124         return defaultMenuColor;
125     return RenderTheme::systemColor(cssValueId);
126 }
127
128 String RenderThemeChromiumDefault::extraDefaultStyleSheet()
129 {
130 #if !OS(WIN)
131     return RenderTheme::extraDefaultStyleSheet() +
132         RenderThemeChromiumSkia::extraDefaultStyleSheet() +
133         String(themeChromiumLinuxUserAgentStyleSheet, sizeof(themeChromiumLinuxUserAgentStyleSheet));
134 #else
135     return RenderTheme::extraDefaultStyleSheet() +
136         RenderThemeChromiumSkia::extraDefaultStyleSheet();
137 #endif
138 }
139
140 bool RenderThemeChromiumDefault::controlSupportsTints(const RenderObject* o) const
141 {
142     return isEnabled(o);
143 }
144
145 Color RenderThemeChromiumDefault::activeListBoxSelectionBackgroundColor() const
146 {
147     return Color(0x28, 0x28, 0x28);
148 }
149
150 Color RenderThemeChromiumDefault::activeListBoxSelectionForegroundColor() const
151 {
152     return Color::black;
153 }
154
155 Color RenderThemeChromiumDefault::inactiveListBoxSelectionBackgroundColor() const
156 {
157     return Color(0xc8, 0xc8, 0xc8);
158 }
159
160 Color RenderThemeChromiumDefault::inactiveListBoxSelectionForegroundColor() const
161 {
162     return Color(0x32, 0x32, 0x32);
163 }
164
165 Color RenderThemeChromiumDefault::platformActiveSelectionBackgroundColor() const
166 {
167     if (useMockTheme())
168         return Color(0x00, 0x00, 0xff); // Royal blue.
169     return m_activeSelectionBackgroundColor;
170 }
171
172 Color RenderThemeChromiumDefault::platformInactiveSelectionBackgroundColor() const
173 {
174     if (useMockTheme())
175         return Color(0x99, 0x99, 0x99); // Medium gray.
176     return m_inactiveSelectionBackgroundColor;
177 }
178
179 Color RenderThemeChromiumDefault::platformActiveSelectionForegroundColor() const
180 {
181     if (useMockTheme())
182         return Color(0xff, 0xff, 0xcc); // Pale yellow.
183     return m_activeSelectionForegroundColor;
184 }
185
186 Color RenderThemeChromiumDefault::platformInactiveSelectionForegroundColor() const
187 {
188     if (useMockTheme())
189         return Color::white;
190     return m_inactiveSelectionForegroundColor;
191 }
192
193 IntSize RenderThemeChromiumDefault::sliderTickSize() const
194 {
195     if (useMockTheme())
196         return IntSize(1, 3);
197     return IntSize(1, 6);
198 }
199
200 int RenderThemeChromiumDefault::sliderTickOffsetFromTrackCenter() const
201 {
202     if (useMockTheme())
203         return 11;
204     return -16;
205 }
206
207 void RenderThemeChromiumDefault::adjustSliderThumbSize(RenderStyle* style, Element* element) const
208 {
209     IntSize size = blink::Platform::current()->themeEngine()->getSize(blink::WebThemeEngine::PartSliderThumb);
210
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));
219     } else
220         RenderThemeChromiumSkia::adjustSliderThumbSize(style, element);
221 }
222
223 void RenderThemeChromiumDefault::setCaretBlinkInterval(double interval)
224 {
225     m_caretBlinkInterval = interval;
226 }
227
228 double RenderThemeChromiumDefault::caretBlinkIntervalInternal() const
229 {
230     return m_caretBlinkInterval;
231 }
232
233 void RenderThemeChromiumDefault::setSelectionColors(
234     unsigned activeBackgroundColor,
235     unsigned activeForegroundColor,
236     unsigned inactiveBackgroundColor,
237     unsigned inactiveForegroundColor)
238 {
239     m_activeSelectionBackgroundColor = activeBackgroundColor;
240     m_activeSelectionForegroundColor = activeForegroundColor;
241     m_inactiveSelectionBackgroundColor = inactiveBackgroundColor;
242     m_inactiveSelectionForegroundColor = inactiveForegroundColor;
243 }
244
245 bool RenderThemeChromiumDefault::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect)
246 {
247     if (i.context->paintingDisabled())
248         return false;
249     blink::WebThemeEngine::ExtraParams extraParams;
250     blink::WebCanvas* canvas = i.context->canvas();
251     extraParams.button.checked = isChecked(o);
252     extraParams.button.indeterminate = isIndeterminate(o);
253
254     float zoomLevel = o->style()->effectiveZoom();
255     GraphicsContextStateSaver stateSaver(*i.context, false);
256     IntRect unzoomedRect = rect;
257     if (zoomLevel != 1) {
258         stateSaver.save();
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());
264     }
265
266     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartCheckbox, getWebThemeState(this, o), blink::WebRect(unzoomedRect), &extraParams);
267     return false;
268 }
269
270 void RenderThemeChromiumDefault::setCheckboxSize(RenderStyle* style) const
271 {
272     // If the width and height are both specified, then we have nothing to do.
273     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
274         return;
275
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);
281 }
282
283 bool RenderThemeChromiumDefault::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect)
284 {
285     if (i.context->paintingDisabled())
286         return false;
287     blink::WebThemeEngine::ExtraParams extraParams;
288     blink::WebCanvas* canvas = i.context->canvas();
289     extraParams.button.checked = isChecked(o);
290
291     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartRadio, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
292     return false;
293 }
294
295 void RenderThemeChromiumDefault::setRadioSize(RenderStyle* style) const
296 {
297     // If the width and height are both specified, then we have nothing to do.
298     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
299         return;
300
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);
306 }
307
308 bool RenderThemeChromiumDefault::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
309 {
310     if (i.context->paintingDisabled())
311         return false;
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();
318
319     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartButton, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
320     return false;
321 }
322
323 bool RenderThemeChromiumDefault::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
324 {
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())
328         return true;
329     if (i.context->paintingDisabled())
330         return false;
331
332     ControlPart part = o->style()->appearance();
333
334     blink::WebThemeEngine::ExtraParams extraParams;
335     extraParams.textField.isTextArea = part == TextAreaPart;
336     extraParams.textField.isListbox = part == ListboxPart;
337
338     blink::WebCanvas* canvas = i.context->canvas();
339
340     Color backgroundColor = o->resolveColor(CSSPropertyBackgroundColor);
341     extraParams.textField.backgroundColor = backgroundColor.rgb();
342
343     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartTextField, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
344     return false;
345 }
346
347 bool RenderThemeChromiumDefault::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect)
348 {
349     if (!o->isBox() || i.context->paintingDisabled())
350         return false;
351
352     const int right = rect.x() + rect.width();
353     const int middle = rect.y() + rect.height() / 2;
354
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();
366
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();
373
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;
382     } else {
383         extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13;
384     }
385
386     blink::WebCanvas* canvas = i.context->canvas();
387
388     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartMenuList, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
389     return false;
390 }
391
392 bool RenderThemeChromiumDefault::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
393 {
394     if (!o->isBox() || i.context->paintingDisabled())
395         return false;
396
397     const int right = rect.x() + rect.width();
398     const int middle = rect.y() + rect.height() / 2;
399
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;
406
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;
416     } else {
417         extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13;
418     }
419
420     blink::WebCanvas* canvas = i.context->canvas();
421
422     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartMenuList, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
423     return false;
424 }
425
426 bool RenderThemeChromiumDefault::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& rect)
427 {
428     if (i.context->paintingDisabled())
429         return false;
430     blink::WebThemeEngine::ExtraParams extraParams;
431     blink::WebCanvas* canvas = i.context->canvas();
432     extraParams.slider.vertical = o->style()->appearance() == SliderVerticalPart;
433
434     paintSliderTicks(o, i, rect);
435
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) {
441         stateSaver.save();
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());
447     }
448
449     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartSliderTrack, getWebThemeState(this, o), blink::WebRect(unzoomedRect), &extraParams);
450
451     return false;
452 }
453
454 bool RenderThemeChromiumDefault::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& rect)
455 {
456     if (i.context->paintingDisabled())
457         return false;
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);
462
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) {
468         stateSaver.save();
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());
474     }
475
476     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartSliderThumb, getWebThemeState(this, o), blink::WebRect(unzoomedRect), &extraParams);
477     return false;
478 }
479
480 void RenderThemeChromiumDefault::adjustInnerSpinButtonStyle(RenderStyle* style, Element*) const
481 {
482     IntSize size = blink::Platform::current()->themeEngine()->getSize(blink::WebThemeEngine::PartInnerSpinButton);
483
484     style->setWidth(Length(size.width(), Fixed));
485     style->setMinWidth(Length(size.width(), Fixed));
486 }
487
488 bool RenderThemeChromiumDefault::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
489 {
490     if (i.context->paintingDisabled())
491         return false;
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);
496
497     blink::Platform::current()->themeEngine()->paint(canvas, blink::WebThemeEngine::PartInnerSpinButton, getWebThemeState(this, o), blink::WebRect(rect), &extraParams);
498     return false;
499 }
500
501 bool RenderThemeChromiumDefault::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& rect)
502 {
503     if (!o->isProgress())
504         return true;
505     if (i.context->paintingDisabled())
506         return false;
507
508     RenderProgress* renderProgress = toRenderProgress(o);
509     IntRect valueRect = progressValueRectFor(renderProgress, rect);
510
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();
517
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);
521     return false;
522 }
523
524 bool RenderThemeChromiumDefault::shouldOpenPickerWithF4Key() const
525 {
526     return true;
527 }
528
529 bool RenderThemeChromiumDefault::shouldUseFallbackTheme(RenderStyle* style) const
530 {
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;
536     }
537     return RenderTheme::shouldUseFallbackTheme(style);
538 }
539
540 } // namespace WebCore