Bug 1705605 - Implement accent-color in nsNativeBasicTheme. r=mstange
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 27 Apr 2021 10:41:00 +0000
changeset 577600 3d9daad425deccc2e8edd8db8eab4a908ef4749d
parent 577599 669ddcc621fdfc3b5537c021efc6c07bfa24d632
child 577601 a19cf8b431ce85294abdcff91df6be63bcd26f0e
push id38410
push usernbeleuzu@mozilla.com
push dateTue, 27 Apr 2021 15:52:11 +0000
treeherdermozilla-central@a765064201f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1705605
milestone90.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1705605 - Implement accent-color in nsNativeBasicTheme. r=mstange This is a new addition for CSS UI Level 4: https://drafts.csswg.org/css-ui-4/#widget-accent I want to provide feedback on some spec issues, and thought it was a kinda neat thing to prototype (it also makes testing contrast and such with random GTK themes easier). For now enable for Nightly only. Differential Revision: https://phabricator.services.mozilla.com/D112312
devtools/server/actors/animation-type-longhand.js
devtools/shared/css/generated/properties-db.js
layout/style/ServoBindings.toml
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/style/test/test_transitions_per_property.html
modules/libpref/init/StaticPrefList.yaml
servo/components/style/properties/longhands/inherited_ui.mako.rs
servo/components/style/values/computed/color.rs
servo/components/style/values/generics/color.rs
servo/components/style/values/resolved/color.rs
servo/components/style/values/specified/color.rs
servo/ports/geckolib/cbindgen.toml
testing/web-platform/tests/css/css-ui/accent-color-checkbox-checked-001-notref.html
testing/web-platform/tests/css/css-ui/accent-color-checkbox-checked-001.tentative.html
testing/web-platform/tests/css/css-ui/accent-color-computed.html
testing/web-platform/tests/css/css-ui/accent-color-parsing.html
widget/nsNativeBasicTheme.cpp
widget/nsNativeBasicTheme.h
widget/nsNativeTheme.cpp
widget/nsNativeTheme.h
--- a/devtools/server/actors/animation-type-longhand.js
+++ b/devtools/server/actors/animation-type-longhand.js
@@ -239,16 +239,17 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
   [
     "color",
     new Set([
       "background-color",
       "border-bottom-color",
       "border-left-color",
       "border-right-color",
       "border-top-color",
+      "accent-color",
       "caret-color",
       "color",
       "column-rule-color",
       "flood-color",
       "-moz-font-smoothing-background-color",
       "lighting-color",
       "outline-color",
       "scrollbar-color",
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -2616,16 +2616,39 @@ exports.CSS_PROPERTIES = {
       "inherit",
       "initial",
       "none",
       "revert",
       "text",
       "unset"
     ]
   },
+  "accent-color": {
+    "isInherited": true,
+    "subproperties": [
+      "accent-color"
+    ],
+    "supports": [
+      "color"
+    ],
+    "values": [
+      "COLOR",
+      "auto",
+      "currentColor",
+      "hsl",
+      "hsla",
+      "inherit",
+      "initial",
+      "revert",
+      "rgb",
+      "rgba",
+      "transparent",
+      "unset"
+    ]
+  },
   "align-content": {
     "isInherited": false,
     "subproperties": [
       "align-content"
     ],
     "supports": [],
     "values": [
       "baseline",
@@ -2971,16 +2994,17 @@ exports.CSS_PROPERTIES = {
       "text-underline-position",
       "text-decoration-skip-ink",
       "cursor",
       "pointer-events",
       "-moz-user-input",
       "-moz-user-modify",
       "-moz-user-focus",
       "caret-color",
+      "accent-color",
       "scrollbar-color",
       "list-style-position",
       "list-style-type",
       "list-style-image",
       "quotes",
       "-moz-image-region",
       "margin-top",
       "margin-right",
@@ -10967,16 +10991,20 @@ exports.PREFERENCES = [
     "overscroll-behavior-x",
     "layout.css.overscroll-behavior.enabled"
   ],
   [
     "overscroll-behavior-y",
     "layout.css.overscroll-behavior.enabled"
   ],
   [
+    "accent-color",
+    "layout.css.accent-color.enabled"
+  ],
+  [
     "align-tracks",
     "layout.css.grid-template-masonry-value.enabled"
   ],
   [
     "backdrop-filter",
     "layout.css.backdrop-filter.enabled"
   ],
   [
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -486,16 +486,17 @@ cbindgen-types = [
     { gecko = "StyleGenericPerspective", servo = "crate::values::generics::box_::Perspective" },
     { gecko = "StyleZIndex", servo = "crate::values::computed::ZIndex" },
     { gecko = "StyleGenericZIndex", servo = "crate::values::generics::position::ZIndex" },
     { gecko = "StyleTransformOrigin", servo = "crate::values::computed::TransformOrigin" },
     { gecko = "StyleTransformStyle", servo = "crate::values::computed::TransformStyle" },
     { gecko = "StyleGenericBorderRadius", servo = "crate::values::generics::border::BorderRadius" },
     { gecko = "StyleLetterSpacing", servo = "crate::values::computed::text::LetterSpacing" },
     { gecko = "StyleGenericLineHeight", servo = "crate::values::generics::text::LineHeight" },
+    { gecko = "StyleCaretColor", servo = "crate::values::computed::color::CaretColor" },
     { gecko = "StyleContain", servo = "crate::values::computed::Contain" },
     { gecko = "StyleRestyleHint", servo = "crate::invalidation::element::restyle_hints::RestyleHint" },
     { gecko = "StyleTouchAction", servo = "crate::values::computed::TouchAction" },
     { gecko = "StyleWillChange", servo = "crate::values::specified::box_::WillChange" },
     { gecko = "StyleTextDecorationLine", servo = "crate::values::computed::TextDecorationLine" },
     { gecko = "StyleTextTransform", servo = "crate::values::computed::TextTransform" },
     { gecko = "StyleTextUnderlinePosition", servo = "crate::values::computed::TextUnderlinePosition" },
     { gecko = "StyleMozListReversed", servo = "crate::values::computed::MozListReversed" },
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3061,28 +3061,30 @@ LogicalSide nsStyleText::TextEmphasisSid
 
 nsStyleUI::nsStyleUI(const Document& aDocument)
     : mInert(StyleInert::None),
       mUserInput(StyleUserInput::Auto),
       mUserModify(StyleUserModify::ReadOnly),
       mUserFocus(StyleUserFocus::None),
       mPointerEvents(StylePointerEvents::Auto),
       mCursor{{}, StyleCursorKind::Auto},
+      mAccentColor(StyleColorOrAuto::Auto()),
       mCaretColor(StyleColorOrAuto::Auto()),
       mScrollbarColor(StyleScrollbarColor::Auto()) {
   MOZ_COUNT_CTOR(nsStyleUI);
 }
 
 nsStyleUI::nsStyleUI(const nsStyleUI& aSource)
     : mInert(aSource.mInert),
       mUserInput(aSource.mUserInput),
       mUserModify(aSource.mUserModify),
       mUserFocus(aSource.mUserFocus),
       mPointerEvents(aSource.mPointerEvents),
       mCursor(aSource.mCursor),
+      mAccentColor(aSource.mAccentColor),
       mCaretColor(aSource.mCaretColor),
       mScrollbarColor(aSource.mScrollbarColor) {
   MOZ_COUNT_CTOR(nsStyleUI);
 }
 
 nsStyleUI::~nsStyleUI() { MOZ_COUNT_DTOR(nsStyleUI); }
 
 void nsStyleUI::TriggerImageLoads(Document& aDocument,
@@ -3123,16 +3125,17 @@ nsChangeHint nsStyleUI::CalcDifference(c
   }
 
   if (mUserFocus != aNewData.mUserFocus || mInert != aNewData.mInert ||
       mUserInput != aNewData.mUserInput) {
     hint |= nsChangeHint_NeutralChange;
   }
 
   if (mCaretColor != aNewData.mCaretColor ||
+      mAccentColor != aNewData.mAccentColor ||
       mScrollbarColor != aNewData.mScrollbarColor) {
     hint |= nsChangeHint_RepaintFrame;
   }
 
   return hint;
 }
 
 //-----------------------
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1742,17 +1742,18 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   mozilla::StyleInert mInert;
   mozilla::StyleUserInput mUserInput;
   mozilla::StyleUserModify mUserModify;  // (modify-content)
   mozilla::StyleUserFocus mUserFocus;    // (auto-select)
   mozilla::StylePointerEvents mPointerEvents;
 
   mozilla::StyleCursor mCursor;
 
-  mozilla::StyleColorOrAuto mCaretColor;
+  mozilla::StyleColorOrAuto mAccentColor;
+  mozilla::StyleCaretColor mCaretColor;
   mozilla::StyleScrollbarColor mScrollbarColor;
 
   inline mozilla::StylePointerEvents GetEffectivePointerEvents(
       nsIFrame* aFrame) const;
 
   bool HasCustomScrollbars() const { return !mScrollbarColor.IsAuto(); }
 };
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -9277,16 +9277,32 @@ var gCSSProperties = {
   "-moz-window-dragging": {
     domProp: "MozWindowDragging",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: ["default"],
     other_values: ["drag", "no-drag"],
     invalid_values: ["none"],
   },
+  "accent-color": {
+    domProp: "accentColor",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    prerequisites: { color: "black" },
+    initial_values: ["auto"],
+    other_values: [
+      "currentcolor",
+      "black",
+      "green",
+      "transparent",
+      "rgba(128,128,128,.5)",
+      "#123",
+    ],
+    invalid_values: ["#0", "#00", "#00000", "cc00ff"],
+  },
   "align-content": {
     domProp: "alignContent",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: ["normal"],
     other_values: [
       "start",
       "end",
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -125,16 +125,19 @@ var supported_properties = {
                         test_length_pair_transition_clamped ],
     "border-top-color": [ test_color_transition,
                           test_currentcolor_transition ],
     "border-top-width": [ test_length_transition,
                           test_length_clamped ],
     "bottom": [ test_length_transition, test_percent_transition,
                 test_length_percent_calc_transition,
                 test_length_unclamped, test_percent_unclamped ],
+    "accent-color": [ test_color_transition,
+                      test_currentcolor_transition,
+                      test_auto_color_transition ],
     "caret-color": [ test_color_transition,
                      test_currentcolor_transition,
                      test_auto_color_transition ],
     "clip": [ test_rect_transition ],
     "clip-path": [ test_basic_shape_or_url_transition ],
     "color": [ test_color_transition,
                test_currentcolor_transition ],
     "fill": [ test_color_transition,
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -6552,16 +6552,23 @@
 
 # Whether the `-moz-outline-radius` pseudo-class is exposed to content.
 - name: layout.css.moz-outline-radius.enabled
   type: RelaxedAtomicBool
   value: false
   mirror: always
   rust: true
 
+# Whether the `accent-color` css property is enabled.
+- name: layout.css.accent-color.enabled
+  type: RelaxedAtomicBool
+  value: @IS_NIGHTLY_BUILD@
+  mirror: always
+  rust: true
+
 # Whether the `aspect-ratio` in css-sizing-4 is enabled.
 - name: layout.css.aspect-ratio.enabled
   type: bool
   value: true
   mirror: always
   rust: true
 
 # Is the codepath for using cached scrollbar styles enabled?
--- a/servo/components/style/properties/longhands/inherited_ui.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_ui.mako.rs
@@ -68,23 +68,36 @@
     gecko_ffi_name="mUserFocus",
     gecko_enum_prefix="StyleUserFocus",
     animation_value_type="discrete",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)",
 )}
 
 ${helpers.predefined_type(
     "caret-color",
+    "color::CaretColor",
+    "generics::color::CaretColor::auto()",
+    engines="gecko",
+    spec="https://drafts.csswg.org/css-ui/#caret-color",
+    animation_value_type="CaretColor",
+    boxed=True,
+    ignored_when_colors_disabled=True,
+)}
+
+${helpers.predefined_type(
+    "accent-color",
     "ColorOrAuto",
     "generics::color::ColorOrAuto::Auto",
     engines="gecko",
-    spec="https://drafts.csswg.org/css-ui/#caret-color",
-    animation_value_type="AnimatedCaretColor",
+    spec="https://drafts.csswg.org/css-ui-4/#widget-accent",
+    gecko_pref="layout.css.accent-color.enabled",
+    animation_value_type="ColorOrAuto",
     boxed=True,
     ignored_when_colors_disabled=True,
+    has_effect_on_gecko_scrollbars=False,
 )}
 
 ${helpers.predefined_type(
     "scrollbar-color",
     "ui::ScrollbarColor",
     "Default::default()",
     engines="gecko",
     spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-color",
--- a/servo/components/style/values/computed/color.rs
+++ b/servo/components/style/values/computed/color.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Computed color values.
 
 use crate::values::animated::color::RGBA as AnimatedRGBA;
 use crate::values::animated::ToAnimatedValue;
-use crate::values::generics::color::{Color as GenericColor, ColorOrAuto as GenericColorOrAuto};
+use crate::values::generics::color::{GenericColor, GenericColorOrAuto, GenericCaretColor};
 use cssparser::{Color as CSSParserColor, RGBA};
 use std::fmt;
 use style_traits::{CssWriter, ToCss};
 
 /// The computed value of the `color` property.
 pub type ColorPropertyValue = RGBA;
 
 /// The computed value of `-moz-font-smoothing-background-color`.
@@ -107,8 +107,11 @@ impl ToAnimatedValue for RGBA {
     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
         // RGBA::from_floats clamps each component values.
         RGBA::from_floats(animated.red, animated.green, animated.blue, animated.alpha)
     }
 }
 
 /// auto | <color>
 pub type ColorOrAuto = GenericColorOrAuto<Color>;
+
+/// caret-color
+pub type CaretColor = GenericCaretColor<Color>;
--- a/servo/components/style/values/generics/color.rs
+++ b/servo/components/style/values/generics/color.rs
@@ -91,20 +91,50 @@ impl<RGBA> From<RGBA> for Color<RGBA> {
     Debug,
     MallocSizeOf,
     PartialEq,
     Parse,
     SpecifiedValueInfo,
     ToAnimatedValue,
     ToAnimatedZero,
     ToComputedValue,
+    ToResolvedValue,
     ToCss,
     ToShmem,
 )]
 #[repr(C, u8)]
 pub enum GenericColorOrAuto<C> {
     /// A `<color>`.
     Color(C),
     /// `auto`
     Auto,
 }
 
 pub use self::GenericColorOrAuto as ColorOrAuto;
+
+/// Caret color is effectively a ColorOrAuto, but resolves `auto` to
+/// currentColor.
+#[derive(
+    Animate,
+    Clone,
+    ComputeSquaredDistance,
+    Copy,
+    Debug,
+    MallocSizeOf,
+    PartialEq,
+    SpecifiedValueInfo,
+    ToAnimatedValue,
+    ToAnimatedZero,
+    ToComputedValue,
+    ToCss,
+    ToShmem,
+)]
+#[repr(transparent)]
+pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
+
+impl<C> GenericCaretColor<C> {
+    /// Returns the `auto` value.
+    pub fn auto() -> Self {
+        GenericCaretColor(GenericColorOrAuto::Auto)
+    }
+}
+
+pub use self::GenericCaretColor as CaretColor;
--- a/servo/components/style/values/resolved/color.rs
+++ b/servo/components/style/values/resolved/color.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Resolved color values.
 
 use super::{Context, ToResolvedValue};
 
-use crate::values::computed;
+use crate::values::computed::color as computed;
 use crate::values::generics::color as generics;
 
 impl ToResolvedValue for computed::Color {
     // A resolved color value is an rgba color, with currentcolor resolved.
     type ResolvedValue = cssparser::RGBA;
 
     #[inline]
     fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
@@ -19,27 +19,27 @@ impl ToResolvedValue for computed::Color
     }
 
     #[inline]
     fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
         generics::Color::rgba(resolved)
     }
 }
 
-impl ToResolvedValue for computed::ColorOrAuto {
+impl ToResolvedValue for computed::CaretColor {
     // A resolved caret-color value is an rgba color, with auto resolving to
     // currentcolor.
     type ResolvedValue = cssparser::RGBA;
 
     #[inline]
     fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
-        let color = match self {
+        let color = match self.0 {
             generics::ColorOrAuto::Color(color) => color,
             generics::ColorOrAuto::Auto => generics::Color::currentcolor(),
         };
         color.to_resolved_value(context)
     }
 
     #[inline]
     fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
-        generics::ColorOrAuto::Color(computed::Color::from_resolved_value(resolved))
+        generics::CaretColor(generics::ColorOrAuto::Color(computed::Color::from_resolved_value(resolved)))
     }
 }
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -4,17 +4,17 @@
 
 //! Specified color values.
 
 use super::AllowQuirks;
 #[cfg(feature = "gecko")]
 use crate::gecko_bindings::structs::nscolor;
 use crate::parser::{Parse, ParserContext};
 use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
-use crate::values::generics::color::ColorOrAuto as GenericColorOrAuto;
+use crate::values::generics::color::{GenericColorOrAuto, GenericCaretColor};
 use crate::values::specified::calc::CalcNode;
 use crate::values::specified::Percentage;
 use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA};
 use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
 use itoa;
 use std::fmt::{self, Write};
 use std::io::Write as IoWrite;
 use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
@@ -784,8 +784,20 @@ impl Parse for ColorPropertyValue {
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         Color::parse_quirky(context, input, AllowQuirks::Yes).map(ColorPropertyValue)
     }
 }
 
 /// auto | <color>
 pub type ColorOrAuto = GenericColorOrAuto<Color>;
+
+/// caret-color
+pub type CaretColor = GenericCaretColor<Color>;
+
+impl Parse for CaretColor {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        ColorOrAuto::parse(context, input).map(GenericCaretColor)
+    }
+}
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -153,16 +153,17 @@ include = [
   "TextOverflow",
   "MozControlCharacterVisibility",
   "RubyPosition",
   "MozListReversed",
   "Owned",
   "OwnedOrNull",
   "Strong",
   "ScrollbarColor",
+  "CaretColor",
   "Color",
   "ColorOrAuto",
   "SystemColor",
   "SystemFont",
   "GradientItem",
   "VerticalAlign",
   "BasicShape",
   "ShapeRadius",
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-ui/accent-color-checkbox-checked-001-notref.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<input type=checkbox style="accent-color: blue">
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-ui/accent-color-checkbox-checked-001.tentative.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Accent color changes colors of a checked checkbox</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" title="https://drafts.csswg.org/css-ui-4/#widget-accent">
+<link rel="help" title="https://bugzilla.mozilla.org/show_bug.cgi?id=1705605">
+<link rel="mismatch" href="accent-color-checkbox-checked-001-notref.html">
+
+<input type=checkbox checked style="accent-color: red">
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-ui/accent-color-computed.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#widget-accent">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+  #outer { accent-color: red; }
+</style>
+<div id="outer">
+  <div id="target"></div>
+</div>
+<script>
+test_computed_value('accent-color', 'initial', 'auto');
+test_computed_value('accent-color', 'inherit', 'rgb(255, 0, 0)');
+test_computed_value('accent-color', 'red', 'rgb(255, 0, 0)');
+test_computed_value('accent-color', 'blue', 'rgb(0, 0, 255)');
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-ui/accent-color-parsing.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Parsing of accent-color</title>
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#widget-accent">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<div id="target"></div>
+<script>
+test_valid_value('accent-color', 'initial');
+test_valid_value('accent-color', 'inherit');
+test_valid_value('accent-color', 'unset');
+test_valid_value('accent-color', 'revert');
+test_valid_value('accent-color', 'auto');
+test_valid_value('accent-color', 'red');
+test_valid_value('accent-color', 'blue');
+test_valid_value('accent-color', 'currentcolor');
+test_valid_value('accent-color', '#fff', 'rgb(255, 255, 255)');
+
+test_invalid_value('accent-color', 'auto auto');
+</script>
--- a/widget/nsNativeBasicTheme.cpp
+++ b/widget/nsNativeBasicTheme.cpp
@@ -65,39 +65,165 @@ static LayoutDeviceIntCoord SnapBorderWi
   }
   return std::max(LayoutDeviceIntCoord(1), (aCssWidth * aDpiRatio).Truncated());
 }
 
 [[nodiscard]] static float ScaleLuminanceBy(float aLuminance, float aFactor) {
   return aLuminance >= 0.18f ? aLuminance * aFactor : aLuminance / aFactor;
 }
 
