1use crate::derives::*;
10use crate::error_reporting::ContextualParseError;
11#[cfg(feature = "gecko")]
12use crate::gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
13#[cfg(feature = "gecko")]
14use crate::gecko_bindings::structs::{self, gfxFontFeatureValueSet};
15use crate::parser::{Parse, ParserContext};
16use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
17use crate::stylesheets::CssRuleType;
18use crate::values::computed::font::FamilyName;
19use crate::values::serialize_atom_identifier;
20use crate::Atom;
21use cssparser::{
22 match_ignore_ascii_case, AtRuleParser, BasicParseErrorKind, CowRcStr, DeclarationParser,
23 Parser, ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
24 Token,
25};
26use std::fmt::{self, Write};
27use style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};
28#[cfg(feature = "gecko")]
29use thin_vec::ThinVec;
30
31#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
38pub struct FFVDeclaration<T> {
39 pub name: Atom,
41 pub value: T,
43}
44
45impl<T: ToCss> ToCss for FFVDeclaration<T> {
46 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
47 where
48 W: Write,
49 {
50 serialize_atom_identifier(&self.name, dest)?;
51 dest.write_str(": ")?;
52 self.value.to_css(dest)?;
53 dest.write_char(';')
54 }
55}
56
57#[cfg(feature = "gecko")]
59pub trait ToGeckoFontFeatureValues {
60 fn to_gecko_font_feature_values(&self) -> ThinVec<u32>;
62}
63
64#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
66pub struct SingleValue(pub u32);
67
68impl Parse for SingleValue {
69 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SingleValue, ParseError> {
70 match *input.next()? {
71 Token::Number {
72 int_value: Some(v), ..
73 } if v >= 0 => Ok(SingleValue(v as u32)),
74 _ => Err(ParseError::unexpected_token()),
75 }
76 }
77}
78
79#[cfg(feature = "gecko")]
80impl ToGeckoFontFeatureValues for SingleValue {
81 fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {
82 thin_vec::thin_vec![self.0]
83 }
84}
85
86#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
88pub struct PairValues(pub u32, pub Option<u32>);
89
90impl Parse for PairValues {
91 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<PairValues, ParseError> {
92 let first = match *input.next()? {
93 Token::Number {
94 int_value: Some(a), ..
95 } if a >= 0 => a as u32,
96 _ => return Err(ParseError::unexpected_token()),
97 };
98 match input.next() {
99 Ok(&Token::Number {
100 int_value: Some(b), ..
101 }) if b >= 0 => Ok(PairValues(first, Some(b as u32))),
102 Ok(_) => Err(ParseError::unexpected_token()),
104 Err(_) => Ok(PairValues(first, None)),
106 }
107 }
108}
109
110#[cfg(feature = "gecko")]
111impl ToGeckoFontFeatureValues for PairValues {
112 fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {
113 let mut result = thin_vec::thin_vec![self.0];
114 if let Some(second) = self.1 {
115 result.push(second);
116 }
117 result
118 }
119}
120
121#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
123pub struct VectorValues(#[css(iterable)] pub Vec<u32>);
124
125impl Parse for VectorValues {
126 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<VectorValues, ParseError> {
127 let mut vec = vec![];
128 loop {
129 match input.next() {
130 Ok(&Token::Number {
131 int_value: Some(a), ..
132 }) if a >= 0 => {
133 vec.push(a as u32);
134 },
135 Ok(_) => return Err(ParseError::unexpected_token()),
137 Err(_) => break,
138 }
139 }
140
141 if vec.is_empty() {
142 return Err(ParseError::from_basic_kind(BasicParseErrorKind::EndOfInput));
143 }
144
145 Ok(VectorValues(vec))
146 }
147}
148
149#[cfg(feature = "gecko")]
150impl ToGeckoFontFeatureValues for VectorValues {
151 fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {
152 self.0.iter().copied().collect()
153 }
154}
155
156pub fn parse_family_name_list(
158 context: &ParserContext,
159 input: &mut Parser,
160) -> Result<Vec<FamilyName>, ParseError> {
161 input
162 .parse_comma_separated(|i| FamilyName::parse(context, i))
163 .map_err(|e| e.into())
164}
165
166struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> {
169 context: &'a ParserContext<'b>,
170 declarations: &'a mut Vec<FFVDeclaration<T>>,
171}
172
173impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
175 type Prelude = ();
176 type AtRule = ();
177 type Error = StyleParseErrorKind;
178}
179
180impl<'a, 'b, 'i, T> QualifiedRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
181 type Prelude = ();
182 type QualifiedRule = ();
183 type Error = StyleParseErrorKind;
184}
185
186impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>
187where
188 T: Parse,
189{
190 type Declaration = ();
191 type Error = StyleParseErrorKind;
192
193 fn parse_value(
194 &mut self,
195 name: CowRcStr<'i>,
196 input: &mut Parser<'i>,
197 _declaration_start: &ParserState,
198 ) -> Result<(), ParseError> {
199 let value = input.parse_entirely(|i| T::parse(self.context, i))?;
200 let new = FFVDeclaration {
201 name: Atom::from(&*name),
202 value,
203 };
204 update_or_push(self.declarations, new);
205 Ok(())
206 }
207}
208
209impl<'a, 'b, 'i, T> RuleBodyItemParser<'i, (), StyleParseErrorKind>
210 for FFVDeclarationsParser<'a, 'b, T>
211where
212 T: Parse,
213{
214 fn parse_declarations(&self) -> bool {
215 true
216 }
217 fn parse_qualified(&self) -> bool {
218 false
219 }
220}
221
222macro_rules! font_feature_values_blocks {
223 (
224 blocks = [
225 $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident / $gecko_enum: ident: $ty: ty, )*
226 ]
227 ) => {
228 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
232 pub struct FontFeatureValuesRule {
233 pub family_names: Vec<FamilyName>,
237 $(
238 #[$doc]
239 pub $ident: Vec<FFVDeclaration<$ty>>,
240 )*
241 pub source_location: SourceLocation,
243 }
244
245 impl FontFeatureValuesRule {
246 fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self {
248 FontFeatureValuesRule {
249 family_names,
250 $(
251 $ident: vec![],
252 )*
253 source_location: location,
254 }
255 }
256
257 pub fn parse(
259 context: &ParserContext,
260 input: &mut Parser,
261 family_names: Vec<FamilyName>,
262 location: SourceLocation,
263 ) -> Self {
264 let mut rule = FontFeatureValuesRule::new(family_names, location);
265 let mut parser = FontFeatureValuesRuleParser {
266 context,
267 rule: &mut rule,
268 };
269 let mut iter = RuleBodyParser::new(input, &mut parser);
270 while let Some(result) = iter.next() {
271 if let Err((error, slice, location)) = result {
272 let error = ContextualParseError::UnsupportedRule(slice, error);
273 context.log_css_error(location, error);
274 }
275 }
276 rule
277 }
278
279 pub fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
281 where
282 W: Write,
283 {
284 $(
285 if self.$ident.len() > 0 {
286 dest.write_str(concat!("@", $name, " {\n"))?;
287 let iter = self.$ident.iter();
288 for val in iter {
289 val.to_css(dest)?;
290 dest.write_str("\n")?
291 }
292 dest.write_str("}\n")?
293 }
294 )*
295 Ok(())
296 }
297
298 pub fn len(&self) -> usize {
300 let mut len = 0;
301 $(
302 len += self.$ident.len();
303 )*
304 len
305 }
306
307 #[cfg(feature = "gecko")]
309 pub fn set_at_rules(&self, dest: *mut gfxFontFeatureValueSet) {
310 for ref family in self.family_names.iter() {
311 let family = family.name.to_ascii_lowercase();
312 $(
313 if self.$ident.len() > 0 {
314 for val in self.$ident.iter() {
315 let array = unsafe {
316 Gecko_AppendFeatureValueHashEntry(
317 dest,
318 family.as_ptr(),
319 structs::$gecko_enum,
320 val.name.as_ptr()
321 )
322 };
323 unsafe {
324 *array = val.value.to_gecko_font_feature_values();
325 }
326 }
327 }
328 )*
329 }
330 }
331 }
332
333 impl ToCssWithGuard for FontFeatureValuesRule {
334 fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
335 dest.write_str("@font-feature-values ")?;
336 self.family_names.to_css(&mut CssWriter::new(dest))?;
337 dest.write_str(" {\n")?;
338 self.value_to_css(&mut CssWriter::new(dest))?;
339 dest.write_char('}')
340 }
341 }
342
343 fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) {
345 if let Some(item) = vec.iter_mut().find(|item| item.name == element.name) {
346 item.value = element.value;
347 } else {
348 vec.push(element);
349 }
350 }
351
352 #[derive(Clone, Copy, Eq, PartialEq)]
354 pub enum FontFeatureValuesBlockType {
355 $(
356 #[$doc]
357 $ident_camel,
358 )*
359 }
360
361 impl FontFeatureValuesBlockType {
362 pub fn from_name(name: &str) -> Option<Self> {
365 Some(match_ignore_ascii_case! { name,
366 $( $name => Self::$ident_camel, )*
367 _ => return None,
368 })
369 }
370 }
371
372 struct FontFeatureValuesRuleParser<'a> {
379 context: &'a ParserContext<'a>,
380 rule: &'a mut FontFeatureValuesRule,
381 }
382
383 impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
385 type Prelude = ();
386 type QualifiedRule = ();
387 type Error = StyleParseErrorKind;
388 }
389
390 impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
391 type Prelude = FontFeatureValuesBlockType;
392 type AtRule = ();
393 type Error = StyleParseErrorKind;
394
395 fn parse_prelude(
396 &mut self,
397 name: CowRcStr<'i>,
398 _input: &mut Parser<'i>,
399 ) -> Result<FontFeatureValuesBlockType, ParseError> {
400 FontFeatureValuesBlockType::from_name(&name)
401 .ok_or_else(|| ParseError::from_basic_kind(BasicParseErrorKind::AtRuleBodyInvalid))
402 }
403
404 fn parse_block(
405 &mut self,
406 prelude: FontFeatureValuesBlockType,
407 _: &ParserState,
408 input: &mut Parser<'i>
409 ) -> Result<Self::AtRule, ParseError> {
410 debug_assert!(self.context.rule_types().contains(CssRuleType::FontFeatureValues));
411 match prelude {
412 $(
413 FontFeatureValuesBlockType::$ident_camel => {
414 let mut parser = FFVDeclarationsParser {
415 context: &self.context,
416 declarations: &mut self.rule.$ident,
417 };
418
419 let mut iter = RuleBodyParser::new(input, &mut parser);
420 while let Some(declaration) = iter.next() {
421 if let Err((error, slice, location)) = declaration {
422 let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, &[]);
425 self.context.log_css_error(location, error);
426 }
427 }
428 },
429 )*
430 }
431
432 Ok(())
433 }
434 }
435
436 impl<'a, 'i> DeclarationParser<'i> for FontFeatureValuesRuleParser<'a> {
437 type Declaration = ();
438 type Error = StyleParseErrorKind;
439 }
440
441 impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind> for FontFeatureValuesRuleParser<'a> {
442 fn parse_declarations(&self) -> bool { false }
443 fn parse_qualified(&self) -> bool { true }
444 }
445 }
446}
447
448font_feature_values_blocks! {
449 blocks = [
450 #[doc = "A @swash blocksck. \
451 Specifies a feature name that will work with the swash() \
452 functional notation of font-variant-alternates."]
453 "swash" swash / Swash / NS_FONT_VARIANT_ALTERNATES_SWASH: SingleValue,
454
455 #[doc = "A @stylistic block. \
456 Specifies a feature name that will work with the annotation() \
457 functional notation of font-variant-alternates."]
458 "stylistic" stylistic / Stylistic / NS_FONT_VARIANT_ALTERNATES_STYLISTIC: SingleValue,
459
460 #[doc = "A @ornaments block. \
461 Specifies a feature name that will work with the ornaments() ] \
462 functional notation of font-variant-alternates."]
463 "ornaments" ornaments / Ornaments / NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: SingleValue,
464
465 #[doc = "A @annotation block. \
466 Specifies a feature name that will work with the stylistic() \
467 functional notation of font-variant-alternates."]
468 "annotation" annotation / Annotation / NS_FONT_VARIANT_ALTERNATES_ANNOTATION: SingleValue,
469
470 #[doc = "A @character-variant block. \
471 Specifies a feature name that will work with the styleset() \
472 functional notation of font-variant-alternates. The value can be a pair."]
473 "character-variant" character_variant / CharacterVariant / NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT:
474 PairValues,
475
476 #[doc = "A @styleset block. \
477 Specifies a feature name that will work with the character-variant() \
478 functional notation of font-variant-alternates. The value can be a list."]
479 "styleset" styleset / Styleset / NS_FONT_VARIANT_ALTERNATES_STYLESET: VectorValues,
480 ]
481}