RenderThemeAndroid.cpp revision f404d29266675808fffe9d71b065bc03b555b58d
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 "RenderSkinMediaButton.h"
42#include "RoundedIntRect.h"
43#include "SkCanvas.h"
44#include "UserAgentStyleSheets.h"
45#include "WebCoreFrameBridge.h"
46
47namespace WebCore {
48
49// Add padding to the fontSize of ListBoxes to get their maximum sizes.
50// Listboxes often have a specified size.  Since we change them into
51// dropdowns, we want a much smaller height, which encompasses the text.
52const int listboxPadding = 5;
53
54// This is the color of selection in a textfield.  It was computed from
55// frameworks/base/core/res/res/values/colors.xml, which uses #9983CC39
56// (decimal a = 153, r = 131, g = 204, b = 57)
57// for all four highlighted text values. Blending this with white yields:
58// R = (131 * 153 + 255 * (255 - 153)) / 255  -> 180.6
59// G = (204 * 153 + 255 * (255 - 153)) / 255  -> 224.4
60// B = ( 57 * 153 + 255 * (255 - 153)) / 255  -> 136.2
61
62const RGBA32 selectionColor = makeRGB(181, 224, 136);
63
64// Colors copied from the holo resources
65const RGBA32 defaultBgColor = makeRGBA(204, 204, 204, 197);
66const RGBA32 defaultBgBright = makeRGBA(213, 213, 213, 221);
67const RGBA32 defaultBgDark = makeRGBA(92, 92, 92, 160);
68const RGBA32 defaultBgMedium = makeRGBA(132, 132, 132, 111);
69const RGBA32 defaultFgColor = makeRGBA(101, 101, 101, 225);
70const RGBA32 defaultCheckColor = makeRGBA(154, 204, 2, 255);
71
72const RGBA32 disabledBgColor = makeRGBA(205, 205, 205, 107);
73const RGBA32 disabledBgBright = makeRGBA(213, 213, 213, 133);
74const RGBA32 disabledBgDark = makeRGBA(92, 92, 92, 96);
75const RGBA32 disabledBgMedium = makeRGBA(132, 132, 132, 111);
76const RGBA32 disabledFgColor = makeRGBA(148, 148, 148, 137);
77
78const int paddingButton = 2;
79const int cornerButton = 2;
80
81// scale factors for various resolutions
82const float scaleFactor[RenderSkinAndroid::ResolutionCount] = {
83    1.0f, // medium res
84    1.5f, // high res
85    2.0f  // extra high res
86};
87
88
89static SkCanvas* getCanvasFromInfo(const PaintInfo& info)
90{
91    return info.context->platformContext()->mCanvas;
92}
93
94static android::WebFrame* getWebFrame(const Node* node)
95{
96    if (!node)
97        return 0;
98    return android::WebFrame::getWebFrame(node->document()->frame());
99}
100
101RenderTheme* theme()
102{
103    DEFINE_STATIC_LOCAL(RenderThemeAndroid, androidTheme, ());
104    return &androidTheme;
105}
106
107PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
108{
109    static RenderTheme* rt = RenderThemeAndroid::create().releaseRef();
110    return rt;
111}
112
113PassRefPtr<RenderTheme> RenderThemeAndroid::create()
114{
115    return adoptRef(new RenderThemeAndroid());
116}
117
118RenderThemeAndroid::RenderThemeAndroid()
119{
120}
121
122RenderThemeAndroid::~RenderThemeAndroid()
123{
124}
125
126void RenderThemeAndroid::close()
127{
128}
129
130bool RenderThemeAndroid::stateChanged(RenderObject* obj, ControlState state) const
131{
132    if (CheckedState == state) {
133        obj->repaint();
134        return true;
135    }
136    return false;
137}
138
139Color RenderThemeAndroid::platformActiveSelectionBackgroundColor() const
140{
141    return Color(selectionColor);
142}
143
144Color RenderThemeAndroid::platformInactiveSelectionBackgroundColor() const
145{
146    return Color(Color::transparent);
147}
148
149Color RenderThemeAndroid::platformActiveSelectionForegroundColor() const
150{
151    return Color::black;
152}
153
154Color RenderThemeAndroid::platformInactiveSelectionForegroundColor() const
155{
156    return Color::black;
157}
158
159Color RenderThemeAndroid::platformTextSearchHighlightColor() const
160{
161    return Color(Color::transparent);
162}
163
164Color RenderThemeAndroid::platformActiveListBoxSelectionBackgroundColor() const
165{
166    return Color(Color::transparent);
167}
168
169Color RenderThemeAndroid::platformInactiveListBoxSelectionBackgroundColor() const
170{
171    return Color(Color::transparent);
172}
173
174Color RenderThemeAndroid::platformActiveListBoxSelectionForegroundColor() const
175{
176    return Color(Color::transparent);
177}
178
179Color RenderThemeAndroid::platformInactiveListBoxSelectionForegroundColor() const
180{
181    return Color(Color::transparent);
182}
183
184int RenderThemeAndroid::baselinePosition(const RenderObject* obj) const
185{
186    // From the description of this function in RenderTheme.h:
187    // A method to obtain the baseline position for a "leaf" control.  This will only be used if a baseline
188    // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of
189    // controls that need to do this.
190    //
191    // Our checkboxes and radio buttons need to be offset to line up properly.
192    return RenderTheme::baselinePosition(obj) - 8;
193}
194
195void RenderThemeAndroid::addIntrinsicMargins(RenderStyle* style) const
196{
197    // Cut out the intrinsic margins completely if we end up using a small font size
198    if (style->fontSize() < 11)
199        return;
200
201    // Intrinsic margin value.
202    const int m = 2;
203
204    // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
205    if (style->width().isIntrinsicOrAuto()) {
206        if (style->marginLeft().quirk())
207            style->setMarginLeft(Length(m, Fixed));
208        if (style->marginRight().quirk())
209            style->setMarginRight(Length(m, Fixed));
210    }
211
212    if (style->height().isAuto()) {
213        if (style->marginTop().quirk())
214            style->setMarginTop(Length(m, Fixed));
215        if (style->marginBottom().quirk())
216            style->setMarginBottom(Length(m, Fixed));
217    }
218}
219
220bool RenderThemeAndroid::supportsFocus(ControlPart appearance)
221{
222    switch (appearance) {
223    case PushButtonPart:
224    case ButtonPart:
225    case TextFieldPart:
226        return true;
227    default:
228        return false;
229    }
230
231    return false;
232}
233
234void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
235{
236}
237
238bool RenderThemeAndroid::paintCheckbox(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
239{
240    paintRadio(obj, info, rect);
241    return false;
242}
243
244bool RenderThemeAndroid::paintButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
245{
246    // If it is a disabled button, simply paint it to the master picture.
247    Node* node = obj->node();
248    Element* formControlElement = static_cast<Element*>(node);
249    if (formControlElement) {
250        android::WebFrame* webFrame = getWebFrame(node);
251        if (webFrame) {
252            GraphicsContext *context = info.context;
253            IntRect innerrect = IntRect(rect.x() + paddingButton, rect.y() + paddingButton,
254                    rect.width() - 2 * paddingButton, rect.height() - 2 * paddingButton);
255            IntSize cornerrect = IntSize(cornerButton, cornerButton);
256            Color bg, bright, dark, medium;
257            if (formControlElement->isEnabledFormControl()) {
258                bg = Color(defaultBgColor);
259                bright = Color(defaultBgBright);
260                dark = Color(defaultBgDark);
261                medium = Color(defaultBgMedium);
262            } else {
263                bg = Color(disabledBgColor);
264                bright = Color(disabledBgBright);
265                dark = Color(disabledBgDark);
266                medium = Color(disabledBgMedium);
267            }
268            context->save();
269            context->clip(
270                    IntRect(innerrect.x(), innerrect.y(), innerrect.width(), 1));
271            context->fillRoundedRect(innerrect, cornerrect, cornerrect,
272                    cornerrect, cornerrect, bright, context->fillColorSpace());
273            context->restore();
274            context->save();
275            context->clip(IntRect(innerrect.x(), innerrect.y() + innerrect.height() - 1,
276                    innerrect.width(), 1));
277            context->fillRoundedRect(innerrect, cornerrect, cornerrect,
278                    cornerrect, cornerrect, dark, context->fillColorSpace());
279            context->restore();
280            context->save();
281            context->clip(IntRect(innerrect.x(), innerrect.y() + 1, innerrect.width(),
282                    innerrect.height() - 2));
283            context->fillRoundedRect(innerrect, cornerrect, cornerrect,
284                    cornerrect, cornerrect, bg, context->fillColorSpace());
285            context->restore();
286            context->setStrokeColor(medium, context->strokeColorSpace());
287            context->setStrokeThickness(1.0f);
288            context->drawLine(IntPoint(innerrect.x(), innerrect.y() + cornerButton),
289                    IntPoint(innerrect.x(), innerrect.y() + innerrect.height() - cornerButton));
290            context->drawLine(IntPoint(innerrect.x() + innerrect.width(), innerrect.y() + cornerButton),
291                    IntPoint(innerrect.x() + innerrect.width(), innerrect.y() + innerrect.height() - cornerButton));
292        }
293    }
294
295
296    // We always return false so we do not request to be redrawn.
297    return false;
298}
299
300#if ENABLE(VIDEO)
301
302String RenderThemeAndroid::extraMediaControlsStyleSheet()
303{
304      return String(mediaControlsAndroidUserAgentStyleSheet, sizeof(mediaControlsAndroidUserAgentStyleSheet));
305}
306
307bool RenderThemeAndroid::shouldRenderMediaControlPart(ControlPart part, Element* e)
308{
309      HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e);
310      switch (part) {
311      case MediaMuteButtonPart:
312          return false;
313      case MediaSeekBackButtonPart:
314      case MediaSeekForwardButtonPart:
315          return false;
316      case MediaRewindButtonPart:
317          return mediaElement->movieLoadType() != MediaPlayer::LiveStream;
318      case MediaReturnToRealtimeButtonPart:
319          return mediaElement->movieLoadType() == MediaPlayer::LiveStream;
320      case MediaFullscreenButtonPart:
321          return mediaElement->supportsFullscreen();
322      case MediaToggleClosedCaptionsButtonPart:
323          return mediaElement->hasClosedCaptions();
324      default:
325          return true;
326      }
327}
328
329bool RenderThemeAndroid::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
330{
331      bool translucent = false;
332      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
333          translucent = true;
334      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FULLSCREEN, translucent);
335      return false;
336}
337
338bool RenderThemeAndroid::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
339{
340      bool translucent = false;
341      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
342          translucent = true;
343      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::MUTE, translucent);
344      return false;
345}
346
347bool RenderThemeAndroid::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
348{
349      bool translucent = false;
350      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
351          translucent = true;
352      if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o->node())) {
353          if (btn->displayType() == MediaPlayButton)
354              RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PLAY, translucent);
355          else
356              RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PAUSE, translucent);
357          return false;
358      }
359      return true;
360}
361
362bool RenderThemeAndroid::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
363{
364      bool translucent = false;
365      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
366          translucent = true;
367      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::REWIND, translucent);
368      return false;
369}
370
371bool RenderThemeAndroid::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
372{
373      bool translucent = false;
374      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
375          translucent = true;
376      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FORWARD, translucent);
377      return false;
378}
379
380bool RenderThemeAndroid::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
381{
382      bool translucent = false;
383      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
384          translucent = true;
385      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect,
386                                  RenderSkinMediaButton::BACKGROUND_SLIDER,
387                                  translucent, 0, false);
388      return false;
389}
390
391bool RenderThemeAndroid::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
392{
393      bool translucent = false;
394      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
395          translucent = true;
396      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect,
397                                  RenderSkinMediaButton::SLIDER_TRACK, translucent, o);
398      return false;
399}
400
401bool RenderThemeAndroid::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
402{
403      bool translucent = false;
404      if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
405          translucent = true;
406      RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect,
407                                  RenderSkinMediaButton::SLIDER_THUMB,
408                                  translucent, 0, false);
409      return false;
410}
411
412void RenderThemeAndroid::adjustSliderThumbSize(RenderObject* o) const
413{
414    static const int sliderThumbWidth = RenderSkinMediaButton::sliderThumbWidth();
415    static const int sliderThumbHeight = RenderSkinMediaButton::sliderThumbHeight();
416    if (o->style()->appearance() == MediaSliderThumbPart) {
417        o->style()->setWidth(Length(sliderThumbWidth, Fixed));
418        o->style()->setHeight(Length(sliderThumbHeight, Fixed));
419    }
420}
421
422#endif
423
424bool RenderThemeAndroid::paintRadio(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
425{
426    Node* node = obj->node();
427    Element* element = static_cast<Element*>(node);
428    if (element) {
429        InputElement* input = element->toInputElement();
430        GraphicsContext* context = info.context;
431        if (!element->isEnabledFormControl()) {
432            context->setAlpha(0.5f);
433        }
434        const IntRect inner = IntRect(rect.x() - 2, rect.y() - 2, rect.width() - 4, rect.height() - 4);
435        context->setFillColor(Color(defaultBgBright), context->fillColorSpace());
436        context->setStrokeColor(Color(defaultBgBright), context->strokeColorSpace());
437        context->setStrokeThickness(1.0f);
438        if (input->isCheckbox()) {
439            context->drawRect(inner);
440        } else {
441            context->drawEllipse(inner);
442        }
443        context->setStrokeColor(Color(defaultFgColor), context->strokeColorSpace());
444        if (input->isCheckbox()) {
445            context->drawRect(IntRect(inner.x() + 2, inner.y() + 2, inner.width() -4, inner.height() - 4));
446        } else {
447            context->drawEllipse(IntRect(inner.x() + 2, inner.y() + 2, inner.width() -4, inner.height() - 4));
448        }
449        if (input->isChecked()) {
450            context->setFillColor(Color(defaultCheckColor), context->fillColorSpace());
451            context->setStrokeColor(Color(defaultCheckColor), context->strokeColorSpace());
452            if (input->isCheckbox()) {
453                const float w2 = ((float) rect.width() / 2);
454                const float cx = ((float) rect.x());
455                const float cy = ((float) rect.y());
456                context->save();
457                // magic numbers due to weird scale in context
458                context->translate(cx + w2 / 2.2f, cy + w2 / 1.2f);
459                context->rotate(3.93f); // 225 degrees
460                context->drawRect(IntRect(0, 0, rect.width() / 4, 2));
461                context->rotate(1.57f); // 90 degrees
462                context->drawRect(IntRect(0, 0, rect.width() / 2, 2));
463                context->restore();
464            } else {
465                context->drawEllipse(IntRect(inner.x() + 5, inner.y() + 5, inner.width() - 10, inner.height() - 10));
466            }
467        }
468    }
469    return false;
470}
471
472void RenderThemeAndroid::setCheckboxSize(RenderStyle* style) const
473{
474    style->setWidth(Length(19, Fixed));
475    style->setHeight(Length(19, Fixed));
476}
477
478void RenderThemeAndroid::setRadioSize(RenderStyle* style) const
479{
480    // This is the same as checkboxes.
481    setCheckboxSize(style);
482}
483
484void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
485{
486    addIntrinsicMargins(style);
487}
488
489bool RenderThemeAndroid::paintTextField(RenderObject*, const PaintInfo&, const IntRect&)
490{
491    return true;
492}
493
494void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
495{
496    addIntrinsicMargins(style);
497}
498
499bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
500{
501    if (obj->isMenuList())
502        paintCombo(obj, info, rect);
503    return true;
504}
505
506void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
507{
508    addIntrinsicMargins(style);
509}
510
511bool RenderThemeAndroid::paintSearchField(RenderObject*, const PaintInfo&, const IntRect&)
512{
513    return true;
514}
515
516static void adjustMenuListStyleCommon(RenderStyle* style)
517{
518    // Added to make room for our arrow and make the touch target less cramped.
519    const int padding = (int)(scaleFactor[RenderSkinAndroid::DrawableResolution()] + 0.5f);
520    style->setPaddingLeft(Length(padding,Fixed));
521    style->setPaddingTop(Length(padding, Fixed));
522    style->setPaddingBottom(Length(padding, Fixed));
523    // allocate height as arrow size
524    int arrow = std::max(18, style->fontMetrics().height() + 2 * padding);
525    style->setPaddingRight(Length(arrow, Fixed));
526    style->setMinHeight(Length(arrow, Fixed));
527    style->setHeight(Length(arrow, Fixed));
528}
529
530void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const
531{
532    adjustMenuListButtonStyle(0, style, 0);
533}
534
535void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
536{
537    adjustMenuListStyleCommon(style);
538    addIntrinsicMargins(style);
539}
540
541bool RenderThemeAndroid::paintCombo(RenderObject* obj, const PaintInfo& info,  const IntRect& rect)
542{
543  if (obj->style() && !obj->style()->visitedDependentColor(CSSPropertyBackgroundColor).alpha())
544        return true;
545    Node* node = obj->node();
546    Element* element = static_cast<Element*>(node);
547    if (element) {
548        InputElement* input = element->toInputElement();
549        GraphicsContext* context = info.context;
550        if (!element->isEnabledFormControl()) {
551            context->setAlpha(0.5f);
552        }
553        IntRect bounds = IntRect(rect.x(), rect.y(), rect.width(), rect.height());
554        // paint bg color
555        RenderStyle* style = obj->style();
556        context->setFillColor(style->visitedDependentColor(CSSPropertyBackgroundColor),
557                context->fillColorSpace());
558        context->fillRect(FloatRect(bounds));
559        // copied form the original RenderSkinCombo:
560        // If this is an appearance where RenderTheme::paint returns true
561        // without doing anything, this means that
562        // RenderBox::PaintBoxDecorationWithSize will end up painting the
563        // border, so we shouldn't paint a border here.
564        if (style->appearance() != MenulistButtonPart &&
565                style->appearance() != ListboxPart &&
566                style->appearance() != TextFieldPart &&
567                style->appearance() != TextAreaPart) {
568            const int arrowSize = bounds.height();
569            // dropdown button bg
570            context->setFillColor(Color(defaultBgColor), context->fillColorSpace());
571            context->fillRect(FloatRect(bounds.maxX() - arrowSize + 0.5f, bounds.y() + .5f,
572                    arrowSize - 1, bounds.height() - 1));
573            // outline
574            context->setStrokeThickness(1.0f);
575            context->setStrokeColor(Color(defaultBgDark), context->strokeColorSpace());
576            context->strokeRect(bounds, 1.0f);
577            // arrow
578            context->setFillColor(Color(defaultFgColor), context->fillColorSpace());
579            Path tri = Path();
580            tri.clear();
581            const float aw = arrowSize - 10;
582            FloatPoint br = FloatPoint(bounds.maxX() - 4, bounds.maxY() - 4);
583            tri.moveTo(br);
584            tri.addLineTo(FloatPoint(br.x() - aw, br.y()));
585            tri.addLineTo(FloatPoint(br.x(), br.y() - aw));
586            context->fillPath(tri);
587        }
588    }
589    return false;
590}
591
592bool RenderThemeAndroid::paintMenuList(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
593{
594    return paintCombo(obj, info, rect);
595}
596
597void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*,
598        RenderStyle* style, Element*) const
599{
600    // Copied from RenderThemeSafari.
601    const float baseFontSize = 11.0f;
602    const int baseBorderRadius = 5;
603    float fontScale = style->fontSize() / baseFontSize;
604
605    style->resetPadding();
606    style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
607
608    const int minHeight = 15;
609    style->setMinHeight(Length(minHeight, Fixed));
610
611    style->setLineHeight(RenderStyle::initialLineHeight());
612    // Found these padding numbers by trial and error.
613    const int padding = 4;
614    style->setPaddingTop(Length(padding, Fixed));
615    style->setPaddingLeft(Length(padding, Fixed));
616    adjustMenuListStyleCommon(style);
617}
618
619bool RenderThemeAndroid::paintMenuListButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
620{
621    return paintCombo(obj, info, rect);
622}
623
624Color RenderThemeAndroid::platformFocusRingColor() const
625{
626    static Color focusRingColor(0x33, 0xB5, 0xE5, 0x66);
627    return focusRingColor;
628}
629
630bool RenderThemeAndroid::supportsFocusRing(const RenderStyle* style) const
631{
632    // Draw the focus ring ourselves unless it is a text area (webkit does borders better)
633    if (!style || !style->hasAppearance())
634        return true;
635    return style->appearance() != TextFieldPart && style->appearance() != TextAreaPart;
636}
637
638} // namespace WebCore
639