+struct ColorPalette {
+  ColorPalette(nscolor aAccent, nscolor aForeground);
+
+  constexpr ColorPalette(sRGBColor aAccent, sRGBColor aForeground,
+                         sRGBColor aLight, sRGBColor aDark, sRGBColor aDarker)
+      : mAccent(aAccent),
+        mForeground(aForeground),
+        mAccentLight(aLight),
+        mAccentDark(aDark),
+        mAccentDarker(aDarker) {}
+
+  constexpr static ColorPalette Default() {
+    return ColorPalette(
+        sRGBColor::UnusualFromARGB(0xff0060df),  // Luminance: 13.69346%
+        sColorWhite,
+        sRGBColor::UnusualFromARGB(0x4d008deb),  // Luminance: 25.04791%
+        sRGBColor::UnusualFromARGB(0xff0250bb),  // Luminance: 9.33808%
+        sRGBColor::UnusualFromARGB(0xff054096)   // Luminance: 5.90106%
+    );
+  }
+
+  // Ensure accent color is opaque by blending with white. This serves two
+  // purposes: On one hand, it avoids surprises if we overdraw. On the other, it
+  // makes our math below make more sense, as we want to match the browser
+  // style, which has an opaque accent color.
+  static nscolor EnsureOpaque(nscolor aAccent) {
+    if (NS_GET_A(aAccent) != 0xff) {
+      return NS_ComposeColors(NS_RGB(0xff, 0xff, 0xff), aAccent);
+    }
+    return aAccent;
+  }
+
+  static nscolor GetLight(nscolor aAccent) {
+    // The luminance from the light color divided by the one of the accent color
+    // in the default palette.
+    constexpr float kLightLuminanceScale = 25.048f / 13.693f;
+    const float lightLuminanceAdjust = ScaleLuminanceBy(
+        RelativeLuminanceUtils::Compute(aAccent), kLightLuminanceScale);
+    nscolor lightColor =
+        RelativeLuminanceUtils::Adjust(aAccent, lightLuminanceAdjust);
+    return NS_RGBA(NS_GET_R(lightColor), NS_GET_G(lightColor),
+                   NS_GET_B(lightColor), 0x4d);
+  }
+
+  static nscolor GetDark(nscolor aAccent) {
+    // Same deal as above (but without the alpha).
+    constexpr float kDarkLuminanceScale = 9.338f / 13.693f;
+    const float darkLuminanceAdjust = ScaleLuminanceBy(
+        RelativeLuminanceUtils::Compute(aAccent), kDarkLuminanceScale);
+    return RelativeLuminanceUtils::Adjust(aAccent, darkLuminanceAdjust);
+  }
+
+  static nscolor GetDarker(nscolor aAccent) {
+    // Same deal as above.
+    constexpr float kDarkerLuminanceScale = 5.901f / 13.693f;
+    const float darkerLuminanceAdjust = ScaleLuminanceBy(
+        RelativeLuminanceUtils::Compute(aAccent), kDarkerLuminanceScale);
+    return RelativeLuminanceUtils::Adjust(aAccent, darkerLuminanceAdjust);
+  }
+
+  sRGBColor mAccent;
+  sRGBColor mForeground;
+
+  // Note that depending on the exact accent color, lighter/darker might really
+  // be inverted.
+  sRGBColor mAccentLight;
+  sRGBColor mAccentDark;
+  sRGBColor mAccentDarker;
+};
+
 static nscolor ThemedAccentColor(bool aBackground) {
   MOZ_ASSERT(StaticPrefs::widget_non_native_theme_use_theme_accent());
   // TODO(emilio): In the future we should probably add dark-color-scheme
   // support for non-native form controls.
-  nscolor color = LookAndFeel::Color(
+  return ColorPalette::EnsureOpaque(LookAndFeel::Color(
       aBackground ? LookAndFeel::ColorID::MozAccentColor
                   : LookAndFeel::ColorID::MozAccentColorForeground,
-      LookAndFeel::ColorScheme::Light, LookAndFeel::UseStandins::No);
-  if (NS_GET_A(color) != 0xff) {
-    // Blend with white, ensuring the color is opaque to avoid surprises if we
-    // overdraw.
-    color = NS_ComposeColors(NS_RGB(0xff, 0xff, 0xff), color);
-  }
-  return color;
+      LookAndFeel::ColorScheme::Light, LookAndFeel::UseStandins::No));
+}
+
+static ColorPalette sDefaultPalette = ColorPalette::Default();
+
+ColorPalette::ColorPalette(nscolor aAccent, nscolor aForeground) {
+  mAccent = sRGBColor::FromABGR(aAccent);
+  mForeground = sRGBColor::FromABGR(aForeground);
+  mAccentLight = sRGBColor::FromABGR(GetLight(aAccent));
+  mAccentDark = sRGBColor::FromABGR(GetDark(aAccent));
+  mAccentDarker = sRGBColor::FromABGR(GetDarker(aAccent));
 }
 
 }  // namespace
 
