From 4395769c4ab7304d73cd9288d3838402a3b76a0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= <emilio@crisal.io>
Date: Wed, 9 Jun 2021 19:08:14 +0200
Subject: [PATCH] -moz-system-color()

---
 layout/style/GeckoBindings.cpp                | 18 ++++++-
 layout/style/GeckoBindings.h                  |  3 +-
 layout/style/ServoBindings.toml               |  1 +
 .../style/values/specified/color.rs           | 54 ++++++++++++++++---
 servo/ports/geckolib/cbindgen.toml            |  1 +
 widget/LookAndFeel.h                          |  2 +
 widget/nsXPLookAndFeel.cpp                    | 13 +++--
 7 files changed, 79 insertions(+), 13 deletions(-)

diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp
index dd1f4c0bf88aa..0118ec4665d06 100644
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -693,10 +693,24 @@ bool Gecko_IsDocumentBody(const Element* aElement) {
   return doc && doc->GetBodyElement() == aElement;
 }
 
-nscolor Gecko_GetLookAndFeelSystemColor(int32_t aId, const Document* aDoc) {
+nscolor Gecko_GetLookAndFeelSystemColor(int32_t aId, const Document* aDoc,
+                                        StyleSystemColorScheme aColorScheme) {
   auto colorId = static_cast<LookAndFeel::ColorID>(aId);
+  auto useStandins = LookAndFeel::ShouldUseStandins(*aDoc, colorId);
+  auto colorScheme = [&] {
+    switch (aColorScheme) {
+      case StyleSystemColorScheme::Default:
+        break;
+      case StyleSystemColorScheme::Light:
+        return LookAndFeel::ColorScheme::Light;
+      case StyleSystemColorScheme::Dark:
+        return LookAndFeel::ColorScheme::Dark;
+    }
+    return LookAndFeel::ColorSchemeForDocument(*aDoc);
+  }();
+
   AutoWriteLock guard(*sServoFFILock);
-  return LookAndFeel::Color(colorId, *aDoc);
+  return LookAndFeel::Color(colorId, colorScheme, useStandins);
 }
 
 int32_t Gecko_GetLookAndFeelInt(int32_t aId) {
diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h
index 79da76e80059e..ffdf2d6a8c6c9 100644
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -541,7 +541,8 @@ bool Gecko_IsDocumentBody(const mozilla::dom::Element* element);
 // We use an int32_t here instead of a LookAndFeel::ColorID
 // because forward-declaring a nested enum/struct is impossible
 nscolor Gecko_GetLookAndFeelSystemColor(int32_t color_id,
-                                        const mozilla::dom::Document*);
+                                        const mozilla::dom::Document*,
+                                        mozilla::StyleSystemColorScheme);
 
 int32_t Gecko_GetLookAndFeelInt(int32_t int_id);
 
diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml
index a5fb9966e0fbc..4e457e4a20e7d 100644
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -507,6 +507,7 @@ cbindgen-types = [
     { gecko = "StyleFontFamilyNameSyntax", servo = "crate::values::computed::font::FontFamilyNameSyntax" },
     { gecko = "StyleGenericColor", servo = "crate::values::generics::color::Color" },
     { gecko = "StyleSystemColor", servo = "crate::values::specified::color::SystemColor" },
+    { gecko = "StyleSystemColorScheme", servo = "crate::values::specified::color::SystemColorScheme" },
     { gecko = "StyleSystemFont", servo = "crate::values::specified::font::SystemFont" },
     { gecko = "StyleGenericColorOrAuto", servo = "crate::values::generics::color::ColorOrAuto" },
     { gecko = "StyleGenericScrollbarColor", servo = "crate::values::generics::ui::ScrollbarColor" },
diff --git a/servo/components/style/values/specified/color.rs b/servo/components/style/values/specified/color.rs
index 693cbd0c94d20..e489f17541c87 100644
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -119,6 +119,20 @@ impl ToCss for ColorMix {
     }
 }
 
+/// The color scheme for a specific system color.
+#[cfg(feature = "gecko")]
+#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
+#[repr(u8)]
+pub enum SystemColorScheme {
+    /// The default color-scheme for the document.
+    #[css(skip)]
+    Default,
+    /// A light color scheme.
+    Light,
+    /// A dark color scheme.
+    Dark,
+}
+
 /// Specified color value
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
 pub enum Color {
@@ -135,7 +149,7 @@ pub enum Color {
     Complex(ComputedColor),
     /// A system color
     #[cfg(feature = "gecko")]
-    System(SystemColor),
+    System(SystemColor, SystemColorScheme),
     /// A color mix.
     ColorMix(Box<ColorMix>),
     /// Quirksmode-only rule for inheriting color from the body
@@ -378,7 +392,7 @@ pub enum SystemColor {
 #[cfg(feature = "gecko")]
 impl SystemColor {
     #[inline]
-    fn compute(&self, cx: &Context) -> ComputedColor {
+    fn compute(&self, cx: &Context, scheme: SystemColorScheme) -> ComputedColor {
         use crate::gecko_bindings::bindings;
 
         let prefs = cx.device().pref_sheet_prefs();
@@ -391,7 +405,7 @@ impl SystemColor {
             SystemColor::Visitedtext => prefs.mVisitedLinkColor,
 
             _ => unsafe {
-                bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())
+                bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document(), scheme)
             },
         })
     }
@@ -467,6 +481,20 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
     }
 }
 
+fn parse_moz_system_color<'i, 't>(
+    context: &ParserContext,
+    input: &mut Parser<'i, 't>,
+) -> Result<(SystemColor, SystemColorScheme), ParseError<'i>> {
+    debug_assert!(context.chrome_rules_enabled());
+    input.expect_function_matching("-moz-system-color")?;
+    input.parse_nested_block(|input| {
+        let color = SystemColor::parse(context, input)?;
+        input.expect_comma()?;
+        let scheme = SystemColorScheme::parse(input)?;
+        Ok((color, scheme))
+    })
+}
+
 impl Parse for Color {
     fn parse<'i, 't>(
         context: &ParserContext,
@@ -492,7 +520,13 @@ impl Parse for Color {
                 #[cfg(feature = "gecko")]
                 {
                     if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) {
-                        return Ok(Color::System(system));
+                        return Ok(Color::System(system, SystemColorScheme::Default));
+                    }
+
+                    if context.chrome_rules_enabled() {
+                        if let Ok((color, scheme)) = input.try_parse(|i| parse_moz_system_color(context, i)) {
+                            return Ok(Color::System(color, scheme));
+                        }
                     }
                 }
 
@@ -531,7 +565,15 @@ impl ToCss for Color {
             Color::Complex(_) => Ok(()),
             Color::ColorMix(ref mix) => mix.to_css(dest),
             #[cfg(feature = "gecko")]
-            Color::System(system) => system.to_css(dest),
+            Color::System(system, scheme) => {
+                if scheme == SystemColorScheme::Default {
+                    system.to_css(dest)
+                } else {
+                    dest.write_str("-moz-system-color(")?;
+                    system.to_css(dest)?;
+                    dest.write_str(")")
+                }
+            }
             #[cfg(feature = "gecko")]
             Color::InheritFromBodyQuirk => Ok(()),
         }
@@ -690,7 +732,7 @@ impl Color {
                 ))
             },
             #[cfg(feature = "gecko")]
