Bug 1700051: part 28) Refactor `WordSplitState<T>::GetDOMWordSeparatorOffset` to...
[gecko.git] / widget / nsNativeBasicTheme.cpp
blob4b515914e4fb133c757bd63d60165f6ecfe26f3b
1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsNativeBasicTheme.h"
8 #include "gfxBlur.h"
9 #include "mozilla/MathAlgorithms.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/gfx/Rect.h"
12 #include "mozilla/gfx/Types.h"
13 #include "mozilla/gfx/Filters.h"
14 #include "mozilla/RelativeLuminanceUtils.h"
15 #include "mozilla/StaticPrefs_widget.h"
16 #include "mozilla/webrender/WebRenderAPI.h"
17 #include "nsCSSColorUtils.h"
18 #include "nsCSSRendering.h"
19 #include "nsLayoutUtils.h"
20 #include "PathHelpers.h"
22 #include "nsDeviceContext.h"
24 #include "nsColorControlFrame.h"
25 #include "nsDateTimeControlFrame.h"
26 #include "nsMeterFrame.h"
27 #include "nsProgressFrame.h"
28 #include "nsRangeFrame.h"
29 #include "mozilla/dom/HTMLMeterElement.h"
30 #include "mozilla/dom/HTMLProgressElement.h"
32 using namespace mozilla;
33 using namespace mozilla::widget;
34 using namespace mozilla::gfx;
36 NS_IMPL_ISUPPORTS_INHERITED(nsNativeBasicTheme, nsNativeTheme, nsITheme)
38 namespace {
40 static constexpr sRGBColor sTransparent = sRGBColor::White(0.0);
42 // This pushes and pops a clip rect to the draw target.
44 // This is done to reduce fuzz in places where we may have antialiasing,
45 // because skia is not clip-invariant: given different clips, it does not
46 // guarantee the same result, even if the painted content doesn't intersect
47 // the clips.
49 // This is a bit sad, overall, but...
50 struct MOZ_RAII AutoClipRect {
51 AutoClipRect(DrawTarget& aDt, const LayoutDeviceRect& aRect) : mDt(aDt) {
52 mDt.PushClipRect(aRect.ToUnknownRect());
55 ~AutoClipRect() { mDt.PopClip(); }
57 private:
58 DrawTarget& mDt;
61 static LayoutDeviceIntCoord SnapBorderWidth(
62 CSSCoord aCssWidth, nsNativeBasicTheme::DPIRatio aDpiRatio) {
63 if (aCssWidth == 0.0f) {
64 return 0;
66 return std::max(LayoutDeviceIntCoord(1), (aCssWidth * aDpiRatio).Truncated());
69 [[nodiscard]] static float ScaleLuminanceBy(float aLuminance, float aFactor) {
70 return aLuminance >= 0.18f ? aLuminance * aFactor : aLuminance / aFactor;
73 static nscolor ThemedAccentColor(bool aBackground) {
74 MOZ_ASSERT(StaticPrefs::widget_non_native_theme_use_theme_accent());
75 // TODO(emilio): In the future we should probably add dark-color-scheme
76 // support for non-native form controls.
77 nscolor color = LookAndFeel::Color(
78 aBackground ? LookAndFeel::ColorID::MozAccentColor
79 : LookAndFeel::ColorID::MozAccentColorForeground,
80 LookAndFeel::ColorScheme::Light, LookAndFeel::UseStandins::No);
81 if (NS_GET_A(color) != 0xff) {
82 // Blend with white, ensuring the color is opaque to avoid surprises if we
83 // overdraw.
84 color = NS_ComposeColors(NS_RGB(0xff, 0xff, 0xff), color);
86 return color;
89 } // namespace
91 sRGBColor nsNativeBasicTheme::sAccentColor = sRGBColor::OpaqueWhite();
92 sRGBColor nsNativeBasicTheme::sAccentColorForeground = sRGBColor::OpaqueWhite();
93 sRGBColor nsNativeBasicTheme::sAccentColorLight = sRGBColor::OpaqueWhite();
94 sRGBColor nsNativeBasicTheme::sAccentColorDark = sRGBColor::OpaqueWhite();
95 sRGBColor nsNativeBasicTheme::sAccentColorDarker = sRGBColor::OpaqueWhite();
96 CSSIntCoord nsNativeBasicTheme::sHorizontalScrollbarHeight = CSSIntCoord(0);
97 CSSIntCoord nsNativeBasicTheme::sVerticalScrollbarWidth = CSSIntCoord(0);
98 bool nsNativeBasicTheme::sOverlayScrollbars = false;
100 static constexpr nsLiteralCString kPrefs[] = {
101 "widget.non-native-theme.use-theme-accent"_ns,
102 "widget.non-native-theme.win.scrollbar.use-system-size"_ns,
103 "widget.non-native-theme.scrollbar.size"_ns,
106 void nsNativeBasicTheme::Init() {
107 for (const auto& pref : kPrefs) {
108 Preferences::RegisterCallback(PrefChangedCallback, pref);
110 LookAndFeelChanged();
113 void nsNativeBasicTheme::Shutdown() {
114 for (const auto& pref : kPrefs) {
115 Preferences::UnregisterCallback(PrefChangedCallback, pref);
119 void nsNativeBasicTheme::LookAndFeelChanged() {
120 RecomputeAccentColors();
121 RecomputeScrollbarParams();
124 void nsNativeBasicTheme::RecomputeAccentColors() {
125 MOZ_RELEASE_ASSERT(NS_IsMainThread());
127 if (!StaticPrefs::widget_non_native_theme_use_theme_accent()) {
128 sAccentColorForeground = sColorWhite;
129 sAccentColor =
130 sRGBColor::UnusualFromARGB(0xff0060df); // Luminance: 13.69346%
131 sAccentColorLight =
132 sRGBColor::UnusualFromARGB(0x4d008deb); // Luminance: 25.04791%
133 sAccentColorDark =
134 sRGBColor::UnusualFromARGB(0xff0250bb); // Luminance: 9.33808%
135 sAccentColorDarker =
136 sRGBColor::UnusualFromARGB(0xff054096); // Luminance: 5.90106%
137 return;
140 sAccentColorForeground = sRGBColor::FromABGR(ThemedAccentColor(false));
141 const nscolor accent = ThemedAccentColor(true);
142 const float luminance = RelativeLuminanceUtils::Compute(accent);
144 constexpr float kLightLuminanceScale = 25.048f / 13.693f;
145 constexpr float kDarkLuminanceScale = 9.338f / 13.693f;
146 constexpr float kDarkerLuminanceScale = 5.901f / 13.693f;
148 const float lightLuminanceAdjust =
149 ScaleLuminanceBy(luminance, kLightLuminanceScale);
150 const float darkLuminanceAdjust =
151 ScaleLuminanceBy(luminance, kDarkLuminanceScale);
152 const float darkerLuminanceAdjust =
153 ScaleLuminanceBy(luminance, kDarkerLuminanceScale);
155 sAccentColor = sRGBColor::FromABGR(accent);
158 nscolor lightColor =
159 RelativeLuminanceUtils::Adjust(accent, lightLuminanceAdjust);
160 lightColor = NS_RGBA(NS_GET_R(lightColor), NS_GET_G(lightColor),
161 NS_GET_B(lightColor), 0x4d);
162 sAccentColorLight = sRGBColor::FromABGR(lightColor);
165 sAccentColorDark = sRGBColor::FromABGR(
166 RelativeLuminanceUtils::Adjust(accent, darkLuminanceAdjust));
167 sAccentColorDarker = sRGBColor::FromABGR(
168 RelativeLuminanceUtils::Adjust(accent, darkerLuminanceAdjust));
171 void nsNativeBasicTheme::RecomputeScrollbarParams() {
172 sOverlayScrollbars =
173 LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
175 uint32_t defaultSize = StaticPrefs::widget_non_native_theme_scrollbar_size();
176 if (StaticPrefs::widget_non_native_theme_win_scrollbar_use_system_size()) {
177 sHorizontalScrollbarHeight = LookAndFeel::GetInt(
178 LookAndFeel::IntID::SystemHorizontalScrollbarHeight, defaultSize);
179 sVerticalScrollbarWidth = LookAndFeel::GetInt(
180 LookAndFeel::IntID::SystemVerticalScrollbarWidth, defaultSize);
181 } else {
182 sHorizontalScrollbarHeight = sVerticalScrollbarWidth = defaultSize;
184 // On GTK, widgets don't account for text scale factor, but that's included
185 // in the usual DPI computations, so we undo that here, just like
186 // GetMonitorScaleFactor does it in nsNativeThemeGTK.
187 float scale =
188 LookAndFeel::GetFloat(LookAndFeel::FloatID::TextScaleFactor, 1.0f);
189 if (scale != 1.0f) {
190 sVerticalScrollbarWidth = float(sVerticalScrollbarWidth) / scale;
191 sHorizontalScrollbarHeight = float(sHorizontalScrollbarHeight) / scale;
195 static bool IsScrollbarWidthThin(nsIFrame* aFrame) {
196 ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
197 auto scrollbarWidth = style->StyleUIReset()->mScrollbarWidth;
198 return scrollbarWidth == StyleScrollbarWidth::Thin;
201 static sRGBColor SystemColor(StyleSystemColor aColor) {
202 // TODO(emilio): We could not hardcode light appearance here with a bit of
203 // work, but doesn't matter for now.
204 return sRGBColor::FromABGR(LookAndFeel::Color(
205 aColor, LookAndFeel::ColorScheme::Light, LookAndFeel::UseStandins::No));
208 template <typename Compute>
209 static sRGBColor SystemColorOrElse(StyleSystemColor aColor, Compute aCompute) {
210 if (auto color =
211 LookAndFeel::GetColor(aColor, LookAndFeel::ColorScheme::Light,
212 LookAndFeel::UseStandins::No)) {
213 return sRGBColor::FromABGR(*color);
215 return aCompute();
218 static std::pair<sRGBColor, sRGBColor> SystemColorPair(
219 StyleSystemColor aFirst, StyleSystemColor aSecond) {
220 return std::make_pair(SystemColor(aFirst), SystemColor(aSecond));
223 /* static */
224 auto nsNativeBasicTheme::GetDPIRatioForScrollbarPart(nsPresContext* aPc)
225 -> DPIRatio {
226 return DPIRatio(float(AppUnitsPerCSSPixel()) /
227 aPc->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
230 /* static */
231 auto nsNativeBasicTheme::GetDPIRatio(nsPresContext* aPc,
232 StyleAppearance aAppearance) -> DPIRatio {
233 // Widgets react to zoom, except scrollbars.
234 if (IsWidgetScrollbarPart(aAppearance)) {
235 return GetDPIRatioForScrollbarPart(aPc);
237 return DPIRatio(float(AppUnitsPerCSSPixel()) / aPc->AppUnitsPerDevPixel());
240 /* static */
241 auto nsNativeBasicTheme::GetDPIRatio(nsIFrame* aFrame,
242 StyleAppearance aAppearance) -> DPIRatio {
243 return GetDPIRatio(aFrame->PresContext(), aAppearance);
246 /* static */
247 bool nsNativeBasicTheme::IsDateTimeResetButton(nsIFrame* aFrame) {
248 if (!aFrame) {
249 return false;
252 nsIFrame* parent = aFrame->GetParent();
253 if (parent && (parent = parent->GetParent()) &&
254 (parent = parent->GetParent())) {
255 nsDateTimeControlFrame* dateTimeFrame = do_QueryFrame(parent);
256 if (dateTimeFrame) {
257 return true;
260 return false;
263 /* static */
264 bool nsNativeBasicTheme::IsColorPickerButton(nsIFrame* aFrame) {
265 nsColorControlFrame* colorPickerButton = do_QueryFrame(aFrame);
266 return colorPickerButton;
269 // Checkbox and radio need to preserve aspect-ratio for compat. We also snap the
270 // size to exact device pixels to avoid snapping disorting the circles.
271 static LayoutDeviceRect CheckBoxRadioRect(const LayoutDeviceRect& aRect) {
272 // Place a square rect in the center of aRect.
273 auto size = std::trunc(std::min(aRect.width, aRect.height));
274 auto position = aRect.Center() - LayoutDevicePoint(size * 0.5, size * 0.5);
275 return LayoutDeviceRect(position, LayoutDeviceSize(size, size));
278 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeCheckboxColors(
279 const EventStates& aState, StyleAppearance aAppearance,
280 UseSystemColors aUseSystemColors) {
281 MOZ_ASSERT(aAppearance == StyleAppearance::Checkbox ||
282 aAppearance == StyleAppearance::Radio);
283 bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
284 bool isPressed =
285 aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
286 bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
287 bool isChecked = aState.HasState(NS_EVENT_STATE_CHECKED);
288 bool isIndeterminate = aAppearance == StyleAppearance::Checkbox &&
289 aState.HasState(NS_EVENT_STATE_INDETERMINATE);
291 if (bool(aUseSystemColors)) {
292 sRGBColor backgroundColor = SystemColor(StyleSystemColor::Buttonface);
293 sRGBColor borderColor = SystemColor(StyleSystemColor::Buttontext);
294 if (isDisabled) {
295 borderColor = SystemColor(StyleSystemColor::Graytext);
296 if (isChecked || isIndeterminate) {
297 backgroundColor = borderColor;
299 } else if (isChecked || isIndeterminate) {
300 backgroundColor = borderColor = SystemColor(StyleSystemColor::Highlight);
302 return {backgroundColor, borderColor};
305 sRGBColor backgroundColor = sColorWhite;
306 sRGBColor borderColor = sColorGrey40;
307 if (isDisabled) {
308 backgroundColor = sColorWhiteAlpha50;
309 borderColor = sColorGrey40Alpha50;
310 if (isChecked || isIndeterminate) {
311 backgroundColor = borderColor;
313 } else if (isChecked || isIndeterminate) {
314 const auto& color = isPressed ? sAccentColorDarker
315 : isHovered ? sAccentColorDark
316 : sAccentColor;
317 backgroundColor = borderColor = color;
318 } else if (isPressed) {
319 backgroundColor = sColorGrey20;
320 borderColor = sColorGrey60;
321 } else if (isHovered) {
322 backgroundColor = sColorWhite;
323 borderColor = sColorGrey50;
326 return std::make_pair(backgroundColor, borderColor);
329 sRGBColor nsNativeBasicTheme::ComputeCheckmarkColor(
330 const EventStates& aState, UseSystemColors aUseSystemColors) {
331 if (bool(aUseSystemColors)) {
332 return SystemColor(StyleSystemColor::Highlighttext);
334 if (aState.HasState(NS_EVENT_STATE_DISABLED)) {
335 return sColorWhiteAlpha80;
337 return sAccentColorForeground;
340 sRGBColor nsNativeBasicTheme::ComputeBorderColor(
341 const EventStates& aState, UseSystemColors aUseSystemColors) {
342 bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
343 if (bool(aUseSystemColors)) {
344 return SystemColor(isDisabled ? StyleSystemColor::Graytext
345 : StyleSystemColor::Buttontext);
347 bool isActive =
348 aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
349 bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
350 bool isFocused = aState.HasState(NS_EVENT_STATE_FOCUSRING);
351 if (isDisabled) {
352 return sColorGrey40Alpha50;
354 if (isFocused) {
355 // We draw the outline over the border for all controls that call into this,
356 // so to prevent issues where the border shows underneath if it snaps in the
357 // wrong direction, we use a transparent border. An alternative to this is
358 // ensuring that we snap the offset in PaintRoundedFocusRect the same was a
359 // we snap border widths, so that negative offsets are guaranteed to cover
360 // the border. But this looks harder to mess up.
361 return sTransparent;
363 if (isActive) {
364 return sColorGrey60;
366 if (isHovered) {
367 return sColorGrey50;
369 return sColorGrey40;
372 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeButtonColors(
373 const EventStates& aState, UseSystemColors aUseSystemColors,
374 nsIFrame* aFrame) {
375 bool isActive =
376 aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
377 bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
378 bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
380 const sRGBColor backgroundColor = [&] {
381 if (bool(aUseSystemColors)) {
382 return SystemColor(StyleSystemColor::Buttonface);
385 if (isDisabled) {
386 return sColorGrey10Alpha50;
388 if (IsDateTimeResetButton(aFrame)) {
389 return sColorWhite;
391 if (isActive) {
392 return sColorGrey30;
394 if (isHovered) {
395 return sColorGrey20;
397 return sColorGrey10;
398 }();
400 const sRGBColor borderColor = ComputeBorderColor(aState, aUseSystemColors);
401 return std::make_pair(backgroundColor, borderColor);
404 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeTextfieldColors(
405 const EventStates& aState, UseSystemColors aUseSystemColors) {
406 const sRGBColor backgroundColor = [&] {
407 if (bool(aUseSystemColors)) {
408 return SystemColor(StyleSystemColor::TextBackground);
410 if (aState.HasState(NS_EVENT_STATE_DISABLED)) {
411 return sColorWhiteAlpha50;
413 return sColorWhite;
414 }();
415 const sRGBColor borderColor = ComputeBorderColor(aState, aUseSystemColors);
416 return std::make_pair(backgroundColor, borderColor);
419 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeRangeProgressColors(
420 const EventStates& aState, UseSystemColors aUseSystemColors) {
421 if (bool(aUseSystemColors)) {
422 return SystemColorPair(StyleSystemColor::Highlight,
423 StyleSystemColor::Buttontext);
426 bool isActive =
427 aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
428 bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
429 bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
431 if (isDisabled) {
432 return std::make_pair(sColorGrey40Alpha50, sColorGrey40Alpha50);
434 if (isActive || isHovered) {
435 return std::make_pair(sAccentColorDark, sAccentColorDarker);
437 return std::make_pair(sAccentColor, sAccentColorDark);
440 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeRangeTrackColors(
441 const EventStates& aState, UseSystemColors aUseSystemColors) {
442 if (bool(aUseSystemColors)) {
443 return SystemColorPair(StyleSystemColor::TextBackground,
444 StyleSystemColor::Buttontext);
446 bool isActive =
447 aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
448 bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
449 bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
451 if (isDisabled) {
452 return std::make_pair(sColorGrey10Alpha50, sColorGrey40Alpha50);
454 if (isActive || isHovered) {
455 return std::make_pair(sColorGrey20, sColorGrey50);
457 return std::make_pair(sColorGrey10, sColorGrey40);
460 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeRangeThumbColors(
461 const EventStates& aState, UseSystemColors aUseSystemColors) {
462 if (bool(aUseSystemColors)) {
463 return SystemColorPair(StyleSystemColor::Highlighttext,
464 StyleSystemColor::Highlight);
467 bool isActive =
468 aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
469 bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
470 bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
472 const sRGBColor& backgroundColor = [&] {
473 if (isDisabled) {
474 return sColorGrey40;
476 if (isActive) {
477 return sAccentColor;
479 if (isHovered) {
480 return sColorGrey60;
482 return sColorGrey50;
483 }();
485 const sRGBColor borderColor = sColorWhite;
487 return std::make_pair(backgroundColor, borderColor);
490 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeProgressColors(
491 UseSystemColors aUseSystemColors) {
492 if (bool(aUseSystemColors)) {
493 return SystemColorPair(StyleSystemColor::Highlight,
494 StyleSystemColor::Buttontext);
496 return std::make_pair(sAccentColor, sAccentColorDark);
499 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeProgressTrackColors(
500 UseSystemColors aUseSystemColors) {
501 if (bool(aUseSystemColors)) {
502 return SystemColorPair(StyleSystemColor::Buttonface,
503 StyleSystemColor::Buttontext);
505 return std::make_pair(sColorGrey10, sColorGrey40);
508 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeMeterchunkColors(
509 const EventStates& aMeterState, UseSystemColors aUseSystemColors) {
510 if (bool(aUseSystemColors)) {
511 return ComputeProgressColors(aUseSystemColors);
513 sRGBColor borderColor = sColorMeterGreen20;
514 sRGBColor chunkColor = sColorMeterGreen10;
516 if (aMeterState.HasState(NS_EVENT_STATE_SUB_OPTIMUM)) {
517 borderColor = sColorMeterYellow20;
518 chunkColor = sColorMeterYellow10;
519 } else if (aMeterState.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM)) {
520 borderColor = sColorMeterRed20;
521 chunkColor = sColorMeterRed10;
524 return std::make_pair(chunkColor, borderColor);
527 sRGBColor nsNativeBasicTheme::ComputeMenulistArrowButtonColor(
528 const EventStates& aState, UseSystemColors aUseSystemColors) {
529 bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
530 if (bool(aUseSystemColors)) {
531 return SystemColor(isDisabled ? StyleSystemColor::Graytext
532 : StyleSystemColor::TextForeground);
534 return isDisabled ? sColorGrey60Alpha50 : sColorGrey60;
537 std::array<sRGBColor, 3> nsNativeBasicTheme::ComputeFocusRectColors(
538 UseSystemColors aUseSystemColors) {
539 if (bool(aUseSystemColors)) {
540 return {SystemColor(StyleSystemColor::Highlight),
541 SystemColor(StyleSystemColor::Buttontext),
542 SystemColor(StyleSystemColor::TextBackground)};
545 return {sAccentColor, sColorWhiteAlpha80, sAccentColorLight};
548 sRGBColor nsNativeBasicTheme::ComputeScrollbarTrackColor(
549 nsIFrame* aFrame, const ComputedStyle& aStyle,
550 const EventStates& aDocumentState, UseSystemColors aUseSystemColors) {
551 const nsStyleUI* ui = aStyle.StyleUI();
552 if (bool(aUseSystemColors)) {
553 return SystemColor(StyleSystemColor::TextBackground);
555 if (ShouldUseDarkScrollbar(aFrame, aStyle)) {
556 return sRGBColor::FromU8(20, 20, 25, 77);
558 if (ui->mScrollbarColor.IsColors()) {
559 return sRGBColor::FromABGR(
560 ui->mScrollbarColor.AsColors().track.CalcColor(aStyle));
562 if (aDocumentState.HasAllStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
563 return SystemColorOrElse(StyleSystemColor::ThemedScrollbarInactive,
564 [] { return sScrollbarColor; });
566 return SystemColorOrElse(StyleSystemColor::ThemedScrollbar,
567 [] { return sScrollbarColor; });
570 nscolor nsNativeBasicTheme::AdjustUnthemedScrollbarThumbColor(
571 nscolor aFaceColor, EventStates aStates) {
572 // In Windows 10, scrollbar thumb has the following colors:
574 // State | Color | Luminance
575 // -------+----------+----------
576 // Normal | Gray 205 | 61.0%
577 // Hover | Gray 166 | 38.1%
578 // Active | Gray 96 | 11.7%
580 // This function is written based on the ratios between the values.
581 bool isActive = aStates.HasState(NS_EVENT_STATE_ACTIVE);
582 bool isHover = aStates.HasState(NS_EVENT_STATE_HOVER);
583 if (!isActive && !isHover) {
584 return aFaceColor;
586 float luminance = RelativeLuminanceUtils::Compute(aFaceColor);
587 if (isActive) {
588 // 11.7 / 61.0
589 luminance = ScaleLuminanceBy(luminance, 0.192f);
590 } else {
591 // 38.1 / 61.0
592 luminance = ScaleLuminanceBy(luminance, 0.625f);
594 return RelativeLuminanceUtils::Adjust(aFaceColor, luminance);
597 /*static*/
598 nscolor nsNativeBasicTheme::GetScrollbarButtonColor(nscolor aTrackColor,
599 EventStates aStates) {
600 // See numbers in GetScrollbarArrowColor.
601 // This function is written based on ratios between values listed there.
603 bool isActive = aStates.HasState(NS_EVENT_STATE_ACTIVE);
604 bool isHover = aStates.HasState(NS_EVENT_STATE_HOVER);
605 if (!isActive && !isHover) {
606 return aTrackColor;
608 float luminance = RelativeLuminanceUtils::Compute(aTrackColor);
609 if (isActive) {
610 if (luminance >= 0.18f) {
611 luminance *= 0.134f;
612 } else {
613 luminance /= 0.134f;
614 luminance = std::min(luminance, 1.0f);
616 } else {
617 if (luminance >= 0.18f) {
618 luminance *= 0.805f;
619 } else {
620 luminance /= 0.805f;
623 return RelativeLuminanceUtils::Adjust(aTrackColor, luminance);
626 /*static*/
627 Maybe<nscolor> nsNativeBasicTheme::GetScrollbarArrowColor(
628 nscolor aButtonColor) {
629 // In Windows 10 scrollbar, there are several gray colors used:
631 // State | Background (lum) | Arrow | Contrast
632 // -------+------------------+---------+---------
633 // Normal | Gray 240 (87.1%) | Gray 96 | 5.5
634 // Hover | Gray 218 (70.1%) | Black | 15.0
635 // Active | Gray 96 (11.7%) | White | 6.3
637 // Contrast value is computed based on the definition in
638 // https://www.w3.org/TR/WCAG20/#contrast-ratiodef
640 // This function is written based on these values.
642 if (NS_GET_A(aButtonColor) == 0) {
643 // If the button color is transparent, because of e.g.
644 // scrollbar-color: <something> transparent, then use
645 // the thumb color, which is expected to have enough
646 // contrast.
647 return Nothing();
650 float luminance = RelativeLuminanceUtils::Compute(aButtonColor);
651 // Color with luminance larger than 0.72 has contrast ratio over 4.6
652 // to color with luminance of gray 96, so this value is chosen for
653 // this range. It is the luminance of gray 221.
654 if (luminance >= 0.72) {
655 // ComputeRelativeLuminanceFromComponents(96). That function cannot
656 // be constexpr because of std::pow.
657 const float GRAY96_LUMINANCE = 0.117f;
658 return Some(RelativeLuminanceUtils::Adjust(aButtonColor, GRAY96_LUMINANCE));
660 // The contrast ratio of a color to black equals that to white when its
661 // luminance is around 0.18, with a contrast ratio ~4.6 to both sides,
662 // thus the value below. It's the lumanince of gray 118.
664 // TODO(emilio): Maybe the button alpha is not the best thing to use here and
665 // we should use the thumb alpha? It seems weird that the color of the arrow
666 // depends on the opacity of the scrollbar thumb...
667 if (luminance >= 0.18) {
668 return Some(NS_RGBA(0, 0, 0, NS_GET_A(aButtonColor)));
670 return Some(NS_RGBA(255, 255, 255, NS_GET_A(aButtonColor)));
673 bool nsNativeBasicTheme::ShouldUseDarkScrollbar(nsIFrame* aFrame,
674 const ComputedStyle& aStyle) {
675 if (StaticPrefs::widget_disable_dark_scrollbar()) {
676 return false;
678 if (aStyle.StyleUI()->mScrollbarColor.IsColors()) {
679 return false;
681 return nsNativeTheme::IsDarkBackground(aFrame);
684 sRGBColor nsNativeBasicTheme::ComputeScrollbarThumbColor(
685 nsIFrame* aFrame, const ComputedStyle& aStyle,
686 const EventStates& aElementState, const EventStates& aDocumentState,
687 UseSystemColors aUseSystemColors) {
688 if (!bool(aUseSystemColors) && ShouldUseDarkScrollbar(aFrame, aStyle)) {
689 const bool forceThemed =
690 aElementState.HasState(NS_EVENT_STATE_ACTIVE) &&
691 StaticPrefs::widget_non_native_theme_scrollbar_active_always_themed();
692 if (!forceThemed) {
693 return sRGBColor::FromABGR(AdjustUnthemedScrollbarThumbColor(
694 NS_RGBA(249, 249, 250, 102), aElementState));
698 const nsStyleUI* ui = aStyle.StyleUI();
699 if (ui->mScrollbarColor.IsColors()) {
700 return sRGBColor::FromABGR(AdjustUnthemedScrollbarThumbColor(
701 ui->mScrollbarColor.AsColors().thumb.CalcColor(aStyle), aElementState));
704 auto systemColor = [&] {
705 if (aDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
706 return StyleSystemColor::ThemedScrollbarThumbInactive;
708 if (aElementState.HasState(NS_EVENT_STATE_ACTIVE)) {
709 if (bool(aUseSystemColors)) {
710 return StyleSystemColor::Highlight;
712 return StyleSystemColor::ThemedScrollbarThumbActive;
714 if (aElementState.HasState(NS_EVENT_STATE_HOVER)) {
715 if (bool(aUseSystemColors)) {
716 return StyleSystemColor::Highlight;
718 return StyleSystemColor::ThemedScrollbarThumbHover;
720 if (bool(aUseSystemColors)) {
721 return StyleSystemColor::TextForeground;
723 return StyleSystemColor::ThemedScrollbarThumb;
724 }();
726 return SystemColorOrElse(systemColor, [&] {
727 return sRGBColor::FromABGR(AdjustUnthemedScrollbarThumbColor(
728 sScrollbarThumbColor.ToABGR(), aElementState));
732 std::pair<sRGBColor, sRGBColor>
733 nsNativeBasicTheme::ComputeScrollbarButtonColors(
734 nsIFrame* aFrame, StyleAppearance aAppearance, const ComputedStyle& aStyle,
735 const EventStates& aElementState, const EventStates& aDocumentState,
736 UseSystemColors aUseSystemColors) {
737 if (bool(aUseSystemColors)) {
738 if (aElementState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE |
739 NS_EVENT_STATE_HOVER)) {
740 return SystemColorPair(StyleSystemColor::Highlight,
741 StyleSystemColor::Buttonface);
743 return SystemColorPair(StyleSystemColor::TextBackground,
744 StyleSystemColor::TextForeground);
747 auto trackColor = ComputeScrollbarTrackColor(aFrame, aStyle, aDocumentState,
748 aUseSystemColors);
749 nscolor buttonColor =
750 GetScrollbarButtonColor(trackColor.ToABGR(), aElementState);
751 auto arrowColor =
752 GetScrollbarArrowColor(buttonColor)
753 .map(sRGBColor::FromABGR)
754 .valueOrFrom([&] {
755 return ComputeScrollbarThumbColor(aFrame, aStyle, aElementState,
756 aDocumentState, aUseSystemColors);
758 return {sRGBColor::FromABGR(buttonColor), arrowColor};
761 static const CSSCoord kInnerFocusOutlineWidth = 2.0f;
763 template <typename PaintBackendData>
764 void nsNativeBasicTheme::PaintRoundedFocusRect(PaintBackendData& aBackendData,
765 const LayoutDeviceRect& aRect,
766 UseSystemColors aUseSystemColors,
767 DPIRatio aDpiRatio,
768 CSSCoord aRadius,
769 CSSCoord aOffset) {
770 // NOTE(emilio): If the widths or offsets here change, make sure to tweak
771 // the GetWidgetOverflow path for FocusOutline.
772 auto [innerColor, middleColor, outerColor] =
773 ComputeFocusRectColors(aUseSystemColors);
775 LayoutDeviceRect focusRect(aRect);
777 // The focus rect is painted outside of the border area (aRect), see:
779 // data:text/html,<div style="border: 1px solid; outline: 2px solid
780 // red">Foobar</div>
782 // But some controls might provide a negative offset to cover the border, if
783 // necessary.
784 CSSCoord strokeWidth = kInnerFocusOutlineWidth;
785 auto strokeWidthDevPx =
786 LayoutDeviceCoord(SnapBorderWidth(strokeWidth, aDpiRatio));
787 CSSCoord strokeRadius = aRadius;
788 focusRect.Inflate(aOffset * aDpiRatio + strokeWidthDevPx);
790 PaintRoundedRectWithRadius(aBackendData, focusRect, sTransparent, innerColor,
791 strokeWidth, strokeRadius, aDpiRatio);
793 strokeWidth = CSSCoord(1.0f);
794 strokeWidthDevPx = LayoutDeviceCoord(SnapBorderWidth(strokeWidth, aDpiRatio));
795 strokeRadius += strokeWidth;
796 focusRect.Inflate(strokeWidthDevPx);
798 PaintRoundedRectWithRadius(aBackendData, focusRect, sTransparent, middleColor,
799 strokeWidth, strokeRadius, aDpiRatio);
801 strokeWidth = CSSCoord(2.0f);
802 strokeWidthDevPx = LayoutDeviceCoord(SnapBorderWidth(strokeWidth, aDpiRatio));
803 strokeRadius += strokeWidth;
804 focusRect.Inflate(strokeWidthDevPx);
806 PaintRoundedRectWithRadius(aBackendData, focusRect, sTransparent, outerColor,
807 strokeWidth, strokeRadius, aDpiRatio);
810 void nsNativeBasicTheme::PaintRoundedRectWithRadius(
811 WebRenderBackendData& aWrData, const LayoutDeviceRect& aRect,
812 const LayoutDeviceRect& aClipRect, const sRGBColor& aBackgroundColor,
813 const sRGBColor& aBorderColor, CSSCoord aBorderWidth, CSSCoord aRadius,
814 DPIRatio aDpiRatio) {
815 const bool kBackfaceIsVisible = true;
816 const LayoutDeviceCoord borderWidth(SnapBorderWidth(aBorderWidth, aDpiRatio));
817 const LayoutDeviceCoord radius(aRadius * aDpiRatio);
818 const wr::LayoutRect dest = wr::ToLayoutRect(aRect);
819 const wr::LayoutRect clip = wr::ToLayoutRect(aClipRect);
821 // Push the background.
822 if (aBackgroundColor.a) {
823 auto backgroundColor = wr::ToColorF(ToDeviceColor(aBackgroundColor));
824 wr::LayoutRect backgroundRect = [&] {
825 LayoutDeviceRect bg = aRect;
826 bg.Deflate(borderWidth);
827 return wr::ToLayoutRect(bg);
828 }();
829 if (!radius) {
830 aWrData.mBuilder.PushRect(backgroundRect, clip, kBackfaceIsVisible,
831 backgroundColor);
832 } else {
833 // NOTE(emilio): This follows DisplayListBuilder::PushRoundedRect and
834 // draws the rounded fill as an extra thick rounded border instead of a
835 // rectangle that's clipped to a rounded clip. Refer to that method for a
836 // justification. See bug 1694269.
837 LayoutDeviceCoord backgroundRadius =
838 std::max(0.0f, float(radius) - float(borderWidth));
839 wr::BorderSide side = {backgroundColor, wr::BorderStyle::Solid};
840 const wr::BorderSide sides[4] = {side, side, side, side};
841 float h = backgroundRect.size.width * 0.6f;
842 float v = backgroundRect.size.height * 0.6f;
843 wr::LayoutSideOffsets widths = {v, h, v, h};
844 wr::BorderRadius radii = {{backgroundRadius, backgroundRadius},
845 {backgroundRadius, backgroundRadius},
846 {backgroundRadius, backgroundRadius},
847 {backgroundRadius, backgroundRadius}};
848 aWrData.mBuilder.PushBorder(backgroundRect, clip, kBackfaceIsVisible,
849 widths, {sides, 4}, radii);
853 if (borderWidth && aBorderColor.a) {
854 // Push the border.
855 const auto borderColor = ToDeviceColor(aBorderColor);
856 const auto side = wr::ToBorderSide(borderColor, StyleBorderStyle::Solid);
857 const wr::BorderSide sides[4] = {side, side, side, side};
858 const LayoutDeviceSize sideRadius(radius, radius);
859 const auto widths =
860 wr::ToBorderWidths(borderWidth, borderWidth, borderWidth, borderWidth);
861 const auto wrRadius =
862 wr::ToBorderRadius(sideRadius, sideRadius, sideRadius, sideRadius);
863 aWrData.mBuilder.PushBorder(dest, clip, kBackfaceIsVisible, widths,
864 {sides, 4}, wrRadius);
868 void nsNativeBasicTheme::FillRect(DrawTarget& aDt,
869 const LayoutDeviceRect& aRect,
870 const sRGBColor& aColor) {
871 aDt.FillRect(aRect.ToUnknownRect(), ColorPattern(ToDeviceColor(aColor)));
874 void nsNativeBasicTheme::FillRect(WebRenderBackendData& aWrData,
875 const LayoutDeviceRect& aRect,
876 const sRGBColor& aColor) {
877 const bool kBackfaceIsVisible = true;
878 auto dest = wr::ToLayoutRect(aRect);
879 aWrData.mBuilder.PushRect(dest, dest, kBackfaceIsVisible,
880 wr::ToColorF(ToDeviceColor(aColor)));
883 void nsNativeBasicTheme::PaintRoundedRectWithRadius(
884 DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect,
885 const LayoutDeviceRect& aClipRect, const sRGBColor& aBackgroundColor,
886 const sRGBColor& aBorderColor, CSSCoord aBorderWidth, CSSCoord aRadius,
887 DPIRatio aDpiRatio) {
888 const LayoutDeviceCoord borderWidth(SnapBorderWidth(aBorderWidth, aDpiRatio));
889 const bool needsClip = !(aRect == aClipRect);
890 if (needsClip) {
891 aDrawTarget.PushClipRect(aClipRect.ToUnknownRect());
894 LayoutDeviceRect rect(aRect);
895 // Deflate the rect by half the border width, so that the middle of the
896 // stroke fills exactly the area we want to fill and not more.
897 rect.Deflate(borderWidth * 0.5f);
899 LayoutDeviceCoord radius(aRadius * aDpiRatio - borderWidth * 0.5f);
900 // Fix up the radius if it's too large with the rect we're going to paint.
902 LayoutDeviceCoord min = std::min(rect.width, rect.height);
903 if (radius * 2.0f > min) {
904 radius = min * 0.5f;
908 Maybe<ColorPattern> backgroundPattern;
909 if (aBackgroundColor.a) {
910 backgroundPattern.emplace(ToDeviceColor(aBackgroundColor));
912 Maybe<ColorPattern> borderPattern;
913 if (borderWidth && aBorderColor.a) {
914 borderPattern.emplace(ToDeviceColor(aBorderColor));
917 if (borderPattern || backgroundPattern) {
918 if (radius) {
919 RectCornerRadii radii(radius, radius, radius, radius);
920 RefPtr<Path> roundedRect =
921 MakePathForRoundedRect(aDrawTarget, rect.ToUnknownRect(), radii);
923 if (backgroundPattern) {
924 aDrawTarget.Fill(roundedRect, *backgroundPattern);
926 if (borderPattern) {
927 aDrawTarget.Stroke(roundedRect, *borderPattern,
928 StrokeOptions(borderWidth));
930 } else {
931 if (backgroundPattern) {
932 aDrawTarget.FillRect(rect.ToUnknownRect(), *backgroundPattern);
934 if (borderPattern) {
935 aDrawTarget.StrokeRect(rect.ToUnknownRect(), *borderPattern,
936 StrokeOptions(borderWidth));
941 if (needsClip) {
942 aDrawTarget.PopClip();
946 void nsNativeBasicTheme::PaintCheckboxControl(DrawTarget& aDrawTarget,
947 const LayoutDeviceRect& aRect,
948 const EventStates& aState,
949 UseSystemColors aUseSystemColors,
950 DPIRatio aDpiRatio) {
951 auto [backgroundColor, borderColor] = ComputeCheckboxColors(
952 aState, StyleAppearance::Checkbox, aUseSystemColors);
954 const CSSCoord radius = 2.0f;
955 CSSCoord borderWidth = kCheckboxRadioBorderWidth;
956 if (backgroundColor == borderColor) {
957 borderWidth = 0.0f;
959 PaintRoundedRectWithRadius(aDrawTarget, aRect, backgroundColor, borderColor,
960 borderWidth, radius, aDpiRatio);
963 if (aState.HasState(NS_EVENT_STATE_INDETERMINATE)) {
964 PaintIndeterminateMark(aDrawTarget, aRect, aState, aUseSystemColors);
965 } else if (aState.HasState(NS_EVENT_STATE_CHECKED)) {
966 PaintCheckMark(aDrawTarget, aRect, aState, aUseSystemColors);
969 if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
970 PaintRoundedFocusRect(aDrawTarget, aRect, aUseSystemColors, aDpiRatio, 5.0f,
971 1.0f);
975 constexpr CSSCoord kCheckboxRadioContentBoxSize = 10.0f;
976 constexpr CSSCoord kCheckboxRadioBorderBoxSize =
977 kCheckboxRadioContentBoxSize + kCheckboxRadioBorderWidth * 2.0f;
979 // Returns the right scale for points in a aSize x aSize sized box, centered at
980 // 0x0 to fill aRect in the smaller dimension.
981 static float ScaleToFillRect(const LayoutDeviceRect& aRect, const float aSize) {
982 return std::min(aRect.width, aRect.height) / aSize;
985 void nsNativeBasicTheme::PaintCheckMark(DrawTarget& aDrawTarget,
986 const LayoutDeviceRect& aRect,
987 const EventStates& aState,
988 UseSystemColors aUseSystemColors) {
989 // Points come from the coordinates on a 14X14 (kCheckboxRadioBorderBoxSize)
990 // unit box centered at 0,0
991 const float checkPolygonX[] = {-4.5f, -1.5f, -0.5f, 5.0f, 4.75f,
992 3.5f, -0.5f, -1.5f, -3.5f};
993 const float checkPolygonY[] = {0.5f, 4.0f, 4.0f, -2.5f, -4.0f,
994 -4.0f, 1.0f, 1.25f, -1.0f};
995 const int32_t checkNumPoints = sizeof(checkPolygonX) / sizeof(float);
996 const float scale = ScaleToFillRect(aRect, kCheckboxRadioBorderBoxSize);
997 auto center = aRect.Center().ToUnknownPoint();
999 RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
1000 Point p = center + Point(checkPolygonX[0] * scale, checkPolygonY[0] * scale);
1001 builder->MoveTo(p);
1002 for (int32_t i = 1; i < checkNumPoints; i++) {
1003 p = center + Point(checkPolygonX[i] * scale, checkPolygonY[i] * scale);
1004 builder->LineTo(p);
1006 RefPtr<Path> path = builder->Finish();
1008 sRGBColor fillColor = ComputeCheckmarkColor(aState, aUseSystemColors);
1009 aDrawTarget.Fill(path, ColorPattern(ToDeviceColor(fillColor)));
1012 void nsNativeBasicTheme::PaintIndeterminateMark(
1013 DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect,
1014 const EventStates& aState, UseSystemColors aUseSystemColors) {
1015 const CSSCoord borderWidth = 2.0f;
1016 const float scale = ScaleToFillRect(aRect, kCheckboxRadioBorderBoxSize);
1018 Rect rect = aRect.ToUnknownRect();
1019 rect.y += (rect.height / 2) - (borderWidth * scale / 2);
1020 rect.height = borderWidth * scale;
1021 rect.x += (borderWidth * scale) + (borderWidth * scale / 8);
1022 rect.width -= ((borderWidth * scale) + (borderWidth * scale / 8)) * 2;
1024 sRGBColor fillColor = ComputeCheckmarkColor(aState, aUseSystemColors);
1025 aDrawTarget.FillRect(rect, ColorPattern(ToDeviceColor(fillColor)));
1028 template <typename PaintBackendData>
1029 void nsNativeBasicTheme::PaintStrokedCircle(PaintBackendData& aPaintData,
1030 const LayoutDeviceRect& aRect,
1031 const sRGBColor& aBackgroundColor,
1032 const sRGBColor& aBorderColor,
1033 const CSSCoord aBorderWidth,
1034 DPIRatio aDpiRatio) {
1035 auto radius = LayoutDeviceCoord(aRect.Size().width) / aDpiRatio;
1036 PaintRoundedRectWithRadius(aPaintData, aRect, aBackgroundColor, aBorderColor,
1037 aBorderWidth, radius, aDpiRatio);
1040 void nsNativeBasicTheme::PaintCircleShadow(WebRenderBackendData& aWrData,
1041 const LayoutDeviceRect& aBoxRect,
1042 const LayoutDeviceRect& aClipRect,
1043 float aShadowAlpha,
1044 const CSSPoint& aShadowOffset,
1045 CSSCoord aShadowBlurStdDev,
1046 DPIRatio aDpiRatio) {
1047 const bool kBackfaceIsVisible = true;
1048 const LayoutDeviceCoord stdDev = aShadowBlurStdDev * aDpiRatio;
1049 const LayoutDevicePoint shadowOffset = aShadowOffset * aDpiRatio;
1050 const IntSize inflation =
1051 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev, stdDev));
1052 LayoutDeviceRect shadowRect = aBoxRect;
1053 shadowRect.MoveBy(shadowOffset);
1054 shadowRect.Inflate(inflation.width, inflation.height);
1055 const auto boxRect = wr::ToLayoutRect(aBoxRect);
1056 aWrData.mBuilder.PushBoxShadow(
1057 wr::ToLayoutRect(shadowRect), wr::ToLayoutRect(aClipRect),
1058 kBackfaceIsVisible, boxRect,
1059 wr::ToLayoutVector2D(aShadowOffset * aDpiRatio),
1060 wr::ToColorF(DeviceColor(0.0f, 0.0f, 0.0f, aShadowAlpha)), stdDev,
1061 /* aSpread = */ 0.0f,
1062 wr::ToBorderRadius(gfx::RectCornerRadii(aBoxRect.Size().width)),
1063 wr::BoxShadowClipMode::Outset);
1066 void nsNativeBasicTheme::PaintCircleShadow(DrawTarget& aDrawTarget,
1067 const LayoutDeviceRect& aBoxRect,
1068 const LayoutDeviceRect& aClipRect,
1069 float aShadowAlpha,
1070 const CSSPoint& aShadowOffset,
1071 CSSCoord aShadowBlurStdDev,
1072 DPIRatio aDpiRatio) {
1073 Float stdDev = aShadowBlurStdDev * aDpiRatio;
1074 Point offset = (aShadowOffset * aDpiRatio).ToUnknownPoint();
1076 RefPtr<FilterNode> blurFilter =
1077 aDrawTarget.CreateFilter(FilterType::GAUSSIAN_BLUR);
1078 if (!blurFilter) {
1079 return;
1082 blurFilter->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION, stdDev);
1084 IntSize inflation =
1085 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev, stdDev));
1086 Rect inflatedRect = aBoxRect.ToUnknownRect();
1087 inflatedRect.Inflate(inflation.width, inflation.height);
1088 Rect sourceRectInFilterSpace =
1089 inflatedRect - aBoxRect.TopLeft().ToUnknownPoint();
1090 Point destinationPointOfSourceRect = inflatedRect.TopLeft() + offset;
1092 IntSize dtSize = RoundedToInt(aBoxRect.Size().ToUnknownSize());
1093 RefPtr<DrawTarget> ellipseDT = aDrawTarget.CreateSimilarDrawTargetForFilter(
1094 dtSize, SurfaceFormat::A8, blurFilter, blurFilter,
1095 sourceRectInFilterSpace, destinationPointOfSourceRect);
1096 if (!ellipseDT) {
1097 return;
1100 AutoClipRect clipRect(aDrawTarget, aClipRect);
1102 RefPtr<Path> ellipse = MakePathForEllipse(
1103 *ellipseDT, (aBoxRect - aBoxRect.TopLeft()).Center().ToUnknownPoint(),
1104 aBoxRect.Size().ToUnknownSize());
1105 ellipseDT->Fill(ellipse,
1106 ColorPattern(DeviceColor(0.0f, 0.0f, 0.0f, aShadowAlpha)));
1107 RefPtr<SourceSurface> ellipseSurface = ellipseDT->Snapshot();
1109 blurFilter->SetInput(IN_GAUSSIAN_BLUR_IN, ellipseSurface);
1110 aDrawTarget.DrawFilter(blurFilter, sourceRectInFilterSpace,
1111 destinationPointOfSourceRect);
1114 template <typename PaintBackendData>
1115 void nsNativeBasicTheme::PaintRadioControl(PaintBackendData& aPaintData,
1116 const LayoutDeviceRect& aRect,
1117 const EventStates& aState,
1118 UseSystemColors aUseSystemColors,
1119 DPIRatio aDpiRatio) {
1120 auto [backgroundColor, borderColor] =
1121 ComputeCheckboxColors(aState, StyleAppearance::Radio, aUseSystemColors);
1123 CSSCoord borderWidth = kCheckboxRadioBorderWidth;
1124 if (backgroundColor == borderColor) {
1125 borderWidth = 0.0f;
1127 PaintStrokedCircle(aPaintData, aRect, backgroundColor, borderColor,
1128 borderWidth, aDpiRatio);
1131 if (aState.HasState(NS_EVENT_STATE_CHECKED)) {
1132 LayoutDeviceRect rect(aRect);
1133 rect.Deflate(SnapBorderWidth(kCheckboxRadioBorderWidth, aDpiRatio));
1135 auto checkColor = ComputeCheckmarkColor(aState, aUseSystemColors);
1136 PaintStrokedCircle(aPaintData, rect, backgroundColor, checkColor,
1137 kCheckboxRadioBorderWidth, aDpiRatio);
1140 if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
1141 PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio, 5.0f,
1142 1.0f);
1146 template <typename PaintBackendData>
1147 void nsNativeBasicTheme::PaintTextField(PaintBackendData& aPaintData,
1148 const LayoutDeviceRect& aRect,
1149 const EventStates& aState,
1150 UseSystemColors aUseSystemColors,
1151 DPIRatio aDpiRatio) {
1152 auto [backgroundColor, borderColor] =
1153 ComputeTextfieldColors(aState, aUseSystemColors);
1155 const CSSCoord radius = 2.0f;
1157 PaintRoundedRectWithRadius(aPaintData, aRect, backgroundColor, borderColor,
1158 kTextFieldBorderWidth, radius, aDpiRatio);
1160 if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
1161 PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
1162 radius + kTextFieldBorderWidth,
1163 -kTextFieldBorderWidth);
1167 template <typename PaintBackendData>
1168 void nsNativeBasicTheme::PaintListbox(PaintBackendData& aPaintData,
1169 const LayoutDeviceRect& aRect,
1170 const EventStates& aState,
1171 UseSystemColors aUseSystemColors,
1172 DPIRatio aDpiRatio) {
1173 const CSSCoord radius = 2.0f;
1174 auto [backgroundColor, borderColor] =
1175 ComputeTextfieldColors(aState, aUseSystemColors);
1177 PaintRoundedRectWithRadius(aPaintData, aRect, backgroundColor, borderColor,
1178 kMenulistBorderWidth, radius, aDpiRatio);
1180 if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
1181 PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
1182 radius + kMenulistBorderWidth, -kMenulistBorderWidth);
1186 template <typename PaintBackendData>
1187 void nsNativeBasicTheme::PaintMenulist(PaintBackendData& aDrawTarget,
1188 const LayoutDeviceRect& aRect,
1189 const EventStates& aState,
1190 UseSystemColors aUseSystemColors,
1191 DPIRatio aDpiRatio) {
1192 const CSSCoord radius = 4.0f;
1193 auto [backgroundColor, borderColor] =
1194 ComputeButtonColors(aState, aUseSystemColors);
1196 PaintRoundedRectWithRadius(aDrawTarget, aRect, backgroundColor, borderColor,
1197 kMenulistBorderWidth, radius, aDpiRatio);
1199 if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
1200 PaintRoundedFocusRect(aDrawTarget, aRect, aUseSystemColors, aDpiRatio,
1201 radius + kMenulistBorderWidth, -kMenulistBorderWidth);
1205 void nsNativeBasicTheme::PaintArrow(DrawTarget& aDrawTarget,
1206 const LayoutDeviceRect& aRect,
1207 const float aArrowPolygonX[],
1208 const float aArrowPolygonY[],
1209 const float aArrowPolygonSize,
1210 const int32_t aArrowNumPoints,
1211 const sRGBColor aFillColor) {
1212 const float scale = ScaleToFillRect(aRect, aArrowPolygonSize);
1214 auto center = aRect.Center().ToUnknownPoint();
1216 RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
1217 Point p =
1218 center + Point(aArrowPolygonX[0] * scale, aArrowPolygonY[0] * scale);
1219 builder->MoveTo(p);
1220 for (int32_t i = 1; i < aArrowNumPoints; i++) {
1221 p = center + Point(aArrowPolygonX[i] * scale, aArrowPolygonY[i] * scale);
1222 builder->LineTo(p);
1224 RefPtr<Path> path = builder->Finish();
1226 aDrawTarget.Fill(path, ColorPattern(ToDeviceColor(aFillColor)));
1229 void nsNativeBasicTheme::PaintMenulistArrowButton(
1230 nsIFrame* aFrame, DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect,
1231 const EventStates& aState, UseSystemColors aUseSystemColors) {
1232 const float kPolygonX[] = {-4.0f, -0.5f, 0.5f, 4.0f, 4.0f,
1233 3.0f, 0.0f, 0.0f, -3.0f, -4.0f};
1234 const float kPolygonY[] = {-1, 3.0f, 3.0f, -1.0f, -2.0f,
1235 -2.0f, 1.5f, 1.5f, -2.0f, -2.0f};
1237 const float kPolygonSize = kMinimumDropdownArrowButtonWidth;
1239 sRGBColor arrowColor =
1240 ComputeMenulistArrowButtonColor(aState, aUseSystemColors);
1241 PaintArrow(aDrawTarget, aRect, kPolygonX, kPolygonY, kPolygonSize,
1242 ArrayLength(kPolygonX), arrowColor);
1245 void nsNativeBasicTheme::PaintSpinnerButton(
1246 nsIFrame* aFrame, DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect,
1247 const EventStates& aState, StyleAppearance aAppearance,
1248 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1249 auto [backgroundColor, borderColor] =
1250 ComputeButtonColors(aState, aUseSystemColors);
1252 aDrawTarget.FillRect(aRect.ToUnknownRect(),
1253 ColorPattern(ToDeviceColor(backgroundColor)));
1255 const float kPolygonX[] = {-3.5f, -0.5f, 0.5f, 3.5f, 3.5f,
1256 2.5f, 0.0f, 0.0f, -2.5f, -3.5f};
1257 float polygonY[] = {-1.5f, 1.5f, 1.5f, -1.5f, -2.5f,
1258 -2.5f, 0.0f, 0.0f, -2.5f, -2.5f};
1260 const float kPolygonSize = kMinimumSpinnerButtonHeight;
1261 if (aAppearance == StyleAppearance::SpinnerUpbutton) {
1262 for (auto& coord : polygonY) {
1263 coord = -coord;
1267 PaintArrow(aDrawTarget, aRect, kPolygonX, polygonY, kPolygonSize,
1268 ArrayLength(kPolygonX), borderColor);
1271 template <typename PaintBackendData>
1272 void nsNativeBasicTheme::PaintRange(nsIFrame* aFrame,
1273 PaintBackendData& aPaintData,
1274 const LayoutDeviceRect& aRect,
1275 const EventStates& aState,
1276 UseSystemColors aUseSystemColors,
1277 DPIRatio aDpiRatio, bool aHorizontal) {
1278 nsRangeFrame* rangeFrame = do_QueryFrame(aFrame);
1279 if (!rangeFrame) {
1280 return;
1283 double progress = rangeFrame->GetValueAsFractionOfRange();
1284 auto rect = aRect;
1285 LayoutDeviceRect thumbRect(0, 0, kMinimumRangeThumbSize * aDpiRatio,
1286 kMinimumRangeThumbSize * aDpiRatio);
1287 LayoutDeviceRect progressClipRect(aRect);
1288 LayoutDeviceRect trackClipRect(aRect);
1289 const LayoutDeviceCoord verticalSize = kRangeHeight * aDpiRatio;
1290 if (aHorizontal) {
1291 rect.height = verticalSize;
1292 rect.y = aRect.y + (aRect.height - rect.height) / 2;
1293 thumbRect.y = aRect.y + (aRect.height - thumbRect.height) / 2;
1295 if (IsFrameRTL(aFrame)) {
1296 thumbRect.x =
1297 aRect.x + (aRect.width - thumbRect.width) * (1.0 - progress);
1298 float midPoint = thumbRect.Center().X();
1299 trackClipRect.SetBoxX(aRect.X(), midPoint);
1300 progressClipRect.SetBoxX(midPoint, aRect.XMost());
1301 } else {
1302 thumbRect.x = aRect.x + (aRect.width - thumbRect.width) * progress;
1303 float midPoint = thumbRect.Center().X();
1304 progressClipRect.SetBoxX(aRect.X(), midPoint);
1305 trackClipRect.SetBoxX(midPoint, aRect.XMost());
1307 } else {
1308 rect.width = verticalSize;
1309 rect.x = aRect.x + (aRect.width - rect.width) / 2;
1310 thumbRect.x = aRect.x + (aRect.width - thumbRect.width) / 2;
1312 thumbRect.y =
1313 aRect.y + (aRect.height - thumbRect.height) * (1.0 - progress);
1314 float midPoint = thumbRect.Center().Y();
1315 trackClipRect.SetBoxY(aRect.Y(), midPoint);
1316 progressClipRect.SetBoxY(midPoint, aRect.YMost());
1319 const CSSCoord borderWidth = 1.0f;
1320 const CSSCoord radius = 3.0f;
1322 auto [progressColor, progressBorderColor] =
1323 ComputeRangeProgressColors(aState, aUseSystemColors);
1324 auto [trackColor, trackBorderColor] =
1325 ComputeRangeTrackColors(aState, aUseSystemColors);
1327 PaintRoundedRectWithRadius(aPaintData, rect, progressClipRect, progressColor,
1328 progressBorderColor, borderWidth, radius,
1329 aDpiRatio);
1331 PaintRoundedRectWithRadius(aPaintData, rect, trackClipRect, trackColor,
1332 trackBorderColor, borderWidth, radius, aDpiRatio);
1334 if (!aState.HasState(NS_EVENT_STATE_DISABLED)) {
1335 // Ensure the shadow doesn't expand outside of our overflow rect declared in
1336 // GetWidgetOverflow().
1337 auto overflowRect = aRect;
1338 overflowRect.Inflate(CSSCoord(6.0f) * aDpiRatio);
1339 // Thumb shadow
1340 PaintCircleShadow(aPaintData, thumbRect, overflowRect, 0.3f,
1341 CSSPoint(0.0f, 2.0f), 2.0f, aDpiRatio);
1344 // Draw the thumb on top.
1345 const CSSCoord thumbBorderWidth = 2.0f;
1346 auto [thumbColor, thumbBorderColor] =
1347 ComputeRangeThumbColors(aState, aUseSystemColors);
1349 PaintStrokedCircle(aPaintData, thumbRect, thumbColor, thumbBorderColor,
1350 thumbBorderWidth, aDpiRatio);
1352 if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
1353 PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
1354 radius, 1.0f);
1358 template <typename PaintBackendData>
1359 void nsNativeBasicTheme::PaintProgress(nsIFrame* aFrame,
1360 PaintBackendData& aPaintData,
1361 const LayoutDeviceRect& aRect,
1362 const EventStates& aState,
1363 UseSystemColors aUseSystemColors,
1364 DPIRatio aDpiRatio, bool aIsMeter) {
1365 const CSSCoord borderWidth = 1.0f;
1366 const CSSCoord radius = aIsMeter ? 6.0f : 3.0f;
1368 LayoutDeviceRect rect(aRect);
1369 const LayoutDeviceCoord thickness =
1370 (aIsMeter ? kMeterHeight : kProgressbarHeight) * aDpiRatio;
1372 const bool isHorizontal = !nsNativeTheme::IsVerticalProgress(aFrame);
1373 if (isHorizontal) {
1374 // Center it vertically.
1375 rect.y += (rect.height - thickness) / 2;
1376 rect.height = thickness;
1377 } else {
1378 // Center it horizontally.
1379 rect.x += (rect.width - thickness) / 2;
1380 rect.width = thickness;
1384 // Paint the track, unclipped.
1385 auto [backgroundColor, borderColor] =
1386 ComputeProgressTrackColors(aUseSystemColors);
1387 PaintRoundedRectWithRadius(aPaintData, rect, rect, backgroundColor,
1388 borderColor, borderWidth, radius, aDpiRatio);
1391 // Now paint the chunk, clipped as needed.
1392 LayoutDeviceRect clipRect = rect;
1393 if (aState.HasState(NS_EVENT_STATE_INDETERMINATE)) {
1394 // For indeterminate progress, we paint an animated chunk of 1/3 of the
1395 // progress size.
1397 // Animation speed and math borrowed from GTK.
1398 const LayoutDeviceCoord size = isHorizontal ? rect.width : rect.height;
1399 const LayoutDeviceCoord barSize = size * 0.3333f;
1400 const LayoutDeviceCoord travel = 2.0f * (size - barSize);
1402 // Period equals to travel / pixelsPerMillisecond where pixelsPerMillisecond
1403 // equals progressSize / 1000.0. This is equivalent to 1600.
1404 const unsigned kPeriod = 1600;
1406 const int t = PR_IntervalToMilliseconds(PR_IntervalNow()) % kPeriod;
1407 const LayoutDeviceCoord dx = travel * float(t) / float(kPeriod);
1408 if (isHorizontal) {
1409 rect.width = barSize;
1410 rect.x += (dx < travel * .5f) ? dx : travel - dx;
1411 } else {
1412 rect.height = barSize;
1413 rect.y += (dx < travel * .5f) ? dx : travel - dx;
1415 clipRect = rect;
1416 // Queue the next frame if needed.
1417 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
1418 NS_WARNING("Couldn't refresh indeterminate <progress>");
1420 } else {
1421 // This is the progress chunk, clip it to the right amount.
1422 double position = [&] {
1423 if (aIsMeter) {
1424 auto* meter = dom::HTMLMeterElement::FromNode(aFrame->GetContent());
1425 if (!meter) {
1426 return 0.0;
1428 return meter->Value() / meter->Max();
1430 auto* progress = dom::HTMLProgressElement::FromNode(aFrame->GetContent());
1431 if (!progress) {
1432 return 0.0;
1434 return progress->Value() / progress->Max();
1435 }();
1436 if (isHorizontal) {
1437 double clipWidth = rect.width * position;
1438 clipRect.width = clipWidth;
1439 if (IsFrameRTL(aFrame)) {
1440 clipRect.x += rect.width - clipWidth;
1442 } else {
1443 double clipHeight = rect.height * position;
1444 clipRect.height = clipHeight;
1445 clipRect.y += rect.height - clipHeight;
1449 auto [backgroundColor, borderColor] =
1450 aIsMeter ? ComputeMeterchunkColors(aState, aUseSystemColors)
1451 : ComputeProgressColors(aUseSystemColors);
1452 PaintRoundedRectWithRadius(aPaintData, rect, clipRect, backgroundColor,
1453 borderColor, borderWidth, radius, aDpiRatio);
1456 template <typename PaintBackendData>
1457 void nsNativeBasicTheme::PaintButton(nsIFrame* aFrame,
1458 PaintBackendData& aPaintData,
1459 const LayoutDeviceRect& aRect,
1460 const EventStates& aState,
1461 UseSystemColors aUseSystemColors,
1462 DPIRatio aDpiRatio) {
1463 const CSSCoord radius = 4.0f;
1464 auto [backgroundColor, borderColor] =
1465 ComputeButtonColors(aState, aUseSystemColors, aFrame);
1467 PaintRoundedRectWithRadius(aPaintData, aRect, backgroundColor, borderColor,
1468 kButtonBorderWidth, radius, aDpiRatio);
1470 if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
1471 PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
1472 radius + kButtonBorderWidth, -kButtonBorderWidth);
1476 template <typename PaintBackendData>
1477 bool nsNativeBasicTheme::DoPaintDefaultScrollbarThumb(
1478 PaintBackendData& aPaintData, const LayoutDeviceRect& aRect,
1479 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
1480 const EventStates& aElementState, const EventStates& aDocumentState,
1481 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1482 sRGBColor thumbColor = ComputeScrollbarThumbColor(
1483 aFrame, aStyle, aElementState, aDocumentState, aUseSystemColors);
1484 FillRect(aPaintData, aRect, thumbColor);
1485 return true;
1488 bool nsNativeBasicTheme::PaintScrollbarThumb(
1489 DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect, bool aHorizontal,
1490 nsIFrame* aFrame, const ComputedStyle& aStyle,
1491 const EventStates& aElementState, const EventStates& aDocumentState,
1492 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1493 return DoPaintDefaultScrollbarThumb(aDrawTarget, aRect, aHorizontal, aFrame,
1494 aStyle, aElementState, aDocumentState,
1495 aUseSystemColors, aDpiRatio);
1498 bool nsNativeBasicTheme::PaintScrollbarThumb(
1499 WebRenderBackendData& aWrData, const LayoutDeviceRect& aRect,
1500 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
1501 const EventStates& aElementState, const EventStates& aDocumentState,
1502 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1503 return DoPaintDefaultScrollbarThumb(aWrData, aRect, aHorizontal, aFrame,
1504 aStyle, aElementState, aDocumentState,
1505 aUseSystemColors, aDpiRatio);
1508 template <typename PaintBackendData>
1509 bool nsNativeBasicTheme::DoPaintDefaultScrollbar(
1510 PaintBackendData& aPaintData, const LayoutDeviceRect& aRect,
1511 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
1512 const EventStates& aElementState, const EventStates& aDocumentState,
1513 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1514 if (sOverlayScrollbars && !aElementState.HasAtLeastOneOfStates(
1515 NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE)) {
1516 return true;
1518 auto scrollbarColor = ComputeScrollbarTrackColor(
1519 aFrame, aStyle, aDocumentState, aUseSystemColors);
1520 FillRect(aPaintData, aRect, scrollbarColor);
1521 return true;
1524 bool nsNativeBasicTheme::PaintScrollbar(
1525 DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect, bool aHorizontal,
1526 nsIFrame* aFrame, const ComputedStyle& aStyle,
1527 const EventStates& aElementState, const EventStates& aDocumentState,
1528 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1529 return DoPaintDefaultScrollbar(aDrawTarget, aRect, aHorizontal, aFrame,
1530 aStyle, aElementState, aDocumentState,
1531 aUseSystemColors, aDpiRatio);
1534 bool nsNativeBasicTheme::PaintScrollbar(
1535 WebRenderBackendData& aWrData, const LayoutDeviceRect& aRect,
1536 bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
1537 const EventStates& aElementState, const EventStates& aDocumentState,
1538 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1539 return DoPaintDefaultScrollbar(aWrData, aRect, aHorizontal, aFrame, aStyle,
1540 aElementState, aDocumentState,
1541 aUseSystemColors, aDpiRatio);
1544 template <typename PaintBackendData>
1545 bool nsNativeBasicTheme::DoPaintDefaultScrollCorner(
1546 PaintBackendData& aPaintData, const LayoutDeviceRect& aRect,
1547 nsIFrame* aFrame, const ComputedStyle& aStyle,
1548 const EventStates& aDocumentState, UseSystemColors aUseSystemColors,
1549 DPIRatio aDpiRatio) {
1550 auto scrollbarColor = ComputeScrollbarTrackColor(
1551 aFrame, aStyle, aDocumentState, aUseSystemColors);
1552 FillRect(aPaintData, aRect, scrollbarColor);
1553 return true;
1556 bool nsNativeBasicTheme::PaintScrollCorner(
1557 DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect, nsIFrame* aFrame,
1558 const ComputedStyle& aStyle, const EventStates& aDocumentState,
1559 UseSystemColors aUseSystemColors, DPIRatio aDpiRatio) {
1560 return DoPaintDefaultScrollCorner(aDrawTarget, aRect, aFrame, aStyle,
1561 aDocumentState, aUseSystemColors,
1562 aDpiRatio);
1565 bool nsNativeBasicTheme::PaintScrollCorner(WebRenderBackendData& aWrData,
1566 const LayoutDeviceRect& aRect,
1567 nsIFrame* aFrame,
1568 const ComputedStyle& aStyle,
1569 const EventStates& aDocumentState,
1570 UseSystemColors aUseSystemColors,
1571 DPIRatio aDpiRatio) {
1572 return DoPaintDefaultScrollCorner(aWrData, aRect, aFrame, aStyle,
1573 aDocumentState, aUseSystemColors,
1574 aDpiRatio);
1577 void nsNativeBasicTheme::PaintScrollbarButton(
1578 DrawTarget& aDrawTarget, StyleAppearance aAppearance,
1579 const LayoutDeviceRect& aRect, nsIFrame* aFrame,
1580 const ComputedStyle& aStyle, const EventStates& aElementState,
1581 const EventStates& aDocumentState, UseSystemColors aUseSystemColors,
1582 DPIRatio aDpiRatio) {
1583 auto [buttonColor, arrowColor] =
1584 ComputeScrollbarButtonColors(aFrame, aAppearance, aStyle, aElementState,
1585 aDocumentState, aUseSystemColors);
1586 aDrawTarget.FillRect(aRect.ToUnknownRect(),
1587 ColorPattern(ToDeviceColor(buttonColor)));
1589 // Start with Up arrow.
1590 float arrowPolygonX[] = {-4.0f, 0.0f, 4.0f, 4.0f, 0.0f, -4.0f};
1591 float arrowPolygonY[] = {0.0f, -4.0f, 0.0f, 3.0f, -1.0f, 3.0f};
1593 const float kPolygonSize = 17;
1595 const int32_t arrowNumPoints = ArrayLength(arrowPolygonX);
1596 switch (aAppearance) {
1597 case StyleAppearance::ScrollbarbuttonUp:
1598 break;
1599 case StyleAppearance::ScrollbarbuttonDown:
1600 for (int32_t i = 0; i < arrowNumPoints; i++) {
1601 arrowPolygonY[i] *= -1;
1603 break;
1604 case StyleAppearance::ScrollbarbuttonLeft:
1605 for (int32_t i = 0; i < arrowNumPoints; i++) {
1606 int32_t temp = arrowPolygonX[i];
1607 arrowPolygonX[i] = arrowPolygonY[i];
1608 arrowPolygonY[i] = temp;
1610 break;
1611 case StyleAppearance::ScrollbarbuttonRight:
1612 for (int32_t i = 0; i < arrowNumPoints; i++) {
1613 int32_t temp = arrowPolygonX[i];
1614 arrowPolygonX[i] = arrowPolygonY[i] * -1;
1615 arrowPolygonY[i] = temp;
1617 break;
1618 default:
1619 return;
1621 PaintArrow(aDrawTarget, aRect, arrowPolygonX, arrowPolygonY, kPolygonSize,
1622 arrowNumPoints, arrowColor);
1625 NS_IMETHODIMP
1626 nsNativeBasicTheme::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
1627 StyleAppearance aAppearance,
1628 const nsRect& aRect,
1629 const nsRect& /* aDirtyRect */,
1630 DrawOverflow aDrawOverflow) {
1631 if (!DoDrawWidgetBackground(*aContext->GetDrawTarget(), aFrame, aAppearance,
1632 aRect, aDrawOverflow)) {
1633 return NS_ERROR_NOT_IMPLEMENTED;
1635 return NS_OK;
1638 bool nsNativeBasicTheme::CreateWebRenderCommandsForWidget(
1639 mozilla::wr::DisplayListBuilder& aBuilder,
1640 mozilla::wr::IpcResourceUpdateQueue& aResources,
1641 const mozilla::layers::StackingContextHelper& aSc,
1642 mozilla::layers::RenderRootStateManager* aManager, nsIFrame* aFrame,
1643 StyleAppearance aAppearance, const nsRect& aRect) {
1644 if (!StaticPrefs::widget_non_native_theme_webrender()) {
1645 return false;
1647 WebRenderBackendData data{aBuilder, aResources, aSc, aManager};
1648 return DoDrawWidgetBackground(data, aFrame, aAppearance, aRect,
1649 DrawOverflow::Yes);
1652 static LayoutDeviceRect ToSnappedRect(const nsRect& aRect,
1653 nscoord aTwipsPerPixel, DrawTarget& aDt) {
1654 return LayoutDeviceRect::FromUnknownRect(
1655 NSRectToSnappedRect(aRect, aTwipsPerPixel, aDt));
1658 static LayoutDeviceRect ToSnappedRect(
1659 const nsRect& aRect, nscoord aTwipsPerPixel,
1660 nsNativeBasicTheme::WebRenderBackendData& aDt) {
1661 // TODO: Do we need to do any more snapping here?
1662 return LayoutDeviceRect::FromAppUnits(aRect, aTwipsPerPixel);
1665 auto nsNativeBasicTheme::ShouldUseSystemColors(const dom::Document& aDoc)
1666 -> UseSystemColors {
1667 // TODO: Do we really want to use system colors even when the page can
1668 // override the high contrast theme? (mUseDocumentColors = true?).
1669 return UseSystemColors(
1670 PreferenceSheet::PrefsFor(aDoc).NonNativeThemeShouldUseSystemColors());
1673 template <typename PaintBackendData>
1674 bool nsNativeBasicTheme::DoDrawWidgetBackground(PaintBackendData& aPaintData,
1675 nsIFrame* aFrame,
1676 StyleAppearance aAppearance,
1677 const nsRect& aRect,
1678 DrawOverflow aDrawOverflow) {
1679 static_assert(std::is_same_v<PaintBackendData, DrawTarget> ||
1680 std::is_same_v<PaintBackendData, WebRenderBackendData>);
1682 const nsPresContext* pc = aFrame->PresContext();
1683 const nscoord twipsPerPixel = pc->AppUnitsPerDevPixel();
1684 const auto devPxRect = ToSnappedRect(aRect, twipsPerPixel, aPaintData);
1686 const EventStates docState = pc->Document()->GetDocumentState();
1687 const auto useSystemColors = ShouldUseSystemColors(*pc->Document());
1688 EventStates eventState = GetContentState(aFrame, aAppearance);
1689 if (aAppearance == StyleAppearance::MozMenulistArrowButton) {
1690 bool isHTML = IsHTMLContent(aFrame);
1691 nsIFrame* parentFrame = aFrame->GetParent();
1692 bool isMenulist = !isHTML && parentFrame->IsMenuFrame();
1693 // HTML select and XUL menulist dropdown buttons get state from the
1694 // parent.
1695 if (isHTML || isMenulist) {
1696 aFrame = parentFrame;
1697 eventState = GetContentState(parentFrame, aAppearance);
1701 if (aDrawOverflow == DrawOverflow::No) {
1702 eventState &= ~NS_EVENT_STATE_FOCUSRING;
1705 // Hack to avoid skia fuzziness: Add a dummy clip if the widget doesn't
1706 // overflow devPxRect.
1707 Maybe<AutoClipRect> maybeClipRect;
1708 if constexpr (std::is_same_v<PaintBackendData, DrawTarget>) {
1709 if (aAppearance != StyleAppearance::FocusOutline &&
1710 aAppearance != StyleAppearance::Range &&
1711 !eventState.HasState(NS_EVENT_STATE_FOCUSRING)) {
1712 maybeClipRect.emplace(aPaintData, devPxRect);
1716 DPIRatio dpiRatio = GetDPIRatio(aFrame, aAppearance);
1718 switch (aAppearance) {
1719 case StyleAppearance::Radio: {
1720 auto rect = CheckBoxRadioRect(devPxRect);
1721 PaintRadioControl(aPaintData, rect, eventState, useSystemColors,
1722 dpiRatio);
1723 break;
1725 case StyleAppearance::Checkbox: {
1726 if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) {
1727 // TODO: Need to figure out how to best draw this using WR.
1728 return false;
1729 } else {
1730 auto rect = CheckBoxRadioRect(devPxRect);
1731 PaintCheckboxControl(aPaintData, rect, eventState, useSystemColors,
1732 dpiRatio);
1734 break;
1736 case StyleAppearance::Textarea:
1737 case StyleAppearance::Textfield:
1738 case StyleAppearance::NumberInput:
1739 PaintTextField(aPaintData, devPxRect, eventState, useSystemColors,
1740 dpiRatio);
1741 break;
1742 case StyleAppearance::Listbox:
1743 PaintListbox(aPaintData, devPxRect, eventState, useSystemColors,
1744 dpiRatio);
1745 break;
1746 case StyleAppearance::MenulistButton:
1747 case StyleAppearance::Menulist:
1748 PaintMenulist(aPaintData, devPxRect, eventState, useSystemColors,
1749 dpiRatio);
1750 break;
1751 case StyleAppearance::MozMenulistArrowButton:
1752 if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) {
1753 // TODO: Need to figure out how to best draw this using WR.
1754 return false;
1755 } else {
1756 PaintMenulistArrowButton(aFrame, aPaintData, devPxRect, eventState,
1757 useSystemColors);
1759 break;
1760 case StyleAppearance::SpinnerUpbutton:
1761 case StyleAppearance::SpinnerDownbutton:
1762 if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) {
1763 // TODO: Need to figure out how to best draw this using WR.
1764 return false;
1765 } else {
1766 PaintSpinnerButton(aFrame, aPaintData, devPxRect, eventState,
1767 aAppearance, useSystemColors, dpiRatio);
1769 break;
1770 case StyleAppearance::Range:
1771 PaintRange(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
1772 dpiRatio, IsRangeHorizontal(aFrame));
1773 break;
1774 case StyleAppearance::RangeThumb:
1775 // Painted as part of StyleAppearance::Range.
1776 break;
1777 case StyleAppearance::ProgressBar:
1778 PaintProgress(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
1779 dpiRatio,
1780 /* aIsMeter = */ false);
1781 break;
1782 case StyleAppearance::Progresschunk:
1783 /* Painted as part of the progress bar */
1784 break;
1785 case StyleAppearance::Meter:
1786 PaintProgress(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
1787 dpiRatio, /* aIsMeter = */ true);
1788 break;
1789 case StyleAppearance::Meterchunk:
1790 /* Painted as part of the meter bar */
1791 break;
1792 case StyleAppearance::ScrollbarthumbHorizontal:
1793 case StyleAppearance::ScrollbarthumbVertical: {
1794 bool isHorizontal =
1795 aAppearance == StyleAppearance::ScrollbarthumbHorizontal;
1796 return PaintScrollbarThumb(aPaintData, devPxRect, isHorizontal, aFrame,
1797 *nsLayoutUtils::StyleForScrollbar(aFrame),
1798 eventState, docState, useSystemColors,
1799 dpiRatio);
1801 case StyleAppearance::ScrollbartrackHorizontal:
1802 case StyleAppearance::ScrollbartrackVertical: {
1803 bool isHorizontal =
1804 aAppearance == StyleAppearance::ScrollbartrackHorizontal;
1805 return PaintScrollbarTrack(aPaintData, devPxRect, isHorizontal, aFrame,
1806 *nsLayoutUtils::StyleForScrollbar(aFrame),
1807 docState, useSystemColors, dpiRatio);
1809 case StyleAppearance::ScrollbarHorizontal:
1810 case StyleAppearance::ScrollbarVertical: {
1811 bool isHorizontal = aAppearance == StyleAppearance::ScrollbarHorizontal;
1812 return PaintScrollbar(aPaintData, devPxRect, isHorizontal, aFrame,
1813 *nsLayoutUtils::StyleForScrollbar(aFrame),
1814 eventState, docState, useSystemColors, dpiRatio);
1816 case StyleAppearance::Scrollcorner:
1817 return PaintScrollCorner(aPaintData, devPxRect, aFrame,
1818 *nsLayoutUtils::StyleForScrollbar(aFrame),
1819 docState, useSystemColors, dpiRatio);
1820 case StyleAppearance::ScrollbarbuttonUp:
1821 case StyleAppearance::ScrollbarbuttonDown:
1822 case StyleAppearance::ScrollbarbuttonLeft:
1823 case StyleAppearance::ScrollbarbuttonRight:
1824 // For scrollbar-width:thin, we don't display the buttons.
1825 if (!IsScrollbarWidthThin(aFrame)) {
1826 if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) {
1827 // TODO: Need to figure out how to best draw this using WR.
1828 return false;
1829 } else {
1830 PaintScrollbarButton(aPaintData, aAppearance, devPxRect, aFrame,
1831 *nsLayoutUtils::StyleForScrollbar(aFrame),
1832 eventState, docState, useSystemColors, dpiRatio);
1835 break;
1836 case StyleAppearance::Button:
1837 PaintButton(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
1838 dpiRatio);
1839 break;
1840 case StyleAppearance::FocusOutline:
1841 PaintAutoStyleOutline(aFrame, aPaintData, devPxRect, useSystemColors,
1842 dpiRatio);
1843 break;
1844 default:
1845 // Various appearance values are used for XUL elements. Normally these
1846 // will not be available in content documents (and thus in the content
1847 // processes where the native basic theme can be used), but tests are
1848 // run with the remote XUL pref enabled and so we can get in here. So
1849 // we just return an error rather than assert.
1850 return false;
1853 return true;
1856 template <typename PaintBackendData>
1857 void nsNativeBasicTheme::PaintAutoStyleOutline(nsIFrame* aFrame,
1858 PaintBackendData& aPaintData,
1859 const LayoutDeviceRect& aRect,
1860 UseSystemColors aUseSystemColors,
1861 DPIRatio aDpiRatio) {
1862 auto [innerColor, middleColor, outerColor] =
1863 ComputeFocusRectColors(aUseSystemColors);
1864 Unused << middleColor;
1865 Unused << outerColor;
1867 LayoutDeviceRect rect(aRect);
1868 auto width =
1869 LayoutDeviceCoord(SnapBorderWidth(kInnerFocusOutlineWidth, aDpiRatio));
1870 rect.Inflate(width);
1872 const nscoord offset = aFrame->StyleOutline()->mOutlineOffset.ToAppUnits();
1873 nscoord cssRadii[8];
1874 if (!aFrame->GetBorderRadii(cssRadii)) {
1875 const CSSCoord cssOffset = CSSCoord::FromAppUnits(offset);
1876 const CSSCoord radius =
1877 cssOffset >= 0.0f
1878 ? kInnerFocusOutlineWidth
1879 : std::max(kInnerFocusOutlineWidth + cssOffset, CSSCoord(0.0f));
1880 return PaintRoundedRectWithRadius(aPaintData, rect, sRGBColor::White(0.0f),
1881 innerColor, kInnerFocusOutlineWidth,
1882 radius, aDpiRatio);
1885 nsPresContext* pc = aFrame->PresContext();
1886 const Float devPixelOffset = pc->AppUnitsToFloatDevPixels(offset);
1888 RectCornerRadii innerRadii;
1889 nsCSSRendering::ComputePixelRadii(cssRadii, pc->AppUnitsPerDevPixel(),
1890 &innerRadii);
1892 const auto borderColor = ToDeviceColor(innerColor);
1893 // NOTE(emilio): This doesn't use PaintRoundedRectWithRadius because we need
1894 // to support arbitrary radii.
1895 RectCornerRadii outerRadii;
1896 if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) {
1897 const Float widths[4] = {width + devPixelOffset, width + devPixelOffset,
1898 width + devPixelOffset, width + devPixelOffset};
1899 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii, widths, &outerRadii);
1901 const auto dest = wr::ToLayoutRect(rect);
1902 const auto side = wr::ToBorderSide(borderColor, StyleBorderStyle::Solid);
1903 const wr::BorderSide sides[4] = {side, side, side, side};
1904 const bool kBackfaceIsVisible = true;
1905 const auto wrWidths = wr::ToBorderWidths(width, width, width, width);
1906 const auto wrRadius = wr::ToBorderRadius(outerRadii);
1907 aPaintData.mBuilder.PushBorder(dest, dest, kBackfaceIsVisible, wrWidths,
1908 {sides, 4}, wrRadius);
1909 } else {
1910 const LayoutDeviceCoord halfWidth = width * 0.5f;
1911 rect.Deflate(halfWidth);
1912 const Float widths[4] = {
1913 halfWidth + devPixelOffset, halfWidth + devPixelOffset,
1914 halfWidth + devPixelOffset, halfWidth + devPixelOffset};
1915 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii, widths, &outerRadii);
1916 RefPtr<Path> path =
1917 MakePathForRoundedRect(aPaintData, rect.ToUnknownRect(), outerRadii);
1918 aPaintData.Stroke(path, ColorPattern(borderColor), StrokeOptions(width));
1922 LayoutDeviceIntMargin nsNativeBasicTheme::GetWidgetBorder(
1923 nsDeviceContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance) {
1924 switch (aAppearance) {
1925 case StyleAppearance::Textfield:
1926 case StyleAppearance::Textarea:
1927 case StyleAppearance::NumberInput:
1928 case StyleAppearance::Listbox:
1929 case StyleAppearance::Menulist:
1930 case StyleAppearance::MenulistButton:
1931 case StyleAppearance::Button:
1932 // Return the border size from the UA sheet, even though what we paint
1933 // doesn't actually match that. We know this is the UA sheet border
1934 // because we disable native theming when different border widths are
1935 // specified by authors, see nsNativeBasicTheme::IsWidgetStyled.
1937 // The Rounded() bit is technically redundant, but needed to appease the
1938 // type system, we should always end up with full device pixels due to
1939 // round_border_to_device_pixels at style time.
1940 return LayoutDeviceIntMargin::FromAppUnits(
1941 aFrame->StyleBorder()->GetComputedBorder(),
1942 aFrame->PresContext()->AppUnitsPerDevPixel())
1943 .Rounded();
1944 case StyleAppearance::Checkbox:
1945 case StyleAppearance::Radio: {
1946 DPIRatio dpiRatio = GetDPIRatio(aFrame, aAppearance);
1947 LayoutDeviceIntCoord w =
1948 SnapBorderWidth(kCheckboxRadioBorderWidth, dpiRatio);
1949 return LayoutDeviceIntMargin(w, w, w, w);
1951 default:
1952 return LayoutDeviceIntMargin();
1956 bool nsNativeBasicTheme::GetWidgetPadding(nsDeviceContext* aContext,
1957 nsIFrame* aFrame,
1958 StyleAppearance aAppearance,
1959 LayoutDeviceIntMargin* aResult) {
1960 switch (aAppearance) {
1961 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1962 // and have a meaningful baseline, so they can't have
1963 // author-specified padding.
1964 case StyleAppearance::Radio:
1965 case StyleAppearance::Checkbox:
1966 aResult->SizeTo(0, 0, 0, 0);
1967 return true;
1968 default:
1969 break;
1971 return false;
1974 bool nsNativeBasicTheme::GetWidgetOverflow(nsDeviceContext* aContext,
1975 nsIFrame* aFrame,
1976 StyleAppearance aAppearance,
1977 nsRect* aOverflowRect) {
1978 nsIntMargin overflow;
1979 switch (aAppearance) {
1980 case StyleAppearance::FocusOutline:
1981 // 2px * one segment
1982 overflow.SizeTo(2, 2, 2, 2);
1983 break;
1984 case StyleAppearance::Radio:
1985 case StyleAppearance::Checkbox:
1986 case StyleAppearance::Range:
1987 // 2px for each outline segment, plus 1px separation, plus we paint with a
1988 // 1px extra offset, so 6px.
1989 overflow.SizeTo(6, 6, 6, 6);
1990 break;
1991 case StyleAppearance::Textarea:
1992 case StyleAppearance::Textfield:
1993 case StyleAppearance::NumberInput:
1994 case StyleAppearance::Listbox:
1995 case StyleAppearance::MenulistButton:
1996 case StyleAppearance::Menulist:
1997 case StyleAppearance::Button:
1998 // 2px for each segment, plus 1px separation, but we paint 1px inside
1999 // the border area so 4px overflow.
2000 overflow.SizeTo(4, 4, 4, 4);
2001 break;
2002 default:
2003 return false;
2006 // TODO: This should convert from device pixels to app units, not from CSS
2007 // pixels. And it should take the dpi ratio into account.
2008 // Using CSS pixels can cause the overflow to be too small if the page is
2009 // zoomed out.
2010 aOverflowRect->Inflate(nsMargin(CSSPixel::ToAppUnits(overflow.top),
2011 CSSPixel::ToAppUnits(overflow.right),
2012 CSSPixel::ToAppUnits(overflow.bottom),
2013 CSSPixel::ToAppUnits(overflow.left)));
2015 return true;
2018 auto nsNativeBasicTheme::GetScrollbarSizes(nsPresContext* aPresContext,
2019 StyleScrollbarWidth aWidth, Overlay)
2020 -> ScrollbarSizes {
2021 CSSIntCoord h = sHorizontalScrollbarHeight;
2022 CSSIntCoord w = sVerticalScrollbarWidth;
2023 if (aWidth == StyleScrollbarWidth::Thin) {
2024 h /= 2;
2025 w /= 2;
2027 auto dpi = GetDPIRatioForScrollbarPart(aPresContext);
2028 return {(CSSCoord(w) * dpi).Rounded(), (CSSCoord(h) * dpi).Rounded()};
2031 nscoord nsNativeBasicTheme::GetCheckboxRadioPrefSize() {
2032 return CSSPixel::ToAppUnits(kCheckboxRadioContentBoxSize);
2035 NS_IMETHODIMP
2036 nsNativeBasicTheme::GetMinimumWidgetSize(nsPresContext* aPresContext,
2037 nsIFrame* aFrame,
2038 StyleAppearance aAppearance,
2039 LayoutDeviceIntSize* aResult,
2040 bool* aIsOverridable) {
2041 DPIRatio dpiRatio = GetDPIRatio(aFrame, aAppearance);
2043 aResult->width = aResult->height = 0;
2044 *aIsOverridable = true;
2046 switch (aAppearance) {
2047 case StyleAppearance::Button:
2048 if (IsColorPickerButton(aFrame)) {
2049 aResult->height = (kMinimumColorPickerHeight * dpiRatio).Rounded();
2051 break;
2052 case StyleAppearance::RangeThumb:
2053 aResult->SizeTo((kMinimumRangeThumbSize * dpiRatio).Rounded(),
2054 (kMinimumRangeThumbSize * dpiRatio).Rounded());
2055 break;
2056 case StyleAppearance::MozMenulistArrowButton:
2057 aResult->width = (kMinimumDropdownArrowButtonWidth * dpiRatio).Rounded();
2058 break;
2059 case StyleAppearance::SpinnerUpbutton:
2060 case StyleAppearance::SpinnerDownbutton:
2061 aResult->width = (kMinimumSpinnerButtonWidth * dpiRatio).Rounded();
2062 aResult->height = (kMinimumSpinnerButtonHeight * dpiRatio).Rounded();
2063 break;
2064 case StyleAppearance::ScrollbarbuttonUp:
2065 case StyleAppearance::ScrollbarbuttonDown:
2066 case StyleAppearance::ScrollbarbuttonLeft:
2067 case StyleAppearance::ScrollbarbuttonRight:
2068 // For scrollbar-width:thin, we don't display the buttons.
2069 if (IsScrollbarWidthThin(aFrame)) {
2070 aResult->SizeTo(0, 0);
2071 break;
2073 [[fallthrough]];
2074 case StyleAppearance::ScrollbarthumbVertical:
2075 case StyleAppearance::ScrollbarthumbHorizontal: {
2076 auto* style = nsLayoutUtils::StyleForScrollbar(aFrame);
2077 auto width = style->StyleUIReset()->mScrollbarWidth;
2078 auto sizes = GetScrollbarSizes(aPresContext, width, Overlay::No);
2079 // TODO: for short scrollbars it could be nice if the thumb could shrink
2080 // under this size.
2081 const bool isHorizontal =
2082 aAppearance == StyleAppearance::ScrollbarthumbHorizontal ||
2083 aAppearance == StyleAppearance::ScrollbarbuttonLeft ||
2084 aAppearance == StyleAppearance::ScrollbarbuttonRight;
2085 const auto size = isHorizontal ? sizes.mHorizontal : sizes.mVertical;
2086 aResult->SizeTo(size, size);
2087 break;
2089 default:
2090 break;
2093 return NS_OK;
2096 nsITheme::Transparency nsNativeBasicTheme::GetWidgetTransparency(
2097 nsIFrame* aFrame, StyleAppearance aAppearance) {
2098 return eUnknownTransparency;
2101 NS_IMETHODIMP
2102 nsNativeBasicTheme::WidgetStateChanged(nsIFrame* aFrame,
2103 StyleAppearance aAppearance,
2104 nsAtom* aAttribute, bool* aShouldRepaint,
2105 const nsAttrValue* aOldValue) {
2106 if (!aAttribute) {
2107 // Hover/focus/active changed. Always repaint.
2108 *aShouldRepaint = true;
2109 } else {
2110 // Check the attribute to see if it's relevant.
2111 // disabled, checked, dlgtype, default, etc.
2112 *aShouldRepaint = false;
2113 if ((aAttribute == nsGkAtoms::disabled) ||
2114 (aAttribute == nsGkAtoms::checked) ||
2115 (aAttribute == nsGkAtoms::selected) ||
2116 (aAttribute == nsGkAtoms::visuallyselected) ||
2117 (aAttribute == nsGkAtoms::menuactive) ||
2118 (aAttribute == nsGkAtoms::sortDirection) ||
2119 (aAttribute == nsGkAtoms::focused) ||
2120 (aAttribute == nsGkAtoms::_default) ||
2121 (aAttribute == nsGkAtoms::open) || (aAttribute == nsGkAtoms::hover)) {
2122 *aShouldRepaint = true;
2126 return NS_OK;
2129 NS_IMETHODIMP
2130 nsNativeBasicTheme::ThemeChanged() { return NS_OK; }
2132 bool nsNativeBasicTheme::WidgetAppearanceDependsOnWindowFocus(
2133 StyleAppearance aAppearance) {
2134 return IsWidgetScrollbarPart(aAppearance);
2137 nsITheme::ThemeGeometryType nsNativeBasicTheme::ThemeGeometryTypeForWidget(
2138 nsIFrame* aFrame, StyleAppearance aAppearance) {
2139 return eThemeGeometryTypeUnknown;
2142 bool nsNativeBasicTheme::ThemeSupportsWidget(nsPresContext* aPresContext,
2143 nsIFrame* aFrame,
2144 StyleAppearance aAppearance) {
2145 switch (aAppearance) {
2146 case StyleAppearance::Radio:
2147 case StyleAppearance::Checkbox:
2148 case StyleAppearance::FocusOutline:
2149 case StyleAppearance::Textarea:
2150 case StyleAppearance::Textfield:
2151 case StyleAppearance::Range:
2152 case StyleAppearance::RangeThumb:
2153 case StyleAppearance::ProgressBar:
2154 case StyleAppearance::Progresschunk:
2155 case StyleAppearance::Meter:
2156 case StyleAppearance::Meterchunk:
2157 case StyleAppearance::ScrollbarbuttonUp:
2158 case StyleAppearance::ScrollbarbuttonDown:
2159 case StyleAppearance::ScrollbarbuttonLeft:
2160 case StyleAppearance::ScrollbarbuttonRight:
2161 case StyleAppearance::ScrollbarthumbHorizontal:
2162 case StyleAppearance::ScrollbarthumbVertical:
2163 case StyleAppearance::ScrollbartrackHorizontal:
2164 case StyleAppearance::ScrollbartrackVertical:
2165 case StyleAppearance::ScrollbarHorizontal:
2166 case StyleAppearance::ScrollbarVertical:
2167 case StyleAppearance::Scrollcorner:
2168 case StyleAppearance::Button:
2169 case StyleAppearance::Listbox:
2170 case StyleAppearance::Menulist:
2171 case StyleAppearance::MenulistButton:
2172 case StyleAppearance::NumberInput:
2173 case StyleAppearance::MozMenulistArrowButton:
2174 case StyleAppearance::SpinnerUpbutton:
2175 case StyleAppearance::SpinnerDownbutton:
2176 return !IsWidgetStyled(aPresContext, aFrame, aAppearance);
2177 default:
2178 return false;
2182 bool nsNativeBasicTheme::WidgetIsContainer(StyleAppearance aAppearance) {
2183 switch (aAppearance) {
2184 case StyleAppearance::MozMenulistArrowButton:
2185 case StyleAppearance::Radio:
2186 case StyleAppearance::Checkbox:
2187 return false;
2188 default:
2189 return true;
2193 bool nsNativeBasicTheme::ThemeDrawsFocusForWidget(StyleAppearance aAppearance) {
2194 return true;
2197 bool nsNativeBasicTheme::ThemeNeedsComboboxDropmarker() { return true; }