1/*
2 * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
3 * Copyright (C) 2014 Google Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "RenderTheme.h"
23
24#include "CSSValueKeywords.h"
25#include "ControlStates.h"
26#include "Document.h"
27#include "FileList.h"
28#include "FloatConversion.h"
29#include "FloatRoundedRect.h"
30#include "FocusController.h"
31#include "FontSelector.h"
32#include "Frame.h"
33#include "FrameSelection.h"
34#include "GraphicsContext.h"
35#include "HTMLInputElement.h"
36#include "HTMLNames.h"
37#include "LocalizedStrings.h"
38#include "MediaControlElements.h"
39#include "Page.h"
40#include "PaintInfo.h"
41#include "RenderStyle.h"
42#include "RenderView.h"
43#include "RuntimeEnabledFeatures.h"
44#include "SpinButtonElement.h"
45#include "StringTruncator.h"
46#include "TextControlInnerElements.h"
47#include <wtf/FileSystem.h>
48#include <wtf/NeverDestroyed.h>
49#include <wtf/text/StringConcatenateNumbers.h>
50
51#if ENABLE(METER_ELEMENT)
52#include "HTMLMeterElement.h"
53#include "RenderMeter.h"
54#endif
55
56#if ENABLE(DATALIST_ELEMENT)
57#include "HTMLCollection.h"
58#include "HTMLDataListElement.h"
59#include "HTMLOptionElement.h"
60#include "HTMLParserIdioms.h"
61#endif
62
63#if USE(NEW_THEME)
64#include "Theme.h"
65#endif
66
67namespace WebCore {
68
69using namespace HTMLNames;
70
71static Color& customFocusRingColor()
72{
73 static NeverDestroyed<Color> color;
74 return color;
75}
76
77RenderTheme::RenderTheme()
78{
79}
80
81void RenderTheme::adjustStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* element, bool UAHasAppearance, const BorderData& border, const FillLayer& background, const Color& backgroundColor)
82{
83 // Force inline and table display styles to be inline-block (except for table- which is block)
84 ControlPart part = style.appearance();
85 if (style.display() == DisplayType::Inline || style.display() == DisplayType::InlineTable || style.display() == DisplayType::TableRowGroup
86 || style.display() == DisplayType::TableHeaderGroup || style.display() == DisplayType::TableFooterGroup
87 || style.display() == DisplayType::TableRow || style.display() == DisplayType::TableColumnGroup || style.display() == DisplayType::TableColumn
88 || style.display() == DisplayType::TableCell || style.display() == DisplayType::TableCaption)
89 style.setDisplay(DisplayType::InlineBlock);
90 else if (style.display() == DisplayType::Compact || style.display() == DisplayType::ListItem || style.display() == DisplayType::Table)
91 style.setDisplay(DisplayType::Block);
92
93 if (UAHasAppearance && isControlStyled(style, border, background, backgroundColor)) {
94 switch (part) {
95 case MenulistPart:
96 style.setAppearance(MenulistButtonPart);
97 part = MenulistButtonPart;
98 break;
99 case TextFieldPart:
100 adjustTextFieldStyle(styleResolver, style, element);
101 FALLTHROUGH;
102 default:
103 style.setAppearance(NoControlPart);
104 break;
105 }
106 }
107
108 if (!style.hasAppearance())
109 return;
110
111 // Never support box-shadow on native controls.
112 style.setBoxShadow(nullptr);
113
114#if USE(NEW_THEME)
115 switch (part) {
116 case CheckboxPart:
117 case InnerSpinButtonPart:
118 case RadioPart:
119 case PushButtonPart:
120 case SquareButtonPart:
121#if ENABLE(INPUT_TYPE_COLOR)
122 case ColorWellPart:
123#endif
124 case DefaultButtonPart:
125 case ButtonPart: {
126 // Border
127 LengthBox borderBox(style.borderTopWidth(), style.borderRightWidth(), style.borderBottomWidth(), style.borderLeftWidth());
128 borderBox = Theme::singleton().controlBorder(part, style.fontCascade(), 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 = Theme::singleton().controlPadding(part, style.fontCascade(), style.paddingBox(), style.effectiveZoom());
158 if (paddingBox != style.paddingBox())
159 style.setPaddingBox(WTFMove(paddingBox));
160
161 // Whitespace
162 if (Theme::singleton().controlRequiresPreWhiteSpace(part))
163 style.setWhiteSpace(WhiteSpace::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 = Theme::singleton().controlSize(part, style.fontCascade(), { style.width(), style.height() }, style.effectiveZoom());
169 if (controlSize.width != style.width())
170 style.setWidth(WTFMove(controlSize.width));
171 if (controlSize.height != style.height())
172 style.setHeight(WTFMove(controlSize.height));
173
174 // Min-Width / Min-Height
175 LengthSize minControlSize = Theme::singleton().minimumControlSize(part, style.fontCascade(), style.effectiveZoom());
176 if (minControlSize.width != style.minWidth())
177 style.setMinWidth(WTFMove(minControlSize.width));
178 if (minControlSize.height != style.minHeight())
179 style.setMinHeight(WTFMove(minControlSize.height));
180
181 // Font
182 if (auto themeFont = Theme::singleton().controlFont(part, style.fontCascade(), style.effectiveZoom())) {
183 // If overriding the specified font with the theme font, also override the line height with the standard line height.
184 style.setLineHeight(RenderStyle::initialLineHeight());
185 if (style.setFontDescription(WTFMove(themeFont.value())))
186 style.fontCascade().update(nullptr);
187 }
188
189 // Special style that tells enabled default buttons in active windows to use the ActiveButtonText color.
190 // The active window part of the test has to be done at paint time since it's not triggered by a style change.
191 style.setInsideDefaultButton(part == DefaultButtonPart && element && !element->isDisabledFormControl());
192 break;
193 }
194 default:
195 break;
196 }
197#endif
198
199 // Call the appropriate style adjustment method based off the appearance value.
200 switch (style.appearance()) {
201#if !USE(NEW_THEME)
202 case CheckboxPart:
203 return adjustCheckboxStyle(styleResolver, style, element);
204 case RadioPart:
205 return adjustRadioStyle(styleResolver, style, element);
206 case PushButtonPart:
207 case SquareButtonPart:
208#if ENABLE(INPUT_TYPE_COLOR)
209 case ColorWellPart:
210#endif
211 case DefaultButtonPart:
212 case ButtonPart:
213 return adjustButtonStyle(styleResolver, style, element);
214 case InnerSpinButtonPart:
215 return adjustInnerSpinButtonStyle(styleResolver, style, element);
216#endif
217 case TextFieldPart:
218 return adjustTextFieldStyle(styleResolver, style, element);
219 case TextAreaPart:
220 return adjustTextAreaStyle(styleResolver, style, element);
221 case MenulistPart:
222 return adjustMenuListStyle(styleResolver, style, element);
223 case MenulistButtonPart:
224 return adjustMenuListButtonStyle(styleResolver, style, element);
225 case MediaPlayButtonPart:
226 case MediaCurrentTimePart:
227 case MediaTimeRemainingPart:
228 case MediaEnterFullscreenButtonPart:
229 case MediaExitFullscreenButtonPart:
230 case MediaMuteButtonPart:
231 case MediaVolumeSliderContainerPart:
232 return adjustMediaControlStyle(styleResolver, style, element);
233 case MediaSliderPart:
234 case MediaVolumeSliderPart:
235 case MediaFullScreenVolumeSliderPart:
236 case SliderHorizontalPart:
237 case SliderVerticalPart:
238 return adjustSliderTrackStyle(styleResolver, style, element);
239 case SliderThumbHorizontalPart:
240 case SliderThumbVerticalPart:
241 return adjustSliderThumbStyle(styleResolver, style, element);
242 case SearchFieldPart:
243 return adjustSearchFieldStyle(styleResolver, style, element);
244 case SearchFieldCancelButtonPart:
245 return adjustSearchFieldCancelButtonStyle(styleResolver, style, element);
246 case SearchFieldDecorationPart:
247 return adjustSearchFieldDecorationPartStyle(styleResolver, style, element);
248 case SearchFieldResultsDecorationPart:
249 return adjustSearchFieldResultsDecorationPartStyle(styleResolver, style, element);
250 case SearchFieldResultsButtonPart:
251 return adjustSearchFieldResultsButtonStyle(styleResolver, style, element);
252 case ProgressBarPart:
253 return adjustProgressBarStyle(styleResolver, style, element);
254#if ENABLE(METER_ELEMENT)
255 case MeterPart:
256 case RelevancyLevelIndicatorPart:
257 case ContinuousCapacityLevelIndicatorPart:
258 case DiscreteCapacityLevelIndicatorPart:
259 case RatingLevelIndicatorPart:
260 return adjustMeterStyle(styleResolver, style, element);
261#endif
262#if ENABLE(SERVICE_CONTROLS)
263 case ImageControlsButtonPart:
264 break;
265#endif
266 case CapsLockIndicatorPart:
267 return adjustCapsLockIndicatorStyle(styleResolver, style, element);
268#if ENABLE(APPLE_PAY)
269 case ApplePayButtonPart:
270 return adjustApplePayButtonStyle(styleResolver, style, element);
271#endif
272#if ENABLE(ATTACHMENT_ELEMENT)
273 case AttachmentPart:
274 case BorderlessAttachmentPart:
275 return adjustAttachmentStyle(styleResolver, style, element);
276#endif
277#if ENABLE(DATALIST_ELEMENT)
278 case ListButtonPart:
279 return adjustListButtonStyle(styleResolver, style, element);
280#endif
281 default:
282 break;
283 }
284}
285
286bool RenderTheme::paint(const RenderBox& box, ControlStates& controlStates, const PaintInfo& paintInfo, const LayoutRect& rect)
287{
288 // If painting is disabled, but we aren't updating control tints, then just bail.
289 // If we are updating control tints, just schedule a repaint if the theme supports tinting
290 // for that control.
291 if (paintInfo.context().invalidatingControlTints()) {
292 if (controlSupportsTints(box))
293 box.repaint();
294 return false;
295 }
296 if (paintInfo.context().paintingDisabled())
297 return false;
298
299 if (UNLIKELY(!paintInfo.context().hasPlatformContext()))
300 return false;
301
302 ControlPart part = box.style().appearance();
303 IntRect integralSnappedRect = snappedIntRect(rect);
304 float deviceScaleFactor = box.document().deviceScaleFactor();
305 FloatRect devicePixelSnappedRect = snapRectToDevicePixels(rect, deviceScaleFactor);
306
307#if USE(NEW_THEME)
308 float pageScaleFactor = box.page().pageScaleFactor();
309
310 switch (part) {
311 case CheckboxPart:
312 case RadioPart:
313 case PushButtonPart:
314 case SquareButtonPart:
315#if ENABLE(INPUT_TYPE_COLOR)
316 case ColorWellPart:
317#endif
318 case DefaultButtonPart:
319 case ButtonPart:
320 case InnerSpinButtonPart:
321 updateControlStatesForRenderer(box, controlStates);
322 Theme::singleton().paint(part, controlStates, paintInfo.context(), devicePixelSnappedRect, box.style().effectiveZoom(), &box.view().frameView(), deviceScaleFactor, pageScaleFactor, box.document().useSystemAppearance(), box.useDarkAppearance());
323 return false;
324 default:
325 break;
326 }
327#else
328 UNUSED_PARAM(controlStates);
329#endif
330
331 // Call the appropriate paint method based off the appearance value.
332 switch (part) {
333#if !USE(NEW_THEME)
334 case CheckboxPart:
335 return paintCheckbox(box, paintInfo, integralSnappedRect);
336 case RadioPart:
337 return paintRadio(box, paintInfo, integralSnappedRect);
338 case PushButtonPart:
339 case SquareButtonPart:
340#if ENABLE(INPUT_TYPE_COLOR)
341 case ColorWellPart:
342#endif
343 case DefaultButtonPart:
344 case ButtonPart:
345 return paintButton(box, paintInfo, integralSnappedRect);
346 case InnerSpinButtonPart:
347 return paintInnerSpinButton(box, paintInfo, integralSnappedRect);
348#endif
349 case MenulistPart:
350 return paintMenuList(box, paintInfo, devicePixelSnappedRect);
351#if ENABLE(METER_ELEMENT)
352 case MeterPart:
353 case RelevancyLevelIndicatorPart:
354 case ContinuousCapacityLevelIndicatorPart:
355 case DiscreteCapacityLevelIndicatorPart:
356 case RatingLevelIndicatorPart:
357 return paintMeter(box, paintInfo, integralSnappedRect);
358#endif
359 case ProgressBarPart:
360 return paintProgressBar(box, paintInfo, integralSnappedRect);
361 case SliderHorizontalPart:
362 case SliderVerticalPart:
363 return paintSliderTrack(box, paintInfo, integralSnappedRect);
364 case SliderThumbHorizontalPart:
365 case SliderThumbVerticalPart:
366 return paintSliderThumb(box, paintInfo, integralSnappedRect);
367 case MediaEnterFullscreenButtonPart:
368 case MediaExitFullscreenButtonPart:
369 return paintMediaFullscreenButton(box, paintInfo, integralSnappedRect);
370 case MediaPlayButtonPart:
371 return paintMediaPlayButton(box, paintInfo, integralSnappedRect);
372 case MediaOverlayPlayButtonPart:
373 return paintMediaOverlayPlayButton(box, paintInfo, integralSnappedRect);
374 case MediaMuteButtonPart:
375 return paintMediaMuteButton(box, paintInfo, integralSnappedRect);
376 case MediaSeekBackButtonPart:
377 return paintMediaSeekBackButton(box, paintInfo, integralSnappedRect);
378 case MediaSeekForwardButtonPart:
379 return paintMediaSeekForwardButton(box, paintInfo, integralSnappedRect);
380 case MediaRewindButtonPart:
381 return paintMediaRewindButton(box, paintInfo, integralSnappedRect);
382 case MediaReturnToRealtimeButtonPart:
383 return paintMediaReturnToRealtimeButton(box, paintInfo, integralSnappedRect);
384 case MediaToggleClosedCaptionsButtonPart:
385 return paintMediaToggleClosedCaptionsButton(box, paintInfo, integralSnappedRect);
386 case MediaSliderPart:
387 return paintMediaSliderTrack(box, paintInfo, integralSnappedRect);
388 case MediaSliderThumbPart:
389 return paintMediaSliderThumb(box, paintInfo, integralSnappedRect);
390 case MediaVolumeSliderMuteButtonPart:
391 return paintMediaMuteButton(box, paintInfo, integralSnappedRect);
392 case MediaVolumeSliderContainerPart:
393 return paintMediaVolumeSliderContainer(box, paintInfo, integralSnappedRect);
394 case MediaVolumeSliderPart:
395 return paintMediaVolumeSliderTrack(box, paintInfo, integralSnappedRect);
396 case MediaVolumeSliderThumbPart:
397 return paintMediaVolumeSliderThumb(box, paintInfo, integralSnappedRect);
398 case MediaFullScreenVolumeSliderPart:
399 return paintMediaFullScreenVolumeSliderTrack(box, paintInfo, integralSnappedRect);
400 case MediaFullScreenVolumeSliderThumbPart:
401 return paintMediaFullScreenVolumeSliderThumb(box, paintInfo, integralSnappedRect);
402 case MediaTimeRemainingPart:
403 return paintMediaTimeRemaining(box, paintInfo, integralSnappedRect);
404 case MediaCurrentTimePart:
405 return paintMediaCurrentTime(box, paintInfo, integralSnappedRect);
406 case MediaControlsBackgroundPart:
407 return paintMediaControlsBackground(box, paintInfo, integralSnappedRect);
408 case MenulistButtonPart:
409 case TextFieldPart:
410 case TextAreaPart:
411 case ListboxPart:
412 return true;
413 case SearchFieldPart:
414 return paintSearchField(box, paintInfo, integralSnappedRect);
415 case SearchFieldCancelButtonPart:
416 return paintSearchFieldCancelButton(box, paintInfo, integralSnappedRect);
417 case SearchFieldDecorationPart:
418 return paintSearchFieldDecorationPart(box, paintInfo, integralSnappedRect);
419 case SearchFieldResultsDecorationPart:
420 return paintSearchFieldResultsDecorationPart(box, paintInfo, integralSnappedRect);
421 case SearchFieldResultsButtonPart:
422 return paintSearchFieldResultsButton(box, paintInfo, integralSnappedRect);
423 case SnapshottedPluginOverlayPart:
424 return paintSnapshottedPluginOverlay(box, paintInfo, integralSnappedRect);
425#if ENABLE(SERVICE_CONTROLS)
426 case ImageControlsButtonPart:
427 return paintImageControlsButton(box, paintInfo, integralSnappedRect);
428#endif
429 case CapsLockIndicatorPart:
430 return paintCapsLockIndicator(box, paintInfo, integralSnappedRect);
431#if ENABLE(APPLE_PAY)
432 case ApplePayButtonPart:
433 return paintApplePayButton(box, paintInfo, integralSnappedRect);
434#endif
435#if ENABLE(ATTACHMENT_ELEMENT)
436 case AttachmentPart:
437 case BorderlessAttachmentPart:
438 return paintAttachment(box, paintInfo, integralSnappedRect);
439#endif
440 default:
441 break;
442 }
443
444 return true; // We don't support the appearance, so let the normal background/border paint.
445}
446
447bool RenderTheme::paintBorderOnly(const RenderBox& box, const PaintInfo& paintInfo, const LayoutRect& rect)
448{
449 if (paintInfo.context().paintingDisabled())
450 return false;
451
452#if PLATFORM(IOS_FAMILY)
453 UNUSED_PARAM(rect);
454 return box.style().appearance() != NoControlPart;
455#else
456 FloatRect devicePixelSnappedRect = snapRectToDevicePixels(rect, box.document().deviceScaleFactor());
457 // Call the appropriate paint method based off the appearance value.
458 switch (box.style().appearance()) {
459 case TextFieldPart:
460 return paintTextField(box, paintInfo, devicePixelSnappedRect);
461 case ListboxPart:
462 case TextAreaPart:
463 return paintTextArea(box, paintInfo, devicePixelSnappedRect);
464 case MenulistButtonPart:
465 case SearchFieldPart:
466 return true;
467 case CheckboxPart:
468 case RadioPart:
469 case PushButtonPart:
470 case SquareButtonPart:
471#if ENABLE(INPUT_TYPE_COLOR)
472 case ColorWellPart:
473#endif
474 case DefaultButtonPart:
475 case ButtonPart:
476 case MenulistPart:
477#if ENABLE(METER_ELEMENT)
478 case MeterPart:
479 case RelevancyLevelIndicatorPart:
480 case ContinuousCapacityLevelIndicatorPart:
481 case DiscreteCapacityLevelIndicatorPart:
482 case RatingLevelIndicatorPart:
483#endif
484 case ProgressBarPart:
485 case SliderHorizontalPart:
486 case SliderVerticalPart:
487 case SliderThumbHorizontalPart:
488 case SliderThumbVerticalPart:
489 case SearchFieldCancelButtonPart:
490 case SearchFieldDecorationPart:
491 case SearchFieldResultsDecorationPart:
492 case SearchFieldResultsButtonPart:
493#if ENABLE(SERVICE_CONTROLS)
494 case ImageControlsButtonPart:
495#endif
496 default:
497 break;
498 }
499
500 return false;
501#endif
502}
503
504bool RenderTheme::paintDecorations(const RenderBox& box, const PaintInfo& paintInfo, const LayoutRect& rect)
505{
506 if (paintInfo.context().paintingDisabled())
507 return false;
508
509 IntRect integralSnappedRect = snappedIntRect(rect);
510 FloatRect devicePixelSnappedRect = snapRectToDevicePixels(rect, box.document().deviceScaleFactor());
511
512 // Call the appropriate paint method based off the appearance value.
513 switch (box.style().appearance()) {
514 case MenulistButtonPart:
515 return paintMenuListButtonDecorations(box, paintInfo, devicePixelSnappedRect);
516 case TextFieldPart:
517 return paintTextFieldDecorations(box, paintInfo, devicePixelSnappedRect);
518 case TextAreaPart:
519 return paintTextAreaDecorations(box, paintInfo, devicePixelSnappedRect);
520 case CheckboxPart:
521 return paintCheckboxDecorations(box, paintInfo, integralSnappedRect);
522 case RadioPart:
523 return paintRadioDecorations(box, paintInfo, integralSnappedRect);
524 case PushButtonPart:
525 return paintPushButtonDecorations(box, paintInfo, integralSnappedRect);
526 case SquareButtonPart:
527 return paintSquareButtonDecorations(box, paintInfo, integralSnappedRect);
528#if ENABLE(INPUT_TYPE_COLOR)
529 case ColorWellPart:
530#endif
531 case ButtonPart:
532 return paintButtonDecorations(box, paintInfo, integralSnappedRect);
533 case MenulistPart:
534 return paintMenuListDecorations(box, paintInfo, integralSnappedRect);
535 case SliderThumbHorizontalPart:
536 case SliderThumbVerticalPart:
537 return paintSliderThumbDecorations(box, paintInfo, integralSnappedRect);
538 case SearchFieldPart:
539 return paintSearchFieldDecorations(box, paintInfo, integralSnappedRect);
540#if ENABLE(METER_ELEMENT)
541 case MeterPart:
542 case RelevancyLevelIndicatorPart:
543 case ContinuousCapacityLevelIndicatorPart:
544 case DiscreteCapacityLevelIndicatorPart:
545 case RatingLevelIndicatorPart:
546#endif
547 case ProgressBarPart:
548 case SliderHorizontalPart:
549 case SliderVerticalPart:
550 case ListboxPart:
551 case DefaultButtonPart:
552 case SearchFieldCancelButtonPart:
553 case SearchFieldDecorationPart:
554 case SearchFieldResultsDecorationPart:
555 case SearchFieldResultsButtonPart:
556#if ENABLE(SERVICE_CONTROLS)
557 case ImageControlsButtonPart:
558#endif
559 default:
560 break;
561 }
562
563 return false;
564}
565
566#if ENABLE(VIDEO)
567
568String RenderTheme::formatMediaControlsTime(float time) const
569{
570 if (!std::isfinite(time))
571 time = 0;
572 // FIXME: Seems like it would be better to use std::lround here.
573 int seconds = static_cast<int>(std::abs(time));
574 int hours = seconds / (60 * 60);
575 int minutes = (seconds / 60) % 60;
576 seconds %= 60;
577 if (hours)
578 return makeString((time < 0 ? "-" : ""), hours, ':', pad('0', 2, minutes), ':', pad('0', 2, seconds));
579 return makeString((time < 0 ? "-" : ""), pad('0', 2, minutes), ':', pad('0', 2, seconds));
580}
581
582String RenderTheme::formatMediaControlsCurrentTime(float currentTime, float /*duration*/) const
583{
584 return formatMediaControlsTime(currentTime);
585}
586
587String RenderTheme::formatMediaControlsRemainingTime(float currentTime, float duration) const
588{
589 return formatMediaControlsTime(currentTime - duration);
590}
591
592LayoutPoint RenderTheme::volumeSliderOffsetFromMuteButton(const RenderBox& muteButtonBox, const LayoutSize& size) const
593{
594 LayoutUnit y = -size.height();
595 FloatPoint absPoint = muteButtonBox.localToAbsolute(FloatPoint(muteButtonBox.offsetLeft(), y), IsFixed | UseTransforms);
596 if (absPoint.y() < 0)
597 y = muteButtonBox.height();
598 return LayoutPoint(0_lu, y);
599}
600
601#endif
602
603Color RenderTheme::activeSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
604{
605 auto& cache = colorCache(options);
606 if (!cache.activeSelectionBackgroundColor.isValid())
607 cache.activeSelectionBackgroundColor = transformSelectionBackgroundColor(platformActiveSelectionBackgroundColor(options), options);
608 return cache.activeSelectionBackgroundColor;
609}
610
611Color RenderTheme::inactiveSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
612{
613 auto& cache = colorCache(options);
614 if (!cache.inactiveSelectionBackgroundColor.isValid())
615 cache.inactiveSelectionBackgroundColor = transformSelectionBackgroundColor(platformInactiveSelectionBackgroundColor(options), options);
616 return cache.inactiveSelectionBackgroundColor;
617}
618
619Color RenderTheme::transformSelectionBackgroundColor(const Color& color, OptionSet<StyleColor::Options>) const
620{
621 return color.blendWithWhite();
622}
623
624Color RenderTheme::activeSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
625{
626 auto& cache = colorCache(options);
627 if (!cache.activeSelectionForegroundColor.isValid() && supportsSelectionForegroundColors(options))
628 cache.activeSelectionForegroundColor = platformActiveSelectionForegroundColor(options);
629 return cache.activeSelectionForegroundColor;
630}
631
632Color RenderTheme::inactiveSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
633{
634 auto& cache = colorCache(options);
635 if (!cache.inactiveSelectionForegroundColor.isValid() && supportsSelectionForegroundColors(options))
636 cache.inactiveSelectionForegroundColor = platformInactiveSelectionForegroundColor(options);
637 return cache.inactiveSelectionForegroundColor;
638}
639
640Color RenderTheme::activeListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
641{
642 auto& cache = colorCache(options);
643 if (!cache.activeListBoxSelectionBackgroundColor.isValid())
644 cache.activeListBoxSelectionBackgroundColor = platformActiveListBoxSelectionBackgroundColor(options);
645 return cache.activeListBoxSelectionBackgroundColor;
646}
647
648Color RenderTheme::inactiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
649{
650 auto& cache = colorCache(options);
651 if (!cache.inactiveListBoxSelectionBackgroundColor.isValid())
652 cache.inactiveListBoxSelectionBackgroundColor = platformInactiveListBoxSelectionBackgroundColor(options);
653 return cache.inactiveListBoxSelectionBackgroundColor;
654}
655
656Color RenderTheme::activeListBoxSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
657{
658 auto& cache = colorCache(options);
659 if (!cache.activeListBoxSelectionForegroundColor.isValid() && supportsListBoxSelectionForegroundColors(options))
660 cache.activeListBoxSelectionForegroundColor = platformActiveListBoxSelectionForegroundColor(options);
661 return cache.activeListBoxSelectionForegroundColor;
662}
663
664Color RenderTheme::inactiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
665{
666 auto& cache = colorCache(options);
667 if (!cache.inactiveListBoxSelectionForegroundColor.isValid() && supportsListBoxSelectionForegroundColors(options))
668 cache.inactiveListBoxSelectionForegroundColor = platformInactiveListBoxSelectionForegroundColor(options);
669 return cache.inactiveListBoxSelectionForegroundColor;
670}
671
672Color RenderTheme::platformActiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
673{
674 // Use a blue color by default if the platform theme doesn't define anything.
675 return Color(0, 0, 255);
676}
677
678Color RenderTheme::platformActiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
679{
680 // Use a white color by default if the platform theme doesn't define anything.
681 return Color::white;
682}
683
684Color RenderTheme::platformInactiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
685{
686 // Use a grey color by default if the platform theme doesn't define anything.
687 // This color matches Firefox's inactive color.
688 return Color(176, 176, 176);
689}
690
691Color RenderTheme::platformInactiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
692{
693 // Use a black color by default.
694 return Color::black;
695}
696
697Color RenderTheme::platformActiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
698{
699 return platformActiveSelectionBackgroundColor(options);
700}
701
702Color RenderTheme::platformActiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
703{
704 return platformActiveSelectionForegroundColor(options);
705}
706
707Color RenderTheme::platformInactiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
708{
709 return platformInactiveSelectionBackgroundColor(options);
710}
711
712Color RenderTheme::platformInactiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
713{
714 return platformInactiveSelectionForegroundColor(options);
715}
716
717int RenderTheme::baselinePosition(const RenderBox& box) const
718{
719#if USE(NEW_THEME)
720 return box.height() + box.marginTop() + Theme::singleton().baselinePositionAdjustment(box.style().appearance()) * box.style().effectiveZoom();
721#else
722 return box.height() + box.marginTop();
723#endif
724}
725
726bool RenderTheme::isControlContainer(ControlPart appearance) const
727{
728 // There are more leaves than this, but we'll patch this function as we add support for
729 // more controls.
730 return appearance != CheckboxPart && appearance != RadioPart;
731}
732
733bool RenderTheme::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
734{
735 switch (style.appearance()) {
736 case PushButtonPart:
737 case SquareButtonPart:
738#if ENABLE(INPUT_TYPE_COLOR)
739 case ColorWellPart:
740#endif
741 case DefaultButtonPart:
742 case ButtonPart:
743 case ListboxPart:
744 case MenulistPart:
745 case ProgressBarPart:
746 case MeterPart:
747 case RelevancyLevelIndicatorPart:
748 case ContinuousCapacityLevelIndicatorPart:
749 case DiscreteCapacityLevelIndicatorPart:
750 case RatingLevelIndicatorPart:
751 // FIXME: SearchFieldPart should be included here when making search fields style-able.
752 case TextFieldPart:
753 case TextAreaPart:
754 // Test the style to see if the UA border and background match.
755 return style.border() != border
756 || style.backgroundLayers() != background
757 || !style.backgroundColorEqualsToColorIgnoringVisited(backgroundColor);
758 default:
759 return false;
760 }
761}
762
763void RenderTheme::adjustRepaintRect(const RenderObject& renderer, FloatRect& rect)
764{
765#if USE(NEW_THEME)
766 ControlStates states(extractControlStatesForRenderer(renderer));
767 Theme::singleton().inflateControlPaintRect(renderer.style().appearance(), states, rect, renderer.style().effectiveZoom());
768#else
769 UNUSED_PARAM(renderer);
770 UNUSED_PARAM(rect);
771#endif
772}
773
774bool RenderTheme::supportsFocusRing(const RenderStyle& style) const
775{
776 return (style.hasAppearance() && style.appearance() != TextFieldPart && style.appearance() != TextAreaPart && style.appearance() != MenulistButtonPart && style.appearance() != ListboxPart);
777}
778
779bool RenderTheme::stateChanged(const RenderObject& o, ControlStates::States state) const
780{
781 // Default implementation assumes the controls don't respond to changes in :hover state
782 if (state == ControlStates::HoverState && !supportsHover(o.style()))
783 return false;
784
785 // Assume pressed state is only responded to if the control is enabled.
786 if (state == ControlStates::PressedState && !isEnabled(o))
787 return false;
788
789 // Repaint the control.
790 o.repaint();
791 return true;
792}
793
794void RenderTheme::updateControlStatesForRenderer(const RenderBox& box, ControlStates& controlStates) const
795{
796 ControlStates newStates = extractControlStatesForRenderer(box);
797 controlStates.setStates(newStates.states());
798 if (isFocused(box))
799 controlStates.setTimeSinceControlWasFocused(box.page().focusController().timeSinceFocusWasSet());
800}
801
802ControlStates::States RenderTheme::extractControlStatesForRenderer(const RenderObject& o) const
803{
804 ControlStates::States states = 0;
805 if (isHovered(o)) {
806 states |= ControlStates::HoverState;
807 if (isSpinUpButtonPartHovered(o))
808 states |= ControlStates::SpinUpState;
809 }
810 if (isPressed(o)) {
811 states |= ControlStates::PressedState;
812 if (isSpinUpButtonPartPressed(o))
813 states |= ControlStates::SpinUpState;
814 }
815 if (isFocused(o) && o.style().outlineStyleIsAuto() == OutlineIsAuto::On)
816 states |= ControlStates::FocusState;
817 if (isEnabled(o))
818 states |= ControlStates::EnabledState;
819 if (isChecked(o))
820 states |= ControlStates::CheckedState;
821 if (isDefault(o))
822 states |= ControlStates::DefaultState;
823 if (!isActive(o))
824 states |= ControlStates::WindowInactiveState;
825 if (isIndeterminate(o))
826 states |= ControlStates::IndeterminateState;
827 if (isPresenting(o))
828 states |= ControlStates::PresentingState;
829 return states;
830}
831
832bool RenderTheme::isActive(const RenderObject& renderer) const
833{
834 return renderer.page().focusController().isActive();
835}
836
837bool RenderTheme::isChecked(const RenderObject& o) const
838{
839 return is<HTMLInputElement>(o.node()) && downcast<HTMLInputElement>(*o.node()).shouldAppearChecked();
840}
841
842bool RenderTheme::isIndeterminate(const RenderObject& o) const
843{
844 return is<HTMLInputElement>(o.node()) && downcast<HTMLInputElement>(*o.node()).shouldAppearIndeterminate();
845}
846
847bool RenderTheme::isEnabled(const RenderObject& renderer) const
848{
849 Node* node = renderer.node();
850 if (!is<Element>(node))
851 return true;
852 return !downcast<Element>(*node).isDisabledFormControl();
853}
854
855bool RenderTheme::isFocused(const RenderObject& renderer) const
856{
857 Node* node = renderer.node();
858 if (!is<Element>(node))
859 return false;
860
861 auto focusDelegate = downcast<Element>(*node).focusDelegate();
862 Document& document = focusDelegate->document();
863 Frame* frame = document.frame();
864 return focusDelegate == document.focusedElement() && frame && frame->selection().isFocusedAndActive();
865}
866
867bool RenderTheme::isPressed(const RenderObject& renderer) const
868{
869 if (!is<Element>(renderer.node()))
870 return false;
871 return downcast<Element>(*renderer.node()).active();
872}
873
874bool RenderTheme::isSpinUpButtonPartPressed(const RenderObject& renderer) const
875{
876 Node* node = renderer.node();
877 if (!is<Element>(node))
878 return false;
879 Element& element = downcast<Element>(*node);
880 if (!element.active() || !is<SpinButtonElement>(element))
881 return false;
882 return downcast<SpinButtonElement>(element).upDownState() == SpinButtonElement::Up;
883}
884
885bool RenderTheme::isReadOnlyControl(const RenderObject& renderer) const
886{
887 Node* node = renderer.node();
888 if (!is<HTMLFormControlElement>(node))
889 return false;
890 return !downcast<Element>(*node).matchesReadWritePseudoClass();
891}
892
893bool RenderTheme::isHovered(const RenderObject& renderer) const
894{
895 Node* node = renderer.node();
896 if (!is<Element>(node))
897 return false;
898 Element& element = downcast<Element>(*node);
899 if (!is<SpinButtonElement>(element))
900 return element.hovered();
901 SpinButtonElement& spinButton = downcast<SpinButtonElement>(element);
902 return spinButton.hovered() && spinButton.upDownState() != SpinButtonElement::Indeterminate;
903}
904
905bool RenderTheme::isSpinUpButtonPartHovered(const RenderObject& renderer) const
906{
907 Node* node = renderer.node();
908 if (!is<SpinButtonElement>(node))
909 return false;
910 return downcast<SpinButtonElement>(*node).upDownState() == SpinButtonElement::Up;
911}
912
913bool RenderTheme::isPresenting(const RenderObject& o) const
914{
915 return is<HTMLInputElement>(o.node()) && downcast<HTMLInputElement>(*o.node()).isPresentingAttachedView();
916}
917
918bool RenderTheme::isDefault(const RenderObject& o) const
919{
920 // A button should only have the default appearance if the page is active
921 if (!isActive(o))
922 return false;
923
924 return o.style().appearance() == DefaultButtonPart;
925}
926
927#if !USE(NEW_THEME)
928
929void RenderTheme::adjustCheckboxStyle(StyleResolver&, RenderStyle& style, const Element*) const
930{
931 // A summary of the rules for checkbox designed to match WinIE:
932 // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.)
933 // font-size - not honored (control has no text), but we use it to decide which control size to use.
934 setCheckboxSize(style);
935
936 // padding - not honored by WinIE, needs to be removed.
937 style.resetPadding();
938
939 // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme)
940 // for now, we will not honor it.
941 style.resetBorder();
942
943 style.setBoxShadow(nullptr);
944}
945
946void RenderTheme::adjustRadioStyle(StyleResolver&, RenderStyle& style, const Element*) const
947{
948 // A summary of the rules for checkbox designed to match WinIE:
949 // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.)
950 // font-size - not honored (control has no text), but we use it to decide which control size to use.
951 setRadioSize(style);
952
953 // padding - not honored by WinIE, needs to be removed.
954 style.resetPadding();
955
956 // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme)
957 // for now, we will not honor it.
958 style.resetBorder();
959
960 style.setBoxShadow(nullptr);
961}
962
963void RenderTheme::adjustButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
964{
965 // Most platforms will completely honor all CSS, and so we have no need to
966 // adjust the style at all by default. We will still allow the theme a crack
967 // at setting up a desired vertical size.
968 setButtonSize(style);
969}
970
971void RenderTheme::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
972{
973}
974#endif
975
976void RenderTheme::adjustTextFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
977{
978}
979
980void RenderTheme::adjustTextAreaStyle(StyleResolver&, RenderStyle&, const Element*) const
981{
982}
983
984void RenderTheme::adjustMenuListStyle(StyleResolver&, RenderStyle&, const Element*) const
985{
986}
987
988#if ENABLE(METER_ELEMENT)
989
990void RenderTheme::adjustMeterStyle(StyleResolver&, RenderStyle& style, const Element*) const
991{
992 style.setBoxShadow(nullptr);
993}
994
995IntSize RenderTheme::meterSizeForBounds(const RenderMeter&, const IntRect& bounds) const
996{
997 return bounds.size();
998}
999
1000bool RenderTheme::supportsMeter(ControlPart) const
1001{
1002 return false;
1003}
1004
1005bool RenderTheme::paintMeter(const RenderObject&, const PaintInfo&, const IntRect&)
1006{
1007 return true;
1008}
1009
1010#endif // METER_ELEMENT
1011
1012void RenderTheme::adjustCapsLockIndicatorStyle(StyleResolver&, RenderStyle&, const Element*) const
1013{
1014}
1015
1016bool RenderTheme::paintCapsLockIndicator(const RenderObject&, const PaintInfo&, const IntRect&)
1017{
1018 return false;
1019}
1020
1021#if ENABLE(ATTACHMENT_ELEMENT)
1022
1023void RenderTheme::adjustAttachmentStyle(StyleResolver&, RenderStyle&, const Element*) const
1024{
1025}
1026
1027bool RenderTheme::paintAttachment(const RenderObject&, const PaintInfo&, const IntRect&)
1028{
1029 return false;
1030}
1031
1032#endif
1033
1034#if ENABLE(INPUT_TYPE_COLOR)
1035
1036String RenderTheme::colorInputStyleSheet() const
1037{
1038 ASSERT(RuntimeEnabledFeatures::sharedFeatures().inputTypeColorEnabled());
1039 return "input[type=\"color\"] { -webkit-appearance: color-well; width: 44px; height: 23px; outline: none; } "_s;
1040}
1041
1042#endif // ENABLE(INPUT_TYPE_COLOR)
1043
1044#if ENABLE(DATALIST_ELEMENT)
1045
1046String RenderTheme::dataListStyleSheet() const
1047{
1048 ASSERT(RuntimeEnabledFeatures::sharedFeatures().dataListElementEnabled());
1049 return "datalist { display: none; }"_s;
1050}
1051
1052void RenderTheme::adjustListButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1053{
1054}
1055
1056LayoutUnit RenderTheme::sliderTickSnappingThreshold() const
1057{
1058 return 0;
1059}
1060
1061void RenderTheme::paintSliderTicks(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& rect)
1062{
1063 if (!is<HTMLInputElement>(o.node()))
1064 return;
1065
1066 auto& input = downcast<HTMLInputElement>(*o.node());
1067
1068 if (!input.list())
1069 return;
1070
1071 auto& dataList = downcast<HTMLDataListElement>(*input.list());
1072
1073 double min = input.minimum();
1074 double max = input.maximum();
1075 ControlPart part = o.style().appearance();
1076 // We don't support ticks on alternate sliders like MediaVolumeSliders.
1077 if (part != SliderHorizontalPart && part != SliderVerticalPart)
1078 return;
1079 bool isHorizontal = part == SliderHorizontalPart;
1080
1081 IntSize thumbSize;
1082 const RenderObject* thumbRenderer = input.sliderThumbElement()->renderer();
1083 if (thumbRenderer) {
1084 const RenderStyle& thumbStyle = thumbRenderer->style();
1085 int thumbWidth = thumbStyle.width().intValue();
1086 int thumbHeight = thumbStyle.height().intValue();
1087 thumbSize.setWidth(isHorizontal ? thumbWidth : thumbHeight);
1088 thumbSize.setHeight(isHorizontal ? thumbHeight : thumbWidth);
1089 }
1090
1091 IntSize tickSize = sliderTickSize();
1092 float zoomFactor = o.style().effectiveZoom();
1093 FloatRect tickRect;
1094 int tickRegionSideMargin = 0;
1095 int tickRegionWidth = 0;
1096 IntRect trackBounds;
1097 RenderObject* trackRenderer = input.sliderTrackElement()->renderer();
1098 // We can ignoring transforms because transform is handled by the graphics context.
1099 if (trackRenderer)
1100 trackBounds = trackRenderer->absoluteBoundingBoxRectIgnoringTransforms();
1101 IntRect sliderBounds = o.absoluteBoundingBoxRectIgnoringTransforms();
1102
1103 // Make position relative to the transformed ancestor element.
1104 trackBounds.setX(trackBounds.x() - sliderBounds.x() + rect.x());
1105 trackBounds.setY(trackBounds.y() - sliderBounds.y() + rect.y());
1106
1107 if (isHorizontal) {
1108 tickRect.setWidth(floor(tickSize.width() * zoomFactor));
1109 tickRect.setHeight(floor(tickSize.height() * zoomFactor));
1110 tickRect.setY(floor(rect.y() + rect.height() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor));
1111 tickRegionSideMargin = trackBounds.x() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0;
1112 tickRegionWidth = trackBounds.width() - thumbSize.width();
1113 } else {
1114 tickRect.setWidth(floor(tickSize.height() * zoomFactor));
1115 tickRect.setHeight(floor(tickSize.width() * zoomFactor));
1116 tickRect.setX(floor(rect.x() + rect.width() / 2.0 + sliderTickOffsetFromTrackCenter() * zoomFactor));
1117 tickRegionSideMargin = trackBounds.y() + (thumbSize.width() - tickSize.width() * zoomFactor) / 2.0;
1118 tickRegionWidth = trackBounds.height() - thumbSize.width();
1119 }
1120 Ref<HTMLCollection> options = dataList.options();
1121 GraphicsContextStateSaver stateSaver(paintInfo.context());
1122 paintInfo.context().setFillColor(o.style().visitedDependentColorWithColorFilter(CSSPropertyColor));
1123 for (unsigned i = 0; Node* node = options->item(i); i++) {
1124 ASSERT(is<HTMLOptionElement>(*node));
1125 HTMLOptionElement& optionElement = downcast<HTMLOptionElement>(*node);
1126 String value = optionElement.value();
1127 if (!input.isValidValue(value))
1128 continue;
1129 double parsedValue = parseToDoubleForNumberType(input.sanitizeValue(value));
1130 double tickFraction = (parsedValue - min) / (max - min);
1131 double tickRatio = isHorizontal && o.style().isLeftToRightDirection() ? tickFraction : 1.0 - tickFraction;
1132 double tickPosition = round(tickRegionSideMargin + tickRegionWidth * tickRatio);
1133 if (isHorizontal)
1134 tickRect.setX(tickPosition);
1135 else
1136 tickRect.setY(tickPosition);
1137 paintInfo.context().fillRect(tickRect);
1138 }
1139}
1140
1141#endif
1142
1143Seconds RenderTheme::animationRepeatIntervalForProgressBar(RenderProgress&) const
1144{
1145 return 0_s;
1146}
1147
1148Seconds RenderTheme::animationDurationForProgressBar(RenderProgress&) const
1149{
1150 return 0_s;
1151}
1152
1153void RenderTheme::adjustProgressBarStyle(StyleResolver&, RenderStyle&, const Element*) const
1154{
1155}
1156
1157IntRect RenderTheme::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
1158{
1159 return bounds;
1160}
1161
1162bool RenderTheme::shouldHaveSpinButton(const HTMLInputElement& inputElement) const
1163{
1164 return inputElement.isSteppable() && !inputElement.isRangeControl();
1165}
1166
1167bool RenderTheme::shouldHaveCapsLockIndicator(const HTMLInputElement&) const
1168{
1169 return false;
1170}
1171
1172void RenderTheme::adjustMenuListButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1173{
1174}
1175
1176void RenderTheme::adjustMediaControlStyle(StyleResolver&, RenderStyle&, const Element*) const
1177{
1178}
1179
1180void RenderTheme::adjustSliderTrackStyle(StyleResolver&, RenderStyle&, const Element*) const
1181{
1182}
1183
1184void RenderTheme::adjustSliderThumbStyle(StyleResolver&, RenderStyle& style, const Element* element) const
1185{
1186 adjustSliderThumbSize(style, element);
1187}
1188
1189void RenderTheme::adjustSliderThumbSize(RenderStyle&, const Element*) const
1190{
1191}
1192
1193void RenderTheme::adjustSearchFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
1194{
1195}
1196
1197void RenderTheme::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1198{
1199}
1200
1201void RenderTheme::adjustSearchFieldDecorationPartStyle(StyleResolver&, RenderStyle&, const Element*) const
1202{
1203}
1204
1205void RenderTheme::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle&, const Element*) const
1206{
1207}
1208
1209void RenderTheme::adjustSearchFieldResultsButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1210{
1211}
1212
1213void RenderTheme::purgeCaches()
1214{
1215 m_colorCacheMap.clear();
1216}
1217
1218void RenderTheme::platformColorsDidChange()
1219{
1220 m_colorCacheMap.clear();
1221
1222 Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
1223}
1224
1225auto RenderTheme::colorCache(OptionSet<StyleColor::Options> options) const -> ColorCache&
1226{
1227 auto optionsIgnoringVisitedLink = options;
1228 optionsIgnoringVisitedLink.remove(StyleColor::Options::ForVisitedLink);
1229
1230 return m_colorCacheMap.ensure(optionsIgnoringVisitedLink.toRaw(), [] {
1231 return ColorCache();
1232 }).iterator->value;
1233}
1234
1235FontCascadeDescription& RenderTheme::cachedSystemFontDescription(CSSValueID systemFontID) const
1236{
1237 static NeverDestroyed<FontCascadeDescription> caption;
1238 static NeverDestroyed<FontCascadeDescription> icon;
1239 static NeverDestroyed<FontCascadeDescription> menu;
1240 static NeverDestroyed<FontCascadeDescription> messageBox;
1241 static NeverDestroyed<FontCascadeDescription> smallCaption;
1242 static NeverDestroyed<FontCascadeDescription> statusBar;
1243 static NeverDestroyed<FontCascadeDescription> webkitMiniControl;
1244 static NeverDestroyed<FontCascadeDescription> webkitSmallControl;
1245 static NeverDestroyed<FontCascadeDescription> webkitControl;
1246 static NeverDestroyed<FontCascadeDescription> defaultDescription;
1247
1248 switch (systemFontID) {
1249 case CSSValueCaption:
1250 return caption;
1251 case CSSValueIcon:
1252 return icon;
1253 case CSSValueMenu:
1254 return menu;
1255 case CSSValueMessageBox:
1256 return messageBox;
1257 case CSSValueSmallCaption:
1258 return smallCaption;
1259 case CSSValueStatusBar:
1260 return statusBar;
1261 case CSSValueWebkitMiniControl:
1262 return webkitMiniControl;
1263 case CSSValueWebkitSmallControl:
1264 return webkitSmallControl;
1265 case CSSValueWebkitControl:
1266 return webkitControl;
1267 case CSSValueNone:
1268 return defaultDescription;
1269 default:
1270 ASSERT_NOT_REACHED();
1271 return defaultDescription;
1272 }
1273}
1274
1275void RenderTheme::systemFont(CSSValueID systemFontID, FontCascadeDescription& fontDescription) const
1276{
1277 fontDescription = cachedSystemFontDescription(systemFontID);
1278 if (fontDescription.isAbsoluteSize())
1279 return;
1280
1281 updateCachedSystemFontDescription(systemFontID, fontDescription);
1282}
1283
1284Color RenderTheme::systemColor(CSSValueID cssValueId, OptionSet<StyleColor::Options> options) const
1285{
1286 switch (cssValueId) {
1287 case CSSValueWebkitLink:
1288 return options.contains(StyleColor::Options::ForVisitedLink) ? 0xFF551A8B : 0xFF0000EE;
1289 case CSSValueWebkitActivelink:
1290 return 0xFFFF0000;
1291 case CSSValueActiveborder:
1292 return 0xFFFFFFFF;
1293 case CSSValueActivebuttontext:
1294 return 0xFF000000;
1295 case CSSValueActivecaption:
1296 return 0xFFCCCCCC;
1297 case CSSValueAppworkspace:
1298 return 0xFFFFFFFF;
1299 case CSSValueBackground:
1300 return 0xFF6363CE;
1301 case CSSValueButtonface:
1302 return 0xFFC0C0C0;
1303 case CSSValueButtonhighlight:
1304 return 0xFFDDDDDD;
1305 case CSSValueButtonshadow:
1306 return 0xFF888888;
1307 case CSSValueButtontext:
1308 return 0xFF000000;
1309 case CSSValueCaptiontext:
1310 return 0xFF000000;
1311 case CSSValueGraytext:
1312 return 0xFF808080;
1313 case CSSValueHighlight:
1314 return 0xFFB5D5FF;
1315 case CSSValueHighlighttext:
1316 return 0xFF000000;
1317 case CSSValueInactiveborder:
1318 return 0xFFFFFFFF;
1319 case CSSValueInactivecaption:
1320 return 0xFFFFFFFF;
1321 case CSSValueInactivecaptiontext:
1322 return 0xFF7F7F7F;
1323 case CSSValueInfobackground:
1324 return 0xFFFBFCC5;
1325 case CSSValueInfotext:
1326 return 0xFF000000;
1327 case CSSValueMenu:
1328 return 0xFFC0C0C0;
1329 case CSSValueMenutext:
1330 return 0xFF000000;
1331 case CSSValueScrollbar:
1332 return 0xFFFFFFFF;
1333 case CSSValueText:
1334 return 0xFF000000;
1335 case CSSValueThreeddarkshadow:
1336 return 0xFF666666;
1337 case CSSValueThreedface:
1338 return 0xFFC0C0C0;
1339 case CSSValueThreedhighlight:
1340 return 0xFFDDDDDD;
1341 case CSSValueThreedlightshadow:
1342 return 0xFFC0C0C0;
1343 case CSSValueThreedshadow:
1344 return 0xFF888888;
1345 case CSSValueWindow:
1346 return 0xFFFFFFFF;
1347 case CSSValueWindowframe:
1348 return 0xFFCCCCCC;
1349 case CSSValueWindowtext:
1350 return 0xFF000000;
1351 default:
1352 break;
1353 }
1354 return Color();
1355}
1356
1357Color RenderTheme::activeTextSearchHighlightColor(OptionSet<StyleColor::Options> options) const
1358{
1359 auto& cache = colorCache(options);
1360 if (!cache.activeTextSearchHighlightColor.isValid())
1361 cache.activeTextSearchHighlightColor = platformActiveTextSearchHighlightColor(options);
1362 return cache.activeTextSearchHighlightColor;
1363}
1364
1365Color RenderTheme::inactiveTextSearchHighlightColor(OptionSet<StyleColor::Options> options) const
1366{
1367 auto& cache = colorCache(options);
1368 if (!cache.inactiveTextSearchHighlightColor.isValid())
1369 cache.inactiveTextSearchHighlightColor = platformInactiveTextSearchHighlightColor(options);
1370 return cache.inactiveTextSearchHighlightColor;
1371}
1372
1373Color RenderTheme::platformActiveTextSearchHighlightColor(OptionSet<StyleColor::Options>) const
1374{
1375 return Color(255, 150, 50); // Orange.
1376}
1377
1378Color RenderTheme::platformInactiveTextSearchHighlightColor(OptionSet<StyleColor::Options>) const
1379{
1380 return Color(255, 255, 0); // Yellow.
1381}
1382
1383#if ENABLE(TOUCH_EVENTS)
1384
1385Color RenderTheme::tapHighlightColor()
1386{
1387 return singleton().platformTapHighlightColor();
1388}
1389
1390#endif
1391
1392// Value chosen by observation. This can be tweaked.
1393static const int minColorContrastValue = 1300;
1394// For transparent or translucent background color, use lightening.
1395static const float minDisabledColorAlphaValue = 0.5;
1396
1397Color RenderTheme::disabledTextColor(const Color& textColor, const Color& backgroundColor) const
1398{
1399 // The explicit check for black is an optimization for the 99% case (black on white).
1400 // This also means that black on black will turn into grey on black when disabled.
1401 Color disabledColor;
1402 if (Color::isBlackColor(textColor) || backgroundColor.alphaAsFloat() < minDisabledColorAlphaValue || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white))
1403 disabledColor = textColor.light();
1404 else
1405 disabledColor = textColor.dark();
1406
1407 // If there's not very much contrast between the disabled color and the background color,
1408 // just leave the text color alone. We don't want to change a good contrast color scheme so that it has really bad contrast.
1409 // If the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
1410 if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue)
1411 return textColor;
1412
1413 return disabledColor;
1414}
1415
1416void RenderTheme::setCustomFocusRingColor(const Color& color)
1417{
1418 customFocusRingColor() = color;
1419}
1420
1421Color RenderTheme::focusRingColor(OptionSet<StyleColor::Options> options) const
1422{
1423 if (customFocusRingColor().isValid())
1424 return customFocusRingColor();
1425
1426 auto& cache = colorCache(options);
1427 if (!cache.systemFocusRingColor.isValid())
1428 cache.systemFocusRingColor = platformFocusRingColor(options);
1429 return cache.systemFocusRingColor;
1430}
1431
1432String RenderTheme::fileListDefaultLabel(bool multipleFilesAllowed) const
1433{
1434 if (multipleFilesAllowed)
1435 return fileButtonNoFilesSelectedLabel();
1436 return fileButtonNoFileSelectedLabel();
1437}
1438
1439String RenderTheme::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
1440{
1441 if (width <= 0)
1442 return String();
1443
1444 String string;
1445 if (fileList->isEmpty())
1446 string = fileListDefaultLabel(multipleFilesAllowed);
1447 else if (fileList->length() == 1)
1448 string = fileList->item(0)->name();
1449 else
1450 return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
1451
1452 return StringTruncator::centerTruncate(string, width, font);
1453}
1454
1455#if USE(SYSTEM_PREVIEW)
1456void RenderTheme::paintSystemPreviewBadge(Image& image, const PaintInfo& paintInfo, const FloatRect& rect)
1457{
1458 // The default implementation paints a small marker
1459 // in the upper right corner, as long as the image is big enough.
1460
1461 UNUSED_PARAM(image);
1462 auto& context = paintInfo.context();
1463
1464 GraphicsContextStateSaver stateSaver { context };
1465
1466 if (rect.width() < 32 || rect.height() < 32)
1467 return;
1468
1469 auto markerRect = FloatRect {rect.x() + rect.width() - 24, rect.y() + 8, 16, 16 };
1470 auto roundedMarkerRect = FloatRoundedRect { markerRect, FloatRoundedRect::Radii { 8 } };
1471 auto color = Color { 255, 0, 0 };
1472 context.fillRoundedRect(roundedMarkerRect, color);
1473}
1474#endif
1475
1476#if ENABLE(TOUCH_EVENTS)
1477
1478Color RenderTheme::platformTapHighlightColor() const
1479{
1480 // This color is expected to be drawn on a semi-transparent overlay,
1481 // making it more transparent than its alpha value indicates.
1482 return Color(0, 0, 0, 102);
1483}
1484
1485#endif
1486
1487} // namespace WebCore
1488