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