Auto merge of #12943 - servo:merged-declaration-block, r=emilio

Merge normal and important declarations in style rules

Have a single Vec instead of two. Fix #3426

---
<!-- 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 #3426.

<!-- Either: -->
- [x] 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/12943)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-08-21 03:43:25 -05:00 committed by GitHub
commit f8b2be1ea4
24 changed files with 1022 additions and 356 deletions

View file

@ -10,15 +10,16 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root}; use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::element::{Element, StylePriority}; use dom::element::Element;
use dom::node::{Node, NodeDamage, window_from_node}; use dom::node::{Node, NodeDamage, window_from_node};
use dom::window::Window; use dom::window::Window;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::ops::Deref; use std::cell::Ref;
use std::slice;
use string_cache::Atom; use string_cache::Atom;
use style::parser::ParserContextExtraData; use style::parser::ParserContextExtraData;
use style::properties::{Shorthand, is_supported_property}; use style::properties::{PropertyDeclaration, Shorthand, Importance};
use style::properties::{parse_one_declaration, parse_style_attribute}; use style::properties::{is_supported_property, parse_one_declaration, parse_style_attribute};
use style::selector_impl::PseudoElement; use style::selector_impl::PseudoElement;
// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface // http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
@ -91,7 +92,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
fn Length(&self) -> u32 { fn Length(&self) -> u32 {
let elem = self.owner.upcast::<Element>(); let elem = self.owner.upcast::<Element>();
let len = match *elem.style_attribute().borrow() { let len = match *elem.style_attribute().borrow() {
Some(ref declarations) => declarations.normal.len() + declarations.important.len(), Some(ref declarations) => declarations.declarations.len(),
None => 0, None => 0,
}; };
len as u32 len as u32
@ -102,19 +103,15 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
let index = index as usize; let index = index as usize;
let elem = self.owner.upcast::<Element>(); let elem = self.owner.upcast::<Element>();
let style_attribute = elem.style_attribute().borrow(); let style_attribute = elem.style_attribute().borrow();
let result = style_attribute.as_ref().and_then(|declarations| { style_attribute.as_ref().and_then(|declarations| {
if index > declarations.normal.len() { declarations.declarations.get(index)
declarations.important }).map(|&(ref declaration, importance)| {
.get(index - declarations.normal.len()) let mut css = declaration.to_css_string();
.map(|decl| format!("{:?} !important", decl)) if importance.important() {
} else { css += " !important";
declarations.normal
.get(index)
.map(|decl| format!("{:?}", decl))
} }
}); DOMString::from(css)
}).unwrap_or_else(DOMString::new)
result.map_or(DOMString::new(), DOMString::from)
} }
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
@ -147,15 +144,26 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
} }
} }
// Step 2.3
// Work around closures not being Clone
#[derive(Clone)]
struct Map<'a, 'b: 'a>(slice::Iter<'a, Ref<'b, (PropertyDeclaration, Importance)>>);
impl<'a, 'b> Iterator for Map<'a, 'b> {
type Item = &'a PropertyDeclaration;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|r| &r.0)
}
}
// TODO: important is hardcoded to false because method does not implement it yet // TODO: important is hardcoded to false because method does not implement it yet
let serialized_value = shorthand.serialize_shorthand_value_to_string( let serialized_value = shorthand.serialize_shorthand_value_to_string(
list.iter().map(Deref::deref as fn(_) -> _), false); Map(list.iter()), Importance::Normal);
return DOMString::from(serialized_value); return DOMString::from(serialized_value);
} }
// Step 3 & 4 // Step 3 & 4
match owner.get_inline_style_declaration(&property) { match owner.get_inline_style_declaration(&property) {
Some(declaration) => DOMString::from(declaration.value()), Some(declaration) => DOMString::from(declaration.0.value()),
None => DOMString::new(), None => DOMString::new(),
} }
} }
@ -176,8 +184,10 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
} }
// Step 3 // Step 3
} else { } else {
if self.owner.get_important_inline_style_declaration(&property).is_some() { if let Some(decl) = self.owner.get_inline_style_declaration(&property) {
return DOMString::from("important"); if decl.1.important() {
return DOMString::from("important");
}
} }
} }
@ -211,8 +221,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// Step 5 // Step 5
let priority = match &*priority { let priority = match &*priority {
"" => StylePriority::Normal, "" => Importance::Normal,
p if p.eq_ignore_ascii_case("important") => StylePriority::Important, p if p.eq_ignore_ascii_case("important") => Importance::Important,
_ => return Ok(()), _ => return Ok(()),
}; };
@ -254,8 +264,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// Step 4 // Step 4
let priority = match &*priority { let priority = match &*priority {
"" => StylePriority::Normal, "" => Importance::Normal,
p if p.eq_ignore_ascii_case("important") => StylePriority::Important, p if p.eq_ignore_ascii_case("important") => Importance::Important,
_ => return Ok(()), _ => return Ok(()),
}; };
@ -354,7 +364,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// Step 3 // Step 3
let decl_block = parse_style_attribute(&value, &window.get_url(), window.css_error_reporter(), let decl_block = parse_style_attribute(&value, &window.get_url(), window.css_error_reporter(),
ParserContextExtraData::default()); ParserContextExtraData::default());
*element.style_attribute().borrow_mut() = if decl_block.normal.is_empty() && decl_block.important.is_empty() { *element.style_attribute().borrow_mut() = if decl_block.declarations.is_empty() {
None // Step 2 None // Step 2
} else { } else {
Some(decl_block) Some(decl_block)

View file

@ -71,7 +71,7 @@ use html5ever::serialize::TraversalScope;
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks}; use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks};
use ref_filter_map::ref_filter_map; use ref_filter_map::ref_filter_map;
use selectors::matching::{DeclarationBlock, ElementFlags, matches}; use selectors::matching::{ElementFlags, matches};
use selectors::matching::{HAS_SLOW_SELECTOR, HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; use selectors::matching::{HAS_SLOW_SELECTOR, HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
use selectors::parser::{AttrSelector, NamespaceConstraint, parse_author_origin_selector_list_from_str}; use selectors::parser::{AttrSelector, NamespaceConstraint, parse_author_origin_selector_list_from_str};
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
@ -79,7 +79,7 @@ use std::borrow::Cow;
use std::cell::{Cell, Ref}; use std::cell::{Cell, Ref};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::default::Default; use std::default::Default;
use std::mem; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use string_cache::{Atom, Namespace, QualName}; use string_cache::{Atom, Namespace, QualName};
@ -87,10 +87,11 @@ use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::element_state::*; use style::element_state::*;
use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes}; use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
use style::parser::ParserContextExtraData; use style::parser::ParserContextExtraData;
use style::properties::DeclaredValue;
use style::properties::longhands::{self, background_image, border_spacing, font_family, overflow_x, font_size}; use style::properties::longhands::{self, background_image, border_spacing, font_family, overflow_x, font_size};
use style::properties::{DeclaredValue, Importance};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute}; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl}; use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl};
use style::selector_matching::DeclarationBlock;
use style::sink::Push; use style::sink::Push;
use style::values::CSSFloat; use style::values::CSSFloat;
use style::values::specified::{self, CSSColor, CSSRGBA, LengthOrPercentage}; use style::values::specified::{self, CSSColor, CSSRGBA, LengthOrPercentage};
@ -115,6 +116,16 @@ pub struct Element {
atomic_flags: AtomicElementFlags, atomic_flags: AtomicElementFlags,
} }
impl fmt::Debug for Element {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "<{}", self.local_name));
if let Some(ref id) = *self.id_attribute.borrow() {
try!(write!(f, " id={}", id));
}
write!(f, ">")
}
}
#[derive(PartialEq, HeapSizeOf)] #[derive(PartialEq, HeapSizeOf)]
pub enum ElementCreator { pub enum ElementCreator {
ParserCreated, ParserCreated,
@ -280,7 +291,7 @@ pub trait LayoutElementHelpers {
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V) unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>>; where V: Push<DeclarationBlock>;
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe fn get_colspan(self) -> u32; unsafe fn get_colspan(self) -> u32;
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -313,11 +324,13 @@ impl LayoutElementHelpers for LayoutJS<Element> {
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>> where V: Push<DeclarationBlock>
{ {
#[inline] #[inline]
fn from_declaration(rule: PropertyDeclaration) -> DeclarationBlock<Vec<PropertyDeclaration>> { fn from_declaration(rule: PropertyDeclaration) -> DeclarationBlock {
DeclarationBlock::from_declarations(Arc::new(vec![rule])) DeclarationBlock::from_declarations(
Arc::new(vec![(rule, Importance::Normal)]),
Importance::Normal)
} }
let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() { let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
@ -660,13 +673,6 @@ impl LayoutElementHelpers for LayoutJS<Element> {
} }
} }
#[derive(PartialEq, Eq, Copy, Clone, HeapSizeOf)]
pub enum StylePriority {
Important,
Normal,
}
impl Element { impl Element {
pub fn html_element_in_html_document(&self) -> bool { pub fn html_element_in_html_document(&self) -> bool {
self.namespace == ns!(html) && self.upcast::<Node>().is_in_html_doc() self.namespace == ns!(html) && self.upcast::<Node>().is_in_html_doc()
@ -756,20 +762,21 @@ impl Element {
fn remove(element: &Element, property: &str) { fn remove(element: &Element, property: &str) {
let mut inline_declarations = element.style_attribute.borrow_mut(); let mut inline_declarations = element.style_attribute.borrow_mut();
if let &mut Some(ref mut declarations) = &mut *inline_declarations { if let &mut Some(ref mut declarations) = &mut *inline_declarations {
let index = declarations.normal let mut importance = None;
.iter() let index = declarations.declarations.iter().position(|&(ref decl, i)| {
.position(|decl| decl.matches(property)); let matching = decl.matches(property);
if matching {
importance = Some(i)
}
matching
});
if let Some(index) = index { if let Some(index) = index {
Arc::make_mut(&mut declarations.normal).remove(index); Arc::make_mut(&mut declarations.declarations).remove(index);
return; if importance.unwrap().important() {
} declarations.important_count -= 1;
} else {
let index = declarations.important declarations.normal_count -= 1;
.iter() }
.position(|decl| decl.matches(property));
if let Some(index) = index {
Arc::make_mut(&mut declarations.important).remove(index);
return;
} }
} }
} }
@ -780,81 +787,87 @@ impl Element {
pub fn update_inline_style(&self, pub fn update_inline_style(&self,
declarations: Vec<PropertyDeclaration>, declarations: Vec<PropertyDeclaration>,
style_priority: StylePriority) { importance: Importance) {
fn update(element: &Element, mut declarations: Vec<PropertyDeclaration>, style_priority: StylePriority) { fn update(element: &Element, declarations: Vec<PropertyDeclaration>,
importance: Importance) {
let mut inline_declarations = element.style_attribute().borrow_mut(); let mut inline_declarations = element.style_attribute().borrow_mut();
if let &mut Some(ref mut existing_declarations) = &mut *inline_declarations { if let &mut Some(ref mut declaration_block) = &mut *inline_declarations {
let existing_declarations = if style_priority == StylePriority::Important { {
&mut existing_declarations.important // Usually, the reference count will be 1 here. But transitions could make it greater
} else { // than that.
&mut existing_declarations.normal let existing_declarations = Arc::make_mut(&mut declaration_block.declarations);
};
// Usually, the reference count will be 1 here. But transitions could make it greater 'outer: for incoming_declaration in declarations {
// than that. for existing_declaration in &mut *existing_declarations {
let existing_declarations = Arc::make_mut(existing_declarations); if existing_declaration.0.name() == incoming_declaration.name() {
match (existing_declaration.1, importance) {
while let Some(mut incoming_declaration) = declarations.pop() { (Importance::Normal, Importance::Important) => {
let mut replaced = false; declaration_block.normal_count -= 1;
for existing_declaration in &mut *existing_declarations { declaration_block.important_count += 1;
if existing_declaration.name() == incoming_declaration.name() { }
mem::swap(existing_declaration, &mut incoming_declaration); (Importance::Important, Importance::Normal) => {
replaced = true; declaration_block.normal_count += 1;
break; declaration_block.important_count -= 1;
}
_ => {}
}
*existing_declaration = (incoming_declaration, importance);
continue 'outer;
}
}
existing_declarations.push((incoming_declaration, importance));
if importance.important() {
declaration_block.important_count += 1;
} else {
declaration_block.normal_count += 1;
} }
} }
if !replaced {
// inserting instead of pushing since the declarations are in reverse order
existing_declarations.insert(0, incoming_declaration);
}
} }
return; return;
} }
let (important, normal) = if style_priority == StylePriority::Important { let (normal_count, important_count) = if importance.important() {
(declarations, vec![]) (0, declarations.len() as u32)
} else { } else {
(vec![], declarations) (declarations.len() as u32, 0)
}; };
*inline_declarations = Some(PropertyDeclarationBlock { *inline_declarations = Some(PropertyDeclarationBlock {
important: Arc::new(important), declarations: Arc::new(declarations.into_iter().map(|d| (d, importance)).collect()),
normal: Arc::new(normal), normal_count: normal_count,
important_count: important_count,
}); });
} }
update(self, declarations, style_priority); update(self, declarations, importance);
self.sync_property_with_attrs_style(); self.sync_property_with_attrs_style();
} }
pub fn set_inline_style_property_priority(&self, pub fn set_inline_style_property_priority(&self,
properties: &[&str], properties: &[&str],
style_priority: StylePriority) { new_importance: Importance) {
{ {
let mut inline_declarations = self.style_attribute().borrow_mut(); let mut inline_declarations = self.style_attribute().borrow_mut();
if let &mut Some(ref mut declarations) = &mut *inline_declarations { if let &mut Some(ref mut block) = &mut *inline_declarations {
let (from, to) = if style_priority == StylePriority::Important { // Usually, the reference counts of `from` and `to` will be 1 here. But transitions
(&mut declarations.normal, &mut declarations.important) // could make them greater than that.
} else { let declarations = Arc::make_mut(&mut block.declarations);
(&mut declarations.important, &mut declarations.normal) for &mut (ref declaration, ref mut importance) in declarations {
}; if properties.iter().any(|p| declaration.name() == **p) {
match (*importance, new_importance) {
// Usually, the reference counts of `from` and `to` will be 1 here. But transitions (Importance::Normal, Importance::Important) => {
// could make them greater than that. block.normal_count -= 1;
let from = Arc::make_mut(from); block.important_count += 1;
let to = Arc::make_mut(to); }
let mut new_from = Vec::new(); (Importance::Important, Importance::Normal) => {
for declaration in from.drain(..) { block.normal_count += 1;
let name = declaration.name(); block.important_count -= 1;
if properties.iter().any(|p| name == **p) { }
to.push(declaration) _ => {}
} else { }
new_from.push(declaration) *importance = new_importance;
} }
} }
mem::replace(from, new_from);
} }
} }
@ -863,25 +876,12 @@ impl Element {
pub fn get_inline_style_declaration(&self, pub fn get_inline_style_declaration(&self,
property: &Atom) property: &Atom)
-> Option<Ref<PropertyDeclaration>> { -> Option<Ref<(PropertyDeclaration, Importance)>> {
ref_filter_map(self.style_attribute.borrow(), |inline_declarations| { ref_filter_map(self.style_attribute.borrow(), |inline_declarations| {
inline_declarations.as_ref().and_then(|declarations| { inline_declarations.as_ref().and_then(|declarations| {
declarations.normal declarations.declarations
.iter() .iter()
.chain(declarations.important.iter()) .find(|&&(ref decl, _)| decl.matches(&property))
.find(|decl| decl.matches(&property))
})
})
}
pub fn get_important_inline_style_declaration(&self,
property: &Atom)
-> Option<Ref<PropertyDeclaration>> {
ref_filter_map(self.style_attribute.borrow(), |inline_declarations| {
inline_declarations.as_ref().and_then(|declarations| {
declarations.important
.iter()
.find(|decl| decl.matches(&property))
}) })
}) })
} }

View file

@ -47,8 +47,9 @@ use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, Lay
use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, ThreadSafeLayoutElement}; use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, ThreadSafeLayoutElement};
use script_layout_interface::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress}; use script_layout_interface::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress};
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
use selectors::matching::{DeclarationBlock, ElementFlags}; use selectors::matching::ElementFlags;
use selectors::parser::{AttrSelector, NamespaceConstraint}; use selectors::parser::{AttrSelector, NamespaceConstraint};
use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::{transmute, transmute_copy}; use std::mem::{transmute, transmute_copy};
use std::sync::Arc; use std::sync::Arc;
@ -59,9 +60,10 @@ use style::context::SharedStyleContext;
use style::data::PrivateStyleData; use style::data::PrivateStyleData;
use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode}; use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode};
use style::element_state::*; use style::element_state::*;
use style::properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock}; use style::properties::{ComputedValues, PropertyDeclarationBlock};
use style::refcell::{Ref, RefCell, RefMut}; use style::refcell::{Ref, RefCell, RefMut};
use style::selector_impl::{ElementSnapshot, NonTSPseudoClass, PseudoElement, ServoSelectorImpl}; use style::selector_impl::{ElementSnapshot, NonTSPseudoClass, PseudoElement, ServoSelectorImpl};
use style::selector_matching::DeclarationBlock;
use style::sink::Push; use style::sink::Push;
use style::str::is_whitespace; use style::str::is_whitespace;
use url::Url; use url::Url;
@ -405,9 +407,19 @@ pub struct ServoLayoutElement<'le> {
chain: PhantomData<&'le ()>, chain: PhantomData<&'le ()>,
} }
impl<'le> fmt::Debug for ServoLayoutElement<'le> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "<{}", self.element.local_name()));
if let &Some(ref id) = unsafe { &*self.element.id_attribute() } {
try!(write!(f, " id={}", id));
}
write!(f, ">")
}
}
impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> { impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>> where V: Push<DeclarationBlock>
{ {
unsafe { unsafe {
self.element.synthesize_presentational_hints_for_legacy_attributes(hints); self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
@ -926,7 +938,7 @@ impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNod
/// A wrapper around elements that ensures layout can only /// A wrapper around elements that ensures layout can only
/// ever access safe properties and cannot race on elements. /// ever access safe properties and cannot race on elements.
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
pub struct ServoThreadSafeLayoutElement<'le> { pub struct ServoThreadSafeLayoutElement<'le> {
element: &'le Element, element: &'le Element,
} }
@ -1059,5 +1071,5 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> { impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V) fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>> {} where V: Push<DeclarationBlock> {}
} }

View file

@ -10,6 +10,7 @@ use gfx_traits::{ByteIndex, LayerId, LayerType};
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use range::Range; use range::Range;
use restyle_damage::RestyleDamage; use restyle_damage::RestyleDamage;
use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
use style::computed_values::display; use style::computed_values::display;
@ -350,7 +351,7 @@ pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode {
unsafe fn dangerous_next_sibling(&self) -> Option<Self>; unsafe fn dangerous_next_sibling(&self) -> Option<Self>;
} }
pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
::selectors::Element<Impl=ServoSelectorImpl> + ::selectors::Element<Impl=ServoSelectorImpl> +
PresentationalHintsSynthetizer { PresentationalHintsSynthetizer {
type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<ConcreteThreadSafeLayoutElement = Self>; type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<ConcreteThreadSafeLayoutElement = Self>;

View file

@ -2235,6 +2235,7 @@ dependencies = [
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"quickersort 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -36,6 +36,7 @@ log = "0.3.5"
matches = "0.1" matches = "0.1"
num-traits = "0.1.32" num-traits = "0.1.32"
ordered-float = "0.2.2" ordered-float = "0.2.2"
quickersort = "2.0.0"
rand = "0.3" rand = "0.3"
rustc-serialize = "0.3" rustc-serialize = "0.3"
selectors = "0.11" selectors = "0.11"

View file

@ -15,8 +15,8 @@ use properties::longhands::animation_iteration_count::computed_value::AnimationI
use properties::longhands::animation_play_state::computed_value::AnimationPlayState; use properties::longhands::animation_play_state::computed_value::AnimationPlayState;
use properties::longhands::transition_timing_function::computed_value::StartEnd; use properties::longhands::transition_timing_function::computed_value::StartEnd;
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction; use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
use properties::{self, ComputedValues}; use properties::{self, ComputedValues, Importance};
use selectors::matching::DeclarationBlock; use selector_matching::DeclarationBlock;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use string_cache::Atom; use string_cache::Atom;
@ -385,7 +385,8 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
KeyframesStepValue::ComputedValues => style_from_cascade.clone(), KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
KeyframesStepValue::Declarations(ref declarations) => { KeyframesStepValue::Declarations(ref declarations) => {
let declaration_block = DeclarationBlock { let declaration_block = DeclarationBlock {
declarations: declarations.clone(), mixed_declarations: declarations.clone(),
importance: Importance::Normal,
source_order: 0, source_order: 0,
specificity: ::std::u32::MAX, specificity: ::std::u32::MAX,
}; };

View file

@ -9,11 +9,11 @@
use context::SharedStyleContext; use context::SharedStyleContext;
use data::PrivateStyleData; use data::PrivateStyleData;
use element_state::ElementState; use element_state::ElementState;
use properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock}; use properties::{ComputedValues, PropertyDeclarationBlock};
use refcell::{Ref, RefMut}; use refcell::{Ref, RefMut};
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
use selector_impl::{ElementExt, PseudoElement}; use selector_impl::{ElementExt, PseudoElement};
use selectors::matching::DeclarationBlock; use selector_matching::DeclarationBlock;
use sink::Push; use sink::Push;
use std::fmt::Debug; use std::fmt::Debug;
use std::ops::BitOr; use std::ops::BitOr;
@ -198,10 +198,10 @@ pub trait TDocument : Sized + Copy + Clone {
pub trait PresentationalHintsSynthetizer { pub trait PresentationalHintsSynthetizer {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>>; where V: Push<DeclarationBlock>;
} }
pub trait TElement : PartialEq + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer { pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer {
type ConcreteNode: TNode<ConcreteElement = Self, ConcreteDocument = Self::ConcreteDocument>; type ConcreteNode: TNode<ConcreteElement = Self, ConcreteDocument = Self::ConcreteDocument>;
type ConcreteDocument: TDocument<ConcreteNode = Self::ConcreteNode, ConcreteElement = Self>; type ConcreteDocument: TDocument<ConcreteNode = Self::ConcreteNode, ConcreteElement = Self>;

View file

@ -2,10 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::{AtRuleParser, Delimiter, Parser, QualifiedRuleParser, RuleListParser}; use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
use cssparser::{DeclarationListParser, DeclarationParser};
use parser::{ParserContext, log_css_error}; use parser::{ParserContext, log_css_error};
use properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty; use properties::animated_properties::TransitionProperty;
use properties::{PropertyDeclaration, parse_property_declaration_list}; use properties::{PropertyDeclaration, Importance};
use std::sync::Arc; use std::sync::Arc;
/// A number from 1 to 100, indicating the percentage of the animation where /// A number from 1 to 100, indicating the percentage of the animation where
@ -70,28 +72,12 @@ impl KeyframeSelector {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Keyframe { pub struct Keyframe {
pub selector: KeyframeSelector, pub selector: KeyframeSelector,
pub declarations: Arc<Vec<PropertyDeclaration>>,
}
impl Keyframe { /// `!important` is not allowed in keyframe declarations,
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Keyframe, ()> { /// so the second value of these tuples is always `Importance::Normal`.
let percentages = try!(input.parse_until_before(Delimiter::CurlyBracketBlock, |input| { /// But including them enables `compute_style_for_animation_step` to create a `DeclarationBlock`
input.parse_comma_separated(|input| KeyframePercentage::parse(input)) /// by cloning an `Arc<_>` (incrementing a reference count) rather than re-creating a `Vec<_>`.
})); pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
let selector = KeyframeSelector(percentages);
try!(input.expect_curly_bracket_block());
let declarations = input.parse_nested_block(|input| {
Ok(parse_property_declaration_list(context, input))
}).unwrap();
// NB: Important declarations are explicitely ignored in the spec.
Ok(Keyframe {
selector: selector,
declarations: declarations.normal,
})
}
} }
/// A keyframes step value. This can be a synthetised keyframes animation, that /// A keyframes step value. This can be a synthetised keyframes animation, that
@ -101,7 +87,8 @@ impl Keyframe {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesStepValue { pub enum KeyframesStepValue {
Declarations(Arc<Vec<PropertyDeclaration>>), /// See `Keyframe::declarations`s docs about the presence of `Importance`.
Declarations(Arc<Vec<(PropertyDeclaration, Importance)>>),
ComputedValues, ComputedValues,
} }
@ -127,7 +114,7 @@ impl KeyframesStep {
value: KeyframesStepValue) -> Self { value: KeyframesStepValue) -> Self {
let declared_timing_function = match value { let declared_timing_function = match value {
KeyframesStepValue::Declarations(ref declarations) => { KeyframesStepValue::Declarations(ref declarations) => {
declarations.iter().any(|prop_decl| { declarations.iter().any(|&(ref prop_decl, _)| {
match *prop_decl { match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => true, PropertyDeclaration::AnimationTimingFunction(..) => true,
_ => false, _ => false,
@ -167,8 +154,8 @@ fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
let mut ret = vec![]; let mut ret = vec![];
// NB: declarations are already deduplicated, so we don't have to check for // NB: declarations are already deduplicated, so we don't have to check for
// it here. // it here.
for declaration in keyframe.declarations.iter() { for &(ref declaration, _) in keyframe.declarations.iter() {
if let Some(property) = TransitionProperty::from_declaration(&declaration) { if let Some(property) = TransitionProperty::from_declaration(declaration) {
ret.push(property); ret.push(property);
} }
} }
@ -259,12 +246,49 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
fn parse_block(&self, prelude: Self::Prelude, input: &mut Parser) fn parse_block(&self, prelude: Self::Prelude, input: &mut Parser)
-> Result<Self::QualifiedRule, ()> { -> Result<Self::QualifiedRule, ()> {
let mut declarations = Vec::new();
let parser = KeyframeDeclarationParser {
context: self.context,
};
let mut iter = DeclarationListParser::new(input, parser);
while let Some(declaration) = iter.next() {
match declaration {
Ok(d) => declarations.extend(d.into_iter().map(|d| (d, Importance::Normal))),
Err(range) => {
let pos = range.start;
let message = format!("Unsupported keyframe property declaration: '{}'",
iter.input.slice(range));
log_css_error(iter.input, pos, &*message, self.context);
}
}
// `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
}
Ok(Keyframe { Ok(Keyframe {
selector: prelude, selector: prelude,
// FIXME: needs parsing different from parse_property_declaration_list: declarations: Arc::new(declarations),
// https://drafts.csswg.org/css-animations/#keyframes
// Paragraph "The <declaration-list> inside of <keyframe-block> ..."
declarations: parse_property_declaration_list(self.context, input).normal,
}) })
} }
} }
struct KeyframeDeclarationParser<'a, 'b: 'a> {
context: &'a ParserContext<'b>,
}
/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for KeyframeDeclarationParser<'a, 'b> {
type Prelude = ();
type AtRule = Vec<PropertyDeclaration>;
}
impl<'a, 'b> DeclarationParser for KeyframeDeclarationParser<'a, 'b> {
type Declaration = Vec<PropertyDeclaration>;
fn parse_value(&self, name: &str, input: &mut Parser) -> Result<Vec<PropertyDeclaration>, ()> {
let mut results = Vec::new();
match PropertyDeclaration::parse(name, self.context, input, &mut results, true) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}
_ => return Err(())
}
Ok(results)
}
}

View file

@ -58,6 +58,7 @@ extern crate log;
extern crate matches; extern crate matches;
extern crate num_traits; extern crate num_traits;
extern crate ordered_float; extern crate ordered_float;
extern crate quickersort;
extern crate rand; extern crate rand;
extern crate rustc_serialize; extern crate rustc_serialize;
extern crate selectors; extern crate selectors;

View file

@ -14,7 +14,7 @@ use context::{StyleContext, SharedStyleContext};
use data::PrivateStyleData; use data::PrivateStyleData;
use dom::{TElement, TNode, TRestyleDamage, UnsafeNode}; use dom::{TElement, TNode, TRestyleDamage, UnsafeNode};
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
use properties::{ComputedValues, PropertyDeclaration, cascade}; use properties::{ComputedValues, cascade};
use selector_impl::{TheSelectorImpl, PseudoElement}; use selector_impl::{TheSelectorImpl, PseudoElement};
use selector_matching::{DeclarationBlock, Stylist}; use selector_matching::{DeclarationBlock, Stylist};
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
@ -118,15 +118,11 @@ impl<'a> ApplicableDeclarationsCacheQuery<'a> {
impl<'a> PartialEq for ApplicableDeclarationsCacheQuery<'a> { impl<'a> PartialEq for ApplicableDeclarationsCacheQuery<'a> {
fn eq(&self, other: &ApplicableDeclarationsCacheQuery<'a>) -> bool { fn eq(&self, other: &ApplicableDeclarationsCacheQuery<'a>) -> bool {
if self.declarations.len() != other.declarations.len() { self.declarations.len() == other.declarations.len() &&
return false self.declarations.iter().zip(other.declarations).all(|(this, other)| {
} arc_ptr_eq(&this.mixed_declarations, &other.mixed_declarations) &&
for (this, other) in self.declarations.iter().zip(other.declarations) { this.importance == other.importance
if !arc_ptr_eq(&this.declarations, &other.declarations) { })
return false
}
}
true
} }
} }
impl<'a> Eq for ApplicableDeclarationsCacheQuery<'a> {} impl<'a> Eq for ApplicableDeclarationsCacheQuery<'a> {}
@ -143,8 +139,9 @@ impl<'a> Hash for ApplicableDeclarationsCacheQuery<'a> {
for declaration in self.declarations { for declaration in self.declarations {
// Each declaration contians an Arc, which is a stable // Each declaration contians an Arc, which is a stable
// pointer; we use that for hashing and equality. // pointer; we use that for hashing and equality.
let ptr = &*declaration.declarations as *const Vec<PropertyDeclaration>; let ptr: *const Vec<_> = &*declaration.mixed_declarations;
ptr.hash(state); ptr.hash(state);
declaration.importance.hash(state);
} }
} }
} }

View file

@ -65,7 +65,8 @@ class Keyword(object):
class Longhand(object): class Longhand(object):
def __init__(self, style_struct, name, animatable=None, derived_from=None, keyword=None, def __init__(self, style_struct, name, animatable=None, derived_from=None, keyword=None,
predefined_type=None, custom_cascade=False, experimental=False, internal=False, predefined_type=None, custom_cascade=False, experimental=False, internal=False,
need_clone=False, need_index=False, gecko_ffi_name=None, depend_on_viewport_size=False): need_clone=False, need_index=False, gecko_ffi_name=None, depend_on_viewport_size=False,
allowed_in_keyframe_block=True):
self.name = name self.name = name
self.keyword = keyword self.keyword = keyword
self.predefined_type = predefined_type self.predefined_type = predefined_type
@ -80,6 +81,13 @@ class Longhand(object):
self.depend_on_viewport_size = depend_on_viewport_size self.depend_on_viewport_size = depend_on_viewport_size
self.derived_from = (derived_from or "").split() self.derived_from = (derived_from or "").split()
# 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.
self.allowed_in_keyframe_block = allowed_in_keyframe_block \
and allowed_in_keyframe_block != "False"
# This is done like this since just a plain bool argument seemed like # This is done like this since just a plain bool argument seemed like
# really random. # really random.
if animatable is None: if animatable is None:
@ -98,7 +106,8 @@ class Longhand(object):
class Shorthand(object): class Shorthand(object):
def __init__(self, name, sub_properties, experimental=False, internal=False): def __init__(self, name, sub_properties, experimental=False, internal=False,
allowed_in_keyframe_block=True):
self.name = name self.name = name
self.ident = to_rust_ident(name) self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident) self.camel_case = to_camel_case(self.ident)
@ -107,6 +116,13 @@ class Shorthand(object):
self.sub_properties = sub_properties self.sub_properties = sub_properties
self.internal = internal self.internal = internal
# 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.
self.allowed_in_keyframe_block = allowed_in_keyframe_block \
and allowed_in_keyframe_block != "False"
class Method(object): class Method(object):
def __init__(self, name, return_type=None, arg_types=None, is_mut=False): def __init__(self, name, return_type=None, arg_types=None, is_mut=False):
@ -169,15 +185,15 @@ class PropertiesData(object):
if self.product not in products: if self.product not in products:
return return
longand = Longhand(self.current_style_struct, name, **kwargs) longhand = Longhand(self.current_style_struct, name, **kwargs)
self.current_style_struct.longhands.append(longand) self.current_style_struct.longhands.append(longhand)
self.longhands.append(longand) self.longhands.append(longhand)
self.longhands_by_name[name] = longand self.longhands_by_name[name] = longhand
for name in longand.derived_from: for name in longhand.derived_from:
self.derived_longhands.setdefault(name, []).append(longand) self.derived_longhands.setdefault(name, []).append(longhand)
return longand return longhand
def declare_shorthand(self, name, sub_properties, products="gecko servo", *args, **kwargs): def declare_shorthand(self, name, sub_properties, products="gecko servo", *args, **kwargs):
products = products.split() products = products.split()

View file

@ -636,7 +636,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
<%helpers:longhand name="animation-name" <%helpers:longhand name="animation-name"
need_index="True" need_index="True"
animatable="False"> animatable="False",
allowed_in_keyframe_block="False">
use values::computed::ComputedValueAsSpecified; use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage; use values::NoViewportPercentage;
@ -699,7 +700,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
<%helpers:longhand name="animation-duration" <%helpers:longhand name="animation-duration"
need_index="True" need_index="True"
animatable="False"> animatable="False",
allowed_in_keyframe_block="False">
pub use super::transition_duration::computed_value; pub use super::transition_duration::computed_value;
pub use super::transition_duration::{get_initial_value, get_initial_single_value}; pub use super::transition_duration::{get_initial_value, get_initial_single_value};
pub use super::transition_duration::{parse, parse_one}; pub use super::transition_duration::{parse, parse_one};
@ -709,7 +711,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
<%helpers:longhand name="animation-timing-function" <%helpers:longhand name="animation-timing-function"
need_index="True" need_index="True"
animatable="False"> animatable="False",
allowed_in_keyframe_block="False">
pub use super::transition_timing_function::computed_value; pub use super::transition_timing_function::computed_value;
pub use super::transition_timing_function::{get_initial_value, get_initial_single_value}; pub use super::transition_timing_function::{get_initial_value, get_initial_single_value};
pub use super::transition_timing_function::{parse, parse_one}; pub use super::transition_timing_function::{parse, parse_one};
@ -719,7 +722,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
<%helpers:longhand name="animation-iteration-count" <%helpers:longhand name="animation-iteration-count"
need_index="True" need_index="True"
animatable="False"> animatable="False",
allowed_in_keyframe_block="False">
use values::computed::ComputedValueAsSpecified; use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage; use values::NoViewportPercentage;
@ -804,22 +808,28 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
${helpers.keyword_list("animation-direction", ${helpers.keyword_list("animation-direction",
"normal reverse alternate alternate-reverse", "normal reverse alternate alternate-reverse",
need_index=True, need_index=True,
animatable=False)} animatable=False,
allowed_in_keyframe_block=False)}
// animation-play-state is the exception to the rule for allowed_in_keyframe_block:
// https://drafts.csswg.org/css-animations/#keyframes
${helpers.keyword_list("animation-play-state", ${helpers.keyword_list("animation-play-state",
"running paused", "running paused",
need_clone=True, need_clone=True,
need_index=True, need_index=True,
animatable=False)} animatable=False,
allowed_in_keyframe_block=True)}
${helpers.keyword_list("animation-fill-mode", ${helpers.keyword_list("animation-fill-mode",
"none forwards backwards both", "none forwards backwards both",
need_index=True, need_index=True,
animatable=False)} animatable=False,
allowed_in_keyframe_block=False)}
<%helpers:longhand name="animation-delay" <%helpers:longhand name="animation-delay"
need_index="True" need_index="True"
animatable="False"> animatable="False",
allowed_in_keyframe_block="False">
pub use super::transition_duration::computed_value; pub use super::transition_duration::computed_value;
pub use super::transition_duration::{get_initial_value, get_initial_single_value}; pub use super::transition_duration::{get_initial_value, get_initial_single_value};
pub use super::transition_duration::{parse, parse_one}; pub use super::transition_duration::{parse, parse_one};

View file

@ -29,7 +29,7 @@ use computed_values;
#[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide}; #[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide};
use logical_geometry::WritingMode; use logical_geometry::WritingMode;
use parser::{ParserContext, ParserContextExtraData, log_css_error}; use parser::{ParserContext, ParserContextExtraData, log_css_error};
use selectors::matching::DeclarationBlock; use selector_matching::DeclarationBlock;
use stylesheets::Origin; use stylesheets::Origin;
use values::LocalToCss; use values::LocalToCss;
use values::HasViewportPercentage; use values::HasViewportPercentage;
@ -261,32 +261,38 @@ mod property_bit_field {
% endif % endif
% endfor % 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,
}
}
}
use std::iter::{Iterator, Chain, Zip, Rev, Repeat, repeat};
use std::slice;
/// Overridden declarations are skipped. /// Overridden declarations are skipped.
// FIXME (https://github.com/servo/servo/issues/3426) // FIXME (https://github.com/servo/servo/issues/3426)
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PropertyDeclarationBlock { pub struct PropertyDeclarationBlock {
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")] #[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub important: Arc<Vec<PropertyDeclaration>>, pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub normal: Arc<Vec<PropertyDeclaration>>,
}
impl PropertyDeclarationBlock { /// The number of entries in `self.declaration` with `Importance::Normal`
/// Provides an iterator of all declarations, with indication of !important value pub normal_count: u32,
pub fn declarations(&self) -> Chain<
Zip<Rev<slice::Iter<PropertyDeclaration>>, Repeat<bool>>, /// The number of entries in `self.declaration` with `Importance::Important`
Zip<Rev<slice::Iter<PropertyDeclaration>>, Repeat<bool>> pub important_count: u32,
> {
// Declarations are stored in reverse order.
let normal = self.normal.iter().rev().zip(repeat(false));
let important = self.important.iter().rev().zip(repeat(true));
normal.chain(important)
}
} }
impl ToCss for PropertyDeclarationBlock { impl ToCss for PropertyDeclarationBlock {
@ -300,7 +306,7 @@ impl ToCss for PropertyDeclarationBlock {
let mut already_serialized = Vec::new(); let mut already_serialized = Vec::new();
// Step 3 // Step 3
for (declaration, important) in self.declarations() { for &(ref declaration, importance) in &*self.declarations {
// Step 3.1 // Step 3.1
let property = declaration.name(); let property = declaration.name();
@ -314,7 +320,7 @@ impl ToCss for PropertyDeclarationBlock {
if !shorthands.is_empty() { if !shorthands.is_empty() {
// Step 3.3.1 // Step 3.3.1
let mut longhands = self.declarations() let mut longhands = self.declarations.iter()
.filter(|d| !already_serialized.contains(&d.0.name())) .filter(|d| !already_serialized.contains(&d.0.name()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -326,11 +332,11 @@ impl ToCss for PropertyDeclarationBlock {
let mut current_longhands = Vec::new(); let mut current_longhands = Vec::new();
let mut important_count = 0; let mut important_count = 0;
for &(longhand, longhand_important) in longhands.iter() { for &&(ref longhand, longhand_importance) in longhands.iter() {
let longhand_name = longhand.name(); let longhand_name = longhand.name();
if properties.iter().any(|p| &longhand_name == *p) { if properties.iter().any(|p| &longhand_name == *p) {
current_longhands.push(longhand); current_longhands.push(longhand);
if longhand_important { if longhand_importance.important() {
important_count += 1; important_count += 1;
} }
} }
@ -370,7 +376,7 @@ impl ToCss for PropertyDeclarationBlock {
for current_longhand in current_longhands { for current_longhand in current_longhands {
// Substep 9 // Substep 9
already_serialized.push(current_longhand.name()); already_serialized.push(current_longhand.name());
let index_to_remove = longhands.iter().position(|l| l.0 == current_longhand); let index_to_remove = longhands.iter().position(|l| l.0 == *current_longhand);
if let Some(index) = index_to_remove { if let Some(index) = index_to_remove {
// Substep 10 // Substep 10
longhands.remove(index); longhands.remove(index);
@ -396,7 +402,7 @@ impl ToCss for PropertyDeclarationBlock {
dest, dest,
&property.to_string(), &property.to_string(),
AppendableValue::Declaration(declaration), AppendableValue::Declaration(declaration),
important, importance,
&mut is_first_serialization)); &mut is_first_serialization));
// Step 3.3.8 // Step 3.3.8
@ -430,7 +436,7 @@ fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool
fn append_declaration_value<'a, W, I> fn append_declaration_value<'a, W, I>
(dest: &mut W, (dest: &mut W,
appendable_value: AppendableValue<'a, I>, appendable_value: AppendableValue<'a, I>,
is_important: bool) importance: Importance)
-> fmt::Result -> fmt::Result
where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> {
match appendable_value { match appendable_value {
@ -445,7 +451,7 @@ fn append_declaration_value<'a, W, I>
} }
} }
if is_important { if importance.important() {
try!(write!(dest, " !important")); try!(write!(dest, " !important"));
} }
@ -455,7 +461,7 @@ fn append_declaration_value<'a, W, I>
fn append_serialization<'a, W, I>(dest: &mut W, fn append_serialization<'a, W, I>(dest: &mut W,
property_name: &str, property_name: &str,
appendable_value: AppendableValue<'a, I>, appendable_value: AppendableValue<'a, I>,
is_important: bool, importance: Importance,
is_first_serialization: &mut bool) is_first_serialization: &mut bool)
-> fmt::Result -> fmt::Result
where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> {
@ -465,7 +471,7 @@ fn append_serialization<'a, W, I>(dest: &mut W,
// Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal // 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 // values, they no longer use the shared property name "overflow" and must be handled differently
if shorthands::is_overflow_shorthand(&appendable_value) { if shorthands::is_overflow_shorthand(&appendable_value) {
return append_declaration_value(dest, appendable_value, is_important); return append_declaration_value(dest, appendable_value, importance);
} }
try!(write!(dest, "{}:", property_name)); try!(write!(dest, "{}:", property_name));
@ -484,7 +490,7 @@ fn append_serialization<'a, W, I>(dest: &mut W,
&AppendableValue::DeclarationsForShorthand(..) => try!(write!(dest, " ")) &AppendableValue::DeclarationsForShorthand(..) => try!(write!(dest, " "))
} }
try!(append_declaration_value(dest, appendable_value, is_important)); try!(append_declaration_value(dest, appendable_value, importance));
write!(dest, ";") write!(dest, ";")
} }
@ -500,7 +506,7 @@ pub fn parse_one_declaration(name: &str, input: &str, base_url: &Url, error_repo
-> Result<Vec<PropertyDeclaration>, ()> { -> Result<Vec<PropertyDeclaration>, ()> {
let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data); let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data);
let mut results = vec![]; let mut results = vec![];
match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results) { match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results, false) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results), PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results),
_ => Err(()) _ => Err(())
} }
@ -514,43 +520,49 @@ struct PropertyDeclarationParser<'a, 'b: 'a> {
/// Default methods reject all at rules. /// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> {
type Prelude = (); type Prelude = ();
type AtRule = (Vec<PropertyDeclaration>, bool); type AtRule = (Vec<PropertyDeclaration>, Importance);
} }
impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> { impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
type Declaration = (Vec<PropertyDeclaration>, bool); type Declaration = (Vec<PropertyDeclaration>, Importance);
fn parse_value(&self, name: &str, input: &mut Parser) -> Result<(Vec<PropertyDeclaration>, bool), ()> { fn parse_value(&self, name: &str, input: &mut Parser)
-> Result<(Vec<PropertyDeclaration>, Importance), ()> {
let mut results = vec![]; let mut results = vec![];
try!(input.parse_until_before(Delimiter::Bang, |input| { try!(input.parse_until_before(Delimiter::Bang, |input| {
match PropertyDeclaration::parse(name, self.context, input, &mut results) { match PropertyDeclaration::parse(name, self.context, input, &mut results, false) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(()), PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(()),
_ => Err(()) _ => Err(())
} }
})); }));
let important = input.try(parse_important).is_ok(); let importance = match input.try(parse_important) {
Ok((results, important)) Ok(()) => Importance::Important,
Err(()) => Importance::Normal,
};
Ok((results, importance))
} }
} }
pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser) pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser)
-> PropertyDeclarationBlock { -> PropertyDeclarationBlock {
let mut important_declarations = Vec::new(); let mut declarations = Vec::new();
let mut normal_declarations = Vec::new(); let mut normal_count = 0;
let mut important_count = 0;
let parser = PropertyDeclarationParser { let parser = PropertyDeclarationParser {
context: context, context: context,
}; };
let mut iter = DeclarationListParser::new(input, parser); let mut iter = DeclarationListParser::new(input, parser);
while let Some(declaration) = iter.next() { while let Some(declaration) = iter.next() {
match declaration { match declaration {
Ok((results, important)) => { Ok((results, importance)) => {
if important { if importance.important() {
important_declarations.extend(results); important_count += results.len() as u32;
} else { } else {
normal_declarations.extend(results); normal_count += results.len() as u32;
} }
declarations.extend(results.into_iter().map(|d| (d, importance)))
} }
Err(range) => { Err(range) => {
let pos = range.start; let pos = range.start;
@ -560,46 +572,90 @@ pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Pars
} }
} }
} }
PropertyDeclarationBlock { let mut block = PropertyDeclarationBlock {
important: Arc::new(deduplicate_property_declarations(important_declarations)), declarations: Arc::new(declarations),
normal: Arc::new(deduplicate_property_declarations(normal_declarations)), normal_count: normal_count,
} 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) {
let mut deduplicated = Vec::new();
let mut seen_normal = PropertyBitField::new();
let mut seen_important = PropertyBitField::new();
let mut seen_custom_normal = Vec::new();
let mut seen_custom_important = Vec::new();
/// Only keep the last declaration for any given property. let declarations = Arc::get_mut(&mut block.declarations).unwrap();
/// The input is in source order, output in reverse source order. for (declaration, importance) in declarations.drain(..).rev() {
fn deduplicate_property_declarations(declarations: Vec<PropertyDeclaration>)
-> Vec<PropertyDeclaration> {
let mut deduplicated = vec![];
let mut seen = PropertyBitField::new();
let mut seen_custom = Vec::new();
for declaration in declarations.into_iter().rev() {
match declaration { match declaration {
% for property in data.longhands: % for property in data.longhands:
PropertyDeclaration::${property.camel_case}(..) => { PropertyDeclaration::${property.camel_case}(..) => {
% if not property.derived_from: % if not property.derived_from:
if seen.get_${property.ident}() { if importance.important() {
continue if seen_important.get_${property.ident}() {
block.important_count -= 1;
continue
}
if seen_normal.get_${property.ident}() {
remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::${property.camel_case}(..), _))
});
block.normal_count -= 1;
}
seen_important.set_${property.ident}()
} else {
if seen_normal.get_${property.ident}() ||
seen_important.get_${property.ident}() {
block.normal_count -= 1;
continue
}
seen_normal.set_${property.ident}()
} }
seen.set_${property.ident}()
% else: % else:
unreachable!(); unreachable!();
% endif % endif
}, },
% endfor % endfor
PropertyDeclaration::Custom(ref name, _) => { PropertyDeclaration::Custom(ref name, _) => {
if seen_custom.contains(name) { if importance.important() {
continue if seen_custom_important.contains(name) {
block.important_count -= 1;
continue
}
if seen_custom_normal.contains(name) {
remove_one(&mut deduplicated, |d| {
matches!(d, &(PropertyDeclaration::Custom(ref n, _), _) if n == name)
});
block.normal_count -= 1;
}
seen_custom_important.push(name.clone())
} else {
if seen_custom_normal.contains(name) ||
seen_custom_important.contains(name) {
block.normal_count -= 1;
continue
}
seen_custom_normal.push(name.clone())
} }
seen_custom.push(name.clone())
} }
} }
deduplicated.push(declaration) deduplicated.push((declaration, importance))
} }
deduplicated deduplicated.reverse();
*declarations = deduplicated;
} }
#[inline]
fn remove_one<T, F: FnMut(&T) -> bool>(v: &mut Vec<T>, mut remove_this: F) {
let previous_len = v.len();
v.retain(|x| !remove_this(x));
debug_assert_eq!(v.len(), previous_len - 1);
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CSSWideKeyword { pub enum CSSWideKeyword {
@ -675,11 +731,11 @@ impl Shorthand {
} }
/// Serializes possible shorthand value to String. /// Serializes possible shorthand value to String.
pub fn serialize_shorthand_value_to_string<'a, I>(self, declarations: I, is_important: bool) -> String pub fn serialize_shorthand_value_to_string<'a, I>(self, declarations: I, importance: Importance) -> String
where I: Iterator<Item=&'a PropertyDeclaration> + Clone { where I: Iterator<Item=&'a PropertyDeclaration> + Clone {
let appendable_value = self.get_shorthand_appendable_value(declarations).unwrap(); let appendable_value = self.get_shorthand_appendable_value(declarations).unwrap();
let mut result = String::new(); let mut result = String::new();
append_declaration_value(&mut result, appendable_value, is_important).unwrap(); append_declaration_value(&mut result, appendable_value, importance).unwrap();
result result
} }
@ -700,7 +756,7 @@ impl Shorthand {
dest, dest,
property_name, property_name,
appendable_value, appendable_value,
false, Importance::Normal,
is_first_serialization is_first_serialization
).and_then(|_| Ok(true)) ).and_then(|_| Ok(true))
} }
@ -782,7 +838,7 @@ impl<T: ToCss> ToCss for DeclaredValue<T> {
} }
} }
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum PropertyDeclaration { pub enum PropertyDeclaration {
% for property in data.longhands: % for property in data.longhands:
@ -811,6 +867,7 @@ pub enum PropertyDeclarationParseResult {
UnknownProperty, UnknownProperty,
ExperimentalProperty, ExperimentalProperty,
InvalidValue, InvalidValue,
AnimationPropertyInKeyframeBlock,
ValidOrIgnoredDeclaration, ValidOrIgnoredDeclaration,
} }
@ -845,6 +902,24 @@ impl fmt::Display for PropertyDeclarationName {
} }
} }
} }
impl fmt::Debug for PropertyDeclaration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "{}: ", self.name()));
match *self {
% for property in data.longhands:
% if not property.derived_from:
PropertyDeclaration::${property.camel_case}(ref value) => value.to_css(f),
% endif
% endfor
PropertyDeclaration::Custom(_, ref value) => value.to_css(f),
% if any(property.derived_from for property in data.longhands):
_ => Err(fmt::Error),
% endif
}
}
}
impl ToCss for PropertyDeclaration { impl ToCss for PropertyDeclaration {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self { match *self {
@ -963,8 +1038,16 @@ 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.
pub fn parse(name: &str, context: &ParserContext, input: &mut Parser, pub fn parse(name: &str, context: &ParserContext, input: &mut Parser,
result_list: &mut Vec<PropertyDeclaration>) -> PropertyDeclarationParseResult { result_list: &mut Vec<PropertyDeclaration>,
in_keyframe_block: bool)
-> PropertyDeclarationParseResult {
if let Ok(name) = ::custom_properties::parse_name(name) { if let Ok(name) = ::custom_properties::parse_name(name) {
let value = match input.try(CSSWideKeyword::parse) { let value = match input.try(CSSWideKeyword::parse) {
Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited
@ -982,6 +1065,11 @@ impl PropertyDeclaration {
% for property in data.longhands: % for property in data.longhands:
% if not property.derived_from: % if not property.derived_from:
"${property.name}" => { "${property.name}" => {
% if not property.allowed_in_keyframe_block:
if in_keyframe_block {
return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock
}
% endif
% if property.internal: % if property.internal:
if context.stylesheet_origin != Origin::UserAgent { if context.stylesheet_origin != Origin::UserAgent {
return PropertyDeclarationParseResult::UnknownProperty return PropertyDeclarationParseResult::UnknownProperty
@ -1007,6 +1095,11 @@ impl PropertyDeclaration {
% endfor % endfor
% for shorthand in data.shorthands: % for shorthand in data.shorthands:
"${shorthand.name}" => { "${shorthand.name}" => {
% if not shorthand.allowed_in_keyframe_block:
if in_keyframe_block {
return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock
}
% endif
% if shorthand.internal: % if shorthand.internal:
if context.stylesheet_origin != Origin::UserAgent { if context.stylesheet_origin != Origin::UserAgent {
return PropertyDeclarationParseResult::UnknownProperty return PropertyDeclarationParseResult::UnknownProperty
@ -1612,7 +1705,7 @@ mod lazy_static_module {
#[allow(unused_mut, unused_imports)] #[allow(unused_mut, unused_imports)]
fn cascade_with_cached_declarations( fn cascade_with_cached_declarations(
viewport_size: Size2D<Au>, viewport_size: Size2D<Au>,
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>], applicable_declarations: &[DeclarationBlock],
shareable: bool, shareable: bool,
parent_style: &ComputedValues, parent_style: &ComputedValues,
cached_style: &ComputedValues, cached_style: &ComputedValues,
@ -1643,8 +1736,7 @@ fn cascade_with_cached_declarations(
// Declaration blocks are stored in increasing precedence order, // Declaration blocks are stored in increasing precedence order,
// we want them in decreasing order here. // we want them in decreasing order here.
for sub_list in applicable_declarations.iter().rev() { for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order. for declaration in sub_list.iter().rev() {
for declaration in sub_list.declarations.iter() {
match *declaration { match *declaration {
% for style_struct in data.active_style_structs(): % for style_struct in data.active_style_structs():
% for property in style_struct.longhands: % for property in style_struct.longhands:
@ -1758,7 +1850,7 @@ static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
/// ///
/// Returns the computed values and a boolean indicating whether the result is cacheable. /// Returns the computed values and a boolean indicating whether the result is cacheable.
pub fn cascade(viewport_size: Size2D<Au>, pub fn cascade(viewport_size: Size2D<Au>,
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>], applicable_declarations: &[DeclarationBlock],
shareable: bool, shareable: bool,
parent_style: Option<<&ComputedValues>, parent_style: Option<<&ComputedValues>,
cached_style: Option<<&ComputedValues>, cached_style: Option<<&ComputedValues>,
@ -1775,8 +1867,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
let mut custom_properties = None; let mut custom_properties = None;
let mut seen_custom = HashSet::new(); let mut seen_custom = HashSet::new();
for sub_list in applicable_declarations.iter().rev() { for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order. for declaration in sub_list.iter().rev() {
for declaration in sub_list.declarations.iter() {
match *declaration { match *declaration {
PropertyDeclaration::Custom(ref name, ref value) => { PropertyDeclaration::Custom(ref name, ref value) => {
::custom_properties::cascade( ::custom_properties::cascade(
@ -1834,8 +1925,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
ComputedValues::do_cascade_property(|cascade_property| { ComputedValues::do_cascade_property(|cascade_property| {
% for category_to_cascade_now in ["early", "other"]: % for category_to_cascade_now in ["early", "other"]:
for sub_list in applicable_declarations.iter().rev() { for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order. for declaration in sub_list.iter().rev() {
for declaration in sub_list.declarations.iter() {
if let PropertyDeclaration::Custom(..) = *declaration { if let PropertyDeclaration::Custom(..) = *declaration {
continue continue
} }

View file

@ -152,7 +152,8 @@ macro_rules! try_parse_one {
sub_properties="animation-name animation-duration sub_properties="animation-name animation-duration
animation-timing-function animation-delay animation-timing-function animation-delay
animation-iteration-count animation-direction animation-iteration-count animation-direction
animation-fill-mode animation-play-state"> animation-fill-mode animation-play-state"
allowed_in_keyframe_block="False">
use properties::longhands::{animation_name, animation_duration, animation_timing_function}; use properties::longhands::{animation_name, animation_duration, animation_timing_function};
use properties::longhands::{animation_delay, animation_iteration_count, animation_direction}; use properties::longhands::{animation_delay, animation_iteration_count, animation_direction};
use properties::longhands::{animation_fill_mode, animation_play_state}; use properties::longhands::{animation_fill_mode, animation_play_state};

View file

@ -9,26 +9,30 @@ use element_state::*;
use error_reporting::StdoutErrorReporter; use error_reporting::StdoutErrorReporter;
use keyframes::KeyframesAnimation; use keyframes::KeyframesAnimation;
use media_queries::{Device, MediaType}; use media_queries::{Device, MediaType};
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock, ComputedValues}; use properties::{self, PropertyDeclaration, PropertyDeclarationBlock, ComputedValues, Importance};
use quickersort::sort_by;
use restyle_hints::{RestyleHint, DependencySet}; use restyle_hints::{RestyleHint, DependencySet};
use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement}; use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement};
use selectors::Element; use selectors::Element;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
use selectors::matching::DeclarationBlock as GenericDeclarationBlock;
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS}; use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
use selectors::matching::{Rule, SelectorMap, StyleRelations}; use selectors::matching::{StyleRelations, matches_complex_selector};
use selectors::parser::Selector; use selectors::parser::{Selector, SimpleSelector, LocalName, ComplexSelector};
use sink::Push; use sink::Push;
use smallvec::VecLike; use smallvec::VecLike;
use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use std::hash::BuildHasherDefault; use std::hash::BuildHasherDefault;
use std::hash::Hash;
use std::slice;
use std::sync::Arc; use std::sync::Arc;
use string_cache::Atom; use string_cache::Atom;
use style_traits::viewport::ViewportConstraints; use style_traits::viewport::ViewportConstraints;
use stylesheets::{CSSRule, CSSRuleIteratorExt, Origin, Stylesheet}; use stylesheets::{CSSRule, CSSRuleIteratorExt, Origin, Stylesheet};
use viewport::{MaybeNew, ViewportRuleCascade}; use viewport::{MaybeNew, ViewportRuleCascade};
pub type DeclarationBlock = GenericDeclarationBlock<Vec<PropertyDeclaration>>; pub type FnvHashMap<K, V> = HashMap<K, V, BuildHasherDefault<::fnv::FnvHasher>>;
/// This structure holds all the selectors and device characteristics /// This structure holds all the selectors and device characteristics
/// for a given document. The selectors are converted into `Rule`s /// for a given document. The selectors are converted into `Rule`s
@ -59,19 +63,15 @@ pub struct Stylist {
/// The selector maps corresponding to a given pseudo-element /// The selector maps corresponding to a given pseudo-element
/// (depending on the implementation) /// (depending on the implementation)
pseudos_map: HashMap<PseudoElement, pseudos_map: FnvHashMap<PseudoElement, PerPseudoElementSelectorMap>,
PerPseudoElementSelectorMap,
BuildHasherDefault<::fnv::FnvHasher>>,
/// A map with all the animations indexed by name. /// A map with all the animations indexed by name.
animations: HashMap<Atom, KeyframesAnimation>, animations: FnvHashMap<Atom, KeyframesAnimation>,
/// Applicable declarations for a given non-eagerly cascaded pseudo-element. /// Applicable declarations for a given non-eagerly cascaded pseudo-element.
/// These are eagerly computed once, and then used to resolve the new /// These are eagerly computed once, and then used to resolve the new
/// computed values on the fly on layout. /// computed values on the fly on layout.
precomputed_pseudo_element_decls: HashMap<PseudoElement, precomputed_pseudo_element_decls: FnvHashMap<PseudoElement, Vec<DeclarationBlock>>,
Vec<DeclarationBlock>,
BuildHasherDefault<::fnv::FnvHasher>>,
rules_source_order: usize, rules_source_order: usize,
@ -96,9 +96,9 @@ impl Stylist {
quirks_mode: false, quirks_mode: false,
element_map: PerPseudoElementSelectorMap::new(), element_map: PerPseudoElementSelectorMap::new(),
pseudos_map: HashMap::with_hasher(Default::default()), pseudos_map: Default::default(),
animations: HashMap::with_hasher(Default::default()), animations: Default::default(),
precomputed_pseudo_element_decls: HashMap::with_hasher(Default::default()), precomputed_pseudo_element_decls: Default::default(),
rules_source_order: 0, rules_source_order: 0,
state_deps: DependencySet::new(), state_deps: DependencySet::new(),
@ -122,13 +122,13 @@ impl Stylist {
} }
self.element_map = PerPseudoElementSelectorMap::new(); self.element_map = PerPseudoElementSelectorMap::new();
self.pseudos_map = HashMap::with_hasher(Default::default()); self.pseudos_map = Default::default();
self.animations = HashMap::with_hasher(Default::default()); self.animations = Default::default();
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| { TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new()); self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
}); });
self.precomputed_pseudo_element_decls = HashMap::with_hasher(Default::default()); self.precomputed_pseudo_element_decls = Default::default();
self.rules_source_order = 0; self.rules_source_order = 0;
self.state_deps.clear(); self.state_deps.clear();
@ -162,8 +162,8 @@ impl Stylist {
// Take apart the StyleRule into individual Rules and insert // Take apart the StyleRule into individual Rules and insert
// them into the SelectorMap of that priority. // them into the SelectorMap of that priority.
macro_rules! append( macro_rules! append(
($style_rule: ident, $priority: ident) => { ($style_rule: ident, $priority: ident, $importance: expr, $count: ident) => {
if !$style_rule.declarations.$priority.is_empty() { if $style_rule.declarations.$count > 0 {
for selector in &$style_rule.selectors { for selector in &$style_rule.selectors {
let map = if let Some(ref pseudo) = selector.pseudo_element { let map = if let Some(ref pseudo) = selector.pseudo_element {
self.pseudos_map self.pseudos_map
@ -178,7 +178,8 @@ impl Stylist {
selector: selector.complex_selector.clone(), selector: selector.complex_selector.clone(),
declarations: DeclarationBlock { declarations: DeclarationBlock {
specificity: selector.specificity, specificity: selector.specificity,
declarations: $style_rule.declarations.$priority.clone(), mixed_declarations: $style_rule.declarations.declarations.clone(),
importance: $importance,
source_order: rules_source_order, source_order: rules_source_order,
}, },
}); });
@ -190,8 +191,8 @@ impl Stylist {
for rule in stylesheet.effective_rules(&self.device) { for rule in stylesheet.effective_rules(&self.device) {
match *rule { match *rule {
CSSRule::Style(ref style_rule) => { CSSRule::Style(ref style_rule) => {
append!(style_rule, normal); append!(style_rule, normal, Importance::Normal, normal_count);
append!(style_rule, important); append!(style_rule, important, Importance::Important, important_count);
rules_source_order += 1; rules_source_order += 1;
for selector in &style_rule.selectors { for selector in &style_rule.selectors {
@ -275,6 +276,7 @@ impl Stylist {
parent: &Arc<ComputedValues>) parent: &Arc<ComputedValues>)
-> Option<Arc<ComputedValues>> -> Option<Arc<ComputedValues>>
where E: Element<Impl=TheSelectorImpl> + where E: Element<Impl=TheSelectorImpl> +
fmt::Debug +
PresentationalHintsSynthetizer PresentationalHintsSynthetizer
{ {
debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy()); debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy());
@ -343,6 +345,7 @@ impl Stylist {
pseudo_element: Option<&PseudoElement>, pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V) -> StyleRelations applicable_declarations: &mut V) -> StyleRelations
where E: Element<Impl=TheSelectorImpl> + where E: Element<Impl=TheSelectorImpl> +
fmt::Debug +
PresentationalHintsSynthetizer, PresentationalHintsSynthetizer,
V: Push<DeclarationBlock> + VecLike<DeclarationBlock> V: Push<DeclarationBlock> + VecLike<DeclarationBlock>
{ {
@ -391,10 +394,14 @@ impl Stylist {
// Step 4: Normal style attributes. // Step 4: Normal style attributes.
if let Some(ref sa) = style_attribute { if let Some(ref sa) = style_attribute {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE; if sa.normal_count > 0 {
Push::push( relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
applicable_declarations, Push::push(
GenericDeclarationBlock::from_declarations(sa.normal.clone())); applicable_declarations,
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Normal));
}
} }
debug!("style attr: {:?}", relations); debug!("style attr: {:?}", relations);
@ -409,9 +416,14 @@ impl Stylist {
// Step 6: `!important` style attributes. // Step 6: `!important` style attributes.
if let Some(ref sa) = style_attribute { if let Some(ref sa) = style_attribute {
Push::push( if sa.important_count > 0 {
applicable_declarations, relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
GenericDeclarationBlock::from_declarations(sa.important.clone())); Push::push(
applicable_declarations,
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Important));
}
} }
debug!("style attr important: {:?}", relations); debug!("style attr important: {:?}", relations);
@ -442,7 +454,7 @@ impl Stylist {
} }
#[inline] #[inline]
pub fn animations(&self) -> &HashMap<Atom, KeyframesAnimation> { pub fn animations(&self) -> &FnvHashMap<Atom, KeyframesAnimation> {
&self.animations &self.animations
} }
@ -525,12 +537,12 @@ impl Stylist {
/// Map that contains the CSS rules for a given origin. /// Map that contains the CSS rules for a given origin.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
struct PerOriginSelectorMap { struct PerOriginSelectorMap {
/// Rules that contains at least one property declararion with /// Rules that contains at least one property declaration with
/// normal importance. /// normal importance.
normal: SelectorMap<Vec<PropertyDeclaration>, TheSelectorImpl>, normal: SelectorMap,
/// Rules that contains at least one property declararion with /// Rules that contains at least one property declaration with
/// !important. /// !important.
important: SelectorMap<Vec<PropertyDeclaration>, TheSelectorImpl>, important: SelectorMap,
} }
impl PerOriginSelectorMap { impl PerOriginSelectorMap {
@ -574,3 +586,318 @@ impl PerPseudoElementSelectorMap {
} }
} }
} }
/// Map element data to Rules whose last simple selector starts with them.
///
/// e.g.,
/// "p > img" would go into the set of Rules corresponding to the
/// element "img"
/// "a .foo .bar.baz" would go into the set of Rules corresponding to
/// the class "bar"
///
/// Because we match Rules right-to-left (i.e., moving up the tree
/// from an element), we need to compare the last simple selector in the
/// Rule with the element.
///
/// So, if an element has ID "id1" and classes "foo" and "bar", then all
/// the rules it matches will have their last simple selector starting
/// either with "#id1" or with ".foo" or with ".bar".
///
/// Hence, the union of the rules keyed on each of element's classes, ID,
/// element name, etc. will contain the Rules that actually match that
/// element.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SelectorMap {
// TODO: Tune the initial capacity of the HashMap
pub id_hash: FnvHashMap<Atom, Vec<Rule>>,
pub class_hash: FnvHashMap<Atom, Vec<Rule>>,
pub local_name_hash: FnvHashMap<Atom, Vec<Rule>>,
/// Same as local_name_hash, but keys are lower-cased.
/// For HTML elements in HTML documents.
pub lower_local_name_hash: FnvHashMap<Atom, Vec<Rule>>,
/// Rules that don't have ID, class, or element selectors.
pub other_rules: Vec<Rule>,
/// Whether this hash is empty.
pub empty: bool,
}
#[inline]
fn sort_by_key<T, F: Fn(&T) -> K, K: Ord>(v: &mut [T], f: F) {
sort_by(v, &|a, b| f(a).cmp(&f(b)))
}
impl SelectorMap {
pub fn new() -> Self {
SelectorMap {
id_hash: HashMap::default(),
class_hash: HashMap::default(),
local_name_hash: HashMap::default(),
lower_local_name_hash: HashMap::default(),
other_rules: Vec::new(),
empty: true,
}
}
/// Append to `rule_list` all Rules in `self` that match element.
///
/// Extract matching rules as per element's ID, classes, tag name, etc..
/// Sort the Rules at the end to maintain cascading order.
pub fn get_all_matching_rules<E, V>(&self,
element: &E,
parent_bf: Option<&BloomFilter>,
matching_rules_list: &mut V,
relations: &mut StyleRelations)
where E: Element<Impl=TheSelectorImpl>,
V: VecLike<DeclarationBlock>
{
if self.empty {
return
}
// At the end, we're going to sort the rules that we added, so remember where we began.
let init_len = matching_rules_list.len();
if let Some(id) = element.get_id() {
SelectorMap::get_matching_rules_from_hash(element,
parent_bf,
&self.id_hash,
&id,
matching_rules_list,
relations)
}
element.each_class(|class| {
SelectorMap::get_matching_rules_from_hash(element,
parent_bf,
&self.class_hash,
class,
matching_rules_list,
relations);
});
let local_name_hash = if element.is_html_element_in_html_document() {
&self.lower_local_name_hash
} else {
&self.local_name_hash
};
SelectorMap::get_matching_rules_from_hash(element,
parent_bf,
local_name_hash,
element.get_local_name(),
matching_rules_list,
relations);
SelectorMap::get_matching_rules(element,
parent_bf,
&self.other_rules,
matching_rules_list,
relations);
// Sort only the rules we just added.
sort_by_key(&mut matching_rules_list[init_len..],
|rule| (rule.specificity, rule.source_order));
}
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
/// `self` sorted by specifity and source order.
pub fn get_universal_rules<V>(&self,
matching_rules_list: &mut V)
where V: VecLike<DeclarationBlock>
{
if self.empty {
return
}
let init_len = matching_rules_list.len();
for rule in self.other_rules.iter() {
if rule.selector.compound_selector.is_empty() &&
rule.selector.next.is_none() {
matching_rules_list.push(rule.declarations.clone());
}
}
sort_by_key(&mut matching_rules_list[init_len..],
|rule| (rule.specificity, rule.source_order));
}
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector>(
element: &E,
parent_bf: Option<&BloomFilter>,
hash: &FnvHashMap<Str, Vec<Rule>>,
key: &BorrowedStr,
matching_rules: &mut Vector,
relations: &mut StyleRelations)
where E: Element<Impl=TheSelectorImpl>,
Str: Borrow<BorrowedStr> + Eq + Hash,
BorrowedStr: Eq + Hash,
Vector: VecLike<DeclarationBlock>
{
if let Some(rules) = hash.get(key) {
SelectorMap::get_matching_rules(element,
parent_bf,
rules,
matching_rules,
relations)
}
}
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
fn get_matching_rules<E, V>(element: &E,
parent_bf: Option<&BloomFilter>,
rules: &[Rule],
matching_rules: &mut V,
relations: &mut StyleRelations)
where E: Element<Impl=TheSelectorImpl>,
V: VecLike<DeclarationBlock>
{
for rule in rules.iter() {
if matches_complex_selector(&*rule.selector,
element, parent_bf, relations) {
matching_rules.push(rule.declarations.clone());
}
}
}
/// Insert rule into the correct hash.
/// Order in which to try: id_hash, class_hash, local_name_hash, other_rules.
pub fn insert(&mut self, rule: Rule) {
self.empty = false;
if let Some(id_name) = SelectorMap::get_id_name(&rule) {
find_push(&mut self.id_hash, id_name, rule);
return;
}
if let Some(class_name) = SelectorMap::get_class_name(&rule) {
find_push(&mut self.class_hash, class_name, rule);
return;
}
if let Some(LocalName { name, lower_name }) = SelectorMap::get_local_name(&rule) {
find_push(&mut self.local_name_hash, name, rule.clone());
find_push(&mut self.lower_local_name_hash, lower_name, rule);
return;
}
self.other_rules.push(rule);
}
/// Retrieve the first ID name in Rule, or None otherwise.
pub fn get_id_name(rule: &Rule) -> Option<Atom> {
for ss in &rule.selector.compound_selector {
// TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode.
if let SimpleSelector::ID(ref id) = *ss {
return Some(id.clone());
}
}
None
}
/// Retrieve the FIRST class name in Rule, or None otherwise.
pub fn get_class_name(rule: &Rule) -> Option<Atom> {
for ss in &rule.selector.compound_selector {
// TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode.
if let SimpleSelector::Class(ref class) = *ss {
return Some(class.clone());
}
}
None
}
/// Retrieve the name if it is a type selector, or None otherwise.
pub fn get_local_name(rule: &Rule) -> Option<LocalName<TheSelectorImpl>> {
for ss in &rule.selector.compound_selector {
if let SimpleSelector::LocalName(ref n) = *ss {
return Some(LocalName {
name: n.name.clone(),
lower_name: n.lower_name.clone(),
})
}
}
None
}
}
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone)]
pub struct Rule {
// This is an Arc because Rule will essentially be cloned for every element
// that it matches. Selector contains an owned vector (through
// ComplexSelector) and we want to avoid the allocation.
pub selector: Arc<ComplexSelector<TheSelectorImpl>>,
pub declarations: DeclarationBlock,
}
/// A property declaration together with its precedence among rules of equal specificity so that
/// we can sort them.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug, Clone)]
pub struct DeclarationBlock {
/// Contains declarations of either importance, but only those of self.importance are relevant.
/// Use DeclarationBlock::iter
pub mixed_declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
pub importance: Importance,
pub source_order: usize,
pub specificity: u32,
}
impl DeclarationBlock {
#[inline]
pub fn from_declarations(declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
importance: Importance)
-> Self {
DeclarationBlock {
mixed_declarations: declarations,
importance: importance,
source_order: 0,
specificity: 0,
}
}
pub fn iter(&self) -> DeclarationBlockIter {
DeclarationBlockIter {
iter: self.mixed_declarations.iter(),
importance: self.importance,
}
}
}
pub struct DeclarationBlockIter<'a> {
iter: slice::Iter<'a, (PropertyDeclaration, Importance)>,
importance: Importance,
}
impl<'a> Iterator for DeclarationBlockIter<'a> {
type Item = &'a PropertyDeclaration;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
while let Some(&(ref declaration, importance)) = self.iter.next() {
if importance == self.importance {
return Some(declaration)
}
}
None
}
}
impl<'a> DoubleEndedIterator for DeclarationBlockIter<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
while let Some(&(ref declaration, importance)) = self.iter.next_back() {
if importance == self.importance {
return Some(declaration)
}
}
None
}
}
fn find_push<Str: Eq + Hash>(map: &mut FnvHashMap<Str, Vec<Rule>>, key: Str, value: Rule) {
map.entry(key).or_insert_with(Vec::new).push(value)
}

View file

@ -149,8 +149,8 @@ that you didn't find it here so it can be added :)
[mdn-pseudo-after]: https://developer.mozilla.org/en/docs/Web/CSS/::after [mdn-pseudo-after]: https://developer.mozilla.org/en/docs/Web/CSS/::after
[mdn-pseudo-selection]: https://developer.mozilla.org/en/docs/Web/CSS/::selection [mdn-pseudo-selection]: https://developer.mozilla.org/en/docs/Web/CSS/::selection
[stylist]: http://doc.servo.org/style/selector_matching/struct.Stylist.html [stylist]: http://doc.servo.org/style/selector_matching/struct.Stylist.html
[selectors-selectormap]: http://doc.servo.org/selectors/matching/struct.SelectorMap.html [selectors-selectormap]: http://doc.servo.org/style/selector_matching/struct.SelectorMap.html
[selectors-rule]: http://doc.servo.org/selectors/matching/struct.Rule.html [selectors-rule]: http://doc.servo.org/style/selector_matching/struct.Rule.html
[per-pseudo-selectormap]: http://doc.servo.org/style/selector_matching/struct.PerPseudoElementSelectorMap.html [per-pseudo-selectormap]: http://doc.servo.org/style/selector_matching/struct.PerPseudoElementSelectorMap.html
[per-origin-selectormap]: http://doc.servo.org/style/selector_matching/struct.PerOriginSelectorMap.html [per-origin-selectormap]: http://doc.servo.org/style/selector_matching/struct.PerOriginSelectorMap.html
[docs-pipeline]: https://github.com/servo/servo/blob/master/docs/glossary.md#pipeline [docs-pipeline]: https://github.com/servo/servo/blob/master/docs/glossary.md#pipeline

1
ports/cef/Cargo.lock generated
View file

@ -2118,6 +2118,7 @@ dependencies = [
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"quickersort 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -365,6 +365,7 @@ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quickersort 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -30,10 +30,10 @@ use gecko_string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use glue::GeckoDeclarationBlock; use glue::GeckoDeclarationBlock;
use libc::uintptr_t; use libc::uintptr_t;
use selectors::Element; use selectors::Element;
use selectors::matching::DeclarationBlock;
use selectors::parser::{AttrSelector, NamespaceConstraint}; use selectors::parser::{AttrSelector, NamespaceConstraint};
use snapshot::GeckoElementSnapshot; use snapshot::GeckoElementSnapshot;
use snapshot_helpers; use snapshot_helpers;
use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::BitOr; use std::ops::BitOr;
use std::ptr; use std::ptr;
@ -45,10 +45,11 @@ use style::element_state::ElementState;
use style::error_reporting::StdoutErrorReporter; use style::error_reporting::StdoutErrorReporter;
use style::gecko_selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PseudoElement}; use style::gecko_selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PseudoElement};
use style::parser::ParserContextExtraData; use style::parser::ParserContextExtraData;
use style::properties::PropertyDeclarationBlock;
use style::properties::{ComputedValues, parse_style_attribute}; use style::properties::{ComputedValues, parse_style_attribute};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
use style::refcell::{Ref, RefCell, RefMut}; use style::refcell::{Ref, RefCell, RefMut};
use style::selector_impl::ElementExt; use style::selector_impl::ElementExt;
use style::selector_matching::DeclarationBlock;
use style::sink::Push; use style::sink::Push;
use url::Url; use url::Url;
@ -382,6 +383,16 @@ pub struct GeckoElement<'le> {
chain: PhantomData<&'le ()>, chain: PhantomData<&'le ()>,
} }
impl<'le> fmt::Debug for GeckoElement<'le> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "<{}", self.get_local_name()));
if let Some(id) = self.get_id() {
try!(write!(f, " id={}", id));
}
write!(f, ">")
}
}
impl<'le> GeckoElement<'le> { impl<'le> GeckoElement<'le> {
pub unsafe fn from_raw(el: *mut RawGeckoElement) -> GeckoElement<'le> { pub unsafe fn from_raw(el: *mut RawGeckoElement) -> GeckoElement<'le> {
GeckoElement { GeckoElement {
@ -462,7 +473,7 @@ impl<'le> PartialEq for GeckoElement<'le> {
impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> { impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V) fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>> where V: Push<DeclarationBlock>
{ {
// FIXME(bholley) - Need to implement this. // FIXME(bholley) - Need to implement this.
} }

View file

@ -23,6 +23,7 @@ mod logical_geometry;
mod media_queries; mod media_queries;
mod parsing; mod parsing;
mod properties; mod properties;
mod selector_matching;
mod str; mod str;
mod stylesheets; mod stylesheets;
mod value; mod value;

View file

@ -5,7 +5,7 @@
pub use cssparser::ToCss; pub use cssparser::ToCss;
pub use std::sync::Arc; pub use std::sync::Arc;
pub use style::computed_values::display::T::inline_block; pub use style::computed_values::display::T::inline_block;
pub use style::properties::{DeclaredValue, PropertyDeclaration, PropertyDeclarationBlock}; pub use style::properties::{DeclaredValue, PropertyDeclaration, PropertyDeclarationBlock, Importance};
pub use style::values::specified::{BorderStyle, CSSColor, Length}; pub use style::values::specified::{BorderStyle, CSSColor, Length};
pub use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent}; pub use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent};
pub use style::properties::longhands::outline_color::computed_value::T as ComputedColor; pub use style::properties::longhands::outline_color::computed_value::T as ComputedColor;
@ -18,39 +18,45 @@ fn property_declaration_block_should_serialize_correctly() {
use style::properties::longhands::overflow_x::computed_value::T as OverflowXValue; use style::properties::longhands::overflow_x::computed_value::T as OverflowXValue;
use style::properties::longhands::overflow_y::computed_value::T as OverflowYContainer; use style::properties::longhands::overflow_y::computed_value::T as OverflowYContainer;
let mut normal = Vec::new(); let declarations = vec![
let mut important = Vec::new(); (PropertyDeclaration::Width(
DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(70f32)))),
Importance::Normal),
let length = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(70f32))); (PropertyDeclaration::MinHeight(
normal.push(PropertyDeclaration::Width(length)); DeclaredValue::Value(LengthOrPercentage::Length(Length::from_px(20f32)))),
Importance::Normal),
let min_height = DeclaredValue::Value(LengthOrPercentage::Length(Length::from_px(20f32))); (PropertyDeclaration::Height(
normal.push(PropertyDeclaration::MinHeight(min_height)); DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(20f32)))),
Importance::Important),
let value = DeclaredValue::Value(inline_block); (PropertyDeclaration::Display(
normal.push(PropertyDeclaration::Display(value)); DeclaredValue::Value(inline_block)),
Importance::Normal),
let overflow_x = DeclaredValue::Value(OverflowXValue::auto); (PropertyDeclaration::OverflowX(
normal.push(PropertyDeclaration::OverflowX(overflow_x)); DeclaredValue::Value(OverflowXValue::auto)),
Importance::Normal),
let overflow_y = DeclaredValue::Value(OverflowYContainer(OverflowXValue::auto)); (PropertyDeclaration::OverflowY(
normal.push(PropertyDeclaration::OverflowY(overflow_y)); DeclaredValue::Value(OverflowYContainer(OverflowXValue::auto))),
Importance::Normal),
];
let height = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(20f32)));
important.push(PropertyDeclaration::Height(height));
normal.reverse();
important.reverse();
let block = PropertyDeclarationBlock { let block = PropertyDeclarationBlock {
normal: Arc::new(normal), declarations: Arc::new(declarations),
important: Arc::new(important)
// Incorrect, but not used here:
normal_count: 0,
important_count: 0,
}; };
let css_string = block.to_css_string(); let css_string = block.to_css_string();
assert_eq!( assert_eq!(
css_string, css_string,
"width: 70px; min-height: 20px; display: inline-block; overflow: auto; height: 20px !important;" "width: 70px; min-height: 20px; height: 20px !important; display: inline-block; overflow: auto;"
); );
} }
@ -59,8 +65,11 @@ mod shorthand_serialization {
pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String { pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String {
let block = PropertyDeclarationBlock { let block = PropertyDeclarationBlock {
normal: Arc::new(properties), declarations: Arc::new(properties.into_iter().map(|d| (d, Importance::Normal)).collect()),
important: Arc::new(Vec::new())
// Incorrect, but not used here:
normal_count: 0,
important_count: 0,
}; };
block.to_css_string() block.to_css_string()

View file

@ -0,0 +1,103 @@
/* 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::Parser;
use selectors::parser::{LocalName, ParserContext, parse_selector_list};
use std::sync::Arc;
use string_cache::Atom;
use style::properties::Importance;
use style::selector_matching::{DeclarationBlock, Rule, SelectorMap};
/// Helper method to get some Rules from selector strings.
/// Each sublist of the result contains the Rules for one StyleRule.
fn get_mock_rules(css_selectors: &[&str]) -> Vec<Vec<Rule>> {
css_selectors.iter().enumerate().map(|(i, selectors)| {
let context = ParserContext::new();
parse_selector_list(&context, &mut Parser::new(*selectors))
.unwrap().into_iter().map(|s| {
Rule {
selector: s.complex_selector.clone(),
declarations: DeclarationBlock {
mixed_declarations: Arc::new(Vec::new()),
importance: Importance::Normal,
specificity: s.specificity,
source_order: i,
}
}
}).collect()
}).collect()
}
fn get_mock_map(selectors: &[&str]) -> SelectorMap {
let mut map = SelectorMap::new();
let selector_rules = get_mock_rules(selectors);
for rules in selector_rules.into_iter() {
for rule in rules.into_iter() {
map.insert(rule)
}
}
map
}
#[test]
fn test_rule_ordering_same_specificity() {
let rules_list = get_mock_rules(&["a.intro", "img.sidebar"]);
let a = &rules_list[0][0].declarations;
let b = &rules_list[1][0].declarations;
assert!((a.specificity, a.source_order) < ((b.specificity, b.source_order)),
"The rule that comes later should win.");
}
#[test]
fn test_get_id_name() {
let rules_list = get_mock_rules(&[".intro", "#top"]);
assert_eq!(SelectorMap::get_id_name(&rules_list[0][0]), None);
assert_eq!(SelectorMap::get_id_name(&rules_list[1][0]), Some(Atom::from("top")));
}
#[test]
fn test_get_class_name() {
let rules_list = get_mock_rules(&[".intro.foo", "#top"]);
assert_eq!(SelectorMap::get_class_name(&rules_list[0][0]), Some(Atom::from("intro")));
assert_eq!(SelectorMap::get_class_name(&rules_list[1][0]), None);
}
#[test]
fn test_get_local_name() {
let rules_list = get_mock_rules(&["img.foo", "#top", "IMG", "ImG"]);
let check = |i: usize, names: Option<(&str, &str)>| {
assert!(SelectorMap::get_local_name(&rules_list[i][0])
== names.map(|(name, lower_name)| LocalName {
name: Atom::from(name),
lower_name: Atom::from(lower_name) }))
};
check(0, Some(("img", "img")));
check(1, None);
check(2, Some(("IMG", "img")));
check(3, Some(("ImG", "img")));
}
#[test]
fn test_insert() {
let rules_list = get_mock_rules(&[".intro.foo", "#top"]);
let mut selector_map = SelectorMap::new();
selector_map.insert(rules_list[1][0].clone());
assert_eq!(1, selector_map.id_hash.get(&atom!("top")).unwrap()[0].declarations.source_order);
selector_map.insert(rules_list[0][0].clone());
assert_eq!(0, selector_map.class_hash.get(&Atom::from("intro")).unwrap()[0].declarations.source_order);
assert!(selector_map.class_hash.get(&Atom::from("foo")).is_none());
}
#[test]
fn test_get_universal_rules() {
let map = get_mock_map(&["*|*", "#foo > *|*", ".klass", "#id"]);
let mut decls = vec![];
map.get_universal_rules(&mut decls);
assert_eq!(decls.len(), 1);
}

View file

@ -13,6 +13,8 @@ use style::error_reporting::ParseErrorReporter;
use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage}; use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage};
use style::parser::ParserContextExtraData; use style::parser::ParserContextExtraData;
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands}; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
use style::properties::Importance;
use style::properties::longhands::animation_play_state;
use style::stylesheets::{Stylesheet, CSSRule, StyleRule, KeyframesRule, Origin}; use style::stylesheets::{Stylesheet, CSSRule, StyleRule, KeyframesRule, Origin};
use style::values::specified::{LengthOrPercentageOrAuto, Percentage}; use style::values::specified::{LengthOrPercentageOrAuto, Percentage};
use url::Url; use url::Url;
@ -22,17 +24,42 @@ fn test_parse_stylesheet() {
let css = r" let css = r"
@namespace url(http://www.w3.org/1999/xhtml); @namespace url(http://www.w3.org/1999/xhtml);
/* FIXME: only if scripting is enabled */ /* FIXME: only if scripting is enabled */
input[type=hidden i] { display: none !important; } input[type=hidden i] {
html , body /**/ { display: block; } display: block !important;
display: none !important;
display: inline;
--a: b !important;
--a: inherit !important;
--a: c;
}
html , body /**/ {
display: none;
display: block;
}
#d1 > .ok { background: blue; } #d1 > .ok { background: blue; }
@keyframes foo { @keyframes foo {
from { width: 0% } from { width: 0% }
to { width: 100%} to {
width: 100%;
width: 50% !important; /* !important not allowed here */
animation-name: 'foo'; /* animation properties not allowed here */
animation-play-state: running; /* … except animation-play-state */
}
}"; }";
let url = Url::parse("about::test").unwrap(); let url = Url::parse("about::test").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent, let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
Box::new(CSSErrorReporterTest), Box::new(CSSErrorReporterTest),
ParserContextExtraData::default()); ParserContextExtraData::default());
macro_rules! assert_eq {
($left: expr, $right: expr) => {
let left = $left;
let right = $right;
if left != right {
panic!("{:#?} != {:#?}", left, right)
}
}
}
assert_eq!(stylesheet, Stylesheet { assert_eq!(stylesheet, Stylesheet {
origin: Origin::UserAgent, origin: Origin::UserAgent,
media: None, media: None,
@ -71,11 +98,15 @@ fn test_parse_stylesheet() {
}, },
], ],
declarations: PropertyDeclarationBlock { declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![]), declarations: Arc::new(vec![
important: Arc::new(vec![ (PropertyDeclaration::Display(DeclaredValue::Value(
PropertyDeclaration::Display(DeclaredValue::Value(
longhands::display::SpecifiedValue::none)), longhands::display::SpecifiedValue::none)),
Importance::Important),
(PropertyDeclaration::Custom(Atom::from("a"), DeclaredValue::Inherit),
Importance::Important),
]), ]),
normal_count: 0,
important_count: 2,
}, },
}), }),
CSSRule::Style(StyleRule { CSSRule::Style(StyleRule {
@ -116,11 +147,13 @@ fn test_parse_stylesheet() {
}, },
], ],
declarations: PropertyDeclarationBlock { declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![ declarations: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::Value( (PropertyDeclaration::Display(DeclaredValue::Value(
longhands::display::SpecifiedValue::block)), longhands::display::SpecifiedValue::block)),
Importance::Normal),
]), ]),
important: Arc::new(vec![]), normal_count: 1,
important_count: 0,
}, },
}), }),
CSSRule::Style(StyleRule { CSSRule::Style(StyleRule {
@ -150,24 +183,33 @@ fn test_parse_stylesheet() {
}, },
], ],
declarations: PropertyDeclarationBlock { declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![ declarations: Arc::new(vec![
PropertyDeclaration::BackgroundClip(DeclaredValue::Initial), (PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
PropertyDeclaration::BackgroundOrigin(DeclaredValue::Initial),
PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
longhands::background_color::SpecifiedValue { longhands::background_color::SpecifiedValue {
authored: Some("blue".to_owned()), authored: Some("blue".to_owned()),
parsed: cssparser::Color::RGBA(cssparser::RGBA { parsed: cssparser::Color::RGBA(cssparser::RGBA {
red: 0., green: 0., blue: 1., alpha: 1. red: 0., green: 0., blue: 1., alpha: 1.
}), }),
} }
)), )),
Importance::Normal),
(PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
Importance::Normal),
(PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
Importance::Normal),
(PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
Importance::Normal),
(PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
Importance::Normal),
(PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
Importance::Normal),
(PropertyDeclaration::BackgroundOrigin(DeclaredValue::Initial),
Importance::Normal),
(PropertyDeclaration::BackgroundClip(DeclaredValue::Initial),
Importance::Normal),
]), ]),
important: Arc::new(vec![]), normal_count: 8,
important_count: 0,
}, },
}), }),
CSSRule::Keyframes(KeyframesRule { CSSRule::Keyframes(KeyframesRule {
@ -177,16 +219,22 @@ fn test_parse_stylesheet() {
selector: KeyframeSelector::new_for_unit_testing( selector: KeyframeSelector::new_for_unit_testing(
vec![KeyframePercentage::new(0.)]), vec![KeyframePercentage::new(0.)]),
declarations: Arc::new(vec![ declarations: Arc::new(vec![
PropertyDeclaration::Width(DeclaredValue::Value( (PropertyDeclaration::Width(DeclaredValue::Value(
LengthOrPercentageOrAuto::Percentage(Percentage(0.)))), LengthOrPercentageOrAuto::Percentage(Percentage(0.)))),
Importance::Normal),
]), ]),
}, },
Keyframe { Keyframe {
selector: KeyframeSelector::new_for_unit_testing( selector: KeyframeSelector::new_for_unit_testing(
vec![KeyframePercentage::new(1.)]), vec![KeyframePercentage::new(1.)]),
declarations: Arc::new(vec![ declarations: Arc::new(vec![
PropertyDeclaration::Width(DeclaredValue::Value( (PropertyDeclaration::Width(DeclaredValue::Value(
LengthOrPercentageOrAuto::Percentage(Percentage(1.)))), LengthOrPercentageOrAuto::Percentage(Percentage(1.)))),
Importance::Normal),
(PropertyDeclaration::AnimationPlayState(DeclaredValue::Value(
animation_play_state::SpecifiedValue(
vec![animation_play_state::SingleSpecifiedValue::running]))),
Importance::Normal),
]), ]),
}, },
] ]