1use crate::context::QuirksMode;
8use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::typed_om::NumericBaseType;
11use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
12use crate::values::computed::Percentage as ComputedPercentage;
13use crate::values::computed::{font as computed, Length, NonNegativeLength};
14use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
15use crate::values::generics::font::{
16 self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
17};
18use crate::values::generics::NonNegative;
19use crate::values::specified::calc::{Leaf, PercentageContext};
20use crate::values::specified::length::{FontBaseSize, LengthUnit, LineHeightBase, PX_PER_PT};
21use crate::values::specified::number::parse_number_with_clamping_mode;
22use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
23use crate::values::specified::{
24 NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber, NonNegativePercentage, Number,
25};
26use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
27use crate::Atom;
28use cssparser::{match_ignore_ascii_case, Parser, Token};
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 enum SystemFont {}
118
119#[allow(missing_docs)]
120#[cfg(feature = "servo")]
121impl SystemFont {
122 pub fn parse(_: &mut Parser) -> Result<Self, ()> {
123 Err(())
124 }
125}
126
127const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
128const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
129
130pub const MIN_FONT_WEIGHT: f32 = 1.;
134
135pub const MAX_FONT_WEIGHT: f32 = 1000.;
139
140#[derive(
144 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
145)]
146pub enum FontWeight {
147 Absolute(AbsoluteFontWeight),
149 Bolder,
151 Lighter,
153 #[css(skip)]
155 System(SystemFont),
156}
157
158impl FontWeight {
159 system_font_methods!(FontWeight, font_weight);
160
161 #[inline]
163 pub fn normal() -> Self {
164 FontWeight::Absolute(AbsoluteFontWeight::Normal)
165 }
166
167 pub fn from_gecko_keyword(kw: u32) -> Self {
169 debug_assert!(kw.is_multiple_of(100));
170 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
171 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
172 }
173}
174
175impl ToComputedValue for FontWeight {
176 type ComputedValue = computed::FontWeight;
177
178 #[inline]
179 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
180 match *self {
181 FontWeight::Absolute(ref abs) => abs.to_computed_value(context),
182 FontWeight::Bolder => context
183 .builder
184 .get_parent_font()
185 .clone_font_weight()
186 .bolder(),
187 FontWeight::Lighter => context
188 .builder
189 .get_parent_font()
190 .clone_font_weight()
191 .lighter(),
192 FontWeight::System(_) => self.compute_system(context),
193 }
194 }
195
196 #[inline]
197 fn from_computed_value(computed: &computed::FontWeight) -> Self {
198 FontWeight::Absolute(AbsoluteFontWeight::from_computed_value(computed))
199 }
200}
201
202#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
206pub enum AbsoluteFontWeight {
207 Weight(Number),
211 Normal,
213 Bold,
215}
216
217impl AbsoluteFontWeight {
218 pub fn compute(&self) -> Option<computed::FontWeight> {
221 match self {
222 AbsoluteFontWeight::Weight(weight) => {
223 Some(computed::FontWeight::from_float(weight.resolve()?))
224 },
225 AbsoluteFontWeight::Normal => Some(computed::FontWeight::NORMAL),
226 AbsoluteFontWeight::Bold => Some(computed::FontWeight::BOLD),
227 }
228 }
229}
230
231impl ToComputedValue for AbsoluteFontWeight {
232 type ComputedValue = computed::FontWeight;
233
234 fn to_computed_value(&self, context: &Context) -> computed::FontWeight {
235 match self {
236 AbsoluteFontWeight::Weight(weight) => {
237 computed::FontWeight::from_float(weight.to_computed_value(context))
238 },
239 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
240 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
241 }
242 }
243
244 fn from_computed_value(computed: &computed::FontWeight) -> Self {
245 AbsoluteFontWeight::Weight(Number::from_computed_value(&computed.value()))
246 }
247}
248
249impl Parse for AbsoluteFontWeight {
250 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
251 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
252 if matches!(number.get(), Some(v) if !(MIN_FONT_WEIGHT..=MAX_FONT_WEIGHT).contains(&v))
256 {
257 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
258 }
259 return Ok(AbsoluteFontWeight::Weight(number));
260 }
261
262 Ok(try_match_ident_ignore_ascii_case! { input,
263 "normal" => AbsoluteFontWeight::Normal,
264 "bold" => AbsoluteFontWeight::Bold,
265 })
266 }
267}
268
269pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
272
273impl ToCss for SpecifiedFontStyle {
274 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
275 where
276 W: Write,
277 {
278 match *self {
279 generics::FontStyle::Italic => dest.write_str("italic"),
280 generics::FontStyle::Oblique(ref angle) => {
281 if *angle == Angle::zero() {
284 dest.write_str("normal")?;
285 } else {
286 dest.write_str("oblique")?;
287 if *angle != Self::default_angle() {
288 dest.write_char(' ')?;
289 angle.to_css(dest)?;
290 }
291 }
292 Ok(())
293 },
294 }
295 }
296}
297
298impl Parse for SpecifiedFontStyle {
299 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
300 Ok(try_match_ident_ignore_ascii_case! { input,
301 "normal" => generics::FontStyle::normal(),
302 "italic" => generics::FontStyle::Italic,
303 "oblique" => {
304 let angle = input.try_parse(|input| Self::parse_angle(context, input))
305 .unwrap_or_else(|_| Self::default_angle());
306
307 generics::FontStyle::Oblique(angle)
308 },
309 })
310 }
311}
312
313impl ToComputedValue for SpecifiedFontStyle {
314 type ComputedValue = computed::FontStyle;
315
316 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
317 match *self {
318 Self::Italic => computed::FontStyle::ITALIC,
319 Self::Oblique(ref angle) => {
320 computed::FontStyle::oblique(angle.to_computed_value(context).degrees())
321 },
322 }
323 }
324
325 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
326 if *computed == computed::FontStyle::ITALIC {
327 return Self::Italic;
328 }
329 let degrees = computed.oblique_degrees();
330 generics::FontStyle::Oblique(Angle::from_degrees(degrees))
331 }
332}
333
334pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
341
342pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
344
345impl SpecifiedFontStyle {
346 pub fn parse_angle(context: &ParserContext, input: &mut Parser) -> Result<Angle, ParseError> {
348 let angle = Angle::parse(context, input)?;
349 if angle.is_calc() {
351 return Ok(angle);
352 }
353
354 let degrees = angle.degrees().unwrap();
355 if !(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES..=FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
356 .contains(°rees)
357 {
358 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
359 }
360 Ok(angle)
361 }
362
363 pub fn default_angle() -> Angle {
365 Angle::from_degrees(computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32)
366 }
367}
368
369#[derive(
371 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
372)]
373#[allow(missing_docs)]
374#[typed(todo_derive_fields)]
375pub enum FontStyle {
376 Specified(SpecifiedFontStyle),
377 #[css(skip)]
378 System(SystemFont),
379}
380
381impl FontStyle {
382 #[inline]
384 pub fn normal() -> Self {
385 FontStyle::Specified(generics::FontStyle::normal())
386 }
387
388 system_font_methods!(FontStyle, font_style);
389}
390
391impl ToComputedValue for FontStyle {
392 type ComputedValue = computed::FontStyle;
393
394 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
395 match *self {
396 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
397 FontStyle::System(..) => self.compute_system(context),
398 }
399 }
400
401 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
402 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
403 }
404}
405
406#[allow(missing_docs)]
410#[derive(
411 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
412)]
413pub enum FontWidth {
414 Width(NonNegativePercentage),
415 Keyword(FontWidthKeyword),
416 #[css(skip)]
417 System(SystemFont),
418}
419
420#[derive(
422 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
423)]
424#[allow(missing_docs)]
425pub enum FontWidthKeyword {
426 Normal,
427 Condensed,
428 UltraCondensed,
429 ExtraCondensed,
430 SemiCondensed,
431 SemiExpanded,
432 Expanded,
433 ExtraExpanded,
434 UltraExpanded,
435}
436
437impl FontWidthKeyword {
438 pub fn compute(&self) -> computed::FontWidth {
440 computed::FontWidth::from_keyword(*self)
441 }
442
443 pub fn from_percentage(p: f32) -> Option<Self> {
446 computed::FontWidth::from_percentage(p).as_keyword()
447 }
448}
449
450impl FontWidth {
451 pub fn normal() -> Self {
453 FontWidth::Keyword(FontWidthKeyword::Normal)
454 }
455
456 system_font_methods!(FontWidth, font_width);
457}
458
459impl ToComputedValue for FontWidth {
460 type ComputedValue = computed::FontWidth;
461
462 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
463 match *self {
464 FontWidth::Width(ref percentage) => {
465 let percentage = percentage.to_computed_value(context).0;
466 computed::FontWidth::from_percentage(percentage.0)
467 },
468 FontWidth::Keyword(ref kw) => kw.compute(),
469 FontWidth::System(_) => self.compute_system(context),
470 }
471 }
472
473 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
474 FontWidth::Width(NonNegativePercentage::from_computed_value(&NonNegative(
475 computed.to_percentage(),
476 )))
477 }
478}
479
480#[derive(
482 Animate,
483 Clone,
484 ComputeSquaredDistance,
485 Copy,
486 Debug,
487 Default,
488 MallocSizeOf,
489 Parse,
490 PartialEq,
491 SpecifiedValueInfo,
492 ToAnimatedValue,
493 ToAnimatedZero,
494 ToComputedValue,
495 ToCss,
496 ToResolvedValue,
497 ToShmem,
498 Serialize,
499 Deserialize,
500 ToTyped,
501)]
502#[allow(missing_docs)]
503#[repr(u8)]
504pub enum FontSizeKeyword {
505 #[css(keyword = "xx-small")]
506 XXSmall,
507 XSmall,
508 Small,
509 #[default]
510 Medium,
511 Large,
512 XLarge,
513 #[css(keyword = "xx-large")]
514 XXLarge,
515 #[css(keyword = "xxx-large")]
516 XXXLarge,
517 #[cfg(feature = "gecko")]
520 Math,
521 #[css(skip)]
522 None,
523}
524
525impl FontSizeKeyword {
526 #[inline]
528 pub fn html_size(self) -> u8 {
529 self as u8
530 }
531
532 #[cfg(feature = "gecko")]
534 pub fn is_math(self) -> bool {
535 matches!(self, Self::Math)
536 }
537
538 #[cfg(feature = "servo")]
540 pub fn is_math(self) -> bool {
541 false
542 }
543}
544
545#[derive(
546 Animate,
547 Clone,
548 ComputeSquaredDistance,
549 Copy,
550 Debug,
551 Deserialize,
552 MallocSizeOf,
553 PartialEq,
554 Serialize,
555 ToAnimatedValue,
556 ToAnimatedZero,
557 ToComputedValue,
558 ToCss,
559 ToResolvedValue,
560 ToShmem,
561 ToTyped,
562)]
563pub struct KeywordInfo {
565 pub kw: FontSizeKeyword,
567 #[css(skip)]
569 pub factor: f32,
570 #[css(skip)]
573 pub offset: CSSPixelLength,
574}
575
576impl KeywordInfo {
577 pub fn medium() -> Self {
579 Self::new(FontSizeKeyword::Medium)
580 }
581
582 pub fn none() -> Self {
584 Self::new(FontSizeKeyword::None)
585 }
586
587 fn new(kw: FontSizeKeyword) -> Self {
588 KeywordInfo {
589 kw,
590 factor: 1.,
591 offset: CSSPixelLength::new(0.),
592 }
593 }
594
595 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
598 debug_assert_ne!(self.kw, FontSizeKeyword::None);
599 #[cfg(feature = "gecko")]
600 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
601 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
602 let zoom_factor = context.style().effective_zoom.value();
603 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
604 + context.maybe_zoom_text(self.offset)
605 }
606
607 fn compose(self, factor: f32) -> Self {
610 if self.kw == FontSizeKeyword::None {
611 return self;
612 }
613 KeywordInfo {
614 kw: self.kw,
615 factor: self.factor * factor,
616 offset: self.offset * factor,
617 }
618 }
619}
620
621impl SpecifiedValueInfo for KeywordInfo {
622 fn collect_completion_keywords(f: KeywordsCollectFn) {
623 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
624 }
625}
626
627#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
628pub enum FontSize {
630 Length(LengthPercentage),
632 Keyword(KeywordInfo),
643 Smaller,
645 Larger,
647 #[css(skip)]
649 System(SystemFont),
650}
651
652#[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss, ToShmem, ToTyped)]
654#[typed(todo_derive_fields)]
655pub enum FontFamily {
656 #[css(comma)]
658 Values(#[css(iterable)] FontFamilyList),
659 #[css(skip)]
661 System(SystemFont),
662}
663
664impl FontFamily {
665 system_font_methods!(FontFamily, font_family);
666}
667
668impl ToComputedValue for FontFamily {
669 type ComputedValue = computed::FontFamily;
670
671 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
672 match *self {
673 FontFamily::Values(ref list) => computed::FontFamily {
674 families: list.clone(),
675 is_system_font: false,
676 is_initial: false,
677 },
678 FontFamily::System(_) => self.compute_system(context),
679 }
680 }
681
682 fn from_computed_value(other: &computed::FontFamily) -> Self {
683 FontFamily::Values(other.families.clone())
684 }
685}
686
687#[cfg(feature = "gecko")]
688impl MallocSizeOf for FontFamily {
689 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
690 match *self {
691 FontFamily::Values(ref v) => {
692 v.list.unconditional_size_of(ops)
695 },
696 FontFamily::System(_) => 0,
697 }
698 }
699}
700
701impl Parse for FontFamily {
702 fn parse(context: &ParserContext, input: &mut Parser) -> Result<FontFamily, ParseError> {
706 let values =
707 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
708 Ok(FontFamily::Values(FontFamilyList {
709 list: crate::ArcSlice::from_iter(values.into_iter()),
710 }))
711 }
712}
713
714impl SpecifiedValueInfo for FontFamily {}
715
716impl Parse for FamilyName {
719 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
720 match SingleFontFamily::parse(context, input) {
721 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
722 Ok(SingleFontFamily::Generic(_)) => {
723 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
724 },
725 Err(e) => Err(e),
726 }
727 }
728}
729
730#[derive(
733 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
734)]
735pub enum FontSizeAdjustFactor {
736 Number(NonNegativeNumber),
738 FromFont,
740}
741
742pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
747
748impl Parse for FontSizeAdjust {
749 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
750 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
752 return Ok(Self::ExHeight(factor));
753 }
754
755 let ident = input.expect_ident()?;
756 let basis = match_ignore_ascii_case! { &ident,
757 "none" => return Ok(Self::None),
758 "ex-height" => Self::ExHeight,
760 "cap-height" => Self::CapHeight,
761 "ch-width" => Self::ChWidth,
762 "ic-width" => Self::IcWidth,
763 "ic-height" => Self::IcHeight,
764 _ => return Err(ParseError::custom(
766 SelectorParseErrorKind::UnexpectedIdent
767 )),
768 };
769
770 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
771 }
772}
773
774const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
777
778pub const FONT_MEDIUM_PX: f32 = 16.0;
780pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
782pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
785pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
788pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
791pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
794
795impl FontSizeKeyword {
796 #[inline]
797 fn to_length(&self, cx: &Context) -> NonNegativeLength {
798 let font = cx.style().get_font();
799
800 #[cfg(feature = "servo")]
801 let family = &font.font_family.families;
802 #[cfg(feature = "gecko")]
803 let family = &font.mFont.family.families;
804
805 let generic = family
806 .single_generic()
807 .unwrap_or(computed::GenericFontFamily::None);
808
809 #[cfg(feature = "gecko")]
810 let base_size = unsafe {
811 Atom::with(font.mLanguage.mRawPtr, |language| {
812 cx.device().base_size_for_generic(language, generic)
813 })
814 };
815 #[cfg(feature = "servo")]
816 let base_size = cx.device().base_size_for_generic(generic);
817
818 self.to_length_without_context(cx.quirks_mode, base_size)
819 }
820
821 #[inline]
823 pub fn to_length_without_context(
824 &self,
825 quirks_mode: QuirksMode,
826 base_size: Length,
827 ) -> NonNegativeLength {
828 #[cfg(feature = "gecko")]
829 debug_assert_ne!(*self, FontSizeKeyword::Math);
830 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
843 [9, 9, 9, 9, 11, 14, 18, 27],
844 [9, 9, 9, 10, 12, 15, 20, 30],
845 [9, 9, 10, 11, 13, 17, 22, 33],
846 [9, 9, 10, 12, 14, 18, 24, 36],
847 [9, 10, 12, 13, 16, 20, 26, 39],
848 [9, 10, 12, 14, 17, 21, 28, 42],
849 [9, 10, 13, 15, 18, 23, 30, 45],
850 [9, 10, 13, 16, 18, 24, 32, 48],
851 ];
852
853 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
862 [9, 9, 9, 9, 11, 14, 18, 28],
863 [9, 9, 9, 10, 12, 15, 20, 31],
864 [9, 9, 9, 11, 13, 17, 22, 34],
865 [9, 9, 10, 12, 14, 18, 24, 37],
866 [9, 9, 10, 13, 16, 20, 26, 40],
867 [9, 9, 11, 14, 17, 21, 28, 42],
868 [9, 10, 12, 15, 17, 23, 30, 45],
869 [9, 10, 13, 16, 18, 24, 32, 48],
870 ];
871
872 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
873 let base_size_px = base_size.px().round() as i32;
874 let html_size = self.html_size() as usize;
875 NonNegative(if (9..=16).contains(&base_size_px) {
876 let mapping = if quirks_mode == QuirksMode::Quirks {
877 QUIRKS_FONT_SIZE_MAPPING
878 } else {
879 FONT_SIZE_MAPPING
880 };
881 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
882 } else {
883 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
884 })
885 }
886}
887
888impl FontSize {
889 pub fn from_html_size(size: u8) -> Self {
891 FontSize::Keyword(KeywordInfo::new(match size {
892 0 | 1 => FontSizeKeyword::XSmall,
894 2 => FontSizeKeyword::Small,
895 3 => FontSizeKeyword::Medium,
896 4 => FontSizeKeyword::Large,
897 5 => FontSizeKeyword::XLarge,
898 6 => FontSizeKeyword::XXLarge,
899 _ => FontSizeKeyword::XXXLarge,
901 }))
902 }
903
904 pub fn to_computed_value_against(
906 &self,
907 context: &Context,
908 base_size: FontBaseSize,
909 line_height_base: LineHeightBase,
910 ) -> computed::FontSize {
911 let compose_keyword = |factor| {
912 context
913 .style()
914 .get_parent_font()
915 .clone_font_size()
916 .keyword_info
917 .compose(factor)
918 };
919 let mut info = KeywordInfo::none();
920 let size =
921 match *self {
922 FontSize::Length(LengthPercentage::Length(ref l)) => {
923 if l.length_unit() == LengthUnit::Em {
924 info = compose_keyword(l.unitless_value());
927 }
928 let result =
929 l.to_computed_value_with_base_size(context, base_size, line_height_base);
930 if l.should_zoom_text() {
931 context.maybe_zoom_text(result)
932 } else {
933 result
934 }
935 },
936 FontSize::Length(LengthPercentage::Percentage(pc)) => {
937 info = compose_keyword(pc.get());
940 (base_size.resolve(context).computed_size() * pc.get()).normalized()
941 },
942 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
943 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
944 calc.resolve(base_size.resolve(context).computed_size())
945 },
946 FontSize::Keyword(i) => {
947 if i.kw.is_math() {
948 info = compose_keyword(1.);
950 info.kw = i.kw;
953 NoCalcLength::from_em(1.).to_computed_value_with_base_size(
954 context,
955 base_size,
956 line_height_base,
957 )
958 } else {
959 info = i;
961 i.to_computed_value(context).clamp_to_non_negative()
962 }
963 },
964 FontSize::Smaller => {
965 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
966 NoCalcLength::from_em(1. / LARGER_FONT_SIZE_RATIO)
967 .to_computed_value_with_base_size(context, base_size, line_height_base)
968 },
969 FontSize::Larger => {
970 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
971 NoCalcLength::from_em(LARGER_FONT_SIZE_RATIO).to_computed_value_with_base_size(
972 context,
973 base_size,
974 line_height_base,
975 )
976 },
977 FontSize::System(_) => {
978 #[cfg(feature = "servo")]
979 {
980 unreachable!()
981 }
982 #[cfg(feature = "gecko")]
983 {
984 context
985 .cached_system_font
986 .as_ref()
987 .unwrap()
988 .font_size
989 .computed_size()
990 .zoom(context.builder.effective_zoom)
991 }
992 },
993 };
994 let size = NonNegative(Self::quantize_font_size(size));
995 computed::FontSize {
996 computed_size: size,
997 used_size: size,
998 keyword_info: info,
999 }
1000 }
1001
1002 #[inline]
1005 pub fn quantize_font_size(size: CSSPixelLength) -> CSSPixelLength {
1006 size_of_test!(CSSPixelLength, std::mem::size_of::<f32>());
1014 const BITS_TO_DROP: u32 = 14; const SCALE_PLUS_ONE: f32 = ((1 << BITS_TO_DROP) + 1) as f32;
1016 const LIMIT: f32 = f32::MAX / SCALE_PLUS_ONE;
1017 if size.px() >= LIMIT {
1018 return CSSPixelLength::new(LIMIT);
1019 }
1020 let d = size.px() * SCALE_PLUS_ONE;
1021 let t = d - size.px();
1022 CSSPixelLength::new(d - t)
1023 }
1024}
1025
1026impl ToComputedValue for FontSize {
1027 type ComputedValue = computed::FontSize;
1028
1029 #[inline]
1030 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1031 self.to_computed_value_against(
1032 context,
1033 FontBaseSize::InheritedStyle,
1034 LineHeightBase::InheritedStyle,
1035 )
1036 }
1037
1038 #[inline]
1039 fn from_computed_value(computed: &computed::FontSize) -> Self {
1040 FontSize::Length(LengthPercentage::Length(
1041 ToComputedValue::from_computed_value(&computed.computed_size()),
1042 ))
1043 }
1044}
1045
1046impl FontSize {
1047 system_font_methods!(FontSize);
1048
1049 #[inline]
1051 pub fn medium() -> Self {
1052 FontSize::Keyword(KeywordInfo::medium())
1053 }
1054
1055 pub fn parse_quirky(
1057 context: &ParserContext,
1058 input: &mut Parser,
1059 allow_quirks: AllowQuirks,
1060 ) -> Result<FontSize, ParseError> {
1061 if let Ok(lp) = input
1062 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1063 {
1064 return Ok(FontSize::Length(lp));
1065 }
1066
1067 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1068 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1069 }
1070
1071 try_match_ident_ignore_ascii_case! { input,
1072 "smaller" => Ok(FontSize::Smaller),
1073 "larger" => Ok(FontSize::Larger),
1074 }
1075 }
1076}
1077
1078impl Parse for FontSize {
1079 fn parse(context: &ParserContext, input: &mut Parser) -> Result<FontSize, ParseError> {
1081 FontSize::parse_quirky(context, input, AllowQuirks::No)
1082 }
1083}
1084
1085bitflags! {
1086 #[derive(Clone, Copy)]
1087 struct VariantAlternatesParsingFlags: u8 {
1089 const NORMAL = 0;
1091 const HISTORICAL_FORMS = 0x01;
1093 const STYLISTIC = 0x02;
1095 const STYLESET = 0x04;
1097 const CHARACTER_VARIANT = 0x08;
1099 const SWASH = 0x10;
1101 const ORNAMENTS = 0x20;
1103 const ANNOTATION = 0x40;
1105 }
1106}
1107
1108#[derive(
1109 Clone,
1110 Debug,
1111 Deserialize,
1112 Hash,
1113 MallocSizeOf,
1114 PartialEq,
1115 Serialize,
1116 SpecifiedValueInfo,
1117 ToCss,
1118 ToComputedValue,
1119 ToResolvedValue,
1120 ToShmem,
1121)]
1122#[repr(C, u8)]
1123pub enum VariantAlternates {
1125 #[css(function)]
1127 Stylistic(CustomIdent),
1128 #[css(comma, function)]
1130 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1131 #[css(comma, function)]
1133 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1134 #[css(function)]
1136 Swash(CustomIdent),
1137 #[css(function)]
1139 Ornaments(CustomIdent),
1140 #[css(function)]
1142 Annotation(CustomIdent),
1143 HistoricalForms,
1145}
1146
1147#[derive(
1148 Clone,
1149 Debug,
1150 Default,
1151 Deserialize,
1152 Hash,
1153 MallocSizeOf,
1154 PartialEq,
1155 Serialize,
1156 SpecifiedValueInfo,
1157 ToComputedValue,
1158 ToCss,
1159 ToResolvedValue,
1160 ToShmem,
1161 ToTyped,
1162)]
1163#[repr(transparent)]
1164#[typed(todo_derive_fields)]
1165pub struct FontVariantAlternates(
1167 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1168);
1169
1170impl FontVariantAlternates {
1171 pub fn is_empty(&self) -> bool {
1173 self.0.is_empty()
1174 }
1175
1176 pub fn iter(&self) -> impl Iterator<Item = &VariantAlternates> {
1178 self.0.iter()
1179 }
1180
1181 pub fn len(&self) -> usize {
1183 self.0.iter().fold(0, |acc, alternate| match *alternate {
1184 VariantAlternates::Swash(_)
1185 | VariantAlternates::Stylistic(_)
1186 | VariantAlternates::Ornaments(_)
1187 | VariantAlternates::Annotation(_) => acc + 1,
1188 VariantAlternates::Styleset(ref slice)
1189 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1190 _ => acc,
1191 })
1192 }
1193}
1194
1195impl Parse for FontVariantAlternates {
1196 fn parse(_: &ParserContext, input: &mut Parser) -> Result<FontVariantAlternates, ParseError> {
1205 if input
1206 .try_parse(|input| input.expect_ident_matching("normal"))
1207 .is_ok()
1208 {
1209 return Ok(Default::default());
1210 }
1211
1212 let mut stylistic = None;
1213 let mut historical = None;
1214 let mut styleset = None;
1215 let mut character_variant = None;
1216 let mut swash = None;
1217 let mut ornaments = None;
1218 let mut annotation = None;
1219
1220 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1222 macro_rules! check_if_parsed(
1223 ($input:expr, $flag:path) => (
1224 if parsed_alternates.contains($flag) {
1225 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1226 }
1227 parsed_alternates |= $flag;
1228 )
1229 );
1230 while input.try_parse(|input| match *input.next()? {
1231 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1232 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1233 historical = Some(VariantAlternates::HistoricalForms);
1234 Ok(())
1235 },
1236 Token::Function(ref name) => {
1237 let name = name.clone();
1238 input.parse_nested_block(|i| {
1239 match_ignore_ascii_case! { &name,
1240 "swash" => {
1241 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1242 let ident = CustomIdent::parse(i, &[])?;
1243 swash = Some(VariantAlternates::Swash(ident));
1244 Ok(())
1245 },
1246 "stylistic" => {
1247 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1248 let ident = CustomIdent::parse(i, &[])?;
1249 stylistic = Some(VariantAlternates::Stylistic(ident));
1250 Ok(())
1251 },
1252 "ornaments" => {
1253 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1254 let ident = CustomIdent::parse(i, &[])?;
1255 ornaments = Some(VariantAlternates::Ornaments(ident));
1256 Ok(())
1257 },
1258 "annotation" => {
1259 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1260 let ident = CustomIdent::parse(i, &[])?;
1261 annotation = Some(VariantAlternates::Annotation(ident));
1262 Ok(())
1263 },
1264 "styleset" => {
1265 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1266 let idents = i.parse_comma_separated(|i| {
1267 CustomIdent::parse(i, &[])
1268 })?;
1269 styleset = Some(VariantAlternates::Styleset(idents.into()));
1270 Ok(())
1271 },
1272 "character-variant" => {
1273 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1274 let idents = i.parse_comma_separated(|i| {
1275 CustomIdent::parse(i, &[])
1276 })?;
1277 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1278 Ok(())
1279 },
1280 _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
1281 }
1282 })
1283 },
1284 _ => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
1285 }).is_ok() {}
1286
1287 if parsed_alternates.is_empty() {
1288 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1289 }
1290
1291 let mut alternates = Vec::new();
1293 macro_rules! push_if_some(
1294 ($value:expr) => (
1295 if let Some(v) = $value {
1296 alternates.push(v);
1297 }
1298 )
1299 );
1300 push_if_some!(stylistic);
1301 push_if_some!(historical);
1302 push_if_some!(styleset);
1303 push_if_some!(character_variant);
1304 push_if_some!(swash);
1305 push_if_some!(ornaments);
1306 push_if_some!(annotation);
1307
1308 Ok(FontVariantAlternates(alternates.into()))
1309 }
1310}
1311
1312#[derive(
1313 Clone,
1314 Copy,
1315 Debug,
1316 Deserialize,
1317 Eq,
1318 Hash,
1319 MallocSizeOf,
1320 PartialEq,
1321 Parse,
1322 Serialize,
1323 SpecifiedValueInfo,
1324 ToComputedValue,
1325 ToCss,
1326 ToResolvedValue,
1327 ToShmem,
1328 ToTyped,
1329)]
1330#[css(bitflags(
1331 single = "normal",
1332 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1333 validate_mixed = "Self::validate_mixed_flags",
1334))]
1335#[repr(C)]
1336pub struct FontVariantEastAsian(u16);
1338bitflags! {
1339 impl FontVariantEastAsian: u16 {
1340 const NORMAL = 0;
1342 const JIS78 = 1 << 0;
1344 const JIS83 = 1 << 1;
1346 const JIS90 = 1 << 2;
1348 const JIS04 = 1 << 3;
1350 const SIMPLIFIED = 1 << 4;
1352 const TRADITIONAL = 1 << 5;
1354
1355 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1357
1358 const FULL_WIDTH = 1 << 6;
1360 const PROPORTIONAL_WIDTH = 1 << 7;
1362 const RUBY = 1 << 8;
1364 }
1365}
1366
1367impl FontVariantEastAsian {
1368 pub const COUNT: usize = 9;
1370
1371 fn validate_mixed_flags(&self) -> bool {
1372 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1373 return false;
1375 }
1376 let jis = self.intersection(Self::JIS_GROUP);
1377 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1378 return false;
1379 }
1380 true
1381 }
1382}
1383
1384#[derive(
1385 Clone,
1386 Copy,
1387 Debug,
1388 Deserialize,
1389 Eq,
1390 Hash,
1391 MallocSizeOf,
1392 PartialEq,
1393 Parse,
1394 Serialize,
1395 SpecifiedValueInfo,
1396 ToComputedValue,
1397 ToCss,
1398 ToResolvedValue,
1399 ToShmem,
1400 ToTyped,
1401)]
1402#[css(bitflags(
1403 single = "normal,none",
1404 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1405 validate_mixed = "Self::validate_mixed_flags",
1406))]
1407#[repr(C)]
1408pub struct FontVariantLigatures(u16);
1410bitflags! {
1411 impl FontVariantLigatures: u16 {
1412 const NORMAL = 0;
1414 const NONE = 1;
1416 const COMMON_LIGATURES = 1 << 1;
1418 const NO_COMMON_LIGATURES = 1 << 2;
1420 const DISCRETIONARY_LIGATURES = 1 << 3;
1422 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1424 const HISTORICAL_LIGATURES = 1 << 5;
1426 const NO_HISTORICAL_LIGATURES = 1 << 6;
1428 const CONTEXTUAL = 1 << 7;
1430 const NO_CONTEXTUAL = 1 << 8;
1432 }
1433}
1434
1435impl FontVariantLigatures {
1436 pub const COUNT: usize = 9;
1438
1439 fn validate_mixed_flags(&self) -> bool {
1440 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1442 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1443 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1444 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1445 {
1446 return false;
1447 }
1448 true
1449 }
1450}
1451
1452#[derive(
1454 Clone,
1455 Copy,
1456 Debug,
1457 Deserialize,
1458 Eq,
1459 Hash,
1460 MallocSizeOf,
1461 PartialEq,
1462 Parse,
1463 Serialize,
1464 SpecifiedValueInfo,
1465 ToComputedValue,
1466 ToCss,
1467 ToResolvedValue,
1468 ToShmem,
1469 ToTyped,
1470)]
1471#[css(bitflags(
1472 single = "normal",
1473 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1474 validate_mixed = "Self::validate_mixed_flags",
1475))]
1476#[repr(C)]
1477pub struct FontVariantNumeric(u8);
1478bitflags! {
1479 impl FontVariantNumeric : u8 {
1480 const NORMAL = 0;
1482 const LINING_NUMS = 1 << 0;
1484 const OLDSTYLE_NUMS = 1 << 1;
1486 const PROPORTIONAL_NUMS = 1 << 2;
1488 const TABULAR_NUMS = 1 << 3;
1490 const DIAGONAL_FRACTIONS = 1 << 4;
1492 const STACKED_FRACTIONS = 1 << 5;
1494 const SLASHED_ZERO = 1 << 6;
1496 const ORDINAL = 1 << 7;
1498 }
1499}
1500
1501impl FontVariantNumeric {
1502 pub const COUNT: usize = 8;
1504
1505 fn validate_mixed_flags(&self) -> bool {
1515 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1516 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1517 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1518 {
1519 return false;
1520 }
1521 true
1522 }
1523}
1524
1525pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1527
1528impl FontFeatureSettings {
1529 pub fn parse_for_font_face_rule(
1532 context: &ParserContext,
1533 input: &mut Parser,
1534 ) -> Result<Self, ParseError> {
1535 let settings = FontFeatureSettings::parse(context, input)?;
1536 if settings
1537 .0
1538 .iter()
1539 .any(|setting| setting.value.resolve().is_none())
1540 {
1541 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1542 }
1543 Ok(settings)
1544 }
1545}
1546
1547pub use crate::values::computed::font::FontLanguageOverride;
1549
1550impl Parse for FontLanguageOverride {
1551 fn parse(_: &ParserContext, input: &mut Parser) -> Result<FontLanguageOverride, ParseError> {
1553 if input
1554 .try_parse(|input| input.expect_ident_matching("normal"))
1555 .is_ok()
1556 {
1557 return Ok(FontLanguageOverride::normal());
1558 }
1559
1560 let string = input.expect_string()?;
1561
1562 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1565 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1566 }
1567
1568 let mut bytes = [b' '; 4];
1569 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1570 *byte = *str_byte;
1571 }
1572
1573 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1574 }
1575}
1576
1577#[repr(u8)]
1579#[derive(
1580 Clone,
1581 Copy,
1582 Debug,
1583 Deserialize,
1584 Eq,
1585 Hash,
1586 MallocSizeOf,
1587 Parse,
1588 PartialEq,
1589 Serialize,
1590 SpecifiedValueInfo,
1591 ToComputedValue,
1592 ToCss,
1593 ToResolvedValue,
1594 ToShmem,
1595 ToTyped,
1596)]
1597pub enum FontSynthesis {
1598 Auto,
1600 None,
1602}
1603
1604#[repr(u8)]
1606#[derive(
1607 Clone,
1608 Copy,
1609 Debug,
1610 Eq,
1611 MallocSizeOf,
1612 Parse,
1613 PartialEq,
1614 SpecifiedValueInfo,
1615 ToComputedValue,
1616 ToCss,
1617 ToResolvedValue,
1618 ToShmem,
1619 ToTyped,
1620)]
1621pub enum FontSynthesisStyle {
1622 Auto,
1624 None,
1626 ObliqueOnly,
1628}
1629
1630#[derive(
1631 Clone,
1632 Debug,
1633 Eq,
1634 MallocSizeOf,
1635 PartialEq,
1636 SpecifiedValueInfo,
1637 ToComputedValue,
1638 ToResolvedValue,
1639 ToShmem,
1640 ToTyped,
1641)]
1642#[repr(C)]
1643#[typed(todo_derive_fields)]
1644pub struct FontPalette(Atom);
1647
1648#[allow(missing_docs)]
1649impl FontPalette {
1650 pub fn normal() -> Self {
1651 Self(atom!("normal"))
1652 }
1653 pub fn light() -> Self {
1654 Self(atom!("light"))
1655 }
1656 pub fn dark() -> Self {
1657 Self(atom!("dark"))
1658 }
1659}
1660
1661impl Parse for FontPalette {
1662 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<FontPalette, ParseError> {
1664 let ident = input.expect_ident()?;
1665 match_ignore_ascii_case! { &ident,
1666 "normal" => Ok(Self::normal()),
1667 "light" => Ok(Self::light()),
1668 "dark" => Ok(Self::dark()),
1669 _ => if ident.starts_with("--") {
1670 Ok(Self(Atom::from(ident.as_ref())))
1671 } else {
1672 Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
1673 },
1674 }
1675 }
1676}
1677
1678impl ToCss for FontPalette {
1679 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1680 where
1681 W: Write,
1682 {
1683 serialize_atom_identifier(&self.0, dest)
1684 }
1685}
1686
1687pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1690
1691fn parse_one_feature_value(
1692 context: &ParserContext,
1693 input: &mut Parser,
1694) -> Result<Integer, ParseError> {
1695 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1696 return Ok(integer);
1697 }
1698
1699 try_match_ident_ignore_ascii_case! { input,
1700 "on" => Ok(Integer::new(1)),
1701 "off" => Ok(Integer::new(0)),
1702 }
1703}
1704
1705impl Parse for FeatureTagValue<Integer> {
1706 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1708 let tag = FontTag::parse(context, input)?;
1709 let value = input
1710 .try_parse(|i| parse_one_feature_value(context, i))
1711 .unwrap_or_else(|_| Integer::new(1));
1712
1713 Ok(Self { tag, value })
1714 }
1715}
1716
1717impl Parse for VariationValue<Number> {
1718 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1721 let tag = FontTag::parse(context, input)?;
1722 let value = Number::parse(context, input)?;
1723 Ok(Self { tag, value })
1724 }
1725}
1726
1727impl FontVariationSettings {
1728 pub fn parse_for_font_face_rule(
1731 context: &ParserContext,
1732 input: &mut Parser,
1733 ) -> Result<Self, ParseError> {
1734 let settings = FontVariationSettings::parse(context, input)?;
1735 if settings
1736 .0
1737 .iter()
1738 .any(|setting| setting.value.resolve().is_none())
1739 {
1740 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1741 }
1742 Ok(settings)
1743 }
1744}
1745
1746#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1750pub enum MetricsOverride {
1751 Override(NonNegativePercentage),
1753 Normal,
1755}
1756
1757impl MetricsOverride {
1758 #[inline]
1759 pub fn normal() -> MetricsOverride {
1761 MetricsOverride::Normal
1762 }
1763
1764 #[inline]
1770 pub fn compute(&self) -> Option<ComputedPercentage> {
1771 Some(ComputedPercentage(match self {
1772 MetricsOverride::Normal => -1.0,
1773 MetricsOverride::Override(percent) => percent.compute()?.0,
1774 }))
1775 }
1776}
1777
1778#[derive(
1779 Clone,
1780 Copy,
1781 Debug,
1782 Deserialize,
1783 MallocSizeOf,
1784 Parse,
1785 PartialEq,
1786 Serialize,
1787 SpecifiedValueInfo,
1788 ToComputedValue,
1789 ToCss,
1790 ToResolvedValue,
1791 ToShmem,
1792 ToTyped,
1793)]
1794#[repr(u8)]
1795pub enum XTextScale {
1797 All,
1799 ZoomOnly,
1801 None,
1803}
1804
1805impl XTextScale {
1806 #[inline]
1808 pub fn text_zoom_enabled(self) -> bool {
1809 self != Self::None
1810 }
1811}
1812
1813#[derive(
1814 Clone,
1815 Debug,
1816 Deserialize,
1817 Eq,
1818 Hash,
1819 MallocSizeOf,
1820 PartialEq,
1821 Serialize,
1822 SpecifiedValueInfo,
1823 ToComputedValue,
1824 ToCss,
1825 ToResolvedValue,
1826 ToShmem,
1827 ToTyped,
1828)]
1829pub struct XLang(#[css(skip)] pub Atom);
1831
1832impl XLang {
1833 #[inline]
1834 pub fn get_initial_value() -> XLang {
1836 XLang(atom!(""))
1837 }
1838}
1839
1840impl Parse for XLang {
1841 fn parse(_: &ParserContext, _input: &mut Parser) -> Result<XLang, ParseError> {
1842 debug_assert!(
1843 false,
1844 "Should be set directly by presentation attributes only."
1845 );
1846 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1847 }
1848}
1849
1850#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1851#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1852pub struct MozScriptMinSize(pub NoCalcLength);
1855
1856impl MozScriptMinSize {
1857 #[inline]
1858 pub fn get_initial_value() -> Length {
1860 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1861 }
1862}
1863
1864impl Parse for MozScriptMinSize {
1865 fn parse(_: &ParserContext, _input: &mut Parser) -> Result<MozScriptMinSize, ParseError> {
1866 debug_assert!(
1867 false,
1868 "Should be set directly by presentation attributes only."
1869 );
1870 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1871 }
1872}
1873
1874#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1877#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1878pub enum MathDepth {
1879 AutoAdd,
1881
1882 #[css(function)]
1884 Add(Integer),
1885
1886 Absolute(Integer),
1888}
1889
1890impl Parse for MathDepth {
1891 fn parse(context: &ParserContext, input: &mut Parser) -> Result<MathDepth, ParseError> {
1892 if input
1893 .try_parse(|i| i.expect_ident_matching("auto-add"))
1894 .is_ok()
1895 {
1896 return Ok(MathDepth::AutoAdd);
1897 }
1898 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1899 return Ok(MathDepth::Absolute(math_depth_value));
1900 }
1901 input.expect_function_matching("add")?;
1902 let math_depth_delta_value =
1903 input.parse_nested_block(|input| Integer::parse(context, input))?;
1904 Ok(MathDepth::Add(math_depth_delta_value))
1905 }
1906}
1907
1908#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1909#[derive(
1910 Clone,
1911 Copy,
1912 Debug,
1913 PartialEq,
1914 SpecifiedValueInfo,
1915 ToComputedValue,
1916 ToCss,
1917 ToResolvedValue,
1918 ToShmem,
1919)]
1920pub struct MozScriptSizeMultiplier(pub f32);
1925
1926impl MozScriptSizeMultiplier {
1927 #[inline]
1928 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1930 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1931 }
1932}
1933
1934impl Parse for MozScriptSizeMultiplier {
1935 fn parse(
1936 _: &ParserContext,
1937 _input: &mut Parser,
1938 ) -> Result<MozScriptSizeMultiplier, ParseError> {
1939 debug_assert!(
1940 false,
1941 "Should be set directly by presentation attributes only."
1942 );
1943 Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
1944 }
1945}
1946
1947impl From<f32> for MozScriptSizeMultiplier {
1948 fn from(v: f32) -> Self {
1949 MozScriptSizeMultiplier(v)
1950 }
1951}
1952
1953impl From<MozScriptSizeMultiplier> for f32 {
1954 fn from(v: MozScriptSizeMultiplier) -> f32 {
1955 v.0
1956 }
1957}
1958
1959pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1961
1962fn parse_line_height_number(
1966 context: &ParserContext,
1967 input: &mut Parser,
1968) -> Result<NonNegativeNumber, ParseError> {
1969 parse_number_with_clamping_mode(
1970 context,
1971 input,
1972 AllowedNumericType::NonNegative,
1973 PercentageContext::allowed_with_hint(NumericBaseType::Length),
1974 )
1975 .map(NonNegative::<Number>)
1976}
1977
1978impl Parse for LineHeight {
1979 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1980 if let Ok(v) = input.try_parse(|input| parse_line_height_number(context, input)) {
1981 return Ok(GenericLineHeight::Number(v));
1982 }
1983 if let Ok(v) = input.try_parse(|input| NonNegativeLengthPercentage::parse(context, input)) {
1984 return Ok(GenericLineHeight::Length(v));
1985 }
1986 let ident = input.expect_ident()?;
1987 match_ignore_ascii_case! { &ident,
1988 "normal" => Ok(GenericLineHeight::Normal),
1989 _ => Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
1990 }
1991 }
1992}
1993
1994fn resolve_line_height_length(context: &Context, length: NoCalcLength) -> CSSPixelLength {
1997 let result = length.to_computed_value_with_base_size(
1998 context,
1999 FontBaseSize::CurrentStyle,
2000 LineHeightBase::InheritedStyle,
2001 );
2002 if length.should_zoom_text() {
2003 context.maybe_zoom_text(result)
2004 } else {
2005 result
2006 }
2007}
2008
2009fn map_line_height_leaf(context: &Context, leaf: &Leaf) -> Leaf {
2012 let length = match leaf {
2013 Leaf::Percentage(p) => NoCalcLength::from_em(p.get()),
2014 Leaf::Length(l) => *l,
2015 _ => return leaf.clone(),
2016 };
2017 Leaf::Length(NoCalcLength::from_px(
2018 resolve_line_height_length(context, length).px(),
2019 ))
2020}
2021
2022impl ToComputedValue for LineHeight {
2023 type ComputedValue = computed::LineHeight;
2024
2025 #[inline]
2026 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
2027 match self {
2028 GenericLineHeight::Normal => GenericLineHeight::Normal,
2029 GenericLineHeight::Number(number) => {
2030 let value = match number.as_calc() {
2031 None => number.to_computed_value(context).0,
2032 Some(calc) => {
2033 let resolved = calc
2034 .node
2035 .resolve_map(|leaf| Ok(map_line_height_leaf(context, leaf)));
2036 let value = match resolved {
2037 Ok(Leaf::Number(n)) => n.get(),
2038 _ => {
2039 debug_assert!(
2040 false,
2041 "Unexpected LineHeight number calc without resolved number"
2042 );
2043 f32::NAN
2044 },
2045 };
2046 calc.clamping_mode
2048 .clamp(crate::values::normalize(value).min(f32::MAX))
2049 },
2050 };
2051 GenericLineHeight::Number(NonNegative(value))
2052 },
2053 GenericLineHeight::Length(non_negative_lp) => {
2054 let result = match non_negative_lp.0 {
2055 LengthPercentage::Length(ref length) => {
2056 resolve_line_height_length(context, *length)
2057 },
2058 LengthPercentage::Percentage(ref p) => {
2059 resolve_line_height_length(context, NoCalcLength::from_em(p.get()))
2060 },
2061 LengthPercentage::Calc(ref calc) => calc
2062 .to_computed_value_zoomed(
2063 context,
2064 FontBaseSize::CurrentStyle,
2065 LineHeightBase::InheritedStyle,
2066 )
2067 .resolve(FontBaseSize::CurrentStyle.resolve(context).computed_size()),
2068 };
2069 GenericLineHeight::Length(result.into())
2070 },
2071 }
2072 }
2073
2074 #[inline]
2075 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
2076 match *computed {
2077 GenericLineHeight::Normal => GenericLineHeight::Normal,
2078 GenericLineHeight::Number(ref number) => {
2079 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
2080 },
2081 GenericLineHeight::Length(ref length) => {
2082 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
2083 },
2084 }
2085 }
2086}
2087
2088#[repr(C)]
2090pub struct QueryFontMetricsFlags(u8);
2091
2092bitflags! {
2093 impl QueryFontMetricsFlags: u8 {
2094 const USE_USER_FONT_SET = 1 << 0;
2096 const NEEDS_CH = 1 << 1;
2098 const NEEDS_IC = 1 << 2;
2100 const NEEDS_MATH_SCALES = 1 << 3;
2102 }
2103}