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,
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,
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,
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) + context.maybe_zoom_text(self.offset)
599 }
600
601 fn compose(self, factor: f32) -> Self {
604 if self.kw == FontSizeKeyword::None {
605 return self;
606 }
607 KeywordInfo {
608 kw: self.kw,
609 factor: self.factor * factor,
610 offset: self.offset * factor,
611 }
612 }
613}
614
615impl SpecifiedValueInfo for KeywordInfo {
616 fn collect_completion_keywords(f: KeywordsCollectFn) {
617 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
618 }
619}
620
621#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
622pub enum FontSize {
624 Length(LengthPercentage),
626 Keyword(KeywordInfo),
637 Smaller,
639 Larger,
641 #[css(skip)]
643 System(SystemFont),
644}
645
646#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
648#[cfg_attr(feature = "servo", derive(Hash))]
649pub enum FontFamily {
650 #[css(comma)]
652 Values(#[css(iterable)] FontFamilyList),
653 #[css(skip)]
655 System(SystemFont),
656}
657
658impl FontFamily {
659 system_font_methods!(FontFamily, font_family);
660}
661
662impl ToComputedValue for FontFamily {
663 type ComputedValue = computed::FontFamily;
664
665 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
666 match *self {
667 FontFamily::Values(ref list) => computed::FontFamily {
668 families: list.clone(),
669 is_system_font: false,
670 is_initial: false,
671 },
672 FontFamily::System(_) => self.compute_system(context),
673 }
674 }
675
676 fn from_computed_value(other: &computed::FontFamily) -> Self {
677 FontFamily::Values(other.families.clone())
678 }
679}
680
681#[cfg(feature = "gecko")]
682impl MallocSizeOf for FontFamily {
683 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
684 match *self {
685 FontFamily::Values(ref v) => {
686 v.list.unconditional_size_of(ops)
689 },
690 FontFamily::System(_) => 0,
691 }
692 }
693}
694
695impl Parse for FontFamily {
696 fn parse<'i, 't>(
700 context: &ParserContext,
701 input: &mut Parser<'i, 't>,
702 ) -> Result<FontFamily, ParseError<'i>> {
703 let values =
704 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
705 Ok(FontFamily::Values(FontFamilyList {
706 list: crate::ArcSlice::from_iter(values.into_iter()),
707 }))
708 }
709}
710
711impl SpecifiedValueInfo for FontFamily {}
712
713impl Parse for FamilyName {
716 fn parse<'i, 't>(
717 context: &ParserContext,
718 input: &mut Parser<'i, 't>,
719 ) -> Result<Self, ParseError<'i>> {
720 match SingleFontFamily::parse(context, input) {
721 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
722 Ok(SingleFontFamily::Generic(_)) => {
723 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
724 },
725 Err(e) => Err(e),
726 }
727 }
728}
729
730#[derive(
733 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
734)]
735pub enum FontSizeAdjustFactor {
736 Number(NonNegativeNumber),
738 FromFont,
740}
741
742pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
747
748impl Parse for FontSizeAdjust {
749 fn parse<'i, 't>(
750 context: &ParserContext,
751 input: &mut Parser<'i, 't>,
752 ) -> Result<Self, ParseError<'i>> {
753 let location = input.current_source_location();
754 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
756 return Ok(Self::ExHeight(factor));
757 }
758
759 let ident = input.expect_ident()?;
760 let basis = match_ignore_ascii_case! { &ident,
761 "none" => return Ok(Self::None),
762 "ex-height" => Self::ExHeight,
764 "cap-height" => Self::CapHeight,
765 "ch-width" => Self::ChWidth,
766 "ic-width" => Self::IcWidth,
767 "ic-height" => Self::IcHeight,
768 _ => return Err(location.new_custom_error(
770 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
771 )),
772 };
773
774 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
775 }
776}
777
778const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
781
782pub const FONT_MEDIUM_PX: f32 = 16.0;
784pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
786
787impl FontSizeKeyword {
788 #[inline]
789 fn to_length(&self, cx: &Context) -> NonNegativeLength {
790 let font = cx.style().get_font();
791
792 #[cfg(feature = "servo")]
793 let family = &font.font_family.families;
794 #[cfg(feature = "gecko")]
795 let family = &font.mFont.family.families;
796
797 let generic = family
798 .single_generic()
799 .unwrap_or(computed::GenericFontFamily::None);
800
801 #[cfg(feature = "gecko")]
802 let base_size = unsafe {
803 Atom::with(font.mLanguage.mRawPtr, |language| {
804 cx.device().base_size_for_generic(language, generic)
805 })
806 };
807 #[cfg(feature = "servo")]
808 let base_size = cx.device().base_size_for_generic(generic);
809
810 self.to_length_without_context(cx.quirks_mode, base_size)
811 }
812
813 #[inline]
815 pub fn to_length_without_context(
816 &self,
817 quirks_mode: QuirksMode,
818 base_size: Length,
819 ) -> NonNegativeLength {
820 #[cfg(feature = "gecko")]
821 debug_assert_ne!(*self, FontSizeKeyword::Math);
822 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
835 [9, 9, 9, 9, 11, 14, 18, 27],
836 [9, 9, 9, 10, 12, 15, 20, 30],
837 [9, 9, 10, 11, 13, 17, 22, 33],
838 [9, 9, 10, 12, 14, 18, 24, 36],
839 [9, 10, 12, 13, 16, 20, 26, 39],
840 [9, 10, 12, 14, 17, 21, 28, 42],
841 [9, 10, 13, 15, 18, 23, 30, 45],
842 [9, 10, 13, 16, 18, 24, 32, 48],
843 ];
844
845 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
854 [9, 9, 9, 9, 11, 14, 18, 28],
855 [9, 9, 9, 10, 12, 15, 20, 31],
856 [9, 9, 9, 11, 13, 17, 22, 34],
857 [9, 9, 10, 12, 14, 18, 24, 37],
858 [9, 9, 10, 13, 16, 20, 26, 40],
859 [9, 9, 11, 14, 17, 21, 28, 42],
860 [9, 10, 12, 15, 17, 23, 30, 45],
861 [9, 10, 13, 16, 18, 24, 32, 48],
862 ];
863
864 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
865 let base_size_px = base_size.px().round() as i32;
866 let html_size = self.html_size() as usize;
867 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
868 let mapping = if quirks_mode == QuirksMode::Quirks {
869 QUIRKS_FONT_SIZE_MAPPING
870 } else {
871 FONT_SIZE_MAPPING
872 };
873 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
874 } else {
875 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
876 })
877 }
878}
879
880impl FontSize {
881 pub fn from_html_size(size: u8) -> Self {
883 FontSize::Keyword(KeywordInfo::new(match size {
884 0 | 1 => FontSizeKeyword::XSmall,
886 2 => FontSizeKeyword::Small,
887 3 => FontSizeKeyword::Medium,
888 4 => FontSizeKeyword::Large,
889 5 => FontSizeKeyword::XLarge,
890 6 => FontSizeKeyword::XXLarge,
891 _ => FontSizeKeyword::XXXLarge,
893 }))
894 }
895
896 pub fn to_computed_value_against(
898 &self,
899 context: &Context,
900 base_size: FontBaseSize,
901 line_height_base: LineHeightBase,
902 ) -> computed::FontSize {
903 let compose_keyword = |factor| {
904 context
905 .style()
906 .get_parent_font()
907 .clone_font_size()
908 .keyword_info
909 .compose(factor)
910 };
911 let mut info = KeywordInfo::none();
912 let size = match *self {
913 FontSize::Length(LengthPercentage::Length(ref l)) => {
914 if let NoCalcLength::FontRelative(ref value) = *l {
915 if let FontRelativeLength::Em(em) = *value {
916 info = compose_keyword(em);
919 }
920 }
921 let result =
922 l.to_computed_value_with_base_size(context, base_size, line_height_base);
923 if l.should_zoom_text() {
924 context.maybe_zoom_text(result)
925 } else {
926 result
927 }
928 },
929 FontSize::Length(LengthPercentage::Percentage(pc)) => {
930 info = compose_keyword(pc.0);
933 (base_size.resolve(context).computed_size() * pc.0).normalized()
934 },
935 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
936 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
937 calc.resolve(base_size.resolve(context).computed_size())
938 },
939 FontSize::Keyword(i) => {
940 if i.kw.is_math() {
941 info = compose_keyword(1.);
943 info.kw = i.kw;
946 FontRelativeLength::Em(1.).to_computed_value(
947 context,
948 base_size,
949 line_height_base,
950 )
951 } else {
952 info = i;
954 i.to_computed_value(context).clamp_to_non_negative()
955 }
956 },
957 FontSize::Smaller => {
958 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
959 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(
960 context,
961 base_size,
962 line_height_base,
963 )
964 },
965 FontSize::Larger => {
966 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
967 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(
968 context,
969 base_size,
970 line_height_base,
971 )
972 },
973
974 FontSize::System(_) => {
975 #[cfg(feature = "servo")]
976 {
977 unreachable!()
978 }
979 #[cfg(feature = "gecko")]
980 {
981 context
982 .cached_system_font
983 .as_ref()
984 .unwrap()
985 .font_size
986 .computed_size()
987 }
988 },
989 };
990 computed::FontSize {
991 computed_size: NonNegative(size),
992 used_size: NonNegative(size),
993 keyword_info: info,
994 }
995 }
996}
997
998impl ToComputedValue for FontSize {
999 type ComputedValue = computed::FontSize;
1000
1001 #[inline]
1002 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1003 self.to_computed_value_against(
1004 context,
1005 FontBaseSize::InheritedStyle,
1006 LineHeightBase::InheritedStyle,
1007 )
1008 }
1009
1010 #[inline]
1011 fn from_computed_value(computed: &computed::FontSize) -> Self {
1012 FontSize::Length(LengthPercentage::Length(
1013 ToComputedValue::from_computed_value(&computed.computed_size()),
1014 ))
1015 }
1016}
1017
1018impl FontSize {
1019 system_font_methods!(FontSize);
1020
1021 #[inline]
1023 pub fn medium() -> Self {
1024 FontSize::Keyword(KeywordInfo::medium())
1025 }
1026
1027 pub fn parse_quirky<'i, 't>(
1029 context: &ParserContext,
1030 input: &mut Parser<'i, 't>,
1031 allow_quirks: AllowQuirks,
1032 ) -> Result<FontSize, ParseError<'i>> {
1033 if let Ok(lp) = input
1034 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1035 {
1036 return Ok(FontSize::Length(lp));
1037 }
1038
1039 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1040 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1041 }
1042
1043 try_match_ident_ignore_ascii_case! { input,
1044 "smaller" => Ok(FontSize::Smaller),
1045 "larger" => Ok(FontSize::Larger),
1046 }
1047 }
1048}
1049
1050impl Parse for FontSize {
1051 fn parse<'i, 't>(
1053 context: &ParserContext,
1054 input: &mut Parser<'i, 't>,
1055 ) -> Result<FontSize, ParseError<'i>> {
1056 FontSize::parse_quirky(context, input, AllowQuirks::No)
1057 }
1058}
1059
1060bitflags! {
1061 #[derive(Clone, Copy)]
1062 struct VariantAlternatesParsingFlags: u8 {
1064 const NORMAL = 0;
1066 const HISTORICAL_FORMS = 0x01;
1068 const STYLISTIC = 0x02;
1070 const STYLESET = 0x04;
1072 const CHARACTER_VARIANT = 0x08;
1074 const SWASH = 0x10;
1076 const ORNAMENTS = 0x20;
1078 const ANNOTATION = 0x40;
1080 }
1081}
1082
1083#[derive(
1084 Clone,
1085 Debug,
1086 MallocSizeOf,
1087 PartialEq,
1088 SpecifiedValueInfo,
1089 ToCss,
1090 ToComputedValue,
1091 ToResolvedValue,
1092 ToShmem,
1093)]
1094#[repr(C, u8)]
1095pub enum VariantAlternates {
1097 #[css(function)]
1099 Stylistic(CustomIdent),
1100 #[css(comma, function)]
1102 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1103 #[css(comma, function)]
1105 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1106 #[css(function)]
1108 Swash(CustomIdent),
1109 #[css(function)]
1111 Ornaments(CustomIdent),
1112 #[css(function)]
1114 Annotation(CustomIdent),
1115 HistoricalForms,
1117}
1118
1119#[derive(
1120 Clone,
1121 Debug,
1122 Default,
1123 MallocSizeOf,
1124 PartialEq,
1125 SpecifiedValueInfo,
1126 ToComputedValue,
1127 ToCss,
1128 ToResolvedValue,
1129 ToShmem,
1130)]
1131#[repr(transparent)]
1132pub struct FontVariantAlternates(
1134 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1135);
1136
1137impl FontVariantAlternates {
1138 pub fn len(&self) -> usize {
1140 self.0.iter().fold(0, |acc, alternate| match *alternate {
1141 VariantAlternates::Swash(_) |
1142 VariantAlternates::Stylistic(_) |
1143 VariantAlternates::Ornaments(_) |
1144 VariantAlternates::Annotation(_) => acc + 1,
1145 VariantAlternates::Styleset(ref slice) |
1146 VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1147 _ => acc,
1148 })
1149 }
1150}
1151
1152impl FontVariantAlternates {
1153 #[inline]
1154 pub fn get_initial_specified_value() -> Self {
1156 Default::default()
1157 }
1158}
1159
1160impl Parse for FontVariantAlternates {
1161 fn parse<'i, 't>(
1170 _: &ParserContext,
1171 input: &mut Parser<'i, 't>,
1172 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1173 if input
1174 .try_parse(|input| input.expect_ident_matching("normal"))
1175 .is_ok()
1176 {
1177 return Ok(Default::default());
1178 }
1179
1180 let mut stylistic = None;
1181 let mut historical = None;
1182 let mut styleset = None;
1183 let mut character_variant = None;
1184 let mut swash = None;
1185 let mut ornaments = None;
1186 let mut annotation = None;
1187
1188 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1190 macro_rules! check_if_parsed(
1191 ($input:expr, $flag:path) => (
1192 if parsed_alternates.contains($flag) {
1193 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1194 }
1195 parsed_alternates |= $flag;
1196 )
1197 );
1198 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1199 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1200 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1201 historical = Some(VariantAlternates::HistoricalForms);
1202 Ok(())
1203 },
1204 Token::Function(ref name) => {
1205 let name = name.clone();
1206 input.parse_nested_block(|i| {
1207 match_ignore_ascii_case! { &name,
1208 "swash" => {
1209 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1210 let ident = CustomIdent::parse(i, &[])?;
1211 swash = Some(VariantAlternates::Swash(ident));
1212 Ok(())
1213 },
1214 "stylistic" => {
1215 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1216 let ident = CustomIdent::parse(i, &[])?;
1217 stylistic = Some(VariantAlternates::Stylistic(ident));
1218 Ok(())
1219 },
1220 "ornaments" => {
1221 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1222 let ident = CustomIdent::parse(i, &[])?;
1223 ornaments = Some(VariantAlternates::Ornaments(ident));
1224 Ok(())
1225 },
1226 "annotation" => {
1227 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1228 let ident = CustomIdent::parse(i, &[])?;
1229 annotation = Some(VariantAlternates::Annotation(ident));
1230 Ok(())
1231 },
1232 "styleset" => {
1233 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1234 let idents = i.parse_comma_separated(|i| {
1235 CustomIdent::parse(i, &[])
1236 })?;
1237 styleset = Some(VariantAlternates::Styleset(idents.into()));
1238 Ok(())
1239 },
1240 "character-variant" => {
1241 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1242 let idents = i.parse_comma_separated(|i| {
1243 CustomIdent::parse(i, &[])
1244 })?;
1245 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1246 Ok(())
1247 },
1248 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1249 }
1250 })
1251 },
1252 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1253 }) {}
1254
1255 if parsed_alternates.is_empty() {
1256 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1257 }
1258
1259 let mut alternates = Vec::new();
1261 macro_rules! push_if_some(
1262 ($value:expr) => (
1263 if let Some(v) = $value {
1264 alternates.push(v);
1265 }
1266 )
1267 );
1268 push_if_some!(stylistic);
1269 push_if_some!(historical);
1270 push_if_some!(styleset);
1271 push_if_some!(character_variant);
1272 push_if_some!(swash);
1273 push_if_some!(ornaments);
1274 push_if_some!(annotation);
1275
1276 Ok(FontVariantAlternates(alternates.into()))
1277 }
1278}
1279
1280#[derive(
1281 Clone,
1282 Copy,
1283 Debug,
1284 Eq,
1285 MallocSizeOf,
1286 PartialEq,
1287 Parse,
1288 SpecifiedValueInfo,
1289 ToComputedValue,
1290 ToCss,
1291 ToResolvedValue,
1292 ToShmem,
1293)]
1294#[css(bitflags(
1295 single = "normal",
1296 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1297 validate_mixed = "Self::validate_mixed_flags",
1298))]
1299#[repr(C)]
1300pub struct FontVariantEastAsian(u16);
1302bitflags! {
1303 impl FontVariantEastAsian: u16 {
1304 const NORMAL = 0;
1306 const JIS78 = 1 << 0;
1308 const JIS83 = 1 << 1;
1310 const JIS90 = 1 << 2;
1312 const JIS04 = 1 << 3;
1314 const SIMPLIFIED = 1 << 4;
1316 const TRADITIONAL = 1 << 5;
1318
1319 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1321
1322 const FULL_WIDTH = 1 << 6;
1324 const PROPORTIONAL_WIDTH = 1 << 7;
1326 const RUBY = 1 << 8;
1328 }
1329}
1330
1331impl FontVariantEastAsian {
1332 pub const COUNT: usize = 9;
1334
1335 fn validate_mixed_flags(&self) -> bool {
1336 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1337 return false;
1339 }
1340 let jis = self.intersection(Self::JIS_GROUP);
1341 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1342 return false;
1343 }
1344 true
1345 }
1346}
1347
1348#[derive(
1349 Clone,
1350 Copy,
1351 Debug,
1352 Eq,
1353 MallocSizeOf,
1354 PartialEq,
1355 Parse,
1356 SpecifiedValueInfo,
1357 ToComputedValue,
1358 ToCss,
1359 ToResolvedValue,
1360 ToShmem,
1361)]
1362#[css(bitflags(
1363 single = "normal,none",
1364 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1365 validate_mixed = "Self::validate_mixed_flags",
1366))]
1367#[repr(C)]
1368pub struct FontVariantLigatures(u16);
1370bitflags! {
1371 impl FontVariantLigatures: u16 {
1372 const NORMAL = 0;
1374 const NONE = 1;
1376 const COMMON_LIGATURES = 1 << 1;
1378 const NO_COMMON_LIGATURES = 1 << 2;
1380 const DISCRETIONARY_LIGATURES = 1 << 3;
1382 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1384 const HISTORICAL_LIGATURES = 1 << 5;
1386 const NO_HISTORICAL_LIGATURES = 1 << 6;
1388 const CONTEXTUAL = 1 << 7;
1390 const NO_CONTEXTUAL = 1 << 8;
1392 }
1393}
1394
1395impl FontVariantLigatures {
1396 pub const COUNT: usize = 9;
1398
1399 fn validate_mixed_flags(&self) -> bool {
1400 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES) ||
1402 self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES) ||
1403 self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES) ||
1404 self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1405 {
1406 return false;
1407 }
1408 true
1409 }
1410}
1411
1412#[derive(
1414 Clone,
1415 Copy,
1416 Debug,
1417 Eq,
1418 MallocSizeOf,
1419 PartialEq,
1420 Parse,
1421 SpecifiedValueInfo,
1422 ToComputedValue,
1423 ToCss,
1424 ToResolvedValue,
1425 ToShmem,
1426)]
1427#[css(bitflags(
1428 single = "normal",
1429 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1430 validate_mixed = "Self::validate_mixed_flags",
1431))]
1432#[repr(C)]
1433pub struct FontVariantNumeric(u8);
1434bitflags! {
1435 impl FontVariantNumeric : u8 {
1436 const NORMAL = 0;
1438 const LINING_NUMS = 1 << 0;
1440 const OLDSTYLE_NUMS = 1 << 1;
1442 const PROPORTIONAL_NUMS = 1 << 2;
1444 const TABULAR_NUMS = 1 << 3;
1446 const DIAGONAL_FRACTIONS = 1 << 4;
1448 const STACKED_FRACTIONS = 1 << 5;
1450 const SLASHED_ZERO = 1 << 6;
1452 const ORDINAL = 1 << 7;
1454 }
1455}
1456
1457impl FontVariantNumeric {
1458 pub const COUNT: usize = 8;
1460
1461 fn validate_mixed_flags(&self) -> bool {
1471 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS) ||
1472 self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS) ||
1473 self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1474 {
1475 return false;
1476 }
1477 true
1478 }
1479}
1480
1481pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1483
1484pub use crate::values::computed::font::FontLanguageOverride;
1486
1487impl Parse for FontLanguageOverride {
1488 fn parse<'i, 't>(
1490 _: &ParserContext,
1491 input: &mut Parser<'i, 't>,
1492 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1493 if input
1494 .try_parse(|input| input.expect_ident_matching("normal"))
1495 .is_ok()
1496 {
1497 return Ok(FontLanguageOverride::normal());
1498 }
1499
1500 let string = input.expect_string()?;
1501
1502 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1505 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1506 }
1507
1508 let mut bytes = [b' '; 4];
1509 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1510 *byte = *str_byte;
1511 }
1512
1513 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1514 }
1515}
1516
1517#[repr(u8)]
1519#[derive(
1520 Clone,
1521 Copy,
1522 Debug,
1523 Eq,
1524 MallocSizeOf,
1525 Parse,
1526 PartialEq,
1527 SpecifiedValueInfo,
1528 ToComputedValue,
1529 ToCss,
1530 ToResolvedValue,
1531 ToShmem,
1532)]
1533pub enum FontSynthesis {
1534 Auto,
1536 None,
1538}
1539
1540#[repr(u8)]
1542#[derive(
1543 Clone,
1544 Copy,
1545 Debug,
1546 Eq,
1547 MallocSizeOf,
1548 Parse,
1549 PartialEq,
1550 SpecifiedValueInfo,
1551 ToComputedValue,
1552 ToCss,
1553 ToResolvedValue,
1554 ToShmem,
1555)]
1556pub enum FontSynthesisStyle {
1557 Auto,
1559 None,
1561 ObliqueOnly,
1563}
1564
1565#[derive(
1566 Clone,
1567 Debug,
1568 Eq,
1569 MallocSizeOf,
1570 PartialEq,
1571 SpecifiedValueInfo,
1572 ToComputedValue,
1573 ToResolvedValue,
1574 ToShmem,
1575)]
1576#[repr(C)]
1577pub struct FontPalette(Atom);
1580
1581#[allow(missing_docs)]
1582impl FontPalette {
1583 pub fn normal() -> Self {
1584 Self(atom!("normal"))
1585 }
1586 pub fn light() -> Self {
1587 Self(atom!("light"))
1588 }
1589 pub fn dark() -> Self {
1590 Self(atom!("dark"))
1591 }
1592}
1593
1594impl Parse for FontPalette {
1595 fn parse<'i, 't>(
1597 _context: &ParserContext,
1598 input: &mut Parser<'i, 't>,
1599 ) -> Result<FontPalette, ParseError<'i>> {
1600 let location = input.current_source_location();
1601 let ident = input.expect_ident()?;
1602 match_ignore_ascii_case! { &ident,
1603 "normal" => Ok(Self::normal()),
1604 "light" => Ok(Self::light()),
1605 "dark" => Ok(Self::dark()),
1606 _ => if ident.starts_with("--") {
1607 Ok(Self(Atom::from(ident.as_ref())))
1608 } else {
1609 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1610 },
1611 }
1612 }
1613}
1614
1615impl ToCss for FontPalette {
1616 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1617 where
1618 W: Write,
1619 {
1620 serialize_atom_identifier(&self.0, dest)
1621 }
1622}
1623
1624pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1627
1628fn parse_one_feature_value<'i, 't>(
1629 context: &ParserContext,
1630 input: &mut Parser<'i, 't>,
1631) -> Result<Integer, ParseError<'i>> {
1632 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1633 return Ok(integer);
1634 }
1635
1636 try_match_ident_ignore_ascii_case! { input,
1637 "on" => Ok(Integer::new(1)),
1638 "off" => Ok(Integer::new(0)),
1639 }
1640}
1641
1642impl Parse for FeatureTagValue<Integer> {
1643 fn parse<'i, 't>(
1645 context: &ParserContext,
1646 input: &mut Parser<'i, 't>,
1647 ) -> Result<Self, ParseError<'i>> {
1648 let tag = FontTag::parse(context, input)?;
1649 let value = input
1650 .try_parse(|i| parse_one_feature_value(context, i))
1651 .unwrap_or_else(|_| Integer::new(1));
1652
1653 Ok(Self { tag, value })
1654 }
1655}
1656
1657impl Parse for VariationValue<Number> {
1658 fn parse<'i, 't>(
1661 context: &ParserContext,
1662 input: &mut Parser<'i, 't>,
1663 ) -> Result<Self, ParseError<'i>> {
1664 let tag = FontTag::parse(context, input)?;
1665 let value = Number::parse(context, input)?;
1666 Ok(Self { tag, value })
1667 }
1668}
1669
1670#[derive(
1674 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1675)]
1676pub enum MetricsOverride {
1677 Override(NonNegativePercentage),
1679 Normal,
1681}
1682
1683impl MetricsOverride {
1684 #[inline]
1685 pub fn normal() -> MetricsOverride {
1687 MetricsOverride::Normal
1688 }
1689
1690 #[inline]
1695 pub fn compute(&self) -> ComputedPercentage {
1696 match *self {
1697 MetricsOverride::Normal => ComputedPercentage(-1.0),
1698 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1699 }
1700 }
1701}
1702
1703#[derive(
1704 Clone,
1705 Copy,
1706 Debug,
1707 MallocSizeOf,
1708 Parse,
1709 PartialEq,
1710 SpecifiedValueInfo,
1711 ToComputedValue,
1712 ToCss,
1713 ToResolvedValue,
1714 ToShmem,
1715)]
1716#[repr(u8)]
1717pub enum XTextScale {
1719 All,
1721 ZoomOnly,
1723 None,
1725}
1726
1727impl XTextScale {
1728 #[inline]
1730 pub fn text_zoom_enabled(self) -> bool {
1731 self != Self::None
1732 }
1733}
1734
1735#[derive(
1736 Clone,
1737 Debug,
1738 MallocSizeOf,
1739 PartialEq,
1740 SpecifiedValueInfo,
1741 ToComputedValue,
1742 ToCss,
1743 ToResolvedValue,
1744 ToShmem,
1745)]
1746#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1747pub struct XLang(#[css(skip)] pub Atom);
1749
1750impl XLang {
1751 #[inline]
1752 pub fn get_initial_value() -> XLang {
1754 XLang(atom!(""))
1755 }
1756}
1757
1758impl Parse for XLang {
1759 fn parse<'i, 't>(
1760 _: &ParserContext,
1761 input: &mut Parser<'i, 't>,
1762 ) -> Result<XLang, ParseError<'i>> {
1763 debug_assert!(
1764 false,
1765 "Should be set directly by presentation attributes only."
1766 );
1767 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1768 }
1769}
1770
1771#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1772#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1773pub struct MozScriptMinSize(pub NoCalcLength);
1776
1777impl MozScriptMinSize {
1778 #[inline]
1779 pub fn get_initial_value() -> Length {
1781 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1782 }
1783}
1784
1785impl Parse for MozScriptMinSize {
1786 fn parse<'i, 't>(
1787 _: &ParserContext,
1788 input: &mut Parser<'i, 't>,
1789 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1790 debug_assert!(
1791 false,
1792 "Should be set directly by presentation attributes only."
1793 );
1794 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1795 }
1796}
1797
1798#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1801#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1802pub enum MathDepth {
1803 AutoAdd,
1805
1806 #[css(function)]
1808 Add(Integer),
1809
1810 Absolute(Integer),
1812}
1813
1814impl Parse for MathDepth {
1815 fn parse<'i, 't>(
1816 context: &ParserContext,
1817 input: &mut Parser<'i, 't>,
1818 ) -> Result<MathDepth, ParseError<'i>> {
1819 if input
1820 .try_parse(|i| i.expect_ident_matching("auto-add"))
1821 .is_ok()
1822 {
1823 return Ok(MathDepth::AutoAdd);
1824 }
1825 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1826 return Ok(MathDepth::Absolute(math_depth_value));
1827 }
1828 input.expect_function_matching("add")?;
1829 let math_depth_delta_value =
1830 input.parse_nested_block(|input| Integer::parse(context, input))?;
1831 Ok(MathDepth::Add(math_depth_delta_value))
1832 }
1833}
1834
1835#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1836#[derive(
1837 Clone,
1838 Copy,
1839 Debug,
1840 PartialEq,
1841 SpecifiedValueInfo,
1842 ToComputedValue,
1843 ToCss,
1844 ToResolvedValue,
1845 ToShmem,
1846)]
1847pub struct MozScriptSizeMultiplier(pub f32);
1852
1853impl MozScriptSizeMultiplier {
1854 #[inline]
1855 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1857 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1858 }
1859}
1860
1861impl Parse for MozScriptSizeMultiplier {
1862 fn parse<'i, 't>(
1863 _: &ParserContext,
1864 input: &mut Parser<'i, 't>,
1865 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1866 debug_assert!(
1867 false,
1868 "Should be set directly by presentation attributes only."
1869 );
1870 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1871 }
1872}
1873
1874impl From<f32> for MozScriptSizeMultiplier {
1875 fn from(v: f32) -> Self {
1876 MozScriptSizeMultiplier(v)
1877 }
1878}
1879
1880impl From<MozScriptSizeMultiplier> for f32 {
1881 fn from(v: MozScriptSizeMultiplier) -> f32 {
1882 v.0
1883 }
1884}
1885
1886pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1888
1889impl ToComputedValue for LineHeight {
1890 type ComputedValue = computed::LineHeight;
1891
1892 #[inline]
1893 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1894 match *self {
1895 GenericLineHeight::Normal => GenericLineHeight::Normal,
1896 #[cfg(feature = "gecko")]
1897 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1898 GenericLineHeight::Number(number) => {
1899 GenericLineHeight::Number(number.to_computed_value(context))
1900 },
1901 GenericLineHeight::Length(ref non_negative_lp) => {
1902 let result = match non_negative_lp.0 {
1903 LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1904 context.maybe_zoom_text(abs.to_computed_value(context))
1905 },
1906 LengthPercentage::Length(ref length) => {
1907 length.to_computed_value_with_base_size(
1912 context,
1913 FontBaseSize::CurrentStyle,
1914 LineHeightBase::InheritedStyle,
1915 )
1916 },
1917 LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1918 .to_computed_value(
1919 context,
1920 FontBaseSize::CurrentStyle,
1921 LineHeightBase::InheritedStyle,
1922 ),
1923 LengthPercentage::Calc(ref calc) => {
1924 let computed_calc = calc.to_computed_value_zoomed(
1925 context,
1926 FontBaseSize::CurrentStyle,
1927 LineHeightBase::InheritedStyle,
1928 );
1929 let base = context.style().get_font().clone_font_size().computed_size();
1930 computed_calc.resolve(base)
1931 },
1932 };
1933 GenericLineHeight::Length(result.into())
1934 },
1935 }
1936 }
1937
1938 #[inline]
1939 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1940 match *computed {
1941 GenericLineHeight::Normal => GenericLineHeight::Normal,
1942 #[cfg(feature = "gecko")]
1943 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1944 GenericLineHeight::Number(ref number) => {
1945 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1946 },
1947 GenericLineHeight::Length(ref length) => {
1948 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1949 },
1950 }
1951 }
1952}
1953
1954#[repr(C)]
1956pub struct QueryFontMetricsFlags(u8);
1957
1958bitflags! {
1959 impl QueryFontMetricsFlags: u8 {
1960 const USE_USER_FONT_SET = 1 << 0;
1962 const NEEDS_CH = 1 << 1;
1964 const NEEDS_IC = 1 << 2;
1966 const NEEDS_MATH_SCALES = 1 << 3;
1968 }
1969}