diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 1eae14e4a3a..c00b9876e6f 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -9,7 +9,7 @@ #![deny(missing_docs)] #[cfg(feature = "gecko")] -use computed_values::{font_feature_settings, font_stretch, font_style, font_weight}; +use computed_values::{font_stretch, font_style, font_weight}; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use cssparser::{SourceLocation, CowRcStr}; use error_reporting::{ContextualParseError, ParseErrorReporter}; @@ -25,6 +25,8 @@ use str::CssStringWriter; use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError}; use style_traits::{StyleParseErrorKind, ToCss}; use values::computed::font::FamilyName; +#[cfg(feature = "gecko")] +use values::specified::font::SpecifiedFontFeatureSettings; use values::specified::url::SpecifiedUrl; /// A source for a font-face rule. @@ -395,8 +397,8 @@ font_face_descriptors! { ], /// The feature settings of this font face. - "font-feature-settings" feature_settings / mFontFeatureSettings: font_feature_settings::T = { - font_feature_settings::T::Normal + "font-feature-settings" feature_settings / mFontFeatureSettings: SpecifiedFontFeatureSettings = { + font_feature_settings::SpecifiedValue::normal() }, /// The language override of this font face. diff --git a/components/style/gecko/rules.rs b/components/style/gecko/rules.rs index 97ec34c6bcf..67dbfd121b2 100644 --- a/components/style/gecko/rules.rs +++ b/components/style/gecko/rules.rs @@ -5,7 +5,7 @@ //! Bindings for CSS Rule objects use byteorder::{BigEndian, WriteBytesExt}; -use computed_values::{font_feature_settings, font_stretch, font_style, font_weight}; +use computed_values::{font_stretch, font_style, font_weight}; use counter_style; use cssparser::UnicodeRange; use font_face::{FontFaceRuleData, Source, FontDisplay, FontWeight}; @@ -21,7 +21,7 @@ use std::fmt::{self, Write}; use std::str; use str::CssStringWriter; use values::computed::font::FamilyName; -use values::generics::font::FontSettings; +use values::specified::font::SpecifiedFontFeatureSettings; /// A @font-face rule pub type FontFaceRule = RefPtr; @@ -50,24 +50,24 @@ impl ToNsCssValue for FontWeight { } } -impl ToNsCssValue for font_feature_settings::T { +impl ToNsCssValue for SpecifiedFontFeatureSettings { fn convert(self, nscssvalue: &mut nsCSSValue) { - match self { - FontSettings::Normal => nscssvalue.set_normal(), - FontSettings::Tag(tags) => { - nscssvalue.set_pair_list(tags.into_iter().map(|entry| { - let mut feature = nsCSSValue::null(); - let mut raw = [0u8; 4]; - (&mut raw[..]).write_u32::(entry.tag.0).unwrap(); - feature.set_string(str::from_utf8(&raw).unwrap()); - - let mut index = nsCSSValue::null(); - index.set_integer(entry.value.0 as i32); - - (feature, index) - })) - } + if self.0.is_empty() { + nscssvalue.set_normal(); + return; } + + nscssvalue.set_pair_list(self.0.into_iter().map(|entry| { + let mut feature = nsCSSValue::null(); + let mut raw = [0u8; 4]; + (&mut raw[..]).write_u32::(entry.tag.0).unwrap(); + feature.set_string(str::from_utf8(&raw).unwrap()); + + let mut index = nsCSSValue::null(); + index.set_integer(entry.value.value()); + + (feature, index) + })) } } diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 231b4adfc08..a810dc22184 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1426,29 +1426,21 @@ impl Clone for ${style_struct.gecko_struct_name} { } -<%def name="impl_font_settings(ident, tag_type)"> +<%def name="impl_font_settings(ident, tag_type, value_type, gecko_value_type)"> <% gecko_ffi_name = to_camel_case_lower(ident) %> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { - use values::generics::font::FontSettings; - let current_settings = &mut self.gecko.mFont.${gecko_ffi_name}; current_settings.clear_pod(); - match v { - FontSettings::Normal => {}, // do nothing, length is already 0 + unsafe { current_settings.set_len_pod(v.0.len() as u32) }; - FontSettings::Tag(other_settings) => { - unsafe { current_settings.set_len_pod(other_settings.len() as u32) }; - - for (current, other) in current_settings.iter_mut().zip(other_settings) { - current.mTag = other.tag.0; - current.mValue = other.value.0; - } - } - }; + for (current, other) in current_settings.iter_mut().zip(v.0.iter()) { + current.mTag = other.tag.0; + current.mValue = other.value as ${gecko_value_type}; + } } pub fn copy_${ident}_from(&mut self, other: &Self) { @@ -1470,21 +1462,17 @@ impl Clone for ${style_struct.gecko_struct_name} { } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use values::generics::font::{FontSettings, FontSettingTag, ${tag_type}}; + use values::generics::font::{FontSettings, ${tag_type}}; use values::specified::font::FontTag; - if self.gecko.mFont.${gecko_ffi_name}.len() == 0 { - FontSettings::Normal - } else { - FontSettings::Tag( - self.gecko.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| { - FontSettingTag { - tag: FontTag(gecko_font_setting.mTag), - value: ${tag_type}(gecko_font_setting.mValue), - } - }).collect() - ) - } + FontSettings( + self.gecko.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| { + ${tag_type} { + tag: FontTag(gecko_font_setting.mTag), + value: gecko_font_setting.mValue as ${value_type}, + } + }).collect::>().into_boxed_slice() + ) } @@ -2338,8 +2326,10 @@ fn static_assert() { <%self:impl_trait style_struct_name="Font" skip_longhands="${skip_font_longhands}"> - <% impl_font_settings("font_feature_settings", "FontSettingTagInt") %> - <% impl_font_settings("font_variation_settings", "FontSettingTagFloat") %> + // Negative numbers are invalid at parse time, but is still an + // i32. + <% impl_font_settings("font_feature_settings", "FeatureTagValue", "i32", "u32") %> + <% impl_font_settings("font_variation_settings", "VariationValue", "f32", "f32") %> pub fn fixup_none_generic(&mut self, device: &Device) { self.gecko.mFont.systemFont = false; diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 0e839a90645..a6d6d4e7a2e 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -17,8 +17,6 @@ use properties::{CSSWideKeyword, PropertyDeclaration}; use properties::longhands; use properties::longhands::font_weight::computed_value::T as FontWeight; use properties::longhands::font_stretch::computed_value::T as FontStretch; -#[cfg(feature = "gecko")] -use properties::longhands::font_variation_settings::computed_value::T as FontVariationSettings; use properties::longhands::visibility::computed_value::T as Visibility; #[cfg(feature = "gecko")] use properties::PropertyId; @@ -51,15 +49,15 @@ use values::computed::transform::Translate as ComputedTranslate; use values::computed::transform::Scale as ComputedScale; use values::generics::transform::{self, Rotate, Translate, Scale, Transform, TransformOperation}; use values::distance::{ComputeSquaredDistance, SquaredDistance}; -#[cfg(feature = "gecko")] use values::generics::font::FontSettings as GenericFontSettings; -#[cfg(feature = "gecko")] use values::generics::font::FontSettingTag as GenericFontSettingTag; -#[cfg(feature = "gecko")] use values::generics::font::FontSettingTagFloat; +use values::generics::font::FontSettings as GenericFontSettings; +use values::computed::font::FontVariationSettings; +use values::generics::font::VariationValue; use values::generics::NonNegative; use values::generics::effects::Filter; use values::generics::position as generic_position; use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint}; use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity}; -#[cfg(feature = "gecko")] use values::specified::font::FontTag; +use values::specified::font::FontTag; /// pub trait RepeatableListAnimatable: Animate {} @@ -817,18 +815,16 @@ impl Into for f64 { } /// -#[cfg(feature = "gecko")] impl Animate for FontVariationSettings { #[inline] fn animate(&self, other: &Self, procedure: Procedure) -> Result { FontSettingTagIter::new(self, other)? .map(|r| r.and_then(|(st, ot)| st.animate(&ot, procedure))) - .collect::, ()>>() - .map(GenericFontSettings::Tag) + .collect::, ()>>() + .map(|v| GenericFontSettings(v.into_boxed_slice())) } } -#[cfg(feature = "gecko")] impl ComputeSquaredDistance for FontVariationSettings { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result { @@ -838,7 +834,6 @@ impl ComputeSquaredDistance for FontVariationSettings { } } -#[cfg(feature = "gecko")] impl ToAnimatedZero for FontVariationSettings { #[inline] fn to_animated_zero(&self) -> Result { @@ -846,45 +841,17 @@ impl ToAnimatedZero for FontVariationSettings { } } -#[cfg(feature = "gecko")] -impl Animate for FontSettingTag { - #[inline] - fn animate(&self, other: &Self, procedure: Procedure) -> Result { - if self.tag != other.tag { - return Err(()); - } - let value = self.value.animate(&other.value, procedure)?; - Ok(FontSettingTag { - tag: self.tag, - value, - }) - } -} +type ComputedVariationValue = VariationValue; -#[cfg(feature = "gecko")] -impl ComputeSquaredDistance for FontSettingTag { - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - if self.tag != other.tag { - return Err(()); - } - self.value.compute_squared_distance(&other.value) - } -} - -#[cfg(feature = "gecko")] -type FontSettingTag = GenericFontSettingTag; - -#[cfg(feature = "gecko")] +// FIXME: Could do a rename, this is only used for font variations. struct FontSettingTagIterState<'a> { - tags: Vec<(&'a FontSettingTag)>, + tags: Vec<(&'a ComputedVariationValue)>, index: usize, prev_tag: FontTag, } -#[cfg(feature = "gecko")] impl<'a> FontSettingTagIterState<'a> { - fn new(tags: Vec<(&'a FontSettingTag)>) -> FontSettingTagIterState<'a> { + fn new(tags: Vec<<&'a ComputedVariationValue>) -> FontSettingTagIterState<'a> { FontSettingTagIterState { index: tags.len(), tags, @@ -898,12 +865,13 @@ impl<'a> FontSettingTagIterState<'a> { /// [CSS fonts level 4](https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-variation-settings) /// defines the animation of font-variation-settings as follows: /// -/// Two declarations of font-feature-settings[sic] can be animated between if they are "like". -/// "Like" declarations are ones where the same set of properties appear (in any order). -/// Because succesive[sic] duplicate properties are applied instead of prior duplicate -/// properties, two declarations can be "like" even if they have differing number of -/// properties. If two declarations are "like" then animation occurs pairwise between -/// corresponding values in the declarations. +/// Two declarations of font-feature-settings[sic] can be animated between if +/// they are "like". "Like" declarations are ones where the same set of +/// properties appear (in any order). Because succesive[sic] duplicate +/// properties are applied instead of prior duplicate properties, two +/// declarations can be "like" even if they have differing number of +/// properties. If two declarations are "like" then animation occurs pairwise +/// between corresponding values in the declarations. /// /// In other words if we have the following lists: /// @@ -915,9 +883,10 @@ impl<'a> FontSettingTagIterState<'a> { /// "wdth" 5, "wght" 2 /// "wght" 4, "wdth" 10 /// -/// This iterator supports this by sorting the two lists, then iterating them in reverse, -/// and skipping entries with repeated tag names. It will return Some(Err()) if it reaches the -/// end of one list before the other, or if the tag names do not match. +/// This iterator supports this by sorting the two lists, then iterating them in +/// reverse, and skipping entries with repeated tag names. It will return +/// Some(Err()) if it reaches the end of one list before the other, or if the +/// tag names do not match. /// /// For the above example, this iterator would return: /// @@ -925,37 +894,33 @@ impl<'a> FontSettingTagIterState<'a> { /// Some(Ok("wdth" 5, "wdth" 10)) /// None /// -#[cfg(feature = "gecko")] struct FontSettingTagIter<'a> { a_state: FontSettingTagIterState<'a>, b_state: FontSettingTagIterState<'a>, } -#[cfg(feature = "gecko")] impl<'a> FontSettingTagIter<'a> { fn new( a_settings: &'a FontVariationSettings, b_settings: &'a FontVariationSettings, ) -> Result, ()> { - if let (&GenericFontSettings::Tag(ref a_tags), &GenericFontSettings::Tag(ref b_tags)) = (a_settings, b_settings) - { - fn as_new_sorted_tags(tags: &Vec) -> Vec<(&FontSettingTag)> { - use std::iter::FromIterator; - let mut sorted_tags: Vec<(&FontSettingTag)> = Vec::from_iter(tags.iter()); - sorted_tags.sort_by_key(|k| k.tag.0); - sorted_tags - }; - - Ok(FontSettingTagIter { - a_state: FontSettingTagIterState::new(as_new_sorted_tags(a_tags)), - b_state: FontSettingTagIterState::new(as_new_sorted_tags(b_tags)), - }) - } else { - Err(()) + if a_settings.0.is_empty() || b_settings.0.is_empty() { + return Err(()); } + fn as_new_sorted_tags(tags: &[ComputedVariationValue]) -> Vec<<&ComputedVariationValue> { + use std::iter::FromIterator; + let mut sorted_tags = Vec::from_iter(tags.iter()); + sorted_tags.sort_by_key(|k| k.tag.0); + sorted_tags + }; + + Ok(FontSettingTagIter { + a_state: FontSettingTagIterState::new(as_new_sorted_tags(&a_settings.0)), + b_state: FontSettingTagIterState::new(as_new_sorted_tags(&b_settings.0)), + }) } - fn next_tag(state: &mut FontSettingTagIterState<'a>) -> Option<(&'a FontSettingTag)> { + fn next_tag(state: &mut FontSettingTagIterState<'a>) -> Option<<&'a ComputedVariationValue> { if state.index == 0 { return None; } @@ -971,11 +936,10 @@ impl<'a> FontSettingTagIter<'a> { } } -#[cfg(feature = "gecko")] impl<'a> Iterator for FontSettingTagIter<'a> { - type Item = Result<(&'a FontSettingTag, &'a FontSettingTag), ()>; + type Item = Result<(&'a ComputedVariationValue, &'a ComputedVariationValue), ()>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option> { match ( FontSettingTagIter::next_tag(&mut self.a_state), FontSettingTagIter::next_tag(&mut self.b_state), diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 046f5cb3593..9533c09ed24 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -144,7 +144,6 @@ ${helpers.predefined_type("font-feature-settings", initial_value="computed::FontFeatureSettings::normal()", initial_specified_value="specified::FontFeatureSettings::normal()", extra_prefixes="moz", - boxed=True, animation_value_type="discrete", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings")} @@ -157,10 +156,10 @@ https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control- %> ${helpers.predefined_type("font-variation-settings", - "FontVariantSettings", + "FontVariationSettings", products="gecko", gecko_pref="layout.css.font-variations.enabled", - initial_value="specified::FontVariantSettings::normal()", + initial_value="computed::FontVariationSettings::normal()", animation_value_type="ComputedValue", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="${variation_spec}")} diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 3779c7c9575..37e927968c4 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -22,13 +22,13 @@ use std::slice; use style_traits::{CssWriter, ParseError, ToCss}; use values::CSSFloat; use values::animated::{ToAnimatedValue, ToAnimatedZero}; -use values::computed::{Context, NonNegativeLength, ToComputedValue}; -use values::generics::font::{FontSettings, FontSettingTagInt}; +use values::computed::{Context, NonNegativeLength, ToComputedValue, Integer, Number}; +use values::generics::font::{FontSettings, FeatureTagValue, VariationValue}; use values::specified::font as specified; use values::specified::length::{FontBaseSize, NoCalcLength}; pub use values::computed::Length as MozScriptMinSize; -pub use values::specified::font::{XTextZoom, XLang, MozScriptSizeMultiplier, FontSynthesis, FontVariantSettings}; +pub use values::specified::font::{XTextZoom, XLang, MozScriptSizeMultiplier, FontSynthesis}; /// As of CSS Fonts Module Level 3, only the following values are /// valid: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 @@ -712,8 +712,11 @@ pub type FontVariantLigatures = specified::VariantLigatures; /// Use VariantNumeric as computed type of FontVariantNumeric pub type FontVariantNumeric = specified::VariantNumeric; -/// Use FontSettings as computed type of FontFeatureSettings -pub type FontFeatureSettings = FontSettings; +/// Use FontSettings as computed type of FontFeatureSettings. +pub type FontFeatureSettings = FontSettings>; + +/// The computed value for font-variation-settings. +pub type FontVariationSettings = FontSettings>; /// font-language-override can only have a single three-letter /// OpenType "language system" tag, so we should be able to compute diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index c91256d7e6e..8ff33022154 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -40,7 +40,7 @@ pub use self::background::{BackgroundSize, BackgroundRepeat}; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderCornerRadius, BorderSpacing}; pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; -pub use self::font::{FontFamily, FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; +pub use self::font::{FontFamily, FontLanguageOverride, FontVariationSettings, FontVariantEastAsian}; pub use self::font::{FontVariantLigatures, FontVariantNumeric, FontFeatureSettings}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XTextZoom, XLang}; pub use self::box_::{AnimationIterationCount, AnimationName, Display, OverscrollBehavior, Contain}; diff --git a/components/style/values/generics/font.rs b/components/style/values/generics/font.rs index 0b3877620e7..ddd3fc0ac3c 100644 --- a/components/style/values/generics/font.rs +++ b/components/style/values/generics/font.rs @@ -4,128 +4,116 @@ //! Generic types for font stuff. -use std::fmt::{self, Write}; use cssparser::Parser; +use num_traits::One; use parser::{Parse, ParserContext}; -use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError, ToCss, StyleParseErrorKind}; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ParseError, ToCss}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::specified::font::FontTag; -/// A settings tag, defined by a four-character tag and a setting value -/// -/// For font-feature-settings, this is a tag and an integer, for -/// font-variation-settings this is a tag and a float. -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] -pub struct FontSettingTag { - /// A four-character tag, packed into a u32 (one byte per character) +/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)] +pub struct FeatureTagValue { + /// A four-character tag, packed into a u32 (one byte per character). pub tag: FontTag, /// The actual value. - pub value: T, + pub value: Integer, } -impl OneOrMoreSeparated for FontSettingTag { - type S = Comma; -} - -impl Parse for FontSettingTag { - /// - /// - /// settings-control-the-font-variation-settings-property - /// [ on | off | ] - /// - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let tag = FontTag::parse(context, input)?; - let value = T::parse(context, input)?; - - Ok(Self { tag, value }) - } -} - -/// A font settings value for font-variation-settings or font-feature-settings -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] -pub enum FontSettings { - /// No settings (default) - Normal, - /// Set of settings - Tag(Vec>) -} - -impl FontSettings { - #[inline] - /// Default value of font settings as `normal` - pub fn normal() -> Self { - FontSettings::Normal - } -} - -impl Parse for FontSettings { - /// - fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - if input.try(|i| i.expect_ident_matching("normal")).is_ok() { - return Ok(FontSettings::Normal); - } - Vec::parse(context, input).map(FontSettings::Tag) - } -} - -/// An integer that can also parse "on" and "off", -/// for font-feature-settings -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)] -pub struct FontSettingTagInt(pub u32); - -/// A number value to be used for font-variation-settings. -/// -/// FIXME(emilio): The spec only says , so we should be able to reuse -/// the other code: -/// -/// https://drafts.csswg.org/css-fonts-4/#propdef-font-variation-settings -#[cfg_attr(feature = "gecko", derive(Animate, ComputeSquaredDistance))] -#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] -pub struct FontSettingTagFloat(pub f32); - -impl ToCss for FontSettingTagInt { +impl ToCss for FeatureTagValue +where + Integer: One + ToCss + PartialEq, +{ fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { - match self.0 { - 1 => Ok(()), - 0 => dest.write_str("off"), - x => x.to_css(dest), + self.tag.to_css(dest)?; + // Don't serialize the default value. + if self.value != Integer::one() { + dest.write_char(' ')?; + self.value.to_css(dest)?; } + + Ok(()) } } -impl Parse for FontSettingTagInt { - fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - // FIXME(emilio): This doesn't handle calc properly. - if let Ok(value) = input.try(|input| input.expect_integer()) { - // handle integer, throw if it is negative - if value >= 0 { - Ok(FontSettingTagInt(value as u32)) - } else { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) +/// Variation setting for a single feature, see: +/// +/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def +#[derive(Animate, Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] +pub struct VariationValue { + /// A four-character tag, packed into a u32 (one byte per character). + #[animation(constant)] + pub tag: FontTag, + /// The actual value. + pub value: Number, +} + +impl ComputeSquaredDistance for VariationValue +where + Number: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + if self.tag != other.tag { + return Err(()); + } + self.value.compute_squared_distance(&other.value) + } +} + +/// A value both for font-variation-settings and font-feature-settings. +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)] +pub struct FontSettings(pub Box<[T]>); + +impl FontSettings { + /// Default value of font settings as `normal`. + #[inline] + pub fn normal() -> Self { + FontSettings(vec![].into_boxed_slice()) + } +} + +impl Parse for FontSettings { + /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings + /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + if input.try(|i| i.expect_ident_matching("normal")).is_ok() { + return Ok(Self::normal()); + } + + Ok(FontSettings( + input.parse_comma_separated(|i| T::parse(context, i))?.into_boxed_slice() + )) + } +} + +impl ToCss for FontSettings { + /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings + /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + if self.0.is_empty() { + return dest.write_str("normal"); + } + + let mut first = true; + for item in self.0.iter() { + if !first { + dest.write_str(", ")?; } - } else if let Ok(_) = input.try(|input| input.expect_ident_matching("on")) { - // on is an alias for '1' - Ok(FontSettingTagInt(1)) - } else if let Ok(_) = input.try(|input| input.expect_ident_matching("off")) { - // off is an alias for '0' - Ok(FontSettingTagInt(0)) - } else { - // empty value is an alias for '1' - Ok(FontSettingTagInt(1)) + first = false; + item.to_css(dest)?; } - } -} -impl Parse for FontSettingTagFloat { - fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - // FIXME(emilio): Should handle calc() using Number::parse. - // - // Also why is this not in font.rs? - input.expect_number().map(FontSettingTagFloat).map_err(|e| e.into()) + Ok(()) } } diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index d7bcf838cff..c823dca799c 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -21,8 +21,8 @@ use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use values::CustomIdent; use values::computed::{font as computed, Context, Length, NonNegativeLength, ToComputedValue}; use values::computed::font::{SingleFontFamily, FontFamilyList, FamilyName}; -use values::generics::font::{FontSettings, FontSettingTagFloat}; -use values::specified::{AllowQuirks, LengthOrPercentage, NoCalcLength, Number}; +use values::generics::font::{FontSettings, FeatureTagValue, VariationValue}; +use values::specified::{AllowQuirks, Integer, LengthOrPercentage, NoCalcLength, Number}; use values::specified::length::{AU_PER_PT, AU_PER_PX, FontBaseSize}; const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8; @@ -164,8 +164,8 @@ impl From for FontSize { } } +/// Specifies a prioritized list of font family names or generic family names. #[derive(Clone, Debug, Eq, Hash, PartialEq)] -/// Specifies a prioritized list of font family names or generic family names pub enum FontFamily { /// List of `font-family` Values(FontFamilyList), @@ -1709,13 +1709,15 @@ impl Parse for FontVariantNumeric { } } -#[cfg_attr(feature = "gecko", derive(MallocSizeOf))] -#[derive(Clone, Debug, PartialEq, ToCss)] -/// Define initial settings that apply when the font defined -/// by an @font-face rule is rendered. +/// This property provides low-level control over OpenType or TrueType font variations. +pub type SpecifiedFontFeatureSettings = FontSettings>; + +/// Define initial settings that apply when the font defined by an @font-face +/// rule is rendered. +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)] pub enum FontFeatureSettings { /// Value of `FontSettings` - Value(computed::FontFeatureSettings), + Value(SpecifiedFontFeatureSettings), /// System font System(SystemFont) } @@ -1724,7 +1726,7 @@ impl FontFeatureSettings { #[inline] /// Get default value of `font-feature-settings` as normal pub fn normal() -> FontFeatureSettings { - FontFeatureSettings::Value(FontSettings::Normal) + FontFeatureSettings::Value(FontSettings::normal()) } /// Get `font-feature-settings` with system font @@ -1745,12 +1747,12 @@ impl FontFeatureSettings { impl ToComputedValue for FontFeatureSettings { type ComputedValue = computed::FontFeatureSettings; - fn to_computed_value(&self, _context: &Context) -> computed::FontFeatureSettings { + fn to_computed_value(&self, context: &Context) -> computed::FontFeatureSettings { match *self { - FontFeatureSettings::Value(ref v) => v.clone(), + FontFeatureSettings::Value(ref v) => v.to_computed_value(context), FontFeatureSettings::System(_) => { #[cfg(feature = "gecko")] { - _context.cached_system_font.as_ref().unwrap().font_feature_settings.clone() + context.cached_system_font.as_ref().unwrap().font_feature_settings.clone() } #[cfg(feature = "servo")] { unreachable!() @@ -1760,7 +1762,7 @@ impl ToComputedValue for FontFeatureSettings { } fn from_computed_value(other: &computed::FontFeatureSettings) -> Self { - FontFeatureSettings::Value(other.clone()) + FontFeatureSettings::Value(ToComputedValue::from_computed_value(other)) } } @@ -1770,7 +1772,7 @@ impl Parse for FontFeatureSettings { context: &ParserContext, input: &mut Parser<'i, 't> ) -> Result> { - computed::FontFeatureSettings::parse(context, input).map(FontFeatureSettings::Value) + SpecifiedFontFeatureSettings::parse(context, input).map(FontFeatureSettings::Value) } } @@ -2006,9 +2008,51 @@ impl ToCss for FontTag { } } +/// This property provides low-level control over OpenType or TrueType font +/// variations. +pub type FontVariationSettings = FontSettings>; + +fn parse_one_feature_value<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, +) -> Result> { + if let Ok(integer) = input.try(|i| Integer::parse_non_negative(context, i)) { + return Ok(integer) + } + + try_match_ident_ignore_ascii_case! { input, + "on" => Ok(Integer::new(1)), + "off" => Ok(Integer::new(0)), + } +} + +impl Parse for FeatureTagValue { + /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let tag = FontTag::parse(context, input)?; + let value = input.try(|i| parse_one_feature_value(context, i)) + .unwrap_or_else(|_| Integer::new(1)); + + Ok(Self { tag, value }) + } +} + +impl Parse for VariationValue { + /// This is the ` ` part of the font-variation-settings + /// syntax. + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let tag = FontTag::parse(context, input)?; + let value = Number::parse(context, input)?; + Ok(Self { tag, value }) + } +} -/// This property provides low-level control over OpenType or TrueType font variations. -pub type FontVariantSettings = FontSettings; #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)] /// text-zoom. Enable if true, disable if false diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index bff8af54aaa..0d5f2a5ccb3 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -9,6 +9,7 @@ use Namespace; use context::QuirksMode; use cssparser::{Parser, Token, serialize_identifier}; +use num_traits::One; use parser::{ParserContext, Parse}; use self::url::SpecifiedUrl; #[allow(unused_imports)] use std::ascii::AsciiExt; @@ -33,7 +34,7 @@ pub use self::background::{BackgroundRepeat, BackgroundSize}; pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth, BorderSpacing}; pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; -pub use self::font::{FontFamily, FontLanguageOverride, FontVariantSettings, FontVariantEastAsian}; +pub use self::font::{FontFamily, FontLanguageOverride, FontVariationSettings, FontVariantEastAsian}; pub use self::font::{FontVariantLigatures, FontVariantNumeric, FontFeatureSettings}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XTextZoom, XLang}; pub use self::box_::{AnimationIterationCount, AnimationName, Display, OverscrollBehavior, Contain}; @@ -382,12 +383,28 @@ impl ToComputedValue for Opacity { /// An specified ``, optionally coming from a `calc()` expression. /// /// -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd)] pub struct Integer { value: CSSInteger, was_calc: bool, } +impl One for Integer { + #[inline] + fn one() -> Self { + Self::new(1) + } +} + +// This is not great, because it loses calc-ness, but it's necessary for One. +impl ::std::ops::Mul for Integer { + type Output = Self; + + fn mul(self, other: Self) -> Self { + Self::new(self.value * other.value) + } +} + impl Integer { /// Trivially constructs a new `Integer` value. pub fn new(val: CSSInteger) -> Self { @@ -435,7 +452,7 @@ impl Integer { pub fn parse_with_minimum<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, - min: i32 + min: i32, ) -> Result> { match Integer::parse(context, input) { // FIXME(emilio): The spec asks us to avoid rejecting it at parse @@ -498,10 +515,11 @@ impl ToCss for Integer { pub type IntegerOrAuto = Either; impl IntegerOrAuto { - #[allow(missing_docs)] - pub fn parse_positive<'i, 't>(context: &ParserContext, - input: &mut Parser<'i, 't>) - -> Result> { + /// Parse `auto` or a positive integer. + pub fn parse_positive<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { match IntegerOrAuto::parse(context, input) { Ok(Either::First(integer)) if integer.value() <= 0 => { Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 701c594ef56..518c5f46964 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -4727,10 +4727,11 @@ pub extern "C" fn Servo_ParseFontDescriptor( ) -> bool { use cssparser::UnicodeRange; use self::nsCSSFontDesc::*; - use style::computed_values::{font_feature_settings, font_stretch, font_style}; + use style::computed_values::{font_stretch, font_style}; use style::font_face::{FontDisplay, FontWeight, Source}; use style::properties::longhands::font_language_override; use style::values::computed::font::FamilyName; + use style::values::specified::font::SpecifiedFontFeatureSettings; let string = unsafe { (*value).to_string() }; let mut input = ParserInput::new(&string); @@ -4779,7 +4780,7 @@ pub extern "C" fn Servo_ParseFontDescriptor( eCSSFontDesc_Stretch / font_stretch::T, eCSSFontDesc_Src / Vec, eCSSFontDesc_UnicodeRange / Vec, - eCSSFontDesc_FontFeatureSettings / font_feature_settings::T, + eCSSFontDesc_FontFeatureSettings / SpecifiedFontFeatureSettings, eCSSFontDesc_FontLanguageOverride / font_language_override::SpecifiedValue, eCSSFontDesc_Display / FontDisplay, ]