style/values/specified/
font.rs

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
7use crate::context::QuirksMode;
8use crate::parser::{Parse, ParserContext};
9use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
10use crate::values::computed::Percentage as ComputedPercentage;
11use crate::values::computed::{font as computed, Length, NonNegativeLength};
12use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
13use crate::values::generics::font::{
14    self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
15};
16use crate::values::generics::NonNegative;
17use crate::values::specified::length::{FontBaseSize, LineHeightBase, PX_PER_PT};
18use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
19use crate::values::specified::{
20    FontRelativeLength, NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber,
21    NonNegativePercentage, Number,
22};
23use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
24use crate::Atom;
25use cssparser::{Parser, Token};
26#[cfg(feature = "gecko")]
27use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
28use std::fmt::{self, Write};
29use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
30use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
31
32// FIXME(emilio): The system font code is copy-pasta, and should be cleaned up.
33macro_rules! system_font_methods {
34    ($ty:ident, $field:ident) => {
35        system_font_methods!($ty);
36
37        fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
38            debug_assert!(matches!(*self, $ty::System(..)));
39            #[cfg(feature = "gecko")]
40            {
41                _context.cached_system_font.as_ref().unwrap().$field.clone()
42            }
43            #[cfg(feature = "servo")]
44            {
45                unreachable!()
46            }
47        }
48    };
49
50    ($ty:ident) => {
51        /// Get a specified value that represents a system font.
52        pub fn system_font(f: SystemFont) -> Self {
53            $ty::System(f)
54        }
55
56        /// Retreive a SystemFont from the specified value.
57        pub fn get_system(&self) -> Option<SystemFont> {
58            if let $ty::System(s) = *self {
59                Some(s)
60            } else {
61                None
62            }
63        }
64    };
65}
66
67/// System fonts.
68#[repr(u8)]
69#[derive(
70    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
71)]
72#[allow(missing_docs)]
73#[cfg(feature = "gecko")]
74pub enum SystemFont {
75    /// https://drafts.csswg.org/css-fonts/#valdef-font-caption
76    Caption,
77    /// https://drafts.csswg.org/css-fonts/#valdef-font-icon
78    Icon,
79    /// https://drafts.csswg.org/css-fonts/#valdef-font-menu
80    Menu,
81    /// https://drafts.csswg.org/css-fonts/#valdef-font-message-box
82    MessageBox,
83    /// https://drafts.csswg.org/css-fonts/#valdef-font-small-caption
84    SmallCaption,
85    /// https://drafts.csswg.org/css-fonts/#valdef-font-status-bar
86    StatusBar,
87    /// Internal system font, used by the `<menupopup>`s on macOS.
88    #[parse(condition = "ParserContext::chrome_rules_enabled")]
89    MozPullDownMenu,
90    /// Internal system font, used for `<button>` elements.
91    #[parse(condition = "ParserContext::chrome_rules_enabled")]
92    MozButton,
93    /// Internal font, used by `<select>` elements.
94    #[parse(condition = "ParserContext::chrome_rules_enabled")]
95    MozList,
96    /// Internal font, used by `<input>` elements.
97    #[parse(condition = "ParserContext::chrome_rules_enabled")]
98    MozField,
99    #[css(skip)]
100    End, // Just for indexing purposes.
101}
102
103// We don't parse system fonts in servo, but in the interest of not
104// littering a lot of code with `if engine == "gecko"` conditionals,
105// we have a dummy system font module that does nothing
106
107#[derive(
108    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
109)]
110#[allow(missing_docs)]
111#[cfg(feature = "servo")]
112/// void enum for system font, can never exist
113pub enum SystemFont {}
114
115#[allow(missing_docs)]
116#[cfg(feature = "servo")]
117impl SystemFont {
118    pub fn parse(_: &mut Parser) -> Result<Self, ()> {
119        Err(())
120    }
121}
122
123const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
124const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
125
126/// The minimum font-weight value per:
127///
128/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
129pub const MIN_FONT_WEIGHT: f32 = 1.;
130
131/// The maximum font-weight value per:
132///
133/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
134pub const MAX_FONT_WEIGHT: f32 = 1000.;
135
136/// A specified font-weight value.
137///
138/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
139#[derive(
140    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
141)]
142pub enum FontWeight {
143    /// `<font-weight-absolute>`
144    Absolute(AbsoluteFontWeight),
145    /// Bolder variant
146    Bolder,
147    /// Lighter variant
148    Lighter,
149    /// System font variant.
150    #[css(skip)]
151    System(SystemFont),
152}
153
154impl FontWeight {
155    system_font_methods!(FontWeight, font_weight);
156
157    /// `normal`
158    #[inline]
159    pub fn normal() -> Self {
160        FontWeight::Absolute(AbsoluteFontWeight::Normal)
161    }
162
163    /// Get a specified FontWeight from a gecko keyword
164    pub fn from_gecko_keyword(kw: u32) -> Self {
165        debug_assert!(kw % 100 == 0);
166        debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
167        FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
168    }
169}
170
171impl ToComputedValue for FontWeight {
172    type ComputedValue = computed::FontWeight;
173
174    #[inline]
175    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
176        match *self {
177            FontWeight::Absolute(ref abs) => abs.compute(),
178            FontWeight::Bolder => context
179                .builder
180                .get_parent_font()
181                .clone_font_weight()
182                .bolder(),
183            FontWeight::Lighter => context
184                .builder
185                .get_parent_font()
186                .clone_font_weight()
187                .lighter(),
188            FontWeight::System(_) => self.compute_system(context),
189        }
190    }
191
192    #[inline]
193    fn from_computed_value(computed: &computed::FontWeight) -> Self {
194        FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
195            &computed.value(),
196        )))
197    }
198}
199
200/// An absolute font-weight value for a @font-face rule.
201///
202/// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values
203#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
204pub enum AbsoluteFontWeight {
205    /// A `<number>`, with the additional constraints specified in:
206    ///
207    ///   https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
208    Weight(Number),
209    /// Normal font weight. Same as 400.
210    Normal,
211    /// Bold font weight. Same as 700.
212    Bold,
213}
214
215impl AbsoluteFontWeight {
216    /// Returns the computed value for this absolute font weight.
217    pub fn compute(&self) -> computed::FontWeight {
218        match *self {
219            AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),
220            AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
221            AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
222        }
223    }
224}
225
226impl Parse for AbsoluteFontWeight {
227    fn parse<'i, 't>(
228        context: &ParserContext,
229        input: &mut Parser<'i, 't>,
230    ) -> Result<Self, ParseError<'i>> {
231        if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
232            // We could add another AllowedNumericType value, but it doesn't
233            // seem worth it just for a single property with such a weird range,
234            // so we do the clamping here manually.
235            if !number.was_calc()
236                && (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
237            {
238                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
239            }
240            return Ok(AbsoluteFontWeight::Weight(number));
241        }
242
243        Ok(try_match_ident_ignore_ascii_case! { input,
244            "normal" => AbsoluteFontWeight::Normal,
245            "bold" => AbsoluteFontWeight::Bold,
246        })
247    }
248}
249
250/// The specified value of the `font-style` property, without the system font
251/// crap.
252pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
253
254impl ToCss for SpecifiedFontStyle {
255    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
256    where
257        W: Write,
258    {
259        match *self {
260            generics::FontStyle::Italic => dest.write_str("italic"),
261            generics::FontStyle::Oblique(ref angle) => {
262                // Not angle.is_zero() because we don't want to serialize
263                // `oblique calc(0deg)` as `normal`.
264                if *angle == Angle::zero() {
265                    dest.write_str("normal")?;
266                } else {
267                    dest.write_str("oblique")?;
268                    if *angle != Self::default_angle() {
269                        dest.write_char(' ')?;
270                        angle.to_css(dest)?;
271                    }
272                }
273                Ok(())
274            },
275        }
276    }
277}
278
279impl Parse for SpecifiedFontStyle {
280    fn parse<'i, 't>(
281        context: &ParserContext,
282        input: &mut Parser<'i, 't>,
283    ) -> Result<Self, ParseError<'i>> {
284        Ok(try_match_ident_ignore_ascii_case! { input,
285            "normal" => generics::FontStyle::normal(),
286            "italic" => generics::FontStyle::Italic,
287            "oblique" => {
288                let angle = input.try_parse(|input| Self::parse_angle(context, input))
289                    .unwrap_or_else(|_| Self::default_angle());
290
291                generics::FontStyle::Oblique(angle)
292            },
293        })
294    }
295}
296
297impl ToComputedValue for SpecifiedFontStyle {
298    type ComputedValue = computed::FontStyle;
299
300    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
301        match *self {
302            Self::Italic => computed::FontStyle::ITALIC,
303            Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),
304        }
305    }
306
307    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
308        if *computed == computed::FontStyle::ITALIC {
309            return Self::Italic;
310        }
311        let degrees = computed.oblique_degrees();
312        generics::FontStyle::Oblique(Angle::from_degrees(degrees, /* was_calc = */ false))
313    }
314}
315
316/// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle:
317///
318///     Values less than -90deg or values greater than 90deg are
319///     invalid and are treated as parse errors.
320///
321/// The maximum angle value that `font-style: oblique` should compute to.
322pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
323
324/// The minimum angle value that `font-style: oblique` should compute to.
325pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
326
327impl SpecifiedFontStyle {
328    /// Gets a clamped angle in degrees from a specified Angle.
329    pub fn compute_angle_degrees(angle: &Angle) -> f32 {
330        angle
331            .degrees()
332            .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
333            .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
334    }
335
336    /// Parse a suitable angle for font-style: oblique.
337    pub fn parse_angle<'i, 't>(
338        context: &ParserContext,
339        input: &mut Parser<'i, 't>,
340    ) -> Result<Angle, ParseError<'i>> {
341        let angle = Angle::parse(context, input)?;
342        if angle.was_calc() {
343            return Ok(angle);
344        }
345
346        let degrees = angle.degrees();
347        if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES
348            || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
349        {
350            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
351        }
352        return Ok(angle);
353    }
354
355    /// The default angle for `font-style: oblique`.
356    pub fn default_angle() -> Angle {
357        Angle::from_degrees(
358            computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,
359            /* was_calc = */ false,
360        )
361    }
362}
363
364/// The specified value of the `font-style` property.
365#[derive(
366    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
367)]
368#[allow(missing_docs)]
369pub enum FontStyle {
370    Specified(SpecifiedFontStyle),
371    #[css(skip)]
372    System(SystemFont),
373}
374
375impl FontStyle {
376    /// Return the `normal` value.
377    #[inline]
378    pub fn normal() -> Self {
379        FontStyle::Specified(generics::FontStyle::normal())
380    }
381
382    system_font_methods!(FontStyle, font_style);
383}
384
385impl ToComputedValue for FontStyle {
386    type ComputedValue = computed::FontStyle;
387
388    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
389        match *self {
390            FontStyle::Specified(ref specified) => specified.to_computed_value(context),
391            FontStyle::System(..) => self.compute_system(context),
392        }
393    }
394
395    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
396        FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
397    }
398}
399
400/// A value for the `font-stretch` property.
401///
402/// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
403#[allow(missing_docs)]
404#[derive(
405    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
406)]
407pub enum FontStretch {
408    Stretch(NonNegativePercentage),
409    Keyword(FontStretchKeyword),
410    #[css(skip)]
411    System(SystemFont),
412}
413
414/// A keyword value for `font-stretch`.
415#[derive(
416    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
417)]
418#[allow(missing_docs)]
419pub enum FontStretchKeyword {
420    Normal,
421    Condensed,
422    UltraCondensed,
423    ExtraCondensed,
424    SemiCondensed,
425    SemiExpanded,
426    Expanded,
427    ExtraExpanded,
428    UltraExpanded,
429}
430
431impl FontStretchKeyword {
432    /// Turns the keyword into a computed value.
433    pub fn compute(&self) -> computed::FontStretch {
434        computed::FontStretch::from_keyword(*self)
435    }
436
437    /// Does the opposite operation to `compute`, in order to serialize keywords
438    /// if possible.
439    pub fn from_percentage(p: f32) -> Option<Self> {
440        computed::FontStretch::from_percentage(p).as_keyword()
441    }
442}
443
444impl FontStretch {
445    /// `normal`.
446    pub fn normal() -> Self {
447        FontStretch::Keyword(FontStretchKeyword::Normal)
448    }
449
450    system_font_methods!(FontStretch, font_stretch);
451}
452
453impl ToComputedValue for FontStretch {
454    type ComputedValue = computed::FontStretch;
455
456    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
457        match *self {
458            FontStretch::Stretch(ref percentage) => {
459                let percentage = percentage.to_computed_value(context).0;
460                computed::FontStretch::from_percentage(percentage.0)
461            },
462            FontStretch::Keyword(ref kw) => kw.compute(),
463            FontStretch::System(_) => self.compute_system(context),
464        }
465    }
466
467    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
468        FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
469            computed.to_percentage(),
470        )))
471    }
472}
473
474/// CSS font keywords
475#[derive(
476    Animate,
477    Clone,
478    ComputeSquaredDistance,
479    Copy,
480    Debug,
481    MallocSizeOf,
482    Parse,
483    PartialEq,
484    SpecifiedValueInfo,
485    ToAnimatedValue,
486    ToAnimatedZero,
487    ToComputedValue,
488    ToCss,
489    ToResolvedValue,
490    ToShmem,
491    Serialize,
492    Deserialize,
493)]
494#[allow(missing_docs)]
495#[repr(u8)]
496pub enum FontSizeKeyword {
497    #[css(keyword = "xx-small")]
498    XXSmall,
499    XSmall,
500    Small,
501    Medium,
502    Large,
503    XLarge,
504    #[css(keyword = "xx-large")]
505    XXLarge,
506    #[css(keyword = "xxx-large")]
507    XXXLarge,
508    /// Indicate whether to apply font-size: math is specified so that extra
509    /// scaling due to math-depth changes is applied during the cascade.
510    #[cfg(feature = "gecko")]
511    Math,
512    #[css(skip)]
513    None,
514}
515
516impl FontSizeKeyword {
517    /// Convert to an HTML <font size> value
518    #[inline]
519    pub fn html_size(self) -> u8 {
520        self as u8
521    }
522
523    /// Returns true if the font size is the math keyword
524    #[cfg(feature = "gecko")]
525    pub fn is_math(self) -> bool {
526        matches!(self, Self::Math)
527    }
528
529    /// Returns true if the font size is the math keyword
530    #[cfg(feature = "servo")]
531    pub fn is_math(self) -> bool {
532        false
533    }
534}
535
536impl Default for FontSizeKeyword {
537    fn default() -> Self {
538        FontSizeKeyword::Medium
539    }
540}
541
542#[derive(
543    Animate,
544    Clone,
545    ComputeSquaredDistance,
546    Copy,
547    Debug,
548    MallocSizeOf,
549    PartialEq,
550    ToAnimatedValue,
551    ToAnimatedZero,
552    ToComputedValue,
553    ToCss,
554    ToResolvedValue,
555    ToShmem,
556)]
557#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
558/// Additional information for keyword-derived font sizes.
559pub struct KeywordInfo {
560    /// The keyword used
561    pub kw: FontSizeKeyword,
562    /// A factor to be multiplied by the computed size of the keyword
563    #[css(skip)]
564    pub factor: f32,
565    /// An additional fixed offset to add to the kw * factor in the case of
566    /// `calc()`.
567    #[css(skip)]
568    pub offset: CSSPixelLength,
569}
570
571impl KeywordInfo {
572    /// KeywordInfo value for font-size: medium
573    pub fn medium() -> Self {
574        Self::new(FontSizeKeyword::Medium)
575    }
576
577    /// KeywordInfo value for font-size: none
578    pub fn none() -> Self {
579        Self::new(FontSizeKeyword::None)
580    }
581
582    fn new(kw: FontSizeKeyword) -> Self {
583        KeywordInfo {
584            kw,
585            factor: 1.,
586            offset: CSSPixelLength::new(0.),
587        }
588    }
589
590    /// Computes the final size for this font-size keyword, accounting for
591    /// text-zoom.
592    fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
593        debug_assert_ne!(self.kw, FontSizeKeyword::None);
594        #[cfg(feature = "gecko")]
595        debug_assert_ne!(self.kw, FontSizeKeyword::Math);
596        let base = context.maybe_zoom_text(self.kw.to_length(context).0);
597        let zoom_factor = context.style().effective_zoom.value();
598        CSSPixelLength::new(base.px() * self.factor * zoom_factor)
599            + context.maybe_zoom_text(self.offset)
600    }
601
602    /// Given a parent keyword info (self), apply an additional factor/offset to
603    /// it.
604    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
616impl SpecifiedValueInfo for KeywordInfo {
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
624pub 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))]
650pub 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
659impl FontFamily {
660    system_font_methods!(FontFamily, font_family);
661}
662
663impl ToComputedValue for FontFamily {
664    type ComputedValue = computed::FontFamily;
665
666    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
667        match *self {
668            FontFamily::Values(ref list) => computed::FontFamily {
669                families: list.clone(),
670                is_system_font: false,
671                is_initial: false,
672            },
673            FontFamily::System(_) => self.compute_system(context),
674        }
675    }
676
677    fn from_computed_value(other: &computed::FontFamily) -> Self {
678        FontFamily::Values(other.families.clone())
679    }
680}
681
682#[cfg(feature = "gecko")]
683impl MallocSizeOf for FontFamily {
684    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
685        match *self {
686            FontFamily::Values(ref v) => {
687                // Although the family list is refcounted, we always attribute
688                // its size to the specified value.
689                v.list.unconditional_size_of(ops)
690            },
691            FontFamily::System(_) => 0,
692        }
693    }
694}
695
696impl Parse for FontFamily {
697    /// <family-name>#
698    /// <family-name> = <string> | [ <ident>+ ]
699    /// TODO: <generic-family>
700    fn parse<'i, 't>(
701        context: &ParserContext,
702        input: &mut Parser<'i, 't>,
703    ) -> Result<FontFamily, ParseError<'i>> {
704        let values =
705            input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
706        Ok(FontFamily::Values(FontFamilyList {
707            list: crate::ArcSlice::from_iter(values.into_iter()),
708        }))
709    }
710}
711
712impl SpecifiedValueInfo for FontFamily {}
713
714/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other
715/// way around because we want the former to exclude generic family keywords.
716impl Parse for FamilyName {
717    fn parse<'i, 't>(
718        context: &ParserContext,
719        input: &mut Parser<'i, 't>,
720    ) -> Result<Self, ParseError<'i>> {
721        match SingleFontFamily::parse(context, input) {
722            Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
723            Ok(SingleFontFamily::Generic(_)) => {
724                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
725            },
726            Err(e) => Err(e),
727        }
728    }
729}
730
731/// A factor for one of the font-size-adjust metrics, which may be either a number
732/// or the `from-font` keyword.
733#[derive(
734    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
735)]
736pub enum FontSizeAdjustFactor {
737    /// An explicitly-specified number.
738    Number(NonNegativeNumber),
739    /// The from-font keyword: resolve the number from font metrics.
740    FromFont,
741}
742
743/// Specified value for font-size-adjust, intended to help
744/// preserve the readability of text when font fallback occurs.
745///
746/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
747pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
748
749impl Parse for FontSizeAdjust {
750    fn parse<'i, 't>(
751        context: &ParserContext,
752        input: &mut Parser<'i, 't>,
753    ) -> Result<Self, ParseError<'i>> {
754        let location = input.current_source_location();
755        // First check if we have an adjustment factor without a metrics-basis keyword.
756        if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
757            return Ok(Self::ExHeight(factor));
758        }
759
760        let ident = input.expect_ident()?;
761        let basis = match_ignore_ascii_case! { &ident,
762            "none" => return Ok(Self::None),
763            // Check for size adjustment basis keywords.
764            "ex-height" => Self::ExHeight,
765            "cap-height" => Self::CapHeight,
766            "ch-width" => Self::ChWidth,
767            "ic-width" => Self::IcWidth,
768            "ic-height" => Self::IcHeight,
769            // Unknown keyword.
770            _ => return Err(location.new_custom_error(
771                SelectorParseErrorKind::UnexpectedIdent(ident.clone())
772            )),
773        };
774
775        Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
776    }
777}
778
779/// This is the ratio applied for font-size: larger
780/// and smaller by both Firefox and Chrome
781const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
782
783/// The default font size.
784pub const FONT_MEDIUM_PX: f32 = 16.0;
785/// The default line height.
786pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
787
788impl FontSizeKeyword {
789    #[inline]
790    fn to_length(&self, cx: &Context) -> NonNegativeLength {
791        let font = cx.style().get_font();
792
793        #[cfg(feature = "servo")]
794        let family = &font.font_family.families;
795        #[cfg(feature = "gecko")]
796        let family = &font.mFont.family.families;
797
798        let generic = family
799            .single_generic()
800            .unwrap_or(computed::GenericFontFamily::None);
801
802        #[cfg(feature = "gecko")]
803        let base_size = unsafe {
804            Atom::with(font.mLanguage.mRawPtr, |language| {
805                cx.device().base_size_for_generic(language, generic)
806            })
807        };
808        #[cfg(feature = "servo")]
809        let base_size = cx.device().base_size_for_generic(generic);
810
811        self.to_length_without_context(cx.quirks_mode, base_size)
812    }
813
814    /// Resolve a keyword length without any context, with explicit arguments.
815    #[inline]
816    pub fn to_length_without_context(
817        &self,
818        quirks_mode: QuirksMode,
819        base_size: Length,
820    ) -> NonNegativeLength {
821        #[cfg(feature = "gecko")]
822        debug_assert_ne!(*self, FontSizeKeyword::Math);
823        // The tables in this function are originally from
824        // nsRuleNode::CalcFontPointSize in Gecko:
825        //
826        // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150
827        //
828        // Mapping from base size and HTML size to pixels
829        // The first index is (base_size - 9), the second is the
830        // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
831        // since HTML size 0 is the same as 1.
832        //
833        //  xxs   xs      s      m     l      xl     xxl   -
834        //  -     0/1     2      3     4      5      6     7
835        static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
836            [9, 9, 9, 9, 11, 14, 18, 27],
837            [9, 9, 9, 10, 12, 15, 20, 30],
838            [9, 9, 10, 11, 13, 17, 22, 33],
839            [9, 9, 10, 12, 14, 18, 24, 36],
840            [9, 10, 12, 13, 16, 20, 26, 39],
841            [9, 10, 12, 14, 17, 21, 28, 42],
842            [9, 10, 13, 15, 18, 23, 30, 45],
843            [9, 10, 13, 16, 18, 24, 32, 48],
844        ];
845
846        // This table gives us compatibility with WinNav4 for the default fonts only.
847        // In WinNav4, the default fonts were:
848        //
849        //     Times/12pt ==   Times/16px at 96ppi
850        //   Courier/10pt == Courier/13px at 96ppi
851        //
852        // xxs   xs     s      m      l     xl     xxl    -
853        // -     1      2      3      4     5      6      7
854        static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
855            [9, 9, 9, 9, 11, 14, 18, 28],
856            [9, 9, 9, 10, 12, 15, 20, 31],
857            [9, 9, 9, 11, 13, 17, 22, 34],
858            [9, 9, 10, 12, 14, 18, 24, 37],
859            [9, 9, 10, 13, 16, 20, 26, 40],
860            [9, 9, 11, 14, 17, 21, 28, 42],
861            [9, 10, 12, 15, 17, 23, 30, 45],
862            [9, 10, 13, 16, 18, 24, 32, 48],
863        ];
864
865        static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
866        let base_size_px = base_size.px().round() as i32;
867        let html_size = self.html_size() as usize;
868        NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
869            let mapping = if quirks_mode == QuirksMode::Quirks {
870                QUIRKS_FONT_SIZE_MAPPING
871            } else {
872                FONT_SIZE_MAPPING
873            };
874            Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
875        } else {
876            base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
877        })
878    }
879}
880
881impl FontSize {
882    /// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
883    pub fn from_html_size(size: u8) -> Self {
884        FontSize::Keyword(KeywordInfo::new(match size {
885            // If value is less than 1, let it be 1.
886            0 | 1 => FontSizeKeyword::XSmall,
887            2 => FontSizeKeyword::Small,
888            3 => FontSizeKeyword::Medium,
889            4 => FontSizeKeyword::Large,
890            5 => FontSizeKeyword::XLarge,
891            6 => FontSizeKeyword::XXLarge,
892            // If value is greater than 7, let it be 7.
893            _ => FontSizeKeyword::XXXLarge,
894        }))
895    }
896
897    /// Compute it against a given base font size
898    pub fn to_computed_value_against(
899        &self,
900        context: &Context,
901        base_size: FontBaseSize,
902        line_height_base: LineHeightBase,
903    ) -> computed::FontSize {
904        let compose_keyword = |factor| {
905            context
906                .style()
907                .get_parent_font()
908                .clone_font_size()
909                .keyword_info
910                .compose(factor)
911        };
912        let mut info = KeywordInfo::none();
913        let size = match *self {
914            FontSize::Length(LengthPercentage::Length(ref l)) => {
915                if let NoCalcLength::FontRelative(ref value) = *l {
916                    if let FontRelativeLength::Em(em) = *value {
917                        // If the parent font was keyword-derived, this is
918                        // too. Tack the em unit onto the factor
919                        info = compose_keyword(em);
920                    }
921                }
922                let result =
923                    l.to_computed_value_with_base_size(context, base_size, line_height_base);
924                if l.should_zoom_text() {
925                    context.maybe_zoom_text(result)
926                } else {
927                    result
928                }
929            },
930            FontSize::Length(LengthPercentage::Percentage(pc)) => {
931                // If the parent font was keyword-derived, this is too.
932                // Tack the % onto the factor
933                info = compose_keyword(pc.0);
934                (base_size.resolve(context).computed_size() * pc.0).normalized()
935            },
936            FontSize::Length(LengthPercentage::Calc(ref calc)) => {
937                let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
938                calc.resolve(base_size.resolve(context).computed_size())
939            },
940            FontSize::Keyword(i) => {
941                if i.kw.is_math() {
942                    // Scaling is done in recompute_math_font_size_if_needed().
943                    info = compose_keyword(1.);
944                    // i.kw will always be FontSizeKeyword::Math here. But writing it this
945                    // allows this code to compile for servo where the Math variant is cfg'd out.
946                    info.kw = i.kw;
947                    FontRelativeLength::Em(1.).to_computed_value(
948                        context,
949                        base_size,
950                        line_height_base,
951                    )
952                } else {
953                    // As a specified keyword, this is keyword derived
954                    info = i;
955                    i.to_computed_value(context).clamp_to_non_negative()
956                }
957            },
958            FontSize::Smaller => {
959                info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
960                FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
961                    context,
962                    base_size,
963                    line_height_base,
964                )
965            },
966            FontSize::Larger => {
967                info = compose_keyword(LARGER_FONT_SIZE_RATIO);
968                FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
969                    context,
970                    base_size,
971                    line_height_base,
972                )
973            },
974
975            FontSize::System(_) => {
976                #[cfg(feature = "servo")]
977                {
978                    unreachable!()
979                }
980                #[cfg(feature = "gecko")]
981                {
982                    context
983                        .cached_system_font
984                        .as_ref()
985                        .unwrap()
986                        .font_size
987                        .computed_size()
988                }
989            },
990        };
991        computed::FontSize {
992            computed_size: NonNegative(size),
993            used_size: NonNegative(size),
994            keyword_info: info,
995        }
996    }
997}
998
999impl ToComputedValue for FontSize {
1000    type ComputedValue = computed::FontSize;
1001
1002    #[inline]
1003    fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1004        self.to_computed_value_against(
1005            context,
1006            FontBaseSize::InheritedStyle,
1007            LineHeightBase::InheritedStyle,
1008        )
1009    }
1010
1011    #[inline]
1012    fn from_computed_value(computed: &computed::FontSize) -> Self {
1013        FontSize::Length(LengthPercentage::Length(
1014            ToComputedValue::from_computed_value(&computed.computed_size()),
1015        ))
1016    }
1017}
1018
1019impl FontSize {
1020    system_font_methods!(FontSize);
1021
1022    /// Get initial value for specified font size.
1023    #[inline]
1024    pub fn medium() -> Self {
1025        FontSize::Keyword(KeywordInfo::medium())
1026    }
1027
1028    /// Parses a font-size, with quirks.
1029    pub fn parse_quirky<'i, 't>(
1030        context: &ParserContext,
1031        input: &mut Parser<'i, 't>,
1032        allow_quirks: AllowQuirks,
1033    ) -> Result<FontSize, ParseError<'i>> {
1034        if let Ok(lp) = input
1035            .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1036        {
1037            return Ok(FontSize::Length(lp));
1038        }
1039
1040        if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1041            return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1042        }
1043
1044        try_match_ident_ignore_ascii_case! { input,
1045            "smaller" => Ok(FontSize::Smaller),
1046            "larger" => Ok(FontSize::Larger),
1047        }
1048    }
1049}
1050
1051impl Parse for FontSize {
1052    /// <length> | <percentage> | <absolute-size> | <relative-size>
1053    fn parse<'i, 't>(
1054        context: &ParserContext,
1055        input: &mut Parser<'i, 't>,
1056    ) -> Result<FontSize, ParseError<'i>> {
1057        FontSize::parse_quirky(context, input, AllowQuirks::No)
1058    }
1059}
1060
1061bitflags! {
1062    #[derive(Clone, Copy)]
1063    /// Flags of variant alternates in bit
1064    struct VariantAlternatesParsingFlags: u8 {
1065        /// None of variant alternates enabled
1066        const NORMAL = 0;
1067        /// Historical forms
1068        const HISTORICAL_FORMS = 0x01;
1069        /// Stylistic Alternates
1070        const STYLISTIC = 0x02;
1071        /// Stylistic Sets
1072        const STYLESET = 0x04;
1073        /// Character Variant
1074        const CHARACTER_VARIANT = 0x08;
1075        /// Swash glyphs
1076        const SWASH = 0x10;
1077        /// Ornaments glyphs
1078        const ORNAMENTS = 0x20;
1079        /// Annotation forms
1080        const ANNOTATION = 0x40;
1081    }
1082}
1083
1084#[derive(
1085    Clone,
1086    Debug,
1087    MallocSizeOf,
1088    PartialEq,
1089    SpecifiedValueInfo,
1090    ToCss,
1091    ToComputedValue,
1092    ToResolvedValue,
1093    ToShmem,
1094)]
1095#[repr(C, u8)]
1096/// Set of variant alternates
1097pub enum VariantAlternates {
1098    /// Enables display of stylistic alternates
1099    #[css(function)]
1100    Stylistic(CustomIdent),
1101    /// Enables display with stylistic sets
1102    #[css(comma, function)]
1103    Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1104    /// Enables display of specific character variants
1105    #[css(comma, function)]
1106    CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1107    /// Enables display of swash glyphs
1108    #[css(function)]
1109    Swash(CustomIdent),
1110    /// Enables replacement of default glyphs with ornaments
1111    #[css(function)]
1112    Ornaments(CustomIdent),
1113    /// Enables display of alternate annotation forms
1114    #[css(function)]
1115    Annotation(CustomIdent),
1116    /// Enables display of historical forms
1117    HistoricalForms,
1118}
1119
1120#[derive(
1121    Clone,
1122    Debug,
1123    Default,
1124    MallocSizeOf,
1125    PartialEq,
1126    SpecifiedValueInfo,
1127    ToComputedValue,
1128    ToCss,
1129    ToResolvedValue,
1130    ToShmem,
1131)]
1132#[repr(transparent)]
1133/// List of Variant Alternates
1134pub struct FontVariantAlternates(
1135    #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1136);
1137
1138impl FontVariantAlternates {
1139    /// Returns the length of all variant alternates.
1140    pub fn len(&self) -> usize {
1141        self.0.iter().fold(0, |acc, alternate| match *alternate {
1142            VariantAlternates::Swash(_)
1143            | VariantAlternates::Stylistic(_)
1144            | VariantAlternates::Ornaments(_)
1145            | VariantAlternates::Annotation(_) => acc + 1,
1146            VariantAlternates::Styleset(ref slice)
1147            | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1148            _ => acc,
1149        })
1150    }
1151}
1152
1153impl FontVariantAlternates {
1154    #[inline]
1155    /// Get initial specified value with VariantAlternatesList
1156    pub fn get_initial_specified_value() -> Self {
1157        Default::default()
1158    }
1159}
1160
1161impl Parse for FontVariantAlternates {
1162    /// normal |
1163    ///  [ stylistic(<feature-value-name>)           ||
1164    ///    historical-forms                          ||
1165    ///    styleset(<feature-value-name> #)          ||
1166    ///    character-variant(<feature-value-name> #) ||
1167    ///    swash(<feature-value-name>)               ||
1168    ///    ornaments(<feature-value-name>)           ||
1169    ///    annotation(<feature-value-name>) ]
1170    fn parse<'i, 't>(
1171        _: &ParserContext,
1172        input: &mut Parser<'i, 't>,
1173    ) -> Result<FontVariantAlternates, ParseError<'i>> {
1174        if input
1175            .try_parse(|input| input.expect_ident_matching("normal"))
1176            .is_ok()
1177        {
1178            return Ok(Default::default());
1179        }
1180
1181        let mut stylistic = None;
1182        let mut historical = None;
1183        let mut styleset = None;
1184        let mut character_variant = None;
1185        let mut swash = None;
1186        let mut ornaments = None;
1187        let mut annotation = None;
1188
1189        // Parse values for the various alternate types in any order.
1190        let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1191        macro_rules! check_if_parsed(
1192            ($input:expr, $flag:path) => (
1193                if parsed_alternates.contains($flag) {
1194                    return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1195                }
1196                parsed_alternates |= $flag;
1197            )
1198        );
1199        while let Ok(_) = input.try_parse(|input| match *input.next()? {
1200            Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1201                check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1202                historical = Some(VariantAlternates::HistoricalForms);
1203                Ok(())
1204            },
1205            Token::Function(ref name) => {
1206                let name = name.clone();
1207                input.parse_nested_block(|i| {
1208                    match_ignore_ascii_case! { &name,
1209                        "swash" => {
1210                            check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1211                            let ident = CustomIdent::parse(i, &[])?;
1212                            swash = Some(VariantAlternates::Swash(ident));
1213                            Ok(())
1214                        },
1215                        "stylistic" => {
1216                            check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1217                            let ident = CustomIdent::parse(i, &[])?;
1218                            stylistic = Some(VariantAlternates::Stylistic(ident));
1219                            Ok(())
1220                        },
1221                        "ornaments" => {
1222                            check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1223                            let ident = CustomIdent::parse(i, &[])?;
1224                            ornaments = Some(VariantAlternates::Ornaments(ident));
1225                            Ok(())
1226                        },
1227                        "annotation" => {
1228                            check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1229                            let ident = CustomIdent::parse(i, &[])?;
1230                            annotation = Some(VariantAlternates::Annotation(ident));
1231                            Ok(())
1232                        },
1233                        "styleset" => {
1234                            check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1235                            let idents = i.parse_comma_separated(|i| {
1236                                CustomIdent::parse(i, &[])
1237                            })?;
1238                            styleset = Some(VariantAlternates::Styleset(idents.into()));
1239                            Ok(())
1240                        },
1241                        "character-variant" => {
1242                            check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1243                            let idents = i.parse_comma_separated(|i| {
1244                                CustomIdent::parse(i, &[])
1245                            })?;
1246                            character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1247                            Ok(())
1248                        },
1249                        _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1250                    }
1251                })
1252            },
1253            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1254        }) {}
1255
1256        if parsed_alternates.is_empty() {
1257            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1258        }
1259
1260        // Collect the parsed values in canonical order, so that we'll serialize correctly.
1261        let mut alternates = Vec::new();
1262        macro_rules! push_if_some(
1263            ($value:expr) => (
1264                if let Some(v) = $value {
1265                    alternates.push(v);
1266                }
1267            )
1268        );
1269        push_if_some!(stylistic);
1270        push_if_some!(historical);
1271        push_if_some!(styleset);
1272        push_if_some!(character_variant);
1273        push_if_some!(swash);
1274        push_if_some!(ornaments);
1275        push_if_some!(annotation);
1276
1277        Ok(FontVariantAlternates(alternates.into()))
1278    }
1279}
1280
1281#[derive(
1282    Clone,
1283    Copy,
1284    Debug,
1285    Eq,
1286    MallocSizeOf,
1287    PartialEq,
1288    Parse,
1289    SpecifiedValueInfo,
1290    ToComputedValue,
1291    ToCss,
1292    ToResolvedValue,
1293    ToShmem,
1294)]
1295#[css(bitflags(
1296    single = "normal",
1297    mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1298    validate_mixed = "Self::validate_mixed_flags",
1299))]
1300#[repr(C)]
1301/// Variants for east asian variant
1302pub struct FontVariantEastAsian(u16);
1303bitflags! {
1304    impl FontVariantEastAsian: u16 {
1305        /// None of the features
1306        const NORMAL = 0;
1307        /// Enables rendering of JIS78 forms (OpenType feature: jp78)
1308        const JIS78  = 1 << 0;
1309        /// Enables rendering of JIS83 forms (OpenType feature: jp83).
1310        const JIS83 = 1 << 1;
1311        /// Enables rendering of JIS90 forms (OpenType feature: jp90).
1312        const JIS90 = 1 << 2;
1313        /// Enables rendering of JIS2004 forms (OpenType feature: jp04).
1314        const JIS04 = 1 << 3;
1315        /// Enables rendering of simplified forms (OpenType feature: smpl).
1316        const SIMPLIFIED = 1 << 4;
1317        /// Enables rendering of traditional forms (OpenType feature: trad).
1318        const TRADITIONAL = 1 << 5;
1319
1320        /// These values are exclusive with each other.
1321        const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1322
1323        /// Enables rendering of full-width variants (OpenType feature: fwid).
1324        const FULL_WIDTH = 1 << 6;
1325        /// Enables rendering of proportionally-spaced variants (OpenType feature: pwid).
1326        const PROPORTIONAL_WIDTH = 1 << 7;
1327        /// Enables display of ruby variant glyphs (OpenType feature: ruby).
1328        const RUBY = 1 << 8;
1329    }
1330}
1331
1332impl FontVariantEastAsian {
1333    /// The number of variants.
1334    pub const COUNT: usize = 9;
1335
1336    fn validate_mixed_flags(&self) -> bool {
1337        if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1338            // full-width and proportional-width are exclusive with each other.
1339            return false;
1340        }
1341        let jis = self.intersection(Self::JIS_GROUP);
1342        if !jis.is_empty() && !jis.bits().is_power_of_two() {
1343            return false;
1344        }
1345        true
1346    }
1347}
1348
1349#[derive(
1350    Clone,
1351    Copy,
1352    Debug,
1353    Eq,
1354    MallocSizeOf,
1355    PartialEq,
1356    Parse,
1357    SpecifiedValueInfo,
1358    ToComputedValue,
1359    ToCss,
1360    ToResolvedValue,
1361    ToShmem,
1362)]
1363#[css(bitflags(
1364    single = "normal,none",
1365    mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1366    validate_mixed = "Self::validate_mixed_flags",
1367))]
1368#[repr(C)]
1369/// Variants of ligatures
1370pub struct FontVariantLigatures(u16);
1371bitflags! {
1372    impl FontVariantLigatures: u16 {
1373        /// Specifies that common default features are enabled
1374        const NORMAL = 0;
1375        /// Specifies that no features are enabled;
1376        const NONE = 1;
1377        /// Enables display of common ligatures
1378        const COMMON_LIGATURES  = 1 << 1;
1379        /// Disables display of common ligatures
1380        const NO_COMMON_LIGATURES  = 1 << 2;
1381        /// Enables display of discretionary ligatures
1382        const DISCRETIONARY_LIGATURES = 1 << 3;
1383        /// Disables display of discretionary ligatures
1384        const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1385        /// Enables display of historical ligatures
1386        const HISTORICAL_LIGATURES = 1 << 5;
1387        /// Disables display of historical ligatures
1388        const NO_HISTORICAL_LIGATURES = 1 << 6;
1389        /// Enables display of contextual alternates
1390        const CONTEXTUAL = 1 << 7;
1391        /// Disables display of contextual alternates
1392        const NO_CONTEXTUAL = 1 << 8;
1393    }
1394}
1395
1396impl FontVariantLigatures {
1397    /// The number of variants.
1398    pub const COUNT: usize = 9;
1399
1400    fn validate_mixed_flags(&self) -> bool {
1401        // Mixing a value and its disabling value is forbidden.
1402        if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1403            || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1404            || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1405            || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1406        {
1407            return false;
1408        }
1409        true
1410    }
1411}
1412
1413/// Variants of numeric values
1414#[derive(
1415    Clone,
1416    Copy,
1417    Debug,
1418    Eq,
1419    MallocSizeOf,
1420    PartialEq,
1421    Parse,
1422    SpecifiedValueInfo,
1423    ToComputedValue,
1424    ToCss,
1425    ToResolvedValue,
1426    ToShmem,
1427)]
1428#[css(bitflags(
1429    single = "normal",
1430    mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1431    validate_mixed = "Self::validate_mixed_flags",
1432))]
1433#[repr(C)]
1434pub struct FontVariantNumeric(u8);
1435bitflags! {
1436    impl FontVariantNumeric : u8 {
1437        /// Specifies that common default features are enabled
1438        const NORMAL = 0;
1439        /// Enables display of lining numerals.
1440        const LINING_NUMS = 1 << 0;
1441        /// Enables display of old-style numerals.
1442        const OLDSTYLE_NUMS = 1 << 1;
1443        /// Enables display of proportional numerals.
1444        const PROPORTIONAL_NUMS = 1 << 2;
1445        /// Enables display of tabular numerals.
1446        const TABULAR_NUMS = 1 << 3;
1447        /// Enables display of lining diagonal fractions.
1448        const DIAGONAL_FRACTIONS = 1 << 4;
1449        /// Enables display of lining stacked fractions.
1450        const STACKED_FRACTIONS = 1 << 5;
1451        /// Enables display of slashed zeros.
1452        const SLASHED_ZERO = 1 << 6;
1453        /// Enables display of letter forms used with ordinal numbers.
1454        const ORDINAL = 1 << 7;
1455    }
1456}
1457
1458impl FontVariantNumeric {
1459    /// The number of variants.
1460    pub const COUNT: usize = 8;
1461
1462    /// normal |
1463    ///  [ <numeric-figure-values>   ||
1464    ///    <numeric-spacing-values>  ||
1465    ///    <numeric-fraction-values> ||
1466    ///    ordinal                   ||
1467    ///    slashed-zero ]
1468    /// <numeric-figure-values>   = [ lining-nums | oldstyle-nums ]
1469    /// <numeric-spacing-values>  = [ proportional-nums | tabular-nums ]
1470    /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
1471    fn validate_mixed_flags(&self) -> bool {
1472        if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1473            || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1474            || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1475        {
1476            return false;
1477        }
1478        true
1479    }
1480}
1481
1482/// This property provides low-level control over OpenType or TrueType font features.
1483pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1484
1485/// For font-language-override, use the same representation as the computed value.
1486pub use crate::values::computed::font::FontLanguageOverride;
1487
1488impl Parse for FontLanguageOverride {
1489    /// normal | <string>
1490    fn parse<'i, 't>(
1491        _: &ParserContext,
1492        input: &mut Parser<'i, 't>,
1493    ) -> Result<FontLanguageOverride, ParseError<'i>> {
1494        if input
1495            .try_parse(|input| input.expect_ident_matching("normal"))
1496            .is_ok()
1497        {
1498            return Ok(FontLanguageOverride::normal());
1499        }
1500
1501        let string = input.expect_string()?;
1502
1503        // The OpenType spec requires tags to be 1 to 4 ASCII characters:
1504        // https://learn.microsoft.com/en-gb/typography/opentype/spec/otff#data-types
1505        if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1506            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1507        }
1508
1509        let mut bytes = [b' '; 4];
1510        for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1511            *byte = *str_byte;
1512        }
1513
1514        Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1515    }
1516}
1517
1518/// A value for any of the font-synthesis-{weight,small-caps,position} properties.
1519#[repr(u8)]
1520#[derive(
1521    Clone,
1522    Copy,
1523    Debug,
1524    Eq,
1525    MallocSizeOf,
1526    Parse,
1527    PartialEq,
1528    SpecifiedValueInfo,
1529    ToComputedValue,
1530    ToCss,
1531    ToResolvedValue,
1532    ToShmem,
1533)]
1534pub enum FontSynthesis {
1535    /// This attribute may be synthesized if not supported by a face.
1536    Auto,
1537    /// Do not attempt to synthesis this style attribute.
1538    None,
1539}
1540
1541/// A value for the font-synthesis-style property.
1542#[repr(u8)]
1543#[derive(
1544    Clone,
1545    Copy,
1546    Debug,
1547    Eq,
1548    MallocSizeOf,
1549    Parse,
1550    PartialEq,
1551    SpecifiedValueInfo,
1552    ToComputedValue,
1553    ToCss,
1554    ToResolvedValue,
1555    ToShmem,
1556)]
1557pub enum FontSynthesisStyle {
1558    /// This attribute may be synthesized if not supported by a face.
1559    Auto,
1560    /// Do not attempt to synthesis this style attribute.
1561    None,
1562    /// Allow synthesis for oblique, but not for italic.
1563    ObliqueOnly,
1564}
1565
1566#[derive(
1567    Clone,
1568    Debug,
1569    Eq,
1570    MallocSizeOf,
1571    PartialEq,
1572    SpecifiedValueInfo,
1573    ToComputedValue,
1574    ToResolvedValue,
1575    ToShmem,
1576)]
1577#[repr(C)]
1578/// Allows authors to choose a palette from those supported by a color font
1579/// (and potentially @font-palette-values overrides).
1580pub struct FontPalette(Atom);
1581
1582#[allow(missing_docs)]
1583impl FontPalette {
1584    pub fn normal() -> Self {
1585        Self(atom!("normal"))
1586    }
1587    pub fn light() -> Self {
1588        Self(atom!("light"))
1589    }
1590    pub fn dark() -> Self {
1591        Self(atom!("dark"))
1592    }
1593}
1594
1595impl Parse for FontPalette {
1596    /// normal | light | dark | dashed-ident
1597    fn parse<'i, 't>(
1598        _context: &ParserContext,
1599        input: &mut Parser<'i, 't>,
1600    ) -> Result<FontPalette, ParseError<'i>> {
1601        let location = input.current_source_location();
1602        let ident = input.expect_ident()?;
1603        match_ignore_ascii_case! { &ident,
1604            "normal" => Ok(Self::normal()),
1605            "light" => Ok(Self::light()),
1606            "dark" => Ok(Self::dark()),
1607            _ => if ident.starts_with("--") {
1608                Ok(Self(Atom::from(ident.as_ref())))
1609            } else {
1610                Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1611            },
1612        }
1613    }
1614}
1615
1616impl ToCss for FontPalette {
1617    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1618    where
1619        W: Write,
1620    {
1621        serialize_atom_identifier(&self.0, dest)
1622    }
1623}
1624
1625/// This property provides low-level control over OpenType or TrueType font
1626/// variations.
1627pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1628
1629fn parse_one_feature_value<'i, 't>(
1630    context: &ParserContext,
1631    input: &mut Parser<'i, 't>,
1632) -> Result<Integer, ParseError<'i>> {
1633    if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1634        return Ok(integer);
1635    }
1636
1637    try_match_ident_ignore_ascii_case! { input,
1638        "on" => Ok(Integer::new(1)),
1639        "off" => Ok(Integer::new(0)),
1640    }
1641}
1642
1643impl Parse for FeatureTagValue<Integer> {
1644    /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
1645    fn parse<'i, 't>(
1646        context: &ParserContext,
1647        input: &mut Parser<'i, 't>,
1648    ) -> Result<Self, ParseError<'i>> {
1649        let tag = FontTag::parse(context, input)?;
1650        let value = input
1651            .try_parse(|i| parse_one_feature_value(context, i))
1652            .unwrap_or_else(|_| Integer::new(1));
1653
1654        Ok(Self { tag, value })
1655    }
1656}
1657
1658impl Parse for VariationValue<Number> {
1659    /// This is the `<string> <number>` part of the font-variation-settings
1660    /// syntax.
1661    fn parse<'i, 't>(
1662        context: &ParserContext,
1663        input: &mut Parser<'i, 't>,
1664    ) -> Result<Self, ParseError<'i>> {
1665        let tag = FontTag::parse(context, input)?;
1666        let value = Number::parse(context, input)?;
1667        Ok(Self { tag, value })
1668    }
1669}
1670
1671/// A metrics override value for a @font-face descriptor
1672///
1673/// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc
1674#[derive(
1675    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1676)]
1677pub enum MetricsOverride {
1678    /// A non-negative `<percentage>` of the computed font size
1679    Override(NonNegativePercentage),
1680    /// Normal metrics from the font.
1681    Normal,
1682}
1683
1684impl MetricsOverride {
1685    #[inline]
1686    /// Get default value with `normal`
1687    pub fn normal() -> MetricsOverride {
1688        MetricsOverride::Normal
1689    }
1690
1691    /// The ToComputedValue implementation, used for @font-face descriptors.
1692    ///
1693    /// Valid override percentages must be non-negative; we return -1.0 to indicate
1694    /// the absence of an override (i.e. 'normal').
1695    #[inline]
1696    pub fn compute(&self) -> ComputedPercentage {
1697        match *self {
1698            MetricsOverride::Normal => ComputedPercentage(-1.0),
1699            MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1700        }
1701    }
1702}
1703
1704#[derive(
1705    Clone,
1706    Copy,
1707    Debug,
1708    MallocSizeOf,
1709    Parse,
1710    PartialEq,
1711    SpecifiedValueInfo,
1712    ToComputedValue,
1713    ToCss,
1714    ToResolvedValue,
1715    ToShmem,
1716)]
1717#[repr(u8)]
1718/// How to do font-size scaling.
1719pub enum XTextScale {
1720    /// Both min-font-size and text zoom are enabled.
1721    All,
1722    /// Text-only zoom is enabled, but min-font-size is not honored.
1723    ZoomOnly,
1724    /// Neither of them is enabled.
1725    None,
1726}
1727
1728impl XTextScale {
1729    /// Returns whether text zoom is enabled.
1730    #[inline]
1731    pub fn text_zoom_enabled(self) -> bool {
1732        self != Self::None
1733    }
1734}
1735
1736#[derive(
1737    Clone,
1738    Debug,
1739    MallocSizeOf,
1740    PartialEq,
1741    SpecifiedValueInfo,
1742    ToComputedValue,
1743    ToCss,
1744    ToResolvedValue,
1745    ToShmem,
1746)]
1747#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1748/// Internal property that reflects the lang attribute
1749pub struct XLang(#[css(skip)] pub Atom);
1750
1751impl XLang {
1752    #[inline]
1753    /// Get default value for `-x-lang`
1754    pub fn get_initial_value() -> XLang {
1755        XLang(atom!(""))
1756    }
1757}
1758
1759impl Parse for XLang {
1760    fn parse<'i, 't>(
1761        _: &ParserContext,
1762        input: &mut Parser<'i, 't>,
1763    ) -> Result<XLang, ParseError<'i>> {
1764        debug_assert!(
1765            false,
1766            "Should be set directly by presentation attributes only."
1767        );
1768        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1769    }
1770}
1771
1772#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1773#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1774/// Specifies the minimum font size allowed due to changes in scriptlevel.
1775/// Ref: https://wiki.mozilla.org/MathML:mstyle
1776pub struct MozScriptMinSize(pub NoCalcLength);
1777
1778impl MozScriptMinSize {
1779    #[inline]
1780    /// Calculate initial value of -moz-script-min-size.
1781    pub fn get_initial_value() -> Length {
1782        Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1783    }
1784}
1785
1786impl Parse for MozScriptMinSize {
1787    fn parse<'i, 't>(
1788        _: &ParserContext,
1789        input: &mut Parser<'i, 't>,
1790    ) -> Result<MozScriptMinSize, ParseError<'i>> {
1791        debug_assert!(
1792            false,
1793            "Should be set directly by presentation attributes only."
1794        );
1795        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1796    }
1797}
1798
1799/// A value for the `math-depth` property.
1800/// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property
1801#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1802#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1803pub enum MathDepth {
1804    /// Increment math-depth if math-style is compact.
1805    AutoAdd,
1806
1807    /// Add the function's argument to math-depth.
1808    #[css(function)]
1809    Add(Integer),
1810
1811    /// Set math-depth to the specified value.
1812    Absolute(Integer),
1813}
1814
1815impl Parse for MathDepth {
1816    fn parse<'i, 't>(
1817        context: &ParserContext,
1818        input: &mut Parser<'i, 't>,
1819    ) -> Result<MathDepth, ParseError<'i>> {
1820        if input
1821            .try_parse(|i| i.expect_ident_matching("auto-add"))
1822            .is_ok()
1823        {
1824            return Ok(MathDepth::AutoAdd);
1825        }
1826        if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1827            return Ok(MathDepth::Absolute(math_depth_value));
1828        }
1829        input.expect_function_matching("add")?;
1830        let math_depth_delta_value =
1831            input.parse_nested_block(|input| Integer::parse(context, input))?;
1832        Ok(MathDepth::Add(math_depth_delta_value))
1833    }
1834}
1835
1836#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1837#[derive(
1838    Clone,
1839    Copy,
1840    Debug,
1841    PartialEq,
1842    SpecifiedValueInfo,
1843    ToComputedValue,
1844    ToCss,
1845    ToResolvedValue,
1846    ToShmem,
1847)]
1848/// Specifies the multiplier to be used to adjust font size
1849/// due to changes in scriptlevel.
1850///
1851/// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs
1852pub struct MozScriptSizeMultiplier(pub f32);
1853
1854impl MozScriptSizeMultiplier {
1855    #[inline]
1856    /// Get default value of `-moz-script-size-multiplier`
1857    pub fn get_initial_value() -> MozScriptSizeMultiplier {
1858        MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1859    }
1860}
1861
1862impl Parse for MozScriptSizeMultiplier {
1863    fn parse<'i, 't>(
1864        _: &ParserContext,
1865        input: &mut Parser<'i, 't>,
1866    ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1867        debug_assert!(
1868            false,
1869            "Should be set directly by presentation attributes only."
1870        );
1871        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1872    }
1873}
1874
1875impl From<f32> for MozScriptSizeMultiplier {
1876    fn from(v: f32) -> Self {
1877        MozScriptSizeMultiplier(v)
1878    }
1879}
1880
1881impl From<MozScriptSizeMultiplier> for f32 {
1882    fn from(v: MozScriptSizeMultiplier) -> f32 {
1883        v.0
1884    }
1885}
1886
1887/// A specified value for the `line-height` property.
1888pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1889
1890impl ToComputedValue for LineHeight {
1891    type ComputedValue = computed::LineHeight;
1892
1893    #[inline]
1894    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1895        match *self {
1896            GenericLineHeight::Normal => GenericLineHeight::Normal,
1897            #[cfg(feature = "gecko")]
1898            GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1899            GenericLineHeight::Number(number) => {
1900                GenericLineHeight::Number(number.to_computed_value(context))
1901            },
1902            GenericLineHeight::Length(ref non_negative_lp) => {
1903                let result = match non_negative_lp.0 {
1904                    LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1905                        context.maybe_zoom_text(abs.to_computed_value(context))
1906                    },
1907                    LengthPercentage::Length(ref length) => {
1908                        // line-height units specifically resolve against parent's
1909                        // font and line-height properties, while the rest of font
1910                        // relative units still resolve against the element's own
1911                        // properties.
1912                        length.to_computed_value_with_base_size(
1913                            context,
1914                            FontBaseSize::CurrentStyle,
1915                            LineHeightBase::InheritedStyle,
1916                        )
1917                    },
1918                    LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1919                        .to_computed_value(
1920                            context,
1921                            FontBaseSize::CurrentStyle,
1922                            LineHeightBase::InheritedStyle,
1923                        ),
1924                    LengthPercentage::Calc(ref calc) => {
1925                        let computed_calc = calc.to_computed_value_zoomed(
1926                            context,
1927                            FontBaseSize::CurrentStyle,
1928                            LineHeightBase::InheritedStyle,
1929                        );
1930                        let base = context.style().get_font().clone_font_size().computed_size();
1931                        computed_calc.resolve(base)
1932                    },
1933                };
1934                GenericLineHeight::Length(result.into())
1935            },
1936        }
1937    }
1938
1939    #[inline]
1940    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1941        match *computed {
1942            GenericLineHeight::Normal => GenericLineHeight::Normal,
1943            #[cfg(feature = "gecko")]
1944            GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1945            GenericLineHeight::Number(ref number) => {
1946                GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1947            },
1948            GenericLineHeight::Length(ref length) => {
1949                GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1950            },
1951        }
1952    }
1953}
1954
1955/// Flags for the query_font_metrics() function.
1956#[repr(C)]
1957pub struct QueryFontMetricsFlags(u8);
1958
1959bitflags! {
1960    impl QueryFontMetricsFlags: u8 {
1961        /// Should we use the user font set?
1962        const USE_USER_FONT_SET = 1 << 0;
1963        /// Does the caller need the `ch` unit (width of the ZERO glyph)?
1964        const NEEDS_CH = 1 << 1;
1965        /// Does the caller need the `ic` unit (width of the WATER ideograph)?
1966        const NEEDS_IC = 1 << 2;
1967        /// Does the caller need math scales to be retrieved?
1968        const NEEDS_MATH_SCALES = 1 << 3;
1969    }
1970}