diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-10-06 11:13:41 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-06 11:13:41 -0500 |
commit | 3b853e57bfbd9df89aa3737080f28cdc42372c19 (patch) | |
tree | 998b35d59ea22e4e86c0a3d3c79297e6ef55c36c | |
parent | f719ac2af7cde4e0ab94facf954c29c0f2edf909 (diff) | |
parent | 540ba90babf7fabff69da323ff25bb4a7ad2b2b5 (diff) | |
download | servo-3b853e57bfbd9df89aa3737080f28cdc42372c19.tar.gz servo-3b853e57bfbd9df89aa3737080f28cdc42372c19.zip |
Auto merge of #13616 - servo:PropertyDeclarationBlock_independence_day, r=Ms2ger
Move PropertyDeclarationBlock into its own module.
<!-- Please describe your changes on the following line: -->
`properties.mako.rs` (its previous location) is a fairly big file.
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).
<!-- Either: -->
- [ ] There are tests for these changes OR
- [x] These changes do not require new tests because refactor
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13616)
<!-- Reviewable:end -->
-rw-r--r-- | components/style/properties/build.py | 3 | ||||
-rw-r--r-- | components/style/properties/declaration_block.rs | 385 | ||||
-rw-r--r-- | components/style/properties/properties.mako.rs | 383 |
3 files changed, 394 insertions, 377 deletions
diff --git a/components/style/properties/build.py b/components/style/properties/build.py index 89e560ac182..4e61f456541 100644 --- a/components/style/properties/build.py +++ b/components/style/properties/build.py @@ -29,7 +29,8 @@ def main(): abort(usage) properties = data.PropertiesData(product=product, testing=testing) - rust = render(os.path.join(BASE, "properties.mako.rs"), product=product, data=properties) + template = os.path.join(BASE, "properties.mako.rs") + rust = render(template, product=product, data=properties, __file__=template) if output == "style-crate": write(os.environ["OUT_DIR"], "properties.rs", rust) if product == "gecko": diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs new file mode 100644 index 00000000000..c13a75d2a88 --- /dev/null +++ b/components/style/properties/declaration_block.rs @@ -0,0 +1,385 @@ +/* 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/. */ + +use cssparser::{DeclarationListParser, parse_important, ToCss}; +use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter}; +use error_reporting::ParseErrorReporter; +use parser::{ParserContext, ParserContextExtraData, log_css_error}; +use std::boxed::Box as StdBox; +use std::fmt; +use stylesheets::Origin; +use super::*; +use url::Url; + + +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Importance { + /// Indicates a declaration without `!important`. + Normal, + + /// Indicates a declaration with `!important`. + Important, +} + +impl Importance { + pub fn important(self) -> bool { + match self { + Importance::Normal => false, + Importance::Important => true, + } + } +} + +/// Overridden declarations are skipped. +// FIXME (https://github.com/servo/servo/issues/3426) +#[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct PropertyDeclarationBlock { + #[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")] + pub declarations: Vec<(PropertyDeclaration, Importance)>, + + /// The number of entries in `self.declaration` with `Importance::Important` + pub important_count: u32, +} + +impl PropertyDeclarationBlock { + /// Returns wheather this block contains any declaration with `!important`. + /// + /// This is based on the `important_count` counter, + /// which should be maintained whenever `declarations` is changed. + // FIXME: make fields private and maintain it here in methods? + pub fn any_important(&self) -> bool { + self.important_count > 0 + } + + /// Returns wheather this block contains any declaration without `!important`. + /// + /// This is based on the `important_count` counter, + /// which should be maintained whenever `declarations` is changed. + // FIXME: make fields private and maintain it here in methods? + pub fn any_normal(&self) -> bool { + self.declarations.len() > self.important_count as usize + } + + pub fn get(&self, property_name: &str) -> Option< &(PropertyDeclaration, Importance)> { + self.declarations.iter().find(|&&(ref decl, _)| decl.matches(property_name)) + } +} + +impl PropertyDeclarationBlock { + // Take a declaration block known to contain a single property, + // and serialize it + pub fn to_css_single_value<W>(&self, dest: &mut W, name: &str) + -> fmt::Result where W: fmt::Write { + match self.declarations.len() { + 0 => Err(fmt::Error), + 1 if self.declarations[0].0.name().eq_str_ignore_ascii_case(name) => { + self.declarations[0].0.to_css(dest) + } + _ => { + // we use this function because a closure won't be `Clone` + fn get_declaration(dec: &(PropertyDeclaration, Importance)) + -> &PropertyDeclaration { + &dec.0 + } + let shorthand = try!(Shorthand::from_name(name).ok_or(fmt::Error)); + if !self.declarations.iter().all(|decl| decl.0.shorthands().contains(&shorthand)) { + return Err(fmt::Error) + } + let success = try!(shorthand.serialize_shorthand_to_buffer( + dest, + self.declarations.iter() + .map(get_declaration as fn(_) -> _), + &mut true) + ); + if success { + Ok(()) + } else { + Err(fmt::Error) + } + } + } + } +} + +impl ToCss for PropertyDeclarationBlock { + // https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut is_first_serialization = true; // trailing serializations should have a prepended space + + // Step 1 -> dest = result list + + // Step 2 + let mut already_serialized = Vec::new(); + + // Step 3 + for &(ref declaration, importance) in &*self.declarations { + // Step 3.1 + let property = declaration.name(); + + // Step 3.2 + if already_serialized.contains(&property) { + continue; + } + + // Step 3.3 + let shorthands = declaration.shorthands(); + if !shorthands.is_empty() { + // Step 3.3.1 + let mut longhands = self.declarations.iter() + .filter(|d| !already_serialized.contains(&d.0.name())) + .collect::<Vec<_>>(); + + // Step 3.3.2 + for shorthand in shorthands { + let properties = shorthand.longhands(); + + // Substep 2 & 3 + let mut current_longhands = Vec::new(); + let mut important_count = 0; + + for &&(ref longhand, longhand_importance) in longhands.iter() { + let longhand_name = longhand.name(); + if properties.iter().any(|p| &longhand_name == *p) { + current_longhands.push(longhand); + if longhand_importance.important() { + important_count += 1; + } + } + } + + // Substep 1 + /* Assuming that the PropertyDeclarationBlock contains no duplicate entries, + if the current_longhands length is equal to the properties length, it means + that the properties that map to shorthand are present in longhands */ + if current_longhands.is_empty() || current_longhands.len() != properties.len() { + continue; + } + + // Substep 4 + let is_important = important_count > 0; + if is_important && important_count != current_longhands.len() { + continue; + } + + // TODO: serialize shorthand does not take is_important into account currently + // Substep 5 + let was_serialized = + try!( + shorthand.serialize_shorthand_to_buffer( + dest, + current_longhands.iter().cloned(), + &mut is_first_serialization + ) + ); + // If serialization occured, Substep 7 & 8 will have been completed + + // Substep 6 + if !was_serialized { + continue; + } + + for current_longhand in current_longhands { + // Substep 9 + already_serialized.push(current_longhand.name()); + let index_to_remove = longhands.iter().position(|l| l.0 == *current_longhand); + if let Some(index) = index_to_remove { + // Substep 10 + longhands.remove(index); + } + } + } + } + + // Step 3.3.4 + if already_serialized.contains(&property) { + continue; + } + + use std::iter::Cloned; + use std::slice; + + // Steps 3.3.5, 3.3.6 & 3.3.7 + // Need to specify an iterator type here even though it’s unused to work around + // "error: unable to infer enough type information about `_`; + // type annotations or generic parameter binding required [E0282]" + // Use the same type as earlier call to reuse generated code. + try!(append_serialization::<W, Cloned<slice::Iter< &PropertyDeclaration>>>( + dest, + &property.to_string(), + AppendableValue::Declaration(declaration), + importance, + &mut is_first_serialization)); + + // Step 3.3.8 + already_serialized.push(property); + } + + // Step 4 + Ok(()) + } +} + +pub enum AppendableValue<'a, I> +where I: Iterator<Item=&'a PropertyDeclaration> { + Declaration(&'a PropertyDeclaration), + DeclarationsForShorthand(Shorthand, I), + Css(&'a str) +} + +fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result where W: fmt::Write { + // after first serialization(key: value;) add whitespace between the pairs + if !*is_first_serialization { + try!(write!(dest, " ")); + } else { + *is_first_serialization = false; + } + + Ok(()) +} + +pub fn append_declaration_value<'a, W, I> + (dest: &mut W, + appendable_value: AppendableValue<'a, I>, + importance: Importance) + -> fmt::Result + where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { + match appendable_value { + AppendableValue::Css(css) => { + try!(write!(dest, "{}", css)) + }, + AppendableValue::Declaration(decl) => { + try!(decl.to_css(dest)); + }, + AppendableValue::DeclarationsForShorthand(shorthand, decls) => { + try!(shorthand.longhands_to_css(decls, dest)); + } + } + + if importance.important() { + try!(write!(dest, " !important")); + } + + Ok(()) +} + +pub fn append_serialization<'a, W, I>(dest: &mut W, + property_name: &str, + appendable_value: AppendableValue<'a, I>, + importance: Importance, + is_first_serialization: &mut bool) + -> fmt::Result + where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { + try!(handle_first_serialization(dest, is_first_serialization)); + + // Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal + // values, they no longer use the shared property name "overflow" and must be handled differently + if shorthands::is_overflow_shorthand(&appendable_value) { + return append_declaration_value(dest, appendable_value, importance); + } + + try!(write!(dest, "{}:", property_name)); + + // for normal parsed values, add a space between key: and value + match &appendable_value { + &AppendableValue::Css(_) => { + try!(write!(dest, " ")) + }, + &AppendableValue::Declaration(decl) => { + if !decl.value_is_unparsed() { + // for normal parsed values, add a space between key: and value + try!(write!(dest, " ")); + } + }, + &AppendableValue::DeclarationsForShorthand(..) => try!(write!(dest, " ")) + } + + try!(append_declaration_value(dest, appendable_value, importance)); + write!(dest, ";") +} + +pub fn parse_style_attribute(input: &str, base_url: &Url, error_reporter: StdBox<ParseErrorReporter + Send>, + extra_data: ParserContextExtraData) + -> PropertyDeclarationBlock { + let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data); + parse_property_declaration_list(&context, &mut Parser::new(input)) +} + +pub fn parse_one_declaration(name: &str, input: &str, base_url: &Url, error_reporter: StdBox<ParseErrorReporter + Send>, + extra_data: ParserContextExtraData) + -> Result<Vec<PropertyDeclaration>, ()> { + let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data); + let mut results = vec![]; + match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results, false) { + PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results), + _ => Err(()) + } +} + +struct PropertyDeclarationParser<'a, 'b: 'a> { + context: &'a ParserContext<'b>, +} + + +/// Default methods reject all at rules. +impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> { + type Prelude = (); + type AtRule = (Vec<PropertyDeclaration>, Importance); +} + + +impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> { + type Declaration = (Vec<PropertyDeclaration>, Importance); + + fn parse_value(&mut self, name: &str, input: &mut Parser) + -> Result<(Vec<PropertyDeclaration>, Importance), ()> { + let mut results = vec![]; + try!(input.parse_until_before(Delimiter::Bang, |input| { + match PropertyDeclaration::parse(name, self.context, input, &mut results, false) { + PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(()), + _ => Err(()) + } + })); + let importance = match input.try(parse_important) { + Ok(()) => Importance::Important, + Err(()) => Importance::Normal, + }; + Ok((results, importance)) + } +} + + +pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser) + -> PropertyDeclarationBlock { + let mut declarations = Vec::new(); + let mut important_count = 0; + let parser = PropertyDeclarationParser { + context: context, + }; + let mut iter = DeclarationListParser::new(input, parser); + while let Some(declaration) = iter.next() { + match declaration { + Ok((results, importance)) => { + if importance.important() { + important_count += results.len() as u32; + } + declarations.extend(results.into_iter().map(|d| (d, importance))) + } + Err(range) => { + let pos = range.start; + let message = format!("Unsupported property declaration: '{}'", + iter.input.slice(range)); + log_css_error(iter.input, pos, &*message, &context); + } + } + } + let mut block = PropertyDeclarationBlock { + declarations: declarations, + important_count: important_count, + }; + super::deduplicate_property_declarations(&mut block); + block +} + diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 46db65c68c6..bcc7a6e3417 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -18,8 +18,7 @@ use std::sync::Arc; use app_units::Au; #[cfg(feature = "servo")] use cssparser::{Color as CSSParserColor, RGBA}; -use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, - DeclarationListParser, parse_important, ToCss, TokenSerializationType}; +use cssparser::{Parser, ToCss, TokenSerializationType}; use error_reporting::ParseErrorReporter; use url::Url; #[cfg(feature = "servo")] use euclid::side_offsets::SideOffsets2D; @@ -28,7 +27,7 @@ use string_cache::Atom; use computed_values; #[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide}; use logical_geometry::WritingMode; -use parser::{ParserContext, ParserContextExtraData, log_css_error}; +use parser::{ParserContext, ParserContextExtraData}; use selector_matching::{ApplicableDeclarationBlock, ApplicableDeclarationBlockReadGuard}; use stylesheets::Origin; use values::LocalToCss; @@ -38,11 +37,16 @@ use cascade_info::CascadeInfo; #[cfg(feature = "servo")] use values::specified::BorderStyle; use self::property_bit_field::PropertyBitField; +pub use self::declaration_block::*; <%! from data import Method, Keyword, to_rust_ident + import os.path %> +#[path="${os.path.join(os.path.dirname(__file__), 'declaration_block.rs')}"] +pub mod declaration_block; + pub mod longhands { use cssparser::Parser; use parser::ParserContext; @@ -262,379 +266,6 @@ mod property_bit_field { % endif % endfor -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum Importance { - /// Indicates a declaration without `!important`. - Normal, - - /// Indicates a declaration with `!important`. - Important, -} - -impl Importance { - pub fn important(self) -> bool { - match self { - Importance::Normal => false, - Importance::Important => true, - } - } -} - -/// Overridden declarations are skipped. -// FIXME (https://github.com/servo/servo/issues/3426) -#[derive(Debug, PartialEq, Clone)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct PropertyDeclarationBlock { - #[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")] - pub declarations: Vec<(PropertyDeclaration, Importance)>, - - /// The number of entries in `self.declaration` with `Importance::Important` - pub important_count: u32, -} - -impl PropertyDeclarationBlock { - /// Returns wheather this block contains any declaration with `!important`. - /// - /// This is based on the `important_count` counter, - /// which should be maintained whenever `declarations` is changed. - // FIXME: make fields private and maintain it here in methods? - pub fn any_important(&self) -> bool { - self.important_count > 0 - } - - /// Returns wheather this block contains any declaration without `!important`. - /// - /// This is based on the `important_count` counter, - /// which should be maintained whenever `declarations` is changed. - // FIXME: make fields private and maintain it here in methods? - pub fn any_normal(&self) -> bool { - self.declarations.len() > self.important_count as usize - } - - pub fn get(&self, property_name: &str) -> Option< &(PropertyDeclaration, Importance)> { - self.declarations.iter().find(|&&(ref decl, _)| decl.matches(property_name)) - } -} - -impl PropertyDeclarationBlock { - // Take a declaration block known to contain a single property, - // and serialize it - pub fn to_css_single_value<W>(&self, dest: &mut W, name: &str) - -> fmt::Result where W: fmt::Write { - match self.declarations.len() { - 0 => Err(fmt::Error), - 1 if self.declarations[0].0.name().eq_str_ignore_ascii_case(name) => { - self.declarations[0].0.to_css(dest) - } - _ => { - // we use this function because a closure won't be `Clone` - fn get_declaration(dec: &(PropertyDeclaration, Importance)) - -> &PropertyDeclaration { - &dec.0 - } - let shorthand = try!(Shorthand::from_name(name).ok_or(fmt::Error)); - if !self.declarations.iter().all(|decl| decl.0.shorthands().contains(&shorthand)) { - return Err(fmt::Error) - } - let success = try!(shorthand.serialize_shorthand_to_buffer( - dest, - self.declarations.iter() - .map(get_declaration as fn(_) -> _), - &mut true) - ); - if success { - Ok(()) - } else { - Err(fmt::Error) - } - } - } - } -} - -impl ToCss for PropertyDeclarationBlock { - // https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - let mut is_first_serialization = true; // trailing serializations should have a prepended space - - // Step 1 -> dest = result list - - // Step 2 - let mut already_serialized = Vec::new(); - - // Step 3 - for &(ref declaration, importance) in &*self.declarations { - // Step 3.1 - let property = declaration.name(); - - // Step 3.2 - if already_serialized.contains(&property) { - continue; - } - - // Step 3.3 - let shorthands = declaration.shorthands(); - if !shorthands.is_empty() { - - // Step 3.3.1 - let mut longhands = self.declarations.iter() - .filter(|d| !already_serialized.contains(&d.0.name())) - .collect::<Vec<_>>(); - - // Step 3.3.2 - for shorthand in shorthands { - let properties = shorthand.longhands(); - - // Substep 2 & 3 - let mut current_longhands = Vec::new(); - let mut important_count = 0; - - for &&(ref longhand, longhand_importance) in longhands.iter() { - let longhand_name = longhand.name(); - if properties.iter().any(|p| &longhand_name == *p) { - current_longhands.push(longhand); - if longhand_importance.important() { - important_count += 1; - } - } - } - - // Substep 1 - /* Assuming that the PropertyDeclarationBlock contains no duplicate entries, - if the current_longhands length is equal to the properties length, it means - that the properties that map to shorthand are present in longhands */ - if current_longhands.is_empty() || current_longhands.len() != properties.len() { - continue; - } - - // Substep 4 - let is_important = important_count > 0; - if is_important && important_count != current_longhands.len() { - continue; - } - - // TODO: serialize shorthand does not take is_important into account currently - // Substep 5 - let was_serialized = - try!( - shorthand.serialize_shorthand_to_buffer( - dest, - current_longhands.iter().cloned(), - &mut is_first_serialization - ) - ); - // If serialization occured, Substep 7 & 8 will have been completed - - // Substep 6 - if !was_serialized { - continue; - } - - for current_longhand in current_longhands { - // Substep 9 - already_serialized.push(current_longhand.name()); - let index_to_remove = longhands.iter().position(|l| l.0 == *current_longhand); - if let Some(index) = index_to_remove { - // Substep 10 - longhands.remove(index); - } - } - } - } - - // Step 3.3.4 - if already_serialized.contains(&property) { - continue; - } - - use std::iter::Cloned; - use std::slice; - - // Steps 3.3.5, 3.3.6 & 3.3.7 - // Need to specify an iterator type here even though it’s unused to work around - // "error: unable to infer enough type information about `_`; - // type annotations or generic parameter binding required [E0282]" - // Use the same type as earlier call to reuse generated code. - try!(append_serialization::<W, Cloned<slice::Iter< &PropertyDeclaration>>>( - dest, - &property.to_string(), - AppendableValue::Declaration(declaration), - importance, - &mut is_first_serialization)); - - // Step 3.3.8 - already_serialized.push(property); - } - - // Step 4 - Ok(()) - } -} - -pub enum AppendableValue<'a, I> -where I: Iterator<Item=&'a PropertyDeclaration> { - Declaration(&'a PropertyDeclaration), - DeclarationsForShorthand(Shorthand, I), - Css(&'a str) -} - -fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result where W: fmt::Write { - // after first serialization(key: value;) add whitespace between the pairs - if !*is_first_serialization { - try!(write!(dest, " ")); - } - else { - *is_first_serialization = false; - } - - Ok(()) -} - -fn append_declaration_value<'a, W, I> - (dest: &mut W, - appendable_value: AppendableValue<'a, I>, - importance: Importance) - -> fmt::Result - where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { - match appendable_value { - AppendableValue::Css(css) => { - try!(write!(dest, "{}", css)) - }, - AppendableValue::Declaration(decl) => { - try!(decl.to_css(dest)); - }, - AppendableValue::DeclarationsForShorthand(shorthand, decls) => { - try!(shorthand.longhands_to_css(decls, dest)); - } - } - - if importance.important() { - try!(write!(dest, " !important")); - } - - Ok(()) -} - -fn append_serialization<'a, W, I>(dest: &mut W, - property_name: &str, - appendable_value: AppendableValue<'a, I>, - importance: Importance, - is_first_serialization: &mut bool) - -> fmt::Result - where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { - - try!(handle_first_serialization(dest, is_first_serialization)); - - // Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal - // values, they no longer use the shared property name "overflow" and must be handled differently - if shorthands::is_overflow_shorthand(&appendable_value) { - return append_declaration_value(dest, appendable_value, importance); - } - - try!(write!(dest, "{}:", property_name)); - - // for normal parsed values, add a space between key: and value - match &appendable_value { - &AppendableValue::Css(_) => { - try!(write!(dest, " ")) - }, - &AppendableValue::Declaration(decl) => { - if !decl.value_is_unparsed() { - // for normal parsed values, add a space between key: and value - try!(write!(dest, " ")); - } - }, - &AppendableValue::DeclarationsForShorthand(..) => try!(write!(dest, " ")) - } - - try!(append_declaration_value(dest, appendable_value, importance)); - write!(dest, ";") -} - -pub fn parse_style_attribute(input: &str, base_url: &Url, error_reporter: StdBox<ParseErrorReporter + Send>, - extra_data: ParserContextExtraData) - -> PropertyDeclarationBlock { - let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data); - parse_property_declaration_list(&context, &mut Parser::new(input)) -} - -pub fn parse_one_declaration(name: &str, input: &str, base_url: &Url, error_reporter: StdBox<ParseErrorReporter + Send>, - extra_data: ParserContextExtraData) - -> Result<Vec<PropertyDeclaration>, ()> { - let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data); - let mut results = vec![]; - match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results, false) { - PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results), - _ => Err(()) - } -} - -struct PropertyDeclarationParser<'a, 'b: 'a> { - context: &'a ParserContext<'b>, -} - - -/// Default methods reject all at rules. -impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> { - type Prelude = (); - type AtRule = (Vec<PropertyDeclaration>, Importance); -} - - -impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> { - type Declaration = (Vec<PropertyDeclaration>, Importance); - - fn parse_value(&mut self, name: &str, input: &mut Parser) - -> Result<(Vec<PropertyDeclaration>, Importance), ()> { - let mut results = vec![]; - try!(input.parse_until_before(Delimiter::Bang, |input| { - match PropertyDeclaration::parse(name, self.context, input, &mut results, false) { - PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(()), - _ => Err(()) - } - })); - let importance = match input.try(parse_important) { - Ok(()) => Importance::Important, - Err(()) => Importance::Normal, - }; - Ok((results, importance)) - } -} - - -pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser) - -> PropertyDeclarationBlock { - let mut declarations = Vec::new(); - let mut important_count = 0; - let parser = PropertyDeclarationParser { - context: context, - }; - let mut iter = DeclarationListParser::new(input, parser); - while let Some(declaration) = iter.next() { - match declaration { - Ok((results, importance)) => { - if importance.important() { - important_count += results.len() as u32; - } - declarations.extend(results.into_iter().map(|d| (d, importance))) - } - Err(range) => { - let pos = range.start; - let message = format!("Unsupported property declaration: '{}'", - iter.input.slice(range)); - log_css_error(iter.input, pos, &*message, &context); - } - } - } - let mut block = PropertyDeclarationBlock { - declarations: declarations, - important_count: important_count, - }; - deduplicate_property_declarations(&mut block); - block -} - /// Only keep the "winning" declaration for any given property, by importance then source order. /// The input and output are in source order fn deduplicate_property_declarations(block: &mut PropertyDeclarationBlock) { |