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(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
205pub enum AbsoluteFontWeight {
206 Weight(Number),
210 Normal,
212 Bold,
214}
215
216impl AbsoluteFontWeight {
217 pub fn compute(&self) -> computed::FontWeight {
219 match *self {
220 AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),
221 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
222 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
223 }
224 }
225}
226
227impl Parse for AbsoluteFontWeight {
228 fn parse<'i, 't>(
229 context: &ParserContext,
230 input: &mut Parser<'i, 't>,
231 ) -> Result<Self, ParseError<'i>> {
232 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
233 if !number.was_calc()
237 && (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
238 {
239 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
240 }
241 return Ok(AbsoluteFontWeight::Weight(number));
242 }
243
244 Ok(try_match_ident_ignore_ascii_case! { input,
245 "normal" => AbsoluteFontWeight::Normal,
246 "bold" => AbsoluteFontWeight::Bold,
247 })
248 }
249}
250
251pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
254
255impl ToCss for SpecifiedFontStyle {
256 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
257 where
258 W: Write,
259 {
260 match *self {
261 generics::FontStyle::Italic => dest.write_str("italic"),
262 generics::FontStyle::Oblique(ref angle) => {
263 if *angle == Angle::zero() {
266 dest.write_str("normal")?;
267 } else {
268 dest.write_str("oblique")?;
269 if *angle != Self::default_angle() {
270 dest.write_char(' ')?;
271 angle.to_css(dest)?;
272 }
273 }
274 Ok(())
275 },
276 }
277 }
278}
279
280impl Parse for SpecifiedFontStyle {
281 fn parse<'i, 't>(
282 context: &ParserContext,
283 input: &mut Parser<'i, 't>,
284 ) -> Result<Self, ParseError<'i>> {
285 Ok(try_match_ident_ignore_ascii_case! { input,
286 "normal" => generics::FontStyle::normal(),
287 "italic" => generics::FontStyle::Italic,
288 "oblique" => {
289 let angle = input.try_parse(|input| Self::parse_angle(context, input))
290 .unwrap_or_else(|_| Self::default_angle());
291
292 generics::FontStyle::Oblique(angle)
293 },
294 })
295 }
296}
297
298impl ToComputedValue for SpecifiedFontStyle {
299 type ComputedValue = computed::FontStyle;
300
301 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
302 match *self {
303 Self::Italic => computed::FontStyle::ITALIC,
304 Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),
305 }
306 }
307
308 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
309 if *computed == computed::FontStyle::ITALIC {
310 return Self::Italic;
311 }
312 let degrees = computed.oblique_degrees();
313 generics::FontStyle::Oblique(Angle::from_degrees(degrees, false))
314 }
315}
316
317pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
324
325pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
327
328impl SpecifiedFontStyle {
329 pub fn compute_angle_degrees(angle: &Angle) -> f32 {
331 angle
332 .degrees()
333 .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
334 .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
335 }
336
337 pub fn parse_angle<'i, 't>(
339 context: &ParserContext,
340 input: &mut Parser<'i, 't>,
341 ) -> Result<Angle, ParseError<'i>> {
342 let angle = Angle::parse(context, input)?;
343 if angle.was_calc() {
344 return Ok(angle);
345 }
346
347 let degrees = angle.degrees();
348 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES
349 || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
350 {
351 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
352 }
353 return Ok(angle);
354 }
355
356 pub fn default_angle() -> Angle {
358 Angle::from_degrees(
359 computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,
360 false,
361 )
362 }
363}
364
365#[derive(
367 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
368)]
369#[allow(missing_docs)]
370pub enum FontStyle {
371 Specified(SpecifiedFontStyle),
372 #[css(skip)]
373 System(SystemFont),
374}
375
376impl FontStyle {
377 #[inline]
379 pub fn normal() -> Self {
380 FontStyle::Specified(generics::FontStyle::normal())
381 }
382
383 system_font_methods!(FontStyle, font_style);
384}
385
386impl ToComputedValue for FontStyle {
387 type ComputedValue = computed::FontStyle;
388
389 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
390 match *self {
391 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
392 FontStyle::System(..) => self.compute_system(context),
393 }
394 }
395
396 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
397 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
398 }
399}
400
401#[allow(missing_docs)]
405#[derive(
406 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
407)]
408#[typed_value(derive_fields)]
409pub enum FontStretch {
410 Stretch(NonNegativePercentage),
411 Keyword(FontStretchKeyword),
412 #[css(skip)]
413 System(SystemFont),
414}
415
416#[derive(
418 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
419)]
420#[allow(missing_docs)]
421pub enum FontStretchKeyword {
422 Normal,
423 Condensed,
424 UltraCondensed,
425 ExtraCondensed,
426 SemiCondensed,
427 SemiExpanded,
428 Expanded,
429 ExtraExpanded,
430 UltraExpanded,
431}
432
433impl FontStretchKeyword {
434 pub fn compute(&self) -> computed::FontStretch {
436 computed::FontStretch::from_keyword(*self)
437 }
438
439 pub fn from_percentage(p: f32) -> Option<Self> {
442 computed::FontStretch::from_percentage(p).as_keyword()
443 }
444}
445
446impl FontStretch {
447 pub fn normal() -> Self {
449 FontStretch::Keyword(FontStretchKeyword::Normal)
450 }
451
452 system_font_methods!(FontStretch, font_stretch);
453}
454
455impl ToComputedValue for FontStretch {
456 type ComputedValue = computed::FontStretch;
457
458 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
459 match *self {
460 FontStretch::Stretch(ref percentage) => {
461 let percentage = percentage.to_computed_value(context).0;
462 computed::FontStretch::from_percentage(percentage.0)
463 },
464 FontStretch::Keyword(ref kw) => kw.compute(),
465 FontStretch::System(_) => self.compute_system(context),
466 }
467 }
468
469 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
470 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
471 computed.to_percentage(),
472 )))
473 }
474}
475
476#[derive(
478 Animate,
479 Clone,
480 ComputeSquaredDistance,
481 Copy,
482 Debug,
483 MallocSizeOf,
484 Parse,
485 PartialEq,
486 SpecifiedValueInfo,
487 ToAnimatedValue,
488 ToAnimatedZero,
489 ToComputedValue,
490 ToCss,
491 ToResolvedValue,
492 ToShmem,
493 Serialize,
494 Deserialize,
495 ToTyped,
496)]
497#[allow(missing_docs)]
498#[repr(u8)]
499pub enum FontSizeKeyword {
500 #[css(keyword = "xx-small")]
501 XXSmall,
502 XSmall,
503 Small,
504 Medium,
505 Large,
506 XLarge,
507 #[css(keyword = "xx-large")]
508 XXLarge,
509 #[css(keyword = "xxx-large")]
510 XXXLarge,
511 #[cfg(feature = "gecko")]
514 Math,
515 #[css(skip)]
516 None,
517}
518
519impl FontSizeKeyword {
520 #[inline]
522 pub fn html_size(self) -> u8 {
523 self as u8
524 }
525
526 #[cfg(feature = "gecko")]
528 pub fn is_math(self) -> bool {
529 matches!(self, Self::Math)
530 }
531
532 #[cfg(feature = "servo")]
534 pub fn is_math(self) -> bool {
535 false
536 }
537}
538
539impl Default for FontSizeKeyword {
540 fn default() -> Self {
541 FontSizeKeyword::Medium
542 }
543}
544
545#[derive(
546 Animate,
547 Clone,
548 ComputeSquaredDistance,
549 Copy,
550 Debug,
551 MallocSizeOf,
552 PartialEq,
553 ToAnimatedValue,
554 ToAnimatedZero,
555 ToComputedValue,
556 ToCss,
557 ToResolvedValue,
558 ToShmem,
559 ToTyped,
560)]
561#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
562#[typed_value(derive_fields)]
563pub struct KeywordInfo {
565 pub kw: FontSizeKeyword,
567 #[css(skip)]
569 pub factor: f32,
570 #[css(skip)]
573 pub offset: CSSPixelLength,
574}
575
576impl KeywordInfo {
577 pub fn medium() -> Self {
579 Self::new(FontSizeKeyword::Medium)
580 }
581
582 pub fn none() -> Self {
584 Self::new(FontSizeKeyword::None)
585 }
586
587 fn new(kw: FontSizeKeyword) -> Self {
588 KeywordInfo {
589 kw,
590 factor: 1.,
591 offset: CSSPixelLength::new(0.),
592 }
593 }
594
595 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
598 debug_assert_ne!(self.kw, FontSizeKeyword::None);
599 #[cfg(feature = "gecko")]
600 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
601 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
602 let zoom_factor = context.style().effective_zoom.value();
603 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
604 + context.maybe_zoom_text(self.offset)
605 }
606
607 fn compose(self, factor: f32) -> Self {
610 if self.kw == FontSizeKeyword::None {
611 return self;
612 }
613 KeywordInfo {
614 kw: self.kw,
615 factor: self.factor * factor,
616 offset: self.offset * factor,
617 }
618 }
619}
620
621impl SpecifiedValueInfo for KeywordInfo {
622 fn collect_completion_keywords(f: KeywordsCollectFn) {
623 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
624 }
625}
626
627#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
628#[typed_value(derive_fields)]
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))]
656pub enum FontFamily {
657 #[css(comma)]
659 Values(#[css(iterable)] FontFamilyList),
660 #[css(skip)]
662 System(SystemFont),
663}
664
665impl FontFamily {
666 system_font_methods!(FontFamily, font_family);
667}
668
669impl ToComputedValue for FontFamily {
670 type ComputedValue = computed::FontFamily;
671
672 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
673 match *self {
674 FontFamily::Values(ref list) => computed::FontFamily {
675 families: list.clone(),
676 is_system_font: false,
677 is_initial: false,
678 },
679 FontFamily::System(_) => self.compute_system(context),
680 }
681 }
682
683 fn from_computed_value(other: &computed::FontFamily) -> Self {
684 FontFamily::Values(other.families.clone())
685 }
686}
687
688#[cfg(feature = "gecko")]
689impl MallocSizeOf for FontFamily {
690 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
691 match *self {
692 FontFamily::Values(ref v) => {
693 v.list.unconditional_size_of(ops)
696 },
697 FontFamily::System(_) => 0,
698 }
699 }
700}
701
702impl Parse for FontFamily {
703 fn parse<'i, 't>(
707 context: &ParserContext,
708 input: &mut Parser<'i, 't>,
709 ) -> Result<FontFamily, ParseError<'i>> {
710 let values =
711 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
712 Ok(FontFamily::Values(FontFamilyList {
713 list: crate::ArcSlice::from_iter(values.into_iter()),
714 }))
715 }
716}
717
718impl SpecifiedValueInfo for FontFamily {}
719
720impl Parse for FamilyName {
723 fn parse<'i, 't>(
724 context: &ParserContext,
725 input: &mut Parser<'i, 't>,
726 ) -> Result<Self, ParseError<'i>> {
727 match SingleFontFamily::parse(context, input) {
728 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
729 Ok(SingleFontFamily::Generic(_)) => {
730 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
731 },
732 Err(e) => Err(e),
733 }
734 }
735}
736
737#[derive(
740 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
741)]
742pub enum FontSizeAdjustFactor {
743 Number(NonNegativeNumber),
745 FromFont,
747}
748
749pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
754
755impl Parse for FontSizeAdjust {
756 fn parse<'i, 't>(
757 context: &ParserContext,
758 input: &mut Parser<'i, 't>,
759 ) -> Result<Self, ParseError<'i>> {
760 let location = input.current_source_location();
761 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
763 return Ok(Self::ExHeight(factor));
764 }
765
766 let ident = input.expect_ident()?;
767 let basis = match_ignore_ascii_case! { &ident,
768 "none" => return Ok(Self::None),
769 "ex-height" => Self::ExHeight,
771 "cap-height" => Self::CapHeight,
772 "ch-width" => Self::ChWidth,
773 "ic-width" => Self::IcWidth,
774 "ic-height" => Self::IcHeight,
775 _ => return Err(location.new_custom_error(
777 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
778 )),
779 };
780
781 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
782 }
783}
784
785const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
788
789pub const FONT_MEDIUM_PX: f32 = 16.0;
791pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
793pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
796pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
799pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
802pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
805
806impl FontSizeKeyword {
807 #[inline]
808 fn to_length(&self, cx: &Context) -> NonNegativeLength {
809 let font = cx.style().get_font();
810
811 #[cfg(feature = "servo")]
812 let family = &font.font_family.families;
813 #[cfg(feature = "gecko")]
814 let family = &font.mFont.family.families;
815
816 let generic = family
817 .single_generic()
818 .unwrap_or(computed::GenericFontFamily::None);
819
820 #[cfg(feature = "gecko")]
821 let base_size = unsafe {
822 Atom::with(font.mLanguage.mRawPtr, |language| {
823 cx.device().base_size_for_generic(language, generic)
824 })
825 };
826 #[cfg(feature = "servo")]
827 let base_size = cx.device().base_size_for_generic(generic);
828
829 self.to_length_without_context(cx.quirks_mode, base_size)
830 }
831
832 #[inline]
834 pub fn to_length_without_context(
835 &self,
836 quirks_mode: QuirksMode,
837 base_size: Length,
838 ) -> NonNegativeLength {
839 #[cfg(feature = "gecko")]
840 debug_assert_ne!(*self, FontSizeKeyword::Math);
841 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
854 [9, 9, 9, 9, 11, 14, 18, 27],
855 [9, 9, 9, 10, 12, 15, 20, 30],
856 [9, 9, 10, 11, 13, 17, 22, 33],
857 [9, 9, 10, 12, 14, 18, 24, 36],
858 [9, 10, 12, 13, 16, 20, 26, 39],
859 [9, 10, 12, 14, 17, 21, 28, 42],
860 [9, 10, 13, 15, 18, 23, 30, 45],
861 [9, 10, 13, 16, 18, 24, 32, 48],
862 ];
863
864 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
873 [9, 9, 9, 9, 11, 14, 18, 28],
874 [9, 9, 9, 10, 12, 15, 20, 31],
875 [9, 9, 9, 11, 13, 17, 22, 34],
876 [9, 9, 10, 12, 14, 18, 24, 37],
877 [9, 9, 10, 13, 16, 20, 26, 40],
878 [9, 9, 11, 14, 17, 21, 28, 42],
879 [9, 10, 12, 15, 17, 23, 30, 45],
880 [9, 10, 13, 16, 18, 24, 32, 48],
881 ];
882
883 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
884 let base_size_px = base_size.px().round() as i32;
885 let html_size = self.html_size() as usize;
886 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
887 let mapping = if quirks_mode == QuirksMode::Quirks {
888 QUIRKS_FONT_SIZE_MAPPING
889 } else {
890 FONT_SIZE_MAPPING
891 };
892 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
893 } else {
894 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
895 })
896 }
897}
898
899impl FontSize {
900 pub fn from_html_size(size: u8) -> Self {
902 FontSize::Keyword(KeywordInfo::new(match size {
903 0 | 1 => FontSizeKeyword::XSmall,
905 2 => FontSizeKeyword::Small,
906 3 => FontSizeKeyword::Medium,
907 4 => FontSizeKeyword::Large,
908 5 => FontSizeKeyword::XLarge,
909 6 => FontSizeKeyword::XXLarge,
910 _ => FontSizeKeyword::XXXLarge,
912 }))
913 }
914
915 pub fn to_computed_value_against(
917 &self,
918 context: &Context,
919 base_size: FontBaseSize,
920 line_height_base: LineHeightBase,
921 ) -> computed::FontSize {
922 let compose_keyword = |factor| {
923 context
924 .style()
925 .get_parent_font()
926 .clone_font_size()
927 .keyword_info
928 .compose(factor)
929 };
930 let mut info = KeywordInfo::none();
931 let size = match *self {
932 FontSize::Length(LengthPercentage::Length(ref l)) => {
933 if let NoCalcLength::FontRelative(ref value) = *l {
934 if let FontRelativeLength::Em(em) = *value {
935 info = compose_keyword(em);
938 }
939 }
940 let result =
941 l.to_computed_value_with_base_size(context, base_size, line_height_base);
942 if l.should_zoom_text() {
943 context.maybe_zoom_text(result)
944 } else {
945 result
946 }
947 },
948 FontSize::Length(LengthPercentage::Percentage(pc)) => {
949 info = compose_keyword(pc.0);
952 (base_size.resolve(context).computed_size() * pc.0).normalized()
953 },
954 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
955 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
956 calc.resolve(base_size.resolve(context).computed_size())
957 },
958 FontSize::Keyword(i) => {
959 if i.kw.is_math() {
960 info = compose_keyword(1.);
962 info.kw = i.kw;
965 FontRelativeLength::Em(1.).to_computed_value(
966 context,
967 base_size,
968 line_height_base,
969 )
970 } else {
971 info = i;
973 i.to_computed_value(context).clamp_to_non_negative()
974 }
975 },
976 FontSize::Smaller => {
977 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
978 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
979 context,
980 base_size,
981 line_height_base,
982 )
983 },
984 FontSize::Larger => {
985 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
986 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
987 context,
988 base_size,
989 line_height_base,
990 )
991 },
992
993 FontSize::System(_) => {
994 #[cfg(feature = "servo")]
995 {
996 unreachable!()
997 }
998 #[cfg(feature = "gecko")]
999 {
1000 context
1001 .cached_system_font
1002 .as_ref()
1003 .unwrap()
1004 .font_size
1005 .computed_size()
1006 }
1007 },
1008 };
1009 computed::FontSize {
1010 computed_size: NonNegative(size),
1011 used_size: NonNegative(size),
1012 keyword_info: info,
1013 }
1014 }
1015}
1016
1017impl ToComputedValue for FontSize {
1018 type ComputedValue = computed::FontSize;
1019
1020 #[inline]
1021 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1022 self.to_computed_value_against(
1023 context,
1024 FontBaseSize::InheritedStyle,
1025 LineHeightBase::InheritedStyle,
1026 )
1027 }
1028
1029 #[inline]
1030 fn from_computed_value(computed: &computed::FontSize) -> Self {
1031 FontSize::Length(LengthPercentage::Length(
1032 ToComputedValue::from_computed_value(&computed.computed_size()),
1033 ))
1034 }
1035}
1036
1037impl FontSize {
1038 system_font_methods!(FontSize);
1039
1040 #[inline]
1042 pub fn medium() -> Self {
1043 FontSize::Keyword(KeywordInfo::medium())
1044 }
1045
1046 pub fn parse_quirky<'i, 't>(
1048 context: &ParserContext,
1049 input: &mut Parser<'i, 't>,
1050 allow_quirks: AllowQuirks,
1051 ) -> Result<FontSize, ParseError<'i>> {
1052 if let Ok(lp) = input
1053 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1054 {
1055 return Ok(FontSize::Length(lp));
1056 }
1057
1058 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1059 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1060 }
1061
1062 try_match_ident_ignore_ascii_case! { input,
1063 "smaller" => Ok(FontSize::Smaller),
1064 "larger" => Ok(FontSize::Larger),
1065 }
1066 }
1067}
1068
1069impl Parse for FontSize {
1070 fn parse<'i, 't>(
1072 context: &ParserContext,
1073 input: &mut Parser<'i, 't>,
1074 ) -> Result<FontSize, ParseError<'i>> {
1075 FontSize::parse_quirky(context, input, AllowQuirks::No)
1076 }
1077}
1078
1079bitflags! {
1080 #[derive(Clone, Copy)]
1081 struct VariantAlternatesParsingFlags: u8 {
1083 const NORMAL = 0;
1085 const HISTORICAL_FORMS = 0x01;
1087 const STYLISTIC = 0x02;
1089 const STYLESET = 0x04;
1091 const CHARACTER_VARIANT = 0x08;
1093 const SWASH = 0x10;
1095 const ORNAMENTS = 0x20;
1097 const ANNOTATION = 0x40;
1099 }
1100}
1101
1102#[derive(
1103 Clone,
1104 Debug,
1105 MallocSizeOf,
1106 PartialEq,
1107 SpecifiedValueInfo,
1108 ToCss,
1109 ToComputedValue,
1110 ToResolvedValue,
1111 ToShmem,
1112)]
1113#[repr(C, u8)]
1114pub enum VariantAlternates {
1116 #[css(function)]
1118 Stylistic(CustomIdent),
1119 #[css(comma, function)]
1121 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1122 #[css(comma, function)]
1124 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1125 #[css(function)]
1127 Swash(CustomIdent),
1128 #[css(function)]
1130 Ornaments(CustomIdent),
1131 #[css(function)]
1133 Annotation(CustomIdent),
1134 HistoricalForms,
1136}
1137
1138#[derive(
1139 Clone,
1140 Debug,
1141 Default,
1142 MallocSizeOf,
1143 PartialEq,
1144 SpecifiedValueInfo,
1145 ToComputedValue,
1146 ToCss,
1147 ToResolvedValue,
1148 ToShmem,
1149 ToTyped,
1150)]
1151#[repr(transparent)]
1152pub struct FontVariantAlternates(
1154 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1155);
1156
1157impl FontVariantAlternates {
1158 pub fn len(&self) -> usize {
1160 self.0.iter().fold(0, |acc, alternate| match *alternate {
1161 VariantAlternates::Swash(_)
1162 | VariantAlternates::Stylistic(_)
1163 | VariantAlternates::Ornaments(_)
1164 | VariantAlternates::Annotation(_) => acc + 1,
1165 VariantAlternates::Styleset(ref slice)
1166 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1167 _ => acc,
1168 })
1169 }
1170}
1171
1172impl FontVariantAlternates {
1173 #[inline]
1174 pub fn get_initial_specified_value() -> Self {
1176 Default::default()
1177 }
1178}
1179
1180impl Parse for FontVariantAlternates {
1181 fn parse<'i, 't>(
1190 _: &ParserContext,
1191 input: &mut Parser<'i, 't>,
1192 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1193 if input
1194 .try_parse(|input| input.expect_ident_matching("normal"))
1195 .is_ok()
1196 {
1197 return Ok(Default::default());
1198 }
1199
1200 let mut stylistic = None;
1201 let mut historical = None;
1202 let mut styleset = None;
1203 let mut character_variant = None;
1204 let mut swash = None;
1205 let mut ornaments = None;
1206 let mut annotation = None;
1207
1208 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1210 macro_rules! check_if_parsed(
1211 ($input:expr, $flag:path) => (
1212 if parsed_alternates.contains($flag) {
1213 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1214 }
1215 parsed_alternates |= $flag;
1216 )
1217 );
1218 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1219 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1220 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1221 historical = Some(VariantAlternates::HistoricalForms);
1222 Ok(())
1223 },
1224 Token::Function(ref name) => {
1225 let name = name.clone();
1226 input.parse_nested_block(|i| {
1227 match_ignore_ascii_case! { &name,
1228 "swash" => {
1229 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1230 let ident = CustomIdent::parse(i, &[])?;
1231 swash = Some(VariantAlternates::Swash(ident));
1232 Ok(())
1233 },
1234 "stylistic" => {
1235 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1236 let ident = CustomIdent::parse(i, &[])?;
1237 stylistic = Some(VariantAlternates::Stylistic(ident));
1238 Ok(())
1239 },
1240 "ornaments" => {
1241 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1242 let ident = CustomIdent::parse(i, &[])?;
1243 ornaments = Some(VariantAlternates::Ornaments(ident));
1244 Ok(())
1245 },
1246 "annotation" => {
1247 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1248 let ident = CustomIdent::parse(i, &[])?;
1249 annotation = Some(VariantAlternates::Annotation(ident));
1250 Ok(())
1251 },
1252 "styleset" => {
1253 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1254 let idents = i.parse_comma_separated(|i| {
1255 CustomIdent::parse(i, &[])
1256 })?;
1257 styleset = Some(VariantAlternates::Styleset(idents.into()));
1258 Ok(())
1259 },
1260 "character-variant" => {
1261 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1262 let idents = i.parse_comma_separated(|i| {
1263 CustomIdent::parse(i, &[])
1264 })?;
1265 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1266 Ok(())
1267 },
1268 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1269 }
1270 })
1271 },
1272 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1273 }) {}
1274
1275 if parsed_alternates.is_empty() {
1276 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1277 }
1278
1279 let mut alternates = Vec::new();
1281 macro_rules! push_if_some(
1282 ($value:expr) => (
1283 if let Some(v) = $value {
1284 alternates.push(v);
1285 }
1286 )
1287 );
1288 push_if_some!(stylistic);
1289 push_if_some!(historical);
1290 push_if_some!(styleset);
1291 push_if_some!(character_variant);
1292 push_if_some!(swash);
1293 push_if_some!(ornaments);
1294 push_if_some!(annotation);
1295
1296 Ok(FontVariantAlternates(alternates.into()))
1297 }
1298}
1299
1300#[derive(
1301 Clone,
1302 Copy,
1303 Debug,
1304 Eq,
1305 MallocSizeOf,
1306 PartialEq,
1307 Parse,
1308 SpecifiedValueInfo,
1309 ToComputedValue,
1310 ToCss,
1311 ToResolvedValue,
1312 ToShmem,
1313 ToTyped,
1314)]
1315#[css(bitflags(
1316 single = "normal",
1317 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1318 validate_mixed = "Self::validate_mixed_flags",
1319))]
1320#[repr(C)]
1321pub struct FontVariantEastAsian(u16);
1323bitflags! {
1324 impl FontVariantEastAsian: u16 {
1325 const NORMAL = 0;
1327 const JIS78 = 1 << 0;
1329 const JIS83 = 1 << 1;
1331 const JIS90 = 1 << 2;
1333 const JIS04 = 1 << 3;
1335 const SIMPLIFIED = 1 << 4;
1337 const TRADITIONAL = 1 << 5;
1339
1340 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1342
1343 const FULL_WIDTH = 1 << 6;
1345 const PROPORTIONAL_WIDTH = 1 << 7;
1347 const RUBY = 1 << 8;
1349 }
1350}
1351
1352impl FontVariantEastAsian {
1353 pub const COUNT: usize = 9;
1355
1356 fn validate_mixed_flags(&self) -> bool {
1357 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1358 return false;
1360 }
1361 let jis = self.intersection(Self::JIS_GROUP);
1362 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1363 return false;
1364 }
1365 true
1366 }
1367}
1368
1369#[derive(
1370 Clone,
1371 Copy,
1372 Debug,
1373 Eq,
1374 MallocSizeOf,
1375 PartialEq,
1376 Parse,
1377 SpecifiedValueInfo,
1378 ToComputedValue,
1379 ToCss,
1380 ToResolvedValue,
1381 ToShmem,
1382 ToTyped,
1383)]
1384#[css(bitflags(
1385 single = "normal,none",
1386 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1387 validate_mixed = "Self::validate_mixed_flags",
1388))]
1389#[repr(C)]
1390pub struct FontVariantLigatures(u16);
1392bitflags! {
1393 impl FontVariantLigatures: u16 {
1394 const NORMAL = 0;
1396 const NONE = 1;
1398 const COMMON_LIGATURES = 1 << 1;
1400 const NO_COMMON_LIGATURES = 1 << 2;
1402 const DISCRETIONARY_LIGATURES = 1 << 3;
1404 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1406 const HISTORICAL_LIGATURES = 1 << 5;
1408 const NO_HISTORICAL_LIGATURES = 1 << 6;
1410 const CONTEXTUAL = 1 << 7;
1412 const NO_CONTEXTUAL = 1 << 8;
1414 }
1415}
1416
1417impl FontVariantLigatures {
1418 pub const COUNT: usize = 9;
1420
1421 fn validate_mixed_flags(&self) -> bool {
1422 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1424 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1425 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1426 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1427 {
1428 return false;
1429 }
1430 true
1431 }
1432}
1433
1434#[derive(
1436 Clone,
1437 Copy,
1438 Debug,
1439 Eq,
1440 MallocSizeOf,
1441 PartialEq,
1442 Parse,
1443 SpecifiedValueInfo,
1444 ToComputedValue,
1445 ToCss,
1446 ToResolvedValue,
1447 ToShmem,
1448 ToTyped,
1449)]
1450#[css(bitflags(
1451 single = "normal",
1452 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1453 validate_mixed = "Self::validate_mixed_flags",
1454))]
1455#[repr(C)]
1456pub struct FontVariantNumeric(u8);
1457bitflags! {
1458 impl FontVariantNumeric : u8 {
1459 const NORMAL = 0;
1461 const LINING_NUMS = 1 << 0;
1463 const OLDSTYLE_NUMS = 1 << 1;
1465 const PROPORTIONAL_NUMS = 1 << 2;
1467 const TABULAR_NUMS = 1 << 3;
1469 const DIAGONAL_FRACTIONS = 1 << 4;
1471 const STACKED_FRACTIONS = 1 << 5;
1473 const SLASHED_ZERO = 1 << 6;
1475 const ORDINAL = 1 << 7;
1477 }
1478}
1479
1480impl FontVariantNumeric {
1481 pub const COUNT: usize = 8;
1483
1484 fn validate_mixed_flags(&self) -> bool {
1494 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1495 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1496 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1497 {
1498 return false;
1499 }
1500 true
1501 }
1502}
1503
1504pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1506
1507pub use crate::values::computed::font::FontLanguageOverride;
1509
1510impl Parse for FontLanguageOverride {
1511 fn parse<'i, 't>(
1513 _: &ParserContext,
1514 input: &mut Parser<'i, 't>,
1515 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1516 if input
1517 .try_parse(|input| input.expect_ident_matching("normal"))
1518 .is_ok()
1519 {
1520 return Ok(FontLanguageOverride::normal());
1521 }
1522
1523 let string = input.expect_string()?;
1524
1525 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1528 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1529 }
1530
1531 let mut bytes = [b' '; 4];
1532 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1533 *byte = *str_byte;
1534 }
1535
1536 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1537 }
1538}
1539
1540#[repr(u8)]
1542#[derive(
1543 Clone,
1544 Copy,
1545 Debug,
1546 Eq,
1547 Hash,
1548 MallocSizeOf,
1549 Parse,
1550 PartialEq,
1551 SpecifiedValueInfo,
1552 ToComputedValue,
1553 ToCss,
1554 ToResolvedValue,
1555 ToShmem,
1556 ToTyped,
1557)]
1558#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1559pub enum FontSynthesis {
1560 Auto,
1562 None,
1564}
1565
1566#[repr(u8)]
1568#[derive(
1569 Clone,
1570 Copy,
1571 Debug,
1572 Eq,
1573 MallocSizeOf,
1574 Parse,
1575 PartialEq,
1576 SpecifiedValueInfo,
1577 ToComputedValue,
1578 ToCss,
1579 ToResolvedValue,
1580 ToShmem,
1581 ToTyped,
1582)]
1583pub enum FontSynthesisStyle {
1584 Auto,
1586 None,
1588 ObliqueOnly,
1590}
1591
1592#[derive(
1593 Clone,
1594 Debug,
1595 Eq,
1596 MallocSizeOf,
1597 PartialEq,
1598 SpecifiedValueInfo,
1599 ToComputedValue,
1600 ToResolvedValue,
1601 ToShmem,
1602 ToTyped,
1603)]
1604#[repr(C)]
1605pub struct FontPalette(Atom);
1608
1609#[allow(missing_docs)]
1610impl FontPalette {
1611 pub fn normal() -> Self {
1612 Self(atom!("normal"))
1613 }
1614 pub fn light() -> Self {
1615 Self(atom!("light"))
1616 }
1617 pub fn dark() -> Self {
1618 Self(atom!("dark"))
1619 }
1620}
1621
1622impl Parse for FontPalette {
1623 fn parse<'i, 't>(
1625 _context: &ParserContext,
1626 input: &mut Parser<'i, 't>,
1627 ) -> Result<FontPalette, ParseError<'i>> {
1628 let location = input.current_source_location();
1629 let ident = input.expect_ident()?;
1630 match_ignore_ascii_case! { &ident,
1631 "normal" => Ok(Self::normal()),
1632 "light" => Ok(Self::light()),
1633 "dark" => Ok(Self::dark()),
1634 _ => if ident.starts_with("--") {
1635 Ok(Self(Atom::from(ident.as_ref())))
1636 } else {
1637 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1638 },
1639 }
1640 }
1641}
1642
1643impl ToCss for FontPalette {
1644 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1645 where
1646 W: Write,
1647 {
1648 serialize_atom_identifier(&self.0, dest)
1649 }
1650}
1651
1652pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1655
1656fn parse_one_feature_value<'i, 't>(
1657 context: &ParserContext,
1658 input: &mut Parser<'i, 't>,
1659) -> Result<Integer, ParseError<'i>> {
1660 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1661 return Ok(integer);
1662 }
1663
1664 try_match_ident_ignore_ascii_case! { input,
1665 "on" => Ok(Integer::new(1)),
1666 "off" => Ok(Integer::new(0)),
1667 }
1668}
1669
1670impl Parse for FeatureTagValue<Integer> {
1671 fn parse<'i, 't>(
1673 context: &ParserContext,
1674 input: &mut Parser<'i, 't>,
1675 ) -> Result<Self, ParseError<'i>> {
1676 let tag = FontTag::parse(context, input)?;
1677 let value = input
1678 .try_parse(|i| parse_one_feature_value(context, i))
1679 .unwrap_or_else(|_| Integer::new(1));
1680
1681 Ok(Self { tag, value })
1682 }
1683}
1684
1685impl Parse for VariationValue<Number> {
1686 fn parse<'i, 't>(
1689 context: &ParserContext,
1690 input: &mut Parser<'i, 't>,
1691 ) -> Result<Self, ParseError<'i>> {
1692 let tag = FontTag::parse(context, input)?;
1693 let value = Number::parse(context, input)?;
1694 Ok(Self { tag, value })
1695 }
1696}
1697
1698#[derive(
1702 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1703)]
1704pub enum MetricsOverride {
1705 Override(NonNegativePercentage),
1707 Normal,
1709}
1710
1711impl MetricsOverride {
1712 #[inline]
1713 pub fn normal() -> MetricsOverride {
1715 MetricsOverride::Normal
1716 }
1717
1718 #[inline]
1723 pub fn compute(&self) -> ComputedPercentage {
1724 match *self {
1725 MetricsOverride::Normal => ComputedPercentage(-1.0),
1726 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1727 }
1728 }
1729}
1730
1731#[derive(
1732 Clone,
1733 Copy,
1734 Debug,
1735 MallocSizeOf,
1736 Parse,
1737 PartialEq,
1738 SpecifiedValueInfo,
1739 ToComputedValue,
1740 ToCss,
1741 ToResolvedValue,
1742 ToShmem,
1743 ToTyped,
1744)]
1745#[repr(u8)]
1746pub enum XTextScale {
1748 All,
1750 ZoomOnly,
1752 None,
1754}
1755
1756impl XTextScale {
1757 #[inline]
1759 pub fn text_zoom_enabled(self) -> bool {
1760 self != Self::None
1761 }
1762}
1763
1764#[derive(
1765 Clone,
1766 Debug,
1767 MallocSizeOf,
1768 PartialEq,
1769 SpecifiedValueInfo,
1770 ToComputedValue,
1771 ToCss,
1772 ToResolvedValue,
1773 ToShmem,
1774 ToTyped,
1775)]
1776#[cfg_attr(feature = "servo", derive(Deserialize, Eq, Hash, Serialize))]
1777pub struct XLang(#[css(skip)] pub Atom);
1779
1780impl XLang {
1781 #[inline]
1782 pub fn get_initial_value() -> XLang {
1784 XLang(atom!(""))
1785 }
1786}
1787
1788impl Parse for XLang {
1789 fn parse<'i, 't>(
1790 _: &ParserContext,
1791 input: &mut Parser<'i, 't>,
1792 ) -> Result<XLang, ParseError<'i>> {
1793 debug_assert!(
1794 false,
1795 "Should be set directly by presentation attributes only."
1796 );
1797 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1798 }
1799}
1800
1801#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1802#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1803pub struct MozScriptMinSize(pub NoCalcLength);
1806
1807impl MozScriptMinSize {
1808 #[inline]
1809 pub fn get_initial_value() -> Length {
1811 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1812 }
1813}
1814
1815impl Parse for MozScriptMinSize {
1816 fn parse<'i, 't>(
1817 _: &ParserContext,
1818 input: &mut Parser<'i, 't>,
1819 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1820 debug_assert!(
1821 false,
1822 "Should be set directly by presentation attributes only."
1823 );
1824 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1825 }
1826}
1827
1828#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1831#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1832pub enum MathDepth {
1833 AutoAdd,
1835
1836 #[css(function)]
1838 Add(Integer),
1839
1840 Absolute(Integer),
1842}
1843
1844impl Parse for MathDepth {
1845 fn parse<'i, 't>(
1846 context: &ParserContext,
1847 input: &mut Parser<'i, 't>,
1848 ) -> Result<MathDepth, ParseError<'i>> {
1849 if input
1850 .try_parse(|i| i.expect_ident_matching("auto-add"))
1851 .is_ok()
1852 {
1853 return Ok(MathDepth::AutoAdd);
1854 }
1855 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1856 return Ok(MathDepth::Absolute(math_depth_value));
1857 }
1858 input.expect_function_matching("add")?;
1859 let math_depth_delta_value =
1860 input.parse_nested_block(|input| Integer::parse(context, input))?;
1861 Ok(MathDepth::Add(math_depth_delta_value))
1862 }
1863}
1864
1865#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1866#[derive(
1867 Clone,
1868 Copy,
1869 Debug,
1870 PartialEq,
1871 SpecifiedValueInfo,
1872 ToComputedValue,
1873 ToCss,
1874 ToResolvedValue,
1875 ToShmem,
1876)]
1877pub struct MozScriptSizeMultiplier(pub f32);
1882
1883impl MozScriptSizeMultiplier {
1884 #[inline]
1885 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1887 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1888 }
1889}
1890
1891impl Parse for MozScriptSizeMultiplier {
1892 fn parse<'i, 't>(
1893 _: &ParserContext,
1894 input: &mut Parser<'i, 't>,
1895 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1896 debug_assert!(
1897 false,
1898 "Should be set directly by presentation attributes only."
1899 );
1900 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1901 }
1902}
1903
1904impl From<f32> for MozScriptSizeMultiplier {
1905 fn from(v: f32) -> Self {
1906 MozScriptSizeMultiplier(v)
1907 }
1908}
1909
1910impl From<MozScriptSizeMultiplier> for f32 {
1911 fn from(v: MozScriptSizeMultiplier) -> f32 {
1912 v.0
1913 }
1914}
1915
1916pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1918
1919impl ToComputedValue for LineHeight {
1920 type ComputedValue = computed::LineHeight;
1921
1922 #[inline]
1923 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1924 match *self {
1925 GenericLineHeight::Normal => GenericLineHeight::Normal,
1926 #[cfg(feature = "gecko")]
1927 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1928 GenericLineHeight::Number(number) => {
1929 GenericLineHeight::Number(number.to_computed_value(context))
1930 },
1931 GenericLineHeight::Length(ref non_negative_lp) => {
1932 let result = match non_negative_lp.0 {
1933 LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1934 context.maybe_zoom_text(abs.to_computed_value(context))
1935 },
1936 LengthPercentage::Length(ref length) => {
1937 length.to_computed_value_with_base_size(
1942 context,
1943 FontBaseSize::CurrentStyle,
1944 LineHeightBase::InheritedStyle,
1945 )
1946 },
1947 LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1948 .to_computed_value(
1949 context,
1950 FontBaseSize::CurrentStyle,
1951 LineHeightBase::InheritedStyle,
1952 ),
1953 LengthPercentage::Calc(ref calc) => {
1954 let computed_calc = calc.to_computed_value_zoomed(
1955 context,
1956 FontBaseSize::CurrentStyle,
1957 LineHeightBase::InheritedStyle,
1958 );
1959 let base = context.style().get_font().clone_font_size().computed_size();
1960 computed_calc.resolve(base)
1961 },
1962 };
1963 GenericLineHeight::Length(result.into())
1964 },
1965 }
1966 }
1967
1968 #[inline]
1969 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1970 match *computed {
1971 GenericLineHeight::Normal => GenericLineHeight::Normal,
1972 #[cfg(feature = "gecko")]
1973 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1974 GenericLineHeight::Number(ref number) => {
1975 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1976 },
1977 GenericLineHeight::Length(ref length) => {
1978 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1979 },
1980 }
1981 }
1982}
1983
1984#[repr(C)]
1986pub struct QueryFontMetricsFlags(u8);
1987
1988bitflags! {
1989 impl QueryFontMetricsFlags: u8 {
1990 const USE_USER_FONT_SET = 1 << 0;
1992 const NEEDS_CH = 1 << 1;
1994 const NEEDS_IC = 1 << 2;
1996 const NEEDS_MATH_SCALES = 1 << 3;
1998 }
1999}