• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
48 namespace WebCore {
49 
50 // Add a constant amount of padding to the textsize to get the final height
51 // of buttons, so that our button images are large enough to properly fit
52 // the text.
53 const int buttonPadding = 18;
54 
55 // Add padding to the fontSize of ListBoxes to get their maximum sizes.
56 // Listboxes often have a specified size.  Since we change them into
57 // dropdowns, we want a much smaller height, which encompasses the text.
58 const int listboxPadding = 5;
59 
60 // This is the color of selection in a textfield.  It was obtained by checking
61 // the color of selection in TextViews in the system.
62 const RGBA32 selectionColor = makeRGB(255, 146, 0);
63 
getCanvasFromInfo(const RenderObject::PaintInfo & info)64 static SkCanvas* getCanvasFromInfo(const RenderObject::PaintInfo& info)
65 {
66     return info.context->platformContext()->mCanvas;
67 }
68 
theme()69 RenderTheme* theme()
70 {
71     DEFINE_STATIC_LOCAL(RenderThemeAndroid, androidTheme, ());
72     return &androidTheme;
73 }
74 
themeForPage(Page * page)75 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
76 {
77     static RenderTheme* rt = RenderThemeAndroid::create().releaseRef();
78     return rt;
79 }
80 
create()81 PassRefPtr<RenderTheme> RenderThemeAndroid::create()
82 {
83     return adoptRef(new RenderThemeAndroid());
84 }
85 
RenderThemeAndroid()86 RenderThemeAndroid::RenderThemeAndroid()
87 {
88 }
89 
~RenderThemeAndroid()90 RenderThemeAndroid::~RenderThemeAndroid()
91 {
92 }
93 
close()94 void RenderThemeAndroid::close()
95 {
96 }
97 
stateChanged(RenderObject * obj,ControlState state) const98 bool RenderThemeAndroid::stateChanged(RenderObject* obj, ControlState state) const
99 {
100     if (CheckedState == state) {
101         obj->repaint();
102         return true;
103     }
104     return false;
105 }
106 
platformActiveSelectionBackgroundColor() const107 Color RenderThemeAndroid::platformActiveSelectionBackgroundColor() const
108 {
109     return Color(selectionColor);
110 }
111 
platformInactiveSelectionBackgroundColor() const112 Color RenderThemeAndroid::platformInactiveSelectionBackgroundColor() const
113 {
114     return Color(Color::transparent);
115 }
116 
platformActiveSelectionForegroundColor() const117 Color RenderThemeAndroid::platformActiveSelectionForegroundColor() const
118 {
119     return Color::black;
120 }
121 
platformInactiveSelectionForegroundColor() const122 Color RenderThemeAndroid::platformInactiveSelectionForegroundColor() const
123 {
124     return Color::black;
125 }
126 
platformTextSearchHighlightColor() const127 Color RenderThemeAndroid::platformTextSearchHighlightColor() const
128 {
129     return Color(Color::transparent);
130 }
131 
platformActiveListBoxSelectionBackgroundColor() const132 Color RenderThemeAndroid::platformActiveListBoxSelectionBackgroundColor() const
133 {
134     return Color(Color::transparent);
135 }
136 
platformInactiveListBoxSelectionBackgroundColor() const137 Color RenderThemeAndroid::platformInactiveListBoxSelectionBackgroundColor() const
138 {
139     return Color(Color::transparent);
140 }
141 
platformActiveListBoxSelectionForegroundColor() const142 Color RenderThemeAndroid::platformActiveListBoxSelectionForegroundColor() const
143 {
144     return Color(Color::transparent);
145 }
146 
platformInactiveListBoxSelectionForegroundColor() const147 Color RenderThemeAndroid::platformInactiveListBoxSelectionForegroundColor() const
148 {
149     return Color(Color::transparent);
150 }
151 
baselinePosition(const RenderObject * obj) const152 int RenderThemeAndroid::baselinePosition(const RenderObject* obj) const
153 {
154     // From the description of this function in RenderTheme.h:
155     // A method to obtain the baseline position for a "leaf" control.  This will only be used if a baseline
156     // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of
157     // controls that need to do this.
158     //
159     // Our checkboxes and radio buttons need to be offset to line up properly.
160     return RenderTheme::baselinePosition(obj) - 2;
161 }
162 
addIntrinsicMargins(RenderStyle * style) const163 void RenderThemeAndroid::addIntrinsicMargins(RenderStyle* style) const
164 {
165     // Cut out the intrinsic margins completely if we end up using a small font size
166     if (style->fontSize() < 11)
167         return;
168 
169     // Intrinsic margin value.
170     const int m = 2;
171 
172     // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
173     if (style->width().isIntrinsicOrAuto()) {
174         if (style->marginLeft().quirk())
175             style->setMarginLeft(Length(m, Fixed));
176         if (style->marginRight().quirk())
177             style->setMarginRight(Length(m, Fixed));
178     }
179 
180     if (style->height().isAuto()) {
181         if (style->marginTop().quirk())
182             style->setMarginTop(Length(m, Fixed));
183         if (style->marginBottom().quirk())
184             style->setMarginBottom(Length(m, Fixed));
185     }
186 }
187 
supportsFocus(ControlPart appearance)188 bool RenderThemeAndroid::supportsFocus(ControlPart appearance)
189 {
190     switch (appearance) {
191     case PushButtonPart:
192     case ButtonPart:
193     case TextFieldPart:
194         return true;
195     default:
196         return false;
197     }
198 
199     return false;
200 }
201 
adjustButtonStyle(CSSStyleSelector *,RenderStyle * style,WebCore::Element *) const202 void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
203 {
204     // Padding code is taken from RenderThemeSafari.cpp
205     // It makes sure we have enough space for the button text.
206     const int padding = 8;
207     style->setPaddingLeft(Length(padding, Fixed));
208     style->setPaddingRight(Length(padding, Fixed));
209     style->setMinHeight(Length(style->fontSize() + buttonPadding, Fixed));
210 }
211 
paintCheckbox(RenderObject * obj,const RenderObject::PaintInfo & info,const IntRect & rect)212 bool RenderThemeAndroid::paintCheckbox(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect)
213 {
214     RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, true);
215     return false;
216 }
217 
paintButton(RenderObject * obj,const RenderObject::PaintInfo & info,const IntRect & rect)218 bool RenderThemeAndroid::paintButton(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect)
219 {
220     // If it is a disabled button, simply paint it to the master picture.
221     Node* node = obj->node();
222     Element* formControlElement = static_cast<Element*>(node);
223     if (formControlElement && !formControlElement->isEnabledFormControl())
224         RenderSkinButton::Draw(getCanvasFromInfo(info), rect, RenderSkinAndroid::kDisabled);
225     else
226         // Store all the important information in the platform context.
227         info.context->platformContext()->storeButtonInfo(node, rect);
228 
229     // We always return false so we do not request to be redrawn.
230     return false;
231 }
232 
233 #if ENABLE(VIDEO)
234 
extraMediaControlsStyleSheet()235 String RenderThemeAndroid::extraMediaControlsStyleSheet()
236 {
237       return String(mediaControlsAndroidUserAgentStyleSheet, sizeof(mediaControlsAndroidUserAgentStyleSheet));
238 }
239 
shouldRenderMediaControlPart(ControlPart part,Element * e)240 bool RenderThemeAndroid::shouldRenderMediaControlPart(ControlPart part, Element* e)
241 {
242       HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e);
243       switch (part) {
244       case MediaMuteButtonPart:
245           return false;
246       case MediaSeekBackButtonPart:
247       case MediaSeekForwardButtonPart:
248           return true;
249       case MediaRewindButtonPart:
250           return mediaElement->movieLoadType() != MediaPlayer::LiveStream;
251       case MediaReturnToRealtimeButtonPart:
252           return mediaElement->movieLoadType() == MediaPlayer::LiveStream;
253       case MediaFullscreenButtonPart:
254           return mediaElement->supportsFullscreen();
255       case MediaToggleClosedCaptionsButtonPart:
256           return mediaElement->hasClosedCaptions();
257       default:
258           return true;
259       }
260 }
261 
paintMediaMuteButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & rect)262 bool RenderThemeAndroid::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
263 {
264       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::MUTE);
265       return false;
266 }
267 
paintMediaPlayButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & rect)268 bool RenderThemeAndroid::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
269 {
270       if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o->node())) {
271           if (btn->displayType() == MediaPlayButton)
272               RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PLAY);
273           else
274               RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PAUSE);
275           return false;
276       }
277       return true;
278 }
279 
paintMediaSeekBackButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & rect)280 bool RenderThemeAndroid::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
281 {
282       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::REWIND);
283       return false;
284 }
285 
paintMediaSeekForwardButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & rect)286 bool RenderThemeAndroid::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
287 {
288       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FORWARD);
289       return false;
290 }
291 
paintMediaControlsBackground(RenderObject * object,const RenderObject::PaintInfo & paintInfo,const IntRect & rect)292 bool RenderThemeAndroid::paintMediaControlsBackground(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
293 {
294       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::BACKGROUND_SLIDER);
295       return false;
296 }
297 
paintMediaSliderTrack(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & rect)298 bool RenderThemeAndroid::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
299 {
300       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::SLIDER_TRACK);
301       return false;
302 }
303 
paintMediaSliderThumb(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & rect)304 bool RenderThemeAndroid::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
305 {
306       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::SLIDER_THUMB);
307       return false;
308 }
309 
adjustSliderThumbSize(RenderObject * o) const310 void RenderThemeAndroid::adjustSliderThumbSize(RenderObject* o) const
311 {
312     static const int sliderThumbWidth = RenderSkinMediaButton::sliderThumbWidth();
313     static const int sliderThumbHeight = RenderSkinMediaButton::sliderThumbHeight();
314     if (o->style()->appearance() == MediaSliderThumbPart) {
315         o->style()->setWidth(Length(sliderThumbWidth, Fixed));
316         o->style()->setHeight(Length(sliderThumbHeight, Fixed));
317     }
318 }
319 
320 #endif
321 
paintRadio(RenderObject * obj,const RenderObject::PaintInfo & info,const IntRect & rect)322 bool RenderThemeAndroid::paintRadio(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect)
323 {
324     RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, false);
325     return false;
326 }
327 
setCheckboxSize(RenderStyle * style) const328 void RenderThemeAndroid::setCheckboxSize(RenderStyle* style) const
329 {
330     style->setWidth(Length(19, Fixed));
331     style->setHeight(Length(19, Fixed));
332 }
333 
setRadioSize(RenderStyle * style) const334 void RenderThemeAndroid::setRadioSize(RenderStyle* style) const
335 {
336     // This is the same as checkboxes.
337     setCheckboxSize(style);
338 }
339 
adjustTextFieldStyle(CSSStyleSelector *,RenderStyle * style,WebCore::Element *) const340 void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
341 {
342     addIntrinsicMargins(style);
343 }
344 
paintTextField(RenderObject *,const RenderObject::PaintInfo &,const IntRect &)345 bool RenderThemeAndroid::paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&)
346 {
347     return true;
348 }
349 
adjustTextAreaStyle(CSSStyleSelector *,RenderStyle * style,WebCore::Element *) const350 void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
351 {
352     addIntrinsicMargins(style);
353 }
354 
paintTextArea(RenderObject * obj,const RenderObject::PaintInfo & info,const IntRect & rect)355 bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect)
356 {
357     if (!obj->isListBox())
358         return true;
359 
360     paintCombo(obj, info, rect);
361     RenderStyle* style = obj->style();
362     if (style)
363         style->setColor(Color::transparent);
364     Node* node = obj->node();
365     if (!node || !node->hasTagName(HTMLNames::selectTag))
366         return true;
367 
368     HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node);
369     // The first item may be visible.  Make sure it does not draw.
370     // If it has a style, it overrides the RenderListBox's style, so we
371     // need to make sure both are set to transparent.
372     node = select->item(0);
373     if (node) {
374         RenderObject* renderer = node->renderer();
375         if (renderer) {
376             RenderStyle* renderStyle = renderer->style();
377             if (renderStyle)
378                 renderStyle->setColor(Color::transparent);
379         }
380     }
381     // Find the first selected option, and draw its text.
382     // FIXME: In a later change, if there is more than one item selected,
383     // draw a string that says "X items" like iPhone Safari does
384     int index = select->selectedIndex();
385     node = select->item(index);
386     if (!node || !node->hasTagName(HTMLNames::optionTag))
387         return true;
388 
389     HTMLOptionElement* option = static_cast<HTMLOptionElement*>(node);
390     String label = option->textIndentedToRespectGroupLabel();
391     SkRect r(rect);
392 
393     SkPaint paint;
394     paint.setAntiAlias(true);
395     paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
396     // Values for text size and positioning determined by trial and error
397     paint.setTextSize(r.height() - SkIntToScalar(6));
398 
399     SkCanvas* canvas = getCanvasFromInfo(info);
400     int saveCount = canvas->save();
401     r.fRight -= SkIntToScalar(RenderSkinCombo::extraWidth());
402     canvas->clipRect(r);
403     canvas->drawText(label.characters(), label.length() << 1,
404              r.fLeft + SkIntToScalar(5), r.fBottom - SkIntToScalar(5), paint);
405     canvas->restoreToCount(saveCount);
406 
407     return true;
408 }
409 
adjustSearchFieldStyle(CSSStyleSelector *,RenderStyle * style,Element *) const410 void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
411 {
412     addIntrinsicMargins(style);
413 }
414 
paintSearchField(RenderObject *,const RenderObject::PaintInfo &,const IntRect &)415 bool RenderThemeAndroid::paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&)
416 {
417     return true;
418 }
419 
adjustListboxStyle(CSSStyleSelector *,RenderStyle * style,Element *) const420 void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
421 {
422     style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed));
423     style->setMaxHeight(Length(style->fontSize() + listboxPadding, Fixed));
424     // Make webkit draw invisible, since it will simply draw the first element
425     style->setColor(Color::transparent);
426     addIntrinsicMargins(style);
427 }
428 
adjustMenuListStyleCommon(RenderStyle * style,Element * e)429 static void adjustMenuListStyleCommon(RenderStyle* style, Element* e)
430 {
431     // Added to make room for our arrow and make the touch target less cramped.
432     style->setPaddingLeft(Length(RenderSkinCombo::padding(), Fixed));
433     style->setPaddingTop(Length(RenderSkinCombo::padding(), Fixed));
434     style->setPaddingBottom(Length(RenderSkinCombo::padding(), Fixed));
435     style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed));
436 }
437 
adjustMenuListStyle(CSSStyleSelector *,RenderStyle * style,Element * e) const438 void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const
439 {
440     adjustMenuListStyleCommon(style, e);
441     addIntrinsicMargins(style);
442 }
443 
paintCombo(RenderObject * obj,const RenderObject::PaintInfo & info,const IntRect & rect)444 bool RenderThemeAndroid::paintCombo(RenderObject* obj, const RenderObject::PaintInfo& info,  const IntRect& rect)
445 {
446     if (obj->style() && !obj->style()->backgroundColor().alpha())
447         return true;
448     return RenderSkinCombo::Draw(getCanvasFromInfo(info), obj->node(), rect.x(), rect.y(), rect.width(), rect.height());
449 }
450 
paintMenuList(RenderObject * obj,const RenderObject::PaintInfo & info,const IntRect & rect)451 bool RenderThemeAndroid::paintMenuList(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect)
452 {
453     return paintCombo(obj, info, rect);
454 }
455 
adjustMenuListButtonStyle(CSSStyleSelector *,RenderStyle * style,Element * e) const456 void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const
457 {
458     // Copied from RenderThemeSafari.
459     const float baseFontSize = 11.0f;
460     const int baseBorderRadius = 5;
461     float fontScale = style->fontSize() / baseFontSize;
462 
463     style->resetPadding();
464     style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
465 
466     const int minHeight = 15;
467     style->setMinHeight(Length(minHeight, Fixed));
468 
469     style->setLineHeight(RenderStyle::initialLineHeight());
470     // Found these padding numbers by trial and error.
471     const int padding = 4;
472     style->setPaddingTop(Length(padding, Fixed));
473     style->setPaddingLeft(Length(padding, Fixed));
474     adjustMenuListStyleCommon(style, e);
475 }
476 
paintMenuListButton(RenderObject * obj,const RenderObject::PaintInfo & info,const IntRect & rect)477 bool RenderThemeAndroid::paintMenuListButton(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect)
478 {
479     return paintCombo(obj, info, rect);
480 }
481 
supportsFocusRing(const RenderStyle * style) const482 bool RenderThemeAndroid::supportsFocusRing(const RenderStyle* style) const
483 {
484     return style->opacity() > 0
485         && style->hasAppearance()
486         && style->appearance() != TextFieldPart
487         && style->appearance() != SearchFieldPart
488         && style->appearance() != TextAreaPart
489         && style->appearance() != CheckboxPart
490         && style->appearance() != RadioPart
491         && style->appearance() != PushButtonPart
492         && style->appearance() != SquareButtonPart
493         && style->appearance() != ButtonPart
494         && style->appearance() != ButtonBevelPart
495         && style->appearance() != MenulistPart
496         && style->appearance() != MenulistButtonPart;
497 }
498 
499 } // namespace WebCore
500