Home | History | Annotate | Download | only in android
      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 
     49 namespace 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.
     54 const 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 
     64 const RGBA32 selectionColor = makeRGB(181, 224, 136);
     65 
     66 static SkCanvas* getCanvasFromInfo(const PaintInfo& info)
     67 {
     68     return info.context->platformContext()->mCanvas;
     69 }
     70 
     71 static android::WebFrame* getWebFrame(const Node* node)
     72 {
     73     if (!node)
     74         return 0;
     75     return android::WebFrame::getWebFrame(node->document()->frame());
     76 }
     77 
     78 RenderTheme* theme()
     79 {
     80     DEFINE_STATIC_LOCAL(RenderThemeAndroid, androidTheme, ());
     81     return &androidTheme;
     82 }
     83 
     84 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
     85 {
     86     static RenderTheme* rt = RenderThemeAndroid::create().releaseRef();
     87     return rt;
     88 }
     89 
     90 PassRefPtr<RenderTheme> RenderThemeAndroid::create()
     91 {
     92     return adoptRef(new RenderThemeAndroid());
     93 }
     94 
     95 RenderThemeAndroid::RenderThemeAndroid()
     96 {
     97 }
     98 
     99 RenderThemeAndroid::~RenderThemeAndroid()
    100 {
    101 }
    102 
    103 void RenderThemeAndroid::close()
    104 {
    105 }
    106 
    107 bool RenderThemeAndroid::stateChanged(RenderObject* obj, ControlState state) const
    108 {
    109     if (CheckedState == state) {
    110         obj->repaint();
    111         return true;
    112     }
    113     return false;
    114 }
    115 
    116 Color RenderThemeAndroid::platformActiveSelectionBackgroundColor() const
    117 {
    118     return Color(selectionColor);
    119 }
    120 
    121 Color RenderThemeAndroid::platformInactiveSelectionBackgroundColor() const
    122 {
    123     return Color(Color::transparent);
    124 }
    125 
    126 Color RenderThemeAndroid::platformActiveSelectionForegroundColor() const
    127 {
    128     return Color::black;
    129 }
    130 
    131 Color RenderThemeAndroid::platformInactiveSelectionForegroundColor() const
    132 {
    133     return Color::black;
    134 }
    135 
    136 Color RenderThemeAndroid::platformTextSearchHighlightColor() const
    137 {
    138     return Color(Color::transparent);
    139 }
    140 
    141 Color RenderThemeAndroid::platformActiveListBoxSelectionBackgroundColor() const
    142 {
    143     return Color(Color::transparent);
    144 }
    145 
    146 Color RenderThemeAndroid::platformInactiveListBoxSelectionBackgroundColor() const
    147 {
    148     return Color(Color::transparent);
    149 }
    150 
    151 Color RenderThemeAndroid::platformActiveListBoxSelectionForegroundColor() const
    152 {
    153     return Color(Color::transparent);
    154 }
    155 
    156 Color RenderThemeAndroid::platformInactiveListBoxSelectionForegroundColor() const
    157 {
    158     return Color(Color::transparent);
    159 }
    160 
    161 int 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 
    172 void 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 
    197 bool 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 
    211 void 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 paddingHoriz = 12;
    216     const int paddingVert = 8;
    217     style->setPaddingLeft(Length(paddingHoriz, Fixed));
    218     style->setPaddingRight(Length(paddingHoriz, Fixed));
    219     style->setPaddingTop(Length(paddingVert, Fixed));
    220     style->setPaddingBottom(Length(paddingVert, Fixed));
    221 
    222     // Set a min-height so that we can't get smaller than the mini button.
    223     style->setMinHeight(Length(15, Fixed));
    224 }
    225 
    226 bool RenderThemeAndroid::paintCheckbox(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
    227 {
    228     RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, true);
    229     return false;
    230 }
    231 
    232 bool RenderThemeAndroid::paintButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
    233 {
    234     // If it is a disabled button, simply paint it to the master picture.
    235     Node* node = obj->node();
    236     Element* formControlElement = static_cast<Element*>(node);
    237     if (formControlElement) {
    238         android::WebFrame* webFrame = getWebFrame(node);
    239         if (webFrame) {
    240             RenderSkinAndroid* skins = webFrame->renderSkins();
    241             if (skins) {
    242                 RenderSkinAndroid::State state = RenderSkinAndroid::kNormal;
    243                 if (!formControlElement->isEnabledFormControl())
    244                     state = RenderSkinAndroid::kDisabled;
    245                 skins->renderSkinButton()->draw(getCanvasFromInfo(info), rect, state);
    246             }
    247         }
    248     }
    249 
    250     // We always return false so we do not request to be redrawn.
    251     return false;
    252 }
    253 
    254 #if ENABLE(VIDEO)
    255 
    256 String RenderThemeAndroid::extraMediaControlsStyleSheet()
    257 {
    258       return String(mediaControlsAndroidUserAgentStyleSheet, sizeof(mediaControlsAndroidUserAgentStyleSheet));
    259 }
    260 
    261 bool RenderThemeAndroid::shouldRenderMediaControlPart(ControlPart part, Element* e)
    262 {
    263       HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e);
    264       switch (part) {
    265       case MediaMuteButtonPart:
    266           return false;
    267       case MediaSeekBackButtonPart:
    268       case MediaSeekForwardButtonPart:
    269           return false;
    270       case MediaRewindButtonPart:
    271           return mediaElement->movieLoadType() != MediaPlayer::LiveStream;
    272       case MediaReturnToRealtimeButtonPart:
    273           return mediaElement->movieLoadType() == MediaPlayer::LiveStream;
    274       case MediaFullscreenButtonPart:
    275           return mediaElement->supportsFullscreen();
    276       case MediaToggleClosedCaptionsButtonPart:
    277           return mediaElement->hasClosedCaptions();
    278       default:
    279           return true;
    280       }
    281 }
    282 
    283 bool RenderThemeAndroid::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    284 {
    285       bool translucent = false;
    286       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    287           translucent = true;
    288       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FULLSCREEN, translucent);
    289       return false;
    290 }
    291 
    292 bool RenderThemeAndroid::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    293 {
    294       bool translucent = false;
    295       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    296           translucent = true;
    297       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::MUTE, translucent);
    298       return false;
    299 }
    300 
    301 bool RenderThemeAndroid::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    302 {
    303       bool translucent = false;
    304       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    305           translucent = true;
    306       if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o->node())) {
    307           if (btn->displayType() == MediaPlayButton)
    308               RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PLAY, translucent);
    309           else
    310               RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::PAUSE, translucent);
    311           return false;
    312       }
    313       return true;
    314 }
    315 
    316 bool RenderThemeAndroid::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    317 {
    318       bool translucent = false;
    319       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    320           translucent = true;
    321       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::REWIND, translucent);
    322       return false;
    323 }
    324 
    325 bool RenderThemeAndroid::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    326 {
    327       bool translucent = false;
    328       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    329           translucent = true;
    330       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::FORWARD, translucent);
    331       return false;
    332 }
    333 
    334 bool RenderThemeAndroid::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    335 {
    336       bool translucent = false;
    337       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    338           translucent = true;
    339       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::BACKGROUND_SLIDER, translucent);
    340       return false;
    341 }
    342 
    343 bool RenderThemeAndroid::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    344 {
    345       bool translucent = false;
    346       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    347           translucent = true;
    348       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect,
    349                                   RenderSkinMediaButton::SLIDER_TRACK, translucent, o);
    350       return false;
    351 }
    352 
    353 bool RenderThemeAndroid::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect)
    354 {
    355       bool translucent = false;
    356       if (o && toParentMediaElement(o) && toParentMediaElement(o)->hasTagName(HTMLNames::videoTag))
    357           translucent = true;
    358       RenderSkinMediaButton::Draw(getCanvasFromInfo(paintInfo), rect, RenderSkinMediaButton::SLIDER_THUMB, translucent);
    359       return false;
    360 }
    361 
    362 void RenderThemeAndroid::adjustSliderThumbSize(RenderObject* o) const
    363 {
    364     static const int sliderThumbWidth = RenderSkinMediaButton::sliderThumbWidth();
    365     static const int sliderThumbHeight = RenderSkinMediaButton::sliderThumbHeight();
    366     if (o->style()->appearance() == MediaSliderThumbPart) {
    367         o->style()->setWidth(Length(sliderThumbWidth, Fixed));
    368         o->style()->setHeight(Length(sliderThumbHeight, Fixed));
    369     }
    370 }
    371 
    372 #endif
    373 
    374 bool RenderThemeAndroid::paintRadio(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
    375 {
    376     RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, false);
    377     return false;
    378 }
    379 
    380 void RenderThemeAndroid::setCheckboxSize(RenderStyle* style) const
    381 {
    382     style->setWidth(Length(19, Fixed));
    383     style->setHeight(Length(19, Fixed));
    384 }
    385 
    386 void RenderThemeAndroid::setRadioSize(RenderStyle* style) const
    387 {
    388     // This is the same as checkboxes.
    389     setCheckboxSize(style);
    390 }
    391 
    392 void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
    393 {
    394     addIntrinsicMargins(style);
    395 }
    396 
    397 bool RenderThemeAndroid::paintTextField(RenderObject*, const PaintInfo&, const IntRect&)
    398 {
    399     return true;
    400 }
    401 
    402 void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const
    403 {
    404     addIntrinsicMargins(style);
    405 }
    406 
    407 bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
    408 {
    409     if (obj->isMenuList())
    410         paintCombo(obj, info, rect);
    411     return true;
    412 }
    413 
    414 void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
    415 {
    416     addIntrinsicMargins(style);
    417 }
    418 
    419 bool RenderThemeAndroid::paintSearchField(RenderObject*, const PaintInfo&, const IntRect&)
    420 {
    421     return true;
    422 }
    423 
    424 static void adjustMenuListStyleCommon(RenderStyle* style)
    425 {
    426     // Added to make room for our arrow and make the touch target less cramped.
    427     style->setPaddingLeft(Length(RenderSkinCombo::padding(), Fixed));
    428     style->setPaddingTop(Length(RenderSkinCombo::padding(), Fixed));
    429     style->setPaddingBottom(Length(RenderSkinCombo::padding(), Fixed));
    430     style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed));
    431     style->setMinHeight(Length(RenderSkinCombo::minHeight(), Fixed));
    432 }
    433 
    434 void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
    435 {
    436     adjustMenuListButtonStyle(0, style, 0);
    437 }
    438 
    439 void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const
    440 {
    441     adjustMenuListStyleCommon(style);
    442     addIntrinsicMargins(style);
    443 }
    444 
    445 bool RenderThemeAndroid::paintCombo(RenderObject* obj, const PaintInfo& info,  const IntRect& rect)
    446 {
    447   if (obj->style() && !obj->style()->visitedDependentColor(CSSPropertyBackgroundColor).alpha())
    448         return true;
    449     return RenderSkinCombo::Draw(getCanvasFromInfo(info), obj->node(), rect.x(), rect.y(), rect.width(), rect.height());
    450 }
    451 
    452 bool RenderThemeAndroid::paintMenuList(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
    453 {
    454     return paintCombo(obj, info, rect);
    455 }
    456 
    457 void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*,
    458         RenderStyle* style, Element*) const
    459 {
    460     // Copied from RenderThemeSafari.
    461     const float baseFontSize = 11.0f;
    462     const int baseBorderRadius = 5;
    463     float fontScale = style->fontSize() / baseFontSize;
    464 
    465     style->resetPadding();
    466     style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
    467 
    468     const int minHeight = 15;
    469     style->setMinHeight(Length(minHeight, Fixed));
    470 
    471     style->setLineHeight(RenderStyle::initialLineHeight());
    472     // Found these padding numbers by trial and error.
    473     const int padding = 4;
    474     style->setPaddingTop(Length(padding, Fixed));
    475     style->setPaddingLeft(Length(padding, Fixed));
    476     adjustMenuListStyleCommon(style);
    477 }
    478 
    479 bool RenderThemeAndroid::paintMenuListButton(RenderObject* obj, const PaintInfo& info, const IntRect& rect)
    480 {
    481     return paintCombo(obj, info, rect);
    482 }
    483 
    484 bool RenderThemeAndroid::supportsFocusRing(const RenderStyle* style) const
    485 {
    486     return style->opacity() > 0
    487         && style->hasAppearance()
    488         && style->appearance() != TextFieldPart
    489         && style->appearance() != SearchFieldPart
    490         && style->appearance() != TextAreaPart
    491         && style->appearance() != CheckboxPart
    492         && style->appearance() != RadioPart
    493         && style->appearance() != PushButtonPart
    494         && style->appearance() != SquareButtonPart
    495         && style->appearance() != ButtonPart
    496         && style->appearance() != ButtonBevelPart
    497         && style->appearance() != MenulistPart
    498         && style->appearance() != MenulistButtonPart;
    499 }
    500 
    501 } // namespace WebCore
    502