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#[css(bitflags(
1379 single = "normal,none",
1380 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1381 validate_mixed = "Self::validate_mixed_flags",
1382))]
1383#[repr(C)]
1384pub struct FontVariantLigatures(u16);
1386bitflags! {
1387 impl FontVariantLigatures: u16 {
1388 const NORMAL = 0;
1390 const NONE = 1;
1392 const COMMON_LIGATURES = 1 << 1;
1394 const NO_COMMON_LIGATURES = 1 << 2;
1396 const DISCRETIONARY_LIGATURES = 1 << 3;
1398 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1400 const HISTORICAL_LIGATURES = 1 << 5;
1402 const NO_HISTORICAL_LIGATURES = 1 << 6;
1404 const CONTEXTUAL = 1 << 7;
1406 const NO_CONTEXTUAL = 1 << 8;
1408 }
1409}
1410
1411impl FontVariantLigatures {
1412 pub const COUNT: usize = 9;
1414
1415 fn validate_mixed_flags(&self) -> bool {
1416 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1418 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1419 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1420 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1421 {
1422 return false;
1423 }
1424 true
1425 }
1426}
1427
1428#[derive(
1430 Clone,
1431 Copy,
1432 Debug,
1433 Eq,
1434 MallocSizeOf,
1435 PartialEq,
1436 Parse,
1437 SpecifiedValueInfo,
1438 ToComputedValue,
1439 ToCss,
1440 ToResolvedValue,
1441 ToShmem,
1442 ToTyped,
1443)]
1444#[css(bitflags(
1445 single = "normal",
1446 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1447 validate_mixed = "Self::validate_mixed_flags",
1448))]
1449#[repr(C)]
1450pub struct FontVariantNumeric(u8);
1451bitflags! {
1452 impl FontVariantNumeric : u8 {
1453 const NORMAL = 0;
1455 const LINING_NUMS = 1 << 0;
1457 const OLDSTYLE_NUMS = 1 << 1;
1459 const PROPORTIONAL_NUMS = 1 << 2;
1461 const TABULAR_NUMS = 1 << 3;
1463 const DIAGONAL_FRACTIONS = 1 << 4;
1465 const STACKED_FRACTIONS = 1 << 5;
1467 const SLASHED_ZERO = 1 << 6;
1469 const ORDINAL = 1 << 7;
1471 }
1472}
1473
1474impl FontVariantNumeric {
1475 pub const COUNT: usize = 8;
1477
1478 fn validate_mixed_flags(&self) -> bool {
1488 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1489 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1490 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1491 {
1492 return false;
1493 }
1494 true
1495 }
1496}
1497
1498pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1500
1501pub use crate::values::computed::font::FontLanguageOverride;
1503
1504impl Parse for FontLanguageOverride {
1505 fn parse<'i, 't>(
1507 _: &ParserContext,
1508 input: &mut Parser<'i, 't>,
1509 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1510 if input
1511 .try_parse(|input| input.expect_ident_matching("normal"))
1512 .is_ok()
1513 {
1514 return Ok(FontLanguageOverride::normal());
1515 }
1516
1517 let string = input.expect_string()?;
1518
1519 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1522 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1523 }
1524
1525 let mut bytes = [b' '; 4];
1526 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1527 *byte = *str_byte;
1528 }
1529
1530 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1531 }
1532}
1533
1534#[repr(u8)]
1536#[derive(
1537 Clone,
1538 Copy,
1539 Debug,
1540 Eq,
1541 Hash,
1542 MallocSizeOf,
1543 Parse,
1544 PartialEq,
1545 SpecifiedValueInfo,
1546 ToComputedValue,
1547 ToCss,
1548 ToResolvedValue,
1549 ToShmem,
1550 ToTyped,
1551)]
1552#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1553pub enum FontSynthesis {
1554 Auto,
1556 None,
1558}
1559
1560#[repr(u8)]
1562#[derive(
1563 Clone,
1564 Copy,
1565 Debug,
1566 Eq,
1567 MallocSizeOf,
1568 Parse,
1569 PartialEq,
1570 SpecifiedValueInfo,
1571 ToComputedValue,
1572 ToCss,
1573 ToResolvedValue,
1574 ToShmem,
1575 ToTyped,
1576)]
1577pub enum FontSynthesisStyle {
1578 Auto,
1580 None,
1582 ObliqueOnly,
1584}
1585
1586#[derive(
1587 Clone,
1588 Debug,
1589 Eq,
1590 MallocSizeOf,
1591 PartialEq,
1592 SpecifiedValueInfo,
1593 ToComputedValue,
1594 ToResolvedValue,
1595 ToShmem,
1596 ToTyped,
1597)]
1598#[repr(C)]
1599#[typed(todo_derive_fields)]
1600pub struct FontPalette(Atom);
1603
1604#[allow(missing_docs)]
1605impl FontPalette {
1606 pub fn normal() -> Self {
1607 Self(atom!("normal"))
1608 }
1609 pub fn light() -> Self {
1610 Self(atom!("light"))
1611 }
1612 pub fn dark() -> Self {
1613 Self(atom!("dark"))
1614 }
1615}
1616
1617impl Parse for FontPalette {
1618 fn parse<'i, 't>(
1620 _context: &ParserContext,
1621 input: &mut Parser<'i, 't>,
1622 ) -> Result<FontPalette, ParseError<'i>> {
1623 let location = input.current_source_location();
1624 let ident = input.expect_ident()?;
1625 match_ignore_ascii_case! { &ident,
1626 "normal" => Ok(Self::normal()),
1627 "light" => Ok(Self::light()),
1628 "dark" => Ok(Self::dark()),
1629 _ => if ident.starts_with("--") {
1630 Ok(Self(Atom::from(ident.as_ref())))
1631 } else {
1632 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1633 },
1634 }
1635 }
1636}
1637
1638impl ToCss for FontPalette {
1639 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1640 where
1641 W: Write,
1642 {
1643 serialize_atom_identifier(&self.0, dest)
1644 }
1645}
1646
1647pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1650
1651fn parse_one_feature_value<'i, 't>(
1652 context: &ParserContext,
1653 input: &mut Parser<'i, 't>,
1654) -> Result<Integer, ParseError<'i>> {
1655 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1656 return Ok(integer);
1657 }
1658
1659 try_match_ident_ignore_ascii_case! { input,
1660 "on" => Ok(Integer::new(1)),
1661 "off" => Ok(Integer::new(0)),
1662 }
1663}
1664
1665impl Parse for FeatureTagValue<Integer> {
1666 fn parse<'i, 't>(
1668 context: &ParserContext,
1669 input: &mut Parser<'i, 't>,
1670 ) -> Result<Self, ParseError<'i>> {
1671 let tag = FontTag::parse(context, input)?;
1672 let value = input
1673 .try_parse(|i| parse_one_feature_value(context, i))
1674 .unwrap_or_else(|_| Integer::new(1));
1675
1676 Ok(Self { tag, value })
1677 }
1678}
1679
1680impl Parse for VariationValue<Number> {
1681 fn parse<'i, 't>(
1684 context: &ParserContext,
1685 input: &mut Parser<'i, 't>,
1686 ) -> Result<Self, ParseError<'i>> {
1687 let tag = FontTag::parse(context, input)?;
1688 let value = Number::parse(context, input)?;
1689 Ok(Self { tag, value })
1690 }
1691}
1692
1693#[derive(
1697 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1698)]
1699pub enum MetricsOverride {
1700 Override(NonNegativePercentage),
1702 Normal,
1704}
1705
1706impl MetricsOverride {
1707 #[inline]
1708 pub fn normal() -> MetricsOverride {
1710 MetricsOverride::Normal
1711 }
1712
1713 #[inline]
1718 pub fn compute(&self) -> ComputedPercentage {
1719 match *self {
1720 MetricsOverride::Normal => ComputedPercentage(-1.0),
1721 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1722 }
1723 }
1724}
1725
1726#[derive(
1727 Clone,
1728 Copy,
1729 Debug,
1730 MallocSizeOf,
1731 Parse,
1732 PartialEq,
1733 SpecifiedValueInfo,
1734 ToComputedValue,
1735 ToCss,
1736 ToResolvedValue,
1737 ToShmem,
1738 ToTyped,
1739)]
1740#[repr(u8)]
1741pub enum XTextScale {
1743 All,
1745 ZoomOnly,
1747 None,
1749}
1750
1751impl XTextScale {
1752 #[inline]
1754 pub fn text_zoom_enabled(self) -> bool {
1755 self != Self::None
1756 }
1757}
1758
1759#[derive(
1760 Clone,
1761 Debug,
1762 MallocSizeOf,
1763 PartialEq,
1764 SpecifiedValueInfo,
1765 ToComputedValue,
1766 ToCss,
1767 ToResolvedValue,
1768 ToShmem,
1769 ToTyped,
1770)]
1771#[cfg_attr(feature = "servo", derive(Deserialize, Eq, Hash, Serialize))]
1772pub struct XLang(#[css(skip)] pub Atom);
1774
1775impl XLang {
1776 #[inline]
1777 pub fn get_initial_value() -> XLang {
1779 XLang(atom!(""))
1780 }
1781}
1782
1783impl Parse for XLang {
1784 fn parse<'i, 't>(
1785 _: &ParserContext,
1786 input: &mut Parser<'i, 't>,
1787 ) -> Result<XLang, ParseError<'i>> {
1788 debug_assert!(
1789 false,
1790 "Should be set directly by presentation attributes only."
1791 );
1792 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1793 }
1794}
1795
1796#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1797#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1798pub struct MozScriptMinSize(pub NoCalcLength);
1801
1802impl MozScriptMinSize {
1803 #[inline]
1804 pub fn get_initial_value() -> Length {
1806 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1807 }
1808}
1809
1810impl Parse for MozScriptMinSize {
1811 fn parse<'i, 't>(
1812 _: &ParserContext,
1813 input: &mut Parser<'i, 't>,
1814 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1815 debug_assert!(
1816 false,
1817 "Should be set directly by presentation attributes only."
1818 );
1819 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1820 }
1821}
1822
1823#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1826#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1827pub enum MathDepth {
1828 AutoAdd,
1830
1831 #[css(function)]
1833 Add(Integer),
1834
1835 Absolute(Integer),
1837}
1838
1839impl Parse for MathDepth {
1840 fn parse<'i, 't>(
1841 context: &ParserContext,
1842 input: &mut Parser<'i, 't>,
1843 ) -> Result<MathDepth, ParseError<'i>> {
1844 if input
1845 .try_parse(|i| i.expect_ident_matching("auto-add"))
1846 .is_ok()
1847 {
1848 return Ok(MathDepth::AutoAdd);
1849 }
1850 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1851 return Ok(MathDepth::Absolute(math_depth_value));
1852 }
1853 input.expect_function_matching("add")?;
1854 let math_depth_delta_value =
1855 input.parse_nested_block(|input| Integer::parse(context, input))?;
1856 Ok(MathDepth::Add(math_depth_delta_value))
1857 }
1858}
1859
1860#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1861#[derive(
1862 Clone,
1863 Copy,
1864 Debug,
1865 PartialEq,
1866 SpecifiedValueInfo,
1867 ToComputedValue,
1868 ToCss,
1869 ToResolvedValue,
1870 ToShmem,
1871)]
1872pub struct MozScriptSizeMultiplier(pub f32);
1877
1878impl MozScriptSizeMultiplier {
1879 #[inline]
1880 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1882 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1883 }
1884}
1885
1886impl Parse for MozScriptSizeMultiplier {
1887 fn parse<'i, 't>(
1888 _: &ParserContext,
1889 input: &mut Parser<'i, 't>,
1890 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1891 debug_assert!(
1892 false,
1893 "Should be set directly by presentation attributes only."
1894 );
1895 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1896 }
1897}
1898
1899impl From<f32> for MozScriptSizeMultiplier {
1900 fn from(v: f32) -> Self {
1901 MozScriptSizeMultiplier(v)
1902 }
1903}
1904
1905impl From<MozScriptSizeMultiplier> for f32 {
1906 fn from(v: MozScriptSizeMultiplier) -> f32 {
1907 v.0
1908 }
1909}
1910
1911pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1913
1914impl ToComputedValue for LineHeight {
1915 type ComputedValue = computed::LineHeight;
1916
1917 #[inline]
1918 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1919 match *self {
1920 GenericLineHeight::Normal => GenericLineHeight::Normal,
1921 #[cfg(feature = "gecko")]
1922 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1923 GenericLineHeight::Number(number) => {
1924 GenericLineHeight::Number(number.to_computed_value(context))
1925 },
1926 GenericLineHeight::Length(ref non_negative_lp) => {
1927 let result = match non_negative_lp.0 {
1928 LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1929 context.maybe_zoom_text(abs.to_computed_value(context))
1930 },
1931 LengthPercentage::Length(ref length) => {
1932 length.to_computed_value_with_base_size(
1937 context,
1938 FontBaseSize::CurrentStyle,
1939 LineHeightBase::InheritedStyle,
1940 )
1941 },
1942 LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1943 .to_computed_value(
1944 context,
1945 FontBaseSize::CurrentStyle,
1946 LineHeightBase::InheritedStyle,
1947 ),
1948 LengthPercentage::Calc(ref calc) => {
1949 let computed_calc = calc.to_computed_value_zoomed(
1950 context,
1951 FontBaseSize::CurrentStyle,
1952 LineHeightBase::InheritedStyle,
1953 );
1954 let base = context.style().get_font().clone_font_size().computed_size();
1955 computed_calc.resolve(base)
1956 },
1957 };
1958 GenericLineHeight::Length(result.into())
1959 },
1960 }
1961 }
1962
1963 #[inline]
1964 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1965 match *computed {
1966 GenericLineHeight::Normal => GenericLineHeight::Normal,
1967 #[cfg(feature = "gecko")]
1968 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1969 GenericLineHeight::Number(ref number) => {
1970 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1971 },
1972 GenericLineHeight::Length(ref length) => {
1973 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1974 },
1975 }
1976 }
1977}
1978
1979#[repr(C)]
1981pub struct QueryFontMetricsFlags(u8);
1982
1983bitflags! {
1984 impl QueryFontMetricsFlags: u8 {
1985 const USE_USER_FONT_SET = 1 << 0;
1987 const NEEDS_CH = 1 << 1;
1989 const NEEDS_IC = 1 << 2;
1991 const NEEDS_MATH_SCALES = 1 << 3;
1993 }
1994}