-            Color::System(system) => system.compute(context?),
+            Color::System(system, scheme) => system.compute(context?, scheme),
             #[cfg(feature = "gecko")]
             Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()),
         })
diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml
index d4cb4578f7e4c..8597c31e083be 100644
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -162,6 +162,7 @@ include = [
   "Color",
   "ColorOrAuto",
   "SystemColor",
+  "SystemColorScheme",
   "SystemFont",
   "GradientItem",
   "VerticalAlign",
diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h
index 023c401bcc54e..6157b662086e8 100644
--- a/widget/LookAndFeel.h
+++ b/widget/LookAndFeel.h
@@ -32,6 +32,7 @@ class FullLookAndFeel;
 }  // namespace widget
 
 enum class StyleSystemColor : uint8_t;
+enum class StyleSystemColorScheme : uint8_t;
 enum class StyleSystemFont : uint8_t;
 
 class LookAndFeel {
@@ -435,6 +436,7 @@ class LookAndFeel {
   // Whether standins for native colors should be used (that is, colors faked,
   // taken from win7, mostly). This forces light appearance, effectively.
   enum class UseStandins : bool { No, Yes };
+  static UseStandins ShouldUseStandins(const dom::Document&, ColorID);
 
   // Returns a native color value (might be overwritten by prefs) for a given
   // color id.
diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp
index 9298c5d5207f9..e078da88fab36 100644
--- a/widget/nsXPLookAndFeel.cpp
+++ b/widget/nsXPLookAndFeel.cpp
@@ -1028,12 +1028,17 @@ static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
   return true;
 }
 
-Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const dom::Document& aDoc) {
-  const bool useStandins =
+LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
+    const dom::Document& aDoc, ColorID aId) {
+  return UseStandins(
       ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId) ||
       (nsContentUtils::UseStandinsForNativeColors() &&
-       !nsContentUtils::IsChromeDoc(&aDoc) && ColorIsCSSAccessible(aId));
-  return GetColor(aId, ColorSchemeForDocument(aDoc), UseStandins(useStandins));
+       !nsContentUtils::IsChromeDoc(&aDoc) && ColorIsCSSAccessible(aId)));
+}
+
+Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const dom::Document& aDoc) {
+  return GetColor(aId, ColorSchemeForDocument(aDoc),
+                  ShouldUseStandins(aDoc, aId));
 }
 
 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
-- 
2.31.1