-sRGBColor nsNativeBasicTheme::sAccentColor = sRGBColor::OpaqueWhite();
-sRGBColor nsNativeBasicTheme::sAccentColorForeground = sRGBColor::OpaqueWhite();
-sRGBColor nsNativeBasicTheme::sAccentColorLight = sRGBColor::OpaqueWhite();
-sRGBColor nsNativeBasicTheme::sAccentColorDark = sRGBColor::OpaqueWhite();
-sRGBColor nsNativeBasicTheme::sAccentColorDarker = sRGBColor::OpaqueWhite();
+class nsNativeBasicTheme::AccentColor {
+  Maybe<nscolor> mAccentColor;
+
+ public:
+  AccentColor() = default;
+
+  explicit AccentColor(const ComputedStyle& aStyle) {
+    const auto& color = aStyle.StyleUI()->mAccentColor;
+    if (color.IsColor()) {
+      mAccentColor.emplace(
+          ColorPalette::EnsureOpaque(color.AsColor().CalcColor(aStyle)));
+    } else {
+      MOZ_ASSERT(color.IsAuto());
+    }
+  }
+
+  sRGBColor Get() const {
+    if (!mAccentColor) {
+      return sDefaultPalette.mAccent;
+    }
+    return sRGBColor::FromABGR(*mAccentColor);
+  }
+
+  sRGBColor GetForeground() const {
+    if (!mAccentColor) {
+      return sDefaultPalette.mForeground;
+    }
+    // TODO(emilio): Maybe sColorBlack is too dark.
+    //
+    // TODO(emilio): We should probably allow the page to specify this one too:
+    // https://github.com/w3c/csswg-drafts/issues/6159
+    return nsNativeTheme::IsDarkColor(*mAccentColor) ? sColorWhite
+                                                     : sColorBlack;
+  }
+
+  sRGBColor GetLight() const {
+    if (!mAccentColor) {
+      return sDefaultPalette.mAccentLight;
+    }
+    return sRGBColor::FromABGR(ColorPalette::GetLight(*mAccentColor));
+  }
+
+  sRGBColor GetDark() const {
+    if (!mAccentColor) {
+      return sDefaultPalette.mAccentDark;
+    }
+    return sRGBColor::FromABGR(ColorPalette::GetDark(*mAccentColor));
+  }
+
+  sRGBColor GetDarker() const {
+    if (!mAccentColor) {
+      return sDefaultPalette.mAccentDarker;
+    }
+    return sRGBColor::FromABGR(ColorPalette::GetDarker(*mAccentColor));
+  }
+};
+
 CSSIntCoord nsNativeBasicTheme::sHorizontalScrollbarHeight = CSSIntCoord(0);
 CSSIntCoord nsNativeBasicTheme::sVerticalScrollbarWidth = CSSIntCoord(0);
 bool nsNativeBasicTheme::sOverlayScrollbars = false;
 
 static constexpr nsLiteralCString kPrefs[] = {
     "widget.non-native-theme.use-theme-accent"_ns,
     "widget.non-native-theme.win.scrollbar.use-system-size"_ns,
     "widget.non-native-theme.scrollbar.size"_ns,
@@ -120,57 +246,22 @@ void nsNativeBasicTheme::LookAndFeelChan
   RecomputeAccentColors();
   RecomputeScrollbarParams();
 }
 
 void nsNativeBasicTheme::RecomputeAccentColors() {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!StaticPrefs::widget_non_native_theme_use_theme_accent()) {
-    sAccentColorForeground = sColorWhite;
-    sAccentColor =
-        sRGBColor::UnusualFromARGB(0xff0060df);  // Luminance: 13.69346%
-    sAccentColorLight =
-        sRGBColor::UnusualFromARGB(0x4d008deb);  // Luminance: 25.04791%
-    sAccentColorDark =
-        sRGBColor::UnusualFromARGB(0xff0250bb);  // Luminance: 9.33808%
-    sAccentColorDarker =
-        sRGBColor::UnusualFromARGB(0xff054096);  // Luminance: 5.90106%
+    sDefaultPalette = ColorPalette::Default();
     return;
   }
 
