1use crate::context::QuirksMode;
8use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
11use crate::values::computed::Percentage as ComputedPercentage;
12use crate::values::computed::{font as computed, Length, NonNegativeLength};
13use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
14use crate::values::generics::font::{
15 self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
16};
17use crate::values::generics::NonNegative;
18use crate::values::specified::length::{FontBaseSize, LengthUnit, LineHeightBase, PX_PER_PT};
19use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
20use crate::values::specified::{
21 NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber, NonNegativePercentage, Number,
22};
23use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
24use crate::Atom;
25use cssparser::{match_ignore_ascii_case, Parser, Token};
26#[cfg(feature = "gecko")]
27use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
28use std::fmt::{self, Write};
29use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
30use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
31
32macro_rules! system_font_methods {
34 ($ty:ident, $field:ident) => {
35 system_font_methods!($ty);
36
37 fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
38 debug_assert!(matches!(*self, $ty::System(..)));
39 #[cfg(feature = "gecko")]
40 {
41 _context.cached_system_font.as_ref().unwrap().$field.clone()
42 }
43 #[cfg(feature = "servo")]
44 {
45 unreachable!()
46 }
47 }
48 };
49
50 ($ty:ident) => {
51 pub fn system_font(f: SystemFont) -> Self {
53 $ty::System(f)
54 }
55
56 pub fn get_system(&self) -> Option<SystemFont> {
58 if let $ty::System(s) = *self {
59 Some(s)
60 } else {
61 None
62 }
63 }
64 };
65}
66
67#[repr(u8)]
69#[derive(
70 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
71)]
72#[allow(missing_docs)]
73#[cfg(feature = "gecko")]
74pub enum SystemFont {
75 Caption,
77 Icon,
79 Menu,
81 MessageBox,
83 SmallCaption,
85 StatusBar,
87 #[parse(condition = "ParserContext::chrome_rules_enabled")]
89 MozPullDownMenu,
90 #[parse(condition = "ParserContext::chrome_rules_enabled")]
92 MozButton,
93 #[parse(condition = "ParserContext::chrome_rules_enabled")]
95 MozList,
96 #[parse(condition = "ParserContext::chrome_rules_enabled")]
98 MozField,
99 #[css(skip)]
100 End, }
102
103#[derive(
108 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
109)]
110#[allow(missing_docs)]
111#[cfg(feature = "servo")]
112pub enum SystemFont {}
114
115#[allow(missing_docs)]
116#[cfg(feature = "servo")]
117impl SystemFont {
118 pub fn parse(_: &mut Parser) -> Result<Self, ()> {
119 Err(())
120 }
121}
122
123const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
124const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
125
126pub const MIN_FONT_WEIGHT: f32 = 1.;
130
131pub const MAX_FONT_WEIGHT: f32 = 1000.;
135
136#[derive(
140 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
141)]
142pub enum FontWeight {
143 Absolute(AbsoluteFontWeight),
145 Bolder,
147 Lighter,
149 #[css(skip)]
151 System(SystemFont),
152}
153
154impl FontWeight {
155 system_font_methods!(FontWeight, font_weight);
156
157 #[inline]
159 pub fn normal() -> Self {
160 FontWeight::Absolute(AbsoluteFontWeight::Normal)
161 }
162
163 pub fn from_gecko_keyword(kw: u32) -> Self {
165 debug_assert!(kw % 100 == 0);
166 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
167 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
168 }
169}
170
171impl ToComputedValue for FontWeight {
172 type ComputedValue = computed::FontWeight;
173
174 #[inline]
175 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
176 match *self {
177 FontWeight::Absolute(ref abs) => abs.to_computed_value(context),
178 FontWeight::Bolder => context
179 .builder
180 .get_parent_font()
181 .clone_font_weight()
182 .bolder(),
183 FontWeight::Lighter => context
184 .builder
185 .get_parent_font()
186 .clone_font_weight()
187 .lighter(),
188 FontWeight::System(_) => self.compute_system(context),
189 }
190 }
191
192 #[inline]
193 fn from_computed_value(computed: &computed::FontWeight) -> Self {
194 FontWeight::Absolute(AbsoluteFontWeight::from_computed_value(computed))
195 }
196}
197
198#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
202pub enum AbsoluteFontWeight {
203 Weight(Number),
207 Normal,
209 Bold,
211}
212
213impl AbsoluteFontWeight {
214 pub fn compute(&self) -> Option<computed::FontWeight> {
217 match self {
218 AbsoluteFontWeight::Weight(weight) => {
219 Some(computed::FontWeight::from_float(weight.resolve()?))
220 },
221 AbsoluteFontWeight::Normal => Some(computed::FontWeight::NORMAL),
222 AbsoluteFontWeight::Bold => Some(computed::FontWeight::BOLD),
223 }
224 }
225}
226
227impl ToComputedValue for AbsoluteFontWeight {
228 type ComputedValue = computed::FontWeight;
229
230 fn to_computed_value(&self, context: &Context) -> computed::FontWeight {
231 match self {
232 AbsoluteFontWeight::Weight(weight) => {
233 computed::FontWeight::from_float(weight.to_computed_value(context))
234 },
235 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
236 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
237 }
238 }
239
240 fn from_computed_value(computed: &computed::FontWeight) -> Self {
241 AbsoluteFontWeight::Weight(Number::from_computed_value(&computed.value()))
242 }
243}
244
245impl Parse for AbsoluteFontWeight {
246 fn parse<'i, 't>(
247 context: &ParserContext,
248 input: &mut Parser<'i, 't>,
249 ) -> Result<Self, ParseError<'i>> {
250 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
251 if matches!(number.get(), Some(v) if v < MIN_FONT_WEIGHT || v > MAX_FONT_WEIGHT) {
255 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
256 }
257 return Ok(AbsoluteFontWeight::Weight(number));
258 }
259
260 Ok(try_match_ident_ignore_ascii_case! { input,
261 "normal" => AbsoluteFontWeight::Normal,
262 "bold" => AbsoluteFontWeight::Bold,
263 })
264 }
265}
266
267pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
270
271impl ToCss for SpecifiedFontStyle {
272 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
273 where
274 W: Write,
275 {
276 match *self {
277 generics::FontStyle::Italic => dest.write_str("italic"),
278 generics::FontStyle::Oblique(ref angle) => {
279 if *angle == Angle::zero() {
282 dest.write_str("normal")?;
283 } else {
284 dest.write_str("oblique")?;
285 if *angle != Self::default_angle() {
286 dest.write_char(' ')?;
287 angle.to_css(dest)?;
288 }
289 }
290 Ok(())
291 },
292 }
293 }
294}
295
296impl Parse for SpecifiedFontStyle {
297 fn parse<'i, 't>(
298 context: &ParserContext,
299 input: &mut Parser<'i, 't>,
300 ) -> Result<Self, ParseError<'i>> {
301 Ok(try_match_ident_ignore_ascii_case! { input,
302 "normal" => generics::FontStyle::normal(),
303 "italic" => generics::FontStyle::Italic,
304 "oblique" => {
305 let angle = input.try_parse(|input| Self::parse_angle(context, input))
306 .unwrap_or_else(|_| Self::default_angle());
307
308 generics::FontStyle::Oblique(angle)
309 },
310 })
311 }
312}
313
314impl ToComputedValue for SpecifiedFontStyle {
315 type ComputedValue = computed::FontStyle;
316
317 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
318 match *self {
319 Self::Italic => computed::FontStyle::ITALIC,
320 Self::Oblique(ref angle) => {
321 computed::FontStyle::oblique(angle.to_computed_value(context).degrees())
322 },
323 }
324 }
325
326 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
327 if *computed == computed::FontStyle::ITALIC {
328 return Self::Italic;
329 }
330 let degrees = computed.oblique_degrees();
331 generics::FontStyle::Oblique(Angle::from_degrees(degrees))
332 }
333}
334
335pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
342
343pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
345
346impl SpecifiedFontStyle {
347 pub fn compute_angle_degrees(angle: &Angle) -> Option<f32> {
349 Some(
350 angle
351 .degrees()?
352 .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
353 .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
354 )
355 }
356
357 pub fn parse_angle<'i, 't>(
359 context: &ParserContext,
360 input: &mut Parser<'i, 't>,
361 ) -> Result<Angle, ParseError<'i>> {
362 let angle = Angle::parse(context, input)?;
363 if angle.is_calc() {
365 return Ok(angle);
366 }
367
368 let degrees = angle.degrees().unwrap();
369 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES
370 || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
371 {
372 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
373 }
374 return Ok(angle);
375 }
376
377 pub fn default_angle() -> Angle {
379 Angle::from_degrees(computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32)
380 }
381}
382
383#[derive(
385 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
386)]
387#[allow(missing_docs)]
388#[typed(todo_derive_fields)]
389pub enum FontStyle {
390 Specified(SpecifiedFontStyle),
391 #[css(skip)]
392 System(SystemFont),
393}
394
395impl FontStyle {
396 #[inline]
398 pub fn normal() -> Self {
399 FontStyle::Specified(generics::FontStyle::normal())
400 }
401
402 system_font_methods!(FontStyle, font_style);
403}
404
405impl ToComputedValue for FontStyle {
406 type ComputedValue = computed::FontStyle;
407
408 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
409 match *self {
410 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
411 FontStyle::System(..) => self.compute_system(context),
412 }
413 }
414
415 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
416 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
417 }
418}
419
420#[allow(missing_docs)]
424#[derive(
425 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
426)]
427pub enum FontStretch {
428 Stretch(NonNegativePercentage),
429 Keyword(FontStretchKeyword),
430 #[css(skip)]
431 System(SystemFont),
432}
433
434#[derive(
436 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
437)]
438#[allow(missing_docs)]
439pub enum FontStretchKeyword {
440 Normal,
441 Condensed,
442 UltraCondensed,
443 ExtraCondensed,
444 SemiCondensed,
445 SemiExpanded,
446 Expanded,
447 ExtraExpanded,
448 UltraExpanded,
449}
450
451impl FontStretchKeyword {
452 pub fn compute(&self) -> computed::FontStretch {
454 computed::FontStretch::from_keyword(*self)
455 }
456
457 pub fn from_percentage(p: f32) -> Option<Self> {
460 computed::FontStretch::from_percentage(p).as_keyword()
461 }
462}
463
464impl FontStretch {
465 pub fn normal() -> Self {
467 FontStretch::Keyword(FontStretchKeyword::Normal)
468 }
469
470 system_font_methods!(FontStretch, font_stretch);
471}
472
473impl ToComputedValue for FontStretch {
474 type ComputedValue = computed::FontStretch;
475
476 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
477 match *self {
478 FontStretch::Stretch(ref percentage) => {
479 let percentage = percentage.to_computed_value(context).0;
480 computed::FontStretch::from_percentage(percentage.0)
481 },
482 FontStretch::Keyword(ref kw) => kw.compute(),
483 FontStretch::System(_) => self.compute_system(context),
484 }
485 }
486
487 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
488 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
489 computed.to_percentage(),
490 )))
491 }
492}
493
494#[derive(
496 Animate,
497 Clone,
498 ComputeSquaredDistance,
499 Copy,
500 Debug,
501 MallocSizeOf,
502 Parse,
503 PartialEq,
504 SpecifiedValueInfo,
505 ToAnimatedValue,
506 ToAnimatedZero,
507 ToComputedValue,
508 ToCss,
509 ToResolvedValue,
510 ToShmem,
511 Serialize,
512 Deserialize,
513 ToTyped,
514)]
515#[allow(missing_docs)]
516#[repr(u8)]
517pub enum FontSizeKeyword {
518 #[css(keyword = "xx-small")]
519 XXSmall,
520 XSmall,
521 Small,
522 Medium,
523 Large,
524 XLarge,
525 #[css(keyword = "xx-large")]
526 XXLarge,
527 #[css(keyword = "xxx-large")]
528 XXXLarge,
529 #[cfg(feature = "gecko")]
532 Math,
533 #[css(skip)]
534 None,
535}
536
537impl FontSizeKeyword {
538 #[inline]
540 pub fn html_size(self) -> u8 {
541 self as u8
542 }
543
544 #[cfg(feature = "gecko")]
546 pub fn is_math(self) -> bool {
547 matches!(self, Self::Math)
548 }
549
550 #[cfg(feature = "servo")]
552 pub fn is_math(self) -> bool {
553 false
554 }
555}
556
557impl Default for FontSizeKeyword {
558 fn default() -> Self {
559 FontSizeKeyword::Medium
560 }
561}
562
563#[derive(
564 Animate,
565 Clone,
566 ComputeSquaredDistance,
567 Copy,
568 Debug,
569 MallocSizeOf,
570 PartialEq,
571 ToAnimatedValue,
572 ToAnimatedZero,
573 ToComputedValue,
574 ToCss,
575 ToResolvedValue,
576 ToShmem,
577 ToTyped,
578)]
579#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
580pub struct KeywordInfo {
582 pub kw: FontSizeKeyword,
584 #[css(skip)]
586 pub factor: f32,
587 #[css(skip)]
590 pub offset: CSSPixelLength,
591}
592
593impl KeywordInfo {
594 pub fn medium() -> Self {
596 Self::new(FontSizeKeyword::Medium)
597 }
598
599 pub fn none() -> Self {
601 Self::new(FontSizeKeyword::None)
602 }
603
604 fn new(kw: FontSizeKeyword) -> Self {
605 KeywordInfo {
606 kw,
607 factor: 1.,
608 offset: CSSPixelLength::new(0.),
609 }
610 }
611
612 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
615 debug_assert_ne!(self.kw, FontSizeKeyword::None);
616 #[cfg(feature = "gecko")]
617 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
618 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
619 let zoom_factor = context.style().effective_zoom.value();
620 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
621 + context.maybe_zoom_text(self.offset)
622 }
623
624 fn compose(self, factor: f32) -> Self {
627 if self.kw == FontSizeKeyword::None {
628 return self;
629 }
630 KeywordInfo {
631 kw: self.kw,
632 factor: self.factor * factor,
633 offset: self.offset * factor,
634 }
635 }
636}
637
638impl SpecifiedValueInfo for KeywordInfo {
639 fn collect_completion_keywords(f: KeywordsCollectFn) {
640 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
641 }
642}
643
644#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
645pub enum FontSize {
647 Length(LengthPercentage),
649 Keyword(KeywordInfo),
660 Smaller,
662 Larger,
664 #[css(skip)]
666 System(SystemFont),
667}
668
669#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem, ToTyped)]
671#[cfg_attr(feature = "servo", derive(Hash))]
672#[typed(todo_derive_fields)]
673pub enum FontFamily {
674 #[css(comma)]
676 Values(#[css(iterable)] FontFamilyList),
677 #[css(skip)]
679 System(SystemFont),
680}
681
682impl FontFamily {
683 system_font_methods!(FontFamily, font_family);
684}
685
686impl ToComputedValue for FontFamily {
687 type ComputedValue = computed::FontFamily;
688
689 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
690 match *self {
691 FontFamily::Values(ref list) => computed::FontFamily {
692 families: list.clone(),
693 is_system_font: false,
694 is_initial: false,
695 },
696 FontFamily::System(_) => self.compute_system(context),
697 }
698 }
699
700 fn from_computed_value(other: &computed::FontFamily) -> Self {
701 FontFamily::Values(other.families.clone())
702 }
703}
704
705#[cfg(feature = "gecko")]
706impl MallocSizeOf for FontFamily {
707 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
708 match *self {
709 FontFamily::Values(ref v) => {
710 v.list.unconditional_size_of(ops)
713 },
714 FontFamily::System(_) => 0,
715 }
716 }
717}
718
719impl Parse for FontFamily {
720 fn parse<'i, 't>(
724 context: &ParserContext,
725 input: &mut Parser<'i, 't>,
726 ) -> Result<FontFamily, ParseError<'i>> {
727 let values =
728 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
729 Ok(FontFamily::Values(FontFamilyList {
730 list: crate::ArcSlice::from_iter(values.into_iter()),
731 }))
732 }
733}
734
735impl SpecifiedValueInfo for FontFamily {}
736
737impl Parse for FamilyName {
740 fn parse<'i, 't>(
741 context: &ParserContext,
742 input: &mut Parser<'i, 't>,
743 ) -> Result<Self, ParseError<'i>> {
744 match SingleFontFamily::parse(context, input) {
745 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
746 Ok(SingleFontFamily::Generic(_)) => {
747 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
748 },
749 Err(e) => Err(e),
750 }
751 }
752}
753
754#[derive(
757 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
758)]
759pub enum FontSizeAdjustFactor {
760 Number(NonNegativeNumber),
762 FromFont,
764}
765
766pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
771
772impl Parse for FontSizeAdjust {
773 fn parse<'i, 't>(
774 context: &ParserContext,
775 input: &mut Parser<'i, 't>,
776 ) -> Result<Self, ParseError<'i>> {
777 let location = input.current_source_location();
778 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
780 return Ok(Self::ExHeight(factor));
781 }
782
783 let ident = input.expect_ident()?;
784 let basis = match_ignore_ascii_case! { &ident,
785 "none" => return Ok(Self::None),
786 "ex-height" => Self::ExHeight,
788 "cap-height" => Self::CapHeight,
789 "ch-width" => Self::ChWidth,
790 "ic-width" => Self::IcWidth,
791 "ic-height" => Self::IcHeight,
792 _ => return Err(location.new_custom_error(
794 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
795 )),
796 };
797
798 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
799 }
800}
801
802const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
805
806pub const FONT_MEDIUM_PX: f32 = 16.0;
808pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
810pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
813pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
816pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
819pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
822
823impl FontSizeKeyword {
824 #[inline]
825 fn to_length(&self, cx: &Context) -> NonNegativeLength {
826 let font = cx.style().get_font();
827
828 #[cfg(feature = "servo")]
829 let family = &font.font_family.families;
830 #[cfg(feature = "gecko")]
831 let family = &font.mFont.family.families;
832
833 let generic = family
834 .single_generic()
835 .unwrap_or(computed::GenericFontFamily::None);
836
837 #[cfg(feature = "gecko")]
838 let base_size = unsafe {
839 Atom::with(font.mLanguage.mRawPtr, |language| {
840 cx.device().base_size_for_generic(language, generic)
841 })
842 };
843 #[cfg(feature = "servo")]
844 let base_size = cx.device().base_size_for_generic(generic);
845
846 self.to_length_without_context(cx.quirks_mode, base_size)
847 }
848
849 #[inline]
851 pub fn to_length_without_context(
852 &self,
853 quirks_mode: QuirksMode,
854 base_size: Length,
855 ) -> NonNegativeLength {
856 #[cfg(feature = "gecko")]
857 debug_assert_ne!(*self, FontSizeKeyword::Math);
858 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
871 [9, 9, 9, 9, 11, 14, 18, 27],
872 [9, 9, 9, 10, 12, 15, 20, 30],
873 [9, 9, 10, 11, 13, 17, 22, 33],
874 [9, 9, 10, 12, 14, 18, 24, 36],
875 [9, 10, 12, 13, 16, 20, 26, 39],
876 [9, 10, 12, 14, 17, 21, 28, 42],
877 [9, 10, 13, 15, 18, 23, 30, 45],
878 [9, 10, 13, 16, 18, 24, 32, 48],
879 ];
880
881 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
890 [9, 9, 9, 9, 11, 14, 18, 28],
891 [9, 9, 9, 10, 12, 15, 20, 31],
892 [9, 9, 9, 11, 13, 17, 22, 34],
893 [9, 9, 10, 12, 14, 18, 24, 37],
894 [9, 9, 10, 13, 16, 20, 26, 40],
895 [9, 9, 11, 14, 17, 21, 28, 42],
896 [9, 10, 12, 15, 17, 23, 30, 45],
897 [9, 10, 13, 16, 18, 24, 32, 48],
898 ];
899
900 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
901 let base_size_px = base_size.px().round() as i32;
902 let html_size = self.html_size() as usize;
903 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
904 let mapping = if quirks_mode == QuirksMode::Quirks {
905 QUIRKS_FONT_SIZE_MAPPING
906 } else {
907 FONT_SIZE_MAPPING
908 };
909 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
910 } else {
911 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
912 })
913 }
914}
915
916impl FontSize {
917 pub fn from_html_size(size: u8) -> Self {
919 FontSize::Keyword(KeywordInfo::new(match size {
920 0 | 1 => FontSizeKeyword::XSmall,
922 2 => FontSizeKeyword::Small,
923 3 => FontSizeKeyword::Medium,
924 4 => FontSizeKeyword::Large,
925 5 => FontSizeKeyword::XLarge,
926 6 => FontSizeKeyword::XXLarge,
927 _ => FontSizeKeyword::XXXLarge,
929 }))
930 }
931
932 pub fn to_computed_value_against(
934 &self,
935 context: &Context,
936 base_size: FontBaseSize,
937 line_height_base: LineHeightBase,
938 ) -> computed::FontSize {
939 let compose_keyword = |factor| {
940 context
941 .style()
942 .get_parent_font()
943 .clone_font_size()
944 .keyword_info
945 .compose(factor)
946 };
947 let mut info = KeywordInfo::none();
948 let size =
949 match *self {
950 FontSize::Length(LengthPercentage::Length(ref l)) => {
951 if l.length_unit() == LengthUnit::Em {
952 info = compose_keyword(l.unitless_value());
955 }
956 let result =
957 l.to_computed_value_with_base_size(context, base_size, line_height_base);
958 if l.should_zoom_text() {
959 context.maybe_zoom_text(result)
960 } else {
961 result
962 }
963 },
964 FontSize::Length(LengthPercentage::Percentage(pc)) => {
965 info = compose_keyword(pc.get());
968 (base_size.resolve(context).computed_size() * pc.get()).normalized()
969 },
970 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
971 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
972 calc.resolve(base_size.resolve(context).computed_size())
973 },
974 FontSize::Keyword(i) => {
975 if i.kw.is_math() {
976 info = compose_keyword(1.);
978 info.kw = i.kw;
981 NoCalcLength::from_em(1.).to_computed_value_with_base_size(
982 context,
983 base_size,
984 line_height_base,
985 )
986 } else {
987 info = i;
989 i.to_computed_value(context).clamp_to_non_negative()
990 }
991 },
992 FontSize::Smaller => {
993 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
994 NoCalcLength::from_em(1. / LARGER_FONT_SIZE_RATIO)
995 .to_computed_value_with_base_size(context, base_size, line_height_base)
996 },
997 FontSize::Larger => {
998 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
999 NoCalcLength::from_em(LARGER_FONT_SIZE_RATIO).to_computed_value_with_base_size(
1000 context,
1001 base_size,
1002 line_height_base,
1003 )
1004 },
1005 FontSize::System(_) => {
1006 #[cfg(feature = "servo")]
1007 {
1008 unreachable!()
1009 }
1010 #[cfg(feature = "gecko")]
1011 {
1012 context
1013 .cached_system_font
1014 .as_ref()
1015 .unwrap()
1016 .font_size
1017 .computed_size()
1018 .zoom(context.builder.effective_zoom)
1019 }
1020 },
1021 };
1022 computed::FontSize {
1023 computed_size: NonNegative(size),
1024 used_size: NonNegative(size),
1025 keyword_info: info,
1026 }
1027 }
1028}
1029
1030impl ToComputedValue for FontSize {
1031 type ComputedValue = computed::FontSize;
1032
1033 #[inline]
1034 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1035 self.to_computed_value_against(
1036 context,
1037 FontBaseSize::InheritedStyle,
1038 LineHeightBase::InheritedStyle,
1039 )
1040 }
1041
1042 #[inline]
1043 fn from_computed_value(computed: &computed::FontSize) -> Self {
1044 FontSize::Length(LengthPercentage::Length(
1045 ToComputedValue::from_computed_value(&computed.computed_size()),
1046 ))
1047 }
1048}
1049
1050impl FontSize {
1051 system_font_methods!(FontSize);
1052
1053 #[inline]
1055 pub fn medium() -> Self {
1056 FontSize::Keyword(KeywordInfo::medium())
1057 }
1058
1059 pub fn parse_quirky<'i, 't>(
1061 context: &ParserContext,
1062 input: &mut Parser<'i, 't>,
1063 allow_quirks: AllowQuirks,
1064 ) -> Result<FontSize, ParseError<'i>> {
1065 if let Ok(lp) = input
1066 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1067 {
1068 return Ok(FontSize::Length(lp));
1069 }
1070
1071 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1072 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1073 }
1074
1075 try_match_ident_ignore_ascii_case! { input,
1076 "smaller" => Ok(FontSize::Smaller),
1077 "larger" => Ok(FontSize::Larger),
1078 }
1079 }
1080}
1081
1082impl Parse for FontSize {
1083 fn parse<'i, 't>(
1085 context: &ParserContext,
1086 input: &mut Parser<'i, 't>,
1087 ) -> Result<FontSize, ParseError<'i>> {
1088 FontSize::parse_quirky(context, input, AllowQuirks::No)
1089 }
1090}
1091
1092bitflags! {
1093 #[derive(Clone, Copy)]
1094 struct VariantAlternatesParsingFlags: u8 {
1096 const NORMAL = 0;
1098 const HISTORICAL_FORMS = 0x01;
1100 const STYLISTIC = 0x02;
1102 const STYLESET = 0x04;
1104 const CHARACTER_VARIANT = 0x08;
1106 const SWASH = 0x10;
1108 const ORNAMENTS = 0x20;
1110 const ANNOTATION = 0x40;
1112 }
1113}
1114
1115#[derive(
1116 Clone,
1117 Debug,
1118 MallocSizeOf,
1119 PartialEq,
1120 SpecifiedValueInfo,
1121 ToCss,
1122 ToComputedValue,
1123 ToResolvedValue,
1124 ToShmem,
1125)]
1126#[repr(C, u8)]
1127pub enum VariantAlternates {
1129 #[css(function)]
1131 Stylistic(CustomIdent),
1132 #[css(comma, function)]
1134 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1135 #[css(comma, function)]
1137 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1138 #[css(function)]
1140 Swash(CustomIdent),
1141 #[css(function)]
1143 Ornaments(CustomIdent),
1144 #[css(function)]
1146 Annotation(CustomIdent),
1147 HistoricalForms,
1149}
1150
1151#[derive(
1152 Clone,
1153 Debug,
1154 Default,
1155 MallocSizeOf,
1156 PartialEq,
1157 SpecifiedValueInfo,
1158 ToComputedValue,
1159 ToCss,
1160 ToResolvedValue,
1161 ToShmem,
1162 ToTyped,
1163)]
1164#[repr(transparent)]
1165#[typed(todo_derive_fields)]
1166pub struct FontVariantAlternates(
1168 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1169);
1170
1171impl FontVariantAlternates {
1172 pub fn len(&self) -> usize {
1174 self.0.iter().fold(0, |acc, alternate| match *alternate {
1175 VariantAlternates::Swash(_)
1176 | VariantAlternates::Stylistic(_)
1177 | VariantAlternates::Ornaments(_)
1178 | VariantAlternates::Annotation(_) => acc + 1,
1179 VariantAlternates::Styleset(ref slice)
1180 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1181 _ => acc,
1182 })
1183 }
1184}
1185
1186impl Parse for FontVariantAlternates {
1187 fn parse<'i, 't>(
1196 _: &ParserContext,
1197 input: &mut Parser<'i, 't>,
1198 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1199 if input
1200 .try_parse(|input| input.expect_ident_matching("normal"))
1201 .is_ok()
1202 {
1203 return Ok(Default::default());
1204 }
1205
1206 let mut stylistic = None;
1207 let mut historical = None;
1208 let mut styleset = None;
1209 let mut character_variant = None;
1210 let mut swash = None;
1211 let mut ornaments = None;
1212 let mut annotation = None;
1213
1214 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1216 macro_rules! check_if_parsed(
1217 ($input:expr, $flag:path) => (
1218 if parsed_alternates.contains($flag) {
1219 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1220 }
1221 parsed_alternates |= $flag;
1222 )
1223 );
1224 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1225 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1226 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1227 historical = Some(VariantAlternates::HistoricalForms);
1228 Ok(())
1229 },
1230 Token::Function(ref name) => {
1231 let name = name.clone();
1232 input.parse_nested_block(|i| {
1233 match_ignore_ascii_case! { &name,
1234 "swash" => {
1235 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1236 let ident = CustomIdent::parse(i, &[])?;
1237 swash = Some(VariantAlternates::Swash(ident));
1238 Ok(())
1239 },
1240 "stylistic" => {
1241 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1242 let ident = CustomIdent::parse(i, &[])?;
1243 stylistic = Some(VariantAlternates::Stylistic(ident));
1244 Ok(())
1245 },
1246 "ornaments" => {
1247 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1248 let ident = CustomIdent::parse(i, &[])?;
1249 ornaments = Some(VariantAlternates::Ornaments(ident));
1250 Ok(())
1251 },
1252 "annotation" => {
1253 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1254 let ident = CustomIdent::parse(i, &[])?;
1255 annotation = Some(VariantAlternates::Annotation(ident));
1256 Ok(())
1257 },
1258 "styleset" => {
1259 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1260 let idents = i.parse_comma_separated(|i| {
1261 CustomIdent::parse(i, &[])
1262 })?;
1263 styleset = Some(VariantAlternates::Styleset(idents.into()));
1264 Ok(())
1265 },
1266 "character-variant" => {
1267 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1268 let idents = i.parse_comma_separated(|i| {
1269 CustomIdent::parse(i, &[])
1270 })?;
1271 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1272 Ok(())
1273 },
1274 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1275 }
1276 })
1277 },
1278 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1279 }) {}
1280
1281 if parsed_alternates.is_empty() {
1282 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1283 }
1284
1285 let mut alternates = Vec::new();
1287 macro_rules! push_if_some(
1288 ($value:expr) => (
1289 if let Some(v) = $value {
1290 alternates.push(v);
1291 }
1292 )
1293 );
1294 push_if_some!(stylistic);
1295 push_if_some!(historical);
1296 push_if_some!(styleset);
1297 push_if_some!(character_variant);
1298 push_if_some!(swash);
1299 push_if_some!(ornaments);
1300 push_if_some!(annotation);
1301
1302 Ok(FontVariantAlternates(alternates.into()))
1303 }
1304}
1305
1306#[derive(
1307 Clone,
1308 Copy,
1309 Debug,
1310 Eq,
1311 MallocSizeOf,
1312 PartialEq,
1313 Parse,
1314 SpecifiedValueInfo,
1315 ToComputedValue,
1316 ToCss,
1317 ToResolvedValue,
1318 ToShmem,
1319 ToTyped,
1320)]
1321#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1322#[css(bitflags(
1323 single = "normal",
1324 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1325 validate_mixed = "Self::validate_mixed_flags",
1326))]
1327#[repr(C)]
1328pub struct FontVariantEastAsian(u16);
1330bitflags! {
1331 impl FontVariantEastAsian: u16 {
1332 const NORMAL = 0;
1334 const JIS78 = 1 << 0;
1336 const JIS83 = 1 << 1;
1338 const JIS90 = 1 << 2;
1340 const JIS04 = 1 << 3;
1342 const SIMPLIFIED = 1 << 4;
1344 const TRADITIONAL = 1 << 5;
1346
1347 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1349
1350 const FULL_WIDTH = 1 << 6;
1352 const PROPORTIONAL_WIDTH = 1 << 7;
1354 const RUBY = 1 << 8;
1356 }
1357}
1358
1359impl FontVariantEastAsian {
1360 pub const COUNT: usize = 9;
1362
1363 fn validate_mixed_flags(&self) -> bool {
1364 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1365 return false;
1367 }
1368 let jis = self.intersection(Self::JIS_GROUP);
1369 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1370 return false;
1371 }
1372 true
1373 }
1374}
1375
1376#[derive(
1377 Clone,
1378 Copy,
1379 Debug,
1380 Eq,
1381 MallocSizeOf,
1382 PartialEq,
1383 Parse,
1384 SpecifiedValueInfo,
1385 ToComputedValue,
1386 ToCss,
1387 ToResolvedValue,
1388 ToShmem,
1389 ToTyped,
1390)]
1391#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1392#[css(bitflags(
1393 single = "normal,none",
1394 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1395 validate_mixed = "Self::validate_mixed_flags",
1396))]
1397#[repr(C)]
1398pub struct FontVariantLigatures(u16);
1400bitflags! {
1401 impl FontVariantLigatures: u16 {
1402 const NORMAL = 0;
1404 const NONE = 1;
1406 const COMMON_LIGATURES = 1 << 1;
1408 const NO_COMMON_LIGATURES = 1 << 2;
1410 const DISCRETIONARY_LIGATURES = 1 << 3;
1412 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1414 const HISTORICAL_LIGATURES = 1 << 5;
1416 const NO_HISTORICAL_LIGATURES = 1 << 6;
1418 const CONTEXTUAL = 1 << 7;
1420 const NO_CONTEXTUAL = 1 << 8;
1422 }
1423}
1424
1425impl FontVariantLigatures {
1426 pub const COUNT: usize = 9;
1428
1429 fn validate_mixed_flags(&self) -> bool {
1430 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1432 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1433 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1434 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1435 {
1436 return false;
1437 }
1438 true
1439 }
1440}
1441
1442#[derive(
1444 Clone,
1445 Copy,
1446 Debug,
1447 Eq,
1448 MallocSizeOf,
1449 PartialEq,
1450 Parse,
1451 SpecifiedValueInfo,
1452 ToComputedValue,
1453 ToCss,
1454 ToResolvedValue,
1455 ToShmem,
1456 ToTyped,
1457)]
1458#[css(bitflags(
1459 single = "normal",
1460 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1461 validate_mixed = "Self::validate_mixed_flags",
1462))]
1463#[cfg_attr(feature = "servo", derive(Serialize, Deserialize, Hash))]
1464#[repr(C)]
1465pub struct FontVariantNumeric(u8);
1466bitflags! {
1467 impl FontVariantNumeric : u8 {
1468 const NORMAL = 0;
1470 const LINING_NUMS = 1 << 0;
1472 const OLDSTYLE_NUMS = 1 << 1;
1474 const PROPORTIONAL_NUMS = 1 << 2;
1476 const TABULAR_NUMS = 1 << 3;
1478 const DIAGONAL_FRACTIONS = 1 << 4;
1480 const STACKED_FRACTIONS = 1 << 5;
1482 const SLASHED_ZERO = 1 << 6;
1484 const ORDINAL = 1 << 7;
1486 }
1487}
1488
1489impl FontVariantNumeric {
1490 pub const COUNT: usize = 8;
1492
1493 fn validate_mixed_flags(&self) -> bool {
1503 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1504 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1505 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1506 {
1507 return false;
1508 }
1509 true
1510 }
1511}
1512
1513pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1515
1516impl FontFeatureSettings {
1517 pub fn parse_for_font_face_rule<'i, 't>(
1520 context: &ParserContext,
1521 input: &mut Parser<'i, 't>,
1522 ) -> Result<Self, ParseError<'i>> {
1523 let settings = FontFeatureSettings::parse(context, input)?;
1524 if settings
1525 .0
1526 .iter()
1527 .any(|setting| setting.value.resolve().is_none())
1528 {
1529 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1530 }
1531 Ok(settings)
1532 }
1533}
1534
1535pub use crate::values::computed::font::FontLanguageOverride;
1537
1538impl Parse for FontLanguageOverride {
1539 fn parse<'i, 't>(
1541 _: &ParserContext,
1542 input: &mut Parser<'i, 't>,
1543 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1544 if input
1545 .try_parse(|input| input.expect_ident_matching("normal"))
1546 .is_ok()
1547 {
1548 return Ok(FontLanguageOverride::normal());
1549 }
1550
1551 let string = input.expect_string()?;
1552
1553 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1556 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1557 }
1558
1559 let mut bytes = [b' '; 4];
1560 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1561 *byte = *str_byte;
1562 }
1563
1564 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1565 }
1566}
1567
1568#[repr(u8)]
1570#[derive(
1571 Clone,
1572 Copy,
1573 Debug,
1574 Eq,
1575 Hash,
1576 MallocSizeOf,
1577 Parse,
1578 PartialEq,
1579 SpecifiedValueInfo,
1580 ToComputedValue,
1581 ToCss,
1582 ToResolvedValue,
1583 ToShmem,
1584 ToTyped,
1585)]
1586#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1587pub enum FontSynthesis {
1588 Auto,
1590 None,
1592}
1593
1594#[repr(u8)]
1596#[derive(
1597 Clone,
1598 Copy,
1599 Debug,
1600 Eq,
1601 MallocSizeOf,
1602 Parse,
1603 PartialEq,
1604 SpecifiedValueInfo,
1605 ToComputedValue,
1606 ToCss,
1607 ToResolvedValue,
1608 ToShmem,
1609 ToTyped,
1610)]
1611pub enum FontSynthesisStyle {
1612 Auto,
1614 None,
1616 ObliqueOnly,
1618}
1619
1620#[derive(
1621 Clone,
1622 Debug,
1623 Eq,
1624 MallocSizeOf,
1625 PartialEq,
1626 SpecifiedValueInfo,
1627 ToComputedValue,
1628 ToResolvedValue,
1629 ToShmem,
1630 ToTyped,
1631)]
1632#[repr(C)]
1633#[typed(todo_derive_fields)]
1634pub struct FontPalette(Atom);
1637
1638#[allow(missing_docs)]
1639impl FontPalette {
1640 pub fn normal() -> Self {
1641 Self(atom!("normal"))
1642 }
1643 pub fn light() -> Self {
1644 Self(atom!("light"))
1645 }
1646 pub fn dark() -> Self {
1647 Self(atom!("dark"))
1648 }
1649}
1650
1651impl Parse for FontPalette {
1652 fn parse<'i, 't>(
1654 _context: &ParserContext,
1655 input: &mut Parser<'i, 't>,
1656 ) -> Result<FontPalette, ParseError<'i>> {
1657 let location = input.current_source_location();
1658 let ident = input.expect_ident()?;
1659 match_ignore_ascii_case! { &ident,
1660 "normal" => Ok(Self::normal()),
1661 "light" => Ok(Self::light()),
1662 "dark" => Ok(Self::dark()),
1663 _ => if ident.starts_with("--") {
1664 Ok(Self(Atom::from(ident.as_ref())))
1665 } else {
1666 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1667 },
1668 }
1669 }
1670}
1671
1672impl ToCss for FontPalette {
1673 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1674 where
1675 W: Write,
1676 {
1677 serialize_atom_identifier(&self.0, dest)
1678 }
1679}
1680
1681pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1684
1685fn parse_one_feature_value<'i, 't>(
1686 context: &ParserContext,
1687 input: &mut Parser<'i, 't>,
1688) -> Result<Integer, ParseError<'i>> {
1689 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1690 return Ok(integer);
1691 }
1692
1693 try_match_ident_ignore_ascii_case! { input,
1694 "on" => Ok(Integer::new(1)),
1695 "off" => Ok(Integer::new(0)),
1696 }
1697}
1698
1699impl Parse for FeatureTagValue<Integer> {
1700 fn parse<'i, 't>(
1702 context: &ParserContext,
1703 input: &mut Parser<'i, 't>,
1704 ) -> Result<Self, ParseError<'i>> {
1705 let tag = FontTag::parse(context, input)?;
1706 let value = input
1707 .try_parse(|i| parse_one_feature_value(context, i))
1708 .unwrap_or_else(|_| Integer::new(1));
1709
1710 Ok(Self { tag, value })
1711 }
1712}
1713
1714impl Parse for VariationValue<Number> {
1715 fn parse<'i, 't>(
1718 context: &ParserContext,
1719 input: &mut Parser<'i, 't>,
1720 ) -> Result<Self, ParseError<'i>> {
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<'i, 't>(
1731 context: &ParserContext,
1732 input: &mut Parser<'i, 't>,
1733 ) -> Result<Self, ParseError<'i>> {
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(input.new_custom_error(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 MallocSizeOf,
1783 Parse,
1784 PartialEq,
1785 SpecifiedValueInfo,
1786 ToComputedValue,
1787 ToCss,
1788 ToResolvedValue,
1789 ToShmem,
1790 ToTyped,
1791)]
1792#[repr(u8)]
1793pub enum XTextScale {
1795 All,
1797 ZoomOnly,
1799 None,
1801}
1802
1803impl XTextScale {
1804 #[inline]
1806 pub fn text_zoom_enabled(self) -> bool {
1807 self != Self::None
1808 }
1809}
1810
1811#[derive(
1812 Clone,
1813 Debug,
1814 MallocSizeOf,
1815 PartialEq,
1816 SpecifiedValueInfo,
1817 ToComputedValue,
1818 ToCss,
1819 ToResolvedValue,
1820 ToShmem,
1821 ToTyped,
1822)]
1823#[cfg_attr(feature = "servo", derive(Deserialize, Eq, Hash, Serialize))]
1824pub struct XLang(#[css(skip)] pub Atom);
1826
1827impl XLang {
1828 #[inline]
1829 pub fn get_initial_value() -> XLang {
1831 XLang(atom!(""))
1832 }
1833}
1834
1835impl Parse for XLang {
1836 fn parse<'i, 't>(
1837 _: &ParserContext,
1838 input: &mut Parser<'i, 't>,
1839 ) -> Result<XLang, ParseError<'i>> {
1840 debug_assert!(
1841 false,
1842 "Should be set directly by presentation attributes only."
1843 );
1844 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1845 }
1846}
1847
1848#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1849#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1850pub struct MozScriptMinSize(pub NoCalcLength);
1853
1854impl MozScriptMinSize {
1855 #[inline]
1856 pub fn get_initial_value() -> Length {
1858 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1859 }
1860}
1861
1862impl Parse for MozScriptMinSize {
1863 fn parse<'i, 't>(
1864 _: &ParserContext,
1865 input: &mut Parser<'i, 't>,
1866 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1867 debug_assert!(
1868 false,
1869 "Should be set directly by presentation attributes only."
1870 );
1871 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1872 }
1873}
1874
1875#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1878#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1879pub enum MathDepth {
1880 AutoAdd,
1882
1883 #[css(function)]
1885 Add(Integer),
1886
1887 Absolute(Integer),
1889}
1890
1891impl Parse for MathDepth {
1892 fn parse<'i, 't>(
1893 context: &ParserContext,
1894 input: &mut Parser<'i, 't>,
1895 ) -> Result<MathDepth, ParseError<'i>> {
1896 if input
1897 .try_parse(|i| i.expect_ident_matching("auto-add"))
1898 .is_ok()
1899 {
1900 return Ok(MathDepth::AutoAdd);
1901 }
1902 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1903 return Ok(MathDepth::Absolute(math_depth_value));
1904 }
1905 input.expect_function_matching("add")?;
1906 let math_depth_delta_value =
1907 input.parse_nested_block(|input| Integer::parse(context, input))?;
1908 Ok(MathDepth::Add(math_depth_delta_value))
1909 }
1910}
1911
1912#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1913#[derive(
1914 Clone,
1915 Copy,
1916 Debug,
1917 PartialEq,
1918 SpecifiedValueInfo,
1919 ToComputedValue,
1920 ToCss,
1921 ToResolvedValue,
1922 ToShmem,
1923)]
1924pub struct MozScriptSizeMultiplier(pub f32);
1929
1930impl MozScriptSizeMultiplier {
1931 #[inline]
1932 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1934 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1935 }
1936}
1937
1938impl Parse for MozScriptSizeMultiplier {
1939 fn parse<'i, 't>(
1940 _: &ParserContext,
1941 input: &mut Parser<'i, 't>,
1942 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1943 debug_assert!(
1944 false,
1945 "Should be set directly by presentation attributes only."
1946 );
1947 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1948 }
1949}
1950
1951impl From<f32> for MozScriptSizeMultiplier {
1952 fn from(v: f32) -> Self {
1953 MozScriptSizeMultiplier(v)
1954 }
1955}
1956
1957impl From<MozScriptSizeMultiplier> for f32 {
1958 fn from(v: MozScriptSizeMultiplier) -> f32 {
1959 v.0
1960 }
1961}
1962
1963pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1965
1966impl ToComputedValue for LineHeight {
1967 type ComputedValue = computed::LineHeight;
1968
1969 #[inline]
1970 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1971 match self {
1972 GenericLineHeight::Normal => GenericLineHeight::Normal,
1973 #[cfg(feature = "gecko")]
1974 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1975 GenericLineHeight::Number(ref number) => {
1976 GenericLineHeight::Number(number.to_computed_value(context))
1977 },
1978 GenericLineHeight::Length(ref non_negative_lp) => {
1979 let result = match non_negative_lp.0 {
1980 LengthPercentage::Length(ref length) if length.length_unit().is_absolute() => {
1981 context.maybe_zoom_text(length.to_computed_value(context))
1982 },
1983 LengthPercentage::Length(ref length) => {
1984 length.to_computed_value_with_base_size(
1989 context,
1990 FontBaseSize::CurrentStyle,
1991 LineHeightBase::InheritedStyle,
1992 )
1993 },
1994 LengthPercentage::Percentage(ref p) => NoCalcLength::from_em(p.get())
1995 .to_computed_value_with_base_size(
1996 context,
1997 FontBaseSize::CurrentStyle,
1998 LineHeightBase::InheritedStyle,
1999 ),
2000 LengthPercentage::Calc(ref calc) => {
2001 let computed_calc = calc.to_computed_value_zoomed(
2002 context,
2003 FontBaseSize::CurrentStyle,
2004 LineHeightBase::InheritedStyle,
2005 );
2006 let base = context.style().get_font().clone_font_size().computed_size();
2007 computed_calc.resolve(base)
2008 },
2009 };
2010 GenericLineHeight::Length(result.into())
2011 },
2012 }
2013 }
2014
2015 #[inline]
2016 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
2017 match *computed {
2018 GenericLineHeight::Normal => GenericLineHeight::Normal,
2019 #[cfg(feature = "gecko")]
2020 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
2021 GenericLineHeight::Number(ref number) => {
2022 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
2023 },
2024 GenericLineHeight::Length(ref length) => {
2025 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
2026 },
2027 }
2028 }
2029}
2030
2031#[repr(C)]
2033pub struct QueryFontMetricsFlags(u8);
2034
2035bitflags! {
2036 impl QueryFontMetricsFlags: u8 {
2037 const USE_USER_FONT_SET = 1 << 0;
2039 const NEEDS_CH = 1 << 1;
2041 const NEEDS_IC = 1 << 2;
2043 const NEEDS_MATH_SCALES = 1 << 3;
2045 }
2046}