Skip to main content

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