-  sAccentColorForeground = sRGBColor::FromABGR(ThemedAccentColor(false));
-  const nscolor accent = ThemedAccentColor(true);
-  const float luminance = RelativeLuminanceUtils::Compute(accent);
-
-  constexpr float kLightLuminanceScale = 25.048f / 13.693f;
-  constexpr float kDarkLuminanceScale = 9.338f / 13.693f;
-  constexpr float kDarkerLuminanceScale = 5.901f / 13.693f;
-
-  const float lightLuminanceAdjust =
-      ScaleLuminanceBy(luminance, kLightLuminanceScale);
-  const float darkLuminanceAdjust =
-      ScaleLuminanceBy(luminance, kDarkLuminanceScale);
-  const float darkerLuminanceAdjust =
-      ScaleLuminanceBy(luminance, kDarkerLuminanceScale);
-
-  sAccentColor = sRGBColor::FromABGR(accent);
-
-  {
-    nscolor lightColor =
-        RelativeLuminanceUtils::Adjust(accent, lightLuminanceAdjust);
-    lightColor = NS_RGBA(NS_GET_R(lightColor), NS_GET_G(lightColor),
-                         NS_GET_B(lightColor), 0x4d);
-    sAccentColorLight = sRGBColor::FromABGR(lightColor);
-  }
-
-  sAccentColorDark = sRGBColor::FromABGR(
-      RelativeLuminanceUtils::Adjust(accent, darkLuminanceAdjust));
-  sAccentColorDarker = sRGBColor::FromABGR(
-      RelativeLuminanceUtils::Adjust(accent, darkerLuminanceAdjust));
+  sDefaultPalette =
+      ColorPalette(ThemedAccentColor(true), ThemedAccentColor(false));
 }
 
 void nsNativeBasicTheme::RecomputeScrollbarParams() {
   sOverlayScrollbars =
       LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
 
   uint32_t defaultSize = StaticPrefs::widget_non_native_theme_scrollbar_size();
   if (StaticPrefs::widget_non_native_theme_win_scrollbar_use_system_size()) {
@@ -272,17 +363,17 @@ static LayoutDeviceRect CheckBoxRadioRec
   // Place a square rect in the center of aRect.
   auto size = std::trunc(std::min(aRect.width, aRect.height));
   auto position = aRect.Center() - LayoutDevicePoint(size * 0.5, size * 0.5);
   return LayoutDeviceRect(position, LayoutDeviceSize(size, size));
 }
 
 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeCheckboxColors(
     const EventStates& aState, StyleAppearance aAppearance,
-    UseSystemColors aUseSystemColors) {
+    const AccentColor& aAccent, UseSystemColors aUseSystemColors) {
   MOZ_ASSERT(aAppearance == StyleAppearance::Checkbox ||
              aAppearance == StyleAppearance::Radio);
   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
   bool isPressed =
       aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
   bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
   bool isChecked = aState.HasState(NS_EVENT_STATE_CHECKED);
   bool isIndeterminate = aAppearance == StyleAppearance::Checkbox &&
@@ -306,40 +397,41 @@ std::pair<sRGBColor, sRGBColor> nsNative
   sRGBColor borderColor = sColorGrey40;
   if (isDisabled) {
     backgroundColor = sColorWhiteAlpha50;
     borderColor = sColorGrey40Alpha50;
     if (isChecked || isIndeterminate) {
       backgroundColor = borderColor;
     }
   } else if (isChecked || isIndeterminate) {
-    const auto& color = isPressed   ? sAccentColorDarker
-                        : isHovered ? sAccentColorDark
-                                    : sAccentColor;
+    const auto& color = isPressed   ? aAccent.GetDarker()
+                        : isHovered ? aAccent.GetDark()
+                                    : aAccent.Get();
     backgroundColor = borderColor = color;
   } else if (isPressed) {
     backgroundColor = sColorGrey20;
     borderColor = sColorGrey60;
   } else if (isHovered) {
     backgroundColor = sColorWhite;
     borderColor = sColorGrey50;
   }
 
   return std::make_pair(backgroundColor, borderColor);
 }
 
 sRGBColor nsNativeBasicTheme::ComputeCheckmarkColor(
-    const EventStates& aState, UseSystemColors aUseSystemColors) {
+    const EventStates& aState, const AccentColor& aAccent,
+    UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
     return SystemColor(StyleSystemColor::Highlighttext);
   }
   if (aState.HasState(NS_EVENT_STATE_DISABLED)) {
     return sColorWhiteAlpha80;
   }
-  return sAccentColorForeground;
+  return aAccent.GetForeground();
 }
 
 sRGBColor nsNativeBasicTheme::ComputeBorderColor(
     const EventStates& aState, UseSystemColors aUseSystemColors) {
   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
   if (bool(aUseSystemColors)) {
     return SystemColor(isDisabled ? StyleSystemColor::Graytext
                                   : StyleSystemColor::Buttontext);
@@ -412,34 +504,35 @@ std::pair<sRGBColor, sRGBColor> nsNative
     }
     return sColorWhite;
   }();
   const sRGBColor borderColor = ComputeBorderColor(aState, aUseSystemColors);
   return std::make_pair(backgroundColor, borderColor);
 }
 
 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeRangeProgressColors(
-    const EventStates& aState, UseSystemColors aUseSystemColors) {
+    const EventStates& aState, const AccentColor& aAccent,
+    UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
     return SystemColorPair(StyleSystemColor::Highlight,
                            StyleSystemColor::Buttontext);
   }
 
   bool isActive =
       aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
   bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
 
   if (isDisabled) {
     return std::make_pair(sColorGrey40Alpha50, sColorGrey40Alpha50);
   }
   if (isActive || isHovered) {
-    return std::make_pair(sAccentColorDark, sAccentColorDarker);
+    return std::make_pair(aAccent.GetDark(), aAccent.GetDarker());
   }
-  return std::make_pair(sAccentColor, sAccentColorDark);
+  return std::make_pair(aAccent.Get(), aAccent.GetDark());
 }
 
 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeRangeTrackColors(
     const EventStates& aState, UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
     return SystemColorPair(StyleSystemColor::TextBackground,
                            StyleSystemColor::Buttontext);
   }
@@ -453,67 +546,69 @@ std::pair<sRGBColor, sRGBColor> nsNative
   }
   if (isActive || isHovered) {
     return std::make_pair(sColorGrey20, sColorGrey50);
   }
   return std::make_pair(sColorGrey10, sColorGrey40);
 }
 
 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeRangeThumbColors(
-    const EventStates& aState, UseSystemColors aUseSystemColors) {
+    const EventStates& aState, const AccentColor& aAccent,
+    UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
     return SystemColorPair(StyleSystemColor::Highlighttext,
                            StyleSystemColor::Highlight);
   }
 
   bool isActive =
       aState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE);
   bool isDisabled = aState.HasState(NS_EVENT_STATE_DISABLED);
   bool isHovered = aState.HasState(NS_EVENT_STATE_HOVER);
 
   const sRGBColor& backgroundColor = [&] {
     if (isDisabled) {
       return sColorGrey40;
     }
     if (isActive) {
-      return sAccentColor;
+      return aAccent.Get();
     }
     if (isHovered) {
       return sColorGrey60;
     }
     return sColorGrey50;
   }();
 
   const sRGBColor borderColor = sColorWhite;
 
   return std::make_pair(backgroundColor, borderColor);
 }
 
 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeProgressColors(
-    UseSystemColors aUseSystemColors) {
+    const AccentColor& aAccent, UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
     return SystemColorPair(StyleSystemColor::Highlight,
                            StyleSystemColor::Buttontext);
   }
-  return std::make_pair(sAccentColor, sAccentColorDark);
+  return std::make_pair(aAccent.Get(), aAccent.GetDark());
 }
 
 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeProgressTrackColors(
     UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
     return SystemColorPair(StyleSystemColor::Buttonface,
                            StyleSystemColor::Buttontext);
   }
   return std::make_pair(sColorGrey10, sColorGrey40);
 }
 
 std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeMeterchunkColors(
     const EventStates& aMeterState, UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
-    return ComputeProgressColors(aUseSystemColors);
+    // Accent color doesn't matter when using system colors.
+    return ComputeProgressColors(AccentColor(), aUseSystemColors);
   }
   sRGBColor borderColor = sColorMeterGreen20;
   sRGBColor chunkColor = sColorMeterGreen10;
 
   if (aMeterState.HasState(NS_EVENT_STATE_SUB_OPTIMUM)) {
     borderColor = sColorMeterYellow20;
     chunkColor = sColorMeterYellow10;
   } else if (aMeterState.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM)) {
@@ -530,24 +625,24 @@ sRGBColor nsNativeBasicTheme::ComputeMen
   if (bool(aUseSystemColors)) {
     return SystemColor(isDisabled ? StyleSystemColor::Graytext
                                   : StyleSystemColor::TextForeground);
   }
   return isDisabled ? sColorGrey60Alpha50 : sColorGrey60;
 }
 
 std::array<sRGBColor, 3> nsNativeBasicTheme::ComputeFocusRectColors(
-    UseSystemColors aUseSystemColors) {
+    const AccentColor& aAccent, UseSystemColors aUseSystemColors) {
   if (bool(aUseSystemColors)) {
     return {SystemColor(StyleSystemColor::Highlight),
             SystemColor(StyleSystemColor::Buttontext),
             SystemColor(StyleSystemColor::TextBackground)};
   }
 
-  return {sAccentColor, sColorWhiteAlpha80, sAccentColorLight};
+  return {aAccent.Get(), sColorWhiteAlpha80, aAccent.GetLight()};
 }
 
 sRGBColor nsNativeBasicTheme::ComputeScrollbarTrackColor(
     nsIFrame* aFrame, const ComputedStyle& aStyle,
     const EventStates& aDocumentState, UseSystemColors aUseSystemColors) {
   const nsStyleUI* ui = aStyle.StyleUI();
   if (bool(aUseSystemColors)) {
     return SystemColor(StyleSystemColor::TextBackground);
@@ -756,26 +851,24 @@ nsNativeBasicTheme::ComputeScrollbarButt
                                               aDocumentState, aUseSystemColors);
           });
   return {sRGBColor::FromABGR(buttonColor), arrowColor};
 }
 
 static const CSSCoord kInnerFocusOutlineWidth = 2.0f;
 
 template <typename PaintBackendData>
