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)]
408pub enum FontStretch {
409 Stretch(NonNegativePercentage),
410 Keyword(FontStretchKeyword),
411 #[css(skip)]
412 System(SystemFont),
413}
414
415#[derive(
417 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
418)]
419#[allow(missing_docs)]
420pub enum FontStretchKeyword {
421 Normal,
422 Condensed,
423 UltraCondensed,
424 ExtraCondensed,
425 SemiCondensed,
426 SemiExpanded,
427 Expanded,
428 ExtraExpanded,
429 UltraExpanded,
430}
431
432impl FontStretchKeyword {
433 pub fn compute(&self) -> computed::FontStretch {
435 computed::FontStretch::from_keyword(*self)
436 }
437
438 pub fn from_percentage(p: f32) -> Option<Self> {
441 computed::FontStretch::from_percentage(p).as_keyword()
442 }
443}
444
445impl FontStretch {
446 pub fn normal() -> Self {
448 FontStretch::Keyword(FontStretchKeyword::Normal)
449 }
450
451 system_font_methods!(FontStretch, font_stretch);
452}
453
454impl ToComputedValue for FontStretch {
455 type ComputedValue = computed::FontStretch;
456
457 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
458 match *self {
459 FontStretch::Stretch(ref percentage) => {
460 let percentage = percentage.to_computed_value(context).0;
461 computed::FontStretch::from_percentage(percentage.0)
462 },
463 FontStretch::Keyword(ref kw) => kw.compute(),
464 FontStretch::System(_) => self.compute_system(context),
465 }
466 }
467
468 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
469 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
470 computed.to_percentage(),
471 )))
472 }
473}
474
475#[derive(
477 Animate,
478 Clone,
479 ComputeSquaredDistance,
480 Copy,
481 Debug,
482 MallocSizeOf,
483 Parse,
484 PartialEq,
485 SpecifiedValueInfo,
486 ToAnimatedValue,
487 ToAnimatedZero,
488 ToComputedValue,
489 ToCss,
490 ToResolvedValue,
491 ToShmem,
492 Serialize,
493 Deserialize,
494)]
495#[allow(missing_docs)]
496#[repr(u8)]
497pub enum FontSizeKeyword {
498 #[css(keyword = "xx-small")]
499 XXSmall,
500 XSmall,
501 Small,
502 Medium,
503 Large,
504 XLarge,
505 #[css(keyword = "xx-large")]
506 XXLarge,
507 #[css(keyword = "xxx-large")]
508 XXXLarge,
509 #[cfg(feature = "gecko")]
512 Math,
513 #[css(skip)]
514 None,
515}
516
517impl FontSizeKeyword {
518 #[inline]
520 pub fn html_size(self) -> u8 {
521 self as u8
522 }
523
524 #[cfg(feature = "gecko")]
526 pub fn is_math(self) -> bool {
527 matches!(self, Self::Math)
528 }
529
530 #[cfg(feature = "servo")]
532 pub fn is_math(self) -> bool {
533 false
534 }
535}
536
537impl Default for FontSizeKeyword {
538 fn default() -> Self {
539 FontSizeKeyword::Medium
540 }
541}
542
543#[derive(
544 Animate,
545 Clone,
546 ComputeSquaredDistance,
547 Copy,
548 Debug,
549 MallocSizeOf,
550 PartialEq,
551 ToAnimatedValue,
552 ToAnimatedZero,
553 ToComputedValue,
554 ToCss,
555 ToResolvedValue,
556 ToShmem,
557)]
558#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
559pub struct KeywordInfo {
561 pub kw: FontSizeKeyword,
563 #[css(skip)]
565 pub factor: f32,
566 #[css(skip)]
569 pub offset: CSSPixelLength,
570}
571
572impl KeywordInfo {
573 pub fn medium() -> Self {
575 Self::new(FontSizeKeyword::Medium)
576 }
577
578 pub fn none() -> Self {
580 Self::new(FontSizeKeyword::None)
581 }
582
583 fn new(kw: FontSizeKeyword) -> Self {
584 KeywordInfo {
585 kw,
586 factor: 1.,
587 offset: CSSPixelLength::new(0.),
588 }
589 }
590
591 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
594 debug_assert_ne!(self.kw, FontSizeKeyword::None);
595 #[cfg(feature = "gecko")]
596 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
597 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
598 let zoom_factor = context.style().effective_zoom.value();
599 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
600 + context.maybe_zoom_text(self.offset)
601 }
602
603 fn compose(self, factor: f32) -> Self {
606 if self.kw == FontSizeKeyword::None {
607 return self;
608 }
609 KeywordInfo {
610 kw: self.kw,
611 factor: self.factor * factor,
612 offset: self.offset * factor,
613 }
614 }
615}
616
617impl SpecifiedValueInfo for KeywordInfo {
618 fn collect_completion_keywords(f: KeywordsCollectFn) {
619 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
620 }
621}
622
623#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
624pub enum FontSize {
626 Length(LengthPercentage),
628 Keyword(KeywordInfo),
639 Smaller,
641 Larger,
643 #[css(skip)]
645 System(SystemFont),
646}
647
648#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem, ToTyped)]
650#[cfg_attr(feature = "servo", derive(Hash))]
651pub enum FontFamily {
652 #[css(comma)]
654 Values(#[css(iterable)] FontFamilyList),
655 #[css(skip)]
657 System(SystemFont),
658}
659
660impl FontFamily {
661 system_font_methods!(FontFamily, font_family);
662}
663
664impl ToComputedValue for FontFamily {
665 type ComputedValue = computed::FontFamily;
666
667 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
668 match *self {
669 FontFamily::Values(ref list) => computed::FontFamily {
670 families: list.clone(),
671 is_system_font: false,
672 is_initial: false,
673 },
674 FontFamily::System(_) => self.compute_system(context),
675 }
676 }
677
678 fn from_computed_value(other: &computed::FontFamily) -> Self {
679 FontFamily::Values(other.families.clone())
680 }
681}
682
683#[cfg(feature = "gecko")]
684impl MallocSizeOf for FontFamily {
685 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
686 match *self {
687 FontFamily::Values(ref v) => {
688 v.list.unconditional_size_of(ops)
691 },
692 FontFamily::System(_) => 0,
693 }
694 }
695}
696
697impl Parse for FontFamily {
698 fn parse<'i, 't>(
702 context: &ParserContext,
703 input: &mut Parser<'i, 't>,
704 ) -> Result<FontFamily, ParseError<'i>> {
705 let values =
706 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
707 Ok(FontFamily::Values(FontFamilyList {
708 list: crate::ArcSlice::from_iter(values.into_iter()),
709 }))
710 }
711}
712
713impl SpecifiedValueInfo for FontFamily {}
714
715impl Parse for FamilyName {
718 fn parse<'i, 't>(
719 context: &ParserContext,
720 input: &mut Parser<'i, 't>,
721 ) -> Result<Self, ParseError<'i>> {
722 match SingleFontFamily::parse(context, input) {
723 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
724 Ok(SingleFontFamily::Generic(_)) => {
725 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
726 },
727 Err(e) => Err(e),
728 }
729 }
730}
731
732#[derive(
735 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
736)]
737pub enum FontSizeAdjustFactor {
738 Number(NonNegativeNumber),
740 FromFont,
742}
743
744pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
749
750impl Parse for FontSizeAdjust {
751 fn parse<'i, 't>(
752 context: &ParserContext,
753 input: &mut Parser<'i, 't>,
754 ) -> Result<Self, ParseError<'i>> {
755 let location = input.current_source_location();
756 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
758 return Ok(Self::ExHeight(factor));
759 }
760
761 let ident = input.expect_ident()?;
762 let basis = match_ignore_ascii_case! { &ident,
763 "none" => return Ok(Self::None),
764 "ex-height" => Self::ExHeight,
766 "cap-height" => Self::CapHeight,
767 "ch-width" => Self::ChWidth,
768 "ic-width" => Self::IcWidth,
769 "ic-height" => Self::IcHeight,
770 _ => return Err(location.new_custom_error(
772 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
773 )),
774 };
775
776 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
777 }
778}
779
780const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
783
784pub const FONT_MEDIUM_PX: f32 = 16.0;
786pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
788pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
791pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
794pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
797pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
800
801impl FontSizeKeyword {
802 #[inline]
803 fn to_length(&self, cx: &Context) -> NonNegativeLength {
804 let font = cx.style().get_font();
805
806 #[cfg(feature = "servo")]
807 let family = &font.font_family.families;
808 #[cfg(feature = "gecko")]
809 let family = &font.mFont.family.families;
810
811 let generic = family
812 .single_generic()
813 .unwrap_or(computed::GenericFontFamily::None);
814
815 #[cfg(feature = "gecko")]
816 let base_size = unsafe {
817 Atom::with(font.mLanguage.mRawPtr, |language| {
818 cx.device().base_size_for_generic(language, generic)
819 })
820 };
821 #[cfg(feature = "servo")]
822 let base_size = cx.device().base_size_for_generic(generic);
823
824 self.to_length_without_context(cx.quirks_mode, base_size)
825 }
826
827 #[inline]
829 pub fn to_length_without_context(
830 &self,
831 quirks_mode: QuirksMode,
832 base_size: Length,
833 ) -> NonNegativeLength {
834 #[cfg(feature = "gecko")]
835 debug_assert_ne!(*self, FontSizeKeyword::Math);
836 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
849 [9, 9, 9, 9, 11, 14, 18, 27],
850 [9, 9, 9, 10, 12, 15, 20, 30],
851 [9, 9, 10, 11, 13, 17, 22, 33],
852 [9, 9, 10, 12, 14, 18, 24, 36],
853 [9, 10, 12, 13, 16, 20, 26, 39],
854 [9, 10, 12, 14, 17, 21, 28, 42],
855 [9, 10, 13, 15, 18, 23, 30, 45],
856 [9, 10, 13, 16, 18, 24, 32, 48],
857 ];
858
859 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
868 [9, 9, 9, 9, 11, 14, 18, 28],
869 [9, 9, 9, 10, 12, 15, 20, 31],
870 [9, 9, 9, 11, 13, 17, 22, 34],
871 [9, 9, 10, 12, 14, 18, 24, 37],
872 [9, 9, 10, 13, 16, 20, 26, 40],
873 [9, 9, 11, 14, 17, 21, 28, 42],
874 [9, 10, 12, 15, 17, 23, 30, 45],
875 [9, 10, 13, 16, 18, 24, 32, 48],
876 ];
877
878 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
879 let base_size_px = base_size.px().round() as i32;
880 let html_size = self.html_size() as usize;
881 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
882 let mapping = if quirks_mode == QuirksMode::Quirks {
883 QUIRKS_FONT_SIZE_MAPPING
884 } else {
885 FONT_SIZE_MAPPING
886 };
887 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
888 } else {
889 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
890 })
891 }
892}
893
894impl FontSize {
895 pub fn from_html_size(size: u8) -> Self {
897 FontSize::Keyword(KeywordInfo::new(match size {
898 0 | 1 => FontSizeKeyword::XSmall,
900 2 => FontSizeKeyword::Small,
901 3 => FontSizeKeyword::Medium,
902 4 => FontSizeKeyword::Large,
903 5 => FontSizeKeyword::XLarge,
904 6 => FontSizeKeyword::XXLarge,
905 _ => FontSizeKeyword::XXXLarge,
907 }))
908 }
909
910 pub fn to_computed_value_against(
912 &self,
913 context: &Context,
914 base_size: FontBaseSize,
915 line_height_base: LineHeightBase,
916 ) -> computed::FontSize {
917 let compose_keyword = |factor| {
918 context
919 .style()
920 .get_parent_font()
921 .clone_font_size()
922 .keyword_info
923 .compose(factor)
924 };
925 let mut info = KeywordInfo::none();
926 let size = match *self {
927 FontSize::Length(LengthPercentage::Length(ref l)) => {
928 if let NoCalcLength::FontRelative(ref value) = *l {
929 if let FontRelativeLength::Em(em) = *value {
930 info = compose_keyword(em);
933 }
934 }
935 let result =
936 l.to_computed_value_with_base_size(context, base_size, line_height_base);
937 if l.should_zoom_text() {
938 context.maybe_zoom_text(result)
939 } else {
940 result
941 }
942 },
943 FontSize::Length(LengthPercentage::Percentage(pc)) => {
944 info = compose_keyword(pc.0);
947 (base_size.resolve(context).computed_size() * pc.0).normalized()
948 },
949 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
950 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
951 calc.resolve(base_size.resolve(context).computed_size())
952 },
953 FontSize::Keyword(i) => {
954 if i.kw.is_math() {
955 info = compose_keyword(1.);
957 info.kw = i.kw;
960 FontRelativeLength::Em(1.).to_computed_value(
961 context,
962 base_size,
963 line_height_base,
964 )
965 } else {
966 info = i;
968 i.to_computed_value(context).clamp_to_non_negative()
969 }
970 },
971 FontSize::Smaller => {
972 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
973 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
974 context,
975 base_size,
976 line_height_base,
977 )
978 },
979 FontSize::Larger => {
980 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
981 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
982 context,
983 base_size,
984 line_height_base,
985 )
986 },
987
988 FontSize::System(_) => {
989 #[cfg(feature = "servo")]
990 {
991 unreachable!()
992 }
993 #[cfg(feature = "gecko")]
994 {
995 context
996 .cached_system_font
997 .as_ref()
998 .unwrap()
999 .font_size
1000 .computed_size()
1001 }
1002 },
1003 };
1004 computed::FontSize {
1005 computed_size: NonNegative(size),
1006 used_size: NonNegative(size),
1007 keyword_info: info,
1008 }
1009 }
1010}
1011
1012impl ToComputedValue for FontSize {
1013 type ComputedValue = computed::FontSize;
1014
1015 #[inline]
1016 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1017 self.to_computed_value_against(
1018 context,
1019 FontBaseSize::InheritedStyle,
1020 LineHeightBase::InheritedStyle,
1021 )
1022 }
1023
1024 #[inline]
1025 fn from_computed_value(computed: &computed::FontSize) -> Self {
1026 FontSize::Length(LengthPercentage::Length(
1027 ToComputedValue::from_computed_value(&computed.computed_size()),
1028 ))
1029 }
1030}
1031
1032impl FontSize {
1033 system_font_methods!(FontSize);
1034
1035 #[inline]
1037 pub fn medium() -> Self {
1038 FontSize::Keyword(KeywordInfo::medium())
1039 }
1040
1041 pub fn parse_quirky<'i, 't>(
1043 context: &ParserContext,
1044 input: &mut Parser<'i, 't>,
1045 allow_quirks: AllowQuirks,
1046 ) -> Result<FontSize, ParseError<'i>> {
1047 if let Ok(lp) = input
1048 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1049 {
1050 return Ok(FontSize::Length(lp));
1051 }
1052
1053 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1054 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1055 }
1056
1057 try_match_ident_ignore_ascii_case! { input,
1058 "smaller" => Ok(FontSize::Smaller),
1059 "larger" => Ok(FontSize::Larger),
1060 }
1061 }
1062}
1063
1064impl Parse for FontSize {
1065 fn parse<'i, 't>(
1067 context: &ParserContext,
1068 input: &mut Parser<'i, 't>,
1069 ) -> Result<FontSize, ParseError<'i>> {
1070 FontSize::parse_quirky(context, input, AllowQuirks::No)
1071 }
1072}
1073
1074bitflags! {
1075 #[derive(Clone, Copy)]
1076 struct VariantAlternatesParsingFlags: u8 {
1078 const NORMAL = 0;
1080 const HISTORICAL_FORMS = 0x01;
1082 const STYLISTIC = 0x02;
1084 const STYLESET = 0x04;
1086 const CHARACTER_VARIANT = 0x08;
1088 const SWASH = 0x10;
1090 const ORNAMENTS = 0x20;
1092 const ANNOTATION = 0x40;
1094 }
1095}
1096
1097#[derive(
1098 Clone,
1099 Debug,
1100 MallocSizeOf,
1101 PartialEq,
1102 SpecifiedValueInfo,
1103 ToCss,
1104 ToComputedValue,
1105 ToResolvedValue,
1106 ToShmem,
1107)]
1108#[repr(C, u8)]
1109pub enum VariantAlternates {
1111 #[css(function)]
1113 Stylistic(CustomIdent),
1114 #[css(comma, function)]
1116 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1117 #[css(comma, function)]
1119 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1120 #[css(function)]
1122 Swash(CustomIdent),
1123 #[css(function)]
1125 Ornaments(CustomIdent),
1126 #[css(function)]
1128 Annotation(CustomIdent),
1129 HistoricalForms,
1131}
1132
1133#[derive(
1134 Clone,
1135 Debug,
1136 Default,
1137 MallocSizeOf,
1138 PartialEq,
1139 SpecifiedValueInfo,
1140 ToComputedValue,
1141 ToCss,
1142 ToResolvedValue,
1143 ToShmem,
1144 ToTyped,
1145)]
1146#[repr(transparent)]
1147pub struct FontVariantAlternates(
1149 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1150);
1151
1152impl FontVariantAlternates {
1153 pub fn len(&self) -> usize {
1155 self.0.iter().fold(0, |acc, alternate| match *alternate {
1156 VariantAlternates::Swash(_)
1157 | VariantAlternates::Stylistic(_)
1158 | VariantAlternates::Ornaments(_)
1159 | VariantAlternates::Annotation(_) => acc + 1,
1160 VariantAlternates::Styleset(ref slice)
1161 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1162 _ => acc,
1163 })
1164 }
1165}
1166
1167impl FontVariantAlternates {
1168 #[inline]
1169 pub fn get_initial_specified_value() -> Self {
1171 Default::default()
1172 }
1173}
1174
1175impl Parse for FontVariantAlternates {
1176 fn parse<'i, 't>(
1185 _: &ParserContext,
1186 input: &mut Parser<'i, 't>,
1187 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1188 if input
1189 .try_parse(|input| input.expect_ident_matching("normal"))
1190 .is_ok()
1191 {
1192 return Ok(Default::default());
1193 }
1194
1195 let mut stylistic = None;
1196 let mut historical = None;
1197 let mut styleset = None;
1198 let mut character_variant = None;
1199 let mut swash = None;
1200 let mut ornaments = None;
1201 let mut annotation = None;
1202
1203 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1205 macro_rules! check_if_parsed(
1206 ($input:expr, $flag:path) => (
1207 if parsed_alternates.contains($flag) {
1208 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1209 }
1210 parsed_alternates |= $flag;
1211 )
1212 );
1213 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1214 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1215 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1216 historical = Some(VariantAlternates::HistoricalForms);
1217 Ok(())
1218 },
1219 Token::Function(ref name) => {
1220 let name = name.clone();
1221 input.parse_nested_block(|i| {
1222 match_ignore_ascii_case! { &name,
1223 "swash" => {
1224 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1225 let ident = CustomIdent::parse(i, &[])?;
1226 swash = Some(VariantAlternates::Swash(ident));
1227 Ok(())
1228 },
1229 "stylistic" => {
1230 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1231 let ident = CustomIdent::parse(i, &[])?;
1232 stylistic = Some(VariantAlternates::Stylistic(ident));
1233 Ok(())
1234 },
1235 "ornaments" => {
1236 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1237 let ident = CustomIdent::parse(i, &[])?;
1238 ornaments = Some(VariantAlternates::Ornaments(ident));
1239 Ok(())
1240 },
1241 "annotation" => {
1242 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1243 let ident = CustomIdent::parse(i, &[])?;
1244 annotation = Some(VariantAlternates::Annotation(ident));
1245 Ok(())
1246 },
1247 "styleset" => {
1248 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1249 let idents = i.parse_comma_separated(|i| {
1250 CustomIdent::parse(i, &[])
1251 })?;
1252 styleset = Some(VariantAlternates::Styleset(idents.into()));
1253 Ok(())
1254 },
1255 "character-variant" => {
1256 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1257 let idents = i.parse_comma_separated(|i| {
1258 CustomIdent::parse(i, &[])
1259 })?;
1260 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1261 Ok(())
1262 },
1263 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1264 }
1265 })
1266 },
1267 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1268 }) {}
1269
1270 if parsed_alternates.is_empty() {
1271 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1272 }
1273
1274 let mut alternates = Vec::new();
1276 macro_rules! push_if_some(
1277 ($value:expr) => (
1278 if let Some(v) = $value {
1279 alternates.push(v);
1280 }
1281 )
1282 );
1283 push_if_some!(stylistic);
1284 push_if_some!(historical);
1285 push_if_some!(styleset);
1286 push_if_some!(character_variant);
1287 push_if_some!(swash);
1288 push_if_some!(ornaments);
1289 push_if_some!(annotation);
1290
1291 Ok(FontVariantAlternates(alternates.into()))
1292 }
1293}
1294
1295#[derive(
1296 Clone,
1297 Copy,
1298 Debug,
1299 Eq,
1300 MallocSizeOf,
1301 PartialEq,
1302 Parse,
1303 SpecifiedValueInfo,
1304 ToComputedValue,
1305 ToCss,
1306 ToResolvedValue,
1307 ToShmem,
1308 ToTyped,
1309)]
1310#[css(bitflags(
1311 single = "normal",
1312 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1313 validate_mixed = "Self::validate_mixed_flags",
1314))]
1315#[repr(C)]
1316pub struct FontVariantEastAsian(u16);
1318bitflags! {
1319 impl FontVariantEastAsian: u16 {
1320 const NORMAL = 0;
1322 const JIS78 = 1 << 0;
1324 const JIS83 = 1 << 1;
1326 const JIS90 = 1 << 2;
1328 const JIS04 = 1 << 3;
1330 const SIMPLIFIED = 1 << 4;
1332 const TRADITIONAL = 1 << 5;
1334
1335 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1337
1338 const FULL_WIDTH = 1 << 6;
1340 const PROPORTIONAL_WIDTH = 1 << 7;
1342 const RUBY = 1 << 8;
1344 }
1345}
1346
1347impl FontVariantEastAsian {
1348 pub const COUNT: usize = 9;
1350
1351 fn validate_mixed_flags(&self) -> bool {
1352 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1353 return false;
1355 }
1356 let jis = self.intersection(Self::JIS_GROUP);
1357 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1358 return false;
1359 }
1360 true
1361 }
1362}
1363
1364#[derive(
1365 Clone,
1366 Copy,
1367 Debug,
1368 Eq,
1369 MallocSizeOf,
1370 PartialEq,
1371 Parse,
1372 SpecifiedValueInfo,
1373 ToComputedValue,
1374 ToCss,
1375 ToResolvedValue,
1376 ToShmem,
1377 ToTyped,
1378)]
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#[repr(C)]
1451pub struct FontVariantNumeric(u8);
1452bitflags! {
1453 impl FontVariantNumeric : u8 {
1454 const NORMAL = 0;
1456 const LINING_NUMS = 1 << 0;
1458 const OLDSTYLE_NUMS = 1 << 1;
1460 const PROPORTIONAL_NUMS = 1 << 2;
1462 const TABULAR_NUMS = 1 << 3;
1464 const DIAGONAL_FRACTIONS = 1 << 4;
1466 const STACKED_FRACTIONS = 1 << 5;
1468 const SLASHED_ZERO = 1 << 6;
1470 const ORDINAL = 1 << 7;
1472 }
1473}
1474
1475impl FontVariantNumeric {
1476 pub const COUNT: usize = 8;
1478
1479 fn validate_mixed_flags(&self) -> bool {
1489 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1490 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1491 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1492 {
1493 return false;
1494 }
1495 true
1496 }
1497}
1498
1499pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1501
1502pub use crate::values::computed::font::FontLanguageOverride;
1504
1505impl Parse for FontLanguageOverride {
1506 fn parse<'i, 't>(
1508 _: &ParserContext,
1509 input: &mut Parser<'i, 't>,
1510 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1511 if input
1512 .try_parse(|input| input.expect_ident_matching("normal"))
1513 .is_ok()
1514 {
1515 return Ok(FontLanguageOverride::normal());
1516 }
1517
1518 let string = input.expect_string()?;
1519
1520 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1523 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1524 }
1525
1526 let mut bytes = [b' '; 4];
1527 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1528 *byte = *str_byte;
1529 }
1530
1531 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1532 }
1533}
1534
1535#[repr(u8)]
1537#[derive(
1538 Clone,
1539 Copy,
1540 Debug,
1541 Eq,
1542 Hash,
1543 MallocSizeOf,
1544 Parse,
1545 PartialEq,
1546 SpecifiedValueInfo,
1547 ToComputedValue,
1548 ToCss,
1549 ToResolvedValue,
1550 ToShmem,
1551 ToTyped,
1552)]
1553#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1554pub enum FontSynthesis {
1555 Auto,
1557 None,
1559}
1560
1561#[repr(u8)]
1563#[derive(
1564 Clone,
1565 Copy,
1566 Debug,
1567 Eq,
1568 MallocSizeOf,
1569 Parse,
1570 PartialEq,
1571 SpecifiedValueInfo,
1572 ToComputedValue,
1573 ToCss,
1574 ToResolvedValue,
1575 ToShmem,
1576 ToTyped,
1577)]
1578pub enum FontSynthesisStyle {
1579 Auto,
1581 None,
1583 ObliqueOnly,
1585}
1586
1587#[derive(
1588 Clone,
1589 Debug,
1590 Eq,
1591 MallocSizeOf,
1592 PartialEq,
1593 SpecifiedValueInfo,
1594 ToComputedValue,
1595 ToResolvedValue,
1596 ToShmem,
1597 ToTyped,
1598)]
1599#[repr(C)]
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, 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}