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"
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 "nsCSSColorUtils.h"
17 #include "nsCSSRendering.h"
18 #include "nsLayoutUtils.h"
19 #include "PathHelpers.h"
21 #include "nsDeviceContext.h"
23 #include "nsColorControlFrame.h"
24 #include "nsDateTimeControlFrame.h"
25 #include "nsMeterFrame.h"
26 #include "nsProgressFrame.h"
27 #include "nsRangeFrame.h"
28 #include "mozilla/dom/HTMLMeterElement.h"
29 #include "mozilla/dom/HTMLProgressElement.h"
31 using namespace mozilla
;
32 using namespace mozilla::widget
;
33 using namespace mozilla::gfx
;
35 NS_IMPL_ISUPPORTS_INHERITED(nsNativeBasicTheme
, nsNativeTheme
, nsITheme
)
39 // This pushes and pops a clip rect to the draw target.
41 // This is done to reduce fuzz in places where we may have antialiasing,
42 // because skia is not clip-invariant: given different clips, it does not
43 // guarantee the same result, even if the painted content doesn't intersect
46 // This is a bit sad, overall, but...
47 struct MOZ_RAII AutoClipRect
{
48 AutoClipRect(DrawTarget
& aDt
, const LayoutDeviceRect
& aRect
) : mDt(aDt
) {
49 mDt
.PushClipRect(aRect
.ToUnknownRect());
52 ~AutoClipRect() { mDt
.PopClip(); }
58 static LayoutDeviceIntCoord
SnapBorderWidth(
59 CSSCoord aCssWidth
, nsNativeBasicTheme::DPIRatio aDpiRatio
) {
60 if (aCssWidth
== 0.0f
) {
63 return std::max(LayoutDeviceIntCoord(1), (aCssWidth
* aDpiRatio
).Truncated());
66 [[nodiscard
]] static float ScaleLuminanceBy(float aLuminance
, float aFactor
) {
67 return aLuminance
>= 0.18f
? aLuminance
* aFactor
: aLuminance
/ aFactor
;
70 static nscolor
ThemedAccentColor(bool aBackground
) {
71 MOZ_ASSERT(StaticPrefs::widget_non_native_use_theme_accent());
72 nscolor color
= LookAndFeel::GetColor(
73 aBackground
? LookAndFeel::ColorID::MozAccentColor
74 : LookAndFeel::ColorID::MozAccentColorForeground
);
75 if (NS_GET_A(color
) != 0xff) {
76 // Blend with white, ensuring the color is opaque to avoid surprises if we
78 color
= NS_ComposeColors(NS_RGB(0xff, 0xff, 0xff), color
);
85 sRGBColor
nsNativeBasicTheme::sAccentColor
= sRGBColor::OpaqueWhite();
86 sRGBColor
nsNativeBasicTheme::sAccentColorForeground
= sRGBColor::OpaqueWhite();
87 sRGBColor
nsNativeBasicTheme::sAccentColorLight
= sRGBColor::OpaqueWhite();
88 sRGBColor
nsNativeBasicTheme::sAccentColorDark
= sRGBColor::OpaqueWhite();
89 sRGBColor
nsNativeBasicTheme::sAccentColorDarker
= sRGBColor::OpaqueWhite();
91 void nsNativeBasicTheme::Init() {
92 Preferences::RegisterCallbackAndCall(PrefChangedCallback
,
93 "widget.non-native.use-theme-accent");
96 void nsNativeBasicTheme::Shutdown() {
97 Preferences::UnregisterCallback(PrefChangedCallback
,
98 "widget.non-native.use-theme-accent");
101 void nsNativeBasicTheme::LookAndFeelChanged() { RecomputeAccentColors(); }
103 void nsNativeBasicTheme::RecomputeAccentColors() {
104 MOZ_RELEASE_ASSERT(NS_IsMainThread());
106 if (!StaticPrefs::widget_non_native_use_theme_accent()) {
107 sAccentColorForeground
= sColorWhite
;
109 sRGBColor::UnusualFromARGB(0xff0060df); // Luminance: 13.69346%
111 sRGBColor::UnusualFromARGB(0x4d008deb); // Luminance: 25.04791%
113 sRGBColor::UnusualFromARGB(0xff0250bb); // Luminance: 9.33808%
115 sRGBColor::UnusualFromARGB(0xff054096); // Luminance: 5.90106%
119 sAccentColorForeground
= sRGBColor::FromABGR(ThemedAccentColor(false));
120 const nscolor accent
= ThemedAccentColor(true);
121 const float luminance
= RelativeLuminanceUtils::Compute(accent
);
123 constexpr float kLightLuminanceScale
= 25.048f
/ 13.693f
;
124 constexpr float kDarkLuminanceScale
= 9.338f
/ 13.693f
;
125 constexpr float kDarkerLuminanceScale
= 5.901f
/ 13.693f
;
127 const float lightLuminanceAdjust
=
128 ScaleLuminanceBy(luminance
, kLightLuminanceScale
);
129 const float darkLuminanceAdjust
=
130 ScaleLuminanceBy(luminance
, kDarkLuminanceScale
);
131 const float darkerLuminanceAdjust
=
132 ScaleLuminanceBy(luminance
, kDarkerLuminanceScale
);
134 sAccentColor
= sRGBColor::FromABGR(accent
);
138 RelativeLuminanceUtils::Adjust(accent
, lightLuminanceAdjust
);
139 lightColor
= NS_RGBA(NS_GET_R(lightColor
), NS_GET_G(lightColor
),
140 NS_GET_B(lightColor
), 0x4d);
141 sAccentColorLight
= sRGBColor::FromABGR(lightColor
);
144 sAccentColorDark
= sRGBColor::FromABGR(
145 RelativeLuminanceUtils::Adjust(accent
, darkLuminanceAdjust
));
146 sAccentColorDarker
= sRGBColor::FromABGR(
147 RelativeLuminanceUtils::Adjust(accent
, darkerLuminanceAdjust
));
150 static bool IsScrollbarWidthThin(nsIFrame
* aFrame
) {
151 ComputedStyle
* style
= nsLayoutUtils::StyleForScrollbar(aFrame
);
152 auto scrollbarWidth
= style
->StyleUIReset()->mScrollbarWidth
;
153 return scrollbarWidth
== StyleScrollbarWidth::Thin
;
157 auto nsNativeBasicTheme::GetDPIRatioForScrollbarPart(nsPresContext
* aPc
)
159 return DPIRatio(float(AppUnitsPerCSSPixel()) /
160 aPc
->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
164 auto nsNativeBasicTheme::GetDPIRatio(nsPresContext
* aPc
,
165 StyleAppearance aAppearance
) -> DPIRatio
{
166 // Widgets react to zoom, except scrollbars.
167 if (IsWidgetScrollbarPart(aAppearance
)) {
168 return GetDPIRatioForScrollbarPart(aPc
);
170 return DPIRatio(float(AppUnitsPerCSSPixel()) / aPc
->AppUnitsPerDevPixel());
174 auto nsNativeBasicTheme::GetDPIRatio(nsIFrame
* aFrame
,
175 StyleAppearance aAppearance
) -> DPIRatio
{
176 return GetDPIRatio(aFrame
->PresContext(), aAppearance
);
180 bool nsNativeBasicTheme::IsDateTimeResetButton(nsIFrame
* aFrame
) {
185 nsIFrame
* parent
= aFrame
->GetParent();
186 if (parent
&& (parent
= parent
->GetParent()) &&
187 (parent
= parent
->GetParent())) {
188 nsDateTimeControlFrame
* dateTimeFrame
= do_QueryFrame(parent
);
197 bool nsNativeBasicTheme::IsColorPickerButton(nsIFrame
* aFrame
) {
198 nsColorControlFrame
* colorPickerButton
= do_QueryFrame(aFrame
);
199 return colorPickerButton
;
203 LayoutDeviceRect
nsNativeBasicTheme::FixAspectRatio(
204 const LayoutDeviceRect
& aRect
) {
205 // Checkbox and radio need to preserve aspect-ratio for compat.
206 LayoutDeviceRect
rect(aRect
);
207 if (rect
.width
== rect
.height
) {
211 if (rect
.width
> rect
.height
) {
212 auto diff
= rect
.width
- rect
.height
;
213 rect
.width
= rect
.height
;
216 auto diff
= rect
.height
- rect
.width
;
217 rect
.height
= rect
.width
;
224 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeCheckboxColors(
225 const EventStates
& aState
, StyleAppearance aAppearance
) {
226 MOZ_ASSERT(aAppearance
== StyleAppearance::Checkbox
||
227 aAppearance
== StyleAppearance::Radio
);
229 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
230 bool isPressed
= !isDisabled
&& aState
.HasAllStates(NS_EVENT_STATE_HOVER
|
231 NS_EVENT_STATE_ACTIVE
);
232 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
233 bool isChecked
= aState
.HasState(NS_EVENT_STATE_CHECKED
);
234 bool isIndeterminate
= aAppearance
== StyleAppearance::Checkbox
&&
235 aState
.HasState(NS_EVENT_STATE_INDETERMINATE
);
237 sRGBColor backgroundColor
= sColorWhite
;
238 sRGBColor borderColor
= sColorGrey40
;
240 if (isChecked
|| isIndeterminate
) {
241 backgroundColor
= borderColor
= sColorGrey40Alpha50
;
243 backgroundColor
= sColorWhiteAlpha50
;
244 borderColor
= sColorGrey40Alpha50
;
247 if (isChecked
|| isIndeterminate
) {
248 const auto& color
= isPressed
? sAccentColorDarker
249 : isHovered
? sAccentColorDark
251 backgroundColor
= borderColor
= color
;
252 } else if (isPressed
) {
253 backgroundColor
= sColorGrey20
;
254 borderColor
= sColorGrey60
;
255 } else if (isHovered
) {
256 backgroundColor
= sColorWhite
;
257 borderColor
= sColorGrey50
;
259 backgroundColor
= sColorWhite
;
260 borderColor
= sColorGrey40
;
264 return std::make_pair(backgroundColor
, borderColor
);
267 sRGBColor
nsNativeBasicTheme::ComputeCheckmarkColor(const EventStates
& aState
) {
268 if (aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
269 return sColorWhiteAlpha50
;
271 return sAccentColorForeground
;
274 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeRadioCheckmarkColors(
275 const EventStates
& aState
) {
276 auto [unusedColor
, checkColor
] =
277 ComputeCheckboxColors(aState
, StyleAppearance::Radio
);
278 Unused
<< unusedColor
;
279 return std::make_pair(ComputeCheckmarkColor(aState
), checkColor
);
282 sRGBColor
nsNativeBasicTheme::ComputeBorderColor(const EventStates
& aState
) {
283 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
285 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
286 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
287 bool isFocused
= aState
.HasState(NS_EVENT_STATE_FOCUSRING
);
289 return sColorGrey40Alpha50
;
303 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeButtonColors(
304 const EventStates
& aState
, nsIFrame
* aFrame
) {
306 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
307 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
308 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
310 const sRGBColor
& backgroundColor
= [&] {
312 return sColorGrey10Alpha50
;
314 if (IsDateTimeResetButton(aFrame
)) {
326 const sRGBColor borderColor
= ComputeBorderColor(aState
);
327 return std::make_pair(backgroundColor
, borderColor
);
330 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeTextfieldColors(
331 const EventStates
& aState
) {
332 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
333 const sRGBColor
& backgroundColor
=
334 isDisabled
? sColorWhiteAlpha50
: sColorWhite
;
335 const sRGBColor borderColor
= ComputeBorderColor(aState
);
337 return std::make_pair(backgroundColor
, borderColor
);
340 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeRangeProgressColors(
341 const EventStates
& aState
) {
343 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
344 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
345 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
348 return std::make_pair(sColorGrey40Alpha50
, sColorGrey40Alpha50
);
350 if (isActive
|| isHovered
) {
351 return std::make_pair(sAccentColorDark
, sAccentColorDarker
);
353 return std::make_pair(sAccentColor
, sAccentColorDark
);
356 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeRangeTrackColors(
357 const EventStates
& aState
) {
359 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
360 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
361 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
364 return std::make_pair(sColorGrey10Alpha50
, sColorGrey40Alpha50
);
366 if (isActive
|| isHovered
) {
367 return std::make_pair(sColorGrey20
, sColorGrey50
);
369 return std::make_pair(sColorGrey10
, sColorGrey40
);
372 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeRangeThumbColors(
373 const EventStates
& aState
) {
375 aState
.HasAllStates(NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
);
376 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
377 bool isHovered
= !isDisabled
&& aState
.HasState(NS_EVENT_STATE_HOVER
);
379 const sRGBColor
& backgroundColor
= [&] {
381 return sColorGrey50Alpha50
;
392 const sRGBColor borderColor
= sColorWhite
;
394 return std::make_pair(backgroundColor
, borderColor
);
397 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeProgressColors() {
398 return std::make_pair(sAccentColor
, sAccentColorDark
);
401 std::pair
<sRGBColor
, sRGBColor
>
402 nsNativeBasicTheme::ComputeProgressTrackColors() {
403 return std::make_pair(sColorGrey10
, sColorGrey40
);
406 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeMeterchunkColors(
407 const EventStates
& aMeterState
) {
408 sRGBColor borderColor
= sColorMeterGreen20
;
409 sRGBColor chunkColor
= sColorMeterGreen10
;
411 if (aMeterState
.HasState(NS_EVENT_STATE_SUB_OPTIMUM
)) {
412 borderColor
= sColorMeterYellow20
;
413 chunkColor
= sColorMeterYellow10
;
414 } else if (aMeterState
.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM
)) {
415 borderColor
= sColorMeterRed20
;
416 chunkColor
= sColorMeterRed10
;
419 return std::make_pair(chunkColor
, borderColor
);
422 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeMeterTrackColors() {
423 return std::make_pair(sColorGrey10
, sColorGrey40
);
426 sRGBColor
nsNativeBasicTheme::ComputeMenulistArrowButtonColor(
427 const EventStates
& aState
) {
428 bool isDisabled
= aState
.HasState(NS_EVENT_STATE_DISABLED
);
429 return isDisabled
? sColorGrey60Alpha50
: sColorGrey60
;
432 std::array
<sRGBColor
, 3> nsNativeBasicTheme::ComputeFocusRectColors() {
433 return {sAccentColor
, sColorWhiteAlpha80
, sAccentColorLight
};
436 std::pair
<sRGBColor
, sRGBColor
> nsNativeBasicTheme::ComputeScrollbarColors(
437 nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
438 const EventStates
& aDocumentState
) {
439 const nsStyleUI
* ui
= aStyle
.StyleUI();
441 if (ui
->mScrollbarColor
.IsColors()) {
442 color
= ui
->mScrollbarColor
.AsColors().track
.CalcColor(aStyle
);
443 } else if (aDocumentState
.HasAllStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE
)) {
444 color
= LookAndFeel::GetColor(LookAndFeel::ColorID::ThemedScrollbarInactive
,
445 sScrollbarColor
.ToABGR());
447 color
= LookAndFeel::GetColor(LookAndFeel::ColorID::ThemedScrollbar
,
448 sScrollbarColor
.ToABGR());
450 return std::make_pair(gfx::sRGBColor::FromABGR(color
), sScrollbarBorderColor
);
453 nscolor
nsNativeBasicTheme::AdjustUnthemedScrollbarThumbColor(
454 nscolor aFaceColor
, EventStates aStates
) {
455 // In Windows 10, scrollbar thumb has the following colors:
457 // State | Color | Luminance
458 // -------+----------+----------
459 // Normal | Gray 205 | 61.0%
460 // Hover | Gray 166 | 38.1%
461 // Active | Gray 96 | 11.7%
463 // This function is written based on the ratios between the values.
464 bool isActive
= aStates
.HasState(NS_EVENT_STATE_ACTIVE
);
465 bool isHover
= aStates
.HasState(NS_EVENT_STATE_HOVER
);
466 if (!isActive
&& !isHover
) {
469 float luminance
= RelativeLuminanceUtils::Compute(aFaceColor
);
472 luminance
= ScaleLuminanceBy(luminance
, 0.192f
);
475 luminance
= ScaleLuminanceBy(luminance
, 0.625f
);
477 return RelativeLuminanceUtils::Adjust(aFaceColor
, luminance
);
480 sRGBColor
nsNativeBasicTheme::ComputeScrollbarThumbColor(
481 nsIFrame
* aFrame
, const ComputedStyle
& aStyle
,
482 const EventStates
& aElementState
, const EventStates
& aDocumentState
) {
483 const nsStyleUI
* ui
= aStyle
.StyleUI();
485 if (ui
->mScrollbarColor
.IsColors()) {
486 color
= AdjustUnthemedScrollbarThumbColor(
487 ui
->mScrollbarColor
.AsColors().thumb
.CalcColor(aStyle
), aElementState
);
488 } else if (aDocumentState
.HasAllStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE
)) {
489 color
= LookAndFeel::GetColor(
490 LookAndFeel::ColorID::ThemedScrollbarThumbInactive
,
491 sScrollbarThumbColor
.ToABGR());
492 } else if (aElementState
.HasAllStates(NS_EVENT_STATE_ACTIVE
)) {
494 LookAndFeel::GetColor(LookAndFeel::ColorID::ThemedScrollbarThumbActive
,
495 sScrollbarThumbColorActive
.ToABGR());
496 } else if (aElementState
.HasAllStates(NS_EVENT_STATE_HOVER
)) {
498 LookAndFeel::GetColor(LookAndFeel::ColorID::ThemedScrollbarThumbHover
,
499 sScrollbarThumbColorHover
.ToABGR());
501 color
= LookAndFeel::GetColor(LookAndFeel::ColorID::ThemedScrollbarThumb
,
502 sScrollbarThumbColor
.ToABGR());
504 return gfx::sRGBColor::FromABGR(color
);
507 std::array
<sRGBColor
, 3> nsNativeBasicTheme::ComputeScrollbarButtonColors(
508 nsIFrame
* aFrame
, StyleAppearance aAppearance
, const ComputedStyle
& aStyle
,
509 const EventStates
& aElementState
, const EventStates
& aDocumentState
) {
510 bool isActive
= aElementState
.HasState(NS_EVENT_STATE_ACTIVE
);
511 bool isHovered
= aElementState
.HasState(NS_EVENT_STATE_HOVER
);
513 bool hasCustomColor
= aStyle
.StyleUI()->mScrollbarColor
.IsColors();
514 sRGBColor buttonColor
;
515 if (hasCustomColor
) {
516 // When scrollbar-color is in use, use the thumb color for the button.
517 buttonColor
= ComputeScrollbarThumbColor(aFrame
, aStyle
, aElementState
,
519 } else if (isActive
) {
520 buttonColor
= sScrollbarButtonActiveColor
;
521 } else if (!hasCustomColor
&& isHovered
) {
522 buttonColor
= sScrollbarButtonHoverColor
;
524 buttonColor
= sScrollbarColor
;
527 sRGBColor arrowColor
;
528 if (hasCustomColor
) {
529 // When scrollbar-color is in use, derive the arrow color from the button
531 nscolor bg
= buttonColor
.ToABGR();
532 bool darken
= NS_GetLuminosity(bg
) >= NS_MAX_LUMINOSITY
/ 2;
534 float c
= darken
? 0.0f
: 1.0f
;
535 arrowColor
= sRGBColor(c
, c
, c
);
537 uint8_t c
= darken
? 0 : 255;
539 sRGBColor::FromABGR(NS_ComposeColors(bg
, NS_RGBA(c
, c
, c
, 160)));
541 } else if (isActive
) {
542 arrowColor
= sScrollbarArrowColorActive
;
543 } else if (isHovered
) {
544 arrowColor
= sScrollbarArrowColorHover
;
546 arrowColor
= sScrollbarArrowColor
;
549 return {buttonColor
, arrowColor
, sScrollbarBorderColor
};
552 static already_AddRefed
<Path
> GetFocusStrokePath(
553 DrawTarget
* aDrawTarget
, LayoutDeviceRect
& aFocusRect
,
554 LayoutDeviceCoord aOffset
, const LayoutDeviceCoord aRadius
,
555 LayoutDeviceCoord aFocusWidth
) {
556 RectCornerRadii
radii(aRadius
, aRadius
, aRadius
, aRadius
);
557 aFocusRect
.Inflate(aOffset
);
559 LayoutDeviceRect
focusRect(aFocusRect
);
560 // Deflate the rect by half the border width, so that the middle of the
561 // stroke fills exactly the area we want to fill and not more.
562 focusRect
.Deflate(aFocusWidth
* 0.5f
);
564 return MakePathForRoundedRect(*aDrawTarget
, focusRect
.ToUnknownRect(), radii
);
567 static const CSSCoord kInnerFocusOutlineWidth
= 2.0f
;
569 void nsNativeBasicTheme::PaintRoundedFocusRect(DrawTarget
* aDrawTarget
,
570 const LayoutDeviceRect
& aRect
,
574 // NOTE(emilio): If the widths or offsets here change, make sure to tweak
575 // the GetWidgetOverflow path for FocusOutline.
576 auto [innerColor
, middleColor
, outerColor
] = ComputeFocusRectColors();
578 LayoutDeviceRect
focusRect(aRect
);
580 // The focus rect is painted outside of the border area (aRect), see:
582 // data:text/html,<div style="border: 1px solid; outline: 2px solid
585 // But some controls might provide a negative offset to cover the border, if
587 LayoutDeviceCoord offset
= aOffset
* aDpiRatio
;
588 LayoutDeviceCoord strokeWidth
= kInnerFocusOutlineWidth
* aDpiRatio
;
589 focusRect
.Inflate(strokeWidth
);
591 LayoutDeviceCoord strokeRadius
= aRadius
* aDpiRatio
;
592 RefPtr
<Path
> roundedRect
= GetFocusStrokePath(aDrawTarget
, focusRect
, offset
,
593 strokeRadius
, strokeWidth
);
594 aDrawTarget
->Stroke(roundedRect
, ColorPattern(ToDeviceColor(innerColor
)),
595 StrokeOptions(strokeWidth
));
597 offset
= CSSCoord(1.0f
) * aDpiRatio
;
598 strokeRadius
+= offset
;
599 strokeWidth
= CSSCoord(1.0f
) * aDpiRatio
;
600 roundedRect
= GetFocusStrokePath(aDrawTarget
, focusRect
, offset
, strokeRadius
,
602 aDrawTarget
->Stroke(roundedRect
, ColorPattern(ToDeviceColor(middleColor
)),
603 StrokeOptions(strokeWidth
));
605 offset
= CSSCoord(2.0f
) * aDpiRatio
;
606 strokeRadius
+= offset
;
607 strokeWidth
= CSSCoord(2.0f
) * aDpiRatio
;
608 roundedRect
= GetFocusStrokePath(aDrawTarget
, focusRect
, offset
, strokeRadius
,
610 aDrawTarget
->Stroke(roundedRect
, ColorPattern(ToDeviceColor(outerColor
)),
611 StrokeOptions(strokeWidth
));
614 void nsNativeBasicTheme::PaintRoundedRectWithRadius(
615 DrawTarget
* aDrawTarget
, const LayoutDeviceRect
& aRect
,
616 const sRGBColor
& aBackgroundColor
, const sRGBColor
& aBorderColor
,
617 CSSCoord aBorderWidth
, CSSCoord aRadius
, DPIRatio aDpiRatio
) {
618 const LayoutDeviceCoord
borderWidth(SnapBorderWidth(aBorderWidth
, aDpiRatio
));
620 LayoutDeviceRect
rect(aRect
);
621 // Deflate the rect by half the border width, so that the middle of the
622 // stroke fills exactly the area we want to fill and not more.
623 rect
.Deflate(borderWidth
* 0.5f
);
625 LayoutDeviceCoord
radius(aRadius
* aDpiRatio
);
626 // Fix up the radius if it's too large with the rect we're going to paint.
628 LayoutDeviceCoord min
= std::min(rect
.width
, rect
.height
);
629 if (radius
* 2.0f
> min
) {
634 RectCornerRadii
radii(radius
, radius
, radius
, radius
);
635 RefPtr
<Path
> roundedRect
=
636 MakePathForRoundedRect(*aDrawTarget
, rect
.ToUnknownRect(), radii
);
638 aDrawTarget
->Fill(roundedRect
, ColorPattern(ToDeviceColor(aBackgroundColor
)));
639 aDrawTarget
->Stroke(roundedRect
, ColorPattern(ToDeviceColor(aBorderColor
)),
640 StrokeOptions(borderWidth
));
643 void nsNativeBasicTheme::PaintCheckboxControl(DrawTarget
* aDrawTarget
,
644 const LayoutDeviceRect
& aRect
,
645 const EventStates
& aState
,
646 DPIRatio aDpiRatio
) {
647 const CSSCoord radius
= 2.0f
;
648 auto [backgroundColor
, borderColor
] =
649 ComputeCheckboxColors(aState
, StyleAppearance::Checkbox
);
650 PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
651 kCheckboxRadioBorderWidth
, radius
, aDpiRatio
);
653 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
654 PaintRoundedFocusRect(aDrawTarget
, aRect
, aDpiRatio
, 5.0f
, 1.0f
);
659 // Returns the right scale for points in a 14x14 unit box centered at 0x0 to
660 // fill aRect in the smaller dimension.
661 static float ScaleToFillRect(const LayoutDeviceRect
& aRect
) {
662 static constexpr float kPathPointsScale
= 14.0f
;
663 return std::min(aRect
.width
, aRect
.height
) / kPathPointsScale
;
666 void nsNativeBasicTheme::PaintCheckMark(DrawTarget
* aDrawTarget
,
667 const LayoutDeviceRect
& aRect
,
668 const EventStates
& aState
) {
669 // Points come from the coordinates on a 14X14 unit box centered at 0,0
670 const float checkPolygonX
[] = {-4.5f
, -1.5f
, -0.5f
, 5.0f
, 4.75f
,
671 3.5f
, -0.5f
, -1.5f
, -3.5f
};
672 const float checkPolygonY
[] = {0.5f
, 4.0f
, 4.0f
, -2.5f
, -4.0f
,
673 -4.0f
, 1.0f
, 1.25f
, -1.0f
};
674 const int32_t checkNumPoints
= sizeof(checkPolygonX
) / sizeof(float);
675 const float scale
= ScaleToFillRect(aRect
);
676 auto center
= aRect
.Center().ToUnknownPoint();
678 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
679 Point p
= center
+ Point(checkPolygonX
[0] * scale
, checkPolygonY
[0] * scale
);
681 for (int32_t i
= 1; i
< checkNumPoints
; i
++) {
682 p
= center
+ Point(checkPolygonX
[i
] * scale
, checkPolygonY
[i
] * scale
);
685 RefPtr
<Path
> path
= builder
->Finish();
687 sRGBColor fillColor
= ComputeCheckmarkColor(aState
);
688 aDrawTarget
->Fill(path
, ColorPattern(ToDeviceColor(fillColor
)));
691 void nsNativeBasicTheme::PaintIndeterminateMark(DrawTarget
* aDrawTarget
,
692 const LayoutDeviceRect
& aRect
,
693 const EventStates
& aState
) {
694 const CSSCoord borderWidth
= 2.0f
;
695 const float scale
= ScaleToFillRect(aRect
);
697 Rect rect
= aRect
.ToUnknownRect();
698 rect
.y
+= (rect
.height
/ 2) - (borderWidth
* scale
/ 2);
699 rect
.height
= borderWidth
* scale
;
700 rect
.x
+= (borderWidth
* scale
) + (borderWidth
* scale
/ 8);
701 rect
.width
-= ((borderWidth
* scale
) + (borderWidth
* scale
/ 8)) * 2;
703 sRGBColor fillColor
= ComputeCheckmarkColor(aState
);
704 aDrawTarget
->FillRect(rect
, ColorPattern(ToDeviceColor(fillColor
)));
707 void nsNativeBasicTheme::PaintStrokedEllipse(DrawTarget
* aDrawTarget
,
708 const LayoutDeviceRect
& aRect
,
709 const sRGBColor
& aBackgroundColor
,
710 const sRGBColor
& aBorderColor
,
711 const CSSCoord aBorderWidth
,
712 DPIRatio aDpiRatio
) {
713 const LayoutDeviceCoord
borderWidth(aBorderWidth
* aDpiRatio
);
714 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
716 // Deflate for the same reason as PaintRoundedRectWithRadius. Note that the
717 // size is the diameter, so we just shrink by the border width once.
718 auto size
= aRect
.Size() - LayoutDeviceSize(borderWidth
, borderWidth
);
719 AppendEllipseToPath(builder
, aRect
.Center().ToUnknownPoint(),
720 size
.ToUnknownSize());
721 RefPtr
<Path
> ellipse
= builder
->Finish();
723 aDrawTarget
->Fill(ellipse
, ColorPattern(ToDeviceColor(aBackgroundColor
)));
724 aDrawTarget
->Stroke(ellipse
, ColorPattern(ToDeviceColor(aBorderColor
)),
725 StrokeOptions(borderWidth
));
728 void nsNativeBasicTheme::PaintEllipseShadow(DrawTarget
* aDrawTarget
,
729 const LayoutDeviceRect
& aRect
,
731 const CSSPoint
& aShadowOffset
,
732 CSSCoord aShadowBlurStdDev
,
733 DPIRatio aDpiRatio
) {
734 Float stdDev
= aShadowBlurStdDev
* aDpiRatio
;
735 Point offset
= (aShadowOffset
* aDpiRatio
).ToUnknownPoint();
737 RefPtr
<FilterNode
> blurFilter
=
738 aDrawTarget
->CreateFilter(FilterType::GAUSSIAN_BLUR
);
743 blurFilter
->SetAttribute(ATT_GAUSSIAN_BLUR_STD_DEVIATION
, stdDev
);
746 gfxAlphaBoxBlur::CalculateBlurRadius(gfxPoint(stdDev
, stdDev
));
747 Rect inflatedRect
= aRect
.ToUnknownRect();
748 inflatedRect
.Inflate(inflation
.width
, inflation
.height
);
749 Rect sourceRectInFilterSpace
=
750 inflatedRect
- aRect
.TopLeft().ToUnknownPoint();
751 Point destinationPointOfSourceRect
= inflatedRect
.TopLeft() + offset
;
753 IntSize dtSize
= RoundedToInt(aRect
.Size().ToUnknownSize());
754 RefPtr
<DrawTarget
> ellipseDT
= aDrawTarget
->CreateSimilarDrawTargetForFilter(
755 dtSize
, SurfaceFormat::A8
, blurFilter
, blurFilter
,
756 sourceRectInFilterSpace
, destinationPointOfSourceRect
);
761 RefPtr
<Path
> ellipse
= MakePathForEllipse(
762 *ellipseDT
, (aRect
- aRect
.TopLeft()).Center().ToUnknownPoint(),
763 aRect
.Size().ToUnknownSize());
764 ellipseDT
->Fill(ellipse
,
765 ColorPattern(DeviceColor(0.0f
, 0.0f
, 0.0f
, aShadowAlpha
)));
766 RefPtr
<SourceSurface
> ellipseSurface
= ellipseDT
->Snapshot();
768 blurFilter
->SetInput(IN_GAUSSIAN_BLUR_IN
, ellipseSurface
);
769 aDrawTarget
->DrawFilter(blurFilter
, sourceRectInFilterSpace
,
770 destinationPointOfSourceRect
);
773 void nsNativeBasicTheme::PaintRadioControl(DrawTarget
* aDrawTarget
,
774 const LayoutDeviceRect
& aRect
,
775 const EventStates
& aState
,
776 DPIRatio aDpiRatio
) {
777 const CSSCoord borderWidth
= 2.0f
;
778 auto [backgroundColor
, borderColor
] =
779 ComputeCheckboxColors(aState
, StyleAppearance::Radio
);
781 PaintStrokedEllipse(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
782 borderWidth
, aDpiRatio
);
784 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
785 PaintRoundedFocusRect(aDrawTarget
, aRect
, aDpiRatio
, 5.0f
, 1.0f
);
789 void nsNativeBasicTheme::PaintRadioCheckmark(DrawTarget
* aDrawTarget
,
790 const LayoutDeviceRect
& aRect
,
791 const EventStates
& aState
,
792 DPIRatio aDpiRatio
) {
793 const CSSCoord borderWidth
= 2.0f
;
794 const float scale
= ScaleToFillRect(aRect
);
795 auto [backgroundColor
, checkColor
] = ComputeRadioCheckmarkColors(aState
);
797 LayoutDeviceRect
rect(aRect
);
798 rect
.Deflate(borderWidth
* scale
);
800 PaintStrokedEllipse(aDrawTarget
, rect
, checkColor
, backgroundColor
,
801 borderWidth
, aDpiRatio
);
804 void nsNativeBasicTheme::PaintTextField(DrawTarget
* aDrawTarget
,
805 const LayoutDeviceRect
& aRect
,
806 const EventStates
& aState
,
807 DPIRatio aDpiRatio
) {
808 auto [backgroundColor
, borderColor
] = ComputeTextfieldColors(aState
);
810 const CSSCoord radius
= 2.0f
;
812 PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
813 kTextFieldBorderWidth
, radius
, aDpiRatio
);
815 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
816 PaintRoundedFocusRect(aDrawTarget
, aRect
, aDpiRatio
, radius
,
817 -kTextFieldBorderWidth
);
821 void nsNativeBasicTheme::PaintListbox(DrawTarget
* aDrawTarget
,
822 const LayoutDeviceRect
& aRect
,
823 const EventStates
& aState
,
824 DPIRatio aDpiRatio
) {
825 const CSSCoord radius
= 2.0f
;
826 auto [backgroundColor
, borderColor
] = ComputeTextfieldColors(aState
);
828 PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
829 kMenulistBorderWidth
, radius
, aDpiRatio
);
831 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
832 PaintRoundedFocusRect(aDrawTarget
, aRect
, aDpiRatio
, radius
,
833 -kMenulistBorderWidth
);
837 void nsNativeBasicTheme::PaintMenulist(DrawTarget
* aDrawTarget
,
838 const LayoutDeviceRect
& aRect
,
839 const EventStates
& aState
,
840 DPIRatio aDpiRatio
) {
841 const CSSCoord radius
= 4.0f
;
842 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
);
844 PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
845 kMenulistBorderWidth
, radius
, aDpiRatio
);
847 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
848 PaintRoundedFocusRect(aDrawTarget
, aRect
, aDpiRatio
, radius
,
849 -kMenulistBorderWidth
);
853 void nsNativeBasicTheme::PaintArrow(DrawTarget
* aDrawTarget
,
854 const LayoutDeviceRect
& aRect
,
855 const float aArrowPolygonX
[],
856 const float aArrowPolygonY
[],
857 const int32_t aArrowNumPoints
,
858 const sRGBColor aFillColor
) {
859 const float scale
= ScaleToFillRect(aRect
);
861 auto center
= aRect
.Center().ToUnknownPoint();
863 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
865 center
+ Point(aArrowPolygonX
[0] * scale
, aArrowPolygonY
[0] * scale
);
867 for (int32_t i
= 1; i
< aArrowNumPoints
; i
++) {
868 p
= center
+ Point(aArrowPolygonX
[i
] * scale
, aArrowPolygonY
[i
] * scale
);
871 RefPtr
<Path
> path
= builder
->Finish();
873 aDrawTarget
->Fill(path
, ColorPattern(ToDeviceColor(aFillColor
)));
876 void nsNativeBasicTheme::PaintMenulistArrowButton(nsIFrame
* aFrame
,
877 DrawTarget
* aDrawTarget
,
878 const LayoutDeviceRect
& aRect
,
879 const EventStates
& aState
) {
880 const float arrowPolygonX
[] = {-3.5f
, -0.5f
, 0.5f
, 3.5f
, 3.5f
,
881 3.0f
, 0.5f
, -0.5f
, -3.0f
, -3.5f
};
882 const float arrowPolygonY
[] = {-0.5f
, 2.5f
, 2.5f
, -0.5f
, -2.0f
,
883 -2.0f
, 1.0f
, 1.0f
, -2.0f
, -2.0f
};
884 const int32_t arrowNumPoints
= ArrayLength(arrowPolygonX
);
885 sRGBColor arrowColor
= ComputeMenulistArrowButtonColor(aState
);
886 PaintArrow(aDrawTarget
, aRect
, arrowPolygonX
, arrowPolygonY
, arrowNumPoints
,
890 void nsNativeBasicTheme::PaintSpinnerButton(nsIFrame
* aFrame
,
891 DrawTarget
* aDrawTarget
,
892 const LayoutDeviceRect
& aRect
,
893 const EventStates
& aState
,
894 StyleAppearance aAppearance
,
895 DPIRatio aDpiRatio
) {
896 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
);
898 aDrawTarget
->FillRect(aRect
.ToUnknownRect(),
899 ColorPattern(ToDeviceColor(backgroundColor
)));
901 const float arrowPolygonX
[] = {-5.25f
, -0.75f
, 0.75f
, 5.25f
, 5.25f
,
902 4.5f
, 0.75f
, -0.75f
, -4.5f
, -5.25f
};
903 const float arrowPolygonY
[] = {-1.875f
, 2.625f
, 2.625f
, -1.875f
, -4.125f
,
904 -4.125f
, 0.375f
, 0.375f
, -4.125f
, -4.125f
};
905 const int32_t arrowNumPoints
= ArrayLength(arrowPolygonX
);
906 const float scaleX
= ScaleToFillRect(aRect
);
908 aAppearance
== StyleAppearance::SpinnerDownbutton
? scaleX
: -scaleX
;
910 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
911 auto center
= aRect
.Center().ToUnknownPoint();
913 center
+ Point(arrowPolygonX
[0] * scaleX
, arrowPolygonY
[0] * scaleY
);
915 for (int32_t i
= 1; i
< arrowNumPoints
; i
++) {
916 p
= center
+ Point(arrowPolygonX
[i
] * scaleX
, arrowPolygonY
[i
] * scaleY
);
919 RefPtr
<Path
> path
= builder
->Finish();
920 aDrawTarget
->Fill(path
, ColorPattern(ToDeviceColor(borderColor
)));
923 void nsNativeBasicTheme::PaintRange(nsIFrame
* aFrame
, DrawTarget
* aDrawTarget
,
924 const LayoutDeviceRect
& aRect
,
925 const EventStates
& aState
,
926 DPIRatio aDpiRatio
, bool aHorizontal
) {
927 nsRangeFrame
* rangeFrame
= do_QueryFrame(aFrame
);
932 double progress
= rangeFrame
->GetValueAsFractionOfRange();
934 LayoutDeviceRect
thumbRect(0, 0, kMinimumRangeThumbSize
* aDpiRatio
,
935 kMinimumRangeThumbSize
* aDpiRatio
);
936 Rect overflowRect
= aRect
.ToUnknownRect();
937 overflowRect
.Inflate(CSSCoord(6.0f
) * aDpiRatio
); // See GetWidgetOverflow
938 Rect
progressClipRect(overflowRect
);
939 Rect
trackClipRect(overflowRect
);
940 const LayoutDeviceCoord verticalSize
= kRangeHeight
* aDpiRatio
;
942 rect
.height
= verticalSize
;
943 rect
.y
= aRect
.y
+ (aRect
.height
- rect
.height
) / 2;
944 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) / 2;
946 if (IsFrameRTL(aFrame
)) {
948 aRect
.x
+ (aRect
.width
- thumbRect
.width
) * (1.0 - progress
);
949 float midPoint
= thumbRect
.Center().X();
950 trackClipRect
.SetBoxX(overflowRect
.X(), midPoint
);
951 progressClipRect
.SetBoxX(midPoint
, overflowRect
.XMost());
953 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) * progress
;
954 float midPoint
= thumbRect
.Center().X();
955 progressClipRect
.SetBoxX(overflowRect
.X(), midPoint
);
956 trackClipRect
.SetBoxX(midPoint
, overflowRect
.XMost());
959 rect
.width
= verticalSize
;
960 rect
.x
= aRect
.x
+ (aRect
.width
- rect
.width
) / 2;
961 thumbRect
.x
= aRect
.x
+ (aRect
.width
- thumbRect
.width
) / 2;
963 thumbRect
.y
= aRect
.y
+ (aRect
.height
- thumbRect
.height
) * progress
;
964 float midPoint
= thumbRect
.Center().Y();
965 trackClipRect
.SetBoxY(overflowRect
.Y(), midPoint
);
966 progressClipRect
.SetBoxY(midPoint
, overflowRect
.YMost());
969 const CSSCoord borderWidth
= 1.0f
;
970 const CSSCoord radius
= 2.0f
;
972 auto [progressColor
, progressBorderColor
] =
973 ComputeRangeProgressColors(aState
);
974 auto [trackColor
, trackBorderColor
] = ComputeRangeTrackColors(aState
);
976 // Make a path that clips out the range thumb.
977 RefPtr
<PathBuilder
> builder
=
978 aDrawTarget
->CreatePathBuilder(FillRule::FILL_EVEN_ODD
);
979 AppendRectToPath(builder
, overflowRect
);
980 AppendEllipseToPath(builder
, thumbRect
.Center().ToUnknownPoint(),
981 thumbRect
.Size().ToUnknownSize());
982 RefPtr
<Path
> path
= builder
->Finish();
984 // Draw the progress and track pieces with the thumb clipped out, so that
985 // they're not visible behind the thumb even if the thumb is partially
986 // transparent (which is the case in the disabled state).
987 aDrawTarget
->PushClip(path
);
989 aDrawTarget
->PushClipRect(progressClipRect
);
990 PaintRoundedRectWithRadius(aDrawTarget
, rect
, progressColor
,
991 progressBorderColor
, borderWidth
, radius
,
993 aDrawTarget
->PopClip();
995 aDrawTarget
->PushClipRect(trackClipRect
);
996 PaintRoundedRectWithRadius(aDrawTarget
, rect
, trackColor
, trackBorderColor
,
997 borderWidth
, radius
, aDpiRatio
);
998 aDrawTarget
->PopClip();
1000 if (!aState
.HasState(NS_EVENT_STATE_DISABLED
)) {
1002 PaintEllipseShadow(aDrawTarget
, thumbRect
, 0.3f
, CSSPoint(0.0f
, 2.0f
),
1006 aDrawTarget
->PopClip();
1008 // Draw the thumb on top.
1009 const CSSCoord thumbBorderWidth
= 2.0f
;
1010 auto [thumbColor
, thumbBorderColor
] = ComputeRangeThumbColors(aState
);
1012 PaintStrokedEllipse(aDrawTarget
, thumbRect
, thumbColor
, thumbBorderColor
,
1013 thumbBorderWidth
, aDpiRatio
);
1015 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1016 PaintRoundedFocusRect(aDrawTarget
, aRect
, aDpiRatio
, radius
, 1.0f
);
1020 // TODO: Indeterminate state.
1021 void nsNativeBasicTheme::PaintProgress(
1022 nsIFrame
* aFrame
, DrawTarget
* aDrawTarget
, const LayoutDeviceRect
& aRect
,
1023 const EventStates
& aState
, DPIRatio aDpiRatio
, bool aIsMeter
, bool aBar
) {
1024 auto [backgroundColor
, borderColor
] = [&] {
1026 return aBar
? ComputeMeterTrackColors() : ComputeMeterchunkColors(aState
);
1028 return aBar
? ComputeProgressTrackColors() : ComputeProgressColors();
1031 const CSSCoord borderWidth
= 1.0f
;
1032 const CSSCoord radius
= aIsMeter
? 5.0f
: 2.0f
;
1034 LayoutDeviceRect
rect(aRect
);
1035 const LayoutDeviceCoord thickness
=
1036 (aIsMeter
? kMeterHeight
: kProgressbarHeight
) * aDpiRatio
;
1038 const bool isHorizontal
= !nsNativeTheme::IsVerticalProgress(aFrame
);
1040 // Center it vertically.
1041 rect
.y
+= (rect
.height
- thickness
) / 2;
1042 rect
.height
= thickness
;
1044 // Center it horizontally.
1045 rect
.x
+= (rect
.width
- thickness
) / 2;
1046 rect
.width
= thickness
;
1049 // This is the progress chunk, clip it to the right amount.
1051 double position
= [&] {
1053 auto* meter
= dom::HTMLMeterElement::FromNode(aFrame
->GetContent());
1057 return meter
->Value() / meter
->Max();
1059 auto* progress
= dom::HTMLProgressElement::FromNode(aFrame
->GetContent());
1063 return progress
->Value() / progress
->Max();
1065 LayoutDeviceRect clipRect
= rect
;
1067 double clipWidth
= rect
.width
* position
;
1068 clipRect
.width
= clipWidth
;
1069 if (IsFrameRTL(aFrame
)) {
1070 clipRect
.x
+= rect
.width
- clipWidth
;
1073 double clipHeight
= rect
.height
* position
;
1074 clipRect
.height
= clipHeight
;
1075 clipRect
.y
+= rect
.height
- clipHeight
;
1077 aDrawTarget
->PushClipRect(clipRect
.ToUnknownRect());
1080 PaintRoundedRectWithRadius(aDrawTarget
, rect
, backgroundColor
, borderColor
,
1081 borderWidth
, radius
, aDpiRatio
);
1084 aDrawTarget
->PopClip();
1088 void nsNativeBasicTheme::PaintButton(nsIFrame
* aFrame
, DrawTarget
* aDrawTarget
,
1089 const LayoutDeviceRect
& aRect
,
1090 const EventStates
& aState
,
1091 DPIRatio aDpiRatio
) {
1092 const CSSCoord radius
= 4.0f
;
1093 auto [backgroundColor
, borderColor
] = ComputeButtonColors(aState
, aFrame
);
1095 PaintRoundedRectWithRadius(aDrawTarget
, aRect
, backgroundColor
, borderColor
,
1096 kButtonBorderWidth
, radius
, aDpiRatio
);
1098 if (aState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1099 PaintRoundedFocusRect(aDrawTarget
, aRect
, aDpiRatio
, radius
,
1100 -kButtonBorderWidth
);
1104 void nsNativeBasicTheme::PaintScrollbarThumb(DrawTarget
* aDrawTarget
,
1105 const LayoutDeviceRect
& aRect
,
1106 bool aHorizontal
, nsIFrame
* aFrame
,
1107 const ComputedStyle
& aStyle
,
1108 const EventStates
& aElementState
,
1109 const EventStates
& aDocumentState
,
1110 DPIRatio aDpiRatio
) {
1111 sRGBColor thumbColor
=
1112 ComputeScrollbarThumbColor(aFrame
, aStyle
, aElementState
, aDocumentState
);
1114 aDrawTarget
->FillRect(aRect
.ToUnknownRect(),
1115 ColorPattern(ToDeviceColor(thumbColor
)));
1118 void nsNativeBasicTheme::PaintScrollbarTrack(DrawTarget
* aDrawTarget
,
1119 const LayoutDeviceRect
& aRect
,
1120 bool aHorizontal
, nsIFrame
* aFrame
,
1121 const ComputedStyle
& aStyle
,
1122 const EventStates
& aDocumentState
,
1123 DPIRatio aDpiRatio
) {
1124 // Draw nothing by default. Subclasses can override this.
1127 void nsNativeBasicTheme::PaintScrollbar(DrawTarget
* aDrawTarget
,
1128 const LayoutDeviceRect
& aRect
,
1129 bool aHorizontal
, nsIFrame
* aFrame
,
1130 const ComputedStyle
& aStyle
,
1131 const EventStates
& aDocumentState
,
1132 DPIRatio aDpiRatio
) {
1133 auto [scrollbarColor
, borderColor
] =
1134 ComputeScrollbarColors(aFrame
, aStyle
, aDocumentState
);
1135 aDrawTarget
->FillRect(aRect
.ToUnknownRect(),
1136 ColorPattern(ToDeviceColor(scrollbarColor
)));
1137 // FIXME(heycam): We should probably derive the border color when custom
1138 // scrollbar colors are in use too. But for now, just skip painting it,
1139 // to avoid ugliness.
1140 if (aStyle
.StyleUI()->mScrollbarColor
.IsAuto()) {
1141 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
1142 LayoutDeviceRect
strokeRect(aRect
);
1143 strokeRect
.Deflate(CSSCoord(0.5f
) * aDpiRatio
);
1144 builder
->MoveTo(strokeRect
.TopLeft().ToUnknownPoint());
1146 (aHorizontal
? strokeRect
.TopRight() : strokeRect
.BottomLeft())
1148 RefPtr
<Path
> path
= builder
->Finish();
1149 aDrawTarget
->Stroke(path
, ColorPattern(ToDeviceColor(borderColor
)),
1150 StrokeOptions(CSSCoord(1.0f
) * aDpiRatio
));
1154 void nsNativeBasicTheme::PaintScrollCorner(DrawTarget
* aDrawTarget
,
1155 const LayoutDeviceRect
& aRect
,
1157 const ComputedStyle
& aStyle
,
1158 const EventStates
& aDocumentState
,
1159 DPIRatio aDpiRatio
) {
1160 auto [scrollbarColor
, borderColor
] =
1161 ComputeScrollbarColors(aFrame
, aStyle
, aDocumentState
);
1162 Unused
<< borderColor
;
1163 aDrawTarget
->FillRect(aRect
.ToUnknownRect(),
1164 ColorPattern(ToDeviceColor(scrollbarColor
)));
1167 void nsNativeBasicTheme::PaintScrollbarButton(
1168 DrawTarget
* aDrawTarget
, StyleAppearance aAppearance
,
1169 const LayoutDeviceRect
& aRect
, nsIFrame
* aFrame
,
1170 const ComputedStyle
& aStyle
, const EventStates
& aElementState
,
1171 const EventStates
& aDocumentState
, DPIRatio aDpiRatio
) {
1172 bool hasCustomColor
= aStyle
.StyleUI()->mScrollbarColor
.IsColors();
1173 auto [buttonColor
, arrowColor
, borderColor
] = ComputeScrollbarButtonColors(
1174 aFrame
, aAppearance
, aStyle
, aElementState
, aDocumentState
);
1175 aDrawTarget
->FillRect(aRect
.ToUnknownRect(),
1176 ColorPattern(ToDeviceColor(buttonColor
)));
1178 // Start with Up arrow.
1179 float arrowPolygonX
[] = {-3.0f
, 0.0f
, 3.0f
, 3.0f
, 0.0f
, -3.0f
};
1180 float arrowPolygonY
[] = {0.4f
, -3.1f
, 0.4f
, 2.7f
, -0.8f
, 2.7f
};
1182 const int32_t arrowNumPoints
= ArrayLength(arrowPolygonX
);
1183 switch (aAppearance
) {
1184 case StyleAppearance::ScrollbarbuttonUp
:
1186 case StyleAppearance::ScrollbarbuttonDown
:
1187 for (int32_t i
= 0; i
< arrowNumPoints
; i
++) {
1188 arrowPolygonY
[i
] *= -1;
1191 case StyleAppearance::ScrollbarbuttonLeft
:
1192 for (int32_t i
= 0; i
< arrowNumPoints
; i
++) {
1193 int32_t temp
= arrowPolygonX
[i
];
1194 arrowPolygonX
[i
] = arrowPolygonY
[i
];
1195 arrowPolygonY
[i
] = temp
;
1198 case StyleAppearance::ScrollbarbuttonRight
:
1199 for (int32_t i
= 0; i
< arrowNumPoints
; i
++) {
1200 int32_t temp
= arrowPolygonX
[i
];
1201 arrowPolygonX
[i
] = arrowPolygonY
[i
] * -1;
1202 arrowPolygonY
[i
] = temp
;
1208 PaintArrow(aDrawTarget
, aRect
, arrowPolygonX
, arrowPolygonY
, arrowNumPoints
,
1211 // FIXME(heycam): We should probably derive the border color when custom
1212 // scrollbar colors are in use too. But for now, just skip painting it,
1213 // to avoid ugliness.
1214 if (!hasCustomColor
) {
1215 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
1216 builder
->MoveTo(Point(aRect
.x
, aRect
.y
));
1217 if (aAppearance
== StyleAppearance::ScrollbarbuttonUp
||
1218 aAppearance
== StyleAppearance::ScrollbarbuttonDown
) {
1219 builder
->LineTo(Point(aRect
.x
, aRect
.y
+ aRect
.height
));
1221 builder
->LineTo(Point(aRect
.x
+ aRect
.width
, aRect
.y
));
1224 RefPtr
<Path
> path
= builder
->Finish();
1225 aDrawTarget
->Stroke(path
, ColorPattern(ToDeviceColor(borderColor
)),
1226 StrokeOptions(CSSCoord(1.0f
) * aDpiRatio
));
1231 nsNativeBasicTheme::DrawWidgetBackground(gfxContext
* aContext
, nsIFrame
* aFrame
,
1232 StyleAppearance aAppearance
,
1233 const nsRect
& aRect
,
1234 const nsRect
& /* aDirtyRect */) {
1235 DrawTarget
* dt
= aContext
->GetDrawTarget();
1236 const nscoord twipsPerPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
1237 EventStates eventState
= GetContentState(aFrame
, aAppearance
);
1238 EventStates docState
= aFrame
->PresContext()->Document()->GetDocumentState();
1239 auto devPxRect
= LayoutDeviceRect::FromUnknownRect(
1240 NSRectToSnappedRect(aRect
, twipsPerPixel
, *dt
));
1242 if (aAppearance
== StyleAppearance::MozMenulistArrowButton
) {
1243 bool isHTML
= IsHTMLContent(aFrame
);
1244 nsIFrame
* parentFrame
= aFrame
->GetParent();
1245 bool isMenulist
= !isHTML
&& parentFrame
->IsMenuFrame();
1246 // HTML select and XUL menulist dropdown buttons get state from the
1248 if (isHTML
|| isMenulist
) {
1249 aFrame
= parentFrame
;
1250 eventState
= GetContentState(parentFrame
, aAppearance
);
1254 // Hack to avoid skia fuzziness: Add a dummy clip if the widget doesn't
1255 // overflow devPxRect.
1256 Maybe
<AutoClipRect
> maybeClipRect
;
1257 if (aAppearance
!= StyleAppearance::FocusOutline
&&
1258 aAppearance
!= StyleAppearance::Range
&&
1259 !eventState
.HasState(NS_EVENT_STATE_FOCUSRING
)) {
1260 maybeClipRect
.emplace(*dt
, devPxRect
);
1263 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1265 switch (aAppearance
) {
1266 case StyleAppearance::Radio
: {
1267 auto rect
= FixAspectRatio(devPxRect
);
1268 PaintRadioControl(dt
, rect
, eventState
, dpiRatio
);
1269 if (IsSelected(aFrame
)) {
1270 PaintRadioCheckmark(dt
, rect
, eventState
, dpiRatio
);
1274 case StyleAppearance::Checkbox
: {
1275 auto rect
= FixAspectRatio(devPxRect
);
1276 PaintCheckboxControl(dt
, rect
, eventState
, dpiRatio
);
1277 if (GetIndeterminate(aFrame
)) {
1278 PaintIndeterminateMark(dt
, rect
, eventState
);
1279 } else if (IsChecked(aFrame
)) {
1280 PaintCheckMark(dt
, rect
, eventState
);
1284 case StyleAppearance::Textarea
:
1285 case StyleAppearance::Textfield
:
1286 case StyleAppearance::NumberInput
:
1287 PaintTextField(dt
, devPxRect
, eventState
, dpiRatio
);
1289 case StyleAppearance::Listbox
:
1290 PaintListbox(dt
, devPxRect
, eventState
, dpiRatio
);
1292 case StyleAppearance::MenulistButton
:
1293 case StyleAppearance::Menulist
:
1294 PaintMenulist(dt
, devPxRect
, eventState
, dpiRatio
);
1296 case StyleAppearance::MozMenulistArrowButton
:
1297 PaintMenulistArrowButton(aFrame
, dt
, devPxRect
, eventState
);
1299 case StyleAppearance::SpinnerUpbutton
:
1300 case StyleAppearance::SpinnerDownbutton
:
1301 PaintSpinnerButton(aFrame
, dt
, devPxRect
, eventState
, aAppearance
,
1304 case StyleAppearance::Range
:
1305 PaintRange(aFrame
, dt
, devPxRect
, eventState
, dpiRatio
,
1306 IsRangeHorizontal(aFrame
));
1308 case StyleAppearance::RangeThumb
:
1309 // Painted as part of StyleAppearance::Range.
1311 case StyleAppearance::ProgressBar
:
1312 PaintProgress(aFrame
, dt
, devPxRect
, eventState
, dpiRatio
,
1313 /* aMeter = */ false, /* aBar = */ true);
1315 case StyleAppearance::Progresschunk
:
1316 if (nsProgressFrame
* f
= do_QueryFrame(aFrame
->GetParent())) {
1317 PaintProgress(f
, dt
, devPxRect
, f
->GetContent()->AsElement()->State(),
1318 dpiRatio
, /* aMeter = */ false, /* aBar = */ false);
1321 case StyleAppearance::Meter
:
1322 PaintProgress(aFrame
, dt
, devPxRect
, eventState
, dpiRatio
,
1323 /* aMeter = */ true, /* aBar = */ true);
1325 case StyleAppearance::Meterchunk
:
1326 if (nsMeterFrame
* f
= do_QueryFrame(aFrame
->GetParent())) {
1327 PaintProgress(f
, dt
, devPxRect
, f
->GetContent()->AsElement()->State(),
1328 dpiRatio
, /* aMeter = */ true, /* aBar = */ false);
1331 case StyleAppearance::ScrollbarthumbHorizontal
:
1332 case StyleAppearance::ScrollbarthumbVertical
: {
1334 aAppearance
== StyleAppearance::ScrollbarthumbHorizontal
;
1335 PaintScrollbarThumb(dt
, devPxRect
, isHorizontal
, aFrame
,
1336 *nsLayoutUtils::StyleForScrollbar(aFrame
), eventState
,
1337 docState
, dpiRatio
);
1340 case StyleAppearance::ScrollbartrackHorizontal
:
1341 case StyleAppearance::ScrollbartrackVertical
: {
1343 aAppearance
== StyleAppearance::ScrollbartrackHorizontal
;
1344 PaintScrollbarTrack(dt
, devPxRect
, isHorizontal
, aFrame
,
1345 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
,
1349 case StyleAppearance::ScrollbarHorizontal
:
1350 case StyleAppearance::ScrollbarVertical
: {
1351 bool isHorizontal
= aAppearance
== StyleAppearance::ScrollbarHorizontal
;
1352 PaintScrollbar(dt
, devPxRect
, isHorizontal
, aFrame
,
1353 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
,
1357 case StyleAppearance::Scrollcorner
:
1358 PaintScrollCorner(dt
, devPxRect
, aFrame
,
1359 *nsLayoutUtils::StyleForScrollbar(aFrame
), docState
,
1362 case StyleAppearance::ScrollbarbuttonUp
:
1363 case StyleAppearance::ScrollbarbuttonDown
:
1364 case StyleAppearance::ScrollbarbuttonLeft
:
1365 case StyleAppearance::ScrollbarbuttonRight
:
1366 // For scrollbar-width:thin, we don't display the buttons.
1367 if (!IsScrollbarWidthThin(aFrame
)) {
1368 PaintScrollbarButton(dt
, aAppearance
, devPxRect
, aFrame
,
1369 *nsLayoutUtils::StyleForScrollbar(aFrame
),
1370 eventState
, docState
, dpiRatio
);
1373 case StyleAppearance::Button
:
1374 PaintButton(aFrame
, dt
, devPxRect
, eventState
, dpiRatio
);
1376 case StyleAppearance::FocusOutline
:
1377 PaintAutoStyleOutline(aFrame
, dt
, devPxRect
, dpiRatio
);
1380 // Various appearance values are used for XUL elements. Normally these
1381 // will not be available in content documents (and thus in the content
1382 // processes where the native basic theme can be used), but tests are
1383 // run with the remote XUL pref enabled and so we can get in here. So
1384 // we just return an error rather than assert.
1385 return NS_ERROR_NOT_IMPLEMENTED
;
1391 void nsNativeBasicTheme::PaintAutoStyleOutline(nsIFrame
* aFrame
,
1393 const LayoutDeviceRect
& aRect
,
1394 DPIRatio aDpiRatio
) {
1395 auto [innerColor
, middleColor
, outerColor
] = ComputeFocusRectColors();
1396 Unused
<< middleColor
;
1397 Unused
<< outerColor
;
1399 const LayoutDeviceCoord width
= kInnerFocusOutlineWidth
* aDpiRatio
;
1400 const LayoutDeviceCoord halfWidth
= width
* 0.5f
;
1402 LayoutDeviceRect
rect(aRect
);
1403 // This is equivalent to Inflate(width), to paint the outline outside of
1404 // aRect, then Deflate(width * 0.5), to stroke at the right place.
1405 rect
.Inflate(halfWidth
);
1407 nscoord cssRadii
[8];
1408 if (!aFrame
->GetBorderRadii(cssRadii
)) {
1409 return aDt
->StrokeRect(rect
.ToUnknownRect(),
1410 ColorPattern(ToDeviceColor(innerColor
)),
1411 StrokeOptions(width
));
1414 nsPresContext
* pc
= aFrame
->PresContext();
1415 const nscoord offset
= aFrame
->StyleOutline()->mOutlineOffset
.ToAppUnits();
1416 const Float devPixelOffset
= pc
->AppUnitsToFloatDevPixels(offset
);
1418 RectCornerRadii innerRadii
;
1419 nsCSSRendering::ComputePixelRadii(cssRadii
, pc
->AppUnitsPerDevPixel(),
1422 RectCornerRadii outerRadii
;
1423 const Float widths
[4] = {
1424 halfWidth
+ devPixelOffset
, halfWidth
+ devPixelOffset
,
1425 halfWidth
+ devPixelOffset
, halfWidth
+ devPixelOffset
};
1426 nsCSSBorderRenderer::ComputeOuterRadii(innerRadii
, widths
, &outerRadii
);
1428 MakePathForRoundedRect(*aDt
, rect
.ToUnknownRect(), outerRadii
);
1429 aDt
->Stroke(path
, ColorPattern(ToDeviceColor(innerColor
)),
1430 StrokeOptions(width
));
1434 nsNativeBasicTheme::CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder&
1435 aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const
1436 mozilla::layers::StackingContextHelper& aSc,
1437 mozilla::layers::RenderRootStateManager*
1438 aManager, nsIFrame* aFrame, StyleAppearance aAppearance, const nsRect& aRect)
1442 LayoutDeviceIntMargin
nsNativeBasicTheme::GetWidgetBorder(
1443 nsDeviceContext
* aContext
, nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1444 switch (aAppearance
) {
1445 case StyleAppearance::Textfield
:
1446 case StyleAppearance::Textarea
:
1447 case StyleAppearance::NumberInput
:
1448 case StyleAppearance::Listbox
:
1449 case StyleAppearance::Menulist
:
1450 case StyleAppearance::MenulistButton
:
1451 case StyleAppearance::Button
:
1452 // Return the border size from the UA sheet, even though what we paint
1453 // doesn't actually match that. We know this is the UA sheet border
1454 // because we disable native theming when different border widths are
1455 // specified by authors, see nsNativeBasicTheme::IsWidgetStyled.
1457 // The Rounded() bit is technically redundant, but needed to appease the
1458 // type system, we should always end up with full device pixels due to
1459 // round_border_to_device_pixels at style time.
1460 return LayoutDeviceIntMargin::FromAppUnits(
1461 aFrame
->StyleBorder()->GetComputedBorder(),
1462 aFrame
->PresContext()->AppUnitsPerDevPixel())
1464 case StyleAppearance::Checkbox
:
1465 case StyleAppearance::Radio
: {
1466 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1467 LayoutDeviceIntCoord w
=
1468 SnapBorderWidth(kCheckboxRadioBorderWidth
, dpiRatio
);
1469 return LayoutDeviceIntMargin(w
, w
, w
, w
);
1472 return LayoutDeviceIntMargin();
1476 bool nsNativeBasicTheme::GetWidgetPadding(nsDeviceContext
* aContext
,
1478 StyleAppearance aAppearance
,
1479 LayoutDeviceIntMargin
* aResult
) {
1480 switch (aAppearance
) {
1481 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1482 // and have a meaningful baseline, so they can't have
1483 // author-specified padding.
1484 case StyleAppearance::Radio
:
1485 case StyleAppearance::Checkbox
:
1486 aResult
->SizeTo(0, 0, 0, 0);
1494 static int GetScrollbarButtonCount() {
1495 int32_t buttons
= LookAndFeel::GetInt(LookAndFeel::IntID::ScrollArrowStyle
);
1496 return CountPopulation32(static_cast<uint32_t>(buttons
));
1499 bool nsNativeBasicTheme::GetWidgetOverflow(nsDeviceContext
* aContext
,
1501 StyleAppearance aAppearance
,
1502 nsRect
* aOverflowRect
) {
1503 nsIntMargin overflow
;
1504 switch (aAppearance
) {
1505 case StyleAppearance::FocusOutline
:
1506 // 2px * one segment
1507 overflow
.SizeTo(2, 2, 2, 2);
1509 case StyleAppearance::Radio
:
1510 case StyleAppearance::Checkbox
:
1511 case StyleAppearance::Range
:
1512 overflow
.SizeTo(6, 6, 6, 6);
1514 case StyleAppearance::Textarea
:
1515 case StyleAppearance::Textfield
:
1516 case StyleAppearance::NumberInput
:
1517 case StyleAppearance::Listbox
:
1518 case StyleAppearance::MenulistButton
:
1519 case StyleAppearance::Menulist
:
1520 case StyleAppearance::Button
:
1521 // 2px for each segment, plus 1px separation, but we paint 1px inside
1522 // the border area so 4px overflow.
1523 overflow
.SizeTo(4, 4, 4, 4);
1529 // TODO: This should convert from device pixels to app units, not from CSS
1530 // pixels. And it should take the dpi ratio into account.
1531 // Using CSS pixels can cause the overflow to be too small if the page is
1533 aOverflowRect
->Inflate(nsMargin(CSSPixel::ToAppUnits(overflow
.top
),
1534 CSSPixel::ToAppUnits(overflow
.right
),
1535 CSSPixel::ToAppUnits(overflow
.bottom
),
1536 CSSPixel::ToAppUnits(overflow
.left
)));
1541 auto nsNativeBasicTheme::GetScrollbarSizes(nsPresContext
* aPresContext
,
1542 StyleScrollbarWidth aWidth
, Overlay
)
1544 CSSCoord size
= aWidth
== StyleScrollbarWidth::Thin
1545 ? kMinimumThinScrollbarSize
1546 : kMinimumScrollbarSize
;
1547 LayoutDeviceIntCoord s
=
1548 (size
* GetDPIRatioForScrollbarPart(aPresContext
)).Rounded();
1552 nscoord
nsNativeBasicTheme::GetCheckboxRadioPrefSize() {
1553 return CSSPixel::ToAppUnits(10);
1557 nsNativeBasicTheme::GetMinimumWidgetSize(nsPresContext
* aPresContext
,
1559 StyleAppearance aAppearance
,
1560 LayoutDeviceIntSize
* aResult
,
1561 bool* aIsOverridable
) {
1562 DPIRatio dpiRatio
= GetDPIRatio(aFrame
, aAppearance
);
1564 aResult
->width
= aResult
->height
= 0;
1565 *aIsOverridable
= true;
1567 switch (aAppearance
) {
1568 case StyleAppearance::Button
:
1569 if (IsColorPickerButton(aFrame
)) {
1570 aResult
->height
= (kMinimumColorPickerHeight
* dpiRatio
).Rounded();
1573 case StyleAppearance::RangeThumb
:
1574 aResult
->SizeTo((kMinimumRangeThumbSize
* dpiRatio
).Rounded(),
1575 (kMinimumRangeThumbSize
* dpiRatio
).Rounded());
1577 case StyleAppearance::MozMenulistArrowButton
:
1578 aResult
->width
= (kMinimumDropdownArrowButtonWidth
* dpiRatio
).Rounded();
1580 case StyleAppearance::SpinnerUpbutton
:
1581 case StyleAppearance::SpinnerDownbutton
:
1582 aResult
->width
= (kMinimumSpinnerButtonWidth
* dpiRatio
).Rounded();
1583 aResult
->height
= (kMinimumSpinnerButtonHeight
* dpiRatio
).Rounded();
1585 case StyleAppearance::ScrollbarbuttonUp
:
1586 case StyleAppearance::ScrollbarbuttonDown
:
1587 case StyleAppearance::ScrollbarbuttonLeft
:
1588 case StyleAppearance::ScrollbarbuttonRight
:
1589 // For scrollbar-width:thin, we don't display the buttons.
1590 if (IsScrollbarWidthThin(aFrame
)) {
1591 aResult
->SizeTo(0, 0);
1595 case StyleAppearance::ScrollbarthumbVertical
:
1596 case StyleAppearance::ScrollbarthumbHorizontal
:
1597 case StyleAppearance::ScrollbarVertical
:
1598 case StyleAppearance::ScrollbarHorizontal
:
1599 case StyleAppearance::ScrollbartrackHorizontal
:
1600 case StyleAppearance::ScrollbartrackVertical
:
1601 case StyleAppearance::Scrollcorner
: {
1602 auto* style
= nsLayoutUtils::StyleForScrollbar(aFrame
);
1603 auto width
= style
->StyleUIReset()->mScrollbarWidth
;
1604 auto sizes
= GetScrollbarSizes(aPresContext
, width
, Overlay::No
);
1605 MOZ_ASSERT(sizes
.mHorizontal
== sizes
.mVertical
);
1606 aResult
->SizeTo(sizes
.mHorizontal
, sizes
.mHorizontal
);
1607 if (width
!= StyleScrollbarWidth::Thin
) {
1608 // If the scrollbar has any buttons, then we increase the minimum
1609 // size so that they fit too.
1611 // FIXME(heycam): We should probably ensure that the thumb disappears
1612 // if a scrollbar is big enough to fit the buttons but not the thumb,
1613 // which is what the Windows native theme does. If we do that, then
1614 // the minimum size here needs to be reduced accordingly.
1615 switch (aAppearance
) {
1616 case StyleAppearance::ScrollbarHorizontal
:
1617 aResult
->width
*= GetScrollbarButtonCount() + 1;
1619 case StyleAppearance::ScrollbarVertical
:
1620 aResult
->height
*= GetScrollbarButtonCount() + 1;
1635 nsITheme::Transparency
nsNativeBasicTheme::GetWidgetTransparency(
1636 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1637 return eUnknownTransparency
;
1641 nsNativeBasicTheme::WidgetStateChanged(nsIFrame
* aFrame
,
1642 StyleAppearance aAppearance
,
1643 nsAtom
* aAttribute
, bool* aShouldRepaint
,
1644 const nsAttrValue
* aOldValue
) {
1646 // Hover/focus/active changed. Always repaint.
1647 *aShouldRepaint
= true;
1649 // Check the attribute to see if it's relevant.
1650 // disabled, checked, dlgtype, default, etc.
1651 *aShouldRepaint
= false;
1652 if ((aAttribute
== nsGkAtoms::disabled
) ||
1653 (aAttribute
== nsGkAtoms::checked
) ||
1654 (aAttribute
== nsGkAtoms::selected
) ||
1655 (aAttribute
== nsGkAtoms::visuallyselected
) ||
1656 (aAttribute
== nsGkAtoms::menuactive
) ||
1657 (aAttribute
== nsGkAtoms::sortDirection
) ||
1658 (aAttribute
== nsGkAtoms::focused
) ||
1659 (aAttribute
== nsGkAtoms::_default
) ||
1660 (aAttribute
== nsGkAtoms::open
) || (aAttribute
== nsGkAtoms::hover
)) {
1661 *aShouldRepaint
= true;
1669 nsNativeBasicTheme::ThemeChanged() { return NS_OK
; }
1671 bool nsNativeBasicTheme::WidgetAppearanceDependsOnWindowFocus(
1672 StyleAppearance aAppearance
) {
1673 return IsWidgetScrollbarPart(aAppearance
);
1676 nsITheme::ThemeGeometryType
nsNativeBasicTheme::ThemeGeometryTypeForWidget(
1677 nsIFrame
* aFrame
, StyleAppearance aAppearance
) {
1678 return eThemeGeometryTypeUnknown
;
1681 bool nsNativeBasicTheme::ThemeSupportsWidget(nsPresContext
* aPresContext
,
1683 StyleAppearance aAppearance
) {
1684 switch (aAppearance
) {
1685 case StyleAppearance::Radio
:
1686 case StyleAppearance::Checkbox
:
1687 case StyleAppearance::FocusOutline
:
1688 case StyleAppearance::Textarea
:
1689 case StyleAppearance::Textfield
:
1690 case StyleAppearance::Range
:
1691 case StyleAppearance::RangeThumb
:
1692 case StyleAppearance::ProgressBar
:
1693 case StyleAppearance::Progresschunk
:
1694 case StyleAppearance::Meter
:
1695 case StyleAppearance::Meterchunk
:
1696 case StyleAppearance::ScrollbarbuttonUp
:
1697 case StyleAppearance::ScrollbarbuttonDown
:
1698 case StyleAppearance::ScrollbarbuttonLeft
:
1699 case StyleAppearance::ScrollbarbuttonRight
:
1700 case StyleAppearance::ScrollbarthumbHorizontal
:
1701 case StyleAppearance::ScrollbarthumbVertical
:
1702 case StyleAppearance::ScrollbartrackHorizontal
:
1703 case StyleAppearance::ScrollbartrackVertical
:
1704 case StyleAppearance::ScrollbarHorizontal
:
1705 case StyleAppearance::ScrollbarVertical
:
1706 case StyleAppearance::Scrollcorner
:
1707 case StyleAppearance::Button
:
1708 case StyleAppearance::Listbox
:
1709 case StyleAppearance::Menulist
:
1710 case StyleAppearance::MenulistButton
:
1711 case StyleAppearance::NumberInput
:
1712 case StyleAppearance::MozMenulistArrowButton
:
1713 case StyleAppearance::SpinnerUpbutton
:
1714 case StyleAppearance::SpinnerDownbutton
:
1715 return !IsWidgetStyled(aPresContext
, aFrame
, aAppearance
);
1721 bool nsNativeBasicTheme::WidgetIsContainer(StyleAppearance aAppearance
) {
1722 switch (aAppearance
) {
1723 case StyleAppearance::MozMenulistArrowButton
:
1724 case StyleAppearance::Radio
:
1725 case StyleAppearance::Checkbox
:
1732 bool nsNativeBasicTheme::ThemeDrawsFocusForWidget(StyleAppearance aAppearance
) {
1736 bool nsNativeBasicTheme::ThemeNeedsComboboxDropmarker() { return true; }