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-feature-values`][font-feature-values] at-rule.
6 //!
7 //! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
8 
9 use crate::error_reporting::ContextualParseError;
10 #[cfg(feature = "gecko")]
11 use crate::gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
12 #[cfg(feature = "gecko")]
13 use crate::gecko_bindings::structs::{self, gfxFontFeatureValueSet, nsTArray};
14 use crate::parser::{Parse, ParserContext};
15 use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
16 use crate::str::CssStringWriter;
17 use crate::stylesheets::CssRuleType;
18 use crate::values::computed::font::FamilyName;
19 use crate::values::serialize_atom_identifier;
20 use crate::Atom;
21 use cssparser::{AtRuleParser, BasicParseErrorKind, CowRcStr};
22 use cssparser::{DeclarationListParser, DeclarationParser, Parser};
23 use cssparser::{ParserState, QualifiedRuleParser, RuleListParser, SourceLocation, Token};
24 use std::fmt::{self, Write};
25 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
26 
27 /// A @font-feature-values block declaration.
28 /// It is `<ident>: <integer>+`.
29 /// This struct can take 3 value types.
30 /// - `SingleValue` is to keep just one unsigned integer value.
31 /// - `PairValues` is to keep one or two unsigned integer values.
32 /// - `VectorValues` is to keep a list of unsigned integer values.
33 #[derive(Clone, Debug, PartialEq, ToShmem)]
34 pub struct FFVDeclaration<T> {
35     /// An `<ident>` for declaration name.
36     pub name: Atom,
37     /// An `<integer>+` for declaration value.
38     pub value: T,
39 }
40 
41 impl<T: ToCss> ToCss for FFVDeclaration<T> {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,42     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
43     where
44         W: Write,
45     {
46         serialize_atom_identifier(&self.name, dest)?;
47         dest.write_str(": ")?;
48         self.value.to_css(dest)?;
49         dest.write_str(";")
50     }
51 }
52 
53 /// A trait for @font-feature-values rule to gecko values conversion.
54 #[cfg(feature = "gecko")]
55 pub trait ToGeckoFontFeatureValues {
56     /// Sets the equivalent of declaration to gecko `nsTArray<u32>` array.
to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>)57     fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>);
58 }
59 
60 /// A @font-feature-values block declaration value that keeps one value.
61 #[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
62 pub struct SingleValue(pub u32);
63 
64 impl Parse for SingleValue {
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<SingleValue, ParseError<'i>>65     fn parse<'i, 't>(
66         _context: &ParserContext,
67         input: &mut Parser<'i, 't>,
68     ) -> Result<SingleValue, ParseError<'i>> {
69         let location = input.current_source_location();
70         match *input.next()? {
71             Token::Number {
72                 int_value: Some(v), ..
73             } if v >= 0 => Ok(SingleValue(v as u32)),
74             ref t => Err(location.new_unexpected_token_error(t.clone())),
75         }
76     }
77 }
78 
79 #[cfg(feature = "gecko")]
80 impl ToGeckoFontFeatureValues for SingleValue {
to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>)81     fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
82         unsafe {
83             array.set_len_pod(1);
84         }
85         array[0] = self.0 as u32;
86     }
87 }
88 
89 /// A @font-feature-values block declaration value that keeps one or two values.
90 #[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
91 pub struct PairValues(pub u32, pub Option<u32>);
92 
93 impl Parse for PairValues {
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<PairValues, ParseError<'i>>94     fn parse<'i, 't>(
95         _context: &ParserContext,
96         input: &mut Parser<'i, 't>,
97     ) -> Result<PairValues, ParseError<'i>> {
98         let location = input.current_source_location();
99         let first = match *input.next()? {
100             Token::Number {
101                 int_value: Some(a), ..
102             } if a >= 0 => a as u32,
103             ref t => return Err(location.new_unexpected_token_error(t.clone())),
104         };
105         let location = input.current_source_location();
106         match input.next() {
107             Ok(&Token::Number {
108                 int_value: Some(b), ..
109             }) if b >= 0 => Ok(PairValues(first, Some(b as u32))),
110             // It can't be anything other than number.
111             Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
112             // It can be just one value.
113             Err(_) => Ok(PairValues(first, None)),
114         }
115     }
116 }
117 
118 #[cfg(feature = "gecko")]
119 impl ToGeckoFontFeatureValues for PairValues {
to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>)120     fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
121         let len = if self.1.is_some() { 2 } else { 1 };
122 
123         unsafe {
124             array.set_len_pod(len);
125         }
126         array[0] = self.0 as u32;
127         if let Some(second) = self.1 {
128             array[1] = second as u32;
129         };
130     }
131 }
132 
133 /// A @font-feature-values block declaration value that keeps a list of values.
134 #[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
135 pub struct VectorValues(#[css(iterable)] pub Vec<u32>);
136 
137 impl Parse for VectorValues {
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<VectorValues, ParseError<'i>>138     fn parse<'i, 't>(
139         _context: &ParserContext,
140         input: &mut Parser<'i, 't>,
141     ) -> Result<VectorValues, ParseError<'i>> {
142         let mut vec = vec![];
143         loop {
144             let location = input.current_source_location();
145             match input.next() {
146                 Ok(&Token::Number {
147                     int_value: Some(a), ..
148                 }) if a >= 0 => {
149                     vec.push(a as u32);
150                 },
151                 // It can't be anything other than number.
152                 Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
153                 Err(_) => break,
154             }
155         }
156 
157         if vec.len() == 0 {
158             return Err(input.new_error(BasicParseErrorKind::EndOfInput));
159         }
160 
161         Ok(VectorValues(vec))
162     }
163 }
164 
165 #[cfg(feature = "gecko")]
166 impl ToGeckoFontFeatureValues for VectorValues {
to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>)167     fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
168         array.assign_from_iter_pod(self.0.iter().map(|v| *v));
169     }
170 }
171 
172 /// Parses a list of `FamilyName`s.
parse_family_name_list<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Vec<FamilyName>, ParseError<'i>>173 pub fn parse_family_name_list<'i, 't>(
174     context: &ParserContext,
175     input: &mut Parser<'i, 't>,
176 ) -> Result<Vec<FamilyName>, ParseError<'i>> {
177     input
178         .parse_comma_separated(|i| FamilyName::parse(context, i))
179         .map_err(|e| e.into())
180 }
181 
182 /// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`.
183 /// (`<ident>: <integer>+`)
184 struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> {
185     context: &'a ParserContext<'b>,
186     declarations: &'a mut Vec<FFVDeclaration<T>>,
187 }
188 
189 /// Default methods reject all at rules.
190 impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
191     type Prelude = ();
192     type AtRule = ();
193     type Error = StyleParseErrorKind<'i>;
194 }
195 
196 impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>
197 where
198     T: Parse,
199 {
200     type Declaration = ();
201     type Error = StyleParseErrorKind<'i>;
202 
parse_value<'t>( &mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>203     fn parse_value<'t>(
204         &mut self,
205         name: CowRcStr<'i>,
206         input: &mut Parser<'i, 't>,
207     ) -> Result<(), ParseError<'i>> {
208         let value = input.parse_entirely(|i| T::parse(self.context, i))?;
209         let new = FFVDeclaration {
210             name: Atom::from(&*name),
211             value: value,
212         };
213         update_or_push(&mut self.declarations, new);
214         Ok(())
215     }
216 }
217 
218 macro_rules! font_feature_values_blocks {
219     (
220         blocks = [
221             $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident / $gecko_enum: ident: $ty: ty, )*
222         ]
223     ) => {
224         /// The [`@font-feature-values`][font-feature-values] at-rule.
225         ///
226         /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
227         #[derive(Clone, Debug, PartialEq, ToShmem)]
228         pub struct FontFeatureValuesRule {
229             /// Font family list for @font-feature-values rule.
230             /// Family names cannot contain generic families. FamilyName
231             /// also accepts only non-generic names.
232             pub family_names: Vec<FamilyName>,
233             $(
234                 #[$doc]
235                 pub $ident: Vec<FFVDeclaration<$ty>>,
236             )*
237             /// The line and column of the rule's source code.
238             pub source_location: SourceLocation,
239         }
240 
241         impl FontFeatureValuesRule {
242             /// Creates an empty FontFeatureValuesRule with given location and family name list.
243             fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self {
244                 FontFeatureValuesRule {
245                     family_names: family_names,
246                     $(
247                         $ident: vec![],
248                     )*
249                     source_location: location,
250                 }
251             }
252 
253             /// Parses a `FontFeatureValuesRule`.
254             pub fn parse(
255                 context: &ParserContext,
256                 input: &mut Parser,
257                 family_names: Vec<FamilyName>,
258                 location: SourceLocation,
259             ) -> Self {
260                 let mut rule = FontFeatureValuesRule::new(family_names, location);
261 
262                 {
263                     let mut iter = RuleListParser::new_for_nested_rule(input, FontFeatureValuesRuleParser {
264                         context: context,
265                         rule: &mut rule,
266                     });
267                     while let Some(result) = iter.next() {
268                         if let Err((error, slice)) = result {
269                             let location = error.location;
270                             let error = ContextualParseError::UnsupportedRule(slice, error);
271                             context.log_css_error(location, error);
272                         }
273                     }
274                 }
275                 rule
276             }
277 
278             /// Prints font family names.
279             pub fn font_family_to_css<W>(
280                 &self,
281                 dest: &mut CssWriter<W>,
282             ) -> fmt::Result
283             where
284                 W: Write,
285             {
286                 let mut iter = self.family_names.iter();
287                 iter.next().unwrap().to_css(dest)?;
288                 for val in iter {
289                     dest.write_str(", ")?;
290                     val.to_css(dest)?;
291                 }
292                 Ok(())
293             }
294 
295             /// Prints inside of `@font-feature-values` block.
296             pub fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
297             where
298                 W: Write,
299             {
300                 $(
301                     if self.$ident.len() > 0 {
302                         dest.write_str(concat!("@", $name, " {\n"))?;
303                         let iter = self.$ident.iter();
304                         for val in iter {
305                             val.to_css(dest)?;
306                             dest.write_str("\n")?
307                         }
308                         dest.write_str("}\n")?
309                     }
310                 )*
311                 Ok(())
312             }
313 
314             /// Returns length of all at-rules.
315             pub fn len(&self) -> usize {
316                 let mut len = 0;
317                 $(
318                     len += self.$ident.len();
319                 )*
320                 len
321             }
322 
323             /// Convert to Gecko gfxFontFeatureValueSet.
324             #[cfg(feature = "gecko")]
325             pub fn set_at_rules(&self, dest: *mut gfxFontFeatureValueSet) {
326                 for ref family in self.family_names.iter() {
327                     let family = family.name.to_ascii_lowercase();
328                     $(
329                         if self.$ident.len() > 0 {
330                             for val in self.$ident.iter() {
331                                 let array = unsafe {
332                                     Gecko_AppendFeatureValueHashEntry(
333                                         dest,
334                                         family.as_ptr(),
335                                         structs::$gecko_enum,
336                                         val.name.as_ptr()
337                                     )
338                                 };
339                                 unsafe {
340                                     val.value.to_gecko_font_feature_values(&mut *array);
341                                 }
342                             }
343                         }
344                     )*
345                 }
346             }
347         }
348 
349         impl ToCssWithGuard for FontFeatureValuesRule {
350             fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
351                 dest.write_str("@font-feature-values ")?;
352                 self.font_family_to_css(&mut CssWriter::new(dest))?;
353                 dest.write_str(" {\n")?;
354                 self.value_to_css(&mut CssWriter::new(dest))?;
355                 dest.write_str("}")
356             }
357         }
358 
359         /// Updates with new value if same `ident` exists, otherwise pushes to the vector.
360         fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) {
361             let position = vec.iter().position(|ref val| val.name == element.name);
362             if let Some(index) = position {
363                 vec[index].value = element.value;
364             } else {
365                 vec.push(element);
366             }
367         }
368 
369         /// Keeps the information about block type like @swash, @styleset etc.
370         enum BlockType {
371             $(
372                 $ident_camel,
373             )*
374         }
375 
376         /// Parser for `FontFeatureValuesRule`. Parses all blocks
377         /// <feature-type> {
378         ///   <feature-value-declaration-list>
379         /// }
380         /// <feature-type> = @stylistic | @historical-forms | @styleset |
381         /// @character-variant | @swash | @ornaments | @annotation
382         struct FontFeatureValuesRuleParser<'a> {
383             context: &'a ParserContext<'a>,
384             rule: &'a mut FontFeatureValuesRule,
385         }
386 
387         /// Default methods reject all qualified rules.
388         impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
389             type Prelude = ();
390             type QualifiedRule = ();
391             type Error = StyleParseErrorKind<'i>;
392         }
393 
394         impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
395             type Prelude = BlockType;
396             type AtRule = ();
397             type Error = StyleParseErrorKind<'i>;
398 
399             fn parse_prelude<'t>(
400                 &mut self,
401                 name: CowRcStr<'i>,
402                 input: &mut Parser<'i, 't>,
403             ) -> Result<BlockType, ParseError<'i>> {
404                 match_ignore_ascii_case! { &*name,
405                     $(
406                         $name => Ok(BlockType::$ident_camel),
407                     )*
408                     _ => Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
409                 }
410             }
411 
412             fn parse_block<'t>(
413                 &mut self,
414                 prelude: BlockType,
415                 _: &ParserState,
416                 input: &mut Parser<'i, 't>
417             ) -> Result<Self::AtRule, ParseError<'i>> {
418                 debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues);
419                 match prelude {
420                     $(
421                         BlockType::$ident_camel => {
422                             let parser = FFVDeclarationsParser {
423                                 context: &self.context,
424                                 declarations: &mut self.rule.$ident,
425                             };
426 
427                             let mut iter = DeclarationListParser::new(input, parser);
428                             while let Some(declaration) = iter.next() {
429                                 if let Err((error, slice)) = declaration {
430                                     let location = error.location;
431                                     let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(
432                                         slice, error
433                                     );
434                                     self.context.log_css_error(location, error);
435                                 }
436                             }
437                         },
438                     )*
439                 }
440 
441                 Ok(())
442             }
443         }
444     }
445 }
446 
447 font_feature_values_blocks! {
448     blocks = [
449         #[doc = "A @swash blocksck. \
450                  Specifies a feature name that will work with the swash() \
451                  functional notation of font-variant-alternates."]
452         "swash" swash / Swash / NS_FONT_VARIANT_ALTERNATES_SWASH: SingleValue,
453 
454         #[doc = "A @stylistic block. \
455                  Specifies a feature name that will work with the annotation() \
456                  functional notation of font-variant-alternates."]
457         "stylistic" stylistic / Stylistic / NS_FONT_VARIANT_ALTERNATES_STYLISTIC: SingleValue,
458 
459         #[doc = "A @ornaments block. \
460                  Specifies a feature name that will work with the ornaments() ] \
461                  functional notation of font-variant-alternates."]
462         "ornaments" ornaments / Ornaments / NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: SingleValue,
463 
464         #[doc = "A @annotation block. \
465                  Specifies a feature name that will work with the stylistic() \
466                  functional notation of font-variant-alternates."]
467         "annotation" annotation / Annotation / NS_FONT_VARIANT_ALTERNATES_ANNOTATION: SingleValue,
468 
469         #[doc = "A @character-variant block. \
470                  Specifies a feature name that will work with the styleset() \
471                  functional notation of font-variant-alternates. The value can be a pair."]
472         "character-variant" character_variant / CharacterVariant / NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT:
473             PairValues,
474 
475         #[doc = "A @styleset block. \
476                  Specifies a feature name that will work with the character-variant() \
477                  functional notation of font-variant-alternates. The value can be a list."]
478         "styleset" styleset / Styleset / NS_FONT_VARIANT_ALTERNATES_STYLESET: VectorValues,
479     ]
480 }
481