1use crate::Atom;
8use crate::context::QuirksMode;
9use crate::derives::*;
10use crate::parser::{Parse, ParserContext};
11use crate::typed_om::NumericBaseType;
12use crate::values::computed::Percentage as ComputedPercentage;
13use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
14use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
15use crate::values::computed::{Length, NonNegativeLength, font as computed};
16use crate::values::generics::NonNegative;
17use crate::values::generics::font::{
18 self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
19};
20use crate::values::specified::calc::{Leaf, PercentageContext};
21use crate::values::specified::length::{FontBaseSize, LengthUnit, LineHeightBase, PX_PER_PT};
22use crate::values::specified::number::parse_number_with_clamping_mode;
23use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
24use crate::values::specified::{
25 NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber, NonNegativePercentage, Number,
26};
27use crate::values::{CustomIdent, SelectorParseErrorKind, serialize_atom_identifier};
28use cssparser::{Parser, Token, match_ignore_ascii_case};
29#[cfg(feature = "gecko")]
30use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
31use std::fmt::{self, Write};
32use style_traits::values::specified::AllowedNumericType;
33use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
34use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
35
36macro_rules! system_font_methods {
38 ($ty:ident, $field:ident) => {
39 system_font_methods!($ty);
40
41 fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
42 debug_assert!(matches!(*self, $ty::System(..)));
43 #[cfg(feature = "gecko")]
44 {
45 _context.cached_system_font.as_ref().unwrap().$field.clone()
46 }
47 #[cfg(feature = "servo")]
48 {
49 unreachable!()
50 }
51 }
52 };
53
54 ($ty:ident) => {
55 pub fn system_font(f: SystemFont) -> Self {
57 $ty::System(f)
58 }
59
60 pub fn get_system(&self) -> Option<SystemFont> {
62 if let $ty::System(s) = *self {
63 Some(s)
64 } else {
65 None
66 }
67 }
68 };
69}
70
71#[repr(u8)]
73#[derive(
74 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
75)]
76#[allow(missing_docs)]
77#[cfg(feature = "gecko")]
78pub enum SystemFont {
79 Caption,
81 Icon,
83 Menu,
85 MessageBox,
87 SmallCaption,
89 StatusBar,
91 #[parse(condition = "ParserContext::chrome_rules_enabled")]
93 MozPullDownMenu,
94 #[parse(condition = "ParserContext::chrome_rules_enabled")]
96 MozButton,
97 #[parse(condition = "ParserContext::chrome_rules_enabled")]
99 MozList,
100 #[parse(condition = "ParserContext::chrome_rules_enabled")]
102 MozField,
103 #[css(skip)]
104 End, }
106
107#[derive(
112 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
113)]
114#[allow(missing_docs)]
115#[cfg(feature = "servo")]
116pub struct SystemFont {
118 #[css(skip)]
119 _forbid_construction: (),
120}
121
122#[allow(missing_docs)]
123#[cfg(feature = "servo")]
124impl SystemFont {
125 pub fn parse(_context: &ParserContext, _input: &mut Parser) -> Result<Self, ()> {
126 Err(())
127 }
128}
129
130const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
131const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
132
133pub const MIN_FONT_WEIGHT: f32 = 1.;
137
138pub const MAX_FONT_WEIGHT: f32 = 1000.;
142
143#[derive(
147 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
148)]
149pub enum FontWeight {
150 Absolute(AbsoluteFontWeight),
152 Bolder,
154 Lighter,
156 #[css(skip)]
158 System(SystemFont),
159}
160
161impl FontWeight {
162 system_font_methods!(FontWeight, font_weight);
163
164 #[inline]
166 pub fn normal() -> Self {
167 FontWeight::Absolute(AbsoluteFontWeight::Normal)
168 }
169
170 pub fn from_gecko_keyword(kw: u32) -> Self {
172 debug_assert!(kw.is_multiple_of(100));
173 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
174 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
175 }
176}
177
178impl ToComputedValue for FontWeight {
179 type ComputedValue = computed::FontWeight;
180
181 #[inline]
182 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
183 match *self {
184 FontWeight::Absolute(ref abs) => abs.to_computed_value(context),
185 FontWeight::Bolder => context.builder.get_parent_font().get_font_weight().bolder(),
186 FontWeight::Lighter => context
187 .builder
188 .get_parent_font()
189 .get_font_weight()
190 .lighter(),
191 FontWeight::System(_) => self.compute_system(context),
192 }
193 }
194
195 #[inline]
196 fn from_computed_value(computed: &computed::FontWeight) -> Self {
197 FontWeight::Absolute(AbsoluteFontWeight::from_computed_value(computed))
198 }
199}
200
201#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
205pub enum AbsoluteFontWeight {
206 Weight(Number),
210 Normal,
212 Bold,
214}
215
216impl AbsoluteFontWeight {
217 pub fn compute(&self) -> Option<computed::FontWeight> {
220 match self {
221 AbsoluteFontWeight::Weight(weight) => {
222 Some(computed::FontWeight::from_float(weight.resolve()?))
223 },
224 AbsoluteFontWeight::Normal => Some(computed::FontWeight::NORMAL),
225 AbsoluteFontWeight::Bold => Some(computed::FontWeight::BOLD),
226 }
227 }
228}
229
230impl ToComputedValue for AbsoluteFontWeight {
231 type ComputedValue = computed::FontWeight;
232
233 fn to_computed_value(&self, context: &Context) -> computed::FontWeight {
234 match self {
235 AbsoluteFontWeight::Weight(weight) => {
236 computed::FontWeight::from_float(weight.to_computed_value(context))
237 },
238 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
239 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
240 }
241 }
242
243 fn from_computed_value(computed: &computed::FontWeight) -> Self {
244 AbsoluteFontWeight::Weight(Number::from_computed_value(&computed.value()))
245 }
246}
247
248impl Parse for AbsoluteFontWeight {
249 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
250 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
251 if matches!(number.get(), Some(v) if !(MIN_FONT_WEIGHT..=MAX_FONT_WEIGHT).contains(&v))
255 {
256 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
257 }
258 return Ok(AbsoluteFontWeight::Weight(number));
259 }
260
261 Ok(try_match_ident_ignore_ascii_case! { input,
262 "normal" => AbsoluteFontWeight::Normal,
263 "bold" => AbsoluteFontWeight::Bold,
264 })
265 }
266}
267
268pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
271
272impl ToCss for SpecifiedFontStyle {
273 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
274 where
275 W: Write,
276 {
277 match *self {
278 generics::FontStyle::Italic => dest.write_str("italic"),
279 generics::FontStyle::Oblique(ref angle) => {
280 if *angle == Angle::zero() {
283 dest.write_str("normal")?;
284 } else {
285 dest.write_str("oblique")?;
286 if *angle != Self::default_angle() {
287 dest.write_char(' ')?;
288 angle.to_css(dest)?;
289 }
290 }
291 Ok(())
292 },
293 }
294 }
295}
296
297impl Parse for SpecifiedFontStyle {
298 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
299 Ok(try_match_ident_ignore_ascii_case! { input,
300 "normal" => generics::FontStyle::normal(),
301 "italic" => generics::FontStyle::Italic,
302 "oblique" => {
303 let angle = input.try_parse(|input| Self::parse_angle(context, input))
304 .unwrap_or_else(|_| Self::default_angle());
305
306 generics::FontStyle::Oblique(angle)
307 },
308 })
309 }
310}
311
312impl ToComputedValue for SpecifiedFontStyle {
313 type ComputedValue = computed::FontStyle;
314
315 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
316 match *self {
317 Self::Italic => computed::FontStyle::ITALIC,
318 Self::Oblique(ref angle) => {
319 computed::FontStyle::oblique(angle.to_computed_value(context).degrees())
320 },
321 }
322 }
323
324 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
325 if *computed == computed::FontStyle::ITALIC {
326 return Self::Italic;
327 }
328 let degrees = computed.oblique_degrees();
329 generics::FontStyle::Oblique(Angle::from_degrees(degrees))
330 }
331}
332
333pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
340
341pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
343
344impl SpecifiedFontStyle {
345 pub fn parse_angle(context: &ParserContext, input: &mut Parser) -> Result<Angle, ParseError> {
347 let angle = Angle::parse(context, input)?;
348 if angle.is_calc() {
350 return Ok(angle);
351 }
352
353 let degrees = angle.degrees().unwrap();
354 if !(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES..=FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
355 .contains(°rees)
356 {
357 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
358 }
359 Ok(angle)
360 }
361
362 pub fn default_angle() -> Angle {
364 Angle::from_degrees(computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32)
365 }
366}
367
368#[derive(
370 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
371)]
372#[allow(missing_docs)]
373#[typed(todo_derive_fields)]
374pub enum FontStyle {
375 Specified(SpecifiedFontStyle),
376 #[css(skip)]
377 System(SystemFont),
378}
379
380impl FontStyle {
381 #[inline]
383 pub fn normal() -> Self {
384 FontStyle::Specified(generics::FontStyle::normal())
385 }
386
387 system_font_methods!(FontStyle, font_style);
388}
389
390impl ToComputedValue for FontStyle {
391 type ComputedValue = computed::FontStyle;
392
393 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
394 match *self {
395 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
396 FontStyle::System(..) => self.compute_system(context),
397 }
398 }
399
400 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
401 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
402 }
403}
404
405#[allow(missing_docs)]
409#[derive(
410 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
411)]
412pub enum FontWidth {
413 Width(NonNegativePercentage),
414 Keyword(FontWidthKeyword),
415 #[css(skip)]
416 System(SystemFont),
417}
418
419#[derive(
421 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
422)]
423#[allow(missing_docs)]
424pub enum FontWidthKeyword {
425 Normal,
426 Condensed,
427 UltraCondensed,
428 ExtraCondensed,
429 SemiCondensed,
430 SemiExpanded,
431 Expanded,
432 ExtraExpanded,
433 UltraExpanded,
434}
435
436impl FontWidthKeyword {
437 pub fn compute(&self) -> computed::FontWidth {
439 computed::FontWidth::from_keyword(*self)
440 }
441
442 pub fn from_percentage(p: f32) -> Option<Self> {
445 computed::FontWidth::from_percentage(p).as_keyword()
446 }
447}
448
449impl FontWidth {
450 pub fn normal() -> Self {
452 FontWidth::Keyword(FontWidthKeyword::Normal)
453 }
454
455 system_font_methods!(FontWidth, font_width);
456}
457
458impl ToComputedValue for FontWidth {
459 type ComputedValue = computed::FontWidth;
460
461 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
462 match *self {
463 FontWidth::Width(ref percentage) => {
464 let percentage = percentage.to_computed_value(context).0;
465 computed::FontWidth::from_percentage(percentage.0)
466 },
467 FontWidth::Keyword(ref kw) => kw.compute(),
468 FontWidth::System(_) => self.compute_system(context),
469 }
470 }
471
472 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
473 FontWidth::Width(NonNegativePercentage::from_computed_value(&NonNegative(
474 computed.to_percentage(),
475 )))
476 }
477}
478
479#[derive(
481 Animate,
482 Clone,
483 ComputeSquaredDistance,
484 Copy,
485 Debug,
486 Default,
487 MallocSizeOf,
488 Parse,
489 PartialEq,
490 SpecifiedValueInfo,
491 ToAnimatedValue,
492 ToAnimatedZero,
493 ToComputedValue,
494 ToCss,
495 ToResolvedValue,
496 ToShmem,
497 Serialize,
498 Deserialize,
499 ToTyped,
500)]
501#[allow(missing_docs)]
502#[repr(u8)]
503pub enum FontSizeKeyword {
504 #[css(keyword = "xx-small")]
505 XXSmall,
506 XSmall,
507 Small,
508 #[default]
509 Medium,
510 Large,
511 XLarge,
512 #[css(keyword = "xx-large")]
513 XXLarge,
514 #[css(keyword = "xxx-large")]
515 XXXLarge,
516 #[cfg(feature = "gecko")]
519 Math,
520 #[css(skip)]
521 None,
522}
523
524impl FontSizeKeyword {
525 #[inline]
527 pub fn html_size(self) -> u8 {
528 self as u8
529 }
530
531 #[cfg(feature = "gecko")]
533 pub fn is_math(self) -> bool {
534 matches!(self, Self::Math)
535 }
536
537 #[cfg(feature = "servo")]
539 pub fn is_math(self) -> bool {
540 false
541 }
542}
543
544#[derive(
545 Animate,
546 Clone,
547 ComputeSquaredDistance,
548 Copy,
549 Debug,
550 Deserialize,
551 MallocSizeOf,
552 PartialEq,
553 Serialize,
554 ToAnimatedValue,
555 ToAnimatedZero,
556 ToComputedValue,
557 ToCss,
558 ToResolvedValue,
559 ToShmem,
560 ToTyped,
561)]
562pub struct KeywordInfo {
564 pub kw: FontSizeKeyword,
566 #[css(skip)]
568 pub factor: f32,
569 #[css(skip)]
572 pub offset: CSSPixelLength,
573}
574
575impl KeywordInfo {
576 pub fn medium() -> Self {
578 Self::new(FontSizeKeyword::Medium)
579 }
580
581 pub fn none() -> Self {
583 Self::new(FontSizeKeyword::None)
584 }
585
586 fn new(kw: FontSizeKeyword) -> Self {
587 KeywordInfo {
588 kw,
589 factor: 1.,
590 offset: CSSPixelLength::new(0.),
591 }
592 }
593
594 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
597 debug_assert_ne!(self.kw, FontSizeKeyword::None);
598 #[cfg(feature = "gecko")]
599 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
600 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
601 let zoom_factor = context.style().effective_zoom.value();
602 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
603 + context.maybe_zoom_text(self.offset)
604 }
605
606 fn compose(self, factor: f32) -> Self {
609 if self.kw == FontSizeKeyword::None {
610 return self;
611 }
612 KeywordInfo {
613 kw: self.kw,
614 factor: self.factor * factor,
615 offset: self.offset * factor,
616 }
617 }
618}
619
620impl SpecifiedValueInfo for KeywordInfo {
621 fn collect_completion_keywords(f: KeywordsCollectFn) {
622 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
623 }
624}
625
626#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
627pub enum FontSize {
629 Length(LengthPercentage),
631 Keyword(KeywordInfo),
642 Smaller,
644 Larger,
646 #[css(skip)]
648 System(SystemFont),
649}
650
651#[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss, ToShmem, ToTyped)]
653#[typed(todo_derive_fields)]
654pub enum FontFamily {
655 #[css(comma)]
657 Values(#[css(iterable)] FontFamilyList),
658 #[css(skip)]
660 System(SystemFont),
661}
662
663impl FontFamily {
664 system_font_methods!(FontFamily, font_family);
665}
666
667impl ToComputedValue for FontFamily {
668 type ComputedValue = computed::FontFamily;
669
670 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
671 match *self {
672 FontFamily::Values(ref list) => computed::FontFamily {
673 families: list.clone(),
674 is_system_font: false,
675 is_initial: false,
676 },
677 FontFamily::System(_) => self.compute_system(context),
678 }
679 }
680
681 fn from_computed_value(other: &computed::FontFamily) -> Self {
682 FontFamily::Values(other.families.clone())
683 }
684}
685
686#[cfg(feature = "gecko")]
687impl MallocSizeOf for FontFamily {
688 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
689 match *self {
690 FontFamily::Values(ref v) => {
691 v.list.unconditional_size_of(ops)
694 },
695 FontFamily::System(_) => 0,
696 }
697 }
698}
699
700impl Parse for FontFamily {
701 fn parse(context: &ParserContext, input: &mut Parser) -> Result<FontFamily, ParseError> {
705 let values =
706 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
707 Ok(FontFamily::Values(FontFamilyList {
708 list: crate::ArcSlice::from_iter(values.into_iter()),
709 }))
710 }
711}
712
713impl SpecifiedValueInfo for FontFamily {}
714
715impl Parse for FamilyName {
718 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
719 match SingleFontFamily::parse(context, input) {
720 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
721 Ok(SingleFontFamily::Generic(_)) => {
722 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
723 },
724 Err(e) => Err(e),
725 }
726 }
727}
728
729#[derive(
732 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
733)]
734pub enum FontSizeAdjustFactor {
735 Number(NonNegativeNumber),
737 FromFont,
739}
740
741pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
746
747impl Parse for FontSizeAdjust {
748 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
749 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
751 return Ok(Self::ExHeight(factor));
752 }
753
754 let ident = input.expect_ident()?;
755 let basis = match_ignore_ascii_case! { &ident,
756 "none" => return Ok(Self::None),
757 "ex-height" => Self::ExHeight,
759 "cap-height" => Self::CapHeight,
760 "ch-width" => Self::ChWidth,
761 "ic-width" => Self::IcWidth,
762 "ic-height" => Self::IcHeight,
763 _ => return Err(ParseError::custom(
765 SelectorParseErrorKind::UnexpectedIdent
766 )),
767 };
768
769 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
770 }
771}
772
773const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
776
777pub const FONT_MEDIUM_PX: f32 = 16.0;
779pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
781pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
784pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
787pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
790pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
793
794impl FontSizeKeyword {
795 #[inline]
796 fn to_length(&self, cx: &Context) -> NonNegativeLength {
797 let font = cx.style().get_font();
798
799 #[cfg(feature = "servo")]
800 let family = &font.font_family.families;
801 #[cfg(feature = "gecko")]
802 let family = &font.mFont.family.families;
803
804 let generic = family
805 .single_generic()
806 .unwrap_or(computed::GenericFontFamily::None);
807
808 #[cfg(feature = "gecko")]
809 let base_size = cx
810 .device()
811 .base_size_for_generic(&font.mLanguage.0, generic);
812 #[cfg(feature = "servo")]
813 let base_size = cx.device().base_size_for_generic(generic);
814
815 self.to_length_without_context(cx.quirks_mode, base_size)
816 }
817
818 #[inline]
820 pub fn to_length_without_context(
821 &self,
822 quirks_mode: QuirksMode,
823 base_size: Length,
824 ) -> NonNegativeLength {
825 #[cfg(feature = "gecko")]
826 debug_assert_ne!(*self, FontSizeKeyword::Math);
827 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
840 [9, 9, 9, 9, 11, 14, 18, 27],
841 [9, 9, 9, 10, 12, 15, 20, 30],
842 [9, 9, 10, 11, 13, 17, 22, 33],
843 [9, 9, 10, 12, 14, 18, 24, 36],
844 [9, 10, 12, 13, 16, 20, 26, 39],
845 [9, 10, 12, 14, 17, 21, 28, 42],
846 [9, 10, 13, 15, 18, 23, 30, 45],
847 [9, 10, 13, 16, 18, 24, 32, 48],
848 ];
849
850 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
859 [9, 9, 9, 9, 11, 14, 18, 28],
860 [9, 9, 9, 10, 12, 15, 20, 31],
861 [9, 9, 9, 11, 13, 17, 22, 34],
862 [9, 9, 10, 12, 14, 18, 24, 37],
863 [9, 9, 10, 13, 16, 20, 26, 40],
864 [9, 9, 11, 14, 17, 21, 28, 42],
865 [9, 10, 12, 15, 17, 23, 30, 45],
866 [9, 10, 13, 16, 18, 24, 32, 48],
867 ];
868
869 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
870 let base_size_px = base_size.px().round() as i32;
871 let html_size = self.html_size() as usize;
872 NonNegative(if (9..=16).contains(&base_size_px) {
873 let mapping = if quirks_mode == QuirksMode::Quirks {
874 QUIRKS_FONT_SIZE_MAPPING
875 } else {
876 FONT_SIZE_MAPPING
877 };
878 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
879 } else {
880 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
881 })
882 }
883}
884
885impl FontSize {
886 pub fn from_html_size(size: u8) -> Self {
888 FontSize::Keyword(KeywordInfo::new(match size {
889 0 | 1 => FontSizeKeyword::XSmall,
891 2 => FontSizeKeyword::Small,
892 3 => FontSizeKeyword::Medium,
893 4 => FontSizeKeyword::Large,
894 5 => FontSizeKeyword::XLarge,
895 6 => FontSizeKeyword::XXLarge,
896 _ => FontSizeKeyword::XXXLarge,
898 }))
899 }
900
901 pub fn to_computed_value_against(
903 &self,
904 context: &Context,
905 base_size: FontBaseSize,
906 line_height_base: LineHeightBase,
907 ) -> computed::FontSize {
908 let compose_keyword = |factor| {
909 context
910 .style()
911 .get_parent_font()
912 .slow_clone_font_size()
913 .keyword_info
914 .compose(factor)
915 };
916 let mut info = KeywordInfo::none();
917 let size =
918 match *self {
919 FontSize::Length(LengthPercentage::Length(ref l)) => {
920 if l.length_unit() == LengthUnit::Em {
921 info = compose_keyword(l.unitless_value());
924 }
925 let result =
926 l.to_computed_value_with_base_size(context, base_size, line_height_base);
927 if l.should_zoom_text() {
928 context.maybe_zoom_text(result)
929 } else {
930 result
931 }
932 },
933 FontSize::Length(LengthPercentage::Percentage(pc)) => {
934 info = compose_keyword(pc.get());
937 (base_size.resolve(context).computed_size() * pc.get()).normalized()
938 },
939 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
940 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
941 calc.resolve(base_size.resolve(context).computed_size())
942 },
943 FontSize::Keyword(i) => {
944 if i.kw.is_math() {
945 info = compose_keyword(1.);
947 info.kw = i.kw;
950 NoCalcLength::from_em(1.).to_computed_value_with_base_size(
951 context,
952 base_size,
953 line_height_base,
954 )
955 } else {
956 info = i;
958 i.to_computed_value(context).clamp_to_non_negative()
959 }
960 },
961 FontSize::Smaller => {
962 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
963 NoCalcLength::from_em(1. / LARGER_FONT_SIZE_RATIO)
964 .to_computed_value_with_base_size(context, base_size, line_height_base)
965 },
966 FontSize::Larger => {
967 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
968 NoCalcLength::from_em(LARGER_FONT_SIZE_RATIO).to_computed_value_with_base_size(
969 context,
970 base_size,
971 line_height_base,
972 )
973 },
974 FontSize::System(_) => {
975 #[cfg(feature = "servo")]
976 {
977 unreachable!()
978 }
979 #[cfg(feature = "gecko")]
980 {
981 context
982 .cached_system_font
983 .as_ref()
984 .unwrap()
985 .font_size
986 .computed_size()
987 .zoom(context.builder.effective_zoom)
988 }
989 },
990 };
991 let size = NonNegative(Self::quantize_font_size(size));
992 computed::FontSize {
993 computed_size: size,
994 used_size: size,
995 keyword_info: info,
996 }
997 }
998
999 #[inline]
1002 pub fn quantize_font_size(size: CSSPixelLength) -> CSSPixelLength {
1003 if size.px() < 1024.0 {
1005 return CSSPixelLength::from(app_units::Au::from_f32_px(size.px()));
1006 }
1007
1008 size_of_test!(CSSPixelLength, std::mem::size_of::<f32>());
1016 const BITS_TO_DROP: u32 = 14; const SCALE_PLUS_ONE: f32 = ((1 << BITS_TO_DROP) + 1) as f32;
1018 const LIMIT: f32 = f32::MAX / SCALE_PLUS_ONE;
1019 if size.px() >= LIMIT {
1020 return CSSPixelLength::new(LIMIT);
1021 }
1022 let d = size.px() * SCALE_PLUS_ONE;
1023 let t = d - size.px();
1024 CSSPixelLength::from(app_units::Au::from_f32_px(d - t))
1026 }
1027}
1028
1029impl ToComputedValue for FontSize {
1030 type ComputedValue = computed::FontSize;
1031
1032 #[inline]
1033 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1034 self.to_computed_value_against(
1035 context,
1036 FontBaseSize::InheritedStyle,
1037 LineHeightBase::InheritedStyle,
1038 )
1039 }
1040
1041 #[inline]
1042 fn from_computed_value(computed: &computed::FontSize) -> Self {
1043 FontSize::Length(LengthPercentage::Length(
1044 ToComputedValue::from_computed_value(&computed.computed_size()),
1045 ))
1046 }
1047}
1048
1049impl FontSize {
1050 system_font_methods!(FontSize);
1051
1052 #[inline]
1054 pub fn medium() -> Self {
1055 FontSize::Keyword(KeywordInfo::medium())
1056 }
1057
1058 pub fn parse_quirky(
1060 context: &ParserContext,
1061 input: &mut Parser,
1062 allow_quirks: AllowQuirks,
1063 ) -> Result<FontSize, ParseError> {
1064 if let Ok(lp) = input
1065 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1066 {
1067 return Ok(FontSize::Length(lp));
1068 }
1069
1070 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1071 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1072 }
1073
1074 try_match_ident_ignore_ascii_case! { input,
1075 "smaller" => Ok(FontSize::Smaller),
1076 "larger" => Ok(FontSize::Larger),
1077 }
1078 }
1079}
1080
1081impl Parse for FontSize {
1082 fn parse(context: &ParserContext, input: &mut Parser) -> Result<FontSize, ParseError> {
1084 FontSize::parse_quirky(context, input, AllowQuirks::No)
1085 }
1086}
1087
1088bitflags! {
1089 #[derive(Clone, Copy)]
1090 struct VariantAlternatesParsingFlags: u8 {
1092 const NORMAL = 0;
1094 const HISTORICAL_FORMS = 0x01;
1096 const STYLISTIC = 0x02;
1098 const STYLESET = 0x04;
1100 const CHARACTER_VARIANT = 0x08;
1102 const SWASH = 0x10;
1104 const ORNAMENTS = 0x20;
1106 const ANNOTATION = 0x40;
1108 }
1109}
1110
1111#[derive(
1112 Clone,
1113 Debug,
1114 Deserialize,
1115 Hash,
1116 MallocSizeOf,
1117 PartialEq,
1118 Serialize,
1119 SpecifiedValueInfo,
1120 ToCss,
1121 ToComputedValue,
1122 ToResolvedValue,
1123 ToShmem,
1124)]
1125#[repr(C, u8)]
1126pub enum VariantAlternates {
1128 #[css(function)]
1130 Stylistic(CustomIdent),
1131 #[css(comma, function)]
1133 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1134 #[css(comma, function)]
1136 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1137 #[css(function)]
1139 Swash(CustomIdent),
1140 #[css(function)]
1142 Ornaments(CustomIdent),
1143 #[css(function)]
1145 Annotation(CustomIdent),
1146 HistoricalForms,
1148}
1149
1150#[derive(
1151 Clone,
1152 Debug,
1153 Default,
1154 Deserialize,
1155 Hash,
1156 MallocSizeOf,
1157 PartialEq,
1158 Serialize,
1159 SpecifiedValueInfo,
1160 ToComputedValue,
1161 ToCss,
1162 ToResolvedValue,
1163 ToShmem,
1164 ToTyped,
1165)]
1166#[repr(transparent)]
1167#[typed(todo_derive_fields)]
1168pub struct FontVariantAlternates(
1170 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1171);
1172
1173impl FontVariantAlternates {
1174 pub fn is_empty(&self) -> bool {
1176 self.0.is_empty()
1177 }
1178
1179 pub fn iter(&self) -> impl Iterator<Item = &VariantAlternates> {
1181 self.0.iter()
1182 }
1183
1184 pub fn len(&self) -> usize {
1186 self.0.iter().fold(0, |acc, alternate| match *alternate {
1187 VariantAlternates::Swash(_)
1188 | VariantAlternates::Stylistic(_)
1189 | VariantAlternates::Ornaments(_)
1190 | VariantAlternates::Annotation(_) => acc + 1,
1191 VariantAlternates::Styleset(ref slice)
1192 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1193 _ => acc,
1194 })
1195 }
1196}
1197
1198impl Parse for FontVariantAlternates {
1199 fn parse(_: &ParserContext, input: &mut Parser) -> Result<FontVariantAlternates, ParseError> {
1208 if input
1209 .try_parse(|input| input.expect_ident_matching("normal"))
1210 .is_ok()
1211 {
1212 return Ok(Default::default());
1213 }
1214
1215 let mut stylistic = None;
1216 let mut historical = None;
1217 let mut styleset = None;
1218 let mut character_variant = None;
1219 let mut swash = None;
1220 let mut ornaments = None;
1221 let mut annotation = None;
1222
1223 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1225 macro_rules! check_if_parsed(
1226 ($input:expr, $flag:path) => (
1227 if parsed_alternates.contains($flag) {
1228 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1229 }
1230 parsed_alternates |= $flag;
1231 )
1232 );
1233 while input.try_parse(|input| match *input.next()? {
1234 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1235 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1236 historical = Some(VariantAlternates::HistoricalForms);
1237 Ok(())
1238 },
1239 Token::Function(ref name) => {
1240 let name = name.clone();
1241 input.parse_nested_block(|i| {
1242 match_ignore_ascii_case! { &name,
1243 "swash" => {
1244 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1245 let ident = CustomIdent::parse(i, &[])?;
1246 swash = Some(VariantAlternates::Swash(ident));
1247 Ok(())
1248 },
1249 "stylistic" => {
1250 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1251 let ident = CustomIdent::parse(i, &[])?;
1252 stylistic = Some(VariantAlternates::Stylistic(ident));
1253 Ok(())
1254 },
1255 "ornaments" => {
1256 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1257 let ident = CustomIdent::parse(i, &[])?;
1258 ornaments = Some(VariantAlternates::Ornaments(ident));
1259 Ok(())
1260 },
1261 "annotation" => {
1262 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1263 let ident = CustomIdent::parse(i, &[])?;
1264 annotation = Some(VariantAlternates::Annotation(ident));
1265 Ok(())
1266 },
1267 "styleset" => {
1268 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1269 let idents = i.parse_comma_separated(|i| {
1270 CustomIdent::parse(i, &[])
1271 })?;
1272 styleset = Some(VariantAlternates::Styleset(idents.into()));
1273 Ok(())
1274 },
1275 "character-variant" => {
1276 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1277 let idents = i.parse_comma_separated(|i| {
1278 CustomIdent::parse(i, &[])
1279 })?;
1280 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1281 Ok(())
1282 },
1283 _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
1284 }
1285 })
1286 },
1287 _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
1288 }).is_ok() {}
1289
1290 if parsed_alternates.is_empty() {
1291 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1292 }
1293
1294 let mut alternates = Vec::new();
1296 macro_rules! push_if_some(
1297 ($value:expr) => (
1298 if let Some(v) = $value {
1299 alternates.push(v);
1300 }
1301 )
1302 );
1303 push_if_some!(stylistic);
1304 push_if_some!(historical);
1305 push_if_some!(styleset);
1306 push_if_some!(character_variant);
1307 push_if_some!(swash);
1308 push_if_some!(ornaments);
1309 push_if_some!(annotation);
1310
1311 Ok(FontVariantAlternates(alternates.into()))
1312 }
1313}
1314
1315#[derive(
1316 Clone,
1317 Copy,
1318 Debug,
1319 Deserialize,
1320 Eq,
1321 Hash,
1322 MallocSizeOf,
1323 PartialEq,
1324 Parse,
1325 Serialize,
1326 SpecifiedValueInfo,
1327 ToComputedValue,
1328 ToCss,
1329 ToResolvedValue,
1330 ToShmem,
1331 ToTyped,
1332)]
1333#[css(bitflags(
1334 single = "normal",
1335 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1336 validate_mixed = "Self::validate_mixed_flags",
1337))]
1338#[repr(C)]
1339pub struct FontVariantEastAsian(u16);
1341bitflags! {
1342 impl FontVariantEastAsian: u16 {
1343 const NORMAL = 0;
1345 const JIS78 = 1 << 0;
1347 const JIS83 = 1 << 1;
1349 const JIS90 = 1 << 2;
1351 const JIS04 = 1 << 3;
1353 const SIMPLIFIED = 1 << 4;
1355 const TRADITIONAL = 1 << 5;
1357
1358 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1360
1361 const FULL_WIDTH = 1 << 6;
1363 const PROPORTIONAL_WIDTH = 1 << 7;
1365 const RUBY = 1 << 8;
1367 }
1368}
1369
1370impl FontVariantEastAsian {
1371 pub const COUNT: usize = 9;
1373
1374 fn validate_mixed_flags(&self) -> bool {
1375 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1376 return false;
1378 }
1379 let jis = self.intersection(Self::JIS_GROUP);
1380 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1381 return false;
1382 }
1383 true
1384 }
1385}
1386
1387#[derive(
1388 Clone,
1389 Copy,
1390 Debug,
1391 Deserialize,
1392 Eq,
1393 Hash,
1394 MallocSizeOf,
1395 PartialEq,
1396 Parse,
1397 Serialize,
1398 SpecifiedValueInfo,
1399 ToComputedValue,
1400 ToCss,
1401 ToResolvedValue,
1402 ToShmem,
1403 ToTyped,
1404)]
1405#[css(bitflags(
1406 single = "normal,none",
1407 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1408 validate_mixed = "Self::validate_mixed_flags",
1409))]
1410#[repr(C)]
1411pub struct FontVariantLigatures(u16);
1413bitflags! {
1414 impl FontVariantLigatures: u16 {
1415 const NORMAL = 0;
1417 const NONE = 1;
1419 const COMMON_LIGATURES = 1 << 1;
1421 const NO_COMMON_LIGATURES = 1 << 2;
1423 const DISCRETIONARY_LIGATURES = 1 << 3;
1425 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1427 const HISTORICAL_LIGATURES = 1 << 5;
1429 const NO_HISTORICAL_LIGATURES = 1 << 6;
1431 const CONTEXTUAL = 1 << 7;
1433 const NO_CONTEXTUAL = 1 << 8;
1435 }
1436}
1437
1438impl FontVariantLigatures {
1439 pub const COUNT: usize = 9;
1441
1442 fn validate_mixed_flags(&self) -> bool {
1443 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1445 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1446 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1447 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1448 {
1449 return false;
1450 }
1451 true
1452 }
1453}
1454
1455#[derive(
1457 Clone,
1458 Copy,
1459 Debug,
1460 Deserialize,
1461 Eq,
1462 Hash,
1463 MallocSizeOf,
1464 PartialEq,
1465 Parse,
1466 Serialize,
1467 SpecifiedValueInfo,
1468 ToComputedValue,
1469 ToCss,
1470 ToResolvedValue,
1471 ToShmem,
1472 ToTyped,
1473)]
1474#[css(bitflags(
1475 single = "normal",
1476 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1477 validate_mixed = "Self::validate_mixed_flags",
1478))]
1479#[repr(C)]
1480pub struct FontVariantNumeric(u8);
1481bitflags! {
1482 impl FontVariantNumeric : u8 {
1483 const NORMAL = 0;
1485 const LINING_NUMS = 1 << 0;
1487 const OLDSTYLE_NUMS = 1 << 1;
1489 const PROPORTIONAL_NUMS = 1 << 2;
1491 const TABULAR_NUMS = 1 << 3;
1493 const DIAGONAL_FRACTIONS = 1 << 4;
1495 const STACKED_FRACTIONS = 1 << 5;
1497 const SLASHED_ZERO = 1 << 6;
1499 const ORDINAL = 1 << 7;
1501 }
1502}
1503
1504impl FontVariantNumeric {
1505 pub const COUNT: usize = 8;
1507
1508 fn validate_mixed_flags(&self) -> bool {
1518 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1519 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1520 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1521 {
1522 return false;
1523 }
1524 true
1525 }
1526}
1527
1528pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1530
1531impl FontFeatureSettings {
1532 pub fn parse_for_font_face_rule(
1535 context: &ParserContext,
1536 input: &mut Parser,
1537 ) -> Result<Self, ParseError> {
1538 let settings = FontFeatureSettings::parse(context, input)?;
1539 if settings
1540 .0
1541 .iter()
1542 .any(|setting| setting.value.resolve().is_none())
1543 {
1544 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1545 }
1546 Ok(settings)
1547 }
1548}
1549
1550pub use crate::values::computed::font::FontLanguageOverride;
1552
1553impl Parse for FontLanguageOverride {
1554 fn parse(_: &ParserContext, input: &mut Parser) -> Result<FontLanguageOverride, ParseError> {
1556 if input
1557 .try_parse(|input| input.expect_ident_matching("normal"))
1558 .is_ok()
1559 {
1560 return Ok(FontLanguageOverride::normal());
1561 }
1562
1563 let string = input.expect_string()?;
1564
1565 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1568 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1569 }
1570
1571 let mut bytes = [b' '; 4];
1572 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1573 *byte = *str_byte;
1574 }
1575
1576 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1577 }
1578}
1579
1580#[repr(u8)]
1582#[derive(
1583 Clone,
1584 Copy,
1585 Debug,
1586 Deserialize,
1587 Eq,
1588 Hash,
1589 MallocSizeOf,
1590 Parse,
1591 PartialEq,
1592 Serialize,
1593 SpecifiedValueInfo,
1594 ToComputedValue,
1595 ToCss,
1596 ToResolvedValue,
1597 ToShmem,
1598 ToTyped,
1599)]
1600pub enum FontSynthesis {
1601 Auto,
1603 None,
1605}
1606
1607#[repr(u8)]
1609#[derive(
1610 Clone,
1611 Copy,
1612 Debug,
1613 Deserialize,
1614 Eq,
1615 Hash,
1616 MallocSizeOf,
1617 Parse,
1618 PartialEq,
1619 Serialize,
1620 SpecifiedValueInfo,
1621 ToComputedValue,
1622 ToCss,
1623 ToResolvedValue,
1624 ToShmem,
1625 ToTyped,
1626)]
1627pub enum FontSynthesisStyle {
1628 Auto,
1630 None,
1632 ObliqueOnly,
1634}
1635
1636#[derive(
1637 Clone,
1638 Debug,
1639 Eq,
1640 MallocSizeOf,
1641 PartialEq,
1642 SpecifiedValueInfo,
1643 ToComputedValue,
1644 ToResolvedValue,
1645 ToShmem,
1646 ToTyped,
1647)]
1648#[repr(C)]
1649#[typed(todo_derive_fields)]
1650pub struct FontPalette(Atom);
1653
1654#[allow(missing_docs)]
1655impl FontPalette {
1656 pub fn normal() -> Self {
1657 Self(atom!("normal"))
1658 }
1659 pub fn light() -> Self {
1660 Self(atom!("light"))
1661 }
1662 pub fn dark() -> Self {
1663 Self(atom!("dark"))
1664 }
1665}
1666
1667impl Parse for FontPalette {
1668 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<FontPalette, ParseError> {
1670 let ident = input.expect_ident()?;
1671 match_ignore_ascii_case! { &ident,
1672 "normal" => Ok(Self::normal()),
1673 "light" => Ok(Self::light()),
1674 "dark" => Ok(Self::dark()),
1675 _ => if ident.starts_with("--") {
1676 Ok(Self(Atom::from(ident.as_ref())))
1677 } else {
1678 Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
1679 },
1680 }
1681 }
1682}
1683
1684impl ToCss for FontPalette {
1685 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1686 where
1687 W: Write,
1688 {
1689 serialize_atom_identifier(&self.0, dest)
1690 }
1691}
1692
1693pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1696
1697fn parse_one_feature_value(
1698 context: &ParserContext,
1699 input: &mut Parser,
1700) -> Result<Integer, ParseError> {
1701 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1702 return Ok(integer);
1703 }
1704
1705 try_match_ident_ignore_ascii_case! { input,
1706 "on" => Ok(Integer::new(1)),
1707 "off" => Ok(Integer::new(0)),
1708 }
1709}
1710
1711impl Parse for FeatureTagValue<Integer> {
1712 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1714 let tag = FontTag::parse(context, input)?;
1715 let value = input
1716 .try_parse(|i| parse_one_feature_value(context, i))
1717 .unwrap_or_else(|_| Integer::new(1));
1718
1719 Ok(Self { tag, value })
1720 }
1721}
1722
1723impl Parse for VariationValue<Number> {
1724 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1727 let tag = FontTag::parse(context, input)?;
1728 let value = Number::parse(context, input)?;
1729 Ok(Self { tag, value })
1730 }
1731}
1732
1733impl FontVariationSettings {
1734 pub fn parse_for_font_face_rule(
1737 context: &ParserContext,
1738 input: &mut Parser,
1739 ) -> Result<Self, ParseError> {
1740 let settings = FontVariationSettings::parse(context, input)?;
1741 if settings
1742 .0
1743 .iter()
1744 .any(|setting| setting.value.resolve().is_none())
1745 {
1746 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1747 }
1748 Ok(settings)
1749 }
1750}
1751
1752#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1756pub enum MetricsOverride {
1757 Override(NonNegativePercentage),
1759 Normal,
1761}
1762
1763impl MetricsOverride {
1764 #[inline]
1765 pub fn normal() -> MetricsOverride {
1767 MetricsOverride::Normal
1768 }
1769
1770 #[inline]
1776 pub fn compute(&self) -> Option<ComputedPercentage> {
1777 Some(ComputedPercentage(match self {
1778 MetricsOverride::Normal => -1.0,
1779 MetricsOverride::Override(percent) => percent.compute()?.0,
1780 }))
1781 }
1782}
1783
1784#[derive(
1785 Clone,
1786 Copy,
1787 Debug,
1788 Deserialize,
1789 MallocSizeOf,
1790 Parse,
1791 PartialEq,
1792 Serialize,
1793 SpecifiedValueInfo,
1794 ToComputedValue,
1795 ToCss,
1796 ToResolvedValue,
1797 ToShmem,
1798 ToTyped,
1799)]
1800#[repr(u8)]
1801pub enum XTextScale {
1803 All,
1805 ZoomOnly,
1807 None,
1809}
1810
1811impl XTextScale {
1812 #[inline]
1814 pub fn text_zoom_enabled(self) -> bool {
1815 self != Self::None
1816 }
1817}
1818
1819#[derive(
1820 Clone,
1821 Debug,
1822 Deserialize,
1823 Eq,
1824 Hash,
1825 MallocSizeOf,
1826 PartialEq,
1827 Serialize,
1828 SpecifiedValueInfo,
1829 ToComputedValue,
1830 ToCss,
1831 ToResolvedValue,
1832 ToShmem,
1833 ToTyped,
1834)]
1835#[repr(transparent)]
1836pub struct XLang(#[css(skip)] pub Atom);
1838
1839impl XLang {
1840 #[inline]
1841 pub fn get_initial_value() -> XLang {
1843 XLang(atom!(""))
1844 }
1845}
1846
1847impl Parse for XLang {
1848 fn parse(_: &ParserContext, _input: &mut Parser) -> Result<XLang, ParseError> {
1849 debug_assert!(
1850 false,
1851 "Should be set directly by presentation attributes only."
1852 );
1853 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1854 }
1855}
1856
1857#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1858pub struct MozScriptMinSize(pub NoCalcLength);
1861
1862impl MozScriptMinSize {
1863 #[inline]
1864 pub fn get_initial_value() -> Length {
1866 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1867 }
1868}
1869
1870impl Parse for MozScriptMinSize {
1871 fn parse(_: &ParserContext, _input: &mut Parser) -> Result<MozScriptMinSize, ParseError> {
1872 debug_assert!(
1873 false,
1874 "Should be set directly by presentation attributes only."
1875 );
1876 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1877 }
1878}
1879
1880#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1883pub enum MathDepth {
1884 AutoAdd,
1886
1887 #[css(function)]
1889 Add(Integer),
1890
1891 Absolute(Integer),
1893}
1894
1895impl Parse for MathDepth {
1896 fn parse(context: &ParserContext, input: &mut Parser) -> Result<MathDepth, ParseError> {
1897 if input
1898 .try_parse(|i| i.expect_ident_matching("auto-add"))
1899 .is_ok()
1900 {
1901 return Ok(MathDepth::AutoAdd);
1902 }
1903 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1904 return Ok(MathDepth::Absolute(math_depth_value));
1905 }
1906 input.expect_function_matching("add")?;
1907 let math_depth_delta_value =
1908 input.parse_nested_block(|input| Integer::parse(context, input))?;
1909 Ok(MathDepth::Add(math_depth_delta_value))
1910 }
1911}
1912
1913#[derive(
1914 Clone,
1915 Copy,
1916 Debug,
1917 MallocSizeOf,
1918 PartialEq,
1919 SpecifiedValueInfo,
1920 ToComputedValue,
1921 ToCss,
1922 ToResolvedValue,
1923 ToShmem,
1924)]
1925pub struct MozScriptSizeMultiplier(pub f32);
1930
1931impl MozScriptSizeMultiplier {
1932 #[inline]
1933 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1935 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1936 }
1937}
1938
1939impl Parse for MozScriptSizeMultiplier {
1940 fn parse(
1941 _: &ParserContext,
1942 _input: &mut Parser,
1943 ) -> Result<MozScriptSizeMultiplier, ParseError> {
1944 debug_assert!(
1945 false,
1946 "Should be set directly by presentation attributes only."
1947 );
1948 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1949 }
1950}
1951
1952impl From<f32> for MozScriptSizeMultiplier {
1953 fn from(v: f32) -> Self {
1954 MozScriptSizeMultiplier(v)
1955 }
1956}
1957
1958impl From<MozScriptSizeMultiplier> for f32 {
1959 fn from(v: MozScriptSizeMultiplier) -> f32 {
1960 v.0
1961 }
1962}
1963
1964pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1966
1967fn parse_line_height_number(
1971 context: &ParserContext,
1972 input: &mut Parser,
1973) -> Result<NonNegativeNumber, ParseError> {
1974 parse_number_with_clamping_mode(
1975 context,
1976 input,
1977 AllowedNumericType::NonNegative,
1978 PercentageContext::allowed_with_hint(NumericBaseType::Length),
1979 )
1980 .map(NonNegative::<Number>)
1981}
1982
1983impl Parse for LineHeight {
1984 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1985 if let Ok(v) = input.try_parse(|input| parse_line_height_number(context, input)) {
1986 return Ok(GenericLineHeight::Number(v));
1987 }
1988 if let Ok(v) = input.try_parse(|input| NonNegativeLengthPercentage::parse(context, input)) {
1989 return Ok(GenericLineHeight::Length(v));
1990 }
1991 let ident = input.expect_ident()?;
1992 match_ignore_ascii_case! { &ident,
1993 "normal" => Ok(GenericLineHeight::Normal),
1994 _ => Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
1995 }
1996 }
1997}
1998
1999fn resolve_line_height_length(context: &Context, length: NoCalcLength) -> CSSPixelLength {
2002 let result = length.to_computed_value_with_base_size(
2003 context,
2004 FontBaseSize::CurrentStyle,
2005 LineHeightBase::InheritedStyle,
2006 );
2007 if length.should_zoom_text() {
2008 context.maybe_zoom_text(result)
2009 } else {
2010 result
2011 }
2012}
2013
2014fn map_line_height_leaf(context: &Context, leaf: &Leaf) -> Leaf {
2017 let length = match leaf {
2018 Leaf::Percentage(p) => NoCalcLength::from_em(p.get()),
2019 Leaf::Length(l) => *l,
2020 _ => return leaf.clone(),
2021 };
2022 Leaf::Length(NoCalcLength::from_px(
2023 resolve_line_height_length(context, length).px(),
2024 ))
2025}
2026
2027impl ToComputedValue for LineHeight {
2028 type ComputedValue = computed::LineHeight;
2029
2030 #[inline]
2031 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
2032 match self {
2033 GenericLineHeight::Normal => GenericLineHeight::Normal,
2034 GenericLineHeight::Number(number) => {
2035 let value = match number.as_calc() {
2036 None => number.to_computed_value(context).0,
2037 Some(calc) => {
2038 let resolved = calc
2039 .node
2040 .resolve_map(|leaf| Ok(map_line_height_leaf(context, leaf)));
2041 let value = match resolved {
2042 Ok(Leaf::Number(n)) => n.get(),
2043 _ => {
2044 debug_assert!(
2045 false,
2046 "Unexpected LineHeight number calc without resolved number"
2047 );
2048 f32::NAN
2049 },
2050 };
2051 calc.clamping_mode
2053 .clamp(crate::values::normalize(value).min(f32::MAX))
2054 },
2055 };
2056 GenericLineHeight::Number(NonNegative(value))
2057 },
2058 GenericLineHeight::Length(non_negative_lp) => {
2059 let result = match non_negative_lp.0 {
2060 LengthPercentage::Length(ref length) => {
2061 resolve_line_height_length(context, *length)
2062 },
2063 LengthPercentage::Percentage(ref p) => {
2064 resolve_line_height_length(context, NoCalcLength::from_em(p.get()))
2065 },
2066 LengthPercentage::Calc(ref calc) => calc
2067 .to_computed_value_zoomed(
2068 context,
2069 FontBaseSize::CurrentStyle,
2070 LineHeightBase::InheritedStyle,
2071 )
2072 .resolve(FontBaseSize::CurrentStyle.resolve(context).computed_size()),
2073 };
2074 GenericLineHeight::Length(result.into())
2075 },
2076 }
2077 }
2078
2079 #[inline]
2080 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
2081 match *computed {
2082 GenericLineHeight::Normal => GenericLineHeight::Normal,
2083 GenericLineHeight::Number(ref number) => {
2084 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
2085 },
2086 GenericLineHeight::Length(ref length) => {
2087 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
2088 },
2089 }
2090 }
2091}
2092
2093#[repr(C)]
2095pub struct QueryFontMetricsFlags(u8);
2096
2097bitflags! {
2098 impl QueryFontMetricsFlags: u8 {
2099 const USE_USER_FONT_SET = 1 << 0;
2101 const NEEDS_CH = 1 << 1;
2103 const NEEDS_IC = 1 << 2;
2105 const NEEDS_MATH_SCALES = 1 << 3;
2107 }
2108}
2109
2110#[allow(missing_docs)]
2112#[derive(
2113 Clone,
2114 Copy,
2115 Debug,
2116 Deserialize,
2117 Eq,
2118 FromPrimitive,
2119 Hash,
2120 MallocSizeOf,
2121 Parse,
2122 PartialEq,
2123 Serialize,
2124 SpecifiedValueInfo,
2125 ToComputedValue,
2126 ToCss,
2127 ToResolvedValue,
2128 ToShmem,
2129 ToTyped,
2130)]
2131#[repr(u8)]
2132pub enum MathShift {
2133 Normal,
2134 Compact,
2135}
2136
2137#[allow(missing_docs)]
2139#[derive(
2140 Clone,
2141 Copy,
2142 Debug,
2143 Deserialize,
2144 Eq,
2145 FromPrimitive,
2146 Hash,
2147 MallocSizeOf,
2148 Parse,
2149 PartialEq,
2150 Serialize,
2151 SpecifiedValueInfo,
2152 ToComputedValue,
2153 ToCss,
2154 ToResolvedValue,
2155 ToShmem,
2156 ToTyped,
2157)]
2158#[repr(u8)]
2159pub enum MathStyle {
2160 Normal,
2161 Compact,
2162}
2163
2164#[allow(missing_docs)]
2166#[derive(
2167 Clone,
2168 Copy,
2169 Debug,
2170 Deserialize,
2171 Eq,
2172 FromPrimitive,
2173 Hash,
2174 MallocSizeOf,
2175 Parse,
2176 PartialEq,
2177 Serialize,
2178 SpecifiedValueInfo,
2179 ToComputedValue,
2180 ToCss,
2181 ToResolvedValue,
2182 ToShmem,
2183 ToTyped,
2184)]
2185#[repr(u8)]
2186pub enum MathVariant {
2187 None,
2188 Normal,
2189 Bold,
2190 Italic,
2191 BoldItalic,
2192 Script,
2193 BoldScript,
2194 Fraktur,
2195 DoubleStruck,
2196 BoldFraktur,
2197 SansSerif,
2198 BoldSansSerif,
2199 SansSerifItalic,
2200 SansSerifBoldItalic,
2201 Monospace,
2202 Initial,
2203 Tailed,
2204 Looped,
2205 Stretched,
2206}
2207
2208#[allow(missing_docs)]
2210#[derive(
2211 Clone,
2212 Copy,
2213 Debug,
2214 Deserialize,
2215 Eq,
2216 FromPrimitive,
2217 Hash,
2218 MallocSizeOf,
2219 Parse,
2220 PartialEq,
2221 Serialize,
2222 SpecifiedValueInfo,
2223 ToComputedValue,
2224 ToCss,
2225 ToResolvedValue,
2226 ToShmem,
2227 ToTyped,
2228)]
2229#[repr(u8)]
2230pub enum FontVariantEmoji {
2231 Normal,
2232 Text,
2233 Emoji,
2234 Unicode,
2235}
2236
2237#[allow(missing_docs)]
2239#[derive(
2240 Clone,
2241 Copy,
2242 Debug,
2243 Deserialize,
2244 Eq,
2245 FromPrimitive,
2246 Hash,
2247 MallocSizeOf,
2248 Parse,
2249 PartialEq,
2250 Serialize,
2251 SpecifiedValueInfo,
2252 ToComputedValue,
2253 ToCss,
2254 ToResolvedValue,
2255 ToShmem,
2256 ToTyped,
2257)]
2258#[repr(u8)]
2259pub enum FontOpticalSizing {
2260 Auto,
2261 None,
2262}
2263
2264#[allow(missing_docs)]
2266#[derive(
2267 Clone,
2268 Copy,
2269 Debug,
2270 Deserialize,
2271 Eq,
2272 FromPrimitive,
2273 Hash,
2274 MallocSizeOf,
2275 Parse,
2276 PartialEq,
2277 Serialize,
2278 SpecifiedValueInfo,
2279 ToComputedValue,
2280 ToCss,
2281 ToResolvedValue,
2282 ToShmem,
2283 ToTyped,
2284)]
2285#[repr(u8)]
2286pub enum FontKerning {
2287 Auto,
2288 None,
2289 Normal,
2290}
2291
2292#[allow(missing_docs)]
2294#[derive(
2295 Clone,
2296 Copy,
2297 Debug,
2298 Deserialize,
2299 Eq,
2300 FromPrimitive,
2301 Hash,
2302 MallocSizeOf,
2303 Parse,
2304 PartialEq,
2305 Serialize,
2306 SpecifiedValueInfo,
2307 ToComputedValue,
2308 ToCss,
2309 ToResolvedValue,
2310 ToShmem,
2311 ToTyped,
2312)]
2313#[repr(u8)]
2314pub enum FontSmoothing {
2315 Auto,
2316 #[cfg_attr(feature = "gecko", parse(aliases = "antialiased"))]
2317 Grayscale,
2318 SubpixelAntialiased,
2319}
2320
2321#[allow(missing_docs)]
2323#[derive(
2324 Clone,
2325 Copy,
2326 Debug,
2327 Deserialize,
2328 Eq,
2329 FromPrimitive,
2330 Hash,
2331 MallocSizeOf,
2332 Parse,
2333 PartialEq,
2334 Serialize,
2335 SpecifiedValueInfo,
2336 ToComputedValue,
2337 ToCss,
2338 ToResolvedValue,
2339 ToShmem,
2340 ToTyped,
2341)]
2342#[repr(u8)]
2343pub enum FontVariantPosition {
2344 Normal,
2345 Sub,
2346 Super,
2347}
2348
2349fn font_variant_caps_all_values_enabled(_: &ParserContext) -> bool {
2350 crate::pref!(
2351 "layout.css.font-variant-caps.all-values.enabled",
2352 gecko = true
2353 )
2354}
2355
2356#[allow(missing_docs)]
2358#[derive(
2359 Clone,
2360 Copy,
2361 Debug,
2362 Deserialize,
2363 Eq,
2364 FromPrimitive,
2365 Hash,
2366 MallocSizeOf,
2367 Parse,
2368 PartialEq,
2369 Serialize,
2370 SpecifiedValueInfo,
2371 ToComputedValue,
2372 ToCss,
2373 ToResolvedValue,
2374 ToShmem,
2375 ToTyped,
2376)]
2377#[repr(u8)]
2378pub enum FontVariantCaps {
2379 Normal,
2380 SmallCaps,
2381 #[parse(condition = "font_variant_caps_all_values_enabled")]
2382 AllSmallCaps,
2383 #[parse(condition = "font_variant_caps_all_values_enabled")]
2384 PetiteCaps,
2385 #[parse(condition = "font_variant_caps_all_values_enabled")]
2386 AllPetiteCaps,
2387 #[parse(condition = "font_variant_caps_all_values_enabled")]
2388 Unicase,
2389 #[parse(condition = "font_variant_caps_all_values_enabled")]
2390 TitlingCaps,
2391}