1/* 2 * Copyright (C) 2007 Apple Inc. 3 * Copyright (C) 2007 Alp Toker <alp@atoker.com> 4 * Copyright (C) 2008 Collabora Ltd. 5 * Copyright (C) 2008, 2009 Google Inc. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24#include "config.h" 25#include "core/rendering/RenderThemeChromiumSkia.h" 26 27#include "core/UserAgentStyleSheets.h" 28#include "core/rendering/PaintInfo.h" 29#include "core/rendering/RenderBox.h" 30#include "core/rendering/RenderMediaControls.h" 31#include "core/rendering/RenderObject.h" 32#include "core/rendering/RenderProgress.h" 33#include "core/rendering/RenderThemeChromiumFontProvider.h" 34#include "platform/LayoutTestSupport.h" 35#include "platform/graphics/GraphicsContext.h" 36#include "platform/graphics/Image.h" 37#include "platform/scroll/ScrollbarTheme.h" 38#include "wtf/CurrentTime.h" 39#include "wtf/StdLibExtras.h" 40 41namespace blink { 42 43enum PaddingType { 44 TopPadding, 45 RightPadding, 46 BottomPadding, 47 LeftPadding 48}; 49 50static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 }; 51 52// These values all match Safari/Win. 53static const float defaultControlFontPixelSize = 13; 54static const float defaultCancelButtonSize = 9; 55static const float minCancelButtonSize = 5; 56static const float maxCancelButtonSize = 21; 57static const float defaultSearchFieldResultsDecorationSize = 13; 58static const float minSearchFieldResultsDecorationSize = 9; 59static const float maxSearchFieldResultsDecorationSize = 30; 60 61RenderThemeChromiumSkia::RenderThemeChromiumSkia() 62{ 63} 64 65RenderThemeChromiumSkia::~RenderThemeChromiumSkia() 66{ 67} 68 69// Use the Windows style sheets to match their metrics. 70String RenderThemeChromiumSkia::extraDefaultStyleSheet() 71{ 72 return RenderTheme::extraDefaultStyleSheet() + 73 String(themeWinCss, sizeof(themeWinCss)) + 74 String(themeChromiumSkiaCss, sizeof(themeChromiumSkiaCss)) + 75 String(themeChromiumCss, sizeof(themeChromiumCss)); 76} 77 78String RenderThemeChromiumSkia::extraQuirksStyleSheet() 79{ 80 return String(themeWinQuirksCss, sizeof(themeWinQuirksCss)); 81} 82 83bool RenderThemeChromiumSkia::supportsHover(const RenderStyle* style) const 84{ 85 return true; 86} 87 88bool RenderThemeChromiumSkia::supportsFocusRing(const RenderStyle* style) const 89{ 90 // This causes WebKit to draw the focus rings for us. 91 return false; 92} 93 94Color RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const 95{ 96 return Color(0x1e, 0x90, 0xff); 97} 98 99Color RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const 100{ 101 return Color(0xc8, 0xc8, 0xc8); 102} 103 104Color RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const 105{ 106 return Color::black; 107} 108 109Color RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const 110{ 111 return Color(0x32, 0x32, 0x32); 112} 113 114Color RenderThemeChromiumSkia::platformFocusRingColor() const 115{ 116 static Color focusRingColor(229, 151, 0, 255); 117 return focusRingColor; 118} 119 120double RenderThemeChromiumSkia::caretBlinkInterval() const 121{ 122 // Disable the blinking caret in layout test mode, as it introduces 123 // a race condition for the pixel tests. http://b/1198440 124 if (LayoutTestSupport::isRunningLayoutTest()) 125 return 0; 126 127 return caretBlinkIntervalInternal(); 128} 129 130void RenderThemeChromiumSkia::systemFont(CSSValueID valueID, FontDescription& fontDescription) const 131{ 132 RenderThemeChromiumFontProvider::systemFont(valueID, fontDescription); 133} 134 135int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const 136{ 137 return 0; 138} 139 140// These are the default dimensions of radio buttons and checkboxes. 141static const int widgetStandardWidth = 13; 142static const int widgetStandardHeight = 13; 143 144// Return a rectangle that has the same center point as |original|, but with a 145// size capped at |width| by |height|. 146IntRect center(const IntRect& original, int width, int height) 147{ 148 width = std::min(original.width(), width); 149 height = std::min(original.height(), height); 150 int x = original.x() + (original.width() - width) / 2; 151 int y = original.y() + (original.height() - height) / 2; 152 153 return IntRect(x, y, width, height); 154} 155 156void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle* style) const 157{ 158 // If the width and height are both specified, then we have nothing to do. 159 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 160 return; 161 162 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary 163 // for now. It matches Firefox. At different DPI settings on Windows, 164 // querying the theme gives you a larger size that accounts for the higher 165 // DPI. Until our entire engine honors a DPI setting other than 96, we 166 // can't rely on the theme's metrics. 167 const IntSize size(widgetStandardWidth, widgetStandardHeight); 168 setSizeIfAuto(style, size); 169} 170 171void RenderThemeChromiumSkia::setRadioSize(RenderStyle* style) const 172{ 173 // Use same sizing for radio box as checkbox. 174 setCheckboxSize(style); 175} 176 177void RenderThemeChromiumSkia::adjustButtonStyle(RenderStyle* style, Element*) const 178{ 179 if (style->appearance() == PushButtonPart) { 180 // Ignore line-height. 181 style->setLineHeight(RenderStyle::initialLineHeight()); 182 } 183} 184 185bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 186{ 187 return paintTextField(o, i, r); 188} 189 190void RenderThemeChromiumSkia::adjustSearchFieldStyle(RenderStyle* style, Element*) const 191{ 192 // Ignore line-height. 193 style->setLineHeight(RenderStyle::initialLineHeight()); 194} 195 196bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r) 197{ 198 return paintTextField(o, i, r); 199} 200 201void RenderThemeChromiumSkia::adjustSearchFieldCancelButtonStyle(RenderStyle* style, Element*) const 202{ 203 // Scale the button size based on the font size 204 float fontScale = style->fontSize() / defaultControlFontPixelSize; 205 int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); 206 style->setWidth(Length(cancelButtonSize, Fixed)); 207 style->setHeight(Length(cancelButtonSize, Fixed)); 208} 209 210IntRect RenderThemeChromiumSkia::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, LayoutRect partRect, const IntRect& localOffset) const 211{ 212 // Compute an offset between the part renderer and the input renderer. 213 LayoutSize offsetFromInputRenderer = -partRenderer->offsetFromAncestorContainer(inputRenderer); 214 // Move the rect into partRenderer's coords. 215 partRect.move(offsetFromInputRenderer); 216 // Account for the local drawing offset. 217 partRect.move(localOffset.x(), localOffset.y()); 218 219 return pixelSnappedIntRect(partRect); 220} 221 222bool RenderThemeChromiumSkia::paintSearchFieldCancelButton(RenderObject* cancelButtonObject, const PaintInfo& paintInfo, const IntRect& r) 223{ 224 // Get the renderer of <input> element. 225 if (!cancelButtonObject->node()) 226 return false; 227 Node* input = cancelButtonObject->node()->shadowHost(); 228 RenderObject* baseRenderer = input ? input->renderer() : cancelButtonObject; 229 if (!baseRenderer->isBox()) 230 return false; 231 RenderBox* inputRenderBox = toRenderBox(baseRenderer); 232 LayoutRect inputContentBox = inputRenderBox->contentBoxRect(); 233 234 // Make sure the scaled button stays square and will fit in its parent's box. 235 LayoutUnit cancelButtonSize = std::min(inputContentBox.width(), std::min<LayoutUnit>(inputContentBox.height(), r.height())); 236 // Calculate cancel button's coordinates relative to the input element. 237 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 238 // be one pixel closer to the bottom of the field. This tends to look better with the text. 239 LayoutRect cancelButtonRect(cancelButtonObject->offsetFromAncestorContainer(inputRenderBox).width(), 240 inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2, 241 cancelButtonSize, cancelButtonSize); 242 IntRect paintingRect = convertToPaintingRect(inputRenderBox, cancelButtonObject, cancelButtonRect, r); 243 244 DEFINE_STATIC_REF(Image, cancelImage, (Image::loadPlatformResource("searchCancel"))); 245 DEFINE_STATIC_REF(Image, cancelPressedImage, (Image::loadPlatformResource("searchCancelPressed"))); 246 paintInfo.context->drawImage(isPressed(cancelButtonObject) ? cancelPressedImage : cancelImage, paintingRect); 247 return false; 248} 249 250void RenderThemeChromiumSkia::adjustSearchFieldDecorationStyle(RenderStyle* style, Element*) const 251{ 252 IntSize emptySize(1, 11); 253 style->setWidth(Length(emptySize.width(), Fixed)); 254 style->setHeight(Length(emptySize.height(), Fixed)); 255} 256 257void RenderThemeChromiumSkia::adjustSearchFieldResultsDecorationStyle(RenderStyle* style, Element*) const 258{ 259 // Scale the decoration size based on the font size 260 float fontScale = style->fontSize() / defaultControlFontPixelSize; 261 int magnifierSize = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 262 maxSearchFieldResultsDecorationSize)); 263 style->setWidth(Length(magnifierSize, Fixed)); 264 style->setHeight(Length(magnifierSize, Fixed)); 265} 266 267bool RenderThemeChromiumSkia::paintSearchFieldResultsDecoration(RenderObject* magnifierObject, const PaintInfo& paintInfo, const IntRect& r) 268{ 269 // Get the renderer of <input> element. 270 if (!magnifierObject->node()) 271 return false; 272 Node* input = magnifierObject->node()->shadowHost(); 273 RenderObject* baseRenderer = input ? input->renderer() : magnifierObject; 274 if (!baseRenderer->isBox()) 275 return false; 276 RenderBox* inputRenderBox = toRenderBox(baseRenderer); 277 LayoutRect inputContentBox = inputRenderBox->contentBoxRect(); 278 279 // Make sure the scaled decoration stays square and will fit in its parent's box. 280 LayoutUnit magnifierSize = std::min(inputContentBox.width(), std::min<LayoutUnit>(inputContentBox.height(), r.height())); 281 // Calculate decoration's coordinates relative to the input element. 282 // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will 283 // be one pixel closer to the bottom of the field. This tends to look better with the text. 284 LayoutRect magnifierRect(magnifierObject->offsetFromAncestorContainer(inputRenderBox).width(), 285 inputContentBox.y() + (inputContentBox.height() - magnifierSize + 1) / 2, 286 magnifierSize, magnifierSize); 287 IntRect paintingRect = convertToPaintingRect(inputRenderBox, magnifierObject, magnifierRect, r); 288 289 DEFINE_STATIC_REF(Image, magnifierImage, (Image::loadPlatformResource("searchMagnifier"))); 290 paintInfo.context->drawImage(magnifierImage, paintingRect); 291 return false; 292} 293 294bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 295{ 296 return RenderMediaControls::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); 297} 298 299bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 300{ 301 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); 302} 303 304void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderStyle* style, Element*) const 305{ 306 RenderMediaControls::adjustMediaSliderThumbSize(style); 307} 308 309bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 310{ 311 return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); 312} 313 314bool RenderThemeChromiumSkia::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 315{ 316 return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); 317} 318 319bool RenderThemeChromiumSkia::paintMediaCastButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 320{ 321 return RenderMediaControls::paintMediaControlsPart(MediaCastOffButton, o, paintInfo, r); 322} 323 324bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 325{ 326 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); 327} 328 329bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 330{ 331 return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); 332} 333 334bool RenderThemeChromiumSkia::paintMediaOverlayPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 335{ 336 return RenderMediaControls::paintMediaControlsPart(MediaOverlayPlayButton, object, paintInfo, rect); 337} 338 339bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 340{ 341 return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); 342} 343 344String RenderThemeChromiumSkia::formatMediaControlsTime(float time) const 345{ 346 return RenderMediaControls::formatMediaControlsTime(time); 347} 348 349String RenderThemeChromiumSkia::formatMediaControlsCurrentTime(float currentTime, float duration) const 350{ 351 return RenderMediaControls::formatMediaControlsCurrentTime(currentTime, duration); 352} 353 354bool RenderThemeChromiumSkia::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 355{ 356 return RenderMediaControls::paintMediaControlsPart(MediaEnterFullscreenButton, object, paintInfo, rect); 357} 358 359void RenderThemeChromiumSkia::adjustMenuListStyle(RenderStyle* style, Element*) const 360{ 361 // Height is locked to auto on all browsers. 362 style->setLineHeight(RenderStyle::initialLineHeight()); 363} 364 365void RenderThemeChromiumSkia::adjustMenuListButtonStyle(RenderStyle* style, Element* e) const 366{ 367 adjustMenuListStyle(style, e); 368} 369 370// Used to paint styled menulists (i.e. with a non-default border) 371bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) 372{ 373 return paintMenuList(o, i, rect); 374} 375 376int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const 377{ 378 return menuListInternalPadding(style, LeftPadding); 379} 380 381int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle* style) const 382{ 383 return menuListInternalPadding(style, RightPadding); 384} 385 386int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle* style) const 387{ 388 return menuListInternalPadding(style, TopPadding); 389} 390 391int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle* style) const 392{ 393 return menuListInternalPadding(style, BottomPadding); 394} 395 396// static 397void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize) 398{ 399 RenderThemeChromiumFontProvider::setDefaultFontSize(fontSize); 400} 401 402double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const 403{ 404 return RenderTheme::caretBlinkInterval(); 405} 406 407int RenderThemeChromiumSkia::menuListArrowPadding() const 408{ 409 return ScrollbarTheme::theme()->scrollbarThickness(); 410} 411 412int RenderThemeChromiumSkia::menuListInternalPadding(RenderStyle* style, int paddingType) const 413{ 414 // This internal padding is in addition to the user-supplied padding. 415 // Matches the FF behavior. 416 int padding = styledMenuListInternalPadding[paddingType]; 417 418 // Reserve the space for right arrow here. The rest of the padding is 419 // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from 420 // RenderMenuList to lay out the individual items in the popup. 421 // If the MenuList actually has appearance "NoAppearance", then that means 422 // we don't draw a button, so don't reserve space for it. 423 const int barType = style->direction() == LTR ? RightPadding : LeftPadding; 424 if (paddingType == barType && style->appearance() != NoControlPart) 425 padding += menuListArrowPadding(); 426 427 return padding; 428} 429 430bool RenderThemeChromiumSkia::shouldShowPlaceholderWhenFocused() const 431{ 432 return true; 433} 434 435// 436// Following values are come from default of GTK+ 437// 438static const int progressActivityBlocks = 5; 439static const int progressAnimationFrames = 10; 440static const double progressAnimationInterval = 0.125; 441 442IntRect RenderThemeChromiumSkia::determinateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const 443{ 444 int dx = rect.width() * renderProgress->position(); 445 return IntRect(rect.x(), rect.y(), dx, rect.height()); 446} 447 448IntRect RenderThemeChromiumSkia::indeterminateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const 449{ 450 451 int valueWidth = rect.width() / progressActivityBlocks; 452 int movableWidth = rect.width() - valueWidth; 453 if (movableWidth <= 0) 454 return IntRect(); 455 456 double progress = renderProgress->animationProgress(); 457 if (progress < 0.5) 458 return IntRect(rect.x() + progress * 2 * movableWidth, rect.y(), valueWidth, rect.height()); 459 return IntRect(rect.x() + (1.0 - progress) * 2 * movableWidth, rect.y(), valueWidth, rect.height()); 460} 461 462double RenderThemeChromiumSkia::animationRepeatIntervalForProgressBar(RenderProgress*) const 463{ 464 return progressAnimationInterval; 465} 466 467double RenderThemeChromiumSkia::animationDurationForProgressBar(RenderProgress* renderProgress) const 468{ 469 return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth 470} 471 472IntRect RenderThemeChromiumSkia::progressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const 473{ 474 return renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, rect) : indeterminateProgressValueRectFor(renderProgress, rect); 475} 476 477RenderThemeChromiumSkia::DirectionFlippingScope::DirectionFlippingScope(RenderObject* renderer, const PaintInfo& paintInfo, const IntRect& rect) 478 : m_needsFlipping(!renderer->style()->isLeftToRightDirection()) 479 , m_paintInfo(paintInfo) 480{ 481 if (!m_needsFlipping) 482 return; 483 m_paintInfo.context->save(); 484 m_paintInfo.context->translate(2 * rect.x() + rect.width(), 0); 485 m_paintInfo.context->scale(-1, 1); 486} 487 488RenderThemeChromiumSkia::DirectionFlippingScope::~DirectionFlippingScope() 489{ 490 if (!m_needsFlipping) 491 return; 492 m_paintInfo.context->restore(); 493} 494 495} // namespace blink 496