Auto merge of #15856 - servo:dedup, r=Manishearth

Deduplicate declarations on insertion, not at the end of parsing a block

<!-- Please describe your changes on the following line: -->

---
<!-- 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
- [x] These changes fix #15558 (github issue number if applicable).

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- 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/15856)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-03-08 03:07:04 -08:00 committed by GitHub
commit 4fc7034370
18 changed files with 531 additions and 574 deletions

View file

@ -62,15 +62,12 @@ impl CSSStyleOwner {
let result = f(&mut pdb, &mut changed);
result
} else {
let mut pdb = PropertyDeclarationBlock {
important_count: 0,
declarations: vec![],
};
let mut pdb = PropertyDeclarationBlock::new();
let result = f(&mut pdb, &mut changed);
// Here `changed` is somewhat silly, because we know the
// exact conditions under it changes.
changed = !pdb.declarations.is_empty();
changed = !pdb.declarations().is_empty();
if changed {
attr = Some(Arc::new(RwLock::new(pdb)));
}
@ -116,10 +113,7 @@ impl CSSStyleOwner {
match *el.style_attribute().borrow() {
Some(ref pdb) => f(&pdb.read()),
None => {
let pdb = PropertyDeclarationBlock {
important_count: 0,
declarations: vec![],
};
let pdb = PropertyDeclarationBlock::new();
f(&pdb)
}
}
@ -250,14 +244,14 @@ impl CSSStyleDeclaration {
// Step 6
let window = self.owner.window();
let declarations =
let result =
parse_one_declaration(id, &value, &self.owner.base_url(),
window.css_error_reporter(),
ParserContextExtraData::default());
// Step 7
let declarations = match declarations {
Ok(declarations) => declarations,
let parsed = match result {
Ok(parsed) => parsed,
Err(_) => {
*changed = false;
return Ok(());
@ -267,9 +261,9 @@ impl CSSStyleDeclaration {
// Step 8
// Step 9
*changed = false;
for declaration in declarations {
*changed |= pdb.set_parsed_declaration(declaration.0, importance);
}
parsed.expand(|declaration| {
*changed |= pdb.set_parsed_declaration(declaration, importance);
});
Ok(())
})
@ -280,7 +274,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
fn Length(&self) -> u32 {
self.owner.with_block(|pdb| {
pdb.declarations.len() as u32
pdb.declarations().len() as u32
})
}
@ -405,7 +399,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
self.owner.with_block(|pdb| {
pdb.declarations.get(index as usize).map(|entry| {
pdb.declarations().get(index as usize).map(|entry| {
let (ref declaration, importance) = *entry;
let mut css = declaration.to_css_string();
if importance.important() {

View file

@ -385,10 +385,9 @@ impl LayoutElementHelpers for LayoutJS<Element> {
#[inline]
fn from_declaration(declaration: PropertyDeclaration) -> ApplicableDeclarationBlock {
ApplicableDeclarationBlock::from_declarations(
Arc::new(RwLock::new(PropertyDeclarationBlock {
declarations: vec![(declaration, Importance::Normal)],
important_count: 0,
})),
Arc::new(RwLock::new(PropertyDeclarationBlock::with_one(
declaration, Importance::Normal
))),
CascadeLevel::PresHints)
}

View file

@ -418,11 +418,11 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
let guard = declarations.read();
// No !important in keyframes.
debug_assert!(guard.declarations.iter()
debug_assert!(guard.declarations().iter()
.all(|&(_, importance)| importance == Importance::Normal));
let iter = || {
guard.declarations.iter().rev().map(|&(ref decl, _importance)| decl)
guard.declarations().iter().rev().map(|&(ref decl, _importance)| decl)
};
let computed =

View file

@ -21,7 +21,7 @@ use values::specified::url::SpecifiedUrl;
/// A source for a font-face rule.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub enum Source {
/// A `url()` source.
Url(UrlSource),
@ -54,7 +54,7 @@ impl OneOrMoreCommaSeparated for Source {}
///
/// https://drafts.csswg.org/css-fonts/#src-desc
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct UrlSource {
/// The specified url.
pub url: SpecifiedUrl,
@ -184,7 +184,6 @@ macro_rules! font_face_descriptors {
///
/// https://drafts.csswg.org/css-fonts/#font-face-rule
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct FontFaceRule {
$(
#[$m_doc]

View file

@ -11,9 +11,8 @@ use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
use parking_lot::RwLock;
use parser::{ParserContext, ParserContextExtraData, log_css_error};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
use properties::{PropertyDeclarationId, LonghandId, DeclaredValue};
use properties::{PropertyDeclarationId, LonghandId, DeclaredValue, ParsedDeclaration};
use properties::LonghandIdSet;
use properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty;
use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
use std::fmt;
@ -71,8 +70,7 @@ impl KeyframePercentage {
/// A keyframes selector is a list of percentages or from/to symbols, which are
/// converted at parse time to percentages.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug, PartialEq)]
pub struct KeyframeSelector(Vec<KeyframePercentage>);
impl KeyframeSelector {
/// Return the list of percentages this selector contains.
@ -94,8 +92,7 @@ impl KeyframeSelector {
}
/// A keyframe.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug)]
pub struct Keyframe {
/// The selector this keyframe was specified from.
pub selector: KeyframeSelector,
@ -104,7 +101,6 @@ pub struct Keyframe {
///
/// Note that `!important` rules in keyframes don't apply, but we keep this
/// `Arc` just for convenience.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub block: Arc<RwLock<PropertyDeclarationBlock>>,
}
@ -149,7 +145,7 @@ impl Keyframe {
/// declarations to apply.
///
/// TODO: Find a better name for this?
#[derive(Debug, Clone)]
#[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesStepValue {
/// A step formed by a declaration block specified by the CSS.
@ -164,7 +160,7 @@ pub enum KeyframesStepValue {
}
/// A single step from a keyframe animation.
#[derive(Debug, Clone)]
#[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframesStep {
/// The percentage of the animation duration when this step starts.
@ -185,7 +181,7 @@ impl KeyframesStep {
value: KeyframesStepValue) -> Self {
let declared_timing_function = match value {
KeyframesStepValue::Declarations { ref block } => {
block.read().declarations.iter().any(|&(ref prop_decl, _)| {
block.read().declarations().iter().any(|&(ref prop_decl, _)| {
match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => true,
_ => false,
@ -236,7 +232,7 @@ impl KeyframesStep {
/// of keyframes, in order.
///
/// It only takes into account animable properties.
#[derive(Debug, Clone)]
#[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframesAnimation {
/// The difference steps of the animation.
@ -253,7 +249,7 @@ fn get_animated_properties(keyframes: &[Arc<RwLock<Keyframe>>]) -> Vec<Transitio
// it here.
for keyframe in keyframes {
let keyframe = keyframe.read();
for &(ref declaration, importance) in keyframe.block.read().declarations.iter() {
for &(ref declaration, importance) in keyframe.block.read().declarations().iter() {
assert!(!importance.important());
if let Some(property) = TransitionProperty::from_declaration(declaration) {
@ -364,12 +360,12 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
-> Result<Self::QualifiedRule, ()> {
let parser = KeyframeDeclarationParser {
context: self.context,
declarations: vec![],
};
let mut iter = DeclarationListParser::new(input, parser);
let mut block = PropertyDeclarationBlock::new();
while let Some(declaration) = iter.next() {
match declaration {
Ok(_) => (),
Ok(parsed) => parsed.expand(|d| block.push(d, Importance::Normal)),
Err(range) => {
let pos = range.start;
let message = format!("Unsupported keyframe property declaration: '{}'",
@ -381,45 +377,36 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
}
Ok(Arc::new(RwLock::new(Keyframe {
selector: prelude,
block: Arc::new(RwLock::new(PropertyDeclarationBlock {
declarations: iter.parser.declarations,
important_count: 0,
})),
block: Arc::new(RwLock::new(block)),
})))
}
}
struct KeyframeDeclarationParser<'a, 'b: 'a> {
context: &'a ParserContext<'b>,
declarations: Vec<(PropertyDeclaration, Importance)>
}
/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for KeyframeDeclarationParser<'a, 'b> {
type Prelude = ();
type AtRule = ();
type AtRule = ParsedDeclaration;
}
impl<'a, 'b> DeclarationParser for KeyframeDeclarationParser<'a, 'b> {
/// We parse rules directly into the declarations object
type Declaration = ();
type Declaration = ParsedDeclaration;
fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<ParsedDeclaration, ()> {
let id = try!(PropertyId::parse(name.into()));
let old_len = self.declarations.len();
match PropertyDeclaration::parse(id, self.context, input, &mut self.declarations, true) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}
_ => {
self.declarations.truncate(old_len);
return Err(());
match ParsedDeclaration::parse(id, self.context, input, true) {
Ok(parsed) => {
// In case there is still unparsed text in the declaration, we should roll back.
if !input.is_exhausted() {
Err(())
} else {
Ok(parsed)
}
}
}
// In case there is still unparsed text in the declaration, we should roll back.
if !input.is_exhausted() {
self.declarations.truncate(old_len);
Err(())
} else {
Ok(())
Err(_) => Err(())
}
}
}

View file

@ -40,28 +40,54 @@ impl Importance {
}
}
impl Default for Importance {
#[inline]
fn default() -> Self {
Importance::Normal
}
}
/// Overridden declarations are skipped.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone)]
pub struct PropertyDeclarationBlock {
/// The group of declarations, along with their importance.
///
/// Only deduplicated declarations appear here.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub declarations: Vec<(PropertyDeclaration, Importance)>,
declarations: Vec<(PropertyDeclaration, Importance)>,
/// The number of entries in `self.declaration` with `Importance::Important`
pub important_count: u32,
important_count: usize,
longhands: LonghandIdSet,
}
impl fmt::Debug for PropertyDeclarationBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.declarations.fmt(f)
}
}
impl PropertyDeclarationBlock {
/// 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
}
/// Returns wheather this block contains any declaration with `!important`.
///
/// This is based on the `important_count` counter,
@ -77,7 +103,7 @@ impl PropertyDeclarationBlock {
/// 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
self.declarations.len() > self.important_count
}
/// Get a declaration for a given property.
@ -171,29 +197,54 @@ impl PropertyDeclarationBlock {
}
/// Adds or overrides the declaration for a given property in this block,
/// without taking into account any kind of priority. Returns whether the
/// declaration block is actually changed.
/// except if an existing declaration for the same property is more important.
pub fn push(&mut self, declaration: PropertyDeclaration, importance: Importance) {
self.push_common(declaration, importance, false);
}
/// Adds or overrides the declaration for a given property in this block,
/// Returns whether the declaration block is actually changed.
pub fn set_parsed_declaration(&mut self,
declaration: PropertyDeclaration,
importance: Importance) -> bool {
for slot in &mut *self.declarations {
if slot.0.id() == declaration.id() {
match (slot.1, importance) {
(Importance::Normal, Importance::Important) => {
self.important_count += 1;
}
(Importance::Important, Importance::Normal) => {
self.important_count -= 1;
}
_ => if slot.0 == declaration {
return false;
self.push_common(declaration, importance, true)
}
fn push_common(&mut self, declaration: PropertyDeclaration, importance: Importance,
overwrite_more_important: 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 {
for slot in &mut *self.declarations {
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 {
self.important_count -= 1;
} else {
return false
}
}
_ => if slot.0 == declaration {
return false;
}
}
*slot = (declaration, importance);
return true
}
*slot = (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;
@ -230,6 +281,11 @@ impl PropertyDeclarationBlock {
///
/// 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;
self.declarations.retain(|&(ref declaration, importance)| {
@ -243,6 +299,10 @@ impl PropertyDeclarationBlock {
!remove
});
if let PropertyId::Longhand(id) = *property {
debug_assert!(removed_at_least_one);
self.longhands.remove(id);
}
removed_at_least_one
}
@ -278,39 +338,6 @@ impl PropertyDeclarationBlock {
}
}
}
/// Only keep the "winning" declaration for any given property, by importance then source order.
pub fn deduplicate(&mut self) {
let mut deduplicated = Vec::with_capacity(self.declarations.len());
let mut seen_normal = PropertyDeclarationIdSet::new();
let mut seen_important = PropertyDeclarationIdSet::new();
for (declaration, importance) in self.declarations.drain(..).rev() {
if importance.important() {
let id = declaration.id();
if seen_important.contains(id) {
self.important_count -= 1;
continue
}
if seen_normal.contains(id) {
let previous_len = deduplicated.len();
deduplicated.retain(|&(ref d, _)| PropertyDeclaration::id(d) != id);
debug_assert_eq!(deduplicated.len(), previous_len - 1);
}
seen_important.insert(id);
} else {
let id = declaration.id();
if seen_normal.contains(id) ||
seen_important.contains(id) {
continue
}
seen_normal.insert(id)
}
deduplicated.push((declaration, importance))
}
deduplicated.reverse();
self.declarations = deduplicated;
}
}
impl ToCss for PropertyDeclarationBlock {
@ -556,65 +583,46 @@ pub fn parse_one_declaration(id: PropertyId,
base_url: &ServoUrl,
error_reporter: StdBox<ParseErrorReporter + Send>,
extra_data: ParserContextExtraData)
-> Result<Vec<(PropertyDeclaration, Importance)>, ()> {
-> Result<ParsedDeclaration, ()> {
let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data);
Parser::new(input).parse_entirely(|parser| {
let mut results = vec![];
match PropertyDeclaration::parse(id, &context, parser, &mut results, false) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results),
_ => Err(())
}
ParsedDeclaration::parse(id, &context, parser, false)
.map_err(|_| ())
})
}
/// A struct to parse property declarations.
struct PropertyDeclarationParser<'a, 'b: 'a> {
context: &'a ParserContext<'b>,
declarations: Vec<(PropertyDeclaration, Importance)>
}
/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> {
type Prelude = ();
type AtRule = (u32, Importance);
type AtRule = (ParsedDeclaration, Importance);
}
impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
/// Declarations are pushed to the internal vector. This will only
/// let you know if the decl was !important and how many longhands
/// it expanded to
type Declaration = (u32, Importance);
type Declaration = (ParsedDeclaration, Importance);
fn parse_value(&mut self, name: &str, input: &mut Parser)
-> Result<(u32, Importance), ()> {
-> Result<(ParsedDeclaration, Importance), ()> {
let id = try!(PropertyId::parse(name.into()));
let old_len = self.declarations.len();
let parse_result = input.parse_until_before(Delimiter::Bang, |input| {
match PropertyDeclaration::parse(id, self.context, input, &mut self.declarations, false) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(()),
_ => Err(())
}
});
if let Err(_) = parse_result {
// rollback
self.declarations.truncate(old_len);
return Err(())
}
let parsed = input.parse_until_before(Delimiter::Bang, |input| {
ParsedDeclaration::parse(id, self.context, input, false)
.map_err(|_| ())
})?;
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.
if !input.is_exhausted() {
self.declarations.truncate(old_len);
return Err(())
}
for decl in &mut self.declarations[old_len..] {
decl.1 = importance
}
Ok(((self.declarations.len() - old_len) as u32, importance))
Ok((parsed, importance))
}
}
@ -624,19 +632,14 @@ impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
pub fn parse_property_declaration_list(context: &ParserContext,
input: &mut Parser)
-> PropertyDeclarationBlock {
let mut important_count = 0;
let mut block = PropertyDeclarationBlock::new();
let parser = PropertyDeclarationParser {
context: context,
declarations: vec![],
};
let mut iter = DeclarationListParser::new(input, parser);
while let Some(declaration) = iter.next() {
match declaration {
Ok((count, importance)) => {
if importance.important() {
important_count += count;
}
}
Ok((parsed, importance)) => parsed.expand(|d| block.push(d, importance)),
Err(range) => {
let pos = range.start;
let message = format!("Unsupported property declaration: '{}'",
@ -645,10 +648,5 @@ pub fn parse_property_declaration_list(context: &ParserContext,
}
}
}
let mut block = PropertyDeclarationBlock {
declarations: iter.parser.declarations,
important_count: important_count,
};
block.deduplicate();
block
}

View file

@ -168,23 +168,19 @@ impl ComputedValues {
% for prop in data.longhands:
% if prop.animatable:
PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}) => {
PropertyDeclarationBlock {
declarations: vec![
(PropertyDeclaration::${prop.camel_case}(DeclaredValue::Value(
% if prop.boxed:
Box::new(
% endif
longhands::${prop.ident}::SpecifiedValue::from_computed_value(
&self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
% if prop.boxed:
)
% endif
)),
Importance::Normal)
],
important_count: 0
}
PropertyDeclarationBlock::with_one(
PropertyDeclaration::${prop.camel_case}(DeclaredValue::Value(
% if prop.boxed:
Box::new(
% endif
longhands::${prop.ident}::SpecifiedValue::from_computed_value(
&self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
% if prop.boxed:
)
% endif
)),
Importance::Normal
)
},
% endif
% endfor

View file

@ -339,7 +339,7 @@
input.reset(start);
let (first_token_type, css) = try!(
::custom_properties::parse_non_custom_with_var(input));
return Ok(DeclaredValue::WithVariables(Box::new(UnparsedValue {
return Ok(DeclaredValue::WithVariables(Arc::new(UnparsedValue {
css: css.into_owned(),
first_token_type: first_token_type,
base_url: context.base_url.clone(),
@ -483,10 +483,10 @@
#[allow(unused_imports)]
use cssparser::Parser;
use parser::ParserContext;
use properties::{DeclaredValue, PropertyDeclaration};
use properties::{DeclaredValue, PropertyDeclaration, ParsedDeclaration};
use properties::{ShorthandId, UnparsedValue, longhands};
use properties::declaration_block::Importance;
use std::fmt;
use std::sync::Arc;
use style_traits::ToCss;
pub struct Longhands {
@ -551,10 +551,7 @@
/// Parse the given shorthand and fill the result into the
/// `declarations` vector.
pub fn parse(context: &ParserContext,
input: &mut Parser,
declarations: &mut Vec<(PropertyDeclaration, Importance)>)
-> Result<(), ()> {
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<ParsedDeclaration, ()> {
input.look_for_var_functions();
let start = input.position();
let value = input.parse_entirely(|input| parse_value(context, input));
@ -563,31 +560,17 @@
}
let var = input.seen_var_functions();
if let Ok(value) = value {
% for sub_property in shorthand.sub_properties:
declarations.push((PropertyDeclaration::${sub_property.camel_case}(
% if sub_property.boxed:
DeclaredValue::Value(Box::new(value.${sub_property.ident}))
% else:
DeclaredValue::Value(value.${sub_property.ident})
% endif
), Importance::Normal));
% endfor
Ok(())
Ok(ParsedDeclaration::${shorthand.camel_case}(value))
} else if var {
input.reset(start);
let (first_token_type, css) = try!(
::custom_properties::parse_non_custom_with_var(input));
% for sub_property in shorthand.sub_properties:
declarations.push((PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::WithVariables(Box::new(UnparsedValue {
css: css.clone().into_owned(),
first_token_type: first_token_type,
base_url: context.base_url.clone(),
from_shorthand: Some(ShorthandId::${shorthand.camel_case}),
}))
), Importance::Normal));
% endfor
Ok(())
Ok(ParsedDeclaration::${shorthand.camel_case}WithVariables(Arc::new(UnparsedValue {
css: css.into_owned(),
first_token_type: first_token_type,
base_url: context.base_url.clone(),
from_shorthand: Some(ShorthandId::${shorthand.camel_case}),
})))
} else {
Err(())
}

View file

@ -177,6 +177,7 @@ pub mod animated_properties {
}
/// A set of longhand properties
#[derive(Clone)]
pub struct LonghandIdSet {
storage: [u32; (${len(data.longhands)} - 1 + 32) / 32]
}
@ -202,6 +203,13 @@ impl LonghandIdSet {
self.storage[bit / 32] |= 1 << (bit % 32);
}
/// Remove the given property from the set
#[inline]
pub fn remove(&mut self, id: LonghandId) {
let bit = id as usize;
self.storage[bit / 32] &= !(1 << (bit % 32));
}
/// Set the corresponding bit of TransitionProperty.
/// This function will panic if TransitionProperty::All is given.
pub fn set_transition_property_bit(&mut self, property: &TransitionProperty) {
@ -563,19 +571,17 @@ impl ShorthandId {
/// Servo's representation of a declared value for a given `T`, which is the
/// declared value for that property.
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum DeclaredValue<T> {
/// A known specified value from the stylesheet.
Value(T),
/// An unparsed value that contains `var()` functions.
WithVariables(Box<UnparsedValue>),
WithVariables(Arc<UnparsedValue>),
/// An CSS-wide keyword.
CSSWideKeyword(CSSWideKeyword),
}
/// An unparsed property value that contains `var()` functions.
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(PartialEq, Eq, Debug)]
pub struct UnparsedValue {
/// The css serialization for this value.
css: String,
@ -797,9 +803,158 @@ impl PropertyId {
}
}
/// Includes shorthands before expansion
pub enum ParsedDeclaration {
% for shorthand in data.shorthands:
/// ${shorthand.name}
${shorthand.camel_case}(shorthands::${shorthand.ident}::Longhands),
/// ${shorthand.name} with a CSS-wide keyword
${shorthand.camel_case}CSSWideKeyword(CSSWideKeyword),
/// ${shorthand.name} with var() functions
${shorthand.camel_case}WithVariables(Arc<UnparsedValue>),
% endfor
/// Not a shorthand
LonghandOrCustom(PropertyDeclaration),
}
impl ParsedDeclaration {
/// Transform this ParsedDeclaration into a sequence of PropertyDeclaration
/// by expanding shorthand declarations into their corresponding longhands
pub fn expand<F>(self, mut push: F) where F: FnMut(PropertyDeclaration) {
match self {
% for shorthand in data.shorthands:
ParsedDeclaration::${shorthand.camel_case}(
shorthands::${shorthand.ident}::Longhands {
% for sub_property in shorthand.sub_properties:
${sub_property.ident},
% endfor
}
) => {
% for sub_property in shorthand.sub_properties:
push(PropertyDeclaration::${sub_property.camel_case}(
% if sub_property.boxed:
DeclaredValue::Value(Box::new(${sub_property.ident}))
% else:
DeclaredValue::Value(${sub_property.ident})
% endif
));
% endfor
}
ParsedDeclaration::${shorthand.camel_case}CSSWideKeyword(keyword) => {
% for sub_property in shorthand.sub_properties:
push(PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::CSSWideKeyword(keyword)
));
% endfor
}
ParsedDeclaration::${shorthand.camel_case}WithVariables(value) => {
debug_assert_eq!(
value.from_shorthand,
Some(ShorthandId::${shorthand.camel_case})
);
% for sub_property in shorthand.sub_properties:
push(PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::WithVariables(value.clone())
));
% endfor
}
% endfor
ParsedDeclaration::LonghandOrCustom(declaration) => push(declaration),
}
}
/// The `in_keyframe_block` parameter controls this:
///
/// https://drafts.csswg.org/css-animations/#keyframes
/// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
/// > except those defined in this specification,
/// > but does accept the `animation-play-state` property and interprets it specially.
///
/// This will not actually parse Importance values, and will always set things
/// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
/// we only set them here so that we don't have to reallocate
pub fn parse(id: PropertyId, context: &ParserContext, input: &mut Parser,
in_keyframe_block: bool)
-> Result<ParsedDeclaration, PropertyDeclarationParseError> {
match id {
PropertyId::Custom(name) => {
let value = match input.try(|i| CSSWideKeyword::parse(context, i)) {
Ok(keyword) => DeclaredValue::CSSWideKeyword(keyword),
Err(()) => match ::custom_properties::SpecifiedValue::parse(context, input) {
Ok(value) => DeclaredValue::Value(value),
Err(()) => return Err(PropertyDeclarationParseError::InvalidValue),
}
};
Ok(ParsedDeclaration::LonghandOrCustom(PropertyDeclaration::Custom(name, value)))
}
PropertyId::Longhand(id) => match id {
% for property in data.longhands:
LonghandId::${property.camel_case} => {
% if not property.derived_from:
% if not property.allowed_in_keyframe_block:
if in_keyframe_block {
return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock)
}
% endif
% if property.internal:
if context.stylesheet_origin != Origin::UserAgent {
return Err(PropertyDeclarationParseError::UnknownProperty)
}
% endif
${property_pref_check(property)}
match longhands::${property.ident}::parse_declared(context, input) {
Ok(value) => {
Ok(ParsedDeclaration::LonghandOrCustom(
PropertyDeclaration::${property.camel_case}(value)
))
},
Err(()) => Err(PropertyDeclarationParseError::InvalidValue),
}
% else:
Err(PropertyDeclarationParseError::UnknownProperty)
% endif
}
% endfor
},
PropertyId::Shorthand(id) => match id {
% for shorthand in data.shorthands:
ShorthandId::${shorthand.camel_case} => {
% if not shorthand.allowed_in_keyframe_block:
if in_keyframe_block {
return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock)
}
% endif
% if shorthand.internal:
if context.stylesheet_origin != Origin::UserAgent {
return Err(PropertyDeclarationParseError::UnknownProperty)
}
% endif
${property_pref_check(shorthand)}
match input.try(|i| CSSWideKeyword::parse(context, i)) {
Ok(keyword) => {
Ok(ParsedDeclaration::${shorthand.camel_case}CSSWideKeyword(keyword))
},
Err(()) => {
shorthands::${shorthand.ident}::parse(context, input)
.map_err(|()| PropertyDeclarationParseError::InvalidValue)
}
}
}
% endfor
}
}
}
}
/// Servo's representation for a property declaration.
#[derive(PartialEq, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum PropertyDeclaration {
% for property in data.longhands:
/// ${property.name}
@ -831,7 +986,7 @@ impl HasViewportPercentage for PropertyDeclaration {
/// The result of parsing a property declaration.
#[derive(Eq, PartialEq, Copy, Clone)]
pub enum PropertyDeclarationParseResult {
pub enum PropertyDeclarationParseError {
/// The property declaration was for an unknown property.
UnknownProperty,
/// The property declaration was for a disabled experimental property.
@ -843,8 +998,6 @@ pub enum PropertyDeclarationParseResult {
///
/// See: https://drafts.csswg.org/css-animations/#keyframes
AnimationPropertyInKeyframeBlock,
/// The declaration was either valid or ignored.
ValidOrIgnoredDeclaration,
}
impl fmt::Debug for PropertyDeclaration {
@ -878,7 +1031,7 @@ impl ToCss for PropertyDeclaration {
% if property.experimental and product == "servo":
if !PREFS.get("${property.experimental}")
.as_boolean().unwrap_or(false) {
return PropertyDeclarationParseResult::ExperimentalProperty
return Err(PropertyDeclarationParseError::ExperimentalProperty)
}
% endif
% if product == "gecko":
@ -895,7 +1048,7 @@ impl ToCss for PropertyDeclaration {
let id = structs::${helpers.to_nscsspropertyid(property.ident)};
let enabled = unsafe { bindings::Gecko_PropertyId_IsPrefEnabled(id) };
if !enabled {
return PropertyDeclarationParseResult::ExperimentalProperty
return Err(PropertyDeclarationParseError::ExperimentalProperty)
}
}
% endif
@ -992,100 +1145,6 @@ impl PropertyDeclaration {
}
}
/// The `in_keyframe_block` parameter controls this:
///
/// https://drafts.csswg.org/css-animations/#keyframes
/// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
/// > except those defined in this specification,
/// > but does accept the `animation-play-state` property and interprets it specially.
///
/// This will not actually parse Importance values, and will always set things
/// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
/// we only set them here so that we don't have to reallocate
pub fn parse(id: PropertyId, context: &ParserContext, input: &mut Parser,
result_list: &mut Vec<(PropertyDeclaration, Importance)>,
in_keyframe_block: bool)
-> PropertyDeclarationParseResult {
match id {
PropertyId::Custom(name) => {
let value = match input.try(|i| CSSWideKeyword::parse(context, i)) {
Ok(keyword) => DeclaredValue::CSSWideKeyword(keyword),
Err(()) => match ::custom_properties::SpecifiedValue::parse(context, input) {
Ok(value) => DeclaredValue::Value(value),
Err(()) => return PropertyDeclarationParseResult::InvalidValue,
}
};
result_list.push((PropertyDeclaration::Custom(name, value),
Importance::Normal));
return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
}
PropertyId::Longhand(id) => match id {
% for property in data.longhands:
LonghandId::${property.camel_case} => {
% if not property.derived_from:
% if not property.allowed_in_keyframe_block:
if in_keyframe_block {
return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock
}
% endif
% if property.internal:
if context.stylesheet_origin != Origin::UserAgent {
return PropertyDeclarationParseResult::UnknownProperty
}
% endif
${property_pref_check(property)}
match longhands::${property.ident}::parse_declared(context, input) {
Ok(value) => {
result_list.push((PropertyDeclaration::${property.camel_case}(value),
Importance::Normal));
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
Err(()) => PropertyDeclarationParseResult::InvalidValue,
}
% else:
PropertyDeclarationParseResult::UnknownProperty
% endif
}
% endfor
},
PropertyId::Shorthand(id) => match id {
% for shorthand in data.shorthands:
ShorthandId::${shorthand.camel_case} => {
% if not shorthand.allowed_in_keyframe_block:
if in_keyframe_block {
return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock
}
% endif
% if shorthand.internal:
if context.stylesheet_origin != Origin::UserAgent {
return PropertyDeclarationParseResult::UnknownProperty
}
% endif
${property_pref_check(shorthand)}
match input.try(|i| CSSWideKeyword::parse(context, i)) {
Ok(keyword) => {
% for sub_property in shorthand.sub_properties:
result_list.push((
PropertyDeclaration::${sub_property.camel_case}(
DeclaredValue::CSSWideKeyword(keyword)), Importance::Normal));
% endfor
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
},
Err(()) => match shorthands::${shorthand.ident}::parse(context, input, result_list) {
Ok(()) => PropertyDeclarationParseResult::ValidOrIgnoredDeclaration,
Err(()) => PropertyDeclarationParseResult::InvalidValue,
}
}
}
% endfor
}
}
}
/// The shorthands that this longhand is part of.
pub fn shorthands(&self) -> &'static [ShorthandId] {
// first generate longhand to shorthands lookup map
@ -1766,7 +1825,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
}).collect::<Vec<_>>();
let iter_declarations = || {
lock_guards.iter().flat_map(|&(ref source, source_importance)| {
source.declarations.iter()
source.declarations().iter()
// Yield declarations later in source order (with more precedence) first.
.rev()
.filter_map(move |&(ref declaration, declaration_importance)| {

View file

@ -99,7 +99,7 @@ impl StyleSource {
let _ = write!(writer, "{:?}", rule.read().selectors);
}
let _ = write!(writer, " -> {:?}", self.read().declarations);
let _ = write!(writer, " -> {:?}", self.read().declarations());
}
/// Read the style source guard, and obtain thus read access to the

View file

@ -52,7 +52,6 @@ pub enum Origin {
/// A set of namespaces applying to a given stylesheet.
#[derive(Default, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub struct Namespaces {
pub default: Option<Namespace>,
@ -389,7 +388,6 @@ impl ToCss for CssRule {
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub struct NamespaceRule {
/// `None` for the default Namespace
@ -536,7 +534,7 @@ impl ToCss for StyleRule {
let declaration_block = self.block.read();
try!(declaration_block.to_css(dest));
// Step 4
if declaration_block.declarations.len() > 0 {
if declaration_block.declarations().len() > 0 {
try!(write!(dest, " "));
}
// Step 5
@ -954,7 +952,7 @@ impl<'a> QualifiedRuleParser for TopLevelRuleParser<'a> {
}
}
#[derive(Clone)] // shallow, relatively cheap clone
#[derive(Clone)] // shallow, relatively cheap .clone
struct NestedRuleParser<'a, 'b: 'a> {
stylesheet_origin: Origin,
context: &'a ParserContext<'b>,

View file

@ -6,7 +6,7 @@
use cssparser::{parse_important, Parser, Token};
use parser::ParserContext;
use properties::{PropertyDeclaration, PropertyId};
use properties::{PropertyId, ParsedDeclaration};
use std::fmt;
use style_traits::ToCss;
@ -205,27 +205,14 @@ impl Declaration {
///
/// https://drafts.csswg.org/css-conditional-3/#support-definition
pub fn eval(&self, cx: &ParserContext) -> bool {
use properties::PropertyDeclarationParseResult::*;
let id = if let Ok(id) = PropertyId::parse((&*self.prop).into()) {
id
} else {
return false
};
let mut input = Parser::new(&self.val);
let mut list = Vec::new();
let res = PropertyDeclaration::parse(id, cx, &mut input,
&mut list, /* in_keyframe */ false);
let res = ParsedDeclaration::parse(id, cx, &mut input, /* in_keyframe */ false);
let _ = input.try(parse_important);
if !input.is_exhausted() {
return false;
}
match res {
UnknownProperty => false,
ExperimentalProperty => false, // only happens for experimental props
// that haven't been enabled
InvalidValue => false,
AnimationPropertyInKeyframeBlock => unreachable!(),
ValidOrIgnoredDeclaration => true,
}
res.is_ok() && input.is_exhausted()
}
}