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