-void nsNativeBasicTheme::PaintRoundedFocusRect(PaintBackendData& aBackendData,
-                                               const LayoutDeviceRect& aRect,
-                                               UseSystemColors aUseSystemColors,
-                                               DPIRatio aDpiRatio,
-                                               CSSCoord aRadius,
-                                               CSSCoord aOffset) {
+void nsNativeBasicTheme::PaintRoundedFocusRect(
+    PaintBackendData& aBackendData, const LayoutDeviceRect& aRect,
+    const AccentColor& aAccent, UseSystemColors aUseSystemColors,
+    DPIRatio aDpiRatio, CSSCoord aRadius, CSSCoord aOffset) {
   // NOTE(emilio): If the widths or offsets here change, make sure to tweak
   // the GetWidgetOverflow path for FocusOutline.
   auto [innerColor, middleColor, outerColor] =
-      ComputeFocusRectColors(aUseSystemColors);
+      ComputeFocusRectColors(aAccent, aUseSystemColors);
 
   LayoutDeviceRect focusRect(aRect);
 
   // The focus rect is painted outside of the border area (aRect), see:
   //
   //   data:text/html,<div style="border: 1px solid; outline: 2px solid
   //   red">Foobar</div>
   //
@@ -941,55 +1034,58 @@ void nsNativeBasicTheme::PaintRoundedRec
   if (needsClip) {
     aDrawTarget.PopClip();
   }
 }
 
 void nsNativeBasicTheme::PaintCheckboxControl(DrawTarget& aDrawTarget,
                                               const LayoutDeviceRect& aRect,
                                               const EventStates& aState,
+                                              const AccentColor& aAccent,
                                               UseSystemColors aUseSystemColors,
                                               DPIRatio aDpiRatio) {
   auto [backgroundColor, borderColor] = ComputeCheckboxColors(
-      aState, StyleAppearance::Checkbox, aUseSystemColors);
+      aState, StyleAppearance::Checkbox, aAccent, aUseSystemColors);
   {
     const CSSCoord radius = 2.0f;
     CSSCoord borderWidth = kCheckboxRadioBorderWidth;
     if (backgroundColor == borderColor) {
       borderWidth = 0.0f;
     }
     PaintRoundedRectWithRadius(aDrawTarget, aRect, backgroundColor, borderColor,
                                borderWidth, radius, aDpiRatio);
   }
 
   if (aState.HasState(NS_EVENT_STATE_INDETERMINATE)) {
-    PaintIndeterminateMark(aDrawTarget, aRect, aState, aUseSystemColors);
+    PaintIndeterminateMark(aDrawTarget, aRect, aState, aAccent,
+                           aUseSystemColors);
   } else if (aState.HasState(NS_EVENT_STATE_CHECKED)) {
-    PaintCheckMark(aDrawTarget, aRect, aState, aUseSystemColors);
+    PaintCheckMark(aDrawTarget, aRect, aState, aAccent, aUseSystemColors);
   }
 
   if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
-    PaintRoundedFocusRect(aDrawTarget, aRect, aUseSystemColors, aDpiRatio, 5.0f,
-                          1.0f);
+    PaintRoundedFocusRect(aDrawTarget, aRect, aAccent, aUseSystemColors,
+                          aDpiRatio, 5.0f, 1.0f);
   }
 }
 
 constexpr CSSCoord kCheckboxRadioContentBoxSize = 10.0f;
 constexpr CSSCoord kCheckboxRadioBorderBoxSize =
     kCheckboxRadioContentBoxSize + kCheckboxRadioBorderWidth * 2.0f;
 
 // Returns the right scale for points in a aSize x aSize sized box, centered at
 // 0x0 to fill aRect in the smaller dimension.
 static float ScaleToFillRect(const LayoutDeviceRect& aRect, const float aSize) {
   return std::min(aRect.width, aRect.height) / aSize;
 }
 
 void nsNativeBasicTheme::PaintCheckMark(DrawTarget& aDrawTarget,
                                         const LayoutDeviceRect& aRect,
                                         const EventStates& aState,
+                                        const AccentColor& aAccent,
                                         UseSystemColors aUseSystemColors) {
   // Points come from the coordinates on a 14X14 (kCheckboxRadioBorderBoxSize)
   // unit box centered at 0,0
   const float checkPolygonX[] = {-4.5f, -1.5f, -0.5f, 5.0f, 4.75f,
                                  3.5f,  -0.5f, -1.5f, -3.5f};
   const float checkPolygonY[] = {0.5f,  4.0f, 4.0f,  -2.5f, -4.0f,
                                  -4.0f, 1.0f, 1.25f, -1.0f};
   const int32_t checkNumPoints = sizeof(checkPolygonX) / sizeof(float);
@@ -1000,33 +1096,36 @@ void nsNativeBasicTheme::PaintCheckMark(
   Point p = center + Point(checkPolygonX[0] * scale, checkPolygonY[0] * scale);
   builder->MoveTo(p);
   for (int32_t i = 1; i < checkNumPoints; i++) {
     p = center + Point(checkPolygonX[i] * scale, checkPolygonY[i] * scale);
     builder->LineTo(p);
   }
   RefPtr<Path> path = builder->Finish();
 
-  sRGBColor fillColor = ComputeCheckmarkColor(aState, aUseSystemColors);
+  sRGBColor fillColor =
+      ComputeCheckmarkColor(aState, aAccent, aUseSystemColors);
   aDrawTarget.Fill(path, ColorPattern(ToDeviceColor(fillColor)));
 }
 
 void nsNativeBasicTheme::PaintIndeterminateMark(
     DrawTarget& aDrawTarget, const LayoutDeviceRect& aRect,
-    const EventStates& aState, UseSystemColors aUseSystemColors) {
+    const EventStates& aState, const AccentColor& aAccent,
+    UseSystemColors aUseSystemColors) {
   const CSSCoord borderWidth = 2.0f;
   const float scale = ScaleToFillRect(aRect, kCheckboxRadioBorderBoxSize);
 
   Rect rect = aRect.ToUnknownRect();
   rect.y += (rect.height / 2) - (borderWidth * scale / 2);
   rect.height = borderWidth * scale;
   rect.x += (borderWidth * scale) + (borderWidth * scale / 8);
   rect.width -= ((borderWidth * scale) + (borderWidth * scale / 8)) * 2;
 
-  sRGBColor fillColor = ComputeCheckmarkColor(aState, aUseSystemColors);
+  sRGBColor fillColor =
+      ComputeCheckmarkColor(aState, aAccent, aUseSystemColors);
   aDrawTarget.FillRect(rect, ColorPattern(ToDeviceColor(fillColor)));
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintStrokedCircle(PaintBackendData& aPaintData,
                                             const LayoutDeviceRect& aRect,
                                             const sRGBColor& aBackgroundColor,
                                             const sRGBColor& aBorderColor,
@@ -1110,100 +1209,106 @@ void nsNativeBasicTheme::PaintCircleShad
   aDrawTarget.DrawFilter(blurFilter, sourceRectInFilterSpace,
                          destinationPointOfSourceRect);
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintRadioControl(PaintBackendData& aPaintData,
                                            const LayoutDeviceRect& aRect,
                                            const EventStates& aState,
+                                           const AccentColor& aAccent,
                                            UseSystemColors aUseSystemColors,
                                            DPIRatio aDpiRatio) {
-  auto [backgroundColor, borderColor] =
-      ComputeCheckboxColors(aState, StyleAppearance::Radio, aUseSystemColors);
+  auto [backgroundColor, borderColor] = ComputeCheckboxColors(
+      aState, StyleAppearance::Radio, aAccent, aUseSystemColors);
   {
     CSSCoord borderWidth = kCheckboxRadioBorderWidth;
     if (backgroundColor == borderColor) {
       borderWidth = 0.0f;
     }
     PaintStrokedCircle(aPaintData, aRect, backgroundColor, borderColor,
                        borderWidth, aDpiRatio);
   }
 
   if (aState.HasState(NS_EVENT_STATE_CHECKED)) {
     LayoutDeviceRect rect(aRect);
     rect.Deflate(SnapBorderWidth(kCheckboxRadioBorderWidth, aDpiRatio));
 
-    auto checkColor = ComputeCheckmarkColor(aState, aUseSystemColors);
+    auto checkColor = ComputeCheckmarkColor(aState, aAccent, aUseSystemColors);
     PaintStrokedCircle(aPaintData, rect, backgroundColor, checkColor,
                        kCheckboxRadioBorderWidth, aDpiRatio);
   }
 
   if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
-    PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio, 5.0f,
-                          1.0f);
+    PaintRoundedFocusRect(aPaintData, aRect, aAccent, aUseSystemColors,
+                          aDpiRatio, 5.0f, 1.0f);
   }
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintTextField(PaintBackendData& aPaintData,
                                         const LayoutDeviceRect& aRect,
                                         const EventStates& aState,
+                                        const AccentColor& aAccent,
                                         UseSystemColors aUseSystemColors,
                                         DPIRatio aDpiRatio) {
   auto [backgroundColor, borderColor] =
       ComputeTextfieldColors(aState, aUseSystemColors);
 
   const CSSCoord radius = 2.0f;
 
   PaintRoundedRectWithRadius(aPaintData, aRect, backgroundColor, borderColor,
                              kTextFieldBorderWidth, radius, aDpiRatio);
 
   if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
-    PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
-                          radius + kTextFieldBorderWidth,
+    PaintRoundedFocusRect(aPaintData, aRect, aAccent, aUseSystemColors,
+                          aDpiRatio, radius + kTextFieldBorderWidth,
                           -kTextFieldBorderWidth);
   }
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintListbox(PaintBackendData& aPaintData,
                                       const LayoutDeviceRect& aRect,
                                       const EventStates& aState,
+                                      const AccentColor& aAccent,
                                       UseSystemColors aUseSystemColors,
                                       DPIRatio aDpiRatio) {
   const CSSCoord radius = 2.0f;
   auto [backgroundColor, borderColor] =
       ComputeTextfieldColors(aState, aUseSystemColors);
 
   PaintRoundedRectWithRadius(aPaintData, aRect, backgroundColor, borderColor,
                              kMenulistBorderWidth, radius, aDpiRatio);
 
   if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
-    PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
-                          radius + kMenulistBorderWidth, -kMenulistBorderWidth);
+    PaintRoundedFocusRect(aPaintData, aRect, aAccent, aUseSystemColors,
+                          aDpiRatio, radius + kMenulistBorderWidth,
+                          -kMenulistBorderWidth);
   }
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintMenulist(PaintBackendData& aDrawTarget,
                                        const LayoutDeviceRect& aRect,
                                        const EventStates& aState,
+                                       const AccentColor& aAccent,
                                        UseSystemColors aUseSystemColors,
                                        DPIRatio aDpiRatio) {
   const CSSCoord radius = 4.0f;
   auto [backgroundColor, borderColor] =
       ComputeButtonColors(aState, aUseSystemColors);
 
   PaintRoundedRectWithRadius(aDrawTarget, aRect, backgroundColor, borderColor,
                              kMenulistBorderWidth, radius, aDpiRatio);
 
   if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
-    PaintRoundedFocusRect(aDrawTarget, aRect, aUseSystemColors, aDpiRatio,
-                          radius + kMenulistBorderWidth, -kMenulistBorderWidth);
+    PaintRoundedFocusRect(aDrawTarget, aRect, aAccent, aUseSystemColors,
+                          aDpiRatio, radius + kMenulistBorderWidth,
+                          -kMenulistBorderWidth);
   }
 }
 
 void nsNativeBasicTheme::PaintArrow(DrawTarget& aDrawTarget,
                                     const LayoutDeviceRect& aRect,
                                     const float aArrowPolygonX[],
                                     const float aArrowPolygonY[],
                                     const float aArrowPolygonSize,
@@ -1268,16 +1373,17 @@ void nsNativeBasicTheme::PaintSpinnerBut
              ArrayLength(kPolygonX), borderColor);
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintRange(nsIFrame* aFrame,
                                     PaintBackendData& aPaintData,
                                     const LayoutDeviceRect& aRect,
                                     const EventStates& aState,
+                                    const AccentColor& aAccent,
                                     UseSystemColors aUseSystemColors,
                                     DPIRatio aDpiRatio, bool aHorizontal) {
   nsRangeFrame* rangeFrame = do_QueryFrame(aFrame);
   if (!rangeFrame) {
     return;
   }
 
   double progress = rangeFrame->GetValueAsFractionOfRange();
@@ -1315,17 +1421,17 @@ void nsNativeBasicTheme::PaintRange(nsIF
     trackClipRect.SetBoxY(aRect.Y(), midPoint);
     progressClipRect.SetBoxY(midPoint, aRect.YMost());
   }
 
   const CSSCoord borderWidth = 1.0f;
   const CSSCoord radius = 3.0f;
 
   auto [progressColor, progressBorderColor] =
-      ComputeRangeProgressColors(aState, aUseSystemColors);
+      ComputeRangeProgressColors(aState, aAccent, aUseSystemColors);
   auto [trackColor, trackBorderColor] =
       ComputeRangeTrackColors(aState, aUseSystemColors);
 
   PaintRoundedRectWithRadius(aPaintData, rect, progressClipRect, progressColor,
                              progressBorderColor, borderWidth, radius,
                              aDpiRatio);
 
   PaintRoundedRectWithRadius(aPaintData, rect, trackClipRect, trackColor,
@@ -1339,32 +1445,33 @@ void nsNativeBasicTheme::PaintRange(nsIF
     // Thumb shadow
     PaintCircleShadow(aPaintData, thumbRect, overflowRect, 0.3f,
                       CSSPoint(0.0f, 2.0f), 2.0f, aDpiRatio);
   }
 
   // Draw the thumb on top.
   const CSSCoord thumbBorderWidth = 2.0f;
   auto [thumbColor, thumbBorderColor] =
-      ComputeRangeThumbColors(aState, aUseSystemColors);
+      ComputeRangeThumbColors(aState, aAccent, aUseSystemColors);
 
   PaintStrokedCircle(aPaintData, thumbRect, thumbColor, thumbBorderColor,
                      thumbBorderWidth, aDpiRatio);
 
   if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
-    PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
-                          radius, 1.0f);
+    PaintRoundedFocusRect(aPaintData, aRect, aAccent, aUseSystemColors,
+                          aDpiRatio, radius, 1.0f);
   }
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintProgress(nsIFrame* aFrame,
                                        PaintBackendData& aPaintData,
                                        const LayoutDeviceRect& aRect,
                                        const EventStates& aState,
+                                       const AccentColor& aAccent,
                                        UseSystemColors aUseSystemColors,
                                        DPIRatio aDpiRatio, bool aIsMeter) {
   const CSSCoord borderWidth = 1.0f;
   const CSSCoord radius = aIsMeter ? 6.0f : 3.0f;
 
   LayoutDeviceRect rect(aRect);
   const LayoutDeviceCoord thickness =
       (aIsMeter ? kMeterHeight : kProgressbarHeight) * aDpiRatio;
@@ -1443,38 +1550,40 @@ void nsNativeBasicTheme::PaintProgress(n
       double clipHeight = rect.height * position;
       clipRect.height = clipHeight;
       clipRect.y += rect.height - clipHeight;
     }
   }
 
   auto [backgroundColor, borderColor] =
       aIsMeter ? ComputeMeterchunkColors(aState, aUseSystemColors)
