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.resolve_to_absolute(None).is_ok() {
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.color.resolve_to_absolute(None).unwrap();
221 let index = c.index.0.resolve().unwrap();
223 unsafe {
224 Gecko_SetFontPaletteOverride(
225 palette_values,
226 index,
227 (&absolute) as *const _ as *mut _,
228 );
229 }
230 }
231 }
232 }
233}
234
235impl ToCssWithGuard for FontPaletteValuesRule {
236 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
237 dest.write_str("@font-palette-values ")?;
238 self.name.to_css(&mut CssWriter::new(dest))?;
239 dest.write_str(" { ")?;
240 self.value_to_css(&mut CssWriter::new(dest))?;
241 dest.write_char('}')
242 }
243}
244
245struct FontPaletteValuesDeclarationParser<'a> {
247 context: &'a ParserContext<'a>,
248 rule: &'a mut FontPaletteValuesRule,
249}
250
251impl<'a, 'i> AtRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
252 type Prelude = ();
253 type AtRule = ();
254 type Error = StyleParseErrorKind<'i>;
255}
256
257impl<'a, 'i> QualifiedRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
258 type Prelude = ();
259 type QualifiedRule = ();
260 type Error = StyleParseErrorKind<'i>;
261}
262
263fn parse_override_colors<'i, 't>(
264 context: &ParserContext,
265 input: &mut Parser<'i, 't>,
266) -> Result<Vec<FontPaletteOverrideColor>, ParseError<'i>> {
267 input.parse_comma_separated(|i| FontPaletteOverrideColor::parse(context, i))
268}
269
270impl<'a, 'b, 'i> DeclarationParser<'i> for FontPaletteValuesDeclarationParser<'a> {
271 type Declaration = ();
272 type Error = StyleParseErrorKind<'i>;
273
274 fn parse_value<'t>(
275 &mut self,
276 name: CowRcStr<'i>,
277 input: &mut Parser<'i, 't>,
278 _declaration_start: &ParserState,
279 ) -> Result<(), ParseError<'i>> {
280 match_ignore_ascii_case! { &*name,
281 "font-family" => {
282 self.rule.family_names = parse_family_name_list(self.context, input)?
283 },
284 "base-palette" => {
285 self.rule.base_palette = Some(input.parse_entirely(|i| FontPaletteBase::parse(self.context, i))?)
286 },
287 "override-colors" => {
288 self.rule.override_colors = parse_override_colors(self.context, input)?
289 },
290 _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
291 }
292 Ok(())
293 }
294}
295
296impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
297 for FontPaletteValuesDeclarationParser<'a>
298{
299 fn parse_declarations(&self) -> bool {
300 true
301 }
302 fn parse_qualified(&self) -> bool {
303 false
304 }
305}