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