-               : ComputeProgressColors(aUseSystemColors);
+               : ComputeProgressColors(aAccent, aUseSystemColors);
   PaintRoundedRectWithRadius(aPaintData, rect, clipRect, backgroundColor,
                              borderColor, borderWidth, radius, aDpiRatio);
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintButton(nsIFrame* aFrame,
                                      PaintBackendData& aPaintData,
                                      const LayoutDeviceRect& aRect,
                                      const EventStates& aState,
+                                     const AccentColor& aAccent,
                                      UseSystemColors aUseSystemColors,
                                      DPIRatio aDpiRatio) {
   const CSSCoord radius = 4.0f;
   auto [backgroundColor, borderColor] =
       ComputeButtonColors(aState, aUseSystemColors, aFrame);
 
   PaintRoundedRectWithRadius(aPaintData, aRect, backgroundColor, borderColor,
                              kButtonBorderWidth, radius, aDpiRatio);
 
   if (aState.HasState(NS_EVENT_STATE_FOCUSRING)) {
-    PaintRoundedFocusRect(aPaintData, aRect, aUseSystemColors, aDpiRatio,
-                          radius + kButtonBorderWidth, -kButtonBorderWidth);
+    PaintRoundedFocusRect(aPaintData, aRect, aAccent, aUseSystemColors,
+                          aDpiRatio, radius + kButtonBorderWidth,
+                          -kButtonBorderWidth);
   }
 }
 
 template <typename PaintBackendData>
 bool nsNativeBasicTheme::DoPaintDefaultScrollbarThumb(
     PaintBackendData& aPaintData, const LayoutDeviceRect& aRect,
     bool aHorizontal, nsIFrame* aFrame, const ComputedStyle& aStyle,
     const EventStates& aElementState, const EventStates& aDocumentState,
@@ -1708,49 +1817,51 @@ bool nsNativeBasicTheme::DoDrawWidgetBac
   if constexpr (std::is_same_v<PaintBackendData, DrawTarget>) {
     if (aAppearance != StyleAppearance::FocusOutline &&
         aAppearance != StyleAppearance::Range &&
         !eventState.HasState(NS_EVENT_STATE_FOCUSRING)) {
       maybeClipRect.emplace(aPaintData, devPxRect);
     }
   }
 
+  const AccentColor accent(*aFrame->Style());
+
   DPIRatio dpiRatio = GetDPIRatio(aFrame, aAppearance);
 
   switch (aAppearance) {
     case StyleAppearance::Radio: {
       auto rect = CheckBoxRadioRect(devPxRect);
-      PaintRadioControl(aPaintData, rect, eventState, useSystemColors,
+      PaintRadioControl(aPaintData, rect, eventState, accent, useSystemColors,
                         dpiRatio);
       break;
     }
     case StyleAppearance::Checkbox: {
       if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) {
         // TODO: Need to figure out how to best draw this using WR.
         return false;
       } else {
         auto rect = CheckBoxRadioRect(devPxRect);
-        PaintCheckboxControl(aPaintData, rect, eventState, useSystemColors,
-                             dpiRatio);
+        PaintCheckboxControl(aPaintData, rect, eventState, accent,
+                             useSystemColors, dpiRatio);
       }
       break;
     }
     case StyleAppearance::Textarea:
     case StyleAppearance::Textfield:
     case StyleAppearance::NumberInput:
-      PaintTextField(aPaintData, devPxRect, eventState, useSystemColors,
+      PaintTextField(aPaintData, devPxRect, eventState, accent, useSystemColors,
                      dpiRatio);
       break;
     case StyleAppearance::Listbox:
-      PaintListbox(aPaintData, devPxRect, eventState, useSystemColors,
+      PaintListbox(aPaintData, devPxRect, eventState, accent, useSystemColors,
                    dpiRatio);
       break;
     case StyleAppearance::MenulistButton:
     case StyleAppearance::Menulist:
-      PaintMenulist(aPaintData, devPxRect, eventState, useSystemColors,
+      PaintMenulist(aPaintData, devPxRect, eventState, accent, useSystemColors,
                     dpiRatio);
       break;
     case StyleAppearance::MozMenulistArrowButton:
       if constexpr (std::is_same_v<PaintBackendData, WebRenderBackendData>) {
         // TODO: Need to figure out how to best draw this using WR.
         return false;
       } else {
         PaintMenulistArrowButton(aFrame, aPaintData, devPxRect, eventState,
@@ -1763,33 +1874,33 @@ bool nsNativeBasicTheme::DoDrawWidgetBac
         // TODO: Need to figure out how to best draw this using WR.
         return false;
       } else {
         PaintSpinnerButton(aFrame, aPaintData, devPxRect, eventState,
                            aAppearance, useSystemColors, dpiRatio);
       }
       break;
     case StyleAppearance::Range:
-      PaintRange(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
-                 dpiRatio, IsRangeHorizontal(aFrame));
+      PaintRange(aFrame, aPaintData, devPxRect, eventState, accent,
+                 useSystemColors, dpiRatio, IsRangeHorizontal(aFrame));
       break;
     case StyleAppearance::RangeThumb:
       // Painted as part of StyleAppearance::Range.
       break;
     case StyleAppearance::ProgressBar:
-      PaintProgress(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
-                    dpiRatio,
+      PaintProgress(aFrame, aPaintData, devPxRect, eventState, accent,
+                    useSystemColors, dpiRatio,
                     /* aIsMeter = */ false);
       break;
     case StyleAppearance::Progresschunk:
       /* Painted as part of the progress bar */
       break;
     case StyleAppearance::Meter:
-      PaintProgress(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
-                    dpiRatio, /* aIsMeter = */ true);
+      PaintProgress(aFrame, aPaintData, devPxRect, eventState, accent,
+                    useSystemColors, dpiRatio, /* aIsMeter = */ true);
       break;
     case StyleAppearance::Meterchunk:
       /* Painted as part of the meter bar */
       break;
     case StyleAppearance::ScrollbarthumbHorizontal:
     case StyleAppearance::ScrollbarthumbVertical: {
       bool isHorizontal =
           aAppearance == StyleAppearance::ScrollbarthumbHorizontal;
@@ -1829,22 +1940,22 @@ bool nsNativeBasicTheme::DoDrawWidgetBac
         } else {
           PaintScrollbarButton(aPaintData, aAppearance, devPxRect, aFrame,
                                *nsLayoutUtils::StyleForScrollbar(aFrame),
                                eventState, docState, useSystemColors, dpiRatio);
         }
       }
       break;
     case StyleAppearance::Button:
-      PaintButton(aFrame, aPaintData, devPxRect, eventState, useSystemColors,
-                  dpiRatio);
+      PaintButton(aFrame, aPaintData, devPxRect, eventState, accent,
+                  useSystemColors, dpiRatio);
       break;
     case StyleAppearance::FocusOutline:
-      PaintAutoStyleOutline(aFrame, aPaintData, devPxRect, useSystemColors,
-                            dpiRatio);
+      PaintAutoStyleOutline(aFrame, aPaintData, devPxRect, accent,
+                            useSystemColors, dpiRatio);
       break;
     default:
       // Various appearance values are used for XUL elements.  Normally these
       // will not be available in content documents (and thus in the content
       // processes where the native basic theme can be used), but tests are
       // run with the remote XUL pref enabled and so we can get in here.  So
       // we just return an error rather than assert.
       return false;
@@ -1852,20 +1963,21 @@ bool nsNativeBasicTheme::DoDrawWidgetBac
 
   return true;
 }
 
 template <typename PaintBackendData>
 void nsNativeBasicTheme::PaintAutoStyleOutline(nsIFrame* aFrame,
                                                PaintBackendData& aPaintData,
                                                const LayoutDeviceRect& aRect,
+                                               const AccentColor& aAccent,
                                                UseSystemColors aUseSystemColors,
                                                DPIRatio aDpiRatio) {
   auto [innerColor, middleColor, outerColor] =
-      ComputeFocusRectColors(aUseSystemColors);
+      ComputeFocusRectColors(aAccent, aUseSystemColors);
   Unused << middleColor;
   Unused << outerColor;
 
   LayoutDeviceRect rect(aRect);
   auto width =
       LayoutDeviceCoord(SnapBorderWidth(kInnerFocusOutlineWidth, aDpiRatio));
   rect.Inflate(width);
 
--- a/widget/nsNativeBasicTheme.h
+++ b/widget/nsNativeBasicTheme.h
@@ -95,16 +95,17 @@ class nsNativeBasicTheme : protected nsN
   using EventStates = mozilla::EventStates;
   using DrawTarget = mozilla::gfx::DrawTarget;
   using Path = mozilla::gfx::Path;
   using Rect = mozilla::gfx::Rect;
   using Point = mozilla::gfx::Point;
   using RectCornerRadii = mozilla::gfx::RectCornerRadii;
   using LayoutDeviceCoord = mozilla::LayoutDeviceCoord;
   using LayoutDeviceRect = mozilla::LayoutDeviceRect;
+  class AccentColor;
 
  public:
   static void Init();
   static void Shutdown();
   static void LookAndFeelChanged();
 
   using DPIRatio = mozilla::CSSToLayoutDeviceScale;
 
@@ -182,38 +183,44 @@ class nsNativeBasicTheme : protected nsN
   static bool IsColorPickerButton(nsIFrame*);
 
   // Whether we should use system colors (for high contrast mode).
   enum class UseSystemColors : bool { No, Yes };
   static UseSystemColors ShouldUseSystemColors(const mozilla::dom::Document&);
 
   std::pair<sRGBColor, sRGBColor> ComputeCheckboxColors(const EventStates&,
                                                         StyleAppearance,
+                                                        const AccentColor&,
                                                         UseSystemColors);
-  sRGBColor ComputeCheckmarkColor(const EventStates&, UseSystemColors);
+  sRGBColor ComputeCheckmarkColor(const EventStates&, const AccentColor&,
+                                  UseSystemColors);
   sRGBColor ComputeBorderColor(const EventStates&, UseSystemColors);
 
   std::pair<sRGBColor, sRGBColor> ComputeButtonColors(const EventStates&,
                                                       UseSystemColors,
                                                       nsIFrame* = nullptr);
   std::pair<sRGBColor, sRGBColor> ComputeTextfieldColors(const EventStates&,
                                                          UseSystemColors);
   std::pair<sRGBColor, sRGBColor> ComputeRangeProgressColors(const EventStates&,
+                                                             const AccentColor&,
                                                              UseSystemColors);
   std::pair<sRGBColor, sRGBColor> ComputeRangeTrackColors(const EventStates&,
                                                           UseSystemColors);
   std::pair<sRGBColor, sRGBColor> ComputeRangeThumbColors(const EventStates&,
+                                                          const AccentColor&,
                                                           UseSystemColors);
-  std::pair<sRGBColor, sRGBColor> ComputeProgressColors(UseSystemColors);
+  std::pair<sRGBColor, sRGBColor> ComputeProgressColors(const AccentColor&,
+                                                        UseSystemColors);
   std::pair<sRGBColor, sRGBColor> ComputeProgressTrackColors(UseSystemColors);
   std::pair<sRGBColor, sRGBColor> ComputeMeterchunkColors(
       const EventStates& aMeterState, UseSystemColors);
   sRGBColor ComputeMenulistArrowButtonColor(const EventStates&,
                                             UseSystemColors);
-  std::array<sRGBColor, 3> ComputeFocusRectColors(UseSystemColors);
+  std::array<sRGBColor, 3> ComputeFocusRectColors(const AccentColor&,
+                                                  UseSystemColors);
 
   static bool ShouldUseDarkScrollbar(nsIFrame*, const ComputedStyle&);
   sRGBColor ComputeScrollbarTrackColor(nsIFrame*, const ComputedStyle&,
                                        const EventStates& aDocumentState,
                                        UseSystemColors);
   sRGBColor ComputeScrollbarThumbColor(nsIFrame*, const ComputedStyle&,
                                        const EventStates& aElementState,
                                        const EventStates& aDocumentState,
@@ -221,22 +228,22 @@ class nsNativeBasicTheme : protected nsN
   // Returned colors are button, arrow.
   std::pair<sRGBColor, sRGBColor> ComputeScrollbarButtonColors(
       nsIFrame*, StyleAppearance, const ComputedStyle&,
       const EventStates& aElementState, const EventStates& aDocumentState,
       UseSystemColors);
 
   template <typename PaintBackendData>
   void PaintRoundedFocusRect(PaintBackendData&, const LayoutDeviceRect&,
-                             UseSystemColors, DPIRatio, CSSCoord aRadius,
-                             CSSCoord aOffset);
+                             const AccentColor&, UseSystemColors, DPIRatio,
+                             CSSCoord aRadius, CSSCoord aOffset);
   template <typename PaintBackendData>
   void PaintAutoStyleOutline(nsIFrame*, PaintBackendData&,
-                             const LayoutDeviceRect&, UseSystemColors,
-                             DPIRatio);
+                             const LayoutDeviceRect&, const AccentColor&,
+                             UseSystemColors, DPIRatio);
 
   static void PaintRoundedRectWithRadius(DrawTarget&,
                                          const LayoutDeviceRect& aRect,
                                          const LayoutDeviceRect& aClipRect,
                                          const sRGBColor& aBackgroundColor,
                                          const sRGBColor& aBorderColor,
                                          CSSCoord aBorderWidth,
                                          CSSCoord aRadius, DPIRatio);
@@ -258,21 +265,23 @@ class nsNativeBasicTheme : protected nsN
                                aBorderColor, aBorderWidth, aRadius, aDpiRatio);
   }
 
   static void FillRect(DrawTarget&, const LayoutDeviceRect&, const sRGBColor&);
   static void FillRect(WebRenderBackendData&, const LayoutDeviceRect&,
                        const sRGBColor&);
 
   void PaintCheckboxControl(DrawTarget& aDrawTarget, const LayoutDeviceRect&,
-                            const EventStates&, UseSystemColors, DPIRatio);
+                            const EventStates&, const AccentColor&,
+                            UseSystemColors, DPIRatio);
   void PaintCheckMark(DrawTarget&, const LayoutDeviceRect&, const EventStates&,
-                      UseSystemColors);
+                      const AccentColor&, UseSystemColors);
   void PaintIndeterminateMark(DrawTarget&, const LayoutDeviceRect&,
-                              const EventStates&, UseSystemColors);
+                              const EventStates&, const AccentColor&,
+                              UseSystemColors);
 
   template <typename PaintBackendData>
   void PaintStrokedCircle(PaintBackendData&, const LayoutDeviceRect&,
                           const sRGBColor& aBackgroundColor,
                           const sRGBColor& aBorderColor,
                           const CSSCoord aBorderWidth, DPIRatio);
   void PaintCircleShadow(DrawTarget&, const LayoutDeviceRect& aBoxRect,
                          const LayoutDeviceRect& aClipRect, float aShadowAlpha,
@@ -280,49 +289,54 @@ class nsNativeBasicTheme : protected nsN
                          CSSCoord aShadowBlurStdDev, DPIRatio);
   void PaintCircleShadow(WebRenderBackendData&,
                          const LayoutDeviceRect& aBoxRect,
                          const LayoutDeviceRect& aClipRect, float aShadowAlpha,
                          const CSSPoint& aShadowOffset,
                          CSSCoord aShadowBlurStdDev, DPIRatio);
   template <typename PaintBackendData>
   void PaintRadioControl(PaintBackendData&, const LayoutDeviceRect&,
-                         const EventStates&, UseSystemColors, DPIRatio);
+                         const EventStates&, const AccentColor&,
+                         UseSystemColors, DPIRatio);
   template <typename PaintBackendData>
   void PaintRadioCheckmark(PaintBackendData&, const LayoutDeviceRect&,
                            const EventStates&, DPIRatio);
   template <typename PaintBackendData>
   void PaintTextField(PaintBackendData&, const LayoutDeviceRect&,
-                      const EventStates&, UseSystemColors, DPIRatio);
+                      const EventStates&, const AccentColor& aAccent,
+                      UseSystemColors, DPIRatio);
   template <typename PaintBackendData>
   void PaintListbox(PaintBackendData&, const LayoutDeviceRect&,
-                    const EventStates&, UseSystemColors, DPIRatio);
+                    const EventStates&, const AccentColor&, UseSystemColors,
+                    DPIRatio);
   template <typename PaintBackendData>
   void PaintMenulist(PaintBackendData&, const LayoutDeviceRect&,
-                     const EventStates&, UseSystemColors, DPIRatio);
+                     const EventStates&, const AccentColor&, UseSystemColors,
+                     DPIRatio);
   void PaintArrow(DrawTarget&, const LayoutDeviceRect&,
                   const float aArrowPolygonX[], const float aArrowPolygonY[],
                   const float aArrowPolygonSize, const int32_t aArrowNumPoints,
                   const sRGBColor aFillColor);
   void PaintMenulistArrowButton(nsIFrame*, DrawTarget&, const LayoutDeviceRect&,
                                 const EventStates&, UseSystemColors);
   void PaintSpinnerButton(nsIFrame*, DrawTarget&, const LayoutDeviceRect&,
                           const EventStates&, StyleAppearance, UseSystemColors,
                           DPIRatio);
   template <typename PaintBackendData>
   void PaintRange(nsIFrame*, PaintBackendData&, const LayoutDeviceRect&,
-                  const EventStates&, UseSystemColors, DPIRatio,
-                  bool aHorizontal);
+                  const EventStates&, const AccentColor&, UseSystemColors,
+                  DPIRatio, bool aHorizontal);
   template <typename PaintBackendData>
   void PaintProgress(nsIFrame*, PaintBackendData&, const LayoutDeviceRect&,
-                     const EventStates&, UseSystemColors, DPIRatio,
-                     bool aIsMeter);
+                     const EventStates&, const AccentColor&, UseSystemColors,
+                     DPIRatio, bool aIsMeter);
   template <typename PaintBackendData>
   void PaintButton(nsIFrame*, PaintBackendData&, const LayoutDeviceRect&,
-                   const EventStates&, UseSystemColors, DPIRatio);
+                   const EventStates&, const AccentColor&, UseSystemColors,
+                   DPIRatio);
 
   void PaintScrollbarButton(DrawTarget&, StyleAppearance,
                             const LayoutDeviceRect&, nsIFrame*,
                             const ComputedStyle&,
                             const EventStates& aElementState,
                             const EventStates& aDocumentState, UseSystemColors,
                             DPIRatio);
 
