servo/components/style/properties/declaration_block.rs

971 lines
37 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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/. */
//! A property declaration block.
#![deny(missing_docs)]
use context::QuirksMode;
use cssparser::{DeclarationListParser, parse_important, ParserInput};
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter};
use error_reporting::{ParseErrorReporter, ContextualParseError};
use parser::{PARSING_MODE_DEFAULT, ParsingMode, ParserContext, log_css_error};
use properties::animated_properties::AnimationValue;
use selectors::parser::SelectorParseError;
use shared_lock::Locked;
use std::fmt;
use std::slice::Iter;
use style_traits::{ToCss, ParseError, StyleParseError};
use stylesheets::{CssRuleType, Origin, UrlExtraData};
use stylesheets::{MallocSizeOf, MallocSizeOfFn};
use super::*;
use values::computed::Context;
#[cfg(feature = "gecko")] use properties::animated_properties::AnimationValueMap;
/// The animation rules.
///
/// The first one is for Animation cascade level, and the second one is for
/// Transition cascade level.
pub struct AnimationRules<'a>(pub Option<&'a Arc<Locked<PropertyDeclarationBlock>>>,
pub Option<&'a Arc<Locked<PropertyDeclarationBlock>>>);
impl<'a> AnimationRules<'a> {
/// Returns whether these animation rules represents an actual rule or not.
pub fn is_empty(&self) -> bool {
self.0.is_none() && self.1.is_none()
}
}
/// A declaration [importance][importance].
///
/// [importance]: https://drafts.csswg.org/css-cascade/#importance
#[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 MallocSizeOf for Importance {
fn malloc_size_of_children(&self, _malloc_size_of: MallocSizeOfFn) -> usize {
0
}
}
impl Importance {
/// Return whether this is an important declaration.
pub fn important(self) -> bool {
match self {
Importance::Normal => false,
Importance::Important => true,
}
}
}
/// Overridden declarations are skipped.
#[derive(Clone)]
pub struct PropertyDeclarationBlock {
/// The group of declarations, along with their importance.
///
/// Only deduplicated declarations appear here.
declarations: Vec<(PropertyDeclaration, Importance)>,
/// The number of entries in `self.declaration` with `Importance::Important`
important_count: usize,
longhands: LonghandIdSet,
}
impl MallocSizeOf for PropertyDeclarationBlock {
fn malloc_size_of_children(&self, malloc_size_of: MallocSizeOfFn) -> usize {
self.declarations.malloc_size_of_children(malloc_size_of)
}
}
/// Iterator for PropertyDeclaration to be generated from PropertyDeclarationBlock.
#[derive(Clone)]
pub struct PropertyDeclarationIterator<'a> {
iter: Iter<'a, (PropertyDeclaration, Importance)>,
}
impl<'a> Iterator for PropertyDeclarationIterator<'a> {
type Item = &'a PropertyDeclaration;
#[inline]
fn next(&mut self) -> Option<&'a PropertyDeclaration> {
// we use this function because a closure won't be `Clone`
fn get_declaration(dec: &(PropertyDeclaration, Importance))
-> &PropertyDeclaration {
&dec.0
}
self.iter.next().map(get_declaration as fn(_) -> _)
}
}
/// Iterator for AnimationValue to be generated from PropertyDeclarationBlock.
pub struct AnimationValueIterator<'a, 'cx, 'cx_a:'cx> {
iter: Iter<'a, (PropertyDeclaration, Importance)>,
context: &'cx mut Context<'cx_a>,
default_values: &'a Arc<ComputedValues>,
}
impl<'a, 'cx, 'cx_a:'cx> AnimationValueIterator<'a, 'cx, 'cx_a> {
fn new(declarations: &'a PropertyDeclarationBlock,
context: &'cx mut Context<'cx_a>,
default_values: &'a Arc<ComputedValues>) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
AnimationValueIterator {
iter: declarations.declarations().iter(),
context: context,
default_values: default_values,
}
}
}
impl<'a, 'cx, 'cx_a:'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {
type Item = (TransitionProperty, AnimationValue);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
use properties::Importance;
loop {
let next = self.iter.next();
match next {
Some(&(ref decl, importance)) => {
if importance == Importance::Normal {
let property = TransitionProperty::from_declaration(decl);
let animation = AnimationValue::from_declaration(decl, &mut self.context,
self.default_values);
debug_assert!(property.is_none() == animation.is_none(),
"The failure condition of TransitionProperty::from_declaration \
and AnimationValue::from_declaration should be the same");
// Skip the property if either ::from_declaration fails.
match (property, animation) {
(Some(p), Some(a)) => return Some((p, a)),
(_, _) => {},
}
}
},
None => return None,
}
}
}
}
impl fmt::Debug for PropertyDeclarationBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.declarations.fmt(f)
}
}
impl PropertyDeclarationBlock {
/// Returns the number of declarations in the block.
pub fn len(&self) -> usize {
self.declarations.len()
}
/// Create an empty block
pub fn new() -> Self {
PropertyDeclarationBlock {
declarations: Vec::new(),
important_count: 0,
longhands: LonghandIdSet::new(),
}
}
/// Create a block with a single declaration
pub fn with_one(declaration: PropertyDeclaration, importance: Importance) -> Self {
let mut longhands = LonghandIdSet::new();
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
longhands.insert(id);
}
PropertyDeclarationBlock {
declarations: vec![(declaration, importance)],
important_count: if importance.important() { 1 } else { 0 },
longhands: longhands,
}
}
/// The declarations in this block
pub fn declarations(&self) -> &[(PropertyDeclaration, Importance)] {
&self.declarations
}
/// Iterate over only PropertyDeclaration.
pub fn declarations_iter(&self) -> PropertyDeclarationIterator {
PropertyDeclarationIterator {
iter: self.declarations.iter(),
}
}
/// Return an iterator of (TransitionProperty, AnimationValue).
pub fn to_animation_value_iter<'a, 'cx, 'cx_a:'cx>(&'a self,
context: &'cx mut Context<'cx_a>,
default_values: &'a Arc<ComputedValues>)
-> AnimationValueIterator<'a, 'cx, 'cx_a> {
AnimationValueIterator::new(self, context, default_values)
}
/// Returns whether 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 whether 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
}
/// Get a declaration for a given property.
///
/// NOTE: This is linear time.
pub fn get(&self, property: PropertyDeclarationId) -> Option< &(PropertyDeclaration, Importance)> {
self.declarations.iter().find(|&&(ref decl, _)| decl.id() == property)
}
/// Find the value of the given property in this block and serialize it
///
/// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
pub fn property_value_to_css<W>(&self, property: &PropertyId, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
// Step 1.1: done when parsing a string to PropertyId
// Step 1.2
match property.as_shorthand() {
Ok(shorthand) => {
// Step 1.2.1
let mut list = Vec::new();
let mut important_count = 0;
// Step 1.2.2
for &longhand in shorthand.longhands() {
// Step 1.2.2.1
let declaration = self.get(PropertyDeclarationId::Longhand(longhand));
// Step 1.2.2.2 & 1.2.2.3
match declaration {
Some(&(ref declaration, importance)) => {
list.push(declaration);
if importance.important() {
important_count += 1;
}
},
None => return Ok(()),
}
}
// If there is one or more longhand with important, and one or more
// without important, we don't serialize it as a shorthand.
if important_count > 0 && important_count != list.len() {
return Ok(());
}
// Step 1.2.3
// We don't print !important when serializing individual properties,
// so we treat this as a normal-importance property
match shorthand.get_shorthand_appendable_value(list) {
Some(appendable_value) =>
append_declaration_value(dest, appendable_value),
None => return Ok(()),
}
}
Err(longhand_or_custom) => {
if let Some(&(ref value, _importance)) = self.get(longhand_or_custom) {
// Step 2
value.to_css(dest)
} else {
// Step 3
Ok(())
}
}
}
}
/// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
pub fn property_priority(&self, property: &PropertyId) -> Importance {
// Step 1: done when parsing a string to PropertyId
// Step 2
match property.as_shorthand() {
Ok(shorthand) => {
// Step 2.1 & 2.2 & 2.3
if shorthand.longhands().iter().all(|&l| {
self.get(PropertyDeclarationId::Longhand(l))
.map_or(false, |&(_, importance)| importance.important())
}) {
Importance::Important
} else {
Importance::Normal
}
}
Err(longhand_or_custom) => {
// Step 3
self.get(longhand_or_custom).map_or(Importance::Normal, |&(_, importance)| importance)
}
}
}
/// Adds or overrides the declaration for a given property in this block,
/// **except** if an existing declaration for the same property is more
/// important.
///
/// Always ensures that the property declaration is at the end.
pub fn extend(&mut self, drain: SourcePropertyDeclarationDrain, importance: Importance) {
self.extend_common(drain, importance, false);
}
/// Adds or overrides the declaration for a given property in this block,
/// **even** if an existing declaration for the same property is more
/// important, and reuses the same position in the block.
///
/// Returns whether anything changed.
pub fn extend_reset(&mut self, drain: SourcePropertyDeclarationDrain,
importance: Importance) -> bool {
self.extend_common(drain, importance, true)
}
fn extend_common(
&mut self,
mut drain: SourcePropertyDeclarationDrain,
importance: Importance,
overwrite_more_important_and_reuse_slot: bool,
) -> bool {
let all_shorthand_len = match drain.all_shorthand {
AllShorthand::NotSet => 0,
AllShorthand::CSSWideKeyword(_) |
AllShorthand::WithVariables(_) => ShorthandId::All.longhands().len()
};
let push_calls_count = drain.declarations.len() + all_shorthand_len;
// With deduplication the actual length increase may be less than this.
self.declarations.reserve(push_calls_count);
let mut changed = false;
for decl in &mut drain.declarations {
changed |= self.push_common(
decl,
importance,
overwrite_more_important_and_reuse_slot,
);
}
match drain.all_shorthand {
AllShorthand::NotSet => {}
AllShorthand::CSSWideKeyword(keyword) => {
for &id in ShorthandId::All.longhands() {
let decl = PropertyDeclaration::CSSWideKeyword(id, keyword);
changed |= self.push_common(
decl,
importance,
overwrite_more_important_and_reuse_slot,
);
}
}
AllShorthand::WithVariables(unparsed) => {
for &id in ShorthandId::All.longhands() {
let decl = PropertyDeclaration::WithVariables(id, unparsed.clone());
changed |= self.push_common(
decl,
importance,
overwrite_more_important_and_reuse_slot,
);
}
}
}
changed
}
/// Adds or overrides the declaration for a given property in this block,
/// **except** if an existing declaration for the same property is more
/// important.
///
/// Ensures that, if inserted, it's inserted at the end of the declaration
/// block.
pub fn push(&mut self, declaration: PropertyDeclaration, importance: Importance) {
self.push_common(declaration, importance, false);
}
fn push_common(
&mut self,
declaration: PropertyDeclaration,
importance: Importance,
overwrite_more_important_and_reuse_slot: bool
) -> bool {
let definitely_new = if let PropertyDeclarationId::Longhand(id) = declaration.id() {
!self.longhands.contains(id)
} else {
false // For custom properties, always scan
};
if !definitely_new {
let mut index_to_remove = None;
for (i, slot) in self.declarations.iter_mut().enumerate() {
if slot.0.id() == declaration.id() {
match (slot.1, importance) {
(Importance::Normal, Importance::Important) => {
self.important_count += 1;
}
(Importance::Important, Importance::Normal) => {
if overwrite_more_important_and_reuse_slot {
self.important_count -= 1;
} else {
return false
}
}
_ => if slot.0 == declaration {
return false;
}
}
if overwrite_more_important_and_reuse_slot {
*slot = (declaration, importance);
return true;
}
// NOTE(emilio): We could avoid this and just override for
// properties not affected by logical props, but it's not
// clear it's worth it given the `definitely_new` check.
index_to_remove = Some(i);
break;
}
}
if let Some(index) = index_to_remove {
self.declarations.remove(index);
self.declarations.push((declaration, importance));
return true;
}
}
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
self.longhands.insert(id);
}
self.declarations.push((declaration, importance));
if importance.important() {
self.important_count += 1;
}
true
}
/// Set the declaration importance for a given property, if found.
///
/// Returns whether any declaration was updated.
pub fn set_importance(&mut self, property: &PropertyId, new_importance: Importance) -> bool {
let mut updated_at_least_one = false;
for &mut (ref declaration, ref mut importance) in &mut self.declarations {
if declaration.id().is_or_is_longhand_of(property) {
match (*importance, new_importance) {
(Importance::Normal, Importance::Important) => {
self.important_count += 1;
}
(Importance::Important, Importance::Normal) => {
self.important_count -= 1;
}
_ => {
continue;
}
}
updated_at_least_one = true;
*importance = new_importance;
}
}
updated_at_least_one
}
/// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
///
/// Returns whether any declaration was actually removed.
pub fn remove_property(&mut self, property: &PropertyId) -> bool {
if let PropertyId::Longhand(id) = *property {
if !self.longhands.contains(id) {
return false
}
}
let important_count = &mut self.important_count;
let mut removed_at_least_one = false;
let longhands = &mut self.longhands;
self.declarations.retain(|&(ref declaration, importance)| {
let id = declaration.id();
let remove = id.is_or_is_longhand_of(property);
if remove {
removed_at_least_one = true;
if let PropertyDeclarationId::Longhand(id) = id {
longhands.remove(id)
}
if importance.important() {
*important_count -= 1
}
}
!remove
});
if let PropertyId::Longhand(_) = *property {
debug_assert!(removed_at_least_one);
}
removed_at_least_one
}
/// Take a declaration block known to contain a single property and serialize it.
pub fn single_value_to_css<W>(&self, property: &PropertyId, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
match property.as_shorthand() {
Err(_longhand_or_custom) => {
if self.declarations.len() == 1 {
self.declarations[0].0.to_css(dest)
} else {
Err(fmt::Error)
}
}
Ok(shorthand) => {
if !self.declarations.iter().all(|decl| decl.0.shorthands().contains(&shorthand)) {
return Err(fmt::Error)
}
let iter = self.declarations_iter();
match shorthand.get_shorthand_appendable_value(iter) {
Some(AppendableValue::Css { css, .. }) => {
dest.write_str(css)
},
Some(AppendableValue::DeclarationsForShorthand(_, decls)) => {
shorthand.longhands_to_css(decls, dest)
}
_ => Ok(())
}
}
}
}
/// Convert AnimationValueMap to PropertyDeclarationBlock.
#[cfg(feature = "gecko")]
pub fn from_animation_value_map(animation_value_map: &AnimationValueMap) -> Self {
let mut declarations = vec![];
let mut longhands = LonghandIdSet::new();
for (property, animation_value) in animation_value_map.iter() {
longhands.set_transition_property_bit(property);
declarations.push((animation_value.uncompute(), Importance::Normal));
}
PropertyDeclarationBlock {
declarations: declarations,
important_count: 0,
longhands: longhands,
}
}
/// Returns true if the declaration block has a CSSWideKeyword for the given
/// property.
#[cfg(feature = "gecko")]
pub fn has_css_wide_keyword(&self, property: &PropertyId) -> bool {
if let PropertyId::Longhand(id) = *property {
if !self.longhands.contains(id) {
return false
}
}
self.declarations.iter().any(|&(ref decl, _)|
decl.id().is_or_is_longhand_of(property) &&
decl.get_css_wide_keyword().is_some()
)
}
}
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.id();
// 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.id()))
.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;
let mut found_system = None;
if shorthand == ShorthandId::Font && longhands.iter().any(|&&(ref l, _)| l.get_system().is_some()) {
for &&(ref longhand, longhand_importance) in longhands.iter() {
if longhand.get_system().is_some() || longhand.is_default_line_height() {
current_longhands.push(longhand);
if found_system.is_none() {
found_system = longhand.get_system();
}
if longhand_importance.important() {
important_count += 1;
}
}
}
} else {
for &&(ref longhand, longhand_importance) in longhands.iter() {
if longhand.id().is_longhand_of(shorthand) {
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.len() != properties.len() {
continue;
}
}
// Substep 4
let is_important = important_count > 0;
if is_important && important_count != current_longhands.len() {
continue;
}
let importance = if is_important {
Importance::Important
} else {
Importance::Normal
};
// Substep 5 - Let value be the result of invoking serialize
// a CSS value of current longhands.
let appendable_value =
match shorthand.get_shorthand_appendable_value(current_longhands.iter().cloned()) {
None => continue,
Some(appendable_value) => appendable_value,
};
// We avoid re-serializing if we're already an
// AppendableValue::Css.
let mut v = String::new();
let value = match (appendable_value, found_system) {
(AppendableValue::Css { css, with_variables }, _) => {
debug_assert!(!css.is_empty());
AppendableValue::Css {
css: css,
with_variables: with_variables,
}
}
#[cfg(feature = "gecko")]
(_, Some(sys)) => {
sys.to_css(&mut v)?;
AppendableValue::Css {
css: &v,
with_variables: false,
}
}
(other, _) => {
append_declaration_value(&mut v, other)?;
// Substep 6
if v.is_empty() {
continue;
}
AppendableValue::Css {
css: &v,
with_variables: false,
}
}
};
// Substeps 7 and 8
// We need to check the shorthand whether it's an alias property or not.
// If it's an alias property, it should be serialized like its longhand.
if shorthand.flags().contains(SHORTHAND_ALIAS_PROPERTY) {
append_serialization::<_, Cloned<slice::Iter< _>>, _>(
dest,
&property,
value,
importance,
&mut is_first_serialization)?;
} else {
append_serialization::<_, Cloned<slice::Iter< _>>, _>(
dest,
&shorthand,
value,
importance,
&mut is_first_serialization)?;
}
for current_longhand in &current_longhands {
// Substep 9
already_serialized.push(current_longhand.id());
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 its 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.
append_serialization::<_, Cloned<slice::Iter<_>>, _>(
dest,
&property,
AppendableValue::Declaration(declaration),
importance,
&mut is_first_serialization)?;
// Step 3.3.8
already_serialized.push(property);
}
// Step 4
Ok(())
}
}
/// A convenient enum to represent different kinds of stuff that can represent a
/// _value_ in the serialization of a property declaration.
pub enum AppendableValue<'a, I>
where I: Iterator<Item=&'a PropertyDeclaration>,
{
/// A given declaration, of which we'll serialize just the value.
Declaration(&'a PropertyDeclaration),
/// A set of declarations for a given shorthand.
///
/// FIXME: This needs more docs, where are the shorthands expanded? We print
/// the property name before-hand, don't we?
DeclarationsForShorthand(ShorthandId, I),
/// A raw CSS string, coming for example from a property with CSS variables,
/// or when storing a serialized shorthand value before appending directly.
Css {
/// The raw CSS string.
css: &'a str,
/// Whether the original serialization contained variables or not.
with_variables: bool,
}
}
/// Potentially appends whitespace after the first (property: value;) pair.
fn handle_first_serialization<W>(dest: &mut W,
is_first_serialization: &mut bool)
-> fmt::Result
where W: fmt::Write,
{
if !*is_first_serialization {
dest.write_str(" ")
} else {
*is_first_serialization = false;
Ok(())
}
}
/// Append a given kind of appendable value to a serialization.
pub fn append_declaration_value<'a, W, I>(dest: &mut W,
appendable_value: AppendableValue<'a, I>)
-> fmt::Result
where W: fmt::Write,
I: Iterator<Item=&'a PropertyDeclaration>,
{
match appendable_value {
AppendableValue::Css { css, .. } => {
dest.write_str(css)
},
AppendableValue::Declaration(decl) => {
decl.to_css(dest)
},
AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
shorthand.longhands_to_css(decls, dest)
}
}
}
/// Append a given property and value pair to a serialization.
pub fn append_serialization<'a, W, I, N>(dest: &mut W,
property_name: &N,
appendable_value: AppendableValue<'a, I>,
importance: Importance,
is_first_serialization: &mut bool)
-> fmt::Result
where W: fmt::Write,
I: Iterator<Item=&'a PropertyDeclaration>,
N: ToCss,
{
try!(handle_first_serialization(dest, is_first_serialization));
try!(property_name.to_css(dest));
try!(dest.write_char(':'));
// for normal parsed values, add a space between key: and value
match appendable_value {
AppendableValue::Declaration(decl) => {
if !decl.value_is_unparsed() {
// For normal parsed values, add a space between key: and value.
dest.write_str(" ")?
}
},
AppendableValue::Css { with_variables, .. } => {
if !with_variables {
dest.write_str(" ")?
}
}
// Currently append_serialization is only called with a Css or
// a Declaration AppendableValue.
AppendableValue::DeclarationsForShorthand(..) => unreachable!(),
}
try!(append_declaration_value(dest, appendable_value));
if importance.important() {
try!(dest.write_str(" !important"));
}
dest.write_char(';')
}
/// A helper to parse the style attribute of an element, in order for this to be
/// shared between Servo and Gecko.
pub fn parse_style_attribute(input: &str,
url_data: &UrlExtraData,
error_reporter: &ParseErrorReporter,
quirks_mode: QuirksMode)
-> PropertyDeclarationBlock {
let context = ParserContext::new(Origin::Author,
url_data,
error_reporter,
Some(CssRuleType::Style),
PARSING_MODE_DEFAULT,
quirks_mode);
let mut input = ParserInput::new(input);
parse_property_declaration_list(&context, &mut Parser::new(&mut input))
}
/// Parse a given property declaration. Can result in multiple
/// `PropertyDeclaration`s when expanding a shorthand, for example.
///
/// This does not attempt to parse !important at all.
pub fn parse_one_declaration_into(declarations: &mut SourcePropertyDeclaration,
id: PropertyId,
input: &str,
url_data: &UrlExtraData,
error_reporter: &ParseErrorReporter,
parsing_mode: ParsingMode,
quirks_mode: QuirksMode)
-> Result<(), ()> {
let context = ParserContext::new(Origin::Author,
url_data,
error_reporter,
Some(CssRuleType::Style),
parsing_mode,
quirks_mode);
let mut input = ParserInput::new(input);
Parser::new(&mut input).parse_entirely(|parser| {
PropertyDeclaration::parse_into(declarations, id, &context, parser)
.map_err(|e| e.into())
}).map_err(|_| ())
}
/// A struct to parse property declarations.
struct PropertyDeclarationParser<'a, 'b: 'a> {
context: &'a ParserContext<'b>,
declarations: &'a mut SourcePropertyDeclaration,
}
/// Default methods reject all at rules.
impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b> {
type Prelude = ();
type AtRule = Importance;
type Error = SelectorParseError<'i, StyleParseError<'i>>;
}
impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
type Declaration = Importance;
type Error = SelectorParseError<'i, StyleParseError<'i>>;
fn parse_value<'t>(&mut self, name: Cow<'i, str>, input: &mut Parser<'i, 't>)
-> Result<Importance, ParseError<'i>> {
let id = try!(PropertyId::parse(name));
input.parse_until_before(Delimiter::Bang, |input| {
PropertyDeclaration::parse_into(self.declarations, id, self.context, input)
.map_err(|e| e.into())
})?;
let importance = match input.try(parse_important) {
Ok(()) => Importance::Important,
Err(_) => Importance::Normal,
};
// In case there is still unparsed text in the declaration, we should roll back.
input.expect_exhausted()?;
Ok(importance)
}
}
/// Parse a list of property declarations and return a property declaration
/// block.
pub fn parse_property_declaration_list(context: &ParserContext,
input: &mut Parser)
-> PropertyDeclarationBlock {
let mut declarations = SourcePropertyDeclaration::new();
let mut block = PropertyDeclarationBlock::new();
let parser = PropertyDeclarationParser {
context: context,
declarations: &mut declarations,
};
let mut iter = DeclarationListParser::new(input, parser);
while let Some(declaration) = iter.next() {
match declaration {
Ok(importance) => {
block.extend(iter.parser.declarations.drain(), importance);
}
Err(err) => {
iter.parser.declarations.clear();
let pos = err.span.start;
let error = ContextualParseError::UnsupportedPropertyDeclaration(
iter.input.slice(err.span), err.error);
log_css_error(iter.input, pos, error, &context);
}
}
}
block
}