RenderThemeAndroid.cpp revision b5618de0bdd440dd8567ef1677dc4e514905dc0b
1/*
2 * Copyright 2009, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "RenderThemeAndroid.h"
28
29#include "Color.h"
30#include "Element.h"
31#include "GraphicsContext.h"
32#include "HTMLNames.h"
33#include "HTMLOptionElement.h"
34#include "HTMLSelectElement.h"
35#include "Node.h"
36#include "PlatformGraphicsContext.h"
37#if ENABLE(VIDEO)
38#include "RenderMediaControls.h"
39#endif
40#include "RenderSkinAndroid.h"
41#include "RenderSkinButton.h"
42#include "RenderSkinCombo.h"
43#include "RenderSkinMediaButton.h"
44#include "RenderSkinRadio.h"
45#include "SkCanvas.h"
46#include "UserAgentStyleSheets.h"
47#include "WebCoreFrameBridge.h"
48
49namespace WebCore {
50
51// Add padding to the fontSize of ListBoxes to get their maximum sizes.
52// Listboxes often have a specified size.  Since we change them into
53// dropdowns, we want a much smaller height, which encompasses the text.
54const int listboxPadding = 5;
55
56// This is the color of selection in a textfield.  It was computed from
57// frameworks/base/core/res/res/values/colors.xml, which uses #9983CC39
58// (decimal a = 153, r = 131, g = 204, b = 57)
59// for all four highlighted text values. Blending this with white yields:
60// R = (131 * 153 + 255 * (255 - 153)) / 255  -> 180.6
61// G = (204 * 153 + 255 * (255 - 153)) / 255  -> 224.4
62// B = ( 57 * 153 + 255 * (255 - 153)) / 255  -> 136.2
63
64const RGBA32 selectionColor = makeRGB(181, 224, 136);
65
66static SkCanvas* getCanvasFromInfo(const PaintInfo& info)
67{
68    return info.context->platformContext()->mCanvas;
69}
70
71static android::WebFrame* getWebFrame(const Node* node)
72{
73    if (!node)
74        return 0;
75    return android::WebFrame::getWebFrame(node->document()->frame());
76}
77
78RenderTheme* theme()
79{
80    DEFINE_STATIC_LOCAL(RenderThemeAndroid, androidTheme, ());
81    return &androidTheme;
82}
83
84PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
85{
86    static RenderTheme* rt = RenderThemeAndroid::create().releaseRef();
87    return rt;
88}
89
90PassRefPtr<RenderTheme> RenderThemeAndroid::create()
91{
92    return adoptRef(new RenderThemeAndroid());
93}
94
95RenderThemeAndroid::RenderThemeAndroid()
96{
97}
98
99RenderThemeAndroid::~RenderThemeAndroid()
100{
101}
102
103void RenderThemeAndroid::close()
104{
105}
106
107bool RenderThemeAndroid::stateChanged(RenderObject* obj, ControlState state) const
108{
109    if (CheckedState == state) {
110        obj->repaint();
111        return true;
112    }
113    return false;
114}
115
116Color RenderThemeAndroid::platformActiveSelectionBackgroundColor() const
117{
118    return Color(selectionColor);
119}
120
121Color RenderThemeAndroid::platformInactiveSelectionBackgroundColor() const
122{
123    return Color(Color::transparent);
124}
125
126Color RenderThemeAndroid::platformActiveSelectionForegroundColor() const
127{
128    return Color::black;
129}
130
131Color RenderThemeAndroid::platformInactiveSelectionForegroundColor() const
132{
133    return Color::black;
134}
135
136Color RenderThemeAndroid::platformTextSearchHighlightColor() const
137{
138    return Color(Color::transparent);
139}
140
141Color RenderThemeAndroid::platformActiveListBoxSelectionBackgroundColor() const
142{
143    return Color(Color::transparent);
144}
145
146Color RenderThemeAndroid::platformInactiveListBoxSelectionBackgroundColor() const
147{
148    return Color(Color::transparent);
149}
150
151Color RenderThemeAndroid::platformActiveListBoxSelectionForegroundColor() const
152{
153    return Color(Color::transparent);
154}
155
156Color RenderThemeAndroid::platformInactiveListBoxSelectionForegroundColor() const
157{
158    return Color(Color::transparent);
159}
160
161int RenderThemeAndroid::baselinePosition(const RenderObject* obj) const
162{
163    // From the description of this function in RenderTheme.h:
164    // A method to obtain the baseline position for a "leaf" control.  This will only be used if a baseline
165    // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of
166    // controls that need to do this.
167    //
168    // Our checkboxes and radio buttons need to be offset to line up properly.
169    return RenderTheme::baselinePosition(obj) - 2;
170}
171
172void RenderThemeAndroid::addIntrinsicMargins(RenderStyle* style) const
173{
174    // Cut out the intrinsic margins completely if we end up using a small font size
175    if (style->fontSize() < 11)
176        return;
177
178    // Intrinsic margin value.
179    const int m = 2;
180
181    // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
182    if (style->width().isIntrinsicOrAuto()) {
183        if (style->marginLeft().quirk())
184            style->setMarginLeft(Length(m, Fixed));
185        if (style->marginRight().quirk())
186            style->setMarginRight(Length(m, Fixed));
187    }
188
189    if (style->height().isAuto()) {
190        if (style->marginTop().quirk())
191            style->setMarginTop(Length(m, Fixed));
192        if (style->marginBottom().quirk())
193            style->setMarginBottom(Length(m, Fixed));
194    }
195}
196
197bool RenderThemeAndroid::supportsFocus(ControlPart appearance)
198{
199    switch (appearance) {
200    case PushButtonPart:
201    case ButtonPart:
202    case TextFieldPart:
203        return true;
204    default:
205        return false;
206    }
207
208    return false;
209}
210
211void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
212{
213    // Code is taken from RenderThemeSafari.cpp
214    // It makes sure we have enough space for the button text.
215    const int padding = 8;
216    style->setPaddingLeft(Length(padding, Fixed));
217    style->setPaddingRight(Length(padding, Fixed));
218
219    // Set a min-height so that we can't get smaller than the mini button.
220    style->setMinHeight(Length(15, Fixed));
221}
222
223bool RenderThemeAndroid::paintCheckbox(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
224{
225    RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, true);
226    return false;
227}
228
229bool RenderThemeAndroid::paintButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
230{
231    // If it is a disabled button, simply paint it to the master picture.
232    Node* node = obj->node();
233    Element* formControlElement = static_cast<Element*>(node);
234    if (formControlElement && !formControlElement->isEnabledFormControl()) {
235        android::WebFrame* webFrame = getWebFrame(node);
236        if (webFrame) {
237            RenderSkinAndroid* skins = webFrame->renderSkins();
238            if (skins)
239                skins->renderSkinButton()->draw(getCanvasFromInfo(info), rect,
240                                                RenderSkinAndroid::kDisabled);
241        }
242    } else
243        // Store all the important information in the platform context.
244        info.context->platformContext()->storeButtonInfo(node, rect);
245
246    // We always return false so we do not request to be redrawn.
247    return false;
248}
249
250#if ENABLE(VIDEO)
251
252String RenderThemeAndroid::extraMediaControlsStyleSheet()
253{
254      return String(mediaControlsAndroidUserAgentStyleSheet, sizeof(mediaControlsAndroidUserAgentStyleSheet));
255}
256
257bool RenderThemeAndroid::shouldRenderMediaControlPart(ControlPart part, Element* e)
258{
259      HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e);
260      switch (part) {
261      case MediaMuteButtonPart:
262          return false;
263      case MediaSeekBackButtonPart:
264      case MediaSeekForwardButtonPart:
265          return false;
266      case MediaRewindButtonPart:
267          return mediaElement->movieLoadType() != MediaPlayer::LiveStream;
268      case MediaReturnToRealtimeButtonPart:
269          return mediaElement->movieLoadType() == MediaPlayer::LiveStream;
270      case MediaFullscreenButtonPart:
271          return mediaElement->supportsFullscreen();
272      case MediaToggleClosedCaptionsButtonPart:
273          return mediaElement->hasClosedCaptions();
274      default:
275          return true;
276      }
277}
278
279bool RenderThemeAndroid::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
280{
281      bool translucent = false;
282      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
283          translucent = true;
284      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FULLSCREEN, translucent);
285      return false;
286}
287
288bool RenderThemeAndroid::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
289{
290      bool translucent = false;
291      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
292          translucent = true;
293      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::MUTE, translucent);
294      return false;
295}
296
297bool RenderThemeAndroid::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
298{
299      bool translucent = false;
300      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
301          translucent = true;
302      if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o->node())) {
303          if (btn->displayType() == MediaPlayButton)
304              RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PLAY, translucent);
305          else
306              RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PAUSE, translucent);
307          return false;
308      }
309      return true;
310}
311
312bool RenderThemeAndroid::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
313{
314      bool translucent = false;
315      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
316          translucent = true;
317      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::REWIND, translucent);
318      return false;
319}
320
321bool RenderThemeAndroid::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
322{
323      bool translucent = false;
324      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
325          translucent = true;
326      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FORWARD, translucent);
327      return false;
328}
329
330bool RenderThemeAndroid::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
331{
332      bool translucent = false;
333      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
334          translucent = true;
335      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::BACKGROUND_SLIDER, translucent);
336      return false;
337}
338
339bool RenderThemeAndroid::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
340{
341      bool translucent = false;
342      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
343          translucent = true;
344      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect,
345                                  RenderSkinMediaButton::SLIDER_TRACK, translucent, o);
346      return false;
347}
348
349bool RenderThemeAndroid::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
350{
351      bool translucent = false;
352      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
353          translucent = true;
354      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::SLIDER_THUMB, translucent);
355      return false;
356}
357
358void RenderThemeAndroid::adjustSliderThumbSize(RenderObject* o) const
359{
360    static const int sliderThumbWidth = RenderSkinMediaButton::sliderThumbWidth();
361    static const int sliderThumbHeight = RenderSkinMediaButton::sliderThumbHeight();
362    if (o->style()->appearance() == MediaSliderThumbPart) {
363        o->style()->setWidth(Length(sliderThumbWidth, Fixed));
364        o->style()->setHeight(Length(sliderThumbHeight, Fixed));
365    }
366}
367
368#endif
369
370bool RenderThemeAndroid::paintRadio(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
371{
372    RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, false);
373    return false;
374}
375
376void RenderThemeAndroid::setCheckboxSize(RenderStyle* style) const
377{
378    style->setWidth(Length(19, Fixed));
379    style->setHeight(Length(19, Fixed));
380}
381
382void RenderThemeAndroid::setRadioSize(RenderStyle* style) const
383{
384    // This is the same as checkboxes.
385    setCheckboxSize(style);
386}
387
388void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
389{
390    addIntrinsicMargins(style);
391}
392
393bool RenderThemeAndroid::paintTextField(RenderObject*, const PaintInfo&, const IntRect&)
394{
395    return true;
396}
397
398void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
399{
400    addIntrinsicMargins(style);
401}
402
403bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
404{
405    if (obj->isMenuList())
406        paintCombo(obj, info, rect);
407    return true;
408}
409
410void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
411{
412    addIntrinsicMargins(style);
413}
414
415bool RenderThemeAndroid::paintSearchField(RenderObject*, const PaintInfo&, const IntRect&)
416{
417    return true;
418}
419
420static void adjustMenuListStyleCommon(RenderStyle* style)
421{
422    // Added to make room for our arrow and make the touch target less cramped.
423    style->setPaddingLeft(Length(RenderSkinCombo::padding(), Fixed));
424    style->setPaddingTop(Length(RenderSkinCombo::padding(), Fixed));
425    style->setPaddingBottom(Length(RenderSkinCombo::padding(), Fixed));
426    style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed));
427}
428
429void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
430{
431    adjustMenuListButtonStyle(0, style, 0);
432}
433
434void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const
435{
436    adjustMenuListStyleCommon(style);
437    addIntrinsicMargins(style);
438}
439
440bool RenderThemeAndroid::paintCombo(RenderObject* obj, const PaintInfo& info,  const IntRect& rect)
441{
442  if (obj->style() && !obj->style()->visitedDependentColor(CSSPropertyBackgroundColor).alpha())
443        return true;
444    return RenderSkinCombo::Draw(getCanvasFromInfo(info), obj->node(), rect.x(), rect.y(), rect.width(), rect.height());
445}
446
447bool RenderThemeAndroid::paintMenuList(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
448{
449    return paintCombo(obj, info, rect);
450}
451
452void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*,
453        RenderStyle* style, Element*) const
454{
455    // Copied from RenderThemeSafari.
456    const float baseFontSize = 11.0f;
457    const int baseBorderRadius = 5;
458    float fontScale = style->fontSize() / baseFontSize;
459
460    style->resetPadding();
461    style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
462
463    const int minHeight = 15;
464    style->setMinHeight(Length(minHeight, Fixed));
465
466    style->setLineHeight(RenderStyle::initialLineHeight());
467    // Found these padding numbers by trial and error.
468    const int padding = 4;
469    style->setPaddingTop(Length(padding, Fixed));
470    style->setPaddingLeft(Length(padding, Fixed));
471    adjustMenuListStyleCommon(style);
472}
473
474bool RenderThemeAndroid::paintMenuListButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
475{
476    return paintCombo(obj, info, rect);
477}
478
479bool RenderThemeAndroid::supportsFocusRing(const RenderStyle* style) const
480{
481    return style->opacity() > 0
482        && style->hasAppearance()
483        && style->appearance() != TextFieldPart
484        && style->appearance() != SearchFieldPart
485        && style->appearance() != TextAreaPart
486        && style->appearance() != CheckboxPart
487        && style->appearance() != RadioPart
488        && style->appearance() != PushButtonPart
489        && style->appearance() != SquareButtonPart
490        && style->appearance() != ButtonPart
491        && style->appearance() != ButtonBevelPart
492        && style->appearance() != MenulistPart
493        && style->appearance() != MenulistButtonPart;
494}
495
496} // namespace WebCore
497