@@ -390,24 +404,16 @@ class nsNativeBasicTheme : protected nsN
                                  const EventStates& aDocumentState,
                                  UseSystemColors, DPIRatio);
   template <typename PaintBackendData>
   bool DoPaintDefaultScrollCorner(PaintBackendData&, const LayoutDeviceRect&,
                                   nsIFrame*, const ComputedStyle&,
                                   const EventStates& aDocumentState,
                                   UseSystemColors, DPIRatio);
 
-  static sRGBColor sAccentColor;
-  static sRGBColor sAccentColorForeground;
-
-  // Note that depending on the exact accent color, lighter/darker might really
-  // be inverted.
-  static sRGBColor sAccentColorLight;
-  static sRGBColor sAccentColorDark;
-  static sRGBColor sAccentColorDarker;
   static CSSIntCoord sHorizontalScrollbarHeight;
   static CSSIntCoord sVerticalScrollbarWidth;
   static bool sOverlayScrollbars;
 
   static void PrefChangedCallback(const char*, void*) { LookAndFeelChanged(); }
   static void RecomputeAccentColors();
   static void RecomputeScrollbarParams();
 };
--- a/widget/nsNativeTheme.cpp
+++ b/widget/nsNativeTheme.cpp
@@ -645,22 +645,17 @@ bool nsNativeTheme::IsDarkBackground(nsI
     scrollFrame = aFrame->GetScrollTargetFrame();
     aFrame = aFrame->GetParent();
   }
   if (!scrollFrame) {
     return false;
   }
 
   if (const auto* style = GetBackgroundStyle(scrollFrame->GetScrolledFrame())) {
-    nscolor bgColor = style->StyleBackground()->BackgroundColor(style);
-    // Consider the background color dark if the sum of the r, g and b values is
-    // less than 384 in a semi-transparent document.  This heuristic matches
-    // what WebKit does, and we can improve it later if needed.
-    return NS_GET_A(bgColor) > 127 &&
-           NS_GET_R(bgColor) + NS_GET_G(bgColor) + NS_GET_B(bgColor) < 384;
+    return IsDarkColor(style->StyleBackground()->BackgroundColor(style));
   }
   return false;
 }
 
 /*static*/
 bool nsNativeTheme::IsWidgetScrollbarPart(StyleAppearance aAppearance) {
   switch (aAppearance) {
     case StyleAppearance::ScrollbarVertical:
--- a/widget/nsNativeTheme.h
+++ b/widget/nsNativeTheme.h
@@ -179,20 +179,25 @@ class nsNativeTheme : public nsITimerCal
   bool QueueAnimatedContentForRefresh(nsIContent* aContent,
                                       uint32_t aMinimumFrameRate);
 
   nsIFrame* GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
                                                       bool aNextSibling);
 
   bool IsRangeHorizontal(nsIFrame* aFrame);
 
-  // scrollbar
   static bool IsDarkBackground(nsIFrame* aFrame);
-  // custom scrollbar
-  typedef nscolor (*AutoColorGetter)(mozilla::ComputedStyle*);
+  static bool IsDarkColor(nscolor aColor) {
+    // Consider a color dark if the sum of the r, g and b values is less than
+    // 384 in a semi-transparent document.  This heuristic matches what WebKit
+    // does, and we can improve it later if needed.
+    return NS_GET_A(aColor) > 127 &&
+           NS_GET_R(aColor) + NS_GET_G(aColor) + NS_GET_B(aColor) < 384;
+  }
+
   static bool IsWidgetScrollbarPart(mozilla::StyleAppearance aAppearance);
 
  private:
   uint32_t mAnimatedContentTimeout;
   nsCOMPtr<nsITimer> mAnimatedContentTimer;
   AutoTArray<nsCOMPtr<nsIContent>, 20> mAnimatedContentList;
 };