1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5 //! Specified values for font properties
6
7 use crate::parser::{Parse, ParserContext};
8 use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
9 use crate::values::computed::{font as computed, Length, NonNegativeLength};
10 use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage};
11 use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
12 use crate::values::computed::FontSizeAdjust as ComputedFontSizeAdjust;
13 use crate::values::generics::font::VariationValue;
14 use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag, GenericFontSizeAdjust};
15 use crate::values::generics::NonNegative;
16 use crate::values::specified::length::{FontBaseSize, AU_PER_PT, AU_PER_PX};
17 use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
18 use crate::values::specified::{NoCalcLength, NonNegativeNumber, Number, NonNegativePercentage};
19 use crate::values::CustomIdent;
20 use crate::Atom;
21 use cssparser::{Parser, Token};
22 #[cfg(feature = "gecko")]
23 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
24 use std::fmt::{self, Write};
25 use style_traits::values::SequenceWriter;
26 use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
27 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
28
29 // FIXME(emilio): The system font code is copy-pasta, and should be cleaned up.
30 macro_rules! system_font_methods {
31 ($ty:ident, $field:ident) => {
32 system_font_methods!($ty);
33
34 fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
35 debug_assert!(matches!(*self, $ty::System(..)));
36 #[cfg(feature = "gecko")]
37 {
38 _context.cached_system_font.as_ref().unwrap().$field.clone()
39 }
40 #[cfg(feature = "servo")]
41 {
42 unreachable!()
43 }
44 }
45 };
46
47 ($ty:ident) => {
48 /// Get a specified value that represents a system font.
49 pub fn system_font(f: SystemFont) -> Self {
50 $ty::System(f)
51 }
52
53 /// Retreive a SystemFont from the specified value.
54 pub fn get_system(&self) -> Option<SystemFont> {
55 if let $ty::System(s) = *self {
56 Some(s)
57 } else {
58 None
59 }
60 }
61 };
62 }
63
64 /// System fonts.
65 #[repr(u8)]
66 #[derive(
67 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem
68 )]
69 #[allow(missing_docs)]
70 pub enum SystemFont {
71 Caption,
72 Icon,
73 Menu,
74 MessageBox,
75 SmallCaption,
76 StatusBar,
77 MozWindow,
78 MozDocument,
79 MozWorkspace,
80 MozDesktop,
81 MozInfo,
82 MozDialog,
83 MozButton,
84 MozPullDownMenu,
85 MozList,
86 MozField,
87 #[css(skip)]
88 End, // Just for indexing purposes.
89 }
90
91 const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
92 const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
93
94 /// The minimum font-weight value per:
95 ///
96 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
97 pub const MIN_FONT_WEIGHT: f32 = 1.;
98
99 /// The maximum font-weight value per:
100 ///
101 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
102 pub const MAX_FONT_WEIGHT: f32 = 1000.;
103
104 /// A specified font-weight value.
105 ///
106 /// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
107 #[derive(
108 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
109 )]
110 pub enum FontWeight {
111 /// `<font-weight-absolute>`
112 Absolute(AbsoluteFontWeight),
113 /// Bolder variant
114 Bolder,
115 /// Lighter variant
116 Lighter,
117 /// System font variant.
118 #[css(skip)]
119 System(SystemFont),
120 }
121
122 impl FontWeight {
123 system_font_methods!(FontWeight, font_weight);
124
125 /// `normal`
126 #[inline]
normal() -> Self127 pub fn normal() -> Self {
128 FontWeight::Absolute(AbsoluteFontWeight::Normal)
129 }
130
131 /// Get a specified FontWeight from a gecko keyword
from_gecko_keyword(kw: u32) -> Self132 pub fn from_gecko_keyword(kw: u32) -> Self {
133 debug_assert!(kw % 100 == 0);
134 debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
135 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
136 }
137 }
138
139 impl ToComputedValue for FontWeight {
140 type ComputedValue = computed::FontWeight;
141
142 #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue143 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
144 match *self {
145 FontWeight::Absolute(ref abs) => abs.compute(),
146 FontWeight::Bolder => context
147 .builder
148 .get_parent_font()
149 .clone_font_weight()
150 .bolder(),
151 FontWeight::Lighter => context
152 .builder
153 .get_parent_font()
154 .clone_font_weight()
155 .lighter(),
156 FontWeight::System(_) => self.compute_system(context),
157 }
158 }
159
160 #[inline]
from_computed_value(computed: &computed::FontWeight) -> Self161 fn from_computed_value(computed: &computed::FontWeight) -> Self {
162 FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
163 &computed.0,
164 )))
165 }
166 }
167
168 /// An absolute font-weight value for a @font-face rule.
169 ///
170 /// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values
171 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
172 pub enum AbsoluteFontWeight {
173 /// A `<number>`, with the additional constraints specified in:
174 ///
175 /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
176 Weight(Number),
177 /// Normal font weight. Same as 400.
178 Normal,
179 /// Bold font weight. Same as 700.
180 Bold,
181 }
182
183 impl AbsoluteFontWeight {
184 /// Returns the computed value for this absolute font weight.
compute(&self) -> computed::FontWeight185 pub fn compute(&self) -> computed::FontWeight {
186 match *self {
187 AbsoluteFontWeight::Weight(weight) => {
188 computed::FontWeight(weight.get().max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT))
189 },
190 AbsoluteFontWeight::Normal => computed::FontWeight::normal(),
191 AbsoluteFontWeight::Bold => computed::FontWeight::bold(),
192 }
193 }
194 }
195
196 impl Parse for AbsoluteFontWeight {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>197 fn parse<'i, 't>(
198 context: &ParserContext,
199 input: &mut Parser<'i, 't>,
200 ) -> Result<Self, ParseError<'i>> {
201 if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
202 // We could add another AllowedNumericType value, but it doesn't
203 // seem worth it just for a single property with such a weird range,
204 // so we do the clamping here manually.
205 if !number.was_calc() &&
206 (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
207 {
208 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
209 }
210 return Ok(AbsoluteFontWeight::Weight(number));
211 }
212
213 Ok(try_match_ident_ignore_ascii_case! { input,
214 "normal" => AbsoluteFontWeight::Normal,
215 "bold" => AbsoluteFontWeight::Bold,
216 })
217 }
218 }
219
220 /// The specified value of the `font-style` property, without the system font
221 /// crap.
222 pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
223
224 impl ToCss for SpecifiedFontStyle {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,225 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
226 where
227 W: Write,
228 {
229 match *self {
230 generics::FontStyle::Normal => dest.write_str("normal"),
231 generics::FontStyle::Italic => dest.write_str("italic"),
232 generics::FontStyle::Oblique(ref angle) => {
233 dest.write_str("oblique")?;
234 if *angle != Self::default_angle() {
235 dest.write_char(' ')?;
236 angle.to_css(dest)?;
237 }
238 Ok(())
239 },
240 }
241 }
242 }
243
244 impl Parse for SpecifiedFontStyle {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>245 fn parse<'i, 't>(
246 context: &ParserContext,
247 input: &mut Parser<'i, 't>,
248 ) -> Result<Self, ParseError<'i>> {
249 Ok(try_match_ident_ignore_ascii_case! { input,
250 "normal" => generics::FontStyle::Normal,
251 "italic" => generics::FontStyle::Italic,
252 "oblique" => {
253 let angle = input.try_parse(|input| Self::parse_angle(context, input))
254 .unwrap_or_else(|_| Self::default_angle());
255
256 generics::FontStyle::Oblique(angle)
257 },
258 })
259 }
260 }
261
262 impl ToComputedValue for SpecifiedFontStyle {
263 type ComputedValue = computed::FontStyle;
264
to_computed_value(&self, _: &Context) -> Self::ComputedValue265 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
266 match *self {
267 generics::FontStyle::Normal => generics::FontStyle::Normal,
268 generics::FontStyle::Italic => generics::FontStyle::Italic,
269 generics::FontStyle::Oblique(ref angle) => {
270 generics::FontStyle::Oblique(FontStyleAngle(Self::compute_angle(angle)))
271 },
272 }
273 }
274
from_computed_value(computed: &Self::ComputedValue) -> Self275 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
276 match *computed {
277 generics::FontStyle::Normal => generics::FontStyle::Normal,
278 generics::FontStyle::Italic => generics::FontStyle::Italic,
279 generics::FontStyle::Oblique(ref angle) => {
280 generics::FontStyle::Oblique(Angle::from_computed_value(&angle.0))
281 },
282 }
283 }
284 }
285
286 /// The default angle for `font-style: oblique`.
287 ///
288 /// NOTE(emilio): As of right now this diverges from the spec, which specifies
289 /// 20, because it's not updated yet to account for the resolution in:
290 ///
291 /// https://github.com/w3c/csswg-drafts/issues/2295
292 pub const DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES: f32 = 14.;
293
294 /// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle:
295 ///
296 /// Values less than -90deg or values greater than 90deg are
297 /// invalid and are treated as parse errors.
298 ///
299 /// The maximum angle value that `font-style: oblique` should compute to.
300 pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
301
302 /// The minimum angle value that `font-style: oblique` should compute to.
303 pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
304
305 impl SpecifiedFontStyle {
306 /// Gets a clamped angle in degrees from a specified Angle.
compute_angle_degrees(angle: &Angle) -> f32307 pub fn compute_angle_degrees(angle: &Angle) -> f32 {
308 angle
309 .degrees()
310 .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
311 .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
312 }
313
compute_angle(angle: &Angle) -> ComputedAngle314 fn compute_angle(angle: &Angle) -> ComputedAngle {
315 ComputedAngle::from_degrees(Self::compute_angle_degrees(angle))
316 }
317
318 /// Parse a suitable angle for font-style: oblique.
parse_angle<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Angle, ParseError<'i>>319 pub fn parse_angle<'i, 't>(
320 context: &ParserContext,
321 input: &mut Parser<'i, 't>,
322 ) -> Result<Angle, ParseError<'i>> {
323 let angle = Angle::parse(context, input)?;
324 if angle.was_calc() {
325 return Ok(angle);
326 }
327
328 let degrees = angle.degrees();
329 if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES ||
330 degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
331 {
332 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
333 }
334 return Ok(angle);
335 }
336
337 /// The default angle for `font-style: oblique`.
default_angle() -> Angle338 pub fn default_angle() -> Angle {
339 Angle::from_degrees(
340 DEFAULT_FONT_STYLE_OBLIQUE_ANGLE_DEGREES,
341 /* was_calc = */ false,
342 )
343 }
344 }
345
346 /// The specified value of the `font-style` property.
347 #[derive(
348 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
349 )]
350 #[allow(missing_docs)]
351 pub enum FontStyle {
352 Specified(SpecifiedFontStyle),
353 #[css(skip)]
354 System(SystemFont),
355 }
356
357 impl FontStyle {
358 /// Return the `normal` value.
359 #[inline]
normal() -> Self360 pub fn normal() -> Self {
361 FontStyle::Specified(generics::FontStyle::Normal)
362 }
363
364 system_font_methods!(FontStyle, font_style);
365 }
366
367 impl ToComputedValue for FontStyle {
368 type ComputedValue = computed::FontStyle;
369
to_computed_value(&self, context: &Context) -> Self::ComputedValue370 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
371 match *self {
372 FontStyle::Specified(ref specified) => specified.to_computed_value(context),
373 FontStyle::System(..) => self.compute_system(context),
374 }
375 }
376
from_computed_value(computed: &Self::ComputedValue) -> Self377 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
378 FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
379 }
380 }
381
382 /// A value for the `font-stretch` property.
383 ///
384 /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
385 #[allow(missing_docs)]
386 #[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
387 #[repr(u8)]
388 pub enum FontStretch {
389 Stretch(NonNegativePercentage),
390 Keyword(FontStretchKeyword),
391 #[css(skip)]
392 System(SystemFont),
393 }
394
395 /// A keyword value for `font-stretch`.
396 #[derive(
397 Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
398 )]
399 #[allow(missing_docs)]
400 pub enum FontStretchKeyword {
401 Normal,
402 Condensed,
403 UltraCondensed,
404 ExtraCondensed,
405 SemiCondensed,
406 SemiExpanded,
407 Expanded,
408 ExtraExpanded,
409 UltraExpanded,
410 }
411
412 impl FontStretchKeyword {
413 /// Resolves the value of the keyword as specified in:
414 ///
415 /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
compute(&self) -> ComputedPercentage416 pub fn compute(&self) -> ComputedPercentage {
417 use self::FontStretchKeyword::*;
418 ComputedPercentage(match *self {
419 UltraCondensed => 0.5,
420 ExtraCondensed => 0.625,
421 Condensed => 0.75,
422 SemiCondensed => 0.875,
423 Normal => 1.,
424 SemiExpanded => 1.125,
425 Expanded => 1.25,
426 ExtraExpanded => 1.5,
427 UltraExpanded => 2.,
428 })
429 }
430
431 /// Does the opposite operation to `compute`, in order to serialize keywords
432 /// if possible.
from_percentage(percentage: f32) -> Option<Self>433 pub fn from_percentage(percentage: f32) -> Option<Self> {
434 use self::FontStretchKeyword::*;
435 // NOTE(emilio): Can't use `match` because of rust-lang/rust#41620.
436 if percentage == 0.5 {
437 return Some(UltraCondensed);
438 }
439 if percentage == 0.625 {
440 return Some(ExtraCondensed);
441 }
442 if percentage == 0.75 {
443 return Some(Condensed);
444 }
445 if percentage == 0.875 {
446 return Some(SemiCondensed);
447 }
448 if percentage == 1. {
449 return Some(Normal);
450 }
451 if percentage == 1.125 {
452 return Some(SemiExpanded);
453 }
454 if percentage == 1.25 {
455 return Some(Expanded);
456 }
457 if percentage == 1.5 {
458 return Some(ExtraExpanded);
459 }
460 if percentage == 2. {
461 return Some(UltraExpanded);
462 }
463 None
464 }
465 }
466
467 impl FontStretch {
468 /// `normal`.
normal() -> Self469 pub fn normal() -> Self {
470 FontStretch::Keyword(FontStretchKeyword::Normal)
471 }
472
473 system_font_methods!(FontStretch, font_stretch);
474 }
475
476 impl ToComputedValue for FontStretch {
477 type ComputedValue = computed::FontStretch;
478
to_computed_value(&self, context: &Context) -> Self::ComputedValue479 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
480 match *self {
481 FontStretch::Stretch(ref percentage) => {
482 computed::FontStretch(percentage.to_computed_value(context))
483 },
484 FontStretch::Keyword(ref kw) => computed::FontStretch(NonNegative(kw.compute())),
485 FontStretch::System(_) => self.compute_system(context),
486 }
487 }
488
from_computed_value(computed: &Self::ComputedValue) -> Self489 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
490 FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative((computed.0).0)))
491 }
492 }
493
494 /// CSS font keywords
495 #[derive(
496 Animate,
497 Clone,
498 ComputeSquaredDistance,
499 Copy,
500 Debug,
501 MallocSizeOf,
502 Parse,
503 PartialEq,
504 SpecifiedValueInfo,
505 ToAnimatedValue,
506 ToAnimatedZero,
507 ToComputedValue,
508 ToCss,
509 ToResolvedValue,
510 ToShmem,
511 Serialize,
512 Deserialize,
513 )]
514 #[allow(missing_docs)]
515 #[repr(u8)]
516 pub enum FontSizeKeyword {
517 #[css(keyword = "xx-small")]
518 XXSmall,
519 XSmall,
520 Small,
521 Medium,
522 Large,
523 XLarge,
524 #[css(keyword = "xx-large")]
525 XXLarge,
526 #[css(keyword = "xxx-large")]
527 XXXLarge,
528 #[css(skip)]
529 None,
530 }
531
532 impl FontSizeKeyword {
533 /// Convert to an HTML <font size> value
534 #[inline]
html_size(self) -> u8535 pub fn html_size(self) -> u8 {
536 self as u8
537 }
538 }
539
540 impl Default for FontSizeKeyword {
default() -> Self541 fn default() -> Self {
542 FontSizeKeyword::Medium
543 }
544 }
545
546 #[derive(
547 Animate,
548 Clone,
549 ComputeSquaredDistance,
550 Copy,
551 Debug,
552 MallocSizeOf,
553 PartialEq,
554 ToAnimatedValue,
555 ToAnimatedZero,
556 ToComputedValue,
557 ToCss,
558 ToResolvedValue,
559 ToShmem,
560 )]
561 #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
562 /// Additional information for keyword-derived font sizes.
563 pub struct KeywordInfo {
564 /// The keyword used
565 pub kw: FontSizeKeyword,
566 /// A factor to be multiplied by the computed size of the keyword
567 #[css(skip)]
568 pub factor: f32,
569 /// An additional fixed offset to add to the kw * factor in the case of
570 /// `calc()`.
571 #[css(skip)]
572 pub offset: CSSPixelLength,
573 }
574
575 impl KeywordInfo {
576 /// KeywordInfo value for font-size: medium
medium() -> Self577 pub fn medium() -> Self {
578 Self::new(FontSizeKeyword::Medium)
579 }
580
581 /// KeywordInfo value for font-size: none
none() -> Self582 pub fn none() -> Self {
583 Self::new(FontSizeKeyword::None)
584 }
585
new(kw: FontSizeKeyword) -> Self586 fn new(kw: FontSizeKeyword) -> Self {
587 KeywordInfo {
588 kw,
589 factor: 1.,
590 offset: CSSPixelLength::new(0.),
591 }
592 }
593
594 /// Computes the final size for this font-size keyword, accounting for
595 /// text-zoom.
to_computed_value(&self, context: &Context) -> CSSPixelLength596 fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
597 debug_assert_ne!(self.kw, FontSizeKeyword::None);
598 let base = context.maybe_zoom_text(self.kw.to_length(context).0);
599 base * self.factor + context.maybe_zoom_text(self.offset)
600 }
601
602 /// Given a parent keyword info (self), apply an additional factor/offset to
603 /// it.
compose(self, factor: f32) -> Self604 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
616 impl SpecifiedValueInfo for KeywordInfo {
collect_completion_keywords(f: KeywordsCollectFn)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)]
623 /// A specified font-size value
624 pub enum FontSize {
625 /// A length; e.g. 10px.
626 Length(LengthPercentage),
627 /// A keyword value, along with a ratio and absolute offset.
628 /// The ratio in any specified keyword value
629 /// will be 1 (with offset 0), but we cascade keywordness even
630 /// after font-relative (percent and em) values
631 /// have been applied, which is where the ratio
632 /// comes in. The offset comes in if we cascaded a calc value,
633 /// where the font-relative portion (em and percentage) will
634 /// go into the ratio, and the remaining units all computed together
635 /// will go into the offset.
636 /// See bug 1355707.
637 Keyword(KeywordInfo),
638 /// font-size: smaller
639 Smaller,
640 /// font-size: larger
641 Larger,
642 /// Derived from a specified system font.
643 #[css(skip)]
644 System(SystemFont),
645 }
646
647 /// Specifies a prioritized list of font family names or generic family names.
648 #[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
649 #[cfg_attr(feature = "servo", derive(Hash))]
650 pub enum FontFamily {
651 /// List of `font-family`
652 #[css(comma)]
653 Values(#[css(iterable)] FontFamilyList),
654 /// System font
655 #[css(skip)]
656 System(SystemFont),
657 }
658
659 impl FontFamily {
660 system_font_methods!(FontFamily, font_family);
661
662 /// Parse a specified font-family value
parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>>663 pub fn parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
664 let values = input.parse_comma_separated(SingleFontFamily::parse)?;
665 Ok(FontFamily::Values(FontFamilyList {
666 list: crate::ArcSlice::from_iter(values.into_iter()),
667 fallback: computed::GenericFontFamily::None,
668 }))
669 }
670 }
671
672 impl ToComputedValue for FontFamily {
673 type ComputedValue = computed::FontFamily;
674
to_computed_value(&self, context: &Context) -> Self::ComputedValue675 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
676 match *self {
677 FontFamily::Values(ref list) => computed::FontFamily {
678 families: list.clone(),
679 is_system_font: false,
680 },
681 FontFamily::System(_) => self.compute_system(context),
682 }
683 }
684
from_computed_value(other: &computed::FontFamily) -> Self685 fn from_computed_value(other: &computed::FontFamily) -> Self {
686 FontFamily::Values(other.families.clone())
687 }
688 }
689
690 #[cfg(feature = "gecko")]
691 impl MallocSizeOf for FontFamily {
size_of(&self, ops: &mut MallocSizeOfOps) -> usize692 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
693 match *self {
694 FontFamily::Values(ref v) => {
695 // Although the family list is refcounted, we always attribute
696 // its size to the specified value.
697 v.list.unconditional_size_of(ops)
698 },
699 FontFamily::System(_) => 0,
700 }
701 }
702 }
703
704 impl Parse for FontFamily {
705 /// <family-name>#
706 /// <family-name> = <string> | [ <ident>+ ]
707 /// TODO: <generic-family>
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontFamily, ParseError<'i>>708 fn parse<'i, 't>(
709 _: &ParserContext,
710 input: &mut Parser<'i, 't>,
711 ) -> Result<FontFamily, ParseError<'i>> {
712 FontFamily::parse_specified(input)
713 }
714 }
715
716 impl SpecifiedValueInfo for FontFamily {}
717
718 /// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other way around
719 /// because we want the former to exclude generic family keywords.
720 impl Parse for FamilyName {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>721 fn parse<'i, 't>(
722 _: &ParserContext,
723 input: &mut Parser<'i, 't>,
724 ) -> Result<Self, ParseError<'i>> {
725 match SingleFontFamily::parse(input) {
726 Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
727 Ok(SingleFontFamily::Generic(_)) => {
728 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
729 },
730 Err(e) => Err(e),
731 }
732 }
733 }
734
735 /// Preserve the readability of text when font fallback occurs
736 #[derive(
737 Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
738 )]
739 #[allow(missing_docs)]
740 pub enum FontSizeAdjust {
741 Value(GenericFontSizeAdjust<NonNegativeNumber>),
742 #[css(skip)]
743 System(SystemFont),
744 }
745
746 impl FontSizeAdjust {
747 #[inline]
748 /// Default value of font-size-adjust
none() -> Self749 pub fn none() -> Self {
750 FontSizeAdjust::Value(GenericFontSizeAdjust::None)
751 }
752
753 system_font_methods!(FontSizeAdjust, font_size_adjust);
754 }
755
756 impl Parse for FontSizeAdjust {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>757 fn parse<'i, 't>(
758 context: &ParserContext,
759 input: &mut Parser<'i, 't>,
760 ) -> Result<Self, ParseError<'i>> {
761 let location = input.current_source_location();
762 if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
763 let basis_enabled = static_prefs::pref!("layout.css.font-size-adjust.basis.enabled");
764 let basis = match_ignore_ascii_case! { &ident,
765 "none" => return Ok(FontSizeAdjust::none()),
766 // Check for size adjustment basis keywords if enabled.
767 "ex-height" if basis_enabled => GenericFontSizeAdjust::ExHeight,
768 "cap-height" if basis_enabled => GenericFontSizeAdjust::CapHeight,
769 "ch-width" if basis_enabled => GenericFontSizeAdjust::ChWidth,
770 "ic-width" if basis_enabled => GenericFontSizeAdjust::IcWidth,
771 "ic-height" if basis_enabled => GenericFontSizeAdjust::IcHeight,
772 // Unknown (or disabled) keyword.
773 _ => return Err(location.new_custom_error(
774 ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident)
775 )),
776 };
777 let value = NonNegativeNumber::parse(context, input)?;
778 return Ok(FontSizeAdjust::Value(basis(value)));
779 }
780 // Without a basis keyword, the number refers to the 'ex-height' metric.
781 let value = NonNegativeNumber::parse(context, input)?;
782 Ok(FontSizeAdjust::Value(GenericFontSizeAdjust::ExHeight(value)))
783 }
784 }
785
786 impl ToComputedValue for FontSizeAdjust {
787 type ComputedValue = ComputedFontSizeAdjust;
788
to_computed_value(&self, context: &Context) -> Self::ComputedValue789 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
790 match *self {
791 FontSizeAdjust::Value(v) => v.to_computed_value(context),
792 FontSizeAdjust::System(_) => self.compute_system(context),
793 }
794 }
795
from_computed_value(computed: &ComputedFontSizeAdjust) -> Self796 fn from_computed_value(computed: &ComputedFontSizeAdjust) -> Self {
797 Self::Value(ToComputedValue::from_computed_value(computed))
798 }
799 }
800
801 /// This is the ratio applied for font-size: larger
802 /// and smaller by both Firefox and Chrome
803 const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
804
805 /// The default font size.
806 pub const FONT_MEDIUM_PX: f32 = 16.0;
807
808 impl FontSizeKeyword {
809 #[inline]
810 #[cfg(feature = "servo")]
to_length(&self, _: &Context) -> NonNegativeLength811 fn to_length(&self, _: &Context) -> NonNegativeLength {
812 let medium = Length::new(FONT_MEDIUM_PX);
813 // https://drafts.csswg.org/css-fonts-3/#font-size-prop
814 NonNegative(match *self {
815 FontSizeKeyword::XXSmall => medium * 3.0 / 5.0,
816 FontSizeKeyword::XSmall => medium * 3.0 / 4.0,
817 FontSizeKeyword::Small => medium * 8.0 / 9.0,
818 FontSizeKeyword::Medium => medium,
819 FontSizeKeyword::Large => medium * 6.0 / 5.0,
820 FontSizeKeyword::XLarge => medium * 3.0 / 2.0,
821 FontSizeKeyword::XXLarge => medium * 2.0,
822 FontSizeKeyword::XXXLarge => medium * 3.0,
823 FontSizeKeyword::None => unreachable!(),
824 })
825 }
826
827 #[cfg(feature = "gecko")]
828 #[inline]
to_length(&self, cx: &Context) -> NonNegativeLength829 fn to_length(&self, cx: &Context) -> NonNegativeLength {
830 use crate::context::QuirksMode;
831
832 // The tables in this function are originally from
833 // nsRuleNode::CalcFontPointSize in Gecko:
834 //
835 // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150
836 //
837 // Mapping from base size and HTML size to pixels
838 // The first index is (base_size - 9), the second is the
839 // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
840 // since HTML size 0 is the same as 1.
841 //
842 // xxs xs s m l xl xxl -
843 // - 0/1 2 3 4 5 6 7
844 static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
845 [9, 9, 9, 9, 11, 14, 18, 27],
846 [9, 9, 9, 10, 12, 15, 20, 30],
847 [9, 9, 10, 11, 13, 17, 22, 33],
848 [9, 9, 10, 12, 14, 18, 24, 36],
849 [9, 10, 12, 13, 16, 20, 26, 39],
850 [9, 10, 12, 14, 17, 21, 28, 42],
851 [9, 10, 13, 15, 18, 23, 30, 45],
852 [9, 10, 13, 16, 18, 24, 32, 48],
853 ];
854
855 // This table gives us compatibility with WinNav4 for the default fonts only.
856 // In WinNav4, the default fonts were:
857 //
858 // Times/12pt == Times/16px at 96ppi
859 // Courier/10pt == Courier/13px at 96ppi
860 //
861 // xxs xs s m l xl xxl -
862 // - 1 2 3 4 5 6 7
863 static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
864 [9, 9, 9, 9, 11, 14, 18, 28],
865 [9, 9, 9, 10, 12, 15, 20, 31],
866 [9, 9, 9, 11, 13, 17, 22, 34],
867 [9, 9, 10, 12, 14, 18, 24, 37],
868 [9, 9, 10, 13, 16, 20, 26, 40],
869 [9, 9, 11, 14, 17, 21, 28, 42],
870 [9, 10, 12, 15, 17, 23, 30, 45],
871 [9, 10, 13, 16, 18, 24, 32, 48],
872 ];
873
874 static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
875
876 let ref gecko_font = cx.style().get_font().gecko();
877 let generic = gecko_font.mFont.family.families.single_generic().unwrap_or(computed::GenericFontFamily::None);
878 let base_size = unsafe {
879 Atom::with(gecko_font.mLanguage.mRawPtr, |atom| {
880 cx.font_metrics_provider.get_size(atom, generic)
881 })
882 };
883
884 let base_size_px = base_size.px().round() as i32;
885 let html_size = self.html_size() as usize;
886 NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
887 let mapping = if cx.quirks_mode == QuirksMode::Quirks {
888 QUIRKS_FONT_SIZE_MAPPING
889 } else {
890 FONT_SIZE_MAPPING
891 };
892 Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
893 } else {
894 base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
895 })
896 }
897 }
898
899 impl FontSize {
900 /// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
from_html_size(size: u8) -> Self901 pub fn from_html_size(size: u8) -> Self {
902 FontSize::Keyword(KeywordInfo::new(match size {
903 // If value is less than 1, let it be 1.
904 0 | 1 => FontSizeKeyword::XSmall,
905 2 => FontSizeKeyword::Small,
906 3 => FontSizeKeyword::Medium,
907 4 => FontSizeKeyword::Large,
908 5 => FontSizeKeyword::XLarge,
909 6 => FontSizeKeyword::XXLarge,
910 // If value is greater than 7, let it be 7.
911 _ => FontSizeKeyword::XXXLarge,
912 }))
913 }
914
915 /// Compute it against a given base font size
to_computed_value_against( &self, context: &Context, base_size: FontBaseSize, ) -> computed::FontSize916 pub fn to_computed_value_against(
917 &self,
918 context: &Context,
919 base_size: FontBaseSize,
920 ) -> computed::FontSize {
921 use crate::values::specified::length::FontRelativeLength;
922
923 let compose_keyword = |factor| {
924 context
925 .style()
926 .get_parent_font()
927 .clone_font_size()
928 .keyword_info
929 .compose(factor)
930 };
931 let mut info = KeywordInfo::none();
932 let size = match *self {
933 FontSize::Length(LengthPercentage::Length(NoCalcLength::FontRelative(value))) => {
934 if let FontRelativeLength::Em(em) = value {
935 // If the parent font was keyword-derived, this is too.
936 // Tack the em unit onto the factor
937 info = compose_keyword(em);
938 }
939 value.to_computed_value(context, base_size)
940 },
941 FontSize::Length(LengthPercentage::Length(NoCalcLength::ServoCharacterWidth(
942 value,
943 ))) => value.to_computed_value(base_size.resolve(context)),
944 FontSize::Length(LengthPercentage::Length(NoCalcLength::Absolute(ref l))) => {
945 context.maybe_zoom_text(l.to_computed_value(context))
946 },
947 FontSize::Length(LengthPercentage::Length(ref l)) => l.to_computed_value(context),
948 FontSize::Length(LengthPercentage::Percentage(pc)) => {
949 // If the parent font was keyword-derived, this is too.
950 // Tack the % onto the factor
951 info = compose_keyword(pc.0);
952 (base_size.resolve(context) * pc.0).normalized()
953 },
954 FontSize::Length(LengthPercentage::Calc(ref calc)) => {
955 let calc = calc.to_computed_value_zoomed(context, base_size);
956 calc.resolve(base_size.resolve(context))
957 },
958 FontSize::Keyword(i) => {
959 // As a specified keyword, this is keyword derived
960 info = i;
961 i.to_computed_value(context).clamp_to_non_negative()
962 },
963 FontSize::Smaller => {
964 info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
965 FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO)
966 .to_computed_value(context, base_size)
967 },
968 FontSize::Larger => {
969 info = compose_keyword(LARGER_FONT_SIZE_RATIO);
970 FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(context, base_size)
971 },
972
973 FontSize::System(_) => {
974 #[cfg(feature = "servo")]
975 {
976 unreachable!()
977 }
978 #[cfg(feature = "gecko")]
979 {
980 context
981 .cached_system_font
982 .as_ref()
983 .unwrap()
984 .font_size
985 .size
986 .0
987 }
988 },
989 };
990 computed::FontSize {
991 size: NonNegative(size),
992 keyword_info: info,
993 }
994 }
995 }
996
997 impl ToComputedValue for FontSize {
998 type ComputedValue = computed::FontSize;
999
1000 #[inline]
to_computed_value(&self, context: &Context) -> computed::FontSize1001 fn to_computed_value(&self, context: &Context) -> computed::FontSize {
1002 self.to_computed_value_against(context, FontBaseSize::InheritedStyle)
1003 }
1004
1005 #[inline]
from_computed_value(computed: &computed::FontSize) -> Self1006 fn from_computed_value(computed: &computed::FontSize) -> Self {
1007 FontSize::Length(LengthPercentage::Length(
1008 ToComputedValue::from_computed_value(&computed.size.0),
1009 ))
1010 }
1011 }
1012
1013 impl FontSize {
1014 system_font_methods!(FontSize);
1015
1016 /// Get initial value for specified font size.
1017 #[inline]
medium() -> Self1018 pub fn medium() -> Self {
1019 FontSize::Keyword(KeywordInfo::medium())
1020 }
1021
1022 /// Parses a font-size, with quirks.
parse_quirky<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, allow_quirks: AllowQuirks, ) -> Result<FontSize, ParseError<'i>>1023 pub fn parse_quirky<'i, 't>(
1024 context: &ParserContext,
1025 input: &mut Parser<'i, 't>,
1026 allow_quirks: AllowQuirks,
1027 ) -> Result<FontSize, ParseError<'i>> {
1028 if let Ok(lp) = input
1029 .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
1030 {
1031 return Ok(FontSize::Length(lp));
1032 }
1033
1034 if let Ok(kw) = input.try_parse(FontSizeKeyword::parse) {
1035 return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
1036 }
1037
1038 try_match_ident_ignore_ascii_case! { input,
1039 "smaller" => Ok(FontSize::Smaller),
1040 "larger" => Ok(FontSize::Larger),
1041 }
1042 }
1043 }
1044
1045 impl Parse for FontSize {
1046 /// <length> | <percentage> | <absolute-size> | <relative-size>
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontSize, ParseError<'i>>1047 fn parse<'i, 't>(
1048 context: &ParserContext,
1049 input: &mut Parser<'i, 't>,
1050 ) -> Result<FontSize, ParseError<'i>> {
1051 FontSize::parse_quirky(context, input, AllowQuirks::No)
1052 }
1053 }
1054
1055 bitflags! {
1056 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
1057 /// Flags of variant alternates in bit
1058 struct VariantAlternatesParsingFlags: u8 {
1059 /// None of variant alternates enabled
1060 const NORMAL = 0;
1061 /// Historical forms
1062 const HISTORICAL_FORMS = 0x01;
1063 /// Stylistic Alternates
1064 const STYLISTIC = 0x02;
1065 /// Stylistic Sets
1066 const STYLESET = 0x04;
1067 /// Character Variant
1068 const CHARACTER_VARIANT = 0x08;
1069 /// Swash glyphs
1070 const SWASH = 0x10;
1071 /// Ornaments glyphs
1072 const ORNAMENTS = 0x20;
1073 /// Annotation forms
1074 const ANNOTATION = 0x40;
1075 }
1076 }
1077
1078 #[derive(
1079 Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToResolvedValue, ToShmem,
1080 )]
1081 #[repr(C, u8)]
1082 /// Set of variant alternates
1083 pub enum VariantAlternates {
1084 /// Enables display of stylistic alternates
1085 #[css(function)]
1086 Stylistic(CustomIdent),
1087 /// Enables display with stylistic sets
1088 #[css(comma, function)]
1089 Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1090 /// Enables display of specific character variants
1091 #[css(comma, function)]
1092 CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
1093 /// Enables display of swash glyphs
1094 #[css(function)]
1095 Swash(CustomIdent),
1096 /// Enables replacement of default glyphs with ornaments
1097 #[css(function)]
1098 Ornaments(CustomIdent),
1099 /// Enables display of alternate annotation forms
1100 #[css(function)]
1101 Annotation(CustomIdent),
1102 /// Enables display of historical forms
1103 HistoricalForms,
1104 }
1105
1106 #[derive(
1107 Clone,
1108 Debug,
1109 Default,
1110 MallocSizeOf,
1111 PartialEq,
1112 SpecifiedValueInfo,
1113 ToCss,
1114 ToResolvedValue,
1115 ToShmem,
1116 )]
1117 #[repr(transparent)]
1118 /// List of Variant Alternates
1119 pub struct VariantAlternatesList(
1120 #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
1121 );
1122
1123 impl VariantAlternatesList {
1124 /// Returns the length of all variant alternates.
len(&self) -> usize1125 pub fn len(&self) -> usize {
1126 self.0.iter().fold(0, |acc, alternate| match *alternate {
1127 VariantAlternates::Swash(_) |
1128 VariantAlternates::Stylistic(_) |
1129 VariantAlternates::Ornaments(_) |
1130 VariantAlternates::Annotation(_) => acc + 1,
1131 VariantAlternates::Styleset(ref slice) |
1132 VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
1133 _ => acc,
1134 })
1135 }
1136 }
1137
1138 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1139 /// Control over the selection of these alternate glyphs
1140 pub enum FontVariantAlternates {
1141 /// Use alternative glyph from value
1142 Value(VariantAlternatesList),
1143 /// Use system font glyph
1144 #[css(skip)]
1145 System(SystemFont),
1146 }
1147
1148 impl FontVariantAlternates {
1149 #[inline]
1150 /// Get initial specified value with VariantAlternatesList
get_initial_specified_value() -> Self1151 pub fn get_initial_specified_value() -> Self {
1152 FontVariantAlternates::Value(Default::default())
1153 }
1154
1155 system_font_methods!(FontVariantAlternates, font_variant_alternates);
1156 }
1157
1158 impl ToComputedValue for FontVariantAlternates {
1159 type ComputedValue = computed::FontVariantAlternates;
1160
to_computed_value(&self, context: &Context) -> computed::FontVariantAlternates1161 fn to_computed_value(&self, context: &Context) -> computed::FontVariantAlternates {
1162 match *self {
1163 FontVariantAlternates::Value(ref v) => v.clone(),
1164 FontVariantAlternates::System(_) => self.compute_system(context),
1165 }
1166 }
1167
from_computed_value(other: &computed::FontVariantAlternates) -> Self1168 fn from_computed_value(other: &computed::FontVariantAlternates) -> Self {
1169 FontVariantAlternates::Value(other.clone())
1170 }
1171 }
1172
1173 impl Parse for FontVariantAlternates {
1174 /// normal |
1175 /// [ stylistic(<feature-value-name>) ||
1176 /// historical-forms ||
1177 /// styleset(<feature-value-name> #) ||
1178 /// character-variant(<feature-value-name> #) ||
1179 /// swash(<feature-value-name>) ||
1180 /// ornaments(<feature-value-name>) ||
1181 /// annotation(<feature-value-name>) ]
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantAlternates, ParseError<'i>>1182 fn parse<'i, 't>(
1183 _: &ParserContext,
1184 input: &mut Parser<'i, 't>,
1185 ) -> Result<FontVariantAlternates, ParseError<'i>> {
1186 if input
1187 .try_parse(|input| input.expect_ident_matching("normal"))
1188 .is_ok()
1189 {
1190 return Ok(FontVariantAlternates::Value(Default::default()));
1191 }
1192
1193 let mut alternates = Vec::new();
1194 let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
1195 macro_rules! check_if_parsed(
1196 ($input:expr, $flag:path) => (
1197 if parsed_alternates.contains($flag) {
1198 return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1199 }
1200 parsed_alternates |= $flag;
1201 )
1202 );
1203 while let Ok(_) = input.try_parse(|input| match *input.next()? {
1204 Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
1205 check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
1206 alternates.push(VariantAlternates::HistoricalForms);
1207 Ok(())
1208 },
1209 Token::Function(ref name) => {
1210 let name = name.clone();
1211 input.parse_nested_block(|i| {
1212 match_ignore_ascii_case! { &name,
1213 "swash" => {
1214 check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
1215 let location = i.current_source_location();
1216 let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1217 alternates.push(VariantAlternates::Swash(ident));
1218 Ok(())
1219 },
1220 "stylistic" => {
1221 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
1222 let location = i.current_source_location();
1223 let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1224 alternates.push(VariantAlternates::Stylistic(ident));
1225 Ok(())
1226 },
1227 "ornaments" => {
1228 check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
1229 let location = i.current_source_location();
1230 let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1231 alternates.push(VariantAlternates::Ornaments(ident));
1232 Ok(())
1233 },
1234 "annotation" => {
1235 check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
1236 let location = i.current_source_location();
1237 let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
1238 alternates.push(VariantAlternates::Annotation(ident));
1239 Ok(())
1240 },
1241 "styleset" => {
1242 check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
1243 let idents = i.parse_comma_separated(|i| {
1244 let location = i.current_source_location();
1245 CustomIdent::from_ident(location, i.expect_ident()?, &[])
1246 })?;
1247 alternates.push(VariantAlternates::Styleset(idents.into()));
1248 Ok(())
1249 },
1250 "character-variant" => {
1251 check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
1252 let idents = i.parse_comma_separated(|i| {
1253 let location = i.current_source_location();
1254 CustomIdent::from_ident(location, i.expect_ident()?, &[])
1255 })?;
1256 alternates.push(VariantAlternates::CharacterVariant(idents.into()));
1257 Ok(())
1258 },
1259 _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1260 }
1261 })
1262 },
1263 _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
1264 }) {}
1265
1266 if parsed_alternates.is_empty() {
1267 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1268 }
1269 Ok(FontVariantAlternates::Value(VariantAlternatesList(
1270 alternates.into(),
1271 )))
1272 }
1273 }
1274
1275 macro_rules! impl_variant_east_asian {
1276 {
1277 $(
1278 $(#[$($meta:tt)+])*
1279 $ident:ident / $css:expr => $gecko:ident = $value:expr,
1280 )+
1281 } => {
1282 bitflags! {
1283 #[derive(MallocSizeOf, ToResolvedValue, ToShmem)]
1284 /// Vairants for east asian variant
1285 pub struct VariantEastAsian: u16 {
1286 /// None of the features
1287 const NORMAL = 0;
1288 $(
1289 $(#[$($meta)+])*
1290 const $ident = $value;
1291 )+
1292 }
1293 }
1294
1295 impl ToCss for VariantEastAsian {
1296 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1297 where
1298 W: Write,
1299 {
1300 if self.is_empty() {
1301 return dest.write_str("normal");
1302 }
1303
1304 let mut writer = SequenceWriter::new(dest, " ");
1305 $(
1306 if self.intersects(VariantEastAsian::$ident) {
1307 writer.raw_item($css)?;
1308 }
1309 )+
1310 Ok(())
1311 }
1312 }
1313
1314 /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
1315 #[cfg(feature = "gecko")]
1316 #[inline]
1317 pub fn assert_variant_east_asian_matches() {
1318 use crate::gecko_bindings::structs;
1319 $(
1320 debug_assert_eq!(structs::$gecko as u16, VariantEastAsian::$ident.bits());
1321 )+
1322 }
1323
1324 impl SpecifiedValueInfo for VariantEastAsian {
1325 fn collect_completion_keywords(f: KeywordsCollectFn) {
1326 f(&["normal", $($css,)+]);
1327 }
1328 }
1329 }
1330 }
1331
1332 impl_variant_east_asian! {
1333 /// Enables rendering of JIS78 forms (OpenType feature: jp78)
1334 JIS78 / "jis78" => NS_FONT_VARIANT_EAST_ASIAN_JIS78 = 0x01,
1335 /// Enables rendering of JIS83 forms (OpenType feature: jp83).
1336 JIS83 / "jis83" => NS_FONT_VARIANT_EAST_ASIAN_JIS83 = 0x02,
1337 /// Enables rendering of JIS90 forms (OpenType feature: jp90).
1338 JIS90 / "jis90" => NS_FONT_VARIANT_EAST_ASIAN_JIS90 = 0x04,
1339 /// Enables rendering of JIS2004 forms (OpenType feature: jp04).
1340 JIS04 / "jis04" => NS_FONT_VARIANT_EAST_ASIAN_JIS04 = 0x08,
1341 /// Enables rendering of simplified forms (OpenType feature: smpl).
1342 SIMPLIFIED / "simplified" => NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED = 0x10,
1343 /// Enables rendering of traditional forms (OpenType feature: trad).
1344 TRADITIONAL / "traditional" => NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL = 0x20,
1345 /// Enables rendering of full-width variants (OpenType feature: fwid).
1346 FULL_WIDTH / "full-width" => NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH = 0x40,
1347 /// Enables rendering of proportionally-spaced variants (OpenType feature: pwid).
1348 PROPORTIONAL_WIDTH / "proportional-width" => NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH = 0x80,
1349 /// Enables display of ruby variant glyphs (OpenType feature: ruby).
1350 RUBY / "ruby" => NS_FONT_VARIANT_EAST_ASIAN_RUBY = 0x100,
1351 }
1352
1353 #[cfg(feature = "gecko")]
1354 impl VariantEastAsian {
1355 /// Obtain a specified value from a Gecko keyword value
1356 ///
1357 /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u16) -> Self1358 pub fn from_gecko_keyword(kw: u16) -> Self {
1359 Self::from_bits_truncate(kw)
1360 }
1361
1362 /// Transform into gecko keyword
to_gecko_keyword(self) -> u161363 pub fn to_gecko_keyword(self) -> u16 {
1364 self.bits()
1365 }
1366 }
1367
1368 #[cfg(feature = "gecko")]
1369 impl_gecko_keyword_conversions!(VariantEastAsian, u16);
1370
1371 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1372 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1373 /// Allows control of glyph substitution and sizing in East Asian text.
1374 pub enum FontVariantEastAsian {
1375 /// Value variant with `variant-east-asian`
1376 Value(VariantEastAsian),
1377 /// System font variant
1378 #[css(skip)]
1379 System(SystemFont),
1380 }
1381
1382 impl FontVariantEastAsian {
1383 #[inline]
1384 /// Get default `font-variant-east-asian` with `empty` variant
empty() -> Self1385 pub fn empty() -> Self {
1386 FontVariantEastAsian::Value(VariantEastAsian::empty())
1387 }
1388
1389 system_font_methods!(FontVariantEastAsian, font_variant_east_asian);
1390 }
1391
1392 impl ToComputedValue for FontVariantEastAsian {
1393 type ComputedValue = computed::FontVariantEastAsian;
1394
to_computed_value(&self, context: &Context) -> computed::FontVariantEastAsian1395 fn to_computed_value(&self, context: &Context) -> computed::FontVariantEastAsian {
1396 match *self {
1397 FontVariantEastAsian::Value(ref v) => v.clone(),
1398 FontVariantEastAsian::System(_) => self.compute_system(context),
1399 }
1400 }
1401
from_computed_value(other: &computed::FontVariantEastAsian) -> Self1402 fn from_computed_value(other: &computed::FontVariantEastAsian) -> Self {
1403 FontVariantEastAsian::Value(other.clone())
1404 }
1405 }
1406
1407 impl Parse for FontVariantEastAsian {
1408 /// normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ]
1409 /// <east-asian-variant-values> = [ jis78 | jis83 | jis90 | jis04 | simplified | traditional ]
1410 /// <east-asian-width-values> = [ full-width | proportional-width ]
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantEastAsian, ParseError<'i>>1411 fn parse<'i, 't>(
1412 _context: &ParserContext,
1413 input: &mut Parser<'i, 't>,
1414 ) -> Result<FontVariantEastAsian, ParseError<'i>> {
1415 let mut result = VariantEastAsian::empty();
1416
1417 if input
1418 .try_parse(|input| input.expect_ident_matching("normal"))
1419 .is_ok()
1420 {
1421 return Ok(FontVariantEastAsian::Value(result));
1422 }
1423
1424 while let Ok(flag) = input.try_parse(|input| {
1425 Ok(
1426 match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
1427 "jis78" =>
1428 exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1429 VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1430 VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1431 ) => VariantEastAsian::JIS78),
1432 "jis83" =>
1433 exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1434 VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1435 VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1436 ) => VariantEastAsian::JIS83),
1437 "jis90" =>
1438 exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1439 VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1440 VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1441 ) => VariantEastAsian::JIS90),
1442 "jis04" =>
1443 exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1444 VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1445 VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1446 ) => VariantEastAsian::JIS04),
1447 "simplified" =>
1448 exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1449 VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1450 VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1451 ) => VariantEastAsian::SIMPLIFIED),
1452 "traditional" =>
1453 exclusive_value!((result, VariantEastAsian::JIS78 | VariantEastAsian::JIS83 |
1454 VariantEastAsian::JIS90 | VariantEastAsian::JIS04 |
1455 VariantEastAsian::SIMPLIFIED | VariantEastAsian::TRADITIONAL
1456 ) => VariantEastAsian::TRADITIONAL),
1457 "full-width" =>
1458 exclusive_value!((result, VariantEastAsian::FULL_WIDTH |
1459 VariantEastAsian::PROPORTIONAL_WIDTH
1460 ) => VariantEastAsian::FULL_WIDTH),
1461 "proportional-width" =>
1462 exclusive_value!((result, VariantEastAsian::FULL_WIDTH |
1463 VariantEastAsian::PROPORTIONAL_WIDTH
1464 ) => VariantEastAsian::PROPORTIONAL_WIDTH),
1465 "ruby" =>
1466 exclusive_value!((result, VariantEastAsian::RUBY) => VariantEastAsian::RUBY),
1467 _ => return Err(()),
1468 },
1469 )
1470 }) {
1471 result.insert(flag);
1472 }
1473
1474 if !result.is_empty() {
1475 Ok(FontVariantEastAsian::Value(result))
1476 } else {
1477 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1478 }
1479 }
1480 }
1481
1482 macro_rules! impl_variant_ligatures {
1483 {
1484 $(
1485 $(#[$($meta:tt)+])*
1486 $ident:ident / $css:expr => $gecko:ident = $value:expr,
1487 )+
1488 } => {
1489 bitflags! {
1490 #[derive(MallocSizeOf, ToResolvedValue, ToShmem)]
1491 /// Variants of ligatures
1492 pub struct VariantLigatures: u16 {
1493 /// Specifies that common default features are enabled
1494 const NORMAL = 0;
1495 $(
1496 $(#[$($meta)+])*
1497 const $ident = $value;
1498 )+
1499 }
1500 }
1501
1502 impl ToCss for VariantLigatures {
1503 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1504 where
1505 W: Write,
1506 {
1507 if self.is_empty() {
1508 return dest.write_str("normal");
1509 }
1510 if self.contains(VariantLigatures::NONE) {
1511 return dest.write_str("none");
1512 }
1513
1514 let mut writer = SequenceWriter::new(dest, " ");
1515 $(
1516 if self.intersects(VariantLigatures::$ident) {
1517 writer.raw_item($css)?;
1518 }
1519 )+
1520 Ok(())
1521 }
1522 }
1523
1524 /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
1525 #[cfg(feature = "gecko")]
1526 #[inline]
1527 pub fn assert_variant_ligatures_matches() {
1528 use crate::gecko_bindings::structs;
1529 $(
1530 debug_assert_eq!(structs::$gecko as u16, VariantLigatures::$ident.bits());
1531 )+
1532 }
1533
1534 impl SpecifiedValueInfo for VariantLigatures {
1535 fn collect_completion_keywords(f: KeywordsCollectFn) {
1536 f(&["normal", $($css,)+]);
1537 }
1538 }
1539 }
1540 }
1541
1542 impl_variant_ligatures! {
1543 /// Specifies that all types of ligatures and contextual forms
1544 /// covered by this property are explicitly disabled
1545 NONE / "none" => NS_FONT_VARIANT_LIGATURES_NONE = 0x01,
1546 /// Enables display of common ligatures
1547 COMMON_LIGATURES / "common-ligatures" => NS_FONT_VARIANT_LIGATURES_COMMON = 0x02,
1548 /// Disables display of common ligatures
1549 NO_COMMON_LIGATURES / "no-common-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_COMMON = 0x04,
1550 /// Enables display of discretionary ligatures
1551 DISCRETIONARY_LIGATURES / "discretionary-ligatures" => NS_FONT_VARIANT_LIGATURES_DISCRETIONARY = 0x08,
1552 /// Disables display of discretionary ligatures
1553 NO_DISCRETIONARY_LIGATURES / "no-discretionary-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY = 0x10,
1554 /// Enables display of historical ligatures
1555 HISTORICAL_LIGATURES / "historical-ligatures" => NS_FONT_VARIANT_LIGATURES_HISTORICAL = 0x20,
1556 /// Disables display of historical ligatures
1557 NO_HISTORICAL_LIGATURES / "no-historical-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL = 0x40,
1558 /// Enables display of contextual alternates
1559 CONTEXTUAL / "contextual" => NS_FONT_VARIANT_LIGATURES_CONTEXTUAL = 0x80,
1560 /// Disables display of contextual alternates
1561 NO_CONTEXTUAL / "no-contextual" => NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL = 0x100,
1562 }
1563
1564 #[cfg(feature = "gecko")]
1565 impl VariantLigatures {
1566 /// Obtain a specified value from a Gecko keyword value
1567 ///
1568 /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u16) -> Self1569 pub fn from_gecko_keyword(kw: u16) -> Self {
1570 Self::from_bits_truncate(kw)
1571 }
1572
1573 /// Transform into gecko keyword
to_gecko_keyword(self) -> u161574 pub fn to_gecko_keyword(self) -> u16 {
1575 self.bits()
1576 }
1577 }
1578
1579 #[cfg(feature = "gecko")]
1580 impl_gecko_keyword_conversions!(VariantLigatures, u16);
1581
1582 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1583 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1584 /// Ligatures and contextual forms are ways of combining glyphs
1585 /// to produce more harmonized forms
1586 pub enum FontVariantLigatures {
1587 /// Value variant with `variant-ligatures`
1588 Value(VariantLigatures),
1589 /// System font variant
1590 #[css(skip)]
1591 System(SystemFont),
1592 }
1593
1594 impl FontVariantLigatures {
1595 system_font_methods!(FontVariantLigatures, font_variant_ligatures);
1596
1597 /// Default value of `font-variant-ligatures` as `empty`
1598 #[inline]
empty() -> FontVariantLigatures1599 pub fn empty() -> FontVariantLigatures {
1600 FontVariantLigatures::Value(VariantLigatures::empty())
1601 }
1602
1603 #[inline]
1604 /// Get `none` variant of `font-variant-ligatures`
none() -> FontVariantLigatures1605 pub fn none() -> FontVariantLigatures {
1606 FontVariantLigatures::Value(VariantLigatures::NONE)
1607 }
1608 }
1609
1610 impl ToComputedValue for FontVariantLigatures {
1611 type ComputedValue = computed::FontVariantLigatures;
1612
to_computed_value(&self, context: &Context) -> computed::FontVariantLigatures1613 fn to_computed_value(&self, context: &Context) -> computed::FontVariantLigatures {
1614 match *self {
1615 FontVariantLigatures::Value(ref v) => v.clone(),
1616 FontVariantLigatures::System(_) => self.compute_system(context),
1617 }
1618 }
1619
from_computed_value(other: &computed::FontVariantLigatures) -> Self1620 fn from_computed_value(other: &computed::FontVariantLigatures) -> Self {
1621 FontVariantLigatures::Value(other.clone())
1622 }
1623 }
1624
1625 impl Parse for FontVariantLigatures {
1626 /// normal | none |
1627 /// [ <common-lig-values> ||
1628 /// <discretionary-lig-values> ||
1629 /// <historical-lig-values> ||
1630 /// <contextual-alt-values> ]
1631 /// <common-lig-values> = [ common-ligatures | no-common-ligatures ]
1632 /// <discretionary-lig-values> = [ discretionary-ligatures | no-discretionary-ligatures ]
1633 /// <historical-lig-values> = [ historical-ligatures | no-historical-ligatures ]
1634 /// <contextual-alt-values> = [ contextual | no-contextual ]
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantLigatures, ParseError<'i>>1635 fn parse<'i, 't>(
1636 _context: &ParserContext,
1637 input: &mut Parser<'i, 't>,
1638 ) -> Result<FontVariantLigatures, ParseError<'i>> {
1639 let mut result = VariantLigatures::empty();
1640
1641 if input
1642 .try_parse(|input| input.expect_ident_matching("normal"))
1643 .is_ok()
1644 {
1645 return Ok(FontVariantLigatures::Value(result));
1646 }
1647 if input
1648 .try_parse(|input| input.expect_ident_matching("none"))
1649 .is_ok()
1650 {
1651 return Ok(FontVariantLigatures::Value(VariantLigatures::NONE));
1652 }
1653
1654 while let Ok(flag) = input.try_parse(|input| {
1655 Ok(
1656 match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
1657 "common-ligatures" =>
1658 exclusive_value!((result, VariantLigatures::COMMON_LIGATURES |
1659 VariantLigatures::NO_COMMON_LIGATURES
1660 ) => VariantLigatures::COMMON_LIGATURES),
1661 "no-common-ligatures" =>
1662 exclusive_value!((result, VariantLigatures::COMMON_LIGATURES |
1663 VariantLigatures::NO_COMMON_LIGATURES
1664 ) => VariantLigatures::NO_COMMON_LIGATURES),
1665 "discretionary-ligatures" =>
1666 exclusive_value!((result, VariantLigatures::DISCRETIONARY_LIGATURES |
1667 VariantLigatures::NO_DISCRETIONARY_LIGATURES
1668 ) => VariantLigatures::DISCRETIONARY_LIGATURES),
1669 "no-discretionary-ligatures" =>
1670 exclusive_value!((result, VariantLigatures::DISCRETIONARY_LIGATURES |
1671 VariantLigatures::NO_DISCRETIONARY_LIGATURES
1672 ) => VariantLigatures::NO_DISCRETIONARY_LIGATURES),
1673 "historical-ligatures" =>
1674 exclusive_value!((result, VariantLigatures::HISTORICAL_LIGATURES |
1675 VariantLigatures::NO_HISTORICAL_LIGATURES
1676 ) => VariantLigatures::HISTORICAL_LIGATURES),
1677 "no-historical-ligatures" =>
1678 exclusive_value!((result, VariantLigatures::HISTORICAL_LIGATURES |
1679 VariantLigatures::NO_HISTORICAL_LIGATURES
1680 ) => VariantLigatures::NO_HISTORICAL_LIGATURES),
1681 "contextual" =>
1682 exclusive_value!((result, VariantLigatures::CONTEXTUAL |
1683 VariantLigatures::NO_CONTEXTUAL
1684 ) => VariantLigatures::CONTEXTUAL),
1685 "no-contextual" =>
1686 exclusive_value!((result, VariantLigatures::CONTEXTUAL |
1687 VariantLigatures::NO_CONTEXTUAL
1688 ) => VariantLigatures::NO_CONTEXTUAL),
1689 _ => return Err(()),
1690 },
1691 )
1692 }) {
1693 result.insert(flag);
1694 }
1695
1696 if !result.is_empty() {
1697 Ok(FontVariantLigatures::Value(result))
1698 } else {
1699 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1700 }
1701 }
1702 }
1703
1704 macro_rules! impl_variant_numeric {
1705 {
1706 $(
1707 $(#[$($meta:tt)+])*
1708 $ident:ident / $css:expr => $gecko:ident = $value:expr,
1709 )+
1710 } => {
1711 bitflags! {
1712 #[derive(MallocSizeOf, ToResolvedValue, ToShmem)]
1713 /// Vairants of numeric values
1714 pub struct VariantNumeric: u8 {
1715 /// None of other variants are enabled.
1716 const NORMAL = 0;
1717 $(
1718 $(#[$($meta)+])*
1719 const $ident = $value;
1720 )+
1721 }
1722 }
1723
1724 impl ToCss for VariantNumeric {
1725 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1726 where
1727 W: Write,
1728 {
1729 if self.is_empty() {
1730 return dest.write_str("normal");
1731 }
1732
1733 let mut writer = SequenceWriter::new(dest, " ");
1734 $(
1735 if self.intersects(VariantNumeric::$ident) {
1736 writer.raw_item($css)?;
1737 }
1738 )+
1739 Ok(())
1740 }
1741 }
1742
1743 /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
1744 #[cfg(feature = "gecko")]
1745 #[inline]
1746 pub fn assert_variant_numeric_matches() {
1747 use crate::gecko_bindings::structs;
1748 $(
1749 debug_assert_eq!(structs::$gecko as u8, VariantNumeric::$ident.bits());
1750 )+
1751 }
1752
1753 impl SpecifiedValueInfo for VariantNumeric {
1754 fn collect_completion_keywords(f: KeywordsCollectFn) {
1755 f(&["normal", $($css,)+]);
1756 }
1757 }
1758 }
1759 }
1760
1761 impl_variant_numeric! {
1762 /// Enables display of lining numerals.
1763 LINING_NUMS / "lining-nums" => NS_FONT_VARIANT_NUMERIC_LINING = 0x01,
1764 /// Enables display of old-style numerals.
1765 OLDSTYLE_NUMS / "oldstyle-nums" => NS_FONT_VARIANT_NUMERIC_OLDSTYLE = 0x02,
1766 /// Enables display of proportional numerals.
1767 PROPORTIONAL_NUMS / "proportional-nums" => NS_FONT_VARIANT_NUMERIC_PROPORTIONAL = 0x04,
1768 /// Enables display of tabular numerals.
1769 TABULAR_NUMS / "tabular-nums" => NS_FONT_VARIANT_NUMERIC_TABULAR = 0x08,
1770 /// Enables display of lining diagonal fractions.
1771 DIAGONAL_FRACTIONS / "diagonal-fractions" => NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS = 0x10,
1772 /// Enables display of lining stacked fractions.
1773 STACKED_FRACTIONS / "stacked-fractions" => NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS = 0x20,
1774 /// Enables display of letter forms used with ordinal numbers.
1775 ORDINAL / "ordinal" => NS_FONT_VARIANT_NUMERIC_ORDINAL = 0x80,
1776 /// Enables display of slashed zeros.
1777 SLASHED_ZERO / "slashed-zero" => NS_FONT_VARIANT_NUMERIC_SLASHZERO = 0x40,
1778 }
1779
1780 #[cfg(feature = "gecko")]
1781 impl VariantNumeric {
1782 /// Obtain a specified value from a Gecko keyword value
1783 ///
1784 /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u8) -> Self1785 pub fn from_gecko_keyword(kw: u8) -> Self {
1786 Self::from_bits_truncate(kw)
1787 }
1788
1789 /// Transform into gecko keyword
to_gecko_keyword(self) -> u81790 pub fn to_gecko_keyword(self) -> u8 {
1791 self.bits()
1792 }
1793 }
1794
1795 #[cfg(feature = "gecko")]
1796 impl_gecko_keyword_conversions!(VariantNumeric, u8);
1797
1798 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
1799 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1800 /// Specifies control over numerical forms.
1801 pub enum FontVariantNumeric {
1802 /// Value variant with `variant-numeric`
1803 Value(VariantNumeric),
1804 /// System font
1805 #[css(skip)]
1806 System(SystemFont),
1807 }
1808
1809 impl FontVariantNumeric {
1810 #[inline]
1811 /// Default value of `font-variant-numeric` as `empty`
empty() -> FontVariantNumeric1812 pub fn empty() -> FontVariantNumeric {
1813 FontVariantNumeric::Value(VariantNumeric::empty())
1814 }
1815
1816 system_font_methods!(FontVariantNumeric, font_variant_numeric);
1817 }
1818
1819 impl ToComputedValue for FontVariantNumeric {
1820 type ComputedValue = computed::FontVariantNumeric;
1821
to_computed_value(&self, context: &Context) -> computed::FontVariantNumeric1822 fn to_computed_value(&self, context: &Context) -> computed::FontVariantNumeric {
1823 match *self {
1824 FontVariantNumeric::Value(ref v) => v.clone(),
1825 FontVariantNumeric::System(_) => self.compute_system(context),
1826 }
1827 }
1828
from_computed_value(other: &computed::FontVariantNumeric) -> Self1829 fn from_computed_value(other: &computed::FontVariantNumeric) -> Self {
1830 FontVariantNumeric::Value(other.clone())
1831 }
1832 }
1833
1834 impl Parse for FontVariantNumeric {
1835 /// normal |
1836 /// [ <numeric-figure-values> ||
1837 /// <numeric-spacing-values> ||
1838 /// <numeric-fraction-values> ||
1839 /// ordinal ||
1840 /// slashed-zero ]
1841 /// <numeric-figure-values> = [ lining-nums | oldstyle-nums ]
1842 /// <numeric-spacing-values> = [ proportional-nums | tabular-nums ]
1843 /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
parse<'i, 't>( _context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariantNumeric, ParseError<'i>>1844 fn parse<'i, 't>(
1845 _context: &ParserContext,
1846 input: &mut Parser<'i, 't>,
1847 ) -> Result<FontVariantNumeric, ParseError<'i>> {
1848 let mut result = VariantNumeric::empty();
1849
1850 if input
1851 .try_parse(|input| input.expect_ident_matching("normal"))
1852 .is_ok()
1853 {
1854 return Ok(FontVariantNumeric::Value(result));
1855 }
1856
1857 while let Ok(flag) = input.try_parse(|input| {
1858 Ok(
1859 match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
1860 "ordinal" =>
1861 exclusive_value!((result, VariantNumeric::ORDINAL) => VariantNumeric::ORDINAL),
1862 "slashed-zero" =>
1863 exclusive_value!((result, VariantNumeric::SLASHED_ZERO) => VariantNumeric::SLASHED_ZERO),
1864 "lining-nums" =>
1865 exclusive_value!((result, VariantNumeric::LINING_NUMS |
1866 VariantNumeric::OLDSTYLE_NUMS
1867 ) => VariantNumeric::LINING_NUMS),
1868 "oldstyle-nums" =>
1869 exclusive_value!((result, VariantNumeric::LINING_NUMS |
1870 VariantNumeric::OLDSTYLE_NUMS
1871 ) => VariantNumeric::OLDSTYLE_NUMS),
1872 "proportional-nums" =>
1873 exclusive_value!((result, VariantNumeric::PROPORTIONAL_NUMS |
1874 VariantNumeric::TABULAR_NUMS
1875 ) => VariantNumeric::PROPORTIONAL_NUMS),
1876 "tabular-nums" =>
1877 exclusive_value!((result, VariantNumeric::PROPORTIONAL_NUMS |
1878 VariantNumeric::TABULAR_NUMS
1879 ) => VariantNumeric::TABULAR_NUMS),
1880 "diagonal-fractions" =>
1881 exclusive_value!((result, VariantNumeric::DIAGONAL_FRACTIONS |
1882 VariantNumeric::STACKED_FRACTIONS
1883 ) => VariantNumeric::DIAGONAL_FRACTIONS),
1884 "stacked-fractions" =>
1885 exclusive_value!((result, VariantNumeric::DIAGONAL_FRACTIONS |
1886 VariantNumeric::STACKED_FRACTIONS
1887 ) => VariantNumeric::STACKED_FRACTIONS),
1888 _ => return Err(()),
1889 },
1890 )
1891 }) {
1892 result.insert(flag);
1893 }
1894
1895 if !result.is_empty() {
1896 Ok(FontVariantNumeric::Value(result))
1897 } else {
1898 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1899 }
1900 }
1901 }
1902
1903 /// This property provides low-level control over OpenType or TrueType font features.
1904 pub type SpecifiedFontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1905
1906 /// Define initial settings that apply when the font defined by an @font-face
1907 /// rule is rendered.
1908 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
1909 pub enum FontFeatureSettings {
1910 /// Value of `FontSettings`
1911 Value(SpecifiedFontFeatureSettings),
1912 /// System font
1913 #[css(skip)]
1914 System(SystemFont),
1915 }
1916
1917 impl FontFeatureSettings {
1918 #[inline]
1919 /// Get default value of `font-feature-settings` as normal
normal() -> FontFeatureSettings1920 pub fn normal() -> FontFeatureSettings {
1921 FontFeatureSettings::Value(FontSettings::normal())
1922 }
1923
1924 system_font_methods!(FontFeatureSettings, font_feature_settings);
1925 }
1926
1927 impl ToComputedValue for FontFeatureSettings {
1928 type ComputedValue = computed::FontFeatureSettings;
1929
to_computed_value(&self, context: &Context) -> computed::FontFeatureSettings1930 fn to_computed_value(&self, context: &Context) -> computed::FontFeatureSettings {
1931 match *self {
1932 FontFeatureSettings::Value(ref v) => v.to_computed_value(context),
1933 FontFeatureSettings::System(_) => self.compute_system(context),
1934 }
1935 }
1936
from_computed_value(other: &computed::FontFeatureSettings) -> Self1937 fn from_computed_value(other: &computed::FontFeatureSettings) -> Self {
1938 FontFeatureSettings::Value(ToComputedValue::from_computed_value(other))
1939 }
1940 }
1941
1942 impl Parse for FontFeatureSettings {
1943 /// normal | <feature-tag-value>#
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontFeatureSettings, ParseError<'i>>1944 fn parse<'i, 't>(
1945 context: &ParserContext,
1946 input: &mut Parser<'i, 't>,
1947 ) -> Result<FontFeatureSettings, ParseError<'i>> {
1948 SpecifiedFontFeatureSettings::parse(context, input).map(FontFeatureSettings::Value)
1949 }
1950 }
1951
1952 #[derive(
1953 Clone,
1954 Copy,
1955 Debug,
1956 MallocSizeOf,
1957 PartialEq,
1958 SpecifiedValueInfo,
1959 ToComputedValue,
1960 ToResolvedValue,
1961 ToShmem,
1962 )]
1963 /// Whether user agents are allowed to synthesize bold or oblique font faces
1964 /// when a font family lacks bold or italic faces
1965 pub struct FontSynthesis {
1966 /// If a `font-weight` is requested that the font family does not contain,
1967 /// the user agent may synthesize the requested weight from the weights
1968 /// that do exist in the font family.
1969 #[css(represents_keyword)]
1970 pub weight: bool,
1971 /// If a font-style is requested that the font family does not contain,
1972 /// the user agent may synthesize the requested style from the normal face in the font family.
1973 #[css(represents_keyword)]
1974 pub style: bool,
1975 }
1976
1977 impl FontSynthesis {
1978 #[inline]
1979 /// Get the default value of font-synthesis
get_initial_value() -> Self1980 pub fn get_initial_value() -> Self {
1981 FontSynthesis {
1982 weight: true,
1983 style: true,
1984 }
1985 }
1986 #[inline]
1987 /// Get the 'none' value of font-synthesis
none() -> Self1988 pub fn none() -> Self {
1989 FontSynthesis {
1990 weight: false,
1991 style: false,
1992 }
1993 }
1994 }
1995
1996 impl Parse for FontSynthesis {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontSynthesis, ParseError<'i>>1997 fn parse<'i, 't>(
1998 _: &ParserContext,
1999 input: &mut Parser<'i, 't>,
2000 ) -> Result<FontSynthesis, ParseError<'i>> {
2001 let mut result = FontSynthesis {
2002 weight: false,
2003 style: false,
2004 };
2005 try_match_ident_ignore_ascii_case! { input,
2006 "none" => Ok(result),
2007 "weight" => {
2008 result.weight = true;
2009 if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() {
2010 result.style = true;
2011 }
2012 Ok(result)
2013 },
2014 "style" => {
2015 result.style = true;
2016 if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() {
2017 result.weight = true;
2018 }
2019 Ok(result)
2020 },
2021 }
2022 }
2023 }
2024
2025 impl ToCss for FontSynthesis {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,2026 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2027 where
2028 W: Write,
2029 {
2030 if self.weight && self.style {
2031 dest.write_str("weight style")
2032 } else if self.style {
2033 dest.write_str("style")
2034 } else if self.weight {
2035 dest.write_str("weight")
2036 } else {
2037 dest.write_str("none")
2038 }
2039 }
2040 }
2041
2042 #[cfg(feature = "gecko")]
2043 impl From<u8> for FontSynthesis {
from(bits: u8) -> FontSynthesis2044 fn from(bits: u8) -> FontSynthesis {
2045 use crate::gecko_bindings::structs;
2046
2047 FontSynthesis {
2048 weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
2049 style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0,
2050 }
2051 }
2052 }
2053
2054 #[cfg(feature = "gecko")]
2055 impl From<FontSynthesis> for u8 {
from(v: FontSynthesis) -> u82056 fn from(v: FontSynthesis) -> u8 {
2057 use crate::gecko_bindings::structs;
2058
2059 let mut bits: u8 = 0;
2060 if v.weight {
2061 bits |= structs::NS_FONT_SYNTHESIS_WEIGHT as u8;
2062 }
2063 if v.style {
2064 bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
2065 }
2066 bits
2067 }
2068 }
2069
2070 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2071 /// Allows authors to explicitly specify the language system of the font,
2072 /// overriding the language system implied by the content language
2073 pub enum FontLanguageOverride {
2074 /// When rendering with OpenType fonts,
2075 /// the content language of the element is
2076 /// used to infer the OpenType language system
2077 Normal,
2078 /// Single three-letter case-sensitive OpenType language system tag,
2079 /// specifies the OpenType language system to be used instead of
2080 /// the language system implied by the language of the element
2081 Override(Box<str>),
2082 /// Use system font
2083 #[css(skip)]
2084 System(SystemFont),
2085 }
2086
2087 impl FontLanguageOverride {
2088 #[inline]
2089 /// Get default value with `normal`
normal() -> FontLanguageOverride2090 pub fn normal() -> FontLanguageOverride {
2091 FontLanguageOverride::Normal
2092 }
2093
2094 /// The ToComputedValue implementation for non-system-font
2095 /// FontLanguageOverride, used for @font-face descriptors.
2096 #[inline]
compute_non_system(&self) -> computed::FontLanguageOverride2097 pub fn compute_non_system(&self) -> computed::FontLanguageOverride {
2098 match *self {
2099 FontLanguageOverride::Normal => computed::FontLanguageOverride::zero(),
2100 FontLanguageOverride::Override(ref lang) => {
2101 computed::FontLanguageOverride::from_str(lang)
2102 },
2103 FontLanguageOverride::System(..) => unreachable!(),
2104 }
2105 }
2106
2107 system_font_methods!(FontLanguageOverride, font_language_override);
2108 }
2109
2110 impl ToComputedValue for FontLanguageOverride {
2111 type ComputedValue = computed::FontLanguageOverride;
2112
2113 #[inline]
to_computed_value(&self, context: &Context) -> computed::FontLanguageOverride2114 fn to_computed_value(&self, context: &Context) -> computed::FontLanguageOverride {
2115 match *self {
2116 FontLanguageOverride::System(_) => self.compute_system(context),
2117 _ => self.compute_non_system(),
2118 }
2119 }
2120 #[inline]
from_computed_value(computed: &computed::FontLanguageOverride) -> Self2121 fn from_computed_value(computed: &computed::FontLanguageOverride) -> Self {
2122 if *computed == computed::FontLanguageOverride::zero() {
2123 return FontLanguageOverride::Normal;
2124 }
2125 FontLanguageOverride::Override(computed.to_str(&mut [0; 4]).into())
2126 }
2127 }
2128
2129 impl Parse for FontLanguageOverride {
2130 /// normal | <string>
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontLanguageOverride, ParseError<'i>>2131 fn parse<'i, 't>(
2132 _: &ParserContext,
2133 input: &mut Parser<'i, 't>,
2134 ) -> Result<FontLanguageOverride, ParseError<'i>> {
2135 if input
2136 .try_parse(|input| input.expect_ident_matching("normal"))
2137 .is_ok()
2138 {
2139 return Ok(FontLanguageOverride::Normal);
2140 }
2141
2142 let string = input.expect_string()?;
2143 Ok(FontLanguageOverride::Override(
2144 string.as_ref().to_owned().into_boxed_str(),
2145 ))
2146 }
2147 }
2148
2149 /// This property provides low-level control over OpenType or TrueType font
2150 /// variations.
2151 pub type SpecifiedFontVariationSettings = FontSettings<VariationValue<Number>>;
2152
2153 /// Define initial settings that apply when the font defined by an @font-face
2154 /// rule is rendered.
2155 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2156 pub enum FontVariationSettings {
2157 /// Value of `FontSettings`
2158 Value(SpecifiedFontVariationSettings),
2159 /// System font
2160 #[css(skip)]
2161 System(SystemFont),
2162 }
2163
2164 impl FontVariationSettings {
2165 #[inline]
2166 /// Get default value of `font-variation-settings` as normal
normal() -> FontVariationSettings2167 pub fn normal() -> FontVariationSettings {
2168 FontVariationSettings::Value(FontSettings::normal())
2169 }
2170
2171 system_font_methods!(FontVariationSettings, font_variation_settings);
2172 }
2173
2174 impl ToComputedValue for FontVariationSettings {
2175 type ComputedValue = computed::FontVariationSettings;
2176
to_computed_value(&self, context: &Context) -> computed::FontVariationSettings2177 fn to_computed_value(&self, context: &Context) -> computed::FontVariationSettings {
2178 match *self {
2179 FontVariationSettings::Value(ref v) => v.to_computed_value(context),
2180 FontVariationSettings::System(_) => self.compute_system(context),
2181 }
2182 }
2183
from_computed_value(other: &computed::FontVariationSettings) -> Self2184 fn from_computed_value(other: &computed::FontVariationSettings) -> Self {
2185 FontVariationSettings::Value(ToComputedValue::from_computed_value(other))
2186 }
2187 }
2188
2189 impl Parse for FontVariationSettings {
2190 /// normal | <variation-tag-value>#
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<FontVariationSettings, ParseError<'i>>2191 fn parse<'i, 't>(
2192 context: &ParserContext,
2193 input: &mut Parser<'i, 't>,
2194 ) -> Result<FontVariationSettings, ParseError<'i>> {
2195 SpecifiedFontVariationSettings::parse(context, input).map(FontVariationSettings::Value)
2196 }
2197 }
2198
parse_one_feature_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Integer, ParseError<'i>>2199 fn parse_one_feature_value<'i, 't>(
2200 context: &ParserContext,
2201 input: &mut Parser<'i, 't>,
2202 ) -> Result<Integer, ParseError<'i>> {
2203 if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
2204 return Ok(integer);
2205 }
2206
2207 try_match_ident_ignore_ascii_case! { input,
2208 "on" => Ok(Integer::new(1)),
2209 "off" => Ok(Integer::new(0)),
2210 }
2211 }
2212
2213 impl Parse for FeatureTagValue<Integer> {
2214 /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>2215 fn parse<'i, 't>(
2216 context: &ParserContext,
2217 input: &mut Parser<'i, 't>,
2218 ) -> Result<Self, ParseError<'i>> {
2219 let tag = FontTag::parse(context, input)?;
2220 let value = input
2221 .try_parse(|i| parse_one_feature_value(context, i))
2222 .unwrap_or_else(|_| Integer::new(1));
2223
2224 Ok(Self { tag, value })
2225 }
2226 }
2227
2228 impl Parse for VariationValue<Number> {
2229 /// This is the `<string> <number>` part of the font-variation-settings
2230 /// syntax.
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>2231 fn parse<'i, 't>(
2232 context: &ParserContext,
2233 input: &mut Parser<'i, 't>,
2234 ) -> Result<Self, ParseError<'i>> {
2235 let tag = FontTag::parse(context, input)?;
2236 let value = Number::parse(context, input)?;
2237 Ok(Self { tag, value })
2238 }
2239 }
2240
2241 /// A metrics override value for a @font-face descriptor
2242 ///
2243 /// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc
2244 #[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2245 pub enum MetricsOverride {
2246 /// A non-negative `<percentage>` of the computed font size
2247 Override(NonNegativePercentage),
2248 /// Normal metrics from the font.
2249 Normal,
2250 }
2251
2252 impl MetricsOverride {
2253 #[inline]
2254 /// Get default value with `normal`
normal() -> MetricsOverride2255 pub fn normal() -> MetricsOverride {
2256 MetricsOverride::Normal
2257 }
2258
2259 /// The ToComputedValue implementation, used for @font-face descriptors.
2260 ///
2261 /// Valid override percentages must be non-negative; we return -1.0 to indicate
2262 /// the absence of an override (i.e. 'normal').
2263 #[inline]
compute(&self) -> ComputedPercentage2264 pub fn compute(&self) -> ComputedPercentage {
2265 match *self {
2266 MetricsOverride::Normal => ComputedPercentage(-1.0),
2267 MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
2268 }
2269 }
2270 }
2271
2272 #[derive(
2273 Clone,
2274 Copy,
2275 Debug,
2276 MallocSizeOf,
2277 PartialEq,
2278 SpecifiedValueInfo,
2279 ToComputedValue,
2280 ToCss,
2281 ToResolvedValue,
2282 ToShmem,
2283 )]
2284 /// text-zoom. Enable if true, disable if false
2285 pub struct XTextZoom(#[css(skip)] pub bool);
2286
2287 impl Parse for XTextZoom {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<XTextZoom, ParseError<'i>>2288 fn parse<'i, 't>(
2289 _: &ParserContext,
2290 input: &mut Parser<'i, 't>,
2291 ) -> Result<XTextZoom, ParseError<'i>> {
2292 debug_assert!(
2293 false,
2294 "Should be set directly by presentation attributes only."
2295 );
2296 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2297 }
2298 }
2299
2300 #[derive(
2301 Clone,
2302 Debug,
2303 MallocSizeOf,
2304 PartialEq,
2305 SpecifiedValueInfo,
2306 ToComputedValue,
2307 ToCss,
2308 ToResolvedValue,
2309 ToShmem,
2310 )]
2311 /// Internal property that reflects the lang attribute
2312 pub struct XLang(#[css(skip)] pub Atom);
2313
2314 impl XLang {
2315 #[inline]
2316 /// Get default value for `-x-lang`
get_initial_value() -> XLang2317 pub fn get_initial_value() -> XLang {
2318 XLang(atom!(""))
2319 }
2320 }
2321
2322 impl Parse for XLang {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<XLang, ParseError<'i>>2323 fn parse<'i, 't>(
2324 _: &ParserContext,
2325 input: &mut Parser<'i, 't>,
2326 ) -> Result<XLang, ParseError<'i>> {
2327 debug_assert!(
2328 false,
2329 "Should be set directly by presentation attributes only."
2330 );
2331 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2332 }
2333 }
2334
2335 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2336 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2337 /// Specifies the minimum font size allowed due to changes in scriptlevel.
2338 /// Ref: https://wiki.mozilla.org/MathML:mstyle
2339 pub struct MozScriptMinSize(pub NoCalcLength);
2340
2341 impl MozScriptMinSize {
2342 #[inline]
2343 /// Calculate initial value of -moz-script-min-size.
get_initial_value() -> Length2344 pub fn get_initial_value() -> Length {
2345 Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * (AU_PER_PT / AU_PER_PX))
2346 }
2347 }
2348
2349 impl Parse for MozScriptMinSize {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<MozScriptMinSize, ParseError<'i>>2350 fn parse<'i, 't>(
2351 _: &ParserContext,
2352 input: &mut Parser<'i, 't>,
2353 ) -> Result<MozScriptMinSize, ParseError<'i>> {
2354 debug_assert!(
2355 false,
2356 "Should be set directly by presentation attributes only."
2357 );
2358 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2359 }
2360 }
2361
2362 /// A value for the `math-depth` property.
2363 /// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property
2364 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2365 #[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
2366 pub enum MathDepth {
2367 /// Increment math-depth if math-style is compact.
2368 AutoAdd,
2369
2370 /// Add the function's argument to math-depth.
2371 #[css(function)]
2372 Add(Integer),
2373
2374 /// Set math-depth to the specified value.
2375 Absolute(Integer),
2376 }
2377
2378 impl Parse for MathDepth {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<MathDepth, ParseError<'i>>2379 fn parse<'i, 't>(
2380 context: &ParserContext,
2381 input: &mut Parser<'i, 't>,
2382 ) -> Result<MathDepth, ParseError<'i>> {
2383 if input
2384 .try_parse(|i| i.expect_ident_matching("auto-add"))
2385 .is_ok()
2386 {
2387 return Ok(MathDepth::AutoAdd);
2388 }
2389 if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
2390 return Ok(MathDepth::Absolute(math_depth_value));
2391 }
2392 input.expect_function_matching("add")?;
2393 let math_depth_delta_value =
2394 input.parse_nested_block(|input| Integer::parse(context, input))?;
2395 Ok(MathDepth::Add(math_depth_delta_value))
2396 }
2397 }
2398
2399 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2400 #[derive(
2401 Clone,
2402 Copy,
2403 Debug,
2404 PartialEq,
2405 SpecifiedValueInfo,
2406 ToComputedValue,
2407 ToCss,
2408 ToResolvedValue,
2409 ToShmem,
2410 )]
2411 /// Specifies the multiplier to be used to adjust font size
2412 /// due to changes in scriptlevel.
2413 ///
2414 /// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs
2415 pub struct MozScriptSizeMultiplier(pub f32);
2416
2417 impl MozScriptSizeMultiplier {
2418 #[inline]
2419 /// Get default value of `-moz-script-size-multiplier`
get_initial_value() -> MozScriptSizeMultiplier2420 pub fn get_initial_value() -> MozScriptSizeMultiplier {
2421 MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
2422 }
2423 }
2424
2425 impl Parse for MozScriptSizeMultiplier {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<MozScriptSizeMultiplier, ParseError<'i>>2426 fn parse<'i, 't>(
2427 _: &ParserContext,
2428 input: &mut Parser<'i, 't>,
2429 ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
2430 debug_assert!(
2431 false,
2432 "Should be set directly by presentation attributes only."
2433 );
2434 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
2435 }
2436 }
2437
2438 impl From<f32> for MozScriptSizeMultiplier {
from(v: f32) -> Self2439 fn from(v: f32) -> Self {
2440 MozScriptSizeMultiplier(v)
2441 }
2442 }
2443
2444 impl From<MozScriptSizeMultiplier> for f32 {
from(v: MozScriptSizeMultiplier) -> f322445 fn from(v: MozScriptSizeMultiplier) -> f32 {
2446 v.0
2447 }
2448 }
2449