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)]
143pub enum FontWeight {
144 Absolute(AbsoluteFontWeight),
146 Bolder,
148 Lighter,
150 #[css(skip)]
152 System(SystemFont),
153}
154
155impl FontWeight {
156 system_font_methods!(FontWeight, font_weight);
157
158 #[inline]
160 pub fn normal() -> Self {
161 FontWeight::Absolute(AbsoluteFontWeight::Normal)
162 }
163
164 pub fn from_gecko_keyword(kw: u32) -> Self {
166 debug_assert!(kw % 100 == 0);
167 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
168 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
169 }
170}
171
172impl ToComputedValue for FontWeight {
173 type ComputedValue = computed::FontWeight;
174
175 #[inline]
176 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
177 match *self {
178 FontWeight::Absolute(ref abs) => abs.compute(),
179 FontWeight::Bolder => context
180 .builder
181 .get_parent_font()
182 .clone_font_weight()
183 .bolder(),
184 FontWeight::Lighter => context
185 .builder
186 .get_parent_font()
187 .clone_font_weight()
188 .lighter(),
189 FontWeight::System(_) => self.compute_system(context),
190 }
191 }
192
193 #[inline]
194 fn from_computed_value(computed: &computed::FontWeight) -> Self {
195 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
196 &computed.value(),
197 )))
198 }
199}
200
201#[derive(
205 Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
206)]
207pub enum AbsoluteFontWeight {
208 Weight(Number),
212 Normal,
214 Bold,
216}
217
218impl AbsoluteFontWeight {
219 pub fn compute(&self) -> computed::FontWeight {
221 match *self {
222 AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),
223 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
224 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
225 }
226 }
227}
228
229impl Parse for AbsoluteFontWeight {
230 fn parse<'i, 't>(
231 context: &ParserContext,
232 input: &mut Parser<'i, 't>,
233 ) -> Result<Self, ParseError<'i>> {
234 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
235 if !number.was_calc()
239 && (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
240 {
241 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
242 }
243 return Ok(AbsoluteFontWeight::Weight(number));
244 }
245
246 Ok(try_match_ident_ignore_ascii_case! { input,
247 "normal" => AbsoluteFontWeight::Normal,
248 "bold" => AbsoluteFontWeight::Bold,
249 })
250 }
251}
252
253pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
256
257impl ToCss for SpecifiedFontStyle {
258 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
259 where
260 W: Write,
261 {
262 match *self {
263 generics::FontStyle::Italic => dest.write_str("italic"),
264 generics::FontStyle::Oblique(ref angle) => {
265 if *angle == Angle::zero() {
268 dest.write_str("normal")?;
269 } else {
270 dest.write_str("oblique")?;
271 if *angle != Self::default_angle() {
272 dest.write_char(' ')?;
273 angle.to_css(dest)?;
274 }
275 }
276 Ok(())
277 },
278 }
279 }
280}
281
282impl Parse for SpecifiedFontStyle {
283 fn parse<'i, 't>(
284 context: &ParserContext,
285 input: &mut Parser<'i, 't>,
286 ) -> Result<Self, ParseError<'i>> {
287 Ok(try_match_ident_ignore_ascii_case! { input,
288 "normal" => generics::FontStyle::normal(),
289 "italic" => generics::FontStyle::Italic,
290 "oblique" => {
291 let angle = input.try_parse(|input| Self::parse_angle(context, input))
292 .unwrap_or_else(|_| Self::default_angle());
293
294 generics::FontStyle::Oblique(angle)
295 },
296 })
297 }
298}
299
300impl ToComputedValue for SpecifiedFontStyle {
301 type ComputedValue = computed::FontStyle;
302
303 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
304 match *self {
305 Self::Italic => computed::FontStyle::ITALIC,
306 Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),
307 }
308 }
309
310 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
311 if *computed == computed::FontStyle::ITALIC {
312 return Self::Italic;
313 }
314 let degrees = computed.oblique_degrees();
315 generics::FontStyle::Oblique(Angle::from_degrees(degrees, false))
316 }
317}
318
319pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
326
327pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
329
330impl SpecifiedFontStyle {
331 pub fn compute_angle_degrees(angle: &Angle) -> f32 {
333 angle
334 .degrees()
335 .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
336 .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
337 }
338
339 pub fn parse_angle<'i, 't>(
341 context: &ParserContext,
342 input: &mut Parser<'i, 't>,
343 ) -> Result<Angle, ParseError<'i>> {
344 let angle = Angle::parse(context, input)?;
345 if angle.was_calc() {
346 return Ok(angle);
347 }
348
349 let degrees = angle.degrees();
350 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES
351 || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
352 {
353 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
354 }
355 return Ok(angle);
356 }
357
358 pub fn default_angle() -> Angle {
360 Angle::from_degrees(
361 computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,
362 false,
363 )
364 }
365}
366
367#[derive(
369 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
370)]
371#[allow(missing_docs)]
372#[typed(todo_derive_fields)]
373pub enum FontStyle {
374 Specified(SpecifiedFontStyle),
375 #[css(skip)]
376 System(SystemFont),
377}
378
379impl FontStyle {
380 #[inline]
382 pub fn normal() -> Self {
383 FontStyle::Specified(generics::FontStyle::normal())
384 }
385
386 system_font_methods!(FontStyle, font_style);
387}
388
389impl ToComputedValue for FontStyle {
390 type ComputedValue = computed::FontStyle;
391
392 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
393 match *self {
394 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
395 FontStyle::System(..) => self.compute_system(context),
396 }
397 }
398
399 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
400 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
401 }
402}
403
404#[allow(missing_docs)]
408#[derive(
409 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
410)]
411pub enum FontStretch {
412 Stretch(NonNegativePercentage),
413 Keyword(FontStretchKeyword),
414 #[css(skip)]
415 System(SystemFont),
416}
417
418#[derive(
420 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
421)]
422#[allow(missing_docs)]
423pub enum FontStretchKeyword {
424 Normal,
425 Condensed,
426 UltraCondensed,
427 ExtraCondensed,
428 SemiCondensed,
429 SemiExpanded,
430 Expanded,
431 ExtraExpanded,
432 UltraExpanded,
433}
434
435impl FontStretchKeyword {
436 pub fn compute(&self) -> computed::FontStretch {
438 computed::FontStretch::from_keyword(*self)
439 }
440
441 pub fn from_percentage(p: f32) -> Option<Self> {
444 computed::FontStretch::from_percentage(p).as_keyword()
445 }
446}
447
448impl FontStretch {
449 pub fn normal() -> Self {
451 FontStretch::Keyword(FontStretchKeyword::Normal)
452 }
453
454 system_font_methods!(FontStretch, font_stretch);
455}
456
457impl ToComputedValue for FontStretch {
458 type ComputedValue = computed::FontStretch;
459
460 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
461 match *self {
462 FontStretch::Stretch(ref percentage) => {
463 let percentage = percentage.to_computed_value(context).0;
464 computed::FontStretch::from_percentage(percentage.0)
465 },
466 FontStretch::Keyword(ref kw) => kw.compute(),
467 FontStretch::System(_) => self.compute_system(context),
468 }
469 }
470
471 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
472 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
473 computed.to_percentage(),
474 )))
475 }
476}
477
478#[derive(
480 Animate,
481 Clone,
482 ComputeSquaredDistance,
483 Copy,
484 Debug,
485 MallocSizeOf,
486 Parse,
487 PartialEq,
488 SpecifiedValueInfo,
489 ToAnimatedValue,
490 ToAnimatedZero,
491 ToComputedValue,
492 ToCss,
493 ToResolvedValue,
494 ToShmem,
495 Serialize,
496 Deserialize,
497 ToTyped,
498)]
499#[allow(missing_docs)]
500#[repr(u8)]
501pub enum FontSizeKeyword {
502 #[css(keyword = "xx-small")]
503 XXSmall,
504 XSmall,
505 Small,
506 Medium,
507 Large,
508 XLarge,
509 #[css(keyword = "xx-large")]
510 XXLarge,
511 #[css(keyword = "xxx-large")]
512 XXXLarge,
513 #[cfg(feature = "gecko")]
516 Math,
517 #[css(skip)]
518 None,
519}
520
521impl FontSizeKeyword {
522 #[inline]
524 pub fn html_size(self) -> u8 {
525 self as u8
526 }
527
528 #[cfg(feature = "gecko")]
530 pub fn is_math(self) -> bool {
531 matches!(self, Self::Math)
532 }
533
534 #[cfg(feature = "servo")]
536 pub fn is_math(self) -> bool {
537 false
538 }
539}
540
541impl Default for FontSizeKeyword {
542 fn default() -> Self {
543 FontSizeKeyword::Medium
544 }
545}
546
547#[derive(
548 Animate,
549 Clone,
550 ComputeSquaredDistance,
551 Copy,
552 Debug,
553 MallocSizeOf,
554 PartialEq,
555 ToAnimatedValue,
556 ToAnimatedZero,
557 ToComputedValue,
558 ToCss,
559 ToResolvedValue,
560 ToShmem,
561 ToTyped,
562)]
563#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
564pub struct KeywordInfo {
566 pub kw: FontSizeKeyword,
568 #[css(skip)]
570 pub factor: f32,
571 #[css(skip)]
574 pub offset: CSSPixelLength,
575}
576
577impl KeywordInfo {
578 pub fn medium() -> Self {
580 Self::new(FontSizeKeyword::Medium)
581 }
582
583 pub fn none() -> Self {
585 Self::new(FontSizeKeyword::None)
586 }
587
588 fn new(kw: FontSizeKeyword) -> Self {
589 KeywordInfo {
590 kw,
591 factor: 1.,
592 offset: CSSPixelLength::new(0.),
593 }
594 }
595
596 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
599 debug_assert_ne!(self.kw, FontSizeKeyword::None);
600 #[cfg(feature = "gecko")]
601 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
602 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
603 let zoom_factor = context.style().effective_zoom.value();
604 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
605 + context.maybe_zoom_text(self.offset)
606 }
607
608 fn compose(self, factor: f32) -> Self {
611 if self.kw == FontSizeKeyword::None {
612 return self;
613 }
614 KeywordInfo {
615 kw: self.kw,
616 factor: self.factor * factor,
617 offset: self.offset * factor,
618 }
619 }
620}
621
622impl SpecifiedValueInfo for KeywordInfo {
623 fn collect_completion_keywords(f: KeywordsCollectFn) {
624 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
625 }
626}
627
628#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
629pub enum FontSize {
631 Length(LengthPercentage),
633 Keyword(KeywordInfo),
644 Smaller,
646 Larger,
648 #[css(skip)]
650 System(SystemFont),
651}
652
653#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem, ToTyped)]
655#[cfg_attr(feature = "servo", derive(Hash))]
656#[typed(todo_derive_fields)]
657pub enum FontFamily {
658 #[css(comma)]
660 Values(#[css(iterable)] FontFamilyList),
661 #[css(skip)]
663 System(SystemFont),
664}
665
666impl FontFamily {
667 system_font_methods!(FontFamily, font_family);
668}
669
670impl ToComputedValue for FontFamily {
671 type ComputedValue = computed::FontFamily;
672
673 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
674 match *self {
675 FontFamily::Values(ref list) => computed::FontFamily {
676 families: list.clone(),
677 is_system_font: false,
678 is_initial: false,
679 },
680 FontFamily::System(_) => self.compute_system(context),
681 }
682 }
683
684 fn from_computed_value(other: &computed::FontFamily) -> Self {
685 FontFamily::Values(other.families.clone())
686 }
687}
688
689#[cfg(feature = "gecko")]
690impl MallocSizeOf for FontFamily {
691 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
692 match *self {
693 FontFamily::Values(ref v) => {
694 v.list.unconditional_size_of(ops)
697 },
698 FontFamily::System(_) => 0,
699 }
700 }
701}
702
703impl Parse for FontFamily {
704 fn parse<'i, 't>(
708 context: &ParserContext,
709 input: &mut Parser<'i, 't>,
710 ) -> Result<FontFamily, ParseError<'i>> {
711 let values =
712 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
713 Ok(FontFamily::Values(FontFamilyList {
714 list: crate::ArcSlice::from_iter(values.into_iter()),
715 }))
716 }
717}
718
719impl SpecifiedValueInfo for FontFamily {}
720
721impl Parse for FamilyName {
724 fn parse<'i, 't>(
725 context: &ParserContext,
726 input: &mut Parser<'i, 't>,
727 ) -> Result<Self, ParseError<'i>> {
728 match SingleFontFamily::parse(context, input) {
729 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
730 Ok(SingleFontFamily::Generic(_)) => {
731 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
732 },
733 Err(e) => Err(e),
734 }
735 }
736}
737
738#[derive(
741 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
742)]
743pub enum FontSizeAdjustFactor {
744 Number(NonNegativeNumber),
746 FromFont,
748}
749
750pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
755
756impl Parse for FontSizeAdjust {
757 fn parse<'i, 't>(
758 context: &ParserContext,
759 input: &mut Parser<'i, 't>,
760 ) -> Result<Self, ParseError<'i>> {
761 let location = input.current_source_location();
762 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
764 return Ok(Self::ExHeight(factor));
765 }
766
767 let ident = input.expect_ident()?;
768 let basis = match_ignore_ascii_case! { &ident,
769 "none" => return Ok(Self::None),
770 "ex-height" => Self::ExHeight,
772 "cap-height" => Self::CapHeight,
773 "ch-width" => Self::ChWidth,
774 "ic-width" => Self::IcWidth,
775 "ic-height" => Self::IcHeight,
776 _ => return Err(location.new_custom_error(
778 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
779 )),
780 };
781
782 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
783 }
784}
785
786const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
789
790pub const FONT_MEDIUM_PX: f32 = 16.0;
792pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
794pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
797pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
800pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
803pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
806
807impl FontSizeKeyword {
808 #[inline]
809 fn to_length(&self, cx: &Context) -> NonNegativeLength {
810 let font = cx.style().get_font();
811
812 #[cfg(feature = "servo")]
813 let family = &font.font_family.families;
814 #[cfg(feature = "gecko")]
815 let family = &font.mFont.family.families;
816
817 let generic = family
818 .single_generic()
819 .unwrap_or(computed::GenericFontFamily::None);
820
821 #[cfg(feature = "gecko")]
822 let base_size = unsafe {
823 Atom::with(font.mLanguage.mRawPtr, |language| {
824 cx.device().base_size_for_generic(language, generic)
825 })
826 };
827 #[cfg(feature = "servo")]
828 let base_size = cx.device().base_size_for_generic(generic);
829
830 self.to_length_without_context(cx.quirks_mode, base_size)
831 }
832
833 #[inline]
835 pub fn to_length_without_context(
836 &self,
837 quirks_mode: QuirksMode,
838 base_size: Length,
839 ) -> NonNegativeLength {
840 #[cfg(feature = "gecko")]
841 debug_assert_ne!(*self, FontSizeKeyword::Math);
842 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
855 [9, 9, 9, 9, 11, 14, 18, 27],
856 [9, 9, 9, 10, 12, 15, 20, 30],
857 [9, 9, 10, 11, 13, 17, 22, 33],
858 [9, 9, 10, 12, 14, 18, 24, 36],
859 [9, 10, 12, 13, 16, 20, 26, 39],
860 [9, 10, 12, 14, 17, 21, 28, 42],
861 [9, 10, 13, 15, 18, 23, 30, 45],
862 [9, 10, 13, 16, 18, 24, 32, 48],
863 ];
864
865 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
874 [9, 9, 9, 9, 11, 14, 18, 28],
875 [9, 9, 9, 10, 12, 15, 20, 31],
876 [9, 9, 9, 11, 13, 17, 22, 34],
877 [9, 9, 10, 12, 14, 18, 24, 37],
878 [9, 9, 10, 13, 16, 20, 26, 40],
879 [9, 9, 11, 14, 17, 21, 28, 42],
880 [9, 10, 12, 15, 17, 23, 30, 45],
881 [9, 10, 13, 16, 18, 24, 32, 48],
882 ];
883
884 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
885 let base_size_px = base_size.px().round() as i32;
886 let html_size = self.html_size() as usize;
887 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
888 let mapping = if quirks_mode == QuirksMode::Quirks {
889 QUIRKS_FONT_SIZE_MAPPING
890 } else {
891 FONT_SIZE_MAPPING
892 };
893 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
894 } else {
895 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
896 })
897 }
898}
899
900impl FontSize {
901 pub fn from_html_size(size: u8) -> Self {
903 FontSize::Keyword(KeywordInfo::new(match size {
904 0 | 1 => FontSizeKeyword::XSmall,
906 2 => FontSizeKeyword::Small,
907 3 => FontSizeKeyword::Medium,
908 4 => FontSizeKeyword::Large,
909 5 => FontSizeKeyword::XLarge,
910 6 => FontSizeKeyword::XXLarge,
911 _ => FontSizeKeyword::XXXLarge,
913 }))
914 }
915
916 pub fn to_computed_value_against(
918 &self,
919 context: &Context,
920 base_size: FontBaseSize,
921 line_height_base: LineHeightBase,
922 ) -> computed::FontSize {
923 let compose_keyword = |factor| {
924 context
925 .style()
926 .get_parent_font()
927 .clone_font_size()
928 .keyword_info
929 .compose(factor)
930 };
931 let mut info = KeywordInfo::none();
932 let size = match *self {
933 FontSize::Length(LengthPercentage::Length(ref l)) => {
934 if let NoCalcLength::FontRelative(ref value) = *l {
935 if let FontRelativeLength::Em(em) = *value {
936 info = compose_keyword(em);
939 }
940 }
941 let result =
942 l.to_computed_value_with_base_size(context, base_size, line_height_base);
943 if l.should_zoom_text() {
944 context.maybe_zoom_text(result)
945 } else {
946 result
947 }
948 },
949 FontSize::Length(LengthPercentage::Percentage(pc)) => {
950 info = compose_keyword(pc.0);
953 (base_size.resolve(context).computed_size() * pc.0).normalized()
954 },
955 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
956 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
957 calc.resolve(base_size.resolve(context).computed_size())
958 },
959 FontSize::Keyword(i) => {
960 if i.kw.is_math() {
961 info = compose_keyword(1.);
963 info.kw = i.kw;
966 FontRelativeLength::Em(1.).to_computed_value(
967 context,
968 base_size,
969 line_height_base,
970 )
971 } else {
972 info = i;
974 i.to_computed_value(context).clamp_to_non_negative()
975 }
976 },
977 FontSize::Smaller => {
978 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
979 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
980 context,
981 base_size,
982 line_height_base,
983 )
984 },
985 FontSize::Larger => {
986 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
987 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
988 context,
989 base_size,
990 line_height_base,
991 )
992 },
993
994 FontSize::System(_) => {
995 #[cfg(feature = "servo")]
996 {
997 unreachable!()
998 }
999 #[cfg(feature = "gecko")]
1000 {
1001 context
1002 .cached_system_font
1003 .as_ref()
1004 .unwrap()
1005 .font_size
1006 .computed_size()
1007 }
1008 },
1009 };
1010 computed::FontSize {
1011 computed_size: NonNegative(size),
1012 used_size: NonNegative(size),
1013 keyword_info: info,
1014 }
1015 }
1016}
1017
1018impl ToComputedValue for FontSize {
1019 type ComputedValue = computed::FontSize;
1020
1021 #[inline]
1022 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1023 self.to_computed_value_against(
1024 context,
1025 FontBaseSize::InheritedStyle,
1026 LineHeightBase::InheritedStyle,
1027 )
1028 }
1029
1030 #[inline]
1031 fn from_computed_value(computed: &computed::FontSize) -> Self {
1032 FontSize::Length(LengthPercentage::Length(
1033 ToComputedValue::from_computed_value(&computed.computed_size()),
1034 ))
1035 }
1036}
1037
1038impl FontSize {
1039 system_font_methods!(FontSize);
1040
1041 #[inline]
1043 pub fn medium() -> Self {
1044 FontSize::Keyword(KeywordInfo::medium())
1045 }
1046
1047 pub fn parse_quirky<'i, 't>(
1049 context: &ParserContext,
1050 input: &mut Parser<'i, 't>,
1051 allow_quirks: AllowQuirks,
1052 ) -> Result<FontSize, ParseError<'i>> {
1053 if let Ok(lp) = input
1054 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1055 {
1056 return Ok(FontSize::Length(lp));
1057 }
1058
1059 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1060 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1061 }
1062
1063 try_match_ident_ignore_ascii_case! { input,
1064 "smaller" => Ok(FontSize::Smaller),
1065 "larger" => Ok(FontSize::Larger),
1066 }
1067 }
1068}
1069
1070impl Parse for FontSize {
1071 fn parse<'i, 't>(
1073 context: &ParserContext,
1074 input: &mut Parser<'i, 't>,
1075 ) -> Result<FontSize, ParseError<'i>> {
1076 FontSize::parse_quirky(context, input, AllowQuirks::No)
1077 }
1078}
1079
1080bitflags! {
1081 #[derive(Clone, Copy)]
1082 struct VariantAlternatesParsingFlags: u8 {
1084 const NORMAL = 0;
1086 const HISTORICAL_FORMS = 0x01;
1088 const STYLISTIC = 0x02;
1090 const STYLESET = 0x04;
1092 const CHARACTER_VARIANT = 0x08;
1094 const SWASH = 0x10;
1096 const ORNAMENTS = 0x20;
1098 const ANNOTATION = 0x40;
1100 }
1101}
1102
1103#[derive(
1104 Clone,
1105 Debug,
1106 MallocSizeOf,
1107 PartialEq,
1108 SpecifiedValueInfo,
1109 ToCss,
1110 ToComputedValue,
1111 ToResolvedValue,
1112 ToShmem,
1113)]
1114#[repr(C, u8)]
1115pub enum VariantAlternates {
1117 #[css(function)]
1119 Stylistic(CustomIdent),
1120 #[css(comma, function)]
1122 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1123 #[css(comma, function)]
1125 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1126 #[css(function)]
1128 Swash(CustomIdent),
1129 #[css(function)]
1131 Ornaments(CustomIdent),
1132 #[css(function)]
1134 Annotation(CustomIdent),
1135 HistoricalForms,
1137}
1138
1139#[derive(
1140 Clone,
1141 Debug,
1142 Default,
1143 MallocSizeOf,
1144 PartialEq,
1145 SpecifiedValueInfo,
1146 ToComputedValue,
1147 ToCss,
1148 ToResolvedValue,
1149 ToShmem,
1150 ToTyped,
1151)]
1152#[repr(transparent)]
1153#[typed(todo_derive_fields)]
1154pub struct FontVariantAlternates(
1156 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1157);
1158
1159impl FontVariantAlternates {
1160 pub fn len(&self) -> usize {
1162 self.0.iter().fold(0, |acc, alternate| match *alternate {
1163 VariantAlternates::Swash(_)
1164 | VariantAlternates::Stylistic(_)
1165 | VariantAlternates::Ornaments(_)
1166 | VariantAlternates::Annotation(_) => acc + 1,
1167 VariantAlternates::Styleset(ref slice)
1168 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1169 _ => acc,
1170 })
1171 }
1172}
1173
1174impl Parse for FontVariantAlternates {
1175 fn parse<'i, 't>(
1184 _: &ParserContext,
1185 input: &mut Parser<'i, 't>,
1186 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1187 if input
1188 .try_parse(|input| input.expect_ident_matching("normal"))
1189 .is_ok()
1190 {
1191 return Ok(Default::default());
1192 }
1193
1194 let mut stylistic = None;
1195 let mut historical = None;
1196 let mut styleset = None;
1197 let mut character_variant = None;
1198 let mut swash = None;
1199 let mut ornaments = None;
1200 let mut annotation = None;
1201
1202 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1204 macro_rules! check_if_parsed(
1205 ($input:expr, $flag:path) => (
1206 if parsed_alternates.contains($flag) {
1207 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1208 }
1209 parsed_alternates |= $flag;
1210 )
1211 );
1212 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1213 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1214 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1215 historical = Some(VariantAlternates::HistoricalForms);
1216 Ok(())
1217 },
1218 Token::Function(ref name) => {
1219 let name = name.clone();
1220 input.parse_nested_block(|i| {
1221 match_ignore_ascii_case! { &name,
1222 "swash" => {
1223 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1224 let ident = CustomIdent::parse(i, &[])?;
1225 swash = Some(VariantAlternates::Swash(ident));
1226 Ok(())
1227 },
1228 "stylistic" => {
1229 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1230 let ident = CustomIdent::parse(i, &[])?;
1231 stylistic = Some(VariantAlternates::Stylistic(ident));
1232 Ok(())
1233 },
1234 "ornaments" => {
1235 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1236 let ident = CustomIdent::parse(i, &[])?;
1237 ornaments = Some(VariantAlternates::Ornaments(ident));
1238 Ok(())
1239 },
1240 "annotation" => {
1241 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1242 let ident = CustomIdent::parse(i, &[])?;
1243 annotation = Some(VariantAlternates::Annotation(ident));
1244 Ok(())
1245 },
1246 "styleset" => {
1247 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1248 let idents = i.parse_comma_separated(|i| {
1249 CustomIdent::parse(i, &[])
1250 })?;
1251 styleset = Some(VariantAlternates::Styleset(idents.into()));
1252 Ok(())
1253 },
1254 "character-variant" => {
1255 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1256 let idents = i.parse_comma_separated(|i| {
1257 CustomIdent::parse(i, &[])
1258 })?;
1259 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1260 Ok(())
1261 },
1262 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1263 }
1264 })
1265 },
1266 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1267 }) {}
1268
1269 if parsed_alternates.is_empty() {
1270 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1271 }
1272
1273 let mut alternates = Vec::new();
1275 macro_rules! push_if_some(
1276 ($value:expr) => (
1277 if let Some(v) = $value {
1278 alternates.push(v);
1279 }
1280 )
1281 );
1282 push_if_some!(stylistic);
1283 push_if_some!(historical);
1284 push_if_some!(styleset);
1285 push_if_some!(character_variant);
1286 push_if_some!(swash);
1287 push_if_some!(ornaments);
1288 push_if_some!(annotation);
1289
1290 Ok(FontVariantAlternates(alternates.into()))
1291 }
1292}
1293
1294#[derive(
1295 Clone,
1296 Copy,
1297 Debug,
1298 Eq,
1299 MallocSizeOf,
1300 PartialEq,
1301 Parse,
1302 SpecifiedValueInfo,
1303 ToComputedValue,
1304 ToCss,
1305 ToResolvedValue,
1306 ToShmem,
1307 ToTyped,
1308)]
1309#[css(bitflags(
1310 single = "normal",
1311 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1312 validate_mixed = "Self::validate_mixed_flags",
1313))]
1314#[repr(C)]
1315pub struct FontVariantEastAsian(u16);
1317bitflags! {
1318 impl FontVariantEastAsian: u16 {
1319 const NORMAL = 0;
1321 const JIS78 = 1 << 0;
1323 const JIS83 = 1 << 1;
1325 const JIS90 = 1 << 2;
1327 const JIS04 = 1 << 3;
1329 const SIMPLIFIED = 1 << 4;
1331 const TRADITIONAL = 1 << 5;
1333
1334 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1336
1337 const FULL_WIDTH = 1 << 6;
1339 const PROPORTIONAL_WIDTH = 1 << 7;
1341 const RUBY = 1 << 8;
1343 }
1344}
1345
1346impl FontVariantEastAsian {
1347 pub const COUNT: usize = 9;
1349
1350 fn validate_mixed_flags(&self) -> bool {
1351 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1352 return false;
1354 }
1355 let jis = self.intersection(Self::JIS_GROUP);
1356 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1357 return false;
1358 }
1359 true
1360 }
1361}
1362
1363#[derive(
1364 Clone,
1365 Copy,
1366 Debug,
1367 Eq,
1368 MallocSizeOf,
1369 PartialEq,
1370 Parse,
1371 SpecifiedValueInfo,
1372 ToComputedValue,
1373 ToCss,
1374 ToResolvedValue,
1375 ToShmem,
1376 ToTyped,
1377)]
1378#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1379#[css(bitflags(
1380 single = "normal,none",
1381 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1382 validate_mixed = "Self::validate_mixed_flags",
1383))]
1384#[repr(C)]
1385pub struct FontVariantLigatures(u16);
1387bitflags! {
1388 impl FontVariantLigatures: u16 {
1389 const NORMAL = 0;
1391 const NONE = 1;
1393 const COMMON_LIGATURES = 1 << 1;
1395 const NO_COMMON_LIGATURES = 1 << 2;
1397 const DISCRETIONARY_LIGATURES = 1 << 3;
1399 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1401 const HISTORICAL_LIGATURES = 1 << 5;
1403 const NO_HISTORICAL_LIGATURES = 1 << 6;
1405 const CONTEXTUAL = 1 << 7;
1407 const NO_CONTEXTUAL = 1 << 8;
1409 }
1410}
1411
1412impl FontVariantLigatures {
1413 pub const COUNT: usize = 9;
1415
1416 fn validate_mixed_flags(&self) -> bool {
1417 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1419 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1420 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1421 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1422 {
1423 return false;
1424 }
1425 true
1426 }
1427}
1428
1429#[derive(
1431 Clone,
1432 Copy,
1433 Debug,
1434 Eq,
1435 MallocSizeOf,
1436 PartialEq,
1437 Parse,
1438 SpecifiedValueInfo,
1439 ToComputedValue,
1440 ToCss,
1441 ToResolvedValue,
1442 ToShmem,
1443 ToTyped,
1444)]
1445#[css(bitflags(
1446 single = "normal",
1447 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1448 validate_mixed = "Self::validate_mixed_flags",
1449))]
1450#[cfg_attr(feature = "servo", derive(Serialize, Deserialize, Hash))]
1451#[repr(C)]
1452pub struct FontVariantNumeric(u8);
1453bitflags! {
1454 impl FontVariantNumeric : u8 {
1455 const NORMAL = 0;
1457 const LINING_NUMS = 1 << 0;
1459 const OLDSTYLE_NUMS = 1 << 1;
1461 const PROPORTIONAL_NUMS = 1 << 2;
1463 const TABULAR_NUMS = 1 << 3;
1465 const DIAGONAL_FRACTIONS = 1 << 4;
1467 const STACKED_FRACTIONS = 1 << 5;
1469 const SLASHED_ZERO = 1 << 6;
1471 const ORDINAL = 1 << 7;
1473 }
1474}
1475
1476impl FontVariantNumeric {
1477 pub const COUNT: usize = 8;
1479
1480 fn validate_mixed_flags(&self) -> bool {
1490 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1491 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1492 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1493 {
1494 return false;
1495 }
1496 true
1497 }
1498}
1499
1500pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1502
1503pub use crate::values::computed::font::FontLanguageOverride;
1505
1506impl Parse for FontLanguageOverride {
1507 fn parse<'i, 't>(
1509 _: &ParserContext,
1510 input: &mut Parser<'i, 't>,
1511 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1512 if input
1513 .try_parse(|input| input.expect_ident_matching("normal"))
1514 .is_ok()
1515 {
1516 return Ok(FontLanguageOverride::normal());
1517 }
1518
1519 let string = input.expect_string()?;
1520
1521 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1524 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1525 }
1526
1527 let mut bytes = [b' '; 4];
1528 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1529 *byte = *str_byte;
1530 }
1531
1532 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1533 }
1534}
1535
1536#[repr(u8)]
1538#[derive(
1539 Clone,
1540 Copy,
1541 Debug,
1542 Eq,
1543 Hash,
1544 MallocSizeOf,
1545 Parse,
1546 PartialEq,
1547 SpecifiedValueInfo,
1548 ToComputedValue,
1549 ToCss,
1550 ToResolvedValue,
1551 ToShmem,
1552 ToTyped,
1553)]
1554#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1555pub enum FontSynthesis {
1556 Auto,
1558 None,
1560}
1561
1562#[repr(u8)]
1564#[derive(
1565 Clone,
1566 Copy,
1567 Debug,
1568 Eq,
1569 MallocSizeOf,
1570 Parse,
1571 PartialEq,
1572 SpecifiedValueInfo,
1573 ToComputedValue,
1574 ToCss,
1575 ToResolvedValue,
1576 ToShmem,
1577 ToTyped,
1578)]
1579pub enum FontSynthesisStyle {
1580 Auto,
1582 None,
1584 ObliqueOnly,
1586}
1587
1588#[derive(
1589 Clone,
1590 Debug,
1591 Eq,
1592 MallocSizeOf,
1593 PartialEq,
1594 SpecifiedValueInfo,
1595 ToComputedValue,
1596 ToResolvedValue,
1597 ToShmem,
1598 ToTyped,
1599)]
1600#[repr(C)]
1601#[typed(todo_derive_fields)]
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}