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<'i, 't>(
44 context: &ParserContext,
45 input: &mut Parser<'i, 't>,
46 ) -> Result<FontPaletteOverrideColor, ParseError<'i>> {
47 let location = input.current_source_location();
48 let index = NonNegativeInteger::parse(context, input)?;
49 if index.0.resolve().is_none() {
50 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
51 }
52
53 let color = SpecifiedColor::parse(context, input)?;
54 if color.to_computed_color(None).is_ok_and(|c| c.is_absolute()) {
60 return Ok(FontPaletteOverrideColor { index, color });
63 }
64 Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
65 }
66}
67
68impl ToCss for FontPaletteOverrideColor {
69 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
70 where
71 W: fmt::Write,
72 {
73 self.index.to_css(dest)?;
74 dest.write_char(' ')?;
75 self.color.to_css(dest)
76 }
77}
78
79impl OneOrMoreSeparated for FontPaletteOverrideColor {
80 type S = Comma;
81}
82
83impl OneOrMoreSeparated for FamilyName {
84 type S = Comma;
85}
86
87#[allow(missing_docs)]
88#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
89pub enum FontPaletteBase {
90 Light,
91 Dark,
92 Index(NonNegativeInteger),
93}
94
95impl Parse for FontPaletteBase {
96 #[inline]
97 fn parse<'i, 't>(
98 context: &ParserContext,
99 input: &mut Parser<'i, 't>,
100 ) -> Result<Self, ParseError<'i>> {
101 let location = input.current_source_location();
102 if let Ok(v) = input.try_parse(|input| NonNegativeInteger::parse(context, input)) {
103 if v.0.resolve().is_none() {
104 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
105 }
106 return Ok(FontPaletteBase::Index(v));
107 }
108
109 let ident = input.expect_ident()?;
110 match_ignore_ascii_case! { &ident,
111 "light" => Ok(FontPaletteBase::Light),
112 "dark" => Ok(FontPaletteBase::Dark),
113 _ => Err(location.new_unexpected_token_error(cssparser::Token::Ident(ident.clone())))
114 }
115 }
116}
117
118#[derive(Clone, Debug, PartialEq, ToShmem)]
122pub struct FontPaletteValuesRule {
123 pub name: DashedIdent,
125 pub family_names: Vec<FamilyName>,
129 pub base_palette: Option<FontPaletteBase>,
131 pub override_colors: Vec<FontPaletteOverrideColor>,
133 pub source_location: SourceLocation,
135}
136
137impl FontPaletteValuesRule {
138 fn new(name: DashedIdent, location: SourceLocation) -> Self {
140 FontPaletteValuesRule {
141 name,
142 family_names: vec![],
143 base_palette: None,
144 override_colors: vec![],
145 source_location: location,
146 }
147 }
148
149 pub fn parse(
151 context: &ParserContext,
152 input: &mut Parser,
153 name: DashedIdent,
154 location: SourceLocation,
155 ) -> Self {
156 let mut rule = FontPaletteValuesRule::new(name, location);
157 let mut parser = FontPaletteValuesDeclarationParser {
158 context,
159 rule: &mut rule,
160 };
161 let mut iter = RuleBodyParser::new(input, &mut parser);
162 while let Some(declaration) = iter.next() {
163 if let Err((error, slice)) = declaration {
164 let location = error.location;
165 let error =
166 ContextualParseError::UnsupportedFontPaletteValuesDescriptor(slice, error);
167 context.log_css_error(location, error);
168 }
169 }
170 rule
171 }
172
173 fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
175 where
176 W: Write,
177 {
178 if !self.family_names.is_empty() {
179 dest.write_str("font-family: ")?;
180 self.family_names.to_css(dest)?;
181 dest.write_str("; ")?;
182 }
183 if let Some(base) = &self.base_palette {
184 dest.write_str("base-palette: ")?;
185 base.to_css(dest)?;
186 dest.write_str("; ")?;
187 }
188 if !self.override_colors.is_empty() {
189 dest.write_str("override-colors: ")?;
190 self.override_colors.to_css(dest)?;
191 dest.write_str("; ")?;
192 }
193 Ok(())
194 }
195
196 #[cfg(feature = "gecko")]
198 pub fn to_gecko_palette_value_set(&self, dest: *mut FontPaletteValueSet) {
199 for ref family in self.family_names.iter() {
200 let family = family.name.to_ascii_lowercase();
201 let palette_values = unsafe {
202 Gecko_AppendPaletteValueHashEntry(dest, family.as_ptr(), self.name.0.as_ptr())
203 };
204 if let Some(base_palette) = &self.base_palette {
205 unsafe {
206 Gecko_SetFontPaletteBase(
207 palette_values,
208 match &base_palette {
209 FontPaletteBase::Light => FontPaletteValueSet_PaletteValues_kLight,
210 FontPaletteBase::Dark => FontPaletteValueSet_PaletteValues_kDark,
211 FontPaletteBase::Index(i) => i.0.resolve().unwrap(),
213 },
214 );
215 }
216 }
217 for c in &self.override_colors {
218 let absolute = c
221 .color
222 .to_computed_color(None)
223 .ok()
224 .and_then(|c| c.as_absolute().copied())
225 .unwrap();
226 let index = c.index.0.resolve().unwrap();
228 unsafe {
229 Gecko_SetFontPaletteOverride(
230 palette_values,
231 index,
232 (&absolute) as *const _ as *mut _,
233 );
234 }
235 }
236 }
237 }
238}
239
240impl ToCssWithGuard for FontPaletteValuesRule {
241 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
242 dest.write_str("@font-palette-values ")?;
243 self.name.to_css(&mut CssWriter::new(dest))?;
244 dest.write_str(" { ")?;
245 self.value_to_css(&mut CssWriter::new(dest))?;
246 dest.write_char('}')
247 }
248}
249
250struct FontPaletteValuesDeclarationParser<'a> {
252 context: &'a ParserContext<'a>,
253 rule: &'a mut FontPaletteValuesRule,
254}
255
256impl<'a, 'i> AtRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
257 type Prelude = ();
258 type AtRule = ();
259 type Error = StyleParseErrorKind<'i>;
260}
261
262impl<'a, 'i> QualifiedRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
263 type Prelude = ();
264 type QualifiedRule = ();
265 type Error = StyleParseErrorKind<'i>;
266}
267
268fn parse_override_colors<'i, 't>(
269 context: &ParserContext,
270 input: &mut Parser<'i, 't>,
271) -> Result<Vec<FontPaletteOverrideColor>, ParseError<'i>> {
272 input.parse_comma_separated(|i| FontPaletteOverrideColor::parse(context, i))
273}
274
275impl<'a, 'b, 'i> DeclarationParser<'i> for FontPaletteValuesDeclarationParser<'a> {
276 type Declaration = ();
277 type Error = StyleParseErrorKind<'i>;
278
279 fn parse_value<'t>(
280 &mut self,
281 name: CowRcStr<'i>,
282 input: &mut Parser<'i, 't>,
283 _declaration_start: &ParserState,
284 ) -> Result<(), ParseError<'i>> {
285 match_ignore_ascii_case! { &*name,
286 "font-family" => {
287 self.rule.family_names = parse_family_name_list(self.context, input)?
288 },
289 "base-palette" => {
290 self.rule.base_palette = Some(input.parse_entirely(|i| FontPaletteBase::parse(self.context, i))?)
291 },
292 "override-colors" => {
293 self.rule.override_colors = parse_override_colors(self.context, input)?
294 },
295 _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
296 }
297 Ok(())
298 }
299}
300
301impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
302 for FontPaletteValuesDeclarationParser<'a>
303{
304 fn parse_declarations(&self) -> bool {
305 true
306 }
307 fn parse_qualified(&self) -> bool {
308 false
309 }
310}