diff options
-rw-r--r-- | components/script/dom/cssrule.rs | 1 | ||||
-rw-r--r-- | components/style/error_reporting.rs | 10 | ||||
-rw-r--r-- | components/style/invalidation/stylesheets.rs | 3 | ||||
-rw-r--r-- | components/style/stylesheets/font_feature_values_rule.rs | 389 | ||||
-rw-r--r-- | components/style/stylesheets/mod.rs | 10 | ||||
-rw-r--r-- | components/style/stylesheets/rule_parser.rs | 21 | ||||
-rw-r--r-- | components/style/stylesheets/rules_iterator.rs | 3 | ||||
-rw-r--r-- | components/style/stylesheets/stylesheet.rs | 1 | ||||
-rw-r--r-- | components/style/stylist.rs | 3 | ||||
-rw-r--r-- | ports/geckolib/error_reporter.rs | 6 | ||||
-rw-r--r-- | tests/unit/style/stylesheets.rs | 55 |
11 files changed, 496 insertions, 6 deletions
diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index 32e6ad05471..40bd087899b 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -78,6 +78,7 @@ impl CSSRule { StyleCssRule::Import(s) => Root::upcast(CSSImportRule::new(window, parent_stylesheet, s)), StyleCssRule::Style(s) => Root::upcast(CSSStyleRule::new(window, parent_stylesheet, s)), StyleCssRule::FontFace(s) => Root::upcast(CSSFontFaceRule::new(window, parent_stylesheet, s)), + StyleCssRule::FontFeatureValues(_) => unimplemented!(), StyleCssRule::CounterStyle(_) => unimplemented!(), StyleCssRule::Keyframes(s) => Root::upcast(CSSKeyframesRule::new(window, parent_stylesheet, s)), StyleCssRule::Media(s) => Root::upcast(CSSMediaRule::new(window, parent_stylesheet, s)), diff --git a/components/style/error_reporting.rs b/components/style/error_reporting.rs index 09b7abf1c1d..56674fc908e 100644 --- a/components/style/error_reporting.rs +++ b/components/style/error_reporting.rs @@ -18,8 +18,12 @@ pub enum ContextualParseError<'a> { UnsupportedPropertyDeclaration(&'a str, ParseError<'a>), /// A font face descriptor was not recognized. UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>), + /// A font feature values descroptor was not recognized. + UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>), /// A keyframe rule was not valid. InvalidKeyframeRule(&'a str, ParseError<'a>), + /// A font feature values rule was not valid. + InvalidFontFeatureValuesRule(&'a str, ParseError<'a>), /// A keyframe property declaration was not recognized. UnsupportedKeyframePropertyDeclaration(&'a str, ParseError<'a>), /// A rule was invalid for some reason. @@ -108,9 +112,15 @@ impl<'a> ContextualParseError<'a> { ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => format!("Unsupported @font-face descriptor declaration: '{}', {}", decl, parse_error_to_str(err)), + ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => + format!("Unsupported @font-feature-values descriptor declaration: '{}', {}", decl, + parse_error_to_str(err)), ContextualParseError::InvalidKeyframeRule(rule, ref err) => format!("Invalid keyframe rule: '{}', {}", rule, parse_error_to_str(err)), + ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => + format!("Invalid font feature value rule: '{}', {}", rule, + parse_error_to_str(err)), ContextualParseError::UnsupportedKeyframePropertyDeclaration(decl, ref err) => format!("Unsupported keyframe property declaration: '{}', {}", decl, parse_error_to_str(err)), diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs index 19c35405676..04a13d934ba 100644 --- a/components/style/invalidation/stylesheets.rs +++ b/components/style/invalidation/stylesheets.rs @@ -301,7 +301,8 @@ impl StylesheetInvalidationSet { CounterStyle(..) | Keyframes(..) | Page(..) | - Viewport(..) => { + Viewport(..) | + FontFeatureValues(..) => { debug!(" > Found unsupported rule, marking the whole subtree \ invalid."); diff --git a/components/style/stylesheets/font_feature_values_rule.rs b/components/style/stylesheets/font_feature_values_rule.rs new file mode 100644 index 00000000000..e18f9174c2b --- /dev/null +++ b/components/style/stylesheets/font_feature_values_rule.rs @@ -0,0 +1,389 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! The [`@font-feature-values`][font-feature-values] at-rule. +//! +//! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule + +use Atom; +use computed_values::font_family::FamilyName; +use cssparser::{AtRuleParser, AtRuleType, BasicParseError, DeclarationListParser, DeclarationParser, Parser}; +use cssparser::{CowRcStr, RuleListParser, SourceLocation, QualifiedRuleParser, Token, serialize_identifier}; +use error_reporting::ContextualParseError; +use parser::{ParserContext, log_css_error, Parse}; +use selectors::parser::SelectorParseError; +use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; +use std::fmt; +use style_traits::{ParseError, StyleParseError, ToCss}; +use stylesheets::CssRuleType; + +/// A @font-feature-values block declaration. +/// It is `<ident>: <integer>+`. +/// This struct can take 3 value types. +/// - `SingleValue` is to keep just one unsigned integer value. +/// - `PairValues` is to keep one or two unsigned integer values. +/// - `VectorValues` is to keep a list of unsigned integer values. +#[derive(Clone, Debug, PartialEq)] +pub struct FFVDeclaration<T> { + /// An `<ident>` for declaration name. + pub name: Atom, + /// An `<integer>+` for declaration value. + pub value: T, +} + +impl<T: ToCss> ToCss for FFVDeclaration<T> { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + serialize_identifier(&self.name.to_string(), dest)?; + dest.write_str(": ")?; + self.value.to_css(dest)?; + dest.write_str(";") + } +} + +/// A @font-feature-values block declaration value that keeps one value. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleValue(pub u32); + +impl Parse for SingleValue { + fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result<SingleValue, ParseError<'i>> { + match *input.next()? { + Token::Number { int_value: Some(v), .. } if v >= 0 => Ok(SingleValue(v as u32)), + ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()), + } + } +} + +impl ToCss for SingleValue { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + write!(dest, "{}", self.0) + } +} + +/// A @font-feature-values block declaration value that keeps one or two values. +#[derive(Clone, Debug, PartialEq)] +pub struct PairValues(pub u32, pub Option<u32>); + +impl Parse for PairValues { + fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result<PairValues, ParseError<'i>> { + let first = match *input.next()? { + Token::Number { int_value: Some(a), .. } if a >= 0 => a as u32, + ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()), + }; + match input.next() { + Ok(&Token::Number { int_value: Some(b), .. }) if b >= 0 => { + Ok(PairValues(first, Some(b as u32))) + } + // It can't be anything other than number. + Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()), + // It can be just one value. + Err(_) => Ok(PairValues(first, None)) + } + } +} + +impl ToCss for PairValues { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + write!(dest, "{}", self.0)?; + if let Some(second) = self.1 { + write!(dest, " {}", second)?; + } + Ok(()) + } +} + +/// A @font-feature-values block declaration value that keeps a list of values. +#[derive(Clone, Debug, PartialEq)] +pub struct VectorValues(pub Vec<u32>); + +impl Parse for VectorValues { + fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result<VectorValues, ParseError<'i>> { + let mut vec = vec![]; + loop { + match input.next() { + Ok(&Token::Number { int_value: Some(a), .. }) if a >= 0 => { + vec.push(a as u32); + }, + // It can't be anything other than number. + Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()), + Err(_) => break, + } + } + + if vec.len() == 0 { + return Err(BasicParseError::EndOfInput.into()); + } + + Ok(VectorValues(vec)) + } +} + +impl ToCss for VectorValues { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut iter = self.0.iter(); + let first = iter.next(); + if let Some(first) = first { + write!(dest, "{}", first)?; + for value in iter { + dest.write_str(" ")?; + write!(dest, "{}", value)?; + } + } + Ok(()) + } +} + +/// Parses a list of `FamilyName`s. +pub fn parse_family_name_list<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result<Vec<FamilyName>, ParseError<'i>> { + input.parse_comma_separated(|i| FamilyName::parse(context, i)).map_err(|e| e.into()) +} + +/// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`. +/// (`<ident>: <integer>+`) +struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> { + context: &'a ParserContext<'b>, + declarations: &'a mut Vec<FFVDeclaration<T>>, +} + +/// Default methods reject all at rules. +impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> { + type Prelude = (); + type AtRule = (); + type Error = SelectorParseError<'i, StyleParseError<'i>>; +} + +impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T> + where T: Parse +{ + type Declaration = (); + type Error = SelectorParseError<'i, StyleParseError<'i>>; + + fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>) + -> Result<(), ParseError<'i>> { + let value = input.parse_entirely(|i| T::parse(self.context, i))?; + let new = FFVDeclaration { + name: Atom::from(&*name), + value: value, + }; + update_or_push(&mut self.declarations, new); + Ok(()) + } +} + +macro_rules! font_feature_values_blocks { + ( + blocks = [ + $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident: $ty: ty, )* + ] + ) => { + /// The [`@font-feature-values`][font-feature-values] at-rule. + /// + /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule + #[derive(Clone, Debug, PartialEq)] + pub struct FontFeatureValuesRule { + /// Font family list for @font-feature-values rule. + /// Family names cannot contain generic families. FamilyName + /// also accepts only non-generic names. + pub family_names: Vec<FamilyName>, + $( + #[$doc] + pub $ident: Vec<FFVDeclaration<$ty>>, + )* + /// The line and column of the rule's source code. + pub source_location: SourceLocation, + } + + impl FontFeatureValuesRule { + /// Creates an empty FontFeatureValuesRule with given location and family name list. + fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self { + FontFeatureValuesRule { + family_names: family_names, + $( + $ident: vec![], + )* + source_location: location, + } + } + + /// Parses a `FontFeatureValuesRule`. + pub fn parse(context: &ParserContext, input: &mut Parser, + family_names: Vec<FamilyName>, location: SourceLocation) + -> FontFeatureValuesRule { + let mut rule = FontFeatureValuesRule::new(family_names, location); + + { + let mut iter = RuleListParser::new_for_nested_rule(input, FontFeatureValuesRuleParser { + context: context, + rule: &mut rule, + }); + while let Some(result) = iter.next() { + if let Err(err) = result { + let pos = err.span.start; + let error = ContextualParseError::UnsupportedRule( + iter.input.slice(err.span), err.error); + log_css_error(iter.input, pos, error, context); + } + } + } + rule + } + + /// Prints font family names. + pub fn font_family_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut iter = self.family_names.iter(); + iter.next().unwrap().to_css(dest)?; + for val in iter { + dest.write_str(", ")?; + val.to_css(dest)?; + } + Ok(()) + } + + /// Prints inside of `@font-feature-values` block. + pub fn value_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + $( + if self.$ident.len() > 0 { + dest.write_str(concat!("@", $name, " {\n"))?; + let iter = self.$ident.iter(); + for val in iter { + val.to_css(dest)?; + dest.write_str("\n")? + } + dest.write_str("}\n")? + } + )* + Ok(()) + } + } + + impl ToCssWithGuard for FontFeatureValuesRule { + fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result + where W: fmt::Write + { + dest.write_str("@font-feature-values ")?; + self.font_family_to_css(dest)?; + dest.write_str(" {\n")?; + self.value_to_css(dest)?; + dest.write_str("}") + } + } + + /// Updates with new value if same `ident` exists, otherwise pushes to the vector. + fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) { + let position = vec.iter().position(|ref val| val.name == element.name); + if let Some(index) = position { + vec[index].value = element.value; + } else { + vec.push(element); + } + } + + /// Keeps the information about block type like @swash, @styleset etc. + enum BlockType { + $( + $ident_camel, + )* + } + + /// Parser for `FontFeatureValuesRule`. Parses all blocks + /// <feature-type> { + /// <feature-value-declaration-list> + /// } + /// <feature-type> = @stylistic | @historical-forms | @styleset | + /// @character-variant | @swash | @ornaments | @annotation + struct FontFeatureValuesRuleParser<'a> { + context: &'a ParserContext<'a>, + rule: &'a mut FontFeatureValuesRule, + } + + /// Default methods reject all qualified rules. + impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> { + type Prelude = (); + type QualifiedRule = (); + type Error = SelectorParseError<'i, StyleParseError<'i>>; + } + + impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> { + type Prelude = BlockType; + type AtRule = (); + type Error = SelectorParseError<'i, StyleParseError<'i>>; + + fn parse_prelude<'t>(&mut self, + name: CowRcStr<'i>, + _input: &mut Parser<'i, 't>) + -> Result<AtRuleType<Self::Prelude, Self::AtRule>, ParseError<'i>> { + match_ignore_ascii_case! { &*name, + $( + $name => Ok(AtRuleType::WithBlock(BlockType::$ident_camel)), + )* + _ => Err(BasicParseError::AtRuleBodyInvalid.into()), + } + } + + fn parse_block<'t>(&mut self, prelude: Self::Prelude, input: &mut Parser<'i, 't>) + -> Result<Self::AtRule, ParseError<'i>> { + let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFeatureValues)); + match prelude { + $( + BlockType::$ident_camel => { + let parser = FFVDeclarationsParser { + context: &context, + declarations: &mut self.rule.$ident, + }; + + let mut iter = DeclarationListParser::new(input, parser); + while let Some(declaration) = iter.next() { + if let Err(err) = declaration { + let pos = err.span.start; + let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration( + iter.input.slice(err.span), err.error); + log_css_error(iter.input, pos, error, &context); + } + } + }, + )* + } + + Ok(()) + } + } + } +} + +font_feature_values_blocks! { + blocks = [ + #[doc = "A @swash blocksck. \ + Specifies a feature name that will work with the swash() \ + functional notation of font-variant-alternates."] + "swash" swash / Swash: SingleValue, + + #[doc = "A @stylistic block. \ + Specifies a feature name that will work with the annotation() \ + functional notation of font-variant-alternates."] + "stylistic" stylistic / Stylistic: SingleValue, + + #[doc = "A @ornaments block. \ + Specifies a feature name that will work with the ornaments() ] \ + functional notation of font-variant-alternates."] + "ornaments" ornaments / Ornaments: SingleValue, + + #[doc = "A @annotation block. \ + Specifies a feature name that will work with the stylistic() \ + functional notation of font-variant-alternates."] + "annotation" annotation / Annotation: SingleValue, + + #[doc = "A @character-variant block. \ + Specifies a feature name that will work with the styleset() \ + functional notation of font-variant-alternates. The value can be a pair."] + "character-variant" character_variant / CharacterVariant: PairValues, + + #[doc = "A @styleset block. \ + Specifies a feature name that will work with the character-variant() \ + functional notation of font-variant-alternates. The value can be a list."] + "styleset" styleset / Styleset: VectorValues, + ] +} diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index c86c7c6f051..09aedb95218 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -7,6 +7,7 @@ mod counter_style_rule; mod document_rule; mod font_face_rule; +pub mod font_feature_values_rule; pub mod import_rule; pub mod keyframes_rule; mod loader; @@ -33,6 +34,7 @@ use style_traits::PARSING_MODE_DEFAULT; pub use self::counter_style_rule::CounterStyleRule; pub use self::document_rule::DocumentRule; pub use self::font_face_rule::FontFaceRule; +pub use self::font_feature_values_rule::FontFeatureValuesRule; pub use self::import_rule::ImportRule; pub use self::keyframes_rule::KeyframesRule; pub use self::loader::StylesheetLoader; @@ -103,6 +105,7 @@ pub enum CssRule { Style(Arc<Locked<StyleRule>>), Media(Arc<Locked<MediaRule>>), FontFace(Arc<Locked<FontFaceRule>>), + FontFeatureValues(Arc<Locked<FontFeatureValuesRule>>), CounterStyle(Arc<Locked<CounterStyleRule>>), Viewport(Arc<Locked<ViewportRule>>), Keyframes(Arc<Locked<KeyframesRule>>), @@ -125,6 +128,7 @@ impl MallocSizeOfWithGuard for CssRule { CssRule::Import(_) => 0, CssRule::Media(_) => 0, CssRule::FontFace(_) => 0, + CssRule::FontFeatureValues(_) => 0, CssRule::CounterStyle(_) => 0, CssRule::Keyframes(_) => 0, CssRule::Namespace(_) => 0, @@ -195,6 +199,7 @@ impl CssRule { CssRule::Import(_) => CssRuleType::Import, CssRule::Media(_) => CssRuleType::Media, CssRule::FontFace(_) => CssRuleType::FontFace, + CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues, CssRule::CounterStyle(_) => CssRuleType::CounterStyle, CssRule::Keyframes(_) => CssRuleType::Keyframes, CssRule::Namespace(_) => CssRuleType::Namespace, @@ -298,6 +303,10 @@ impl DeepCloneWithLock for CssRule { CssRule::FontFace(Arc::new(lock.wrap( rule.clone_conditionally_gecko_or_servo()))) }, + CssRule::FontFeatureValues(ref arc) => { + let rule = arc.read_with(guard); + CssRule::FontFeatureValues(Arc::new(lock.wrap(rule.clone()))) + }, CssRule::CounterStyle(ref arc) => { let rule = arc.read_with(guard); CssRule::CounterStyle(Arc::new(lock.wrap( @@ -340,6 +349,7 @@ impl ToCssWithGuard for CssRule { CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::FontFeatureValues(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest), diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 9c223be1f5d..fd78401d59a 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -5,6 +5,7 @@ //! Parsing of the stylesheet contents. use {Namespace, Prefix}; +use computed_values::font_family::FamilyName; use counter_style::{parse_counter_style_body, parse_counter_style_name}; use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser}; use cssparser::{CowRcStr, SourceLocation, BasicParseError}; @@ -21,9 +22,10 @@ use shared_lock::{Locked, SharedRwLock}; use str::starts_with_ignore_ascii_case; use style_traits::{StyleParseError, ParseError}; use stylesheets::{CssRule, CssRules, CssRuleType, Origin, StylesheetLoader}; -use stylesheets::{DocumentRule, KeyframesRule, MediaRule, NamespaceRule, PageRule}; -use stylesheets::{StyleRule, SupportsRule, ViewportRule}; +use stylesheets::{DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule}; +use stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule}; use stylesheets::document_rule::DocumentCondition; +use stylesheets::font_feature_values_rule::parse_family_name_list; use stylesheets::keyframes_rule::parse_keyframe_list; use stylesheets::stylesheet::Namespaces; use stylesheets::supports_rule::SupportsCondition; @@ -101,6 +103,8 @@ pub enum VendorPrefix { pub enum AtRulePrelude { /// A @font-face rule prelude. FontFace(SourceLocation), + /// A @font-feature-values rule prelude, with its FamilyName list. + FontFeatureValues(Vec<FamilyName>, SourceLocation), /// A @counter-style rule prelude, with its counter style name. CounterStyle(CustomIdent), /// A @media rule prelude, with its media queries. @@ -345,6 +349,14 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { "font-face" => { Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace(location))) }, + "font-feature-values" => { + if !cfg!(feature = "gecko") && !cfg!(feature = "testing") { + // Support for this rule is not fully implemented in Servo yet. + return Err(StyleParseError::UnsupportedAtRule(name.clone()).into()) + } + let family_names = parse_family_name_list(self.context, input)?; + Ok(AtRuleType::WithBlock(AtRulePrelude::FontFeatureValues(family_names, location))) + }, "counter-style" => { if !cfg!(feature = "gecko") { // Support for this rule is not fully implemented in Servo yet. @@ -413,6 +425,11 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap( parse_font_face_block(&context, input, location).into())))) } + AtRulePrelude::FontFeatureValues(family_names, location) => { + let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFeatureValues)); + Ok(CssRule::FontFeatureValues(Arc::new(self.shared_lock.wrap( + FontFeatureValuesRule::parse(&context, input, family_names, location))))) + } AtRulePrelude::CounterStyle(name) => { let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::CounterStyle)); Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap( diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs index cb8f83abb7d..95a3ab8b388 100644 --- a/components/style/stylesheets/rules_iterator.rs +++ b/components/style/stylesheets/rules_iterator.rs @@ -86,7 +86,8 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C> CssRule::CounterStyle(_) | CssRule::Viewport(_) | CssRule::Keyframes(_) | - CssRule::Page(_) => { + CssRule::Page(_) | + CssRule::FontFeatureValues(_) => { return Some(rule) }, CssRule::Import(ref import_rule) => { diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 805a539ef98..f25b2894c49 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -247,6 +247,7 @@ pub trait StylesheetInDocument { effective_style_rules(Style => StyleRule), effective_media_rules(Media => MediaRule), effective_font_face_rules(FontFace => FontFaceRule), + effective_font_face_feature_values_rules(FontFeatureValues => FontFeatureValuesRule), effective_counter_style_rules(CounterStyle => CounterStyleRule), effective_viewport_rules(Viewport => ViewportRule), effective_keyframes_rules(Keyframes => KeyframesRule), diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 1629827a96c..6ec47c45675 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -995,7 +995,8 @@ impl Stylist { CssRule::Keyframes(..) | CssRule::Page(..) | CssRule::Viewport(..) | - CssRule::Document(..) => { + CssRule::Document(..) | + CssRule::FontFeatureValues(..) => { // Not affected by device changes. continue; } diff --git a/ports/geckolib/error_reporter.rs b/ports/geckolib/error_reporter.rs index 7c6690716f0..536ad19aa69 100644 --- a/ports/geckolib/error_reporter.rs +++ b/ports/geckolib/error_reporter.rs @@ -203,7 +203,9 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> { match self { ContextualParseError::UnsupportedPropertyDeclaration(s, err) | ContextualParseError::UnsupportedFontFaceDescriptor(s, err) | + ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) | ContextualParseError::InvalidKeyframeRule(s, err) | + ContextualParseError::InvalidFontFeatureValuesRule(s, err) | ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) | ContextualParseError::InvalidRule(s, err) | ContextualParseError::UnsupportedRule(s, err) | @@ -289,7 +291,9 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> { ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) | ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols | ContextualParseError::InvalidCounterStyleExtendsWithSymbols | - ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => + ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols | + ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) | + ContextualParseError::InvalidFontFeatureValuesRule(..) => (b"PEUnknownAtRule\0", Action::Skip), } } diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 1be7fe16b69..4eb371317f9 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -14,6 +14,7 @@ use servo_url::ServoUrl; use std::borrow::ToOwned; use std::sync::Mutex; use std::sync::atomic::AtomicBool; +use style::computed_values::font_family::FamilyName; use style::context::QuirksMode; use style::error_reporting::{ParseErrorReporter, ContextualParseError}; use style::media_queries::MediaList; @@ -24,6 +25,8 @@ use style::properties::longhands::animation_timing_function; use style::shared_lock::SharedRwLock; use style::stylesheets::{Origin, Namespaces}; use style::stylesheets::{Stylesheet, StylesheetContents, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule}; +use style::stylesheets::font_feature_values_rule::{FFVDeclaration, FontFeatureValuesRule}; +use style::stylesheets::font_feature_values_rule::{SingleValue, PairValues, VectorValues}; use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePercentage}; use style::values::{KeyframesName, CustomIdent}; use style::values::computed::Percentage; @@ -65,6 +68,14 @@ fn test_parse_stylesheet() { animation-name: 'foo'; /* animation properties not allowed here */ animation-timing-function: ease; /* … except animation-timing-function */ } + } + @font-feature-values test { + @swash { foo: 12; bar: 24; } + @swash { bar: 36; baz: 48; } + @stylistic { fooo: 14; } + @rubbish { shouldnt-parse: 1; } + @styleset { hello: 10 11 12; } + @character-variant { ok: 78 2; } }"; let url = ServoUrl::parse("about::test").unwrap(); let lock = SharedRwLock::new(); @@ -239,6 +250,50 @@ fn test_parse_stylesheet() { line: 16, column: 19, }, + }))), + CssRule::FontFeatureValues(Arc::new(stylesheet.shared_lock.wrap(FontFeatureValuesRule { + family_names: vec![FamilyName { + name: Atom::from("test"), + quoted: false, + }], + swash: vec![ + FFVDeclaration { + name: "foo".into(), + value: SingleValue(12 as u32), + }, + FFVDeclaration { + name: "bar".into(), + value: SingleValue(36 as u32), + }, + FFVDeclaration { + name: "baz".into(), + value: SingleValue(48 as u32), + } + ], + stylistic: vec![ + FFVDeclaration { + name: "fooo".into(), + value: SingleValue(14 as u32), + } + ], + ornaments: vec![], + annotation: vec![], + character_variant: vec![ + FFVDeclaration { + name: "ok".into(), + value: PairValues(78 as u32, Some(2 as u32)), + }, + ], + styleset: vec![ + FFVDeclaration { + name: "hello".into(), + value: VectorValues(vec![10 as u32, 11 as u32, 12 as u32]), + }, + ], + source_location: SourceLocation { + line: 25, + column: 29, + }, }))) ], &stylesheet.shared_lock), |