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 //! Specified values for font properties
6 
7 use crate::parser::{Parse, ParserContext};
8 use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
9 use crate::values::computed::{font as computed, Length, NonNegativeLength};
10 use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage};
11 use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
12 use crate::values::computed::FontSizeAdjust as ComputedFontSizeAdjust;
13 use crate::values::generics::font::VariationValue;
14 use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag, GenericFontSizeAdjust};
15 use crate::values::generics::NonNegative;
16 use crate::values::specified::length::{FontBaseSize, AU_PER_PT, AU_PER_PX};
17 use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
18 use crate::values::specified::{NoCalcLength, NonNegativeNumber, Number, NonNegativePercentage};
19 use crate::values::CustomIdent;
20 use crate::Atom;
21 use cssparser::{Parser, Token};
22 #[cfg(feature = "gecko")]
23 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
24 use std::fmt::{self, Write};
25 use style_traits::values::SequenceWriter;
26 use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
27 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
28 
29 // FIXME(emilio): The system font code is copy-pasta, and should be cleaned up.
30 macro_rules! system_font_methods {
31     ($ty:ident, $field:ident) => {
32         system_font_methods!($ty);
33 
34         fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
35             debug_assert!(matches!(*self, $ty::System(..)));
36             #[cfg(feature = "gecko")]
37             {
38                 _context.cached_system_font.as_ref().unwrap().$field.clone()
39             }
40             #[cfg(feature = "servo")]
41             {
42                 unreachable!()
43             }
44         }
45     };
46 
47     ($ty:ident) => {
48         /// Get a specified value that represents a system font.
49         pub fn system_font(f: SystemFont) -> Self {
50             $ty::System(f)
51         }
52 
53         /// Retreive a SystemFont from the specified value.
54         pub fn get_system(&self) -> Option<SystemFont> {
55             if let $ty::System(s) = *self {
56                 Some(s)
57             } else {
58                 None
59             }
60         }
61     };
62 }
63 
64 /// System fonts.
65 #[repr(u8)]
66 #[derive(
67     Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem
68 )]
69 #[allow(missing_docs)]
70 pub enum SystemFont {
71     Caption,
72     Icon,
73     Menu,
74     MessageBox,
75     SmallCaption,
76     StatusBar,
77     MozWindow,
78     MozDocument,
79     MozWorkspace,
80     MozDesktop,
81     MozInfo,
82     MozDialog,
83     MozButton,
84     MozPullDownMenu,
85     MozList,
86     MozField,
87     #[css(skip)]
88     End, // Just for indexing purposes.
89 }
90 
91 const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
92 const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
93 
94 /// The minimum font-weight value per:
95 ///
96 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
97 pub const MIN_FONT_WEIGHT: f32 = 1.;
98 
99 /// The maximum font-weight value per:
100 ///
101 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
102 pub const MAX_FONT_WEIGHT: f32 = 1000.;
103 
104 /// A specified font-weight value.
105 ///
106 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
107 #[derive(
108     Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
109 )]
110 pub enum FontWeight {
111     /// `<font-weight-absolute>`
112     Absolute(AbsoluteFontWeight),
113     /// Bolder variant
114     Bolder,
115     /// Lighter variant
116     Lighter,
117     /// System font variant.
118     #[css(skip)]
119     System(SystemFont),
120 }
121 
122 impl FontWeight {
123     system_font_methods!(FontWeight, font_weight);
124 
125     /// `normal`
126     #[inline]
normal() -> Self127     pub fn normal() -> Self {
128         FontWeight::Absolute(AbsoluteFontWeight::Normal)
129     }
130 
131     /// Get a specified FontWeight from a gecko keyword
from_gecko_keyword(kw: u32) -> Self132     pub fn from_gecko_keyword(kw: u32) -> Self {
133         debug_assert!(kw % 100 == 0);
134         debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
135         FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
136     }
137 }
138 
139 impl ToComputedValue for FontWeight {
140     type ComputedValue = computed::FontWeight;
141 
142     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue143     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
144         match *self {
145             FontWeight::Absolute(ref abs) => abs.compute(),
146             FontWeight::Bolder => context
147                 .builder
148                 .get_parent_font()
149                 .clone_font_weight()
150                 .bolder(),
151             FontWeight::Lighter => context
152                 .builder
153                 .get_parent_font()
154                 .clone_font_weight()
155                 .lighter(),
156             FontWeight::System(_) => self.compute_system(context),
157         }
158     }
159 
160     #[inline]
from_computed_value(computed: &computed::FontWeight) -> Self161     fn from_computed_value(computed: &computed::FontWeight) -> Self {
162         FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
163             &computed.0,
164         )))
165     }
166 }
167 
168 /// An absolute font-weight value for a @font-face rule.
169 ///
170 /// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values
171 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
172 pub enum AbsoluteFontWeight {
173     /// A `<number>`, with the additional constraints specified in:
174     ///
175     ///   https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
176     Weight(Number),
177     /// Normal font weight. Same as 400.
178     Normal,
179     /// Bold font weight. Same as 700.
180     Bold,
181 }
182 
183 impl AbsoluteFontWeight {
184     /// Returns the computed value for this absolute font weight.
compute(&self) -> computed::FontWeight185     pub fn compute(&self) -> computed::FontWeight {
186         match *self {
187             AbsoluteFontWeight::Weight(weight) => {
188                 computed::FontWeight(weight.get().max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT))
189             },
190             AbsoluteFontWeight::Normal => computed::FontWeight::normal(),
191             AbsoluteFontWeight::Bold => computed::FontWeight::bold(),
192         }
193     }
194 }
195 
196 impl Parse for AbsoluteFontWeight {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>197     fn parse<'i, 't>(
198         context: &ParserContext,
199         input: &mut Parser<'i, 't>,
200     ) -> Result<Self, ParseError<'i>> {
201         if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
202             // We could add another AllowedNumericType value, but it doesn't
203             // seem worth it just for a single property with such a weird range,
204             // so we do the clamping here manually.
205             if !number.was_calc() &&
206                 (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
207             {
208                 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
209             }
210             return Ok(AbsoluteFontWeight::Weight(number));
211         }
212 
213         Ok(try_match_ident_ignore_ascii_case! { input,
214             "normal" => AbsoluteFontWeight::Normal,
215             "bold" => AbsoluteFontWeight::Bold,
216         })
217     }
218 }
219 
220 /// The specified value of the `font-style` property, without the system font
221 /// crap.
222 pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
223 
224 impl ToCss for SpecifiedFontStyle {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,225     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
226     where
227         W: Write,
228     {
229         match *self {
230             generics::FontStyle::Normal => dest.write_str("normal"),
231             generics::FontStyle::Italic => dest.write_str("italic"),
232             generics::FontStyle::Oblique(ref angle) => {
233                 dest.write_str("oblique")?;
234                 if *angle != Self::default_angle() {
235                     dest.write_char(' ')?;
236                     angle.to_css(dest)?;
237                 }
238                 Ok(())
239             },
240         }
241     }
242 }
243 
244 impl Parse for SpecifiedFontStyle {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>245     fn parse<'i, 't>(
246         context: &ParserContext,
247         input: &mut Parser<'i, 't>,
248     ) -> Result<Self, ParseError<'i>> {
249         Ok(try_match_ident_ignore_ascii_case! { input,
250             "normal" => generics::FontStyle::Normal,
251             "italic" => generics::FontStyle::Italic,
252             "oblique" => {
253                 let angle = input.try_parse(|input| Self::parse_angle(context, input))
254                     .unwrap_or_else(|_| Self::default_angle());
255 
256                 generics::FontStyle::Oblique(angle)
257             },
258         })
259     }
260 }
261 
262 impl ToComputedValue for SpecifiedFontStyle {
263     type ComputedValue = computed::FontStyle;
264 
to_computed_value(&self, _: &Context) -> Self::ComputedValue265     fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
266         match *self {
267             generics::FontStyle::Normal => generics::FontStyle::Normal,
268             generics::FontStyle::Italic => generics::FontStyle::Italic,
269             generics::FontStyle::Oblique(ref angle) => {
270                 generics::FontStyle::Oblique(FontStyleAngle(Self::compute_angle(angle)))
271             },
272         }
273     }
274 
from_computed_value(computed: &Self::ComputedValue) -> Self275     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
276         match *computed {
277             generics::FontStyle::Normal => generics::FontStyle::Normal,
278             generics::FontStyle::Italic => generics::FontStyle::Italic,
279             generics::FontStyle::Oblique(ref angle) => {
280                 generics::FontStyle::Oblique(Angle::from_computed_value(&angle.0))
281             },
282         }
283     }
284 }
285 
286 /// The default angle for `font-style: oblique`.
287 ///
288 /// NOTE(emilio): As of right now this diverges from the spec, which specifies
289 /// 20, because it's not updated yet to account for the resolution in:
290 ///
291 ///   https://github.com/w3c/csswg-drafts/issues/2295
292 pub const DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES: f32 = 14.;
293 
294 /// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle:
295 ///
296 ///     Values less than -90deg or values greater than 90deg are
297 ///     invalid and are treated as parse errors.
298 ///
299 /// The maximum angle value that `font-style: oblique` should compute to.
300 pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
301 
302 /// The minimum angle value that `font-style: oblique` should compute to.
303 pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
304 
305 impl SpecifiedFontStyle {
306     /// Gets a clamped angle in degrees from a specified Angle.
compute_angle_degrees(angle: &Angle) -> f32307     pub fn compute_angle_degrees(angle: &Angle) -> f32 {
308         angle
309             .degrees()
310             .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
311             .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
312     }
313 
compute_angle(angle: &Angle) -> ComputedAngle314     fn compute_angle(angle: &Angle) -> ComputedAngle {
315         ComputedAngle::from_degrees(Self::compute_angle_degrees(angle))
316     }
317 
318     /// Parse a suitable angle for font-style: oblique.
parse_angle<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Angle, ParseError<'i>>319     pub fn parse_angle<'i, 't>(
320         context: &ParserContext,
321         input: &mut Parser<'i, 't>,
322     ) -> Result<Angle, ParseError<'i>> {
323         let angle = Angle::parse(context, input)?;
324         if angle.was_calc() {
325             return Ok(angle);
326         }
327 
328         let degrees = angle.degrees();
329         if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES ||
330             degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
331         {
332             return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
333         }
334         return Ok(angle);
335     }
336 
337     /// The default angle for `font-style: oblique`.
default_angle() -> Angle338     pub fn default_angle() -> Angle {
339         Angle::from_degrees(
340             DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES,
341             /* was_calc = */ false,
342         )
343     }
344 }
345 
346 /// The specified value of the `font-style` property.
347 #[derive(
348     Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
349 )]
350 #[allow(missing_docs)]
351 pub enum FontStyle {
352     Specified(SpecifiedFontStyle),
353     #[css(skip)]
354     System(SystemFont),
355 }
356 
357 impl FontStyle {
358     /// Return the `normal` value.
359     #[inline]
normal() -> Self360     pub fn normal() -> Self {
361         FontStyle::Specified(generics::FontStyle::Normal)
362     }
363 
364     system_font_methods!(FontStyle, font_style);
365 }
366 
367 impl ToComputedValue for FontStyle {
368     type ComputedValue = computed::FontStyle;
369 
to_computed_value(&self, context: &Context) -> Self::ComputedValue370     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
371         match *self {
372             FontStyle::Specified(ref specified) => specified.to_computed_value(context),
373             FontStyle::System(..) => self.compute_system(context),
374         }
375     }
376 
from_computed_value(computed: &Self::ComputedValue) -> Self377     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
378         FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
379     }
380 }
381 
382 /// A value for the `font-stretch` property.
383 ///
384 /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
385 #[allow(missing_docs)]
386 #[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
387 #[repr(u8)]
388 pub enum FontStretch {
389     Stretch(NonNegativePercentage),
390     Keyword(FontStretchKeyword),
391     #[css(skip)]
392     System(SystemFont),
393 }
394 
395 /// A keyword value for `font-stretch`.
396 #[derive(
397     Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
398 )]
399 #[allow(missing_docs)]
400 pub enum FontStretchKeyword {
401     Normal,
402     Condensed,
403     UltraCondensed,
404     ExtraCondensed,
405     SemiCondensed,
406     SemiExpanded,
407     Expanded,
408     ExtraExpanded,
409     UltraExpanded,
410 }
411 
412 impl FontStretchKeyword {
413     /// Resolves the value of the keyword as specified in:
414     ///
415     /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
compute(&self) -> ComputedPercentage416     pub fn compute(&self) -> ComputedPercentage {
417         use self::FontStretchKeyword::*;
418         ComputedPercentage(match *self {
419             UltraCondensed => 0.5,
420             ExtraCondensed => 0.625,
421             Condensed => 0.75,
422             SemiCondensed => 0.875,
423             Normal => 1.,
424             SemiExpanded => 1.125,
425             Expanded => 1.25,
426             ExtraExpanded => 1.5,
427             UltraExpanded => 2.,
428         })
429     }
430 
431     /// Does the opposite operation to `compute`, in order to serialize keywords
432     /// if possible.
from_percentage(percentage: f32) -> Option<Self>433     pub fn from_percentage(percentage: f32) -> Option<Self> {
434         use self::FontStretchKeyword::*;
435         // NOTE(emilio): Can't use `match` because of rust-lang/rust#41620.
436         if percentage == 0.5 {
437             return Some(UltraCondensed);
438         }
439         if percentage == 0.625 {
440             return Some(ExtraCondensed);
441         }
442         if percentage == 0.75 {
443             return Some(Condensed);
444         }
445         if percentage == 0.875 {
446             return Some(SemiCondensed);
447         }
448         if percentage == 1. {
449             return Some(Normal);
450         }
451         if percentage == 1.125 {
452             return Some(SemiExpanded);
453         }
454         if percentage == 1.25 {
455             return Some(Expanded);
456         }
457         if percentage == 1.5 {
458             return Some(ExtraExpanded);
459         }
460         if percentage == 2. {
461             return Some(UltraExpanded);
462         }
463         None
464     }
465 }
466 
467 impl FontStretch {
468     /// `normal`.
normal() -> Self469     pub fn normal() -> Self {
470         FontStretch::Keyword(FontStretchKeyword::Normal)
471     }
472 
473     system_font_methods!(FontStretch, font_stretch);
474 }
475 
476 impl ToComputedValue for FontStretch {
477     type ComputedValue = computed::FontStretch;
478 
to_computed_value(&self, context: &Context) -> Self::ComputedValue479     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
480         match *self {
481             FontStretch::Stretch(ref percentage) => {
482                 computed::FontStretch(percentage.to_computed_value(context))
483             },
484             FontStretch::Keyword(ref kw) => computed::FontStretch(NonNegative(kw.compute())),
485             FontStretch::System(_) => self.compute_system(context),
486         }
487     }
488 
from_computed_value(computed: &Self::ComputedValue) -> Self489     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
490         FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative((computed.0).0)))
491     }
492 }
493 
494 /// CSS font keywords
495 #[derive(
496     Animate,
497     Clone,
498     ComputeSquaredDistance,
499     Copy,
500     Debug,
501     MallocSizeOf,
502     Parse,
503     PartialEq,
504     SpecifiedValueInfo,
505     ToAnimatedValue,
506     ToAnimatedZero,
507     ToComputedValue,
508     ToCss,
509     ToResolvedValue,
510     ToShmem,
511     Serialize,
512     Deserialize,
513 )]
514 #[allow(missing_docs)]
515 #[repr(u8)]
516 pub enum FontSizeKeyword {
517     #[css(keyword = "xx-small")]
518     XXSmall,
519     XSmall,
520     Small,
521     Medium,
522     Large,
523     XLarge,
524     #[css(keyword = "xx-large")]
525     XXLarge,
526     #[css(keyword = "xxx-large")]
527     XXXLarge,
528     #[css(skip)]
529     None,
530 }
531 
532 impl FontSizeKeyword {
533     /// Convert to an HTML <font size> value
534     #[inline]
html_size(self) -> u8535     pub fn html_size(self) -> u8 {
536         self as u8
537     }
538 }
539 
540 impl Default for FontSizeKeyword {
default() -> Self541     fn default() -> Self {
542         FontSizeKeyword::Medium
543     }
544 }
545 
546 #[derive(
547     Animate,
548     Clone,
549     ComputeSquaredDistance,
550     Copy,
551     Debug,
552     MallocSizeOf,
553     PartialEq,
554     ToAnimatedValue,
555     ToAnimatedZero,
556     ToComputedValue,
557     ToCss,
558     ToResolvedValue,
559     ToShmem,
560 )]
561 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
562 /// Additional information for keyword-derived font sizes.
563 pub struct KeywordInfo {
564     /// The keyword used
565     pub kw: FontSizeKeyword,
566     /// A factor to be multiplied by the computed size of the keyword
567     #[css(skip)]
568     pub factor: f32,
569     /// An additional fixed offset to add to the kw * factor in the case of
570     /// `calc()`.
571     #[css(skip)]
572     pub offset: CSSPixelLength,
573 }
574 
575 impl KeywordInfo {
576     /// KeywordInfo value for font-size: medium
medium() -> Self577     pub fn medium() -> Self {
578         Self::new(FontSizeKeyword::Medium)
579     }
580 
581     /// KeywordInfo value for font-size: none
none() -> Self582     pub fn none() -> Self {
583         Self::new(FontSizeKeyword::None)
584     }
585 
new(kw: FontSizeKeyword) -> Self586     fn new(kw: FontSizeKeyword) -> Self {
587         KeywordInfo {
588             kw,
589             factor: 1.,
590             offset: CSSPixelLength::new(0.),
591         }
592     }
593 
594     /// Computes the final size for this font-size keyword, accounting for
595     /// text-zoom.
to_computed_value(&self, context: &Context) -> CSSPixelLength596     fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
597         debug_assert_ne!(self.kw, FontSizeKeyword::None);
598         let base = context.maybe_zoom_text(self.kw.to_length(context).0);
599         base * self.factor + context.maybe_zoom_text(self.offset)
600     }
601 
602     /// Given a parent keyword info (self), apply an additional factor/offset to
603     /// it.
compose(self, factor: f32) -> Self604     fn compose(self, factor: f32) -> Self {
605         if self.kw == FontSizeKeyword::None {
606             return self;
607         }
608         KeywordInfo {
609             kw: self.kw,
610             factor: self.factor * factor,
611             offset: self.offset * factor,
612         }
613     }
614 }
615 
616 impl SpecifiedValueInfo for KeywordInfo {
collect_completion_keywords(f: KeywordsCollectFn)617     fn collect_completion_keywords(f: KeywordsCollectFn) {
618         <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
619     }
620 }
621 
622 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
623 /// A specified font-size value
624 pub enum FontSize {
625     /// A length; e.g. 10px.
626     Length(LengthPercentage),
627     /// A keyword value, along with a ratio and absolute offset.
628     /// The ratio in any specified keyword value
629     /// will be 1 (with offset 0), but we cascade keywordness even
630     /// after font-relative (percent and em) values
631     /// have been applied, which is where the ratio
632     /// comes in. The offset comes in if we cascaded a calc value,
633     /// where the font-relative portion (em and percentage) will
634     /// go into the ratio, and the remaining units all computed together
635     /// will go into the offset.
636     /// See bug 1355707.
637     Keyword(KeywordInfo),
638     /// font-size: smaller
639     Smaller,
640     /// font-size: larger
641     Larger,
642     /// Derived from a specified system font.
643     #[css(skip)]
644     System(SystemFont),
645 }
646 
647 /// Specifies a prioritized list of font family names or generic family names.
648 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
649 #[cfg_attr(feature = "servo", derive(Hash))]
650 pub enum FontFamily {
651     /// List of `font-family`
652     #[css(comma)]
653     Values(#[css(iterable)] FontFamilyList),
654     /// System font
655     #[css(skip)]
656     System(SystemFont),
657 }
658 
659 impl FontFamily {
660     system_font_methods!(FontFamily, font_family);
661 
662     /// Parse a specified font-family value
parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>663     pub fn parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
664         let values = input.parse_comma_separated(SingleFontFamily::parse)?;
665         Ok(FontFamily::Values(FontFamilyList {
666             list: crate::ArcSlice::from_iter(values.into_iter()),
667             fallback: computed::GenericFontFamily::None,
668         }))
669     }
670 }
671 
672 impl ToComputedValue for FontFamily {
673     type ComputedValue = computed::FontFamily;
674 
to_computed_value(&self, context: &Context) -> Self::ComputedValue675     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
676         match *self {
677             FontFamily::Values(ref list) => computed::FontFamily {
678                 families: list.clone(),
679                 is_system_font: false,
680             },
681             FontFamily::System(_) => self.compute_system(context),
682         }
683     }
684 
from_computed_value(other: &computed::FontFamily) -> Self685     fn from_computed_value(other: &computed::FontFamily) -> Self {
686         FontFamily::Values(other.families.clone())
687     }
688 }
689 
690 #[cfg(feature = "gecko")]
691 impl MallocSizeOf for FontFamily {
size_of(&self, ops: &mut MallocSizeOfOps) -> usize692     fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
693         match *self {
694             FontFamily::Values(ref v) => {
695                 // Although the family list is refcounted, we always attribute
696                 // its size to the specified value.
697                 v.list.unconditional_size_of(ops)
698             },
699             FontFamily::System(_) => 0,
700         }
701     }
702 }
703 
704 impl Parse for FontFamily {
705     /// <family-name>#
706     /// <family-name> = <string> | [ <ident>+ ]
707     /// TODO: <generic-family>
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontFamily, ParseError<'i>>708     fn parse<'i, 't>(
709         _: &ParserContext,
710         input: &mut Parser<'i, 't>,
711     ) -> Result<FontFamily, ParseError<'i>> {
712         FontFamily::parse_specified(input)
713     }
714 }
715 
716 impl SpecifiedValueInfo for FontFamily {}
717 
718 /// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other way around
719 /// because we want the former to exclude generic family keywords.
720 impl Parse for FamilyName {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>721     fn parse<'i, 't>(
722         _: &ParserContext,
723         input: &mut Parser<'i, 't>,
724     ) -> Result<Self, ParseError<'i>> {
725         match SingleFontFamily::parse(input) {
726             Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
727             Ok(SingleFontFamily::Generic(_)) => {
728                 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
729             },
730             Err(e) => Err(e),
731         }
732     }
733 }
734 
735 /// Preserve the readability of text when font fallback occurs
736 #[derive(
737     Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
738 )]
739 #[allow(missing_docs)]
740 pub enum FontSizeAdjust {
741     Value(GenericFontSizeAdjust<NonNegativeNumber>),
742     #[css(skip)]
743     System(SystemFont),
744 }
745 
746 impl FontSizeAdjust {
747     #[inline]
748     /// Default value of font-size-adjust
none() -> Self749     pub fn none() -> Self {
750         FontSizeAdjust::Value(GenericFontSizeAdjust::None)
751     }
752 
753     system_font_methods!(FontSizeAdjust, font_size_adjust);
754 }
755 
756 impl Parse for FontSizeAdjust {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>757     fn parse<'i, 't>(
758         context: &ParserContext,
759         input: &mut Parser<'i, 't>,
760     ) -> Result<Self, ParseError<'i>> {
761         let location = input.current_source_location();
762         if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
763             let basis_enabled = static_prefs::pref!("layout.css.font-size-adjust.basis.enabled");
764             let basis = match_ignore_ascii_case! { &ident,
765                 "none" => return Ok(FontSizeAdjust::none()),
766                 // Check for size adjustment basis keywords if enabled.
767                 "ex-height" if basis_enabled => GenericFontSizeAdjust::ExHeight,
768                 "cap-height" if basis_enabled => GenericFontSizeAdjust::CapHeight,
769                 "ch-width" if basis_enabled => GenericFontSizeAdjust::ChWidth,
770                 "ic-width" if basis_enabled => GenericFontSizeAdjust::IcWidth,
771                 "ic-height" if basis_enabled => GenericFontSizeAdjust::IcHeight,
772                 // Unknown (or disabled) keyword.
773                 _ => return Err(location.new_custom_error(
774                     ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident)
775                 )),
776             };
777             let value = NonNegativeNumber::parse(context, input)?;
778             return Ok(FontSizeAdjust::Value(basis(value)));
779         }
780         // Without a basis keyword, the number refers to the 'ex-height' metric.
781         let value = NonNegativeNumber::parse(context, input)?;
782         Ok(FontSizeAdjust::Value(GenericFontSizeAdjust::ExHeight(value)))
783     }
784 }
785 
786 impl ToComputedValue for FontSizeAdjust {
787     type ComputedValue = ComputedFontSizeAdjust;
788 
to_computed_value(&self, context: &Context) -> Self::ComputedValue789     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
790         match *self {
791             FontSizeAdjust::Value(v) => v.to_computed_value(context),
792             FontSizeAdjust::System(_) => self.compute_system(context),
793         }
794     }
795 
from_computed_value(computed: &ComputedFontSizeAdjust) -> Self796     fn from_computed_value(computed: &ComputedFontSizeAdjust) -> Self {
797         Self::Value(ToComputedValue::from_computed_value(computed))
798     }
799 }
800 
801 /// This is the ratio applied for font-size: larger
802 /// and smaller by both Firefox and Chrome
803 const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
804 
805 /// The default font size.
806 pub const FONT_MEDIUM_PX: f32 = 16.0;
807 
808 impl FontSizeKeyword {
809     #[inline]
810     #[cfg(feature = "servo")]
to_length(&self, _: &Context) -> NonNegativeLength811     fn to_length(&self, _: &Context) -> NonNegativeLength {
812         let medium = Length::new(FONT_MEDIUM_PX);
813         // https://drafts.csswg.org/css-fonts-3/#font-size-prop
814         NonNegative(match *self {
815             FontSizeKeyword::XXSmall => medium * 3.0 / 5.0,
816             FontSizeKeyword::XSmall => medium * 3.0 / 4.0,
817             FontSizeKeyword::Small => medium * 8.0 / 9.0,
818             FontSizeKeyword::Medium => medium,
819             FontSizeKeyword::Large => medium * 6.0 / 5.0,
820             FontSizeKeyword::XLarge => medium * 3.0 / 2.0,
821             FontSizeKeyword::XXLarge => medium * 2.0,
822             FontSizeKeyword::XXXLarge => medium * 3.0,
823             FontSizeKeyword::None => unreachable!(),
824         })
825     }
826 
827     #[cfg(feature = "gecko")]
828     #[inline]
to_length(&self, cx: &Context) -> NonNegativeLength829     fn to_length(&self, cx: &Context) -> NonNegativeLength {
830         use crate::context::QuirksMode;
831 
832         // The tables in this function are originally from
833         // nsRuleNode::CalcFontPointSize in Gecko:
834         //
835         // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150
836         //
837         // Mapping from base size and HTML size to pixels
838         // The first index is (base_size - 9), the second is the
839         // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
840         // since HTML size 0 is the same as 1.
841         //
842         //  xxs   xs      s      m     l      xl     xxl   -
843         //  -     0/1     2      3     4      5      6     7
844         static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
845             [9, 9, 9, 9, 11, 14, 18, 27],
846             [9, 9, 9, 10, 12, 15, 20, 30],
847             [9, 9, 10, 11, 13, 17, 22, 33],
848             [9, 9, 10, 12, 14, 18, 24, 36],
849             [9, 10, 12, 13, 16, 20, 26, 39],
850             [9, 10, 12, 14, 17, 21, 28, 42],
851             [9, 10, 13, 15, 18, 23, 30, 45],
852             [9, 10, 13, 16, 18, 24, 32, 48],
853         ];
854 
855         // This table gives us compatibility with WinNav4 for the default fonts only.
856         // In WinNav4, the default fonts were:
857         //
858         //     Times/12pt ==   Times/16px at 96ppi
859         //   Courier/10pt == Courier/13px at 96ppi
860         //
861         // xxs   xs     s      m      l     xl     xxl    -
862         // -     1      2      3      4     5      6      7
863         static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
864             [9, 9, 9, 9, 11, 14, 18, 28],
865             [9, 9, 9, 10, 12, 15, 20, 31],
866             [9, 9, 9, 11, 13, 17, 22, 34],
867             [9, 9, 10, 12, 14, 18, 24, 37],
868             [9, 9, 10, 13, 16, 20, 26, 40],
869             [9, 9, 11, 14, 17, 21, 28, 42],
870             [9, 10, 12, 15, 17, 23, 30, 45],
871             [9, 10, 13, 16, 18, 24, 32, 48],
872         ];
873 
874         static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
875 
876         let ref gecko_font = cx.style().get_font().gecko();
877         let generic = gecko_font.mFont.family.families.single_generic().unwrap_or(computed::GenericFontFamily::None);
878         let base_size = unsafe {
879             Atom::with(gecko_font.mLanguage.mRawPtr, |atom| {
880                 cx.font_metrics_provider.get_size(atom, generic)
881             })
882         };
883 
884         let base_size_px = base_size.px().round() as i32;
885         let html_size = self.html_size() as usize;
886         NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
887             let mapping = if cx.quirks_mode == QuirksMode::Quirks {
888                 QUIRKS_FONT_SIZE_MAPPING
889             } else {
890                 FONT_SIZE_MAPPING
891             };
892             Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
893         } else {
894             base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
895         })
896     }
897 }
898 
899 impl FontSize {
900     /// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
from_html_size(size: u8) -> Self901     pub fn from_html_size(size: u8) -> Self {
902         FontSize::Keyword(KeywordInfo::new(match size {
903             // If value is less than 1, let it be 1.
904             0 | 1 => FontSizeKeyword::XSmall,
905             2 => FontSizeKeyword::Small,
906             3 => FontSizeKeyword::Medium,
907             4 => FontSizeKeyword::Large,
908             5 => FontSizeKeyword::XLarge,
909             6 => FontSizeKeyword::XXLarge,
910             // If value is greater than 7, let it be 7.
911             _ => FontSizeKeyword::XXXLarge,
912         }))
913     }
914 
915     /// Compute it against a given base font size
to_computed_value_against( &self, context: &Context, base_size: FontBaseSize, ) -> computed::FontSize916     pub fn to_computed_value_against(
917         &self,
918         context: &Context,
919         base_size: FontBaseSize,
920     ) -> computed::FontSize {
921         use crate::values::specified::length::FontRelativeLength;
922 
923         let compose_keyword = |factor| {
924             context
925                 .style()
926                 .get_parent_font()
927                 .clone_font_size()
928                 .keyword_info
929                 .compose(factor)
930         };
931         let mut info = KeywordInfo::none();
932         let size = match *self {
933             FontSize::Length(LengthPercentage::Length(NoCalcLength::FontRelative(value))) => {
934                 if let FontRelativeLength::Em(em) = value {
935                     // If the parent font was keyword-derived, this is too.
936                     // Tack the em unit onto the factor
937                     info = compose_keyword(em);
938                 }
939                 value.to_computed_value(context, base_size)
940             },
941             FontSize::Length(LengthPercentage::Length(NoCalcLength::ServoCharacterWidth(
942                 value,
943             ))) => value.to_computed_value(base_size.resolve(context)),
944             FontSize::Length(LengthPercentage::Length(NoCalcLength::Absolute(ref l))) => {
945                 context.maybe_zoom_text(l.to_computed_value(context))
946             },
947             FontSize::Length(LengthPercentage::Length(ref l)) => l.to_computed_value(context),
948             FontSize::Length(LengthPercentage::Percentage(pc)) => {
949                 // If the parent font was keyword-derived, this is too.
950                 // Tack the % onto the factor
951                 info = compose_keyword(pc.0);
952                 (base_size.resolve(context) * pc.0).normalized()
953             },
954             FontSize::Length(LengthPercentage::Calc(ref calc)) => {
955                 let calc = calc.to_computed_value_zoomed(context, base_size);
956                 calc.resolve(base_size.resolve(context))
957             },
958             FontSize::Keyword(i) => {
959                 // As a specified keyword, this is keyword derived
960                 info = i;
961                 i.to_computed_value(context).clamp_to_non_negative()
962             },
963             FontSize::Smaller => {
964                 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
965                 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO)
966                     .to_computed_value(context, base_size)
967             },
968             FontSize::Larger => {
969                 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
970                 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(context, base_size)
971             },
972 
973             FontSize::System(_) => {
974                 #[cfg(feature = "servo")]
975                 {
976                     unreachable!()
977                 }
978                 #[cfg(feature = "gecko")]
979                 {
980                     context
981                         .cached_system_font
982                         .as_ref()
983                         .unwrap()
984                         .font_size
985                         .size
986                         .0
987                 }
988             },
989         };
990         computed::FontSize {
991             size: NonNegative(size),
992             keyword_info: info,
993         }
994     }
995 }
996 
997 impl ToComputedValue for FontSize {
998     type ComputedValue = computed::FontSize;
999 
1000     #[inline]
to_computed_value(&self, context: &Context) -> computed::FontSize1001     fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1002         self.to_computed_value_against(context, FontBaseSize::InheritedStyle)
1003     }
1004 
1005     #[inline]
from_computed_value(computed: &computed::FontSize) -> Self1006     fn from_computed_value(computed: &computed::FontSize) -> Self {
1007         FontSize::Length(LengthPercentage::Length(
1008             ToComputedValue::from_computed_value(&computed.size.0),
1009         ))
1010     }
1011 }
1012 
1013 impl FontSize {
1014     system_font_methods!(FontSize);
1015 
1016     /// Get initial value for specified font size.
1017     #[inline]
medium() -> Self1018     pub fn medium() -> Self {
1019         FontSize::Keyword(KeywordInfo::medium())
1020     }
1021 
1022     /// Parses a font-size, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<FontSize, ParseError<'i>>1023     pub fn parse_quirky<'i, 't>(
1024         context: &ParserContext,
1025         input: &mut Parser<'i, 't>,
1026         allow_quirks: AllowQuirks,
1027     ) -> Result<FontSize, ParseError<'i>> {
1028         if let Ok(lp) = input
1029             .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1030         {
1031             return Ok(FontSize::Length(lp));
1032         }
1033 
1034         if let Ok(kw) = input.try_parse(FontSizeKeyword::parse) {
1035             return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1036         }
1037 
1038         try_match_ident_ignore_ascii_case! { input,
1039             "smaller" => Ok(FontSize::Smaller),
1040             "larger" => Ok(FontSize::Larger),
1041         }
1042     }
1043 }
1044 
1045 impl Parse for FontSize {
1046     /// <length> | <percentage> | <absolute-size> | <relative-size>
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontSize, ParseError<'i>>1047     fn parse<'i, 't>(
1048         context: &ParserContext,
1049         input: &mut Parser<'i, 't>,
1050     ) -> Result<FontSize, ParseError<'i>> {
1051         FontSize::parse_quirky(context, input, AllowQuirks::No)
1052     }
1053 }
1054 
1055 bitflags! {
1056     #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
1057     /// Flags of variant alternates in bit
1058     struct VariantAlternatesParsingFlags: u8 {
1059         /// None of variant alternates enabled
1060         const NORMAL = 0;
1061         /// Historical forms
1062         const HISTORICAL_FORMS = 0x01;
1063         /// Stylistic Alternates
1064         const STYLISTIC = 0x02;
1065         /// Stylistic Sets
1066         const STYLESET = 0x04;
1067         /// Character Variant
1068         const CHARACTER_VARIANT = 0x08;
1069         /// Swash glyphs
1070         const SWASH = 0x10;
1071         /// Ornaments glyphs
1072         const ORNAMENTS = 0x20;
1073         /// Annotation forms
1074         const ANNOTATION = 0x40;
1075     }
1076 }
1077 
1078 #[derive(
1079     Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToResolvedValue, ToShmem,
1080 )]
1081 #[repr(C, u8)]
1082 /// Set of variant alternates
1083 pub enum VariantAlternates {
1084     /// Enables display of stylistic alternates
1085     #[css(function)]
1086     Stylistic(CustomIdent),
1087     /// Enables display with stylistic sets
1088     #[css(comma, function)]
1089     Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1090     /// Enables display of specific character variants
1091     #[css(comma, function)]
1092     CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1093     /// Enables display of swash glyphs
1094     #[css(function)]
1095     Swash(CustomIdent),
1096     /// Enables replacement of default glyphs with ornaments
1097     #[css(function)]
1098     Ornaments(CustomIdent),
1099     /// Enables display of alternate annotation forms
1100     #[css(function)]
1101     Annotation(CustomIdent),
1102     /// Enables display of historical forms
1103     HistoricalForms,
1104 }
1105 
1106 #[derive(
1107     Clone,
1108     Debug,
1109     Default,
1110     MallocSizeOf,
1111     PartialEq,
1112     SpecifiedValueInfo,
1113     ToCss,
1114     ToResolvedValue,
1115     ToShmem,
1116 )]
1117 #[repr(transparent)]
1118 /// List of Variant Alternates
1119 pub struct VariantAlternatesList(
1120     #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1121 );
1122 
1123 impl VariantAlternatesList {
1124     /// Returns the length of all variant alternates.
len(&self) -> usize1125     pub fn len(&self) -> usize {
1126         self.0.iter().fold(0, |acc, alternate| match *alternate {
1127             VariantAlternates::Swash(_) |
1128             VariantAlternates::Stylistic(_) |
1129             VariantAlternates::Ornaments(_) |
1130             VariantAlternates::Annotation(_) => acc + 1,
1131             VariantAlternates::Styleset(ref slice) |
1132             VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1133             _ => acc,
1134         })
1135     }
1136 }
1137 
1138 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1139 /// Control over the selection of these alternate glyphs
1140 pub enum FontVariantAlternates {
1141     /// Use alternative glyph from value
1142     Value(VariantAlternatesList),
1143     /// Use system font glyph
1144     #[css(skip)]
1145     System(SystemFont),
1146 }
1147 
1148 impl FontVariantAlternates {
1149     #[inline]
1150     /// Get initial specified value with VariantAlternatesList
get_initial_specified_value() -> Self1151     pub fn get_initial_specified_value() -> Self {
1152         FontVariantAlternates::Value(Default::default())
1153     }
1154 
1155     system_font_methods!(FontVariantAlternates, font_variant_alternates);
1156 }
1157 
1158 impl ToComputedValue for FontVariantAlternates {
1159     type ComputedValue = computed::FontVariantAlternates;
1160 
to_computed_value(&self, context: &Context) -> computed::FontVariantAlternates1161     fn to_computed_value(&self, context: &Context) -> computed::FontVariantAlternates {
1162         match *self {
1163             FontVariantAlternates::Value(ref v) => v.clone(),
1164             FontVariantAlternates::System(_) => self.compute_system(context),
1165         }
1166     }
1167 
from_computed_value(other: &computed::FontVariantAlternates) -> Self1168     fn from_computed_value(other: &computed::FontVariantAlternates) -> Self {
1169         FontVariantAlternates::Value(other.clone())
1170     }
1171 }
1172 
1173 impl Parse for FontVariantAlternates {
1174     /// normal |
1175     ///  [ stylistic(<feature-value-name>)           ||
1176     ///    historical-forms                          ||
1177     ///    styleset(<feature-value-name> #)          ||
1178     ///    character-variant(<feature-value-name> #) ||
1179     ///    swash(<feature-value-name>)               ||
1180     ///    ornaments(<feature-value-name>)           ||
1181     ///    annotation(<feature-value-name>) ]
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantAlternates, ParseError<'i>>1182     fn parse<'i, 't>(
1183         _: &ParserContext,
1184         input: &mut Parser<'i, 't>,
1185     ) -> Result<FontVariantAlternates, ParseError<'i>> {
1186         if input
1187             .try_parse(|input| input.expect_ident_matching("normal"))
1188             .is_ok()
1189         {
1190             return Ok(FontVariantAlternates::Value(Default::default()));
1191         }
1192 
1193         let mut alternates = Vec::new();
1194         let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1195         macro_rules! check_if_parsed(
1196             ($input:expr, $flag:path) => (
1197                 if parsed_alternates.contains($flag) {
1198                     return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1199                 }
1200                 parsed_alternates |= $flag;
1201             )
1202         );
1203         while let Ok(_) = input.try_parse(|input| match *input.next()? {
1204             Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1205                 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1206                 alternates.push(VariantAlternates::HistoricalForms);
1207                 Ok(())
1208             },
1209             Token::Function(ref name) => {
1210                 let name = name.clone();
1211                 input.parse_nested_block(|i| {
1212                     match_ignore_ascii_case! { &name,
1213                         "swash" => {
1214                             check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1215                             let location = i.current_source_location();
1216                             let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1217                             alternates.push(VariantAlternates::Swash(ident));
1218                             Ok(())
1219                         },
1220                         "stylistic" => {
1221                             check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1222                             let location = i.current_source_location();
1223                             let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1224                             alternates.push(VariantAlternates::Stylistic(ident));
1225                             Ok(())
1226                         },
1227                         "ornaments" => {
1228                             check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1229                             let location = i.current_source_location();
1230                             let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1231                             alternates.push(VariantAlternates::Ornaments(ident));
1232                             Ok(())
1233                         },
1234                         "annotation" => {
1235                             check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1236                             let location = i.current_source_location();
1237                             let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1238                             alternates.push(VariantAlternates::Annotation(ident));
1239                             Ok(())
1240                         },
1241                         "styleset" => {
1242                             check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1243                             let idents = i.parse_comma_separated(|i| {
1244                                 let location = i.current_source_location();
1245                                 CustomIdent::from_ident(location, i.expect_ident()?, &[])
1246                             })?;
1247                             alternates.push(VariantAlternates::Styleset(idents.into()));
1248                             Ok(())
1249                         },
1250                         "character-variant" => {
1251                             check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1252                             let idents = i.parse_comma_separated(|i| {
1253                                 let location = i.current_source_location();
1254                                 CustomIdent::from_ident(location, i.expect_ident()?, &[])
1255                             })?;
1256                             alternates.push(VariantAlternates::CharacterVariant(idents.into()));
1257                             Ok(())
1258                         },
1259                         _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1260                     }
1261                 })
1262             },
1263             _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1264         }) {}
1265 
1266         if parsed_alternates.is_empty() {
1267             return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1268         }
1269         Ok(FontVariantAlternates::Value(VariantAlternatesList(
1270             alternates.into(),
1271         )))
1272     }
1273 }
1274 
1275 macro_rules! impl_variant_east_asian {
1276     {
1277         $(
1278             $(#[$($meta:tt)+])*
1279             $ident:ident / $css:expr => $gecko:ident = $value:expr,
1280         )+
1281     } => {
1282         bitflags! {
1283             #[derive(MallocSizeOf, ToResolvedValue, ToShmem)]
1284             /// Vairants for east asian variant
1285             pub struct VariantEastAsian: u16 {
1286                 /// None of the features
1287                 const NORMAL = 0;
1288                 $(
1289                     $(#[$($meta)+])*
1290                     const $ident = $value;
1291                 )+
1292             }
1293         }
1294 
1295         impl ToCss for VariantEastAsian {
1296             fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1297             where
1298                 W: Write,
1299             {
1300                 if self.is_empty() {
1301                     return dest.write_str("normal");
1302                 }
1303 
1304                 let mut writer = SequenceWriter::new(dest, " ");
1305                 $(
1306                     if self.intersects(VariantEastAsian::$ident) {
1307                         writer.raw_item($css)?;
1308                     }
1309                 )+
1310                 Ok(())
1311             }
1312         }
1313 
1314         /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
1315         #[cfg(feature = "gecko")]
1316         #[inline]
1317         pub fn assert_variant_east_asian_matches() {
1318             use crate::gecko_bindings::structs;
1319             $(
1320                 debug_assert_eq!(structs::$gecko as u16, VariantEastAsian::$ident.bits());
1321             )+
1322         }
1323 
1324         impl SpecifiedValueInfo for VariantEastAsian {
1325             fn collect_completion_keywords(f: KeywordsCollectFn) {
1326                 f(&["normal", $($css,)+]);
1327             }
1328         }
1329     }
1330 }
1331 
1332 impl_variant_east_asian! {
1333     /// Enables rendering of JIS78 forms (OpenType feature: jp78)
1334     JIS78 / "jis78" => NS_FONT_VARIANT_EAST_ASIAN_JIS78 = 0x01,
1335     /// Enables rendering of JIS83 forms (OpenType feature: jp83).
1336     JIS83 / "jis83" => NS_FONT_VARIANT_EAST_ASIAN_JIS83 = 0x02,
1337     /// Enables rendering of JIS90 forms (OpenType feature: jp90).
1338     JIS90 / "jis90" => NS_FONT_VARIANT_EAST_ASIAN_JIS90 = 0x04,
1339     /// Enables rendering of JIS2004 forms (OpenType feature: jp04).
1340     JIS04 / "jis04" => NS_FONT_VARIANT_EAST_ASIAN_JIS04 = 0x08,
1341     /// Enables rendering of simplified forms (OpenType feature: smpl).
1342     SIMPLIFIED / "simplified" => NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED = 0x10,
1343     /// Enables rendering of traditional forms (OpenType feature: trad).
1344     TRADITIONAL / "traditional" => NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL = 0x20,
1345     /// Enables rendering of full-width variants (OpenType feature: fwid).
1346     FULL_WIDTH / "full-width" => NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH = 0x40,
1347     /// Enables rendering of proportionally-spaced variants (OpenType feature: pwid).
1348     PROPORTIONAL_WIDTH / "proportional-width" => NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH = 0x80,
1349     /// Enables display of ruby variant glyphs (OpenType feature: ruby).
1350     RUBY / "ruby" => NS_FONT_VARIANT_EAST_ASIAN_RUBY = 0x100,
1351 }
1352 
1353 #[cfg(feature = "gecko")]
1354 impl VariantEastAsian {
1355     /// Obtain a specified value from a Gecko keyword value
1356     ///
1357     /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u16) -> Self1358     pub fn from_gecko_keyword(kw: u16) -> Self {
1359         Self::from_bits_truncate(kw)
1360     }
1361 
1362     /// Transform into gecko keyword
to_gecko_keyword(self) -> u161363     pub fn to_gecko_keyword(self) -> u16 {
1364         self.bits()
1365     }
1366 }
1367 
1368 #[cfg(feature = "gecko")]
1369 impl_gecko_keyword_conversions!(VariantEastAsian, u16);
1370 
1371 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1372 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1373 /// Allows control of glyph substitution and sizing in East Asian text.
1374 pub enum FontVariantEastAsian {
1375     /// Value variant with `variant-east-asian`
1376     Value(VariantEastAsian),
1377     /// System font variant
1378     #[css(skip)]
1379     System(SystemFont),
1380 }
1381 
1382 impl FontVariantEastAsian {
1383     #[inline]
1384     /// Get default `font-variant-east-asian` with `empty` variant
empty() -> Self1385     pub fn empty() -> Self {
1386         FontVariantEastAsian::Value(VariantEastAsian::empty())
1387     }
1388 
1389     system_font_methods!(FontVariantEastAsian, font_variant_east_asian);
1390 }
1391 
1392 impl ToComputedValue for FontVariantEastAsian {
1393     type ComputedValue = computed::FontVariantEastAsian;
1394 
to_computed_value(&self, context: &Context) -> computed::FontVariantEastAsian1395     fn to_computed_value(&self, context: &Context) -> computed::FontVariantEastAsian {
1396         match *self {
1397             FontVariantEastAsian::Value(ref v) => v.clone(),
1398             FontVariantEastAsian::System(_) => self.compute_system(context),
1399         }
1400     }
1401 
from_computed_value(other: &computed::FontVariantEastAsian) -> Self1402     fn from_computed_value(other: &computed::FontVariantEastAsian) -> Self {
1403         FontVariantEastAsian::Value(other.clone())
1404     }
1405 }
1406 
1407 impl Parse for FontVariantEastAsian {
1408     /// normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ]
1409     /// <east-asian-variant-values> = [ jis78 | jis83 | jis90 | jis04 | simplified | traditional ]
1410     /// <east-asian-width-values>   = [ full-width | proportional-width ]
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantEastAsian, ParseError<'i>>1411     fn parse<'i, 't>(
1412         _context: &ParserContext,
1413         input: &mut Parser<'i, 't>,
1414     ) -> Result<FontVariantEastAsian, ParseError<'i>> {
1415         let mut result = VariantEastAsian::empty();
1416 
1417         if input
1418             .try_parse(|input| input.expect_ident_matching("normal"))
1419             .is_ok()
1420         {
1421             return Ok(FontVariantEastAsian::Value(result));
1422         }
1423 
1424         while let Ok(flag) = input.try_parse(|input| {
1425             Ok(
1426                 match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
1427                     "jis78" =>
1428                         exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1429                                                   VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1430                                                   VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1431                                         ) => VariantEastAsian::JIS78),
1432                     "jis83" =>
1433                         exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1434                                                   VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1435                                                   VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1436                                         ) => VariantEastAsian::JIS83),
1437                     "jis90" =>
1438                         exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1439                                                   VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1440                                                   VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1441                                         ) => VariantEastAsian::JIS90),
1442                     "jis04" =>
1443                         exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1444                                                   VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1445                                                   VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1446                                         ) => VariantEastAsian::JIS04),
1447                     "simplified" =>
1448                         exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1449                                                   VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1450                                                   VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1451                                         ) => VariantEastAsian::SIMPLIFIED),
1452                     "traditional" =>
1453                         exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1454                                                   VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1455                                                   VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1456                                         ) => VariantEastAsian::TRADITIONAL),
1457                     "full-width" =>
1458                         exclusive_value!((result, VariantEastAsian::FULL_WIDTH |
1459                                                   VariantEastAsian::PROPORTIONAL_WIDTH
1460                                         ) => VariantEastAsian::FULL_WIDTH),
1461                     "proportional-width" =>
1462                         exclusive_value!((result, VariantEastAsian::FULL_WIDTH |
1463                                                   VariantEastAsian::PROPORTIONAL_WIDTH
1464                                         ) => VariantEastAsian::PROPORTIONAL_WIDTH),
1465                     "ruby" =>
1466                         exclusive_value!((result, VariantEastAsian::RUBY) => VariantEastAsian::RUBY),
1467                     _ => return Err(()),
1468                 },
1469             )
1470         }) {
1471             result.insert(flag);
1472         }
1473 
1474         if !result.is_empty() {
1475             Ok(FontVariantEastAsian::Value(result))
1476         } else {
1477             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1478         }
1479     }
1480 }
1481 
1482 macro_rules! impl_variant_ligatures {
1483     {
1484         $(
1485             $(#[$($meta:tt)+])*
1486             $ident:ident / $css:expr => $gecko:ident = $value:expr,
1487         )+
1488     } => {
1489         bitflags! {
1490             #[derive(MallocSizeOf, ToResolvedValue, ToShmem)]
1491             /// Variants of ligatures
1492             pub struct VariantLigatures: u16 {
1493                 /// Specifies that common default features are enabled
1494                 const NORMAL = 0;
1495                 $(
1496                     $(#[$($meta)+])*
1497                     const $ident = $value;
1498                 )+
1499             }
1500         }
1501 
1502         impl ToCss for VariantLigatures {
1503             fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1504             where
1505                 W: Write,
1506             {
1507                 if self.is_empty() {
1508                     return dest.write_str("normal");
1509                 }
1510                 if self.contains(VariantLigatures::NONE) {
1511                     return dest.write_str("none");
1512                 }
1513 
1514                 let mut writer = SequenceWriter::new(dest, " ");
1515                 $(
1516                     if self.intersects(VariantLigatures::$ident) {
1517                         writer.raw_item($css)?;
1518                     }
1519                 )+
1520                 Ok(())
1521             }
1522         }
1523 
1524         /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
1525         #[cfg(feature = "gecko")]
1526         #[inline]
1527         pub fn assert_variant_ligatures_matches() {
1528             use crate::gecko_bindings::structs;
1529             $(
1530                 debug_assert_eq!(structs::$gecko as u16, VariantLigatures::$ident.bits());
1531             )+
1532         }
1533 
1534         impl SpecifiedValueInfo for VariantLigatures {
1535             fn collect_completion_keywords(f: KeywordsCollectFn) {
1536                 f(&["normal", $($css,)+]);
1537             }
1538         }
1539     }
1540 }
1541 
1542 impl_variant_ligatures! {
1543     /// Specifies that all types of ligatures and contextual forms
1544     /// covered by this property are explicitly disabled
1545     NONE / "none" => NS_FONT_VARIANT_LIGATURES_NONE = 0x01,
1546     /// Enables display of common ligatures
1547     COMMON_LIGATURES / "common-ligatures" => NS_FONT_VARIANT_LIGATURES_COMMON = 0x02,
1548     /// Disables display of common ligatures
1549     NO_COMMON_LIGATURES / "no-common-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_COMMON = 0x04,
1550     /// Enables display of discretionary ligatures
1551     DISCRETIONARY_LIGATURES / "discretionary-ligatures" => NS_FONT_VARIANT_LIGATURES_DISCRETIONARY = 0x08,
1552     /// Disables display of discretionary ligatures
1553     NO_DISCRETIONARY_LIGATURES / "no-discretionary-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY = 0x10,
1554     /// Enables display of historical ligatures
1555     HISTORICAL_LIGATURES / "historical-ligatures" => NS_FONT_VARIANT_LIGATURES_HISTORICAL = 0x20,
1556     /// Disables display of historical ligatures
1557     NO_HISTORICAL_LIGATURES / "no-historical-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL = 0x40,
1558     /// Enables display of contextual alternates
1559     CONTEXTUAL / "contextual" => NS_FONT_VARIANT_LIGATURES_CONTEXTUAL = 0x80,
1560     /// Disables display of contextual alternates
1561     NO_CONTEXTUAL / "no-contextual" => NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL = 0x100,
1562 }
1563 
1564 #[cfg(feature = "gecko")]
1565 impl VariantLigatures {
1566     /// Obtain a specified value from a Gecko keyword value
1567     ///
1568     /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u16) -> Self1569     pub fn from_gecko_keyword(kw: u16) -> Self {
1570         Self::from_bits_truncate(kw)
1571     }
1572 
1573     /// Transform into gecko keyword
to_gecko_keyword(self) -> u161574     pub fn to_gecko_keyword(self) -> u16 {
1575         self.bits()
1576     }
1577 }
1578 
1579 #[cfg(feature = "gecko")]
1580 impl_gecko_keyword_conversions!(VariantLigatures, u16);
1581 
1582 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1583 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1584 /// Ligatures and contextual forms are ways of combining glyphs
1585 /// to produce more harmonized forms
1586 pub enum FontVariantLigatures {
1587     /// Value variant with `variant-ligatures`
1588     Value(VariantLigatures),
1589     /// System font variant
1590     #[css(skip)]
1591     System(SystemFont),
1592 }
1593 
1594 impl FontVariantLigatures {
1595     system_font_methods!(FontVariantLigatures, font_variant_ligatures);
1596 
1597     /// Default value of `font-variant-ligatures` as `empty`
1598     #[inline]
empty() -> FontVariantLigatures1599     pub fn empty() -> FontVariantLigatures {
1600         FontVariantLigatures::Value(VariantLigatures::empty())
1601     }
1602 
1603     #[inline]
1604     /// Get `none` variant of `font-variant-ligatures`
none() -> FontVariantLigatures1605     pub fn none() -> FontVariantLigatures {
1606         FontVariantLigatures::Value(VariantLigatures::NONE)
1607     }
1608 }
1609 
1610 impl ToComputedValue for FontVariantLigatures {
1611     type ComputedValue = computed::FontVariantLigatures;
1612 
to_computed_value(&self, context: &Context) -> computed::FontVariantLigatures1613     fn to_computed_value(&self, context: &Context) -> computed::FontVariantLigatures {
1614         match *self {
1615             FontVariantLigatures::Value(ref v) => v.clone(),
1616             FontVariantLigatures::System(_) => self.compute_system(context),
1617         }
1618     }
1619 
from_computed_value(other: &computed::FontVariantLigatures) -> Self1620     fn from_computed_value(other: &computed::FontVariantLigatures) -> Self {
1621         FontVariantLigatures::Value(other.clone())
1622     }
1623 }
1624 
1625 impl Parse for FontVariantLigatures {
1626     /// normal | none |
1627     /// [ <common-lig-values> ||
1628     ///   <discretionary-lig-values> ||
1629     ///   <historical-lig-values> ||
1630     ///   <contextual-alt-values> ]
1631     /// <common-lig-values>        = [ common-ligatures | no-common-ligatures ]
1632     /// <discretionary-lig-values> = [ discretionary-ligatures | no-discretionary-ligatures ]
1633     /// <historical-lig-values>    = [ historical-ligatures | no-historical-ligatures ]
1634     /// <contextual-alt-values>    = [ contextual | no-contextual ]
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantLigatures, ParseError<'i>>1635     fn parse<'i, 't>(
1636         _context: &ParserContext,
1637         input: &mut Parser<'i, 't>,
1638     ) -> Result<FontVariantLigatures, ParseError<'i>> {
1639         let mut result = VariantLigatures::empty();
1640 
1641         if input
1642             .try_parse(|input| input.expect_ident_matching("normal"))
1643             .is_ok()
1644         {
1645             return Ok(FontVariantLigatures::Value(result));
1646         }
1647         if input
1648             .try_parse(|input| input.expect_ident_matching("none"))
1649             .is_ok()
1650         {
1651             return Ok(FontVariantLigatures::Value(VariantLigatures::NONE));
1652         }
1653 
1654         while let Ok(flag) = input.try_parse(|input| {
1655             Ok(
1656                 match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
1657                     "common-ligatures" =>
1658                         exclusive_value!((result, VariantLigatures::COMMON_LIGATURES |
1659                                                   VariantLigatures::NO_COMMON_LIGATURES
1660                                         ) => VariantLigatures::COMMON_LIGATURES),
1661                     "no-common-ligatures" =>
1662                         exclusive_value!((result, VariantLigatures::COMMON_LIGATURES |
1663                                                   VariantLigatures::NO_COMMON_LIGATURES
1664                                         ) => VariantLigatures::NO_COMMON_LIGATURES),
1665                     "discretionary-ligatures" =>
1666                         exclusive_value!((result, VariantLigatures::DISCRETIONARY_LIGATURES |
1667                                                   VariantLigatures::NO_DISCRETIONARY_LIGATURES
1668                                         ) => VariantLigatures::DISCRETIONARY_LIGATURES),
1669                     "no-discretionary-ligatures" =>
1670                         exclusive_value!((result, VariantLigatures::DISCRETIONARY_LIGATURES |
1671                                                   VariantLigatures::NO_DISCRETIONARY_LIGATURES
1672                                         ) => VariantLigatures::NO_DISCRETIONARY_LIGATURES),
1673                     "historical-ligatures" =>
1674                         exclusive_value!((result, VariantLigatures::HISTORICAL_LIGATURES |
1675                                                   VariantLigatures::NO_HISTORICAL_LIGATURES
1676                                         ) => VariantLigatures::HISTORICAL_LIGATURES),
1677                     "no-historical-ligatures" =>
1678                         exclusive_value!((result, VariantLigatures::HISTORICAL_LIGATURES |
1679                                                   VariantLigatures::NO_HISTORICAL_LIGATURES
1680                                         ) => VariantLigatures::NO_HISTORICAL_LIGATURES),
1681                     "contextual" =>
1682                         exclusive_value!((result, VariantLigatures::CONTEXTUAL |
1683                                                   VariantLigatures::NO_CONTEXTUAL
1684                                         ) => VariantLigatures::CONTEXTUAL),
1685                     "no-contextual" =>
1686                         exclusive_value!((result, VariantLigatures::CONTEXTUAL |
1687                                                   VariantLigatures::NO_CONTEXTUAL
1688                                         ) => VariantLigatures::NO_CONTEXTUAL),
1689                     _ => return Err(()),
1690                 },
1691             )
1692         }) {
1693             result.insert(flag);
1694         }
1695 
1696         if !result.is_empty() {
1697             Ok(FontVariantLigatures::Value(result))
1698         } else {
1699             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1700         }
1701     }
1702 }
1703 
1704 macro_rules! impl_variant_numeric {
1705     {
1706         $(
1707             $(#[$($meta:tt)+])*
1708             $ident:ident / $css:expr => $gecko:ident = $value:expr,
1709         )+
1710     } => {
1711         bitflags! {
1712             #[derive(MallocSizeOf, ToResolvedValue, ToShmem)]
1713             /// Vairants of numeric values
1714             pub struct VariantNumeric: u8 {
1715                 /// None of other variants are enabled.
1716                 const NORMAL = 0;
1717                 $(
1718                     $(#[$($meta)+])*
1719                     const $ident = $value;
1720                 )+
1721             }
1722         }
1723 
1724         impl ToCss for VariantNumeric {
1725             fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1726             where
1727                 W: Write,
1728             {
1729                 if self.is_empty() {
1730                     return dest.write_str("normal");
1731                 }
1732 
1733                 let mut writer = SequenceWriter::new(dest, " ");
1734                 $(
1735                     if self.intersects(VariantNumeric::$ident) {
1736                         writer.raw_item($css)?;
1737                     }
1738                 )+
1739                 Ok(())
1740             }
1741         }
1742 
1743         /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
1744         #[cfg(feature = "gecko")]
1745         #[inline]
1746         pub fn assert_variant_numeric_matches() {
1747             use crate::gecko_bindings::structs;
1748             $(
1749                 debug_assert_eq!(structs::$gecko as u8, VariantNumeric::$ident.bits());
1750             )+
1751         }
1752 
1753         impl SpecifiedValueInfo for VariantNumeric {
1754             fn collect_completion_keywords(f: KeywordsCollectFn) {
1755                 f(&["normal", $($css,)+]);
1756             }
1757         }
1758     }
1759 }
1760 
1761 impl_variant_numeric! {
1762     /// Enables display of lining numerals.
1763     LINING_NUMS / "lining-nums" => NS_FONT_VARIANT_NUMERIC_LINING = 0x01,
1764     /// Enables display of old-style numerals.
1765     OLDSTYLE_NUMS / "oldstyle-nums" => NS_FONT_VARIANT_NUMERIC_OLDSTYLE = 0x02,
1766     /// Enables display of proportional numerals.
1767     PROPORTIONAL_NUMS / "proportional-nums" => NS_FONT_VARIANT_NUMERIC_PROPORTIONAL = 0x04,
1768     /// Enables display of tabular numerals.
1769     TABULAR_NUMS / "tabular-nums" => NS_FONT_VARIANT_NUMERIC_TABULAR = 0x08,
1770     /// Enables display of lining diagonal fractions.
1771     DIAGONAL_FRACTIONS / "diagonal-fractions" => NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS = 0x10,
1772     /// Enables display of lining stacked fractions.
1773     STACKED_FRACTIONS / "stacked-fractions" => NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS = 0x20,
1774     /// Enables display of letter forms used with ordinal numbers.
1775     ORDINAL / "ordinal" => NS_FONT_VARIANT_NUMERIC_ORDINAL = 0x80,
1776     /// Enables display of slashed zeros.
1777     SLASHED_ZERO / "slashed-zero" => NS_FONT_VARIANT_NUMERIC_SLASHZERO = 0x40,
1778 }
1779 
1780 #[cfg(feature = "gecko")]
1781 impl VariantNumeric {
1782     /// Obtain a specified value from a Gecko keyword value
1783     ///
1784     /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u8) -> Self1785     pub fn from_gecko_keyword(kw: u8) -> Self {
1786         Self::from_bits_truncate(kw)
1787     }
1788 
1789     /// Transform into gecko keyword
to_gecko_keyword(self) -> u81790     pub fn to_gecko_keyword(self) -> u8 {
1791         self.bits()
1792     }
1793 }
1794 
1795 #[cfg(feature = "gecko")]
1796 impl_gecko_keyword_conversions!(VariantNumeric, u8);
1797 
1798 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1799 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1800 /// Specifies control over numerical forms.
1801 pub enum FontVariantNumeric {
1802     /// Value variant with `variant-numeric`
1803     Value(VariantNumeric),
1804     /// System font
1805     #[css(skip)]
1806     System(SystemFont),
1807 }
1808 
1809 impl FontVariantNumeric {
1810     #[inline]
1811     /// Default value of `font-variant-numeric` as `empty`
empty() -> FontVariantNumeric1812     pub fn empty() -> FontVariantNumeric {
1813         FontVariantNumeric::Value(VariantNumeric::empty())
1814     }
1815 
1816     system_font_methods!(FontVariantNumeric, font_variant_numeric);
1817 }
1818 
1819 impl ToComputedValue for FontVariantNumeric {
1820     type ComputedValue = computed::FontVariantNumeric;
1821 
to_computed_value(&self, context: &Context) -> computed::FontVariantNumeric1822     fn to_computed_value(&self, context: &Context) -> computed::FontVariantNumeric {
1823         match *self {
1824             FontVariantNumeric::Value(ref v) => v.clone(),
1825             FontVariantNumeric::System(_) => self.compute_system(context),
1826         }
1827     }
1828 
from_computed_value(other: &computed::FontVariantNumeric) -> Self1829     fn from_computed_value(other: &computed::FontVariantNumeric) -> Self {
1830         FontVariantNumeric::Value(other.clone())
1831     }
1832 }
1833 
1834 impl Parse for FontVariantNumeric {
1835     /// normal |
1836     ///  [ <numeric-figure-values>   ||
1837     ///    <numeric-spacing-values>  ||
1838     ///    <numeric-fraction-values> ||
1839     ///    ordinal                   ||
1840     ///    slashed-zero ]
1841     /// <numeric-figure-values>   = [ lining-nums | oldstyle-nums ]
1842     /// <numeric-spacing-values>  = [ proportional-nums | tabular-nums ]
1843     /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantNumeric, ParseError<'i>>1844     fn parse<'i, 't>(
1845         _context: &ParserContext,
1846         input: &mut Parser<'i, 't>,
1847     ) -> Result<FontVariantNumeric, ParseError<'i>> {
1848         let mut result = VariantNumeric::empty();
1849 
1850         if input
1851             .try_parse(|input| input.expect_ident_matching("normal"))
1852             .is_ok()
1853         {
1854             return Ok(FontVariantNumeric::Value(result));
1855         }
1856 
1857         while let Ok(flag) = input.try_parse(|input| {
1858             Ok(
1859                 match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
1860                     "ordinal" =>
1861                         exclusive_value!((result, VariantNumeric::ORDINAL) => VariantNumeric::ORDINAL),
1862                     "slashed-zero" =>
1863                         exclusive_value!((result, VariantNumeric::SLASHED_ZERO) => VariantNumeric::SLASHED_ZERO),
1864                     "lining-nums" =>
1865                         exclusive_value!((result, VariantNumeric::LINING_NUMS |
1866                                                   VariantNumeric::OLDSTYLE_NUMS
1867                                         ) => VariantNumeric::LINING_NUMS),
1868                     "oldstyle-nums" =>
1869                         exclusive_value!((result, VariantNumeric::LINING_NUMS |
1870                                                   VariantNumeric::OLDSTYLE_NUMS
1871                                         ) => VariantNumeric::OLDSTYLE_NUMS),
1872                     "proportional-nums" =>
1873                         exclusive_value!((result, VariantNumeric::PROPORTIONAL_NUMS |
1874                                                   VariantNumeric::TABULAR_NUMS
1875                                         ) => VariantNumeric::PROPORTIONAL_NUMS),
1876                     "tabular-nums" =>
1877                         exclusive_value!((result, VariantNumeric::PROPORTIONAL_NUMS |
1878                                                   VariantNumeric::TABULAR_NUMS
1879                                         ) => VariantNumeric::TABULAR_NUMS),
1880                     "diagonal-fractions" =>
1881                         exclusive_value!((result, VariantNumeric::DIAGONAL_FRACTIONS |
1882                                                   VariantNumeric::STACKED_FRACTIONS
1883                                         ) => VariantNumeric::DIAGONAL_FRACTIONS),
1884                     "stacked-fractions" =>
1885                         exclusive_value!((result, VariantNumeric::DIAGONAL_FRACTIONS |
1886                                                   VariantNumeric::STACKED_FRACTIONS
1887                                         ) => VariantNumeric::STACKED_FRACTIONS),
1888                     _ => return Err(()),
1889                 },
1890             )
1891         }) {
1892             result.insert(flag);
1893         }
1894 
1895         if !result.is_empty() {
1896             Ok(FontVariantNumeric::Value(result))
1897         } else {
1898             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1899         }
1900     }
1901 }
1902 
1903 /// This property provides low-level control over OpenType or TrueType font features.
1904 pub type SpecifiedFontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1905 
1906 /// Define initial settings that apply when the font defined by an @font-face
1907 /// rule is rendered.
1908 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1909 pub enum FontFeatureSettings {
1910     /// Value of `FontSettings`
1911     Value(SpecifiedFontFeatureSettings),
1912     /// System font
1913     #[css(skip)]
1914     System(SystemFont),
1915 }
1916 
1917 impl FontFeatureSettings {
1918     #[inline]
1919     /// Get default value of `font-feature-settings` as normal
normal() -> FontFeatureSettings1920     pub fn normal() -> FontFeatureSettings {
1921         FontFeatureSettings::Value(FontSettings::normal())
1922     }
1923 
1924     system_font_methods!(FontFeatureSettings, font_feature_settings);
1925 }
1926 
1927 impl ToComputedValue for FontFeatureSettings {
1928     type ComputedValue = computed::FontFeatureSettings;
1929 
to_computed_value(&self, context: &Context) -> computed::FontFeatureSettings1930     fn to_computed_value(&self, context: &Context) -> computed::FontFeatureSettings {
1931         match *self {
1932             FontFeatureSettings::Value(ref v) => v.to_computed_value(context),
1933             FontFeatureSettings::System(_) => self.compute_system(context),
1934         }
1935     }
1936 
from_computed_value(other: &computed::FontFeatureSettings) -> Self1937     fn from_computed_value(other: &computed::FontFeatureSettings) -> Self {
1938         FontFeatureSettings::Value(ToComputedValue::from_computed_value(other))
1939     }
1940 }
1941 
1942 impl Parse for FontFeatureSettings {
1943     /// normal | <feature-tag-value>#
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontFeatureSettings, ParseError<'i>>1944     fn parse<'i, 't>(
1945         context: &ParserContext,
1946         input: &mut Parser<'i, 't>,
1947     ) -> Result<FontFeatureSettings, ParseError<'i>> {
1948         SpecifiedFontFeatureSettings::parse(context, input).map(FontFeatureSettings::Value)
1949     }
1950 }
1951 
1952 #[derive(
1953     Clone,
1954     Copy,
1955     Debug,
1956     MallocSizeOf,
1957     PartialEq,
1958     SpecifiedValueInfo,
1959     ToComputedValue,
1960     ToResolvedValue,
1961     ToShmem,
1962 )]
1963 /// Whether user agents are allowed to synthesize bold or oblique font faces
1964 /// when a font family lacks bold or italic faces
1965 pub struct FontSynthesis {
1966     /// If a `font-weight` is requested that the font family does not contain,
1967     /// the user agent may synthesize the requested weight from the weights
1968     /// that do exist in the font family.
1969     #[css(represents_keyword)]
1970     pub weight: bool,
1971     /// If a font-style is requested that the font family does not contain,
1972     /// the user agent may synthesize the requested style from the normal face in the font family.
1973     #[css(represents_keyword)]
1974     pub style: bool,
1975 }
1976 
1977 impl FontSynthesis {
1978     #[inline]
1979     /// Get the default value of font-synthesis
get_initial_value() -> Self1980     pub fn get_initial_value() -> Self {
1981         FontSynthesis {
1982             weight: true,
1983             style: true,
1984         }
1985     }
1986     #[inline]
1987     /// Get the 'none' value of font-synthesis
none() -> Self1988     pub fn none() -> Self {
1989         FontSynthesis {
1990             weight: false,
1991             style: false,
1992         }
1993     }
1994 }
1995 
1996 impl Parse for FontSynthesis {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontSynthesis, ParseError<'i>>1997     fn parse<'i, 't>(
1998         _: &ParserContext,
1999         input: &mut Parser<'i, 't>,
2000     ) -> Result<FontSynthesis, ParseError<'i>> {
2001         let mut result = FontSynthesis {
2002             weight: false,
2003             style: false,
2004         };
2005         try_match_ident_ignore_ascii_case! { input,
2006             "none" => Ok(result),
2007             "weight" => {
2008                 result.weight = true;
2009                 if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() {
2010                     result.style = true;
2011                 }
2012                 Ok(result)
2013             },
2014             "style" => {
2015                 result.style = true;
2016                 if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() {
2017                     result.weight = true;
2018                 }
2019                 Ok(result)
2020             },
2021         }
2022     }
2023 }
2024 
2025 impl ToCss for FontSynthesis {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,2026     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2027     where
2028         W: Write,
2029     {
2030         if self.weight && self.style {
2031             dest.write_str("weight style")
2032         } else if self.style {
2033             dest.write_str("style")
2034         } else if self.weight {
2035             dest.write_str("weight")
2036         } else {
2037             dest.write_str("none")
2038         }
2039     }
2040 }
2041 
2042 #[cfg(feature = "gecko")]
2043 impl From<u8> for FontSynthesis {
from(bits: u8) -> FontSynthesis2044     fn from(bits: u8) -> FontSynthesis {
2045         use crate::gecko_bindings::structs;
2046 
2047         FontSynthesis {
2048             weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
2049             style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0,
2050         }
2051     }
2052 }
2053 
2054 #[cfg(feature = "gecko")]
2055 impl From<FontSynthesis> for u8 {
from(v: FontSynthesis) -> u82056     fn from(v: FontSynthesis) -> u8 {
2057         use crate::gecko_bindings::structs;
2058 
2059         let mut bits: u8 = 0;
2060         if v.weight {
2061             bits |= structs::NS_FONT_SYNTHESIS_WEIGHT as u8;
2062         }
2063         if v.style {
2064             bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
2065         }
2066         bits
2067     }
2068 }
2069 
2070 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2071 /// Allows authors to explicitly specify the language system of the font,
2072 /// overriding the language system implied by the content language
2073 pub enum FontLanguageOverride {
2074     /// When rendering with OpenType fonts,
2075     /// the content language of the element is
2076     /// used to infer the OpenType language system
2077     Normal,
2078     /// Single three-letter case-sensitive OpenType language system tag,
2079     /// specifies the OpenType language system to be used instead of
2080     /// the language system implied by the language of the element
2081     Override(Box<str>),
2082     /// Use system font
2083     #[css(skip)]
2084     System(SystemFont),
2085 }
2086 
2087 impl FontLanguageOverride {
2088     #[inline]
2089     /// Get default value with `normal`
normal() -> FontLanguageOverride2090     pub fn normal() -> FontLanguageOverride {
2091         FontLanguageOverride::Normal
2092     }
2093 
2094     /// The ToComputedValue implementation for non-system-font
2095     /// FontLanguageOverride, used for @font-face descriptors.
2096     #[inline]
compute_non_system(&self) -> computed::FontLanguageOverride2097     pub fn compute_non_system(&self) -> computed::FontLanguageOverride {
2098         match *self {
2099             FontLanguageOverride::Normal => computed::FontLanguageOverride::zero(),
2100             FontLanguageOverride::Override(ref lang) => {
2101                 computed::FontLanguageOverride::from_str(lang)
2102             },
2103             FontLanguageOverride::System(..) => unreachable!(),
2104         }
2105     }
2106 
2107     system_font_methods!(FontLanguageOverride, font_language_override);
2108 }
2109 
2110 impl ToComputedValue for FontLanguageOverride {
2111     type ComputedValue = computed::FontLanguageOverride;
2112 
2113     #[inline]
to_computed_value(&self, context: &Context) -> computed::FontLanguageOverride2114     fn to_computed_value(&self, context: &Context) -> computed::FontLanguageOverride {
2115         match *self {
2116             FontLanguageOverride::System(_) => self.compute_system(context),
2117             _ => self.compute_non_system(),
2118         }
2119     }
2120     #[inline]
from_computed_value(computed: &computed::FontLanguageOverride) -> Self2121     fn from_computed_value(computed: &computed::FontLanguageOverride) -> Self {
2122         if *computed == computed::FontLanguageOverride::zero() {
2123             return FontLanguageOverride::Normal;
2124         }
2125         FontLanguageOverride::Override(computed.to_str(&mut [0; 4]).into())
2126     }
2127 }
2128 
2129 impl Parse for FontLanguageOverride {
2130     /// normal | <string>
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontLanguageOverride, ParseError<'i>>2131     fn parse<'i, 't>(
2132         _: &ParserContext,
2133         input: &mut Parser<'i, 't>,
2134     ) -> Result<FontLanguageOverride, ParseError<'i>> {
2135         if input
2136             .try_parse(|input| input.expect_ident_matching("normal"))
2137             .is_ok()
2138         {
2139             return Ok(FontLanguageOverride::Normal);
2140         }
2141 
2142         let string = input.expect_string()?;
2143         Ok(FontLanguageOverride::Override(
2144             string.as_ref().to_owned().into_boxed_str(),
2145         ))
2146     }
2147 }
2148 
2149 /// This property provides low-level control over OpenType or TrueType font
2150 /// variations.
2151 pub type SpecifiedFontVariationSettings = FontSettings<VariationValue<Number>>;
2152 
2153 /// Define initial settings that apply when the font defined by an @font-face
2154 /// rule is rendered.
2155 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2156 pub enum FontVariationSettings {
2157     /// Value of `FontSettings`
2158     Value(SpecifiedFontVariationSettings),
2159     /// System font
2160     #[css(skip)]
2161     System(SystemFont),
2162 }
2163 
2164 impl FontVariationSettings {
2165     #[inline]
2166     /// Get default value of `font-variation-settings` as normal
normal() -> FontVariationSettings2167     pub fn normal() -> FontVariationSettings {
2168         FontVariationSettings::Value(FontSettings::normal())
2169     }
2170 
2171     system_font_methods!(FontVariationSettings, font_variation_settings);
2172 }
2173 
2174 impl ToComputedValue for FontVariationSettings {
2175     type ComputedValue = computed::FontVariationSettings;
2176 
to_computed_value(&self, context: &Context) -> computed::FontVariationSettings2177     fn to_computed_value(&self, context: &Context) -> computed::FontVariationSettings {
2178         match *self {
2179             FontVariationSettings::Value(ref v) => v.to_computed_value(context),
2180             FontVariationSettings::System(_) => self.compute_system(context),
2181         }
2182     }
2183 
from_computed_value(other: &computed::FontVariationSettings) -> Self2184     fn from_computed_value(other: &computed::FontVariationSettings) -> Self {
2185         FontVariationSettings::Value(ToComputedValue::from_computed_value(other))
2186     }
2187 }
2188 
2189 impl Parse for FontVariationSettings {
2190     /// normal | <variation-tag-value>#
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariationSettings, ParseError<'i>>2191     fn parse<'i, 't>(
2192         context: &ParserContext,
2193         input: &mut Parser<'i, 't>,
2194     ) -> Result<FontVariationSettings, ParseError<'i>> {
2195         SpecifiedFontVariationSettings::parse(context, input).map(FontVariationSettings::Value)
2196     }
2197 }
2198 
parse_one_feature_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Integer, ParseError<'i>>2199 fn parse_one_feature_value<'i, 't>(
2200     context: &ParserContext,
2201     input: &mut Parser<'i, 't>,
2202 ) -> Result<Integer, ParseError<'i>> {
2203     if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
2204         return Ok(integer);
2205     }
2206 
2207     try_match_ident_ignore_ascii_case! { input,
2208         "on" => Ok(Integer::new(1)),
2209         "off" => Ok(Integer::new(0)),
2210     }
2211 }
2212 
2213 impl Parse for FeatureTagValue<Integer> {
2214     /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>2215     fn parse<'i, 't>(
2216         context: &ParserContext,
2217         input: &mut Parser<'i, 't>,
2218     ) -> Result<Self, ParseError<'i>> {
2219         let tag = FontTag::parse(context, input)?;
2220         let value = input
2221             .try_parse(|i| parse_one_feature_value(context, i))
2222             .unwrap_or_else(|_| Integer::new(1));
2223 
2224         Ok(Self { tag, value })
2225     }
2226 }
2227 
2228 impl Parse for VariationValue<Number> {
2229     /// This is the `<string> <number>` part of the font-variation-settings
2230     /// syntax.
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>2231     fn parse<'i, 't>(
2232         context: &ParserContext,
2233         input: &mut Parser<'i, 't>,
2234     ) -> Result<Self, ParseError<'i>> {
2235         let tag = FontTag::parse(context, input)?;
2236         let value = Number::parse(context, input)?;
2237         Ok(Self { tag, value })
2238     }
2239 }
2240 
2241 /// A metrics override value for a @font-face descriptor
2242 ///
2243 /// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc
2244 #[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2245 pub enum MetricsOverride {
2246     /// A non-negative `<percentage>` of the computed font size
2247     Override(NonNegativePercentage),
2248     /// Normal metrics from the font.
2249     Normal,
2250 }
2251 
2252 impl MetricsOverride {
2253     #[inline]
2254     /// Get default value with `normal`
normal() -> MetricsOverride2255     pub fn normal() -> MetricsOverride {
2256         MetricsOverride::Normal
2257     }
2258 
2259     /// The ToComputedValue implementation, used for @font-face descriptors.
2260     ///
2261     /// Valid override percentages must be non-negative; we return -1.0 to indicate
2262     /// the absence of an override (i.e. 'normal').
2263     #[inline]
compute(&self) -> ComputedPercentage2264     pub fn compute(&self) -> ComputedPercentage {
2265         match *self {
2266             MetricsOverride::Normal => ComputedPercentage(-1.0),
2267             MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
2268         }
2269     }
2270 }
2271 
2272 #[derive(
2273     Clone,
2274     Copy,
2275     Debug,
2276     MallocSizeOf,
2277     PartialEq,
2278     SpecifiedValueInfo,
2279     ToComputedValue,
2280     ToCss,
2281     ToResolvedValue,
2282     ToShmem,
2283 )]
2284 /// text-zoom. Enable if true, disable if false
2285 pub struct XTextZoom(#[css(skip)] pub bool);
2286 
2287 impl Parse for XTextZoom {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<XTextZoom, ParseError<'i>>2288     fn parse<'i, 't>(
2289         _: &ParserContext,
2290         input: &mut Parser<'i, 't>,
2291     ) -> Result<XTextZoom, ParseError<'i>> {
2292         debug_assert!(
2293             false,
2294             "Should be set directly by presentation attributes only."
2295         );
2296         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2297     }
2298 }
2299 
2300 #[derive(
2301     Clone,
2302     Debug,
2303     MallocSizeOf,
2304     PartialEq,
2305     SpecifiedValueInfo,
2306     ToComputedValue,
2307     ToCss,
2308     ToResolvedValue,
2309     ToShmem,
2310 )]
2311 /// Internal property that reflects the lang attribute
2312 pub struct XLang(#[css(skip)] pub Atom);
2313 
2314 impl XLang {
2315     #[inline]
2316     /// Get default value for `-x-lang`
get_initial_value() -> XLang2317     pub fn get_initial_value() -> XLang {
2318         XLang(atom!(""))
2319     }
2320 }
2321 
2322 impl Parse for XLang {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<XLang, ParseError<'i>>2323     fn parse<'i, 't>(
2324         _: &ParserContext,
2325         input: &mut Parser<'i, 't>,
2326     ) -> Result<XLang, ParseError<'i>> {
2327         debug_assert!(
2328             false,
2329             "Should be set directly by presentation attributes only."
2330         );
2331         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2332     }
2333 }
2334 
2335 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2336 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2337 /// Specifies the minimum font size allowed due to changes in scriptlevel.
2338 /// Ref: https://wiki.mozilla.org/MathML:mstyle
2339 pub struct MozScriptMinSize(pub NoCalcLength);
2340 
2341 impl MozScriptMinSize {
2342     #[inline]
2343     /// Calculate initial value of -moz-script-min-size.
get_initial_value() -> Length2344     pub fn get_initial_value() -> Length {
2345         Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * (AU_PER_PT / AU_PER_PX))
2346     }
2347 }
2348 
2349 impl Parse for MozScriptMinSize {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<MozScriptMinSize, ParseError<'i>>2350     fn parse<'i, 't>(
2351         _: &ParserContext,
2352         input: &mut Parser<'i, 't>,
2353     ) -> Result<MozScriptMinSize, ParseError<'i>> {
2354         debug_assert!(
2355             false,
2356             "Should be set directly by presentation attributes only."
2357         );
2358         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2359     }
2360 }
2361 
2362 /// A value for the `math-depth` property.
2363 /// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property
2364 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2365 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2366 pub enum MathDepth {
2367     /// Increment math-depth if math-style is compact.
2368     AutoAdd,
2369 
2370     /// Add the function's argument to math-depth.
2371     #[css(function)]
2372     Add(Integer),
2373 
2374     /// Set math-depth to the specified value.
2375     Absolute(Integer),
2376 }
2377 
2378 impl Parse for MathDepth {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<MathDepth, ParseError<'i>>2379     fn parse<'i, 't>(
2380         context: &ParserContext,
2381         input: &mut Parser<'i, 't>,
2382     ) -> Result<MathDepth, ParseError<'i>> {
2383         if input
2384             .try_parse(|i| i.expect_ident_matching("auto-add"))
2385             .is_ok()
2386         {
2387             return Ok(MathDepth::AutoAdd);
2388         }
2389         if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
2390             return Ok(MathDepth::Absolute(math_depth_value));
2391         }
2392         input.expect_function_matching("add")?;
2393         let math_depth_delta_value =
2394             input.parse_nested_block(|input| Integer::parse(context, input))?;
2395         Ok(MathDepth::Add(math_depth_delta_value))
2396     }
2397 }
2398 
2399 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2400 #[derive(
2401     Clone,
2402     Copy,
2403     Debug,
2404     PartialEq,
2405     SpecifiedValueInfo,
2406     ToComputedValue,
2407     ToCss,
2408     ToResolvedValue,
2409     ToShmem,
2410 )]
2411 /// Specifies the multiplier to be used to adjust font size
2412 /// due to changes in scriptlevel.
2413 ///
2414 /// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs
2415 pub struct MozScriptSizeMultiplier(pub f32);
2416 
2417 impl MozScriptSizeMultiplier {
2418     #[inline]
2419     /// Get default value of `-moz-script-size-multiplier`
get_initial_value() -> MozScriptSizeMultiplier2420     pub fn get_initial_value() -> MozScriptSizeMultiplier {
2421         MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
2422     }
2423 }
2424 
2425 impl Parse for MozScriptSizeMultiplier {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<MozScriptSizeMultiplier, ParseError<'i>>2426     fn parse<'i, 't>(
2427         _: &ParserContext,
2428         input: &mut Parser<'i, 't>,
2429     ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
2430         debug_assert!(
2431             false,
2432             "Should be set directly by presentation attributes only."
2433         );
2434         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2435     }
2436 }
2437 
2438 impl From<f32> for MozScriptSizeMultiplier {
from(v: f32) -> Self2439     fn from(v: f32) -> Self {
2440         MozScriptSizeMultiplier(v)
2441     }
2442 }
2443 
2444 impl From<MozScriptSizeMultiplier> for f32 {
from(v: MozScriptSizeMultiplier) -> f322445     fn from(v: MozScriptSizeMultiplier) -> f32 {
2446         v.0
2447     }
2448 }
2449