tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

font.rs (47983B)


      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 //! Computed values for font properties
      6 
      7 use crate::derives::*;
      8 use crate::parser::{Parse, ParserContext};
      9 use crate::values::animated::ToAnimatedValue;
     10 use crate::values::computed::{
     11    Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
     12    ToComputedValue, Zoom,
     13 };
     14 use crate::values::generics::font::{
     15    FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
     16 };
     17 use crate::values::generics::{font as generics, NonNegative};
     18 use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
     19 use crate::values::specified::font::{
     20    self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
     21 };
     22 use crate::values::specified::length::{FontBaseSize, LineHeightBase, NoCalcLength};
     23 use crate::values::CSSInteger;
     24 use crate::Atom;
     25 use cssparser::{match_ignore_ascii_case, serialize_identifier, CssStringWriter, Parser};
     26 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
     27 use num_traits::abs;
     28 use num_traits::cast::AsPrimitive;
     29 use std::fmt::{self, Write};
     30 use style_traits::{CssWriter, ParseError, ToCss};
     31 
     32 pub use crate::values::computed::Length as MozScriptMinSize;
     33 pub use crate::values::specified::font::MozScriptSizeMultiplier;
     34 pub use crate::values::specified::font::{FontPalette, FontSynthesis, FontSynthesisStyle};
     35 pub use crate::values::specified::font::{
     36    FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric,
     37    QueryFontMetricsFlags, XLang, XTextScale,
     38 };
     39 pub use crate::values::specified::Integer as SpecifiedInteger;
     40 pub use crate::values::specified::Number as SpecifiedNumber;
     41 
     42 /// Generic template for font property type classes that use a fixed-point
     43 /// internal representation with `FRACTION_BITS` for the fractional part.
     44 ///
     45 /// Values are constructed from and exposed as floating-point, but stored
     46 /// internally as fixed point, so there will be a quantization effect on
     47 /// fractional values, depending on the number of fractional bits used.
     48 ///
     49 /// Using (16-bit) fixed-point types rather than floats for these style
     50 /// attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle; it
     51 /// will also tend to reduce the number of distinct font instances that get
     52 /// created, particularly when styles are animated or set to arbitrary values
     53 /// (e.g. by sliders in the UI), which should reduce pressure on graphics
     54 /// resources and improve cache hit rates.
     55 ///
     56 /// cbindgen:derive-lt
     57 /// cbindgen:derive-lte
     58 /// cbindgen:derive-gt
     59 /// cbindgen:derive-gte
     60 #[repr(C)]
     61 #[derive(
     62    Clone,
     63    ComputeSquaredDistance,
     64    Copy,
     65    Debug,
     66    Eq,
     67    Hash,
     68    MallocSizeOf,
     69    PartialEq,
     70    PartialOrd,
     71    ToResolvedValue,
     72 )]
     73 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
     74 pub struct FixedPoint<T, const FRACTION_BITS: u16> {
     75    /// The actual representation.
     76    pub value: T,
     77 }
     78 
     79 impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
     80 where
     81    T: AsPrimitive<f32>,
     82    f32: AsPrimitive<T>,
     83    u16: AsPrimitive<T>,
     84 {
     85    const SCALE: u16 = 1 << FRACTION_BITS;
     86    const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
     87 
     88    /// Returns a fixed-point bit from a floating-point context.
     89    pub fn from_float(v: f32) -> Self {
     90        Self {
     91            value: (v * Self::SCALE as f32).round().as_(),
     92        }
     93    }
     94 
     95    /// Returns the floating-point representation.
     96    pub fn to_float(&self) -> f32 {
     97        self.value.as_() * Self::INVERSE_SCALE
     98    }
     99 }
    100 
    101 // We implement this and mul below only for u16 types, because u32 types might need more care about
    102 // overflow. But it's not hard to implement in either case.
    103 impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {
    104    type Output = Self;
    105    fn div(self, rhs: Self) -> Self {
    106        Self {
    107            value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,
    108        }
    109    }
    110 }
    111 impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {
    112    type Output = Self;
    113    fn mul(self, rhs: Self) -> Self {
    114        Self {
    115            value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,
    116        }
    117    }
    118 }
    119 
    120 /// font-weight: range 1..1000, fractional values permitted; keywords
    121 /// 'normal', 'bold' aliased to 400, 700 respectively.
    122 ///
    123 /// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)
    124 pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
    125 
    126 /// This is an alias which is useful mostly as a cbindgen / C++ inference
    127 /// workaround.
    128 pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
    129 
    130 /// A value for the font-weight property per:
    131 ///
    132 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
    133 ///
    134 /// cbindgen:derive-lt
    135 /// cbindgen:derive-lte
    136 /// cbindgen:derive-gt
    137 /// cbindgen:derive-gte
    138 #[derive(
    139    Clone,
    140    ComputeSquaredDistance,
    141    Copy,
    142    Debug,
    143    Hash,
    144    MallocSizeOf,
    145    PartialEq,
    146    PartialOrd,
    147    ToResolvedValue,
    148    ToTyped,
    149 )]
    150 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    151 #[repr(C)]
    152 pub struct FontWeight(FontWeightFixedPoint);
    153 impl ToAnimatedValue for FontWeight {
    154    type AnimatedValue = Number;
    155 
    156    #[inline]
    157    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
    158        self.value()
    159    }
    160 
    161    #[inline]
    162    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    163        FontWeight::from_float(animated)
    164    }
    165 }
    166 
    167 impl ToCss for FontWeight {
    168    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    169    where
    170        W: fmt::Write,
    171    {
    172        self.value().to_css(dest)
    173    }
    174 }
    175 
    176 impl FontWeight {
    177    /// The `normal` keyword.
    178    pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
    179        value: 400 << FONT_WEIGHT_FRACTION_BITS,
    180    });
    181 
    182    /// The `bold` value.
    183    pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
    184        value: 700 << FONT_WEIGHT_FRACTION_BITS,
    185    });
    186 
    187    /// The threshold from which we consider a font bold.
    188    pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
    189        value: 600 << FONT_WEIGHT_FRACTION_BITS,
    190    });
    191 
    192    /// The threshold above which CSS font matching prefers bolder faces
    193    /// over lighter ones.
    194    pub const PREFER_BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
    195        value: 500 << FONT_WEIGHT_FRACTION_BITS,
    196    });
    197 
    198    /// Returns the `normal` keyword value.
    199    pub fn normal() -> Self {
    200        Self::NORMAL
    201    }
    202 
    203    /// Whether this weight is bold
    204    pub fn is_bold(&self) -> bool {
    205        *self >= Self::BOLD_THRESHOLD
    206    }
    207 
    208    /// Returns the value as a float.
    209    pub fn value(&self) -> f32 {
    210        self.0.to_float()
    211    }
    212 
    213    /// Construct a valid weight from a float value.
    214    pub fn from_float(v: f32) -> Self {
    215        Self(FixedPoint::from_float(
    216            v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
    217        ))
    218    }
    219 
    220    /// Return the bolder weight.
    221    ///
    222    /// See the table in:
    223    /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
    224    pub fn bolder(self) -> Self {
    225        let value = self.value();
    226        if value < 350. {
    227            return Self::NORMAL;
    228        }
    229        if value < 550. {
    230            return Self::BOLD;
    231        }
    232        Self::from_float(value.max(900.))
    233    }
    234 
    235    /// Return the lighter weight.
    236    ///
    237    /// See the table in:
    238    /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
    239    pub fn lighter(self) -> Self {
    240        let value = self.value();
    241        if value < 550. {
    242            return Self::from_float(value.min(100.));
    243        }
    244        if value < 750. {
    245            return Self::NORMAL;
    246        }
    247        Self::BOLD
    248    }
    249 }
    250 
    251 #[derive(
    252    Animate,
    253    Clone,
    254    ComputeSquaredDistance,
    255    Copy,
    256    Debug,
    257    MallocSizeOf,
    258    PartialEq,
    259    ToAnimatedZero,
    260    ToCss,
    261    ToTyped,
    262 )]
    263 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
    264 /// The computed value of font-size
    265 pub struct FontSize {
    266    /// The computed size, that we use to compute ems etc. This accounts for
    267    /// e.g., text-zoom.
    268    pub computed_size: NonNegativeLength,
    269    /// The actual used size. This is the computed font size, potentially
    270    /// constrained by other factors like minimum font-size settings and so on.
    271    #[css(skip)]
    272    pub used_size: NonNegativeLength,
    273    /// If derived from a keyword, the keyword and additional transformations applied to it
    274    #[css(skip)]
    275    pub keyword_info: KeywordInfo,
    276 }
    277 
    278 impl FontSize {
    279    /// The actual computed font size.
    280    #[inline]
    281    pub fn computed_size(&self) -> Length {
    282        self.computed_size.0
    283    }
    284 
    285    /// The actual used font size.
    286    #[inline]
    287    pub fn used_size(&self) -> Length {
    288        self.used_size.0
    289    }
    290 
    291    /// Apply zoom to the font-size. This is usually done by ToComputedValue.
    292    #[inline]
    293    pub fn zoom(&self, zoom: Zoom) -> Self {
    294        Self {
    295            computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
    296            used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
    297            keyword_info: self.keyword_info,
    298        }
    299    }
    300 
    301    #[inline]
    302    /// Get default value of font size.
    303    pub fn medium() -> Self {
    304        Self {
    305            computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
    306            used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
    307            keyword_info: KeywordInfo::medium(),
    308        }
    309    }
    310 }
    311 
    312 impl ToAnimatedValue for FontSize {
    313    type AnimatedValue = Length;
    314 
    315    #[inline]
    316    fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
    317        self.computed_size.0.to_animated_value(context)
    318    }
    319 
    320    #[inline]
    321    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
    322        FontSize {
    323            computed_size: NonNegative(animated.clamp_to_non_negative()),
    324            used_size: NonNegative(animated.clamp_to_non_negative()),
    325            keyword_info: KeywordInfo::none(),
    326        }
    327    }
    328 }
    329 
    330 impl ToResolvedValue for FontSize {
    331    type ResolvedValue = NonNegativeLength;
    332 
    333    #[inline]
    334    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
    335        self.computed_size.to_resolved_value(context)
    336    }
    337 
    338    #[inline]
    339    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
    340        let computed_size = NonNegativeLength::from_resolved_value(resolved);
    341        Self {
    342            computed_size,
    343            used_size: computed_size,
    344            keyword_info: KeywordInfo::none(),
    345        }
    346    }
    347 }
    348 
    349 #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)]
    350 #[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
    351 /// Specifies a prioritized list of font family names or generic family names.
    352 #[repr(C)]
    353 pub struct FontFamily {
    354    /// The actual list of family names.
    355    pub families: FontFamilyList,
    356    /// Whether this font-family came from a specified system-font.
    357    pub is_system_font: bool,
    358    /// Whether this is the initial font-family that might react to language
    359    /// changes.
    360    pub is_initial: bool,
    361 }
    362 
    363 macro_rules! static_font_family {
    364    ($ident:ident, $family:expr) => {
    365        static $ident: std::sync::LazyLock<FontFamily> = std::sync::LazyLock::new(|| FontFamily {
    366            families: FontFamilyList {
    367                list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
    368            },
    369            is_system_font: false,
    370            is_initial: false,
    371        });
    372    };
    373 }
    374 
    375 impl FontFamily {
    376    #[inline]
    377    /// Get default font family as `serif` which is a generic font-family
    378    pub fn serif() -> Self {
    379        Self::generic(GenericFontFamily::Serif).clone()
    380    }
    381 
    382    /// Returns the font family for `-moz-bullet-font`.
    383    #[cfg(feature = "gecko")]
    384    pub(crate) fn moz_bullet() -> &'static Self {
    385        static_font_family!(
    386            MOZ_BULLET,
    387            SingleFontFamily::FamilyName(FamilyName {
    388                name: atom!("-moz-bullet-font"),
    389                syntax: FontFamilyNameSyntax::Identifiers,
    390            })
    391        );
    392 
    393        &*MOZ_BULLET
    394    }
    395 
    396    /// Returns a font family for a single system font.
    397    #[cfg(feature = "gecko")]
    398    pub fn for_system_font(name: &str) -> Self {
    399        Self {
    400            families: FontFamilyList {
    401                list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
    402                    FamilyName {
    403                        name: Atom::from(name),
    404                        syntax: FontFamilyNameSyntax::Identifiers,
    405                    },
    406                ))),
    407            },
    408            is_system_font: true,
    409            is_initial: false,
    410        }
    411    }
    412 
    413    /// Returns a generic font family.
    414    pub fn generic(generic: GenericFontFamily) -> &'static Self {
    415        macro_rules! generic_font_family {
    416            ($ident:ident, $family:ident) => {
    417                static_font_family!(
    418                    $ident,
    419                    SingleFontFamily::Generic(GenericFontFamily::$family)
    420                )
    421            };
    422        }
    423 
    424        generic_font_family!(SERIF, Serif);
    425        generic_font_family!(SANS_SERIF, SansSerif);
    426        generic_font_family!(MONOSPACE, Monospace);
    427        generic_font_family!(CURSIVE, Cursive);
    428        generic_font_family!(FANTASY, Fantasy);
    429        #[cfg(feature = "gecko")]
    430        generic_font_family!(MATH, Math);
    431        #[cfg(feature = "gecko")]
    432        generic_font_family!(MOZ_EMOJI, MozEmoji);
    433        generic_font_family!(SYSTEM_UI, SystemUi);
    434 
    435        let family = match generic {
    436            GenericFontFamily::None => {
    437                debug_assert!(false, "Bogus caller!");
    438                &*SERIF
    439            },
    440            GenericFontFamily::Serif => &*SERIF,
    441            GenericFontFamily::SansSerif => &*SANS_SERIF,
    442            GenericFontFamily::Monospace => &*MONOSPACE,
    443            GenericFontFamily::Cursive => &*CURSIVE,
    444            GenericFontFamily::Fantasy => &*FANTASY,
    445            #[cfg(feature = "gecko")]
    446            GenericFontFamily::Math => &*MATH,
    447            #[cfg(feature = "gecko")]
    448            GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
    449            GenericFontFamily::SystemUi => &*SYSTEM_UI,
    450        };
    451        debug_assert_eq!(
    452            *family.families.iter().next().unwrap(),
    453            SingleFontFamily::Generic(generic)
    454        );
    455        family
    456    }
    457 }
    458 
    459 impl MallocSizeOf for FontFamily {
    460    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
    461        use malloc_size_of::MallocUnconditionalSizeOf;
    462        // SharedFontList objects are generally measured from the pointer stored
    463        // in the specified value. So only count this if the SharedFontList is
    464        // unshared.
    465        let shared_font_list = &self.families.list;
    466        if shared_font_list.is_unique() {
    467            shared_font_list.unconditional_size_of(ops)
    468        } else {
    469            0
    470        }
    471    }
    472 }
    473 
    474 impl ToCss for FontFamily {
    475    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    476    where
    477        W: fmt::Write,
    478    {
    479        let mut iter = self.families.iter();
    480        match iter.next() {
    481            Some(f) => f.to_css(dest)?,
    482            None => return Ok(()),
    483        }
    484        for family in iter {
    485            dest.write_str(", ")?;
    486            family.to_css(dest)?;
    487        }
    488        Ok(())
    489    }
    490 }
    491 
    492 /// The name of a font family of choice.
    493 #[derive(
    494    Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
    495 )]
    496 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    497 #[repr(C)]
    498 pub struct FamilyName {
    499    /// Name of the font family.
    500    pub name: Atom,
    501    /// Syntax of the font family.
    502    pub syntax: FontFamilyNameSyntax,
    503 }
    504 
    505 #[cfg(feature = "gecko")]
    506 impl FamilyName {
    507    fn is_known_icon_font_family(&self) -> bool {
    508        use crate::gecko_bindings::bindings;
    509        unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
    510    }
    511 }
    512 
    513 #[cfg(feature = "servo")]
    514 impl FamilyName {
    515    fn is_known_icon_font_family(&self) -> bool {
    516        false
    517    }
    518 }
    519 
    520 impl ToCss for FamilyName {
    521    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    522    where
    523        W: fmt::Write,
    524    {
    525        match self.syntax {
    526            FontFamilyNameSyntax::Quoted => {
    527                dest.write_char('"')?;
    528                write!(CssStringWriter::new(dest), "{}", self.name)?;
    529                dest.write_char('"')
    530            },
    531            FontFamilyNameSyntax::Identifiers => {
    532                let mut first = true;
    533                for ident in self.name.to_string().split(' ') {
    534                    if first {
    535                        first = false;
    536                    } else {
    537                        dest.write_char(' ')?;
    538                    }
    539                    debug_assert!(
    540                        !ident.is_empty(),
    541                        "Family name with leading, \
    542                         trailing, or consecutive white spaces should \
    543                         have been marked quoted by the parser"
    544                    );
    545                    serialize_identifier(ident, dest)?;
    546                }
    547                Ok(())
    548            },
    549        }
    550    }
    551 }
    552 
    553 #[derive(
    554    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
    555 )]
    556 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    557 /// Font family names must either be given quoted as strings,
    558 /// or unquoted as a sequence of one or more identifiers.
    559 #[repr(u8)]
    560 pub enum FontFamilyNameSyntax {
    561    /// The family name was specified in a quoted form, e.g. "Font Name"
    562    /// or 'Font Name'.
    563    Quoted,
    564 
    565    /// The family name was specified in an unquoted form as a sequence of
    566    /// identifiers.
    567    Identifiers,
    568 }
    569 
    570 /// A set of faces that vary in weight, width or slope.
    571 /// cbindgen:derive-mut-casts=true
    572 #[derive(
    573    Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
    574 )]
    575 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
    576 #[repr(u8)]
    577 pub enum SingleFontFamily {
    578    /// The name of a font family of choice.
    579    FamilyName(FamilyName),
    580    /// Generic family name.
    581    Generic(GenericFontFamily),
    582 }
    583 
    584 fn system_ui_enabled(_: &ParserContext) -> bool {
    585    static_prefs::pref!("layout.css.system-ui.enabled")
    586 }
    587 
    588 #[cfg(feature = "gecko")]
    589 fn math_enabled(context: &ParserContext) -> bool {
    590    context.chrome_rules_enabled() || static_prefs::pref!("mathml.font_family_math.enabled")
    591 }
    592 
    593 /// A generic font-family name.
    594 ///
    595 /// The order here is important, if you change it make sure that
    596 /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
    597 /// sSingleGenerics are updated as well.
    598 ///
    599 /// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC
    600 /// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /
    601 /// bug 1726515.
    602 #[derive(
    603    Clone,
    604    Copy,
    605    Debug,
    606    Eq,
    607    Hash,
    608    MallocSizeOf,
    609    PartialEq,
    610    Parse,
    611    ToCss,
    612    ToComputedValue,
    613    ToResolvedValue,
    614    ToShmem,
    615 )]
    616 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    617 #[repr(u32)]
    618 #[allow(missing_docs)]
    619 pub enum GenericFontFamily {
    620    /// No generic family specified, only for internal usage.
    621    ///
    622    /// NOTE(emilio): Gecko code relies on this variant being zero.
    623    #[css(skip)]
    624    None = 0,
    625    Serif,
    626    SansSerif,
    627    #[parse(aliases = "-moz-fixed")]
    628    Monospace,
    629    Cursive,
    630    Fantasy,
    631    #[cfg(feature = "gecko")]
    632    #[parse(condition = "math_enabled")]
    633    Math,
    634    #[parse(condition = "system_ui_enabled")]
    635    SystemUi,
    636    /// An internal value for emoji font selection.
    637    #[css(skip)]
    638    #[cfg(feature = "gecko")]
    639    MozEmoji,
    640 }
    641 
    642 impl GenericFontFamily {
    643    /// When we disallow websites to override fonts, we ignore some generic
    644    /// families that the website might specify, since they're not configured by
    645    /// the user. See bug 789788 and bug 1730098.
    646    pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
    647        match self {
    648            Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false,
    649            #[cfg(feature = "gecko")]
    650            Self::Math | Self::MozEmoji => false,
    651            Self::Serif | Self::SansSerif | Self::Monospace => true,
    652        }
    653    }
    654 }
    655 
    656 impl Parse for SingleFontFamily {
    657    /// Parse a font-family value.
    658    fn parse<'i, 't>(
    659        context: &ParserContext,
    660        input: &mut Parser<'i, 't>,
    661    ) -> Result<Self, ParseError<'i>> {
    662        if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
    663            return Ok(SingleFontFamily::FamilyName(FamilyName {
    664                name: Atom::from(&*value),
    665                syntax: FontFamilyNameSyntax::Quoted,
    666            }));
    667        }
    668 
    669        if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
    670            return Ok(SingleFontFamily::Generic(generic));
    671        }
    672 
    673        let first_ident = input.expect_ident_cloned()?;
    674        let reserved = match_ignore_ascii_case! { &first_ident,
    675            // https://drafts.csswg.org/css-fonts/#propdef-font-family
    676            // "Font family names that happen to be the same as a keyword value
    677            //  (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)
    678            //  must be quoted to prevent confusion with the keywords with the same names.
    679            //  The keywords ‘initial’ and ‘default’ are reserved for future use
    680            //  and must also be quoted when used as font names.
    681            //  UAs must not consider these keywords as matching the <family-name> type."
    682            "inherit" | "initial" | "unset" | "revert" | "default" => true,
    683            _ => false,
    684        };
    685 
    686        let mut value = first_ident.as_ref().to_owned();
    687        let mut serialize_quoted = value.contains(' ');
    688 
    689        // These keywords are not allowed by themselves.
    690        // The only way this value can be valid with with another keyword.
    691        if reserved {
    692            let ident = input.expect_ident()?;
    693            serialize_quoted = serialize_quoted || ident.contains(' ');
    694            value.push(' ');
    695            value.push_str(&ident);
    696        }
    697        while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
    698            serialize_quoted = serialize_quoted || ident.contains(' ');
    699            value.push(' ');
    700            value.push_str(&ident);
    701        }
    702        let syntax = if serialize_quoted {
    703            // For font family names which contains special white spaces, e.g.
    704            // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
    705            // as identifiers correctly. Just mark them quoted so we don't need
    706            // to worry about them in serialization code.
    707            FontFamilyNameSyntax::Quoted
    708        } else {
    709            FontFamilyNameSyntax::Identifiers
    710        };
    711        Ok(SingleFontFamily::FamilyName(FamilyName {
    712            name: Atom::from(value),
    713            syntax,
    714        }))
    715    }
    716 }
    717 
    718 /// A list of font families.
    719 #[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
    720 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
    721 #[repr(C)]
    722 pub struct FontFamilyList {
    723    /// The actual list of font families specified.
    724    pub list: crate::ArcSlice<SingleFontFamily>,
    725 }
    726 
    727 impl FontFamilyList {
    728    /// Return iterator of SingleFontFamily
    729    pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
    730        self.list.iter()
    731    }
    732 
    733    /// If there's a generic font family on the list which is suitable for user
    734    /// font prioritization, then move it ahead of the other families in the list,
    735    /// except for any families known to be ligature-based icon fonts, where using a
    736    /// generic instead of the site's specified font may cause substantial breakage.
    737    /// If no suitable generic is found in the list, insert the default generic ahead
    738    /// of all the listed families except for known ligature-based icon fonts.
    739    #[cfg_attr(feature = "servo", allow(unused))]
    740    pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
    741        let mut index_of_first_generic = None;
    742        let mut target_index = None;
    743 
    744        for (i, f) in self.iter().enumerate() {
    745            match &*f {
    746                SingleFontFamily::Generic(f) => {
    747                    if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
    748                        // If we haven't found a target position, there's nothing to do;
    749                        // this entry is already ahead of everything except any whitelisted
    750                        // icon fonts.
    751                        if target_index.is_none() {
    752                            return;
    753                        }
    754                        index_of_first_generic = Some(i);
    755                        break;
    756                    }
    757                    // A non-prioritized generic (e.g. cursive, fantasy) becomes the target
    758                    // position for prioritization, just like arbitrary named families.
    759                    if target_index.is_none() {
    760                        target_index = Some(i);
    761                    }
    762                },
    763                SingleFontFamily::FamilyName(fam) => {
    764                    // Target position for the first generic is in front of the first
    765                    // non-whitelisted icon font family we find.
    766                    if target_index.is_none() && !fam.is_known_icon_font_family() {
    767                        target_index = Some(i);
    768                    }
    769                },
    770            }
    771        }
    772 
    773        let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
    774        let first_generic = match index_of_first_generic {
    775            Some(i) => new_list.remove(i),
    776            None => SingleFontFamily::Generic(generic),
    777        };
    778 
    779        if let Some(i) = target_index {
    780            new_list.insert(i, first_generic);
    781        } else {
    782            new_list.push(first_generic);
    783        }
    784        self.list = crate::ArcSlice::from_iter(new_list.into_iter());
    785    }
    786 
    787    /// Returns whether we need to prioritize user fonts.
    788    #[cfg_attr(feature = "servo", allow(unused))]
    789    pub(crate) fn needs_user_font_prioritization(&self) -> bool {
    790        self.iter().next().map_or(true, |f| match f {
    791            SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
    792            _ => true,
    793        })
    794    }
    795 
    796    /// Return the generic ID if it is a single generic font
    797    pub fn single_generic(&self) -> Option<GenericFontFamily> {
    798        let mut iter = self.iter();
    799        if let Some(SingleFontFamily::Generic(f)) = iter.next() {
    800            if iter.next().is_none() {
    801                return Some(*f);
    802            }
    803        }
    804        None
    805    }
    806 }
    807 
    808 /// Preserve the readability of text when font fallback occurs.
    809 pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
    810 
    811 impl FontSizeAdjust {
    812    #[inline]
    813    /// Default value of font-size-adjust
    814    pub fn none() -> Self {
    815        FontSizeAdjust::None
    816    }
    817 }
    818 
    819 impl ToComputedValue for specified::FontSizeAdjust {
    820    type ComputedValue = FontSizeAdjust;
    821 
    822    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    823        use crate::font_metrics::FontMetricsOrientation;
    824 
    825        let font_metrics = |vertical, flags| {
    826            let orient = if vertical {
    827                FontMetricsOrientation::MatchContextPreferVertical
    828            } else {
    829                FontMetricsOrientation::Horizontal
    830            };
    831            let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
    832            let font_size = context.style().get_font().clone_font_size().used_size.0;
    833            (metrics, font_size)
    834        };
    835 
    836        // Macro to resolve a from-font value using the given metric field. If not present,
    837        // returns the fallback value, or if that is negative, resolves using ascent instead
    838        // of the missing field (this is the fallback for cap-height).
    839        macro_rules! resolve {
    840            ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
    841                match $value {
    842                    specified::FontSizeAdjustFactor::Number(f) => {
    843                        FontSizeAdjust::$basis(f.to_computed_value(context))
    844                    },
    845                    specified::FontSizeAdjustFactor::FromFont => {
    846                        let (metrics, font_size) = font_metrics($vertical, $flags);
    847                        let ratio = if let Some(metric) = metrics.$field {
    848                            metric / font_size
    849                        } else if $fallback >= 0.0 {
    850                            $fallback
    851                        } else {
    852                            metrics.ascent / font_size
    853                        };
    854                        if ratio.is_nan() {
    855                            FontSizeAdjust::$basis(NonNegative(abs($fallback)))
    856                        } else {
    857                            FontSizeAdjust::$basis(NonNegative(ratio))
    858                        }
    859                    },
    860                }
    861            }};
    862        }
    863 
    864        match *self {
    865            Self::None => FontSizeAdjust::None,
    866            Self::ExHeight(val) => {
    867                resolve!(
    868                    ExHeight,
    869                    val,
    870                    false,
    871                    x_height,
    872                    0.5,
    873                    QueryFontMetricsFlags::empty()
    874                )
    875            },
    876            Self::CapHeight(val) => {
    877                resolve!(
    878                    CapHeight,
    879                    val,
    880                    false,
    881                    cap_height,
    882                    -1.0, /* fall back to ascent */
    883                    QueryFontMetricsFlags::empty()
    884                )
    885            },
    886            Self::ChWidth(val) => {
    887                resolve!(
    888                    ChWidth,
    889                    val,
    890                    false,
    891                    zero_advance_measure,
    892                    0.5,
    893                    QueryFontMetricsFlags::NEEDS_CH
    894                )
    895            },
    896            Self::IcWidth(val) => {
    897                resolve!(
    898                    IcWidth,
    899                    val,
    900                    false,
    901                    ic_width,
    902                    1.0,
    903                    QueryFontMetricsFlags::NEEDS_IC
    904                )
    905            },
    906            Self::IcHeight(val) => {
    907                resolve!(
    908                    IcHeight,
    909                    val,
    910                    true,
    911                    ic_width,
    912                    1.0,
    913                    QueryFontMetricsFlags::NEEDS_IC
    914                )
    915            },
    916        }
    917    }
    918 
    919    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    920        macro_rules! case {
    921            ($basis:ident, $val:expr) => {
    922                Self::$basis(specified::FontSizeAdjustFactor::Number(
    923                    ToComputedValue::from_computed_value($val),
    924                ))
    925            };
    926        }
    927        match *computed {
    928            FontSizeAdjust::None => Self::None,
    929            FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
    930            FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
    931            FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
    932            FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
    933            FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
    934        }
    935    }
    936 }
    937 
    938 /// Use FontSettings as computed type of FontFeatureSettings.
    939 pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
    940 
    941 /// The computed value for font-variation-settings.
    942 pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
    943 
    944 // The computed value of font-{feature,variation}-settings discards values
    945 // with duplicate tags, keeping only the last occurrence of each tag.
    946 fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
    947 where
    948    T: TaggedFontValue,
    949 {
    950    if settings_list.len() > 1 {
    951        settings_list.sort_by_key(|k| k.tag().0);
    952        // dedup() keeps the first of any duplicates, but we want the last,
    953        // so we implement it manually here.
    954        let mut prev_tag = settings_list.last().unwrap().tag();
    955        for i in (0..settings_list.len() - 1).rev() {
    956            let cur_tag = settings_list[i].tag();
    957            if cur_tag == prev_tag {
    958                settings_list.remove(i);
    959            }
    960            prev_tag = cur_tag;
    961        }
    962    }
    963 }
    964 
    965 impl<T> ToComputedValue for FontSettings<T>
    966 where
    967    T: ToComputedValue,
    968    <T as ToComputedValue>::ComputedValue: TaggedFontValue,
    969 {
    970    type ComputedValue = FontSettings<T::ComputedValue>;
    971 
    972    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
    973        let mut v = self
    974            .0
    975            .iter()
    976            .map(|item| item.to_computed_value(context))
    977            .collect::<Vec<_>>();
    978        dedup_font_settings(&mut v);
    979        FontSettings(v.into_boxed_slice())
    980    }
    981 
    982    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
    983        Self(computed.0.iter().map(T::from_computed_value).collect())
    984    }
    985 }
    986 
    987 /// font-language-override can only have a single 1-4 ASCII character
    988 /// OpenType "language system" tag, so we should be able to compute
    989 /// it and store it as a 32-bit integer
    990 /// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
    991 #[derive(
    992    Clone,
    993    Copy,
    994    Debug,
    995    Eq,
    996    MallocSizeOf,
    997    PartialEq,
    998    SpecifiedValueInfo,
    999    ToComputedValue,
   1000    ToResolvedValue,
   1001    ToShmem,
   1002    ToTyped,
   1003 )]
   1004 #[repr(C)]
   1005 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
   1006 #[value_info(other_values = "normal")]
   1007 pub struct FontLanguageOverride(pub u32);
   1008 
   1009 impl FontLanguageOverride {
   1010    #[inline]
   1011    /// Get computed default value of `font-language-override` with 0
   1012    pub fn normal() -> FontLanguageOverride {
   1013        FontLanguageOverride(0)
   1014    }
   1015 
   1016    /// Returns this value as a `&str`, backed by `storage`.
   1017    #[inline]
   1018    pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
   1019        *storage = u32::to_be_bytes(self.0);
   1020        // Safe because we ensure it's ASCII during parsing
   1021        let slice = if cfg!(debug_assertions) {
   1022            std::str::from_utf8(&storage[..]).unwrap()
   1023        } else {
   1024            unsafe { std::str::from_utf8_unchecked(&storage[..]) }
   1025        };
   1026        slice.trim_end()
   1027    }
   1028 
   1029    /// Unsafe because `Self::to_str` requires the value to represent a UTF-8
   1030    /// string.
   1031    #[inline]
   1032    pub unsafe fn from_u32(value: u32) -> Self {
   1033        Self(value)
   1034    }
   1035 }
   1036 
   1037 impl ToCss for FontLanguageOverride {
   1038    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1039    where
   1040        W: fmt::Write,
   1041    {
   1042        if self.0 == 0 {
   1043            return dest.write_str("normal");
   1044        }
   1045        self.to_str(&mut [0; 4]).to_css(dest)
   1046    }
   1047 }
   1048 
   1049 impl ToComputedValue for specified::MozScriptMinSize {
   1050    type ComputedValue = MozScriptMinSize;
   1051 
   1052    fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
   1053        // this value is used in the computation of font-size, so
   1054        // we use the parent size
   1055        let base_size = FontBaseSize::InheritedStyle;
   1056        let line_height_base = LineHeightBase::InheritedStyle;
   1057        match self.0 {
   1058            NoCalcLength::FontRelative(value) => {
   1059                value.to_computed_value(cx, base_size, line_height_base)
   1060            },
   1061            NoCalcLength::ServoCharacterWidth(value) => {
   1062                value.to_computed_value(base_size.resolve(cx).computed_size())
   1063            },
   1064            ref l => l.to_computed_value(cx),
   1065        }
   1066    }
   1067 
   1068    fn from_computed_value(other: &MozScriptMinSize) -> Self {
   1069        specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
   1070    }
   1071 }
   1072 
   1073 /// The computed value of the math-depth property.
   1074 pub type MathDepth = i8;
   1075 
   1076 #[cfg(feature = "gecko")]
   1077 impl ToComputedValue for specified::MathDepth {
   1078    type ComputedValue = MathDepth;
   1079 
   1080    fn to_computed_value(&self, cx: &Context) -> i8 {
   1081        use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
   1082        use std::{cmp, i8};
   1083 
   1084        let int = match *self {
   1085            specified::MathDepth::AutoAdd => {
   1086                let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
   1087                let style = cx.builder.get_parent_font().clone_math_style();
   1088                if style == MathStyleValue::Compact {
   1089                    parent.saturating_add(1)
   1090                } else {
   1091                    parent
   1092                }
   1093            },
   1094            specified::MathDepth::Add(rel) => {
   1095                let parent = cx.builder.get_parent_font().clone_math_depth();
   1096                (parent as i32).saturating_add(rel.to_computed_value(cx))
   1097            },
   1098            specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
   1099        };
   1100        cmp::min(int, i8::MAX as i32) as i8
   1101    }
   1102 
   1103    fn from_computed_value(other: &i8) -> Self {
   1104        let computed_value = *other as i32;
   1105        specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
   1106    }
   1107 }
   1108 
   1109 impl ToAnimatedValue for MathDepth {
   1110    type AnimatedValue = CSSInteger;
   1111 
   1112    #[inline]
   1113    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
   1114        self.into()
   1115    }
   1116 
   1117    #[inline]
   1118    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
   1119        use std::{cmp, i8};
   1120        cmp::min(animated, i8::MAX as i32) as i8
   1121    }
   1122 }
   1123 
   1124 /// - Use a signed 8.8 fixed-point value (representable range -128.0..128)
   1125 ///
   1126 /// Values of <angle> below -90 or above 90 are not permitted, so we use an out
   1127 /// of range value to represent `italic`.
   1128 pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
   1129 
   1130 /// This is an alias which is useful mostly as a cbindgen / C++ inference
   1131 /// workaround.
   1132 pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
   1133 
   1134 /// The computed value of `font-style`.
   1135 ///
   1136 /// - Define angle of zero degrees as `normal`
   1137 /// - Define out-of-range value 100 degrees as `italic`
   1138 /// - Other values represent `oblique <angle>`
   1139 ///
   1140 /// cbindgen:derive-lt
   1141 /// cbindgen:derive-lte
   1142 /// cbindgen:derive-gt
   1143 /// cbindgen:derive-gte
   1144 #[derive(
   1145    Clone,
   1146    ComputeSquaredDistance,
   1147    Copy,
   1148    Debug,
   1149    Eq,
   1150    Hash,
   1151    MallocSizeOf,
   1152    PartialEq,
   1153    PartialOrd,
   1154    ToResolvedValue,
   1155    ToTyped,
   1156 )]
   1157 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
   1158 #[repr(C)]
   1159 pub struct FontStyle(FontStyleFixedPoint);
   1160 
   1161 impl FontStyle {
   1162    /// The `normal` keyword, equal to `oblique` with angle zero.
   1163    pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
   1164        value: 0 << FONT_STYLE_FRACTION_BITS,
   1165    });
   1166 
   1167    /// The italic keyword.
   1168    pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
   1169        value: 100 << FONT_STYLE_FRACTION_BITS,
   1170    });
   1171 
   1172    /// The default angle for `font-style: oblique`.
   1173    /// See also https://github.com/w3c/csswg-drafts/issues/2295
   1174    pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
   1175 
   1176    /// The `oblique` keyword with the default degrees.
   1177    pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
   1178        value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
   1179    });
   1180 
   1181    /// The `normal` value.
   1182    #[inline]
   1183    pub fn normal() -> Self {
   1184        Self::NORMAL
   1185    }
   1186 
   1187    /// Returns the oblique angle for this style.
   1188    pub fn oblique(degrees: f32) -> Self {
   1189        Self(FixedPoint::from_float(
   1190            degrees
   1191                .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
   1192                .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
   1193        ))
   1194    }
   1195 
   1196    /// Returns the oblique angle for this style.
   1197    pub fn oblique_degrees(&self) -> f32 {
   1198        debug_assert_ne!(*self, Self::ITALIC);
   1199        self.0.to_float()
   1200    }
   1201 }
   1202 
   1203 impl ToCss for FontStyle {
   1204    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1205    where
   1206        W: fmt::Write,
   1207    {
   1208        if *self == Self::NORMAL {
   1209            return dest.write_str("normal");
   1210        }
   1211        if *self == Self::ITALIC {
   1212            return dest.write_str("italic");
   1213        }
   1214        dest.write_str("oblique")?;
   1215        if *self != Self::OBLIQUE {
   1216            // It's not the default oblique amount, so append the angle in degrees.
   1217            dest.write_char(' ')?;
   1218            Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
   1219        }
   1220        Ok(())
   1221    }
   1222 }
   1223 
   1224 impl ToAnimatedValue for FontStyle {
   1225    type AnimatedValue = generics::FontStyle<Angle>;
   1226 
   1227    #[inline]
   1228    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
   1229        if self == Self::ITALIC {
   1230            return generics::FontStyle::Italic;
   1231        }
   1232        generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
   1233    }
   1234 
   1235    #[inline]
   1236    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
   1237        match animated {
   1238            generics::FontStyle::Italic => Self::ITALIC,
   1239            generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
   1240        }
   1241    }
   1242 }
   1243 
   1244 /// font-stretch is a percentage relative to normal.
   1245 ///
   1246 /// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)
   1247 ///
   1248 /// We arbitrarily limit here to 1000%. (If that becomes a problem, we could
   1249 /// reduce the number of fractional bits and increase the limit.)
   1250 pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
   1251 
   1252 /// This is an alias which is useful mostly as a cbindgen / C++ inference
   1253 /// workaround.
   1254 pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
   1255 
   1256 /// A value for the font-stretch property per:
   1257 ///
   1258 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch
   1259 ///
   1260 /// cbindgen:derive-lt
   1261 /// cbindgen:derive-lte
   1262 /// cbindgen:derive-gt
   1263 /// cbindgen:derive-gte
   1264 #[derive(
   1265    Clone,
   1266    ComputeSquaredDistance,
   1267    Copy,
   1268    Debug,
   1269    MallocSizeOf,
   1270    PartialEq,
   1271    PartialOrd,
   1272    ToResolvedValue,
   1273    ToTyped,
   1274 )]
   1275 #[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
   1276 #[repr(C)]
   1277 pub struct FontStretch(pub FontStretchFixedPoint);
   1278 
   1279 impl FontStretch {
   1280    /// The fraction bits, as an easy-to-access-constant.
   1281    pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
   1282    /// 0.5 in our floating point representation.
   1283    pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
   1284 
   1285    /// The `ultra-condensed` keyword.
   1286    pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
   1287        value: 50 << Self::FRACTION_BITS,
   1288    });
   1289    /// The `extra-condensed` keyword.
   1290    pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
   1291        value: (62 << Self::FRACTION_BITS) + Self::HALF,
   1292    });
   1293    /// The `condensed` keyword.
   1294    pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
   1295        value: 75 << Self::FRACTION_BITS,
   1296    });
   1297    /// The `semi-condensed` keyword.
   1298    pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
   1299        value: (87 << Self::FRACTION_BITS) + Self::HALF,
   1300    });
   1301    /// The `normal` keyword.
   1302    pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
   1303        value: 100 << Self::FRACTION_BITS,
   1304    });
   1305    /// The `semi-expanded` keyword.
   1306    pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
   1307        value: (112 << Self::FRACTION_BITS) + Self::HALF,
   1308    });
   1309    /// The `expanded` keyword.
   1310    pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
   1311        value: 125 << Self::FRACTION_BITS,
   1312    });
   1313    /// The `extra-expanded` keyword.
   1314    pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
   1315        value: 150 << Self::FRACTION_BITS,
   1316    });
   1317    /// The `ultra-expanded` keyword.
   1318    pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
   1319        value: 200 << Self::FRACTION_BITS,
   1320    });
   1321 
   1322    /// 100%
   1323    pub fn hundred() -> Self {
   1324        Self::NORMAL
   1325    }
   1326 
   1327    /// Converts to a computed percentage.
   1328    #[inline]
   1329    pub fn to_percentage(&self) -> Percentage {
   1330        Percentage(self.0.to_float() / 100.0)
   1331    }
   1332 
   1333    /// Converts from a computed percentage value.
   1334    pub fn from_percentage(p: f32) -> Self {
   1335        Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
   1336    }
   1337 
   1338    /// Returns a relevant stretch value from a keyword.
   1339    /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
   1340    pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
   1341        use specified::FontStretchKeyword::*;
   1342        match kw {
   1343            UltraCondensed => Self::ULTRA_CONDENSED,
   1344            ExtraCondensed => Self::EXTRA_CONDENSED,
   1345            Condensed => Self::CONDENSED,
   1346            SemiCondensed => Self::SEMI_CONDENSED,
   1347            Normal => Self::NORMAL,
   1348            SemiExpanded => Self::SEMI_EXPANDED,
   1349            Expanded => Self::EXPANDED,
   1350            ExtraExpanded => Self::EXTRA_EXPANDED,
   1351            UltraExpanded => Self::ULTRA_EXPANDED,
   1352        }
   1353    }
   1354 
   1355    /// Returns the stretch keyword if we map to one of the relevant values.
   1356    pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
   1357        use specified::FontStretchKeyword::*;
   1358        // TODO: Can we use match here?
   1359        if *self == Self::ULTRA_CONDENSED {
   1360            return Some(UltraCondensed);
   1361        }
   1362        if *self == Self::EXTRA_CONDENSED {
   1363            return Some(ExtraCondensed);
   1364        }
   1365        if *self == Self::CONDENSED {
   1366            return Some(Condensed);
   1367        }
   1368        if *self == Self::SEMI_CONDENSED {
   1369            return Some(SemiCondensed);
   1370        }
   1371        if *self == Self::NORMAL {
   1372            return Some(Normal);
   1373        }
   1374        if *self == Self::SEMI_EXPANDED {
   1375            return Some(SemiExpanded);
   1376        }
   1377        if *self == Self::EXPANDED {
   1378            return Some(Expanded);
   1379        }
   1380        if *self == Self::EXTRA_EXPANDED {
   1381            return Some(ExtraExpanded);
   1382        }
   1383        if *self == Self::ULTRA_EXPANDED {
   1384            return Some(UltraExpanded);
   1385        }
   1386        None
   1387    }
   1388 }
   1389 
   1390 impl ToCss for FontStretch {
   1391    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1392    where
   1393        W: fmt::Write,
   1394    {
   1395        self.to_percentage().to_css(dest)
   1396    }
   1397 }
   1398 
   1399 impl ToAnimatedValue for FontStretch {
   1400    type AnimatedValue = Percentage;
   1401 
   1402    #[inline]
   1403    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
   1404        self.to_percentage()
   1405    }
   1406 
   1407    #[inline]
   1408    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
   1409        Self::from_percentage(animated.0)
   1410    }
   1411 }
   1412 
   1413 /// A computed value for the `line-height` property.
   1414 pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
   1415 
   1416 impl ToResolvedValue for LineHeight {
   1417    type ResolvedValue = Self;
   1418 
   1419    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
   1420        #[cfg(feature = "gecko")]
   1421        {
   1422            // Resolve <number> to an absolute <length> based on font size.
   1423            if matches!(self, Self::Normal | Self::MozBlockHeight) {
   1424                return self;
   1425            }
   1426            let wm = context.style.writing_mode;
   1427            Self::Length(
   1428                context
   1429                    .device
   1430                    .calc_line_height(
   1431                        context.style.get_font(),
   1432                        wm,
   1433                        Some(context.element_info.element),
   1434                    )
   1435                    .to_resolved_value(context),
   1436            )
   1437        }
   1438        #[cfg(feature = "servo")]
   1439        {
   1440            if let LineHeight::Number(num) = &self {
   1441                let size = context.style.get_font().clone_font_size().computed_size();
   1442                LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
   1443            } else {
   1444                self
   1445            }
   1446        }
   1447    }
   1448 
   1449    #[inline]
   1450    fn from_resolved_value(value: Self::ResolvedValue) -> Self {
   1451        value
   1452    }
   1453 }