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