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) + context.maybe_zoom_text(self.offset)
599    }
600
601    /// Given a parent keyword info (self), apply an additional factor/offset to
602    /// it.
603    fn compose(self, factor: f32) -> Self {
604        if self.kw == FontSizeKeyword::None {
605            return self;
606        }
607        KeywordInfo {
608            kw: self.kw,
609            factor: self.factor * factor,
610            offset: self.offset * factor,
611        }
612    }
613}
614
615impl SpecifiedValueInfo for KeywordInfo {
616    fn collect_completion_keywords(f: KeywordsCollectFn) {
617        <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
618    }
619}
620
621#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
622/// A specified font-size value
623pub enum FontSize {
624    /// A length; e.g. 10px.
625    Length(LengthPercentage),
626    /// A keyword value, along with a ratio and absolute offset.
627    /// The ratio in any specified keyword value
628    /// will be 1 (with offset 0), but we cascade keywordness even
629    /// after font-relative (percent and em) values
630    /// have been applied, which is where the ratio
631    /// comes in. The offset comes in if we cascaded a calc value,
632    /// where the font-relative portion (em and percentage) will
633    /// go into the ratio, and the remaining units all computed together
634    /// will go into the offset.
635    /// See bug 1355707.
636    Keyword(KeywordInfo),
637    /// font-size: smaller
638    Smaller,
639    /// font-size: larger
640    Larger,
641    /// Derived from a specified system font.
642    #[css(skip)]
643    System(SystemFont),
644}
645
646/// Specifies a prioritized list of font family names or generic family names.
647#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
648#[cfg_attr(feature = "servo", derive(Hash))]
649pub enum FontFamily {
650    /// List of `font-family`
651    #[css(comma)]
652    Values(#[css(iterable)] FontFamilyList),
653    /// System font
654    #[css(skip)]
655    System(SystemFont),
656}
657
658impl FontFamily {
659    system_font_methods!(FontFamily, font_family);
660}
661
662impl ToComputedValue for FontFamily {
663    type ComputedValue = computed::FontFamily;
664
665    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
666        match *self {
667            FontFamily::Values(ref list) => computed::FontFamily {
668                families: list.clone(),
669                is_system_font: false,
670                is_initial: false,
671            },
672            FontFamily::System(_) => self.compute_system(context),
673        }
674    }
675
676    fn from_computed_value(other: &computed::FontFamily) -> Self {
677        FontFamily::Values(other.families.clone())
678    }
679}
680
681#[cfg(feature = "gecko")]
682impl MallocSizeOf for FontFamily {
683    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
684        match *self {
685            FontFamily::Values(ref v) => {
686                // Although the family list is refcounted, we always attribute
687                // its size to the specified value.
688                v.list.unconditional_size_of(ops)
689            },
690            FontFamily::System(_) => 0,
691        }
692    }
693}
694
695impl Parse for FontFamily {
696    /// <family-name>#
697    /// <family-name> = <string> | [ <ident>+ ]
698    /// TODO: <generic-family>
699    fn parse<'i, 't>(
700        context: &ParserContext,
701        input: &mut Parser<'i, 't>,
702    ) -> Result<FontFamily, ParseError<'i>> {
703        let values =
704            input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
705        Ok(FontFamily::Values(FontFamilyList {
706            list: crate::ArcSlice::from_iter(values.into_iter()),
707        }))
708    }
709}
710
711impl SpecifiedValueInfo for FontFamily {}
712
713/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other
714/// way around because we want the former to exclude generic family keywords.
715impl Parse for FamilyName {
716    fn parse<'i, 't>(
717        context: &ParserContext,
718        input: &mut Parser<'i, 't>,
719    ) -> Result<Self, ParseError<'i>> {
720        match SingleFontFamily::parse(context, input) {
721            Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
722            Ok(SingleFontFamily::Generic(_)) => {
723                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
724            },
725            Err(e) => Err(e),
726        }
727    }
728}
729
730/// A factor for one of the font-size-adjust metrics, which may be either a number
731/// or the `from-font` keyword.
732#[derive(
733    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
734)]
735pub enum FontSizeAdjustFactor {
736    /// An explicitly-specified number.
737    Number(NonNegativeNumber),
738    /// The from-font keyword: resolve the number from font metrics.
739    FromFont,
740}
741
742/// Specified value for font-size-adjust, intended to help
743/// preserve the readability of text when font fallback occurs.
744///
745/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
746pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
747
748impl Parse for FontSizeAdjust {
749    fn parse<'i, 't>(
750        context: &ParserContext,
751        input: &mut Parser<'i, 't>,
752    ) -> Result<Self, ParseError<'i>> {
753        let location = input.current_source_location();
754        // First check if we have an adjustment factor without a metrics-basis keyword.
755        if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
756            return Ok(Self::ExHeight(factor));
757        }
758
759        let ident = input.expect_ident()?;
760        let basis = match_ignore_ascii_case! { &ident,
761            "none" => return Ok(Self::None),
762            // Check for size adjustment basis keywords.
763            "ex-height" => Self::ExHeight,
764            "cap-height" => Self::CapHeight,
765            "ch-width" => Self::ChWidth,
766            "ic-width" => Self::IcWidth,
767            "ic-height" => Self::IcHeight,
768            // Unknown keyword.
769            _ => return Err(location.new_custom_error(
770                SelectorParseErrorKind::UnexpectedIdent(ident.clone())
771            )),
772        };
773
774        Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
775    }
776}
777
778/// This is the ratio applied for font-size: larger
779/// and smaller by both Firefox and Chrome
780const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
781
782/// The default font size.
783pub const FONT_MEDIUM_PX: f32 = 16.0;
784/// The default line height.
785pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
786
787impl FontSizeKeyword {
788    #[inline]
789    fn to_length(&self, cx: &Context) -> NonNegativeLength {
790        let font = cx.style().get_font();
791
792        #[cfg(feature = "servo")]
793        let family = &font.font_family.families;
794        #[cfg(feature = "gecko")]
795        let family = &font.mFont.family.families;
796
797        let generic = family
798            .single_generic()
799            .unwrap_or(computed::GenericFontFamily::None);
800
801        #[cfg(feature = "gecko")]
802        let base_size = unsafe {
803            Atom::with(font.mLanguage.mRawPtr, |language| {
804                cx.device().base_size_for_generic(language, generic)
805            })
806        };
807        #[cfg(feature = "servo")]
808        let base_size = cx.device().base_size_for_generic(generic);
809
810        self.to_length_without_context(cx.quirks_mode, base_size)
811    }
812
813    /// Resolve a keyword length without any context, with explicit arguments.
814    #[inline]
815    pub fn to_length_without_context(
816        &self,
817        quirks_mode: QuirksMode,
818        base_size: Length,
819    ) -> NonNegativeLength {
820        #[cfg(feature = "gecko")]
821        debug_assert_ne!(*self, FontSizeKeyword::Math);
822        // The tables in this function are originally from
823        // nsRuleNode::CalcFontPointSize in Gecko:
824        //
825        // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150
826        //
827        // Mapping from base size and HTML size to pixels
828        // The first index is (base_size - 9), the second is the
829        // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
830        // since HTML size 0 is the same as 1.
831        //
832        //  xxs   xs      s      m     l      xl     xxl   -
833        //  -     0/1     2      3     4      5      6     7
834        static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
835            [9, 9, 9, 9, 11, 14, 18, 27],
836            [9, 9, 9, 10, 12, 15, 20, 30],
837            [9, 9, 10, 11, 13, 17, 22, 33],
838            [9, 9, 10, 12, 14, 18, 24, 36],
839            [9, 10, 12, 13, 16, 20, 26, 39],
840            [9, 10, 12, 14, 17, 21, 28, 42],
841            [9, 10, 13, 15, 18, 23, 30, 45],
842            [9, 10, 13, 16, 18, 24, 32, 48],
843        ];
844
845        // This table gives us compatibility with WinNav4 for the default fonts only.
846        // In WinNav4, the default fonts were:
847        //
848        //     Times/12pt ==   Times/16px at 96ppi
849        //   Courier/10pt == Courier/13px at 96ppi
850        //
851        // xxs   xs     s      m      l     xl     xxl    -
852        // -     1      2      3      4     5      6      7
853        static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
854            [9, 9, 9, 9, 11, 14, 18, 28],
855            [9, 9, 9, 10, 12, 15, 20, 31],
856            [9, 9, 9, 11, 13, 17, 22, 34],
857            [9, 9, 10, 12, 14, 18, 24, 37],
858            [9, 9, 10, 13, 16, 20, 26, 40],
859            [9, 9, 11, 14, 17, 21, 28, 42],
860            [9, 10, 12, 15, 17, 23, 30, 45],
861            [9, 10, 13, 16, 18, 24, 32, 48],
862        ];
863
864        static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
865        let base_size_px = base_size.px().round() as i32;
866        let html_size = self.html_size() as usize;
867        NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
868            let mapping = if quirks_mode == QuirksMode::Quirks {
869                QUIRKS_FONT_SIZE_MAPPING
870            } else {
871                FONT_SIZE_MAPPING
872            };
873            Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
874        } else {
875            base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
876        })
877    }
878}
879
880impl FontSize {
881    /// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
882    pub fn from_html_size(size: u8) -> Self {
883        FontSize::Keyword(KeywordInfo::new(match size {
884            // If value is less than 1, let it be 1.
885            0 | 1 => FontSizeKeyword::XSmall,
886            2 => FontSizeKeyword::Small,
887            3 => FontSizeKeyword::Medium,
888            4 => FontSizeKeyword::Large,
889            5 => FontSizeKeyword::XLarge,
890            6 => FontSizeKeyword::XXLarge,
891            // If value is greater than 7, let it be 7.
892            _ => FontSizeKeyword::XXXLarge,
893        }))
894    }
895
896    /// Compute it against a given base font size
897    pub fn to_computed_value_against(
898        &self,
899        context: &Context,
900        base_size: FontBaseSize,
901        line_height_base: LineHeightBase,
902    ) -> computed::FontSize {
903        let compose_keyword = |factor| {
904            context
905                .style()
906                .get_parent_font()
907                .clone_font_size()
908                .keyword_info
909                .compose(factor)
910        };
911        let mut info = KeywordInfo::none();
912        let size = match *self {
913            FontSize::Length(LengthPercentage::Length(ref l)) => {
914                if let NoCalcLength::FontRelative(ref value) = *l {
915                    if let FontRelativeLength::Em(em) = *value {
916                        // If the parent font was keyword-derived, this is
917                        // too. Tack the em unit onto the factor
918                        info = compose_keyword(em);
919                    }
920                }
921                let result =
922                    l.to_computed_value_with_base_size(context, base_size, line_height_base);
923                if l.should_zoom_text() {
924                    context.maybe_zoom_text(result)
925                } else {
926                    result
927                }
928            },
929            FontSize::Length(LengthPercentage::Percentage(pc)) => {
930                // If the parent font was keyword-derived, this is too.
931                // Tack the % onto the factor
932                info = compose_keyword(pc.0);
933                (base_size.resolve(context).computed_size() * pc.0).normalized()
934            },
935            FontSize::Length(LengthPercentage::Calc(ref calc)) => {
936                let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
937                calc.resolve(base_size.resolve(context).computed_size())
938            },
939            FontSize::Keyword(i) => {
940                if i.kw.is_math() {
941                    // Scaling is done in recompute_math_font_size_if_needed().
942                    info = compose_keyword(1.);
943                    // i.kw will always be FontSizeKeyword::Math here. But writing it this
944                    // allows this code to compile for servo where the Math variant is cfg'd out.
945                    info.kw = i.kw;
946                    FontRelativeLength::Em(1.).to_computed_value(
947                        context,
948                        base_size,
949                        line_height_base,
950                    )
951                } else {
952                    // As a specified keyword, this is keyword derived
953                    info = i;
954                    i.to_computed_value(context).clamp_to_non_negative()
955                }
956            },
957            FontSize::Smaller => {
958                info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
959                FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
960                    context,
961                    base_size,
962                    line_height_base,
963                )
964            },
965            FontSize::Larger => {
966                info = compose_keyword(LARGER_FONT_SIZE_RATIO);
967                FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
968                    context,
969                    base_size,
970                    line_height_base,
971                )
972            },
973
974            FontSize::System(_) => {
975                #[cfg(feature = "servo")]
976                {
977                    unreachable!()
978                }
979                #[cfg(feature = "gecko")]
980                {
981                    context
982                        .cached_system_font
983                        .as_ref()
984                        .unwrap()
985                        .font_size
986                        .computed_size()
987                }
988            },
989        };
990        computed::FontSize {
991            computed_size: NonNegative(size),
992            used_size: NonNegative(size),
993            keyword_info: info,
994        }
995    }
996}
997
998impl ToComputedValue for FontSize {
999    type ComputedValue = computed::FontSize;
1000
1001    #[inline]
1002    fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1003        self.to_computed_value_against(
1004            context,
1005            FontBaseSize::InheritedStyle,
1006            LineHeightBase::InheritedStyle,
1007        )
1008    }
1009
1010    #[inline]
1011    fn from_computed_value(computed: &computed::FontSize) -> Self {
1012        FontSize::Length(LengthPercentage::Length(
1013            ToComputedValue::from_computed_value(&computed.computed_size()),
1014        ))
1015    }
1016}
1017
1018impl FontSize {
1019    system_font_methods!(FontSize);
1020
1021    /// Get initial value for specified font size.
1022    #[inline]
1023    pub fn medium() -> Self {
1024        FontSize::Keyword(KeywordInfo::medium())
1025    }
1026
1027    /// Parses a font-size, with quirks.
1028    pub fn parse_quirky<'i, 't>(
1029        context: &ParserContext,
1030        input: &mut Parser<'i, 't>,
1031        allow_quirks: AllowQuirks,
1032    ) -> Result<FontSize, ParseError<'i>> {
1033        if let Ok(lp) = input
1034            .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1035        {
1036            return Ok(FontSize::Length(lp));
1037        }
1038
1039        if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1040            return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1041        }
1042
1043        try_match_ident_ignore_ascii_case! { input,
1044            "smaller" => Ok(FontSize::Smaller),
1045            "larger" => Ok(FontSize::Larger),
1046        }
1047    }
1048}
1049
1050impl Parse for FontSize {
1051    /// <length> | <percentage> | <absolute-size> | <relative-size>
1052    fn parse<'i, 't>(
1053        context: &ParserContext,
1054        input: &mut Parser<'i, 't>,
1055    ) -> Result<FontSize, ParseError<'i>> {
1056        FontSize::parse_quirky(context, input, AllowQuirks::No)
1057    }
1058}
1059
1060bitflags! {
1061    #[derive(Clone, Copy)]
1062    /// Flags of variant alternates in bit
1063    struct VariantAlternatesParsingFlags: u8 {
1064        /// None of variant alternates enabled
1065        const NORMAL = 0;
1066        /// Historical forms
1067        const HISTORICAL_FORMS = 0x01;
1068        /// Stylistic Alternates
1069        const STYLISTIC = 0x02;
1070        /// Stylistic Sets
1071        const STYLESET = 0x04;
1072        /// Character Variant
1073        const CHARACTER_VARIANT = 0x08;
1074        /// Swash glyphs
1075        const SWASH = 0x10;
1076        /// Ornaments glyphs
1077        const ORNAMENTS = 0x20;
1078        /// Annotation forms
1079        const ANNOTATION = 0x40;
1080    }
1081}
1082
1083#[derive(
1084    Clone,
1085    Debug,
1086    MallocSizeOf,
1087    PartialEq,
1088    SpecifiedValueInfo,
1089    ToCss,
1090    ToComputedValue,
1091    ToResolvedValue,
1092    ToShmem,
1093)]
1094#[repr(C, u8)]
1095/// Set of variant alternates
1096pub enum VariantAlternates {
1097    /// Enables display of stylistic alternates
1098    #[css(function)]
1099    Stylistic(CustomIdent),
1100    /// Enables display with stylistic sets
1101    #[css(comma, function)]
1102    Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1103    /// Enables display of specific character variants
1104    #[css(comma, function)]
1105    CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1106    /// Enables display of swash glyphs
1107    #[css(function)]
1108    Swash(CustomIdent),
1109    /// Enables replacement of default glyphs with ornaments
1110    #[css(function)]
1111    Ornaments(CustomIdent),
1112    /// Enables display of alternate annotation forms
1113    #[css(function)]
1114    Annotation(CustomIdent),
1115    /// Enables display of historical forms
1116    HistoricalForms,
1117}
1118
1119#[derive(
1120    Clone,
1121    Debug,
1122    Default,
1123    MallocSizeOf,
1124    PartialEq,
1125    SpecifiedValueInfo,
1126    ToComputedValue,
1127    ToCss,
1128    ToResolvedValue,
1129    ToShmem,
1130)]
1131#[repr(transparent)]
1132/// List of Variant Alternates
1133pub struct FontVariantAlternates(
1134    #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1135);
1136
1137impl FontVariantAlternates {
1138    /// Returns the length of all variant alternates.
1139    pub fn len(&self) -> usize {
1140        self.0.iter().fold(0, |acc, alternate| match *alternate {
1141            VariantAlternates::Swash(_) |
1142            VariantAlternates::Stylistic(_) |
1143            VariantAlternates::Ornaments(_) |
1144            VariantAlternates::Annotation(_) => acc + 1,
1145            VariantAlternates::Styleset(ref slice) |
1146            VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1147            _ => acc,
1148        })
1149    }
1150}
1151
1152impl FontVariantAlternates {
1153    #[inline]
1154    /// Get initial specified value with VariantAlternatesList
1155    pub fn get_initial_specified_value() -> Self {
1156        Default::default()
1157    }
1158}
1159
1160impl Parse for FontVariantAlternates {
1161    /// normal |
1162    ///  [ stylistic(<feature-value-name>)           ||
1163    ///    historical-forms                          ||
1164    ///    styleset(<feature-value-name> #)          ||
1165    ///    character-variant(<feature-value-name> #) ||
1166    ///    swash(<feature-value-name>)               ||
1167    ///    ornaments(<feature-value-name>)           ||
1168    ///    annotation(<feature-value-name>) ]
1169    fn parse<'i, 't>(
1170        _: &ParserContext,
1171        input: &mut Parser<'i, 't>,
1172    ) -> Result<FontVariantAlternates, ParseError<'i>> {
1173        if input
1174            .try_parse(|input| input.expect_ident_matching("normal"))
1175            .is_ok()
1176        {
1177            return Ok(Default::default());
1178        }
1179
1180        let mut stylistic = None;
1181        let mut historical = None;
1182        let mut styleset = None;
1183        let mut character_variant = None;
1184        let mut swash = None;
1185        let mut ornaments = None;
1186        let mut annotation = None;
1187
1188        // Parse values for the various alternate types in any order.
1189        let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1190        macro_rules! check_if_parsed(
1191            ($input:expr, $flag:path) => (
1192                if parsed_alternates.contains($flag) {
1193                    return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1194                }
1195                parsed_alternates |= $flag;
1196            )
1197        );
1198        while let Ok(_) = input.try_parse(|input| match *input.next()? {
1199            Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1200                check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1201                historical = Some(VariantAlternates::HistoricalForms);
1202                Ok(())
1203            },
1204            Token::Function(ref name) => {
1205                let name = name.clone();
1206                input.parse_nested_block(|i| {
1207                    match_ignore_ascii_case! { &name,
1208                        "swash" => {
1209                            check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1210                            let ident = CustomIdent::parse(i, &[])?;
1211                            swash = Some(VariantAlternates::Swash(ident));
1212                            Ok(())
1213                        },
1214                        "stylistic" => {
1215                            check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1216                            let ident = CustomIdent::parse(i, &[])?;
1217                            stylistic = Some(VariantAlternates::Stylistic(ident));
1218                            Ok(())
1219                        },
1220                        "ornaments" => {
1221                            check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1222                            let ident = CustomIdent::parse(i, &[])?;
1223                            ornaments = Some(VariantAlternates::Ornaments(ident));
1224                            Ok(())
1225                        },
1226                        "annotation" => {
1227                            check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1228                            let ident = CustomIdent::parse(i, &[])?;
1229                            annotation = Some(VariantAlternates::Annotation(ident));
1230                            Ok(())
1231                        },
1232                        "styleset" => {
1233                            check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1234                            let idents = i.parse_comma_separated(|i| {
1235                                CustomIdent::parse(i, &[])
1236                            })?;
1237                            styleset = Some(VariantAlternates::Styleset(idents.into()));
1238                            Ok(())
1239                        },
1240                        "character-variant" => {
1241                            check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1242                            let idents = i.parse_comma_separated(|i| {
1243                                CustomIdent::parse(i, &[])
1244                            })?;
1245                            character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1246                            Ok(())
1247                        },
1248                        _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1249                    }
1250                })
1251            },
1252            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1253        }) {}
1254
1255        if parsed_alternates.is_empty() {
1256            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1257        }
1258
1259        // Collect the parsed values in canonical order, so that we'll serialize correctly.
1260        let mut alternates = Vec::new();
1261        macro_rules! push_if_some(
1262            ($value:expr) => (
1263                if let Some(v) = $value {
1264                    alternates.push(v);
1265                }
1266            )
1267        );
1268        push_if_some!(stylistic);
1269        push_if_some!(historical);
1270        push_if_some!(styleset);
1271        push_if_some!(character_variant);
1272        push_if_some!(swash);
1273        push_if_some!(ornaments);
1274        push_if_some!(annotation);
1275
1276        Ok(FontVariantAlternates(alternates.into()))
1277    }
1278}
1279
1280#[derive(
1281    Clone,
1282    Copy,
1283    Debug,
1284    Eq,
1285    MallocSizeOf,
1286    PartialEq,
1287    Parse,
1288    SpecifiedValueInfo,
1289    ToComputedValue,
1290    ToCss,
1291    ToResolvedValue,
1292    ToShmem,
1293)]
1294#[css(bitflags(
1295    single = "normal",
1296    mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1297    validate_mixed = "Self::validate_mixed_flags",
1298))]
1299#[repr(C)]
1300/// Variants for east asian variant
1301pub struct FontVariantEastAsian(u16);
1302bitflags! {
1303    impl FontVariantEastAsian: u16 {
1304        /// None of the features
1305        const NORMAL = 0;
1306        /// Enables rendering of JIS78 forms (OpenType feature: jp78)
1307        const JIS78  = 1 << 0;
1308        /// Enables rendering of JIS83 forms (OpenType feature: jp83).
1309        const JIS83 = 1 << 1;
1310        /// Enables rendering of JIS90 forms (OpenType feature: jp90).
1311        const JIS90 = 1 << 2;
1312        /// Enables rendering of JIS2004 forms (OpenType feature: jp04).
1313        const JIS04 = 1 << 3;
1314        /// Enables rendering of simplified forms (OpenType feature: smpl).
1315        const SIMPLIFIED = 1 << 4;
1316        /// Enables rendering of traditional forms (OpenType feature: trad).
1317        const TRADITIONAL = 1 << 5;
1318
1319        /// These values are exclusive with each other.
1320        const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1321
1322        /// Enables rendering of full-width variants (OpenType feature: fwid).
1323        const FULL_WIDTH = 1 << 6;
1324        /// Enables rendering of proportionally-spaced variants (OpenType feature: pwid).
1325        const PROPORTIONAL_WIDTH = 1 << 7;
1326        /// Enables display of ruby variant glyphs (OpenType feature: ruby).
1327        const RUBY = 1 << 8;
1328    }
1329}
1330
1331impl FontVariantEastAsian {
1332    /// The number of variants.
1333    pub const COUNT: usize = 9;
1334
1335    fn validate_mixed_flags(&self) -> bool {
1336        if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1337            // full-width and proportional-width are exclusive with each other.
1338            return false;
1339        }
1340        let jis = self.intersection(Self::JIS_GROUP);
1341        if !jis.is_empty() && !jis.bits().is_power_of_two() {
1342            return false;
1343        }
1344        true
1345    }
1346}
1347
1348#[derive(
1349    Clone,
1350    Copy,
1351    Debug,
1352    Eq,
1353    MallocSizeOf,
1354    PartialEq,
1355    Parse,
1356    SpecifiedValueInfo,
1357    ToComputedValue,
1358    ToCss,
1359    ToResolvedValue,
1360    ToShmem,
1361)]
1362#[css(bitflags(
1363    single = "normal,none",
1364    mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1365    validate_mixed = "Self::validate_mixed_flags",
1366))]
1367#[repr(C)]
1368/// Variants of ligatures
1369pub struct FontVariantLigatures(u16);
1370bitflags! {
1371    impl FontVariantLigatures: u16 {
1372        /// Specifies that common default features are enabled
1373        const NORMAL = 0;
1374        /// Specifies that no features are enabled;
1375        const NONE = 1;
1376        /// Enables display of common ligatures
1377        const COMMON_LIGATURES  = 1 << 1;
1378        /// Disables display of common ligatures
1379        const NO_COMMON_LIGATURES  = 1 << 2;
1380        /// Enables display of discretionary ligatures
1381        const DISCRETIONARY_LIGATURES = 1 << 3;
1382        /// Disables display of discretionary ligatures
1383        const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1384        /// Enables display of historical ligatures
1385        const HISTORICAL_LIGATURES = 1 << 5;
1386        /// Disables display of historical ligatures
1387        const NO_HISTORICAL_LIGATURES = 1 << 6;
1388        /// Enables display of contextual alternates
1389        const CONTEXTUAL = 1 << 7;
1390        /// Disables display of contextual alternates
1391        const NO_CONTEXTUAL = 1 << 8;
1392    }
1393}
1394
1395impl FontVariantLigatures {
1396    /// The number of variants.
1397    pub const COUNT: usize = 9;
1398
1399    fn validate_mixed_flags(&self) -> bool {
1400        // Mixing a value and its disabling value is forbidden.
1401        if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES) ||
1402            self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES) ||
1403            self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES) ||
1404            self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1405        {
1406            return false;
1407        }
1408        true
1409    }
1410}
1411
1412/// Variants of numeric values
1413#[derive(
1414    Clone,
1415    Copy,
1416    Debug,
1417    Eq,
1418    MallocSizeOf,
1419    PartialEq,
1420    Parse,
1421    SpecifiedValueInfo,
1422    ToComputedValue,
1423    ToCss,
1424    ToResolvedValue,
1425    ToShmem,
1426)]
1427#[css(bitflags(
1428    single = "normal",
1429    mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1430    validate_mixed = "Self::validate_mixed_flags",
1431))]
1432#[repr(C)]
1433pub struct FontVariantNumeric(u8);
1434bitflags! {
1435    impl FontVariantNumeric : u8 {
1436        /// Specifies that common default features are enabled
1437        const NORMAL = 0;
1438        /// Enables display of lining numerals.
1439        const LINING_NUMS = 1 << 0;
1440        /// Enables display of old-style numerals.
1441        const OLDSTYLE_NUMS = 1 << 1;
1442        /// Enables display of proportional numerals.
1443        const PROPORTIONAL_NUMS = 1 << 2;
1444        /// Enables display of tabular numerals.
1445        const TABULAR_NUMS = 1 << 3;
1446        /// Enables display of lining diagonal fractions.
1447        const DIAGONAL_FRACTIONS = 1 << 4;
1448        /// Enables display of lining stacked fractions.
1449        const STACKED_FRACTIONS = 1 << 5;
1450        /// Enables display of slashed zeros.
1451        const SLASHED_ZERO = 1 << 6;
1452        /// Enables display of letter forms used with ordinal numbers.
1453        const ORDINAL = 1 << 7;
1454    }
1455}
1456
1457impl FontVariantNumeric {
1458    /// The number of variants.
1459    pub const COUNT: usize = 8;
1460
1461    /// normal |
1462    ///  [ <numeric-figure-values>   ||
1463    ///    <numeric-spacing-values>  ||
1464    ///    <numeric-fraction-values> ||
1465    ///    ordinal                   ||
1466    ///    slashed-zero ]
1467    /// <numeric-figure-values>   = [ lining-nums | oldstyle-nums ]
1468    /// <numeric-spacing-values>  = [ proportional-nums | tabular-nums ]
1469    /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
1470    fn validate_mixed_flags(&self) -> bool {
1471        if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS) ||
1472            self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS) ||
1473            self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1474        {
1475            return false;
1476        }
1477        true
1478    }
1479}
1480
1481/// This property provides low-level control over OpenType or TrueType font features.
1482pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1483
1484/// For font-language-override, use the same representation as the computed value.
1485pub use crate::values::computed::font::FontLanguageOverride;
1486
1487impl Parse for FontLanguageOverride {
1488    /// normal | <string>
1489    fn parse<'i, 't>(
1490        _: &ParserContext,
1491        input: &mut Parser<'i, 't>,
1492    ) -> Result<FontLanguageOverride, ParseError<'i>> {
1493        if input
1494            .try_parse(|input| input.expect_ident_matching("normal"))
1495            .is_ok()
1496        {
1497            return Ok(FontLanguageOverride::normal());
1498        }
1499
1500        let string = input.expect_string()?;
1501
1502        // The OpenType spec requires tags to be 1 to 4 ASCII characters:
1503        // https://learn.microsoft.com/en-gb/typography/opentype/spec/otff#data-types
1504        if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1505            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1506        }
1507
1508        let mut bytes = [b' '; 4];
1509        for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1510            *byte = *str_byte;
1511        }
1512
1513        Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1514    }
1515}
1516
1517/// A value for any of the font-synthesis-{weight,small-caps,position} properties.
1518#[repr(u8)]
1519#[derive(
1520    Clone,
1521    Copy,
1522    Debug,
1523    Eq,
1524    MallocSizeOf,
1525    Parse,
1526    PartialEq,
1527    SpecifiedValueInfo,
1528    ToComputedValue,
1529    ToCss,
1530    ToResolvedValue,
1531    ToShmem,
1532)]
1533pub enum FontSynthesis {
1534    /// This attribute may be synthesized if not supported by a face.
1535    Auto,
1536    /// Do not attempt to synthesis this style attribute.
1537    None,
1538}
1539
1540/// A value for the font-synthesis-style property.
1541#[repr(u8)]
1542#[derive(
1543    Clone,
1544    Copy,
1545    Debug,
1546    Eq,
1547    MallocSizeOf,
1548    Parse,
1549    PartialEq,
1550    SpecifiedValueInfo,
1551    ToComputedValue,
1552    ToCss,
1553    ToResolvedValue,
1554    ToShmem,
1555)]
1556pub enum FontSynthesisStyle {
1557    /// This attribute may be synthesized if not supported by a face.
1558    Auto,
1559    /// Do not attempt to synthesis this style attribute.
1560    None,
1561    /// Allow synthesis for oblique, but not for italic.
1562    ObliqueOnly,
1563}
1564
1565#[derive(
1566    Clone,
1567    Debug,
1568    Eq,
1569    MallocSizeOf,
1570    PartialEq,
1571    SpecifiedValueInfo,
1572    ToComputedValue,
1573    ToResolvedValue,
1574    ToShmem,
1575)]
1576#[repr(C)]
1577/// Allows authors to choose a palette from those supported by a color font
1578/// (and potentially @font-palette-values overrides).
1579pub struct FontPalette(Atom);
1580
1581#[allow(missing_docs)]
1582impl FontPalette {
1583    pub fn normal() -> Self {
1584        Self(atom!("normal"))
1585    }
1586    pub fn light() -> Self {
1587        Self(atom!("light"))
1588    }
1589    pub fn dark() -> Self {
1590        Self(atom!("dark"))
1591    }
1592}
1593
1594impl Parse for FontPalette {
1595    /// normal | light | dark | dashed-ident
1596    fn parse<'i, 't>(
1597        _context: &ParserContext,
1598        input: &mut Parser<'i, 't>,
1599    ) -> Result<FontPalette, ParseError<'i>> {
1600        let location = input.current_source_location();
1601        let ident = input.expect_ident()?;
1602        match_ignore_ascii_case! { &ident,
1603            "normal" => Ok(Self::normal()),
1604            "light" => Ok(Self::light()),
1605            "dark" => Ok(Self::dark()),
1606            _ => if ident.starts_with("--") {
1607                Ok(Self(Atom::from(ident.as_ref())))
1608            } else {
1609                Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1610            },
1611        }
1612    }
1613}
1614
1615impl ToCss for FontPalette {
1616    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1617    where
1618        W: Write,
1619    {
1620        serialize_atom_identifier(&self.0, dest)
1621    }
1622}
1623
1624/// This property provides low-level control over OpenType or TrueType font
1625/// variations.
1626pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1627
1628fn parse_one_feature_value<'i, 't>(
1629    context: &ParserContext,
1630    input: &mut Parser<'i, 't>,
1631) -> Result<Integer, ParseError<'i>> {
1632    if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1633        return Ok(integer);
1634    }
1635
1636    try_match_ident_ignore_ascii_case! { input,
1637        "on" => Ok(Integer::new(1)),
1638        "off" => Ok(Integer::new(0)),
1639    }
1640}
1641
1642impl Parse for FeatureTagValue<Integer> {
1643    /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
1644    fn parse<'i, 't>(
1645        context: &ParserContext,
1646        input: &mut Parser<'i, 't>,
1647    ) -> Result<Self, ParseError<'i>> {
1648        let tag = FontTag::parse(context, input)?;
1649        let value = input
1650            .try_parse(|i| parse_one_feature_value(context, i))
1651            .unwrap_or_else(|_| Integer::new(1));
1652
1653        Ok(Self { tag, value })
1654    }
1655}
1656
1657impl Parse for VariationValue<Number> {
1658    /// This is the `<string> <number>` part of the font-variation-settings
1659    /// syntax.
1660    fn parse<'i, 't>(
1661        context: &ParserContext,
1662        input: &mut Parser<'i, 't>,
1663    ) -> Result<Self, ParseError<'i>> {
1664        let tag = FontTag::parse(context, input)?;
1665        let value = Number::parse(context, input)?;
1666        Ok(Self { tag, value })
1667    }
1668}
1669
1670/// A metrics override value for a @font-face descriptor
1671///
1672/// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc
1673#[derive(
1674    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1675)]
1676pub enum MetricsOverride {
1677    /// A non-negative `<percentage>` of the computed font size
1678    Override(NonNegativePercentage),
1679    /// Normal metrics from the font.
1680    Normal,
1681}
1682
1683impl MetricsOverride {
1684    #[inline]
1685    /// Get default value with `normal`
1686    pub fn normal() -> MetricsOverride {
1687        MetricsOverride::Normal
1688    }
1689
1690    /// The ToComputedValue implementation, used for @font-face descriptors.
1691    ///
1692    /// Valid override percentages must be non-negative; we return -1.0 to indicate
1693    /// the absence of an override (i.e. 'normal').
1694    #[inline]
1695    pub fn compute(&self) -> ComputedPercentage {
1696        match *self {
1697            MetricsOverride::Normal => ComputedPercentage(-1.0),
1698            MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1699        }
1700    }
1701}
1702
1703#[derive(
1704    Clone,
1705    Copy,
1706    Debug,
1707    MallocSizeOf,
1708    Parse,
1709    PartialEq,
1710    SpecifiedValueInfo,
1711    ToComputedValue,
1712    ToCss,
1713    ToResolvedValue,
1714    ToShmem,
1715)]
1716#[repr(u8)]
1717/// How to do font-size scaling.
1718pub enum XTextScale {
1719    /// Both min-font-size and text zoom are enabled.
1720    All,
1721    /// Text-only zoom is enabled, but min-font-size is not honored.
1722    ZoomOnly,
1723    /// Neither of them is enabled.
1724    None,
1725}
1726
1727impl XTextScale {
1728    /// Returns whether text zoom is enabled.
1729    #[inline]
1730    pub fn text_zoom_enabled(self) -> bool {
1731        self != Self::None
1732    }
1733}
1734
1735#[derive(
1736    Clone,
1737    Debug,
1738    MallocSizeOf,
1739    PartialEq,
1740    SpecifiedValueInfo,
1741    ToComputedValue,
1742    ToCss,
1743    ToResolvedValue,
1744    ToShmem,
1745)]
1746#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1747/// Internal property that reflects the lang attribute
1748pub struct XLang(#[css(skip)] pub Atom);
1749
1750impl XLang {
1751    #[inline]
1752    /// Get default value for `-x-lang`
1753    pub fn get_initial_value() -> XLang {
1754        XLang(atom!(""))
1755    }
1756}
1757
1758impl Parse for XLang {
1759    fn parse<'i, 't>(
1760        _: &ParserContext,
1761        input: &mut Parser<'i, 't>,
1762    ) -> Result<XLang, ParseError<'i>> {
1763        debug_assert!(
1764            false,
1765            "Should be set directly by presentation attributes only."
1766        );
1767        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1768    }
1769}
1770
1771#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1772#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1773/// Specifies the minimum font size allowed due to changes in scriptlevel.
1774/// Ref: https://wiki.mozilla.org/MathML:mstyle
1775pub struct MozScriptMinSize(pub NoCalcLength);
1776
1777impl MozScriptMinSize {
1778    #[inline]
1779    /// Calculate initial value of -moz-script-min-size.
1780    pub fn get_initial_value() -> Length {
1781        Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1782    }
1783}
1784
1785impl Parse for MozScriptMinSize {
1786    fn parse<'i, 't>(
1787        _: &ParserContext,
1788        input: &mut Parser<'i, 't>,
1789    ) -> Result<MozScriptMinSize, ParseError<'i>> {
1790        debug_assert!(
1791            false,
1792            "Should be set directly by presentation attributes only."
1793        );
1794        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1795    }
1796}
1797
1798/// A value for the `math-depth` property.
1799/// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property
1800#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1801#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1802pub enum MathDepth {
1803    /// Increment math-depth if math-style is compact.
1804    AutoAdd,
1805
1806    /// Add the function's argument to math-depth.
1807    #[css(function)]
1808    Add(Integer),
1809
1810    /// Set math-depth to the specified value.
1811    Absolute(Integer),
1812}
1813
1814impl Parse for MathDepth {
1815    fn parse<'i, 't>(
1816        context: &ParserContext,
1817        input: &mut Parser<'i, 't>,
1818    ) -> Result<MathDepth, ParseError<'i>> {
1819        if input
1820            .try_parse(|i| i.expect_ident_matching("auto-add"))
1821            .is_ok()
1822        {
1823            return Ok(MathDepth::AutoAdd);
1824        }
1825        if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1826            return Ok(MathDepth::Absolute(math_depth_value));
1827        }
1828        input.expect_function_matching("add")?;
1829        let math_depth_delta_value =
1830            input.parse_nested_block(|input| Integer::parse(context, input))?;
1831        Ok(MathDepth::Add(math_depth_delta_value))
1832    }
1833}
1834
1835#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1836#[derive(
1837    Clone,
1838    Copy,
1839    Debug,
1840    PartialEq,
1841    SpecifiedValueInfo,
1842    ToComputedValue,
1843    ToCss,
1844    ToResolvedValue,
1845    ToShmem,
1846)]
1847/// Specifies the multiplier to be used to adjust font size
1848/// due to changes in scriptlevel.
1849///
1850/// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs
1851pub struct MozScriptSizeMultiplier(pub f32);
1852
1853impl MozScriptSizeMultiplier {
1854    #[inline]
1855    /// Get default value of `-moz-script-size-multiplier`
1856    pub fn get_initial_value() -> MozScriptSizeMultiplier {
1857        MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1858    }
1859}
1860
1861impl Parse for MozScriptSizeMultiplier {
1862    fn parse<'i, 't>(
1863        _: &ParserContext,
1864        input: &mut Parser<'i, 't>,
1865    ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1866        debug_assert!(
1867            false,
1868            "Should be set directly by presentation attributes only."
1869        );
1870        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1871    }
1872}
1873
1874impl From<f32> for MozScriptSizeMultiplier {
1875    fn from(v: f32) -> Self {
1876        MozScriptSizeMultiplier(v)
1877    }
1878}
1879
1880impl From<MozScriptSizeMultiplier> for f32 {
1881    fn from(v: MozScriptSizeMultiplier) -> f32 {
1882        v.0
1883    }
1884}
1885
1886/// A specified value for the `line-height` property.
1887pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1888
1889impl ToComputedValue for LineHeight {
1890    type ComputedValue = computed::LineHeight;
1891
1892    #[inline]
1893    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1894        match *self {
1895            GenericLineHeight::Normal => GenericLineHeight::Normal,
1896            #[cfg(feature = "gecko")]
1897            GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1898            GenericLineHeight::Number(number) => {
1899                GenericLineHeight::Number(number.to_computed_value(context))
1900            },
1901            GenericLineHeight::Length(ref non_negative_lp) => {
1902                let result = match non_negative_lp.0 {
1903                    LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1904                        context.maybe_zoom_text(abs.to_computed_value(context))
1905                    },
1906                    LengthPercentage::Length(ref length) => {
1907                        // line-height units specifically resolve against parent's
1908                        // font and line-height properties, while the rest of font
1909                        // relative units still resolve against the element's own
1910                        // properties.
1911                        length.to_computed_value_with_base_size(
1912                            context,
1913                            FontBaseSize::CurrentStyle,
1914                            LineHeightBase::InheritedStyle,
1915                        )
1916                    },
1917                    LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1918                        .to_computed_value(
1919                            context,
1920                            FontBaseSize::CurrentStyle,
1921                            LineHeightBase::InheritedStyle,
1922                        ),
1923                    LengthPercentage::Calc(ref calc) => {
1924                        let computed_calc = calc.to_computed_value_zoomed(
1925                            context,
1926                            FontBaseSize::CurrentStyle,
1927                            LineHeightBase::InheritedStyle,
1928                        );
1929                        let base = context.style().get_font().clone_font_size().computed_size();
1930                        computed_calc.resolve(base)
1931                    },
1932                };
1933                GenericLineHeight::Length(result.into())
1934            },
1935        }
1936    }
1937
1938    #[inline]
1939    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1940        match *computed {
1941            GenericLineHeight::Normal => GenericLineHeight::Normal,
1942            #[cfg(feature = "gecko")]
1943            GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1944            GenericLineHeight::Number(ref number) => {
1945                GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1946            },
1947            GenericLineHeight::Length(ref length) => {
1948                GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1949            },
1950        }
1951    }
1952}
1953
1954/// Flags for the query_font_metrics() function.
1955#[repr(C)]
1956pub struct QueryFontMetricsFlags(u8);
1957
1958bitflags! {
1959    impl QueryFontMetricsFlags: u8 {
1960        /// Should we use the user font set?
1961        const USE_USER_FONT_SET = 1 << 0;
1962        /// Does the caller need the `ch` unit (width of the ZERO glyph)?
1963        const NEEDS_CH = 1 << 1;
1964        /// Does the caller need the `ic` unit (width of the WATER ideograph)?
1965        const NEEDS_IC = 1 << 2;
1966        /// Does the caller need math scales to be retrieved?
1967        const NEEDS_MATH_SCALES = 1 << 3;
1968    }
1969}