1use crate::context::QuirksMode;
8use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
11use crate::values::computed::Percentage as ComputedPercentage;
12use crate::values::computed::{font as computed, Length, NonNegativeLength};
13use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
14use crate::values::generics::font::{
15 self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
16};
17use crate::values::generics::NonNegative;
18use crate::values::specified::length::{FontBaseSize, LineHeightBase, PX_PER_PT};
19use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
20use crate::values::specified::{
21 FontRelativeLength, NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber,
22 NonNegativePercentage, Number,
23};
24use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
25use crate::Atom;
26use cssparser::{match_ignore_ascii_case, Parser, Token};
27#[cfg(feature = "gecko")]
28use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
29use std::fmt::{self, Write};
30use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
31use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
32
33macro_rules! system_font_methods {
35 ($ty:ident, $field:ident) => {
36 system_font_methods!($ty);
37
38 fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
39 debug_assert!(matches!(*self, $ty::System(..)));
40 #[cfg(feature = "gecko")]
41 {
42 _context.cached_system_font.as_ref().unwrap().$field.clone()
43 }
44 #[cfg(feature = "servo")]
45 {
46 unreachable!()
47 }
48 }
49 };
50
51 ($ty:ident) => {
52 pub fn system_font(f: SystemFont) -> Self {
54 $ty::System(f)
55 }
56
57 pub fn get_system(&self) -> Option<SystemFont> {
59 if let $ty::System(s) = *self {
60 Some(s)
61 } else {
62 None
63 }
64 }
65 };
66}
67
68#[repr(u8)]
70#[derive(
71 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
72)]
73#[allow(missing_docs)]
74#[cfg(feature = "gecko")]
75pub enum SystemFont {
76 Caption,
78 Icon,
80 Menu,
82 MessageBox,
84 SmallCaption,
86 StatusBar,
88 #[parse(condition = "ParserContext::chrome_rules_enabled")]
90 MozPullDownMenu,
91 #[parse(condition = "ParserContext::chrome_rules_enabled")]
93 MozButton,
94 #[parse(condition = "ParserContext::chrome_rules_enabled")]
96 MozList,
97 #[parse(condition = "ParserContext::chrome_rules_enabled")]
99 MozField,
100 #[css(skip)]
101 End, }
103
104#[derive(
109 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
110)]
111#[allow(missing_docs)]
112#[cfg(feature = "servo")]
113pub enum SystemFont {}
115
116#[allow(missing_docs)]
117#[cfg(feature = "servo")]
118impl SystemFont {
119 pub fn parse(_: &mut Parser) -> Result<Self, ()> {
120 Err(())
121 }
122}
123
124const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
125const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
126
127pub const MIN_FONT_WEIGHT: f32 = 1.;
131
132pub const MAX_FONT_WEIGHT: f32 = 1000.;
136
137#[derive(
141 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
142)]
143pub enum FontWeight {
144 Absolute(AbsoluteFontWeight),
146 Bolder,
148 Lighter,
150 #[css(skip)]
152 System(SystemFont),
153}
154
155impl FontWeight {
156 system_font_methods!(FontWeight, font_weight);
157
158 #[inline]
160 pub fn normal() -> Self {
161 FontWeight::Absolute(AbsoluteFontWeight::Normal)
162 }
163
164 pub fn from_gecko_keyword(kw: u32) -> Self {
166 debug_assert!(kw % 100 == 0);
167 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
168 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
169 }
170}
171
172impl ToComputedValue for FontWeight {
173 type ComputedValue = computed::FontWeight;
174
175 #[inline]
176 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
177 match *self {
178 FontWeight::Absolute(ref abs) => abs.compute(),
179 FontWeight::Bolder => context
180 .builder
181 .get_parent_font()
182 .clone_font_weight()
183 .bolder(),
184 FontWeight::Lighter => context
185 .builder
186 .get_parent_font()
187 .clone_font_weight()
188 .lighter(),
189 FontWeight::System(_) => self.compute_system(context),
190 }
191 }
192
193 #[inline]
194 fn from_computed_value(computed: &computed::FontWeight) -> Self {
195 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
196 &computed.value(),
197 )))
198 }
199}
200
201#[derive(
205 Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
206)]
207pub enum AbsoluteFontWeight {
208 Weight(Number),
212 Normal,
214 Bold,
216}
217
218impl AbsoluteFontWeight {
219 pub fn compute(&self) -> computed::FontWeight {
221 match *self {
222 AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),
223 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
224 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
225 }
226 }
227}
228
229impl Parse for AbsoluteFontWeight {
230 fn parse<'i, 't>(
231 context: &ParserContext,
232 input: &mut Parser<'i, 't>,
233 ) -> Result<Self, ParseError<'i>> {
234 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
235 if !number.was_calc()
239 && (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
240 {
241 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
242 }
243 return Ok(AbsoluteFontWeight::Weight(number));
244 }
245
246 Ok(try_match_ident_ignore_ascii_case! { input,
247 "normal" => AbsoluteFontWeight::Normal,
248 "bold" => AbsoluteFontWeight::Bold,
249 })
250 }
251}
252
253pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
256
257impl ToCss for SpecifiedFontStyle {
258 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
259 where
260 W: Write,
261 {
262 match *self {
263 generics::FontStyle::Italic => dest.write_str("italic"),
264 generics::FontStyle::Oblique(ref angle) => {
265 if *angle == Angle::zero() {
268 dest.write_str("normal")?;
269 } else {
270 dest.write_str("oblique")?;
271 if *angle != Self::default_angle() {
272 dest.write_char(' ')?;
273 angle.to_css(dest)?;
274 }
275 }
276 Ok(())
277 },
278 }
279 }
280}
281
282impl Parse for SpecifiedFontStyle {
283 fn parse<'i, 't>(
284 context: &ParserContext,
285 input: &mut Parser<'i, 't>,
286 ) -> Result<Self, ParseError<'i>> {
287 Ok(try_match_ident_ignore_ascii_case! { input,
288 "normal" => generics::FontStyle::normal(),
289 "italic" => generics::FontStyle::Italic,
290 "oblique" => {
291 let angle = input.try_parse(|input| Self::parse_angle(context, input))
292 .unwrap_or_else(|_| Self::default_angle());
293
294 generics::FontStyle::Oblique(angle)
295 },
296 })
297 }
298}
299
300impl ToComputedValue for SpecifiedFontStyle {
301 type ComputedValue = computed::FontStyle;
302
303 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
304 match *self {
305 Self::Italic => computed::FontStyle::ITALIC,
306 Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),
307 }
308 }
309
310 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
311 if *computed == computed::FontStyle::ITALIC {
312 return Self::Italic;
313 }
314 let degrees = computed.oblique_degrees();
315 generics::FontStyle::Oblique(Angle::from_degrees(degrees, false))
316 }
317}
318
319pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
326
327pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
329
330impl SpecifiedFontStyle {
331 pub fn compute_angle_degrees(angle: &Angle) -> f32 {
333 angle
334 .degrees()
335 .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
336 .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
337 }
338
339 pub fn parse_angle<'i, 't>(
341 context: &ParserContext,
342 input: &mut Parser<'i, 't>,
343 ) -> Result<Angle, ParseError<'i>> {
344 let angle = Angle::parse(context, input)?;
345 if angle.was_calc() {
346 return Ok(angle);
347 }
348
349 let degrees = angle.degrees();
350 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES
351 || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
352 {
353 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
354 }
355 return Ok(angle);
356 }
357
358 pub fn default_angle() -> Angle {
360 Angle::from_degrees(
361 computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,
362 false,
363 )
364 }
365}
366
367#[derive(
369 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
370)]
371#[allow(missing_docs)]
372#[typed(todo_derive_fields)]
373pub enum FontStyle {
374 Specified(SpecifiedFontStyle),
375 #[css(skip)]
376 System(SystemFont),
377}
378
379impl FontStyle {
380 #[inline]
382 pub fn normal() -> Self {
383 FontStyle::Specified(generics::FontStyle::normal())
384 }
385
386 system_font_methods!(FontStyle, font_style);
387}
388
389impl ToComputedValue for FontStyle {
390 type ComputedValue = computed::FontStyle;
391
392 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
393 match *self {
394 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
395 FontStyle::System(..) => self.compute_system(context),
396 }
397 }
398
399 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
400 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
401 }
402}
403
404#[allow(missing_docs)]
408#[derive(
409 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
410)]
411pub enum FontStretch {
412 Stretch(NonNegativePercentage),
413 Keyword(FontStretchKeyword),
414 #[css(skip)]
415 System(SystemFont),
416}
417
418#[derive(
420 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
421)]
422#[allow(missing_docs)]
423pub enum FontStretchKeyword {
424 Normal,
425 Condensed,
426 UltraCondensed,
427 ExtraCondensed,
428 SemiCondensed,
429 SemiExpanded,
430 Expanded,
431 ExtraExpanded,
432 UltraExpanded,
433}
434
435impl FontStretchKeyword {
436 pub fn compute(&self) -> computed::FontStretch {
438 computed::FontStretch::from_keyword(*self)
439 }
440
441 pub fn from_percentage(p: f32) -> Option<Self> {
444 computed::FontStretch::from_percentage(p).as_keyword()
445 }
446}
447
448impl FontStretch {
449 pub fn normal() -> Self {
451 FontStretch::Keyword(FontStretchKeyword::Normal)
452 }
453
454 system_font_methods!(FontStretch, font_stretch);
455}
456
457impl ToComputedValue for FontStretch {
458 type ComputedValue = computed::FontStretch;
459
460 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
461 match *self {
462 FontStretch::Stretch(ref percentage) => {
463 let percentage = percentage.to_computed_value(context).0;
464 computed::FontStretch::from_percentage(percentage.0)
465 },
466 FontStretch::Keyword(ref kw) => kw.compute(),
467 FontStretch::System(_) => self.compute_system(context),
468 }
469 }
470
471 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
472 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
473 computed.to_percentage(),
474 )))
475 }
476}
477
478#[derive(
480 Animate,
481 Clone,
482 ComputeSquaredDistance,
483 Copy,
484 Debug,
485 MallocSizeOf,
486 Parse,
487 PartialEq,
488 SpecifiedValueInfo,
489 ToAnimatedValue,
490 ToAnimatedZero,
491 ToComputedValue,
492 ToCss,
493 ToResolvedValue,
494 ToShmem,
495 Serialize,
496 Deserialize,
497 ToTyped,
498)]
499#[allow(missing_docs)]
500#[repr(u8)]
501pub enum FontSizeKeyword {
502 #[css(keyword = "xx-small")]
503 XXSmall,
504 XSmall,
505 Small,
506 Medium,
507 Large,
508 XLarge,
509 #[css(keyword = "xx-large")]
510 XXLarge,
511 #[css(keyword = "xxx-large")]
512 XXXLarge,
513 #[cfg(feature = "gecko")]
516 Math,
517 #[css(skip)]
518 None,
519}
520
521impl FontSizeKeyword {
522 #[inline]
524 pub fn html_size(self) -> u8 {
525 self as u8
526 }
527
528 #[cfg(feature = "gecko")]
530 pub fn is_math(self) -> bool {
531 matches!(self, Self::Math)
532 }
533
534 #[cfg(feature = "servo")]
536 pub fn is_math(self) -> bool {
537 false
538 }
539}
540
541impl Default for FontSizeKeyword {
542 fn default() -> Self {
543 FontSizeKeyword::Medium
544 }
545}
546
547#[derive(
548 Animate,
549 Clone,
550 ComputeSquaredDistance,
551 Copy,
552 Debug,
553 MallocSizeOf,
554 PartialEq,
555 ToAnimatedValue,
556 ToAnimatedZero,
557 ToComputedValue,
558 ToCss,
559 ToResolvedValue,
560 ToShmem,
561 ToTyped,
562)]
563#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
564pub struct KeywordInfo {
566 pub kw: FontSizeKeyword,
568 #[css(skip)]
570 pub factor: f32,
571 #[css(skip)]
574 pub offset: CSSPixelLength,
575}
576
577impl KeywordInfo {
578 pub fn medium() -> Self {
580 Self::new(FontSizeKeyword::Medium)
581 }
582
583 pub fn none() -> Self {
585 Self::new(FontSizeKeyword::None)
586 }
587
588 fn new(kw: FontSizeKeyword) -> Self {
589 KeywordInfo {
590 kw,
591 factor: 1.,
592 offset: CSSPixelLength::new(0.),
593 }
594 }
595
596 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
599 debug_assert_ne!(self.kw, FontSizeKeyword::None);
600 #[cfg(feature = "gecko")]
601 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
602 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
603 let zoom_factor = context.style().effective_zoom.value();
604 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
605 + context.maybe_zoom_text(self.offset)
606 }
607
608 fn compose(self, factor: f32) -> Self {
611 if self.kw == FontSizeKeyword::None {
612 return self;
613 }
614 KeywordInfo {
615 kw: self.kw,
616 factor: self.factor * factor,
617 offset: self.offset * factor,
618 }
619 }
620}
621
622impl SpecifiedValueInfo for KeywordInfo {
623 fn collect_completion_keywords(f: KeywordsCollectFn) {
624 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
625 }
626}
627
628#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
629pub enum FontSize {
631 Length(LengthPercentage),
633 Keyword(KeywordInfo),
644 Smaller,
646 Larger,
648 #[css(skip)]
650 System(SystemFont),
651}
652
653#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem, ToTyped)]
655#[cfg_attr(feature = "servo", derive(Hash))]
656#[typed(todo_derive_fields)]
657pub enum FontFamily {
658 #[css(comma)]
660 Values(#[css(iterable)] FontFamilyList),
661 #[css(skip)]
663 System(SystemFont),
664}
665
666impl FontFamily {
667 system_font_methods!(FontFamily, font_family);
668}
669
670impl ToComputedValue for FontFamily {
671 type ComputedValue = computed::FontFamily;
672
673 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
674 match *self {
675 FontFamily::Values(ref list) => computed::FontFamily {
676 families: list.clone(),
677 is_system_font: false,
678 is_initial: false,
679 },
680 FontFamily::System(_) => self.compute_system(context),
681 }
682 }
683
684 fn from_computed_value(other: &computed::FontFamily) -> Self {
685 FontFamily::Values(other.families.clone())
686 }
687}
688
689#[cfg(feature = "gecko")]
690impl MallocSizeOf for FontFamily {
691 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
692 match *self {
693 FontFamily::Values(ref v) => {
694 v.list.unconditional_size_of(ops)
697 },
698 FontFamily::System(_) => 0,
699 }
700 }
701}
702
703impl Parse for FontFamily {
704 fn parse<'i, 't>(
708 context: &ParserContext,
709 input: &mut Parser<'i, 't>,
710 ) -> Result<FontFamily, ParseError<'i>> {
711 let values =
712 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
713 Ok(FontFamily::Values(FontFamilyList {
714 list: crate::ArcSlice::from_iter(values.into_iter()),
715 }))
716 }
717}
718
719impl SpecifiedValueInfo for FontFamily {}
720
721impl Parse for FamilyName {
724 fn parse<'i, 't>(
725 context: &ParserContext,
726 input: &mut Parser<'i, 't>,
727 ) -> Result<Self, ParseError<'i>> {
728 match SingleFontFamily::parse(context, input) {
729 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
730 Ok(SingleFontFamily::Generic(_)) => {
731 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
732 },
733 Err(e) => Err(e),
734 }
735 }
736}
737
738#[derive(
741 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
742)]
743pub enum FontSizeAdjustFactor {
744 Number(NonNegativeNumber),
746 FromFont,
748}
749
750pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
755
756impl Parse for FontSizeAdjust {
757 fn parse<'i, 't>(
758 context: &ParserContext,
759 input: &mut Parser<'i, 't>,
760 ) -> Result<Self, ParseError<'i>> {
761 let location = input.current_source_location();
762 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
764 return Ok(Self::ExHeight(factor));
765 }
766
767 let ident = input.expect_ident()?;
768 let basis = match_ignore_ascii_case! { &ident,
769 "none" => return Ok(Self::None),
770 "ex-height" => Self::ExHeight,
772 "cap-height" => Self::CapHeight,
773 "ch-width" => Self::ChWidth,
774 "ic-width" => Self::IcWidth,
775 "ic-height" => Self::IcHeight,
776 _ => return Err(location.new_custom_error(
778 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
779 )),
780 };
781
782 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
783 }
784}
785
786const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
789
790pub const FONT_MEDIUM_PX: f32 = 16.0;
792pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
794pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
797pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
800pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
803pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
806
807impl FontSizeKeyword {
808 #[inline]
809 fn to_length(&self, cx: &Context) -> NonNegativeLength {
810 let font = cx.style().get_font();
811
812 #[cfg(feature = "servo")]
813 let family = &font.font_family.families;
814 #[cfg(feature = "gecko")]
815 let family = &font.mFont.family.families;
816
817 let generic = family
818 .single_generic()
819 .unwrap_or(computed::GenericFontFamily::None);
820
821 #[cfg(feature = "gecko")]
822 let base_size = unsafe {
823 Atom::with(font.mLanguage.mRawPtr, |language| {
824 cx.device().base_size_for_generic(language, generic)
825 })
826 };
827 #[cfg(feature = "servo")]
828 let base_size = cx.device().base_size_for_generic(generic);
829
830 self.to_length_without_context(cx.quirks_mode, base_size)
831 }
832
833 #[inline]
835 pub fn to_length_without_context(
836 &self,
837 quirks_mode: QuirksMode,
838 base_size: Length,
839 ) -> NonNegativeLength {
840 #[cfg(feature = "gecko")]
841 debug_assert_ne!(*self, FontSizeKeyword::Math);
842 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
855 [9, 9, 9, 9, 11, 14, 18, 27],
856 [9, 9, 9, 10, 12, 15, 20, 30],
857 [9, 9, 10, 11, 13, 17, 22, 33],
858 [9, 9, 10, 12, 14, 18, 24, 36],
859 [9, 10, 12, 13, 16, 20, 26, 39],
860 [9, 10, 12, 14, 17, 21, 28, 42],
861 [9, 10, 13, 15, 18, 23, 30, 45],
862 [9, 10, 13, 16, 18, 24, 32, 48],
863 ];
864
865 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
874 [9, 9, 9, 9, 11, 14, 18, 28],
875 [9, 9, 9, 10, 12, 15, 20, 31],
876 [9, 9, 9, 11, 13, 17, 22, 34],
877 [9, 9, 10, 12, 14, 18, 24, 37],
878 [9, 9, 10, 13, 16, 20, 26, 40],
879 [9, 9, 11, 14, 17, 21, 28, 42],
880 [9, 10, 12, 15, 17, 23, 30, 45],
881 [9, 10, 13, 16, 18, 24, 32, 48],
882 ];
883
884 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
885 let base_size_px = base_size.px().round() as i32;
886 let html_size = self.html_size() as usize;
887 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
888 let mapping = if quirks_mode == QuirksMode::Quirks {
889 QUIRKS_FONT_SIZE_MAPPING
890 } else {
891 FONT_SIZE_MAPPING
892 };
893 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
894 } else {
895 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
896 })
897 }
898}
899
900impl FontSize {
901 pub fn from_html_size(size: u8) -> Self {
903 FontSize::Keyword(KeywordInfo::new(match size {
904 0 | 1 => FontSizeKeyword::XSmall,
906 2 => FontSizeKeyword::Small,
907 3 => FontSizeKeyword::Medium,
908 4 => FontSizeKeyword::Large,
909 5 => FontSizeKeyword::XLarge,
910 6 => FontSizeKeyword::XXLarge,
911 _ => FontSizeKeyword::XXXLarge,
913 }))
914 }
915
916 pub fn to_computed_value_against(
918 &self,
919 context: &Context,
920 base_size: FontBaseSize,
921 line_height_base: LineHeightBase,
922 ) -> computed::FontSize {
923 let compose_keyword = |factor| {
924 context
925 .style()
926 .get_parent_font()
927 .clone_font_size()
928 .keyword_info
929 .compose(factor)
930 };
931 let mut info = KeywordInfo::none();
932 let size = match *self {
933 FontSize::Length(LengthPercentage::Length(ref l)) => {
934 if let NoCalcLength::FontRelative(ref value) = *l {
935 if let FontRelativeLength::Em(em) = *value {
936 info = compose_keyword(em);
939 }
940 }
941 let result =
942 l.to_computed_value_with_base_size(context, base_size, line_height_base);
943 if l.should_zoom_text() {
944 context.maybe_zoom_text(result)
945 } else {
946 result
947 }
948 },
949 FontSize::Length(LengthPercentage::Percentage(pc)) => {
950 info = compose_keyword(pc.0);
953 (base_size.resolve(context).computed_size() * pc.0).normalized()
954 },
955 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
956 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
957 calc.resolve(base_size.resolve(context).computed_size())
958 },
959 FontSize::Keyword(i) => {
960 if i.kw.is_math() {
961 info = compose_keyword(1.);
963 info.kw = i.kw;
966 FontRelativeLength::Em(1.).to_computed_value(
967 context,
968 base_size,
969 line_height_base,
970 )
971 } else {
972 info = i;
974 i.to_computed_value(context).clamp_to_non_negative()
975 }
976 },
977 FontSize::Smaller => {
978 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
979 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
980 context,
981 base_size,
982 line_height_base,
983 )
984 },
985 FontSize::Larger => {
986 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
987 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
988 context,
989 base_size,
990 line_height_base,
991 )
992 },
993
994 FontSize::System(_) => {
995 #[cfg(feature = "servo")]
996 {
997 unreachable!()
998 }
999 #[cfg(feature = "gecko")]
1000 {
1001 context
1002 .cached_system_font
1003 .as_ref()
1004 .unwrap()
1005 .font_size
1006 .computed_size()
1007 }
1008 },
1009 };
1010 computed::FontSize {
1011 computed_size: NonNegative(size),
1012 used_size: NonNegative(size),
1013 keyword_info: info,
1014 }
1015 }
1016}
1017
1018impl ToComputedValue for FontSize {
1019 type ComputedValue = computed::FontSize;
1020
1021 #[inline]
1022 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1023 self.to_computed_value_against(
1024 context,
1025 FontBaseSize::InheritedStyle,
1026 LineHeightBase::InheritedStyle,
1027 )
1028 }
1029
1030 #[inline]
1031 fn from_computed_value(computed: &computed::FontSize) -> Self {
1032 FontSize::Length(LengthPercentage::Length(
1033 ToComputedValue::from_computed_value(&computed.computed_size()),
1034 ))
1035 }
1036}
1037
1038impl FontSize {
1039 system_font_methods!(FontSize);
1040
1041 #[inline]
1043 pub fn medium() -> Self {
1044 FontSize::Keyword(KeywordInfo::medium())
1045 }
1046
1047 pub fn parse_quirky<'i, 't>(
1049 context: &ParserContext,
1050 input: &mut Parser<'i, 't>,
1051 allow_quirks: AllowQuirks,
1052 ) -> Result<FontSize, ParseError<'i>> {
1053 if let Ok(lp) = input
1054 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1055 {
1056 return Ok(FontSize::Length(lp));
1057 }
1058
1059 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1060 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1061 }
1062
1063 try_match_ident_ignore_ascii_case! { input,
1064 "smaller" => Ok(FontSize::Smaller),
1065 "larger" => Ok(FontSize::Larger),
1066 }
1067 }
1068}
1069
1070impl Parse for FontSize {
1071 fn parse<'i, 't>(
1073 context: &ParserContext,
1074 input: &mut Parser<'i, 't>,
1075 ) -> Result<FontSize, ParseError<'i>> {
1076 FontSize::parse_quirky(context, input, AllowQuirks::No)
1077 }
1078}
1079
1080bitflags! {
1081 #[derive(Clone, Copy)]
1082 struct VariantAlternatesParsingFlags: u8 {
1084 const NORMAL = 0;
1086 const HISTORICAL_FORMS = 0x01;
1088 const STYLISTIC = 0x02;
1090 const STYLESET = 0x04;
1092 const CHARACTER_VARIANT = 0x08;
1094 const SWASH = 0x10;
1096 const ORNAMENTS = 0x20;
1098 const ANNOTATION = 0x40;
1100 }
1101}
1102
1103#[derive(
1104 Clone,
1105 Debug,
1106 MallocSizeOf,
1107 PartialEq,
1108 SpecifiedValueInfo,
1109 ToCss,
1110 ToComputedValue,
1111 ToResolvedValue,
1112 ToShmem,
1113)]
1114#[repr(C, u8)]
1115pub enum VariantAlternates {
1117 #[css(function)]
1119 Stylistic(CustomIdent),
1120 #[css(comma, function)]
1122 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1123 #[css(comma, function)]
1125 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1126 #[css(function)]
1128 Swash(CustomIdent),
1129 #[css(function)]
1131 Ornaments(CustomIdent),
1132 #[css(function)]
1134 Annotation(CustomIdent),
1135 HistoricalForms,
1137}
1138
1139#[derive(
1140 Clone,
1141 Debug,
1142 Default,
1143 MallocSizeOf,
1144 PartialEq,
1145 SpecifiedValueInfo,
1146 ToComputedValue,
1147 ToCss,
1148 ToResolvedValue,
1149 ToShmem,
1150 ToTyped,
1151)]
1152#[repr(transparent)]
1153#[typed(todo_derive_fields)]
1154pub struct FontVariantAlternates(
1156 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1157);
1158
1159impl FontVariantAlternates {
1160 pub fn len(&self) -> usize {
1162 self.0.iter().fold(0, |acc, alternate| match *alternate {
1163 VariantAlternates::Swash(_)
1164 | VariantAlternates::Stylistic(_)
1165 | VariantAlternates::Ornaments(_)
1166 | VariantAlternates::Annotation(_) => acc + 1,
1167 VariantAlternates::Styleset(ref slice)
1168 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1169 _ => acc,
1170 })
1171 }
1172}
1173
1174impl Parse for FontVariantAlternates {
1175 fn parse<'i, 't>(
1184 _: &ParserContext,
1185 input: &mut Parser<'i, 't>,
1186 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1187 if input
1188 .try_parse(|input| input.expect_ident_matching("normal"))
1189 .is_ok()
1190 {
1191 return Ok(Default::default());
1192 }
1193
1194 let mut stylistic = None;
1195 let mut historical = None;
1196 let mut styleset = None;
1197 let mut character_variant = None;
1198 let mut swash = None;
1199 let mut ornaments = None;
1200 let mut annotation = None;
1201
1202 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1204 macro_rules! check_if_parsed(
1205 ($input:expr, $flag:path) => (
1206 if parsed_alternates.contains($flag) {
1207 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1208 }
1209 parsed_alternates |= $flag;
1210 )
1211 );
1212 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1213 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1214 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1215 historical = Some(VariantAlternates::HistoricalForms);
1216 Ok(())
1217 },
1218 Token::Function(ref name) => {
1219 let name = name.clone();
1220 input.parse_nested_block(|i| {
1221 match_ignore_ascii_case! { &name,
1222 "swash" => {
1223 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1224 let ident = CustomIdent::parse(i, &[])?;
1225 swash = Some(VariantAlternates::Swash(ident));
1226 Ok(())
1227 },
1228 "stylistic" => {
1229 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1230 let ident = CustomIdent::parse(i, &[])?;
1231 stylistic = Some(VariantAlternates::Stylistic(ident));
1232 Ok(())
1233 },
1234 "ornaments" => {
1235 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1236 let ident = CustomIdent::parse(i, &[])?;
1237 ornaments = Some(VariantAlternates::Ornaments(ident));
1238 Ok(())
1239 },
1240 "annotation" => {
1241 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1242 let ident = CustomIdent::parse(i, &[])?;
1243 annotation = Some(VariantAlternates::Annotation(ident));
1244 Ok(())
1245 },
1246 "styleset" => {
1247 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1248 let idents = i.parse_comma_separated(|i| {
1249 CustomIdent::parse(i, &[])
1250 })?;
1251 styleset = Some(VariantAlternates::Styleset(idents.into()));
1252 Ok(())
1253 },
1254 "character-variant" => {
1255 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1256 let idents = i.parse_comma_separated(|i| {
1257 CustomIdent::parse(i, &[])
1258 })?;
1259 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1260 Ok(())
1261 },
1262 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1263 }
1264 })
1265 },
1266 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1267 }) {}
1268
1269 if parsed_alternates.is_empty() {
1270 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1271 }
1272
1273 let mut alternates = Vec::new();
1275 macro_rules! push_if_some(
1276 ($value:expr) => (
1277 if let Some(v) = $value {
1278 alternates.push(v);
1279 }
1280 )
1281 );
1282 push_if_some!(stylistic);
1283 push_if_some!(historical);
1284 push_if_some!(styleset);
1285 push_if_some!(character_variant);
1286 push_if_some!(swash);
1287 push_if_some!(ornaments);
1288 push_if_some!(annotation);
1289
1290 Ok(FontVariantAlternates(alternates.into()))
1291 }
1292}
1293
1294#[derive(
1295 Clone,
1296 Copy,
1297 Debug,
1298 Eq,
1299 MallocSizeOf,
1300 PartialEq,
1301 Parse,
1302 SpecifiedValueInfo,
1303 ToComputedValue,
1304 ToCss,
1305 ToResolvedValue,
1306 ToShmem,
1307 ToTyped,
1308)]
1309#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1310#[css(bitflags(
1311 single = "normal",
1312 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1313 validate_mixed = "Self::validate_mixed_flags",
1314))]
1315#[repr(C)]
1316pub struct FontVariantEastAsian(u16);
1318bitflags! {
1319 impl FontVariantEastAsian: u16 {
1320 const NORMAL = 0;
1322 const JIS78 = 1 << 0;
1324 const JIS83 = 1 << 1;
1326 const JIS90 = 1 << 2;
1328 const JIS04 = 1 << 3;
1330 const SIMPLIFIED = 1 << 4;
1332 const TRADITIONAL = 1 << 5;
1334
1335 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1337
1338 const FULL_WIDTH = 1 << 6;
1340 const PROPORTIONAL_WIDTH = 1 << 7;
1342 const RUBY = 1 << 8;
1344 }
1345}
1346
1347impl FontVariantEastAsian {
1348 pub const COUNT: usize = 9;
1350
1351 fn validate_mixed_flags(&self) -> bool {
1352 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1353 return false;
1355 }
1356 let jis = self.intersection(Self::JIS_GROUP);
1357 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1358 return false;
1359 }
1360 true
1361 }
1362}
1363
1364#[derive(
1365 Clone,
1366 Copy,
1367 Debug,
1368 Eq,
1369 MallocSizeOf,
1370 PartialEq,
1371 Parse,
1372 SpecifiedValueInfo,
1373 ToComputedValue,
1374 ToCss,
1375 ToResolvedValue,
1376 ToShmem,
1377 ToTyped,
1378)]
1379#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1380#[css(bitflags(
1381 single = "normal,none",
1382 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1383 validate_mixed = "Self::validate_mixed_flags",
1384))]
1385#[repr(C)]
1386pub struct FontVariantLigatures(u16);
1388bitflags! {
1389 impl FontVariantLigatures: u16 {
1390 const NORMAL = 0;
1392 const NONE = 1;
1394 const COMMON_LIGATURES = 1 << 1;
1396 const NO_COMMON_LIGATURES = 1 << 2;
1398 const DISCRETIONARY_LIGATURES = 1 << 3;
1400 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1402 const HISTORICAL_LIGATURES = 1 << 5;
1404 const NO_HISTORICAL_LIGATURES = 1 << 6;
1406 const CONTEXTUAL = 1 << 7;
1408 const NO_CONTEXTUAL = 1 << 8;
1410 }
1411}
1412
1413impl FontVariantLigatures {
1414 pub const COUNT: usize = 9;
1416
1417 fn validate_mixed_flags(&self) -> bool {
1418 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1420 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1421 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1422 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1423 {
1424 return false;
1425 }
1426 true
1427 }
1428}
1429
1430#[derive(
1432 Clone,
1433 Copy,
1434 Debug,
1435 Eq,
1436 MallocSizeOf,
1437 PartialEq,
1438 Parse,
1439 SpecifiedValueInfo,
1440 ToComputedValue,
1441 ToCss,
1442 ToResolvedValue,
1443 ToShmem,
1444 ToTyped,
1445)]
1446#[css(bitflags(
1447 single = "normal",
1448 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1449 validate_mixed = "Self::validate_mixed_flags",
1450))]
1451#[cfg_attr(feature = "servo", derive(Serialize, Deserialize, Hash))]
1452#[repr(C)]
1453pub struct FontVariantNumeric(u8);
1454bitflags! {
1455 impl FontVariantNumeric : u8 {
1456 const NORMAL = 0;
1458 const LINING_NUMS = 1 << 0;
1460 const OLDSTYLE_NUMS = 1 << 1;
1462 const PROPORTIONAL_NUMS = 1 << 2;
1464 const TABULAR_NUMS = 1 << 3;
1466 const DIAGONAL_FRACTIONS = 1 << 4;
1468 const STACKED_FRACTIONS = 1 << 5;
1470 const SLASHED_ZERO = 1 << 6;
1472 const ORDINAL = 1 << 7;
1474 }
1475}
1476
1477impl FontVariantNumeric {
1478 pub const COUNT: usize = 8;
1480
1481 fn validate_mixed_flags(&self) -> bool {
1491 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1492 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1493 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1494 {
1495 return false;
1496 }
1497 true
1498 }
1499}
1500
1501pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1503
1504pub use crate::values::computed::font::FontLanguageOverride;
1506
1507impl Parse for FontLanguageOverride {
1508 fn parse<'i, 't>(
1510 _: &ParserContext,
1511 input: &mut Parser<'i, 't>,
1512 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1513 if input
1514 .try_parse(|input| input.expect_ident_matching("normal"))
1515 .is_ok()
1516 {
1517 return Ok(FontLanguageOverride::normal());
1518 }
1519
1520 let string = input.expect_string()?;
1521
1522 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1525 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1526 }
1527
1528 let mut bytes = [b' '; 4];
1529 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1530 *byte = *str_byte;
1531 }
1532
1533 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1534 }
1535}
1536
1537#[repr(u8)]
1539#[derive(
1540 Clone,
1541 Copy,
1542 Debug,
1543 Eq,
1544 Hash,
1545 MallocSizeOf,
1546 Parse,
1547 PartialEq,
1548 SpecifiedValueInfo,
1549 ToComputedValue,
1550 ToCss,
1551 ToResolvedValue,
1552 ToShmem,
1553 ToTyped,
1554)]
1555#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1556pub enum FontSynthesis {
1557 Auto,
1559 None,
1561}
1562
1563#[repr(u8)]
1565#[derive(
1566 Clone,
1567 Copy,
1568 Debug,
1569 Eq,
1570 MallocSizeOf,
1571 Parse,
1572 PartialEq,
1573 SpecifiedValueInfo,
1574 ToComputedValue,
1575 ToCss,
1576 ToResolvedValue,
1577 ToShmem,
1578 ToTyped,
1579)]
1580pub enum FontSynthesisStyle {
1581 Auto,
1583 None,
1585 ObliqueOnly,
1587}
1588
1589#[derive(
1590 Clone,
1591 Debug,
1592 Eq,
1593 MallocSizeOf,
1594 PartialEq,
1595 SpecifiedValueInfo,
1596 ToComputedValue,
1597 ToResolvedValue,
1598 ToShmem,
1599 ToTyped,
1600)]
1601#[repr(C)]
1602#[typed(todo_derive_fields)]
1603pub struct FontPalette(Atom);
1606
1607#[allow(missing_docs)]
1608impl FontPalette {
1609 pub fn normal() -> Self {
1610 Self(atom!("normal"))
1611 }
1612 pub fn light() -> Self {
1613 Self(atom!("light"))
1614 }
1615 pub fn dark() -> Self {
1616 Self(atom!("dark"))
1617 }
1618}
1619
1620impl Parse for FontPalette {
1621 fn parse<'i, 't>(
1623 _context: &ParserContext,
1624 input: &mut Parser<'i, 't>,
1625 ) -> Result<FontPalette, ParseError<'i>> {
1626 let location = input.current_source_location();
1627 let ident = input.expect_ident()?;
1628 match_ignore_ascii_case! { &ident,
1629 "normal" => Ok(Self::normal()),
1630 "light" => Ok(Self::light()),
1631 "dark" => Ok(Self::dark()),
1632 _ => if ident.starts_with("--") {
1633 Ok(Self(Atom::from(ident.as_ref())))
1634 } else {
1635 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1636 },
1637 }
1638 }
1639}
1640
1641impl ToCss for FontPalette {
1642 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1643 where
1644 W: Write,
1645 {
1646 serialize_atom_identifier(&self.0, dest)
1647 }
1648}
1649
1650pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1653
1654fn parse_one_feature_value<'i, 't>(
1655 context: &ParserContext,
1656 input: &mut Parser<'i, 't>,
1657) -> Result<Integer, ParseError<'i>> {
1658 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1659 return Ok(integer);
1660 }
1661
1662 try_match_ident_ignore_ascii_case! { input,
1663 "on" => Ok(Integer::new(1)),
1664 "off" => Ok(Integer::new(0)),
1665 }
1666}
1667
1668impl Parse for FeatureTagValue<Integer> {
1669 fn parse<'i, 't>(
1671 context: &ParserContext,
1672 input: &mut Parser<'i, 't>,
1673 ) -> Result<Self, ParseError<'i>> {
1674 let tag = FontTag::parse(context, input)?;
1675 let value = input
1676 .try_parse(|i| parse_one_feature_value(context, i))
1677 .unwrap_or_else(|_| Integer::new(1));
1678
1679 Ok(Self { tag, value })
1680 }
1681}
1682
1683impl Parse for VariationValue<Number> {
1684 fn parse<'i, 't>(
1687 context: &ParserContext,
1688 input: &mut Parser<'i, 't>,
1689 ) -> Result<Self, ParseError<'i>> {
1690 let tag = FontTag::parse(context, input)?;
1691 let value = Number::parse(context, input)?;
1692 Ok(Self { tag, value })
1693 }
1694}
1695
1696#[derive(
1700 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1701)]
1702pub enum MetricsOverride {
1703 Override(NonNegativePercentage),
1705 Normal,
1707}
1708
1709impl MetricsOverride {
1710 #[inline]
1711 pub fn normal() -> MetricsOverride {
1713 MetricsOverride::Normal
1714 }
1715
1716 #[inline]
1721 pub fn compute(&self) -> ComputedPercentage {
1722 match *self {
1723 MetricsOverride::Normal => ComputedPercentage(-1.0),
1724 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1725 }
1726 }
1727}
1728
1729#[derive(
1730 Clone,
1731 Copy,
1732 Debug,
1733 MallocSizeOf,
1734 Parse,
1735 PartialEq,
1736 SpecifiedValueInfo,
1737 ToComputedValue,
1738 ToCss,
1739 ToResolvedValue,
1740 ToShmem,
1741 ToTyped,
1742)]
1743#[repr(u8)]
1744pub enum XTextScale {
1746 All,
1748 ZoomOnly,
1750 None,
1752}
1753
1754impl XTextScale {
1755 #[inline]
1757 pub fn text_zoom_enabled(self) -> bool {
1758 self != Self::None
1759 }
1760}
1761
1762#[derive(
1763 Clone,
1764 Debug,
1765 MallocSizeOf,
1766 PartialEq,
1767 SpecifiedValueInfo,
1768 ToComputedValue,
1769 ToCss,
1770 ToResolvedValue,
1771 ToShmem,
1772 ToTyped,
1773)]
1774#[cfg_attr(feature = "servo", derive(Deserialize, Eq, Hash, Serialize))]
1775pub struct XLang(#[css(skip)] pub Atom);
1777
1778impl XLang {
1779 #[inline]
1780 pub fn get_initial_value() -> XLang {
1782 XLang(atom!(""))
1783 }
1784}
1785
1786impl Parse for XLang {
1787 fn parse<'i, 't>(
1788 _: &ParserContext,
1789 input: &mut Parser<'i, 't>,
1790 ) -> Result<XLang, ParseError<'i>> {
1791 debug_assert!(
1792 false,
1793 "Should be set directly by presentation attributes only."
1794 );
1795 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1796 }
1797}
1798
1799#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1800#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1801pub struct MozScriptMinSize(pub NoCalcLength);
1804
1805impl MozScriptMinSize {
1806 #[inline]
1807 pub fn get_initial_value() -> Length {
1809 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1810 }
1811}
1812
1813impl Parse for MozScriptMinSize {
1814 fn parse<'i, 't>(
1815 _: &ParserContext,
1816 input: &mut Parser<'i, 't>,
1817 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1818 debug_assert!(
1819 false,
1820 "Should be set directly by presentation attributes only."
1821 );
1822 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1823 }
1824}
1825
1826#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1829#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1830pub enum MathDepth {
1831 AutoAdd,
1833
1834 #[css(function)]
1836 Add(Integer),
1837
1838 Absolute(Integer),
1840}
1841
1842impl Parse for MathDepth {
1843 fn parse<'i, 't>(
1844 context: &ParserContext,
1845 input: &mut Parser<'i, 't>,
1846 ) -> Result<MathDepth, ParseError<'i>> {
1847 if input
1848 .try_parse(|i| i.expect_ident_matching("auto-add"))
1849 .is_ok()
1850 {
1851 return Ok(MathDepth::AutoAdd);
1852 }
1853 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1854 return Ok(MathDepth::Absolute(math_depth_value));
1855 }
1856 input.expect_function_matching("add")?;
1857 let math_depth_delta_value =
1858 input.parse_nested_block(|input| Integer::parse(context, input))?;
1859 Ok(MathDepth::Add(math_depth_delta_value))
1860 }
1861}
1862
1863#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1864#[derive(
1865 Clone,
1866 Copy,
1867 Debug,
1868 PartialEq,
1869 SpecifiedValueInfo,
1870 ToComputedValue,
1871 ToCss,
1872 ToResolvedValue,
1873 ToShmem,
1874)]
1875pub struct MozScriptSizeMultiplier(pub f32);
1880
1881impl MozScriptSizeMultiplier {
1882 #[inline]
1883 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1885 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1886 }
1887}
1888
1889impl Parse for MozScriptSizeMultiplier {
1890 fn parse<'i, 't>(
1891 _: &ParserContext,
1892 input: &mut Parser<'i, 't>,
1893 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1894 debug_assert!(
1895 false,
1896 "Should be set directly by presentation attributes only."
1897 );
1898 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1899 }
1900}
1901
1902impl From<f32> for MozScriptSizeMultiplier {
1903 fn from(v: f32) -> Self {
1904 MozScriptSizeMultiplier(v)
1905 }
1906}
1907
1908impl From<MozScriptSizeMultiplier> for f32 {
1909 fn from(v: MozScriptSizeMultiplier) -> f32 {
1910 v.0
1911 }
1912}
1913
1914pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1916
1917impl ToComputedValue for LineHeight {
1918 type ComputedValue = computed::LineHeight;
1919
1920 #[inline]
1921 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1922 match *self {
1923 GenericLineHeight::Normal => GenericLineHeight::Normal,
1924 #[cfg(feature = "gecko")]
1925 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1926 GenericLineHeight::Number(number) => {
1927 GenericLineHeight::Number(number.to_computed_value(context))
1928 },
1929 GenericLineHeight::Length(ref non_negative_lp) => {
1930 let result = match non_negative_lp.0 {
1931 LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1932 context.maybe_zoom_text(abs.to_computed_value(context))
1933 },
1934 LengthPercentage::Length(ref length) => {
1935 length.to_computed_value_with_base_size(
1940 context,
1941 FontBaseSize::CurrentStyle,
1942 LineHeightBase::InheritedStyle,
1943 )
1944 },
1945 LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1946 .to_computed_value(
1947 context,
1948 FontBaseSize::CurrentStyle,
1949 LineHeightBase::InheritedStyle,
1950 ),
1951 LengthPercentage::Calc(ref calc) => {
1952 let computed_calc = calc.to_computed_value_zoomed(
1953 context,
1954 FontBaseSize::CurrentStyle,
1955 LineHeightBase::InheritedStyle,
1956 );
1957 let base = context.style().get_font().clone_font_size().computed_size();
1958 computed_calc.resolve(base)
1959 },
1960 };
1961 GenericLineHeight::Length(result.into())
1962 },
1963 }
1964 }
1965
1966 #[inline]
1967 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1968 match *computed {
1969 GenericLineHeight::Normal => GenericLineHeight::Normal,
1970 #[cfg(feature = "gecko")]
1971 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1972 GenericLineHeight::Number(ref number) => {
1973 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1974 },
1975 GenericLineHeight::Length(ref length) => {
1976 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1977 },
1978 }
1979 }
1980}
1981
1982#[repr(C)]
1984pub struct QueryFontMetricsFlags(u8);
1985
1986bitflags! {
1987 impl QueryFontMetricsFlags: u8 {
1988 const USE_USER_FONT_SET = 1 << 0;
1990 const NEEDS_CH = 1 << 1;
1992 const NEEDS_IC = 1 << 2;
1994 const NEEDS_MATH_SCALES = 1 << 3;
1996 }
1997}