[ContentChangeObserver] Content observation should be limited to the current document.
[WebKit-https.git] / Source / WebCore / rendering / RenderTheme.cpp
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
67 namespace WebCore {
68
69 using namespace HTMLNames;
70
71 static Color& customFocusRingColor()
72 {
73     static NeverDestroyed<Color> color;
74     return color;
75 }
76
77 RenderTheme::RenderTheme()
78 {
79 }
80
81 void 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
286 bool 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
447 bool 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
504 bool 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
568 String 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
582 String RenderTheme::formatMediaControlsCurrentTime(float currentTime, float /*duration*/) const
583 {
584     return formatMediaControlsTime(currentTime);
585 }
586
587 String RenderTheme::formatMediaControlsRemainingTime(float currentTime, float duration) const
588 {
589     return formatMediaControlsTime(currentTime - duration);
590 }
591
592 LayoutPoint 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
603 Color 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
611 Color 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
619 Color RenderTheme::transformSelectionBackgroundColor(const Color& color, OptionSet<StyleColor::Options>) const
620 {
621     return color.blendWithWhite();
622 }
623
624 Color 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
632 Color 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
640 Color 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
648 Color 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
656 Color 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
664 Color 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
672 Color 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
678 Color 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
684 Color 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
691 Color RenderTheme::platformInactiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
692 {
693     // Use a black color by default.
694     return Color::black;
695 }
696
697 Color RenderTheme::platformActiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
698 {
699     return platformActiveSelectionBackgroundColor(options);
700 }
701
702 Color RenderTheme::platformActiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
703 {
704     return platformActiveSelectionForegroundColor(options);
705 }
706
707 Color RenderTheme::platformInactiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options> options) const
708 {
709     return platformInactiveSelectionBackgroundColor(options);
710 }
711
712 Color RenderTheme::platformInactiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options> options) const
713 {
714     return platformInactiveSelectionForegroundColor(options);
715 }
716
717 int 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
726 bool 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
733 bool 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
763 void 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
774 bool 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
779 bool 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
794 void 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
802 ControlStates::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
832 bool RenderTheme::isActive(const RenderObject& renderer) const
833 {
834     return renderer.page().focusController().isActive();
835 }
836
837 bool RenderTheme::isChecked(const RenderObject& o) const
838 {
839     return is<HTMLInputElement>(o.node()) && downcast<HTMLInputElement>(*o.node()).shouldAppearChecked();
840 }
841
842 bool RenderTheme::isIndeterminate(const RenderObject& o) const
843 {
844     return is<HTMLInputElement>(o.node()) && downcast<HTMLInputElement>(*o.node()).shouldAppearIndeterminate();
845 }
846
847 bool 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
855 bool 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
867 bool 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
874 bool 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
885 bool 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
893 bool 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
905 bool 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
913 bool RenderTheme::isPresenting(const RenderObject& o) const
914 {
915     return is<HTMLInputElement>(o.node()) && downcast<HTMLInputElement>(*o.node()).isPresentingAttachedView();
916 }
917
918 bool 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
929 void 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
946 void 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
963 void 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
971 void RenderTheme::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
972 {
973 }
974 #endif
975
976 void RenderTheme::adjustTextFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
977 {
978 }
979
980 void RenderTheme::adjustTextAreaStyle(StyleResolver&, RenderStyle&, const Element*) const
981 {
982 }
983
984 void RenderTheme::adjustMenuListStyle(StyleResolver&, RenderStyle&, const Element*) const
985 {
986 }
987
988 #if ENABLE(METER_ELEMENT)
989
990 void RenderTheme::adjustMeterStyle(StyleResolver&, RenderStyle& style, const Element*) const
991 {
992     style.setBoxShadow(nullptr);
993 }
994
995 IntSize RenderTheme::meterSizeForBounds(const RenderMeter&, const IntRect& bounds) const
996 {
997     return bounds.size();
998 }
999
1000 bool RenderTheme::supportsMeter(ControlPart) const
1001 {
1002     return false;
1003 }
1004
1005 bool RenderTheme::paintMeter(const RenderObject&, const PaintInfo&, const IntRect&)
1006 {
1007     return true;
1008 }
1009
1010 #endif // METER_ELEMENT
1011
1012 void RenderTheme::adjustCapsLockIndicatorStyle(StyleResolver&, RenderStyle&, const Element*) const
1013 {
1014 }
1015
1016 bool RenderTheme::paintCapsLockIndicator(const RenderObject&, const PaintInfo&, const IntRect&)
1017 {
1018     return false;
1019 }
1020
1021 #if ENABLE(ATTACHMENT_ELEMENT)
1022
1023 void RenderTheme::adjustAttachmentStyle(StyleResolver&, RenderStyle&, const Element*) const
1024 {
1025 }
1026
1027 bool RenderTheme::paintAttachment(const RenderObject&, const PaintInfo&, const IntRect&)
1028 {
1029     return false;
1030 }
1031
1032 #endif
1033
1034 #if ENABLE(INPUT_TYPE_COLOR)
1035
1036 String 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
1046 String RenderTheme::dataListStyleSheet() const
1047 {
1048     ASSERT(RuntimeEnabledFeatures::sharedFeatures().dataListElementEnabled());
1049     return "datalist { display: none; }"_s;
1050 }
1051
1052 void RenderTheme::adjustListButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1053 {
1054 }
1055
1056 LayoutUnit RenderTheme::sliderTickSnappingThreshold() const
1057 {
1058     return 0;
1059 }
1060
1061 void 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
1143 Seconds RenderTheme::animationRepeatIntervalForProgressBar(RenderProgress&) const
1144 {
1145     return 0_s;
1146 }
1147
1148 Seconds RenderTheme::animationDurationForProgressBar(RenderProgress&) const
1149 {
1150     return 0_s;
1151 }
1152
1153 void RenderTheme::adjustProgressBarStyle(StyleResolver&, RenderStyle&, const Element*) const
1154 {
1155 }
1156
1157 IntRect RenderTheme::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
1158 {
1159     return bounds;
1160 }
1161
1162 bool RenderTheme::shouldHaveSpinButton(const HTMLInputElement& inputElement) const
1163 {
1164     return inputElement.isSteppable() && !inputElement.isRangeControl();
1165 }
1166
1167 bool RenderTheme::shouldHaveCapsLockIndicator(const HTMLInputElement&) const
1168 {
1169     return false;
1170 }
1171
1172 void RenderTheme::adjustMenuListButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1173 {
1174 }
1175
1176 void RenderTheme::adjustMediaControlStyle(StyleResolver&, RenderStyle&, const Element*) const
1177 {
1178 }
1179
1180 void RenderTheme::adjustSliderTrackStyle(StyleResolver&, RenderStyle&, const Element*) const
1181 {
1182 }
1183
1184 void RenderTheme::adjustSliderThumbStyle(StyleResolver&, RenderStyle& style, const Element* element) const
1185 {
1186     adjustSliderThumbSize(style, element);
1187 }
1188
1189 void RenderTheme::adjustSliderThumbSize(RenderStyle&, const Element*) const
1190 {
1191 }
1192
1193 void RenderTheme::adjustSearchFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
1194 {
1195 }
1196
1197 void RenderTheme::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1198 {
1199 }
1200
1201 void RenderTheme::adjustSearchFieldDecorationPartStyle(StyleResolver&, RenderStyle&, const Element*) const
1202 {
1203 }
1204
1205 void RenderTheme::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle&, const Element*) const
1206 {
1207 }
1208
1209 void RenderTheme::adjustSearchFieldResultsButtonStyle(StyleResolver&, RenderStyle&, const Element*) const
1210 {
1211 }
1212
1213 void RenderTheme::purgeCaches()
1214 {
1215     m_colorCache = ColorCache();
1216     m_darkColorCache = ColorCache();
1217 }
1218
1219 void RenderTheme::platformColorsDidChange()
1220 {
1221     m_colorCache = ColorCache();
1222     m_darkColorCache = ColorCache();
1223
1224     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
1225 }
1226
1227 auto RenderTheme::colorCache(OptionSet<StyleColor::Options> options) const -> ColorCache&
1228 {
1229     if (options.contains(StyleColor::Options::UseDarkAppearance))
1230         return m_darkColorCache;
1231     return m_colorCache;
1232 }
1233
1234 FontCascadeDescription& RenderTheme::cachedSystemFontDescription(CSSValueID systemFontID) const
1235 {
1236     static NeverDestroyed<FontCascadeDescription> caption;
1237     static NeverDestroyed<FontCascadeDescription> icon;
1238     static NeverDestroyed<FontCascadeDescription> menu;
1239     static NeverDestroyed<FontCascadeDescription> messageBox;
1240     static NeverDestroyed<FontCascadeDescription> smallCaption;
1241     static NeverDestroyed<FontCascadeDescription> statusBar;
1242     static NeverDestroyed<FontCascadeDescription> webkitMiniControl;
1243     static NeverDestroyed<FontCascadeDescription> webkitSmallControl;
1244     static NeverDestroyed<FontCascadeDescription> webkitControl;
1245     static NeverDestroyed<FontCascadeDescription> defaultDescription;
1246
1247     switch (systemFontID) {
1248     case CSSValueCaption:
1249         return caption;
1250     case CSSValueIcon:
1251         return icon;
1252     case CSSValueMenu:
1253         return menu;
1254     case CSSValueMessageBox:
1255         return messageBox;
1256     case CSSValueSmallCaption:
1257         return smallCaption;
1258     case CSSValueStatusBar:
1259         return statusBar;
1260     case CSSValueWebkitMiniControl:
1261         return webkitMiniControl;
1262     case CSSValueWebkitSmallControl:
1263         return webkitSmallControl;
1264     case CSSValueWebkitControl:
1265         return webkitControl;
1266     case CSSValueNone:
1267         return defaultDescription;
1268     default:
1269         ASSERT_NOT_REACHED();
1270         return defaultDescription;
1271     }
1272 }
1273
1274 void RenderTheme::systemFont(CSSValueID systemFontID, FontCascadeDescription& fontDescription) const
1275 {
1276     fontDescription = cachedSystemFontDescription(systemFontID);
1277     if (fontDescription.isAbsoluteSize())
1278         return;
1279
1280     updateCachedSystemFontDescription(systemFontID, fontDescription);
1281 }
1282
1283 Color RenderTheme::systemColor(CSSValueID cssValueId, OptionSet<StyleColor::Options> options) const
1284 {
1285     switch (cssValueId) {
1286     case CSSValueWebkitLink:
1287         return options.contains(StyleColor::Options::ForVisitedLink) ? 0xFF551A8B : 0xFF0000EE;
1288     case CSSValueWebkitActivelink:
1289         return 0xFFFF0000;
1290     case CSSValueActiveborder:
1291         return 0xFFFFFFFF;
1292     case CSSValueActivebuttontext:
1293         return 0xFF000000;
1294     case CSSValueActivecaption:
1295         return 0xFFCCCCCC;
1296     case CSSValueAppworkspace:
1297         return 0xFFFFFFFF;
1298     case CSSValueBackground:
1299         return 0xFF6363CE;
1300     case CSSValueButtonface:
1301         return 0xFFC0C0C0;
1302     case CSSValueButtonhighlight:
1303         return 0xFFDDDDDD;
1304     case CSSValueButtonshadow:
1305         return 0xFF888888;
1306     case CSSValueButtontext:
1307         return 0xFF000000;
1308     case CSSValueCaptiontext:
1309         return 0xFF000000;
1310     case CSSValueGraytext:
1311         return 0xFF808080;
1312     case CSSValueHighlight:
1313         return 0xFFB5D5FF;
1314     case CSSValueHighlighttext:
1315         return 0xFF000000;
1316     case CSSValueInactiveborder:
1317         return 0xFFFFFFFF;
1318     case CSSValueInactivecaption:
1319         return 0xFFFFFFFF;
1320     case CSSValueInactivecaptiontext:
1321         return 0xFF7F7F7F;
1322     case CSSValueInfobackground:
1323         return 0xFFFBFCC5;
1324     case CSSValueInfotext:
1325         return 0xFF000000;
1326     case CSSValueMenu:
1327         return 0xFFC0C0C0;
1328     case CSSValueMenutext:
1329         return 0xFF000000;
1330     case CSSValueScrollbar:
1331         return 0xFFFFFFFF;
1332     case CSSValueText:
1333         return 0xFF000000;
1334     case CSSValueThreeddarkshadow:
1335         return 0xFF666666;
1336     case CSSValueThreedface:
1337         return 0xFFC0C0C0;
1338     case CSSValueThreedhighlight:
1339         return 0xFFDDDDDD;
1340     case CSSValueThreedlightshadow:
1341         return 0xFFC0C0C0;
1342     case CSSValueThreedshadow:
1343         return 0xFF888888;
1344     case CSSValueWindow:
1345         return 0xFFFFFFFF;
1346     case CSSValueWindowframe:
1347         return 0xFFCCCCCC;
1348     case CSSValueWindowtext:
1349         return 0xFF000000;
1350     default:
1351         break;
1352     }
1353     return Color();
1354 }
1355
1356 Color RenderTheme::activeTextSearchHighlightColor(OptionSet<StyleColor::Options> options) const
1357 {
1358     auto& cache = colorCache(options);
1359     if (!cache.activeTextSearchHighlightColor.isValid())
1360         cache.activeTextSearchHighlightColor = platformActiveTextSearchHighlightColor(options);
1361     return cache.activeTextSearchHighlightColor;
1362 }
1363
1364 Color RenderTheme::inactiveTextSearchHighlightColor(OptionSet<StyleColor::Options> options) const
1365 {
1366     auto& cache = colorCache(options);
1367     if (!cache.inactiveTextSearchHighlightColor.isValid())
1368         cache.inactiveTextSearchHighlightColor = platformInactiveTextSearchHighlightColor(options);
1369     return cache.inactiveTextSearchHighlightColor;
1370 }
1371
1372 Color RenderTheme::platformActiveTextSearchHighlightColor(OptionSet<StyleColor::Options>) const
1373 {
1374     return Color(255, 150, 50); // Orange.
1375 }
1376
1377 Color RenderTheme::platformInactiveTextSearchHighlightColor(OptionSet<StyleColor::Options>) const
1378 {
1379     return Color(255, 255, 0); // Yellow.
1380 }
1381
1382 #if ENABLE(TOUCH_EVENTS)
1383
1384 Color RenderTheme::tapHighlightColor()
1385 {
1386     return singleton().platformTapHighlightColor();
1387 }
1388
1389 #endif
1390
1391 // Value chosen by observation. This can be tweaked.
1392 static const int minColorContrastValue = 1300;
1393 // For transparent or translucent background color, use lightening.
1394 static const float minDisabledColorAlphaValue = 0.5;
1395
1396 Color RenderTheme::disabledTextColor(const Color& textColor, const Color& backgroundColor) const
1397 {
1398     // The explicit check for black is an optimization for the 99% case (black on white).
1399     // This also means that black on black will turn into grey on black when disabled.
1400     Color disabledColor;
1401     if (Color::isBlackColor(textColor) || backgroundColor.alphaAsFloat() < minDisabledColorAlphaValue || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white))
1402         disabledColor = textColor.light();
1403     else
1404         disabledColor = textColor.dark();
1405     
1406     // If there's not very much contrast between the disabled color and the background color,
1407     // just leave the text color alone. We don't want to change a good contrast color scheme so that it has really bad contrast.
1408     // If the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
1409     if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue)
1410         return textColor;
1411     
1412     return disabledColor;
1413 }
1414
1415 void RenderTheme::setCustomFocusRingColor(const Color& color)
1416 {
1417     customFocusRingColor() = color;
1418 }
1419
1420 Color RenderTheme::focusRingColor(OptionSet<StyleColor::Options> options) const
1421 {
1422     if (customFocusRingColor().isValid())
1423         return customFocusRingColor();
1424
1425     auto& cache = colorCache(options);
1426     if (!cache.systemFocusRingColor.isValid())
1427         cache.systemFocusRingColor = platformFocusRingColor(options);
1428     return cache.systemFocusRingColor;
1429 }
1430
1431 String RenderTheme::fileListDefaultLabel(bool multipleFilesAllowed) const
1432 {
1433     if (multipleFilesAllowed)
1434         return fileButtonNoFilesSelectedLabel();
1435     return fileButtonNoFileSelectedLabel();
1436 }
1437
1438 String RenderTheme::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
1439 {
1440     if (width <= 0)
1441         return String();
1442
1443     String string;
1444     if (fileList->isEmpty())
1445         string = fileListDefaultLabel(multipleFilesAllowed);
1446     else if (fileList->length() == 1)
1447         string = fileList->item(0)->name();
1448     else
1449         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
1450
1451     return StringTruncator::centerTruncate(string, width, font);
1452 }
1453
1454 #if USE(SYSTEM_PREVIEW)
1455 void RenderTheme::paintSystemPreviewBadge(Image& image, const PaintInfo& paintInfo, const FloatRect& rect)
1456 {
1457     // The default implementation paints a small marker
1458     // in the upper right corner, as long as the image is big enough.
1459
1460     UNUSED_PARAM(image);
1461     auto& context = paintInfo.context();
1462
1463     GraphicsContextStateSaver stateSaver { context };
1464
1465     if (rect.width() < 32 || rect.height() < 32)
1466         return;
1467
1468     auto markerRect = FloatRect {rect.x() + rect.width() - 24, rect.y() + 8, 16, 16 };
1469     auto roundedMarkerRect = FloatRoundedRect { markerRect, FloatRoundedRect::Radii { 8 } };
1470     auto color = Color { 255, 0, 0 };
1471     context.fillRoundedRect(roundedMarkerRect, color);
1472 }
1473 #endif
1474
1475 #if ENABLE(TOUCH_EVENTS)
1476
1477 Color RenderTheme::platformTapHighlightColor() const
1478 {
1479     // This color is expected to be drawn on a semi-transparent overlay,
1480     // making it more transparent than its alpha value indicates.
1481     return Color(0, 0, 0, 102);
1482 }
1483
1484 #endif
1485
1486 } // namespace WebCore