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/. */
8 #include "ThemeCocoa.h"
10 #include "ThemeDrawing.h"
12 #include "mozilla/MathAlgorithms.h"
13 #include "mozilla/ClearOnShutdown.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/HTMLMeterElement.h"
16 #include "mozilla/dom/HTMLProgressElement.h"
17 #include "mozilla/gfx/Rect.h"
18 #include "mozilla/gfx/Types.h"
19 #include "mozilla/gfx/Filters.h"
20 #include "mozilla/RelativeLuminanceUtils.h"
21 #include "mozilla/StaticPrefs_widget.h"
22 #include "mozilla/webrender/WebRenderAPI.h"
23 #include "nsCSSColorUtils.h"
24 #include "nsCSSRendering.h"
25 #include "nsScrollbarFrame.h"
26 #include "nsIScrollbarMediator.h"
27 #include "nsDeviceContext.h"
28 #include "nsLayoutUtils.h"
29 #include "nsRangeFrame.h"
30 #include "PathHelpers.h"
31 #include "ScrollbarDrawingAndroid.h"
32 #include "ScrollbarDrawingCocoa.h"
33 #include "ScrollbarDrawingGTK.h"
34 #include "ScrollbarDrawingWin.h"
35 #include "ScrollbarDrawingWin11.h"
38 # include "mozilla/WindowsVersion.h"
41 using namespace mozilla
;
42 using namespace mozilla::dom
;
43 using namespace mozilla::gfx
;
44 using namespace mozilla::widget
;
48 static constexpr gfx::sRGBColor
sColorGrey10(
49 gfx::sRGBColor::UnusualFromARGB(0xffe9e9ed));
50 static constexpr gfx::sRGBColor
sColorGrey10Alpha50(
51 gfx::sRGBColor::UnusualFromARGB(0x7fe9e9ed));
52 static constexpr gfx::sRGBColor
sColorGrey20(
53 gfx::sRGBColor::UnusualFromARGB(0xffd0d0d7));
54 static constexpr gfx::sRGBColor
sColorGrey30(
55 gfx::sRGBColor::UnusualFromARGB(0xffb1b1b9));
56 static constexpr gfx::sRGBColor
sColorGrey40(
57 gfx::sRGBColor::UnusualFromARGB(0xff8f8f9d));
58 static constexpr gfx::sRGBColor
sColorGrey40Alpha50(
59 gfx::sRGBColor::UnusualFromARGB(0x7f8f8f9d));
60 static constexpr gfx::sRGBColor
sColorGrey50(
61 gfx::sRGBColor::UnusualFromARGB(0xff676774));
62 static constexpr gfx::sRGBColor
sColorGrey60(
63 gfx::sRGBColor::UnusualFromARGB(0xff484851));
65 static constexpr gfx::sRGBColor
sColorMeterGreen10(
66 gfx::sRGBColor::UnusualFromARGB(0xff00ab60));
67 static constexpr gfx::sRGBColor
sColorMeterGreen20(
68 gfx::sRGBColor::UnusualFromARGB(0xff056139));
69 static constexpr gfx::sRGBColor
sColorMeterYellow10(
70 gfx::sRGBColor::UnusualFromARGB(0xffffbd4f));
71 static constexpr gfx::sRGBColor
sColorMeterYellow20(
72 gfx::sRGBColor::UnusualFromARGB(0xffd2811e));
73 static constexpr gfx::sRGBColor
sColorMeterRed10(
74 gfx::sRGBColor::UnusualFromARGB(0xffe22850));
75 static constexpr gfx::sRGBColor
sColorMeterRed20(
76 gfx::sRGBColor::UnusualFromARGB(0xff810220));
78 static const CSSCoord kMinimumRangeThumbSize
= 20.0f
;
79 static const CSSCoord kMinimumDropdownArrowButtonWidth
= 18.0f
;
80 static const CSSCoord kMinimumSpinnerButtonWidth
= 18.0f
;
81 static const CSSCoord kMinimumSpinnerButtonHeight
= 9.0f
;
82 static const CSSCoord kButtonBorderWidth
= 1.0f
;
83 static const CSSCoord kMenulistBorderWidth
= 1.0f
;
84 static const CSSCoord kTextFieldBorderWidth
= 1.0f
;
85 static const CSSCoord kRangeHeight
= 6.0f
;
86 static const CSSCoord kProgressbarHeight
= 6.0f
;
87 static const CSSCoord kMeterHeight
= 12.0f
;
89 // nsCheckboxRadioFrame takes the bottom of the content box as the baseline.
90 // This border-width makes its baseline 2px under the bottom, which is nice.
91 static constexpr CSSCoord kCheckboxRadioBorderWidth
= 2.0f
;
93 static constexpr sRGBColor sTransparent
= sRGBColor::White(0.0);
95 // This pushes and pops a clip rect to the draw target.
97 // This is done to reduce fuzz in places where we may have antialiasing,
98 // because skia is not clip-invariant: given different clips, it does not
99 // guarantee the same result, even if the painted content doesn't intersect
102 // This is a bit sad, overall, but...
103 struct MOZ_RAII AutoClipRect
{
104 AutoClipRect(DrawTarget
& aDt
, const LayoutDeviceRect
& aRect
) : mDt(aDt
) {
105 mDt
.PushClipRect(aRect
.ToUnknownRect());
108 ~AutoClipRect() { mDt
.PopClip(); }
114 static StaticRefPtr
<Theme
> gNativeInstance
;
115 static StaticRefPtr
<Theme
> gNonNativeInstance
;
116 static StaticRefPtr
<Theme
> gRDMInstance
;
121 already_AddRefed
<Theme
> do_CreateNativeThemeDoNotUseDirectly() {
122 // Android doesn't have a native theme.
123 return do_AddRef(new Theme(Theme::ScrollbarStyle()));
127 already_AddRefed
<nsITheme
> do_GetBasicNativeThemeDoNotUseDirectly() {
128 if (MOZ_UNLIKELY(!gNonNativeInstance
)) {
129 UniquePtr
<ScrollbarDrawing
> scrollbarDrawing
= Theme::ScrollbarStyle();
130 #ifdef MOZ_WIDGET_COCOA
131 gNonNativeInstance
= new ThemeCocoa(std::move(scrollbarDrawing
));
133 gNonNativeInstance
= new Theme(std::move(scrollbarDrawing
));
135 ClearOnShutdown(&gNonNativeInstance
);
137 return do_AddRef(gNonNativeInstance
);
140 already_AddRefed
<nsITheme
> do_GetNativeThemeDoNotUseDirectly() {
141 if (MOZ_UNLIKELY(!gNativeInstance
)) {
142 gNativeInstance
= do_CreateNativeThemeDoNotUseDirectly();
143 ClearOnShutdown(&gNativeInstance
);
145 return do_AddRef(gNativeInstance
);
148 already_AddRefed
<nsITheme
> do_GetRDMThemeDoNotUseDirectly() {
149 if (MOZ_UNLIKELY(!gRDMInstance
)) {
150 gRDMInstance
= new Theme(MakeUnique
<ScrollbarDrawingAndroid
>());
151 ClearOnShutdown(&gRDMInstance
);
153 return do_AddRef(gRDMInstance
);
156 namespace mozilla::widget
{
158 NS_IMPL_ISUPPORTS_INHERITED(Theme
, nsNativeTheme
, nsITheme
)
160 static constexpr nsLiteralCString kPrefs
[] = {
161 "widget.non-native-theme.use-theme-accent"_ns
,
162 "widget.non-native-theme.win.scrollbar.use-system-size"_ns
,
163 "widget.non-native-theme.scrollbar.size.override"_ns
,
164 "widget.non-native-theme.scrollbar.style"_ns
,
168 for (const auto& pref
: kPrefs
) {
169 Preferences::RegisterCallback(PrefChangedCallback
, pref
);
171 LookAndFeelChanged();
174 void Theme::Shutdown() {
175 for (const auto& pref
: kPrefs
) {
176 Preferences::UnregisterCallback(PrefChangedCallback
, pref
);
181 void Theme::LookAndFeelChanged() {
182 ThemeColors::RecomputeAccentColors();
183 if (gNonNativeInstance
) {
184 gNonNativeInstance
->SetScrollbarDrawing(ScrollbarStyle());
186 if (gNativeInstance
) {
187 gNativeInstance
->SetScrollbarDrawing(ScrollbarStyle());
192 auto Theme::GetDPIRatio(nsPresContext
* aPc
, StyleAppearance aAppearance
)
194 // Widgets react to zoom, except scrollbars.
195 if (IsWidgetScrollbarPart(aAppearance
)) {
196 return ScrollbarDrawing::GetDPIRatioForScrollbarPart(aPc
);
198 return DPIRatio(float(AppUnitsPerCSSPixel()) / aPc
->AppUnitsPerDevPixel());
202 auto Theme::GetDPIRatio(nsIFrame
* aFrame
, StyleAppearance aAppearance
)
204 return GetDPIRatio(aFrame
->PresContext(), aAppearance
);
207 // Checkbox and radio need to preserve aspect-ratio for compat. We also snap the
208 // size to exact device pixels to avoid snapping disorting the circles.
209 static LayoutDeviceRect
CheckBoxRadioRect(const LayoutDeviceRect
& aRect
) {
210 // Place a square rect in the center of aRect.
211 auto size
= std::trunc(std::min(aRect
.width
, aRect
.height
));
212 auto position
= aRect
.Center() - LayoutDevicePoint(size
* 0.5, size
* 0.5);
213 return LayoutDeviceRect(position
, LayoutDeviceSize(size
, size
));
216 std::tuple
<sRGBColor
, sRGBColor
, sRGBColor
> Theme::ComputeCheckboxColors(
217 const ElementState
& aState
, StyleAppearance aAppearance
,
218 const Colors
& aColors
) {
219 MOZ_ASSERT(aAppearance
== StyleAppearance::Checkbox
||
220 aAppearance
== StyleAppearance::Radio
);
222 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
223 bool isChecked
= aState
.HasState(ElementState::CHECKED
);
224 bool isIndeterminate
= aAppearance
== StyleAppearance::Checkbox
&&
225 aState
.HasState(ElementState::INDETERMINATE
);
227 if (isChecked
|| isIndeterminate
) {
229 auto bg
= ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::No
);
230 auto fg
= aColors
.HighContrast()
231 ? aColors
.System(StyleSystemColor::Graytext
)
232 : sRGBColor::White(.8f
);
233 return std::make_tuple(bg
, bg
, fg
);
236 if (aColors
.HighContrast()) {
237 auto bg
= aColors
.System(StyleSystemColor::Selecteditem
);
238 auto fg
= aColors
.System(StyleSystemColor::Selecteditemtext
);
239 return std::make_tuple(bg
, bg
, fg
);
243 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
244 bool isHovered
= aState
.HasState(ElementState::HOVER
);
245 const auto& bg
= isActive
? aColors
.Accent().GetDarker()
246 : isHovered
? aColors
.Accent().GetDark()
247 : aColors
.Accent().Get();
248 const auto& fg
= aColors
.Accent().GetForeground();
249 return std::make_tuple(bg
, bg
, fg
);
253 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::No
);
254 // We don't paint a checkmark in this case so any color would do.
255 return std::make_tuple(bg
, border
, sTransparent
);
258 sRGBColor
Theme::ComputeBorderColor(const ElementState
& aState
,
259 const Colors
& aColors
,
260 OutlineCoversBorder aOutlineCoversBorder
) {
261 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
262 if (aColors
.HighContrast()) {
263 return aColors
.System(isDisabled
? StyleSystemColor::Graytext
264 : StyleSystemColor::Buttontext
);
267 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
268 bool isHovered
= aState
.HasState(ElementState::HOVER
);
269 bool isFocused
= aState
.HasState(ElementState::FOCUSRING
);
271 return sColorGrey40Alpha50
;
273 if (isFocused
&& aOutlineCoversBorder
== OutlineCoversBorder::Yes
) {
274 // If we draw the outline over the border, prevent issues where the border
275 // shows underneath if it snaps in the wrong direction by using a
276 // transparent border. An alternative to this is ensuring that we snap the
277 // offset in PaintRoundedFocusRect the same was a we snap border widths, so
278 // that negative offsets are guaranteed to cover the border.
279 // But this looks harder to mess up.
282 bool dark
= aColors
.IsDark();
284 return dark
? sColorGrey20
: sColorGrey60
;
287 return dark
? sColorGrey30
: sColorGrey50
;
292 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeButtonColors(
293 const ElementState
& aState
, const Colors
& aColors
, nsIFrame
* aFrame
) {
295 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
296 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
297 bool isHovered
= aState
.HasState(ElementState::HOVER
);
299 nscolor backgroundColor
= [&] {
301 return aColors
.SystemNs(StyleSystemColor::MozButtondisabledface
);
304 return aColors
.SystemNs(StyleSystemColor::MozButtonactiveface
);
307 return aColors
.SystemNs(StyleSystemColor::MozButtonhoverface
);
309 return aColors
.SystemNs(StyleSystemColor::Buttonface
);
312 if (aState
.HasState(ElementState::AUTOFILL
)) {
313 backgroundColor
= NS_ComposeColors(
315 aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
));
318 const sRGBColor borderColor
=
319 ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::Yes
);
320 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
323 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeTextfieldColors(
324 const ElementState
& aState
, const Colors
& aColors
,
325 OutlineCoversBorder aOutlineCoversBorder
) {
326 nscolor backgroundColor
= [&] {
327 if (aState
.HasState(ElementState::DISABLED
)) {
328 return aColors
.SystemNs(StyleSystemColor::MozDisabledfield
);
330 return aColors
.SystemNs(StyleSystemColor::Field
);
333 if (aState
.HasState(ElementState::AUTOFILL
)) {
334 backgroundColor
= NS_ComposeColors(
336 aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
));
339 const sRGBColor borderColor
=
340 ComputeBorderColor(aState
, aColors
, aOutlineCoversBorder
);
341 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
344 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeProgressColors(
345 const ElementState
& aState
, const Colors
& aColors
) {
346 if (aColors
.HighContrast()) {
347 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
348 StyleSystemColor::Buttontext
);
352 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
353 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
354 bool isHovered
= aState
.HasState(ElementState::HOVER
);
357 return std::make_pair(sColorGrey40Alpha50
, sColorGrey40Alpha50
);
359 if (isActive
|| isHovered
) {
360 return std::make_pair(aColors
.Accent().GetDark(),
361 aColors
.Accent().GetDarker());
363 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
366 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeTrackColors(
367 const ElementState
& aState
, const Colors
& aColors
) {
368 if (aColors
.HighContrast()) {
369 return aColors
.SystemPair(StyleSystemColor::Window
,
370 StyleSystemColor::Buttontext
);
373 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
374 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
375 bool isHovered
= aState
.HasState(ElementState::HOVER
);
378 return std::make_pair(sColorGrey10Alpha50
, sColorGrey40Alpha50
);
380 if (isActive
|| isHovered
) {
381 return std::make_pair(sColorGrey20
, sColorGrey50
);
383 return std::make_pair(sColorGrey10
, sColorGrey40
);
386 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeThumbColors(
387 const ElementState
& aState
, const Colors
& aColors
) {
388 if (aColors
.HighContrast()) {
389 return aColors
.SystemPair(StyleSystemColor::Selecteditemtext
,
390 StyleSystemColor::Selecteditem
);
394 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
395 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
396 bool isHovered
= aState
.HasState(ElementState::HOVER
);
398 const sRGBColor
& backgroundColor
= [&] {
403 return aColors
.Accent().Get();
411 const sRGBColor borderColor
= sRGBColor::OpaqueWhite();
412 return std::make_pair(backgroundColor
, borderColor
);
415 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressColors(
416 const Colors
& aColors
) {
417 if (aColors
.HighContrast()) {
418 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
419 StyleSystemColor::Buttontext
);
421 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
424 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressTrackColors(
425 const Colors
& aColors
) {
426 if (aColors
.HighContrast()) {
427 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
428 StyleSystemColor::Buttontext
);
430 return std::make_pair(sColorGrey10
, sColorGrey40
);
433 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeMeterchunkColors(
434 const ElementState
& aMeterState
, const Colors
& aColors
) {
435 if (aColors
.HighContrast()) {
436 return ComputeProgressColors(aColors
);
438 sRGBColor borderColor
= sColorMeterGreen20
;
439 sRGBColor chunkColor
= sColorMeterGreen10
;
441 if (aMeterState
.HasState(ElementState::SUB_OPTIMUM
)) {
442 borderColor
= sColorMeterYellow20
;
443 chunkColor
= sColorMeterYellow10
;
444 } else if (aMeterState
.HasState(ElementState::SUB_SUB_OPTIMUM
)) {
445 borderColor
= sColorMeterRed20
;
446 chunkColor
= sColorMeterRed10
;
449 return std::make_pair(chunkColor
, borderColor
);
452 std::array
<sRGBColor
, 3> Theme::ComputeFocusRectColors(const Colors
& aColors
) {
453 if (aColors
.HighContrast()) {
454 return {aColors
.System(StyleSystemColor::Selecteditem
),
455 aColors
.System(StyleSystemColor::Buttontext
),
456 aColors
.System(StyleSystemColor::Window
)};
458 const auto& accent
= aColors
.Accent();
459 const sRGBColor middle
=
460 aColors
.IsDark() ? sRGBColor::Black(.3f
) : sRGBColor::White(.3f
);
461 return {accent
.Get(), middle
, accent
.GetLight()};
464 template <typename PaintBackendData
>
465 void Theme::PaintRoundedFocusRect(PaintBackendData
& aBackendData
,
466 const LayoutDeviceRect
& aRect
,
467 const Colors
& aColors
, DPIRatio aDpiRatio
,
468 CSSCoord aRadius
, CSSCoord aOffset
) {
469 // NOTE(emilio): If the widths or offsets here change, make sure to tweak
470 // the GetWidgetOverflow path for FocusOutline.
471 auto [innerColor
, middleColor
, outerColor
] = ComputeFocusRectColors(aColors
);
473 LayoutDeviceRect
focusRect(aRect
);
475 // The focus rect is painted outside of the border area (aRect), see:
477 // data:text/html,<div style="border: 1px solid; outline: 2px solid
480 // But some controls might provide a negative offset to cover the border, if
482 CSSCoord strokeWidth
= 2.0f
;
483 auto strokeWidthDevPx
=
484 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
485 CSSCoord strokeRadius
= aRadius
;
486 focusRect
.Inflate(aOffset
* aDpiRatio
+ strokeWidthDevPx
);
488 ThemeDrawing::PaintRoundedRectWithRadius(
489 aBackendData
, focusRect
, sTransparent
, innerColor
, strokeWidth
,
490 strokeRadius
, aDpiRatio
);
492 strokeWidth
= CSSCoord(1.0f
);
494 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
495 strokeRadius
+= strokeWidth
;
496 focusRect
.Inflate(strokeWidthDevPx
);
498 ThemeDrawing::PaintRoundedRectWithRadius(
499 aBackendData
, focusRect
, sTransparent
, middleColor
, strokeWidth
,
500 strokeRadius
, aDpiRatio
);
502 strokeWidth
= CSSCoord(2.0f
);
504 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
505 strokeRadius
+= strokeWidth
;
506 focusRect
.Inflate(strokeWidthDevPx
);
508 ThemeDrawing::PaintRoundedRectWithRadius(
509 aBackendData
, focusRect
, sTransparent
, outerColor
, strokeWidth
,
510 strokeRadius
, aDpiRatio
);
513 void Theme::PaintCheckboxControl(DrawTarget
& aDrawTarget
,
514 const LayoutDeviceRect
& aRect
,
515 const ElementState
& aState
,
516 const Colors
& aColors
, DPIRatio aDpiRatio
) {
517 auto [backgroundColor
, borderColor
, checkColor
] =
518 ComputeCheckboxColors(aState
, StyleAppearance::Checkbox
, aColors
);
520 const CSSCoord radius
= 2.0f
;
521 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
522 if (backgroundColor
== borderColor
) {
525 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
,
526 backgroundColor
, borderColor
,
527 borderWidth
, radius
, aDpiRatio
);
530 if (aState
.HasState(ElementState::INDETERMINATE
)) {
531 PaintIndeterminateMark(aDrawTarget
, aRect
, checkColor
);
532 } else if (aState
.HasState(ElementState::CHECKED
)) {
533 PaintCheckMark(aDrawTarget
, aRect
, checkColor
);
536 if (aState
.HasState(ElementState::FOCUSRING
)) {
537 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
541 constexpr CSSCoord kCheckboxRadioContentBoxSize
= 10.0f
;
542 constexpr CSSCoord kCheckboxRadioBorderBoxSize
=
543 kCheckboxRadioContentBoxSize
+ kCheckboxRadioBorderWidth
* 2.0f
;
545 void Theme::PaintCheckMark(DrawTarget
& aDrawTarget
,
546 const LayoutDeviceRect
& aRect
,
547 const sRGBColor
& aColor
) {
548 // Points come from the coordinates on a 14X14 (kCheckboxRadioBorderBoxSize)
549 // unit box centered at 0,0
550 const float checkPolygonX
[] = {-4.5f
, -1.5f
, -0.5f
, 5.0f
, 4.75f
,
551 3.5f
, -0.5f
, -1.5f
, -3.5f
};
552 const float checkPolygonY
[] = {0.5f
, 4.0f
, 4.0f
, -2.5f
, -4.0f
,
553 -4.0f
, 1.0f
, 1.25f
, -1.0f
};
554 const int32_t checkNumPoints
= sizeof(checkPolygonX
) / sizeof(float);
556 ThemeDrawing::ScaleToFillRect(aRect
, kCheckboxRadioBorderBoxSize
);
557 auto center
= aRect
.Center().ToUnknownPoint();
559 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
560 Point p
= center
+ Point(checkPolygonX
[0] * scale
, checkPolygonY
[0] * scale
);
562 for (int32_t i
= 1; i
< checkNumPoints
; i
++) {
563 p
= center
+ Point(checkPolygonX
[i
] * scale
, checkPolygonY
[i
] * scale
);
566 RefPtr
<Path
> path
= builder
->Finish();
568 aDrawTarget
.Fill(path
, ColorPattern(ToDeviceColor(aColor
)));
571 void Theme::PaintIndeterminateMark(DrawTarget
& aDrawTarget
,
572 const LayoutDeviceRect
& aRect
,
573 const sRGBColor
& aColor
) {
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 aDrawTarget
.FillRect(rect
, ColorPattern(ToDeviceColor(aColor
)));
587 template <typename PaintBackendData
>
588 void Theme::PaintStrokedCircle(PaintBackendData
& aPaintData
,
589 const LayoutDeviceRect
& aRect
,
590 const sRGBColor
& aBackgroundColor
,
591 const sRGBColor
& aBorderColor
,
592 const CSSCoord aBorderWidth
,
593 DPIRatio aDpiRatio
) {
594 auto radius
= LayoutDeviceCoord(aRect
.Size().width
) / aDpiRatio
;
595 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, aBackgroundColor
,
596 aBorderColor
, aBorderWidth
, radius
,
600 void Theme::PaintCircleShadow(WebRenderBackendData
& aWrData
,
601 const LayoutDeviceRect
& aBoxRect
,
602 const LayoutDeviceRect
& aClipRect
,
603 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
604 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
605 const bool kBackfaceIsVisible
= true;
606 const LayoutDeviceCoord stdDev
= aShadowBlurStdDev
* aDpiRatio
;
607 const LayoutDevicePoint shadowOffset
= aShadowOffset
* aDpiRatio
;
608 const IntSize inflation
=
609 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
610 LayoutDeviceRect shadowRect
= aBoxRect
;
611 shadowRect
.MoveBy(shadowOffset
);
612 shadowRect
.Inflate(inflation
.width
, inflation
.height
);
613 const auto boxRect
= wr::ToLayoutRect(aBoxRect
);
614 aWrData
.mBuilder
.PushBoxShadow(
615 wr::ToLayoutRect(shadowRect
), wr::ToLayoutRect(aClipRect
),
616 kBackfaceIsVisible
, boxRect
,
617 wr::ToLayoutVector2D(aShadowOffset
* aDpiRatio
),
618 wr::ToColorF(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)), stdDev
,
619 /* aSpread = */ 0.0f
,
620 wr::ToBorderRadius(gfx::RectCornerRadii(aBoxRect
.Size().width
)),
621 wr::BoxShadowClipMode::Outset
);
624 void Theme::PaintCircleShadow(DrawTarget
& aDrawTarget
,
625 const LayoutDeviceRect
& aBoxRect
,
626 const LayoutDeviceRect
& aClipRect
,
627 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
628 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
629 Float stdDev
= aShadowBlurStdDev
* aDpiRatio
;
630 Point offset
= (aShadowOffset
* aDpiRatio
).ToUnknownPoint();
632 RefPtr
<FilterNode
> blurFilter
=
633 aDrawTarget
.CreateFilter(FilterType::GAUSSIAN_BLUR
);
638 blurFilter
->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION
, stdDev
);
641 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
642 Rect inflatedRect
= aBoxRect
.ToUnknownRect();
643 inflatedRect
.Inflate(inflation
.width
, inflation
.height
);
644 Rect sourceRectInFilterSpace
=
645 inflatedRect
- aBoxRect
.TopLeft().ToUnknownPoint();
646 Point destinationPointOfSourceRect
= inflatedRect
.TopLeft() + offset
;
648 IntSize dtSize
= RoundedToInt(aBoxRect
.Size().ToUnknownSize());
649 RefPtr
<DrawTarget
> ellipseDT
= aDrawTarget
.CreateSimilarDrawTargetForFilter(
650 dtSize
, SurfaceFormat::A8
, blurFilter
, blurFilter
,
651 sourceRectInFilterSpace
, destinationPointOfSourceRect
);
656 AutoClipRect
clipRect(aDrawTarget
, aClipRect
);
658 RefPtr
<Path
> ellipse
= MakePathForEllipse(
659 *ellipseDT
, (aBoxRect
- aBoxRect
.TopLeft()).Center().ToUnknownPoint(),
660 aBoxRect
.Size().ToUnknownSize());
661 ellipseDT
->Fill(ellipse
,
662 ColorPattern(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)));
663 RefPtr
<SourceSurface
> ellipseSurface
= ellipseDT
->Snapshot();
665 blurFilter
->SetInput(IN_GAUSSIAN_BLUR_IN
, ellipseSurface
);
666 aDrawTarget
.DrawFilter(blurFilter
, sourceRectInFilterSpace
,
667 destinationPointOfSourceRect
);
670 template <typename PaintBackendData
>
671 void Theme::PaintRadioControl(PaintBackendData
& aPaintData
,
672 const LayoutDeviceRect
& aRect
,
673 const ElementState
& aState
, const Colors
& aColors
,
674 DPIRatio aDpiRatio
) {
675 auto [backgroundColor
, borderColor
, checkColor
] =
676 ComputeCheckboxColors(aState
, StyleAppearance::Radio
, aColors
);
678 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
679 if (backgroundColor
== borderColor
) {
682 PaintStrokedCircle(aPaintData
, aRect
, backgroundColor
, borderColor
,
683 borderWidth
, aDpiRatio
);
686 if (aState
.HasState(ElementState::CHECKED
)) {
687 LayoutDeviceRect
rect(aRect
);
688 auto width
= LayoutDeviceCoord(
689 ThemeDrawing::SnapBorderWidth(kCheckboxRadioBorderWidth
, aDpiRatio
));
692 PaintStrokedCircle(aPaintData
, rect
, backgroundColor
, checkColor
,
693 kCheckboxRadioBorderWidth
, aDpiRatio
);
696 if (aState
.HasState(ElementState::FOCUSRING
)) {
697 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
701 template <typename PaintBackendData
>
702 void Theme::PaintTextField(PaintBackendData
& aPaintData
,
703 const LayoutDeviceRect
& aRect
,
704 const ElementState
& aState
, const Colors
& aColors
,
705 DPIRatio aDpiRatio
) {
706 auto [backgroundColor
, borderColor
] =
707 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
709 const CSSCoord radius
= 2.0f
;
711 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
712 borderColor
, kTextFieldBorderWidth
,
715 if (aState
.HasState(ElementState::FOCUSRING
)) {
716 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
717 radius
+ kTextFieldBorderWidth
,
718 -kTextFieldBorderWidth
);
722 template <typename PaintBackendData
>
723 void Theme::PaintListbox(PaintBackendData
& aPaintData
,
724 const LayoutDeviceRect
& aRect
,
725 const ElementState
& aState
, const Colors
& aColors
,
726 DPIRatio aDpiRatio
) {
727 const CSSCoord radius
= 2.0f
;
728 auto [backgroundColor
, borderColor
] =
729 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
731 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
732 borderColor
, kMenulistBorderWidth
,
735 if (aState
.HasState(ElementState::FOCUSRING
)) {
736 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
737 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
741 template <typename PaintBackendData
>
742 void Theme::PaintMenulist(PaintBackendData
& aDrawTarget
,
743 const LayoutDeviceRect
& aRect
,
744 const ElementState
& aState
, const Colors
& aColors
,
745 DPIRatio aDpiRatio
) {
746 const CSSCoord radius
= 4.0f
;
747 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
749 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
,
750 borderColor
, kMenulistBorderWidth
,
753 if (aState
.HasState(ElementState::FOCUSRING
)) {
754 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
,
755 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
759 void Theme::PaintMenulistArrowButton(nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
,
760 const LayoutDeviceRect
& aRect
,
761 const ElementState
& aState
) {
762 // not const: these may be negated in-place below
763 float polygonX
[] = {-4.0f
, -0.5f
, 0.5f
, 4.0f
, 4.0f
,
764 3.0f
, 0.0f
, 0.0f
, -3.0f
, -4.0f
};
765 float polygonY
[] = {-1, 3.0f
, 3.0f
, -1.0f
, -2.0f
,
766 -2.0f
, 1.5f
, 1.5f
, -2.0f
, -2.0f
};
768 const float kPolygonSize
= kMinimumDropdownArrowButtonWidth
;
770 auto const [xs
, ys
] = [&] {
771 using Pair
= std::pair
<const float*, const float*>;
772 switch (aFrame
->GetWritingMode().GetBlockDir()) {
773 case WritingMode::BlockDir::eBlockRL
:
774 // rotate 90°: [[0,1],[-1,0]]
775 for (float& f
: polygonY
) {
778 return Pair(polygonY
, polygonX
);
780 case WritingMode::BlockDir::eBlockLR
:
781 // rotate 270°: [[0,-1],[1,0]]
782 for (float& f
: polygonX
) {
785 return Pair(polygonY
, polygonX
);
787 case WritingMode::BlockDir::eBlockTB
:
788 // rotate 0°: [[1,0],[0,1]]
789 return Pair(polygonX
, polygonY
);
791 MOZ_ASSERT_UNREACHABLE("unhandled BlockDir");
792 return Pair(polygonX
, polygonY
);
795 const auto arrowColor
= sRGBColor::FromABGR(
796 nsLayoutUtils::GetColor(aFrame
, &nsStyleText::mWebkitTextFillColor
));
797 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, xs
, ys
, kPolygonSize
,
798 ArrayLength(polygonX
), arrowColor
);
801 void Theme::PaintSpinnerButton(nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
,
802 const LayoutDeviceRect
& aRect
,
803 const ElementState
& aState
,
804 StyleAppearance aAppearance
,
805 const Colors
& aColors
, DPIRatio aDpiRatio
) {
806 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
808 aDrawTarget
.FillRect(aRect
.ToUnknownRect(),
809 ColorPattern(ToDeviceColor(backgroundColor
)));
811 const float kPolygonX
[] = {-3.5f
, -0.5f
, 0.5f
, 3.5f
, 3.5f
,
812 2.5f
, 0.0f
, 0.0f
, -2.5f
, -3.5f
};
813 float polygonY
[] = {-1.5f
, 1.5f
, 1.5f
, -1.5f
, -2.5f
,
814 -2.5f
, 0.0f
, 0.0f
, -2.5f
, -2.5f
};
816 const float kPolygonSize
= kMinimumSpinnerButtonHeight
;
817 if (aAppearance
== StyleAppearance::SpinnerUpbutton
) {
818 for (auto& coord
: polygonY
) {
823 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, kPolygonX
, polygonY
,
824 kPolygonSize
, ArrayLength(kPolygonX
), borderColor
);
827 template <typename PaintBackendData
>
828 void Theme::PaintRange(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
829 const LayoutDeviceRect
& aRect
,
830 const ElementState
& aState
, const Colors
& aColors
,
831 DPIRatio aDpiRatio
, bool aHorizontal
) {
832 nsRangeFrame
* rangeFrame
= do_QueryFrame(aFrame
);
837 auto tickMarks
= rangeFrame
->TickMarks();
838 double progress
= rangeFrame
->GetValueAsFractionOfRange();
840 LayoutDeviceRect
thumbRect(0, 0, kMinimumRangeThumbSize
* aDpiRatio
,
841 kMinimumRangeThumbSize
* aDpiRatio
);
842 LayoutDeviceRect
progressClipRect(aRect
);
843 LayoutDeviceRect
trackClipRect(aRect
);
844 const LayoutDeviceCoord verticalSize
= kRangeHeight
* aDpiRatio
;
845 const LayoutDeviceCoord
tickMarkWidth(
846 ThemeDrawing::SnapBorderWidth(1.0f
, aDpiRatio
));
847 const LayoutDeviceCoord
tickMarkHeight(
848 ThemeDrawing::SnapBorderWidth(5.0f
, aDpiRatio
));
849 LayoutDevicePoint tickMarkOrigin
, tickMarkDirection
;
850 LayoutDeviceSize tickMarkSize
;
852 rect
.height
= verticalSize
;
853 rect
.y
= aRect
.y
+ (aRect
.height
- rect
.height
) / 2;
854 tickMarkSize
= LayoutDeviceSize(tickMarkWidth
, tickMarkHeight
);
855 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) / 2;
857 if (IsFrameRTL(aFrame
)) {
859 LayoutDevicePoint(aRect
.XMost() - thumbRect
.width
/ 2, aRect
.YMost());
860 tickMarkDirection
= LayoutDevicePoint(-1.0f
, 0.0f
);
862 aRect
.x
+ (aRect
.width
- thumbRect
.width
) * (1.0 - progress
);
863 float midPoint
= thumbRect
.Center().X();
864 trackClipRect
.SetBoxX(aRect
.X(), midPoint
);
865 progressClipRect
.SetBoxX(midPoint
, aRect
.XMost());
868 LayoutDevicePoint(aRect
.x
+ thumbRect
.width
/ 2, aRect
.YMost());
869 tickMarkDirection
= LayoutDevicePoint(1.0, 0.0f
);
870 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) * progress
;
871 float midPoint
= thumbRect
.Center().X();
872 progressClipRect
.SetBoxX(aRect
.X(), midPoint
);
873 trackClipRect
.SetBoxX(midPoint
, aRect
.XMost());
876 rect
.width
= verticalSize
;
877 rect
.x
= aRect
.x
+ (aRect
.width
- rect
.width
) / 2;
878 tickMarkOrigin
= LayoutDevicePoint(aRect
.XMost() - tickMarkHeight
/ 4,
879 aRect
.YMost() - thumbRect
.width
/ 2);
880 tickMarkDirection
= LayoutDevicePoint(0.0f
, -1.0f
);
881 tickMarkSize
= LayoutDeviceSize(tickMarkHeight
, tickMarkWidth
);
882 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) / 2;
885 aRect
.y
+ (aRect
.height
- thumbRect
.height
) * (1.0 - progress
);
886 float midPoint
= thumbRect
.Center().Y();
887 trackClipRect
.SetBoxY(aRect
.Y(), midPoint
);
888 progressClipRect
.SetBoxY(midPoint
, aRect
.YMost());
891 const CSSCoord borderWidth
= 1.0f
;
892 const CSSCoord radius
= 3.0f
;
894 auto [progressColor
, progressBorderColor
] =
895 ComputeRangeProgressColors(aState
, aColors
);
896 auto [trackColor
, trackBorderColor
] =
897 ComputeRangeTrackColors(aState
, aColors
);
898 auto tickMarkColor
= trackBorderColor
;
900 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, progressClipRect
,
901 progressColor
, progressBorderColor
,
902 borderWidth
, radius
, aDpiRatio
);
904 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, trackClipRect
,
905 trackColor
, trackBorderColor
,
906 borderWidth
, radius
, aDpiRatio
);
908 if (!aState
.HasState(ElementState::DISABLED
)) {
909 // Ensure the shadow doesn't expand outside of our overflow rect declared in
910 // GetWidgetOverflow().
911 auto overflowRect
= aRect
;
912 overflowRect
.Inflate(CSSCoord(6.0f
) * aDpiRatio
);
914 PaintCircleShadow(aPaintData
, thumbRect
, overflowRect
, 0.3f
,
915 CSSPoint(0.0f
, 2.0f
), 2.0f
, aDpiRatio
);
918 tickMarkDirection
.x
*= aRect
.width
- thumbRect
.width
;
919 tickMarkDirection
.y
*= aRect
.height
- thumbRect
.height
;
921 LayoutDevicePoint(tickMarkSize
.width
, tickMarkSize
.height
) / 2;
922 auto tickMarkRect
= LayoutDeviceRect(tickMarkOrigin
, tickMarkSize
);
923 for (auto tickMark
: tickMarks
) {
924 auto tickMarkOffset
=
926 float(rangeFrame
->GetDoubleAsFractionOfRange(tickMark
));
927 ThemeDrawing::FillRect(aPaintData
, tickMarkRect
+ tickMarkOffset
,
931 // Draw the thumb on top.
932 const CSSCoord thumbBorderWidth
= 2.0f
;
933 auto [thumbColor
, thumbBorderColor
] =
934 ComputeRangeThumbColors(aState
, aColors
);
936 PaintStrokedCircle(aPaintData
, thumbRect
, thumbColor
, thumbBorderColor
,
937 thumbBorderWidth
, aDpiRatio
);
939 if (aState
.HasState(ElementState::FOCUSRING
)) {
940 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, radius
, 1.0f
);
944 template <typename PaintBackendData
>
945 void Theme::PaintProgress(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
946 const LayoutDeviceRect
& aRect
,
947 const ElementState
& aState
, const Colors
& aColors
,
948 DPIRatio aDpiRatio
, bool aIsMeter
) {
949 const CSSCoord borderWidth
= 1.0f
;
950 const CSSCoord radius
= aIsMeter
? 6.0f
: 3.0f
;
952 LayoutDeviceRect
rect(aRect
);
953 const LayoutDeviceCoord thickness
=
954 (aIsMeter
? kMeterHeight
: kProgressbarHeight
) * aDpiRatio
;
956 const bool isHorizontal
= !nsNativeTheme::IsVerticalProgress(aFrame
);
958 // Center it vertically.
959 rect
.y
+= (rect
.height
- thickness
) / 2;
960 rect
.height
= thickness
;
962 // Center it horizontally.
963 rect
.x
+= (rect
.width
- thickness
) / 2;
964 rect
.width
= thickness
;
968 // Paint the track, unclipped.
969 auto [backgroundColor
, borderColor
] = ComputeProgressTrackColors(aColors
);
970 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, rect
,
971 backgroundColor
, borderColor
,
972 borderWidth
, radius
, aDpiRatio
);
975 // Now paint the chunk, clipped as needed.
976 LayoutDeviceRect clipRect
= rect
;
977 if (aState
.HasState(ElementState::INDETERMINATE
)) {
978 // For indeterminate progress, we paint an animated chunk of 1/3 of the
981 // Animation speed and math borrowed from GTK.
982 const LayoutDeviceCoord size
= isHorizontal
? rect
.width
: rect
.height
;
983 const LayoutDeviceCoord barSize
= size
* 0.3333f
;
984 const LayoutDeviceCoord travel
= 2.0f
* (size
- barSize
);
986 // Period equals to travel / pixelsPerMillisecond where pixelsPerMillisecond
987 // equals progressSize / 1000.0. This is equivalent to 1600.
988 const unsigned kPeriod
= 1600;
990 const int t
= PR_IntervalToMilliseconds(PR_IntervalNow()) % kPeriod
;
991 const LayoutDeviceCoord dx
= travel
* float(t
) / float(kPeriod
);
993 rect
.width
= barSize
;
994 rect
.x
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
996 rect
.height
= barSize
;
997 rect
.y
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
1000 // Queue the next frame if needed.
1001 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 60)) {
1002 NS_WARNING("Couldn't refresh indeterminate <progress>");
1005 // This is the progress chunk, clip it to the right amount.
1006 double position
= [&] {
1008 auto* meter
= dom::HTMLMeterElement::FromNode(aFrame
->GetContent());
1012 return meter
->Position();
1014 auto* progress
= dom::HTMLProgressElement::FromNode(aFrame
->GetContent());
1018 return progress
->Position();
1021 double clipWidth
= rect
.width
* position
;
1022 clipRect
.width
= clipWidth
;
1023 if (IsFrameRTL(aFrame
)) {
1024 clipRect
.x
+= rect
.width
- clipWidth
;
1027 double clipHeight
= rect
.height
* position
;
1028 clipRect
.height
= clipHeight
;
1029 clipRect
.y
+= rect
.height
- clipHeight
;
1033 auto [backgroundColor
, borderColor
] =
1034 aIsMeter
? ComputeMeterchunkColors(aState
, aColors
)
1035 : ComputeProgressColors(aColors
);
1036 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, clipRect
,
1037 backgroundColor
, borderColor
,
1038 borderWidth
, radius
, aDpiRatio
);
1041 template <typename PaintBackendData
>
1042 void Theme::PaintButton(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
1043 const LayoutDeviceRect
& aRect
,
1044 const ElementState
& aState
, const Colors
& aColors
,
1045 DPIRatio aDpiRatio
) {
1046 const CSSCoord radius
= 4.0f
;
1047 auto [backgroundColor
, borderColor
] =
1048 ComputeButtonColors(aState
, aColors
, aFrame
);
1050 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
1051 borderColor
, kButtonBorderWidth
,
1054 if (aState
.HasState(ElementState::FOCUSRING
)) {
1055 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
1056 radius
+ kButtonBorderWidth
, -kButtonBorderWidth
);
1061 Theme::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
1062 StyleAppearance aAppearance
, const nsRect
& aRect
,
1063 const nsRect
& /* aDirtyRect */,
1064 DrawOverflow aDrawOverflow
) {
1065 if (!DoDrawWidgetBackground(*aContext
->GetDrawTarget(), aFrame
, aAppearance
,
1066 aRect
, aDrawOverflow
)) {
1067 return NS_ERROR_NOT_IMPLEMENTED
;
1072 bool Theme::CreateWebRenderCommandsForWidget(
1073 mozilla::wr::DisplayListBuilder
& aBuilder
,
1074 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
1075 const mozilla::layers::StackingContextHelper
& aSc
,
1076 mozilla::layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
1077 StyleAppearance aAppearance
, const nsRect
& aRect
) {
1078 if (!StaticPrefs::widget_non_native_theme_webrender()) {
1081 WebRenderBackendData data
{aBuilder
, aResources
, aSc
, aManager
};
1082 return DoDrawWidgetBackground(data
, aFrame
, aAppearance
, aRect
,
1086 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1087 nscoord aTwipsPerPixel
, DrawTarget
& aDt
) {
1088 return LayoutDeviceRect::FromUnknownRect(
1089 NSRectToSnappedRect(aRect
, aTwipsPerPixel
, aDt
));
1092 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1093 nscoord aTwipsPerPixel
,
1094 WebRenderBackendData
& aDt
) {
1095 // TODO: Do we need to do any more snapping here?
1096 return LayoutDeviceRect::FromAppUnits(aRect
, aTwipsPerPixel
);
1099 static ScrollbarDrawing::ScrollbarKind
ComputeScrollbarKind(
1100 nsIFrame
* aFrame
, bool aIsHorizontal
) {
1101 if (aIsHorizontal
) {
1102 return ScrollbarDrawing::ScrollbarKind::Horizontal
;
1104 nsIFrame
* scrollbar
= ScrollbarDrawing::GetParentScrollbarFrame(aFrame
);
1105 if (NS_WARN_IF(!scrollbar
)) {
1106 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1108 MOZ_ASSERT(scrollbar
->IsScrollbarFrame());
1109 nsIScrollbarMediator
* sm
=
1110 static_cast<nsScrollbarFrame
*>(scrollbar
)->GetScrollbarMediator();
1111 if (NS_WARN_IF(!sm
)) {
1112 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1114 return sm
->IsScrollbarOnRight()
1115 ? ScrollbarDrawing::ScrollbarKind::VerticalRight
1116 : ScrollbarDrawing::ScrollbarKind::VerticalLeft
;
1119 template <typename PaintBackendData
>
1120 bool Theme::DoDrawWidgetBackground(PaintBackendData
& aPaintData
,
1122 StyleAppearance aAppearance
,
1123 const nsRect
& aRect
,
1124 DrawOverflow aDrawOverflow
) {
1125 static_assert(std::is_same_v
<PaintBackendData
, DrawTarget
> ||
1126 std::is_same_v
<PaintBackendData
, WebRenderBackendData
>);
1128 const nsPresContext
* pc
= aFrame
->PresContext();
1129 const nscoord twipsPerPixel
= pc
->AppUnitsPerDevPixel();
1130 const auto devPxRect
= ToSnappedRect(aRect
, twipsPerPixel
, aPaintData
);
1132 const DocumentState docState
= pc
->Document()->GetDocumentState();
1133 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1134 if (aAppearance
== StyleAppearance::MozMenulistArrowButton
) {
1135 bool isHTML
= IsHTMLContent(aFrame
);
1136 nsIFrame
* parentFrame
= aFrame
->GetParent();
1137 bool isMenulist
= !isHTML
&& parentFrame
->IsMenuFrame();
1138 // HTML select and XUL menulist dropdown buttons get state from the
1140 if (isHTML
|| isMenulist
) {
1141 aFrame
= parentFrame
;
1142 elementState
= GetContentState(parentFrame
, aAppearance
);
1146 // Paint the outline iff we're asked to draw overflow and we have
1147 // outline-style: auto.
1148 if (aDrawOverflow
== DrawOverflow::Yes
&&
1149 aFrame
->StyleOutline()->mOutlineStyle
.IsAuto()) {
1150 elementState
|= ElementState::FOCUSRING
;
1152 elementState
&= ~ElementState::FOCUSRING
;
1155 // Hack to avoid skia fuzziness: Add a dummy clip if the widget doesn't
1156 // overflow devPxRect.
1157 Maybe
<AutoClipRect
> maybeClipRect
;
1158 if constexpr (std::is_same_v
<PaintBackendData
, DrawTarget
>) {
1159 if (aAppearance
!= StyleAppearance::FocusOutline
&&
1160 aAppearance
!= StyleAppearance::Range
&&
1161 !elementState
.HasState(ElementState::FOCUSRING
)) {
1162 maybeClipRect
.emplace(aPaintData
, devPxRect
);
1166 const Colors
colors(aFrame
, aAppearance
);
1167 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1169 switch (aAppearance
) {
1170 case StyleAppearance::Radio
: {
1171 auto rect
= CheckBoxRadioRect(devPxRect
);
1172 PaintRadioControl(aPaintData
, rect
, elementState
, colors
, dpiRatio
);
1175 case StyleAppearance::Checkbox
: {
1176 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1177 // TODO: Need to figure out how to best draw this using WR.
1180 auto rect
= CheckBoxRadioRect(devPxRect
);
1181 PaintCheckboxControl(aPaintData
, rect
, elementState
, colors
, dpiRatio
);
1185 case StyleAppearance::Textarea
:
1186 case StyleAppearance::Textfield
:
1187 case StyleAppearance::NumberInput
:
1188 PaintTextField(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1190 case StyleAppearance::Listbox
:
1191 PaintListbox(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1193 case StyleAppearance::MenulistButton
:
1194 case StyleAppearance::Menulist
:
1195 PaintMenulist(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1197 case StyleAppearance::MozMenulistArrowButton
:
1198 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1199 // TODO: Need to figure out how to best draw this using WR.
1202 PaintMenulistArrowButton(aFrame
, aPaintData
, devPxRect
, elementState
);
1205 case StyleAppearance::Tooltip
: {
1206 const CSSCoord
strokeWidth(1.0f
);
1207 const CSSCoord
strokeRadius(2.0f
);
1208 ThemeDrawing::PaintRoundedRectWithRadius(
1209 aPaintData
, devPxRect
,
1210 colors
.System(StyleSystemColor::Infobackground
),
1211 colors
.System(StyleSystemColor::Infotext
), strokeWidth
, strokeRadius
,
1215 case StyleAppearance::Menuitem
: {
1216 ThemeDrawing::FillRect(aPaintData
, devPxRect
, [&] {
1217 if (CheckBooleanAttr(aFrame
, nsGkAtoms::menuactive
)) {
1218 if (elementState
.HasState(ElementState::DISABLED
)) {
1219 return colors
.System(StyleSystemColor::MozMenuhoverdisabled
);
1221 return colors
.System(StyleSystemColor::MozMenuhover
);
1223 return sTransparent
;
1227 case StyleAppearance::SpinnerUpbutton
:
1228 case StyleAppearance::SpinnerDownbutton
:
1229 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1230 // TODO: Need to figure out how to best draw this using WR.
1233 PaintSpinnerButton(aFrame
, aPaintData
, devPxRect
, elementState
,
1234 aAppearance
, colors
, dpiRatio
);
1237 case StyleAppearance::Range
:
1238 PaintRange(aFrame
, aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
,
1239 IsRangeHorizontal(aFrame
));
1241 case StyleAppearance::RangeThumb
:
1242 // Painted as part of StyleAppearance::Range.
1244 case StyleAppearance::ProgressBar
:
1245 PaintProgress(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1247 /* aIsMeter = */ false);
1249 case StyleAppearance::Progresschunk
:
1250 /* Painted as part of the progress bar */
1252 case StyleAppearance::Meter
:
1253 PaintProgress(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1255 /* aIsMeter = */ true);
1257 case StyleAppearance::Meterchunk
:
1258 /* Painted as part of the meter bar */
1260 case StyleAppearance::ScrollbarthumbHorizontal
:
1261 case StyleAppearance::ScrollbarthumbVertical
: {
1263 aAppearance
== StyleAppearance::ScrollbarthumbHorizontal
;
1264 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1265 return GetScrollbarDrawing().PaintScrollbarThumb(
1266 aPaintData
, devPxRect
, kind
, aFrame
,
1267 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1270 case StyleAppearance::ScrollbartrackHorizontal
:
1271 case StyleAppearance::ScrollbartrackVertical
: {
1273 aAppearance
== StyleAppearance::ScrollbartrackHorizontal
;
1274 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1275 return GetScrollbarDrawing().PaintScrollbarTrack(
1276 aPaintData
, devPxRect
, kind
, aFrame
,
1277 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
, colors
,
1280 case StyleAppearance::ScrollbarHorizontal
:
1281 case StyleAppearance::ScrollbarVertical
: {
1282 bool isHorizontal
= aAppearance
== StyleAppearance::ScrollbarHorizontal
;
1283 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1284 return GetScrollbarDrawing().PaintScrollbar(
1285 aPaintData
, devPxRect
, kind
, aFrame
,
1286 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1289 case StyleAppearance::Scrollcorner
: {
1290 auto kind
= ComputeScrollbarKind(aFrame
, false);
1291 return GetScrollbarDrawing().PaintScrollCorner(
1292 aPaintData
, devPxRect
, kind
, aFrame
,
1293 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
, colors
,
1296 case StyleAppearance::ScrollbarbuttonUp
:
1297 case StyleAppearance::ScrollbarbuttonDown
:
1298 case StyleAppearance::ScrollbarbuttonLeft
:
1299 case StyleAppearance::ScrollbarbuttonRight
: {
1300 // For scrollbar-width:thin, we don't display the buttons.
1301 if (!ScrollbarDrawing::IsScrollbarWidthThin(aFrame
)) {
1302 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1303 // TODO: Need to figure out how to best draw this using WR.
1307 aAppearance
== StyleAppearance::ScrollbarbuttonLeft
||
1308 aAppearance
== StyleAppearance::ScrollbarbuttonRight
;
1309 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1310 GetScrollbarDrawing().PaintScrollbarButton(
1311 aPaintData
, aAppearance
, devPxRect
, kind
, aFrame
,
1312 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1318 case StyleAppearance::Button
:
1319 PaintButton(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1322 case StyleAppearance::FocusOutline
:
1323 PaintAutoStyleOutline(aFrame
, aPaintData
, devPxRect
, colors
, dpiRatio
);
1326 // Various appearance values are used for XUL elements. Normally these
1327 // will not be available in content documents (and thus in the content
1328 // processes where the native basic theme can be used), but tests are
1329 // run with the remote XUL pref enabled and so we can get in here. So
1330 // we just return an error rather than assert.
1337 template <typename PaintBackendData
>
1338 void Theme::PaintAutoStyleOutline(nsIFrame
* aFrame
,
1339 PaintBackendData
& aPaintData
,
1340 const LayoutDeviceRect
& aRect
,
1341 const Colors
& aColors
, DPIRatio aDpiRatio
) {
1342 const auto& accentColor
= aColors
.Accent();
1343 const bool solid
= StaticPrefs::widget_non_native_theme_solid_outline_style();
1344 LayoutDeviceCoord
strokeWidth(ThemeDrawing::SnapBorderWidth(2.0f
, aDpiRatio
));
1346 LayoutDeviceRect
rect(aRect
);
1347 rect
.Inflate(strokeWidth
);
1349 const nscoord a2d
= aFrame
->PresContext()->AppUnitsPerDevPixel();
1350 nscoord cssOffset
= aFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits();
1351 nscoord cssRadii
[8] = {0};
1352 if (!aFrame
->GetBorderRadii(cssRadii
)) {
1353 const auto twoPixels
= 2 * AppUnitsPerCSSPixel();
1354 const nscoord radius
=
1355 cssOffset
>= 0 ? twoPixels
: std::max(twoPixels
+ cssOffset
, 0);
1356 cssOffset
= -twoPixels
;
1357 for (auto& r
: cssRadii
) {
1362 auto offset
= LayoutDevicePixel::FromAppUnits(cssOffset
, a2d
);
1363 RectCornerRadii innerRadii
;
1364 nsCSSRendering::ComputePixelRadii(cssRadii
, a2d
, &innerRadii
);
1366 // NOTE(emilio): This doesn't use PaintRoundedRectWithRadius because we need
1367 // to support arbitrary radii.
1368 auto DrawRect
= [&](const sRGBColor
& aColor
) {
1369 RectCornerRadii outerRadii
;
1370 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1371 const Float widths
[4] = {strokeWidth
+ offset
, strokeWidth
+ offset
,
1372 strokeWidth
+ offset
, strokeWidth
+ offset
};
1373 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1374 const auto dest
= wr::ToLayoutRect(rect
);
1376 wr::ToBorderSide(ToDeviceColor(aColor
), StyleBorderStyle::Solid
);
1377 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
1378 const bool kBackfaceIsVisible
= true;
1379 const auto wrWidths
= wr::ToBorderWidths(strokeWidth
, strokeWidth
,
1380 strokeWidth
, strokeWidth
);
1381 const auto wrRadius
= wr::ToBorderRadius(outerRadii
);
1382 aPaintData
.mBuilder
.PushBorder(dest
, dest
, kBackfaceIsVisible
, wrWidths
,
1383 {sides
, 4}, wrRadius
);
1385 const LayoutDeviceCoord halfWidth
= strokeWidth
* 0.5f
;
1386 const Float widths
[4] = {halfWidth
+ offset
, halfWidth
+ offset
,
1387 halfWidth
+ offset
, halfWidth
+ offset
};
1388 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1389 LayoutDeviceRect
dest(rect
);
1390 dest
.Deflate(halfWidth
);
1392 MakePathForRoundedRect(aPaintData
, dest
.ToUnknownRect(), outerRadii
);
1393 aPaintData
.Stroke(path
, ColorPattern(ToDeviceColor(aColor
)),
1394 StrokeOptions(strokeWidth
));
1398 DrawRect(accentColor
.Get());
1404 offset
+= strokeWidth
;
1407 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(1.0f
, aDpiRatio
));
1408 rect
.Inflate(strokeWidth
);
1410 DrawRect(accentColor
.GetForeground());
1413 LayoutDeviceIntMargin
Theme::GetWidgetBorder(nsDeviceContext
* aContext
,
1415 StyleAppearance aAppearance
) {
1416 switch (aAppearance
) {
1417 case StyleAppearance::Textfield
:
1418 case StyleAppearance::Textarea
:
1419 case StyleAppearance::NumberInput
:
1420 case StyleAppearance::Listbox
:
1421 case StyleAppearance::Menulist
:
1422 case StyleAppearance::MenulistButton
:
1423 case StyleAppearance::Button
:
1424 // Return the border size from the UA sheet, even though what we paint
1425 // doesn't actually match that. We know this is the UA sheet border
1426 // because we disable native theming when different border widths are
1427 // specified by authors, see Theme::IsWidgetStyled.
1429 // The Rounded() bit is technically redundant, but needed to appease the
1430 // type system, we should always end up with full device pixels due to
1431 // round_border_to_device_pixels at style time.
1432 return LayoutDeviceIntMargin::FromAppUnits(
1433 aFrame
->StyleBorder()->GetComputedBorder(),
1434 aFrame
->PresContext()->AppUnitsPerDevPixel())
1436 case StyleAppearance::Checkbox
:
1437 case StyleAppearance::Radio
: {
1438 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1439 LayoutDeviceIntCoord w
=
1440 ThemeDrawing::SnapBorderWidth(kCheckboxRadioBorderWidth
, dpiRatio
);
1441 return LayoutDeviceIntMargin(w
, w
, w
, w
);
1444 return LayoutDeviceIntMargin();
1448 bool Theme::GetWidgetPadding(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1449 StyleAppearance aAppearance
,
1450 LayoutDeviceIntMargin
* aResult
) {
1451 switch (aAppearance
) {
1452 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1453 // and have a meaningful baseline, so they can't have
1454 // author-specified padding.
1455 case StyleAppearance::Radio
:
1456 case StyleAppearance::Checkbox
:
1457 aResult
->SizeTo(0, 0, 0, 0);
1465 bool Theme::GetWidgetOverflow(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1466 StyleAppearance aAppearance
,
1467 nsRect
* aOverflowRect
) {
1468 CSSIntMargin overflow
;
1469 switch (aAppearance
) {
1470 case StyleAppearance::FocusOutline
: {
1471 // 2px * one segment, or 2px + 1px
1473 StaticPrefs::widget_non_native_theme_solid_outline_style() ? 2 : 3;
1474 overflow
.SizeTo(width
, width
, width
, width
);
1477 case StyleAppearance::Radio
:
1478 case StyleAppearance::Checkbox
:
1479 case StyleAppearance::Range
:
1480 // 2px for each outline segment, plus 1px separation, plus we paint with a
1481 // 1px extra offset, so 6px.
1482 overflow
.SizeTo(6, 6, 6, 6);
1484 case StyleAppearance::Textarea
:
1485 case StyleAppearance::Textfield
:
1486 case StyleAppearance::NumberInput
:
1487 case StyleAppearance::Listbox
:
1488 case StyleAppearance::MenulistButton
:
1489 case StyleAppearance::Menulist
:
1490 case StyleAppearance::Button
:
1491 // 2px for each segment, plus 1px separation, but we paint 1px inside
1492 // the border area so 4px overflow.
1493 overflow
.SizeTo(4, 4, 4, 4);
1499 // TODO: This should convert from device pixels to app units, not from CSS
1500 // pixels. And it should take the dpi ratio into account.
1501 // Using CSS pixels can cause the overflow to be too small if the page is
1503 aOverflowRect
->Inflate(CSSPixel::ToAppUnits(overflow
));
1507 auto Theme::GetScrollbarSizes(nsPresContext
* aPresContext
,
1508 StyleScrollbarWidth aWidth
, Overlay aOverlay
)
1510 return GetScrollbarDrawing().GetScrollbarSizes(aPresContext
, aWidth
,
1514 nscoord
Theme::GetCheckboxRadioPrefSize() {
1515 return CSSPixel::ToAppUnits(kCheckboxRadioContentBoxSize
);
1519 UniquePtr
<ScrollbarDrawing
> Theme::ScrollbarStyle() {
1520 switch (StaticPrefs::widget_non_native_theme_scrollbar_style()) {
1522 return MakeUnique
<ScrollbarDrawingCocoa
>();
1524 return MakeUnique
<ScrollbarDrawingGTK
>();
1526 return MakeUnique
<ScrollbarDrawingAndroid
>();
1528 return MakeUnique
<ScrollbarDrawingWin
>();
1530 return MakeUnique
<ScrollbarDrawingWin11
>();
1534 // Default to native scrollbar style for each platform.
1536 if (IsWin11OrLater()) {
1537 return MakeUnique
<ScrollbarDrawingWin11
>();
1539 return MakeUnique
<ScrollbarDrawingWin
>();
1540 #elif MOZ_WIDGET_COCOA
1541 return MakeUnique
<ScrollbarDrawingCocoa
>();
1542 #elif MOZ_WIDGET_GTK
1543 return MakeUnique
<ScrollbarDrawingGTK
>();
1545 return MakeUnique
<ScrollbarDrawingAndroid
>();
1547 # error "Unknown platform, need scrollbar implementation."
1551 LayoutDeviceIntSize
Theme::GetMinimumWidgetSize(nsPresContext
* aPresContext
,
1553 StyleAppearance aAppearance
) {
1554 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1556 if (IsWidgetScrollbarPart(aAppearance
)) {
1557 return GetScrollbarDrawing().GetMinimumWidgetSize(aPresContext
, aAppearance
,
1561 LayoutDeviceIntSize result
;
1562 switch (aAppearance
) {
1563 case StyleAppearance::RangeThumb
:
1564 result
.SizeTo((kMinimumRangeThumbSize
* dpiRatio
).Rounded(),
1565 (kMinimumRangeThumbSize
* dpiRatio
).Rounded());
1567 case StyleAppearance::MozMenulistArrowButton
:
1568 result
.width
= (kMinimumDropdownArrowButtonWidth
* dpiRatio
).Rounded();
1570 case StyleAppearance::SpinnerUpbutton
:
1571 case StyleAppearance::SpinnerDownbutton
:
1572 result
.width
= (kMinimumSpinnerButtonWidth
* dpiRatio
).Rounded();
1573 result
.height
= (kMinimumSpinnerButtonHeight
* dpiRatio
).Rounded();
1581 nsITheme::Transparency
Theme::GetWidgetTransparency(
1582 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1583 if (auto scrollbar
= GetScrollbarDrawing().GetScrollbarPartTransparency(
1584 aFrame
, aAppearance
)) {
1587 if (aAppearance
== StyleAppearance::Tooltip
) {
1588 // We draw a rounded rect, so we need transparency.
1589 return eTransparent
;
1591 return eUnknownTransparency
;
1595 Theme::WidgetStateChanged(nsIFrame
* aFrame
, StyleAppearance aAppearance
,
1596 nsAtom
* aAttribute
, bool* aShouldRepaint
,
1597 const nsAttrValue
* aOldValue
) {
1599 // Hover/focus/active changed. Always repaint.
1600 *aShouldRepaint
= true;
1602 // Check the attribute to see if it's relevant.
1603 // disabled, checked, dlgtype, default, etc.
1604 *aShouldRepaint
= false;
1605 if (aAttribute
== nsGkAtoms::disabled
|| aAttribute
== nsGkAtoms::checked
||
1606 aAttribute
== nsGkAtoms::selected
||
1607 aAttribute
== nsGkAtoms::visuallyselected
||
1608 aAttribute
== nsGkAtoms::menuactive
||
1609 aAttribute
== nsGkAtoms::sortDirection
||
1610 aAttribute
== nsGkAtoms::focused
|| aAttribute
== nsGkAtoms::_default
||
1611 aAttribute
== nsGkAtoms::open
|| aAttribute
== nsGkAtoms::hover
) {
1612 *aShouldRepaint
= true;
1620 Theme::ThemeChanged() { return NS_OK
; }
1622 bool Theme::WidgetAppearanceDependsOnWindowFocus(StyleAppearance aAppearance
) {
1623 return IsWidgetScrollbarPart(aAppearance
);
1626 nsITheme::ThemeGeometryType
Theme::ThemeGeometryTypeForWidget(
1627 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1628 return eThemeGeometryTypeUnknown
;
1631 bool Theme::ThemeSupportsWidget(nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1632 StyleAppearance aAppearance
) {
1633 switch (aAppearance
) {
1634 case StyleAppearance::Radio
:
1635 case StyleAppearance::Checkbox
:
1636 case StyleAppearance::FocusOutline
:
1637 case StyleAppearance::Textarea
:
1638 case StyleAppearance::Textfield
:
1639 case StyleAppearance::Range
:
1640 case StyleAppearance::RangeThumb
:
1641 case StyleAppearance::ProgressBar
:
1642 case StyleAppearance::Progresschunk
:
1643 case StyleAppearance::Meter
:
1644 case StyleAppearance::Meterchunk
:
1645 case StyleAppearance::ScrollbarbuttonUp
:
1646 case StyleAppearance::ScrollbarbuttonDown
:
1647 case StyleAppearance::ScrollbarbuttonLeft
:
1648 case StyleAppearance::ScrollbarbuttonRight
:
1649 case StyleAppearance::ScrollbarthumbHorizontal
:
1650 case StyleAppearance::ScrollbarthumbVertical
:
1651 case StyleAppearance::ScrollbartrackHorizontal
:
1652 case StyleAppearance::ScrollbartrackVertical
:
1653 case StyleAppearance::ScrollbarHorizontal
:
1654 case StyleAppearance::ScrollbarVertical
:
1655 case StyleAppearance::Scrollcorner
:
1656 case StyleAppearance::Button
:
1657 case StyleAppearance::Listbox
:
1658 case StyleAppearance::Menulist
:
1659 case StyleAppearance::MenulistButton
:
1660 case StyleAppearance::NumberInput
:
1661 case StyleAppearance::MozMenulistArrowButton
:
1662 case StyleAppearance::SpinnerUpbutton
:
1663 case StyleAppearance::SpinnerDownbutton
:
1664 case StyleAppearance::Menuitem
:
1665 case StyleAppearance::Tooltip
:
1666 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
1672 bool Theme::WidgetIsContainer(StyleAppearance aAppearance
) {
1673 switch (aAppearance
) {
1674 case StyleAppearance::MozMenulistArrowButton
:
1675 case StyleAppearance::Radio
:
1676 case StyleAppearance::Checkbox
:
1683 bool Theme::ThemeDrawsFocusForWidget(nsIFrame
*, StyleAppearance
) {
1687 bool Theme::ThemeNeedsComboboxDropmarker() { return true; }
1689 bool Theme::ThemeSupportsScrollbarButtons() {
1690 return GetScrollbarDrawing().ShouldDrawScrollbarButtons();
1693 } // namespace mozilla::widget