1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsNativeBasicTheme.h"
9 #include "mozilla/MathAlgorithms.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/gfx/Rect.h"
12 #include "mozilla/gfx/Types.h"
13 #include "mozilla/gfx/Filters.h"
14 #include "mozilla/RelativeLuminanceUtils.h"
15 #include "mozilla/StaticPrefs_widget.h"
16 #include "mozilla/webrender/WebRenderAPI.h"
17 #include "nsCSSColorUtils.h"
18 #include "nsCSSRendering.h"
19 #include "nsLayoutUtils.h"
20 #include "PathHelpers.h"
22 #include "nsDeviceContext.h"
24 #include "nsColorControlFrame.h"
25 #include "nsDateTimeControlFrame.h"
26 #include "nsMeterFrame.h"
27 #include "nsProgressFrame.h"
28 #include "nsRangeFrame.h"
29 #include "mozilla/dom/HTMLMeterElement.h"
30 #include "mozilla/dom/HTMLProgressElement.h"
32 using namespace mozilla
;
33 using namespace mozilla::widget
;
34 using namespace mozilla::gfx
;
36 NS_IMPL_ISUPPORTS_INHERITED(nsNativeBasicTheme
, nsNativeTheme
, nsITheme
)
40 static constexpr sRGBColor sTransparent
= sRGBColor::White(0.0);
42 // This pushes and pops a clip rect to the draw target.
44 // This is done to reduce fuzz in places where we may have antialiasing,
45 // because skia is not clip-invariant: given different clips, it does not
46 // guarantee the same result, even if the painted content doesn't intersect
49 // This is a bit sad, overall, but...
50 struct MOZ_RAII AutoClipRect
{
51 AutoClipRect(DrawTarget
& aDt
, const LayoutDeviceRect
& aRect
) : mDt(aDt
) {
52 mDt
.PushClipRect(aRect
.ToUnknownRect());
55 ~AutoClipRect() { mDt
.PopClip(); }
61 static LayoutDeviceIntCoord
SnapBorderWidth(
62 CSSCoord aCssWidth
, nsNativeBasicTheme::DPIRatio aDpiRatio
) {
63 if (aCssWidth
== 0.0f
) {
66 return std::max(LayoutDeviceIntCoord(1), (aCssWidth
* aDpiRatio
).Truncated());
69 [[nodiscard
]] static float ScaleLuminanceBy(float aLuminance
, float aFactor
) {
70 return aLuminance
>= 0.18f
? aLuminance
* aFactor
: aLuminance
/ aFactor
;
73 static nscolor
ThemedAccentColor(bool aBackground
) {
74 MOZ_ASSERT(StaticPrefs::widget_non_native_theme_use_theme_accent());
75 nscolor color
= LookAndFeel::GetColor(
76 aBackground
? LookAndFeel::ColorID::MozAccentColor
77 : LookAndFeel::ColorID::MozAccentColorForeground
);
78 if (NS_GET_A(color
) != 0xff) {
79 // Blend with white, ensuring the color is opaque to avoid surprises if we
81 color
= NS_ComposeColors(NS_RGB(0xff, 0xff, 0xff), color
);
88 sRGBColor
nsNativeBasicTheme::sAccentColor
= sRGBColor::OpaqueWhite();
89 sRGBColor
nsNativeBasicTheme::sAccentColorForeground
= sRGBColor::OpaqueWhite();
90 sRGBColor
nsNativeBasicTheme::sAccentColorLight
= sRGBColor::OpaqueWhite();
91 sRGBColor
nsNativeBasicTheme::sAccentColorDark
= sRGBColor::OpaqueWhite();
92 sRGBColor
nsNativeBasicTheme::sAccentColorDarker
= sRGBColor::OpaqueWhite();
94 void nsNativeBasicTheme::Init() {
95 Preferences::RegisterCallbackAndCall(PrefChangedCallback
,
96 "widget.non-native.use-theme-accent");
99 void nsNativeBasicTheme::Shutdown() {
100 Preferences::UnregisterCallback(PrefChangedCallback
,
101 "widget.non-native.use-theme-accent");
104 void nsNativeBasicTheme::LookAndFeelChanged() { RecomputeAccentColors(); }
106 void nsNativeBasicTheme::RecomputeAccentColors() {
107 MOZ_RELEASE_ASSERT(NS_IsMainThread());
109 if (!StaticPrefs::widget_non_native_theme_use_theme_accent()) {
110 sAccentColorForeground
= sColorWhite
;
112 sRGBColor::UnusualFromARGB(0xff0060df); // Luminance: 13.69346%
114 sRGBColor::UnusualFromARGB(0x4d008deb); // Luminance: 25.04791%
116 sRGBColor::UnusualFromARGB(0xff0250bb); // Luminance: 9.33808%
118 sRGBColor::UnusualFromARGB(0xff054096); // Luminance: 5.90106%
122 sAccentColorForeground
= sRGBColor::FromABGR(ThemedAccentColor(false));
123 const nscolor accent
= ThemedAccentColor(true);
124 const float luminance
= RelativeLuminanceUtils::Compute(accent
);
126 constexpr float kLightLuminanceScale
= 25.048f
/ 13.693f
;
127 constexpr float kDarkLuminanceScale
= 9.338f
/ 13.693f
;
128 constexpr float kDarkerLuminanceScale
= 5.901f
/ 13.693f
;
130 const float lightLuminanceAdjust
=
131 ScaleLuminanceBy(luminance
, kLightLuminanceScale
);
132 const float darkLuminanceAdjust
=
133 ScaleLuminanceBy(luminance
, kDarkLuminanceScale
);
134 const float darkerLuminanceAdjust
=
135 ScaleLuminanceBy(luminance
, kDarkerLuminanceScale
);
137 sAccentColor
= sRGBColor::FromABGR(accent
);
141 RelativeLuminanceUtils::Adjust(accent
, lightLuminanceAdjust
);
142 lightColor
= NS_RGBA(NS_GET_R(lightColor
), NS_GET_G(lightColor
),
143 NS_GET_B(lightColor
), 0x4d);
144 sAccentColorLight
= sRGBColor::FromABGR(lightColor
);
147 sAccentColorDark
= sRGBColor::FromABGR(
148 RelativeLuminanceUtils::Adjust(accent
, darkLuminanceAdjust
));
149 sAccentColorDarker
= sRGBColor::FromABGR(
150 RelativeLuminanceUtils::Adjust(accent
, darkerLuminanceAdjust
));
153 static bool IsScrollbarWidthThin(nsIFrame
* aFrame
) {
154 ComputedStyle
* style
= nsLayoutUtils::StyleForScrollbar(aFrame
);
155 auto scrollbarWidth
= style
->StyleUIReset()->mScrollbarWidth
;
156 return scrollbarWidth
== StyleScrollbarWidth::Thin
;
159 static sRGBColor
SystemColor(StyleSystemColor aColor
) {
160 return sRGBColor::FromABGR(LookAndFeel::GetColor(aColor
));
163 static std::pair
<sRGBColor
, sRGBColor
> SystemColorPair(
164 StyleSystemColor aFirst
, StyleSystemColor aSecond
) {
165 return std::make_pair(SystemColor(aFirst
), SystemColor(aSecond
));
169 auto nsNativeBasicTheme::GetDPIRatioForScrollbarPart(nsPresContext
* aPc
)
171 return DPIRatio(float(AppUnitsPerCSSPixel()) /
172 aPc
->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
176 auto nsNativeBasicTheme::GetDPIRatio(nsPresContext
* aPc
,
177 StyleAppearance aAppearance
) -> DPIRatio
{
178 // Widgets react to zoom, except scrollbars.
179 if (IsWidgetScrollbarPart(aAppearance
)) {
180 return GetDPIRatioForScrollbarPart(aPc
);
182 return DPIRatio(float(AppUnitsPerCSSPixel()) / aPc
->AppUnitsPerDevPixel());
186 auto nsNativeBasicTheme::GetDPIRatio(nsIFrame
* aFrame
,
187 StyleAppearance aAppearance
) -> DPIRatio
{
188 return GetDPIRatio(aFrame
->PresContext(), aAppearance
);
192 bool nsNativeBasicTheme::IsDateTimeResetButton(nsIFrame
* aFrame
) {
197 nsIFrame
* parent
= aFrame
->GetParent();
198 if (parent
&& (parent
= parent
->GetParent()) &&
199 (parent
= parent
->GetParent())) {
200 nsDateTimeControlFrame
* dateTimeFrame
= do_QueryFrame(parent
);
209 bool nsNativeBasicTheme::IsColorPickerButton(nsIFrame
* aFrame
) {
210 nsColorControlFrame
* colorPickerButton
= do_QueryFrame(aFrame
);
211 return colorPickerButton
;
214 // Checkbox and radio need to preserve aspect-ratio for compat. We also snap the
215 // size to exact device pixels to avoid snapping disorting the circles.
216 static LayoutDeviceRect
CheckBoxRadioRect(const LayoutDeviceRect
& aRect
) {
217 // Place a square rect in the center of aRect.
218 auto size
= std::trunc(std::min(aRect
.width
, aRect
.height
));
219 auto position
= aRect
.Center() - LayoutDevicePoint(size
* 0.5, size
* 0.5);
220 return LayoutDeviceRect(position
, LayoutDeviceSize(size
, size
));
223 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeCheckboxColors(
224 const EventStates
& aState
, StyleAppearance aAppearance
,
225 UseSystemColors aUseSystemColors
) {
226 MOZ_ASSERT(aAppearance
== StyleAppearance::Checkbox
||
227 aAppearance
== StyleAppearance::Radio
);
228 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
229 bool isPressed
= !isDisabled
&& aState
.HasAllStates(NS_EVENT_STATE_HOVER
|
230 NS_EVENT_STATE_ACTIVE
);
231 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
232 bool isChecked
= aState
.HasState(NS_EVENT_STATE_CHECKED
);
233 bool isIndeterminate
= aAppearance
== StyleAppearance::Checkbox
&&
234 aState
.HasState(NS_EVENT_STATE_INDETERMINATE
);
236 if (bool(aUseSystemColors
)) {
237 sRGBColor backgroundColor
= SystemColor(StyleSystemColor::Buttonface
);
238 sRGBColor borderColor
= SystemColor(StyleSystemColor::Buttontext
);
240 borderColor
= SystemColor(StyleSystemColor::Graytext
);
241 if (isChecked
|| isIndeterminate
) {
242 backgroundColor
= borderColor
;
244 } else if (isChecked
|| isIndeterminate
) {
245 backgroundColor
= borderColor
= SystemColor(StyleSystemColor::Highlight
);
247 return {backgroundColor
, borderColor
};
250 sRGBColor backgroundColor
= sColorWhite
;
251 sRGBColor borderColor
= sColorGrey40
;
253 if (isChecked
|| isIndeterminate
) {
254 backgroundColor
= borderColor
= sColorGrey40Alpha50
;
256 backgroundColor
= sColorWhiteAlpha50
;
257 borderColor
= sColorGrey40Alpha50
;
260 if (isChecked
|| isIndeterminate
) {
261 const auto& color
= isPressed
? sAccentColorDarker
262 : isHovered
? sAccentColorDark
264 backgroundColor
= borderColor
= color
;
265 } else if (isPressed
) {
266 backgroundColor
= sColorGrey20
;
267 borderColor
= sColorGrey60
;
268 } else if (isHovered
) {
269 backgroundColor
= sColorWhite
;
270 borderColor
= sColorGrey50
;
272 backgroundColor
= sColorWhite
;
273 borderColor
= sColorGrey40
;
277 return std::make_pair(backgroundColor
, borderColor
);
280 sRGBColor
nsNativeBasicTheme::ComputeCheckmarkColor(
281 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
282 if (bool(aUseSystemColors
)) {
283 return SystemColor(StyleSystemColor::Highlighttext
);
285 if (aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
286 return sColorWhiteAlpha50
;
288 return sAccentColorForeground
;
291 sRGBColor
nsNativeBasicTheme::ComputeBorderColor(
292 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
293 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
294 if (bool(aUseSystemColors
)) {
295 return SystemColor(isDisabled
? StyleSystemColor::Graytext
296 : StyleSystemColor::Buttontext
);
299 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
300 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
301 bool isFocused
= aState
.HasState(NS_EVENT_STATE_FOCUSRING
);
303 return sColorGrey40Alpha50
;
306 // We draw the outline over the border for all controls that call into this,
307 // so to prevent issues where the border shows underneath if it snaps in the
308 // wrong direction, we use a transparent border. An alternative to this is
309 // ensuring that we snap the offset in PaintRoundedFocusRect the same was a
310 // we snap border widths, so that negative offsets are guaranteed to cover
311 // the border. But this looks harder to mess up.
323 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeButtonColors(
324 const EventStates
& aState
, UseSystemColors aUseSystemColors
,
327 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
328 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
329 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
331 const sRGBColor backgroundColor
= [&] {
332 if (bool(aUseSystemColors
)) {
333 return SystemColor(StyleSystemColor::Buttonface
);
337 return sColorGrey10Alpha50
;
339 if (IsDateTimeResetButton(aFrame
)) {
351 const sRGBColor borderColor
= ComputeBorderColor(aState
, aUseSystemColors
);
352 return std::make_pair(backgroundColor
, borderColor
);
355 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeTextfieldColors(
356 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
357 const sRGBColor backgroundColor
= [&] {
358 if (bool(aUseSystemColors
)) {
359 return SystemColor(StyleSystemColor::TextBackground
);
361 if (aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
362 return sColorWhiteAlpha50
;
366 const sRGBColor borderColor
= ComputeBorderColor(aState
, aUseSystemColors
);
367 return std::make_pair(backgroundColor
, borderColor
);
370 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeRangeProgressColors(
371 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
372 if (bool(aUseSystemColors
)) {
373 return SystemColorPair(StyleSystemColor::Highlight
,
374 StyleSystemColor::Buttontext
);
378 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
379 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
380 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
383 return std::make_pair(sColorGrey40Alpha50
, sColorGrey40Alpha50
);
385 if (isActive
|| isHovered
) {
386 return std::make_pair(sAccentColorDark
, sAccentColorDarker
);
388 return std::make_pair(sAccentColor
, sAccentColorDark
);
391 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeRangeTrackColors(
392 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
393 if (bool(aUseSystemColors
)) {
394 return SystemColorPair(StyleSystemColor::TextBackground
,
395 StyleSystemColor::Buttontext
);
398 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
399 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
400 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
403 return std::make_pair(sColorGrey10Alpha50
, sColorGrey40Alpha50
);
405 if (isActive
|| isHovered
) {
406 return std::make_pair(sColorGrey20
, sColorGrey50
);
408 return std::make_pair(sColorGrey10
, sColorGrey40
);
411 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeRangeThumbColors(
412 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
413 if (bool(aUseSystemColors
)) {
414 return SystemColorPair(StyleSystemColor::Highlighttext
,
415 StyleSystemColor::Highlight
);
419 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
420 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
421 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
423 const sRGBColor
& backgroundColor
= [&] {
436 const sRGBColor borderColor
= sColorWhite
;
438 return std::make_pair(backgroundColor
, borderColor
);
441 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeProgressColors(
442 UseSystemColors aUseSystemColors
) {
443 if (bool(aUseSystemColors
)) {
444 return SystemColorPair(StyleSystemColor::Highlight
,
445 StyleSystemColor::Buttontext
);
447 return std::make_pair(sAccentColor
, sAccentColorDark
);
450 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeProgressTrackColors(
451 UseSystemColors aUseSystemColors
) {
452 if (bool(aUseSystemColors
)) {
453 return SystemColorPair(StyleSystemColor::Buttonface
,
454 StyleSystemColor::Buttontext
);
456 return std::make_pair(sColorGrey10
, sColorGrey40
);
459 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeMeterchunkColors(
460 const EventStates
& aMeterState
, UseSystemColors aUseSystemColors
) {
461 if (bool(aUseSystemColors
)) {
462 return ComputeProgressColors(aUseSystemColors
);
464 sRGBColor borderColor
= sColorMeterGreen20
;
465 sRGBColor chunkColor
= sColorMeterGreen10
;
467 if (aMeterState
.HasState(NS_EVENT_STATE_SUB_OPTIMUM
)) {
468 borderColor
= sColorMeterYellow20
;
469 chunkColor
= sColorMeterYellow10
;
470 } else if (aMeterState
.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM
)) {
471 borderColor
= sColorMeterRed20
;
472 chunkColor
= sColorMeterRed10
;
475 return std::make_pair(chunkColor
, borderColor
);
478 sRGBColor
nsNativeBasicTheme::ComputeMenulistArrowButtonColor(
479 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
480 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
481 if (bool(aUseSystemColors
)) {
482 return SystemColor(isDisabled
? StyleSystemColor::Graytext
483 : StyleSystemColor::TextForeground
);
485 return isDisabled
? sColorGrey60Alpha50
: sColorGrey60
;
488 std::array
<sRGBColor
, 3> nsNativeBasicTheme::ComputeFocusRectColors(
489 UseSystemColors aUseSystemColors
) {
490 if (bool(aUseSystemColors
)) {
491 return {SystemColor(StyleSystemColor::Highlight
),
492 SystemColor(StyleSystemColor::Buttontext
),
493 SystemColor(StyleSystemColor::TextBackground
)};
496 return {sAccentColor
, sColorWhiteAlpha80
, sAccentColorLight
};
499 sRGBColor
nsNativeBasicTheme::ComputeScrollbarColor(
500 nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
501 const EventStates
& aDocumentState
, UseSystemColors aUseSystemColors
) {
502 const nsStyleUI
* ui
= aStyle
.StyleUI();
503 if (bool(aUseSystemColors
)) {
504 return SystemColor(StyleSystemColor::TextBackground
);
506 if (ShouldUseDarkScrollbar(aFrame
, aStyle
)) {
507 return sRGBColor::FromU8(20, 20, 25, 77);
510 if (ui
->mScrollbarColor
.IsColors()) {
511 color
= ui
->mScrollbarColor
.AsColors().track
.CalcColor(aStyle
);
512 } else if (aDocumentState
.HasAllStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE
)) {
513 color
= LookAndFeel::GetColor(LookAndFeel::ColorID::ThemedScrollbarInactive
,
514 sScrollbarColor
.ToABGR());
516 color
= LookAndFeel::GetColor(LookAndFeel::ColorID::ThemedScrollbar
,
517 sScrollbarColor
.ToABGR());
519 return gfx::sRGBColor::FromABGR(color
);
522 nscolor
nsNativeBasicTheme::AdjustUnthemedScrollbarThumbColor(
523 nscolor aFaceColor
, EventStates aStates
) {
524 // In Windows 10, scrollbar thumb has the following colors:
526 // State | Color | Luminance
527 // -------+----------+----------
528 // Normal | Gray 205 | 61.0%
529 // Hover | Gray 166 | 38.1%
530 // Active | Gray 96 | 11.7%
532 // This function is written based on the ratios between the values.
533 bool isActive
= aStates
.HasState(NS_EVENT_STATE_ACTIVE
);
534 bool isHover
= aStates
.HasState(NS_EVENT_STATE_HOVER
);
535 if (!isActive
&& !isHover
) {
538 float luminance
= RelativeLuminanceUtils::Compute(aFaceColor
);
541 luminance
= ScaleLuminanceBy(luminance
, 0.192f
);
544 luminance
= ScaleLuminanceBy(luminance
, 0.625f
);
546 return RelativeLuminanceUtils::Adjust(aFaceColor
, luminance
);
550 nscolor
nsNativeBasicTheme::GetScrollbarButtonColor(nscolor aTrackColor
,
551 EventStates aStates
) {
552 // See numbers in GetScrollbarArrowColor.
553 // This function is written based on ratios between values listed there.
555 bool isActive
= aStates
.HasState(NS_EVENT_STATE_ACTIVE
);
556 bool isHover
= aStates
.HasState(NS_EVENT_STATE_HOVER
);
557 if (!isActive
&& !isHover
) {
560 float luminance
= RelativeLuminanceUtils::Compute(aTrackColor
);
562 if (luminance
>= 0.18f
) {
566 luminance
= std::min(luminance
, 1.0f
);
569 if (luminance
>= 0.18f
) {
575 return RelativeLuminanceUtils::Adjust(aTrackColor
, luminance
);
579 nscolor
nsNativeBasicTheme::GetScrollbarArrowColor(nscolor aButtonColor
) {
580 // In Windows 10 scrollbar, there are several gray colors used:
582 // State | Background (lum) | Arrow | Contrast
583 // -------+------------------+---------+---------
584 // Normal | Gray 240 (87.1%) | Gray 96 | 5.5
585 // Hover | Gray 218 (70.1%) | Black | 15.0
586 // Active | Gray 96 (11.7%) | White | 6.3
588 // Contrast value is computed based on the definition in
589 // https://www.w3.org/TR/WCAG20/#contrast-ratiodef
591 // This function is written based on these values.
593 float luminance
= RelativeLuminanceUtils::Compute(aButtonColor
);
594 // Color with luminance larger than 0.72 has contrast ratio over 4.6
595 // to color with luminance of gray 96, so this value is chosen for
596 // this range. It is the luminance of gray 221.
597 if (luminance
>= 0.72) {
598 // ComputeRelativeLuminanceFromComponents(96). That function cannot
599 // be constexpr because of std::pow.
600 const float GRAY96_LUMINANCE
= 0.117f
;
601 return RelativeLuminanceUtils::Adjust(aButtonColor
, GRAY96_LUMINANCE
);
603 // The contrast ratio of a color to black equals that to white when its
604 // luminance is around 0.18, with a contrast ratio ~4.6 to both sides,
605 // thus the value below. It's the lumanince of gray 118.
606 if (luminance
>= 0.18) {
607 return NS_RGBA(0, 0, 0, NS_GET_A(aButtonColor
));
609 return NS_RGBA(255, 255, 255, NS_GET_A(aButtonColor
));
612 bool nsNativeBasicTheme::ShouldUseDarkScrollbar(nsIFrame
* aFrame
,
613 const ComputedStyle
& aStyle
) {
614 if (StaticPrefs::widget_disable_dark_scrollbar()) {
617 if (aStyle
.StyleUI()->mScrollbarColor
.IsColors()) {
620 return nsNativeTheme::IsDarkBackground(aFrame
);
623 sRGBColor
nsNativeBasicTheme::ComputeScrollbarThumbColor(
624 nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
625 const EventStates
& aElementState
, const EventStates
& aDocumentState
,
626 UseSystemColors aUseSystemColors
) {
627 if (!bool(aUseSystemColors
) && ShouldUseDarkScrollbar(aFrame
, aStyle
)) {
628 return sRGBColor::FromABGR(AdjustUnthemedScrollbarThumbColor(
629 NS_RGBA(249, 249, 250, 102), aElementState
));
631 const nsStyleUI
* ui
= aStyle
.StyleUI();
633 if (ui
->mScrollbarColor
.IsColors()) {
634 return sRGBColor::FromABGR(AdjustUnthemedScrollbarThumbColor(
635 ui
->mScrollbarColor
.AsColors().thumb
.CalcColor(aStyle
), aElementState
));
638 auto systemColor
= [&] {
639 if (aDocumentState
.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE
)) {
640 return StyleSystemColor::ThemedScrollbarThumbInactive
;
642 if (aElementState
.HasState(NS_EVENT_STATE_ACTIVE
)) {
643 if (bool(aUseSystemColors
)) {
644 return StyleSystemColor::Highlight
;
646 return StyleSystemColor::ThemedScrollbarThumbActive
;
648 if (aElementState
.HasState(NS_EVENT_STATE_HOVER
)) {
649 if (bool(aUseSystemColors
)) {
650 return StyleSystemColor::Highlight
;
652 return StyleSystemColor::ThemedScrollbarThumbHover
;
654 if (bool(aUseSystemColors
)) {
655 return StyleSystemColor::TextForeground
;
657 return StyleSystemColor::ThemedScrollbarThumb
;
660 if (NS_FAILED(LookAndFeel::GetColor(systemColor
, &color
))) {
661 color
= AdjustUnthemedScrollbarThumbColor(sScrollbarThumbColor
.ToABGR(),
664 return gfx::sRGBColor::FromABGR(color
);
667 std::pair
<sRGBColor
, sRGBColor
>
668 nsNativeBasicTheme::ComputeScrollbarButtonColors(
669 nsIFrame
* aFrame
, StyleAppearance aAppearance
, const ComputedStyle
& aStyle
,
670 const EventStates
& aElementState
, const EventStates
& aDocumentState
,
671 UseSystemColors aUseSystemColors
) {
672 if (bool(aUseSystemColors
)) {
673 if (aElementState
.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE
|
674 NS_EVENT_STATE_HOVER
)) {
675 return SystemColorPair(StyleSystemColor::Highlight
,
676 StyleSystemColor::Buttonface
);
678 return SystemColorPair(StyleSystemColor::TextBackground
,
679 StyleSystemColor::TextForeground
);
683 ComputeScrollbarColor(aFrame
, aStyle
, aDocumentState
, aUseSystemColors
);
684 nscolor buttonColor
=
685 GetScrollbarButtonColor(trackColor
.ToABGR(), aElementState
);
686 nscolor arrowColor
= GetScrollbarArrowColor(buttonColor
);
687 return {sRGBColor::FromABGR(buttonColor
), sRGBColor::FromABGR(arrowColor
)};
690 static const CSSCoord kInnerFocusOutlineWidth
= 2.0f
;
692 template <typename PaintBackendData
>
693 void nsNativeBasicTheme::PaintRoundedFocusRect(PaintBackendData
& aBackendData
,
694 const LayoutDeviceRect
& aRect
,
695 UseSystemColors aUseSystemColors
,
699 // NOTE(emilio): If the widths or offsets here change, make sure to tweak
700 // the GetWidgetOverflow path for FocusOutline.
701 auto [innerColor
, middleColor
, outerColor
] =
702 ComputeFocusRectColors(aUseSystemColors
);
704 LayoutDeviceRect
focusRect(aRect
);
706 // The focus rect is painted outside of the border area (aRect), see:
708 // data:text/html,<div style="border: 1px solid; outline: 2px solid
711 // But some controls might provide a negative offset to cover the border, if
713 CSSCoord strokeWidth
= kInnerFocusOutlineWidth
;
714 auto strokeWidthDevPx
=
715 LayoutDeviceCoord(SnapBorderWidth(strokeWidth
, aDpiRatio
));
716 CSSCoord strokeRadius
= aRadius
;
717 focusRect
.Inflate(aOffset
* aDpiRatio
+ strokeWidthDevPx
);
719 PaintRoundedRectWithRadius(aBackendData
, focusRect
, sTransparent
, innerColor
,
720 strokeWidth
, strokeRadius
, aDpiRatio
);
722 strokeWidth
= CSSCoord(1.0f
);
723 strokeWidthDevPx
= LayoutDeviceCoord(SnapBorderWidth(strokeWidth
, aDpiRatio
));
724 strokeRadius
+= strokeWidth
;
725 focusRect
.Inflate(strokeWidthDevPx
);
727 PaintRoundedRectWithRadius(aBackendData
, focusRect
, sTransparent
, middleColor
,
728 strokeWidth
, strokeRadius
, aDpiRatio
);
730 strokeWidth
= CSSCoord(2.0f
);
731 strokeWidthDevPx
= LayoutDeviceCoord(SnapBorderWidth(strokeWidth
, aDpiRatio
));
732 strokeRadius
+= strokeWidth
;
733 focusRect
.Inflate(strokeWidthDevPx
);
735 PaintRoundedRectWithRadius(aBackendData
, focusRect
, sTransparent
, outerColor
,
736 strokeWidth
, strokeRadius
, aDpiRatio
);
739 void nsNativeBasicTheme::PaintRoundedRectWithRadius(
740 WebRenderBackendData
& aWrData
, const LayoutDeviceRect
& aRect
,
741 const LayoutDeviceRect
& aClipRect
, const sRGBColor
& aBackgroundColor
,
742 const sRGBColor
& aBorderColor
, CSSCoord aBorderWidth
, CSSCoord aRadius
,
743 DPIRatio aDpiRatio
) {
744 const bool kBackfaceIsVisible
= true;
745 const LayoutDeviceCoord
borderWidth(SnapBorderWidth(aBorderWidth
, aDpiRatio
));
746 const LayoutDeviceCoord
radius(aRadius
* aDpiRatio
);
747 const wr::LayoutRect dest
= wr::ToLayoutRect(aRect
);
748 const wr::LayoutRect clip
= wr::ToLayoutRect(aClipRect
);
750 // Push the background.
751 if (aBackgroundColor
.a
) {
752 auto backgroundColor
= wr::ToColorF(ToDeviceColor(aBackgroundColor
));
753 wr::LayoutRect backgroundRect
= [&] {
754 LayoutDeviceRect bg
= aRect
;
755 bg
.Deflate(borderWidth
);
756 return wr::ToLayoutRect(bg
);
759 aWrData
.mBuilder
.PushRect(backgroundRect
, clip
, kBackfaceIsVisible
,
762 // NOTE(emilio): This follows DisplayListBuilder::PushRoundedRect and
763 // draws the rounded fill as an extra thick rounded border instead of a
764 // rectangle that's clipped to a rounded clip. Refer to that method for a
765 // justification. See bug 1694269.
766 LayoutDeviceCoord backgroundRadius
=
767 std::max(0.0f
, float(radius
) - float(borderWidth
));
768 wr::BorderSide side
= {backgroundColor
, wr::BorderStyle::Solid
};
769 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
770 float h
= backgroundRect
.size
.width
* 0.6f
;
771 float v
= backgroundRect
.size
.height
* 0.6f
;
772 wr::LayoutSideOffsets widths
= {v
, h
, v
, h
};
773 wr::BorderRadius radii
= {{backgroundRadius
, backgroundRadius
},
774 {backgroundRadius
, backgroundRadius
},
775 {backgroundRadius
, backgroundRadius
},
776 {backgroundRadius
, backgroundRadius
}};
777 aWrData
.mBuilder
.PushBorder(backgroundRect
, clip
, kBackfaceIsVisible
,
778 widths
, {sides
, 4}, radii
);
782 if (borderWidth
&& aBorderColor
.a
) {
784 const auto borderColor
= ToDeviceColor(aBorderColor
);
785 const auto side
= wr::ToBorderSide(borderColor
, StyleBorderStyle::Solid
);
786 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
787 const LayoutDeviceSize
sideRadius(radius
, radius
);
789 wr::ToBorderWidths(borderWidth
, borderWidth
, borderWidth
, borderWidth
);
790 const auto wrRadius
=
791 wr::ToBorderRadius(sideRadius
, sideRadius
, sideRadius
, sideRadius
);
792 aWrData
.mBuilder
.PushBorder(dest
, clip
, kBackfaceIsVisible
, widths
,
793 {sides
, 4}, wrRadius
);
797 void nsNativeBasicTheme::FillRect(DrawTarget
& aDt
,
798 const LayoutDeviceRect
& aRect
,
799 const sRGBColor
& aColor
) {
800 aDt
.FillRect(aRect
.ToUnknownRect(), ColorPattern(ToDeviceColor(aColor
)));
803 void nsNativeBasicTheme::FillRect(WebRenderBackendData
& aWrData
,
804 const LayoutDeviceRect
& aRect
,
805 const sRGBColor
& aColor
) {
806 const bool kBackfaceIsVisible
= true;
807 auto dest
= wr::ToLayoutRect(aRect
);
808 aWrData
.mBuilder
.PushRect(dest
, dest
, kBackfaceIsVisible
,
809 wr::ToColorF(ToDeviceColor(aColor
)));
812 void nsNativeBasicTheme::PaintRoundedRectWithRadius(
813 DrawTarget
& aDrawTarget
, const LayoutDeviceRect
& aRect
,
814 const LayoutDeviceRect
& aClipRect
, const sRGBColor
& aBackgroundColor
,
815 const sRGBColor
& aBorderColor
, CSSCoord aBorderWidth
, CSSCoord aRadius
,
816 DPIRatio aDpiRatio
) {
817 const LayoutDeviceCoord
borderWidth(SnapBorderWidth(aBorderWidth
, aDpiRatio
));
818 const bool needsClip
= !(aRect
== aClipRect
);
820 aDrawTarget
.PushClipRect(aClipRect
.ToUnknownRect());
823 LayoutDeviceRect
rect(aRect
);
824 // Deflate the rect by half the border width, so that the middle of the
825 // stroke fills exactly the area we want to fill and not more.
826 rect
.Deflate(borderWidth
* 0.5f
);
828 LayoutDeviceCoord
radius(aRadius
* aDpiRatio
);
829 // Fix up the radius if it's too large with the rect we're going to paint.
831 LayoutDeviceCoord min
= std::min(rect
.width
, rect
.height
);
832 if (radius
* 2.0f
> min
) {
837 Maybe
<ColorPattern
> backgroundPattern
;
838 if (aBackgroundColor
.a
) {
839 backgroundPattern
.emplace(ToDeviceColor(aBackgroundColor
));
841 Maybe
<ColorPattern
> borderPattern
;
842 if (borderWidth
&& aBorderColor
.a
) {
843 borderPattern
.emplace(ToDeviceColor(aBorderColor
));
846 if (borderPattern
|| backgroundPattern
) {
848 RectCornerRadii
radii(radius
, radius
, radius
, radius
);
849 RefPtr
<Path
> roundedRect
=
850 MakePathForRoundedRect(aDrawTarget
, rect
.ToUnknownRect(), radii
);
852 if (backgroundPattern
) {
853 aDrawTarget
.Fill(roundedRect
, *backgroundPattern
);
856 aDrawTarget
.Stroke(roundedRect
, *borderPattern
,
857 StrokeOptions(borderWidth
));
860 if (backgroundPattern
) {
861 aDrawTarget
.FillRect(rect
.ToUnknownRect(), *backgroundPattern
);
864 aDrawTarget
.StrokeRect(rect
.ToUnknownRect(), *borderPattern
,
865 StrokeOptions(borderWidth
));
871 aDrawTarget
.PopClip();
875 void nsNativeBasicTheme::PaintCheckboxControl(DrawTarget
& aDrawTarget
,
876 const LayoutDeviceRect
& aRect
,
877 const EventStates
& aState
,
878 UseSystemColors aUseSystemColors
,
879 DPIRatio aDpiRatio
) {
880 auto [backgroundColor
, borderColor
] = ComputeCheckboxColors(
881 aState
, StyleAppearance::Checkbox
, aUseSystemColors
);
883 const CSSCoord radius
= 2.0f
;
884 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
885 if (backgroundColor
== borderColor
) {
888 PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
889 borderWidth
, radius
, aDpiRatio
);
892 if (aState
.HasState(NS_EVENT_STATE_INDETERMINATE
)) {
893 PaintIndeterminateMark(aDrawTarget
, aRect
, aState
, aUseSystemColors
);
894 } else if (aState
.HasState(NS_EVENT_STATE_CHECKED
)) {
895 PaintCheckMark(aDrawTarget
, aRect
, aState
, aUseSystemColors
);
898 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
899 PaintRoundedFocusRect(aDrawTarget
, aRect
, aUseSystemColors
, aDpiRatio
, 5.0f
,
904 constexpr CSSCoord kCheckboxRadioContentBoxSize
= 10.0f
;
905 constexpr CSSCoord kCheckboxRadioBorderBoxSize
=
906 kCheckboxRadioContentBoxSize
+ kCheckboxRadioBorderWidth
* 2.0f
;
908 // Returns the right scale for points in a aSize x aSize sized box, centered at
909 // 0x0 to fill aRect in the smaller dimension.
910 static float ScaleToFillRect(const LayoutDeviceRect
& aRect
, const float aSize
) {
911 return std::min(aRect
.width
, aRect
.height
) / aSize
;
914 void nsNativeBasicTheme::PaintCheckMark(DrawTarget
& aDrawTarget
,
915 const LayoutDeviceRect
& aRect
,
916 const EventStates
& aState
,
917 UseSystemColors aUseSystemColors
) {
918 // Points come from the coordinates on a 14X14 (kCheckboxRadioBorderBoxSize)
919 // unit box centered at 0,0
920 const float checkPolygonX
[] = {-4.5f
, -1.5f
, -0.5f
, 5.0f
, 4.75f
,
921 3.5f
, -0.5f
, -1.5f
, -3.5f
};
922 const float checkPolygonY
[] = {0.5f
, 4.0f
, 4.0f
, -2.5f
, -4.0f
,
923 -4.0f
, 1.0f
, 1.25f
, -1.0f
};
924 const int32_t checkNumPoints
= sizeof(checkPolygonX
) / sizeof(float);
925 const float scale
= ScaleToFillRect(aRect
, kCheckboxRadioBorderBoxSize
);
926 auto center
= aRect
.Center().ToUnknownPoint();
928 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
929 Point p
= center
+ Point(checkPolygonX
[0] * scale
, checkPolygonY
[0] * scale
);
931 for (int32_t i
= 1; i
< checkNumPoints
; i
++) {
932 p
= center
+ Point(checkPolygonX
[i
] * scale
, checkPolygonY
[i
] * scale
);
935 RefPtr
<Path
> path
= builder
->Finish();
937 sRGBColor fillColor
= ComputeCheckmarkColor(aState
, aUseSystemColors
);
938 aDrawTarget
.Fill(path
, ColorPattern(ToDeviceColor(fillColor
)));
941 void nsNativeBasicTheme::PaintIndeterminateMark(
942 DrawTarget
& aDrawTarget
, const LayoutDeviceRect
& aRect
,
943 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
944 const CSSCoord borderWidth
= 2.0f
;
945 const float scale
= ScaleToFillRect(aRect
, kCheckboxRadioBorderBoxSize
);
947 Rect rect
= aRect
.ToUnknownRect();
948 rect
.y
+= (rect
.height
/ 2) - (borderWidth
* scale
/ 2);
949 rect
.height
= borderWidth
* scale
;
950 rect
.x
+= (borderWidth
* scale
) + (borderWidth
* scale
/ 8);
951 rect
.width
-= ((borderWidth
* scale
) + (borderWidth
* scale
/ 8)) * 2;
953 sRGBColor fillColor
= ComputeCheckmarkColor(aState
, aUseSystemColors
);
954 aDrawTarget
.FillRect(rect
, ColorPattern(ToDeviceColor(fillColor
)));
957 template <typename PaintBackendData
>
958 void nsNativeBasicTheme::PaintStrokedCircle(PaintBackendData
& aPaintData
,
959 const LayoutDeviceRect
& aRect
,
960 const sRGBColor
& aBackgroundColor
,
961 const sRGBColor
& aBorderColor
,
962 const CSSCoord aBorderWidth
,
963 DPIRatio aDpiRatio
) {
964 auto radius
= LayoutDeviceCoord(aRect
.Size().width
) / aDpiRatio
;
965 PaintRoundedRectWithRadius(aPaintData
, aRect
, aBackgroundColor
, aBorderColor
,
966 aBorderWidth
, radius
, aDpiRatio
);
969 void nsNativeBasicTheme::PaintCircleShadow(WebRenderBackendData
& aWrData
,
970 const LayoutDeviceRect
& aBoxRect
,
971 const LayoutDeviceRect
& aClipRect
,
973 const CSSPoint
& aShadowOffset
,
974 CSSCoord aShadowBlurStdDev
,
975 DPIRatio aDpiRatio
) {
976 const bool kBackfaceIsVisible
= true;
977 const LayoutDeviceCoord stdDev
= aShadowBlurStdDev
* aDpiRatio
;
978 const LayoutDevicePoint shadowOffset
= aShadowOffset
* aDpiRatio
;
979 const IntSize inflation
=
980 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
981 LayoutDeviceRect shadowRect
= aBoxRect
;
982 shadowRect
.MoveBy(shadowOffset
);
983 shadowRect
.Inflate(inflation
.width
, inflation
.height
);
984 const auto boxRect
= wr::ToLayoutRect(aBoxRect
);
985 aWrData
.mBuilder
.PushBoxShadow(
986 wr::ToLayoutRect(shadowRect
), wr::ToLayoutRect(aClipRect
),
987 kBackfaceIsVisible
, boxRect
,
988 wr::ToLayoutVector2D(aShadowOffset
* aDpiRatio
),
989 wr::ToColorF(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)), stdDev
,
990 /* aSpread = */ 0.0f
,
991 wr::ToBorderRadius(gfx::RectCornerRadii(aBoxRect
.Size().width
)),
992 wr::BoxShadowClipMode::Outset
);
995 void nsNativeBasicTheme::PaintCircleShadow(DrawTarget
& aDrawTarget
,
996 const LayoutDeviceRect
& aBoxRect
,
997 const LayoutDeviceRect
& aClipRect
,
999 const CSSPoint
& aShadowOffset
,
1000 CSSCoord aShadowBlurStdDev
,
1001 DPIRatio aDpiRatio
) {
1002 Float stdDev
= aShadowBlurStdDev
* aDpiRatio
;
1003 Point offset
= (aShadowOffset
* aDpiRatio
).ToUnknownPoint();
1005 RefPtr
<FilterNode
> blurFilter
=
1006 aDrawTarget
.CreateFilter(FilterType::GAUSSIAN_BLUR
);
1011 blurFilter
->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION
, stdDev
);
1014 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
1015 Rect inflatedRect
= aBoxRect
.ToUnknownRect();
1016 inflatedRect
.Inflate(inflation
.width
, inflation
.height
);
1017 Rect sourceRectInFilterSpace
=
1018 inflatedRect
- aBoxRect
.TopLeft().ToUnknownPoint();
1019 Point destinationPointOfSourceRect
= inflatedRect
.TopLeft() + offset
;
1021 IntSize dtSize
= RoundedToInt(aBoxRect
.Size().ToUnknownSize());
1022 RefPtr
<DrawTarget
> ellipseDT
= aDrawTarget
.CreateSimilarDrawTargetForFilter(
1023 dtSize
, SurfaceFormat::A8
, blurFilter
, blurFilter
,
1024 sourceRectInFilterSpace
, destinationPointOfSourceRect
);
1029 AutoClipRect
clipRect(aDrawTarget
, aClipRect
);
1031 RefPtr
<Path
> ellipse
= MakePathForEllipse(
1032 *ellipseDT
, (aBoxRect
- aBoxRect
.TopLeft()).Center().ToUnknownPoint(),
1033 aBoxRect
.Size().ToUnknownSize());
1034 ellipseDT
->Fill(ellipse
,
1035 ColorPattern(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)));
1036 RefPtr
<SourceSurface
> ellipseSurface
= ellipseDT
->Snapshot();
1038 blurFilter
->SetInput(IN_GAUSSIAN_BLUR_IN
, ellipseSurface
);
1039 aDrawTarget
.DrawFilter(blurFilter
, sourceRectInFilterSpace
,
1040 destinationPointOfSourceRect
);
1043 template <typename PaintBackendData
>
1044 void nsNativeBasicTheme::PaintRadioControl(PaintBackendData
& aPaintData
,
1045 const LayoutDeviceRect
& aRect
,
1046 const EventStates
& aState
,
1047 UseSystemColors aUseSystemColors
,
1048 DPIRatio aDpiRatio
) {
1049 auto [backgroundColor
, borderColor
] =
1050 ComputeCheckboxColors(aState
, StyleAppearance::Radio
, aUseSystemColors
);
1052 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
1053 if (backgroundColor
== borderColor
) {
1056 PaintStrokedCircle(aPaintData
, aRect
, backgroundColor
, borderColor
,
1057 borderWidth
, aDpiRatio
);
1060 if (aState
.HasState(NS_EVENT_STATE_CHECKED
)) {
1061 LayoutDeviceRect
rect(aRect
);
1062 rect
.Deflate(SnapBorderWidth(kCheckboxRadioBorderWidth
, aDpiRatio
));
1064 auto checkColor
= ComputeCheckmarkColor(aState
, aUseSystemColors
);
1065 PaintStrokedCircle(aPaintData
, rect
, backgroundColor
, checkColor
,
1066 kCheckboxRadioBorderWidth
, aDpiRatio
);
1069 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1070 PaintRoundedFocusRect(aPaintData
, aRect
, aUseSystemColors
, aDpiRatio
, 5.0f
,
1075 template <typename PaintBackendData
>
1076 void nsNativeBasicTheme::PaintTextField(PaintBackendData
& aPaintData
,
1077 const LayoutDeviceRect
& aRect
,
1078 const EventStates
& aState
,
1079 UseSystemColors aUseSystemColors
,
1080 DPIRatio aDpiRatio
) {
1081 auto [backgroundColor
, borderColor
] =
1082 ComputeTextfieldColors(aState
, aUseSystemColors
);
1084 const CSSCoord radius
= 2.0f
;
1086 PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
, borderColor
,
1087 kTextFieldBorderWidth
, radius
, aDpiRatio
);
1089 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1090 PaintRoundedFocusRect(aPaintData
, aRect
, aUseSystemColors
, aDpiRatio
,
1091 radius
+ kTextFieldBorderWidth
,
1092 -kTextFieldBorderWidth
);
1096 template <typename PaintBackendData
>
1097 void nsNativeBasicTheme::PaintListbox(PaintBackendData
& aPaintData
,
1098 const LayoutDeviceRect
& aRect
,
1099 const EventStates
& aState
,
1100 UseSystemColors aUseSystemColors
,
1101 DPIRatio aDpiRatio
) {
1102 const CSSCoord radius
= 2.0f
;
1103 auto [backgroundColor
, borderColor
] =
1104 ComputeTextfieldColors(aState
, aUseSystemColors
);
1106 PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
, borderColor
,
1107 kMenulistBorderWidth
, radius
, aDpiRatio
);
1109 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1110 PaintRoundedFocusRect(aPaintData
, aRect
, aUseSystemColors
, aDpiRatio
,
1111 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
1115 template <typename PaintBackendData
>
1116 void nsNativeBasicTheme::PaintMenulist(PaintBackendData
& aDrawTarget
,
1117 const LayoutDeviceRect
& aRect
,
1118 const EventStates
& aState
,
1119 UseSystemColors aUseSystemColors
,
1120 DPIRatio aDpiRatio
) {
1121 const CSSCoord radius
= 4.0f
;
1122 auto [backgroundColor
, borderColor
] =
1123 ComputeButtonColors(aState
, aUseSystemColors
);
1125 PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
1126 kMenulistBorderWidth
, radius
, aDpiRatio
);
1128 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1129 PaintRoundedFocusRect(aDrawTarget
, aRect
, aUseSystemColors
, aDpiRatio
,
1130 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
1134 void nsNativeBasicTheme::PaintArrow(DrawTarget
& aDrawTarget
,
1135 const LayoutDeviceRect
& aRect
,
1136 const float aArrowPolygonX
[],
1137 const float aArrowPolygonY
[],
1138 const float aArrowPolygonSize
,
1139 const int32_t aArrowNumPoints
,
1140 const sRGBColor aFillColor
) {
1141 const float scale
= ScaleToFillRect(aRect
, aArrowPolygonSize
);
1143 auto center
= aRect
.Center().ToUnknownPoint();
1145 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
1147 center
+ Point(aArrowPolygonX
[0] * scale
, aArrowPolygonY
[0] * scale
);
1149 for (int32_t i
= 1; i
< aArrowNumPoints
; i
++) {
1150 p
= center
+ Point(aArrowPolygonX
[i
] * scale
, aArrowPolygonY
[i
] * scale
);
1153 RefPtr
<Path
> path
= builder
->Finish();
1155 aDrawTarget
.Fill(path
, ColorPattern(ToDeviceColor(aFillColor
)));
1158 void nsNativeBasicTheme::PaintMenulistArrowButton(
1159 nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
, const LayoutDeviceRect
& aRect
,
1160 const EventStates
& aState
, UseSystemColors aUseSystemColors
) {
1161 const float kPolygonX
[] = {-4.0f
, -0.5f
, 0.5f
, 4.0f
, 4.0f
,
1162 3.0f
, 0.0f
, 0.0f
, -3.0f
, -4.0f
};
1163 const float kPolygonY
[] = {-1, 3.0f
, 3.0f
, -1.0f
, -2.0f
,
1164 -2.0f
, 1.5f
, 1.5f
, -2.0f
, -2.0f
};
1166 const float kPolygonSize
= kMinimumDropdownArrowButtonWidth
;
1168 sRGBColor arrowColor
=
1169 ComputeMenulistArrowButtonColor(aState
, aUseSystemColors
);
1170 PaintArrow(aDrawTarget
, aRect
, kPolygonX
, kPolygonY
, kPolygonSize
,
1171 ArrayLength(kPolygonX
), arrowColor
);
1174 void nsNativeBasicTheme::PaintSpinnerButton(
1175 nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
, const LayoutDeviceRect
& aRect
,
1176 const EventStates
& aState
, StyleAppearance aAppearance
,
1177 UseSystemColors aUseSystemColors
, DPIRatio aDpiRatio
) {
1178 auto [backgroundColor
, borderColor
] =
1179 ComputeButtonColors(aState
, aUseSystemColors
);
1181 aDrawTarget
.FillRect(aRect
.ToUnknownRect(),
1182 ColorPattern(ToDeviceColor(backgroundColor
)));
1184 const float kPolygonX
[] = {-3.5f
, -0.5f
, 0.5f
, 3.5f
, 3.5f
,
1185 2.5f
, 0.0f
, 0.0f
, -2.5f
, -3.5f
};
1186 float polygonY
[] = {-1.5f
, 1.5f
, 1.5f
, -1.5f
, -2.5f
,
1187 -2.5f
, 0.0f
, 0.0f
, -2.5f
, -2.5f
};
1189 const float kPolygonSize
= kMinimumSpinnerButtonHeight
;
1190 if (aAppearance
== StyleAppearance::SpinnerUpbutton
) {
1191 for (auto& coord
: polygonY
) {
1196 PaintArrow(aDrawTarget
, aRect
, kPolygonX
, polygonY
, kPolygonSize
,
1197 ArrayLength(kPolygonX
), borderColor
);
1200 template <typename PaintBackendData
>
1201 void nsNativeBasicTheme::PaintRange(nsIFrame
* aFrame
,
1202 PaintBackendData
& aPaintData
,
1203 const LayoutDeviceRect
& aRect
,
1204 const EventStates
& aState
,
1205 UseSystemColors aUseSystemColors
,
1206 DPIRatio aDpiRatio
, bool aHorizontal
) {
1207 nsRangeFrame
* rangeFrame
= do_QueryFrame(aFrame
);
1212 double progress
= rangeFrame
->GetValueAsFractionOfRange();
1214 LayoutDeviceRect
thumbRect(0, 0, kMinimumRangeThumbSize
* aDpiRatio
,
1215 kMinimumRangeThumbSize
* aDpiRatio
);
1216 LayoutDeviceRect
progressClipRect(aRect
);
1217 LayoutDeviceRect
trackClipRect(aRect
);
1218 const LayoutDeviceCoord verticalSize
= kRangeHeight
* aDpiRatio
;
1220 rect
.height
= verticalSize
;
1221 rect
.y
= aRect
.y
+ (aRect
.height
- rect
.height
) / 2;
1222 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) / 2;
1224 if (IsFrameRTL(aFrame
)) {
1226 aRect
.x
+ (aRect
.width
- thumbRect
.width
) * (1.0 - progress
);
1227 float midPoint
= thumbRect
.Center().X();
1228 trackClipRect
.SetBoxX(aRect
.X(), midPoint
);
1229 progressClipRect
.SetBoxX(midPoint
, aRect
.XMost());
1231 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) * progress
;
1232 float midPoint
= thumbRect
.Center().X();
1233 progressClipRect
.SetBoxX(aRect
.X(), midPoint
);
1234 trackClipRect
.SetBoxX(midPoint
, aRect
.XMost());
1237 rect
.width
= verticalSize
;
1238 rect
.x
= aRect
.x
+ (aRect
.width
- rect
.width
) / 2;
1239 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) / 2;
1242 aRect
.y
+ (aRect
.height
- thumbRect
.height
) * (1.0 - progress
);
1243 float midPoint
= thumbRect
.Center().Y();
1244 trackClipRect
.SetBoxY(aRect
.Y(), midPoint
);
1245 progressClipRect
.SetBoxY(midPoint
, aRect
.YMost());
1248 const CSSCoord borderWidth
= 1.0f
;
1249 const CSSCoord radius
= 2.0f
;
1251 auto [progressColor
, progressBorderColor
] =
1252 ComputeRangeProgressColors(aState
, aUseSystemColors
);
1253 auto [trackColor
, trackBorderColor
] =
1254 ComputeRangeTrackColors(aState
, aUseSystemColors
);
1256 PaintRoundedRectWithRadius(aPaintData
, rect
, progressClipRect
, progressColor
,
1257 progressBorderColor
, borderWidth
, radius
,
1260 PaintRoundedRectWithRadius(aPaintData
, rect
, trackClipRect
, trackColor
,
1261 trackBorderColor
, borderWidth
, radius
, aDpiRatio
);
1263 if (!aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
1264 // Ensure the shadow doesn't expand outside of our overflow rect declared in
1265 // GetWidgetOverflow().
1266 auto overflowRect
= aRect
;
1267 overflowRect
.Inflate(CSSCoord(6.0f
) * aDpiRatio
);
1269 PaintCircleShadow(aPaintData
, thumbRect
, overflowRect
, 0.3f
,
1270 CSSPoint(0.0f
, 2.0f
), 2.0f
, aDpiRatio
);
1273 // Draw the thumb on top.
1274 const CSSCoord thumbBorderWidth
= 2.0f
;
1275 auto [thumbColor
, thumbBorderColor
] =
1276 ComputeRangeThumbColors(aState
, aUseSystemColors
);
1278 PaintStrokedCircle(aPaintData
, thumbRect
, thumbColor
, thumbBorderColor
,
1279 thumbBorderWidth
, aDpiRatio
);
1281 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1282 PaintRoundedFocusRect(aPaintData
, aRect
, aUseSystemColors
, aDpiRatio
,
1287 template <typename PaintBackendData
>
1288 void nsNativeBasicTheme::PaintProgress(nsIFrame
* aFrame
,
1289 PaintBackendData
& aPaintData
,
1290 const LayoutDeviceRect
& aRect
,
1291 const EventStates
& aState
,
1292 UseSystemColors aUseSystemColors
,
1293 DPIRatio aDpiRatio
, bool aIsMeter
) {
1294 const CSSCoord borderWidth
= 1.0f
;
1295 const CSSCoord radius
= aIsMeter
? 5.0f
: 2.0f
;
1297 LayoutDeviceRect
rect(aRect
);
1298 const LayoutDeviceCoord thickness
=
1299 (aIsMeter
? kMeterHeight
: kProgressbarHeight
) * aDpiRatio
;
1301 const bool isHorizontal
= !nsNativeTheme::IsVerticalProgress(aFrame
);
1303 // Center it vertically.
1304 rect
.y
+= (rect
.height
- thickness
) / 2;
1305 rect
.height
= thickness
;
1307 // Center it horizontally.
1308 rect
.x
+= (rect
.width
- thickness
) / 2;
1309 rect
.width
= thickness
;
1313 // Paint the track, unclipped.
1314 auto [backgroundColor
, borderColor
] =
1315 ComputeProgressTrackColors(aUseSystemColors
);
1316 PaintRoundedRectWithRadius(aPaintData
, rect
, rect
, backgroundColor
,
1317 borderColor
, borderWidth
, radius
, aDpiRatio
);
1320 // Now paint the chunk, clipped as needed.
1321 LayoutDeviceRect clipRect
= rect
;
1322 if (aState
.HasState(NS_EVENT_STATE_INDETERMINATE
)) {
1323 // For indeterminate progress, we paint an animated chunk of 1/3 of the
1326 // Animation speed and math borrowed from GTK.
1327 const LayoutDeviceCoord size
= isHorizontal
? rect
.width
: rect
.height
;
1328 const LayoutDeviceCoord barSize
= size
* 0.3333f
;
1329 const LayoutDeviceCoord travel
= 2.0f
* (size
- barSize
);
1331 // Period equals to travel / pixelsPerMillisecond where pixelsPerMillisecond
1332 // equals progressSize / 1000.0. This is equivalent to 1600.
1333 const unsigned kPeriod
= 1600;
1335 const int t
= PR_IntervalToMilliseconds(PR_IntervalNow()) % kPeriod
;
1336 const LayoutDeviceCoord dx
= travel
* float(t
) / float(kPeriod
);
1338 rect
.width
= barSize
;
1339 rect
.x
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
1341 rect
.height
= barSize
;
1342 rect
.y
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
1345 // Queue the next frame if needed.
1346 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 60)) {
1347 NS_WARNING("Couldn't refresh indeterminate <progress>");
1350 // This is the progress chunk, clip it to the right amount.
1351 double position
= [&] {
1353 auto* meter
= dom::HTMLMeterElement::FromNode(aFrame
->GetContent());
1357 return meter
->Value() / meter
->Max();
1359 auto* progress
= dom::HTMLProgressElement::FromNode(aFrame
->GetContent());
1363 return progress
->Value() / progress
->Max();
1366 double clipWidth
= rect
.width
* position
;
1367 clipRect
.width
= clipWidth
;
1368 if (IsFrameRTL(aFrame
)) {
1369 clipRect
.x
+= rect
.width
- clipWidth
;
1372 double clipHeight
= rect
.height
* position
;
1373 clipRect
.height
= clipHeight
;
1374 clipRect
.y
+= rect
.height
- clipHeight
;
1378 auto [backgroundColor
, borderColor
] =
1379 aIsMeter
? ComputeMeterchunkColors(aState
, aUseSystemColors
)
1380 : ComputeProgressColors(aUseSystemColors
);
1381 PaintRoundedRectWithRadius(aPaintData
, rect
, clipRect
, backgroundColor
,
1382 borderColor
, borderWidth
, radius
, aDpiRatio
);
1385 template <typename PaintBackendData
>
1386 void nsNativeBasicTheme::PaintButton(nsIFrame
* aFrame
,
1387 PaintBackendData
& aPaintData
,
1388 const LayoutDeviceRect
& aRect
,
1389 const EventStates
& aState
,
1390 UseSystemColors aUseSystemColors
,
1391 DPIRatio aDpiRatio
) {
1392 const CSSCoord radius
= 4.0f
;
1393 auto [backgroundColor
, borderColor
] =
1394 ComputeButtonColors(aState
, aUseSystemColors
, aFrame
);
1396 PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
, borderColor
,
1397 kButtonBorderWidth
, radius
, aDpiRatio
);
1399 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1400 PaintRoundedFocusRect(aPaintData
, aRect
, aUseSystemColors
, aDpiRatio
,
1401 radius
+ kButtonBorderWidth
, -kButtonBorderWidth
);
1405 template <typename PaintBackendData
>
1406 bool nsNativeBasicTheme::DoPaintDefaultScrollbarThumb(
1407 PaintBackendData
& aPaintData
, const LayoutDeviceRect
& aRect
,
1408 bool aHorizontal
, nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
1409 const EventStates
& aElementState
, const EventStates
& aDocumentState
,
1410 UseSystemColors aUseSystemColors
, DPIRatio aDpiRatio
) {
1411 sRGBColor thumbColor
= ComputeScrollbarThumbColor(
1412 aFrame
, aStyle
, aElementState
, aDocumentState
, aUseSystemColors
);
1413 FillRect(aPaintData
, aRect
, thumbColor
);
1417 bool nsNativeBasicTheme::PaintScrollbarThumb(
1418 DrawTarget
& aDrawTarget
, const LayoutDeviceRect
& aRect
, bool aHorizontal
,
1419 nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
1420 const EventStates
& aElementState
, const EventStates
& aDocumentState
,
1421 UseSystemColors aUseSystemColors
, DPIRatio aDpiRatio
) {
1422 return DoPaintDefaultScrollbarThumb(aDrawTarget
, aRect
, aHorizontal
, aFrame
,
1423 aStyle
, aElementState
, aDocumentState
,
1424 aUseSystemColors
, aDpiRatio
);
1427 bool nsNativeBasicTheme::PaintScrollbarThumb(
1428 WebRenderBackendData
& aWrData
, const LayoutDeviceRect
& aRect
,
1429 bool aHorizontal
, nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
1430 const EventStates
& aElementState
, const EventStates
& aDocumentState
,
1431 UseSystemColors aUseSystemColors
, DPIRatio aDpiRatio
) {
1432 return DoPaintDefaultScrollbarThumb(aWrData
, aRect
, aHorizontal
, aFrame
,
1433 aStyle
, aElementState
, aDocumentState
,
1434 aUseSystemColors
, aDpiRatio
);
1437 template <typename PaintBackendData
>
1438 bool nsNativeBasicTheme::DoPaintDefaultScrollbar(
1439 PaintBackendData
& aPaintData
, const LayoutDeviceRect
& aRect
,
1440 bool aHorizontal
, nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
1441 const EventStates
& aDocumentState
, UseSystemColors aUseSystemColors
,
1442 DPIRatio aDpiRatio
) {
1443 auto scrollbarColor
=
1444 ComputeScrollbarColor(aFrame
, aStyle
, aDocumentState
, aUseSystemColors
);
1445 FillRect(aPaintData
, aRect
, scrollbarColor
);
1449 bool nsNativeBasicTheme::PaintScrollbar(DrawTarget
& aDrawTarget
,
1450 const LayoutDeviceRect
& aRect
,
1451 bool aHorizontal
, nsIFrame
* aFrame
,
1452 const ComputedStyle
& aStyle
,
1453 const EventStates
& aDocumentState
,
1454 UseSystemColors aUseSystemColors
,
1455 DPIRatio aDpiRatio
) {
1456 return DoPaintDefaultScrollbar(aDrawTarget
, aRect
, aHorizontal
, aFrame
,
1457 aStyle
, aDocumentState
, aUseSystemColors
,
1461 bool nsNativeBasicTheme::PaintScrollbar(WebRenderBackendData
& aWrData
,
1462 const LayoutDeviceRect
& aRect
,
1463 bool aHorizontal
, nsIFrame
* aFrame
,
1464 const ComputedStyle
& aStyle
,
1465 const EventStates
& aDocumentState
,
1466 UseSystemColors aUseSystemColors
,
1467 DPIRatio aDpiRatio
) {
1468 return DoPaintDefaultScrollbar(aWrData
, aRect
, aHorizontal
, aFrame
, aStyle
,
1469 aDocumentState
, aUseSystemColors
, aDpiRatio
);
1472 template <typename PaintBackendData
>
1473 bool nsNativeBasicTheme::DoPaintDefaultScrollCorner(
1474 PaintBackendData
& aPaintData
, const LayoutDeviceRect
& aRect
,
1475 nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
1476 const EventStates
& aDocumentState
, UseSystemColors aUseSystemColors
,
1477 DPIRatio aDpiRatio
) {
1478 auto scrollbarColor
=
1479 ComputeScrollbarColor(aFrame
, aStyle
, aDocumentState
, aUseSystemColors
);
1480 FillRect(aPaintData
, aRect
, scrollbarColor
);
1484 bool nsNativeBasicTheme::PaintScrollCorner(
1485 DrawTarget
& aDrawTarget
, const LayoutDeviceRect
& aRect
, nsIFrame
* aFrame
,
1486 const ComputedStyle
& aStyle
, const EventStates
& aDocumentState
,
1487 UseSystemColors aUseSystemColors
, DPIRatio aDpiRatio
) {
1488 return DoPaintDefaultScrollCorner(aDrawTarget
, aRect
, aFrame
, aStyle
,
1489 aDocumentState
, aUseSystemColors
,
1493 bool nsNativeBasicTheme::PaintScrollCorner(WebRenderBackendData
& aWrData
,
1494 const LayoutDeviceRect
& aRect
,
1496 const ComputedStyle
& aStyle
,
1497 const EventStates
& aDocumentState
,
1498 UseSystemColors aUseSystemColors
,
1499 DPIRatio aDpiRatio
) {
1500 return DoPaintDefaultScrollCorner(aWrData
, aRect
, aFrame
, aStyle
,
1501 aDocumentState
, aUseSystemColors
,
1505 void nsNativeBasicTheme::PaintScrollbarButton(
1506 DrawTarget
& aDrawTarget
, StyleAppearance aAppearance
,
1507 const LayoutDeviceRect
& aRect
, nsIFrame
* aFrame
,
1508 const ComputedStyle
& aStyle
, const EventStates
& aElementState
,
1509 const EventStates
& aDocumentState
, UseSystemColors aUseSystemColors
,
1510 DPIRatio aDpiRatio
) {
1511 auto [buttonColor
, arrowColor
] =
1512 ComputeScrollbarButtonColors(aFrame
, aAppearance
, aStyle
, aElementState
,
1513 aDocumentState
, aUseSystemColors
);
1514 aDrawTarget
.FillRect(aRect
.ToUnknownRect(),
1515 ColorPattern(ToDeviceColor(buttonColor
)));
1517 // Start with Up arrow.
1518 float arrowPolygonX
[] = {-4.0f
, 0.0f
, 4.0f
, 4.0f
, 0.0f
, -4.0f
};
1519 float arrowPolygonY
[] = {0.0f
, -4.0f
, 0.0f
, 3.0f
, -1.0f
, 3.0f
};
1521 const float kPolygonSize
= kMinimumScrollbarSize
;
1523 const int32_t arrowNumPoints
= ArrayLength(arrowPolygonX
);
1524 switch (aAppearance
) {
1525 case StyleAppearance::ScrollbarbuttonUp
:
1527 case StyleAppearance::ScrollbarbuttonDown
:
1528 for (int32_t i
= 0; i
< arrowNumPoints
; i
++) {
1529 arrowPolygonY
[i
] *= -1;
1532 case StyleAppearance::ScrollbarbuttonLeft
:
1533 for (int32_t i
= 0; i
< arrowNumPoints
; i
++) {
1534 int32_t temp
= arrowPolygonX
[i
];
1535 arrowPolygonX
[i
] = arrowPolygonY
[i
];
1536 arrowPolygonY
[i
] = temp
;
1539 case StyleAppearance::ScrollbarbuttonRight
:
1540 for (int32_t i
= 0; i
< arrowNumPoints
; i
++) {
1541 int32_t temp
= arrowPolygonX
[i
];
1542 arrowPolygonX
[i
] = arrowPolygonY
[i
] * -1;
1543 arrowPolygonY
[i
] = temp
;
1549 PaintArrow(aDrawTarget
, aRect
, arrowPolygonX
, arrowPolygonY
, kPolygonSize
,
1550 arrowNumPoints
, arrowColor
);
1554 nsNativeBasicTheme::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
1555 StyleAppearance aAppearance
,
1556 const nsRect
& aRect
,
1557 const nsRect
& /* aDirtyRect */) {
1558 if (!DoDrawWidgetBackground(*aContext
->GetDrawTarget(), aFrame
, aAppearance
,
1560 return NS_ERROR_NOT_IMPLEMENTED
;
1565 bool nsNativeBasicTheme::CreateWebRenderCommandsForWidget(
1566 mozilla::wr::DisplayListBuilder
& aBuilder
,
1567 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
1568 const mozilla::layers::StackingContextHelper
& aSc
,
1569 mozilla::layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
1570 StyleAppearance aAppearance
, const nsRect
& aRect
) {
1571 if (!StaticPrefs::widget_non_native_theme_webrender()) {
1574 WebRenderBackendData data
{aBuilder
, aResources
, aSc
, aManager
};
1575 return DoDrawWidgetBackground(data
, aFrame
, aAppearance
, aRect
);
1578 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1579 nscoord aTwipsPerPixel
, DrawTarget
& aDt
) {
1580 return LayoutDeviceRect::FromUnknownRect(
1581 NSRectToSnappedRect(aRect
, aTwipsPerPixel
, aDt
));
1584 static LayoutDeviceRect
ToSnappedRect(
1585 const nsRect
& aRect
, nscoord aTwipsPerPixel
,
1586 nsNativeBasicTheme::WebRenderBackendData
& aDt
) {
1587 // TODO: Do we need to do any more snapping here?
1588 return LayoutDeviceRect::FromAppUnits(aRect
, aTwipsPerPixel
);
1591 auto nsNativeBasicTheme::ShouldUseSystemColors(const dom::Document
& aDoc
)
1592 -> UseSystemColors
{
1593 // TODO: Do we really want to use system colors even when the page can
1594 // override the high contrast theme? (mUseDocumentColors = true?).
1595 return UseSystemColors(
1596 PreferenceSheet::PrefsFor(aDoc
).NonNativeThemeShouldUseSystemColors());
1599 template <typename PaintBackendData
>
1600 bool nsNativeBasicTheme::DoDrawWidgetBackground(PaintBackendData
& aPaintData
,
1602 StyleAppearance aAppearance
,
1603 const nsRect
& aRect
) {
1604 static_assert(std::is_same_v
<PaintBackendData
, DrawTarget
> ||
1605 std::is_same_v
<PaintBackendData
, WebRenderBackendData
>);
1607 const nsPresContext
* pc
= aFrame
->PresContext();
1608 const nscoord twipsPerPixel
= pc
->AppUnitsPerDevPixel();
1609 const auto devPxRect
= ToSnappedRect(aRect
, twipsPerPixel
, aPaintData
);
1611 const EventStates docState
= pc
->Document()->GetDocumentState();
1612 const auto useSystemColors
= ShouldUseSystemColors(*pc
->Document());
1613 EventStates eventState
= GetContentState(aFrame
, aAppearance
);
1614 if (aAppearance
== StyleAppearance::MozMenulistArrowButton
) {
1615 bool isHTML
= IsHTMLContent(aFrame
);
1616 nsIFrame
* parentFrame
= aFrame
->GetParent();
1617 bool isMenulist
= !isHTML
&& parentFrame
->IsMenuFrame();
1618 // HTML select and XUL menulist dropdown buttons get state from the
1620 if (isHTML
|| isMenulist
) {
1621 aFrame
= parentFrame
;
1622 eventState
= GetContentState(parentFrame
, aAppearance
);
1626 // Hack to avoid skia fuzziness: Add a dummy clip if the widget doesn't
1627 // overflow devPxRect.
1628 Maybe
<AutoClipRect
> maybeClipRect
;
1629 if constexpr (std::is_same_v
<PaintBackendData
, DrawTarget
>) {
1630 if (aAppearance
!= StyleAppearance::FocusOutline
&&
1631 aAppearance
!= StyleAppearance::Range
&&
1632 !eventState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1633 maybeClipRect
.emplace(aPaintData
, devPxRect
);
1637 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1639 switch (aAppearance
) {
1640 case StyleAppearance::Radio
: {
1641 auto rect
= CheckBoxRadioRect(devPxRect
);
1642 PaintRadioControl(aPaintData
, rect
, eventState
, useSystemColors
,
1646 case StyleAppearance::Checkbox
: {
1647 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1648 // TODO: Need to figure out how to best draw this using WR.
1651 auto rect
= CheckBoxRadioRect(devPxRect
);
1652 PaintCheckboxControl(aPaintData
, rect
, eventState
, useSystemColors
,
1657 case StyleAppearance::Textarea
:
1658 case StyleAppearance::Textfield
:
1659 case StyleAppearance::NumberInput
:
1660 PaintTextField(aPaintData
, devPxRect
, eventState
, useSystemColors
,
1663 case StyleAppearance::Listbox
:
1664 PaintListbox(aPaintData
, devPxRect
, eventState
, useSystemColors
,
1667 case StyleAppearance::MenulistButton
:
1668 case StyleAppearance::Menulist
:
1669 PaintMenulist(aPaintData
, devPxRect
, eventState
, useSystemColors
,
1672 case StyleAppearance::MozMenulistArrowButton
:
1673 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1674 // TODO: Need to figure out how to best draw this using WR.
1677 PaintMenulistArrowButton(aFrame
, aPaintData
, devPxRect
, eventState
,
1681 case StyleAppearance::SpinnerUpbutton
:
1682 case StyleAppearance::SpinnerDownbutton
:
1683 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1684 // TODO: Need to figure out how to best draw this using WR.
1687 PaintSpinnerButton(aFrame
, aPaintData
, devPxRect
, eventState
,
1688 aAppearance
, useSystemColors
, dpiRatio
);
1691 case StyleAppearance::Range
:
1692 PaintRange(aFrame
, aPaintData
, devPxRect
, eventState
, useSystemColors
,
1693 dpiRatio
, IsRangeHorizontal(aFrame
));
1695 case StyleAppearance::RangeThumb
:
1696 // Painted as part of StyleAppearance::Range.
1698 case StyleAppearance::ProgressBar
:
1699 PaintProgress(aFrame
, aPaintData
, devPxRect
, eventState
, useSystemColors
,
1701 /* aIsMeter = */ false);
1703 case StyleAppearance::Progresschunk
:
1704 /* Painted as part of the progress bar */
1706 case StyleAppearance::Meter
:
1707 PaintProgress(aFrame
, aPaintData
, devPxRect
, eventState
, useSystemColors
,
1708 dpiRatio
, /* aIsMeter = */ true);
1710 case StyleAppearance::Meterchunk
:
1711 /* Painted as part of the meter bar */
1713 case StyleAppearance::ScrollbarthumbHorizontal
:
1714 case StyleAppearance::ScrollbarthumbVertical
: {
1716 aAppearance
== StyleAppearance::ScrollbarthumbHorizontal
;
1717 return PaintScrollbarThumb(aPaintData
, devPxRect
, isHorizontal
, aFrame
,
1718 *nsLayoutUtils::StyleForScrollbar(aFrame
),
1719 eventState
, docState
, useSystemColors
,
1722 case StyleAppearance::ScrollbartrackHorizontal
:
1723 case StyleAppearance::ScrollbartrackVertical
: {
1725 aAppearance
== StyleAppearance::ScrollbartrackHorizontal
;
1726 return PaintScrollbarTrack(aPaintData
, devPxRect
, isHorizontal
, aFrame
,
1727 *nsLayoutUtils::StyleForScrollbar(aFrame
),
1728 docState
, useSystemColors
, dpiRatio
);
1730 case StyleAppearance::ScrollbarHorizontal
:
1731 case StyleAppearance::ScrollbarVertical
: {
1732 bool isHorizontal
= aAppearance
== StyleAppearance::ScrollbarHorizontal
;
1733 return PaintScrollbar(aPaintData
, devPxRect
, isHorizontal
, aFrame
,
1734 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
,
1735 useSystemColors
, dpiRatio
);
1737 case StyleAppearance::Scrollcorner
:
1738 return PaintScrollCorner(aPaintData
, devPxRect
, aFrame
,
1739 *nsLayoutUtils::StyleForScrollbar(aFrame
),
1740 docState
, useSystemColors
, dpiRatio
);
1741 case StyleAppearance::ScrollbarbuttonUp
:
1742 case StyleAppearance::ScrollbarbuttonDown
:
1743 case StyleAppearance::ScrollbarbuttonLeft
:
1744 case StyleAppearance::ScrollbarbuttonRight
:
1745 // For scrollbar-width:thin, we don't display the buttons.
1746 if (!IsScrollbarWidthThin(aFrame
)) {
1747 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1748 // TODO: Need to figure out how to best draw this using WR.
1751 PaintScrollbarButton(aPaintData
, aAppearance
, devPxRect
, aFrame
,
1752 *nsLayoutUtils::StyleForScrollbar(aFrame
),
1753 eventState
, docState
, useSystemColors
, dpiRatio
);
1757 case StyleAppearance::Button
:
1758 PaintButton(aFrame
, aPaintData
, devPxRect
, eventState
, useSystemColors
,
1761 case StyleAppearance::FocusOutline
:
1762 PaintAutoStyleOutline(aFrame
, aPaintData
, devPxRect
, useSystemColors
,
1766 // Various appearance values are used for XUL elements. Normally these
1767 // will not be available in content documents (and thus in the content
1768 // processes where the native basic theme can be used), but tests are
1769 // run with the remote XUL pref enabled and so we can get in here. So
1770 // we just return an error rather than assert.
1777 template <typename PaintBackendData
>
1778 void nsNativeBasicTheme::PaintAutoStyleOutline(nsIFrame
* aFrame
,
1779 PaintBackendData
& aPaintData
,
1780 const LayoutDeviceRect
& aRect
,
1781 UseSystemColors aUseSystemColors
,
1782 DPIRatio aDpiRatio
) {
1783 auto [innerColor
, middleColor
, outerColor
] =
1784 ComputeFocusRectColors(aUseSystemColors
);
1785 Unused
<< middleColor
;
1786 Unused
<< outerColor
;
1788 LayoutDeviceRect
rect(aRect
);
1790 LayoutDeviceCoord(SnapBorderWidth(kInnerFocusOutlineWidth
, aDpiRatio
));
1791 rect
.Inflate(width
);
1793 nscoord cssRadii
[8];
1794 if (!aFrame
->GetBorderRadii(cssRadii
)) {
1795 return PaintRoundedRectWithRadius(aPaintData
, rect
, sRGBColor::White(0.0f
),
1796 innerColor
, kInnerFocusOutlineWidth
,
1797 /* aRadius = */ 0.0f
, aDpiRatio
);
1800 nsPresContext
* pc
= aFrame
->PresContext();
1801 const nscoord offset
= aFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits();
1802 const Float devPixelOffset
= pc
->AppUnitsToFloatDevPixels(offset
);
1804 RectCornerRadii innerRadii
;
1805 nsCSSRendering::ComputePixelRadii(cssRadii
, pc
->AppUnitsPerDevPixel(),
1808 const auto borderColor
= ToDeviceColor(innerColor
);
1809 // NOTE(emilio): This doesn't use PaintRoundedRectWithRadius because we need
1810 // to support arbitrary radii.
1811 RectCornerRadii outerRadii
;
1812 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1813 const Float widths
[4] = {devPixelOffset
, devPixelOffset
, devPixelOffset
,
1815 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1817 const auto dest
= wr::ToLayoutRect(rect
);
1818 const auto side
= wr::ToBorderSide(borderColor
, StyleBorderStyle::Solid
);
1819 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
1820 const bool kBackfaceIsVisible
= true;
1821 const auto wrWidths
= wr::ToBorderWidths(width
, width
, width
, width
);
1822 const auto wrRadius
= wr::ToBorderRadius(outerRadii
);
1823 aPaintData
.mBuilder
.PushBorder(dest
, dest
, kBackfaceIsVisible
, wrWidths
,
1824 {sides
, 4}, wrRadius
);
1826 const LayoutDeviceCoord halfWidth
= width
* 0.5f
;
1827 rect
.Deflate(halfWidth
);
1828 const Float widths
[4] = {
1829 halfWidth
+ devPixelOffset
, halfWidth
+ devPixelOffset
,
1830 halfWidth
+ devPixelOffset
, halfWidth
+ devPixelOffset
};
1831 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1833 MakePathForRoundedRect(aPaintData
, rect
.ToUnknownRect(), outerRadii
);
1834 aPaintData
.Stroke(path
, ColorPattern(borderColor
), StrokeOptions(width
));
1838 LayoutDeviceIntMargin
nsNativeBasicTheme::GetWidgetBorder(
1839 nsDeviceContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1840 switch (aAppearance
) {
1841 case StyleAppearance::Textfield
:
1842 case StyleAppearance::Textarea
:
1843 case StyleAppearance::NumberInput
:
1844 case StyleAppearance::Listbox
:
1845 case StyleAppearance::Menulist
:
1846 case StyleAppearance::MenulistButton
:
1847 case StyleAppearance::Button
:
1848 // Return the border size from the UA sheet, even though what we paint
1849 // doesn't actually match that. We know this is the UA sheet border
1850 // because we disable native theming when different border widths are
1851 // specified by authors, see nsNativeBasicTheme::IsWidgetStyled.
1853 // The Rounded() bit is technically redundant, but needed to appease the
1854 // type system, we should always end up with full device pixels due to
1855 // round_border_to_device_pixels at style time.
1856 return LayoutDeviceIntMargin::FromAppUnits(
1857 aFrame
->StyleBorder()->GetComputedBorder(),
1858 aFrame
->PresContext()->AppUnitsPerDevPixel())
1860 case StyleAppearance::Checkbox
:
1861 case StyleAppearance::Radio
: {
1862 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1863 LayoutDeviceIntCoord w
=
1864 SnapBorderWidth(kCheckboxRadioBorderWidth
, dpiRatio
);
1865 return LayoutDeviceIntMargin(w
, w
, w
, w
);
1868 return LayoutDeviceIntMargin();
1872 bool nsNativeBasicTheme::GetWidgetPadding(nsDeviceContext
* aContext
,
1874 StyleAppearance aAppearance
,
1875 LayoutDeviceIntMargin
* aResult
) {
1876 switch (aAppearance
) {
1877 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1878 // and have a meaningful baseline, so they can't have
1879 // author-specified padding.
1880 case StyleAppearance::Radio
:
1881 case StyleAppearance::Checkbox
:
1882 aResult
->SizeTo(0, 0, 0, 0);
1890 bool nsNativeBasicTheme::GetWidgetOverflow(nsDeviceContext
* aContext
,
1892 StyleAppearance aAppearance
,
1893 nsRect
* aOverflowRect
) {
1894 nsIntMargin overflow
;
1895 switch (aAppearance
) {
1896 case StyleAppearance::FocusOutline
:
1897 // 2px * one segment
1898 overflow
.SizeTo(2, 2, 2, 2);
1900 case StyleAppearance::Radio
:
1901 case StyleAppearance::Checkbox
:
1902 case StyleAppearance::Range
:
1903 // 2px for each outline segment, plus 1px separation, plus we paint with a
1904 // 1px extra offset, so 6px.
1905 overflow
.SizeTo(6, 6, 6, 6);
1907 case StyleAppearance::Textarea
:
1908 case StyleAppearance::Textfield
:
1909 case StyleAppearance::NumberInput
:
1910 case StyleAppearance::Listbox
:
1911 case StyleAppearance::MenulistButton
:
1912 case StyleAppearance::Menulist
:
1913 case StyleAppearance::Button
:
1914 // 2px for each segment, plus 1px separation, but we paint 1px inside
1915 // the border area so 4px overflow.
1916 overflow
.SizeTo(4, 4, 4, 4);
1922 // TODO: This should convert from device pixels to app units, not from CSS
1923 // pixels. And it should take the dpi ratio into account.
1924 // Using CSS pixels can cause the overflow to be too small if the page is
1926 aOverflowRect
->Inflate(nsMargin(CSSPixel::ToAppUnits(overflow
.top
),
1927 CSSPixel::ToAppUnits(overflow
.right
),
1928 CSSPixel::ToAppUnits(overflow
.bottom
),
1929 CSSPixel::ToAppUnits(overflow
.left
)));
1934 auto nsNativeBasicTheme::GetScrollbarSizes(nsPresContext
* aPresContext
,
1935 StyleScrollbarWidth aWidth
, Overlay
)
1937 CSSCoord size
= aWidth
== StyleScrollbarWidth::Thin
1938 ? kMinimumThinScrollbarSize
1939 : kMinimumScrollbarSize
;
1940 LayoutDeviceIntCoord s
=
1941 (size
* GetDPIRatioForScrollbarPart(aPresContext
)).Rounded();
1945 nscoord
nsNativeBasicTheme::GetCheckboxRadioPrefSize() {
1946 return CSSPixel::ToAppUnits(kCheckboxRadioContentBoxSize
);
1950 nsNativeBasicTheme::GetMinimumWidgetSize(nsPresContext
* aPresContext
,
1952 StyleAppearance aAppearance
,
1953 LayoutDeviceIntSize
* aResult
,
1954 bool* aIsOverridable
) {
1955 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1957 aResult
->width
= aResult
->height
= 0;
1958 *aIsOverridable
= true;
1960 switch (aAppearance
) {
1961 case StyleAppearance::Button
:
1962 if (IsColorPickerButton(aFrame
)) {
1963 aResult
->height
= (kMinimumColorPickerHeight
* dpiRatio
).Rounded();
1966 case StyleAppearance::RangeThumb
:
1967 aResult
->SizeTo((kMinimumRangeThumbSize
* dpiRatio
).Rounded(),
1968 (kMinimumRangeThumbSize
* dpiRatio
).Rounded());
1970 case StyleAppearance::MozMenulistArrowButton
:
1971 aResult
->width
= (kMinimumDropdownArrowButtonWidth
* dpiRatio
).Rounded();
1973 case StyleAppearance::SpinnerUpbutton
:
1974 case StyleAppearance::SpinnerDownbutton
:
1975 aResult
->width
= (kMinimumSpinnerButtonWidth
* dpiRatio
).Rounded();
1976 aResult
->height
= (kMinimumSpinnerButtonHeight
* dpiRatio
).Rounded();
1978 case StyleAppearance::ScrollbarbuttonUp
:
1979 case StyleAppearance::ScrollbarbuttonDown
:
1980 case StyleAppearance::ScrollbarbuttonLeft
:
1981 case StyleAppearance::ScrollbarbuttonRight
:
1982 // For scrollbar-width:thin, we don't display the buttons.
1983 if (IsScrollbarWidthThin(aFrame
)) {
1984 aResult
->SizeTo(0, 0);
1988 case StyleAppearance::ScrollbarthumbVertical
:
1989 case StyleAppearance::ScrollbarthumbHorizontal
: {
1990 auto* style
= nsLayoutUtils::StyleForScrollbar(aFrame
);
1991 auto width
= style
->StyleUIReset()->mScrollbarWidth
;
1992 auto sizes
= GetScrollbarSizes(aPresContext
, width
, Overlay::No
);
1993 MOZ_ASSERT(sizes
.mHorizontal
== sizes
.mVertical
);
1994 // TODO: for short scrollbars it could be nice if the thumb could shrink
1996 aResult
->SizeTo(sizes
.mHorizontal
, sizes
.mHorizontal
);
2006 nsITheme::Transparency
nsNativeBasicTheme::GetWidgetTransparency(
2007 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
2008 return eUnknownTransparency
;
2012 nsNativeBasicTheme::WidgetStateChanged(nsIFrame
* aFrame
,
2013 StyleAppearance aAppearance
,
2014 nsAtom
* aAttribute
, bool* aShouldRepaint
,
2015 const nsAttrValue
* aOldValue
) {
2017 // Hover/focus/active changed. Always repaint.
2018 *aShouldRepaint
= true;
2020 // Check the attribute to see if it's relevant.
2021 // disabled, checked, dlgtype, default, etc.
2022 *aShouldRepaint
= false;
2023 if ((aAttribute
== nsGkAtoms::disabled
) ||
2024 (aAttribute
== nsGkAtoms::checked
) ||
2025 (aAttribute
== nsGkAtoms::selected
) ||
2026 (aAttribute
== nsGkAtoms::visuallyselected
) ||
2027 (aAttribute
== nsGkAtoms::menuactive
) ||
2028 (aAttribute
== nsGkAtoms::sortDirection
) ||
2029 (aAttribute
== nsGkAtoms::focused
) ||
2030 (aAttribute
== nsGkAtoms::_default
) ||
2031 (aAttribute
== nsGkAtoms::open
) || (aAttribute
== nsGkAtoms::hover
)) {
2032 *aShouldRepaint
= true;
2040 nsNativeBasicTheme::ThemeChanged() { return NS_OK
; }
2042 bool nsNativeBasicTheme::WidgetAppearanceDependsOnWindowFocus(
2043 StyleAppearance aAppearance
) {
2044 return IsWidgetScrollbarPart(aAppearance
);
2047 nsITheme::ThemeGeometryType
nsNativeBasicTheme::ThemeGeometryTypeForWidget(
2048 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
2049 return eThemeGeometryTypeUnknown
;
2052 bool nsNativeBasicTheme::ThemeSupportsWidget(nsPresContext
* aPresContext
,
2054 StyleAppearance aAppearance
) {
2055 switch (aAppearance
) {
2056 case StyleAppearance::Radio
:
2057 case StyleAppearance::Checkbox
:
2058 case StyleAppearance::FocusOutline
:
2059 case StyleAppearance::Textarea
:
2060 case StyleAppearance::Textfield
:
2061 case StyleAppearance::Range
:
2062 case StyleAppearance::RangeThumb
:
2063 case StyleAppearance::ProgressBar
:
2064 case StyleAppearance::Progresschunk
:
2065 case StyleAppearance::Meter
:
2066 case StyleAppearance::Meterchunk
:
2067 case StyleAppearance::ScrollbarbuttonUp
:
2068 case StyleAppearance::ScrollbarbuttonDown
:
2069 case StyleAppearance::ScrollbarbuttonLeft
:
2070 case StyleAppearance::ScrollbarbuttonRight
:
2071 case StyleAppearance::ScrollbarthumbHorizontal
:
2072 case StyleAppearance::ScrollbarthumbVertical
:
2073 case StyleAppearance::ScrollbartrackHorizontal
:
2074 case StyleAppearance::ScrollbartrackVertical
:
2075 case StyleAppearance::ScrollbarHorizontal
:
2076 case StyleAppearance::ScrollbarVertical
:
2077 case StyleAppearance::Scrollcorner
:
2078 case StyleAppearance::Button
:
2079 case StyleAppearance::Listbox
:
2080 case StyleAppearance::Menulist
:
2081 case StyleAppearance::MenulistButton
:
2082 case StyleAppearance::NumberInput
:
2083 case StyleAppearance::MozMenulistArrowButton
:
2084 case StyleAppearance::SpinnerUpbutton
:
2085 case StyleAppearance::SpinnerDownbutton
:
2086 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
2092 bool nsNativeBasicTheme::WidgetIsContainer(StyleAppearance aAppearance
) {
2093 switch (aAppearance
) {
2094 case StyleAppearance::MozMenulistArrowButton
:
2095 case StyleAppearance::Radio
:
2096 case StyleAppearance::Checkbox
:
2103 bool nsNativeBasicTheme::ThemeDrawsFocusForWidget(StyleAppearance aAppearance
) {
2107 bool nsNativeBasicTheme::ThemeNeedsComboboxDropmarker() { return true; }