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 #[cfg(feature = "gecko")]
8 use crate::gecko_bindings::{bindings, structs};
9 use crate::parser::{Parse, ParserContext};
10 use crate::values::animated::ToAnimatedValue;
11 use crate::values::computed::{
12     Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, NonNegativePercentage,
13 };
14 use crate::values::computed::{Number, Percentage, ToComputedValue};
15 use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue};
16 use crate::values::generics::{font as generics, NonNegative};
17 use crate::values::specified::font::{
18     self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
19 };
20 use crate::values::specified::length::{FontBaseSize, NoCalcLength};
21 use crate::values::CSSFloat;
22 use crate::Atom;
23 use cssparser::{serialize_identifier, CssStringWriter, Parser};
24 #[cfg(feature = "gecko")]
25 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
26 use std::fmt::{self, Write};
27 use std::hash::{Hash, Hasher};
28 use style_traits::{CssWriter, ParseError, ToCss};
29 
30 pub use crate::values::computed::Length as MozScriptMinSize;
31 pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier};
32 pub use crate::values::specified::font::{XLang, XTextZoom};
33 pub use crate::values::specified::Integer as SpecifiedInteger;
34 
35 /// A value for the font-weight property per:
36 ///
37 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
38 ///
39 /// This is effectively just a `Number`.
40 #[derive(
41     Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue,
42 )]
43 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
44 pub struct FontWeight(pub Number);
45 
46 impl Hash for FontWeight {
hash<H: Hasher>(&self, hasher: &mut H)47     fn hash<H: Hasher>(&self, hasher: &mut H) {
48         hasher.write_u64((self.0 * 10000.).trunc() as u64);
49     }
50 }
51 
52 impl ToAnimatedValue for FontWeight {
53     type AnimatedValue = Number;
54 
55     #[inline]
to_animated_value(self) -> Self::AnimatedValue56     fn to_animated_value(self) -> Self::AnimatedValue {
57         self.0
58     }
59 
60     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self61     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
62         FontWeight(animated.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT))
63     }
64 }
65 
66 #[derive(
67     Animate,
68     Clone,
69     ComputeSquaredDistance,
70     Copy,
71     Debug,
72     MallocSizeOf,
73     PartialEq,
74     ToAnimatedZero,
75     ToCss,
76     ToResolvedValue,
77 )]
78 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
79 /// The computed value of font-size
80 pub struct FontSize {
81     /// The size.
82     pub size: NonNegativeLength,
83     /// If derived from a keyword, the keyword and additional transformations applied to it
84     #[css(skip)]
85     pub keyword_info: KeywordInfo,
86 }
87 
88 impl FontWeight {
89     /// Value for normal
normal() -> Self90     pub fn normal() -> Self {
91         FontWeight(400.)
92     }
93 
94     /// Value for bold
bold() -> Self95     pub fn bold() -> Self {
96         FontWeight(700.)
97     }
98 
99     /// Convert from an Gecko weight
100     #[cfg(feature = "gecko")]
from_gecko_weight(weight: structs::FontWeight) -> Self101     pub fn from_gecko_weight(weight: structs::FontWeight) -> Self {
102         // we allow a wider range of weights than is parseable
103         // because system fonts may provide custom values
104         let weight = unsafe { bindings::Gecko_FontWeight_ToFloat(weight) };
105         FontWeight(weight)
106     }
107 
108     /// Weither this weight is bold
is_bold(&self) -> bool109     pub fn is_bold(&self) -> bool {
110         self.0 > 500.
111     }
112 
113     /// Return the bolder weight.
114     ///
115     /// See the table in:
116     /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
bolder(self) -> Self117     pub fn bolder(self) -> Self {
118         if self.0 < 350. {
119             FontWeight(400.)
120         } else if self.0 < 550. {
121             FontWeight(700.)
122         } else {
123             FontWeight(self.0.max(900.))
124         }
125     }
126 
127     /// Return the lighter weight.
128     ///
129     /// See the table in:
130     /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
lighter(self) -> Self131     pub fn lighter(self) -> Self {
132         if self.0 < 550. {
133             FontWeight(self.0.min(100.))
134         } else if self.0 < 750. {
135             FontWeight(400.)
136         } else {
137             FontWeight(700.)
138         }
139     }
140 }
141 
142 impl FontSize {
143     /// The actual computed font size.
144     #[inline]
size(&self) -> Length145     pub fn size(&self) -> Length {
146         self.size.0
147     }
148 
149     #[inline]
150     /// Get default value of font size.
medium() -> Self151     pub fn medium() -> Self {
152         Self {
153             size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
154             keyword_info: KeywordInfo::medium(),
155         }
156     }
157 }
158 
159 impl ToAnimatedValue for FontSize {
160     type AnimatedValue = Length;
161 
162     #[inline]
to_animated_value(self) -> Self::AnimatedValue163     fn to_animated_value(self) -> Self::AnimatedValue {
164         self.size.0
165     }
166 
167     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self168     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
169         FontSize {
170             size: NonNegative(animated.clamp_to_non_negative()),
171             keyword_info: KeywordInfo::none(),
172         }
173     }
174 }
175 
176 #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
177 #[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
178 /// Specifies a prioritized list of font family names or generic family names.
179 #[repr(C)]
180 pub struct FontFamily {
181     /// The actual list of family names.
182     pub families: FontFamilyList,
183     /// Whether this font-family came from a specified system-font.
184     pub is_system_font: bool,
185     /// Whether this is the initial font-family that might react to language
186     /// changes.
187     pub is_initial: bool,
188 }
189 
190 macro_rules! static_font_family {
191     ($ident:ident, $family:expr) => {
192         lazy_static! {
193             static ref $ident: FontFamily = FontFamily {
194                 families: FontFamilyList {
195                     list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
196                 },
197                 is_system_font: false,
198                 is_initial: false,
199             };
200         }
201     };
202 }
203 
204 impl FontFamily {
205     #[inline]
206     /// Get default font family as `serif` which is a generic font-family
serif() -> Self207     pub fn serif() -> Self {
208         Self::generic(GenericFontFamily::Serif).clone()
209     }
210 
211     /// Returns the font family for `-moz-bullet-font`.
moz_bullet() -> &'static Self212     pub(crate) fn moz_bullet() -> &'static Self {
213         static_font_family!(
214             MOZ_BULLET,
215             SingleFontFamily::FamilyName(FamilyName {
216                 name: atom!("-moz-bullet-font"),
217                 syntax: FontFamilyNameSyntax::Identifiers,
218             })
219         );
220 
221         &*MOZ_BULLET
222     }
223 
224     /// Returns a font family for a single system font.
for_system_font(name: &str) -> Self225     pub fn for_system_font(name: &str) -> Self {
226         Self {
227             families: FontFamilyList {
228                 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
229                     FamilyName {
230                         name: Atom::from(name),
231                         syntax: FontFamilyNameSyntax::Identifiers,
232                     },
233                 ))),
234             },
235             is_system_font: true,
236             is_initial: false,
237         }
238     }
239 
240     /// Returns a generic font family.
generic(generic: GenericFontFamily) -> &'static Self241     pub fn generic(generic: GenericFontFamily) -> &'static Self {
242         macro_rules! generic_font_family {
243             ($ident:ident, $family:ident) => {
244                 static_font_family!(
245                     $ident,
246                     SingleFontFamily::Generic(GenericFontFamily::$family)
247                 )
248             };
249         }
250 
251         generic_font_family!(SERIF, Serif);
252         generic_font_family!(SANS_SERIF, SansSerif);
253         generic_font_family!(MONOSPACE, Monospace);
254         generic_font_family!(CURSIVE, Cursive);
255         generic_font_family!(FANTASY, Fantasy);
256         generic_font_family!(MOZ_EMOJI, MozEmoji);
257         generic_font_family!(SYSTEM_UI, SystemUi);
258 
259         match generic {
260             GenericFontFamily::None => {
261                 debug_assert!(false, "Bogus caller!");
262                 &*SERIF
263             },
264             GenericFontFamily::Serif => &*SERIF,
265             GenericFontFamily::SansSerif => &*SANS_SERIF,
266             GenericFontFamily::Monospace => &*MONOSPACE,
267             GenericFontFamily::Cursive => &*CURSIVE,
268             GenericFontFamily::Fantasy => &*FANTASY,
269             GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
270             GenericFontFamily::SystemUi => &*SYSTEM_UI,
271         }
272     }
273 }
274 
275 #[cfg(feature = "gecko")]
276 impl MallocSizeOf for FontFamily {
size_of(&self, ops: &mut MallocSizeOfOps) -> usize277     fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
278         use malloc_size_of::MallocUnconditionalSizeOf;
279         // SharedFontList objects are generally measured from the pointer stored
280         // in the specified value. So only count this if the SharedFontList is
281         // unshared.
282         let shared_font_list = &self.families.list;
283         if shared_font_list.is_unique() {
284             shared_font_list.unconditional_size_of(ops)
285         } else {
286             0
287         }
288     }
289 }
290 
291 impl ToCss for FontFamily {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,292     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
293     where
294         W: fmt::Write,
295     {
296         let mut iter = self.families.iter();
297         match iter.next() {
298             Some(f) => f.to_css(dest)?,
299             None => return Ok(()),
300         }
301         for family in iter {
302             dest.write_str(", ")?;
303             family.to_css(dest)?;
304         }
305         Ok(())
306     }
307 }
308 
309 /// The name of a font family of choice.
310 #[derive(
311     Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
312 )]
313 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
314 #[repr(C)]
315 pub struct FamilyName {
316     /// Name of the font family.
317     pub name: Atom,
318     /// Syntax of the font family.
319     pub syntax: FontFamilyNameSyntax,
320 }
321 
322 impl ToCss for FamilyName {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,323     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
324     where
325         W: fmt::Write,
326     {
327         match self.syntax {
328             FontFamilyNameSyntax::Quoted => {
329                 dest.write_char('"')?;
330                 write!(CssStringWriter::new(dest), "{}", self.name)?;
331                 dest.write_char('"')
332             },
333             FontFamilyNameSyntax::Identifiers => {
334                 let mut first = true;
335                 for ident in self.name.to_string().split(' ') {
336                     if first {
337                         first = false;
338                     } else {
339                         dest.write_char(' ')?;
340                     }
341                     debug_assert!(
342                         !ident.is_empty(),
343                         "Family name with leading, \
344                          trailing, or consecutive white spaces should \
345                          have been marked quoted by the parser"
346                     );
347                     serialize_identifier(ident, dest)?;
348                 }
349                 Ok(())
350             },
351         }
352     }
353 }
354 
355 #[derive(
356     Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
357 )]
358 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
359 /// Font family names must either be given quoted as strings,
360 /// or unquoted as a sequence of one or more identifiers.
361 #[repr(u8)]
362 pub enum FontFamilyNameSyntax {
363     /// The family name was specified in a quoted form, e.g. "Font Name"
364     /// or 'Font Name'.
365     Quoted,
366 
367     /// The family name was specified in an unquoted form as a sequence of
368     /// identifiers.
369     Identifiers,
370 }
371 
372 /// A set of faces that vary in weight, width or slope.
373 /// cbindgen:derive-mut-casts=true
374 #[derive(
375     Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
376 )]
377 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
378 #[repr(u8)]
379 pub enum SingleFontFamily {
380     /// The name of a font family of choice.
381     FamilyName(FamilyName),
382     /// Generic family name.
383     Generic(GenericFontFamily),
384 }
385 
system_ui_enabled(_: &ParserContext) -> bool386 fn system_ui_enabled(_: &ParserContext) -> bool {
387     static_prefs::pref!("layout.css.system-ui.enabled")
388 }
389 
390 /// A generic font-family name.
391 ///
392 /// The order here is important, if you change it make sure that
393 /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
394 /// sSingleGenerics are updated as well.
395 ///
396 /// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC
397 /// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /
398 /// bug 1726515.
399 #[derive(
400     Clone,
401     Copy,
402     Debug,
403     Eq,
404     Hash,
405     MallocSizeOf,
406     PartialEq,
407     Parse,
408     ToCss,
409     ToComputedValue,
410     ToResolvedValue,
411     ToShmem,
412 )]
413 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
414 #[repr(u32)]
415 #[allow(missing_docs)]
416 pub enum GenericFontFamily {
417     /// No generic family specified, only for internal usage.
418     ///
419     /// NOTE(emilio): Gecko code relies on this variant being zero.
420     #[css(skip)]
421     None = 0,
422     Serif,
423     SansSerif,
424     #[parse(aliases = "-moz-fixed")]
425     Monospace,
426     Cursive,
427     Fantasy,
428     #[parse(condition = "system_ui_enabled")]
429     SystemUi,
430     /// An internal value for emoji font selection.
431     #[css(skip)]
432     #[cfg(feature = "gecko")]
433     MozEmoji,
434 }
435 
436 impl GenericFontFamily {
437     /// When we disallow websites to override fonts, we ignore some generic
438     /// families that the website might specify, since they're not configured by
439     /// the user. See bug 789788 and bug 1730098.
valid_for_user_font_prioritization(self) -> bool440     pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
441         match self {
442             Self::None | Self::Fantasy | Self::Cursive | Self::SystemUi | Self::MozEmoji => false,
443 
444             Self::Serif | Self::SansSerif | Self::Monospace => true,
445         }
446     }
447 }
448 
449 impl Parse for SingleFontFamily {
450     /// Parse a font-family value.
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>451     fn parse<'i, 't>(
452         context: &ParserContext,
453         input: &mut Parser<'i, 't>,
454     ) -> Result<Self, ParseError<'i>> {
455         if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
456             return Ok(SingleFontFamily::FamilyName(FamilyName {
457                 name: Atom::from(&*value),
458                 syntax: FontFamilyNameSyntax::Quoted,
459             }));
460         }
461 
462         if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
463             return Ok(SingleFontFamily::Generic(generic));
464         }
465 
466         let first_ident = input.expect_ident_cloned()?;
467         let reserved = match_ignore_ascii_case! { &first_ident,
468             // https://drafts.csswg.org/css-fonts/#propdef-font-family
469             // "Font family names that happen to be the same as a keyword value
470             //  (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)
471             //  must be quoted to prevent confusion with the keywords with the same names.
472             //  The keywords ‘initial’ and ‘default’ are reserved for future use
473             //  and must also be quoted when used as font names.
474             //  UAs must not consider these keywords as matching the <family-name> type."
475             "inherit" | "initial" | "unset" | "revert" | "default" => true,
476             _ => false,
477         };
478 
479         let mut value = first_ident.as_ref().to_owned();
480         let mut serialize_quoted = value.contains(' ');
481 
482         // These keywords are not allowed by themselves.
483         // The only way this value can be valid with with another keyword.
484         if reserved {
485             let ident = input.expect_ident()?;
486             serialize_quoted = serialize_quoted || ident.contains(' ');
487             value.push(' ');
488             value.push_str(&ident);
489         }
490         while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
491             serialize_quoted = serialize_quoted || ident.contains(' ');
492             value.push(' ');
493             value.push_str(&ident);
494         }
495         let syntax = if serialize_quoted {
496             // For font family names which contains special white spaces, e.g.
497             // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
498             // as identifiers correctly. Just mark them quoted so we don't need
499             // to worry about them in serialization code.
500             FontFamilyNameSyntax::Quoted
501         } else {
502             FontFamilyNameSyntax::Identifiers
503         };
504         Ok(SingleFontFamily::FamilyName(FamilyName {
505             name: Atom::from(value),
506             syntax,
507         }))
508     }
509 }
510 
511 #[cfg(feature = "servo")]
512 impl SingleFontFamily {
513     /// Get the corresponding font-family with Atom
from_atom(input: Atom) -> SingleFontFamily514     pub fn from_atom(input: Atom) -> SingleFontFamily {
515         match input {
516             atom!("serif") => return SingleFontFamily::Generic(GenericFontFamily::Serif),
517             atom!("sans-serif") => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
518             atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
519             atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
520             atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
521             _ => {},
522         }
523 
524         match_ignore_ascii_case! { &input,
525             "serif" => return SingleFontFamily::Generic(GenericFontFamily::Serif),
526             "sans-serif" => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
527             "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
528             "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
529             "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
530             _ => {}
531         }
532 
533         // We don't know if it's quoted or not. So we set it to
534         // quoted by default.
535         SingleFontFamily::FamilyName(FamilyName {
536             name: input,
537             syntax: FontFamilyNameSyntax::Quoted,
538         })
539     }
540 }
541 
542 /// A list of font families.
543 #[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
544 #[repr(C)]
545 pub struct FontFamilyList {
546     /// The actual list of font families specified.
547     pub list: crate::ArcSlice<SingleFontFamily>,
548 }
549 
550 impl FontFamilyList {
551     /// Return iterator of SingleFontFamily
iter(&self) -> impl Iterator<Item = &SingleFontFamily>552     pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
553         self.list.iter()
554     }
555 
556     /// If there's a generic font family on the list which is suitable for user
557     /// font prioritization, then move it to the front of the list. Otherwise,
558     /// prepend the default generic.
prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily)559     pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
560         let index_of_first_generic = self.iter().position(|f| match *f {
561             SingleFontFamily::Generic(f) => f.valid_for_user_font_prioritization(),
562             _ => false,
563         });
564 
565         if let Some(0) = index_of_first_generic {
566             return; // Already first
567         }
568 
569         let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
570         let element_to_prepend = match index_of_first_generic {
571             Some(i) => new_list.remove(i),
572             None => SingleFontFamily::Generic(generic),
573         };
574 
575         new_list.insert(0, element_to_prepend);
576         self.list = crate::ArcSlice::from_iter(new_list.into_iter());
577     }
578 
579     /// Returns whether we need to prioritize user fonts.
needs_user_font_prioritization(&self) -> bool580     pub(crate) fn needs_user_font_prioritization(&self) -> bool {
581         self.iter().next().map_or(true, |f| match f {
582             SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
583             _ => true,
584         })
585     }
586 
587     /// Return the generic ID if it is a single generic font
single_generic(&self) -> Option<GenericFontFamily>588     pub fn single_generic(&self) -> Option<GenericFontFamily> {
589         let mut iter = self.iter();
590         if let Some(SingleFontFamily::Generic(f)) = iter.next() {
591             if iter.next().is_none() {
592                 return Some(*f);
593             }
594         }
595         None
596     }
597 }
598 
599 /// Preserve the readability of text when font fallback occurs
600 pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
601 
602 impl FontSizeAdjust {
603     #[inline]
604     /// Default value of font-size-adjust
none() -> Self605     pub fn none() -> Self {
606         FontSizeAdjust::None
607     }
608 }
609 
610 /// Use VariantAlternatesList as computed type of FontVariantAlternates
611 pub type FontVariantAlternates = specified::VariantAlternatesList;
612 
613 impl FontVariantAlternates {
614     /// Get initial value with VariantAlternatesList
615     #[inline]
get_initial_value() -> Self616     pub fn get_initial_value() -> Self {
617         Self::default()
618     }
619 }
620 
621 /// Use VariantEastAsian as computed type of FontVariantEastAsian
622 pub type FontVariantEastAsian = specified::VariantEastAsian;
623 
624 /// Use VariantLigatures as computed type of FontVariantLigatures
625 pub type FontVariantLigatures = specified::VariantLigatures;
626 
627 /// Use VariantNumeric as computed type of FontVariantNumeric
628 pub type FontVariantNumeric = specified::VariantNumeric;
629 
630 /// Use FontSettings as computed type of FontFeatureSettings.
631 pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
632 
633 /// The computed value for font-variation-settings.
634 pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
635 
636 /// font-language-override can only have a single three-letter
637 /// OpenType "language system" tag, so we should be able to compute
638 /// it and store it as a 32-bit integer
639 /// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
640 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToResolvedValue)]
641 #[repr(C)]
642 pub struct FontLanguageOverride(pub u32);
643 
644 impl FontLanguageOverride {
645     #[inline]
646     /// Get computed default value of `font-language-override` with 0
zero() -> FontLanguageOverride647     pub fn zero() -> FontLanguageOverride {
648         FontLanguageOverride(0)
649     }
650 
651     /// Returns this value as a `&str`, backed by `storage`.
652     #[inline]
to_str(self, storage: &mut [u8; 4]) -> &str653     pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
654         *storage = u32::to_be_bytes(self.0);
655         // Safe because we ensure it's ASCII during computing
656         let slice = if cfg!(debug_assertions) {
657             std::str::from_utf8(&storage[..]).unwrap()
658         } else {
659             unsafe { std::str::from_utf8_unchecked(&storage[..]) }
660         };
661         slice.trim_end()
662     }
663 
664     /// Parses a str, return `Self::zero()` if the input isn't a valid OpenType
665     /// "language system" tag.
666     #[inline]
from_str(lang: &str) -> Self667     pub fn from_str(lang: &str) -> Self {
668         if lang.is_empty() || lang.len() > 4 {
669             return Self::zero();
670         }
671         let mut bytes = [b' '; 4];
672         for (byte, lang_byte) in bytes.iter_mut().zip(lang.as_bytes()) {
673             if !lang_byte.is_ascii() {
674                 return Self::zero();
675             }
676             *byte = *lang_byte;
677         }
678         Self(u32::from_be_bytes(bytes))
679     }
680 
681     /// Unsafe because `Self::to_str` requires the value to represent a UTF-8
682     /// string.
683     #[inline]
from_u32(value: u32) -> Self684     pub unsafe fn from_u32(value: u32) -> Self {
685         Self(value)
686     }
687 }
688 
689 impl ToCss for FontLanguageOverride {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,690     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
691     where
692         W: fmt::Write,
693     {
694         if self.0 == 0 {
695             return dest.write_str("normal");
696         }
697         self.to_str(&mut [0; 4]).to_css(dest)
698     }
699 }
700 
701 // FIXME(emilio): Make Gecko use the cbindgen'd fontLanguageOverride, then
702 // remove this.
703 #[cfg(feature = "gecko")]
704 impl From<u32> for FontLanguageOverride {
from(v: u32) -> Self705     fn from(v: u32) -> Self {
706         unsafe { Self::from_u32(v) }
707     }
708 }
709 
710 #[cfg(feature = "gecko")]
711 impl From<FontLanguageOverride> for u32 {
from(v: FontLanguageOverride) -> u32712     fn from(v: FontLanguageOverride) -> u32 {
713         v.0
714     }
715 }
716 
717 impl ToComputedValue for specified::MozScriptMinSize {
718     type ComputedValue = MozScriptMinSize;
719 
to_computed_value(&self, cx: &Context) -> MozScriptMinSize720     fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
721         // this value is used in the computation of font-size, so
722         // we use the parent size
723         let base_size = FontBaseSize::InheritedStyle;
724         match self.0 {
725             NoCalcLength::FontRelative(value) => value.to_computed_value(cx, base_size),
726             NoCalcLength::ServoCharacterWidth(value) => {
727                 value.to_computed_value(base_size.resolve(cx))
728             },
729             ref l => l.to_computed_value(cx),
730         }
731     }
732 
from_computed_value(other: &MozScriptMinSize) -> Self733     fn from_computed_value(other: &MozScriptMinSize) -> Self {
734         specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
735     }
736 }
737 
738 /// The computed value of the math-depth property.
739 pub type MathDepth = i8;
740 
741 #[cfg(feature = "gecko")]
742 impl ToComputedValue for specified::MathDepth {
743     type ComputedValue = MathDepth;
744 
to_computed_value(&self, cx: &Context) -> i8745     fn to_computed_value(&self, cx: &Context) -> i8 {
746         use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
747         use std::{cmp, i8};
748 
749         let int = match *self {
750             specified::MathDepth::AutoAdd => {
751                 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
752                 let style = cx.builder.get_parent_font().clone_math_style();
753                 if style == MathStyleValue::Compact {
754                     parent.saturating_add(1)
755                 } else {
756                     parent
757                 }
758             },
759             specified::MathDepth::Add(rel) => {
760                 let parent = cx.builder.get_parent_font().clone_math_depth();
761                 (parent as i32).saturating_add(rel.to_computed_value(cx))
762             },
763             specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
764         };
765         cmp::min(int, i8::MAX as i32) as i8
766     }
767 
from_computed_value(other: &i8) -> Self768     fn from_computed_value(other: &i8) -> Self {
769         let computed_value = *other as i32;
770         specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
771     }
772 }
773 
774 /// A wrapper over an `Angle`, that handles clamping to the appropriate range
775 /// for `font-style` animation.
776 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
777 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
778 pub struct FontStyleAngle(pub Angle);
779 
780 impl ToAnimatedValue for FontStyleAngle {
781     type AnimatedValue = Angle;
782 
783     #[inline]
to_animated_value(self) -> Self::AnimatedValue784     fn to_animated_value(self) -> Self::AnimatedValue {
785         self.0
786     }
787 
788     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self789     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
790         FontStyleAngle(Angle::from_degrees(
791             animated
792                 .degrees()
793                 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
794                 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES),
795         ))
796     }
797 }
798 
799 impl Hash for FontStyleAngle {
hash<H: Hasher>(&self, hasher: &mut H)800     fn hash<H: Hasher>(&self, hasher: &mut H) {
801         hasher.write_u64((self.0.degrees() * 10000.).trunc() as u64);
802     }
803 }
804 
805 /// The computed value of `font-style`.
806 ///
807 /// FIXME(emilio): Angle should be a custom type to handle clamping during
808 /// animation.
809 pub type FontStyle = generics::FontStyle<FontStyleAngle>;
810 
811 impl FontStyle {
812     /// The `normal` value.
813     #[inline]
normal() -> Self814     pub fn normal() -> Self {
815         generics::FontStyle::Normal
816     }
817 
818     /// The default angle for font-style: oblique. This is 20deg per spec:
819     ///
820     /// https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle
821     #[inline]
default_angle() -> FontStyleAngle822     pub fn default_angle() -> FontStyleAngle {
823         FontStyleAngle(Angle::from_degrees(
824             specified::DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES,
825         ))
826     }
827 
828     /// Get the font style from Gecko's nsFont struct.
829     #[cfg(feature = "gecko")]
from_gecko(style: structs::FontSlantStyle) -> Self830     pub fn from_gecko(style: structs::FontSlantStyle) -> Self {
831         let mut angle = 0.;
832         let mut italic = false;
833         let mut normal = false;
834         unsafe {
835             bindings::Gecko_FontSlantStyle_Get(style, &mut normal, &mut italic, &mut angle);
836         }
837         if normal {
838             return generics::FontStyle::Normal;
839         }
840         if italic {
841             return generics::FontStyle::Italic;
842         }
843         generics::FontStyle::Oblique(FontStyleAngle(Angle::from_degrees(angle)))
844     }
845 }
846 
847 impl ToCss for FontStyle {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write,848     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
849     where
850         W: fmt::Write,
851     {
852         match *self {
853             generics::FontStyle::Normal => dest.write_str("normal"),
854             generics::FontStyle::Italic => dest.write_str("italic"),
855             generics::FontStyle::Oblique(ref angle) => {
856                 dest.write_str("oblique")?;
857                 // Use `degrees` instead of just comparing Angle because
858                 // `degrees` can return slightly different values due to
859                 // floating point conversions.
860                 if angle.0.degrees() != Self::default_angle().0.degrees() {
861                     dest.write_char(' ')?;
862                     angle.to_css(dest)?;
863                 }
864                 Ok(())
865             },
866         }
867     }
868 }
869 
870 /// A value for the font-stretch property per:
871 ///
872 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch
873 #[derive(
874     Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue,
875 )]
876 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
877 pub struct FontStretch(pub NonNegativePercentage);
878 
879 impl FontStretch {
880     /// 100%
hundred() -> Self881     pub fn hundred() -> Self {
882         FontStretch(NonNegativePercentage::hundred())
883     }
884 
885     /// The float value of the percentage
886     #[inline]
value(&self) -> CSSFloat887     pub fn value(&self) -> CSSFloat {
888         ((self.0).0).0
889     }
890 }
891 
892 impl ToAnimatedValue for FontStretch {
893     type AnimatedValue = Percentage;
894 
895     #[inline]
to_animated_value(self) -> Self::AnimatedValue896     fn to_animated_value(self) -> Self::AnimatedValue {
897         self.0.to_animated_value()
898     }
899 
900     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self901     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
902         FontStretch(NonNegativePercentage::from_animated_value(animated))
903     }
904 }
905 
906 impl Hash for FontStretch {
hash<H: Hasher>(&self, hasher: &mut H)907     fn hash<H: Hasher>(&self, hasher: &mut H) {
908         hasher.write_u64((self.value() * 10000.).trunc() as u64);
909     }
910 }
911