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