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)
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)]
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)]
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)]
1132#[repr(transparent)]
1133pub struct FontVariantAlternates(
1135 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1136);
1137
1138impl FontVariantAlternates {
1139 pub fn len(&self) -> usize {
1141 self.0.iter().fold(0, |acc, alternate| match *alternate {
1142 VariantAlternates::Swash(_)
1143 | VariantAlternates::Stylistic(_)
1144 | VariantAlternates::Ornaments(_)
1145 | VariantAlternates::Annotation(_) => acc + 1,
1146 VariantAlternates::Styleset(ref slice)
1147 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1148 _ => acc,
1149 })
1150 }
1151}
1152
1153impl FontVariantAlternates {
1154 #[inline]
1155 pub fn get_initial_specified_value() -> Self {
1157 Default::default()
1158 }
1159}
1160
1161impl Parse for FontVariantAlternates {
1162 fn parse<'i, 't>(
1171 _: &ParserContext,
1172 input: &mut Parser<'i, 't>,
1173 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1174 if input
1175 .try_parse(|input| input.expect_ident_matching("normal"))
1176 .is_ok()
1177 {
1178 return Ok(Default::default());
1179 }
1180
1181 let mut stylistic = None;
1182 let mut historical = None;
1183 let mut styleset = None;
1184 let mut character_variant = None;
1185 let mut swash = None;
1186 let mut ornaments = None;
1187 let mut annotation = None;
1188
1189 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1191 macro_rules! check_if_parsed(
1192 ($input:expr, $flag:path) => (
1193 if parsed_alternates.contains($flag) {
1194 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1195 }
1196 parsed_alternates |= $flag;
1197 )
1198 );
1199 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1200 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1201 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1202 historical = Some(VariantAlternates::HistoricalForms);
1203 Ok(())
1204 },
1205 Token::Function(ref name) => {
1206 let name = name.clone();
1207 input.parse_nested_block(|i| {
1208 match_ignore_ascii_case! { &name,
1209 "swash" => {
1210 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1211 let ident = CustomIdent::parse(i, &[])?;
1212 swash = Some(VariantAlternates::Swash(ident));
1213 Ok(())
1214 },
1215 "stylistic" => {
1216 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1217 let ident = CustomIdent::parse(i, &[])?;
1218 stylistic = Some(VariantAlternates::Stylistic(ident));
1219 Ok(())
1220 },
1221 "ornaments" => {
1222 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1223 let ident = CustomIdent::parse(i, &[])?;
1224 ornaments = Some(VariantAlternates::Ornaments(ident));
1225 Ok(())
1226 },
1227 "annotation" => {
1228 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1229 let ident = CustomIdent::parse(i, &[])?;
1230 annotation = Some(VariantAlternates::Annotation(ident));
1231 Ok(())
1232 },
1233 "styleset" => {
1234 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1235 let idents = i.parse_comma_separated(|i| {
1236 CustomIdent::parse(i, &[])
1237 })?;
1238 styleset = Some(VariantAlternates::Styleset(idents.into()));
1239 Ok(())
1240 },
1241 "character-variant" => {
1242 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1243 let idents = i.parse_comma_separated(|i| {
1244 CustomIdent::parse(i, &[])
1245 })?;
1246 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1247 Ok(())
1248 },
1249 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1250 }
1251 })
1252 },
1253 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1254 }) {}
1255
1256 if parsed_alternates.is_empty() {
1257 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1258 }
1259
1260 let mut alternates = Vec::new();
1262 macro_rules! push_if_some(
1263 ($value:expr) => (
1264 if let Some(v) = $value {
1265 alternates.push(v);
1266 }
1267 )
1268 );
1269 push_if_some!(stylistic);
1270 push_if_some!(historical);
1271 push_if_some!(styleset);
1272 push_if_some!(character_variant);
1273 push_if_some!(swash);
1274 push_if_some!(ornaments);
1275 push_if_some!(annotation);
1276
1277 Ok(FontVariantAlternates(alternates.into()))
1278 }
1279}
1280
1281#[derive(
1282 Clone,
1283 Copy,
1284 Debug,
1285 Eq,
1286 MallocSizeOf,
1287 PartialEq,
1288 Parse,
1289 SpecifiedValueInfo,
1290 ToComputedValue,
1291 ToCss,
1292 ToResolvedValue,
1293 ToShmem,
1294)]
1295#[css(bitflags(
1296 single = "normal",
1297 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1298 validate_mixed = "Self::validate_mixed_flags",
1299))]
1300#[repr(C)]
1301pub struct FontVariantEastAsian(u16);
1303bitflags! {
1304 impl FontVariantEastAsian: u16 {
1305 const NORMAL = 0;
1307 const JIS78 = 1 << 0;
1309 const JIS83 = 1 << 1;
1311 const JIS90 = 1 << 2;
1313 const JIS04 = 1 << 3;
1315 const SIMPLIFIED = 1 << 4;
1317 const TRADITIONAL = 1 << 5;
1319
1320 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1322
1323 const FULL_WIDTH = 1 << 6;
1325 const PROPORTIONAL_WIDTH = 1 << 7;
1327 const RUBY = 1 << 8;
1329 }
1330}
1331
1332impl FontVariantEastAsian {
1333 pub const COUNT: usize = 9;
1335
1336 fn validate_mixed_flags(&self) -> bool {
1337 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1338 return false;
1340 }
1341 let jis = self.intersection(Self::JIS_GROUP);
1342 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1343 return false;
1344 }
1345 true
1346 }
1347}
1348
1349#[derive(
1350 Clone,
1351 Copy,
1352 Debug,
1353 Eq,
1354 MallocSizeOf,
1355 PartialEq,
1356 Parse,
1357 SpecifiedValueInfo,
1358 ToComputedValue,
1359 ToCss,
1360 ToResolvedValue,
1361 ToShmem,
1362)]
1363#[css(bitflags(
1364 single = "normal,none",
1365 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1366 validate_mixed = "Self::validate_mixed_flags",
1367))]
1368#[repr(C)]
1369pub struct FontVariantLigatures(u16);
1371bitflags! {
1372 impl FontVariantLigatures: u16 {
1373 const NORMAL = 0;
1375 const NONE = 1;
1377 const COMMON_LIGATURES = 1 << 1;
1379 const NO_COMMON_LIGATURES = 1 << 2;
1381 const DISCRETIONARY_LIGATURES = 1 << 3;
1383 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1385 const HISTORICAL_LIGATURES = 1 << 5;
1387 const NO_HISTORICAL_LIGATURES = 1 << 6;
1389 const CONTEXTUAL = 1 << 7;
1391 const NO_CONTEXTUAL = 1 << 8;
1393 }
1394}
1395
1396impl FontVariantLigatures {
1397 pub const COUNT: usize = 9;
1399
1400 fn validate_mixed_flags(&self) -> bool {
1401 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1403 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1404 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1405 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1406 {
1407 return false;
1408 }
1409 true
1410 }
1411}
1412
1413#[derive(
1415 Clone,
1416 Copy,
1417 Debug,
1418 Eq,
1419 MallocSizeOf,
1420 PartialEq,
1421 Parse,
1422 SpecifiedValueInfo,
1423 ToComputedValue,
1424 ToCss,
1425 ToResolvedValue,
1426 ToShmem,
1427)]
1428#[css(bitflags(
1429 single = "normal",
1430 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1431 validate_mixed = "Self::validate_mixed_flags",
1432))]
1433#[repr(C)]
1434pub struct FontVariantNumeric(u8);
1435bitflags! {
1436 impl FontVariantNumeric : u8 {
1437 const NORMAL = 0;
1439 const LINING_NUMS = 1 << 0;
1441 const OLDSTYLE_NUMS = 1 << 1;
1443 const PROPORTIONAL_NUMS = 1 << 2;
1445 const TABULAR_NUMS = 1 << 3;
1447 const DIAGONAL_FRACTIONS = 1 << 4;
1449 const STACKED_FRACTIONS = 1 << 5;
1451 const SLASHED_ZERO = 1 << 6;
1453 const ORDINAL = 1 << 7;
1455 }
1456}
1457
1458impl FontVariantNumeric {
1459 pub const COUNT: usize = 8;
1461
1462 fn validate_mixed_flags(&self) -> bool {
1472 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1473 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1474 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1475 {
1476 return false;
1477 }
1478 true
1479 }
1480}
1481
1482pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1484
1485pub use crate::values::computed::font::FontLanguageOverride;
1487
1488impl Parse for FontLanguageOverride {
1489 fn parse<'i, 't>(
1491 _: &ParserContext,
1492 input: &mut Parser<'i, 't>,
1493 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1494 if input
1495 .try_parse(|input| input.expect_ident_matching("normal"))
1496 .is_ok()
1497 {
1498 return Ok(FontLanguageOverride::normal());
1499 }
1500
1501 let string = input.expect_string()?;
1502
1503 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1506 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1507 }
1508
1509 let mut bytes = [b' '; 4];
1510 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1511 *byte = *str_byte;
1512 }
1513
1514 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1515 }
1516}
1517
1518#[repr(u8)]
1520#[derive(
1521 Clone,
1522 Copy,
1523 Debug,
1524 Eq,
1525 MallocSizeOf,
1526 Parse,
1527 PartialEq,
1528 SpecifiedValueInfo,
1529 ToComputedValue,
1530 ToCss,
1531 ToResolvedValue,
1532 ToShmem,
1533)]
1534pub enum FontSynthesis {
1535 Auto,
1537 None,
1539}
1540
1541#[repr(u8)]
1543#[derive(
1544 Clone,
1545 Copy,
1546 Debug,
1547 Eq,
1548 MallocSizeOf,
1549 Parse,
1550 PartialEq,
1551 SpecifiedValueInfo,
1552 ToComputedValue,
1553 ToCss,
1554 ToResolvedValue,
1555 ToShmem,
1556)]
1557pub enum FontSynthesisStyle {
1558 Auto,
1560 None,
1562 ObliqueOnly,
1564}
1565
1566#[derive(
1567 Clone,
1568 Debug,
1569 Eq,
1570 MallocSizeOf,
1571 PartialEq,
1572 SpecifiedValueInfo,
1573 ToComputedValue,
1574 ToResolvedValue,
1575 ToShmem,
1576)]
1577#[repr(C)]
1578pub struct FontPalette(Atom);
1581
1582#[allow(missing_docs)]
1583impl FontPalette {
1584 pub fn normal() -> Self {
1585 Self(atom!("normal"))
1586 }
1587 pub fn light() -> Self {
1588 Self(atom!("light"))
1589 }
1590 pub fn dark() -> Self {
1591 Self(atom!("dark"))
1592 }
1593}
1594
1595impl Parse for FontPalette {
1596 fn parse<'i, 't>(
1598 _context: &ParserContext,
1599 input: &mut Parser<'i, 't>,
1600 ) -> Result<FontPalette, ParseError<'i>> {
1601 let location = input.current_source_location();
1602 let ident = input.expect_ident()?;
1603 match_ignore_ascii_case! { &ident,
1604 "normal" => Ok(Self::normal()),
1605 "light" => Ok(Self::light()),
1606 "dark" => Ok(Self::dark()),
1607 _ => if ident.starts_with("--") {
1608 Ok(Self(Atom::from(ident.as_ref())))
1609 } else {
1610 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1611 },
1612 }
1613 }
1614}
1615
1616impl ToCss for FontPalette {
1617 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1618 where
1619 W: Write,
1620 {
1621 serialize_atom_identifier(&self.0, dest)
1622 }
1623}
1624
1625pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1628
1629fn parse_one_feature_value<'i, 't>(
1630 context: &ParserContext,
1631 input: &mut Parser<'i, 't>,
1632) -> Result<Integer, ParseError<'i>> {
1633 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1634 return Ok(integer);
1635 }
1636
1637 try_match_ident_ignore_ascii_case! { input,
1638 "on" => Ok(Integer::new(1)),
1639 "off" => Ok(Integer::new(0)),
1640 }
1641}
1642
1643impl Parse for FeatureTagValue<Integer> {
1644 fn parse<'i, 't>(
1646 context: &ParserContext,
1647 input: &mut Parser<'i, 't>,
1648 ) -> Result<Self, ParseError<'i>> {
1649 let tag = FontTag::parse(context, input)?;
1650 let value = input
1651 .try_parse(|i| parse_one_feature_value(context, i))
1652 .unwrap_or_else(|_| Integer::new(1));
1653
1654 Ok(Self { tag, value })
1655 }
1656}
1657
1658impl Parse for VariationValue<Number> {
1659 fn parse<'i, 't>(
1662 context: &ParserContext,
1663 input: &mut Parser<'i, 't>,
1664 ) -> Result<Self, ParseError<'i>> {
1665 let tag = FontTag::parse(context, input)?;
1666 let value = Number::parse(context, input)?;
1667 Ok(Self { tag, value })
1668 }
1669}
1670
1671#[derive(
1675 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1676)]
1677pub enum MetricsOverride {
1678 Override(NonNegativePercentage),
1680 Normal,
1682}
1683
1684impl MetricsOverride {
1685 #[inline]
1686 pub fn normal() -> MetricsOverride {
1688 MetricsOverride::Normal
1689 }
1690
1691 #[inline]
1696 pub fn compute(&self) -> ComputedPercentage {
1697 match *self {
1698 MetricsOverride::Normal => ComputedPercentage(-1.0),
1699 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
1700 }
1701 }
1702}
1703
1704#[derive(
1705 Clone,
1706 Copy,
1707 Debug,
1708 MallocSizeOf,
1709 Parse,
1710 PartialEq,
1711 SpecifiedValueInfo,
1712 ToComputedValue,
1713 ToCss,
1714 ToResolvedValue,
1715 ToShmem,
1716)]
1717#[repr(u8)]
1718pub enum XTextScale {
1720 All,
1722 ZoomOnly,
1724 None,
1726}
1727
1728impl XTextScale {
1729 #[inline]
1731 pub fn text_zoom_enabled(self) -> bool {
1732 self != Self::None
1733 }
1734}
1735
1736#[derive(
1737 Clone,
1738 Debug,
1739 MallocSizeOf,
1740 PartialEq,
1741 SpecifiedValueInfo,
1742 ToComputedValue,
1743 ToCss,
1744 ToResolvedValue,
1745 ToShmem,
1746)]
1747#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1748pub struct XLang(#[css(skip)] pub Atom);
1750
1751impl XLang {
1752 #[inline]
1753 pub fn get_initial_value() -> XLang {
1755 XLang(atom!(""))
1756 }
1757}
1758
1759impl Parse for XLang {
1760 fn parse<'i, 't>(
1761 _: &ParserContext,
1762 input: &mut Parser<'i, 't>,
1763 ) -> Result<XLang, ParseError<'i>> {
1764 debug_assert!(
1765 false,
1766 "Should be set directly by presentation attributes only."
1767 );
1768 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1769 }
1770}
1771
1772#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1773#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1774pub struct MozScriptMinSize(pub NoCalcLength);
1777
1778impl MozScriptMinSize {
1779 #[inline]
1780 pub fn get_initial_value() -> Length {
1782 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1783 }
1784}
1785
1786impl Parse for MozScriptMinSize {
1787 fn parse<'i, 't>(
1788 _: &ParserContext,
1789 input: &mut Parser<'i, 't>,
1790 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1791 debug_assert!(
1792 false,
1793 "Should be set directly by presentation attributes only."
1794 );
1795 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1796 }
1797}
1798
1799#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1802#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1803pub enum MathDepth {
1804 AutoAdd,
1806
1807 #[css(function)]
1809 Add(Integer),
1810
1811 Absolute(Integer),
1813}
1814
1815impl Parse for MathDepth {
1816 fn parse<'i, 't>(
1817 context: &ParserContext,
1818 input: &mut Parser<'i, 't>,
1819 ) -> Result<MathDepth, ParseError<'i>> {
1820 if input
1821 .try_parse(|i| i.expect_ident_matching("auto-add"))
1822 .is_ok()
1823 {
1824 return Ok(MathDepth::AutoAdd);
1825 }
1826 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1827 return Ok(MathDepth::Absolute(math_depth_value));
1828 }
1829 input.expect_function_matching("add")?;
1830 let math_depth_delta_value =
1831 input.parse_nested_block(|input| Integer::parse(context, input))?;
1832 Ok(MathDepth::Add(math_depth_delta_value))
1833 }
1834}
1835
1836#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1837#[derive(
1838 Clone,
1839 Copy,
1840 Debug,
1841 PartialEq,
1842 SpecifiedValueInfo,
1843 ToComputedValue,
1844 ToCss,
1845 ToResolvedValue,
1846 ToShmem,
1847)]
1848pub struct MozScriptSizeMultiplier(pub f32);
1853
1854impl MozScriptSizeMultiplier {
1855 #[inline]
1856 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1858 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1859 }
1860}
1861
1862impl Parse for MozScriptSizeMultiplier {
1863 fn parse<'i, 't>(
1864 _: &ParserContext,
1865 input: &mut Parser<'i, 't>,
1866 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1867 debug_assert!(
1868 false,
1869 "Should be set directly by presentation attributes only."
1870 );
1871 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1872 }
1873}
1874
1875impl From<f32> for MozScriptSizeMultiplier {
1876 fn from(v: f32) -> Self {
1877 MozScriptSizeMultiplier(v)
1878 }
1879}
1880
1881impl From<MozScriptSizeMultiplier> for f32 {
1882 fn from(v: MozScriptSizeMultiplier) -> f32 {
1883 v.0
1884 }
1885}
1886
1887pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
1889
1890impl ToComputedValue for LineHeight {
1891 type ComputedValue = computed::LineHeight;
1892
1893 #[inline]
1894 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1895 match *self {
1896 GenericLineHeight::Normal => GenericLineHeight::Normal,
1897 #[cfg(feature = "gecko")]
1898 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1899 GenericLineHeight::Number(number) => {
1900 GenericLineHeight::Number(number.to_computed_value(context))
1901 },
1902 GenericLineHeight::Length(ref non_negative_lp) => {
1903 let result = match non_negative_lp.0 {
1904 LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
1905 context.maybe_zoom_text(abs.to_computed_value(context))
1906 },
1907 LengthPercentage::Length(ref length) => {
1908 length.to_computed_value_with_base_size(
1913 context,
1914 FontBaseSize::CurrentStyle,
1915 LineHeightBase::InheritedStyle,
1916 )
1917 },
1918 LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
1919 .to_computed_value(
1920 context,
1921 FontBaseSize::CurrentStyle,
1922 LineHeightBase::InheritedStyle,
1923 ),
1924 LengthPercentage::Calc(ref calc) => {
1925 let computed_calc = calc.to_computed_value_zoomed(
1926 context,
1927 FontBaseSize::CurrentStyle,
1928 LineHeightBase::InheritedStyle,
1929 );
1930 let base = context.style().get_font().clone_font_size().computed_size();
1931 computed_calc.resolve(base)
1932 },
1933 };
1934 GenericLineHeight::Length(result.into())
1935 },
1936 }
1937 }
1938
1939 #[inline]
1940 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1941 match *computed {
1942 GenericLineHeight::Normal => GenericLineHeight::Normal,
1943 #[cfg(feature = "gecko")]
1944 GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
1945 GenericLineHeight::Number(ref number) => {
1946 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
1947 },
1948 GenericLineHeight::Length(ref length) => {
1949 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
1950 },
1951 }
1952 }
1953}
1954
1955#[repr(C)]
1957pub struct QueryFontMetricsFlags(u8);
1958
1959bitflags! {
1960 impl QueryFontMetricsFlags: u8 {
1961 const USE_USER_FONT_SET = 1 << 0;
1963 const NEEDS_CH = 1 << 1;
1965 const NEEDS_IC = 1 << 2;
1967 const NEEDS_MATH_SCALES = 1 << 3;
1969 }
1970}