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/. */
7 #include "ThemeCocoa.h"
9 #include "mozilla/MathAlgorithms.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/HTMLMeterElement.h"
13 #include "mozilla/dom/HTMLProgressElement.h"
14 #include "mozilla/gfx/Rect.h"
15 #include "mozilla/gfx/Types.h"
16 #include "mozilla/gfx/Filters.h"
17 #include "mozilla/RelativeLuminanceUtils.h"
18 #include "mozilla/StaticPrefs_widget.h"
19 #include "mozilla/webrender/WebRenderAPI.h"
20 #include "nsCSSColorUtils.h"
21 #include "nsCSSRendering.h"
22 #include "nsScrollbarFrame.h"
23 #include "nsIScrollbarMediator.h"
24 #include "nsDeviceContext.h"
25 #include "nsLayoutUtils.h"
26 #include "nsRangeFrame.h"
27 #include "PathHelpers.h"
28 #include "ScrollbarDrawingAndroid.h"
29 #include "ScrollbarDrawingCocoa.h"
30 #include "ScrollbarDrawingGTK.h"
31 #include "ScrollbarDrawingWin.h"
32 #include "ScrollbarDrawingWin11.h"
35 # include "mozilla/WindowsVersion.h"
38 using namespace mozilla
;
39 using namespace mozilla::dom
;
40 using namespace mozilla::gfx
;
41 using namespace mozilla::widget
;
45 static constexpr gfx::sRGBColor
sColorGrey10(
46 gfx::sRGBColor::UnusualFromARGB(0xffe9e9ed));
47 static constexpr gfx::sRGBColor
sColorGrey10Alpha50(
48 gfx::sRGBColor::UnusualFromARGB(0x7fe9e9ed));
49 static constexpr gfx::sRGBColor
sColorGrey20(
50 gfx::sRGBColor::UnusualFromARGB(0xffd0d0d7));
51 static constexpr gfx::sRGBColor
sColorGrey30(
52 gfx::sRGBColor::UnusualFromARGB(0xffb1b1b9));
53 static constexpr gfx::sRGBColor
sColorGrey40(
54 gfx::sRGBColor::UnusualFromARGB(0xff8f8f9d));
55 static constexpr gfx::sRGBColor
sColorGrey40Alpha50(
56 gfx::sRGBColor::UnusualFromARGB(0x7f8f8f9d));
57 static constexpr gfx::sRGBColor
sColorGrey50(
58 gfx::sRGBColor::UnusualFromARGB(0xff676774));
59 static constexpr gfx::sRGBColor
sColorGrey60(
60 gfx::sRGBColor::UnusualFromARGB(0xff484851));
62 static constexpr gfx::sRGBColor
sColorMeterGreen10(
63 gfx::sRGBColor::UnusualFromARGB(0xff00ab60));
64 static constexpr gfx::sRGBColor
sColorMeterGreen20(
65 gfx::sRGBColor::UnusualFromARGB(0xff056139));
66 static constexpr gfx::sRGBColor
sColorMeterYellow10(
67 gfx::sRGBColor::UnusualFromARGB(0xffffbd4f));
68 static constexpr gfx::sRGBColor
sColorMeterYellow20(
69 gfx::sRGBColor::UnusualFromARGB(0xffd2811e));
70 static constexpr gfx::sRGBColor
sColorMeterRed10(
71 gfx::sRGBColor::UnusualFromARGB(0xffe22850));
72 static constexpr gfx::sRGBColor
sColorMeterRed20(
73 gfx::sRGBColor::UnusualFromARGB(0xff810220));
75 static const CSSCoord kMinimumColorPickerHeight
= 32.0f
;
76 static const CSSCoord kMinimumRangeThumbSize
= 20.0f
;
77 static const CSSCoord kMinimumDropdownArrowButtonWidth
= 18.0f
;
78 static const CSSCoord kMinimumSpinnerButtonWidth
= 18.0f
;
79 static const CSSCoord kMinimumSpinnerButtonHeight
= 9.0f
;
80 static const CSSCoord kButtonBorderWidth
= 1.0f
;
81 static const CSSCoord kMenulistBorderWidth
= 1.0f
;
82 static const CSSCoord kTextFieldBorderWidth
= 1.0f
;
83 static const CSSCoord kRangeHeight
= 6.0f
;
84 static const CSSCoord kProgressbarHeight
= 6.0f
;
85 static const CSSCoord kMeterHeight
= 12.0f
;
87 // nsCheckboxRadioFrame takes the bottom of the content box as the baseline.
88 // This border-width makes its baseline 2px under the bottom, which is nice.
89 static constexpr CSSCoord kCheckboxRadioBorderWidth
= 2.0f
;
91 static constexpr sRGBColor sTransparent
= sRGBColor::White(0.0);
93 // This pushes and pops a clip rect to the draw target.
95 // This is done to reduce fuzz in places where we may have antialiasing,
96 // because skia is not clip-invariant: given different clips, it does not
97 // guarantee the same result, even if the painted content doesn't intersect
100 // This is a bit sad, overall, but...
101 struct MOZ_RAII AutoClipRect
{
102 AutoClipRect(DrawTarget
& aDt
, const LayoutDeviceRect
& aRect
) : mDt(aDt
) {
103 mDt
.PushClipRect(aRect
.ToUnknownRect());
106 ~AutoClipRect() { mDt
.PopClip(); }
112 static StaticRefPtr
<Theme
> gNativeInstance
;
113 static StaticRefPtr
<Theme
> gNonNativeInstance
;
114 static StaticRefPtr
<Theme
> gRDMInstance
;
119 already_AddRefed
<Theme
> do_CreateNativeThemeDoNotUseDirectly() {
120 // Android doesn't have a native theme.
121 return do_AddRef(new Theme(Theme::ScrollbarStyle()));
125 already_AddRefed
<nsITheme
> do_GetBasicNativeThemeDoNotUseDirectly() {
126 if (MOZ_UNLIKELY(!gNonNativeInstance
)) {
127 UniquePtr
<ScrollbarDrawing
> scrollbarDrawing
= Theme::ScrollbarStyle();
128 #ifdef MOZ_WIDGET_COCOA
129 gNonNativeInstance
= new ThemeCocoa(std::move(scrollbarDrawing
));
131 gNonNativeInstance
= new Theme(std::move(scrollbarDrawing
));
133 ClearOnShutdown(&gNonNativeInstance
);
135 return do_AddRef(gNonNativeInstance
);
138 already_AddRefed
<nsITheme
> do_GetNativeThemeDoNotUseDirectly() {
139 if (MOZ_UNLIKELY(!gNativeInstance
)) {
140 gNativeInstance
= do_CreateNativeThemeDoNotUseDirectly();
141 ClearOnShutdown(&gNativeInstance
);
143 return do_AddRef(gNativeInstance
);
146 already_AddRefed
<nsITheme
> do_GetRDMThemeDoNotUseDirectly() {
147 if (MOZ_UNLIKELY(!gRDMInstance
)) {
148 gRDMInstance
= new Theme(MakeUnique
<ScrollbarDrawingAndroid
>());
149 ClearOnShutdown(&gRDMInstance
);
151 return do_AddRef(gRDMInstance
);
154 namespace mozilla::widget
{
156 NS_IMPL_ISUPPORTS_INHERITED(Theme
, nsNativeTheme
, nsITheme
)
158 static constexpr nsLiteralCString kPrefs
[] = {
159 "widget.non-native-theme.use-theme-accent"_ns
,
160 "widget.non-native-theme.win.scrollbar.use-system-size"_ns
,
161 "widget.non-native-theme.scrollbar.size.override"_ns
,
162 "widget.non-native-theme.scrollbar.style"_ns
,
166 for (const auto& pref
: kPrefs
) {
167 Preferences::RegisterCallback(PrefChangedCallback
, pref
);
169 LookAndFeelChanged();
172 void Theme::Shutdown() {
173 for (const auto& pref
: kPrefs
) {
174 Preferences::UnregisterCallback(PrefChangedCallback
, pref
);
179 void Theme::LookAndFeelChanged() {
180 ThemeColors::RecomputeAccentColors();
181 if (gNonNativeInstance
) {
182 gNonNativeInstance
->SetScrollbarDrawing(ScrollbarStyle());
184 if (gNativeInstance
) {
185 gNativeInstance
->SetScrollbarDrawing(ScrollbarStyle());
190 auto Theme::GetDPIRatio(nsPresContext
* aPc
, StyleAppearance aAppearance
)
192 // Widgets react to zoom, except scrollbars.
193 if (IsWidgetScrollbarPart(aAppearance
)) {
194 return ScrollbarDrawing::GetDPIRatioForScrollbarPart(aPc
);
196 return DPIRatio(float(AppUnitsPerCSSPixel()) / aPc
->AppUnitsPerDevPixel());
200 auto Theme::GetDPIRatio(nsIFrame
* aFrame
, StyleAppearance aAppearance
)
202 return GetDPIRatio(aFrame
->PresContext(), aAppearance
);
205 // Checkbox and radio need to preserve aspect-ratio for compat. We also snap the
206 // size to exact device pixels to avoid snapping disorting the circles.
207 static LayoutDeviceRect
CheckBoxRadioRect(const LayoutDeviceRect
& aRect
) {
208 // Place a square rect in the center of aRect.
209 auto size
= std::trunc(std::min(aRect
.width
, aRect
.height
));
210 auto position
= aRect
.Center() - LayoutDevicePoint(size
* 0.5, size
* 0.5);
211 return LayoutDeviceRect(position
, LayoutDeviceSize(size
, size
));
214 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeCheckboxColors(
215 const EventStates
& aState
, StyleAppearance aAppearance
,
216 const Colors
& aColors
) {
217 MOZ_ASSERT(aAppearance
== StyleAppearance::Checkbox
||
218 aAppearance
== StyleAppearance::Radio
);
220 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
221 bool isChecked
= aState
.HasState(NS_EVENT_STATE_CHECKED
);
222 bool isIndeterminate
= aAppearance
== StyleAppearance::Checkbox
&&
223 aState
.HasState(NS_EVENT_STATE_INDETERMINATE
);
225 if (isChecked
|| isIndeterminate
) {
227 auto color
= ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::No
);
228 return std::make_pair(color
, color
);
232 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
233 bool isHovered
= aState
.HasState(NS_EVENT_STATE_HOVER
);
234 const auto& color
= isActive
? aColors
.Accent().GetDarker()
235 : isHovered
? aColors
.Accent().GetDark()
236 : aColors
.Accent().Get();
237 return std::make_pair(color
, color
);
240 return ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::No
);
243 sRGBColor
Theme::ComputeCheckmarkColor(const EventStates
& aState
,
244 const Colors
& aColors
) {
245 if (aColors
.HighContrast()) {
246 return aColors
.System(StyleSystemColor::Selecteditemtext
);
248 if (aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
249 return sRGBColor::White(.8f
);
251 return aColors
.Accent().GetForeground();
254 sRGBColor
Theme::ComputeBorderColor(const EventStates
& aState
,
255 const Colors
& aColors
,
256 OutlineCoversBorder aOutlineCoversBorder
) {
257 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
258 if (aColors
.HighContrast()) {
259 return aColors
.System(isDisabled
? StyleSystemColor::Graytext
260 : StyleSystemColor::Buttontext
);
263 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
264 bool isHovered
= aState
.HasState(NS_EVENT_STATE_HOVER
);
265 bool isFocused
= aState
.HasState(NS_EVENT_STATE_FOCUSRING
);
267 return sColorGrey40Alpha50
;
269 if (isFocused
&& aOutlineCoversBorder
== OutlineCoversBorder::Yes
) {
270 // If we draw the outline over the border, prevent issues where the border
271 // shows underneath if it snaps in the wrong direction by using a
272 // transparent border. An alternative to this is ensuring that we snap the
273 // offset in PaintRoundedFocusRect the same was a we snap border widths, so
274 // that negative offsets are guaranteed to cover the border.
275 // But this looks harder to mess up.
278 bool dark
= aColors
.IsDark();
280 return dark
? sColorGrey20
: sColorGrey60
;
283 return dark
? sColorGrey30
: sColorGrey50
;
288 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeButtonColors(
289 const EventStates
& aState
, const Colors
& aColors
, nsIFrame
* aFrame
) {
291 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
292 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
293 bool isHovered
= aState
.HasState(NS_EVENT_STATE_HOVER
);
295 nscolor backgroundColor
= [&] {
297 return aColors
.SystemNs(StyleSystemColor::MozButtondisabledface
);
300 return aColors
.SystemNs(StyleSystemColor::MozButtonactiveface
);
303 return aColors
.SystemNs(StyleSystemColor::MozButtonhoverface
);
305 return aColors
.SystemNs(StyleSystemColor::Buttonface
);
308 if (aState
.HasState(NS_EVENT_STATE_AUTOFILL
)) {
309 backgroundColor
= NS_ComposeColors(
311 aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
));
314 const sRGBColor borderColor
=
315 ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::Yes
);
316 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
319 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeTextfieldColors(
320 const EventStates
& aState
, const Colors
& aColors
,
321 OutlineCoversBorder aOutlineCoversBorder
) {
322 nscolor backgroundColor
= [&] {
323 if (aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
324 return aColors
.SystemNs(StyleSystemColor::MozDisabledfield
);
326 return aColors
.SystemNs(StyleSystemColor::Field
);
329 if (aState
.HasState(NS_EVENT_STATE_AUTOFILL
)) {
330 backgroundColor
= NS_ComposeColors(
332 aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
));
335 const sRGBColor borderColor
=
336 ComputeBorderColor(aState
, aColors
, aOutlineCoversBorder
);
337 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
340 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeProgressColors(
341 const EventStates
& aState
, const Colors
& aColors
) {
342 if (aColors
.HighContrast()) {
343 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
344 StyleSystemColor::Buttontext
);
348 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
349 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
350 bool isHovered
= aState
.HasState(NS_EVENT_STATE_HOVER
);
353 return std::make_pair(sColorGrey40Alpha50
, sColorGrey40Alpha50
);
355 if (isActive
|| isHovered
) {
356 return std::make_pair(aColors
.Accent().GetDark(),
357 aColors
.Accent().GetDarker());
359 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
362 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeTrackColors(
363 const EventStates
& aState
, const Colors
& aColors
) {
364 if (aColors
.HighContrast()) {
365 return aColors
.SystemPair(StyleSystemColor::Window
,
366 StyleSystemColor::Buttontext
);
369 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
370 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
371 bool isHovered
= aState
.HasState(NS_EVENT_STATE_HOVER
);
374 return std::make_pair(sColorGrey10Alpha50
, sColorGrey40Alpha50
);
376 if (isActive
|| isHovered
) {
377 return std::make_pair(sColorGrey20
, sColorGrey50
);
379 return std::make_pair(sColorGrey10
, sColorGrey40
);
382 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeThumbColors(
383 const EventStates
& aState
, const Colors
& aColors
) {
384 if (aColors
.HighContrast()) {
385 return aColors
.SystemPair(StyleSystemColor::Selecteditemtext
,
386 StyleSystemColor::Selecteditem
);
390 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
391 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
392 bool isHovered
= aState
.HasState(NS_EVENT_STATE_HOVER
);
394 const sRGBColor
& backgroundColor
= [&] {
399 return aColors
.Accent().Get();
407 const sRGBColor borderColor
= sRGBColor::OpaqueWhite();
408 return std::make_pair(backgroundColor
, borderColor
);
411 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressColors(
412 const Colors
& aColors
) {
413 if (aColors
.HighContrast()) {
414 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
415 StyleSystemColor::Buttontext
);
417 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
420 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressTrackColors(
421 const Colors
& aColors
) {
422 if (aColors
.HighContrast()) {
423 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
424 StyleSystemColor::Buttontext
);
426 return std::make_pair(sColorGrey10
, sColorGrey40
);
429 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeMeterchunkColors(
430 const EventStates
& aMeterState
, const Colors
& aColors
) {
431 if (aColors
.HighContrast()) {
432 return ComputeProgressColors(aColors
);
434 sRGBColor borderColor
= sColorMeterGreen20
;
435 sRGBColor chunkColor
= sColorMeterGreen10
;
437 if (aMeterState
.HasState(NS_EVENT_STATE_SUB_OPTIMUM
)) {
438 borderColor
= sColorMeterYellow20
;
439 chunkColor
= sColorMeterYellow10
;
440 } else if (aMeterState
.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM
)) {
441 borderColor
= sColorMeterRed20
;
442 chunkColor
= sColorMeterRed10
;
445 return std::make_pair(chunkColor
, borderColor
);
448 std::array
<sRGBColor
, 3> Theme::ComputeFocusRectColors(const Colors
& aColors
) {
449 if (aColors
.HighContrast()) {
450 return {aColors
.System(StyleSystemColor::Selecteditem
),
451 aColors
.System(StyleSystemColor::Buttontext
),
452 aColors
.System(StyleSystemColor::Window
)};
454 const auto& accent
= aColors
.Accent();
455 const sRGBColor middle
=
456 aColors
.IsDark() ? sRGBColor::Black(.3f
) : sRGBColor::White(.3f
);
457 return {accent
.Get(), middle
, accent
.GetLight()};
460 static const CSSCoord kInnerFocusOutlineWidth
= 2.0f
;
462 template <typename PaintBackendData
>
463 void Theme::PaintRoundedFocusRect(PaintBackendData
& aBackendData
,
464 const LayoutDeviceRect
& aRect
,
465 const Colors
& aColors
, DPIRatio aDpiRatio
,
466 CSSCoord aRadius
, CSSCoord aOffset
) {
467 // NOTE(emilio): If the widths or offsets here change, make sure to tweak
468 // the GetWidgetOverflow path for FocusOutline.
469 auto [innerColor
, middleColor
, outerColor
] = ComputeFocusRectColors(aColors
);
471 LayoutDeviceRect
focusRect(aRect
);
473 // The focus rect is painted outside of the border area (aRect), see:
475 // data:text/html,<div style="border: 1px solid; outline: 2px solid
478 // But some controls might provide a negative offset to cover the border, if
480 CSSCoord strokeWidth
= kInnerFocusOutlineWidth
;
481 auto strokeWidthDevPx
=
482 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
483 CSSCoord strokeRadius
= aRadius
;
484 focusRect
.Inflate(aOffset
* aDpiRatio
+ strokeWidthDevPx
);
486 ThemeDrawing::PaintRoundedRectWithRadius(
487 aBackendData
, focusRect
, sTransparent
, innerColor
, strokeWidth
,
488 strokeRadius
, aDpiRatio
);
490 strokeWidth
= CSSCoord(1.0f
);
492 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
493 strokeRadius
+= strokeWidth
;
494 focusRect
.Inflate(strokeWidthDevPx
);
496 ThemeDrawing::PaintRoundedRectWithRadius(
497 aBackendData
, focusRect
, sTransparent
, middleColor
, strokeWidth
,
498 strokeRadius
, aDpiRatio
);
500 strokeWidth
= CSSCoord(2.0f
);
502 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
503 strokeRadius
+= strokeWidth
;
504 focusRect
.Inflate(strokeWidthDevPx
);
506 ThemeDrawing::PaintRoundedRectWithRadius(
507 aBackendData
, focusRect
, sTransparent
, outerColor
, strokeWidth
,
508 strokeRadius
, aDpiRatio
);
511 void Theme::PaintCheckboxControl(DrawTarget
& aDrawTarget
,
512 const LayoutDeviceRect
& aRect
,
513 const EventStates
& aState
,
514 const Colors
& aColors
, DPIRatio aDpiRatio
) {
515 auto [backgroundColor
, borderColor
] =
516 ComputeCheckboxColors(aState
, StyleAppearance::Checkbox
, aColors
);
518 const CSSCoord radius
= 2.0f
;
519 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
520 if (backgroundColor
== borderColor
) {
523 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
,
524 backgroundColor
, borderColor
,
525 borderWidth
, radius
, aDpiRatio
);
528 if (aState
.HasState(NS_EVENT_STATE_INDETERMINATE
)) {
529 PaintIndeterminateMark(aDrawTarget
, aRect
, aState
, aColors
);
530 } else if (aState
.HasState(NS_EVENT_STATE_CHECKED
)) {
531 PaintCheckMark(aDrawTarget
, aRect
, aState
, aColors
);
534 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
535 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
539 constexpr CSSCoord kCheckboxRadioContentBoxSize
= 10.0f
;
540 constexpr CSSCoord kCheckboxRadioBorderBoxSize
=
541 kCheckboxRadioContentBoxSize
+ kCheckboxRadioBorderWidth
* 2.0f
;
543 void Theme::PaintCheckMark(DrawTarget
& aDrawTarget
,
544 const LayoutDeviceRect
& aRect
,
545 const EventStates
& aState
, const Colors
& aColors
) {
546 // Points come from the coordinates on a 14X14 (kCheckboxRadioBorderBoxSize)
547 // unit box centered at 0,0
548 const float checkPolygonX
[] = {-4.5f
, -1.5f
, -0.5f
, 5.0f
, 4.75f
,
549 3.5f
, -0.5f
, -1.5f
, -3.5f
};
550 const float checkPolygonY
[] = {0.5f
, 4.0f
, 4.0f
, -2.5f
, -4.0f
,
551 -4.0f
, 1.0f
, 1.25f
, -1.0f
};
552 const int32_t checkNumPoints
= sizeof(checkPolygonX
) / sizeof(float);
554 ThemeDrawing::ScaleToFillRect(aRect
, kCheckboxRadioBorderBoxSize
);
555 auto center
= aRect
.Center().ToUnknownPoint();
557 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
558 Point p
= center
+ Point(checkPolygonX
[0] * scale
, checkPolygonY
[0] * scale
);
560 for (int32_t i
= 1; i
< checkNumPoints
; i
++) {
561 p
= center
+ Point(checkPolygonX
[i
] * scale
, checkPolygonY
[i
] * scale
);
564 RefPtr
<Path
> path
= builder
->Finish();
566 sRGBColor fillColor
= ComputeCheckmarkColor(aState
, aColors
);
567 aDrawTarget
.Fill(path
, ColorPattern(ToDeviceColor(fillColor
)));
570 void Theme::PaintIndeterminateMark(DrawTarget
& aDrawTarget
,
571 const LayoutDeviceRect
& aRect
,
572 const EventStates
& aState
,
573 const Colors
& aColors
) {
574 const CSSCoord borderWidth
= 2.0f
;
576 ThemeDrawing::ScaleToFillRect(aRect
, kCheckboxRadioBorderBoxSize
);
578 Rect rect
= aRect
.ToUnknownRect();
579 rect
.y
+= (rect
.height
/ 2) - (borderWidth
* scale
/ 2);
580 rect
.height
= borderWidth
* scale
;
581 rect
.x
+= (borderWidth
* scale
) + (borderWidth
* scale
/ 8);
582 rect
.width
-= ((borderWidth
* scale
) + (borderWidth
* scale
/ 8)) * 2;
584 sRGBColor fillColor
= ComputeCheckmarkColor(aState
, aColors
);
585 aDrawTarget
.FillRect(rect
, ColorPattern(ToDeviceColor(fillColor
)));
588 template <typename PaintBackendData
>
589 void Theme::PaintStrokedCircle(PaintBackendData
& aPaintData
,
590 const LayoutDeviceRect
& aRect
,
591 const sRGBColor
& aBackgroundColor
,
592 const sRGBColor
& aBorderColor
,
593 const CSSCoord aBorderWidth
,
594 DPIRatio aDpiRatio
) {
595 auto radius
= LayoutDeviceCoord(aRect
.Size().width
) / aDpiRatio
;
596 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, aBackgroundColor
,
597 aBorderColor
, aBorderWidth
, radius
,
601 void Theme::PaintCircleShadow(WebRenderBackendData
& aWrData
,
602 const LayoutDeviceRect
& aBoxRect
,
603 const LayoutDeviceRect
& aClipRect
,
604 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
605 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
606 const bool kBackfaceIsVisible
= true;
607 const LayoutDeviceCoord stdDev
= aShadowBlurStdDev
* aDpiRatio
;
608 const LayoutDevicePoint shadowOffset
= aShadowOffset
* aDpiRatio
;
609 const IntSize inflation
=
610 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
611 LayoutDeviceRect shadowRect
= aBoxRect
;
612 shadowRect
.MoveBy(shadowOffset
);
613 shadowRect
.Inflate(inflation
.width
, inflation
.height
);
614 const auto boxRect
= wr::ToLayoutRect(aBoxRect
);
615 aWrData
.mBuilder
.PushBoxShadow(
616 wr::ToLayoutRect(shadowRect
), wr::ToLayoutRect(aClipRect
),
617 kBackfaceIsVisible
, boxRect
,
618 wr::ToLayoutVector2D(aShadowOffset
* aDpiRatio
),
619 wr::ToColorF(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)), stdDev
,
620 /* aSpread = */ 0.0f
,
621 wr::ToBorderRadius(gfx::RectCornerRadii(aBoxRect
.Size().width
)),
622 wr::BoxShadowClipMode::Outset
);
625 void Theme::PaintCircleShadow(DrawTarget
& aDrawTarget
,
626 const LayoutDeviceRect
& aBoxRect
,
627 const LayoutDeviceRect
& aClipRect
,
628 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
629 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
630 Float stdDev
= aShadowBlurStdDev
* aDpiRatio
;
631 Point offset
= (aShadowOffset
* aDpiRatio
).ToUnknownPoint();
633 RefPtr
<FilterNode
> blurFilter
=
634 aDrawTarget
.CreateFilter(FilterType::GAUSSIAN_BLUR
);
639 blurFilter
->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION
, stdDev
);
642 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
643 Rect inflatedRect
= aBoxRect
.ToUnknownRect();
644 inflatedRect
.Inflate(inflation
.width
, inflation
.height
);
645 Rect sourceRectInFilterSpace
=
646 inflatedRect
- aBoxRect
.TopLeft().ToUnknownPoint();
647 Point destinationPointOfSourceRect
= inflatedRect
.TopLeft() + offset
;
649 IntSize dtSize
= RoundedToInt(aBoxRect
.Size().ToUnknownSize());
650 RefPtr
<DrawTarget
> ellipseDT
= aDrawTarget
.CreateSimilarDrawTargetForFilter(
651 dtSize
, SurfaceFormat::A8
, blurFilter
, blurFilter
,
652 sourceRectInFilterSpace
, destinationPointOfSourceRect
);
657 AutoClipRect
clipRect(aDrawTarget
, aClipRect
);
659 RefPtr
<Path
> ellipse
= MakePathForEllipse(
660 *ellipseDT
, (aBoxRect
- aBoxRect
.TopLeft()).Center().ToUnknownPoint(),
661 aBoxRect
.Size().ToUnknownSize());
662 ellipseDT
->Fill(ellipse
,
663 ColorPattern(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)));
664 RefPtr
<SourceSurface
> ellipseSurface
= ellipseDT
->Snapshot();
666 blurFilter
->SetInput(IN_GAUSSIAN_BLUR_IN
, ellipseSurface
);
667 aDrawTarget
.DrawFilter(blurFilter
, sourceRectInFilterSpace
,
668 destinationPointOfSourceRect
);
671 template <typename PaintBackendData
>
672 void Theme::PaintRadioControl(PaintBackendData
& aPaintData
,
673 const LayoutDeviceRect
& aRect
,
674 const EventStates
& aState
, const Colors
& aColors
,
675 DPIRatio aDpiRatio
) {
676 auto [backgroundColor
, borderColor
] =
677 ComputeCheckboxColors(aState
, StyleAppearance::Radio
, aColors
);
679 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
680 if (backgroundColor
== borderColor
) {
683 PaintStrokedCircle(aPaintData
, aRect
, backgroundColor
, borderColor
,
684 borderWidth
, aDpiRatio
);
687 if (aState
.HasState(NS_EVENT_STATE_CHECKED
)) {
688 LayoutDeviceRect
rect(aRect
);
689 auto width
= LayoutDeviceCoord(
690 ThemeDrawing::SnapBorderWidth(kCheckboxRadioBorderWidth
, aDpiRatio
));
693 auto checkColor
= ComputeCheckmarkColor(aState
, aColors
);
694 PaintStrokedCircle(aPaintData
, rect
, backgroundColor
, checkColor
,
695 kCheckboxRadioBorderWidth
, aDpiRatio
);
698 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
699 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
703 template <typename PaintBackendData
>
704 void Theme::PaintTextField(PaintBackendData
& aPaintData
,
705 const LayoutDeviceRect
& aRect
,
706 const EventStates
& aState
, const Colors
& aColors
,
707 DPIRatio aDpiRatio
) {
708 auto [backgroundColor
, borderColor
] =
709 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
711 const CSSCoord radius
= 2.0f
;
713 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
714 borderColor
, kTextFieldBorderWidth
,
717 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
718 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
719 radius
+ kTextFieldBorderWidth
,
720 -kTextFieldBorderWidth
);
724 template <typename PaintBackendData
>
725 void Theme::PaintListbox(PaintBackendData
& aPaintData
,
726 const LayoutDeviceRect
& aRect
,
727 const EventStates
& aState
, const Colors
& aColors
,
728 DPIRatio aDpiRatio
) {
729 const CSSCoord radius
= 2.0f
;
730 auto [backgroundColor
, borderColor
] =
731 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
733 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
734 borderColor
, kMenulistBorderWidth
,
737 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
738 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
739 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
743 template <typename PaintBackendData
>
744 void Theme::PaintMenulist(PaintBackendData
& aDrawTarget
,
745 const LayoutDeviceRect
& aRect
,
746 const EventStates
& aState
, const Colors
& aColors
,
747 DPIRatio aDpiRatio
) {
748 const CSSCoord radius
= 4.0f
;
749 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
751 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
,
752 borderColor
, kMenulistBorderWidth
,
755 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
756 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
,
757 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
761 void Theme::PaintMenulistArrowButton(nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
,
762 const LayoutDeviceRect
& aRect
,
763 const EventStates
& aState
) {
764 const float kPolygonX
[] = {-4.0f
, -0.5f
, 0.5f
, 4.0f
, 4.0f
,
765 3.0f
, 0.0f
, 0.0f
, -3.0f
, -4.0f
};
766 const float kPolygonY
[] = {-1, 3.0f
, 3.0f
, -1.0f
, -2.0f
,
767 -2.0f
, 1.5f
, 1.5f
, -2.0f
, -2.0f
};
769 const float kPolygonSize
= kMinimumDropdownArrowButtonWidth
;
771 const auto arrowColor
= sRGBColor::FromABGR(
772 nsLayoutUtils::GetColor(aFrame
, &nsStyleText::mWebkitTextFillColor
));
773 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, kPolygonX
, kPolygonY
,
774 kPolygonSize
, ArrayLength(kPolygonX
), arrowColor
);
777 void Theme::PaintSpinnerButton(nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
,
778 const LayoutDeviceRect
& aRect
,
779 const EventStates
& aState
,
780 StyleAppearance aAppearance
,
781 const Colors
& aColors
, DPIRatio aDpiRatio
) {
782 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
784 aDrawTarget
.FillRect(aRect
.ToUnknownRect(),
785 ColorPattern(ToDeviceColor(backgroundColor
)));
787 const float kPolygonX
[] = {-3.5f
, -0.5f
, 0.5f
, 3.5f
, 3.5f
,
788 2.5f
, 0.0f
, 0.0f
, -2.5f
, -3.5f
};
789 float polygonY
[] = {-1.5f
, 1.5f
, 1.5f
, -1.5f
, -2.5f
,
790 -2.5f
, 0.0f
, 0.0f
, -2.5f
, -2.5f
};
792 const float kPolygonSize
= kMinimumSpinnerButtonHeight
;
793 if (aAppearance
== StyleAppearance::SpinnerUpbutton
) {
794 for (auto& coord
: polygonY
) {
799 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, kPolygonX
, polygonY
,
800 kPolygonSize
, ArrayLength(kPolygonX
), borderColor
);
803 template <typename PaintBackendData
>
804 void Theme::PaintRange(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
805 const LayoutDeviceRect
& aRect
, const EventStates
& aState
,
806 const Colors
& aColors
, DPIRatio aDpiRatio
,
808 nsRangeFrame
* rangeFrame
= do_QueryFrame(aFrame
);
813 double progress
= rangeFrame
->GetValueAsFractionOfRange();
815 LayoutDeviceRect
thumbRect(0, 0, kMinimumRangeThumbSize
* aDpiRatio
,
816 kMinimumRangeThumbSize
* aDpiRatio
);
817 LayoutDeviceRect
progressClipRect(aRect
);
818 LayoutDeviceRect
trackClipRect(aRect
);
819 const LayoutDeviceCoord verticalSize
= kRangeHeight
* aDpiRatio
;
821 rect
.height
= verticalSize
;
822 rect
.y
= aRect
.y
+ (aRect
.height
- rect
.height
) / 2;
823 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) / 2;
825 if (IsFrameRTL(aFrame
)) {
827 aRect
.x
+ (aRect
.width
- thumbRect
.width
) * (1.0 - progress
);
828 float midPoint
= thumbRect
.Center().X();
829 trackClipRect
.SetBoxX(aRect
.X(), midPoint
);
830 progressClipRect
.SetBoxX(midPoint
, aRect
.XMost());
832 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) * progress
;
833 float midPoint
= thumbRect
.Center().X();
834 progressClipRect
.SetBoxX(aRect
.X(), midPoint
);
835 trackClipRect
.SetBoxX(midPoint
, aRect
.XMost());
838 rect
.width
= verticalSize
;
839 rect
.x
= aRect
.x
+ (aRect
.width
- rect
.width
) / 2;
840 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) / 2;
843 aRect
.y
+ (aRect
.height
- thumbRect
.height
) * (1.0 - progress
);
844 float midPoint
= thumbRect
.Center().Y();
845 trackClipRect
.SetBoxY(aRect
.Y(), midPoint
);
846 progressClipRect
.SetBoxY(midPoint
, aRect
.YMost());
849 const CSSCoord borderWidth
= 1.0f
;
850 const CSSCoord radius
= 3.0f
;
852 auto [progressColor
, progressBorderColor
] =
853 ComputeRangeProgressColors(aState
, aColors
);
854 auto [trackColor
, trackBorderColor
] =
855 ComputeRangeTrackColors(aState
, aColors
);
857 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, progressClipRect
,
858 progressColor
, progressBorderColor
,
859 borderWidth
, radius
, aDpiRatio
);
861 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, trackClipRect
,
862 trackColor
, trackBorderColor
,
863 borderWidth
, radius
, aDpiRatio
);
865 if (!aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
866 // Ensure the shadow doesn't expand outside of our overflow rect declared in
867 // GetWidgetOverflow().
868 auto overflowRect
= aRect
;
869 overflowRect
.Inflate(CSSCoord(6.0f
) * aDpiRatio
);
871 PaintCircleShadow(aPaintData
, thumbRect
, overflowRect
, 0.3f
,
872 CSSPoint(0.0f
, 2.0f
), 2.0f
, aDpiRatio
);
875 // Draw the thumb on top.
876 const CSSCoord thumbBorderWidth
= 2.0f
;
877 auto [thumbColor
, thumbBorderColor
] =
878 ComputeRangeThumbColors(aState
, aColors
);
880 PaintStrokedCircle(aPaintData
, thumbRect
, thumbColor
, thumbBorderColor
,
881 thumbBorderWidth
, aDpiRatio
);
883 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
884 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, radius
, 1.0f
);
888 template <typename PaintBackendData
>
889 void Theme::PaintProgress(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
890 const LayoutDeviceRect
& aRect
,
891 const EventStates
& aState
, const Colors
& aColors
,
892 DPIRatio aDpiRatio
, bool aIsMeter
) {
893 const CSSCoord borderWidth
= 1.0f
;
894 const CSSCoord radius
= aIsMeter
? 6.0f
: 3.0f
;
896 LayoutDeviceRect
rect(aRect
);
897 const LayoutDeviceCoord thickness
=
898 (aIsMeter
? kMeterHeight
: kProgressbarHeight
) * aDpiRatio
;
900 const bool isHorizontal
= !nsNativeTheme::IsVerticalProgress(aFrame
);
902 // Center it vertically.
903 rect
.y
+= (rect
.height
- thickness
) / 2;
904 rect
.height
= thickness
;
906 // Center it horizontally.
907 rect
.x
+= (rect
.width
- thickness
) / 2;
908 rect
.width
= thickness
;
912 // Paint the track, unclipped.
913 auto [backgroundColor
, borderColor
] = ComputeProgressTrackColors(aColors
);
914 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, rect
,
915 backgroundColor
, borderColor
,
916 borderWidth
, radius
, aDpiRatio
);
919 // Now paint the chunk, clipped as needed.
920 LayoutDeviceRect clipRect
= rect
;
921 if (aState
.HasState(NS_EVENT_STATE_INDETERMINATE
)) {
922 // For indeterminate progress, we paint an animated chunk of 1/3 of the
925 // Animation speed and math borrowed from GTK.
926 const LayoutDeviceCoord size
= isHorizontal
? rect
.width
: rect
.height
;
927 const LayoutDeviceCoord barSize
= size
* 0.3333f
;
928 const LayoutDeviceCoord travel
= 2.0f
* (size
- barSize
);
930 // Period equals to travel / pixelsPerMillisecond where pixelsPerMillisecond
931 // equals progressSize / 1000.0. This is equivalent to 1600.
932 const unsigned kPeriod
= 1600;
934 const int t
= PR_IntervalToMilliseconds(PR_IntervalNow()) % kPeriod
;
935 const LayoutDeviceCoord dx
= travel
* float(t
) / float(kPeriod
);
937 rect
.width
= barSize
;
938 rect
.x
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
940 rect
.height
= barSize
;
941 rect
.y
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
944 // Queue the next frame if needed.
945 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 60)) {
946 NS_WARNING("Couldn't refresh indeterminate <progress>");
949 // This is the progress chunk, clip it to the right amount.
950 double position
= [&] {
952 auto* meter
= dom::HTMLMeterElement::FromNode(aFrame
->GetContent());
956 return meter
->Position();
958 auto* progress
= dom::HTMLProgressElement::FromNode(aFrame
->GetContent());
962 return progress
->Position();
965 double clipWidth
= rect
.width
* position
;
966 clipRect
.width
= clipWidth
;
967 if (IsFrameRTL(aFrame
)) {
968 clipRect
.x
+= rect
.width
- clipWidth
;
971 double clipHeight
= rect
.height
* position
;
972 clipRect
.height
= clipHeight
;
973 clipRect
.y
+= rect
.height
- clipHeight
;
977 auto [backgroundColor
, borderColor
] =
978 aIsMeter
? ComputeMeterchunkColors(aState
, aColors
)
979 : ComputeProgressColors(aColors
);
980 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, clipRect
,
981 backgroundColor
, borderColor
,
982 borderWidth
, radius
, aDpiRatio
);
985 template <typename PaintBackendData
>
986 void Theme::PaintButton(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
987 const LayoutDeviceRect
& aRect
,
988 const EventStates
& aState
, const Colors
& aColors
,
989 DPIRatio aDpiRatio
) {
990 const CSSCoord radius
= 4.0f
;
991 auto [backgroundColor
, borderColor
] =
992 ComputeButtonColors(aState
, aColors
, aFrame
);
994 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
995 borderColor
, kButtonBorderWidth
,
998 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
999 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
1000 radius
+ kButtonBorderWidth
, -kButtonBorderWidth
);
1005 Theme::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
1006 StyleAppearance aAppearance
, const nsRect
& aRect
,
1007 const nsRect
& /* aDirtyRect */,
1008 DrawOverflow aDrawOverflow
) {
1009 if (!DoDrawWidgetBackground(*aContext
->GetDrawTarget(), aFrame
, aAppearance
,
1010 aRect
, aDrawOverflow
)) {
1011 return NS_ERROR_NOT_IMPLEMENTED
;
1016 bool Theme::CreateWebRenderCommandsForWidget(
1017 mozilla::wr::DisplayListBuilder
& aBuilder
,
1018 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
1019 const mozilla::layers::StackingContextHelper
& aSc
,
1020 mozilla::layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
1021 StyleAppearance aAppearance
, const nsRect
& aRect
) {
1022 if (!StaticPrefs::widget_non_native_theme_webrender()) {
1025 WebRenderBackendData data
{aBuilder
, aResources
, aSc
, aManager
};
1026 return DoDrawWidgetBackground(data
, aFrame
, aAppearance
, aRect
,
1030 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1031 nscoord aTwipsPerPixel
, DrawTarget
& aDt
) {
1032 return LayoutDeviceRect::FromUnknownRect(
1033 NSRectToSnappedRect(aRect
, aTwipsPerPixel
, aDt
));
1036 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1037 nscoord aTwipsPerPixel
,
1038 WebRenderBackendData
& aDt
) {
1039 // TODO: Do we need to do any more snapping here?
1040 return LayoutDeviceRect::FromAppUnits(aRect
, aTwipsPerPixel
);
1043 static ScrollbarDrawing::ScrollbarKind
ComputeScrollbarKind(
1044 nsIFrame
* aFrame
, bool aIsHorizontal
) {
1045 if (aIsHorizontal
) {
1046 return ScrollbarDrawing::ScrollbarKind::Horizontal
;
1048 nsIFrame
* scrollbar
= ScrollbarDrawing::GetParentScrollbarFrame(aFrame
);
1049 if (NS_WARN_IF(!scrollbar
)) {
1050 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1052 MOZ_ASSERT(scrollbar
->IsScrollbarFrame());
1053 nsIScrollbarMediator
* sm
=
1054 static_cast<nsScrollbarFrame
*>(scrollbar
)->GetScrollbarMediator();
1055 if (NS_WARN_IF(!sm
)) {
1056 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1058 return sm
->IsScrollbarOnRight()
1059 ? ScrollbarDrawing::ScrollbarKind::VerticalRight
1060 : ScrollbarDrawing::ScrollbarKind::VerticalLeft
;
1063 template <typename PaintBackendData
>
1064 bool Theme::DoDrawWidgetBackground(PaintBackendData
& aPaintData
,
1066 StyleAppearance aAppearance
,
1067 const nsRect
& aRect
,
1068 DrawOverflow aDrawOverflow
) {
1069 static_assert(std::is_same_v
<PaintBackendData
, DrawTarget
> ||
1070 std::is_same_v
<PaintBackendData
, WebRenderBackendData
>);
1072 const nsPresContext
* pc
= aFrame
->PresContext();
1073 const nscoord twipsPerPixel
= pc
->AppUnitsPerDevPixel();
1074 const auto devPxRect
= ToSnappedRect(aRect
, twipsPerPixel
, aPaintData
);
1076 const EventStates docState
= pc
->Document()->GetDocumentState();
1077 EventStates eventState
= GetContentState(aFrame
, aAppearance
);
1078 if (aAppearance
== StyleAppearance::MozMenulistArrowButton
) {
1079 bool isHTML
= IsHTMLContent(aFrame
);
1080 nsIFrame
* parentFrame
= aFrame
->GetParent();
1081 bool isMenulist
= !isHTML
&& parentFrame
->IsMenuFrame();
1082 // HTML select and XUL menulist dropdown buttons get state from the
1084 if (isHTML
|| isMenulist
) {
1085 aFrame
= parentFrame
;
1086 eventState
= GetContentState(parentFrame
, aAppearance
);
1090 // Paint the outline iff we're asked to draw overflow and we have
1091 // outline-style: auto.
1092 if (aDrawOverflow
== DrawOverflow::Yes
&&
1093 aFrame
->StyleOutline()->mOutlineStyle
.IsAuto()) {
1094 eventState
|= NS_EVENT_STATE_FOCUSRING
;
1096 eventState
&= ~NS_EVENT_STATE_FOCUSRING
;
1099 // Hack to avoid skia fuzziness: Add a dummy clip if the widget doesn't
1100 // overflow devPxRect.
1101 Maybe
<AutoClipRect
> maybeClipRect
;
1102 if constexpr (std::is_same_v
<PaintBackendData
, DrawTarget
>) {
1103 if (aAppearance
!= StyleAppearance::FocusOutline
&&
1104 aAppearance
!= StyleAppearance::Range
&&
1105 !eventState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1106 maybeClipRect
.emplace(aPaintData
, devPxRect
);
1110 const Colors
colors(aFrame
, aAppearance
);
1111 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1113 switch (aAppearance
) {
1114 case StyleAppearance::Radio
: {
1115 auto rect
= CheckBoxRadioRect(devPxRect
);
1116 PaintRadioControl(aPaintData
, rect
, eventState
, colors
, dpiRatio
);
1119 case StyleAppearance::Checkbox
: {
1120 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1121 // TODO: Need to figure out how to best draw this using WR.
1124 auto rect
= CheckBoxRadioRect(devPxRect
);
1125 PaintCheckboxControl(aPaintData
, rect
, eventState
, colors
, dpiRatio
);
1129 case StyleAppearance::Textarea
:
1130 case StyleAppearance::Textfield
:
1131 case StyleAppearance::NumberInput
:
1132 PaintTextField(aPaintData
, devPxRect
, eventState
, colors
, dpiRatio
);
1134 case StyleAppearance::Listbox
:
1135 PaintListbox(aPaintData
, devPxRect
, eventState
, colors
, dpiRatio
);
1137 case StyleAppearance::MenulistButton
:
1138 case StyleAppearance::Menulist
:
1139 PaintMenulist(aPaintData
, devPxRect
, eventState
, colors
, dpiRatio
);
1141 case StyleAppearance::MozMenulistArrowButton
:
1142 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1143 // TODO: Need to figure out how to best draw this using WR.
1146 PaintMenulistArrowButton(aFrame
, aPaintData
, devPxRect
, eventState
);
1149 case StyleAppearance::SpinnerUpbutton
:
1150 case StyleAppearance::SpinnerDownbutton
:
1151 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1152 // TODO: Need to figure out how to best draw this using WR.
1155 PaintSpinnerButton(aFrame
, aPaintData
, devPxRect
, eventState
,
1156 aAppearance
, colors
, dpiRatio
);
1159 case StyleAppearance::Range
:
1160 PaintRange(aFrame
, aPaintData
, devPxRect
, eventState
, colors
, dpiRatio
,
1161 IsRangeHorizontal(aFrame
));
1163 case StyleAppearance::RangeThumb
:
1164 // Painted as part of StyleAppearance::Range.
1166 case StyleAppearance::ProgressBar
:
1167 PaintProgress(aFrame
, aPaintData
, devPxRect
, eventState
, colors
, dpiRatio
,
1168 /* aIsMeter = */ false);
1170 case StyleAppearance::Progresschunk
:
1171 /* Painted as part of the progress bar */
1173 case StyleAppearance::Meter
:
1174 PaintProgress(aFrame
, aPaintData
, devPxRect
, eventState
, colors
, dpiRatio
,
1175 /* aIsMeter = */ true);
1177 case StyleAppearance::Meterchunk
:
1178 /* Painted as part of the meter bar */
1180 case StyleAppearance::ScrollbarthumbHorizontal
:
1181 case StyleAppearance::ScrollbarthumbVertical
: {
1183 aAppearance
== StyleAppearance::ScrollbarthumbHorizontal
;
1184 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1185 return GetScrollbarDrawing().PaintScrollbarThumb(
1186 aPaintData
, devPxRect
, kind
, aFrame
,
1187 *nsLayoutUtils::StyleForScrollbar(aFrame
), eventState
, docState
,
1190 case StyleAppearance::ScrollbartrackHorizontal
:
1191 case StyleAppearance::ScrollbartrackVertical
: {
1193 aAppearance
== StyleAppearance::ScrollbartrackHorizontal
;
1194 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1195 return GetScrollbarDrawing().PaintScrollbarTrack(
1196 aPaintData
, devPxRect
, kind
, aFrame
,
1197 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
, colors
,
1200 case StyleAppearance::ScrollbarHorizontal
:
1201 case StyleAppearance::ScrollbarVertical
: {
1202 bool isHorizontal
= aAppearance
== StyleAppearance::ScrollbarHorizontal
;
1203 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1204 return GetScrollbarDrawing().PaintScrollbar(
1205 aPaintData
, devPxRect
, kind
, aFrame
,
1206 *nsLayoutUtils::StyleForScrollbar(aFrame
), eventState
, docState
,
1209 case StyleAppearance::Scrollcorner
: {
1210 auto kind
= ComputeScrollbarKind(aFrame
, false);
1211 return GetScrollbarDrawing().PaintScrollCorner(
1212 aPaintData
, devPxRect
, kind
, aFrame
,
1213 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
, colors
,
1216 case StyleAppearance::ScrollbarbuttonUp
:
1217 case StyleAppearance::ScrollbarbuttonDown
:
1218 case StyleAppearance::ScrollbarbuttonLeft
:
1219 case StyleAppearance::ScrollbarbuttonRight
: {
1220 // For scrollbar-width:thin, we don't display the buttons.
1221 if (!ScrollbarDrawing::IsScrollbarWidthThin(aFrame
)) {
1222 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1223 // TODO: Need to figure out how to best draw this using WR.
1227 aAppearance
== StyleAppearance::ScrollbarbuttonLeft
||
1228 aAppearance
== StyleAppearance::ScrollbarbuttonRight
;
1229 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1230 GetScrollbarDrawing().PaintScrollbarButton(
1231 aPaintData
, aAppearance
, devPxRect
, kind
, aFrame
,
1232 *nsLayoutUtils::StyleForScrollbar(aFrame
), eventState
, docState
,
1238 case StyleAppearance::Button
:
1239 PaintButton(aFrame
, aPaintData
, devPxRect
, eventState
, colors
, dpiRatio
);
1241 case StyleAppearance::FocusOutline
:
1242 PaintAutoStyleOutline(aFrame
, aPaintData
, devPxRect
, colors
, dpiRatio
);
1245 // Various appearance values are used for XUL elements. Normally these
1246 // will not be available in content documents (and thus in the content
1247 // processes where the native basic theme can be used), but tests are
1248 // run with the remote XUL pref enabled and so we can get in here. So
1249 // we just return an error rather than assert.
1256 template <typename PaintBackendData
>
1257 void Theme::PaintAutoStyleOutline(nsIFrame
* aFrame
,
1258 PaintBackendData
& aPaintData
,
1259 const LayoutDeviceRect
& aRect
,
1260 const Colors
& aColors
, DPIRatio aDpiRatio
) {
1261 auto [innerColor
, middleColor
, outerColor
] = ComputeFocusRectColors(aColors
);
1262 Unused
<< middleColor
;
1263 Unused
<< outerColor
;
1265 LayoutDeviceRect
rect(aRect
);
1266 auto width
= LayoutDeviceCoord(
1267 ThemeDrawing::SnapBorderWidth(kInnerFocusOutlineWidth
, aDpiRatio
));
1268 rect
.Inflate(width
);
1270 const nscoord offset
= aFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits();
1271 nscoord cssRadii
[8];
1272 if (!aFrame
->GetBorderRadii(cssRadii
)) {
1273 const CSSCoord cssOffset
= CSSCoord::FromAppUnits(offset
);
1274 const CSSCoord radius
=
1276 ? kInnerFocusOutlineWidth
1277 : std::max(kInnerFocusOutlineWidth
+ cssOffset
, CSSCoord(0.0f
));
1278 return ThemeDrawing::PaintRoundedRectWithRadius(
1279 aPaintData
, rect
, sRGBColor::White(0.0f
), innerColor
,
1280 kInnerFocusOutlineWidth
, radius
, aDpiRatio
);
1283 nsPresContext
* pc
= aFrame
->PresContext();
1284 const Float devPixelOffset
= pc
->AppUnitsToFloatDevPixels(offset
);
1286 RectCornerRadii innerRadii
;
1287 nsCSSRendering::ComputePixelRadii(cssRadii
, pc
->AppUnitsPerDevPixel(),
1290 const auto borderColor
= ToDeviceColor(innerColor
);
1291 // NOTE(emilio): This doesn't use PaintRoundedRectWithRadius because we need
1292 // to support arbitrary radii.
1293 RectCornerRadii outerRadii
;
1294 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1295 const Float widths
[4] = {width
+ devPixelOffset
, width
+ devPixelOffset
,
1296 width
+ devPixelOffset
, width
+ devPixelOffset
};
1297 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1299 const auto dest
= wr::ToLayoutRect(rect
);
1300 const auto side
= wr::ToBorderSide(borderColor
, StyleBorderStyle::Solid
);
1301 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
1302 const bool kBackfaceIsVisible
= true;
1303 const auto wrWidths
= wr::ToBorderWidths(width
, width
, width
, width
);
1304 const auto wrRadius
= wr::ToBorderRadius(outerRadii
);
1305 aPaintData
.mBuilder
.PushBorder(dest
, dest
, kBackfaceIsVisible
, wrWidths
,
1306 {sides
, 4}, wrRadius
);
1308 const LayoutDeviceCoord halfWidth
= width
* 0.5f
;
1309 rect
.Deflate(halfWidth
);
1310 const Float widths
[4] = {
1311 halfWidth
+ devPixelOffset
, halfWidth
+ devPixelOffset
,
1312 halfWidth
+ devPixelOffset
, halfWidth
+ devPixelOffset
};
1313 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1315 MakePathForRoundedRect(aPaintData
, rect
.ToUnknownRect(), outerRadii
);
1316 aPaintData
.Stroke(path
, ColorPattern(borderColor
), StrokeOptions(width
));
1320 LayoutDeviceIntMargin
Theme::GetWidgetBorder(nsDeviceContext
* aContext
,
1322 StyleAppearance aAppearance
) {
1323 switch (aAppearance
) {
1324 case StyleAppearance::Textfield
:
1325 case StyleAppearance::Textarea
:
1326 case StyleAppearance::NumberInput
:
1327 case StyleAppearance::Listbox
:
1328 case StyleAppearance::Menulist
:
1329 case StyleAppearance::MenulistButton
:
1330 case StyleAppearance::Button
:
1331 // Return the border size from the UA sheet, even though what we paint
1332 // doesn't actually match that. We know this is the UA sheet border
1333 // because we disable native theming when different border widths are
1334 // specified by authors, see Theme::IsWidgetStyled.
1336 // The Rounded() bit is technically redundant, but needed to appease the
1337 // type system, we should always end up with full device pixels due to
1338 // round_border_to_device_pixels at style time.
1339 return LayoutDeviceIntMargin::FromAppUnits(
1340 aFrame
->StyleBorder()->GetComputedBorder(),
1341 aFrame
->PresContext()->AppUnitsPerDevPixel())
1343 case StyleAppearance::Checkbox
:
1344 case StyleAppearance::Radio
: {
1345 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1346 LayoutDeviceIntCoord w
=
1347 ThemeDrawing::SnapBorderWidth(kCheckboxRadioBorderWidth
, dpiRatio
);
1348 return LayoutDeviceIntMargin(w
, w
, w
, w
);
1351 return LayoutDeviceIntMargin();
1355 bool Theme::GetWidgetPadding(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1356 StyleAppearance aAppearance
,
1357 LayoutDeviceIntMargin
* aResult
) {
1358 switch (aAppearance
) {
1359 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1360 // and have a meaningful baseline, so they can't have
1361 // author-specified padding.
1362 case StyleAppearance::Radio
:
1363 case StyleAppearance::Checkbox
:
1364 aResult
->SizeTo(0, 0, 0, 0);
1372 bool Theme::GetWidgetOverflow(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1373 StyleAppearance aAppearance
,
1374 nsRect
* aOverflowRect
) {
1375 nsIntMargin overflow
;
1376 switch (aAppearance
) {
1377 case StyleAppearance::FocusOutline
:
1378 // 2px * one segment
1379 overflow
.SizeTo(2, 2, 2, 2);
1381 case StyleAppearance::Radio
:
1382 case StyleAppearance::Checkbox
:
1383 case StyleAppearance::Range
:
1384 // 2px for each outline segment, plus 1px separation, plus we paint with a
1385 // 1px extra offset, so 6px.
1386 overflow
.SizeTo(6, 6, 6, 6);
1388 case StyleAppearance::Textarea
:
1389 case StyleAppearance::Textfield
:
1390 case StyleAppearance::NumberInput
:
1391 case StyleAppearance::Listbox
:
1392 case StyleAppearance::MenulistButton
:
1393 case StyleAppearance::Menulist
:
1394 case StyleAppearance::Button
:
1395 // 2px for each segment, plus 1px separation, but we paint 1px inside
1396 // the border area so 4px overflow.
1397 overflow
.SizeTo(4, 4, 4, 4);
1403 // TODO: This should convert from device pixels to app units, not from CSS
1404 // pixels. And it should take the dpi ratio into account.
1405 // Using CSS pixels can cause the overflow to be too small if the page is
1407 aOverflowRect
->Inflate(nsMargin(CSSPixel::ToAppUnits(overflow
.top
),
1408 CSSPixel::ToAppUnits(overflow
.right
),
1409 CSSPixel::ToAppUnits(overflow
.bottom
),
1410 CSSPixel::ToAppUnits(overflow
.left
)));
1415 auto Theme::GetScrollbarSizes(nsPresContext
* aPresContext
,
1416 StyleScrollbarWidth aWidth
, Overlay aOverlay
)
1418 return GetScrollbarDrawing().GetScrollbarSizes(aPresContext
, aWidth
,
1422 nscoord
Theme::GetCheckboxRadioPrefSize() {
1423 return CSSPixel::ToAppUnits(kCheckboxRadioContentBoxSize
);
1427 UniquePtr
<ScrollbarDrawing
> Theme::ScrollbarStyle() {
1428 switch (StaticPrefs::widget_non_native_theme_scrollbar_style()) {
1430 return MakeUnique
<ScrollbarDrawingCocoa
>();
1432 return MakeUnique
<ScrollbarDrawingGTK
>();
1434 return MakeUnique
<ScrollbarDrawingAndroid
>();
1436 return MakeUnique
<ScrollbarDrawingWin
>();
1438 return MakeUnique
<ScrollbarDrawingWin11
>();
1442 // Default to native scrollbar style for each platform.
1444 if (IsWin11OrLater()) {
1445 return MakeUnique
<ScrollbarDrawingWin11
>();
1447 return MakeUnique
<ScrollbarDrawingWin
>();
1448 #elif MOZ_WIDGET_COCOA
1449 return MakeUnique
<ScrollbarDrawingCocoa
>();
1450 #elif MOZ_WIDGET_GTK
1451 return MakeUnique
<ScrollbarDrawingGTK
>();
1453 return MakeUnique
<ScrollbarDrawingAndroid
>();
1455 # error "Unknown platform, need scrollbar implementation."
1460 Theme::GetMinimumWidgetSize(nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1461 StyleAppearance aAppearance
,
1462 LayoutDeviceIntSize
* aResult
,
1463 bool* aIsOverridable
) {
1464 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1466 aResult
->width
= aResult
->height
= 0;
1467 *aIsOverridable
= true;
1469 if (IsWidgetScrollbarPart(aAppearance
)) {
1470 *aResult
= GetScrollbarDrawing().GetMinimumWidgetSize(aPresContext
,
1471 aAppearance
, aFrame
);
1475 switch (aAppearance
) {
1476 case StyleAppearance::Button
:
1477 if (aFrame
->IsColorControlFrame()) {
1478 aResult
->height
= (kMinimumColorPickerHeight
* dpiRatio
).Rounded();
1481 case StyleAppearance::RangeThumb
:
1482 aResult
->SizeTo((kMinimumRangeThumbSize
* dpiRatio
).Rounded(),
1483 (kMinimumRangeThumbSize
* dpiRatio
).Rounded());
1485 case StyleAppearance::MozMenulistArrowButton
:
1486 aResult
->width
= (kMinimumDropdownArrowButtonWidth
* dpiRatio
).Rounded();
1488 case StyleAppearance::SpinnerUpbutton
:
1489 case StyleAppearance::SpinnerDownbutton
:
1490 aResult
->width
= (kMinimumSpinnerButtonWidth
* dpiRatio
).Rounded();
1491 aResult
->height
= (kMinimumSpinnerButtonHeight
* dpiRatio
).Rounded();
1500 nsITheme::Transparency
Theme::GetWidgetTransparency(
1501 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1502 return GetScrollbarDrawing()
1503 .GetScrollbarPartTransparency(aFrame
, aAppearance
)
1504 .valueOr(eUnknownTransparency
);
1508 Theme::WidgetStateChanged(nsIFrame
* aFrame
, StyleAppearance aAppearance
,
1509 nsAtom
* aAttribute
, bool* aShouldRepaint
,
1510 const nsAttrValue
* aOldValue
) {
1512 // Hover/focus/active changed. Always repaint.
1513 *aShouldRepaint
= true;
1515 // Check the attribute to see if it's relevant.
1516 // disabled, checked, dlgtype, default, etc.
1517 *aShouldRepaint
= false;
1518 if (aAttribute
== nsGkAtoms::disabled
|| aAttribute
== nsGkAtoms::checked
||
1519 aAttribute
== nsGkAtoms::selected
||
1520 aAttribute
== nsGkAtoms::visuallyselected
||
1521 aAttribute
== nsGkAtoms::menuactive
||
1522 aAttribute
== nsGkAtoms::sortDirection
||
1523 aAttribute
== nsGkAtoms::focused
|| aAttribute
== nsGkAtoms::_default
||
1524 aAttribute
== nsGkAtoms::open
|| aAttribute
== nsGkAtoms::hover
) {
1525 *aShouldRepaint
= true;
1533 Theme::ThemeChanged() { return NS_OK
; }
1535 bool Theme::WidgetAppearanceDependsOnWindowFocus(StyleAppearance aAppearance
) {
1536 return IsWidgetScrollbarPart(aAppearance
);
1539 nsITheme::ThemeGeometryType
Theme::ThemeGeometryTypeForWidget(
1540 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1541 return eThemeGeometryTypeUnknown
;
1544 bool Theme::ThemeSupportsWidget(nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1545 StyleAppearance aAppearance
) {
1546 switch (aAppearance
) {
1547 case StyleAppearance::Radio
:
1548 case StyleAppearance::Checkbox
:
1549 case StyleAppearance::FocusOutline
:
1550 case StyleAppearance::Textarea
:
1551 case StyleAppearance::Textfield
:
1552 case StyleAppearance::Range
:
1553 case StyleAppearance::RangeThumb
:
1554 case StyleAppearance::ProgressBar
:
1555 case StyleAppearance::Progresschunk
:
1556 case StyleAppearance::Meter
:
1557 case StyleAppearance::Meterchunk
:
1558 case StyleAppearance::ScrollbarbuttonUp
:
1559 case StyleAppearance::ScrollbarbuttonDown
:
1560 case StyleAppearance::ScrollbarbuttonLeft
:
1561 case StyleAppearance::ScrollbarbuttonRight
:
1562 case StyleAppearance::ScrollbarthumbHorizontal
:
1563 case StyleAppearance::ScrollbarthumbVertical
:
1564 case StyleAppearance::ScrollbartrackHorizontal
:
1565 case StyleAppearance::ScrollbartrackVertical
:
1566 case StyleAppearance::ScrollbarHorizontal
:
1567 case StyleAppearance::ScrollbarVertical
:
1568 case StyleAppearance::Scrollcorner
:
1569 case StyleAppearance::Button
:
1570 case StyleAppearance::Listbox
:
1571 case StyleAppearance::Menulist
:
1572 case StyleAppearance::MenulistButton
:
1573 case StyleAppearance::NumberInput
:
1574 case StyleAppearance::MozMenulistArrowButton
:
1575 case StyleAppearance::SpinnerUpbutton
:
1576 case StyleAppearance::SpinnerDownbutton
:
1577 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
1583 bool Theme::WidgetIsContainer(StyleAppearance aAppearance
) {
1584 switch (aAppearance
) {
1585 case StyleAppearance::MozMenulistArrowButton
:
1586 case StyleAppearance::Radio
:
1587 case StyleAppearance::Checkbox
:
1594 bool Theme::ThemeDrawsFocusForWidget(nsIFrame
*, StyleAppearance
) {
1598 bool Theme::ThemeNeedsComboboxDropmarker() { return true; }
1600 bool Theme::ThemeSupportsScrollbarButtons() {
1601 return GetScrollbarDrawing().ShouldDrawScrollbarButtons();
1604 } // namespace mozilla::widget