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/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 bool RenderThemeChromiumSkia::supportsClosedCaptioning() const 95 { 96 return true; 97 } 98 99 Color RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const 100 { 101 return Color(0x1e, 0x90, 0xff); 102 } 103 104 Color RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const 105 { 106 return Color(0xc8, 0xc8, 0xc8); 107 } 108 109 Color RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const 110 { 111 return Color::black; 112 } 113 114 Color RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const 115 { 116 return Color(0x32, 0x32, 0x32); 117 } 118 119 Color RenderThemeChromiumSkia::platformFocusRingColor() const 120 { 121 static Color focusRingColor(229, 151, 0, 255); 122 return focusRingColor; 123 } 124 125 double RenderThemeChromiumSkia::caretBlinkInterval() const 126 { 127 // Disable the blinking caret in layout test mode, as it introduces 128 // a race condition for the pixel tests. http://b/1198440 129 if (isRunningLayoutTest()) 130 return 0; 131 132 return caretBlinkIntervalInternal(); 133 } 134 135 void RenderThemeChromiumSkia::systemFont(CSSValueID valueID, FontDescription& fontDescription) const 136 { 137 RenderThemeChromiumFontProvider::systemFont(valueID, fontDescription); 138 } 139 140 int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const 141 { 142 return 0; 143 } 144 145 // These are the default dimensions of radio buttons and checkboxes. 146 static const int widgetStandardWidth = 13; 147 static const int widgetStandardHeight = 13; 148 149 // Return a rectangle that has the same center point as |original|, but with a 150 // size capped at |width| by |height|. 151 IntRect center(const IntRect& original, int width, int height) 152 { 153 width = std::min(original.width(), width); 154 height = std::min(original.height(), height); 155 int x = original.x() + (original.width() - width) / 2; 156 int y = original.y() + (original.height() - height) / 2; 157 158 return IntRect(x, y, width, height); 159 } 160 161 void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle* style) const 162 { 163 // If the width and height are both specified, then we have nothing to do. 164 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 165 return; 166 167 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary 168 // for now. It matches Firefox. At different DPI settings on Windows, 169 // querying the theme gives you a larger size that accounts for the higher 170 // DPI. Until our entire engine honors a DPI setting other than 96, we 171 // can't rely on the theme's metrics. 172 const IntSize size(widgetStandardWidth, widgetStandardHeight); 173 setSizeIfAuto(style, size); 174 } 175 176 void RenderThemeChromiumSkia::setRadioSize(RenderStyle* style) const 177 { 178 // Use same sizing for radio box as checkbox. 179 setCheckboxSize(style); 180 } 181 182 void RenderThemeChromiumSkia::adjustButtonStyle(RenderStyle* style, Element*) const 183 { 184 if (style->appearance() == PushButtonPart) { 185 // Ignore line-height. 186 style->setLineHeight(RenderStyle::initialLineHeight()); 187 } 188 } 189 190 bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) 191 { 192 return paintTextField(o, i, r); 193 } 194 195 void RenderThemeChromiumSkia::adjustSearchFieldStyle(RenderStyle* style, Element*) const 196 { 197 // Ignore line-height. 198 style->setLineHeight(RenderStyle::initialLineHeight()); 199 } 200 201 bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r) 202 { 203 return paintTextField(o, i, r); 204 } 205 206 void RenderThemeChromiumSkia::adjustSearchFieldCancelButtonStyle(RenderStyle* style, Element*) const 207 { 208 // Scale the button size based on the font size 209 float fontScale = style->fontSize() / defaultControlFontPixelSize; 210 int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); 211 style->setWidth(Length(cancelButtonSize, Fixed)); 212 style->setHeight(Length(cancelButtonSize, Fixed)); 213 } 214 215 IntRect RenderThemeChromiumSkia::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, LayoutRect partRect, const IntRect& localOffset) const 216 { 217 // Compute an offset between the part renderer and the input renderer. 218 LayoutSize offsetFromInputRenderer = -partRenderer->offsetFromAncestorContainer(inputRenderer); 219 // Move the rect into partRenderer's coords. 220 partRect.move(offsetFromInputRenderer); 221 // Account for the local drawing offset. 222 partRect.move(localOffset.x(), localOffset.y()); 223 224 return pixelSnappedIntRect(partRect); 225 } 226 227 bool RenderThemeChromiumSkia::paintSearchFieldCancelButton(RenderObject* cancelButtonObject, const PaintInfo& paintInfo, const IntRect& r) 228 { 229 // Get the renderer of <input> element. 230 Node* input = cancelButtonObject->node()->shadowHost(); 231 RenderObject* baseRenderer = input ? input->renderer() : cancelButtonObject; 232 if (!baseRenderer->isBox()) 233 return false; 234 RenderBox* inputRenderBox = toRenderBox(baseRenderer); 235 LayoutRect inputContentBox = inputRenderBox->contentBoxRect(); 236 237 // Make sure the scaled button stays square and will fit in its parent's box. 238 LayoutUnit cancelButtonSize = std::min(inputContentBox.width(), std::min<LayoutUnit>(inputContentBox.height(), r.height())); 239 // Calculate cancel button's coordinates relative to the input element. 240 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 241 // be one pixel closer to the bottom of the field. This tends to look better with the text. 242 LayoutRect cancelButtonRect(cancelButtonObject->offsetFromAncestorContainer(inputRenderBox).width(), 243 inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2, 244 cancelButtonSize, cancelButtonSize); 245 IntRect paintingRect = convertToPaintingRect(inputRenderBox, cancelButtonObject, cancelButtonRect, r); 246 247 DEFINE_STATIC_REF(Image, cancelImage, (Image::loadPlatformResource("searchCancel"))); 248 DEFINE_STATIC_REF(Image, cancelPressedImage, (Image::loadPlatformResource("searchCancelPressed"))); 249 paintInfo.context->drawImage(isPressed(cancelButtonObject) ? cancelPressedImage : cancelImage, paintingRect); 250 return false; 251 } 252 253 void RenderThemeChromiumSkia::adjustSearchFieldDecorationStyle(RenderStyle* style, Element*) const 254 { 255 IntSize emptySize(1, 11); 256 style->setWidth(Length(emptySize.width(), Fixed)); 257 style->setHeight(Length(emptySize.height(), Fixed)); 258 } 259 260 void RenderThemeChromiumSkia::adjustSearchFieldResultsDecorationStyle(RenderStyle* style, Element*) const 261 { 262 // Scale the decoration size based on the font size 263 float fontScale = style->fontSize() / defaultControlFontPixelSize; 264 int magnifierSize = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 265 maxSearchFieldResultsDecorationSize)); 266 style->setWidth(Length(magnifierSize, Fixed)); 267 style->setHeight(Length(magnifierSize, Fixed)); 268 } 269 270 bool RenderThemeChromiumSkia::paintSearchFieldResultsDecoration(RenderObject* magnifierObject, const PaintInfo& paintInfo, const IntRect& r) 271 { 272 // Get the renderer of <input> element. 273 Node* input = magnifierObject->node()->shadowHost(); 274 RenderObject* baseRenderer = input ? input->renderer() : magnifierObject; 275 if (!baseRenderer->isBox()) 276 return false; 277 RenderBox* inputRenderBox = toRenderBox(baseRenderer); 278 LayoutRect inputContentBox = inputRenderBox->contentBoxRect(); 279 280 // Make sure the scaled decoration stays square and will fit in its parent's box. 281 LayoutUnit magnifierSize = std::min(inputContentBox.width(), std::min<LayoutUnit>(inputContentBox.height(), r.height())); 282 // Calculate decoration's coordinates relative to the input element. 283 // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will 284 // be one pixel closer to the bottom of the field. This tends to look better with the text. 285 LayoutRect magnifierRect(magnifierObject->offsetFromAncestorContainer(inputRenderBox).width(), 286 inputContentBox.y() + (inputContentBox.height() - magnifierSize + 1) / 2, 287 magnifierSize, magnifierSize); 288 IntRect paintingRect = convertToPaintingRect(inputRenderBox, magnifierObject, magnifierRect, r); 289 290 DEFINE_STATIC_REF(Image, magnifierImage, (Image::loadPlatformResource("searchMagnifier"))); 291 paintInfo.context->drawImage(magnifierImage, paintingRect); 292 return false; 293 } 294 295 bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 296 { 297 return RenderMediaControls::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); 298 } 299 300 bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 301 { 302 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); 303 } 304 305 void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderStyle* style, Element*) const 306 { 307 RenderMediaControls::adjustMediaSliderThumbSize(style); 308 } 309 310 bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 311 { 312 return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); 313 } 314 315 bool RenderThemeChromiumSkia::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 316 { 317 return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r); 318 } 319 320 bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 321 { 322 return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); 323 } 324 325 bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 326 { 327 return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); 328 } 329 330 bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 331 { 332 return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); 333 } 334 335 String RenderThemeChromiumSkia::formatMediaControlsTime(float time) const 336 { 337 return RenderMediaControls::formatMediaControlsTime(time); 338 } 339 340 String RenderThemeChromiumSkia::formatMediaControlsCurrentTime(float currentTime, float duration) const 341 { 342 return RenderMediaControls::formatMediaControlsCurrentTime(currentTime, duration); 343 } 344 345 bool RenderThemeChromiumSkia::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) 346 { 347 return RenderMediaControls::paintMediaControlsPart(MediaEnterFullscreenButton, object, paintInfo, rect); 348 } 349 350 void RenderThemeChromiumSkia::adjustMenuListStyle(RenderStyle* style, WebCore::Element*) const 351 { 352 // Height is locked to auto on all browsers. 353 style->setLineHeight(RenderStyle::initialLineHeight()); 354 } 355 356 void RenderThemeChromiumSkia::adjustMenuListButtonStyle(RenderStyle* style, Element* e) const 357 { 358 adjustMenuListStyle(style, e); 359 } 360 361 // Used to paint styled menulists (i.e. with a non-default border) 362 bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) 363 { 364 return paintMenuList(o, i, rect); 365 } 366 367 int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const 368 { 369 return menuListInternalPadding(style, LeftPadding); 370 } 371 372 int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle* style) const 373 { 374 return menuListInternalPadding(style, RightPadding); 375 } 376 377 int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle* style) const 378 { 379 return menuListInternalPadding(style, TopPadding); 380 } 381 382 int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle* style) const 383 { 384 return menuListInternalPadding(style, BottomPadding); 385 } 386 387 // static 388 void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize) 389 { 390 RenderThemeChromiumFontProvider::setDefaultFontSize(fontSize); 391 } 392 393 double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const 394 { 395 return RenderTheme::caretBlinkInterval(); 396 } 397 398 int RenderThemeChromiumSkia::menuListArrowPadding() const 399 { 400 return ScrollbarTheme::theme()->scrollbarThickness(); 401 } 402 403 int RenderThemeChromiumSkia::menuListInternalPadding(RenderStyle* style, int paddingType) const 404 { 405 // This internal padding is in addition to the user-supplied padding. 406 // Matches the FF behavior. 407 int padding = styledMenuListInternalPadding[paddingType]; 408 409 // Reserve the space for right arrow here. The rest of the padding is 410 // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from 411 // RenderMenuList to lay out the individual items in the popup. 412 // If the MenuList actually has appearance "NoAppearance", then that means 413 // we don't draw a button, so don't reserve space for it. 414 const int barType = style->direction() == LTR ? RightPadding : LeftPadding; 415 if (paddingType == barType && style->appearance() != NoControlPart) 416 padding += menuListArrowPadding(); 417 418 return padding; 419 } 420 421 bool RenderThemeChromiumSkia::shouldShowPlaceholderWhenFocused() const 422 { 423 return true; 424 } 425 426 // 427 // Following values are come from default of GTK+ 428 // 429 static const int progressActivityBlocks = 5; 430 static const int progressAnimationFrames = 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 * progressAnimationFrames * 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