1use crate::context::QuirksMode;
8use crate::parser::{Parse, ParserContext};
9use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
10use crate::values::computed::Percentage as ComputedPercentage;
11use crate::values::computed::{font as computed, Length, NonNegativeLength};
12use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
13use crate::values::generics::font::{
14 self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
15};
16use crate::values::generics::NonNegative;
17use crate::values::specified::length::{FontBaseSize, LineHeightBase, PX_PER_PT};
18use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
19use crate::values::specified::{
20 FontRelativeLength, NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber,
21 NonNegativePercentage, Number,
22};
23use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
24use crate::Atom;
25use cssparser::{Parser, Token};
26#[cfg(feature = "gecko")]
27use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
28use std::fmt::{self, Write};
29use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
30use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
31
32macro_rules! system_font_methods {
34 ($ty:ident, $field:ident) => {
35 system_font_methods!($ty);
36
37 fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
38 debug_assert!(matches!(*self, $ty::System(..)));
39 #[cfg(feature = "gecko")]
40 {
41 _context.cached_system_font.as_ref().unwrap().$field.clone()
42 }
43 #[cfg(feature = "servo")]
44 {
45 unreachable!()
46 }
47 }
48 };
49
50 ($ty:ident) => {
51 pub fn system_font(f: SystemFont) -> Self {
53 $ty::System(f)
54 }
55
56 pub fn get_system(&self) -> Option<SystemFont> {
58 if let $ty::System(s) = *self {
59 Some(s)
60 } else {
61 None
62 }
63 }
64 };
65}
66
67#[repr(u8)]
69#[derive(
70 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
71)]
72#[allow(missing_docs)]
73#[cfg(feature = "gecko")]
74pub enum SystemFont {
75 Caption,
77 Icon,
79 Menu,
81 MessageBox,
83 SmallCaption,
85 StatusBar,
87 #[parse(condition = "ParserContext::chrome_rules_enabled")]
89 MozPullDownMenu,
90 #[parse(condition = "ParserContext::chrome_rules_enabled")]
92 MozButton,
93 #[parse(condition = "ParserContext::chrome_rules_enabled")]
95 MozList,
96 #[parse(condition = "ParserContext::chrome_rules_enabled")]
98 MozField,
99 #[css(skip)]
100 End, }
102
103#[derive(
108 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
109)]
110#[allow(missing_docs)]
111#[cfg(feature = "servo")]
112pub enum SystemFont {}
114
115#[allow(missing_docs)]
116#[cfg(feature = "servo")]
117impl SystemFont {
118 pub fn parse(_: &mut Parser) -> Result<Self, ()> {
119 Err(())
120 }
121}
122
123const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
124const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
125
126pub const MIN_FONT_WEIGHT: f32 = 1.;
130
131pub const MAX_FONT_WEIGHT: f32 = 1000.;
135
136#[derive(
140 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
141)]
142pub enum FontWeight {
143 Absolute(AbsoluteFontWeight),
145 Bolder,
147 Lighter,
149 #[css(skip)]
151 System(SystemFont),
152}
153
154impl FontWeight {
155 system_font_methods!(FontWeight, font_weight);
156
157 #[inline]
159 pub fn normal() -> Self {
160 FontWeight::Absolute(AbsoluteFontWeight::Normal)
161 }
162
163 pub fn from_gecko_keyword(kw: u32) -> Self {
165 debug_assert!(kw % 100 == 0);
166 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
167 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
168 }
169}
170
171impl ToComputedValue for FontWeight {
172 type ComputedValue = computed::FontWeight;
173
174 #[inline]
175 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
176 match *self {
177 FontWeight::Absolute(ref abs) => abs.compute(),
178 FontWeight::Bolder => context
179 .builder
180 .get_parent_font()
181 .clone_font_weight()
182 .bolder(),
183 FontWeight::Lighter => context
184 .builder
185 .get_parent_font()
186 .clone_font_weight()
187 .lighter(),
188 FontWeight::System(_) => self.compute_system(context),
189 }
190 }
191
192 #[inline]
193 fn from_computed_value(computed: &computed::FontWeight) -> Self {
194 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
195 &computed.value(),
196 )))
197 }
198}
199
200#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
204pub enum AbsoluteFontWeight {
205 Weight(Number),
209 Normal,
211 Bold,
213}
214
215impl AbsoluteFontWeight {
216 pub fn compute(&self) -> computed::FontWeight {
218 match *self {
219 AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),
220 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
221 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
222 }
223 }
224}
225
226impl Parse for AbsoluteFontWeight {
227 fn parse<'i, 't>(
228 context: &ParserContext,
229 input: &mut Parser<'i, 't>,
230 ) -> Result<Self, ParseError<'i>> {
231 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
232 if !number.was_calc()
236 && (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
237 {
238 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
239 }
240 return Ok(AbsoluteFontWeight::Weight(number));
241 }
242
243 Ok(try_match_ident_ignore_ascii_case! { input,
244 "normal" => AbsoluteFontWeight::Normal,
245 "bold" => AbsoluteFontWeight::Bold,
246 })
247 }
248}
249
250pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
253
254impl ToCss for SpecifiedFontStyle {
255 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
256 where
257 W: Write,
258 {
259 match *self {
260 generics::FontStyle::Italic => dest.write_str("italic"),
261 generics::FontStyle::Oblique(ref angle) => {
262 if *angle == Angle::zero() {
265 dest.write_str("normal")?;
266 } else {
267 dest.write_str("oblique")?;
268 if *angle != Self::default_angle() {
269 dest.write_char(' ')?;
270 angle.to_css(dest)?;
271 }
272 }
273 Ok(())
274 },
275 }
276 }
277}
278
279impl Parse for SpecifiedFontStyle {
280 fn parse<'i, 't>(
281 context: &ParserContext,
282 input: &mut Parser<'i, 't>,
283 ) -> Result<Self, ParseError<'i>> {
284 Ok(try_match_ident_ignore_ascii_case! { input,
285 "normal" => generics::FontStyle::normal(),
286 "italic" => generics::FontStyle::Italic,
287 "oblique" => {
288 let angle = input.try_parse(|input| Self::parse_angle(context, input))
289 .unwrap_or_else(|_| Self::default_angle());
290
291 generics::FontStyle::Oblique(angle)
292 },
293 })
294 }
295}
296
297impl ToComputedValue for SpecifiedFontStyle {
298 type ComputedValue = computed::FontStyle;
299
300 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
301 match *self {
302 Self::Italic => computed::FontStyle::ITALIC,
303 Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),
304 }
305 }
306
307 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
308 if *computed == computed::FontStyle::ITALIC {
309 return Self::Italic;
310 }
311 let degrees = computed.oblique_degrees();
312 generics::FontStyle::Oblique(Angle::from_degrees(degrees, false))
313 }
314}
315
316pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
323
324pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
326
327impl SpecifiedFontStyle {
328 pub fn compute_angle_degrees(angle: &Angle) -> f32 {
330 angle
331 .degrees()
332 .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
333 .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
334 }
335
336 pub fn parse_angle<'i, 't>(
338 context: &ParserContext,
339 input: &mut Parser<'i, 't>,
340 ) -> Result<Angle, ParseError<'i>> {
341 let angle = Angle::parse(context, input)?;
342 if angle.was_calc() {
343 return Ok(angle);
344 }
345
346 let degrees = angle.degrees();
347 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES
348 || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
349 {
350 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
351 }
352 return Ok(angle);
353 }
354
355 pub fn default_angle() -> Angle {
357 Angle::from_degrees(
358 computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,
359 false,
360 )
361 }
362}
363
364#[derive(
366 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
367)]
368#[allow(missing_docs)]
369pub enum FontStyle {
370 Specified(SpecifiedFontStyle),
371 #[css(skip)]
372 System(SystemFont),
373}
374
375impl FontStyle {
376 #[inline]
378 pub fn normal() -> Self {
379 FontStyle::Specified(generics::FontStyle::normal())
380 }
381
382 system_font_methods!(FontStyle, font_style);
383}
384
385impl ToComputedValue for FontStyle {
386 type ComputedValue = computed::FontStyle;
387
388 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
389 match *self {
390 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
391 FontStyle::System(..) => self.compute_system(context),
392 }
393 }
394
395 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
396 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
397 }
398}
399
400#[allow(missing_docs)]
404#[derive(
405 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
406)]
407pub enum FontStretch {
408 Stretch(NonNegativePercentage),
409 Keyword(FontStretchKeyword),
410 #[css(skip)]
411 System(SystemFont),
412}
413
414#[derive(
416 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
417)]
418#[allow(missing_docs)]
419pub enum FontStretchKeyword {
420 Normal,
421 Condensed,
422 UltraCondensed,
423 ExtraCondensed,
424 SemiCondensed,
425 SemiExpanded,
426 Expanded,
427 ExtraExpanded,
428 UltraExpanded,
429}
430
431impl FontStretchKeyword {
432 pub fn compute(&self) -> computed::FontStretch {
434 computed::FontStretch::from_keyword(*self)
435 }
436
437 pub fn from_percentage(p: f32) -> Option<Self> {
440 computed::FontStretch::from_percentage(p).as_keyword()
441 }
442}
443
444impl FontStretch {
445 pub fn normal() -> Self {
447 FontStretch::Keyword(FontStretchKeyword::Normal)
448 }
449
450 system_font_methods!(FontStretch, font_stretch);
451}
452
453impl ToComputedValue for FontStretch {
454 type ComputedValue = computed::FontStretch;
455
456 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
457 match *self {
458 FontStretch::Stretch(ref percentage) => {
459 let percentage = percentage.to_computed_value(context).0;
460 computed::FontStretch::from_percentage(percentage.0)
461 },
462 FontStretch::Keyword(ref kw) => kw.compute(),
463 FontStretch::System(_) => self.compute_system(context),
464 }
465 }
466
467 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
468 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
469 computed.to_percentage(),
470 )))
471 }
472}
473
474#[derive(
476 Animate,
477 Clone,
478 ComputeSquaredDistance,
479 Copy,
480 Debug,
481 MallocSizeOf,
482 Parse,
483 PartialEq,
484 SpecifiedValueInfo,
485 ToAnimatedValue,
486 ToAnimatedZero,
487 ToComputedValue,
488 ToCss,
489 ToResolvedValue,
490 ToShmem,
491 Serialize,
492 Deserialize,
493)]
494#[allow(missing_docs)]
495#[repr(u8)]
496pub enum FontSizeKeyword {
497 #[css(keyword = "xx-small")]
498 XXSmall,
499 XSmall,
500 Small,
501 Medium,
502 Large,
503 XLarge,
504 #[css(keyword = "xx-large")]
505 XXLarge,
506 #[css(keyword = "xxx-large")]
507 XXXLarge,
508 #[cfg(feature = "gecko")]
511 Math,
512 #[css(skip)]
513 None,
514}
515
516impl FontSizeKeyword {
517 #[inline]
519 pub fn html_size(self) -> u8 {
520 self as u8
521 }
522
523 #[cfg(feature = "gecko")]
525 pub fn is_math(self) -> bool {
526 matches!(self, Self::Math)
527 }
528
529 #[cfg(feature = "servo")]
531 pub fn is_math(self) -> bool {
532 false
533 }
534}
535
536impl Default for FontSizeKeyword {
537 fn default() -> Self {
538 FontSizeKeyword::Medium
539 }
540}
541
542#[derive(
543 Animate,
544 Clone,
545 ComputeSquaredDistance,
546 Copy,
547 Debug,
548 MallocSizeOf,
549 PartialEq,
550 ToAnimatedValue,
551 ToAnimatedZero,
552 ToComputedValue,
553 ToCss,
554 ToResolvedValue,
555 ToShmem,
556)]
557#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
558pub struct KeywordInfo {
560 pub kw: FontSizeKeyword,
562 #[css(skip)]
564 pub factor: f32,
565 #[css(skip)]
568 pub offset: CSSPixelLength,
569}
570
571impl KeywordInfo {
572 pub fn medium() -> Self {
574 Self::new(FontSizeKeyword::Medium)
575 }
576
577 pub fn none() -> Self {
579 Self::new(FontSizeKeyword::None)
580 }
581
582 fn new(kw: FontSizeKeyword) -> Self {
583 KeywordInfo {
584 kw,
585 factor: 1.,
586 offset: CSSPixelLength::new(0.),
587 }
588 }
589
590 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
593 debug_assert_ne!(self.kw, FontSizeKeyword::None);
594 #[cfg(feature = "gecko")]
595 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
596 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
597 let zoom_factor = context.style().effective_zoom.value();
598 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
599 + context.maybe_zoom_text(self.offset)
600 }
601
602 fn compose(self, factor: f32) -> Self {
605 if self.kw == FontSizeKeyword::None {
606 return self;
607 }
608 KeywordInfo {
609 kw: self.kw,
610 factor: self.factor * factor,
611 offset: self.offset * factor,
612 }
613 }
614}
615
616impl SpecifiedValueInfo for KeywordInfo {
617 fn collect_completion_keywords(f: KeywordsCollectFn) {
618 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
619 }
620}
621
622#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
623pub enum FontSize {
625 Length(LengthPercentage),
627 Keyword(KeywordInfo),
638 Smaller,
640 Larger,
642 #[css(skip)]
644 System(SystemFont),
645}
646
647#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem, ToTyped)]
649#[cfg_attr(feature = "servo", derive(Hash))]
650pub enum FontFamily {
651 #[css(comma)]
653 Values(#[css(iterable)] FontFamilyList),
654 #[css(skip)]
656 System(SystemFont),
657}
658
659impl FontFamily {
660 system_font_methods!(FontFamily, font_family);
661}
662
663impl ToComputedValue for FontFamily {
664 type ComputedValue = computed::FontFamily;
665
666 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
667 match *self {
668 FontFamily::Values(ref list) => computed::FontFamily {
669 families: list.clone(),
670 is_system_font: false,
671 is_initial: false,
672 },
673 FontFamily::System(_) => self.compute_system(context),
674 }
675 }
676
677 fn from_computed_value(other: &computed::FontFamily) -> Self {
678 FontFamily::Values(other.families.clone())
679 }
680}
681
682#[cfg(feature = "gecko")]
683impl MallocSizeOf for FontFamily {
684 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
685 match *self {
686 FontFamily::Values(ref v) => {
687 v.list.unconditional_size_of(ops)
690 },
691 FontFamily::System(_) => 0,
692 }
693 }
694}
695
696impl Parse for FontFamily {
697 fn parse<'i, 't>(
701 context: &ParserContext,
702 input: &mut Parser<'i, 't>,
703 ) -> Result<FontFamily, ParseError<'i>> {
704 let values =
705 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
706 Ok(FontFamily::Values(FontFamilyList {
707 list: crate::ArcSlice::from_iter(values.into_iter()),
708 }))
709 }
710}
711
712impl SpecifiedValueInfo for FontFamily {}
713
714impl Parse for FamilyName {
717 fn parse<'i, 't>(
718 context: &ParserContext,
719 input: &mut Parser<'i, 't>,
720 ) -> Result<Self, ParseError<'i>> {
721 match SingleFontFamily::parse(context, input) {
722 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
723 Ok(SingleFontFamily::Generic(_)) => {
724 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
725 },
726 Err(e) => Err(e),
727 }
728 }
729}
730
731#[derive(
734 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
735)]
736pub enum FontSizeAdjustFactor {
737 Number(NonNegativeNumber),
739 FromFont,
741}
742
743pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
748
749impl Parse for FontSizeAdjust {
750 fn parse<'i, 't>(
751 context: &ParserContext,
752 input: &mut Parser<'i, 't>,
753 ) -> Result<Self, ParseError<'i>> {
754 let location = input.current_source_location();
755 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
757 return Ok(Self::ExHeight(factor));
758 }
759
760 let ident = input.expect_ident()?;
761 let basis = match_ignore_ascii_case! { &ident,
762 "none" => return Ok(Self::None),
763 "ex-height" => Self::ExHeight,
765 "cap-height" => Self::CapHeight,
766 "ch-width" => Self::ChWidth,
767 "ic-width" => Self::IcWidth,
768 "ic-height" => Self::IcHeight,
769 _ => return Err(location.new_custom_error(
771 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
772 )),
773 };
774
775 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
776 }
777}
778
779const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
782
783pub const FONT_MEDIUM_PX: f32 = 16.0;
785pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
787
788impl FontSizeKeyword {
789 #[inline]
790 fn to_length(&self, cx: &Context) -> NonNegativeLength {
791 let font = cx.style().get_font();
792
793 #[cfg(feature = "servo")]
794 let family = &font.font_family.families;
795 #[cfg(feature = "gecko")]
796 let family = &font.mFont.family.families;
797
798 let generic = family
799 .single_generic()
800 .unwrap_or(computed::GenericFontFamily::None);
801
802 #[cfg(feature = "gecko")]
803 let base_size = unsafe {
804 Atom::with(font.mLanguage.mRawPtr, |language| {
805 cx.device().base_size_for_generic(language, generic)
806 })
807 };
808 #[cfg(feature = "servo")]
809 let base_size = cx.device().base_size_for_generic(generic);
810
811 self.to_length_without_context(cx.quirks_mode, base_size)
812 }
813
814 #[inline]
816 pub fn to_length_without_context(
817 &self,
818 quirks_mode: QuirksMode,
819 base_size: Length,
820 ) -> NonNegativeLength {
821 #[cfg(feature = "gecko")]
822 debug_assert_ne!(*self, FontSizeKeyword::Math);
823 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
836 [9, 9, 9, 9, 11, 14, 18, 27],
837 [9, 9, 9, 10, 12, 15, 20, 30],
838 [9, 9, 10, 11, 13, 17, 22, 33],
839 [9, 9, 10, 12, 14, 18, 24, 36],
840 [9, 10, 12, 13, 16, 20, 26, 39],
841 [9, 10, 12, 14, 17, 21, 28, 42],
842 [9, 10, 13, 15, 18, 23, 30, 45],
843 [9, 10, 13, 16, 18, 24, 32, 48],
844 ];
845
846 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
855 [9, 9, 9, 9, 11, 14, 18, 28],
856 [9, 9, 9, 10, 12, 15, 20, 31],
857 [9, 9, 9, 11, 13, 17, 22, 34],
858 [9, 9, 10, 12, 14, 18, 24, 37],
859 [9, 9, 10, 13, 16, 20, 26, 40],
860 [9, 9, 11, 14, 17, 21, 28, 42],
861 [9, 10, 12, 15, 17, 23, 30, 45],
862 [9, 10, 13, 16, 18, 24, 32, 48],
863 ];
864
865 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
866 let base_size_px = base_size.px().round() as i32;
867 let html_size = self.html_size() as usize;
868 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
869 let mapping = if quirks_mode == QuirksMode::Quirks {
870 QUIRKS_FONT_SIZE_MAPPING
871 } else {
872 FONT_SIZE_MAPPING
873 };
874 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
875 } else {
876 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
877 })
878 }
879}
880
881impl FontSize {
882 pub fn from_html_size(size: u8) -> Self {
884 FontSize::Keyword(KeywordInfo::new(match size {
885 0 | 1 => FontSizeKeyword::XSmall,
887 2 => FontSizeKeyword::Small,
888 3 => FontSizeKeyword::Medium,
889 4 => FontSizeKeyword::Large,
890 5 => FontSizeKeyword::XLarge,
891 6 => FontSizeKeyword::XXLarge,
892 _ => FontSizeKeyword::XXXLarge,
894 }))
895 }
896
897 pub fn to_computed_value_against(
899 &self,
900 context: &Context,
901 base_size: FontBaseSize,
902 line_height_base: LineHeightBase,
903 ) -> computed::FontSize {
904 let compose_keyword = |factor| {
905 context
906 .style()
907 .get_parent_font()
908 .clone_font_size()
909 .keyword_info
910 .compose(factor)
911 };
912 let mut info = KeywordInfo::none();
913 let size = match *self {
914 FontSize::Length(LengthPercentage::Length(ref l)) => {
915 if let NoCalcLength::FontRelative(ref value) = *l {
916 if let FontRelativeLength::Em(em) = *value {
917 info = compose_keyword(em);
920 }
921 }
922 let result =
923 l.to_computed_value_with_base_size(context, base_size, line_height_base);
924 if l.should_zoom_text() {
925 context.maybe_zoom_text(result)
926 } else {
927 result
928 }
929 },
930 FontSize::Length(LengthPercentage::Percentage(pc)) => {
931 info = compose_keyword(pc.0);
934 (base_size.resolve(context).computed_size() * pc.0).normalized()
935 },
936 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
937 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
938 calc.resolve(base_size.resolve(context).computed_size())
939 },
940 FontSize::Keyword(i) => {
941 if i.kw.is_math() {
942 info = compose_keyword(1.);
944 info.kw = i.kw;
947 FontRelativeLength::Em(1.).to_computed_value(
948 context,
949 base_size,
950 line_height_base,
951 )
952 } else {
953 info = i;
955 i.to_computed_value(context).clamp_to_non_negative()
956 }
957 },
958 FontSize::Smaller => {
959 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
960 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
961 context,
962 base_size,
963 line_height_base,
964 )
965 },
966 FontSize::Larger => {
967 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
968 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
969 context,
970 base_size,
971 line_height_base,
972 )
973 },
974
975 FontSize::System(_) => {
976 #[cfg(feature = "servo")]
977 {
978 unreachable!()
979 }
980 #[cfg(feature = "gecko")]
981 {
982 context
983 .cached_system_font
984 .as_ref()
985 .unwrap()
986 .font_size
987 .computed_size()
988 }
989 },
990 };
991 computed::FontSize {
992 computed_size: NonNegative(size),
993 used_size: NonNegative(size),
994 keyword_info: info,
995 }
996 }
997}
998
999impl ToComputedValue for FontSize {
1000 type ComputedValue = computed::FontSize;
1001
1002 #[inline]
1003 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1004 self.to_computed_value_against(
1005 context,
1006 FontBaseSize::InheritedStyle,
1007 LineHeightBase::InheritedStyle,
1008 )
1009 }
1010
1011 #[inline]
1012 fn from_computed_value(computed: &computed::FontSize) -> Self {
1013 FontSize::Length(LengthPercentage::Length(
1014 ToComputedValue::from_computed_value(&computed.computed_size()),
1015 ))
1016 }
1017}
1018
1019impl FontSize {
1020 system_font_methods!(FontSize);
1021
1022 #[inline]
1024 pub fn medium() -> Self {
1025 FontSize::Keyword(KeywordInfo::medium())
1026 }
1027
1028 pub fn parse_quirky<'i, 't>(
1030 context: &ParserContext,
1031 input: &mut Parser<'i, 't>,
1032 allow_quirks: AllowQuirks,
1033 ) -> Result<FontSize, ParseError<'i>> {
1034 if let Ok(lp) = input
1035 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1036 {
1037 return Ok(FontSize::Length(lp));
1038 }
1039
1040 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1041 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1042 }
1043
1044 try_match_ident_ignore_ascii_case! { input,
1045 "smaller" => Ok(FontSize::Smaller),
1046 "larger" => Ok(FontSize::Larger),
1047 }
1048 }
1049}
1050
1051impl Parse for FontSize {
1052 fn parse<'i, 't>(
1054 context: &ParserContext,
1055 input: &mut Parser<'i, 't>,
1056 ) -> Result<FontSize, ParseError<'i>> {
1057 FontSize::parse_quirky(context, input, AllowQuirks::No)
1058 }
1059}
1060
1061bitflags! {
1062 #[derive(Clone, Copy)]
1063 struct VariantAlternatesParsingFlags: u8 {
1065 const NORMAL = 0;
1067 const HISTORICAL_FORMS = 0x01;
1069 const STYLISTIC = 0x02;
1071 const STYLESET = 0x04;
1073 const CHARACTER_VARIANT = 0x08;
1075 const SWASH = 0x10;
1077 const ORNAMENTS = 0x20;
1079 const ANNOTATION = 0x40;
1081 }
1082}
1083
1084#[derive(
1085 Clone,
1086 Debug,
1087 MallocSizeOf,
1088 PartialEq,
1089 SpecifiedValueInfo,
1090 ToCss,
1091 ToComputedValue,
1092 ToResolvedValue,
1093 ToShmem,
1094)]
1095#[repr(C, u8)]
1096pub enum VariantAlternates {
1098 #[css(function)]
1100 Stylistic(CustomIdent),
1101 #[css(comma, function)]
1103 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1104 #[css(comma, function)]
1106 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1107 #[css(function)]
1109 Swash(CustomIdent),
1110 #[css(function)]
1112 Ornaments(CustomIdent),
1113 #[css(function)]
1115 Annotation(CustomIdent),
1116 HistoricalForms,
1118}
1119
1120#[derive(
1121 Clone,
1122 Debug,
1123 Default,
1124 MallocSizeOf,
1125 PartialEq,
1126 SpecifiedValueInfo,
1127 ToComputedValue,
1128 ToCss,
1129 ToResolvedValue,
1130 ToShmem,
1131 ToTyped,
1132)]
1133#[repr(transparent)]
1134pub struct FontVariantAlternates(
1136 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1137);
1138
1139impl FontVariantAlternates {
1140 pub fn len(&self) -> usize {
1142 self.0.iter().fold(0, |acc, alternate| match *alternate {
1143 VariantAlternates::Swash(_)
1144 | VariantAlternates::Stylistic(_)
1145 | VariantAlternates::Ornaments(_)
1146 | VariantAlternates::Annotation(_) => acc + 1,
1147 VariantAlternates::Styleset(ref slice)
1148 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1149 _ => acc,
1150 })
1151 }
1152}
1153
1154impl FontVariantAlternates {
1155 #[inline]
1156 pub fn get_initial_specified_value() -> Self {
1158 Default::default()
1159 }
1160}
1161
1162impl Parse for FontVariantAlternates {
1163 fn parse<'i, 't>(
1172 _: &ParserContext,
1173 input: &mut Parser<'i, 't>,
1174 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1175 if input
1176 .try_parse(|input| input.expect_ident_matching("normal"))
1177 .is_ok()
1178 {
1179 return Ok(Default::default());
1180 }
1181
1182 let mut stylistic = None;
1183 let mut historical = None;
1184 let mut styleset = None;
1185 let mut character_variant = None;
1186 let mut swash = None;
1187 let mut ornaments = None;
1188 let mut annotation = None;
1189
1190 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1192 macro_rules! check_if_parsed(
1193 ($input:expr, $flag:path) => (
1194 if parsed_alternates.contains($flag) {
1195 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1196 }
1197 parsed_alternates |= $flag;
1198 )
1199 );
1200 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1201 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1202 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1203 historical = Some(VariantAlternates::HistoricalForms);
1204 Ok(())
1205 },
1206 Token::Function(ref name) => {
1207 let name = name.clone();
1208 input.parse_nested_block(|i| {
1209 match_ignore_ascii_case! { &name,
1210 "swash" => {
1211 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1212 let ident = CustomIdent::parse(i, &[])?;
1213 swash = Some(VariantAlternates::Swash(ident));
1214 Ok(())
1215 },
1216 "stylistic" => {
1217 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1218 let ident = CustomIdent::parse(i, &[])?;
1219 stylistic = Some(VariantAlternates::Stylistic(ident));
1220 Ok(())
1221 },
1222 "ornaments" => {
1223 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1224 let ident = CustomIdent::parse(i, &[])?;
1225 ornaments = Some(VariantAlternates::Ornaments(ident));
1226 Ok(())
1227 },
1228 "annotation" => {
1229 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1230 let ident = CustomIdent::parse(i, &[])?;
1231 annotation = Some(VariantAlternates::Annotation(ident));
1232 Ok(())
1233 },
1234 "styleset" => {
1235 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1236 let idents = i.parse_comma_separated(|i| {
1237 CustomIdent::parse(i, &[])
1238 })?;
1239 styleset = Some(VariantAlternates::Styleset(idents.into()));
1240 Ok(())
1241 },
1242 "character-variant" => {
1243 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1244 let idents = i.parse_comma_separated(|i| {
1245 CustomIdent::parse(i, &[])
1246 })?;
1247 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1248 Ok(())
1249 },
1250 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1251 }
1252 })
1253 },
1254 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1255 }) {}
1256
1257 if parsed_alternates.is_empty() {
1258 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1259 }
1260
1261 let mut alternates = Vec::new();
1263 macro_rules! push_if_some(
1264 ($value:expr) => (
1265 if let Some(v) = $value {
1266 alternates.push(v);
1267 }
1268 )
1269 );
1270 push_if_some!(stylistic);
1271 push_if_some!(historical);
1272 push_if_some!(styleset);
1273 push_if_some!(character_variant);
1274 push_if_some!(swash);
1275 push_if_some!(ornaments);
1276 push_if_some!(annotation);
1277
1278 Ok(FontVariantAlternates(alternates.into()))
1279 }
1280}
1281
1282#[derive(
1283 Clone,
1284 Copy,
1285 Debug,
1286 Eq,
1287 MallocSizeOf,
1288 PartialEq,
1289 Parse,
1290 SpecifiedValueInfo,
1291 ToComputedValue,
1292 ToCss,
1293 ToResolvedValue,
1294 ToShmem,
1295 ToTyped,
1296)]
1297#[css(bitflags(
1298 single = "normal",
1299 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1300 validate_mixed = "Self::validate_mixed_flags",
1301))]
1302#[repr(C)]
1303pub struct FontVariantEastAsian(u16);
1305bitflags! {
1306 impl FontVariantEastAsian: u16 {
1307 const NORMAL = 0;
1309 const JIS78 = 1 << 0;
1311 const JIS83 = 1 << 1;
1313 const JIS90 = 1 << 2;
1315 const JIS04 = 1 << 3;
1317 const SIMPLIFIED = 1 << 4;
1319 const TRADITIONAL = 1 << 5;
1321
1322 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1324
1325 const FULL_WIDTH = 1 << 6;
1327 const PROPORTIONAL_WIDTH = 1 << 7;
1329 const RUBY = 1 << 8;
1331 }
1332}
1333
1334impl FontVariantEastAsian {
1335 pub const COUNT: usize = 9;
1337
1338 fn validate_mixed_flags(&self) -> bool {
1339 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1340 return false;
1342 }
1343 let jis = self.intersection(Self::JIS_GROUP);
1344 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1345 return false;
1346 }
1347 true
1348 }
1349}
1350
1351#[derive(
1352 Clone,
1353 Copy,
1354 Debug,
1355 Eq,
1356 MallocSizeOf,
1357 PartialEq,
1358 Parse,
1359 SpecifiedValueInfo,
1360 ToComputedValue,
1361 ToCss,
1362 ToResolvedValue,
1363 ToShmem,
1364 ToTyped,
1365)]
1366#[css(bitflags(
1367 single = "normal,none",
1368 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1369 validate_mixed = "Self::validate_mixed_flags",
1370))]
1371#[repr(C)]
1372pub struct FontVariantLigatures(u16);
1374bitflags! {
1375 impl FontVariantLigatures: u16 {
1376 const NORMAL = 0;
1378 const NONE = 1;
1380 const COMMON_LIGATURES = 1 << 1;
1382 const NO_COMMON_LIGATURES = 1 << 2;
1384 const DISCRETIONARY_LIGATURES = 1 << 3;
1386 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1388 const HISTORICAL_LIGATURES = 1 << 5;
1390 const NO_HISTORICAL_LIGATURES = 1 << 6;
1392 const CONTEXTUAL = 1 << 7;
1394 const NO_CONTEXTUAL = 1 << 8;
1396 }
1397}
1398
1399impl FontVariantLigatures {
1400 pub const COUNT: usize = 9;
1402
1403 fn validate_mixed_flags(&self) -> bool {
1404 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1406 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1407 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1408 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1409 {
1410 return false;
1411 }
1412 true
1413 }
1414}
1415
1416#[derive(
1418 Clone,
1419 Copy,
1420 Debug,
1421 Eq,
1422 MallocSizeOf,
1423 PartialEq,
1424 Parse,
1425 SpecifiedValueInfo,
1426 ToComputedValue,
1427 ToCss,
1428 ToResolvedValue,
1429 ToShmem,
1430 ToTyped,
1431)]
1432#[css(bitflags(
1433 single = "normal",
1434 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1435 validate_mixed = "Self::validate_mixed_flags",
1436))]
1437#[repr(C)]
1438pub struct FontVariantNumeric(u8);
1439bitflags! {
1440 impl FontVariantNumeric : u8 {
1441 const NORMAL = 0;
1443 const LINING_NUMS = 1 << 0;
1445 const OLDSTYLE_NUMS = 1 << 1;
1447 const PROPORTIONAL_NUMS = 1 << 2;
1449 const TABULAR_NUMS = 1 << 3;
1451 const DIAGONAL_FRACTIONS = 1 << 4;
1453 const STACKED_FRACTIONS = 1 << 5;
1455 const SLASHED_ZERO = 1 << 6;
1457 const ORDINAL = 1 << 7;
1459 }
1460}
1461
1462impl FontVariantNumeric {
1463 pub const COUNT: usize = 8;
1465
1466 fn validate_mixed_flags(&self) -> bool {
1476 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1477 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1478 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1479 {
1480 return false;
1481 }
1482 true
1483 }
1484}
1485
1486pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1488
1489pub use crate::values::computed::font::FontLanguageOverride;
1491
1492impl Parse for FontLanguageOverride {
1493 fn parse<'i, 't>(
1495 _: &ParserContext,
1496 input: &mut Parser<'i, 't>,
1497 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1498 if input
1499 .try_parse(|input| input.expect_ident_matching("normal"))
1500 .is_ok()
1501 {
1502 return Ok(FontLanguageOverride::normal());
1503 }
1504
1505 let string = input.expect_string()?;
1506
1507 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1510 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1511 }
1512
1513 let mut bytes = [b' '; 4];
1514 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1515 *byte = *str_byte;
1516 }
1517
1518 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1519 }
1520}
1521
1522#[repr(u8)]
1524#[derive(
1525 Clone,
1526 Copy,
1527 Debug,
1528 Eq,
1529 Hash,
1530 MallocSizeOf,
1531 Parse,
1532 PartialEq,
1533 SpecifiedValueInfo,
1534 ToComputedValue,
1535 ToCss,
1536 ToResolvedValue,
1537 ToShmem,
1538 ToTyped,
1539)]
1540#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1541pub enum FontSynthesis {
1542 Auto,
1544 None,
1546}
1547
1548#[repr(u8)]
1550#[derive(
1551 Clone,
1552 Copy,
1553 Debug,
1554 Eq,
1555 MallocSizeOf,
1556 Parse,
1557 PartialEq,
1558 SpecifiedValueInfo,
1559 ToComputedValue,
1560 ToCss,
1561 ToResolvedValue,
1562 ToShmem,
1563 ToTyped,
1564)]
1565pub enum FontSynthesisStyle {
1566 Auto,
1568 None,
1570 ObliqueOnly,
1572}
1573
1574#[derive(
1575 Clone,
1576 Debug,
1577 Eq,
1578 MallocSizeOf,
1579 PartialEq,
1580 SpecifiedValueInfo,
1581 ToComputedValue,
1582 ToResolvedValue,
1583 ToShmem,
1584 ToTyped,
1585)]
1586#[repr(C)]
1587pub struct FontPalette(Atom);
1590
1591#[allow(missing_docs)]
1592impl FontPalette {
1593 pub fn normal() -> Self {
1594 Self(atom!("normal"))
1595 }
1596 pub fn light() -> Self {
1597 Self(atom!("light"))
1598 }
1599 pub fn dark() -> Self {
1600 Self(atom!("dark"))
1601 }
1602}
1603
1604impl Parse for FontPalette {
1605 fn parse<'i, 't>(
1607 _context: &ParserContext,
1608 input: &mut Parser<'i, 't>,
1609 ) -> Result<FontPalette, ParseError<'i>> {
1610 let location = input.current_source_location();
1611 let ident = input.expect_ident()?;
1612 match_ignore_ascii_case! { &ident,
1613 "normal" => Ok(Self::normal()),
1614 "light" => Ok(Self::light()),
1615 "dark" => Ok(Self::dark()),
1616 _ => if ident.starts_with("--") {
1617 Ok(Self(Atom::from(ident.as_ref())))
1618 } else {
1619 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1620 },
1621 }
1622 }
1623}
1624
1625impl ToCss for FontPalette {
1626 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1627 where
1628 W: Write,
1629 {
1630 serialize_atom_identifier(&self.0, dest)
1631 }
1632}
1633
1634pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1637
1638fn parse_one_feature_value<'i, 't>(
1639 context: &ParserContext,
1640 input: &mut Parser<'i, 't>,
1641) -> Result<Integer, ParseError<'i>> {
1642 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1643 return Ok(integer);
1644 }
1645
1646 try_match_ident_ignore_ascii_case! { input,
1647 "on" => Ok(Integer::new(1)),
1648 "off" => Ok(Integer::new(0)),
1649 }
1650}
1651
1652impl Parse for FeatureTagValue<Integer> {
1653 fn parse<'i, 't>(
1655 context: &ParserContext,
1656 input: &mut Parser<'i, 't>,
1657 ) -> Result<Self, ParseError<'i>> {
1658 let tag = FontTag::parse(context, input)?;
1659 let value = input
1660 .try_parse(|i| parse_one_feature_value(context, i))
1661 .unwrap_or_else(|_| Integer::new(1));
1662
1663 Ok(Self { tag, value })
1664 }
1665}
1666
1667impl Parse for VariationValue<Number> {
1668 fn parse<'i, 't>(
1671 context: &ParserContext,
1672 input: &mut Parser<'i, 't>,
1673 ) -> Result<Self, ParseError<'i>> {
1674 let tag = FontTag::parse(context, input)?;
1675 let value = Number::parse(context, input)?;
1676 Ok(Self { tag, value })
1677 }
1678}
1679
1680#[derive(
1684 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1685)]
1686pub enum MetricsOverride {
1687 Override(NonNegativePercentage),
1689 Normal,
1691}
1692
1693impl MetricsOverride {
1694 #[inline]
1695 pub fn normal() -> MetricsOverride {
1697 MetricsOverride::Normal
1698 }
1699
1700 #[inline]
1705 pub fn compute(&self) -> ComputedPercentage {
1706 match *self {
1707 MetricsOverride::Normal => ComputedPercentage(-1.0),
1708 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1709 }
1710 }
1711}
1712
1713#[derive(
1714 Clone,
1715 Copy,
1716 Debug,
1717 MallocSizeOf,
1718 Parse,
1719 PartialEq,
1720 SpecifiedValueInfo,
1721 ToComputedValue,
1722 ToCss,
1723 ToResolvedValue,
1724 ToShmem,
1725 ToTyped,
1726)]
1727#[repr(u8)]
1728pub enum XTextScale {
1730 All,
1732 ZoomOnly,
1734 None,
1736}
1737
1738impl XTextScale {
1739 #[inline]
1741 pub fn text_zoom_enabled(self) -> bool {
1742 self != Self::None
1743 }
1744}
1745
1746#[derive(
1747 Clone,
1748 Debug,
1749 MallocSizeOf,
1750 PartialEq,
1751 SpecifiedValueInfo,
1752 ToComputedValue,
1753 ToCss,
1754 ToResolvedValue,
1755 ToShmem,
1756 ToTyped,
1757)]
1758#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1759pub struct XLang(#[css(skip)] pub Atom);
1761
1762impl XLang {
1763 #[inline]
1764 pub fn get_initial_value() -> XLang {
1766 XLang(atom!(""))
1767 }
1768}
1769
1770impl Parse for XLang {
1771 fn parse<'i, 't>(
1772 _: &ParserContext,
1773 input: &mut Parser<'i, 't>,
1774 ) -> Result<XLang, ParseError<'i>> {
1775 debug_assert!(
1776 false,
1777 "Should be set directly by presentation attributes only."
1778 );
1779 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1780 }
1781}
1782
1783#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1784#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1785pub struct MozScriptMinSize(pub NoCalcLength);
1788
1789impl MozScriptMinSize {
1790 #[inline]
1791 pub fn get_initial_value() -> Length {
1793 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1794 }
1795}
1796
1797impl Parse for MozScriptMinSize {
1798 fn parse<'i, 't>(
1799 _: &ParserContext,
1800 input: &mut Parser<'i, 't>,
1801 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1802 debug_assert!(
1803 false,
1804 "Should be set directly by presentation attributes only."
1805 );
1806 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1807 }
1808}
1809
1810#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1813#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1814pub enum MathDepth {
1815 AutoAdd,
1817
1818 #[css(function)]
1820 Add(Integer),
1821
1822 Absolute(Integer),
1824}
1825
1826impl Parse for MathDepth {
1827 fn parse<'i, 't>(
1828 context: &ParserContext,
1829 input: &mut Parser<'i, 't>,
1830 ) -> Result<MathDepth, ParseError<'i>> {
1831 if input
1832 .try_parse(|i| i.expect_ident_matching("auto-add"))
1833 .is_ok()
1834 {
1835 return Ok(MathDepth::AutoAdd);
1836 }
1837 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1838 return Ok(MathDepth::Absolute(math_depth_value));
1839 }
1840 input.expect_function_matching("add")?;
1841 let math_depth_delta_value =
1842 input.parse_nested_block(|input| Integer::parse(context, input))?;
1843 Ok(MathDepth::Add(math_depth_delta_value))
1844 }
1845}
1846
1847#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1848#[derive(
1849 Clone,
1850 Copy,
1851 Debug,
1852 PartialEq,
1853 SpecifiedValueInfo,
1854 ToComputedValue,
1855 ToCss,
1856 ToResolvedValue,
1857 ToShmem,
1858)]
1859pub struct MozScriptSizeMultiplier(pub f32);
1864
1865impl MozScriptSizeMultiplier {
1866 #[inline]
1867 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1869 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1870 }
1871}
1872
1873impl Parse for MozScriptSizeMultiplier {
1874 fn parse<'i, 't>(
1875 _: &ParserContext,
1876 input: &mut Parser<'i, 't>,
1877 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1878 debug_assert!(
1879 false,
1880 "Should be set directly by presentation attributes only."
1881 );
1882 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1883 }
1884}
1885
1886impl From<f32> for MozScriptSizeMultiplier {
1887 fn from(v: f32) -> Self {
1888 MozScriptSizeMultiplier(v)
1889 }
1890}
1891
1892impl From<MozScriptSizeMultiplier> for f32 {
1893 fn from(v: MozScriptSizeMultiplier) -> f32 {
1894 v.0
1895 }
1896}
1897
1898pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1900
1901impl ToComputedValue for LineHeight {
1902 type ComputedValue = computed::LineHeight;
1903
1904 #[inline]
1905 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1906 match *self {
1907 GenericLineHeight::Normal => GenericLineHeight::Normal,
1908 #[cfg(feature = "gecko")]
1909 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1910 GenericLineHeight::Number(number) => {
1911 GenericLineHeight::Number(number.to_computed_value(context))
1912 },
1913 GenericLineHeight::Length(ref non_negative_lp) => {
1914 let result = match non_negative_lp.0 {
1915 LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1916 context.maybe_zoom_text(abs.to_computed_value(context))
1917 },
1918 LengthPercentage::Length(ref length) => {
1919 length.to_computed_value_with_base_size(
1924 context,
1925 FontBaseSize::CurrentStyle,
1926 LineHeightBase::InheritedStyle,
1927 )
1928 },
1929 LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1930 .to_computed_value(
1931 context,
1932 FontBaseSize::CurrentStyle,
1933 LineHeightBase::InheritedStyle,
1934 ),
1935 LengthPercentage::Calc(ref calc) => {
1936 let computed_calc = calc.to_computed_value_zoomed(
1937 context,
1938 FontBaseSize::CurrentStyle,
1939 LineHeightBase::InheritedStyle,
1940 );
1941 let base = context.style().get_font().clone_font_size().computed_size();
1942 computed_calc.resolve(base)
1943 },
1944 };
1945 GenericLineHeight::Length(result.into())
1946 },
1947 }
1948 }
1949
1950 #[inline]
1951 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1952 match *computed {
1953 GenericLineHeight::Normal => GenericLineHeight::Normal,
1954 #[cfg(feature = "gecko")]
1955 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1956 GenericLineHeight::Number(ref number) => {
1957 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1958 },
1959 GenericLineHeight::Length(ref length) => {
1960 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1961 },
1962 }
1963 }
1964}
1965
1966#[repr(C)]
1968pub struct QueryFontMetricsFlags(u8);
1969
1970bitflags! {
1971 impl QueryFontMetricsFlags: u8 {
1972 const USE_USER_FONT_SET = 1 << 0;
1974 const NEEDS_CH = 1 << 1;
1976 const NEEDS_IC = 1 << 2;
1978 const NEEDS_MATH_SCALES = 1 << 3;
1980 }
1981}