From 7724bb01a34a8eb6f8338911ab9035886144dff4 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 19 Jan 2017 17:53:41 +0100 Subject: [PATCH 01/12] Remove intermediate FontFaceDescriptorDeclaration enum. Have `@font-face` parsing write to `FontFaceRule` directly. --- components/style/font_face.rs | 57 ++++++++++++++--------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index d7eaaf79e6c..a25c1948bd3 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -108,33 +108,26 @@ impl ToCss for FontFaceRule { /// Note that the prelude parsing code lives in the `stylesheets` module. pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser) -> Result { - let mut family = None; - let mut src = None; - let mut iter = DeclarationListParser::new(input, FontFaceRuleParser { context: context }); - while let Some(declaration) = iter.next() { - match declaration { - Err(range) => { + let mut rule = FontFaceRule { + family: FontFamily::Generic(atom!("")), + sources: Vec::new(), + }; + { + let parser = FontFaceRuleParser { context: context, rule: &mut rule }; + let mut iter = DeclarationListParser::new(input, parser); + while let Some(declaration) = iter.next() { + if let Err(range) = declaration { let pos = range.start; let message = format!("Unsupported @font-face descriptor declaration: '{}'", iter.input.slice(range)); log_css_error(iter.input, pos, &*message, context); } - Ok(FontFaceDescriptorDeclaration::Family(value)) => { - family = Some(value); - } - Ok(FontFaceDescriptorDeclaration::Src(value)) => { - src = Some(value); - } } } - match (family, src) { - (Some(family), Some(src)) => { - Ok(FontFaceRule { - family: family, - sources: src, - }) - } - _ => Err(()) + if rule.family != FontFamily::Generic(atom!("")) && !rule.sources.is_empty() { + Ok(rule) + } else { + Err(()) } } @@ -171,40 +164,34 @@ impl iter::Iterator for EffectiveSources { } } -enum FontFaceDescriptorDeclaration { - Family(FontFamily), - Src(Vec), -} - - struct FontFaceRuleParser<'a, 'b: 'a> { context: &'a ParserContext<'b>, + rule: &'a mut FontFaceRule, } - /// Default methods reject all at rules. impl<'a, 'b> AtRuleParser for FontFaceRuleParser<'a, 'b> { type Prelude = (); - type AtRule = FontFaceDescriptorDeclaration; + type AtRule = (); } impl<'a, 'b> DeclarationParser for FontFaceRuleParser<'a, 'b> { - type Declaration = FontFaceDescriptorDeclaration; + type Declaration = (); - fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result { + fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> { match_ignore_ascii_case! { name, "font-family" => { - Ok(FontFaceDescriptorDeclaration::Family(try!( - parse_one_family(input)))) + self.rule.family = parse_one_family(input)?; }, "src" => { - Ok(FontFaceDescriptorDeclaration::Src(try!(input.parse_comma_separated(|input| { + self.rule.sources = input.parse_comma_separated(|input| { parse_one_src(self.context, input) - })))) + })?; }, - _ => Err(()) + _ => return Err(()) } + Ok(()) } } From 2b83d844da5c07bc5068309bc5dcacbfc96c50d0 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 27 Jan 2017 17:54:15 +0100 Subject: [PATCH 02/12] Use the Parse trait for `@font-face` parsing. --- components/style/font_face.rs | 63 +++++++------ .../style/properties/longhand/font.mako.rs | 94 ++++++++++--------- .../style/properties/shorthand/font.mako.rs | 4 +- 3 files changed, 88 insertions(+), 73 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index a25c1948bd3..0386ec16abf 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -11,7 +11,6 @@ use computed_values::font_family::FontFamily; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; -use properties::longhands::font_family::parse_one_family; use std::fmt; use std::iter; use style_traits::ToCss; @@ -181,40 +180,44 @@ impl<'a, 'b> DeclarationParser for FontFaceRuleParser<'a, 'b> { fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> { match_ignore_ascii_case! { name, - "font-family" => { - self.rule.family = parse_one_family(input)?; - }, - "src" => { - self.rule.sources = input.parse_comma_separated(|input| { - parse_one_src(self.context, input) - })?; - }, + "font-family" => self.rule.family = Parse::parse(self.context, input)?, + "src" => self.rule.sources = Parse::parse(self.context, input)?, _ => return Err(()) } Ok(()) } } -fn parse_one_src(context: &ParserContext, input: &mut Parser) -> Result { - if input.try(|input| input.expect_function_matching("local")).is_ok() { - return Ok(Source::Local(try!(input.parse_nested_block(parse_one_family)))) +impl Parse for Vec { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + input.parse_comma_separated(|input| Source::parse(context, input)) + } +} + +impl Parse for Source { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + if input.try(|input| input.expect_function_matching("local")).is_ok() { + return input.parse_nested_block(|input| { + FontFamily::parse(context, input) + }).map(Source::Local) + } + + let url = SpecifiedUrl::parse(context, input)?; + + // Parsing optional format() + let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() { + input.parse_nested_block(|input| { + input.parse_comma_separated(|input| { + Ok(input.expect_string()?.into_owned()) + }) + })? + } else { + vec![] + }; + + Ok(Source::Url(UrlSource { + url: url, + format_hints: format_hints, + })) } - - let url = try!(SpecifiedUrl::parse(context, input)); - - // Parsing optional format() - let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() { - try!(input.parse_nested_block(|input| { - input.parse_comma_separated(|input| { - Ok((try!(input.expect_string())).into_owned()) - }) - })) - } else { - vec![] - }; - - Ok(Source::Url(UrlSource { - url: url, - format_hints: format_hints, - })) } diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index b25d40fefea..a110f389bbc 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -107,56 +107,66 @@ pub fn get_initial_value() -> computed_value::T { computed_value::T(vec![FontFamily::Generic(atom!("serif"))]) } + /// # /// = | [ + ] /// TODO: - pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { - input.parse_comma_separated(parse_one_family).map(SpecifiedValue) + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + Vec::::parse(context, input).map(SpecifiedValue) } - pub fn parse_one_family(input: &mut Parser) -> Result { - if let Ok(value) = input.try(|input| input.expect_string()) { - return Ok(FontFamily::FamilyName(Atom::from(&*value))) - } - let first_ident = try!(input.expect_ident()); - // FIXME(bholley): The fast thing to do here would be to look up the - // string (as lowercase) in the static atoms table. We don't have an - // API to do that yet though, so we do the simple thing for now. - let mut css_wide_keyword = false; - match_ignore_ascii_case! { first_ident, - "serif" => return Ok(FontFamily::Generic(atom!("serif"))), - "sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))), - "cursive" => return Ok(FontFamily::Generic(atom!("cursive"))), - "fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))), - "monospace" => return Ok(FontFamily::Generic(atom!("monospace"))), + impl Parse for Vec { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + input.parse_comma_separated(|input| FontFamily::parse(context, input)) + } + } - // https://drafts.csswg.org/css-fonts/#propdef-font-family - // "Font family names that happen to be the same as a keyword value - // (‘inherit’, ‘serif’, ‘sans-serif’, ‘monospace’, ‘fantasy’, and ‘cursive’) - // must be quoted to prevent confusion with the keywords with the same names. - // The keywords ‘initial’ and ‘default’ are reserved for future use - // and must also be quoted when used as font names. - // UAs must not consider these keywords as matching the type." - "inherit" => css_wide_keyword = true, - "initial" => css_wide_keyword = true, - "unset" => css_wide_keyword = true, - "default" => css_wide_keyword = true, - _ => {} - } + impl Parse for FontFamily { + fn parse(_context: &ParserContext, input: &mut Parser) -> Result { + if let Ok(value) = input.try(|input| input.expect_string()) { + return Ok(FontFamily::FamilyName(Atom::from(&*value))) + } + let first_ident = try!(input.expect_ident()); - let mut value = first_ident.into_owned(); - // These keywords are not allowed by themselves. - // The only way this value can be valid with with another keyword. - if css_wide_keyword { - let ident = input.expect_ident()?; - value.push_str(" "); - value.push_str(&ident); + // FIXME(bholley): The fast thing to do here would be to look up the + // string (as lowercase) in the static atoms table. We don't have an + // API to do that yet though, so we do the simple thing for now. + let mut css_wide_keyword = false; + match_ignore_ascii_case! { first_ident, + "serif" => return Ok(FontFamily::Generic(atom!("serif"))), + "sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))), + "cursive" => return Ok(FontFamily::Generic(atom!("cursive"))), + "fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))), + "monospace" => return Ok(FontFamily::Generic(atom!("monospace"))), + + // https://drafts.csswg.org/css-fonts/#propdef-font-family + // "Font family names that happen to be the same as a keyword value + // (‘inherit’, ‘serif’, ‘sans-serif’, ‘monospace’, ‘fantasy’, and ‘cursive’) + // must be quoted to prevent confusion with the keywords with the same names. + // The keywords ‘initial’ and ‘default’ are reserved for future use + // and must also be quoted when used as font names. + // UAs must not consider these keywords as matching the type." + "inherit" => css_wide_keyword = true, + "initial" => css_wide_keyword = true, + "unset" => css_wide_keyword = true, + "default" => css_wide_keyword = true, + _ => {} + } + + let mut value = first_ident.into_owned(); + // These keywords are not allowed by themselves. + // The only way this value can be valid with with another keyword. + if css_wide_keyword { + let ident = input.expect_ident()?; + value.push_str(" "); + value.push_str(&ident); + } + while let Ok(ident) = input.try(|input| input.expect_ident()) { + value.push_str(" "); + value.push_str(&ident); + } + Ok(FontFamily::FamilyName(Atom::from(value))) } - while let Ok(ident) = input.try(|input| input.expect_ident()) { - value.push_str(" "); - value.push_str(&ident); - } - Ok(FontFamily::FamilyName(Atom::from(value))) } diff --git a/components/style/properties/shorthand/font.mako.rs b/components/style/properties/shorthand/font.mako.rs index f058c56a777..7d151e84956 100644 --- a/components/style/properties/shorthand/font.mako.rs +++ b/components/style/properties/shorthand/font.mako.rs @@ -7,8 +7,10 @@ <%helpers:shorthand name="font" sub_properties="font-style font-variant font-weight font-stretch font-size line-height font-family" spec="https://drafts.csswg.org/css-fonts-3/#propdef-font"> + use parser::Parse; use properties::longhands::{font_style, font_variant, font_weight, font_stretch}; use properties::longhands::{font_size, line_height, font_family}; + use properties::longhands::font_family::computed_value::FontFamily; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result { let mut nb_normals = 0; @@ -64,7 +66,7 @@ } else { None }; - let family = try!(input.parse_comma_separated(font_family::parse_one_family)); + let family = Vec::::parse(context, input)?; Ok(Longhands { font_style: style, font_variant: variant, From 4bcae573b3a93332802b95bebcf470caa98aa209 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 31 Jan 2017 18:07:06 +0100 Subject: [PATCH 03/12] Macroize @font-face descriptor definitions --- components/style/font_face.rs | 129 +++++++++++++++++------------- components/style_traits/lib.rs | 2 +- components/style_traits/values.rs | 15 ++++ 3 files changed, 89 insertions(+), 57 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 0386ec16abf..649593c4cc9 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -13,7 +13,7 @@ use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; use std::fmt; use std::iter; -use style_traits::ToCss; +use style_traits::{ToCss, CommaSeparated}; use values::specified::url::SpecifiedUrl; /// A source for a font-face rule. @@ -44,6 +44,8 @@ impl ToCss for Source { } } +impl CommaSeparated for Source {} + /// A `UrlSource` represents a font-face source that has been specified with a /// `url()` function. /// @@ -65,52 +67,12 @@ impl ToCss for UrlSource { } } -/// A `@font-face` rule. -/// -/// https://drafts.csswg.org/css-fonts/#font-face-rule -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct FontFaceRule { - /// The font family specified with the `font-family` property declaration. - pub family: FontFamily, - /// The list of sources specified with the different `src` property - /// declarations. - pub sources: Vec, -} - -impl ToCss for FontFaceRule { - // Serialization of FontFaceRule is not specced. - fn to_css(&self, dest: &mut W) -> fmt::Result - where W: fmt::Write, - { - try!(dest.write_str("@font-face { font-family: ")); - try!(self.family.to_css(dest)); - try!(dest.write_str(";")); - - if self.sources.len() > 0 { - try!(dest.write_str(" src: ")); - let mut iter = self.sources.iter(); - try!(iter.next().unwrap().to_css(dest)); - for source in iter { - try!(dest.write_str(", ")); - try!(source.to_css(dest)); - } - try!(dest.write_str(";")); - } - - dest.write_str(" }") - } -} - /// Parse the block inside a `@font-face` rule. /// /// Note that the prelude parsing code lives in the `stylesheets` module. pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser) -> Result { - let mut rule = FontFaceRule { - family: FontFamily::Generic(atom!("")), - sources: Vec::new(), - }; + let mut rule = FontFaceRule::initial(); { let parser = FontFaceRuleParser { context: context, rule: &mut rule }; let mut iter = DeclarationListParser::new(input, parser); @@ -174,20 +136,6 @@ impl<'a, 'b> AtRuleParser for FontFaceRuleParser<'a, 'b> { type AtRule = (); } - -impl<'a, 'b> DeclarationParser for FontFaceRuleParser<'a, 'b> { - type Declaration = (); - - fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> { - match_ignore_ascii_case! { name, - "font-family" => self.rule.family = Parse::parse(self.context, input)?, - "src" => self.rule.sources = Parse::parse(self.context, input)?, - _ => return Err(()) - } - Ok(()) - } -} - impl Parse for Vec { fn parse(context: &ParserContext, input: &mut Parser) -> Result { input.parse_comma_separated(|input| Source::parse(context, input)) @@ -221,3 +169,72 @@ impl Parse for Source { })) } } + +macro_rules! font_face_descriptors { + ( $( #[$doc: meta] $name: tt $ident: ident : $ty: ty = $initial: expr, )+ ) => { + /// A `@font-face` rule. + /// + /// https://drafts.csswg.org/css-fonts/#font-face-rule + #[derive(Debug, PartialEq, Eq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub struct FontFaceRule { + $( + #[$doc] + pub $ident: $ty, + )+ + } + + impl FontFaceRule { + fn initial() -> Self { + FontFaceRule { + $( + $ident: $initial, + )+ + } + } + } + + impl ToCss for FontFaceRule { + // Serialization of FontFaceRule is not specced. + fn to_css(&self, dest: &mut W) -> fmt::Result + where W: fmt::Write, + { + dest.write_str("@font-face {\n")?; + $( + // Because of parse_font_face_block, + // this condition is always true for "src" and "font-family". + // But it can be false for other descriptors. + if self.$ident != $initial { + dest.write_str(concat!(" ", $name, ": "))?; + self.$ident.to_css(dest)?; + dest.write_str(";\n")?; + } + )+ + dest.write_str("}") + } + } + + impl<'a, 'b> DeclarationParser for FontFaceRuleParser<'a, 'b> { + type Declaration = (); + + fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> { + match_ignore_ascii_case! { name, + $( + $name => self.rule.$ident = Parse::parse(self.context, input)?, + )+ + _ => return Err(()) + } + Ok(()) + } + } + } +} + +/// css-name rust_identifier: Type = initial_value, +font_face_descriptors! { + /// The specified url. + "font-family" family: FontFamily = FontFamily::Generic(atom!("")), + + /// The format hints specified with the `format()` function. + "src" sources: Vec = Vec::new(), +} diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs index 857624a5f9c..bc66db33d9d 100644 --- a/components/style_traits/lib.rs +++ b/components/style_traits/lib.rs @@ -59,4 +59,4 @@ pub mod cursor; pub mod values; pub mod viewport; -pub use values::ToCss; +pub use values::{ToCss, CommaSeparated}; diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs index 59be4900523..5c23d74fbfa 100644 --- a/components/style_traits/values.rs +++ b/components/style_traits/values.rs @@ -24,6 +24,21 @@ pub trait ToCss { } } +/// Marker trait to automatically implement ToCss for Vec. +pub trait CommaSeparated {} + +impl ToCss for Vec where T: ToCss + CommaSeparated { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut iter = self.iter(); + iter.next().unwrap().to_css(dest)?; + for item in iter { + dest.write_str(", ")?; + item.to_css(dest)?; + } + Ok(()) + } +} + impl ToCss for Au { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { write!(dest, "{}px", self.to_f64_px()) From 44f6c60d91eb0ec97a1a82e1d8f5fdc78309e7fd Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 19:00:33 +0100 Subject: [PATCH 04/12] Separate mandatory and optional descriptors in @font-face parsing macro. --- components/style/font_face.rs | 99 +++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 649593c4cc9..6d9932b5bf6 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -74,7 +74,11 @@ pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser) -> Result { let mut rule = FontFaceRule::initial(); { - let parser = FontFaceRuleParser { context: context, rule: &mut rule }; + let parser = FontFaceRuleParser { + context: context, + rule: &mut rule, + missing: MissingDescriptors::new(), + }; let mut iter = DeclarationListParser::new(input, parser); while let Some(declaration) = iter.next() { if let Err(range) = declaration { @@ -84,12 +88,11 @@ pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser) log_css_error(iter.input, pos, &*message, context); } } + if iter.parser.missing.any() { + return Err(()) + } } - if rule.family != FontFamily::Generic(atom!("")) && !rule.sources.is_empty() { - Ok(rule) - } else { - Err(()) - } + Ok(rule) } /// A list of effective sources that we send over through IPC to the font cache. @@ -128,6 +131,7 @@ impl iter::Iterator for EffectiveSources { struct FontFaceRuleParser<'a, 'b: 'a> { context: &'a ParserContext<'b>, rule: &'a mut FontFaceRule, + missing: MissingDescriptors, } /// Default methods reject all at rules. @@ -171,7 +175,14 @@ impl Parse for Source { } macro_rules! font_face_descriptors { - ( $( #[$doc: meta] $name: tt $ident: ident : $ty: ty = $initial: expr, )+ ) => { + ( + mandatory descriptors = [ + $( #[$m_doc: meta] $m_name: tt $m_ident: ident : $m_ty: ty = $m_initial: expr, )* + ] + optional descriptors = [ + $( #[$o_doc: meta] $o_name: tt $o_ident: ident : $o_ty: ty = $o_initial: expr, )* + ] + ) => { /// A `@font-face` rule. /// /// https://drafts.csswg.org/css-fonts/#font-face-rule @@ -179,17 +190,46 @@ macro_rules! font_face_descriptors { #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct FontFaceRule { $( - #[$doc] - pub $ident: $ty, - )+ + #[$m_doc] + pub $m_ident: $m_ty, + )* + $( + #[$o_doc] + pub $o_ident: $o_ty, + )* + } + + struct MissingDescriptors { + $( + $m_ident: bool, + )* + } + + impl MissingDescriptors { + fn new() -> Self { + MissingDescriptors { + $( + $m_ident: true, + )* + } + } + + fn any(&self) -> bool { + $( + self.$m_ident + )||* + } } impl FontFaceRule { fn initial() -> Self { FontFaceRule { $( - $ident: $initial, - )+ + $m_ident: $m_initial, + )* + $( + $o_ident: $o_initial, + )* } } } @@ -200,16 +240,21 @@ macro_rules! font_face_descriptors { where W: fmt::Write, { dest.write_str("@font-face {\n")?; + $( + dest.write_str(concat!(" ", $m_name, ": "))?; + self.$m_ident.to_css(dest)?; + dest.write_str(";\n")?; + )* $( // Because of parse_font_face_block, // this condition is always true for "src" and "font-family". // But it can be false for other descriptors. - if self.$ident != $initial { - dest.write_str(concat!(" ", $name, ": "))?; - self.$ident.to_css(dest)?; + if self.$o_ident != $o_initial { + dest.write_str(concat!(" ", $o_name, ": "))?; + self.$o_ident.to_css(dest)?; dest.write_str(";\n")?; } - )+ + )* dest.write_str("}") } } @@ -220,8 +265,14 @@ macro_rules! font_face_descriptors { fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> { match_ignore_ascii_case! { name, $( - $name => self.rule.$ident = Parse::parse(self.context, input)?, - )+ + $m_name => { + self.rule.$m_ident = Parse::parse(self.context, input)?; + self.missing.$m_ident = false + }, + )* + $( + $o_name => self.rule.$o_ident = Parse::parse(self.context, input)?, + )* _ => return Err(()) } Ok(()) @@ -232,9 +283,13 @@ macro_rules! font_face_descriptors { /// css-name rust_identifier: Type = initial_value, font_face_descriptors! { - /// The specified url. - "font-family" family: FontFamily = FontFamily::Generic(atom!("")), + mandatory descriptors = [ + /// The specified url. + "font-family" family: FontFamily = FontFamily::Generic(atom!("")), - /// The format hints specified with the `format()` function. - "src" sources: Vec = Vec::new(), + /// The format hints specified with the `format()` function. + "src" sources: Vec = Vec::new(), + ] + optional descriptors = [ + ] } From 52aa2431a9323199765a3a7c77187f749b126ccf Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 18:30:36 +0100 Subject: [PATCH 05/12] Introduce a FamilyName type, like FontFamily but not a generic family. --- components/style/properties/gecko.mako.rs | 2 +- .../style/properties/longhand/font.mako.rs | 42 ++++++++++++++----- tests/unit/gfx/font_cache_thread.rs | 6 +-- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 8d9db1a9583..0c80a294d78 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -944,7 +944,7 @@ fn static_assert() { for family in &v.0 { match *family { FontFamily::FamilyName(ref name) => { - unsafe { Gecko_FontFamilyList_AppendNamed(list, name.as_ptr()); } + unsafe { Gecko_FontFamilyList_AppendNamed(list, name.0.as_ptr()); } } FontFamily::Generic(ref name) => { let family_type = diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index a110f389bbc..838851b8a4e 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -10,7 +10,7 @@ additional_methods=[Method("compute_font_hash", is_mut=True)]) %> <%helpers:longhand name="font-family" animatable="False" need_index="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-family"> - use self::computed_value::FontFamily; + use self::computed_value::{FontFamily, FamilyName}; use values::NoViewportPercentage; use values::computed::ComputedValueAsSpecified; pub use self::computed_value::T as SpecifiedValue; @@ -28,15 +28,19 @@ #[derive(Debug, PartialEq, Eq, Clone, Hash)] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] pub enum FontFamily { - FamilyName(Atom), + FamilyName(FamilyName), Generic(Atom), } + #[derive(Debug, PartialEq, Eq, Clone, Hash)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] + pub struct FamilyName(pub Atom); + impl FontFamily { #[inline] pub fn atom(&self) -> &Atom { match *self { - FontFamily::FamilyName(ref name) => name, + FontFamily::FamilyName(ref name) => &name.0, FontFamily::Generic(ref name) => name, } } @@ -67,18 +71,22 @@ "monospace" => return FontFamily::Generic(atom!("monospace")), _ => {} } - FontFamily::FamilyName(input) + FontFamily::FamilyName(FamilyName(input)) + } + } + + impl ToCss for FamilyName { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_char('"')?; + write!(CssStringWriter::new(dest), "{}", self.0)?; + dest.write_char('"') } } impl ToCss for FontFamily { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self { - FontFamily::FamilyName(ref name) => { - dest.write_char('"')?; - write!(CssStringWriter::new(dest), "{}", name)?; - dest.write_char('"') - } + FontFamily::FamilyName(ref name) => name.to_css(dest), // All generic values accepted by the parser are known to not require escaping. FontFamily::Generic(ref name) => write!(dest, "{}", name), @@ -121,10 +129,22 @@ } } + /// `FamilyName::parse` is based on `FontFamily::parse` and not the other way around + /// because we want the former to exclude generic family keywords. + impl Parse for FamilyName { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + match FontFamily::parse(context, input) { + Ok(FontFamily::FamilyName(name)) => Ok(name), + Ok(FontFamily::Generic(_)) | + Err(()) => Err(()) + } + } + } + impl Parse for FontFamily { fn parse(_context: &ParserContext, input: &mut Parser) -> Result { if let Ok(value) = input.try(|input| input.expect_string()) { - return Ok(FontFamily::FamilyName(Atom::from(&*value))) + return Ok(FontFamily::FamilyName(FamilyName(Atom::from(&*value)))) } let first_ident = try!(input.expect_ident()); @@ -165,7 +185,7 @@ value.push_str(" "); value.push_str(&ident); } - Ok(FontFamily::FamilyName(Atom::from(value))) + Ok(FontFamily::FamilyName(FamilyName(Atom::from(value)))) } } diff --git a/tests/unit/gfx/font_cache_thread.rs b/tests/unit/gfx/font_cache_thread.rs index 7020679893a..0b68f627137 100644 --- a/tests/unit/gfx/font_cache_thread.rs +++ b/tests/unit/gfx/font_cache_thread.rs @@ -4,7 +4,7 @@ use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc; -use style::computed_values::font_family::FontFamily; +use style::computed_values::font_family::{FontFamily, FamilyName}; use style::font_face::{FontFaceRule, Source}; #[test] @@ -12,8 +12,8 @@ fn test_local_web_font() { let (inp_chan, _) = ipc::channel().unwrap(); let (out_chan, out_receiver) = ipc::channel().unwrap(); let font_cache_thread = FontCacheThread::new(inp_chan, None); - let family_name = FontFamily::FamilyName(From::from("test family")); - let variant_name = FontFamily::FamilyName(From::from("test font face")); + let family_name = FontFamily::FamilyName(FamilyName(From::from("test family"))); + let variant_name = FontFamily::FamilyName(FamilyName(From::from("test font face"))); let font_face_rule = FontFaceRule { family: family_name.clone(), sources: vec![Source::Local(variant_name)], From 5405fb0d733f1c9e8120b70b5faa7c54fe32d971 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 19:01:40 +0100 Subject: [PATCH 06/12] Use FamilyName instead of FontFamily in @font-face MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … to exclude generic families, per spec: https://drafts.csswg.org/css-fonts/#font-family-desc --- components/gfx/font_cache_thread.rs | 8 ++++---- components/style/font_face.rs | 12 ++++++------ tests/unit/gfx/font_cache_thread.rs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index a365ef47c5e..ee6465913b4 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -25,7 +25,7 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::u32; use style::font_face::{EffectiveSources, Source}; -use style::properties::longhands::font_family::computed_value::FontFamily; +use style::properties::longhands::font_family::computed_value::{FontFamily, FamilyName}; use webrender_traits; /// A list of font templates that make up a given font family. @@ -269,7 +269,7 @@ impl FontCache { }); } Source::Local(ref font) => { - let font_face_name = LowercaseString::new(font.name()); + let font_face_name = LowercaseString::new(&font.0); let templates = &mut self.web_families.get_mut(&family_name).unwrap(); let mut found = false; for_each_variation(&font_face_name, |path| { @@ -461,8 +461,8 @@ impl FontCacheThread { } } - pub fn add_web_font(&self, family: FontFamily, sources: EffectiveSources, sender: IpcSender<()>) { - self.chan.send(Command::AddWebFont(LowercaseString::new(family.name()), sources, sender)).unwrap(); + pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) { + self.chan.send(Command::AddWebFont(LowercaseString::new(&family.0), sources, sender)).unwrap(); } pub fn exit(&self) { diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 6d9932b5bf6..a574fd1e5b2 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -8,7 +8,7 @@ #![deny(missing_docs)] -use computed_values::font_family::FontFamily; +use computed_values::font_family::FamilyName; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; use std::fmt; @@ -23,7 +23,7 @@ pub enum Source { /// A `url()` source. Url(UrlSource), /// A `local()` source. - Local(FontFamily), + Local(FamilyName), } impl ToCss for Source { @@ -150,7 +150,7 @@ impl Parse for Source { fn parse(context: &ParserContext, input: &mut Parser) -> Result { if input.try(|input| input.expect_function_matching("local")).is_ok() { return input.parse_nested_block(|input| { - FontFamily::parse(context, input) + FamilyName::parse(context, input) }).map(Source::Local) } @@ -177,10 +177,10 @@ impl Parse for Source { macro_rules! font_face_descriptors { ( mandatory descriptors = [ - $( #[$m_doc: meta] $m_name: tt $m_ident: ident : $m_ty: ty = $m_initial: expr, )* + $( #[$m_doc: meta] $m_name: tt $m_ident: ident: $m_ty: ty = $m_initial: expr, )* ] optional descriptors = [ - $( #[$o_doc: meta] $o_name: tt $o_ident: ident : $o_ty: ty = $o_initial: expr, )* + $( #[$o_doc: meta] $o_name: tt $o_ident: ident: $o_ty: ty = $o_initial: expr, )* ] ) => { /// A `@font-face` rule. @@ -285,7 +285,7 @@ macro_rules! font_face_descriptors { font_face_descriptors! { mandatory descriptors = [ /// The specified url. - "font-family" family: FontFamily = FontFamily::Generic(atom!("")), + "font-family" family: FamilyName = FamilyName(atom!("")), /// The format hints specified with the `format()` function. "src" sources: Vec = Vec::new(), diff --git a/tests/unit/gfx/font_cache_thread.rs b/tests/unit/gfx/font_cache_thread.rs index 0b68f627137..3161199c7dd 100644 --- a/tests/unit/gfx/font_cache_thread.rs +++ b/tests/unit/gfx/font_cache_thread.rs @@ -12,8 +12,8 @@ fn test_local_web_font() { let (inp_chan, _) = ipc::channel().unwrap(); let (out_chan, out_receiver) = ipc::channel().unwrap(); let font_cache_thread = FontCacheThread::new(inp_chan, None); - let family_name = FontFamily::FamilyName(FamilyName(From::from("test family"))); - let variant_name = FontFamily::FamilyName(FamilyName(From::from("test font face"))); + let family_name = FamilyName(From::from("test family")); + let variant_name = FamilyName(From::from("test font face")); let font_face_rule = FontFaceRule { family: family_name.clone(), sources: vec![Source::Local(variant_name)], From 8767b0d7cc4f09a0206fc727e19cfd6a770daedc Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 19:45:12 +0100 Subject: [PATCH 07/12] Add font-style descriptor in @font-face --- components/style/font_face.rs | 17 +++++++++++++++++ components/style/properties/helpers.mako.rs | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index a574fd1e5b2..868ca8524a2 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -9,6 +9,7 @@ #![deny(missing_docs)] use computed_values::font_family::FamilyName; +#[cfg(feature = "gecko")] use computed_values::font_style; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; use std::fmt; @@ -282,6 +283,22 @@ macro_rules! font_face_descriptors { } /// css-name rust_identifier: Type = initial_value, +#[cfg(feature = "gecko")] +font_face_descriptors! { + mandatory descriptors = [ + /// The specified url. + "font-family" family: FamilyName = FamilyName(atom!("")), + + /// The format hints specified with the `format()` function. + "src" sources: Vec = Vec::new(), + ] + optional descriptors = [ + /// The style of this font face + "font-style" style: font_style::T = font_style::T::normal, + ] +} + +#[cfg(feature = "servo")] font_face_descriptors! { mandatory descriptors = [ /// The specified url. diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index b2c52e1e868..7156e9f9585 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -380,6 +380,13 @@ -> Result { SpecifiedValue::parse(input) } + impl Parse for SpecifiedValue { + #[inline] + fn parse(_context: &ParserContext, input: &mut Parser) + -> Result { + SpecifiedValue::parse(input) + } + } % if vector: <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> From fe8f5fad161ff863b91b3103d5fdd99c30216f8e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 19:52:57 +0100 Subject: [PATCH 08/12] Add font-weight descriptor to @font-face --- components/style/font_face.rs | 5 ++++- components/style/properties/longhand/font.mako.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 868ca8524a2..b4f4b6185cb 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -8,8 +8,8 @@ #![deny(missing_docs)] +#[cfg(feature = "gecko")] use computed_values::{font_style, font_weight}; use computed_values::font_family::FamilyName; -#[cfg(feature = "gecko")] use computed_values::font_style; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; use std::fmt; @@ -295,6 +295,9 @@ font_face_descriptors! { optional descriptors = [ /// The style of this font face "font-style" style: font_style::T = font_style::T::normal, + + /// The weight of this font face + "font-weight" weight: font_weight::T = font_weight::T::Weight400 /* normal */, ] } diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 838851b8a4e..b8d682ffcdf 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -273,6 +273,20 @@ ${helpers.single_keyword("font-variant-caps", } }) } + + /// Used in @font-face, where relative keywords are not allowed. + impl Parse for computed_value::T { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + match parse(context, input)? { + % for weight in range(100, 901, 100): + SpecifiedValue::Weight${weight} => Ok(computed_value::T::Weight${weight}), + % endfor + SpecifiedValue::Bolder | + SpecifiedValue::Lighter => Err(()) + } + } + } + pub mod computed_value { use std::fmt; #[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)] From f6bf397bb279d52f6a4516aae9fc76ec4302a20f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 19:54:21 +0100 Subject: [PATCH 09/12] Add font-stretch descriptor to @font-face --- components/style/font_face.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index b4f4b6185cb..acf604903b3 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -8,7 +8,7 @@ #![deny(missing_docs)] -#[cfg(feature = "gecko")] use computed_values::{font_style, font_weight}; +#[cfg(feature = "gecko")] use computed_values::{font_style, font_weight, font_stretch}; use computed_values::font_family::FamilyName; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; @@ -298,6 +298,9 @@ font_face_descriptors! { /// The weight of this font face "font-weight" weight: font_weight::T = font_weight::T::Weight400 /* normal */, + + /// The stretch of this font face + "font-stretch" stretch: font_stretch::T = font_stretch::T::normal, ] } From 0f351cb85153b19ee305a2a1dc1c5c6143075a17 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 20:00:40 +0100 Subject: [PATCH 10/12] Fix incorrectly copy-pasted doc-comments. --- components/style/font_face.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index acf604903b3..5130f03e45a 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -286,10 +286,10 @@ macro_rules! font_face_descriptors { #[cfg(feature = "gecko")] font_face_descriptors! { mandatory descriptors = [ - /// The specified url. + /// The name of this font face "font-family" family: FamilyName = FamilyName(atom!("")), - /// The format hints specified with the `format()` function. + /// The alternative sources for this font face. "src" sources: Vec = Vec::new(), ] optional descriptors = [ @@ -307,10 +307,10 @@ font_face_descriptors! { #[cfg(feature = "servo")] font_face_descriptors! { mandatory descriptors = [ - /// The specified url. + /// The name of this font face "font-family" family: FamilyName = FamilyName(atom!("")), - /// The format hints specified with the `format()` function. + /// The alternative sources for this font face. "src" sources: Vec = Vec::new(), ] optional descriptors = [ From 4701850f379e3de4ec51ba357ce36238ab74f899 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 2 Feb 2017 20:28:21 +0100 Subject: [PATCH 11/12] Add unicode-range descriptor to @font-face --- components/style/font_face.rs | 62 +++++++++++++++++++++++++++++++---- components/style/parser.rs | 7 ++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 5130f03e45a..f320bb0e201 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -8,7 +8,8 @@ #![deny(missing_docs)] -#[cfg(feature = "gecko")] use computed_values::{font_style, font_weight, font_stretch}; +#[cfg(feature = "gecko")] +use computed_values::{font_style, font_weight, font_stretch}; use computed_values::font_family::FamilyName; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; @@ -141,12 +142,6 @@ impl<'a, 'b> AtRuleParser for FontFaceRuleParser<'a, 'b> { type AtRule = (); } -impl Parse for Vec { - fn parse(context: &ParserContext, input: &mut Parser) -> Result { - input.parse_comma_separated(|input| Source::parse(context, input)) - } -} - impl Parse for Source { fn parse(context: &ParserContext, input: &mut Parser) -> Result { if input.try(|input| input.expect_function_matching("local")).is_ok() { @@ -301,6 +296,11 @@ font_face_descriptors! { /// The stretch of this font face "font-stretch" stretch: font_stretch::T = font_stretch::T::normal, + + /// The ranges of code points outside of which this font face should not be used. + "unicode-range" unicode_range: Vec = vec![ + unicode_range::Range { start: 0, end: unicode_range::MAX } + ], ] } @@ -316,3 +316,51 @@ font_face_descriptors! { optional descriptors = [ ] } + +/// https://drafts.csswg.org/css-fonts/#unicode-range-desc +#[cfg(feature = "gecko")] +pub mod unicode_range { + use cssparser::{Parser, Token}; + use parser::{ParserContext, Parse}; + use std::fmt; + use style_traits::{ToCss, CommaSeparated}; + + /// Maximum value of the end of a range + pub const MAX: u32 = ::std::char::MAX as u32; + + /// A single range: https://drafts.csswg.org/css-fonts/#urange-value + #[derive(Debug, PartialEq, Eq)] + pub struct Range { + /// Start of the range, inclusive + pub start: u32, + + /// End of the range, inclusive + pub end: u32, + } + + impl CommaSeparated for Range {} + + impl Parse for Range { + fn parse(_context: &ParserContext, input: &mut Parser) -> Result { + // FIXME: The unicode-range token has been removed from the CSS Syntax spec, + // cssparser should be updated accordingly + // and implement https://drafts.csswg.org/css-syntax/#urange instead + match input.next() { + Ok(Token::UnicodeRange(start, end)) => { + if end <= MAX && start <= end { + Ok(Range { start: start, end: end }) + } else { + Err(()) + } + } + _ => Err(()) + } + } + } + + impl ToCss for Range { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + Token::UnicodeRange(self.start, self.end).to_css(dest) + } + } +} diff --git a/components/style/parser.rs b/components/style/parser.rs index 60efcb8a4be..e325547b368 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -11,6 +11,7 @@ use error_reporting::ParseErrorReporter; #[cfg(feature = "gecko")] use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI}; use servo_url::ServoUrl; +use style_traits::CommaSeparated; use stylesheets::{MemoryHoleReporter, Origin}; /// Extra data that the style backend may need to parse stylesheets. @@ -102,3 +103,9 @@ pub trait Parse : Sized { /// Returns an error on failure. fn parse(context: &ParserContext, input: &mut Parser) -> Result; } + +impl Parse for Vec where T: Parse + CommaSeparated { + fn parse(context: &ParserContext, input: &mut Parser) -> Result { + input.parse_comma_separated(|input| T::parse(context, input)) + } +} From 9ec8418d8c4b3307e040d771eca8ff96aa145144 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 3 Feb 2017 13:15:53 +0100 Subject: [PATCH 12/12] Rename CommaSeparated to OneOrMoreCommaSeparated, reflecting its behavior. --- components/style/font_face.rs | 8 ++++---- components/style/parser.rs | 4 ++-- components/style_traits/lib.rs | 2 +- components/style_traits/values.rs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index f320bb0e201..c5e9e330f82 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -15,7 +15,7 @@ use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use parser::{ParserContext, log_css_error, Parse}; use std::fmt; use std::iter; -use style_traits::{ToCss, CommaSeparated}; +use style_traits::{ToCss, OneOrMoreCommaSeparated}; use values::specified::url::SpecifiedUrl; /// A source for a font-face rule. @@ -46,7 +46,7 @@ impl ToCss for Source { } } -impl CommaSeparated for Source {} +impl OneOrMoreCommaSeparated for Source {} /// A `UrlSource` represents a font-face source that has been specified with a /// `url()` function. @@ -323,7 +323,7 @@ pub mod unicode_range { use cssparser::{Parser, Token}; use parser::{ParserContext, Parse}; use std::fmt; - use style_traits::{ToCss, CommaSeparated}; + use style_traits::{ToCss, OneOrMoreCommaSeparated}; /// Maximum value of the end of a range pub const MAX: u32 = ::std::char::MAX as u32; @@ -338,7 +338,7 @@ pub mod unicode_range { pub end: u32, } - impl CommaSeparated for Range {} + impl OneOrMoreCommaSeparated for Range {} impl Parse for Range { fn parse(_context: &ParserContext, input: &mut Parser) -> Result { diff --git a/components/style/parser.rs b/components/style/parser.rs index e325547b368..a9d15352984 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -11,7 +11,7 @@ use error_reporting::ParseErrorReporter; #[cfg(feature = "gecko")] use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI}; use servo_url::ServoUrl; -use style_traits::CommaSeparated; +use style_traits::OneOrMoreCommaSeparated; use stylesheets::{MemoryHoleReporter, Origin}; /// Extra data that the style backend may need to parse stylesheets. @@ -104,7 +104,7 @@ pub trait Parse : Sized { fn parse(context: &ParserContext, input: &mut Parser) -> Result; } -impl Parse for Vec where T: Parse + CommaSeparated { +impl Parse for Vec where T: Parse + OneOrMoreCommaSeparated { fn parse(context: &ParserContext, input: &mut Parser) -> Result { input.parse_comma_separated(|input| T::parse(context, input)) } diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs index bc66db33d9d..4eadd640402 100644 --- a/components/style_traits/lib.rs +++ b/components/style_traits/lib.rs @@ -59,4 +59,4 @@ pub mod cursor; pub mod values; pub mod viewport; -pub use values::{ToCss, CommaSeparated}; +pub use values::{ToCss, OneOrMoreCommaSeparated}; diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs index 5c23d74fbfa..d59520c626d 100644 --- a/components/style_traits/values.rs +++ b/components/style_traits/values.rs @@ -25,9 +25,9 @@ pub trait ToCss { } /// Marker trait to automatically implement ToCss for Vec. -pub trait CommaSeparated {} +pub trait OneOrMoreCommaSeparated {} -impl ToCss for Vec where T: ToCss + CommaSeparated { +impl ToCss for Vec where T: ToCss + OneOrMoreCommaSeparated { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { let mut iter = self.iter(); iter.next().unwrap().to_css(dest)?;