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