font_palette_values_rule.rs (9874B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! The [`@font-palette-values`][font-palette-values] at-rule. 6 //! 7 //! [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values 8 9 use crate::derives::*; 10 use crate::error_reporting::ContextualParseError; 11 #[cfg(feature = "gecko")] 12 use 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 }; 19 use crate::parser::{Parse, ParserContext}; 20 use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; 21 use crate::stylesheets::font_feature_values_rule::parse_family_name_list; 22 use crate::values::computed::font::FamilyName; 23 use crate::values::specified::Color as SpecifiedColor; 24 use crate::values::specified::NonNegativeInteger; 25 use crate::values::DashedIdent; 26 use cssparser::{ 27 match_ignore_ascii_case, AtRuleParser, CowRcStr, DeclarationParser, Parser, ParserState, 28 QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, 29 }; 30 use selectors::parser::SelectorParseErrorKind; 31 use std::fmt::{self, Write}; 32 use style_traits::{Comma, OneOrMoreSeparated}; 33 use style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss}; 34 35 #[allow(missing_docs)] 36 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] 37 pub struct FontPaletteOverrideColor { 38 index: NonNegativeInteger, 39 color: SpecifiedColor, 40 } 41 42 impl 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 // Only absolute colors are accepted here: 51 // https://drafts.csswg.org/css-fonts/#override-color 52 // https://drafts.csswg.org/css-color-5/#absolute-color 53 // so check that the specified color can be resolved without a context 54 // or currentColor value. 55 if color.resolve_to_absolute().is_some() { 56 // We store the specified color (not the resolved absolute color) 57 // because that is what the rule exposes to authors. 58 return Ok(FontPaletteOverrideColor { index, color }); 59 } 60 Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 61 } 62 } 63 64 impl 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 75 impl OneOrMoreSeparated for FontPaletteOverrideColor { 76 type S = Comma; 77 } 78 79 impl OneOrMoreSeparated for FamilyName { 80 type S = Comma; 81 } 82 83 #[allow(missing_docs)] 84 #[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] 85 pub enum FontPaletteBase { 86 Light, 87 Dark, 88 Index(NonNegativeInteger), 89 } 90 91 /// The [`@font-palette-values`][font-palette-values] at-rule. 92 /// 93 /// [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values 94 #[derive(Clone, Debug, PartialEq, ToShmem)] 95 pub struct FontPaletteValuesRule { 96 /// Palette name. 97 pub name: DashedIdent, 98 /// Font family list for @font-palette-values rule. 99 /// Family names cannot contain generic families. FamilyName 100 /// also accepts only non-generic names. 101 pub family_names: Vec<FamilyName>, 102 /// The base palette. 103 pub base_palette: Option<FontPaletteBase>, 104 /// The list of override colors. 105 pub override_colors: Vec<FontPaletteOverrideColor>, 106 /// The line and column of the rule's source code. 107 pub source_location: SourceLocation, 108 } 109 110 impl FontPaletteValuesRule { 111 /// Creates an empty FontPaletteValuesRule with given location and name. 112 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 /// Parses a `FontPaletteValuesRule`. 123 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 /// Prints inside of `@font-palette-values` block. 147 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 /// Convert to Gecko FontPaletteValueSet. 170 #[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 // We checked at parse time that the specified color can be resolved 191 // in this way, so the unwrap() here will succeed. 192 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 205 impl 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 215 /// Parser for declarations in `FontPaletteValuesRule`. 216 struct FontPaletteValuesDeclarationParser<'a> { 217 context: &'a ParserContext<'a>, 218 rule: &'a mut FontPaletteValuesRule, 219 } 220 221 impl<'a, 'i> AtRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> { 222 type Prelude = (); 223 type AtRule = (); 224 type Error = StyleParseErrorKind<'i>; 225 } 226 227 impl<'a, 'i> QualifiedRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> { 228 type Prelude = (); 229 type QualifiedRule = (); 230 type Error = StyleParseErrorKind<'i>; 231 } 232 233 fn 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 240 impl<'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 266 impl<'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 }