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