1use crate::derives::*;
10use crate::error_reporting::ContextualParseError;
11#[cfg(feature = "gecko")]
12use crate::gecko_bindings::{
13 bindings::Gecko_AppendPaletteValueHashEntry,
14 bindings::{Gecko_SetFontPaletteBase, Gecko_SetFontPaletteOverride},
15 structs::gfx::FontPaletteValueSet,
16 structs::gfx::FontPaletteValueSet_PaletteValues_kDark,
17 structs::gfx::FontPaletteValueSet_PaletteValues_kLight,
18};
19use crate::parser::{Parse, ParserContext};
20use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
21use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
22use crate::values::computed::font::FamilyName;
23use crate::values::specified::Color as SpecifiedColor;
24use crate::values::specified::NonNegativeInteger;
25use crate::values::DashedIdent;
26use cssparser::{
27 match_ignore_ascii_case, AtRuleParser, CowRcStr, DeclarationParser, Parser, ParserState,
28 QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
29};
30use selectors::parser::SelectorParseErrorKind;
31use std::fmt::{self, Write};
32use style_traits::{Comma, OneOrMoreSeparated};
33use style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};
34
35#[allow(missing_docs)]
36#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
37pub struct FontPaletteOverrideColor {
38 index: NonNegativeInteger,
39 color: SpecifiedColor,
40}
41
42impl Parse for FontPaletteOverrideColor {
43 fn parse(
44 context: &ParserContext,
45 input: &mut Parser,
46 ) -> Result<FontPaletteOverrideColor, ParseError> {
47 let index = NonNegativeInteger::parse(context, input)?;
48 if index.0.resolve().is_none() {
49 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
50 }
51
52 let color = SpecifiedColor::parse(context, input)?;
53 if color.to_computed_color(None).is_ok_and(|c| c.is_absolute()) {
59 return Ok(FontPaletteOverrideColor { index, color });
62 }
63 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
64 }
65}
66
67impl ToCss for FontPaletteOverrideColor {
68 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
69 where
70 W: fmt::Write,
71 {
72 self.index.to_css(dest)?;
73 dest.write_char(' ')?;
74 self.color.to_css(dest)
75 }
76}
77
78impl OneOrMoreSeparated for FontPaletteOverrideColor {
79 type S = Comma;
80}
81
82impl OneOrMoreSeparated for FamilyName {
83 type S = Comma;
84}
85
86#[allow(missing_docs)]
87#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
88pub enum FontPaletteBase {
89 Light,
90 Dark,
91 Index(NonNegativeInteger),
92}
93
94impl Parse for FontPaletteBase {
95 #[inline]
96 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
97 if let Ok(v) = input.try_parse(|input| NonNegativeInteger::parse(context, input)) {
98 if v.0.resolve().is_none() {
99 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
100 }
101 return Ok(FontPaletteBase::Index(v));
102 }
103
104 let ident = input.expect_ident()?;
105 match_ignore_ascii_case! { &ident,
106 "light" => Ok(FontPaletteBase::Light),
107 "dark" => Ok(FontPaletteBase::Dark),
108 _ => Err(ParseError::unexpected_token())
109 }
110 }
111}
112
113#[derive(Clone, Debug, PartialEq, ToShmem)]
117pub struct FontPaletteValuesRule {
118 pub name: DashedIdent,
120 pub family_names: Vec<FamilyName>,
124 pub base_palette: Option<FontPaletteBase>,
126 pub override_colors: Vec<FontPaletteOverrideColor>,
128 pub source_location: SourceLocation,
130}
131
132impl FontPaletteValuesRule {
133 fn new(name: DashedIdent, location: SourceLocation) -> Self {
135 FontPaletteValuesRule {
136 name,
137 family_names: vec![],
138 base_palette: None,
139 override_colors: vec![],
140 source_location: location,
141 }
142 }
143
144 pub fn parse(
146 context: &ParserContext,
147 input: &mut Parser,
148 name: DashedIdent,
149 location: SourceLocation,
150 ) -> Self {
151 let mut rule = FontPaletteValuesRule::new(name, location);
152 let mut parser = FontPaletteValuesDeclarationParser {
153 context,
154 rule: &mut rule,
155 };
156 let iter = RuleBodyParser::new(input, &mut parser);
157 for declaration in iter {
158 if let Err((error, slice, location)) = declaration {
159 let error =
160 ContextualParseError::UnsupportedFontPaletteValuesDescriptor(slice, error);
161 context.log_css_error(location, error);
162 }
163 }
164 rule
165 }
166
167 fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
169 where
170 W: Write,
171 {
172 if !self.family_names.is_empty() {
173 dest.write_str("font-family: ")?;
174 self.family_names.to_css(dest)?;
175 dest.write_str("; ")?;
176 }
177 if let Some(base) = &self.base_palette {
178 dest.write_str("base-palette: ")?;
179 base.to_css(dest)?;
180 dest.write_str("; ")?;
181 }
182 if !self.override_colors.is_empty() {
183 dest.write_str("override-colors: ")?;
184 self.override_colors.to_css(dest)?;
185 dest.write_str("; ")?;
186 }
187 Ok(())
188 }
189
190 #[cfg(feature = "gecko")]
192 pub fn to_gecko_palette_value_set(&self, dest: *mut FontPaletteValueSet) {
193 for family in self.family_names.iter() {
194 let family = family.name.to_ascii_lowercase();
195 let palette_values = unsafe {
196 Gecko_AppendPaletteValueHashEntry(dest, family.as_ptr(), self.name.0.as_ptr())
197 };
198 if let Some(base_palette) = &self.base_palette {
199 unsafe {
200 Gecko_SetFontPaletteBase(
201 palette_values,
202 match &base_palette {
203 FontPaletteBase::Light => FontPaletteValueSet_PaletteValues_kLight,
204 FontPaletteBase::Dark => FontPaletteValueSet_PaletteValues_kDark,
205 FontPaletteBase::Index(i) => i.0.resolve().unwrap(),
207 },
208 );
209 }
210 }
211 for c in &self.override_colors {
212 let absolute = c
215 .color
216 .to_computed_color(None)
217 .ok()
218 .and_then(|c| c.as_absolute().copied())
219 .unwrap();
220 let index = c.index.0.resolve().unwrap();
222 unsafe {
223 Gecko_SetFontPaletteOverride(
224 palette_values,
225 index,
226 (&absolute) as *const _ as *mut _,
227 );
228 }
229 }
230 }
231 }
232}
233
234impl ToCssWithGuard for FontPaletteValuesRule {
235 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
236 dest.write_str("@font-palette-values ")?;
237 self.name.to_css(&mut CssWriter::new(dest))?;
238 dest.write_str(" { ")?;
239 self.value_to_css(&mut CssWriter::new(dest))?;
240 dest.write_char('}')
241 }
242}
243
244struct FontPaletteValuesDeclarationParser<'a> {
246 context: &'a ParserContext<'a>,
247 rule: &'a mut FontPaletteValuesRule,
248}
249
250impl<'a, 'i> AtRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
251 type Prelude = ();
252 type AtRule = ();
253 type Error = StyleParseErrorKind;
254}
255
256impl<'a, 'i> QualifiedRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
257 type Prelude = ();
258 type QualifiedRule = ();
259 type Error = StyleParseErrorKind;
260}
261
262fn parse_override_colors(
263 context: &ParserContext,
264 input: &mut Parser,
265) -> Result<Vec<FontPaletteOverrideColor>, ParseError> {
266 input.parse_comma_separated(|i| FontPaletteOverrideColor::parse(context, i))
267}
268
269impl<'a, 'b, 'i> DeclarationParser<'i> for FontPaletteValuesDeclarationParser<'a> {
270 type Declaration = ();
271 type Error = StyleParseErrorKind;
272
273 fn parse_value(
274 &mut self,
275 name: CowRcStr<'i>,
276 input: &mut Parser<'i>,
277 _declaration_start: &ParserState,
278 ) -> Result<(), ParseError> {
279 match_ignore_ascii_case! { &*name,
280 "font-family" => {
281 self.rule.family_names = parse_family_name_list(self.context, input)?
282 },
283 "base-palette" => {
284 self.rule.base_palette = Some(input.parse_entirely(|i| FontPaletteBase::parse(self.context, i))?)
285 },
286 "override-colors" => {
287 self.rule.override_colors = parse_override_colors(self.context, input)?
288 },
289 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
290 }
291 Ok(())
292 }
293}
294
295impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind>
296 for FontPaletteValuesDeclarationParser<'a>
297{
298 fn parse_declarations(&self) -> bool {
299 true
300 }
301 fn parse_qualified(&self) -> bool {
302 false
303 }
304}