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 "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 41 namespace WebCore { 42 43 enum PaddingType { 44 TopPadding, 45 RightPadding, 46 BottomPadding, 47 LeftPadding 48 }; 49 50 static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 }; 51 52 // These values all match Safari/Win. 53 static const float defaultControlFontPixelSize = 13; 54 static const float defaultCancelButtonSize = 9; 55 static const float minCancelButtonSize = 5; 56 static const float maxCancelButtonSize = 21; 57 static const float defaultSearchFieldResultsDecorationSize = 13; 58 static const float minSearchFieldResultsDecorationSize = 9; 59 static const float maxSearchFieldResultsDecorationSize = 30; 60 61 RenderThemeChromiumSkia::RenderThemeChromiumSkia() 62 { 63 } 64 65 RenderThemeChromiumSkia::~RenderThemeChromiumSkia() 66 { 67 } 68 69 // Use the Windows style sheets to match their metrics. 70 String RenderThemeChromiumSkia::extraDefaultStyleSheet() 71 { 72 return RenderTheme::extraDefaultStyleSheet() + 73 String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)) + 74 String(themeChromiumSkiaUserAgentStyleSheet, sizeof(themeChromiumSkiaUserAgentStyleSheet)) + 75 String(themeChromiumUserAgentStyleSheet, sizeof(themeChromiumUserAgentStyleSheet)); 76 } 77 78 String RenderThemeChromiumSkia::extraQuirksStyleSheet() 79 { 80 return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); 81 } 82 83 bool RenderThemeChromiumSkia::supportsHover(const RenderStyle* style) const 84 { 85 return true; 86 } 87 88 bool RenderThemeChromiumSkia::supportsFocusRing(const RenderStyle* style) const 89 { 90 // This causes WebKit to draw the focus rings for us. 91 return false; 92 } 93 94 Color RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const 95 { 96 return Color(0x1e, 0x90, 0xff); 97 } 98 99 Color RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const 100 { 101 return Color(0xc8, 0xc8, 0xc8); 102 } 103 104 Color RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const 105 { 106 return Color::black; 107 } 108 109 Color RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const 110 { 111 return Color(0x32, 0x32, 0x32); 112 } 113 114 Color RenderThemeChromiumSkia::platformFocusRingColor() const 115 { 116 static Color focusRingColor(229, 151, 0, 255); 117 return focusRingColor; 118 } 119 120 double 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 (isRunningLayoutTest()) 125 return 0; 126 127 return caretBlinkIntervalInternal(); 128 } 129 130 void RenderThemeChromiumSkia::systemFont(CSSValueID valueID, FontDescription& fontDescription) const 131 { 132 RenderThemeChromiumFontProvider::systemFont(valueID, fontDescription); 133 } 134 135 int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const 136 { 137 return 0; 138 } 139 140 // These are the default dimensions of radio buttons and checkboxes. 141 static const int widgetStandardWidth = 13; 142 static 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|. 146 IntRect 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 156 void 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 171 void RenderThemeChromiumSkia::setRadioSize(RenderStyle* style) const 172 { 173 // Use same sizing for radio box as checkbox. 174 setCheckboxSize(style); 175 } 176 177 void RenderThemeChromiumSkia::adjustButtonStyle(RenderStyle* style, Element*) const 178 { 179 if (style->appearance() == PushButtonPart) { 180 // Ignore line-height. 181 style->setLineHeight(RenderStyle::initialLineHeight()); 182 } 183 } 184 185 bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 186 { 187 return paintTextField(o, i, r); 188 } 189 190 void RenderThemeChromiumSkia::adjustSearchFieldStyle(RenderStyle* style, Element*) const 191 { 192 // Ignore line-height. 193 style->setLineHeight(RenderStyle::initialLineHeight()); 194 } 195 196 bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r) 197 { 198 return paintTextField(o, i, r); 199 } 200 201 void 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 210 IntRect 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 222 bool 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 250 void 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 257 void 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 267 bool 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 294 bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 295 { 296 return RenderMediaControls::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); 297 } 298 299 bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 300 { 301 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); 302 } 303 304 void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderStyle* style, Element*) const 305 { 306 RenderMediaControls::adjustMediaSliderThumbSize(style); 307 } 308 309 bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 310 { 311 return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); 312 } 313 314 bool RenderThemeChromiumSkia::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 315 { 316 return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); 317 } 318 319 bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 320 { 321 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); 322 } 323 324 bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 325 { 326 return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); 327 } 328 329 bool RenderThemeChromiumSkia::paintMediaOverlayPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 330 { 331 return RenderMediaControls::paintMediaControlsPart(MediaOverlayPlayButton, object, paintInfo, rect); 332 } 333 334 bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 335 { 336 return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); 337 } 338 339 String RenderThemeChromiumSkia::formatMediaControlsTime(float time) const 340 { 341 return RenderMediaControls::formatMediaControlsTime(time); 342 } 343 344 String RenderThemeChromiumSkia::formatMediaControlsCurrentTime(float currentTime, float duration) const 345 { 346 return RenderMediaControls::formatMediaControlsCurrentTime(currentTime, duration); 347 } 348 349 bool RenderThemeChromiumSkia::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 350 { 351 return RenderMediaControls::paintMediaControlsPart(MediaEnterFullscreenButton, object, paintInfo, rect); 352 } 353 354 void RenderThemeChromiumSkia::adjustMenuListStyle(RenderStyle* style, WebCore::Element*) const 355 { 356 // Height is locked to auto on all browsers. 357 style->setLineHeight(RenderStyle::initialLineHeight()); 358 } 359 360 void RenderThemeChromiumSkia::adjustMenuListButtonStyle(RenderStyle* style, Element* e) const 361 { 362 adjustMenuListStyle(style, e); 363 } 364 365 // Used to paint styled menulists (i.e. with a non-default border) 366 bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) 367 { 368 return paintMenuList(o, i, rect); 369 } 370 371 int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const 372 { 373 return menuListInternalPadding(style, LeftPadding); 374 } 375 376 int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle* style) const 377 { 378 return menuListInternalPadding(style, RightPadding); 379 } 380 381 int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle* style) const 382 { 383 return menuListInternalPadding(style, TopPadding); 384 } 385 386 int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle* style) const 387 { 388 return menuListInternalPadding(style, BottomPadding); 389 } 390 391 // static 392 void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize) 393 { 394 RenderThemeChromiumFontProvider::setDefaultFontSize(fontSize); 395 } 396 397 double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const 398 { 399 return RenderTheme::caretBlinkInterval(); 400 } 401 402 int RenderThemeChromiumSkia::menuListArrowPadding() const 403 { 404 return ScrollbarTheme::theme()->scrollbarThickness(); 405 } 406 407 int RenderThemeChromiumSkia::menuListInternalPadding(RenderStyle* style, int paddingType) const 408 { 409 // This internal padding is in addition to the user-supplied padding. 410 // Matches the FF behavior. 411 int padding = styledMenuListInternalPadding[paddingType]; 412 413 // Reserve the space for right arrow here. The rest of the padding is 414 // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from 415 // RenderMenuList to lay out the individual items in the popup. 416 // If the MenuList actually has appearance "NoAppearance", then that means 417 // we don't draw a button, so don't reserve space for it. 418 const int barType = style->direction() == LTR ? RightPadding : LeftPadding; 419 if (paddingType == barType && style->appearance() != NoControlPart) 420 padding += menuListArrowPadding(); 421 422 return padding; 423 } 424 425 bool RenderThemeChromiumSkia::shouldShowPlaceholderWhenFocused() const 426 { 427 return true; 428 } 429 430 // 431 // Following values are come from default of GTK+ 432 // 433 static const int progressActivityBlocks = 5; 434 static const int progressAnimationFrames = 10; 435 static const double progressAnimationInterval = 0.125; 436 437 IntRect RenderThemeChromiumSkia::determinateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const 438 { 439 int dx = rect.width() * renderProgress->position(); 440 return IntRect(rect.x(), rect.y(), dx, rect.height()); 441 } 442 443 IntRect RenderThemeChromiumSkia::indeterminateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const 444 { 445 446 int valueWidth = rect.width() / progressActivityBlocks; 447 int movableWidth = rect.width() - valueWidth; 448 if (movableWidth <= 0) 449 return IntRect(); 450 451 double progress = renderProgress->animationProgress(); 452 if (progress < 0.5) 453 return IntRect(rect.x() + progress * 2 * movableWidth, rect.y(), valueWidth, rect.height()); 454 return IntRect(rect.x() + (1.0 - progress) * 2 * movableWidth, rect.y(), valueWidth, rect.height()); 455 } 456 457 double RenderThemeChromiumSkia::animationRepeatIntervalForProgressBar(RenderProgress*) const 458 { 459 return progressAnimationInterval; 460 } 461 462 double RenderThemeChromiumSkia::animationDurationForProgressBar(RenderProgress* renderProgress) const 463 { 464 return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth 465 } 466 467 IntRect RenderThemeChromiumSkia::progressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const 468 { 469 return renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, rect) : indeterminateProgressValueRectFor(renderProgress, rect); 470 } 471 472 RenderThemeChromiumSkia::DirectionFlippingScope::DirectionFlippingScope(RenderObject* renderer, const PaintInfo& paintInfo, const IntRect& rect) 473 : m_needsFlipping(!renderer->style()->isLeftToRightDirection()) 474 , m_paintInfo(paintInfo) 475 { 476 if (!m_needsFlipping) 477 return; 478 m_paintInfo.context->save(); 479 m_paintInfo.context->translate(2 * rect.x() + rect.width(), 0); 480 m_paintInfo.context->scale(-1, 1); 481 } 482 483 RenderThemeChromiumSkia::DirectionFlippingScope::~DirectionFlippingScope() 484 { 485 if (!m_needsFlipping) 486 return; 487 m_paintInfo.context->restore(); 488 } 489 490 } // namespace WebCore 491