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/ScrollContainerFrame.h"
22 #include "mozilla/StaticPrefs_widget.h"
23 #include "mozilla/webrender/WebRenderAPI.h"
24 #include "nsCSSColorUtils.h"
25 #include "nsCSSRendering.h"
26 #include "nsScrollbarFrame.h"
27 #include "nsIScrollbarMediator.h"
28 #include "nsDeviceContext.h"
29 #include "nsLayoutUtils.h"
30 #include "nsRangeFrame.h"
31 #include "PathHelpers.h"
32 #include "ScrollbarDrawingAndroid.h"
33 #include "ScrollbarDrawingCocoa.h"
34 #include "ScrollbarDrawingGTK.h"
35 #include "ScrollbarDrawingWin.h"
36 #include "ScrollbarDrawingWin11.h"
39 # include "mozilla/WindowsVersion.h"
42 using namespace mozilla
;
43 using namespace mozilla::dom
;
44 using namespace mozilla::gfx
;
45 using namespace mozilla::widget
;
49 static constexpr gfx::sRGBColor
sColorGrey10(
50 gfx::sRGBColor::UnusualFromARGB(0xffe9e9ed));
51 static constexpr gfx::sRGBColor
sColorGrey10Alpha50(
52 gfx::sRGBColor::UnusualFromARGB(0x7fe9e9ed));
53 static constexpr gfx::sRGBColor
sColorGrey20(
54 gfx::sRGBColor::UnusualFromARGB(0xffd0d0d7));
55 static constexpr gfx::sRGBColor
sColorGrey30(
56 gfx::sRGBColor::UnusualFromARGB(0xffb1b1b9));
57 static constexpr gfx::sRGBColor
sColorGrey40(
58 gfx::sRGBColor::UnusualFromARGB(0xff8f8f9d));
59 static constexpr gfx::sRGBColor
sColorGrey40Alpha50(
60 gfx::sRGBColor::UnusualFromARGB(0x7f8f8f9d));
61 static constexpr gfx::sRGBColor
sColorGrey50(
62 gfx::sRGBColor::UnusualFromARGB(0xff676774));
63 static constexpr gfx::sRGBColor
sColorGrey60(
64 gfx::sRGBColor::UnusualFromARGB(0xff484851));
66 static constexpr gfx::sRGBColor
sColorMeterGreen10(
67 gfx::sRGBColor::UnusualFromARGB(0xff00ab60));
68 static constexpr gfx::sRGBColor
sColorMeterGreen20(
69 gfx::sRGBColor::UnusualFromARGB(0xff056139));
70 static constexpr gfx::sRGBColor
sColorMeterYellow10(
71 gfx::sRGBColor::UnusualFromARGB(0xffffbd4f));
72 static constexpr gfx::sRGBColor
sColorMeterYellow20(
73 gfx::sRGBColor::UnusualFromARGB(0xffd2811e));
74 static constexpr gfx::sRGBColor
sColorMeterRed10(
75 gfx::sRGBColor::UnusualFromARGB(0xffe22850));
76 static constexpr gfx::sRGBColor
sColorMeterRed20(
77 gfx::sRGBColor::UnusualFromARGB(0xff810220));
79 static const CSSCoord kMinimumRangeThumbSize
= 20.0f
;
80 static const CSSCoord kMinimumDropdownArrowButtonWidth
= 18.0f
;
81 static const CSSCoord kMinimumSpinnerButtonWidth
= 18.0f
;
82 static const CSSCoord kMinimumSpinnerButtonHeight
= 9.0f
;
83 static const CSSCoord kButtonBorderWidth
= 1.0f
;
84 static const CSSCoord kMenulistBorderWidth
= 1.0f
;
85 static const CSSCoord kTextFieldBorderWidth
= 1.0f
;
86 static const CSSCoord kRangeHeight
= 6.0f
;
87 static const CSSCoord kProgressbarHeight
= 6.0f
;
88 static const CSSCoord kMeterHeight
= 12.0f
;
90 static constexpr CSSCoord kCheckboxRadioBorderWidth
= 2.0f
;
91 static constexpr CSSCoord kCheckboxRadioSize
= 14.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());
191 auto Theme::GetDPIRatio(nsIFrame
* aFrame
,
192 StyleAppearance aAppearance
) -> DPIRatio
{
193 // Widgets react to zoom, except scrollbars.
194 nsPresContext
* pc
= aFrame
->PresContext();
195 if (IsWidgetScrollbarPart(aAppearance
)) {
196 return GetScrollbarDrawing().GetDPIRatioForScrollbarPart(pc
);
198 return DPIRatio(aFrame
->Style()->EffectiveZoom().Zoom(
199 float(AppUnitsPerCSSPixel()) / pc
->AppUnitsPerDevPixel()));
202 // Checkbox and radio need to preserve aspect-ratio for compat. We also snap the
203 // size to exact device pixels to avoid snapping disorting the circles.
204 static LayoutDeviceRect
CheckBoxRadioRect(const LayoutDeviceRect
& aRect
) {
205 // Place a square rect in the center of aRect.
206 auto size
= std::trunc(std::min(aRect
.width
, aRect
.height
));
207 auto position
= aRect
.Center() - LayoutDevicePoint(size
* 0.5, size
* 0.5);
208 return LayoutDeviceRect(position
, LayoutDeviceSize(size
, size
));
211 std::tuple
<sRGBColor
, sRGBColor
, sRGBColor
> Theme::ComputeCheckboxColors(
212 const ElementState
& aState
, StyleAppearance aAppearance
,
213 const Colors
& aColors
) {
214 MOZ_ASSERT(aAppearance
== StyleAppearance::Checkbox
||
215 aAppearance
== StyleAppearance::Radio
);
217 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
218 bool isChecked
= aState
.HasState(ElementState::CHECKED
);
219 bool isIndeterminate
= aAppearance
== StyleAppearance::Checkbox
&&
220 aState
.HasState(ElementState::INDETERMINATE
);
222 if (isChecked
|| isIndeterminate
) {
224 auto bg
= ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::No
);
225 auto fg
= aColors
.HighContrast()
226 ? aColors
.System(StyleSystemColor::Buttonface
)
227 : sRGBColor::White(aColors
.IsDark() ? .4f
: .8f
);
228 return std::make_tuple(bg
, bg
, fg
);
232 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
233 bool isHovered
= aState
.HasState(ElementState::HOVER
);
235 if (aColors
.HighContrast()) {
236 sRGBColor border
= (isHovered
&& !isActive
)
237 ? aColors
.System(StyleSystemColor::Selecteditem
)
238 : aColors
.System(StyleSystemColor::Buttontext
);
239 auto bg
= (isHovered
|| isActive
)
240 ? aColors
.System(StyleSystemColor::Selecteditem
)
241 : aColors
.System(StyleSystemColor::Buttontext
);
242 auto fg
= (isHovered
|| isActive
)
243 ? aColors
.System(StyleSystemColor::Selecteditemtext
)
244 : aColors
.System(StyleSystemColor::Buttonface
);
245 return std::make_tuple(bg
, border
, fg
);
248 const auto& bg
= isActive
? aColors
.Accent().GetDarker()
249 : isHovered
? aColors
.Accent().GetDark()
250 : aColors
.Accent().Get();
251 const auto& fg
= aColors
.Accent().GetForeground();
252 return std::make_tuple(bg
, bg
, fg
);
256 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::No
);
257 // We don't paint a checkmark in this case so any color would do.
258 return std::make_tuple(bg
, border
, sTransparent
);
261 sRGBColor
Theme::ComputeBorderColor(const ElementState
& aState
,
262 const Colors
& aColors
,
263 OutlineCoversBorder aOutlineCoversBorder
) {
264 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
266 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
267 bool isHovered
= aState
.HasState(ElementState::HOVER
);
268 if (aColors
.HighContrast()) {
269 return aColors
.System(isDisabled
? StyleSystemColor::Graytext
270 : (isHovered
&& !isActive
)
271 ? StyleSystemColor::Selecteditem
272 : StyleSystemColor::Buttontext
);
274 bool isFocused
= aState
.HasState(ElementState::FOCUSRING
);
276 return sColorGrey40Alpha50
;
278 if (isFocused
&& aOutlineCoversBorder
== OutlineCoversBorder::Yes
) {
279 // If we draw the outline over the border, prevent issues where the border
280 // shows underneath if it snaps in the wrong direction by using a
281 // transparent border. An alternative to this is ensuring that we snap the
282 // offset in PaintRoundedFocusRect the same was a we snap border widths, so
283 // that negative offsets are guaranteed to cover the border.
284 // But this looks harder to mess up.
287 bool dark
= aColors
.IsDark();
289 return dark
? sColorGrey20
: sColorGrey60
;
292 return dark
? sColorGrey30
: sColorGrey50
;
297 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeButtonColors(
298 const ElementState
& aState
, const Colors
& aColors
, nsIFrame
* aFrame
) {
300 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
301 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
302 bool isHovered
= aState
.HasState(ElementState::HOVER
);
304 nscolor backgroundColor
= [&] {
305 if (aState
.HasState(ElementState::AUTOFILL
)) {
306 return aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
);
309 return aColors
.SystemNs(StyleSystemColor::MozButtondisabledface
);
312 return aColors
.SystemNs(StyleSystemColor::MozButtonactiveface
);
315 return aColors
.SystemNs(StyleSystemColor::MozButtonhoverface
);
317 return aColors
.SystemNs(StyleSystemColor::Buttonface
);
320 const sRGBColor borderColor
=
321 ComputeBorderColor(aState
, aColors
, OutlineCoversBorder::Yes
);
322 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
325 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeTextfieldColors(
326 const ElementState
& aState
, const Colors
& aColors
,
327 OutlineCoversBorder aOutlineCoversBorder
) {
328 nscolor backgroundColor
= [&] {
329 if (aState
.HasState(ElementState::AUTOFILL
)) {
330 return aColors
.SystemNs(StyleSystemColor::MozAutofillBackground
);
332 if (aState
.HasState(ElementState::DISABLED
)) {
333 return aColors
.SystemNs(StyleSystemColor::MozDisabledfield
);
335 return aColors
.SystemNs(StyleSystemColor::Field
);
338 const sRGBColor borderColor
=
339 ComputeBorderColor(aState
, aColors
, aOutlineCoversBorder
);
340 return std::make_pair(sRGBColor::FromABGR(backgroundColor
), borderColor
);
343 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeProgressColors(
344 const ElementState
& aState
, const Colors
& aColors
) {
346 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
347 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
348 bool isHovered
= aState
.HasState(ElementState::HOVER
);
350 if (aColors
.HighContrast()) {
352 return aColors
.SystemPair(StyleSystemColor::Graytext
,
353 StyleSystemColor::Graytext
);
356 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
357 StyleSystemColor::Buttontext
);
360 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
361 StyleSystemColor::Selecteditem
);
363 return aColors
.SystemPair(StyleSystemColor::Buttontext
,
364 StyleSystemColor::Buttontext
);
368 return std::make_pair(sColorGrey40Alpha50
, sColorGrey40Alpha50
);
370 if (isActive
|| isHovered
) {
371 return std::make_pair(aColors
.Accent().GetDark(),
372 aColors
.Accent().GetDarker());
374 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
377 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeTrackColors(
378 const ElementState
& aState
, const Colors
& aColors
) {
380 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
381 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
382 bool isHovered
= aState
.HasState(ElementState::HOVER
);
384 if (aColors
.HighContrast()) {
386 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
387 StyleSystemColor::Graytext
);
390 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
391 StyleSystemColor::Buttontext
);
394 return aColors
.SystemPair(StyleSystemColor::Selecteditemtext
,
395 StyleSystemColor::Selecteditem
);
397 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
398 StyleSystemColor::Buttontext
);
402 return std::make_pair(sColorGrey10Alpha50
, sColorGrey40Alpha50
);
404 if (isActive
|| isHovered
) {
405 return std::make_pair(sColorGrey20
, sColorGrey50
);
407 return std::make_pair(sColorGrey10
, sColorGrey40
);
410 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeRangeThumbColors(
411 const ElementState
& aState
, const Colors
& aColors
) {
413 aState
.HasAllStates(ElementState::HOVER
| ElementState::ACTIVE
);
414 bool isDisabled
= aState
.HasState(ElementState::DISABLED
);
415 bool isHovered
= aState
.HasState(ElementState::HOVER
);
417 if (aColors
.HighContrast()) {
419 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
420 StyleSystemColor::Graytext
);
422 if (isActive
|| isHovered
) {
423 return aColors
.SystemPair(StyleSystemColor::Selecteditemtext
,
424 StyleSystemColor::Selecteditem
);
426 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
427 StyleSystemColor::Buttontext
);
430 const sRGBColor
& backgroundColor
= [&] {
435 return aColors
.Accent().Get();
443 const sRGBColor borderColor
= sRGBColor::OpaqueWhite();
444 return std::make_pair(backgroundColor
, borderColor
);
447 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressColors(
448 const Colors
& aColors
) {
449 if (aColors
.HighContrast()) {
450 return aColors
.SystemPair(StyleSystemColor::Selecteditem
,
451 StyleSystemColor::Buttontext
);
453 return std::make_pair(aColors
.Accent().Get(), aColors
.Accent().GetDark());
456 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeProgressTrackColors(
457 const Colors
& aColors
) {
458 if (aColors
.HighContrast()) {
459 return aColors
.SystemPair(StyleSystemColor::Buttonface
,
460 StyleSystemColor::Buttontext
);
462 return std::make_pair(sColorGrey10
, sColorGrey40
);
465 std::pair
<sRGBColor
, sRGBColor
> Theme::ComputeMeterchunkColors(
466 const ElementState
& aMeterState
, const Colors
& aColors
) {
467 if (aColors
.HighContrast()) {
468 return ComputeProgressColors(aColors
);
470 sRGBColor borderColor
= sColorMeterGreen20
;
471 sRGBColor chunkColor
= sColorMeterGreen10
;
473 if (aMeterState
.HasState(ElementState::SUB_OPTIMUM
)) {
474 borderColor
= sColorMeterYellow20
;
475 chunkColor
= sColorMeterYellow10
;
476 } else if (aMeterState
.HasState(ElementState::SUB_SUB_OPTIMUM
)) {
477 borderColor
= sColorMeterRed20
;
478 chunkColor
= sColorMeterRed10
;
481 return std::make_pair(chunkColor
, borderColor
);
484 std::array
<sRGBColor
, 3> Theme::ComputeFocusRectColors(const Colors
& aColors
) {
485 if (aColors
.HighContrast()) {
486 return {aColors
.System(StyleSystemColor::Selecteditem
),
487 aColors
.System(StyleSystemColor::Window
),
488 aColors
.System(StyleSystemColor::Buttontext
)};
490 const auto& accent
= aColors
.Accent();
491 const sRGBColor middle
=
492 aColors
.IsDark() ? sRGBColor::Black(.3f
) : sRGBColor::White(.3f
);
493 return {accent
.Get(), middle
, accent
.GetLight()};
496 template <typename PaintBackendData
>
497 void Theme::PaintRoundedFocusRect(PaintBackendData
& aBackendData
,
498 const LayoutDeviceRect
& aRect
,
499 const Colors
& aColors
, DPIRatio aDpiRatio
,
500 CSSCoord aRadius
, CSSCoord aOffset
) {
501 // NOTE(emilio): If the widths or offsets here change, make sure to tweak
502 // the GetWidgetOverflow path for FocusOutline.
503 auto [innerColor
, middleColor
, outerColor
] = ComputeFocusRectColors(aColors
);
505 LayoutDeviceRect
focusRect(aRect
);
507 // The focus rect is painted outside of the border area (aRect), see:
509 // data:text/html,<div style="border: 1px solid; outline: 2px solid
512 // But some controls might provide a negative offset to cover the border, if
514 CSSCoord strokeWidth
= 2.0f
;
515 auto strokeWidthDevPx
=
516 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
517 CSSCoord strokeRadius
= aRadius
;
518 focusRect
.Inflate(aOffset
* aDpiRatio
+ strokeWidthDevPx
);
520 ThemeDrawing::PaintRoundedRectWithRadius(
521 aBackendData
, focusRect
, sTransparent
, innerColor
, strokeWidth
,
522 strokeRadius
, aDpiRatio
);
524 strokeWidth
= CSSCoord(1.0f
);
526 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
527 strokeRadius
+= strokeWidth
;
528 focusRect
.Inflate(strokeWidthDevPx
);
530 ThemeDrawing::PaintRoundedRectWithRadius(
531 aBackendData
, focusRect
, sTransparent
, middleColor
, strokeWidth
,
532 strokeRadius
, aDpiRatio
);
534 strokeWidth
= CSSCoord(2.0f
);
536 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(strokeWidth
, aDpiRatio
));
537 strokeRadius
+= strokeWidth
;
538 focusRect
.Inflate(strokeWidthDevPx
);
540 ThemeDrawing::PaintRoundedRectWithRadius(
541 aBackendData
, focusRect
, sTransparent
, outerColor
, strokeWidth
,
542 strokeRadius
, aDpiRatio
);
545 void Theme::PaintCheckboxControl(DrawTarget
& aDrawTarget
,
546 const LayoutDeviceRect
& aRect
,
547 const ElementState
& aState
,
548 const Colors
& aColors
, DPIRatio aDpiRatio
) {
549 auto [backgroundColor
, borderColor
, checkColor
] =
550 ComputeCheckboxColors(aState
, StyleAppearance::Checkbox
, aColors
);
552 const CSSCoord radius
= 2.0f
;
553 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
554 if (backgroundColor
== borderColor
) {
557 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
,
558 backgroundColor
, borderColor
,
559 borderWidth
, radius
, aDpiRatio
);
562 if (aState
.HasState(ElementState::INDETERMINATE
)) {
563 PaintIndeterminateMark(aDrawTarget
, aRect
, checkColor
);
564 } else if (aState
.HasState(ElementState::CHECKED
)) {
565 PaintCheckMark(aDrawTarget
, aRect
, checkColor
);
568 if (aState
.HasState(ElementState::FOCUSRING
)) {
569 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
573 void Theme::PaintCheckMark(DrawTarget
& aDrawTarget
,
574 const LayoutDeviceRect
& aRect
,
575 const sRGBColor
& aColor
) {
576 // Points come from the coordinates on a 14X14 (kCheckboxRadioSize)
577 // unit box centered at 0,0
578 const float checkPolygonX
[] = {-4.5f
, -1.5f
, -0.5f
, 5.0f
, 4.75f
,
579 3.5f
, -0.5f
, -1.5f
, -3.5f
};
580 const float checkPolygonY
[] = {0.5f
, 4.0f
, 4.0f
, -2.5f
, -4.0f
,
581 -4.0f
, 1.0f
, 1.25f
, -1.0f
};
582 const int32_t checkNumPoints
= sizeof(checkPolygonX
) / sizeof(float);
583 const float scale
= ThemeDrawing::ScaleToFillRect(aRect
, kCheckboxRadioSize
);
584 auto center
= aRect
.Center().ToUnknownPoint();
586 RefPtr
<PathBuilder
> builder
= aDrawTarget
.CreatePathBuilder();
587 Point p
= center
+ Point(checkPolygonX
[0] * scale
, checkPolygonY
[0] * scale
);
589 for (int32_t i
= 1; i
< checkNumPoints
; i
++) {
590 p
= center
+ Point(checkPolygonX
[i
] * scale
, checkPolygonY
[i
] * scale
);
593 RefPtr
<Path
> path
= builder
->Finish();
595 aDrawTarget
.Fill(path
, ColorPattern(ToDeviceColor(aColor
)));
598 void Theme::PaintIndeterminateMark(DrawTarget
& aDrawTarget
,
599 const LayoutDeviceRect
& aRect
,
600 const sRGBColor
& aColor
) {
601 const CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
602 const float scale
= ThemeDrawing::ScaleToFillRect(aRect
, kCheckboxRadioSize
);
603 Rect rect
= aRect
.ToUnknownRect();
604 rect
.y
+= (rect
.height
/ 2) - (borderWidth
* scale
/ 2);
605 rect
.height
= borderWidth
* scale
;
606 rect
.x
+= (borderWidth
* scale
) + (borderWidth
* scale
/ 8);
607 rect
.width
-= ((borderWidth
* scale
) + (borderWidth
* scale
/ 8)) * 2;
609 aDrawTarget
.FillRect(rect
, ColorPattern(ToDeviceColor(aColor
)));
612 template <typename PaintBackendData
>
613 void Theme::PaintStrokedCircle(PaintBackendData
& aPaintData
,
614 const LayoutDeviceRect
& aRect
,
615 const sRGBColor
& aBackgroundColor
,
616 const sRGBColor
& aBorderColor
,
617 const CSSCoord aBorderWidth
,
618 DPIRatio aDpiRatio
) {
619 auto radius
= LayoutDeviceCoord(aRect
.Size().width
) / aDpiRatio
;
620 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, aBackgroundColor
,
621 aBorderColor
, aBorderWidth
, radius
,
625 void Theme::PaintCircleShadow(WebRenderBackendData
& aWrData
,
626 const LayoutDeviceRect
& aBoxRect
,
627 const LayoutDeviceRect
& aClipRect
,
628 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
629 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
630 const bool kBackfaceIsVisible
= true;
631 const LayoutDeviceCoord stdDev
= aShadowBlurStdDev
* aDpiRatio
;
632 const LayoutDevicePoint shadowOffset
= aShadowOffset
* aDpiRatio
;
633 const IntSize inflation
=
634 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
635 LayoutDeviceRect shadowRect
= aBoxRect
;
636 shadowRect
.MoveBy(shadowOffset
);
637 shadowRect
.Inflate(inflation
.width
, inflation
.height
);
638 const auto boxRect
= wr::ToLayoutRect(aBoxRect
);
639 aWrData
.mBuilder
.PushBoxShadow(
640 wr::ToLayoutRect(shadowRect
), wr::ToLayoutRect(aClipRect
),
641 kBackfaceIsVisible
, boxRect
,
642 wr::ToLayoutVector2D(aShadowOffset
* aDpiRatio
),
643 wr::ToColorF(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)), stdDev
,
644 /* aSpread = */ 0.0f
,
645 wr::ToBorderRadius(gfx::RectCornerRadii(aBoxRect
.Size().width
)),
646 wr::BoxShadowClipMode::Outset
);
649 void Theme::PaintCircleShadow(DrawTarget
& aDrawTarget
,
650 const LayoutDeviceRect
& aBoxRect
,
651 const LayoutDeviceRect
& aClipRect
,
652 float aShadowAlpha
, const CSSPoint
& aShadowOffset
,
653 CSSCoord aShadowBlurStdDev
, DPIRatio aDpiRatio
) {
654 Float stdDev
= aShadowBlurStdDev
* aDpiRatio
;
655 Point offset
= (aShadowOffset
* aDpiRatio
).ToUnknownPoint();
657 RefPtr
<FilterNode
> blurFilter
=
658 aDrawTarget
.CreateFilter(FilterType::GAUSSIAN_BLUR
);
663 blurFilter
->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION
, stdDev
);
666 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
667 Rect inflatedRect
= aBoxRect
.ToUnknownRect();
668 inflatedRect
.Inflate(inflation
.width
, inflation
.height
);
669 Rect sourceRectInFilterSpace
=
670 inflatedRect
- aBoxRect
.TopLeft().ToUnknownPoint();
671 Point destinationPointOfSourceRect
= inflatedRect
.TopLeft() + offset
;
673 IntSize dtSize
= RoundedToInt(aBoxRect
.Size().ToUnknownSize());
674 RefPtr
<DrawTarget
> ellipseDT
= aDrawTarget
.CreateSimilarDrawTargetForFilter(
675 dtSize
, SurfaceFormat::A8
, blurFilter
, blurFilter
,
676 sourceRectInFilterSpace
, destinationPointOfSourceRect
);
681 AutoClipRect
clipRect(aDrawTarget
, aClipRect
);
683 RefPtr
<Path
> ellipse
= MakePathForEllipse(
684 *ellipseDT
, (aBoxRect
- aBoxRect
.TopLeft()).Center().ToUnknownPoint(),
685 aBoxRect
.Size().ToUnknownSize());
686 ellipseDT
->Fill(ellipse
,
687 ColorPattern(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)));
688 RefPtr
<SourceSurface
> ellipseSurface
= ellipseDT
->Snapshot();
690 blurFilter
->SetInput(IN_GAUSSIAN_BLUR_IN
, ellipseSurface
);
691 aDrawTarget
.DrawFilter(blurFilter
, sourceRectInFilterSpace
,
692 destinationPointOfSourceRect
);
695 template <typename PaintBackendData
>
696 void Theme::PaintRadioControl(PaintBackendData
& aPaintData
,
697 const LayoutDeviceRect
& aRect
,
698 const ElementState
& aState
, const Colors
& aColors
,
699 DPIRatio aDpiRatio
) {
700 auto [backgroundColor
, borderColor
, checkColor
] =
701 ComputeCheckboxColors(aState
, StyleAppearance::Radio
, aColors
);
703 CSSCoord borderWidth
= kCheckboxRadioBorderWidth
;
704 if (backgroundColor
== borderColor
) {
707 PaintStrokedCircle(aPaintData
, aRect
, backgroundColor
, borderColor
,
708 borderWidth
, aDpiRatio
);
711 if (aState
.HasState(ElementState::CHECKED
)) {
712 LayoutDeviceRect
rect(aRect
);
713 auto width
= LayoutDeviceCoord(
714 ThemeDrawing::SnapBorderWidth(kCheckboxRadioBorderWidth
, aDpiRatio
));
717 PaintStrokedCircle(aPaintData
, rect
, backgroundColor
, checkColor
,
718 kCheckboxRadioBorderWidth
, aDpiRatio
);
721 if (aState
.HasState(ElementState::FOCUSRING
)) {
722 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, 5.0f
, 1.0f
);
726 template <typename PaintBackendData
>
727 void Theme::PaintTextField(PaintBackendData
& aPaintData
,
728 const LayoutDeviceRect
& aRect
,
729 const ElementState
& aState
, const Colors
& aColors
,
730 DPIRatio aDpiRatio
) {
731 auto [backgroundColor
, borderColor
] =
732 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
734 const CSSCoord radius
= 2.0f
;
736 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
737 borderColor
, kTextFieldBorderWidth
,
740 if (aState
.HasState(ElementState::FOCUSRING
)) {
741 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
742 radius
+ kTextFieldBorderWidth
,
743 -kTextFieldBorderWidth
);
747 template <typename PaintBackendData
>
748 void Theme::PaintListbox(PaintBackendData
& aPaintData
,
749 const LayoutDeviceRect
& aRect
,
750 const ElementState
& aState
, const Colors
& aColors
,
751 DPIRatio aDpiRatio
) {
752 const CSSCoord radius
= 2.0f
;
753 auto [backgroundColor
, borderColor
] =
754 ComputeTextfieldColors(aState
, aColors
, OutlineCoversBorder::Yes
);
756 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
757 borderColor
, kMenulistBorderWidth
,
760 if (aState
.HasState(ElementState::FOCUSRING
)) {
761 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
762 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
766 template <typename PaintBackendData
>
767 void Theme::PaintMenulist(PaintBackendData
& aDrawTarget
,
768 const LayoutDeviceRect
& aRect
,
769 const ElementState
& aState
, const Colors
& aColors
,
770 DPIRatio aDpiRatio
) {
771 const CSSCoord radius
= 4.0f
;
772 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
774 ThemeDrawing::PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
,
775 borderColor
, kMenulistBorderWidth
,
778 if (aState
.HasState(ElementState::FOCUSRING
)) {
779 PaintRoundedFocusRect(aDrawTarget
, aRect
, aColors
, aDpiRatio
,
780 radius
+ kMenulistBorderWidth
, -kMenulistBorderWidth
);
784 enum class PhysicalArrowDirection
{
790 void Theme::PaintMenulistArrow(nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
,
791 const LayoutDeviceRect
& aRect
) {
792 // not const: these may be negated in-place below
793 float polygonX
[] = {-4.0f
, -0.5f
, 0.5f
, 4.0f
, 4.0f
,
794 3.0f
, 0.0f
, 0.0f
, -3.0f
, -4.0f
};
795 float polygonY
[] = {-1, 3.0f
, 3.0f
, -1.0f
, -2.0f
,
796 -2.0f
, 1.5f
, 1.5f
, -2.0f
, -2.0f
};
798 const float kPolygonSize
= kMinimumDropdownArrowButtonWidth
;
799 const auto direction
= [&] {
800 const auto wm
= aFrame
->GetWritingMode();
801 switch (wm
.GetBlockDir()) {
802 case WritingMode::BlockDir::LR
:
803 return PhysicalArrowDirection::Right
;
804 case WritingMode::BlockDir::RL
:
805 return PhysicalArrowDirection::Left
;
806 case WritingMode::BlockDir::TB
:
807 return PhysicalArrowDirection::Bottom
;
809 MOZ_ASSERT_UNREACHABLE("Unknown direction?");
810 return PhysicalArrowDirection::Bottom
;
813 auto const [xs
, ys
] = [&] {
814 using Pair
= std::pair
<const float*, const float*>;
816 case PhysicalArrowDirection::Left
:
817 // rotate 90°: [[0,1],[-1,0]]
818 for (float& f
: polygonY
) {
821 return Pair(polygonY
, polygonX
);
823 case PhysicalArrowDirection::Right
:
824 // rotate 270°: [[0,-1],[1,0]]
825 for (float& f
: polygonX
) {
828 return Pair(polygonY
, polygonX
);
830 case PhysicalArrowDirection::Bottom
:
831 // rotate 0°: [[1,0],[0,1]]
832 return Pair(polygonX
, polygonY
);
834 MOZ_ASSERT_UNREACHABLE("Unknown direction?");
835 return Pair(polygonX
, polygonY
);
838 const auto arrowColor
= sRGBColor::FromABGR(
839 nsLayoutUtils::GetTextColor(aFrame
, &nsStyleText::mWebkitTextFillColor
));
840 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, xs
, ys
, kPolygonSize
,
841 ArrayLength(polygonX
), arrowColor
);
844 void Theme::PaintSpinnerButton(nsIFrame
* aFrame
, DrawTarget
& aDrawTarget
,
845 const LayoutDeviceRect
& aRect
,
846 const ElementState
& aState
,
847 StyleAppearance aAppearance
,
848 const Colors
& aColors
, DPIRatio aDpiRatio
) {
849 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aColors
);
851 aDrawTarget
.FillRect(aRect
.ToUnknownRect(),
852 ColorPattern(ToDeviceColor(backgroundColor
)));
854 const float kPolygonX
[] = {-3.5f
, -0.5f
, 0.5f
, 3.5f
, 3.5f
,
855 2.5f
, 0.0f
, 0.0f
, -2.5f
, -3.5f
};
856 float polygonY
[] = {-1.5f
, 1.5f
, 1.5f
, -1.5f
, -2.5f
,
857 -2.5f
, 0.0f
, 0.0f
, -2.5f
, -2.5f
};
859 const float kPolygonSize
= kMinimumSpinnerButtonHeight
;
860 if (aAppearance
== StyleAppearance::SpinnerUpbutton
) {
861 for (auto& coord
: polygonY
) {
866 ThemeDrawing::PaintArrow(aDrawTarget
, aRect
, kPolygonX
, polygonY
,
867 kPolygonSize
, ArrayLength(kPolygonX
), borderColor
);
870 template <typename PaintBackendData
>
871 void Theme::PaintRange(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
872 const LayoutDeviceRect
& aRect
,
873 const ElementState
& aState
, const Colors
& aColors
,
874 DPIRatio aDpiRatio
, bool aHorizontal
) {
875 nsRangeFrame
* rangeFrame
= do_QueryFrame(aFrame
);
880 auto tickMarks
= rangeFrame
->TickMarks();
881 double progress
= rangeFrame
->GetValueAsFractionOfRange();
883 LayoutDeviceRect
thumbRect(0, 0, kMinimumRangeThumbSize
* aDpiRatio
,
884 kMinimumRangeThumbSize
* aDpiRatio
);
885 LayoutDeviceRect
progressClipRect(aRect
);
886 LayoutDeviceRect
trackClipRect(aRect
);
887 const LayoutDeviceCoord verticalSize
= kRangeHeight
* aDpiRatio
;
888 const LayoutDeviceCoord
tickMarkWidth(
889 ThemeDrawing::SnapBorderWidth(1.0f
, aDpiRatio
));
890 const LayoutDeviceCoord
tickMarkHeight(
891 ThemeDrawing::SnapBorderWidth(5.0f
, aDpiRatio
));
892 LayoutDevicePoint tickMarkOrigin
, tickMarkDirection
;
893 LayoutDeviceSize tickMarkSize
;
895 rect
.height
= verticalSize
;
896 rect
.y
= aRect
.y
+ (aRect
.height
- rect
.height
) / 2;
897 tickMarkSize
= LayoutDeviceSize(tickMarkWidth
, tickMarkHeight
);
898 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) / 2;
900 if (IsFrameRTL(aFrame
)) {
902 LayoutDevicePoint(aRect
.XMost() - thumbRect
.width
/ 2, aRect
.YMost());
903 tickMarkDirection
= LayoutDevicePoint(-1.0f
, 0.0f
);
905 aRect
.x
+ (aRect
.width
- thumbRect
.width
) * (1.0 - progress
);
906 float midPoint
= thumbRect
.Center().X();
907 trackClipRect
.SetBoxX(aRect
.X(), midPoint
);
908 progressClipRect
.SetBoxX(midPoint
, aRect
.XMost());
911 LayoutDevicePoint(aRect
.x
+ thumbRect
.width
/ 2, aRect
.YMost());
912 tickMarkDirection
= LayoutDevicePoint(1.0, 0.0f
);
913 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) * progress
;
914 float midPoint
= thumbRect
.Center().X();
915 progressClipRect
.SetBoxX(aRect
.X(), midPoint
);
916 trackClipRect
.SetBoxX(midPoint
, aRect
.XMost());
919 rect
.width
= verticalSize
;
920 rect
.x
= aRect
.x
+ (aRect
.width
- rect
.width
) / 2;
921 tickMarkOrigin
= LayoutDevicePoint(aRect
.XMost() - tickMarkHeight
/ 4,
922 aRect
.YMost() - thumbRect
.width
/ 2);
923 tickMarkDirection
= LayoutDevicePoint(0.0f
, -1.0f
);
924 tickMarkSize
= LayoutDeviceSize(tickMarkHeight
, tickMarkWidth
);
925 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) / 2;
927 if (rangeFrame
->IsUpwards()) {
929 aRect
.y
+ (aRect
.height
- thumbRect
.height
) * (1.0 - progress
);
930 float midPoint
= thumbRect
.Center().Y();
931 trackClipRect
.SetBoxY(aRect
.Y(), midPoint
);
932 progressClipRect
.SetBoxY(midPoint
, aRect
.YMost());
934 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) * progress
;
935 float midPoint
= thumbRect
.Center().Y();
936 trackClipRect
.SetBoxY(midPoint
, aRect
.YMost());
937 progressClipRect
.SetBoxY(aRect
.Y(), midPoint
);
941 const CSSCoord borderWidth
= 1.0f
;
942 const CSSCoord radius
= 3.0f
;
944 auto [progressColor
, progressBorderColor
] =
945 ComputeRangeProgressColors(aState
, aColors
);
946 auto [trackColor
, trackBorderColor
] =
947 ComputeRangeTrackColors(aState
, aColors
);
948 auto tickMarkColor
= trackBorderColor
;
950 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, progressClipRect
,
951 progressColor
, progressBorderColor
,
952 borderWidth
, radius
, aDpiRatio
);
954 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, trackClipRect
,
955 trackColor
, trackBorderColor
,
956 borderWidth
, radius
, aDpiRatio
);
958 if (!aState
.HasState(ElementState::DISABLED
)) {
959 // Ensure the shadow doesn't expand outside of our overflow rect declared in
960 // GetWidgetOverflow().
961 auto overflowRect
= aRect
;
962 overflowRect
.Inflate(CSSCoord(6.0f
) * aDpiRatio
);
964 PaintCircleShadow(aPaintData
, thumbRect
, overflowRect
, 0.3f
,
965 CSSPoint(0.0f
, 2.0f
), 2.0f
, aDpiRatio
);
968 tickMarkDirection
.x
*= aRect
.width
- thumbRect
.width
;
969 tickMarkDirection
.y
*= aRect
.height
- thumbRect
.height
;
971 LayoutDevicePoint(tickMarkSize
.width
, tickMarkSize
.height
) / 2;
972 auto tickMarkRect
= LayoutDeviceRect(tickMarkOrigin
, tickMarkSize
);
973 for (auto tickMark
: tickMarks
) {
974 auto tickMarkOffset
=
976 float(rangeFrame
->GetDoubleAsFractionOfRange(tickMark
));
977 ThemeDrawing::FillRect(aPaintData
, tickMarkRect
+ tickMarkOffset
,
981 // Draw the thumb on top.
982 const CSSCoord thumbBorderWidth
= 2.0f
;
983 auto [thumbColor
, thumbBorderColor
] =
984 ComputeRangeThumbColors(aState
, aColors
);
986 PaintStrokedCircle(aPaintData
, thumbRect
, thumbColor
, thumbBorderColor
,
987 thumbBorderWidth
, aDpiRatio
);
989 if (aState
.HasState(ElementState::FOCUSRING
)) {
990 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
, radius
, 1.0f
);
994 template <typename PaintBackendData
>
995 void Theme::PaintProgress(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
996 const LayoutDeviceRect
& aRect
,
997 const ElementState
& aState
, const Colors
& aColors
,
998 DPIRatio aDpiRatio
, bool aIsMeter
) {
999 const CSSCoord borderWidth
= 1.0f
;
1000 const CSSCoord radius
= aIsMeter
? 6.0f
: 3.0f
;
1002 LayoutDeviceRect
rect(aRect
);
1003 const LayoutDeviceCoord thickness
=
1004 (aIsMeter
? kMeterHeight
: kProgressbarHeight
) * aDpiRatio
;
1006 const bool isHorizontal
= !nsNativeTheme::IsVerticalProgress(aFrame
);
1008 // Center it vertically.
1009 rect
.y
+= (rect
.height
- thickness
) / 2;
1010 rect
.height
= thickness
;
1012 // Center it horizontally.
1013 rect
.x
+= (rect
.width
- thickness
) / 2;
1014 rect
.width
= thickness
;
1018 // Paint the track, unclipped.
1019 auto [backgroundColor
, borderColor
] = ComputeProgressTrackColors(aColors
);
1020 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, rect
,
1021 backgroundColor
, borderColor
,
1022 borderWidth
, radius
, aDpiRatio
);
1025 // Now paint the chunk, clipped as needed.
1026 LayoutDeviceRect clipRect
= rect
;
1027 if (aState
.HasState(ElementState::INDETERMINATE
)) {
1028 // For indeterminate progress, we paint an animated chunk of 1/3 of the
1031 // Animation speed and math borrowed from GTK.
1032 const LayoutDeviceCoord size
= isHorizontal
? rect
.width
: rect
.height
;
1033 const LayoutDeviceCoord barSize
= size
* 0.3333f
;
1034 const LayoutDeviceCoord travel
= 2.0f
* (size
- barSize
);
1036 // Period equals to travel / pixelsPerMillisecond where pixelsPerMillisecond
1037 // equals progressSize / 1000.0. This is equivalent to 1600.
1038 const unsigned kPeriod
= 1600;
1040 const int t
= PR_IntervalToMilliseconds(PR_IntervalNow()) % kPeriod
;
1041 const LayoutDeviceCoord dx
= travel
* float(t
) / float(kPeriod
);
1043 rect
.width
= barSize
;
1044 rect
.x
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
1046 rect
.height
= barSize
;
1047 rect
.y
+= (dx
< travel
* .5f
) ? dx
: travel
- dx
;
1050 // Queue the next frame if needed.
1051 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 60)) {
1052 NS_WARNING("Couldn't refresh indeterminate <progress>");
1055 // This is the progress chunk, clip it to the right amount.
1056 double position
= [&] {
1058 auto* meter
= dom::HTMLMeterElement::FromNode(aFrame
->GetContent());
1062 return meter
->Position();
1064 auto* progress
= dom::HTMLProgressElement::FromNode(aFrame
->GetContent());
1068 return progress
->Position();
1071 double clipWidth
= rect
.width
* position
;
1072 clipRect
.width
= clipWidth
;
1073 if (IsFrameRTL(aFrame
)) {
1074 clipRect
.x
+= rect
.width
- clipWidth
;
1077 double clipHeight
= rect
.height
* position
;
1078 clipRect
.height
= clipHeight
;
1079 clipRect
.y
+= rect
.height
- clipHeight
;
1083 auto [backgroundColor
, borderColor
] =
1084 aIsMeter
? ComputeMeterchunkColors(aState
, aColors
)
1085 : ComputeProgressColors(aColors
);
1086 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, rect
, clipRect
,
1087 backgroundColor
, borderColor
,
1088 borderWidth
, radius
, aDpiRatio
);
1091 template <typename PaintBackendData
>
1092 void Theme::PaintButton(nsIFrame
* aFrame
, PaintBackendData
& aPaintData
,
1093 const LayoutDeviceRect
& aRect
,
1094 StyleAppearance aAppearance
, const ElementState
& aState
,
1095 const Colors
& aColors
, DPIRatio aDpiRatio
) {
1096 const CSSCoord radius
= 4.0f
;
1097 auto [backgroundColor
, borderColor
] =
1098 ComputeButtonColors(aState
, aColors
, aFrame
);
1100 if (aAppearance
== StyleAppearance::Toolbarbutton
&&
1101 (!aState
.HasState(ElementState::HOVER
) ||
1102 aState
.HasState(ElementState::DISABLED
))) {
1103 borderColor
= sTransparent
;
1106 ThemeDrawing::PaintRoundedRectWithRadius(aPaintData
, aRect
, backgroundColor
,
1107 borderColor
, kButtonBorderWidth
,
1110 if (aState
.HasState(ElementState::FOCUSRING
)) {
1111 PaintRoundedFocusRect(aPaintData
, aRect
, aColors
, aDpiRatio
,
1112 radius
+ kButtonBorderWidth
, -kButtonBorderWidth
);
1117 Theme::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
1118 StyleAppearance aAppearance
, const nsRect
& aRect
,
1119 const nsRect
& /* aDirtyRect */,
1120 DrawOverflow aDrawOverflow
) {
1121 if (!DoDrawWidgetBackground(*aContext
->GetDrawTarget(), aFrame
, aAppearance
,
1122 aRect
, aDrawOverflow
)) {
1123 return NS_ERROR_NOT_IMPLEMENTED
;
1128 bool Theme::CreateWebRenderCommandsForWidget(
1129 mozilla::wr::DisplayListBuilder
& aBuilder
,
1130 mozilla::wr::IpcResourceUpdateQueue
& aResources
,
1131 const mozilla::layers::StackingContextHelper
& aSc
,
1132 mozilla::layers::RenderRootStateManager
* aManager
, nsIFrame
* aFrame
,
1133 StyleAppearance aAppearance
, const nsRect
& aRect
) {
1134 WebRenderBackendData data
{aBuilder
, aResources
, aSc
, aManager
};
1135 return DoDrawWidgetBackground(data
, aFrame
, aAppearance
, aRect
,
1139 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1140 nscoord aTwipsPerPixel
, DrawTarget
& aDt
) {
1141 return LayoutDeviceRect::FromUnknownRect(
1142 NSRectToSnappedRect(aRect
, aTwipsPerPixel
, aDt
));
1145 static LayoutDeviceRect
ToSnappedRect(const nsRect
& aRect
,
1146 nscoord aTwipsPerPixel
,
1147 WebRenderBackendData
& aDt
) {
1148 // TODO: Do we need to do any more snapping here?
1149 return LayoutDeviceRect::FromAppUnits(aRect
, aTwipsPerPixel
);
1152 static ScrollbarDrawing::ScrollbarKind
ComputeScrollbarKind(
1153 nsIFrame
* aFrame
, bool aIsHorizontal
) {
1154 if (aIsHorizontal
) {
1155 return ScrollbarDrawing::ScrollbarKind::Horizontal
;
1157 nsScrollbarFrame
* scrollbar
=
1158 ScrollbarDrawing::GetParentScrollbarFrame(aFrame
);
1159 if (NS_WARN_IF(!scrollbar
)) {
1160 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1162 nsIScrollbarMediator
* sm
= scrollbar
->GetScrollbarMediator();
1163 if (NS_WARN_IF(!sm
)) {
1164 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1166 return sm
->IsScrollbarOnRight()
1167 ? ScrollbarDrawing::ScrollbarKind::VerticalRight
1168 : ScrollbarDrawing::ScrollbarKind::VerticalLeft
;
1171 static ScrollbarDrawing::ScrollbarKind
ComputeScrollbarKindForScrollCorner(
1173 ScrollContainerFrame
* sf
= do_QueryFrame(aFrame
->GetParent());
1175 return ScrollbarDrawing::ScrollbarKind::VerticalRight
;
1177 return sf
->IsScrollbarOnRight()
1178 ? ScrollbarDrawing::ScrollbarKind::VerticalRight
1179 : ScrollbarDrawing::ScrollbarKind::VerticalLeft
;
1182 template <typename PaintBackendData
>
1183 bool Theme::DoDrawWidgetBackground(PaintBackendData
& aPaintData
,
1185 StyleAppearance aAppearance
,
1186 const nsRect
& aRect
,
1187 DrawOverflow aDrawOverflow
) {
1188 static_assert(std::is_same_v
<PaintBackendData
, DrawTarget
> ||
1189 std::is_same_v
<PaintBackendData
, WebRenderBackendData
>);
1191 const nsPresContext
* pc
= aFrame
->PresContext();
1192 const nscoord twipsPerPixel
= pc
->AppUnitsPerDevPixel();
1193 const auto devPxRect
= ToSnappedRect(aRect
, twipsPerPixel
, aPaintData
);
1195 const DocumentState docState
= pc
->Document()->State();
1196 ElementState elementState
= GetContentState(aFrame
, aAppearance
);
1197 // Paint the outline iff we're asked to draw overflow and we have
1198 // outline-style: auto.
1199 if (aDrawOverflow
== DrawOverflow::Yes
&&
1200 aFrame
->StyleOutline()->mOutlineStyle
.IsAuto()) {
1201 elementState
|= ElementState::FOCUSRING
;
1203 elementState
&= ~ElementState::FOCUSRING
;
1206 // Hack to avoid skia fuzziness: Add a dummy clip if the widget doesn't
1207 // overflow devPxRect.
1208 Maybe
<AutoClipRect
> maybeClipRect
;
1209 if constexpr (std::is_same_v
<PaintBackendData
, DrawTarget
>) {
1210 if (aAppearance
!= StyleAppearance::FocusOutline
&&
1211 aAppearance
!= StyleAppearance::Range
&&
1212 !elementState
.HasState(ElementState::FOCUSRING
)) {
1213 maybeClipRect
.emplace(aPaintData
, devPxRect
);
1217 const Colors
colors(aFrame
, aAppearance
);
1218 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1220 switch (aAppearance
) {
1221 case StyleAppearance::Radio
: {
1222 auto rect
= CheckBoxRadioRect(devPxRect
);
1223 PaintRadioControl(aPaintData
, rect
, elementState
, colors
, dpiRatio
);
1226 case StyleAppearance::Checkbox
: {
1227 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1228 // TODO: Need to figure out how to best draw this using WR.
1231 auto rect
= CheckBoxRadioRect(devPxRect
);
1232 PaintCheckboxControl(aPaintData
, rect
, elementState
, colors
, dpiRatio
);
1236 case StyleAppearance::Textarea
:
1237 case StyleAppearance::Textfield
:
1238 case StyleAppearance::NumberInput
:
1239 case StyleAppearance::PasswordInput
:
1240 PaintTextField(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1242 case StyleAppearance::Listbox
:
1243 PaintListbox(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1245 case StyleAppearance::MenulistButton
:
1246 case StyleAppearance::Menulist
:
1247 PaintMenulist(aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
);
1249 case StyleAppearance::MozMenulistArrowButton
:
1250 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1251 // TODO: Need to figure out how to best draw this using WR.
1254 PaintMenulistArrow(aFrame
, aPaintData
, devPxRect
);
1257 case StyleAppearance::Tooltip
: {
1258 const CSSCoord
strokeWidth(1.0f
);
1259 const CSSCoord
strokeRadius(2.0f
);
1260 ThemeDrawing::PaintRoundedRectWithRadius(
1261 aPaintData
, devPxRect
,
1262 colors
.System(StyleSystemColor::Infobackground
),
1263 colors
.System(StyleSystemColor::Infotext
), strokeWidth
, strokeRadius
,
1267 case StyleAppearance::SpinnerUpbutton
:
1268 case StyleAppearance::SpinnerDownbutton
:
1269 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1270 // TODO: Need to figure out how to best draw this using WR.
1273 PaintSpinnerButton(aFrame
, aPaintData
, devPxRect
, elementState
,
1274 aAppearance
, colors
, dpiRatio
);
1277 case StyleAppearance::Range
:
1278 PaintRange(aFrame
, aPaintData
, devPxRect
, elementState
, colors
, dpiRatio
,
1279 IsRangeHorizontal(aFrame
));
1281 case StyleAppearance::RangeThumb
:
1282 // Painted as part of StyleAppearance::Range.
1284 case StyleAppearance::ProgressBar
:
1285 PaintProgress(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1287 /* aIsMeter = */ false);
1289 case StyleAppearance::Progresschunk
:
1290 /* Painted as part of the progress bar */
1292 case StyleAppearance::Meter
:
1293 PaintProgress(aFrame
, aPaintData
, devPxRect
, elementState
, colors
,
1295 /* aIsMeter = */ true);
1297 case StyleAppearance::Meterchunk
:
1298 /* Painted as part of the meter bar */
1300 case StyleAppearance::ScrollbarthumbHorizontal
:
1301 case StyleAppearance::ScrollbarthumbVertical
: {
1303 aAppearance
== StyleAppearance::ScrollbarthumbHorizontal
;
1304 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1305 return GetScrollbarDrawing().PaintScrollbarThumb(
1306 aPaintData
, devPxRect
, kind
, aFrame
,
1307 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1310 case StyleAppearance::ScrollbarHorizontal
:
1311 case StyleAppearance::ScrollbarVertical
: {
1312 bool isHorizontal
= aAppearance
== StyleAppearance::ScrollbarHorizontal
;
1313 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1314 return GetScrollbarDrawing().PaintScrollbar(
1315 aPaintData
, devPxRect
, kind
, aFrame
,
1316 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1319 case StyleAppearance::Scrollcorner
: {
1320 auto kind
= ComputeScrollbarKindForScrollCorner(aFrame
);
1321 return GetScrollbarDrawing().PaintScrollCorner(
1322 aPaintData
, devPxRect
, kind
, aFrame
,
1323 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
, colors
,
1326 case StyleAppearance::ScrollbarbuttonUp
:
1327 case StyleAppearance::ScrollbarbuttonDown
:
1328 case StyleAppearance::ScrollbarbuttonLeft
:
1329 case StyleAppearance::ScrollbarbuttonRight
: {
1330 // For scrollbar-width:thin, we don't display the buttons.
1331 if (!ScrollbarDrawing::IsScrollbarWidthThin(aFrame
)) {
1332 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1333 // TODO: Need to figure out how to best draw this using WR.
1337 aAppearance
== StyleAppearance::ScrollbarbuttonLeft
||
1338 aAppearance
== StyleAppearance::ScrollbarbuttonRight
;
1339 auto kind
= ComputeScrollbarKind(aFrame
, isHorizontal
);
1340 GetScrollbarDrawing().PaintScrollbarButton(
1341 aPaintData
, aAppearance
, devPxRect
, kind
, aFrame
,
1342 *nsLayoutUtils::StyleForScrollbar(aFrame
), elementState
, docState
,
1348 case StyleAppearance::Button
:
1349 case StyleAppearance::Toolbarbutton
:
1350 PaintButton(aFrame
, aPaintData
, devPxRect
, aAppearance
, elementState
,
1353 case StyleAppearance::FocusOutline
:
1354 PaintAutoStyleOutline(aFrame
, aPaintData
, devPxRect
, colors
, dpiRatio
);
1357 // Various appearance values are used for XUL elements. Normally these
1358 // will not be available in content documents (and thus in the content
1359 // processes where the native basic theme can be used), but tests are
1360 // run with the remote XUL pref enabled and so we can get in here. So
1361 // we just return an error rather than assert.
1368 template <typename PaintBackendData
>
1369 void Theme::PaintAutoStyleOutline(nsIFrame
* aFrame
,
1370 PaintBackendData
& aPaintData
,
1371 const LayoutDeviceRect
& aRect
,
1372 const Colors
& aColors
, DPIRatio aDpiRatio
) {
1373 const auto& accentColor
= aColors
.Accent();
1374 LayoutDeviceCoord
strokeWidth(ThemeDrawing::SnapBorderWidth(2.0f
, aDpiRatio
));
1376 LayoutDeviceRect
rect(aRect
);
1377 rect
.Inflate(strokeWidth
);
1379 const nscoord a2d
= aFrame
->PresContext()->AppUnitsPerDevPixel();
1380 nscoord cssOffset
= aFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits();
1381 nscoord cssRadii
[8] = {0};
1382 if (!aFrame
->GetBorderRadii(cssRadii
)) {
1383 const auto twoPixels
= 2 * AppUnitsPerCSSPixel();
1384 const nscoord radius
=
1385 cssOffset
>= 0 ? twoPixels
: std::max(twoPixels
+ cssOffset
, 0);
1386 cssOffset
= -twoPixels
;
1387 for (auto& r
: cssRadii
) {
1392 auto offset
= LayoutDevicePixel::FromAppUnits(cssOffset
, a2d
);
1393 RectCornerRadii innerRadii
;
1394 nsCSSRendering::ComputePixelRadii(cssRadii
, a2d
, &innerRadii
);
1396 // NOTE(emilio): This doesn't use PaintRoundedRectWithRadius because we need
1397 // to support arbitrary radii.
1398 auto DrawRect
= [&](const sRGBColor
& aColor
) {
1399 RectCornerRadii outerRadii
;
1400 if constexpr (std::is_same_v
<PaintBackendData
, WebRenderBackendData
>) {
1401 const Float widths
[4] = {strokeWidth
+ offset
, strokeWidth
+ offset
,
1402 strokeWidth
+ offset
, strokeWidth
+ offset
};
1403 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1404 const auto dest
= wr::ToLayoutRect(rect
);
1406 wr::ToBorderSide(ToDeviceColor(aColor
), StyleBorderStyle::Solid
);
1407 const wr::BorderSide sides
[4] = {side
, side
, side
, side
};
1408 const bool kBackfaceIsVisible
= true;
1409 const auto wrWidths
= wr::ToBorderWidths(strokeWidth
, strokeWidth
,
1410 strokeWidth
, strokeWidth
);
1411 const auto wrRadius
= wr::ToBorderRadius(outerRadii
);
1412 aPaintData
.mBuilder
.PushBorder(dest
, dest
, kBackfaceIsVisible
, wrWidths
,
1413 {sides
, 4}, wrRadius
);
1415 const LayoutDeviceCoord halfWidth
= strokeWidth
* 0.5f
;
1416 const Float widths
[4] = {halfWidth
+ offset
, halfWidth
+ offset
,
1417 halfWidth
+ offset
, halfWidth
+ offset
};
1418 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1419 LayoutDeviceRect
dest(rect
);
1420 dest
.Deflate(halfWidth
);
1422 MakePathForRoundedRect(aPaintData
, dest
.ToUnknownRect(), outerRadii
);
1423 aPaintData
.Stroke(path
, ColorPattern(ToDeviceColor(aColor
)),
1424 StrokeOptions(strokeWidth
));
1428 auto primaryColor
= aColors
.HighContrast()
1429 ? aColors
.System(StyleSystemColor::Selecteditem
)
1430 : accentColor
.Get();
1431 DrawRect(primaryColor
);
1433 offset
+= strokeWidth
;
1436 LayoutDeviceCoord(ThemeDrawing::SnapBorderWidth(1.0f
, aDpiRatio
));
1437 rect
.Inflate(strokeWidth
);
1439 auto secondaryColor
= aColors
.HighContrast()
1440 ? aColors
.System(StyleSystemColor::Canvastext
)
1441 : accentColor
.GetForeground();
1442 DrawRect(secondaryColor
);
1445 LayoutDeviceIntMargin
Theme::GetWidgetBorder(nsDeviceContext
* aContext
,
1447 StyleAppearance aAppearance
) {
1448 switch (aAppearance
) {
1449 case StyleAppearance::Textfield
:
1450 case StyleAppearance::Textarea
:
1451 case StyleAppearance::NumberInput
:
1452 case StyleAppearance::PasswordInput
:
1453 case StyleAppearance::Listbox
:
1454 case StyleAppearance::Menulist
:
1455 case StyleAppearance::MenulistButton
:
1456 case StyleAppearance::Button
:
1457 case StyleAppearance::Toolbarbutton
:
1458 // Return the border size from the UA sheet, even though what we paint
1459 // doesn't actually match that. We know this is the UA sheet border
1460 // because we disable native theming when different border widths are
1461 // specified by authors, see Theme::IsWidgetStyled.
1463 // The Rounded() bit is technically redundant, but needed to appease the
1464 // type system, we should always end up with full device pixels due to
1465 // round_border_to_device_pixels at style time.
1466 return LayoutDeviceIntMargin::FromAppUnits(
1467 aFrame
->StyleBorder()->GetComputedBorder(),
1468 aFrame
->PresContext()->AppUnitsPerDevPixel())
1471 return LayoutDeviceIntMargin();
1475 bool Theme::GetWidgetPadding(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1476 StyleAppearance aAppearance
,
1477 LayoutDeviceIntMargin
* aResult
) {
1478 switch (aAppearance
) {
1479 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1480 // and have a meaningful baseline, so they can't have
1481 // author-specified padding.
1482 case StyleAppearance::Radio
:
1483 case StyleAppearance::Checkbox
:
1484 aResult
->SizeTo(0, 0, 0, 0);
1492 bool Theme::GetWidgetOverflow(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
1493 StyleAppearance aAppearance
,
1494 nsRect
* aOverflowRect
) {
1495 CSSIntMargin overflow
;
1496 switch (aAppearance
) {
1497 case StyleAppearance::FocusOutline
: {
1498 const auto width
= 3;
1499 overflow
.SizeTo(width
, width
, width
, width
);
1502 case StyleAppearance::Radio
:
1503 case StyleAppearance::Checkbox
:
1504 case StyleAppearance::Range
:
1505 // 2px for each outline segment, plus 1px separation, plus we paint with a
1506 // 1px extra offset, so 6px.
1507 overflow
.SizeTo(6, 6, 6, 6);
1509 case StyleAppearance::Textarea
:
1510 case StyleAppearance::Textfield
:
1511 case StyleAppearance::NumberInput
:
1512 case StyleAppearance::PasswordInput
:
1513 case StyleAppearance::Listbox
:
1514 case StyleAppearance::MenulistButton
:
1515 case StyleAppearance::Menulist
:
1516 case StyleAppearance::Button
:
1517 case StyleAppearance::Toolbarbutton
:
1518 // 2px for each segment, plus 1px separation, but we paint 1px inside
1519 // the border area so 4px overflow.
1520 overflow
.SizeTo(4, 4, 4, 4);
1526 // TODO: This should convert from device pixels to app units, not from CSS
1527 // pixels. And it should take the dpi ratio into account.
1528 // Using CSS pixels can cause the overflow to be too small if the page is
1530 aOverflowRect
->Inflate(CSSPixel::ToAppUnits(overflow
));
1534 LayoutDeviceIntCoord
Theme::GetScrollbarSize(const nsPresContext
* aPresContext
,
1535 StyleScrollbarWidth aWidth
,
1537 return GetScrollbarDrawing().GetScrollbarSize(aPresContext
, aWidth
, aOverlay
);
1540 CSSCoord
Theme::GetCheckboxRadioPrefSize() { return kCheckboxRadioSize
; }
1543 UniquePtr
<ScrollbarDrawing
> Theme::ScrollbarStyle() {
1544 switch (StaticPrefs::widget_non_native_theme_scrollbar_style()) {
1546 return MakeUnique
<ScrollbarDrawingCocoa
>();
1548 return MakeUnique
<ScrollbarDrawingGTK
>();
1550 return MakeUnique
<ScrollbarDrawingAndroid
>();
1552 return MakeUnique
<ScrollbarDrawingWin
>();
1554 return MakeUnique
<ScrollbarDrawingWin11
>();
1558 // Default to native scrollbar style for each platform.
1560 if (IsWin11OrLater()) {
1561 return MakeUnique
<ScrollbarDrawingWin11
>();
1563 return MakeUnique
<ScrollbarDrawingWin
>();
1564 #elif defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_UIKIT)
1565 return MakeUnique
<ScrollbarDrawingCocoa
>();
1566 #elif MOZ_WIDGET_GTK
1567 return MakeUnique
<ScrollbarDrawingGTK
>();
1569 return MakeUnique
<ScrollbarDrawingAndroid
>();
1571 # error "Unknown platform, need scrollbar implementation."
1575 LayoutDeviceIntSize
Theme::GetMinimumWidgetSize(nsPresContext
* aPresContext
,
1577 StyleAppearance aAppearance
) {
1578 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1580 if (IsWidgetScrollbarPart(aAppearance
)) {
1581 return GetScrollbarDrawing().GetMinimumWidgetSize(aPresContext
, aAppearance
,
1585 LayoutDeviceIntSize result
;
1586 switch (aAppearance
) {
1587 case StyleAppearance::RangeThumb
:
1588 result
.SizeTo((kMinimumRangeThumbSize
* dpiRatio
).Rounded(),
1589 (kMinimumRangeThumbSize
* dpiRatio
).Rounded());
1591 case StyleAppearance::MozMenulistArrowButton
:
1592 result
.width
= (kMinimumDropdownArrowButtonWidth
* dpiRatio
).Rounded();
1594 case StyleAppearance::SpinnerUpbutton
:
1595 case StyleAppearance::SpinnerDownbutton
:
1596 result
.width
= (kMinimumSpinnerButtonWidth
* dpiRatio
).Rounded();
1597 result
.height
= (kMinimumSpinnerButtonHeight
* dpiRatio
).Rounded();
1605 nsITheme::Transparency
Theme::GetWidgetTransparency(
1606 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1607 if (auto scrollbar
= GetScrollbarDrawing().GetScrollbarPartTransparency(
1608 aFrame
, aAppearance
)) {
1611 if (aAppearance
== StyleAppearance::Tooltip
) {
1612 // We draw a rounded rect, so we need transparency.
1613 return eTransparent
;
1615 return eUnknownTransparency
;
1618 bool Theme::WidgetAttributeChangeRequiresRepaint(StyleAppearance aAppearance
,
1619 nsAtom
* aAttribute
) {
1620 // Check the attribute to see if it's relevant.
1621 // TODO(emilio): The non-native theme doesn't use these attributes. Other
1622 // themes do, but not all of them (and not all of the ones they check are
1624 return aAttribute
== nsGkAtoms::disabled
||
1625 aAttribute
== nsGkAtoms::checked
||
1626 aAttribute
== nsGkAtoms::selected
||
1627 aAttribute
== nsGkAtoms::visuallyselected
||
1628 aAttribute
== nsGkAtoms::menuactive
||
1629 aAttribute
== nsGkAtoms::sortDirection
||
1630 aAttribute
== nsGkAtoms::focused
||
1631 aAttribute
== nsGkAtoms::_default
|| aAttribute
== nsGkAtoms::open
;
1635 Theme::ThemeChanged() { return NS_OK
; }
1637 bool Theme::WidgetAppearanceDependsOnWindowFocus(StyleAppearance aAppearance
) {
1638 return IsWidgetScrollbarPart(aAppearance
);
1641 nsITheme::ThemeGeometryType
Theme::ThemeGeometryTypeForWidget(
1642 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1643 return eThemeGeometryTypeUnknown
;
1646 bool Theme::ThemeSupportsWidget(nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
1647 StyleAppearance aAppearance
) {
1648 switch (aAppearance
) {
1649 case StyleAppearance::Radio
:
1650 case StyleAppearance::Checkbox
:
1651 case StyleAppearance::FocusOutline
:
1652 case StyleAppearance::Textarea
:
1653 case StyleAppearance::Textfield
:
1654 case StyleAppearance::Range
:
1655 case StyleAppearance::RangeThumb
:
1656 case StyleAppearance::ProgressBar
:
1657 case StyleAppearance::Progresschunk
:
1658 case StyleAppearance::Meter
:
1659 case StyleAppearance::Meterchunk
:
1660 case StyleAppearance::ScrollbarbuttonUp
:
1661 case StyleAppearance::ScrollbarbuttonDown
:
1662 case StyleAppearance::ScrollbarbuttonLeft
:
1663 case StyleAppearance::ScrollbarbuttonRight
:
1664 case StyleAppearance::ScrollbarthumbHorizontal
:
1665 case StyleAppearance::ScrollbarthumbVertical
:
1666 case StyleAppearance::ScrollbarHorizontal
:
1667 case StyleAppearance::ScrollbarVertical
:
1668 case StyleAppearance::Scrollcorner
:
1669 case StyleAppearance::Button
:
1670 case StyleAppearance::Toolbarbutton
:
1671 case StyleAppearance::Listbox
:
1672 case StyleAppearance::Menulist
:
1673 case StyleAppearance::MenulistButton
:
1674 case StyleAppearance::NumberInput
:
1675 case StyleAppearance::PasswordInput
:
1676 case StyleAppearance::MozMenulistArrowButton
:
1677 case StyleAppearance::SpinnerUpbutton
:
1678 case StyleAppearance::SpinnerDownbutton
:
1679 case StyleAppearance::Tooltip
:
1680 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
1686 bool Theme::WidgetIsContainer(StyleAppearance aAppearance
) {
1687 switch (aAppearance
) {
1688 case StyleAppearance::MozMenulistArrowButton
:
1689 case StyleAppearance::Radio
:
1690 case StyleAppearance::Checkbox
:
1697 bool Theme::ThemeDrawsFocusForWidget(nsIFrame
*, StyleAppearance
) {
1701 bool Theme::ThemeNeedsComboboxDropmarker() { return true; }
1703 bool Theme::ThemeSupportsScrollbarButtons() {
1704 return GetScrollbarDrawing().ShouldDrawScrollbarButtons();
1707 } // namespace mozilla::widget