1use crate::context::QuirksMode;
8use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
11use crate::values::computed::Percentage as ComputedPercentage;
12use crate::values::computed::{font as computed, Length, NonNegativeLength};
13use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
14use crate::values::generics::font::{
15 self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,
16};
17use crate::values::generics::NonNegative;
18use crate::values::specified::length::{FontBaseSize, LengthUnit, LineHeightBase, PX_PER_PT};
19use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
20use crate::values::specified::{
21 NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber, NonNegativePercentage, Number,
22};
23use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
24use crate::Atom;
25use cssparser::{match_ignore_ascii_case, 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, 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.to_computed_value(context),
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::from_computed_value(computed))
195 }
196}
197
198#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
202pub enum AbsoluteFontWeight {
203 Weight(Number),
207 Normal,
209 Bold,
211}
212
213impl AbsoluteFontWeight {
214 pub fn compute(&self) -> Option<computed::FontWeight> {
217 match self {
218 AbsoluteFontWeight::Weight(weight) => {
219 Some(computed::FontWeight::from_float(weight.resolve()?))
220 },
221 AbsoluteFontWeight::Normal => Some(computed::FontWeight::NORMAL),
222 AbsoluteFontWeight::Bold => Some(computed::FontWeight::BOLD),
223 }
224 }
225}
226
227impl ToComputedValue for AbsoluteFontWeight {
228 type ComputedValue = computed::FontWeight;
229
230 fn to_computed_value(&self, context: &Context) -> computed::FontWeight {
231 match self {
232 AbsoluteFontWeight::Weight(weight) => {
233 computed::FontWeight::from_float(weight.to_computed_value(context))
234 },
235 AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
236 AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
237 }
238 }
239
240 fn from_computed_value(computed: &computed::FontWeight) -> Self {
241 AbsoluteFontWeight::Weight(Number::from_computed_value(&computed.value()))
242 }
243}
244
245impl Parse for AbsoluteFontWeight {
246 fn parse<'i, 't>(
247 context: &ParserContext,
248 input: &mut Parser<'i, 't>,
249 ) -> Result<Self, ParseError<'i>> {
250 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
251 if matches!(number.get(), Some(v) if v < MIN_FONT_WEIGHT || v > MAX_FONT_WEIGHT) {
255 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
256 }
257 return Ok(AbsoluteFontWeight::Weight(number));
258 }
259
260 Ok(try_match_ident_ignore_ascii_case! { input,
261 "normal" => AbsoluteFontWeight::Normal,
262 "bold" => AbsoluteFontWeight::Bold,
263 })
264 }
265}
266
267pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
270
271impl ToCss for SpecifiedFontStyle {
272 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
273 where
274 W: Write,
275 {
276 match *self {
277 generics::FontStyle::Italic => dest.write_str("italic"),
278 generics::FontStyle::Oblique(ref angle) => {
279 if *angle == Angle::zero() {
282 dest.write_str("normal")?;
283 } else {
284 dest.write_str("oblique")?;
285 if *angle != Self::default_angle() {
286 dest.write_char(' ')?;
287 angle.to_css(dest)?;
288 }
289 }
290 Ok(())
291 },
292 }
293 }
294}
295
296impl Parse for SpecifiedFontStyle {
297 fn parse<'i, 't>(
298 context: &ParserContext,
299 input: &mut Parser<'i, 't>,
300 ) -> Result<Self, ParseError<'i>> {
301 Ok(try_match_ident_ignore_ascii_case! { input,
302 "normal" => generics::FontStyle::normal(),
303 "italic" => generics::FontStyle::Italic,
304 "oblique" => {
305 let angle = input.try_parse(|input| Self::parse_angle(context, input))
306 .unwrap_or_else(|_| Self::default_angle());
307
308 generics::FontStyle::Oblique(angle)
309 },
310 })
311 }
312}
313
314impl ToComputedValue for SpecifiedFontStyle {
315 type ComputedValue = computed::FontStyle;
316
317 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
318 match *self {
319 Self::Italic => computed::FontStyle::ITALIC,
320 Self::Oblique(ref angle) => {
321 computed::FontStyle::oblique(angle.to_computed_value(context).degrees())
322 },
323 }
324 }
325
326 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
327 if *computed == computed::FontStyle::ITALIC {
328 return Self::Italic;
329 }
330 let degrees = computed.oblique_degrees();
331 generics::FontStyle::Oblique(Angle::from_degrees(degrees))
332 }
333}
334
335pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
342
343pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
345
346impl SpecifiedFontStyle {
347 pub fn parse_angle<'i, 't>(
349 context: &ParserContext,
350 input: &mut Parser<'i, 't>,
351 ) -> Result<Angle, ParseError<'i>> {
352 let angle = Angle::parse(context, input)?;
353 if angle.is_calc() {
355 return Ok(angle);
356 }
357
358 let degrees = angle.degrees().unwrap();
359 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES
360 || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
361 {
362 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
363 }
364 return Ok(angle);
365 }
366
367 pub fn default_angle() -> Angle {
369 Angle::from_degrees(computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32)
370 }
371}
372
373#[derive(
375 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
376)]
377#[allow(missing_docs)]
378#[typed(todo_derive_fields)]
379pub enum FontStyle {
380 Specified(SpecifiedFontStyle),
381 #[css(skip)]
382 System(SystemFont),
383}
384
385impl FontStyle {
386 #[inline]
388 pub fn normal() -> Self {
389 FontStyle::Specified(generics::FontStyle::normal())
390 }
391
392 system_font_methods!(FontStyle, font_style);
393}
394
395impl ToComputedValue for FontStyle {
396 type ComputedValue = computed::FontStyle;
397
398 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
399 match *self {
400 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
401 FontStyle::System(..) => self.compute_system(context),
402 }
403 }
404
405 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
406 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
407 }
408}
409
410#[allow(missing_docs)]
414#[derive(
415 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
416)]
417pub enum FontStretch {
418 Stretch(NonNegativePercentage),
419 Keyword(FontStretchKeyword),
420 #[css(skip)]
421 System(SystemFont),
422}
423
424#[derive(
426 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
427)]
428#[allow(missing_docs)]
429pub enum FontStretchKeyword {
430 Normal,
431 Condensed,
432 UltraCondensed,
433 ExtraCondensed,
434 SemiCondensed,
435 SemiExpanded,
436 Expanded,
437 ExtraExpanded,
438 UltraExpanded,
439}
440
441impl FontStretchKeyword {
442 pub fn compute(&self) -> computed::FontStretch {
444 computed::FontStretch::from_keyword(*self)
445 }
446
447 pub fn from_percentage(p: f32) -> Option<Self> {
450 computed::FontStretch::from_percentage(p).as_keyword()
451 }
452}
453
454impl FontStretch {
455 pub fn normal() -> Self {
457 FontStretch::Keyword(FontStretchKeyword::Normal)
458 }
459
460 system_font_methods!(FontStretch, font_stretch);
461}
462
463impl ToComputedValue for FontStretch {
464 type ComputedValue = computed::FontStretch;
465
466 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
467 match *self {
468 FontStretch::Stretch(ref percentage) => {
469 let percentage = percentage.to_computed_value(context).0;
470 computed::FontStretch::from_percentage(percentage.0)
471 },
472 FontStretch::Keyword(ref kw) => kw.compute(),
473 FontStretch::System(_) => self.compute_system(context),
474 }
475 }
476
477 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
478 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
479 computed.to_percentage(),
480 )))
481 }
482}
483
484#[derive(
486 Animate,
487 Clone,
488 ComputeSquaredDistance,
489 Copy,
490 Debug,
491 MallocSizeOf,
492 Parse,
493 PartialEq,
494 SpecifiedValueInfo,
495 ToAnimatedValue,
496 ToAnimatedZero,
497 ToComputedValue,
498 ToCss,
499 ToResolvedValue,
500 ToShmem,
501 Serialize,
502 Deserialize,
503 ToTyped,
504)]
505#[allow(missing_docs)]
506#[repr(u8)]
507pub enum FontSizeKeyword {
508 #[css(keyword = "xx-small")]
509 XXSmall,
510 XSmall,
511 Small,
512 Medium,
513 Large,
514 XLarge,
515 #[css(keyword = "xx-large")]
516 XXLarge,
517 #[css(keyword = "xxx-large")]
518 XXXLarge,
519 #[cfg(feature = "gecko")]
522 Math,
523 #[css(skip)]
524 None,
525}
526
527impl FontSizeKeyword {
528 #[inline]
530 pub fn html_size(self) -> u8 {
531 self as u8
532 }
533
534 #[cfg(feature = "gecko")]
536 pub fn is_math(self) -> bool {
537 matches!(self, Self::Math)
538 }
539
540 #[cfg(feature = "servo")]
542 pub fn is_math(self) -> bool {
543 false
544 }
545}
546
547impl Default for FontSizeKeyword {
548 fn default() -> Self {
549 FontSizeKeyword::Medium
550 }
551}
552
553#[derive(
554 Animate,
555 Clone,
556 ComputeSquaredDistance,
557 Copy,
558 Debug,
559 Deserialize,
560 MallocSizeOf,
561 PartialEq,
562 Serialize,
563 ToAnimatedValue,
564 ToAnimatedZero,
565 ToComputedValue,
566 ToCss,
567 ToResolvedValue,
568 ToShmem,
569 ToTyped,
570)]
571pub struct KeywordInfo {
573 pub kw: FontSizeKeyword,
575 #[css(skip)]
577 pub factor: f32,
578 #[css(skip)]
581 pub offset: CSSPixelLength,
582}
583
584impl KeywordInfo {
585 pub fn medium() -> Self {
587 Self::new(FontSizeKeyword::Medium)
588 }
589
590 pub fn none() -> Self {
592 Self::new(FontSizeKeyword::None)
593 }
594
595 fn new(kw: FontSizeKeyword) -> Self {
596 KeywordInfo {
597 kw,
598 factor: 1.,
599 offset: CSSPixelLength::new(0.),
600 }
601 }
602
603 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
606 debug_assert_ne!(self.kw, FontSizeKeyword::None);
607 #[cfg(feature = "gecko")]
608 debug_assert_ne!(self.kw, FontSizeKeyword::Math);
609 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
610 let zoom_factor = context.style().effective_zoom.value();
611 CSSPixelLength::new(base.px() * self.factor * zoom_factor)
612 + context.maybe_zoom_text(self.offset)
613 }
614
615 fn compose(self, factor: f32) -> Self {
618 if self.kw == FontSizeKeyword::None {
619 return self;
620 }
621 KeywordInfo {
622 kw: self.kw,
623 factor: self.factor * factor,
624 offset: self.offset * factor,
625 }
626 }
627}
628
629impl SpecifiedValueInfo for KeywordInfo {
630 fn collect_completion_keywords(f: KeywordsCollectFn) {
631 <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
632 }
633}
634
635#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
636pub enum FontSize {
638 Length(LengthPercentage),
640 Keyword(KeywordInfo),
651 Smaller,
653 Larger,
655 #[css(skip)]
657 System(SystemFont),
658}
659
660#[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss, ToShmem, ToTyped)]
662#[typed(todo_derive_fields)]
663pub enum FontFamily {
664 #[css(comma)]
666 Values(#[css(iterable)] FontFamilyList),
667 #[css(skip)]
669 System(SystemFont),
670}
671
672impl FontFamily {
673 system_font_methods!(FontFamily, font_family);
674}
675
676impl ToComputedValue for FontFamily {
677 type ComputedValue = computed::FontFamily;
678
679 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
680 match *self {
681 FontFamily::Values(ref list) => computed::FontFamily {
682 families: list.clone(),
683 is_system_font: false,
684 is_initial: false,
685 },
686 FontFamily::System(_) => self.compute_system(context),
687 }
688 }
689
690 fn from_computed_value(other: &computed::FontFamily) -> Self {
691 FontFamily::Values(other.families.clone())
692 }
693}
694
695#[cfg(feature = "gecko")]
696impl MallocSizeOf for FontFamily {
697 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
698 match *self {
699 FontFamily::Values(ref v) => {
700 v.list.unconditional_size_of(ops)
703 },
704 FontFamily::System(_) => 0,
705 }
706 }
707}
708
709impl Parse for FontFamily {
710 fn parse<'i, 't>(
714 context: &ParserContext,
715 input: &mut Parser<'i, 't>,
716 ) -> Result<FontFamily, ParseError<'i>> {
717 let values =
718 input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
719 Ok(FontFamily::Values(FontFamilyList {
720 list: crate::ArcSlice::from_iter(values.into_iter()),
721 }))
722 }
723}
724
725impl SpecifiedValueInfo for FontFamily {}
726
727impl Parse for FamilyName {
730 fn parse<'i, 't>(
731 context: &ParserContext,
732 input: &mut Parser<'i, 't>,
733 ) -> Result<Self, ParseError<'i>> {
734 match SingleFontFamily::parse(context, input) {
735 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
736 Ok(SingleFontFamily::Generic(_)) => {
737 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
738 },
739 Err(e) => Err(e),
740 }
741 }
742}
743
744#[derive(
747 Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
748)]
749pub enum FontSizeAdjustFactor {
750 Number(NonNegativeNumber),
752 FromFont,
754}
755
756pub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;
761
762impl Parse for FontSizeAdjust {
763 fn parse<'i, 't>(
764 context: &ParserContext,
765 input: &mut Parser<'i, 't>,
766 ) -> Result<Self, ParseError<'i>> {
767 let location = input.current_source_location();
768 if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {
770 return Ok(Self::ExHeight(factor));
771 }
772
773 let ident = input.expect_ident()?;
774 let basis = match_ignore_ascii_case! { &ident,
775 "none" => return Ok(Self::None),
776 "ex-height" => Self::ExHeight,
778 "cap-height" => Self::CapHeight,
779 "ch-width" => Self::ChWidth,
780 "ic-width" => Self::IcWidth,
781 "ic-height" => Self::IcHeight,
782 _ => return Err(location.new_custom_error(
784 SelectorParseErrorKind::UnexpectedIdent(ident.clone())
785 )),
786 };
787
788 Ok(basis(FontSizeAdjustFactor::parse(context, input)?))
789 }
790}
791
792const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
795
796pub const FONT_MEDIUM_PX: f32 = 16.0;
798pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;
800pub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;
803pub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;
806pub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;
809pub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;
812
813impl FontSizeKeyword {
814 #[inline]
815 fn to_length(&self, cx: &Context) -> NonNegativeLength {
816 let font = cx.style().get_font();
817
818 #[cfg(feature = "servo")]
819 let family = &font.font_family.families;
820 #[cfg(feature = "gecko")]
821 let family = &font.mFont.family.families;
822
823 let generic = family
824 .single_generic()
825 .unwrap_or(computed::GenericFontFamily::None);
826
827 #[cfg(feature = "gecko")]
828 let base_size = unsafe {
829 Atom::with(font.mLanguage.mRawPtr, |language| {
830 cx.device().base_size_for_generic(language, generic)
831 })
832 };
833 #[cfg(feature = "servo")]
834 let base_size = cx.device().base_size_for_generic(generic);
835
836 self.to_length_without_context(cx.quirks_mode, base_size)
837 }
838
839 #[inline]
841 pub fn to_length_without_context(
842 &self,
843 quirks_mode: QuirksMode,
844 base_size: Length,
845 ) -> NonNegativeLength {
846 #[cfg(feature = "gecko")]
847 debug_assert_ne!(*self, FontSizeKeyword::Math);
848 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
861 [9, 9, 9, 9, 11, 14, 18, 27],
862 [9, 9, 9, 10, 12, 15, 20, 30],
863 [9, 9, 10, 11, 13, 17, 22, 33],
864 [9, 9, 10, 12, 14, 18, 24, 36],
865 [9, 10, 12, 13, 16, 20, 26, 39],
866 [9, 10, 12, 14, 17, 21, 28, 42],
867 [9, 10, 13, 15, 18, 23, 30, 45],
868 [9, 10, 13, 16, 18, 24, 32, 48],
869 ];
870
871 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
880 [9, 9, 9, 9, 11, 14, 18, 28],
881 [9, 9, 9, 10, 12, 15, 20, 31],
882 [9, 9, 9, 11, 13, 17, 22, 34],
883 [9, 9, 10, 12, 14, 18, 24, 37],
884 [9, 9, 10, 13, 16, 20, 26, 40],
885 [9, 9, 11, 14, 17, 21, 28, 42],
886 [9, 10, 12, 15, 17, 23, 30, 45],
887 [9, 10, 13, 16, 18, 24, 32, 48],
888 ];
889
890 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
891 let base_size_px = base_size.px().round() as i32;
892 let html_size = self.html_size() as usize;
893 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
894 let mapping = if quirks_mode == QuirksMode::Quirks {
895 QUIRKS_FONT_SIZE_MAPPING
896 } else {
897 FONT_SIZE_MAPPING
898 };
899 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
900 } else {
901 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
902 })
903 }
904}
905
906impl FontSize {
907 pub fn from_html_size(size: u8) -> Self {
909 FontSize::Keyword(KeywordInfo::new(match size {
910 0 | 1 => FontSizeKeyword::XSmall,
912 2 => FontSizeKeyword::Small,
913 3 => FontSizeKeyword::Medium,
914 4 => FontSizeKeyword::Large,
915 5 => FontSizeKeyword::XLarge,
916 6 => FontSizeKeyword::XXLarge,
917 _ => FontSizeKeyword::XXXLarge,
919 }))
920 }
921
922 pub fn to_computed_value_against(
924 &self,
925 context: &Context,
926 base_size: FontBaseSize,
927 line_height_base: LineHeightBase,
928 ) -> computed::FontSize {
929 let compose_keyword = |factor| {
930 context
931 .style()
932 .get_parent_font()
933 .clone_font_size()
934 .keyword_info
935 .compose(factor)
936 };
937 let mut info = KeywordInfo::none();
938 let size =
939 match *self {
940 FontSize::Length(LengthPercentage::Length(ref l)) => {
941 if l.length_unit() == LengthUnit::Em {
942 info = compose_keyword(l.unitless_value());
945 }
946 let result =
947 l.to_computed_value_with_base_size(context, base_size, line_height_base);
948 if l.should_zoom_text() {
949 context.maybe_zoom_text(result)
950 } else {
951 result
952 }
953 },
954 FontSize::Length(LengthPercentage::Percentage(pc)) => {
955 info = compose_keyword(pc.get());
958 (base_size.resolve(context).computed_size() * pc.get()).normalized()
959 },
960 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
961 let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);
962 calc.resolve(base_size.resolve(context).computed_size())
963 },
964 FontSize::Keyword(i) => {
965 if i.kw.is_math() {
966 info = compose_keyword(1.);
968 info.kw = i.kw;
971 NoCalcLength::from_em(1.).to_computed_value_with_base_size(
972 context,
973 base_size,
974 line_height_base,
975 )
976 } else {
977 info = i;
979 i.to_computed_value(context).clamp_to_non_negative()
980 }
981 },
982 FontSize::Smaller => {
983 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
984 NoCalcLength::from_em(1. / LARGER_FONT_SIZE_RATIO)
985 .to_computed_value_with_base_size(context, base_size, line_height_base)
986 },
987 FontSize::Larger => {
988 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
989 NoCalcLength::from_em(LARGER_FONT_SIZE_RATIO).to_computed_value_with_base_size(
990 context,
991 base_size,
992 line_height_base,
993 )
994 },
995 FontSize::System(_) => {
996 #[cfg(feature = "servo")]
997 {
998 unreachable!()
999 }
1000 #[cfg(feature = "gecko")]
1001 {
1002 context
1003 .cached_system_font
1004 .as_ref()
1005 .unwrap()
1006 .font_size
1007 .computed_size()
1008 .zoom(context.builder.effective_zoom)
1009 }
1010 },
1011 };
1012 let size = NonNegative(Self::quantize_font_size(size));
1013 computed::FontSize {
1014 computed_size: size,
1015 used_size: size,
1016 keyword_info: info,
1017 }
1018 }
1019
1020 #[inline]
1023 pub fn quantize_font_size(size: CSSPixelLength) -> CSSPixelLength {
1024 size_of_test!(CSSPixelLength, std::mem::size_of::<f32>());
1032 const BITS_TO_DROP: u32 = 14; const SCALE_PLUS_ONE: f32 = ((1 << BITS_TO_DROP) + 1) as f32;
1034 const LIMIT: f32 = f32::MAX / SCALE_PLUS_ONE;
1035 if size.px() >= LIMIT {
1036 return CSSPixelLength::new(LIMIT);
1037 }
1038 let d = size.px() * SCALE_PLUS_ONE;
1039 let t = d - size.px();
1040 CSSPixelLength::new(d - t)
1041 }
1042}
1043
1044impl ToComputedValue for FontSize {
1045 type ComputedValue = computed::FontSize;
1046
1047 #[inline]
1048 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1049 self.to_computed_value_against(
1050 context,
1051 FontBaseSize::InheritedStyle,
1052 LineHeightBase::InheritedStyle,
1053 )
1054 }
1055
1056 #[inline]
1057 fn from_computed_value(computed: &computed::FontSize) -> Self {
1058 FontSize::Length(LengthPercentage::Length(
1059 ToComputedValue::from_computed_value(&computed.computed_size()),
1060 ))
1061 }
1062}
1063
1064impl FontSize {
1065 system_font_methods!(FontSize);
1066
1067 #[inline]
1069 pub fn medium() -> Self {
1070 FontSize::Keyword(KeywordInfo::medium())
1071 }
1072
1073 pub fn parse_quirky<'i, 't>(
1075 context: &ParserContext,
1076 input: &mut Parser<'i, 't>,
1077 allow_quirks: AllowQuirks,
1078 ) -> Result<FontSize, ParseError<'i>> {
1079 if let Ok(lp) = input
1080 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1081 {
1082 return Ok(FontSize::Length(lp));
1083 }
1084
1085 if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {
1086 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1087 }
1088
1089 try_match_ident_ignore_ascii_case! { input,
1090 "smaller" => Ok(FontSize::Smaller),
1091 "larger" => Ok(FontSize::Larger),
1092 }
1093 }
1094}
1095
1096impl Parse for FontSize {
1097 fn parse<'i, 't>(
1099 context: &ParserContext,
1100 input: &mut Parser<'i, 't>,
1101 ) -> Result<FontSize, ParseError<'i>> {
1102 FontSize::parse_quirky(context, input, AllowQuirks::No)
1103 }
1104}
1105
1106bitflags! {
1107 #[derive(Clone, Copy)]
1108 struct VariantAlternatesParsingFlags: u8 {
1110 const NORMAL = 0;
1112 const HISTORICAL_FORMS = 0x01;
1114 const STYLISTIC = 0x02;
1116 const STYLESET = 0x04;
1118 const CHARACTER_VARIANT = 0x08;
1120 const SWASH = 0x10;
1122 const ORNAMENTS = 0x20;
1124 const ANNOTATION = 0x40;
1126 }
1127}
1128
1129#[derive(
1130 Clone,
1131 Debug,
1132 Deserialize,
1133 Hash,
1134 MallocSizeOf,
1135 PartialEq,
1136 Serialize,
1137 SpecifiedValueInfo,
1138 ToCss,
1139 ToComputedValue,
1140 ToResolvedValue,
1141 ToShmem,
1142)]
1143#[repr(C, u8)]
1144pub enum VariantAlternates {
1146 #[css(function)]
1148 Stylistic(CustomIdent),
1149 #[css(comma, function)]
1151 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1152 #[css(comma, function)]
1154 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1155 #[css(function)]
1157 Swash(CustomIdent),
1158 #[css(function)]
1160 Ornaments(CustomIdent),
1161 #[css(function)]
1163 Annotation(CustomIdent),
1164 HistoricalForms,
1166}
1167
1168#[derive(
1169 Clone,
1170 Debug,
1171 Default,
1172 Deserialize,
1173 Hash,
1174 MallocSizeOf,
1175 PartialEq,
1176 Serialize,
1177 SpecifiedValueInfo,
1178 ToComputedValue,
1179 ToCss,
1180 ToResolvedValue,
1181 ToShmem,
1182 ToTyped,
1183)]
1184#[repr(transparent)]
1185#[typed(todo_derive_fields)]
1186pub struct FontVariantAlternates(
1188 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1189);
1190
1191impl FontVariantAlternates {
1192 pub fn is_empty(&self) -> bool {
1194 self.0.is_empty()
1195 }
1196
1197 pub fn iter(&self) -> impl Iterator<Item=&VariantAlternates> {
1199 self.0.iter()
1200 }
1201
1202 pub fn len(&self) -> usize {
1204 self.0.iter().fold(0, |acc, alternate| match *alternate {
1205 VariantAlternates::Swash(_)
1206 | VariantAlternates::Stylistic(_)
1207 | VariantAlternates::Ornaments(_)
1208 | VariantAlternates::Annotation(_) => acc + 1,
1209 VariantAlternates::Styleset(ref slice)
1210 | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1211 _ => acc,
1212 })
1213 }
1214}
1215
1216impl Parse for FontVariantAlternates {
1217 fn parse<'i, 't>(
1226 _: &ParserContext,
1227 input: &mut Parser<'i, 't>,
1228 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1229 if input
1230 .try_parse(|input| input.expect_ident_matching("normal"))
1231 .is_ok()
1232 {
1233 return Ok(Default::default());
1234 }
1235
1236 let mut stylistic = None;
1237 let mut historical = None;
1238 let mut styleset = None;
1239 let mut character_variant = None;
1240 let mut swash = None;
1241 let mut ornaments = None;
1242 let mut annotation = None;
1243
1244 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1246 macro_rules! check_if_parsed(
1247 ($input:expr, $flag:path) => (
1248 if parsed_alternates.contains($flag) {
1249 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1250 }
1251 parsed_alternates |= $flag;
1252 )
1253 );
1254 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1255 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1256 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1257 historical = Some(VariantAlternates::HistoricalForms);
1258 Ok(())
1259 },
1260 Token::Function(ref name) => {
1261 let name = name.clone();
1262 input.parse_nested_block(|i| {
1263 match_ignore_ascii_case! { &name,
1264 "swash" => {
1265 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1266 let ident = CustomIdent::parse(i, &[])?;
1267 swash = Some(VariantAlternates::Swash(ident));
1268 Ok(())
1269 },
1270 "stylistic" => {
1271 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1272 let ident = CustomIdent::parse(i, &[])?;
1273 stylistic = Some(VariantAlternates::Stylistic(ident));
1274 Ok(())
1275 },
1276 "ornaments" => {
1277 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1278 let ident = CustomIdent::parse(i, &[])?;
1279 ornaments = Some(VariantAlternates::Ornaments(ident));
1280 Ok(())
1281 },
1282 "annotation" => {
1283 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1284 let ident = CustomIdent::parse(i, &[])?;
1285 annotation = Some(VariantAlternates::Annotation(ident));
1286 Ok(())
1287 },
1288 "styleset" => {
1289 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1290 let idents = i.parse_comma_separated(|i| {
1291 CustomIdent::parse(i, &[])
1292 })?;
1293 styleset = Some(VariantAlternates::Styleset(idents.into()));
1294 Ok(())
1295 },
1296 "character-variant" => {
1297 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1298 let idents = i.parse_comma_separated(|i| {
1299 CustomIdent::parse(i, &[])
1300 })?;
1301 character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
1302 Ok(())
1303 },
1304 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1305 }
1306 })
1307 },
1308 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1309 }) {}
1310
1311 if parsed_alternates.is_empty() {
1312 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1313 }
1314
1315 let mut alternates = Vec::new();
1317 macro_rules! push_if_some(
1318 ($value:expr) => (
1319 if let Some(v) = $value {
1320 alternates.push(v);
1321 }
1322 )
1323 );
1324 push_if_some!(stylistic);
1325 push_if_some!(historical);
1326 push_if_some!(styleset);
1327 push_if_some!(character_variant);
1328 push_if_some!(swash);
1329 push_if_some!(ornaments);
1330 push_if_some!(annotation);
1331
1332 Ok(FontVariantAlternates(alternates.into()))
1333 }
1334}
1335
1336#[derive(
1337 Clone,
1338 Copy,
1339 Debug,
1340 Deserialize,
1341 Eq,
1342 Hash,
1343 MallocSizeOf,
1344 PartialEq,
1345 Parse,
1346 Serialize,
1347 SpecifiedValueInfo,
1348 ToComputedValue,
1349 ToCss,
1350 ToResolvedValue,
1351 ToShmem,
1352 ToTyped,
1353)]
1354#[css(bitflags(
1355 single = "normal",
1356 mixed = "jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby",
1357 validate_mixed = "Self::validate_mixed_flags",
1358))]
1359#[repr(C)]
1360pub struct FontVariantEastAsian(u16);
1362bitflags! {
1363 impl FontVariantEastAsian: u16 {
1364 const NORMAL = 0;
1366 const JIS78 = 1 << 0;
1368 const JIS83 = 1 << 1;
1370 const JIS90 = 1 << 2;
1372 const JIS04 = 1 << 3;
1374 const SIMPLIFIED = 1 << 4;
1376 const TRADITIONAL = 1 << 5;
1378
1379 const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;
1381
1382 const FULL_WIDTH = 1 << 6;
1384 const PROPORTIONAL_WIDTH = 1 << 7;
1386 const RUBY = 1 << 8;
1388 }
1389}
1390
1391impl FontVariantEastAsian {
1392 pub const COUNT: usize = 9;
1394
1395 fn validate_mixed_flags(&self) -> bool {
1396 if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {
1397 return false;
1399 }
1400 let jis = self.intersection(Self::JIS_GROUP);
1401 if !jis.is_empty() && !jis.bits().is_power_of_two() {
1402 return false;
1403 }
1404 true
1405 }
1406}
1407
1408#[derive(
1409 Clone,
1410 Copy,
1411 Debug,
1412 Deserialize,
1413 Eq,
1414 Hash,
1415 MallocSizeOf,
1416 PartialEq,
1417 Parse,
1418 Serialize,
1419 SpecifiedValueInfo,
1420 ToComputedValue,
1421 ToCss,
1422 ToResolvedValue,
1423 ToShmem,
1424 ToTyped,
1425)]
1426#[css(bitflags(
1427 single = "normal,none",
1428 mixed = "common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual",
1429 validate_mixed = "Self::validate_mixed_flags",
1430))]
1431#[repr(C)]
1432pub struct FontVariantLigatures(u16);
1434bitflags! {
1435 impl FontVariantLigatures: u16 {
1436 const NORMAL = 0;
1438 const NONE = 1;
1440 const COMMON_LIGATURES = 1 << 1;
1442 const NO_COMMON_LIGATURES = 1 << 2;
1444 const DISCRETIONARY_LIGATURES = 1 << 3;
1446 const NO_DISCRETIONARY_LIGATURES = 1 << 4;
1448 const HISTORICAL_LIGATURES = 1 << 5;
1450 const NO_HISTORICAL_LIGATURES = 1 << 6;
1452 const CONTEXTUAL = 1 << 7;
1454 const NO_CONTEXTUAL = 1 << 8;
1456 }
1457}
1458
1459impl FontVariantLigatures {
1460 pub const COUNT: usize = 9;
1462
1463 fn validate_mixed_flags(&self) -> bool {
1464 if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)
1466 || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)
1467 || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)
1468 || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)
1469 {
1470 return false;
1471 }
1472 true
1473 }
1474}
1475
1476#[derive(
1478 Clone,
1479 Copy,
1480 Debug,
1481 Deserialize,
1482 Eq,
1483 Hash,
1484 MallocSizeOf,
1485 PartialEq,
1486 Parse,
1487 Serialize,
1488 SpecifiedValueInfo,
1489 ToComputedValue,
1490 ToCss,
1491 ToResolvedValue,
1492 ToShmem,
1493 ToTyped,
1494)]
1495#[css(bitflags(
1496 single = "normal",
1497 mixed = "lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero",
1498 validate_mixed = "Self::validate_mixed_flags",
1499))]
1500#[repr(C)]
1501pub struct FontVariantNumeric(u8);
1502bitflags! {
1503 impl FontVariantNumeric : u8 {
1504 const NORMAL = 0;
1506 const LINING_NUMS = 1 << 0;
1508 const OLDSTYLE_NUMS = 1 << 1;
1510 const PROPORTIONAL_NUMS = 1 << 2;
1512 const TABULAR_NUMS = 1 << 3;
1514 const DIAGONAL_FRACTIONS = 1 << 4;
1516 const STACKED_FRACTIONS = 1 << 5;
1518 const SLASHED_ZERO = 1 << 6;
1520 const ORDINAL = 1 << 7;
1522 }
1523}
1524
1525impl FontVariantNumeric {
1526 pub const COUNT: usize = 8;
1528
1529 fn validate_mixed_flags(&self) -> bool {
1539 if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)
1540 || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)
1541 || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)
1542 {
1543 return false;
1544 }
1545 true
1546 }
1547}
1548
1549pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1551
1552impl FontFeatureSettings {
1553 pub fn parse_for_font_face_rule<'i, 't>(
1556 context: &ParserContext,
1557 input: &mut Parser<'i, 't>,
1558 ) -> Result<Self, ParseError<'i>> {
1559 let settings = FontFeatureSettings::parse(context, input)?;
1560 if settings
1561 .0
1562 .iter()
1563 .any(|setting| setting.value.resolve().is_none())
1564 {
1565 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1566 }
1567 Ok(settings)
1568 }
1569}
1570
1571pub use crate::values::computed::font::FontLanguageOverride;
1573
1574impl Parse for FontLanguageOverride {
1575 fn parse<'i, 't>(
1577 _: &ParserContext,
1578 input: &mut Parser<'i, 't>,
1579 ) -> Result<FontLanguageOverride, ParseError<'i>> {
1580 if input
1581 .try_parse(|input| input.expect_ident_matching("normal"))
1582 .is_ok()
1583 {
1584 return Ok(FontLanguageOverride::normal());
1585 }
1586
1587 let string = input.expect_string()?;
1588
1589 if string.is_empty() || string.len() > 4 || !string.is_ascii() {
1592 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1593 }
1594
1595 let mut bytes = [b' '; 4];
1596 for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
1597 *byte = *str_byte;
1598 }
1599
1600 Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
1601 }
1602}
1603
1604#[repr(u8)]
1606#[derive(
1607 Clone,
1608 Copy,
1609 Debug,
1610 Deserialize,
1611 Eq,
1612 Hash,
1613 MallocSizeOf,
1614 Parse,
1615 PartialEq,
1616 Serialize,
1617 SpecifiedValueInfo,
1618 ToComputedValue,
1619 ToCss,
1620 ToResolvedValue,
1621 ToShmem,
1622 ToTyped,
1623)]
1624pub enum FontSynthesis {
1625 Auto,
1627 None,
1629}
1630
1631#[repr(u8)]
1633#[derive(
1634 Clone,
1635 Copy,
1636 Debug,
1637 Eq,
1638 MallocSizeOf,
1639 Parse,
1640 PartialEq,
1641 SpecifiedValueInfo,
1642 ToComputedValue,
1643 ToCss,
1644 ToResolvedValue,
1645 ToShmem,
1646 ToTyped,
1647)]
1648pub enum FontSynthesisStyle {
1649 Auto,
1651 None,
1653 ObliqueOnly,
1655}
1656
1657#[derive(
1658 Clone,
1659 Debug,
1660 Eq,
1661 MallocSizeOf,
1662 PartialEq,
1663 SpecifiedValueInfo,
1664 ToComputedValue,
1665 ToResolvedValue,
1666 ToShmem,
1667 ToTyped,
1668)]
1669#[repr(C)]
1670#[typed(todo_derive_fields)]
1671pub struct FontPalette(Atom);
1674
1675#[allow(missing_docs)]
1676impl FontPalette {
1677 pub fn normal() -> Self {
1678 Self(atom!("normal"))
1679 }
1680 pub fn light() -> Self {
1681 Self(atom!("light"))
1682 }
1683 pub fn dark() -> Self {
1684 Self(atom!("dark"))
1685 }
1686}
1687
1688impl Parse for FontPalette {
1689 fn parse<'i, 't>(
1691 _context: &ParserContext,
1692 input: &mut Parser<'i, 't>,
1693 ) -> Result<FontPalette, ParseError<'i>> {
1694 let location = input.current_source_location();
1695 let ident = input.expect_ident()?;
1696 match_ignore_ascii_case! { &ident,
1697 "normal" => Ok(Self::normal()),
1698 "light" => Ok(Self::light()),
1699 "dark" => Ok(Self::dark()),
1700 _ => if ident.starts_with("--") {
1701 Ok(Self(Atom::from(ident.as_ref())))
1702 } else {
1703 Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1704 },
1705 }
1706 }
1707}
1708
1709impl ToCss for FontPalette {
1710 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1711 where
1712 W: Write,
1713 {
1714 serialize_atom_identifier(&self.0, dest)
1715 }
1716}
1717
1718pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1721
1722fn parse_one_feature_value<'i, 't>(
1723 context: &ParserContext,
1724 input: &mut Parser<'i, 't>,
1725) -> Result<Integer, ParseError<'i>> {
1726 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
1727 return Ok(integer);
1728 }
1729
1730 try_match_ident_ignore_ascii_case! { input,
1731 "on" => Ok(Integer::new(1)),
1732 "off" => Ok(Integer::new(0)),
1733 }
1734}
1735
1736impl Parse for FeatureTagValue<Integer> {
1737 fn parse<'i, 't>(
1739 context: &ParserContext,
1740 input: &mut Parser<'i, 't>,
1741 ) -> Result<Self, ParseError<'i>> {
1742 let tag = FontTag::parse(context, input)?;
1743 let value = input
1744 .try_parse(|i| parse_one_feature_value(context, i))
1745 .unwrap_or_else(|_| Integer::new(1));
1746
1747 Ok(Self { tag, value })
1748 }
1749}
1750
1751impl Parse for VariationValue<Number> {
1752 fn parse<'i, 't>(
1755 context: &ParserContext,
1756 input: &mut Parser<'i, 't>,
1757 ) -> Result<Self, ParseError<'i>> {
1758 let tag = FontTag::parse(context, input)?;
1759 let value = Number::parse(context, input)?;
1760 Ok(Self { tag, value })
1761 }
1762}
1763
1764impl FontVariationSettings {
1765 pub fn parse_for_font_face_rule<'i, 't>(
1768 context: &ParserContext,
1769 input: &mut Parser<'i, 't>,
1770 ) -> Result<Self, ParseError<'i>> {
1771 let settings = FontVariationSettings::parse(context, input)?;
1772 if settings
1773 .0
1774 .iter()
1775 .any(|setting| setting.value.resolve().is_none())
1776 {
1777 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1778 }
1779 Ok(settings)
1780 }
1781}
1782
1783#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1787pub enum MetricsOverride {
1788 Override(NonNegativePercentage),
1790 Normal,
1792}
1793
1794impl MetricsOverride {
1795 #[inline]
1796 pub fn normal() -> MetricsOverride {
1798 MetricsOverride::Normal
1799 }
1800
1801 #[inline]
1807 pub fn compute(&self) -> Option<ComputedPercentage> {
1808 Some(ComputedPercentage(match self {
1809 MetricsOverride::Normal => -1.0,
1810 MetricsOverride::Override(percent) => percent.compute()?.0,
1811 }))
1812 }
1813}
1814
1815#[derive(
1816 Clone,
1817 Copy,
1818 Debug,
1819 Deserialize,
1820 MallocSizeOf,
1821 Parse,
1822 PartialEq,
1823 Serialize,
1824 SpecifiedValueInfo,
1825 ToComputedValue,
1826 ToCss,
1827 ToResolvedValue,
1828 ToShmem,
1829 ToTyped,
1830)]
1831#[repr(u8)]
1832pub enum XTextScale {
1834 All,
1836 ZoomOnly,
1838 None,
1840}
1841
1842impl XTextScale {
1843 #[inline]
1845 pub fn text_zoom_enabled(self) -> bool {
1846 self != Self::None
1847 }
1848}
1849
1850#[derive(
1851 Clone,
1852 Debug,
1853 Deserialize,
1854 Eq,
1855 Hash,
1856 MallocSizeOf,
1857 PartialEq,
1858 Serialize,
1859 SpecifiedValueInfo,
1860 ToComputedValue,
1861 ToCss,
1862 ToResolvedValue,
1863 ToShmem,
1864 ToTyped,
1865)]
1866pub struct XLang(#[css(skip)] pub Atom);
1868
1869impl XLang {
1870 #[inline]
1871 pub fn get_initial_value() -> XLang {
1873 XLang(atom!(""))
1874 }
1875}
1876
1877impl Parse for XLang {
1878 fn parse<'i, 't>(
1879 _: &ParserContext,
1880 input: &mut Parser<'i, 't>,
1881 ) -> Result<XLang, ParseError<'i>> {
1882 debug_assert!(
1883 false,
1884 "Should be set directly by presentation attributes only."
1885 );
1886 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1887 }
1888}
1889
1890#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1891#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1892pub struct MozScriptMinSize(pub NoCalcLength);
1895
1896impl MozScriptMinSize {
1897 #[inline]
1898 pub fn get_initial_value() -> Length {
1900 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
1901 }
1902}
1903
1904impl Parse for MozScriptMinSize {
1905 fn parse<'i, 't>(
1906 _: &ParserContext,
1907 input: &mut Parser<'i, 't>,
1908 ) -> Result<MozScriptMinSize, ParseError<'i>> {
1909 debug_assert!(
1910 false,
1911 "Should be set directly by presentation attributes only."
1912 );
1913 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1914 }
1915}
1916
1917#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1920#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]
1921pub enum MathDepth {
1922 AutoAdd,
1924
1925 #[css(function)]
1927 Add(Integer),
1928
1929 Absolute(Integer),
1931}
1932
1933impl Parse for MathDepth {
1934 fn parse<'i, 't>(
1935 context: &ParserContext,
1936 input: &mut Parser<'i, 't>,
1937 ) -> Result<MathDepth, ParseError<'i>> {
1938 if input
1939 .try_parse(|i| i.expect_ident_matching("auto-add"))
1940 .is_ok()
1941 {
1942 return Ok(MathDepth::AutoAdd);
1943 }
1944 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
1945 return Ok(MathDepth::Absolute(math_depth_value));
1946 }
1947 input.expect_function_matching("add")?;
1948 let math_depth_delta_value =
1949 input.parse_nested_block(|input| Integer::parse(context, input))?;
1950 Ok(MathDepth::Add(math_depth_delta_value))
1951 }
1952}
1953
1954#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1955#[derive(
1956 Clone,
1957 Copy,
1958 Debug,
1959 PartialEq,
1960 SpecifiedValueInfo,
1961 ToComputedValue,
1962 ToCss,
1963 ToResolvedValue,
1964 ToShmem,
1965)]
1966pub struct MozScriptSizeMultiplier(pub f32);
1971
1972impl MozScriptSizeMultiplier {
1973 #[inline]
1974 pub fn get_initial_value() -> MozScriptSizeMultiplier {
1976 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
1977 }
1978}
1979
1980impl Parse for MozScriptSizeMultiplier {
1981 fn parse<'i, 't>(
1982 _: &ParserContext,
1983 input: &mut Parser<'i, 't>,
1984 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
1985 debug_assert!(
1986 false,
1987 "Should be set directly by presentation attributes only."
1988 );
1989 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1990 }
1991}
1992
1993impl From<f32> for MozScriptSizeMultiplier {
1994 fn from(v: f32) -> Self {
1995 MozScriptSizeMultiplier(v)
1996 }
1997}
1998
1999impl From<MozScriptSizeMultiplier> for f32 {
2000 fn from(v: MozScriptSizeMultiplier) -> f32 {
2001 v.0
2002 }
2003}
2004
2005pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
2007
2008impl ToComputedValue for LineHeight {
2009 type ComputedValue = computed::LineHeight;
2010
2011 #[inline]
2012 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
2013 match self {
2014 GenericLineHeight::Normal => GenericLineHeight::Normal,
2015 GenericLineHeight::Number(ref number) => {
2016 GenericLineHeight::Number(number.to_computed_value(context))
2017 },
2018 GenericLineHeight::Length(ref non_negative_lp) => {
2019 let result = match non_negative_lp.0 {
2020 LengthPercentage::Length(ref length) if length.length_unit().is_absolute() => {
2021 context.maybe_zoom_text(length.to_computed_value(context))
2022 },
2023 LengthPercentage::Length(ref length) => {
2024 length.to_computed_value_with_base_size(
2029 context,
2030 FontBaseSize::CurrentStyle,
2031 LineHeightBase::InheritedStyle,
2032 )
2033 },
2034 LengthPercentage::Percentage(ref p) => NoCalcLength::from_em(p.get())
2035 .to_computed_value_with_base_size(
2036 context,
2037 FontBaseSize::CurrentStyle,
2038 LineHeightBase::InheritedStyle,
2039 ),
2040 LengthPercentage::Calc(ref calc) => {
2041 let computed_calc = calc.to_computed_value_zoomed(
2042 context,
2043 FontBaseSize::CurrentStyle,
2044 LineHeightBase::InheritedStyle,
2045 );
2046 let base = context.style().get_font().clone_font_size().computed_size();
2047 computed_calc.resolve(base)
2048 },
2049 };
2050 GenericLineHeight::Length(result.into())
2051 },
2052 }
2053 }
2054
2055 #[inline]
2056 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
2057 match *computed {
2058 GenericLineHeight::Normal => GenericLineHeight::Normal,
2059 GenericLineHeight::Number(ref number) => {
2060 GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
2061 },
2062 GenericLineHeight::Length(ref length) => {
2063 GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
2064 },
2065 }
2066 }
2067}
2068
2069#[repr(C)]
2071pub struct QueryFontMetricsFlags(u8);
2072
2073bitflags! {
2074 impl QueryFontMetricsFlags: u8 {
2075 const USE_USER_FONT_SET = 1 << 0;
2077 const NEEDS_CH = 1 << 1;
2079 const NEEDS_IC = 1 << 2;
2081 const NEEDS_MATH_SCALES = 1 << 3;
2083 }
2084}