Upstream version 9.38.198.0
[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 "core/CSSValueKeywords.h"
29 #include "core/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 blink {
43
44 static bool useMockTheme()
45 {
46     return LayoutTestSupport::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 WebThemeEngine::State getWebThemeState(const RenderTheme* theme, const RenderObject* o)
63 {
64     if (!theme->isEnabled(o))
65         return WebThemeEngine::StateDisabled;
66     if (useMockTheme() && theme->isReadOnlyControl(o))
67         return WebThemeEngine::StateReadonly;
68     if (theme->isPressed(o))
69         return WebThemeEngine::StatePressed;
70     if (useMockTheme() && theme->isFocused(o))
71         return WebThemeEngine::StateFocused;
72     if (theme->isHovered(o))
73         return WebThemeEngine::StateHover;
74
75     return 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     // FIXME: We should not have OS() branches here.
131     // We should have something like RenderThemeWin, RenderThemeLinux, or
132     // should concatenate UA stylesheets on build time.
133 #if !OS(WIN)
134     return RenderThemeChromiumSkia::extraDefaultStyleSheet() +
135 #if !OS(ANDROID)
136         String(themeInputMultipleFieldsCss, sizeof(themeInputMultipleFieldsCss)) +
137 #endif
138         String(themeChromiumLinuxCss, sizeof(themeChromiumLinuxCss));
139 #else
140     return RenderThemeChromiumSkia::extraDefaultStyleSheet() +
141         String(themeInputMultipleFieldsCss, sizeof(themeInputMultipleFieldsCss));
142 #endif
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 = Platform::current()->themeEngine()->getSize(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     WebThemeEngine::ExtraParams extraParams;
248     WebCanvas* canvas = i.context->canvas();
249     extraParams.button.checked = isChecked(o);
250     extraParams.button.indeterminate = isIndeterminate(o);
251
252     float zoomLevel = o->style()->effectiveZoom();
253     GraphicsContextStateSaver stateSaver(*i.context, false);
254     IntRect unzoomedRect = rect;
255     if (zoomLevel != 1) {
256         stateSaver.save();
257         unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
258         unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
259         i.context->translate(unzoomedRect.x(), unzoomedRect.y());
260         i.context->scale(zoomLevel, zoomLevel);
261         i.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
262     }
263
264     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartCheckbox, getWebThemeState(this, o), WebRect(unzoomedRect), &extraParams);
265     return false;
266 }
267
268 void RenderThemeChromiumDefault::setCheckboxSize(RenderStyle* style) const
269 {
270     // If the width and height are both specified, then we have nothing to do.
271     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
272         return;
273
274     IntSize size = Platform::current()->themeEngine()->getSize(WebThemeEngine::PartCheckbox);
275     float zoomLevel = style->effectiveZoom();
276     size.setWidth(size.width() * zoomLevel);
277     size.setHeight(size.height() * zoomLevel);
278     setSizeIfAuto(style, size);
279 }
280
281 bool RenderThemeChromiumDefault::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect)
282 {
283     WebThemeEngine::ExtraParams extraParams;
284     WebCanvas* canvas = i.context->canvas();
285     extraParams.button.checked = isChecked(o);
286
287     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartRadio, getWebThemeState(this, o), WebRect(rect), &extraParams);
288     return false;
289 }
290
291 void RenderThemeChromiumDefault::setRadioSize(RenderStyle* style) const
292 {
293     // If the width and height are both specified, then we have nothing to do.
294     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
295         return;
296
297     IntSize size = Platform::current()->themeEngine()->getSize(WebThemeEngine::PartRadio);
298     float zoomLevel = style->effectiveZoom();
299     size.setWidth(size.width() * zoomLevel);
300     size.setHeight(size.height() * zoomLevel);
301     setSizeIfAuto(style, size);
302 }
303
304 bool RenderThemeChromiumDefault::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
305 {
306     WebThemeEngine::ExtraParams extraParams;
307     WebCanvas* canvas = i.context->canvas();
308     extraParams.button.hasBorder = true;
309     extraParams.button.backgroundColor = useMockTheme() ? 0xffc0c0c0 : defaultButtonBackgroundColor;
310     if (o->hasBackground())
311         extraParams.button.backgroundColor = o->resolveColor(CSSPropertyBackgroundColor).rgb();
312
313     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartButton, getWebThemeState(this, o), WebRect(rect), &extraParams);
314     return false;
315 }
316
317 bool RenderThemeChromiumDefault::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
318 {
319     // WebThemeEngine does not handle border rounded corner and background image
320     // so return true to draw CSS border and background.
321     if (o->style()->hasBorderRadius() || o->style()->hasBackgroundImage())
322         return true;
323
324     ControlPart part = o->style()->appearance();
325
326     WebThemeEngine::ExtraParams extraParams;
327     extraParams.textField.isTextArea = part == TextAreaPart;
328     extraParams.textField.isListbox = part == ListboxPart;
329
330     WebCanvas* canvas = i.context->canvas();
331
332     Color backgroundColor = o->resolveColor(CSSPropertyBackgroundColor);
333     extraParams.textField.backgroundColor = backgroundColor.rgb();
334
335     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartTextField, getWebThemeState(this, o), WebRect(rect), &extraParams);
336     return false;
337 }
338
339 bool RenderThemeChromiumDefault::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect)
340 {
341     if (!o->isBox())
342         return false;
343
344     const int right = rect.x() + rect.width();
345     const int middle = rect.y() + rect.height() / 2;
346
347     WebThemeEngine::ExtraParams extraParams;
348     extraParams.menuList.arrowY = middle;
349     const RenderBox* box = toRenderBox(o);
350     // Match Chromium Win behaviour of showing all borders if any are shown.
351     extraParams.menuList.hasBorder = box->borderRight() || box->borderLeft() || box->borderTop() || box->borderBottom();
352     extraParams.menuList.hasBorderRadius = o->style()->hasBorderRadius();
353     // Fallback to transparent if the specified color object is invalid.
354     Color backgroundColor(Color::transparent);
355     if (o->hasBackground())
356         backgroundColor = o->resolveColor(CSSPropertyBackgroundColor);
357     extraParams.menuList.backgroundColor = backgroundColor.rgb();
358
359     // If we have a background image, don't fill the content area to expose the
360     // parent's background. Also, we shouldn't fill the content area if the
361     // alpha of the color is 0. The API of Windows GDI ignores the alpha.
362     // FIXME: the normal Aura theme doesn't care about this, so we should
363     // investigate if we really need fillContentArea.
364     extraParams.menuList.fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha();
365
366     if (useMockTheme()) {
367         // The size and position of the drop-down button is different between
368         // the mock theme and the regular aura theme.
369         int spacingTop = box->borderTop() + box->paddingTop();
370         int spacingBottom = box->borderBottom() + box->paddingBottom();
371         int spacingRight = box->borderRight() + box->paddingRight();
372         extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 4 + spacingRight: right - 13 - spacingRight;
373         extraParams.menuList.arrowHeight = rect.height() - spacingBottom - spacingTop;
374     } else {
375         extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13;
376     }
377
378     WebCanvas* canvas = i.context->canvas();
379
380     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartMenuList, getWebThemeState(this, o), WebRect(rect), &extraParams);
381     return false;
382 }
383
384 bool RenderThemeChromiumDefault::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
385 {
386     if (!o->isBox())
387         return false;
388
389     const int right = rect.x() + rect.width();
390     const int middle = rect.y() + rect.height() / 2;
391
392     WebThemeEngine::ExtraParams extraParams;
393     extraParams.menuList.arrowY = middle;
394     extraParams.menuList.hasBorder = false;
395     extraParams.menuList.hasBorderRadius = o->style()->hasBorderRadius();
396     extraParams.menuList.backgroundColor = Color::transparent;
397     extraParams.menuList.fillContentArea = false;
398
399     if (useMockTheme()) {
400         const RenderBox* box = toRenderBox(o);
401         // The size and position of the drop-down button is different between
402         // the mock theme and the regular aura theme.
403         int spacingTop = box->borderTop() + box->paddingTop();
404         int spacingBottom = box->borderBottom() + box->paddingBottom();
405         int spacingRight = box->borderRight() + box->paddingRight();
406         extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 4 + spacingRight: right - 13 - spacingRight;
407         extraParams.menuList.arrowHeight = rect.height() - spacingBottom - spacingTop;
408     } else {
409         extraParams.menuList.arrowX = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13;
410     }
411
412     WebCanvas* canvas = i.context->canvas();
413
414     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartMenuList, getWebThemeState(this, o), WebRect(rect), &extraParams);
415     return false;
416 }
417
418 bool RenderThemeChromiumDefault::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& rect)
419 {
420     WebThemeEngine::ExtraParams extraParams;
421     WebCanvas* canvas = i.context->canvas();
422     extraParams.slider.vertical = o->style()->appearance() == SliderVerticalPart;
423
424     paintSliderTicks(o, i, rect);
425
426     // FIXME: Mock theme doesn't handle zoomed sliders.
427     float zoomLevel = useMockTheme() ? 1 : o->style()->effectiveZoom();
428     GraphicsContextStateSaver stateSaver(*i.context, false);
429     IntRect unzoomedRect = rect;
430     if (zoomLevel != 1) {
431         stateSaver.save();
432         unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
433         unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
434         i.context->translate(unzoomedRect.x(), unzoomedRect.y());
435         i.context->scale(zoomLevel, zoomLevel);
436         i.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
437     }
438
439     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartSliderTrack, getWebThemeState(this, o), WebRect(unzoomedRect), &extraParams);
440
441     return false;
442 }
443
444 bool RenderThemeChromiumDefault::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& rect)
445 {
446     WebThemeEngine::ExtraParams extraParams;
447     WebCanvas* canvas = i.context->canvas();
448     extraParams.slider.vertical = o->style()->appearance() == SliderThumbVerticalPart;
449     extraParams.slider.inDrag = isPressed(o);
450
451     // FIXME: Mock theme doesn't handle zoomed sliders.
452     float zoomLevel = useMockTheme() ? 1 : o->style()->effectiveZoom();
453     GraphicsContextStateSaver stateSaver(*i.context, false);
454     IntRect unzoomedRect = rect;
455     if (zoomLevel != 1) {
456         stateSaver.save();
457         unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
458         unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
459         i.context->translate(unzoomedRect.x(), unzoomedRect.y());
460         i.context->scale(zoomLevel, zoomLevel);
461         i.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
462     }
463
464     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartSliderThumb, getWebThemeState(this, o), WebRect(unzoomedRect), &extraParams);
465     return false;
466 }
467
468 void RenderThemeChromiumDefault::adjustInnerSpinButtonStyle(RenderStyle* style, Element*) const
469 {
470     IntSize size = Platform::current()->themeEngine()->getSize(WebThemeEngine::PartInnerSpinButton);
471
472     style->setWidth(Length(size.width(), Fixed));
473     style->setMinWidth(Length(size.width(), Fixed));
474 }
475
476 bool RenderThemeChromiumDefault::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
477 {
478     WebThemeEngine::ExtraParams extraParams;
479     WebCanvas* canvas = i.context->canvas();
480     extraParams.innerSpin.spinUp = (controlStatesForRenderer(o) & SpinUpControlState);
481     extraParams.innerSpin.readOnly = isReadOnlyControl(o);
482
483     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartInnerSpinButton, getWebThemeState(this, o), WebRect(rect), &extraParams);
484     return false;
485 }
486
487 bool RenderThemeChromiumDefault::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& rect)
488 {
489     if (!o->isProgress())
490         return true;
491
492     RenderProgress* renderProgress = toRenderProgress(o);
493     IntRect valueRect = progressValueRectFor(renderProgress, rect);
494
495     WebThemeEngine::ExtraParams extraParams;
496     extraParams.progressBar.determinate = renderProgress->isDeterminate();
497     extraParams.progressBar.valueRectX = valueRect.x();
498     extraParams.progressBar.valueRectY = valueRect.y();
499     extraParams.progressBar.valueRectWidth = valueRect.width();
500     extraParams.progressBar.valueRectHeight = valueRect.height();
501
502     DirectionFlippingScope scope(o, i, rect);
503     WebCanvas* canvas = i.context->canvas();
504     Platform::current()->themeEngine()->paint(canvas, WebThemeEngine::PartProgressBar, getWebThemeState(this, o), WebRect(rect), &extraParams);
505     return false;
506 }
507
508 bool RenderThemeChromiumDefault::shouldOpenPickerWithF4Key() const
509 {
510     return true;
511 }
512
513 bool RenderThemeChromiumDefault::shouldUseFallbackTheme(RenderStyle* style) const
514 {
515     if (useMockTheme()) {
516         // The mock theme can't handle zoomed controls, so we fall back to the "fallback" theme.
517         ControlPart part = style->appearance();
518         if (part == CheckboxPart || part == RadioPart)
519             return style->effectiveZoom() != 1;
520     }
521     return RenderTheme::shouldUseFallbackTheme(style);
522 }
523
524 } // namespace blink