1 /* 2 * Copyright (C) 2007 Apple Inc. 3 * Copyright (C) 2007 Alp Toker <alp (at) 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 40 namespace WebCore { 41 42 enum PaddingType { 43 TopPadding, 44 RightPadding, 45 BottomPadding, 46 LeftPadding 47 }; 48 49 static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 }; 50 51 // These values all match Safari/Win. 52 static const float defaultControlFontPixelSize = 13; 53 static const float defaultCancelButtonSize = 9; 54 static const float minCancelButtonSize = 5; 55 static const float maxCancelButtonSize = 21; 56 static const float defaultSearchFieldResultsDecorationSize = 13; 57 static const float minSearchFieldResultsDecorationSize = 9; 58 static const float maxSearchFieldResultsDecorationSize = 30; 59 60 RenderThemeChromiumSkia::RenderThemeChromiumSkia() 61 { 62 } 63 64 RenderThemeChromiumSkia::~RenderThemeChromiumSkia() 65 { 66 } 67 68 // Use the Windows style sheets to match their metrics. 69 String RenderThemeChromiumSkia::extraDefaultStyleSheet() 70 { 71 return RenderTheme::extraDefaultStyleSheet() + 72 String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)) + 73 String(themeChromiumSkiaUserAgentStyleSheet, sizeof(themeChromiumSkiaUserAgentStyleSheet)) + 74 String(themeChromiumUserAgentStyleSheet, sizeof(themeChromiumUserAgentStyleSheet)); 75 } 76 77 String RenderThemeChromiumSkia::extraQuirksStyleSheet() 78 { 79 return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); 80 } 81 82 bool RenderThemeChromiumSkia::supportsHover(const RenderStyle* style) const 83 { 84 return true; 85 } 86 87 bool RenderThemeChromiumSkia::supportsFocusRing(const RenderStyle* style) const 88 { 89 // This causes WebKit to draw the focus rings for us. 90 return false; 91 } 92 93 bool RenderThemeChromiumSkia::supportsClosedCaptioning() const 94 { 95 return true; 96 } 97 98 Color RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const 99 { 100 return Color(0x1e, 0x90, 0xff); 101 } 102 103 Color RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const 104 { 105 return Color(0xc8, 0xc8, 0xc8); 106 } 107 108 Color RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const 109 { 110 return Color::black; 111 } 112 113 Color RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const 114 { 115 return Color(0x32, 0x32, 0x32); 116 } 117 118 Color RenderThemeChromiumSkia::platformFocusRingColor() const 119 { 120 static Color focusRingColor(229, 151, 0, 255); 121 return focusRingColor; 122 } 123 124 double 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 134 void RenderThemeChromiumSkia::systemFont(CSSValueID valueID, FontDescription& fontDescription) const 135 { 136 RenderThemeChromiumFontProvider::systemFont(valueID, fontDescription); 137 } 138 139 int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const 140 { 141 return 0; 142 } 143 144 // These are the default dimensions of radio buttons and checkboxes. 145 static const int widgetStandardWidth = 13; 146 static 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|. 150 IntRect 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 160 void 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 175 void RenderThemeChromiumSkia::setRadioSize(RenderStyle* style) const 176 { 177 // Use same sizing for radio box as checkbox. 178 setCheckboxSize(style); 179 } 180 181 void RenderThemeChromiumSkia::adjustButtonStyle(RenderStyle* style, Element*) const 182 { 183 if (style->appearance() == PushButtonPart) { 184 // Ignore line-height. 185 style->setLineHeight(RenderStyle::initialLineHeight()); 186 } 187 } 188 189 bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 190 { 191 return paintTextField(o, i, r); 192 } 193 194 void RenderThemeChromiumSkia::adjustSearchFieldStyle(RenderStyle* style, Element*) const 195 { 196 // Ignore line-height. 197 style->setLineHeight(RenderStyle::initialLineHeight()); 198 } 199 200 bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r) 201 { 202 return paintTextField(o, i, r); 203 } 204 205 void 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 214 IntRect 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 226 bool 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 252 void 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 259 void 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 269 bool 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 294 bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 295 { 296 return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); 297 } 298 299 bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 300 { 301 return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); 302 } 303 304 void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderStyle* style, Element*) const 305 { 306 RenderMediaControlsChromium::adjustMediaSliderThumbSize(style); 307 } 308 309 bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 310 { 311 return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); 312 } 313 314 bool RenderThemeChromiumSkia::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 315 { 316 return RenderMediaControlsChromium::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); 317 } 318 319 bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 320 { 321 return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); 322 } 323 324 bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 325 { 326 return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); 327 } 328 329 bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 330 { 331 return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); 332 } 333 334 String RenderThemeChromiumSkia::formatMediaControlsTime(float time) const 335 { 336 return RenderMediaControlsChromium::formatMediaControlsTime(time); 337 } 338 339 String RenderThemeChromiumSkia::formatMediaControlsCurrentTime(float currentTime, float duration) const 340 { 341 return RenderMediaControlsChromium::formatMediaControlsCurrentTime(currentTime, duration); 342 } 343 344 bool RenderThemeChromiumSkia::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 345 { 346 return RenderMediaControlsChromium::paintMediaControlsPart(MediaEnterFullscreenButton, object, paintInfo, rect); 347 } 348 349 void RenderThemeChromiumSkia::adjustMenuListStyle(RenderStyle* style, WebCore::Element*) const 350 { 351 // Height is locked to auto on all browsers. 352 style->setLineHeight(RenderStyle::initialLineHeight()); 353 } 354 355 void 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) 361 bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) 362 { 363 return paintMenuList(o, i, rect); 364 } 365 366 int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const 367 { 368 return menuListInternalPadding(style, LeftPadding); 369 } 370 371 int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle* style) const 372 { 373 return menuListInternalPadding(style, RightPadding); 374 } 375 376 int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle* style) const 377 { 378 return menuListInternalPadding(style, TopPadding); 379 } 380 381 int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle* style) const 382 { 383 return menuListInternalPadding(style, BottomPadding); 384 } 385 386 // static 387 void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize) 388 { 389 RenderThemeChromiumFontProvider::setDefaultFontSize(fontSize); 390 } 391 392 double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const 393 { 394 return RenderTheme::caretBlinkInterval(); 395 } 396 397 int RenderThemeChromiumSkia::menuListArrowPadding() const 398 { 399 return ScrollbarTheme::theme()->scrollbarThickness(); 400 } 401 402 int 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 420 bool RenderThemeChromiumSkia::shouldShowPlaceholderWhenFocused() const 421 { 422 return true; 423 } 424 425 // 426 // Following values are come from default of GTK+ 427 // 428 static const int progressDeltaPixelsPerSecond = 100; 429 static const int progressActivityBlocks = 5; 430 static const int progressAnimationFrmaes = 10; 431 static const double progressAnimationInterval = 0.125; 432 433 IntRect 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 439 IntRect 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 453 double RenderThemeChromiumSkia::animationRepeatIntervalForProgressBar(RenderProgress*) const 454 { 455 return progressAnimationInterval; 456 } 457 458 double RenderThemeChromiumSkia::animationDurationForProgressBar(RenderProgress* renderProgress) const 459 { 460 return progressAnimationInterval * progressAnimationFrmaes * 2; // "2" for back and forth 461 } 462 463 IntRect RenderThemeChromiumSkia::progressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const 464 { 465 return renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, rect) : indeterminateProgressValueRectFor(renderProgress, rect); 466 } 467 468 RenderThemeChromiumSkia::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 479 RenderThemeChromiumSkia::DirectionFlippingScope::~DirectionFlippingScope() 480 { 481 if (!m_needsFlipping) 482 return; 483 m_paintInfo.context->restore(); 484 } 485 486 } // namespace WebCore 487