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