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 Eq,
1614 MallocSizeOf,
1615 Parse,
1616 PartialEq,
1617 SpecifiedValueInfo,
1618 ToComputedValue,
1619 ToCss,
1620 ToResolvedValue,
1621 ToShmem,
1622 ToTyped,
1623)]
1624pub enum FontSynthesisStyle {
1625 Auto,
1627 None,
1629 ObliqueOnly,
1631}
1632
1633#[derive(
1634 Clone,
1635 Debug,
1636 Eq,
1637 MallocSizeOf,
1638 PartialEq,
1639 SpecifiedValueInfo,
1640 ToComputedValue,
1641 ToResolvedValue,
1642 ToShmem,
1643 ToTyped,
1644)]
1645#[repr(C)]
1646#[typed(todo_derive_fields)]
1647pub struct FontPalette(Atom);
1650
1651#[allow(missing_docs)]
1652impl FontPalette {
1653 pub fn normal() -> Self {
1654 Self(atom!("normal"))
1655 }
1656 pub fn light() -> Self {
1657 Self(atom!("light"))
1658 }
1659 pub fn dark() -> Self {
1660 Self(atom!("dark"))
1661 }
1662}
1663
1664impl Parse for FontPalette {
1665 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<FontPalette, ParseError> {
1667 let ident = input.expect_ident()?;
1668 match_ignore_ascii_case! { &ident,
1669 "normal" => Ok(Self::normal()),
1670 "light" => Ok(Self::light()),
1671 "dark" => Ok(Self::dark()),
1672 _ => if ident.starts_with("--") {
1673 Ok(Self(Atom::from(ident.as_ref())))
1674 } else {
1675 Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
1676 },
1677 }
1678 }
1679}
1680
1681impl ToCss for FontPalette {
1682 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1683 where
1684 W: Write,
1685 {
1686 serialize_atom_identifier(&self.0, dest)
1687 }
1688}
1689
1690pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1693
1694fn parse_one_feature_value(
1695 context: &ParserContext,
1696 input: &mut Parser,
1697) -> Result<Integer, ParseError> {
1698 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1699 return Ok(integer);
1700 }
1701
1702 try_match_ident_ignore_ascii_case! { input,
1703 "on" => Ok(Integer::new(1)),
1704 "off" => Ok(Integer::new(0)),
1705 }
1706}
1707
1708impl Parse for FeatureTagValue<Integer> {
1709 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1711 let tag = FontTag::parse(context, input)?;
1712 let value = input
1713 .try_parse(|i| parse_one_feature_value(context, i))
1714 .unwrap_or_else(|_| Integer::new(1));
1715
1716 Ok(Self { tag, value })
1717 }
1718}
1719
1720impl Parse for VariationValue<Number> {
1721 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1724 let tag = FontTag::parse(context, input)?;
1725 let value = Number::parse(context, input)?;
1726 Ok(Self { tag, value })
1727 }
1728}
1729
1730impl FontVariationSettings {
1731 pub fn parse_for_font_face_rule(
1734 context: &ParserContext,
1735 input: &mut Parser,
1736 ) -> Result<Self, ParseError> {
1737 let settings = FontVariationSettings::parse(context, input)?;
1738 if settings
1739 .0
1740 .iter()
1741 .any(|setting| setting.value.resolve().is_none())
1742 {
1743 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1744 }
1745 Ok(settings)
1746 }
1747}
1748
1749#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1753pub enum MetricsOverride {
1754 Override(NonNegativePercentage),
1756 Normal,
1758}
1759
1760impl MetricsOverride {
1761 #[inline]
1762 pub fn normal() -> MetricsOverride {
1764 MetricsOverride::Normal
1765 }
1766
1767 #[inline]
1773 pub fn compute(&self) -> Option<ComputedPercentage> {
1774 Some(ComputedPercentage(match self {
1775 MetricsOverride::Normal => -1.0,
1776 MetricsOverride::Override(percent) => percent.compute()?.0,
1777 }))
1778 }
1779}
1780
1781#[derive(
1782 Clone,
1783 Copy,
1784 Debug,
1785 Deserialize,
1786 MallocSizeOf,
1787 Parse,
1788 PartialEq,
1789 Serialize,
1790 SpecifiedValueInfo,
1791 ToComputedValue,
1792 ToCss,
1793 ToResolvedValue,
1794 ToShmem,
1795 ToTyped,
1796)]
1797#[repr(u8)]
1798pub enum XTextScale {
1800 All,
1802 ZoomOnly,
1804 None,
1806}
1807
1808impl XTextScale {
1809 #[inline]
1811 pub fn text_zoom_enabled(self) -> bool {
1812 self != Self::None
1813 }
1814}
1815
1816#[derive(
1817 Clone,
1818 Debug,
1819 Deserialize,
1820 Eq,
1821 Hash,
1822 MallocSizeOf,
1823 PartialEq,
1824 Serialize,
1825 SpecifiedValueInfo,
1826 ToComputedValue,
1827 ToCss,
1828 ToResolvedValue,
1829 ToShmem,
1830 ToTyped,
1831)]
1832#[repr(transparent)]
1833pub struct XLang(#[css(skip)] pub Atom);
1835
1836impl XLang {
1837 #[inline]
1838 pub fn get_initial_value() -> XLang {
1840 XLang(atom!(""))
1841 }
1842}
1843
1844impl Parse for XLang {
1845 fn parse(_: &ParserContext, _input: &mut Parser) -> Result<XLang, ParseError> {
1846 debug_assert!(
1847 false,
1848 "Should be set directly by presentation attributes only."
1849 );
1850 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1851 }
1852}
1853
1854#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1855pub struct MozScriptMinSize(pub NoCalcLength);
1858
1859impl MozScriptMinSize {
1860 #[inline]
1861 pub fn get_initial_value() -> Length {
1863 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1864 }
1865}
1866
1867impl Parse for MozScriptMinSize {
1868 fn parse(_: &ParserContext, _input: &mut Parser) -> Result<MozScriptMinSize, ParseError> {
1869 debug_assert!(
1870 false,
1871 "Should be set directly by presentation attributes only."
1872 );
1873 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1874 }
1875}
1876
1877#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1880pub enum MathDepth {
1881 AutoAdd,
1883
1884 #[css(function)]
1886 Add(Integer),
1887
1888 Absolute(Integer),
1890}
1891
1892impl Parse for MathDepth {
1893 fn parse(context: &ParserContext, input: &mut Parser) -> Result<MathDepth, ParseError> {
1894 if input
1895 .try_parse(|i| i.expect_ident_matching("auto-add"))
1896 .is_ok()
1897 {
1898 return Ok(MathDepth::AutoAdd);
1899 }
1900 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1901 return Ok(MathDepth::Absolute(math_depth_value));
1902 }
1903 input.expect_function_matching("add")?;
1904 let math_depth_delta_value =
1905 input.parse_nested_block(|input| Integer::parse(context, input))?;
1906 Ok(MathDepth::Add(math_depth_delta_value))
1907 }
1908}
1909
1910#[derive(
1911 Clone,
1912 Copy,
1913 Debug,
1914 MallocSizeOf,
1915 PartialEq,
1916 SpecifiedValueInfo,
1917 ToComputedValue,
1918 ToCss,
1919 ToResolvedValue,
1920 ToShmem,
1921)]
1922pub struct MozScriptSizeMultiplier(pub f32);
1927
1928impl MozScriptSizeMultiplier {
1929 #[inline]
1930 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1932 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1933 }
1934}
1935
1936impl Parse for MozScriptSizeMultiplier {
1937 fn parse(
1938 _: &ParserContext,
1939 _input: &mut Parser,
1940 ) -> Result<MozScriptSizeMultiplier, ParseError> {
1941 debug_assert!(
1942 false,
1943 "Should be set directly by presentation attributes only."
1944 );
1945 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1946 }
1947}
1948
1949impl From<f32> for MozScriptSizeMultiplier {
1950 fn from(v: f32) -> Self {
1951 MozScriptSizeMultiplier(v)
1952 }
1953}
1954
1955impl From<MozScriptSizeMultiplier> for f32 {
1956 fn from(v: MozScriptSizeMultiplier) -> f32 {
1957 v.0
1958 }
1959}
1960
1961pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1963
1964fn parse_line_height_number(
1968 context: &ParserContext,
1969 input: &mut Parser,
1970) -> Result<NonNegativeNumber, ParseError> {
1971 parse_number_with_clamping_mode(
1972 context,
1973 input,
1974 AllowedNumericType::NonNegative,
1975 PercentageContext::allowed_with_hint(NumericBaseType::Length),
1976 )
1977 .map(NonNegative::<Number>)
1978}
1979
1980impl Parse for LineHeight {
1981 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1982 if let Ok(v) = input.try_parse(|input| parse_line_height_number(context, input)) {
1983 return Ok(GenericLineHeight::Number(v));
1984 }
1985 if let Ok(v) = input.try_parse(|input| NonNegativeLengthPercentage::parse(context, input)) {
1986 return Ok(GenericLineHeight::Length(v));
1987 }
1988 let ident = input.expect_ident()?;
1989 match_ignore_ascii_case! { &ident,
1990 "normal" => Ok(GenericLineHeight::Normal),
1991 _ => Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
1992 }
1993 }
1994}
1995
1996fn resolve_line_height_length(context: &Context, length: NoCalcLength) -> CSSPixelLength {
1999 let result = length.to_computed_value_with_base_size(
2000 context,
2001 FontBaseSize::CurrentStyle,
2002 LineHeightBase::InheritedStyle,
2003 );
2004 if length.should_zoom_text() {
2005 context.maybe_zoom_text(result)
2006 } else {
2007 result
2008 }
2009}
2010
2011fn map_line_height_leaf(context: &Context, leaf: &Leaf) -> Leaf {
2014 let length = match leaf {
2015 Leaf::Percentage(p) => NoCalcLength::from_em(p.get()),
2016 Leaf::Length(l) => *l,
2017 _ => return leaf.clone(),
2018 };
2019 Leaf::Length(NoCalcLength::from_px(
2020 resolve_line_height_length(context, length).px(),
2021 ))
2022}
2023
2024impl ToComputedValue for LineHeight {
2025 type ComputedValue = computed::LineHeight;
2026
2027 #[inline]
2028 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
2029 match self {
2030 GenericLineHeight::Normal => GenericLineHeight::Normal,
2031 GenericLineHeight::Number(number) => {
2032 let value = match number.as_calc() {
2033 None => number.to_computed_value(context).0,
2034 Some(calc) => {
2035 let resolved = calc
2036 .node
2037 .resolve_map(|leaf| Ok(map_line_height_leaf(context, leaf)));
2038 let value = match resolved {
2039 Ok(Leaf::Number(n)) => n.get(),
2040 _ => {
2041 debug_assert!(
2042 false,
2043 "Unexpected LineHeight number calc without resolved number"
2044 );
2045 f32::NAN
2046 },
2047 };
2048 calc.clamping_mode
2050 .clamp(crate::values::normalize(value).min(f32::MAX))
2051 },
2052 };
2053 GenericLineHeight::Number(NonNegative(value))
2054 },
2055 GenericLineHeight::Length(non_negative_lp) => {
2056 let result = match non_negative_lp.0 {
2057 LengthPercentage::Length(ref length) => {
2058 resolve_line_height_length(context, *length)
2059 },
2060 LengthPercentage::Percentage(ref p) => {
2061 resolve_line_height_length(context, NoCalcLength::from_em(p.get()))
2062 },
2063 LengthPercentage::Calc(ref calc) => calc
2064 .to_computed_value_zoomed(
2065 context,
2066 FontBaseSize::CurrentStyle,
2067 LineHeightBase::InheritedStyle,
2068 )
2069 .resolve(FontBaseSize::CurrentStyle.resolve(context).computed_size()),
2070 };
2071 GenericLineHeight::Length(result.into())
2072 },
2073 }
2074 }
2075
2076 #[inline]
2077 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
2078 match *computed {
2079 GenericLineHeight::Normal => GenericLineHeight::Normal,
2080 GenericLineHeight::Number(ref number) => {
2081 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
2082 },
2083 GenericLineHeight::Length(ref length) => {
2084 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
2085 },
2086 }
2087 }
2088}
2089
2090#[repr(C)]
2092pub struct QueryFontMetricsFlags(u8);
2093
2094bitflags! {
2095 impl QueryFontMetricsFlags: u8 {
2096 const USE_USER_FONT_SET = 1 << 0;
2098 const NEEDS_CH = 1 << 1;
2100 const NEEDS_IC = 1 << 2;
2102 const NEEDS_MATH_SCALES = 1 << 3;
2104 }
2105}
2106
2107#[allow(missing_docs)]
2109#[derive(
2110 Clone,
2111 Copy,
2112 Debug,
2113 Deserialize,
2114 Eq,
2115 FromPrimitive,
2116 Hash,
2117 MallocSizeOf,
2118 Parse,
2119 PartialEq,
2120 Serialize,
2121 SpecifiedValueInfo,
2122 ToComputedValue,
2123 ToCss,
2124 ToResolvedValue,
2125 ToShmem,
2126 ToTyped,
2127)]
2128#[repr(u8)]
2129pub enum MathShift {
2130 Normal,
2131 Compact,
2132}
2133
2134#[allow(missing_docs)]
2136#[derive(
2137 Clone,
2138 Copy,
2139 Debug,
2140 Deserialize,
2141 Eq,
2142 FromPrimitive,
2143 Hash,
2144 MallocSizeOf,
2145 Parse,
2146 PartialEq,
2147 Serialize,
2148 SpecifiedValueInfo,
2149 ToComputedValue,
2150 ToCss,
2151 ToResolvedValue,
2152 ToShmem,
2153 ToTyped,
2154)]
2155#[repr(u8)]
2156pub enum MathStyle {
2157 Normal,
2158 Compact,
2159}
2160
2161#[allow(missing_docs)]
2163#[derive(
2164 Clone,
2165 Copy,
2166 Debug,
2167 Deserialize,
2168 Eq,
2169 FromPrimitive,
2170 Hash,
2171 MallocSizeOf,
2172 Parse,
2173 PartialEq,
2174 Serialize,
2175 SpecifiedValueInfo,
2176 ToComputedValue,
2177 ToCss,
2178 ToResolvedValue,
2179 ToShmem,
2180 ToTyped,
2181)]
2182#[repr(u8)]
2183pub enum MathVariant {
2184 None,
2185 Normal,
2186 Bold,
2187 Italic,
2188 BoldItalic,
2189 Script,
2190 BoldScript,
2191 Fraktur,
2192 DoubleStruck,
2193 BoldFraktur,
2194 SansSerif,
2195 BoldSansSerif,
2196 SansSerifItalic,
2197 SansSerifBoldItalic,
2198 Monospace,
2199 Initial,
2200 Tailed,
2201 Looped,
2202 Stretched,
2203}
2204
2205#[allow(missing_docs)]
2207#[derive(
2208 Clone,
2209 Copy,
2210 Debug,
2211 Deserialize,
2212 Eq,
2213 FromPrimitive,
2214 Hash,
2215 MallocSizeOf,
2216 Parse,
2217 PartialEq,
2218 Serialize,
2219 SpecifiedValueInfo,
2220 ToComputedValue,
2221 ToCss,
2222 ToResolvedValue,
2223 ToShmem,
2224 ToTyped,
2225)]
2226#[repr(u8)]
2227pub enum FontVariantEmoji {
2228 Normal,
2229 Text,
2230 Emoji,
2231 Unicode,
2232}
2233
2234#[allow(missing_docs)]
2236#[derive(
2237 Clone,
2238 Copy,
2239 Debug,
2240 Deserialize,
2241 Eq,
2242 FromPrimitive,
2243 Hash,
2244 MallocSizeOf,
2245 Parse,
2246 PartialEq,
2247 Serialize,
2248 SpecifiedValueInfo,
2249 ToComputedValue,
2250 ToCss,
2251 ToResolvedValue,
2252 ToShmem,
2253 ToTyped,
2254)]
2255#[repr(u8)]
2256pub enum FontOpticalSizing {
2257 Auto,
2258 None,
2259}
2260
2261#[allow(missing_docs)]
2263#[derive(
2264 Clone,
2265 Copy,
2266 Debug,
2267 Deserialize,
2268 Eq,
2269 FromPrimitive,
2270 Hash,
2271 MallocSizeOf,
2272 Parse,
2273 PartialEq,
2274 Serialize,
2275 SpecifiedValueInfo,
2276 ToComputedValue,
2277 ToCss,
2278 ToResolvedValue,
2279 ToShmem,
2280 ToTyped,
2281)]
2282#[repr(u8)]
2283pub enum FontKerning {
2284 Auto,
2285 None,
2286 Normal,
2287}
2288
2289#[allow(missing_docs)]
2291#[derive(
2292 Clone,
2293 Copy,
2294 Debug,
2295 Deserialize,
2296 Eq,
2297 FromPrimitive,
2298 Hash,
2299 MallocSizeOf,
2300 Parse,
2301 PartialEq,
2302 Serialize,
2303 SpecifiedValueInfo,
2304 ToComputedValue,
2305 ToCss,
2306 ToResolvedValue,
2307 ToShmem,
2308 ToTyped,
2309)]
2310#[repr(u8)]
2311pub enum FontSmoothing {
2312 Auto,
2313 #[cfg_attr(feature = "gecko", parse(aliases = "antialiased"))]
2314 Grayscale,
2315 SubpixelAntialiased,
2316}
2317
2318#[allow(missing_docs)]
2320#[derive(
2321 Clone,
2322 Copy,
2323 Debug,
2324 Deserialize,
2325 Eq,
2326 FromPrimitive,
2327 Hash,
2328 MallocSizeOf,
2329 Parse,
2330 PartialEq,
2331 Serialize,
2332 SpecifiedValueInfo,
2333 ToComputedValue,
2334 ToCss,
2335 ToResolvedValue,
2336 ToShmem,
2337 ToTyped,
2338)]
2339#[repr(u8)]
2340pub enum FontVariantPosition {
2341 Normal,
2342 Sub,
2343 Super,
2344}
2345
2346#[allow(missing_docs)]
2348#[derive(
2349 Clone,
2350 Copy,
2351 Debug,
2352 Deserialize,
2353 Eq,
2354 FromPrimitive,
2355 Hash,
2356 MallocSizeOf,
2357 Parse,
2358 PartialEq,
2359 Serialize,
2360 SpecifiedValueInfo,
2361 ToComputedValue,
2362 ToCss,
2363 ToResolvedValue,
2364 ToShmem,
2365 ToTyped,
2366)]
2367#[repr(u8)]
2368pub enum FontVariantCaps {
2369 Normal,
2370 SmallCaps,
2371 #[cfg(feature = "gecko")]
2372 AllSmallCaps,
2373 #[cfg(feature = "gecko")]
2374 PetiteCaps,
2375 #[cfg(feature = "gecko")]
2376 AllPetiteCaps,
2377 #[cfg(feature = "gecko")]
2378 Unicase,
2379 #[cfg(feature = "gecko")]
2380 TitlingCaps,
2381}