no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / widget / Theme.cpp
blob564c16e5870df830ff3272101812fa58c6a52715
1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "Theme.h"
7 #include <utility>
8 #include "ThemeCocoa.h"
10 #include "ThemeDrawing.h"
11 #include "Units.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"
38 #ifdef XP_WIN
39 # include "mozilla/WindowsVersion.h"
40 #endif
42 using namespace mozilla;
43 using namespace mozilla::dom;
44 using namespace mozilla::gfx;
45 using namespace mozilla::widget;
47 namespace {
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
100 // the clips.
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(); }
110 private:
111 DrawTarget& mDt;
114 static StaticRefPtr<Theme> gNativeInstance;
115 static StaticRefPtr<Theme> gNonNativeInstance;
116 static StaticRefPtr<Theme> gRDMInstance;
118 } // namespace
120 #ifdef ANDROID
121 already_AddRefed<Theme> do_CreateNativeThemeDoNotUseDirectly() {
122 // Android doesn't have a native theme.
123 return do_AddRef(new Theme(Theme::ScrollbarStyle()));
125 #endif
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));
132 #else
133 gNonNativeInstance = new Theme(std::move(scrollbarDrawing));
134 #endif
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,
167 void Theme::Init() {
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);
180 /* static */
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) {
223 if (isDisabled) {
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);
231 bool isActive =
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);
255 auto [bg, border] =
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);
265 bool isActive =
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);
275 if (isDisabled) {
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.
285 return sTransparent;
287 bool dark = aColors.IsDark();
288 if (isActive) {
289 return dark ? sColorGrey20 : sColorGrey60;
291 if (isHovered) {
292 return dark ? sColorGrey30 : sColorGrey50;
294 return sColorGrey40;
297 std::pair<sRGBColor, sRGBColor> Theme::ComputeButtonColors(
298 const ElementState& aState, const Colors& aColors, nsIFrame* aFrame) {
299 bool isActive =
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);
308 if (isDisabled) {
309 return aColors.SystemNs(StyleSystemColor::MozButtondisabledface);
311 if (isActive) {
312 return aColors.SystemNs(StyleSystemColor::MozButtonactiveface);
314 if (isHovered) {
315 return aColors.SystemNs(StyleSystemColor::MozButtonhoverface);
317 return aColors.SystemNs(StyleSystemColor::Buttonface);
318 }();
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);
336 }();
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) {
345 bool isActive =
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()) {
351 if (isDisabled) {
352 return aColors.SystemPair(StyleSystemColor::Graytext,
353 StyleSystemColor::Graytext);
355 if (isActive) {
356 return aColors.SystemPair(StyleSystemColor::Selecteditem,
357 StyleSystemColor::Buttontext);
359 if (isHovered) {
360 return aColors.SystemPair(StyleSystemColor::Selecteditem,
361 StyleSystemColor::Selecteditem);
363 return aColors.SystemPair(StyleSystemColor::Buttontext,
364 StyleSystemColor::Buttontext);
367 if (isDisabled) {
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) {
379 bool isActive =
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()) {
385 if (isDisabled) {
386 return aColors.SystemPair(StyleSystemColor::Buttonface,
387 StyleSystemColor::Graytext);
389 if (isActive) {
390 return aColors.SystemPair(StyleSystemColor::Buttonface,
391 StyleSystemColor::Buttontext);
393 if (isHovered) {
394 return aColors.SystemPair(StyleSystemColor::Selecteditemtext,
395 StyleSystemColor::Selecteditem);
397 return aColors.SystemPair(StyleSystemColor::Buttonface,
398 StyleSystemColor::Buttontext);
401 if (isDisabled) {
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) {
412 bool isActive =
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()) {
418 if (isDisabled) {
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 = [&] {
431 if (isDisabled) {
432 return sColorGrey40;
434 if (isActive) {
435 return aColors.Accent().Get();
437 if (isHovered) {
438 return sColorGrey60;
440 return sColorGrey50;
441 }();
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
510 // red">Foobar</div>
512 // But some controls might provide a negative offset to cover the border, if
513 // necessary.
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);
525 strokeWidthDevPx =
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);
535 strokeWidthDevPx =
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) {
555 borderWidth = 0.0f;
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);
588 builder->MoveTo(p);
589 for (int32_t i = 1; i < checkNumPoints; i++) {
590 p = center + Point(checkPolygonX[i] * scale, checkPolygonY[i] * scale);
591 builder->LineTo(p);
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,
622 aDpiRatio);
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);
659 if (!blurFilter) {
660 return;
663 blurFilter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdDev);
665 IntSize inflation =
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);
677 if (!ellipseDT) {
678 return;
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) {
705 borderWidth = 0.0f;
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));
715 rect.Deflate(width);
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,
738 radius, aDpiRatio);
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,
758 radius, aDpiRatio);
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,
776 radius, aDpiRatio);
778 if (aState.HasState(ElementState::FOCUSRING)) {
779 PaintRoundedFocusRect(aDrawTarget, aRect, aColors, aDpiRatio,
780 radius + kMenulistBorderWidth, -kMenulistBorderWidth);
784 enum class PhysicalArrowDirection {
785 Right,
786 Left,
787 Bottom,
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;
811 }();
813 auto const [xs, ys] = [&] {
814 using Pair = std::pair<const float*, const float*>;
815 switch (direction) {
816 case PhysicalArrowDirection::Left:
817 // rotate 90°: [[0,1],[-1,0]]
818 for (float& f : polygonY) {
819 f = -f;
821 return Pair(polygonY, polygonX);
823 case PhysicalArrowDirection::Right:
824 // rotate 270°: [[0,-1],[1,0]]
825 for (float& f : polygonX) {
826 f = -f;
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);
836 }();
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) {
862 coord = -coord;
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);
876 if (!rangeFrame) {
877 return;
880 auto tickMarks = rangeFrame->TickMarks();
881 double progress = rangeFrame->GetValueAsFractionOfRange();
882 auto rect = aRect;
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;
894 if (aHorizontal) {
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)) {
901 tickMarkOrigin =
902 LayoutDevicePoint(aRect.XMost() - thumbRect.width / 2, aRect.YMost());
903 tickMarkDirection = LayoutDevicePoint(-1.0f, 0.0f);
904 thumbRect.x =
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());
909 } else {
910 tickMarkOrigin =
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());
918 } else {
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()) {
928 thumbRect.y =
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());
933 } else {
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);
963 // Thumb shadow
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;
970 tickMarkOrigin -=
971 LayoutDevicePoint(tickMarkSize.width, tickMarkSize.height) / 2;
972 auto tickMarkRect = LayoutDeviceRect(tickMarkOrigin, tickMarkSize);
973 for (auto tickMark : tickMarks) {
974 auto tickMarkOffset =
975 tickMarkDirection *
976 float(rangeFrame->GetDoubleAsFractionOfRange(tickMark));
977 ThemeDrawing::FillRect(aPaintData, tickMarkRect + tickMarkOffset,
978 tickMarkColor);
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);
1007 if (isHorizontal) {
1008 // Center it vertically.
1009 rect.y += (rect.height - thickness) / 2;
1010 rect.height = thickness;
1011 } else {
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
1029 // progress size.
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);
1042 if (isHorizontal) {
1043 rect.width = barSize;
1044 rect.x += (dx < travel * .5f) ? dx : travel - dx;
1045 } else {
1046 rect.height = barSize;
1047 rect.y += (dx < travel * .5f) ? dx : travel - dx;
1049 clipRect = rect;
1050 // Queue the next frame if needed.
1051 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
1052 NS_WARNING("Couldn't refresh indeterminate <progress>");
1054 } else {
1055 // This is the progress chunk, clip it to the right amount.
1056 double position = [&] {
1057 if (aIsMeter) {
1058 auto* meter = dom::HTMLMeterElement::FromNode(aFrame->GetContent());
1059 if (!meter) {
1060 return 0.0;
1062 return meter->Position();
1064 auto* progress = dom::HTMLProgressElement::FromNode(aFrame->GetContent());
1065 if (!progress) {
1066 return 0.0;
1068 return progress->Position();
1069 }();
1070 if (isHorizontal) {
1071 double clipWidth = rect.width * position;
1072 clipRect.width = clipWidth;
1073 if (IsFrameRTL(aFrame)) {
1074 clipRect.x += rect.width - clipWidth;
1076 } else {
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,
1108 radius, aDpiRatio);
1110 if (aState.HasState(ElementState::FOCUSRING)) {
1111 PaintRoundedFocusRect(aPaintData, aRect, aColors, aDpiRatio,
1112 radius + kButtonBorderWidth, -kButtonBorderWidth);
1116 NS_IMETHODIMP
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;
1125 return NS_OK;
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,
1136 DrawOverflow::Yes);
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(
1172 nsIFrame* aFrame) {
1173 ScrollContainerFrame* sf = do_QueryFrame(aFrame->GetParent());
1174 if (!sf) {
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,
1184 nsIFrame* aFrame,
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;
1202 } else {
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);
1224 break;
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.
1229 return false;
1230 } else {
1231 auto rect = CheckBoxRadioRect(devPxRect);
1232 PaintCheckboxControl(aPaintData, rect, elementState, colors, dpiRatio);
1234 break;
1236 case StyleAppearance::Textarea:
1237 case StyleAppearance::Textfield:
1238 case StyleAppearance::NumberInput:
1239 case StyleAppearance::PasswordInput:
1240 PaintTextField(aPaintData, devPxRect, elementState, colors, dpiRatio);
1241 break;
1242 case StyleAppearance::Listbox:
1243 PaintListbox(aPaintData, devPxRect, elementState, colors, dpiRatio);
1244 break;
1245 case StyleAppearance::MenulistButton:
1246 case StyleAppearance::Menulist:
1247 PaintMenulist(aPaintData, devPxRect, elementState, colors, dpiRatio);
1248 break;
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.
1252 return false;
1253 } else {
1254 PaintMenulistArrow(aFrame, aPaintData, devPxRect);
1256 break;
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,
1264 dpiRatio);
1265 break;
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.
1271 return false;
1272 } else {
1273 PaintSpinnerButton(aFrame, aPaintData, devPxRect, elementState,
1274 aAppearance, colors, dpiRatio);
1276 break;
1277 case StyleAppearance::Range:
1278 PaintRange(aFrame, aPaintData, devPxRect, elementState, colors, dpiRatio,
1279 IsRangeHorizontal(aFrame));
1280 break;
1281 case StyleAppearance::RangeThumb:
1282 // Painted as part of StyleAppearance::Range.
1283 break;
1284 case StyleAppearance::ProgressBar:
1285 PaintProgress(aFrame, aPaintData, devPxRect, elementState, colors,
1286 dpiRatio,
1287 /* aIsMeter = */ false);
1288 break;
1289 case StyleAppearance::Progresschunk:
1290 /* Painted as part of the progress bar */
1291 break;
1292 case StyleAppearance::Meter:
1293 PaintProgress(aFrame, aPaintData, devPxRect, elementState, colors,
1294 dpiRatio,
1295 /* aIsMeter = */ true);
1296 break;
1297 case StyleAppearance::Meterchunk:
1298 /* Painted as part of the meter bar */
1299 break;
1300 case StyleAppearance::ScrollbarthumbHorizontal:
1301 case StyleAppearance::ScrollbarthumbVertical: {
1302 bool isHorizontal =
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,
1308 colors, dpiRatio);
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,
1317 colors, dpiRatio);
1319 case StyleAppearance::Scrollcorner: {
1320 auto kind = ComputeScrollbarKindForScrollCorner(aFrame);
1321 return GetScrollbarDrawing().PaintScrollCorner(
1322 aPaintData, devPxRect, kind, aFrame,
1323 *nsLayoutUtils::StyleForScrollbar(aFrame), docState, colors,
1324 dpiRatio);
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.
1334 return false;
1335 } else {
1336 bool isHorizontal =
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,
1343 colors, dpiRatio);
1346 break;
1348 case StyleAppearance::Button:
1349 case StyleAppearance::Toolbarbutton:
1350 PaintButton(aFrame, aPaintData, devPxRect, aAppearance, elementState,
1351 colors, dpiRatio);
1352 break;
1353 case StyleAppearance::FocusOutline:
1354 PaintAutoStyleOutline(aFrame, aPaintData, devPxRect, colors, dpiRatio);
1355 break;
1356 default:
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.
1362 return false;
1365 return true;
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) {
1388 r = radius;
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);
1405 const auto side =
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);
1414 } else {
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);
1421 RefPtr<Path> path =
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;
1435 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,
1446 nsIFrame* aFrame,
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())
1469 .Rounded();
1470 default:
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);
1485 return true;
1486 default:
1487 break;
1489 return false;
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);
1500 break;
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);
1508 break;
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);
1521 break;
1522 default:
1523 return false;
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
1529 // zoomed out.
1530 aOverflowRect->Inflate(CSSPixel::ToAppUnits(overflow));
1531 return true;
1534 LayoutDeviceIntCoord Theme::GetScrollbarSize(const nsPresContext* aPresContext,
1535 StyleScrollbarWidth aWidth,
1536 Overlay aOverlay) {
1537 return GetScrollbarDrawing().GetScrollbarSize(aPresContext, aWidth, aOverlay);
1540 CSSCoord Theme::GetCheckboxRadioPrefSize() { return kCheckboxRadioSize; }
1542 /* static */
1543 UniquePtr<ScrollbarDrawing> Theme::ScrollbarStyle() {
1544 switch (StaticPrefs::widget_non_native_theme_scrollbar_style()) {
1545 case 1:
1546 return MakeUnique<ScrollbarDrawingCocoa>();
1547 case 2:
1548 return MakeUnique<ScrollbarDrawingGTK>();
1549 case 3:
1550 return MakeUnique<ScrollbarDrawingAndroid>();
1551 case 4:
1552 return MakeUnique<ScrollbarDrawingWin>();
1553 case 5:
1554 return MakeUnique<ScrollbarDrawingWin11>();
1555 default:
1556 break;
1558 // Default to native scrollbar style for each platform.
1559 #ifdef XP_WIN
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>();
1568 #elif ANDROID
1569 return MakeUnique<ScrollbarDrawingAndroid>();
1570 #else
1571 # error "Unknown platform, need scrollbar implementation."
1572 #endif
1575 LayoutDeviceIntSize Theme::GetMinimumWidgetSize(nsPresContext* aPresContext,
1576 nsIFrame* aFrame,
1577 StyleAppearance aAppearance) {
1578 DPIRatio dpiRatio = GetDPIRatio(aFrame, aAppearance);
1580 if (IsWidgetScrollbarPart(aAppearance)) {
1581 return GetScrollbarDrawing().GetMinimumWidgetSize(aPresContext, aAppearance,
1582 aFrame);
1585 LayoutDeviceIntSize result;
1586 switch (aAppearance) {
1587 case StyleAppearance::RangeThumb:
1588 result.SizeTo((kMinimumRangeThumbSize * dpiRatio).Rounded(),
1589 (kMinimumRangeThumbSize * dpiRatio).Rounded());
1590 break;
1591 case StyleAppearance::MozMenulistArrowButton:
1592 result.width = (kMinimumDropdownArrowButtonWidth * dpiRatio).Rounded();
1593 break;
1594 case StyleAppearance::SpinnerUpbutton:
1595 case StyleAppearance::SpinnerDownbutton:
1596 result.width = (kMinimumSpinnerButtonWidth * dpiRatio).Rounded();
1597 result.height = (kMinimumSpinnerButtonHeight * dpiRatio).Rounded();
1598 break;
1599 default:
1600 break;
1602 return result;
1605 nsITheme::Transparency Theme::GetWidgetTransparency(
1606 nsIFrame* aFrame, StyleAppearance aAppearance) {
1607 if (auto scrollbar = GetScrollbarDrawing().GetScrollbarPartTransparency(
1608 aFrame, aAppearance)) {
1609 return *scrollbar;
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
1623 // here).
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;
1634 NS_IMETHODIMP
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);
1681 default:
1682 return false;
1686 bool Theme::WidgetIsContainer(StyleAppearance aAppearance) {
1687 switch (aAppearance) {
1688 case StyleAppearance::MozMenulistArrowButton:
1689 case StyleAppearance::Radio:
1690 case StyleAppearance::Checkbox:
1691 return false;
1692 default:
1693 return true;
1697 bool Theme::ThemeDrawsFocusForWidget(nsIFrame*, StyleAppearance) {
1698 return true;
1701 bool Theme::ThemeNeedsComboboxDropmarker() { return true; }
1703 bool Theme::ThemeSupportsScrollbarButtons() {
1704 return GetScrollbarDrawing().ShouldDrawScrollbarButtons();
1707 } // namespace mozilla::widget