1 /** 2 * This file is part of the theme implementation for form controls in WebCore. 3 * 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Computer, Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #include "config.h" 23 #include "core/rendering/RenderTheme.h" 24 25 #include "core/CSSValueKeywords.h" 26 #include "core/HTMLNames.h" 27 #include "core/InputTypeNames.h" 28 #include "core/dom/Document.h" 29 #include "core/dom/shadow/ElementShadow.h" 30 #include "core/editing/FrameSelection.h" 31 #include "core/fileapi/FileList.h" 32 #include "core/frame/LocalFrame.h" 33 #include "core/html/HTMLCollection.h" 34 #include "core/html/HTMLDataListElement.h" 35 #include "core/html/HTMLDataListOptionsCollection.h" 36 #include "core/html/HTMLFormControlElement.h" 37 #include "core/html/HTMLInputElement.h" 38 #include "core/html/HTMLMeterElement.h" 39 #include "core/html/HTMLOptionElement.h" 40 #include "core/html/parser/HTMLParserIdioms.h" 41 #include "core/html/shadow/MediaControlElements.h" 42 #include "core/html/shadow/ShadowElementNames.h" 43 #include "core/html/shadow/SpinButtonElement.h" 44 #include "core/html/shadow/TextControlInnerElements.h" 45 #include "core/page/FocusController.h" 46 #include "core/page/Page.h" 47 #include "core/frame/Settings.h" 48 #include "core/rendering/PaintInfo.h" 49 #include "core/rendering/RenderMeter.h" 50 #include "core/rendering/RenderView.h" 51 #include "core/rendering/style/RenderStyle.h" 52 #include "platform/FileMetadata.h" 53 #include "platform/FloatConversion.h" 54 #include "platform/RuntimeEnabledFeatures.h" 55 #include "platform/fonts/FontSelector.h" 56 #include "platform/graphics/GraphicsContextStateSaver.h" 57 #include "platform/text/PlatformLocale.h" 58 #include "platform/text/StringTruncator.h" 59 #include "public/platform/Platform.h" 60 #include "public/platform/WebFallbackThemeEngine.h" 61 #include "public/platform/WebRect.h" 62 #include "wtf/text/StringBuilder.h" 63 64 // The methods in this file are shared by all themes on every platform. 65 66 namespace blink { 67 68 using namespace HTMLNames; 69 70 static WebFallbackThemeEngine::State getWebFallbackThemeState(const RenderTheme* theme, const RenderObject* o) 71 { 72 if (!theme->isEnabled(o)) 73 return WebFallbackThemeEngine::StateDisabled; 74 if (theme->isPressed(o)) 75 return WebFallbackThemeEngine::StatePressed; 76 if (theme->isHovered(o)) 77 return WebFallbackThemeEngine::StateHover; 78 79 return WebFallbackThemeEngine::StateNormal; 80 } 81 82 RenderTheme::RenderTheme() 83 : m_hasCustomFocusRingColor(false) 84 #if USE(NEW_THEME) 85 , m_platformTheme(platformTheme()) 86 #endif 87 { 88 } 89 90 void RenderTheme::adjustStyle(RenderStyle* style, Element* e, const CachedUAStyle* uaStyle) 91 { 92 // Force inline and table display styles to be inline-block (except for table- which is block) 93 ControlPart part = style->appearance(); 94 if (style->display() == INLINE || style->display() == INLINE_TABLE || style->display() == TABLE_ROW_GROUP 95 || style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_FOOTER_GROUP 96 || style->display() == TABLE_ROW || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_COLUMN 97 || style->display() == TABLE_CELL || style->display() == TABLE_CAPTION) 98 style->setDisplay(INLINE_BLOCK); 99 else if (style->display() == LIST_ITEM || style->display() == TABLE) 100 style->setDisplay(BLOCK); 101 102 if (uaStyle && uaStyle->hasAppearance && isControlStyled(style, uaStyle)) { 103 if (part == MenulistPart) { 104 style->setAppearance(MenulistButtonPart); 105 part = MenulistButtonPart; 106 } else 107 style->setAppearance(NoControlPart); 108 } 109 110 if (!style->hasAppearance()) 111 return; 112 113 if (shouldUseFallbackTheme(style)) { 114 adjustStyleUsingFallbackTheme(style, e); 115 return; 116 } 117 118 #if USE(NEW_THEME) 119 switch (part) { 120 case CheckboxPart: 121 case InnerSpinButtonPart: 122 case RadioPart: 123 case PushButtonPart: 124 case SquareButtonPart: 125 case ButtonPart: { 126 // Border 127 LengthBox borderBox(style->borderTopWidth(), style->borderRightWidth(), style->borderBottomWidth(), style->borderLeftWidth()); 128 borderBox = m_platformTheme->controlBorder(part, style->font().fontDescription(), borderBox, style->effectiveZoom()); 129 if (borderBox.top().value() != static_cast<int>(style->borderTopWidth())) { 130 if (borderBox.top().value()) 131 style->setBorderTopWidth(borderBox.top().value()); 132 else 133 style->resetBorderTop(); 134 } 135 if (borderBox.right().value() != static_cast<int>(style->borderRightWidth())) { 136 if (borderBox.right().value()) 137 style->setBorderRightWidth(borderBox.right().value()); 138 else 139 style->resetBorderRight(); 140 } 141 if (borderBox.bottom().value() != static_cast<int>(style->borderBottomWidth())) { 142 style->setBorderBottomWidth(borderBox.bottom().value()); 143 if (borderBox.bottom().value()) 144 style->setBorderBottomWidth(borderBox.bottom().value()); 145 else 146 style->resetBorderBottom(); 147 } 148 if (borderBox.left().value() != static_cast<int>(style->borderLeftWidth())) { 149 style->setBorderLeftWidth(borderBox.left().value()); 150 if (borderBox.left().value()) 151 style->setBorderLeftWidth(borderBox.left().value()); 152 else 153 style->resetBorderLeft(); 154 } 155 156 // Padding 157 LengthBox paddingBox = m_platformTheme->controlPadding(part, style->font().fontDescription(), style->paddingBox(), style->effectiveZoom()); 158 if (paddingBox != style->paddingBox()) 159 style->setPaddingBox(paddingBox); 160 161 // Whitespace 162 if (m_platformTheme->controlRequiresPreWhiteSpace(part)) 163 style->setWhiteSpace(PRE); 164 165 // Width / Height 166 // The width and height here are affected by the zoom. 167 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. 168 LengthSize controlSize = m_platformTheme->controlSize(part, style->font().fontDescription(), LengthSize(style->width(), style->height()), style->effectiveZoom()); 169 if (controlSize.width() != style->width()) 170 style->setWidth(controlSize.width()); 171 if (controlSize.height() != style->height()) 172 style->setHeight(controlSize.height()); 173 174 // Min-Width / Min-Height 175 LengthSize minControlSize = m_platformTheme->minimumControlSize(part, style->font().fontDescription(), style->effectiveZoom()); 176 if (minControlSize.width() != style->minWidth()) 177 style->setMinWidth(minControlSize.width()); 178 if (minControlSize.height() != style->minHeight()) 179 style->setMinHeight(minControlSize.height()); 180 181 // Font 182 FontDescription controlFont = m_platformTheme->controlFont(part, style->font().fontDescription(), style->effectiveZoom()); 183 if (controlFont != style->font().fontDescription()) { 184 // Reset our line-height 185 style->setLineHeight(RenderStyle::initialLineHeight()); 186 187 // Now update our font. 188 if (style->setFontDescription(controlFont)) 189 style->font().update(nullptr); 190 } 191 } 192 default: 193 break; 194 } 195 #endif 196 197 // Call the appropriate style adjustment method based off the appearance value. 198 switch (style->appearance()) { 199 #if !USE(NEW_THEME) 200 case CheckboxPart: 201 return adjustCheckboxStyle(style, e); 202 case RadioPart: 203 return adjustRadioStyle(style, e); 204 case PushButtonPart: 205 case SquareButtonPart: 206 case ButtonPart: 207 return adjustButtonStyle(style, e); 208 case InnerSpinButtonPart: 209 return adjustInnerSpinButtonStyle(style, e); 210 #endif 211 case MenulistPart: 212 return adjustMenuListStyle(style, e); 213 case MenulistButtonPart: 214 return adjustMenuListButtonStyle(style, e); 215 case SliderThumbHorizontalPart: 216 case SliderThumbVerticalPart: 217 return adjustSliderThumbStyle(style, e); 218 case SearchFieldPart: 219 return adjustSearchFieldStyle(style, e); 220 case SearchFieldCancelButtonPart: 221 return adjustSearchFieldCancelButtonStyle(style, e); 222 case SearchFieldDecorationPart: 223 return adjustSearchFieldDecorationStyle(style, e); 224 case SearchFieldResultsDecorationPart: 225 return adjustSearchFieldResultsDecorationStyle(style, e); 226 default: 227 break; 228 } 229 } 230 231 bool RenderTheme::paint(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 232 { 233 ControlPart part = o->style()->appearance(); 234 235 if (shouldUseFallbackTheme(o->style())) 236 return paintUsingFallbackTheme(o, paintInfo, r); 237 238 #if USE(NEW_THEME) 239 switch (part) { 240 case CheckboxPart: 241 case RadioPart: 242 case PushButtonPart: 243 case SquareButtonPart: 244 case ButtonPart: 245 case InnerSpinButtonPart: 246 m_platformTheme->paint(part, controlStatesForRenderer(o), const_cast<GraphicsContext*>(paintInfo.context), r, o->style()->effectiveZoom(), o->view()->frameView()); 247 return false; 248 default: 249 break; 250 } 251 #endif 252 253 // Call the appropriate paint method based off the appearance value. 254 switch (part) { 255 #if !USE(NEW_THEME) 256 case CheckboxPart: 257 return paintCheckbox(o, paintInfo, r); 258 case RadioPart: 259 return paintRadio(o, paintInfo, r); 260 case PushButtonPart: 261 case SquareButtonPart: 262 case ButtonPart: 263 return paintButton(o, paintInfo, r); 264 case InnerSpinButtonPart: 265 return paintInnerSpinButton(o, paintInfo, r); 266 #endif 267 case MenulistPart: 268 return paintMenuList(o, paintInfo, r); 269 case MeterPart: 270 case RelevancyLevelIndicatorPart: 271 case ContinuousCapacityLevelIndicatorPart: 272 case DiscreteCapacityLevelIndicatorPart: 273 case RatingLevelIndicatorPart: 274 return paintMeter(o, paintInfo, r); 275 case ProgressBarPart: 276 return paintProgressBar(o, paintInfo, r); 277 case SliderHorizontalPart: 278 case SliderVerticalPart: 279 return paintSliderTrack(o, paintInfo, r); 280 case SliderThumbHorizontalPart: 281 case SliderThumbVerticalPart: 282 return paintSliderThumb(o, paintInfo, r); 283 case MediaEnterFullscreenButtonPart: 284 case MediaExitFullscreenButtonPart: 285 return paintMediaFullscreenButton(o, paintInfo, r); 286 case MediaPlayButtonPart: 287 return paintMediaPlayButton(o, paintInfo, r); 288 case MediaOverlayPlayButtonPart: 289 return paintMediaOverlayPlayButton(o, paintInfo, r); 290 case MediaMuteButtonPart: 291 return paintMediaMuteButton(o, paintInfo, r); 292 case MediaToggleClosedCaptionsButtonPart: 293 return paintMediaToggleClosedCaptionsButton(o, paintInfo, r); 294 case MediaSliderPart: 295 return paintMediaSliderTrack(o, paintInfo, r); 296 case MediaSliderThumbPart: 297 return paintMediaSliderThumb(o, paintInfo, r); 298 case MediaVolumeSliderContainerPart: 299 return paintMediaVolumeSliderContainer(o, paintInfo, r); 300 case MediaVolumeSliderPart: 301 return paintMediaVolumeSliderTrack(o, paintInfo, r); 302 case MediaVolumeSliderThumbPart: 303 return paintMediaVolumeSliderThumb(o, paintInfo, r); 304 case MediaFullScreenVolumeSliderPart: 305 return paintMediaFullScreenVolumeSliderTrack(o, paintInfo, r); 306 case MediaFullScreenVolumeSliderThumbPart: 307 return paintMediaFullScreenVolumeSliderThumb(o, paintInfo, r); 308 case MediaTimeRemainingPart: 309 return paintMediaTimeRemaining(o, paintInfo, r); 310 case MediaCurrentTimePart: 311 return paintMediaCurrentTime(o, paintInfo, r); 312 case MediaControlsBackgroundPart: 313 return paintMediaControlsBackground(o, paintInfo, r); 314 case MediaCastOffButtonPart: 315 return paintMediaCastButton(o, paintInfo, r); 316 case MediaOverlayCastOffButtonPart: 317 return paintMediaCastButton(o, paintInfo, r); 318 case MenulistButtonPart: 319 case TextFieldPart: 320 case TextAreaPart: 321 return true; 322 case SearchFieldPart: 323 return paintSearchField(o, paintInfo, r); 324 case SearchFieldCancelButtonPart: 325 return paintSearchFieldCancelButton(o, paintInfo, r); 326 case SearchFieldDecorationPart: 327 return paintSearchFieldDecoration(o, paintInfo, r); 328 case SearchFieldResultsDecorationPart: 329 return paintSearchFieldResultsDecoration(o, paintInfo, r); 330 default: 331 break; 332 } 333 334 return true; // We don't support the appearance, so let the normal background/border paint. 335 } 336 337 bool RenderTheme::paintBorderOnly(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 338 { 339 // Call the appropriate paint method based off the appearance value. 340 switch (o->style()->appearance()) { 341 case TextFieldPart: 342 return paintTextField(o, paintInfo, r); 343 case TextAreaPart: 344 return paintTextArea(o, paintInfo, r); 345 case MenulistButtonPart: 346 case SearchFieldPart: 347 case ListboxPart: 348 return true; 349 case CheckboxPart: 350 case RadioPart: 351 case PushButtonPart: 352 case SquareButtonPart: 353 case ButtonPart: 354 case MenulistPart: 355 case MeterPart: 356 case RelevancyLevelIndicatorPart: 357 case ContinuousCapacityLevelIndicatorPart: 358 case DiscreteCapacityLevelIndicatorPart: 359 case RatingLevelIndicatorPart: 360 case ProgressBarPart: 361 case SliderHorizontalPart: 362 case SliderVerticalPart: 363 case SliderThumbHorizontalPart: 364 case SliderThumbVerticalPart: 365 case SearchFieldCancelButtonPart: 366 case SearchFieldDecorationPart: 367 case SearchFieldResultsDecorationPart: 368 default: 369 break; 370 } 371 372 return false; 373 } 374 375 bool RenderTheme::paintDecorations(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) 376 { 377 // Call the appropriate paint method based off the appearance value. 378 switch (o->style()->appearance()) { 379 case MenulistButtonPart: 380 return paintMenuListButton(o, paintInfo, r); 381 case TextFieldPart: 382 case TextAreaPart: 383 case CheckboxPart: 384 case RadioPart: 385 case PushButtonPart: 386 case SquareButtonPart: 387 case ButtonPart: 388 case MenulistPart: 389 case MeterPart: 390 case RelevancyLevelIndicatorPart: 391 case ContinuousCapacityLevelIndicatorPart: 392 case DiscreteCapacityLevelIndicatorPart: 393 case RatingLevelIndicatorPart: 394 case ProgressBarPart: 395 case SliderHorizontalPart: 396 case SliderVerticalPart: 397 case SliderThumbHorizontalPart: 398 case SliderThumbVerticalPart: 399 case SearchFieldPart: 400 case SearchFieldCancelButtonPart: 401 case SearchFieldDecorationPart: 402 case SearchFieldResultsDecorationPart: 403 default: 404 break; 405 } 406 407 return false; 408 } 409 410 String RenderTheme::extraDefaultStyleSheet() 411 { 412 StringBuilder runtimeCSS; 413 if (RuntimeEnabledFeatures::contextMenuEnabled()) 414 runtimeCSS.appendLiteral("menu[type=\"popup\" i] { display: none; }"); 415 return runtimeCSS.toString(); 416 } 417 418 String RenderTheme::formatMediaControlsTime(float time) const 419 { 420 if (!std::isfinite(time)) 421 time = 0; 422 int seconds = (int)fabsf(time); 423 int hours = seconds / (60 * 60); 424 int minutes = (seconds / 60) % 60; 425 seconds %= 60; 426 if (hours) { 427 if (hours > 9) 428 return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); 429 430 return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); 431 } 432 433 return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds); 434 } 435 436 String RenderTheme::formatMediaControlsCurrentTime(float currentTime, float /*duration*/) const 437 { 438 return formatMediaControlsTime(currentTime); 439 } 440 441 Color RenderTheme::activeSelectionBackgroundColor() const 442 { 443 return platformActiveSelectionBackgroundColor().blendWithWhite(); 444 } 445 446 Color RenderTheme::inactiveSelectionBackgroundColor() const 447 { 448 return platformInactiveSelectionBackgroundColor().blendWithWhite(); 449 } 450 451 Color RenderTheme::activeSelectionForegroundColor() const 452 { 453 return platformActiveSelectionForegroundColor(); 454 } 455 456 Color RenderTheme::inactiveSelectionForegroundColor() const 457 { 458 return platformInactiveSelectionForegroundColor(); 459 } 460 461 Color RenderTheme::activeListBoxSelectionBackgroundColor() const 462 { 463 return platformActiveListBoxSelectionBackgroundColor(); 464 } 465 466 Color RenderTheme::inactiveListBoxSelectionBackgroundColor() const 467 { 468 return platformInactiveListBoxSelectionBackgroundColor(); 469 } 470 471 Color RenderTheme::activeListBoxSelectionForegroundColor() const 472 { 473 return platformActiveListBoxSelectionForegroundColor(); 474 } 475 476 Color RenderTheme::inactiveListBoxSelectionForegroundColor() const 477 { 478 return platformInactiveListBoxSelectionForegroundColor(); 479 } 480 481 Color RenderTheme::platformActiveSelectionBackgroundColor() const 482 { 483 // Use a blue color by default if the platform theme doesn't define anything. 484 return Color(0, 0, 255); 485 } 486 487 Color RenderTheme::platformActiveSelectionForegroundColor() const 488 { 489 // Use a white color by default if the platform theme doesn't define anything. 490 return Color::white; 491 } 492 493 Color RenderTheme::platformInactiveSelectionBackgroundColor() const 494 { 495 // Use a grey color by default if the platform theme doesn't define anything. 496 // This color matches Firefox's inactive color. 497 return Color(176, 176, 176); 498 } 499 500 Color RenderTheme::platformInactiveSelectionForegroundColor() const 501 { 502 // Use a black color by default. 503 return Color::black; 504 } 505 506 Color RenderTheme::platformActiveListBoxSelectionBackgroundColor() const 507 { 508 return platformActiveSelectionBackgroundColor(); 509 } 510 511 Color RenderTheme::platformActiveListBoxSelectionForegroundColor() const 512 { 513 return platformActiveSelectionForegroundColor(); 514 } 515 516 Color RenderTheme::platformInactiveListBoxSelectionBackgroundColor() const 517 { 518 return platformInactiveSelectionBackgroundColor(); 519 } 520 521 Color RenderTheme::platformInactiveListBoxSelectionForegroundColor() const 522 { 523 return platformInactiveSelectionForegroundColor(); 524 } 525 526 int RenderTheme::baselinePosition(const RenderObject* o) const 527 { 528 if (!o->isBox()) 529 return 0; 530 531 const RenderBox* box = toRenderBox(o); 532 533 #if USE(NEW_THEME) 534 return box->height() + box->marginTop() + m_platformTheme->baselinePositionAdjustment(o->style()->appearance()) * o->style()->effectiveZoom(); 535 #else 536 return box->height() + box->marginTop(); 537 #endif 538 } 539 540 bool RenderTheme::isControlContainer(ControlPart appearance) const 541 { 542 // There are more leaves than this, but we'll patch this function as we add support for 543 // more controls. 544 return appearance != CheckboxPart && appearance != RadioPart; 545 } 546 547 static bool isBackgroundOrBorderStyled(const RenderStyle& style, const CachedUAStyle& uaStyle) 548 { 549 // Code below excludes the background-repeat from comparison by resetting it 550 FillLayer backgroundCopy = uaStyle.backgroundLayers; 551 FillLayer backgroundLayersCopy = style.backgroundLayers(); 552 backgroundCopy.setRepeatX(NoRepeatFill); 553 backgroundCopy.setRepeatY(NoRepeatFill); 554 backgroundLayersCopy.setRepeatX(NoRepeatFill); 555 backgroundLayersCopy.setRepeatY(NoRepeatFill); 556 // Test the style to see if the UA border and background match. 557 return style.border() != uaStyle.border 558 || backgroundLayersCopy != backgroundCopy 559 || style.visitedDependentColor(CSSPropertyBackgroundColor) != uaStyle.backgroundColor; 560 } 561 562 bool RenderTheme::isControlStyled(const RenderStyle* style, const CachedUAStyle* uaStyle) const 563 { 564 ASSERT(uaStyle); 565 566 switch (style->appearance()) { 567 case PushButtonPart: 568 case SquareButtonPart: 569 case ButtonPart: 570 case ProgressBarPart: 571 case MeterPart: 572 case RelevancyLevelIndicatorPart: 573 case ContinuousCapacityLevelIndicatorPart: 574 case DiscreteCapacityLevelIndicatorPart: 575 case RatingLevelIndicatorPart: 576 return isBackgroundOrBorderStyled(*style, *uaStyle); 577 578 case MenulistPart: 579 case SearchFieldPart: 580 case TextAreaPart: 581 case TextFieldPart: 582 return isBackgroundOrBorderStyled(*style, *uaStyle) || style->boxShadow(); 583 584 case SliderHorizontalPart: 585 case SliderVerticalPart: 586 return style->boxShadow(); 587 588 default: 589 return false; 590 } 591 } 592 593 void RenderTheme::adjustPaintInvalidationRect(const RenderObject* o, IntRect& r) 594 { 595 #if USE(NEW_THEME) 596 m_platformTheme->inflateControlPaintRect(o->style()->appearance(), controlStatesForRenderer(o), r, o->style()->effectiveZoom()); 597 #endif 598 } 599 600 bool RenderTheme::shouldDrawDefaultFocusRing(RenderObject* renderer) const 601 { 602 if (supportsFocusRing(renderer->style())) 603 return false; 604 Node* node = renderer->node(); 605 if (!node) 606 return true; 607 if (!renderer->style()->hasAppearance() && !node->isLink()) 608 return true; 609 // We can't use RenderTheme::isFocused because outline:auto might be 610 // specified to non-:focus rulesets. 611 if (node->focused() && !node->shouldHaveFocusAppearance()) 612 return false; 613 return true; 614 } 615 616 bool RenderTheme::supportsFocusRing(const RenderStyle* style) const 617 { 618 return (style->hasAppearance() && style->appearance() != TextFieldPart && style->appearance() != TextAreaPart && style->appearance() != MenulistButtonPart && style->appearance() != ListboxPart); 619 } 620 621 bool RenderTheme::stateChanged(RenderObject* o, ControlState state) const 622 { 623 // Default implementation assumes the controls don't respond to changes in :hover state 624 if (state == HoverControlState && !supportsHover(o->style())) 625 return false; 626 627 // Assume pressed state is only responded to if the control is enabled. 628 if (state == PressedControlState && !isEnabled(o)) 629 return false; 630 631 o->setShouldDoFullPaintInvalidation(true); 632 return true; 633 } 634 635 ControlStates RenderTheme::controlStatesForRenderer(const RenderObject* o) const 636 { 637 ControlStates result = 0; 638 if (isHovered(o)) { 639 result |= HoverControlState; 640 if (isSpinUpButtonPartHovered(o)) 641 result |= SpinUpControlState; 642 } 643 if (isPressed(o)) { 644 result |= PressedControlState; 645 if (isSpinUpButtonPartPressed(o)) 646 result |= SpinUpControlState; 647 } 648 if (isFocused(o) && o->style()->outlineStyleIsAuto()) 649 result |= FocusControlState; 650 if (isEnabled(o)) 651 result |= EnabledControlState; 652 if (isChecked(o)) 653 result |= CheckedControlState; 654 if (isReadOnlyControl(o)) 655 result |= ReadOnlyControlState; 656 if (!isActive(o)) 657 result |= WindowInactiveControlState; 658 if (isIndeterminate(o)) 659 result |= IndeterminateControlState; 660 return result; 661 } 662 663 bool RenderTheme::isActive(const RenderObject* o) const 664 { 665 Node* node = o->node(); 666 if (!node) 667 return false; 668 669 Page* page = node->document().page(); 670 if (!page) 671 return false; 672 673 return page->focusController().isActive(); 674 } 675 676 bool RenderTheme::isChecked(const RenderObject* o) const 677 { 678 if (!isHTMLInputElement(o->node())) 679 return false; 680 return toHTMLInputElement(o->node())->shouldAppearChecked(); 681 } 682 683 bool RenderTheme::isIndeterminate(const RenderObject* o) const 684 { 685 if (!isHTMLInputElement(o->node())) 686 return false; 687 return toHTMLInputElement(o->node())->shouldAppearIndeterminate(); 688 } 689 690 bool RenderTheme::isEnabled(const RenderObject* o) const 691 { 692 Node* node = o->node(); 693 if (!node || !node->isElementNode()) 694 return true; 695 return !toElement(node)->isDisabledFormControl(); 696 } 697 698 bool RenderTheme::isFocused(const RenderObject* o) const 699 { 700 Node* node = o->node(); 701 if (!node) 702 return false; 703 704 node = node->focusDelegate(); 705 Document& document = node->document(); 706 LocalFrame* frame = document.frame(); 707 return node == document.focusedElement() && node->focused() && node->shouldHaveFocusAppearance() && frame && frame->selection().isFocusedAndActive(); 708 } 709 710 bool RenderTheme::isPressed(const RenderObject* o) const 711 { 712 if (!o->node()) 713 return false; 714 return o->node()->active(); 715 } 716 717 bool RenderTheme::isSpinUpButtonPartPressed(const RenderObject* o) const 718 { 719 Node* node = o->node(); 720 if (!node || !node->active() || !node->isElementNode() 721 || !toElement(node)->isSpinButtonElement()) 722 return false; 723 SpinButtonElement* element = toSpinButtonElement(node); 724 return element->upDownState() == SpinButtonElement::Up; 725 } 726 727 bool RenderTheme::isReadOnlyControl(const RenderObject* o) const 728 { 729 Node* node = o->node(); 730 if (!node || !node->isElementNode() || !toElement(node)->isFormControlElement()) 731 return false; 732 HTMLFormControlElement* element = toHTMLFormControlElement(node); 733 return element->isReadOnly(); 734 } 735 736 bool RenderTheme::isHovered(const RenderObject* o) const 737 { 738 Node* node = o->node(); 739 if (!node) 740 return false; 741 if (!node->isElementNode() || !toElement(node)->isSpinButtonElement()) 742 return node->hovered(); 743 SpinButtonElement* element = toSpinButtonElement(node); 744 return element->hovered() && element->upDownState() != SpinButtonElement::Indeterminate; 745 } 746 747 bool RenderTheme::isSpinUpButtonPartHovered(const RenderObject* o) const 748 { 749 Node* node = o->node(); 750 if (!node || !node->isElementNode() || !toElement(node)->isSpinButtonElement()) 751 return false; 752 SpinButtonElement* element = toSpinButtonElement(node); 753 return element->upDownState() == SpinButtonElement::Up; 754 } 755 756 #if !USE(NEW_THEME) 757 758 void RenderTheme::adjustCheckboxStyle(RenderStyle* style, Element*) const 759 { 760 // A summary of the rules for checkbox designed to match WinIE: 761 // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.) 762 // font-size - not honored (control has no text), but we use it to decide which control size to use. 763 setCheckboxSize(style); 764 765 // padding - not honored by WinIE, needs to be removed. 766 style->resetPadding(); 767 768 // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme) 769 // for now, we will not honor it. 770 style->resetBorder(); 771 } 772 773 void RenderTheme::adjustRadioStyle(RenderStyle* style, Element*) const 774 { 775 // A summary of the rules for checkbox designed to match WinIE: 776 // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.) 777 // font-size - not honored (control has no text), but we use it to decide which control size to use. 778 setRadioSize(style); 779 780 // padding - not honored by WinIE, needs to be removed. 781 style->resetPadding(); 782 783 // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme) 784 // for now, we will not honor it. 785 style->resetBorder(); 786 } 787 788 void RenderTheme::adjustButtonStyle(RenderStyle* style, Element*) const 789 { 790 } 791 792 void RenderTheme::adjustInnerSpinButtonStyle(RenderStyle*, Element*) const 793 { 794 } 795 #endif 796 797 void RenderTheme::adjustMenuListStyle(RenderStyle*, Element*) const 798 { 799 } 800 801 IntSize RenderTheme::meterSizeForBounds(const RenderMeter*, const IntRect& bounds) const 802 { 803 return bounds.size(); 804 } 805 806 bool RenderTheme::supportsMeter(ControlPart) const 807 { 808 return false; 809 } 810 811 bool RenderTheme::paintMeter(RenderObject*, const PaintInfo&, const IntRect&) 812 { 813 return true; 814 } 815 816 void RenderTheme::paintSliderTicks(RenderObject* o, const PaintInfo& paintInfo, const IntRect& rect) 817 { 818 Node* node = o->node(); 819 if (!isHTMLInputElement(node)) 820 return; 821 822 HTMLInputElement* input = toHTMLInputElement(node); 823 if (input->type() != InputTypeNames::range) 824 return; 825 826 HTMLDataListElement* dataList = input->dataList(); 827 if (!dataList) 828 return; 829 830 double min = input->minimum(); 831 double max = input->maximum(); 832 ControlPart part = o->style()->appearance(); 833 // We don't support ticks on alternate sliders like MediaVolumeSliders. 834 if (part != SliderHorizontalPart && part != SliderVerticalPart) 835 return; 836 bool isHorizontal = part == SliderHorizontalPart; 837 838 IntSize thumbSize; 839 RenderObject* thumbRenderer = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderThumb())->renderer(); 840 if (thumbRenderer) { 841 RenderStyle* thumbStyle = thumbRenderer->style(); 842 int thumbWidth = thumbStyle->width().intValue(); 843 int thumbHeight = thumbStyle->height().intValue(); 844 thumbSize.setWidth(isHorizontal ? thumbWidth : thumbHeight); 845 thumbSize.setHeight(isHorizontal ? thumbHeight : thumbWidth); 846 } 847 848 IntSize tickSize = sliderTickSize(); 849 float zoomFactor = o->style()->effectiveZoom(); 850 FloatRect tickRect; 851 int tickRegionSideMargin = 0; 852 int tickRegionWidth = 0; 853 IntRect trackBounds; 854 RenderObject* trackRenderer = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderTrack())->renderer(); 855 // We can ignoring transforms because transform is handled by the graphics context. 856 if (trackRenderer) 857 trackBounds = trackRenderer->absoluteBoundingBoxRectIgnoringTransforms(); 858 IntRect sliderBounds = o->absoluteBoundingBoxRectIgnoringTransforms(); 859 860 // Make position relative to the transformed ancestor element. 861 trackBounds.setX(trackBounds.x() - sliderBounds.x() + rect.x()); 862 trackBounds.setY(trackBounds.y() - sliderBounds.y() + rect.y()); 863 864 if (isHorizontal) { 865 tickRect.setWidth(floor(tickSize.width() * zoomFactor)); 866 tickRect.setHeight(floor(tickSize.height() * zoomFactor)); 867 tickRect.setY(floor(rect.y() + rect.height() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor)); 868 tickRegionSideMargin = trackBounds.x() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0; 869 tickRegionWidth = trackBounds.width() - thumbSize.width(); 870 } else { 871 tickRect.setWidth(floor(tickSize.height() * zoomFactor)); 872 tickRect.setHeight(floor(tickSize.width() * zoomFactor)); 873 tickRect.setX(floor(rect.x() + rect.width() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor)); 874 tickRegionSideMargin = trackBounds.y() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0; 875 tickRegionWidth = trackBounds.height() - thumbSize.width(); 876 } 877 RefPtrWillBeRawPtr<HTMLDataListOptionsCollection> options = dataList->options(); 878 GraphicsContextStateSaver stateSaver(*paintInfo.context); 879 paintInfo.context->setFillColor(o->resolveColor(CSSPropertyColor)); 880 for (unsigned i = 0; HTMLOptionElement* optionElement = options->item(i); i++) { 881 String value = optionElement->value(); 882 if (!input->isValidValue(value)) 883 continue; 884 double parsedValue = parseToDoubleForNumberType(input->sanitizeValue(value)); 885 double tickFraction = (parsedValue - min) / (max - min); 886 double tickRatio = isHorizontal && o->style()->isLeftToRightDirection() ? tickFraction : 1.0 - tickFraction; 887 double tickPosition = round(tickRegionSideMargin + tickRegionWidth * tickRatio); 888 if (isHorizontal) 889 tickRect.setX(tickPosition); 890 else 891 tickRect.setY(tickPosition); 892 paintInfo.context->fillRect(tickRect); 893 } 894 } 895 896 double RenderTheme::animationRepeatIntervalForProgressBar(RenderProgress*) const 897 { 898 return 0; 899 } 900 901 double RenderTheme::animationDurationForProgressBar(RenderProgress*) const 902 { 903 return 0; 904 } 905 906 bool RenderTheme::shouldHaveSpinButton(HTMLInputElement* inputElement) const 907 { 908 return inputElement->isSteppable() && inputElement->type() != InputTypeNames::range; 909 } 910 911 void RenderTheme::adjustMenuListButtonStyle(RenderStyle*, Element*) const 912 { 913 } 914 915 void RenderTheme::adjustSliderThumbStyle(RenderStyle* style, Element* element) const 916 { 917 adjustSliderThumbSize(style, element); 918 } 919 920 void RenderTheme::adjustSliderThumbSize(RenderStyle*, Element*) const 921 { 922 } 923 924 void RenderTheme::adjustSearchFieldStyle(RenderStyle*, Element*) const 925 { 926 } 927 928 void RenderTheme::adjustSearchFieldCancelButtonStyle(RenderStyle*, Element*) const 929 { 930 } 931 932 void RenderTheme::adjustSearchFieldDecorationStyle(RenderStyle*, Element*) const 933 { 934 } 935 936 void RenderTheme::adjustSearchFieldResultsDecorationStyle(RenderStyle*, Element*) const 937 { 938 } 939 940 void RenderTheme::platformColorsDidChange() 941 { 942 Page::scheduleForcedStyleRecalcForAllPages(); 943 } 944 945 Color RenderTheme::systemColor(CSSValueID cssValueId) const 946 { 947 switch (cssValueId) { 948 case CSSValueActiveborder: 949 return 0xFFFFFFFF; 950 case CSSValueActivecaption: 951 return 0xFFCCCCCC; 952 case CSSValueAppworkspace: 953 return 0xFFFFFFFF; 954 case CSSValueBackground: 955 return 0xFF6363CE; 956 case CSSValueButtonface: 957 return 0xFFC0C0C0; 958 case CSSValueButtonhighlight: 959 return 0xFFDDDDDD; 960 case CSSValueButtonshadow: 961 return 0xFF888888; 962 case CSSValueButtontext: 963 return 0xFF000000; 964 case CSSValueCaptiontext: 965 return 0xFF000000; 966 case CSSValueGraytext: 967 return 0xFF808080; 968 case CSSValueHighlight: 969 return 0xFFB5D5FF; 970 case CSSValueHighlighttext: 971 return 0xFF000000; 972 case CSSValueInactiveborder: 973 return 0xFFFFFFFF; 974 case CSSValueInactivecaption: 975 return 0xFFFFFFFF; 976 case CSSValueInactivecaptiontext: 977 return 0xFF7F7F7F; 978 case CSSValueInfobackground: 979 return 0xFFFBFCC5; 980 case CSSValueInfotext: 981 return 0xFF000000; 982 case CSSValueMenu: 983 return 0xFFC0C0C0; 984 case CSSValueMenutext: 985 return 0xFF000000; 986 case CSSValueScrollbar: 987 return 0xFFFFFFFF; 988 case CSSValueText: 989 return 0xFF000000; 990 case CSSValueThreeddarkshadow: 991 return 0xFF666666; 992 case CSSValueThreedface: 993 return 0xFFC0C0C0; 994 case CSSValueThreedhighlight: 995 return 0xFFDDDDDD; 996 case CSSValueThreedlightshadow: 997 return 0xFFC0C0C0; 998 case CSSValueThreedshadow: 999 return 0xFF888888; 1000 case CSSValueWindow: 1001 return 0xFFFFFFFF; 1002 case CSSValueWindowframe: 1003 return 0xFFCCCCCC; 1004 case CSSValueWindowtext: 1005 return 0xFF000000; 1006 case CSSValueInternalActiveListBoxSelection: 1007 return activeListBoxSelectionBackgroundColor(); 1008 break; 1009 case CSSValueInternalActiveListBoxSelectionText: 1010 return activeListBoxSelectionForegroundColor(); 1011 break; 1012 case CSSValueInternalInactiveListBoxSelection: 1013 return inactiveListBoxSelectionBackgroundColor(); 1014 break; 1015 case CSSValueInternalInactiveListBoxSelectionText: 1016 return inactiveListBoxSelectionForegroundColor(); 1017 break; 1018 default: 1019 break; 1020 } 1021 ASSERT_NOT_REACHED(); 1022 return Color(); 1023 } 1024 1025 Color RenderTheme::platformActiveTextSearchHighlightColor() const 1026 { 1027 return Color(255, 150, 50); // Orange. 1028 } 1029 1030 Color RenderTheme::platformInactiveTextSearchHighlightColor() const 1031 { 1032 return Color(255, 255, 0); // Yellow. 1033 } 1034 1035 Color RenderTheme::tapHighlightColor() 1036 { 1037 return theme().platformTapHighlightColor(); 1038 } 1039 1040 void RenderTheme::setCustomFocusRingColor(const Color& c) 1041 { 1042 m_customFocusRingColor = c; 1043 m_hasCustomFocusRingColor = true; 1044 } 1045 1046 Color RenderTheme::focusRingColor() const 1047 { 1048 return m_hasCustomFocusRingColor ? m_customFocusRingColor : theme().platformFocusRingColor(); 1049 } 1050 1051 String RenderTheme::fileListNameForWidth(Locale& locale, const FileList* fileList, const Font& font, int width) const 1052 { 1053 if (width <= 0) 1054 return String(); 1055 1056 String string; 1057 if (fileList->isEmpty()) { 1058 string = locale.queryString(WebLocalizedString::FileButtonNoFileSelectedLabel); 1059 } else if (fileList->length() == 1) { 1060 string = fileList->item(0)->name(); 1061 } else { 1062 // FIXME: Localization of fileList->length(). 1063 return StringTruncator::rightTruncate(locale.queryString(WebLocalizedString::MultipleFileUploadText, String::number(fileList->length())), width, font); 1064 } 1065 1066 return StringTruncator::centerTruncate(string, width, font); 1067 } 1068 1069 bool RenderTheme::shouldOpenPickerWithF4Key() const 1070 { 1071 return false; 1072 } 1073 1074 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 1075 bool RenderTheme::supportsCalendarPicker(const AtomicString& type) const 1076 { 1077 return type == InputTypeNames::date 1078 || type == InputTypeNames::datetime 1079 || type == InputTypeNames::datetime_local 1080 || type == InputTypeNames::month 1081 || type == InputTypeNames::week; 1082 } 1083 #endif 1084 1085 bool RenderTheme::shouldUseFallbackTheme(RenderStyle*) const 1086 { 1087 return false; 1088 } 1089 1090 void RenderTheme::adjustStyleUsingFallbackTheme(RenderStyle* style, Element* e) 1091 { 1092 ControlPart part = style->appearance(); 1093 switch (part) { 1094 case CheckboxPart: 1095 return adjustCheckboxStyleUsingFallbackTheme(style, e); 1096 case RadioPart: 1097 return adjustRadioStyleUsingFallbackTheme(style, e); 1098 default: 1099 break; 1100 } 1101 } 1102 1103 bool RenderTheme::paintUsingFallbackTheme(RenderObject* o, const PaintInfo& i, const IntRect& r) 1104 { 1105 ControlPart part = o->style()->appearance(); 1106 switch (part) { 1107 case CheckboxPart: 1108 return paintCheckboxUsingFallbackTheme(o, i, r); 1109 case RadioPart: 1110 return paintRadioUsingFallbackTheme(o, i, r); 1111 default: 1112 break; 1113 } 1114 return true; 1115 } 1116 1117 // static 1118 void RenderTheme::setSizeIfAuto(RenderStyle* style, const IntSize& size) 1119 { 1120 if (style->width().isIntrinsicOrAuto()) 1121 style->setWidth(Length(size.width(), Fixed)); 1122 if (style->height().isAuto()) 1123 style->setHeight(Length(size.height(), Fixed)); 1124 } 1125 1126 bool RenderTheme::paintCheckboxUsingFallbackTheme(RenderObject* o, const PaintInfo& i, const IntRect& r) 1127 { 1128 WebFallbackThemeEngine::ExtraParams extraParams; 1129 WebCanvas* canvas = i.context->canvas(); 1130 extraParams.button.checked = isChecked(o); 1131 extraParams.button.indeterminate = isIndeterminate(o); 1132 1133 float zoomLevel = o->style()->effectiveZoom(); 1134 GraphicsContextStateSaver stateSaver(*i.context); 1135 IntRect unzoomedRect = r; 1136 if (zoomLevel != 1) { 1137 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); 1138 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); 1139 i.context->translate(unzoomedRect.x(), unzoomedRect.y()); 1140 i.context->scale(zoomLevel, zoomLevel); 1141 i.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); 1142 } 1143 1144 Platform::current()->fallbackThemeEngine()->paint(canvas, WebFallbackThemeEngine::PartCheckbox, getWebFallbackThemeState(this, o), WebRect(unzoomedRect), &extraParams); 1145 return false; 1146 } 1147 1148 void RenderTheme::adjustCheckboxStyleUsingFallbackTheme(RenderStyle* style, Element*) const 1149 { 1150 // If the width and height are both specified, then we have nothing to do. 1151 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 1152 return; 1153 1154 IntSize size = Platform::current()->fallbackThemeEngine()->getSize(WebFallbackThemeEngine::PartCheckbox); 1155 float zoomLevel = style->effectiveZoom(); 1156 size.setWidth(size.width() * zoomLevel); 1157 size.setHeight(size.height() * zoomLevel); 1158 setSizeIfAuto(style, size); 1159 1160 // padding - not honored by WinIE, needs to be removed. 1161 style->resetPadding(); 1162 1163 // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme) 1164 // for now, we will not honor it. 1165 style->resetBorder(); 1166 } 1167 1168 bool RenderTheme::paintRadioUsingFallbackTheme(RenderObject* o, const PaintInfo& i, const IntRect& r) 1169 { 1170 WebFallbackThemeEngine::ExtraParams extraParams; 1171 WebCanvas* canvas = i.context->canvas(); 1172 extraParams.button.checked = isChecked(o); 1173 extraParams.button.indeterminate = isIndeterminate(o); 1174 1175 float zoomLevel = o->style()->effectiveZoom(); 1176 GraphicsContextStateSaver stateSaver(*i.context); 1177 IntRect unzoomedRect = r; 1178 if (zoomLevel != 1) { 1179 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); 1180 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); 1181 i.context->translate(unzoomedRect.x(), unzoomedRect.y()); 1182 i.context->scale(zoomLevel, zoomLevel); 1183 i.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); 1184 } 1185 1186 Platform::current()->fallbackThemeEngine()->paint(canvas, WebFallbackThemeEngine::PartRadio, getWebFallbackThemeState(this, o), WebRect(unzoomedRect), &extraParams); 1187 return false; 1188 } 1189 1190 void RenderTheme::adjustRadioStyleUsingFallbackTheme(RenderStyle* style, Element*) const 1191 { 1192 // If the width and height are both specified, then we have nothing to do. 1193 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 1194 return; 1195 1196 IntSize size = Platform::current()->fallbackThemeEngine()->getSize(WebFallbackThemeEngine::PartRadio); 1197 float zoomLevel = style->effectiveZoom(); 1198 size.setWidth(size.width() * zoomLevel); 1199 size.setHeight(size.height() * zoomLevel); 1200 setSizeIfAuto(style, size); 1201 1202 // padding - not honored by WinIE, needs to be removed. 1203 style->resetPadding(); 1204 1205 // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme) 1206 // for now, we will not honor it. 1207 style->resetBorder(); 1208 } 1209 1210 } // namespace blink 1211