--- a/servo/components/script/dom/cssrule.rs
+++ b/servo/components/script/dom/cssrule.rs
@@ -73,16 +73,17 @@ impl CSSRule {
// CSSRule based on which rule it is
pub fn new_specific(window: &Window, parent_stylesheet: &CSSStyleSheet,
rule: StyleCssRule) -> Root<CSSRule> {
// be sure to update the match in as_specific when this is updated
match rule {
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)),
StyleCssRule::Namespace(s) => Root::upcast(CSSNamespaceRule::new(window, parent_stylesheet, s)),
StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent_stylesheet, s)),
StyleCssRule::Supports(s) => Root::upcast(CSSSupportsRule::new(window, parent_stylesheet, s)),
StyleCssRule::Page(_) => unreachable!(),
StyleCssRule::Document(_) => unimplemented!(), // TODO
--- a/servo/components/style/error_reporting.rs
+++ b/servo/components/style/error_reporting.rs
@@ -13,18 +13,22 @@ use style_traits::ParseError;
use stylesheets::UrlExtraData;
/// Errors that can be encountered while parsing CSS.
pub enum ContextualParseError<'a> {
/// A property declaration was not recognized.
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.
InvalidRule(&'a str, ParseError<'a>),
/// A rule was not recognized.
UnsupportedRule(&'a str, ParseError<'a>),
/// A viewport descriptor declaration was not recognized.
UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>),
@@ -103,19 +107,25 @@ impl<'a> ContextualParseError<'a> {
match *self {
ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err) =>
format!("Unsupported property declaration: '{}', {}", decl,
parse_error_to_str(err)),
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)),
ContextualParseError::InvalidRule(rule, ref err) =>
format!("Invalid rule: '{}', {}", rule, parse_error_to_str(err)),
ContextualParseError::UnsupportedRule(rule, ref err) =>
format!("Unsupported rule: '{}', {}", rule, parse_error_to_str(err)),
ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) =>
--- a/servo/components/style/invalidation/stylesheets.rs
+++ b/servo/components/style/invalidation/stylesheets.rs
@@ -296,17 +296,18 @@ impl StylesheetInvalidationSet {
Supports(..) => {
// Do nothing, relevant nested rules are visited as part of the
// iteration.
}
FontFace(..) |
CounterStyle(..) |
Keyframes(..) |
Page(..) |
- Viewport(..) => {
+ Viewport(..) |
+ FontFeatureValues(..) => {
debug!(" > Found unsupported rule, marking the whole subtree \
invalid.");
// TODO(emilio): Can we do better here?
//
// At least in `@page`, we could check the relevant media, I
// guess.
self.fully_invalid = true;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/stylesheets/font_feature_values_rule.rs
@@ -0,0 +1,391 @@
+/* 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::{CompactCowStr, 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)),
+ t => Err(BasicParseError::UnexpectedToken(t).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>> {
+ match input.next()? {
+ Token::Number { int_value: Some(a), .. } if a >= 0 => {
+ match input.next() {
+ Ok(Token::Number { int_value: Some(b), .. }) if b >= 0 => {
+ Ok(PairValues(a as u32, Some(b as u32)))
+ }
+ // It can't be anything other than number.
+ Ok(t) => Err(BasicParseError::UnexpectedToken(t).into()),
+ // It can be just one value.
+ Err(_) => Ok(PairValues(a as u32, None))
+ }
+ },
+ t => Err(BasicParseError::UnexpectedToken(t).into())
+ }
+ }
+}
+
+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).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: CompactCowStr<'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: CompactCowStr<'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,
+ ]
+}
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -2,16 +2,17 @@
* 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/. */
//! Style sheets and their CSS rules.
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;
mod media_rule;
mod memory;
mod namespace_rule;
mod page_rule;
mod rule_list;
@@ -28,16 +29,17 @@ use parser::ParserContext;
use servo_arc::Arc;
use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt;
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;
pub use self::media_rule::MediaRule;
pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
pub use self::namespace_rule::NamespaceRule;
pub use self::page_rule::PageRule;
pub use self::rule_parser::{State, TopLevelRuleParser};
@@ -98,16 +100,17 @@ pub enum CssRule {
// No Charset here, CSSCharsetRule has been removed from CSSOM
// https://drafts.csswg.org/cssom/#changes-from-5-december-2013
Namespace(Arc<Locked<NamespaceRule>>),
Import(Arc<Locked<ImportRule>>),
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>>),
Supports(Arc<Locked<SupportsRule>>),
Page(Arc<Locked<PageRule>>),
Document(Arc<Locked<DocumentRule>>),
}
@@ -120,16 +123,17 @@ impl MallocSizeOfWithGuard for CssRule {
match *self {
CssRule::Style(ref lock) => {
lock.read_with(guard).malloc_size_of_children(guard, malloc_size_of)
},
// Measurement of these fields may be added later.
CssRule::Import(_) => 0,
CssRule::Media(_) => 0,
CssRule::FontFace(_) => 0,
+ CssRule::FontFeatureValues(_) => 0,
CssRule::CounterStyle(_) => 0,
CssRule::Keyframes(_) => 0,
CssRule::Namespace(_) => 0,
CssRule::Viewport(_) => 0,
CssRule::Supports(_) => 0,
CssRule::Page(_) => 0,
CssRule::Document(_) => 0,
}
@@ -190,16 +194,17 @@ impl From<SingleRuleParseError> for Rule
impl CssRule {
/// Returns the CSSOM rule type of this rule.
pub fn rule_type(&self) -> CssRuleType {
match *self {
CssRule::Style(_) => CssRuleType::Style,
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,
CssRule::Viewport(_) => CssRuleType::Viewport,
CssRule::Supports(_) => CssRuleType::Supports,
CssRule::Page(_) => CssRuleType::Page,
CssRule::Document(_) => CssRuleType::Document,
}
@@ -293,16 +298,20 @@ impl DeepCloneWithLock for CssRule {
CssRule::Media(Arc::new(
lock.wrap(rule.deep_clone_with_lock(lock, guard, params))))
},
CssRule::FontFace(ref arc) => {
let rule = arc.read_with(guard);
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(
rule.clone_conditionally_gecko_or_servo())))
},
CssRule::Viewport(ref arc) => {
let rule = arc.read_with(guard);
CssRule::Viewport(Arc::new(lock.wrap(rule.clone())))
@@ -335,16 +344,17 @@ impl ToCssWithGuard for CssRule {
// https://drafts.csswg.org/cssom/#serialize-a-css-rule
fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
where W: fmt::Write {
match *self {
CssRule::Namespace(ref lock) => lock.read_with(guard).to_css(guard, dest),
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),
CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest),
}
--- a/servo/components/style/stylesheets/rule_parser.rs
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -1,15 +1,16 @@
/* 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/. */
//! 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::{CompactCowStr, SourceLocation, BasicParseError};
use error_reporting::ContextualParseError;
use font_face::parse_font_face_block;
use media_queries::{parse_media_query_list, MediaList};
use parser::{Parse, ParserContext, log_css_error};
use properties::parse_property_declaration_list;
@@ -17,19 +18,20 @@ use selector_parser::{SelectorImpl, Sele
use selectors::SelectorList;
use selectors::parser::SelectorParseError;
use servo_arc::Arc;
use shared_lock::{Locked, SharedRwLock};
use std::borrow::Cow;
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;
use stylesheets::viewport_rule;
use values::CustomIdent;
use values::KeyframesName;
use values::specified::url::SpecifiedUrl;
@@ -97,16 +99,18 @@ pub enum VendorPrefix {
/// -webkit prefix.
WebKit,
}
/// A rule prelude for a given at-rule.
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.
Media(Arc<Locked<MediaList>>, SourceLocation),
/// An @supports rule, with its conditional
Supports(SupportsCondition, SourceLocation),
/// A @viewport rule prelude.
Viewport,
@@ -341,16 +345,24 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for Ne
},
"supports" => {
let cond = SupportsCondition::parse(input)?;
Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond, location)))
},
"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.
return Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
}
let name = parse_counter_style_name(input)?;
// ASCII-case-insensitive matches for "decimal" and "disc".
// The name is already lower-cased by `parse_counter_style_name`
@@ -409,16 +421,21 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for Ne
input: &mut Parser<'i, 't>
) -> Result<CssRule, ParseError<'i>> {
match prelude {
AtRulePrelude::FontFace(location) => {
let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFace));
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(
parse_counter_style_body(name, &context, input)?.into()))))
}
AtRulePrelude::Media(media_queries, location) => {
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
media_queries: media_queries,
--- a/servo/components/style/stylesheets/rules_iterator.rs
+++ b/servo/components/style/stylesheets/rules_iterator.rs
@@ -81,17 +81,18 @@ impl<'a, 'b, C> Iterator for RulesIterat
match *rule {
CssRule::Namespace(_) |
CssRule::Style(_) |
CssRule::FontFace(_) |
CssRule::CounterStyle(_) |
CssRule::Viewport(_) |
CssRule::Keyframes(_) |
- CssRule::Page(_) => {
+ CssRule::Page(_) |
+ CssRule::FontFeatureValues(_) => {
return Some(rule)
},
CssRule::Import(ref import_rule) => {
let import_rule = import_rule.read_with(self.guard);
if !C::process_import(self.guard,
self.device,
self.quirks_mode,
import_rule) {
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -242,16 +242,17 @@ pub trait StylesheetInDocument {
) -> EffectiveRulesIterator<'a, 'b> {
self.iter_rules::<EffectiveRules>(device, guard)
}
rule_filter! {
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),
effective_supports_rules(Supports => SupportsRule),
effective_page_rules(Page => PageRule),
effective_document_rules(Document => DocumentRule),
}
}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -987,17 +987,18 @@ impl Stylist {
CssRule::Style(..) |
CssRule::Namespace(..) |
CssRule::FontFace(..) |
CssRule::CounterStyle(..) |
CssRule::Supports(..) |
CssRule::Keyframes(..) |
CssRule::Page(..) |
CssRule::Viewport(..) |
- CssRule::Document(..) => {
+ CssRule::Document(..) |
+ CssRule::FontFeatureValues(..) => {
// Not affected by device changes.
continue;
}
CssRule::Import(ref lock) => {
let import_rule = lock.read_with(guard);
let effective_now =
import_rule.stylesheet
.is_effective_for_device(&self.device, guard);
--- a/servo/ports/geckolib/error_reporter.rs
+++ b/servo/ports/geckolib/error_reporter.rs
@@ -191,17 +191,19 @@ trait ErrorHelpers<'a> {
fn to_gecko_message(&self) -> &'static [u8];
}
impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
fn error_data(self) -> (CompactCowStr<'a>, ParseError<'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) |
ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) =>
(s.into(), err),
ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
@@ -277,17 +279,19 @@ impl<'a> ErrorHelpers<'a> for Contextual
ContextualParseError::UnsupportedRule(..) =>
b"PEDeclDropped\0",
ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
- ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
+ ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
+ ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
+ ContextualParseError::InvalidFontFeatureValuesRule(..) =>
b"PEUnknownAtRule\0",
}
}
}
impl ParseErrorReporter for ErrorReporter {
fn report_error<'a>(&self,
input: &mut Parser,
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -9,26 +9,29 @@ use parking_lot::RwLock;
use selectors::attr::*;
use selectors::parser::*;
use servo_arc::Arc;
use servo_atoms::Atom;
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;
use style::properties::Importance;
use style::properties::{CSSWideKeyword, DeclaredValueOwned, PropertyDeclaration, PropertyDeclarationBlock};
use style::properties::longhands;
use style::properties::longhands::animation_play_state;
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;
use style::values::specified::{LengthOrPercentageOrAuto, PositionComponent};
pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
let mut block = PropertyDeclarationBlock::new();
@@ -59,16 +62,24 @@ fn test_parse_stylesheet() {
@keyframes foo {
from { width: 0% }
to {
width: 100%;
width: 50% !important; /* !important not allowed here */
animation-name: 'foo'; /* animation properties not allowed here */
animation-play-state: running; /* … except animation-play-state */
}
+ }
+ @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();
let media = Arc::new(lock.wrap(MediaList::empty()));
let stylesheet = Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0u64);
let mut namespaces = Namespaces::default();
namespaces.default = Some((ns!(html), ()));
@@ -233,16 +244,60 @@ fn test_parse_stylesheet() {
]))),
})),
],
vendor_prefix: None,
source_location: SourceLocation {
line: 16,
column: 18,
},
+ }))),
+ 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: 28,
+ },
})))
], &stylesheet.shared_lock),
},
media: Arc::new(stylesheet.shared_lock.wrap(MediaList::empty())),
shared_lock: stylesheet.shared_lock.clone(),
disabled: AtomicBool::new(false),
};