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