diff options
author | Manish Goregaokar <manishearth@gmail.com> | 2017-05-23 18:15:47 -0700 |
---|---|---|
committer | Manish Goregaokar <manishsmail@gmail.com> | 2017-05-27 10:47:43 -0700 |
commit | 9ed5a7a05ef28906a2d3b8ee96fba1551b713bbb (patch) | |
tree | 6f479e45f1109724b126907aeead156735b81e02 | |
parent | dd1cc6bb451f043a254bb4305690dc74d3a6f2bb (diff) | |
download | servo-9ed5a7a05ef28906a2d3b8ee96fba1551b713bbb.tar.gz servo-9ed5a7a05ef28906a2d3b8ee96fba1551b713bbb.zip |
stylo: Support font-variation-settings
-rw-r--r-- | components/style/gecko/generated/structs_debug.rs | 14 | ||||
-rw-r--r-- | components/style/gecko/generated/structs_release.rs | 14 | ||||
-rw-r--r-- | components/style/gecko/rules.rs | 7 | ||||
-rw-r--r-- | components/style/properties/gecko.mako.rs | 44 | ||||
-rw-r--r-- | components/style/properties/longhand/font.mako.rs | 138 | ||||
-rw-r--r-- | components/style/values/generics/mod.rs | 151 |
6 files changed, 255 insertions, 113 deletions
diff --git a/components/style/gecko/generated/structs_debug.rs b/components/style/gecko/generated/structs_debug.rs index 0e1193df0a7..3a5b6b8cdec 100644 --- a/components/style/gecko/generated/structs_debug.rs +++ b/components/style/gecko/generated/structs_debug.rs @@ -2196,7 +2196,8 @@ pub mod root { #[repr(C)] #[derive(Debug, Copy)] pub struct FontVariation { - pub _bindgen_opaque_blob: [u32; 2usize], + pub mTag: u32, + pub mValue: f32, } #[test] fn bindgen_test_layout_FontVariation() { @@ -2207,6 +2208,17 @@ pub mod root { concat ! ( "Alignment of " , stringify ! ( FontVariation ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontVariation ) ) . mTag as * + const _ as usize } , 0usize , concat ! ( + "Alignment of field: " , stringify ! ( + FontVariation ) , "::" , stringify ! ( mTag ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontVariation ) ) . mValue as + * const _ as usize } , 4usize , concat ! ( + "Alignment of field: " , stringify ! ( + FontVariation ) , "::" , stringify ! ( mValue ) + )); } impl Clone for FontVariation { fn clone(&self) -> Self { *self } diff --git a/components/style/gecko/generated/structs_release.rs b/components/style/gecko/generated/structs_release.rs index 0c495d5cbd6..12839bf225f 100644 --- a/components/style/gecko/generated/structs_release.rs +++ b/components/style/gecko/generated/structs_release.rs @@ -2102,7 +2102,8 @@ pub mod root { #[repr(C)] #[derive(Debug, Copy)] pub struct FontVariation { - pub _bindgen_opaque_blob: [u32; 2usize], + pub mTag: u32, + pub mValue: f32, } #[test] fn bindgen_test_layout_FontVariation() { @@ -2113,6 +2114,17 @@ pub mod root { concat ! ( "Alignment of " , stringify ! ( FontVariation ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontVariation ) ) . mTag as * + const _ as usize } , 0usize , concat ! ( + "Alignment of field: " , stringify ! ( + FontVariation ) , "::" , stringify ! ( mTag ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontVariation ) ) . mValue as + * const _ as usize } , 4usize , concat ! ( + "Alignment of field: " , stringify ! ( + FontVariation ) , "::" , stringify ! ( mValue ) + )); } impl Clone for FontVariation { fn clone(&self) -> Self { *self } diff --git a/components/style/gecko/rules.rs b/components/style/gecko/rules.rs index 80aa7a7acb2..e9165f7629f 100644 --- a/components/style/gecko/rules.rs +++ b/components/style/gecko/rules.rs @@ -17,6 +17,7 @@ use gecko_bindings::sugar::ns_css_value::ToNsCssValue; use gecko_bindings::sugar::refptr::{RefPtr, UniqueRefPtr}; use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard}; use std::{fmt, str}; +use values::generics::FontSettings; /// A @font-face rule pub type FontFaceRule = RefPtr<nsCSSFontFaceRule>; @@ -36,8 +37,8 @@ impl ToNsCssValue for font_weight::T { impl ToNsCssValue for font_feature_settings::T { fn convert(self, nscssvalue: &mut nsCSSValue) { match self { - font_feature_settings::T::Normal => nscssvalue.set_normal(), - font_feature_settings::T::Tag(tags) => { + 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]; @@ -45,7 +46,7 @@ impl ToNsCssValue for font_feature_settings::T { feature.set_string(str::from_utf8(&raw).unwrap()); let mut index = nsCSSValue::null(); - index.set_integer(entry.value as i32); + index.set_integer(entry.value.0 as i32); (feature, index) })) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index a0a372bcb75..4b1c2cef4fe 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1487,27 +1487,27 @@ fn static_assert() { font-synthesis -x-lang font-variant-alternates font-variant-east-asian font-variant-ligatures font-variant-numeric font-language-override - font-feature-settings""" + font-feature-settings font-variation-settings""" %> <%self:impl_trait style_struct_name="Font" skip_longhands="${skip_font_longhands}" skip_additionals="*"> pub fn set_font_feature_settings(&mut self, v: longhands::font_feature_settings::computed_value::T) { - use properties::longhands::font_feature_settings::computed_value::T; + use values::generics::FontSettings; let current_settings = &mut self.gecko.mFont.fontFeatureSettings; current_settings.clear_pod(); match v { - T::Normal => unsafe { current_settings.set_len_pod(0) }, + FontSettings::Normal => (), // do nothing, length is already 0 - T::Tag(feature_settings) => { + FontSettings::Tag(feature_settings) => { unsafe { current_settings.set_len_pod(feature_settings.len() as u32) }; for (current, feature) in current_settings.iter_mut().zip(feature_settings) { current.mTag = feature.tag; - current.mValue = feature.value; + current.mValue = feature.value.0; } } }; @@ -1527,6 +1527,40 @@ fn static_assert() { } } + pub fn set_font_variation_settings(&mut self, v: longhands::font_variation_settings::computed_value::T) { + use values::generics::FontSettings; + + let current_settings = &mut self.gecko.mFont.fontVariationSettings; + current_settings.clear_pod(); + + match v { + FontSettings::Normal => (), // do nothing, length is already 0 + + FontSettings::Tag(feature_settings) => { + unsafe { current_settings.set_len_pod(feature_settings.len() as u32) }; + + for (current, feature) in current_settings.iter_mut().zip(feature_settings) { + current.mTag = feature.tag; + current.mValue = feature.value.0; + } + } + }; + } + + pub fn copy_font_variation_settings_from(&mut self, other: &Self ) { + let current_settings = &mut self.gecko.mFont.fontVariationSettings; + let feature_settings = &other.gecko.mFont.fontVariationSettings; + let settings_length = feature_settings.len() as u32; + + current_settings.clear_pod(); + unsafe { current_settings.set_len_pod(settings_length) }; + + for (current, feature) in current_settings.iter_mut().zip(feature_settings.iter()) { + current.mTag = feature.mTag; + current.mValue = feature.mValue; + } + } + pub fn fixup_none_generic(&mut self, device: &Device) { unsafe { bindings::Gecko_nsStyleFont_FixupNoneGeneric(&mut self.gecko, &*device.pres_context) diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 110ae7d6800..d4e6f1de977 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -1774,6 +1774,7 @@ ${helpers.single_keyword_system("font-variant-position", use properties::longhands::system_font::SystemFont; use std::fmt; use style_traits::ToCss; + use values::generics::FontSettings; #[derive(Debug, Clone, PartialEq)] pub enum SpecifiedValue { @@ -1785,125 +1786,58 @@ ${helpers.single_keyword_system("font-variant-position", <%self:simple_system_boilerplate name="font_feature_settings"></%self:simple_system_boilerplate> pub mod computed_value { - use cssparser::Parser; - use parser::{Parse, ParserContext}; - use std::fmt; - use style_traits::ToCss; - - #[derive(Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub enum T { - Normal, - Tag(Vec<FeatureTagValue>) - } + use values::generics::{FontSettings, FontSettingTagInt}; + pub type T = FontSettings<FontSettingTagInt>; + } - #[derive(Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct FeatureTagValue { - pub tag: u32, - pub value: u32 - } + #[inline] + pub fn get_initial_value() -> computed_value::T { + FontSettings::Normal + } - impl ToCss for T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - T::Normal => dest.write_str("normal"), - T::Tag(ref ftvs) => { - let mut iter = ftvs.iter(); - // handle head element - try!(iter.next().unwrap().to_css(dest)); - // handle tail, precede each with a delimiter - for ftv in iter { - try!(dest.write_str(", ")); - try!(ftv.to_css(dest)); - } - Ok(()) - } - } - } - } + #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + SpecifiedValue::Value(FontSettings::Normal) + } - impl Parse for T { - /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings - fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { - if input.try(|i| i.expect_ident_matching("normal")).is_ok() { - return Ok(T::Normal); - } - input.parse_comma_separated(|i| FeatureTagValue::parse(context, i)).map(T::Tag) - } - } + /// normal | <feature-tag-value># + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + computed_value::T::parse(context, input).map(SpecifiedValue::Value) + } +</%helpers:longhand> - impl ToCss for FeatureTagValue { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - use std::str; - use byteorder::{WriteBytesExt, BigEndian}; - use cssparser::serialize_string; - - let mut raw: Vec<u8> = vec!(); - raw.write_u32::<BigEndian>(self.tag).unwrap(); - serialize_string(str::from_utf8(&raw).unwrap_or_default(), dest)?; - - match self.value { - 1 => Ok(()), - 0 => dest.write_str(" off"), - x => write!(dest, " {}", x) - } - } - } +<% +# This spec link is too long to fit elsewhere +variation_spec = """\ +https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control-the-font-variation-settings-property\ +""" +%> +<%helpers:longhand name="font-variation-settings" products="gecko" animation_value_type="none" + boxed="True" + spec="${variation_spec}"> + use values::computed::ComputedValueAsSpecified; + use values::generics::FontSettings; - impl Parse for FeatureTagValue { - /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings - /// <string> [ on | off | <integer> ] - fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { - use std::io::Cursor; - use byteorder::{ReadBytesExt, BigEndian}; + impl ComputedValueAsSpecified for SpecifiedValue {} - let tag = try!(input.expect_string()); + pub type SpecifiedValue = computed_value::T; - // allowed strings of length 4 containing chars: <U+20, U+7E> - if tag.len() != 4 || - tag.chars().any(|c| c < ' ' || c > '~') - { - return Err(()) - } + no_viewport_percentage!(SpecifiedValue); - let mut raw = Cursor::new(tag.as_bytes()); - let u_tag = raw.read_u32::<BigEndian>().unwrap(); - if let Ok(value) = input.try(|input| input.expect_integer()) { - // handle integer, throw if it is negative - if value >= 0 { - Ok(FeatureTagValue { tag: u_tag, value: value as u32 }) - } else { - Err(()) - } - } else if let Ok(_) = input.try(|input| input.expect_ident_matching("on")) { - // on is an alias for '1' - Ok(FeatureTagValue { tag: u_tag, value: 1 }) - } else if let Ok(_) = input.try(|input| input.expect_ident_matching("off")) { - // off is an alias for '0' - Ok(FeatureTagValue { tag: u_tag, value: 0 }) - } else { - // empty value is an alias for '1' - Ok(FeatureTagValue { tag: u_tag, value: 1 }) - } - } - } + pub mod computed_value { + use values::generics::{FontSettings, FontSettingTagFloat}; + pub type T = FontSettings<FontSettingTagFloat>; } #[inline] pub fn get_initial_value() -> computed_value::T { - computed_value::T::Normal - } - - #[inline] - pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue::Value(computed_value::T::Normal) + FontSettings::Normal } /// normal | <feature-tag-value># pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { - computed_value::T::parse(context, input).map(SpecifiedValue::Value) + computed_value::T::parse(context, input) } </%helpers:longhand> diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 1efc617b0b0..7f903eed95b 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -10,7 +10,7 @@ use cssparser::Parser; use euclid::size::Size2D; use parser::{Parse, ParserContext}; use std::fmt; -use style_traits::{HasViewportPercentage, ToCss}; +use style_traits::{HasViewportPercentage, OneOrMoreCommaSeparated, ToCss}; use super::CustomIdent; pub use self::basic_shape::serialize_radius_values; @@ -108,3 +108,152 @@ impl ToCss for CounterStyleOrNone { } } } + +/// 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, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct FontSettingTag<T> { + /// A four-character tag, packed into a u32 (one byte per character) + pub tag: u32, + /// The value + pub value: T, +} + +impl<T> OneOrMoreCommaSeparated for FontSettingTag<T> {} + +impl<T: ToCss> ToCss for FontSettingTag<T> { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + use byteorder::{WriteBytesExt, BigEndian}; + use cssparser::serialize_string; + use std::str; + + let mut raw: Vec<u8> = vec!(); + raw.write_u32::<BigEndian>(self.tag).unwrap(); + serialize_string(str::from_utf8(&raw).unwrap_or_default(), dest)?; + + self.value.to_css(dest) + } +} + +impl<T: Parse> Parse for FontSettingTag<T> { + /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings + /// https://drafts.csswg.org/css-fonts-4/#low-level-font-variation- + /// settings-control-the-font-variation-settings-property + /// <string> [ on | off | <integer> ] + /// <string> <number> + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + use byteorder::{ReadBytesExt, BigEndian}; + use std::io::Cursor; + + let tag = try!(input.expect_string()); + + // allowed strings of length 4 containing chars: <U+20, U+7E> + if tag.len() != 4 || + tag.chars().any(|c| c < ' ' || c > '~') + { + return Err(()) + } + + let mut raw = Cursor::new(tag.as_bytes()); + let u_tag = raw.read_u32::<BigEndian>().unwrap(); + + Ok(FontSettingTag { tag: u_tag, value: T::parse(context, input)? }) + } +} + + +/// A font settings value for font-variation-settings or font-feature-settings +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum FontSettings<T> { + /// No settings (default) + Normal, + /// Set of settings + Tag(Vec<FontSettingTag<T>>) +} + +impl<T: ToCss> ToCss for FontSettings<T> { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + FontSettings::Normal => dest.write_str("normal"), + FontSettings::Tag(ref ftvs) => ftvs.to_css(dest) + } + } +} + +impl<T: Parse> Parse for FontSettings<T> { + /// https://www.w3.org/TR/css-fonts-3/#propdef-font-feature-settings + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + 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 +/// +/// Do not use this type anywhere except within FontSettings +/// because it serializes with the preceding space +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct FontSettingTagInt(pub u32); +/// A number value to be used for font-variation-settings +/// +/// Do not use this type anywhere except within FontSettings +/// because it serializes with the preceding space +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct FontSettingTagFloat(pub f32); + +impl ToCss for FontSettingTagInt { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + 1 => Ok(()), + 0 => dest.write_str(" off"), + x => write!(dest, " {}", x) + } + } +} + +impl Parse for FontSettingTagInt { + fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + 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(()) + } + } 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)) + } + } +} + + +impl Parse for FontSettingTagFloat { + fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + input.expect_number().map(FontSettingTagFloat) + } +} + +impl ToCss for FontSettingTagFloat { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str(" ")?; + self.0.to_css(dest) + } +} + + |