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::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::element::{Element, StylePriority};
use dom::element::Element;
use dom::node::{Node, NodeDamage, window_from_node};
use dom::window::Window;
use std::ascii::AsciiExt;
use std::ops::Deref;
use std::cell::Ref;
use std::slice;
use string_cache::Atom;
use style::parser::ParserContextExtraData;
use style::properties::{Shorthand, is_supported_property};
use style::properties::{parse_one_declaration, parse_style_attribute};
use style::properties::{PropertyDeclaration, Shorthand, Importance};
use style::properties::{is_supported_property, parse_one_declaration, parse_style_attribute};
use style::selector_impl::PseudoElement;
// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
@ -91,7 +92,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
fn Length(&self) -> u32 {
let elem = self.owner.upcast::<Element>();
let len = match *elem.style_attribute().borrow() {
Some(ref declarations) => declarations.normal.len() + declarations.important.len(),
Some(ref declarations) => declarations.declarations.len(),
None => 0,
};
len as u32
@ -102,19 +103,15 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
let index = index as usize;
let elem = self.owner.upcast::<Element>();
let style_attribute = elem.style_attribute().borrow();
let result = style_attribute.as_ref().and_then(|declarations| {
if index > declarations.normal.len() {
declarations.important
.get(index - declarations.normal.len())
.map(|decl| format!("{:?} !important", decl))
} else {
declarations.normal
.get(index)
.map(|decl| format!("{:?}", decl))
style_attribute.as_ref().and_then(|declarations| {
declarations.declarations.get(index)
}).map(|&(ref declaration, importance)| {
let mut css = declaration.to_css_string();
if importance.important() {
css += " !important";
}
});
result.map_or(DOMString::new(), DOMString::from)
DOMString::from(css)
}).unwrap_or_else(DOMString::new)
}
// 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
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);
}
// Step 3 & 4
match owner.get_inline_style_declaration(&property) {
Some(declaration) => DOMString::from(declaration.value()),
Some(declaration) => DOMString::from(declaration.0.value()),
None => DOMString::new(),
}
}
@ -176,8 +184,10 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
}
// Step 3
} else {
if self.owner.get_important_inline_style_declaration(&property).is_some() {
return DOMString::from("important");
if let Some(decl) = self.owner.get_inline_style_declaration(&property) {
if decl.1.important() {
return DOMString::from("important");
}
}
}
@ -211,8 +221,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// Step 5
let priority = match &*priority {
"" => StylePriority::Normal,
p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
"" => Importance::Normal,
p if p.eq_ignore_ascii_case("important") => Importance::Important,
_ => return Ok(()),
};
@ -254,8 +264,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// Step 4
let priority = match &*priority {
"" => StylePriority::Normal,
p if p.eq_ignore_ascii_case("important") => StylePriority::Important,
"" => Importance::Normal,
p if p.eq_ignore_ascii_case("important") => Importance::Important,
_ => return Ok(()),
};
@ -354,7 +364,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// Step 3
let decl_block = parse_style_attribute(&value, &window.get_url(), window.css_error_reporter(),
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
} else {
Some(decl_block)

View file

@ -71,7 +71,7 @@ use html5ever::serialize::TraversalScope;
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks};
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::parser::{AttrSelector, NamespaceConstraint, parse_author_origin_selector_list_from_str};
use std::ascii::AsciiExt;
@ -79,7 +79,7 @@ use std::borrow::Cow;
use std::cell::{Cell, Ref};
use std::convert::TryFrom;
use std::default::Default;
use std::mem;
use std::fmt;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use string_cache::{Atom, Namespace, QualName};
@ -87,10 +87,11 @@ use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::element_state::*;
use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
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::{DeclaredValue, Importance};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl};
use style::selector_matching::DeclarationBlock;
use style::sink::Push;
use style::values::CSSFloat;
use style::values::specified::{self, CSSColor, CSSRGBA, LengthOrPercentage};
@ -115,6 +116,16 @@ pub struct Element {
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)]
pub enum ElementCreator {
ParserCreated,
@ -280,7 +291,7 @@ pub trait LayoutElementHelpers {
#[allow(unsafe_code)]
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)]
unsafe fn get_colspan(self) -> u32;
#[allow(unsafe_code)]
@ -313,11 +324,13 @@ impl LayoutElementHelpers for LayoutJS<Element> {
#[allow(unsafe_code)]
unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>>
where V: Push<DeclarationBlock>
{
#[inline]
fn from_declaration(rule: PropertyDeclaration) -> DeclarationBlock<Vec<PropertyDeclaration>> {
DeclarationBlock::from_declarations(Arc::new(vec![rule]))
fn from_declaration(rule: PropertyDeclaration) -> DeclarationBlock {
DeclarationBlock::from_declarations(
Arc::new(vec![(rule, Importance::Normal)]),
Importance::Normal)
}
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 {
pub fn html_element_in_html_document(&self) -> bool {
self.namespace == ns!(html) && self.upcast::<Node>().is_in_html_doc()
@ -756,20 +762,21 @@ impl Element {
fn remove(element: &Element, property: &str) {
let mut inline_declarations = element.style_attribute.borrow_mut();
if let &mut Some(ref mut declarations) = &mut *inline_declarations {
let index = declarations.normal
.iter()
.position(|decl| decl.matches(property));
let mut importance = None;
let index = declarations.declarations.iter().position(|&(ref decl, i)| {
let matching = decl.matches(property);
if matching {
importance = Some(i)
}
matching
});
if let Some(index) = index {
Arc::make_mut(&mut declarations.normal).remove(index);
return;
}
let index = declarations.important
.iter()
.position(|decl| decl.matches(property));
if let Some(index) = index {
Arc::make_mut(&mut declarations.important).remove(index);
return;
Arc::make_mut(&mut declarations.declarations).remove(index);
if importance.unwrap().important() {
declarations.important_count -= 1;
} else {
declarations.normal_count -= 1;
}
}
}
}
@ -780,81 +787,87 @@ impl Element {
pub fn update_inline_style(&self,
declarations: Vec<PropertyDeclaration>,
style_priority: StylePriority) {
fn update(element: &Element, mut declarations: Vec<PropertyDeclaration>, style_priority: StylePriority) {
importance: Importance) {
fn update(element: &Element, declarations: Vec<PropertyDeclaration>,
importance: Importance) {
let mut inline_declarations = element.style_attribute().borrow_mut();
if let &mut Some(ref mut existing_declarations) = &mut *inline_declarations {
let existing_declarations = if style_priority == StylePriority::Important {
&mut existing_declarations.important
} else {
&mut existing_declarations.normal
};
if let &mut Some(ref mut declaration_block) = &mut *inline_declarations {
{
// Usually, the reference count will be 1 here. But transitions could make it greater
// than that.
let existing_declarations = Arc::make_mut(&mut declaration_block.declarations);
// Usually, the reference count will be 1 here. But transitions could make it greater
// than that.
let existing_declarations = Arc::make_mut(existing_declarations);
while let Some(mut incoming_declaration) = declarations.pop() {
let mut replaced = false;
for existing_declaration in &mut *existing_declarations {
if existing_declaration.name() == incoming_declaration.name() {
mem::swap(existing_declaration, &mut incoming_declaration);
replaced = true;
break;
'outer: for incoming_declaration in declarations {
for existing_declaration in &mut *existing_declarations {
if existing_declaration.0.name() == incoming_declaration.name() {
match (existing_declaration.1, importance) {
(Importance::Normal, Importance::Important) => {
declaration_block.normal_count -= 1;
declaration_block.important_count += 1;
}
(Importance::Important, Importance::Normal) => {
declaration_block.normal_count += 1;
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;
}
let (important, normal) = if style_priority == StylePriority::Important {
(declarations, vec![])
let (normal_count, important_count) = if importance.important() {
(0, declarations.len() as u32)
} else {
(vec![], declarations)
(declarations.len() as u32, 0)
};
*inline_declarations = Some(PropertyDeclarationBlock {
important: Arc::new(important),
normal: Arc::new(normal),
declarations: Arc::new(declarations.into_iter().map(|d| (d, importance)).collect()),
normal_count: normal_count,
important_count: important_count,
});
}
update(self, declarations, style_priority);
update(self, declarations, importance);
self.sync_property_with_attrs_style();
}
pub fn set_inline_style_property_priority(&self,
properties: &[&str],
style_priority: StylePriority) {
new_importance: Importance) {
{
let mut inline_declarations = self.style_attribute().borrow_mut();
if let &mut Some(ref mut declarations) = &mut *inline_declarations {
let (from, to) = if style_priority == StylePriority::Important {
(&mut declarations.normal, &mut declarations.important)
} else {
(&mut declarations.important, &mut declarations.normal)
};
// Usually, the reference counts of `from` and `to` will be 1 here. But transitions
// could make them greater than that.
let from = Arc::make_mut(from);
let to = Arc::make_mut(to);
let mut new_from = Vec::new();
for declaration in from.drain(..) {
let name = declaration.name();
if properties.iter().any(|p| name == **p) {
to.push(declaration)
} else {
new_from.push(declaration)
}
}
mem::replace(from, new_from);
if let &mut Some(ref mut block) = &mut *inline_declarations {
// Usually, the reference counts of `from` and `to` will be 1 here. But transitions
// could make them greater than that.
let declarations = Arc::make_mut(&mut block.declarations);
for &mut (ref declaration, ref mut importance) in declarations {
if properties.iter().any(|p| declaration.name() == **p) {
match (*importance, new_importance) {
(Importance::Normal, Importance::Important) => {
block.normal_count -= 1;
block.important_count += 1;
}
(Importance::Important, Importance::Normal) => {
block.normal_count += 1;
block.important_count -= 1;
}
_ => {}
}
*importance = new_importance;
}
}
}
}
@ -863,25 +876,12 @@ impl Element {
pub fn get_inline_style_declaration(&self,
property: &Atom)
-> Option<Ref<PropertyDeclaration>> {
-> Option<Ref<(PropertyDeclaration, Importance)>> {
ref_filter_map(self.style_attribute.borrow(), |inline_declarations| {
inline_declarations.as_ref().and_then(|declarations| {
declarations.normal
declarations.declarations
.iter()
.chain(declarations.important.iter())
.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))
.find(|&&(ref 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::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress};
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
use selectors::matching::{DeclarationBlock, ElementFlags};
use selectors::matching::ElementFlags;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use std::fmt;
use std::marker::PhantomData;
use std::mem::{transmute, transmute_copy};
use std::sync::Arc;
@ -59,9 +60,10 @@ use style::context::SharedStyleContext;
use style::data::PrivateStyleData;
use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode};
use style::element_state::*;
use style::properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
use style::properties::{ComputedValues, PropertyDeclarationBlock};
use style::refcell::{Ref, RefCell, RefMut};
use style::selector_impl::{ElementSnapshot, NonTSPseudoClass, PseudoElement, ServoSelectorImpl};
use style::selector_matching::DeclarationBlock;
use style::sink::Push;
use style::str::is_whitespace;
use url::Url;
@ -405,9 +407,19 @@ pub struct ServoLayoutElement<'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> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: Push<DeclarationBlock<Vec<PropertyDeclaration>>>
where V: Push<DeclarationBlock>
{
unsafe {
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
/// ever access safe properties and cannot race on elements.
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub struct ServoThreadSafeLayoutElement<'le> {
element: &'le Element,
}
@ -1059,5 +1071,5 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> {
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 range::Range;
use restyle_damage::RestyleDamage;
use std::fmt::Debug;
use std::sync::Arc;
use string_cache::{Atom, Namespace};
use style::computed_values::display;
@ -350,7 +351,7 @@ pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode {
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> +
PresentationalHintsSynthetizer {
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)",
"ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
"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)",

View file

@ -36,6 +36,7 @@ log = "0.3.5"
matches = "0.1"
num-traits = "0.1.32"
ordered-float = "0.2.2"
quickersort = "2.0.0"
rand = "0.3"
rustc-serialize = "0.3"
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::transition_timing_function::computed_value::StartEnd;
use properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
use properties::{self, ComputedValues};
use selectors::matching::DeclarationBlock;
use properties::{self, ComputedValues, Importance};
use selector_matching::DeclarationBlock;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use string_cache::Atom;
@ -385,7 +385,8 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
KeyframesStepValue::Declarations(ref declarations) => {
let declaration_block = DeclarationBlock {
declarations: declarations.clone(),
mixed_declarations: declarations.clone(),
importance: Importance::Normal,
source_order: 0,
specificity: ::std::u32::MAX,
};

View file

@ -9,11 +9,11 @@
use context::SharedStyleContext;
use data::PrivateStyleData;
use element_state::ElementState;
use properties::{ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
use properties::{ComputedValues, PropertyDeclarationBlock};
use refcell::{Ref, RefMut};
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
use selector_impl::{ElementExt, PseudoElement};
use selectors::matching::DeclarationBlock;
use selector_matching::DeclarationBlock;
use sink::Push;
use std::fmt::Debug;
use std::ops::BitOr;
@ -198,10 +198,10 @@ pub trait TDocument : Sized + Copy + Clone {
pub trait PresentationalHintsSynthetizer {
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 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
* 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 properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty;
use properties::{PropertyDeclaration, parse_property_declaration_list};
use properties::{PropertyDeclaration, Importance};
use std::sync::Arc;
/// 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))]
pub struct Keyframe {
pub selector: KeyframeSelector,
pub declarations: Arc<Vec<PropertyDeclaration>>,
}
impl Keyframe {
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Keyframe, ()> {
let percentages = try!(input.parse_until_before(Delimiter::CurlyBracketBlock, |input| {
input.parse_comma_separated(|input| KeyframePercentage::parse(input))
}));
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,
})
}
/// `!important` is not allowed in keyframe declarations,
/// so the second value of these tuples is always `Importance::Normal`.
/// But including them enables `compute_style_for_animation_step` to create a `DeclarationBlock`
/// by cloning an `Arc<_>` (incrementing a reference count) rather than re-creating a `Vec<_>`.
pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
}
/// A keyframes step value. This can be a synthetised keyframes animation, that
@ -101,7 +87,8 @@ impl Keyframe {
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesStepValue {
Declarations(Arc<Vec<PropertyDeclaration>>),
/// See `Keyframe::declarations`s docs about the presence of `Importance`.
Declarations(Arc<Vec<(PropertyDeclaration, Importance)>>),
ComputedValues,
}
@ -127,7 +114,7 @@ impl KeyframesStep {
value: KeyframesStepValue) -> Self {
let declared_timing_function = match value {
KeyframesStepValue::Declarations(ref declarations) => {
declarations.iter().any(|prop_decl| {
declarations.iter().any(|&(ref prop_decl, _)| {
match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => true,
_ => false,
@ -167,8 +154,8 @@ fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
let mut ret = vec![];
// NB: declarations are already deduplicated, so we don't have to check for
// it here.
for declaration in keyframe.declarations.iter() {
if let Some(property) = TransitionProperty::from_declaration(&declaration) {
for &(ref declaration, _) in keyframe.declarations.iter() {
if let Some(property) = TransitionProperty::from_declaration(declaration) {
ret.push(property);
}
}
@ -259,12 +246,49 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
fn parse_block(&self, prelude: Self::Prelude, input: &mut Parser)
-> 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 {
selector: prelude,
// FIXME: needs parsing different from parse_property_declaration_list:
// https://drafts.csswg.org/css-animations/#keyframes
// Paragraph "The <declaration-list> inside of <keyframe-block> ..."
declarations: parse_property_declaration_list(self.context, input).normal,
declarations: Arc::new(declarations),
})
}
}
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 num_traits;
extern crate ordered_float;
extern crate quickersort;
extern crate rand;
extern crate rustc_serialize;
extern crate selectors;

View file

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

View file

@ -65,7 +65,8 @@ class Keyword(object):
class Longhand(object):
def __init__(self, style_struct, name, animatable=None, derived_from=None, keyword=None,
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.keyword = keyword
self.predefined_type = predefined_type
@ -80,6 +81,13 @@ class Longhand(object):
self.depend_on_viewport_size = depend_on_viewport_size
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
# really random.
if animatable is None:
@ -98,7 +106,8 @@ class Longhand(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.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident)
@ -107,6 +116,13 @@ class Shorthand(object):
self.sub_properties = sub_properties
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):
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:
return
longand = Longhand(self.current_style_struct, name, **kwargs)
self.current_style_struct.longhands.append(longand)
self.longhands.append(longand)
self.longhands_by_name[name] = longand
longhand = Longhand(self.current_style_struct, name, **kwargs)
self.current_style_struct.longhands.append(longhand)
self.longhands.append(longhand)
self.longhands_by_name[name] = longhand
for name in longand.derived_from:
self.derived_longhands.setdefault(name, []).append(longand)
for name in longhand.derived_from:
self.derived_longhands.setdefault(name, []).append(longhand)
return longand
return longhand
def declare_shorthand(self, name, sub_properties, products="gecko servo", *args, **kwargs):
products = products.split()

View file

@ -636,7 +636,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
<%helpers:longhand name="animation-name"
need_index="True"
animatable="False">
animatable="False",
allowed_in_keyframe_block="False">
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
@ -699,7 +700,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
<%helpers:longhand name="animation-duration"
need_index="True"
animatable="False">
animatable="False",
allowed_in_keyframe_block="False">
pub use super::transition_duration::computed_value;
pub use super::transition_duration::{get_initial_value, get_initial_single_value};
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"
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::{get_initial_value, get_initial_single_value};
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"
need_index="True"
animatable="False">
animatable="False",
allowed_in_keyframe_block="False">
use values::computed::ComputedValueAsSpecified;
use values::NoViewportPercentage;
@ -804,22 +808,28 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
${helpers.keyword_list("animation-direction",
"normal reverse alternate alternate-reverse",
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",
"running paused",
need_clone=True,
need_index=True,
animatable=False)}
animatable=False,
allowed_in_keyframe_block=True)}
${helpers.keyword_list("animation-fill-mode",
"none forwards backwards both",
need_index=True,
animatable=False)}
animatable=False,
allowed_in_keyframe_block=False)}
<%helpers:longhand name="animation-delay"
need_index="True"
animatable="False">
animatable="False",
allowed_in_keyframe_block="False">
pub use super::transition_duration::computed_value;
pub use super::transition_duration::{get_initial_value, get_initial_single_value};
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};
use logical_geometry::WritingMode;
use parser::{ParserContext, ParserContextExtraData, log_css_error};
use selectors::matching::DeclarationBlock;
use selector_matching::DeclarationBlock;
use stylesheets::Origin;
use values::LocalToCss;
use values::HasViewportPercentage;
@ -261,32 +261,38 @@ mod property_bit_field {
% endif
% endfor
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Importance {
/// Indicates a declaration without `!important`.
Normal,
/// Indicates a declaration with `!important`.
Important,
}
impl Importance {
pub fn important(self) -> bool {
match self {
Importance::Normal => false,
Importance::Important => true,
}
}
}
use std::iter::{Iterator, Chain, Zip, Rev, Repeat, repeat};
use std::slice;
/// Overridden declarations are skipped.
// FIXME (https://github.com/servo/servo/issues/3426)
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PropertyDeclarationBlock {
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub important: Arc<Vec<PropertyDeclaration>>,
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub normal: Arc<Vec<PropertyDeclaration>>,
}
pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
impl PropertyDeclarationBlock {
/// Provides an iterator of all declarations, with indication of !important value
pub fn declarations(&self) -> Chain<
Zip<Rev<slice::Iter<PropertyDeclaration>>, Repeat<bool>>,
Zip<Rev<slice::Iter<PropertyDeclaration>>, Repeat<bool>>
> {
// 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)
}
/// The number of entries in `self.declaration` with `Importance::Normal`
pub normal_count: u32,
/// The number of entries in `self.declaration` with `Importance::Important`
pub important_count: u32,
}
impl ToCss for PropertyDeclarationBlock {
@ -300,7 +306,7 @@ impl ToCss for PropertyDeclarationBlock {
let mut already_serialized = Vec::new();
// Step 3
for (declaration, important) in self.declarations() {
for &(ref declaration, importance) in &*self.declarations {
// Step 3.1
let property = declaration.name();
@ -314,7 +320,7 @@ impl ToCss for PropertyDeclarationBlock {
if !shorthands.is_empty() {
// Step 3.3.1
let mut longhands = self.declarations()
let mut longhands = self.declarations.iter()
.filter(|d| !already_serialized.contains(&d.0.name()))
.collect::<Vec<_>>();
@ -326,11 +332,11 @@ impl ToCss for PropertyDeclarationBlock {
let mut current_longhands = Vec::new();
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();
if properties.iter().any(|p| &longhand_name == *p) {
current_longhands.push(longhand);
if longhand_important {
if longhand_importance.important() {
important_count += 1;
}
}
@ -370,7 +376,7 @@ impl ToCss for PropertyDeclarationBlock {
for current_longhand in current_longhands {
// Substep 9
already_serialized.push(current_longhand.name());
let index_to_remove = longhands.iter().position(|l| l.0 == current_longhand);
let index_to_remove = longhands.iter().position(|l| l.0 == *current_longhand);
if let Some(index) = index_to_remove {
// Substep 10
longhands.remove(index);
@ -396,7 +402,7 @@ impl ToCss for PropertyDeclarationBlock {
dest,
&property.to_string(),
AppendableValue::Declaration(declaration),
important,
importance,
&mut is_first_serialization));
// 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>
(dest: &mut W,
appendable_value: AppendableValue<'a, I>,
is_important: bool)
importance: Importance)
-> fmt::Result
where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> {
match appendable_value {
@ -445,7 +451,7 @@ fn append_declaration_value<'a, W, I>
}
}
if is_important {
if importance.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,
property_name: &str,
appendable_value: AppendableValue<'a, I>,
is_important: bool,
importance: Importance,
is_first_serialization: &mut bool)
-> fmt::Result
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
// values, they no longer use the shared property name "overflow" and must be handled differently
if shorthands::is_overflow_shorthand(&appendable_value) {
return append_declaration_value(dest, appendable_value, is_important);
return append_declaration_value(dest, appendable_value, importance);
}
try!(write!(dest, "{}:", property_name));
@ -484,7 +490,7 @@ fn append_serialization<'a, W, I>(dest: &mut W,
&AppendableValue::DeclarationsForShorthand(..) => try!(write!(dest, " "))
}
try!(append_declaration_value(dest, appendable_value, is_important));
try!(append_declaration_value(dest, appendable_value, importance));
write!(dest, ";")
}
@ -500,7 +506,7 @@ pub fn parse_one_declaration(name: &str, input: &str, base_url: &Url, error_repo
-> Result<Vec<PropertyDeclaration>, ()> {
let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data);
let mut results = vec![];
match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results) {
match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results, false) {
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results),
_ => Err(())
}
@ -514,43 +520,49 @@ struct PropertyDeclarationParser<'a, 'b: 'a> {
/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> {
type Prelude = ();
type AtRule = (Vec<PropertyDeclaration>, bool);
type AtRule = (Vec<PropertyDeclaration>, Importance);
}
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![];
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(()),
_ => Err(())
}
}));
let important = input.try(parse_important).is_ok();
Ok((results, important))
let importance = match input.try(parse_important) {
Ok(()) => Importance::Important,
Err(()) => Importance::Normal,
};
Ok((results, importance))
}
}
pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser)
-> PropertyDeclarationBlock {
let mut important_declarations = Vec::new();
let mut normal_declarations = Vec::new();
let mut declarations = Vec::new();
let mut normal_count = 0;
let mut important_count = 0;
let parser = PropertyDeclarationParser {
context: context,
};
let mut iter = DeclarationListParser::new(input, parser);
while let Some(declaration) = iter.next() {
match declaration {
Ok((results, important)) => {
if important {
important_declarations.extend(results);
Ok((results, importance)) => {
if importance.important() {
important_count += results.len() as u32;
} else {
normal_declarations.extend(results);
normal_count += results.len() as u32;
}
declarations.extend(results.into_iter().map(|d| (d, importance)))
}
Err(range) => {
let pos = range.start;
@ -560,46 +572,90 @@ pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Pars
}
}
}
PropertyDeclarationBlock {
important: Arc::new(deduplicate_property_declarations(important_declarations)),
normal: Arc::new(deduplicate_property_declarations(normal_declarations)),
}
let mut block = PropertyDeclarationBlock {
declarations: Arc::new(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.
/// The input is in source order, output in reverse source order.
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() {
let declarations = Arc::get_mut(&mut block.declarations).unwrap();
for (declaration, importance) in declarations.drain(..).rev() {
match declaration {
% for property in data.longhands:
PropertyDeclaration::${property.camel_case}(..) => {
% if not property.derived_from:
if seen.get_${property.ident}() {
continue
if importance.important() {
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:
unreachable!();
% endif
},
% endfor
PropertyDeclaration::Custom(ref name, _) => {
if seen_custom.contains(name) {
continue
if importance.important() {
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)]
pub enum CSSWideKeyword {
@ -675,11 +731,11 @@ impl Shorthand {
}
/// 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 {
let appendable_value = self.get_shorthand_appendable_value(declarations).unwrap();
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
}
@ -700,7 +756,7 @@ impl Shorthand {
dest,
property_name,
appendable_value,
false,
Importance::Normal,
is_first_serialization
).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))]
pub enum PropertyDeclaration {
% for property in data.longhands:
@ -811,6 +867,7 @@ pub enum PropertyDeclarationParseResult {
UnknownProperty,
ExperimentalProperty,
InvalidValue,
AnimationPropertyInKeyframeBlock,
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 {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
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,
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) {
let value = match input.try(CSSWideKeyword::parse) {
Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited
@ -982,6 +1065,11 @@ impl PropertyDeclaration {
% for property in data.longhands:
% if not property.derived_from:
"${property.name}" => {
% if not property.allowed_in_keyframe_block:
if in_keyframe_block {
return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock
}
% endif
% if property.internal:
if context.stylesheet_origin != Origin::UserAgent {
return PropertyDeclarationParseResult::UnknownProperty
@ -1007,6 +1095,11 @@ impl PropertyDeclaration {
% endfor
% for shorthand in data.shorthands:
"${shorthand.name}" => {
% if not shorthand.allowed_in_keyframe_block:
if in_keyframe_block {
return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock
}
% endif
% if shorthand.internal:
if context.stylesheet_origin != Origin::UserAgent {
return PropertyDeclarationParseResult::UnknownProperty
@ -1612,7 +1705,7 @@ mod lazy_static_module {
#[allow(unused_mut, unused_imports)]
fn cascade_with_cached_declarations(
viewport_size: Size2D<Au>,
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
applicable_declarations: &[DeclarationBlock],
shareable: bool,
parent_style: &ComputedValues,
cached_style: &ComputedValues,
@ -1643,8 +1736,7 @@ fn cascade_with_cached_declarations(
// Declaration blocks are stored in increasing precedence order,
// we want them in decreasing order here.
for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order.
for declaration in sub_list.declarations.iter() {
for declaration in sub_list.iter().rev() {
match *declaration {
% for style_struct in data.active_style_structs():
% 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.
pub fn cascade(viewport_size: Size2D<Au>,
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
applicable_declarations: &[DeclarationBlock],
shareable: bool,
parent_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 seen_custom = HashSet::new();
for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order.
for declaration in sub_list.declarations.iter() {
for declaration in sub_list.iter().rev() {
match *declaration {
PropertyDeclaration::Custom(ref name, ref value) => {
::custom_properties::cascade(
@ -1834,8 +1925,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
ComputedValues::do_cascade_property(|cascade_property| {
% for category_to_cascade_now in ["early", "other"]:
for sub_list in applicable_declarations.iter().rev() {
// Declarations are already stored in reverse order.
for declaration in sub_list.declarations.iter() {
for declaration in sub_list.iter().rev() {
if let PropertyDeclaration::Custom(..) = *declaration {
continue
}

View file

@ -152,7 +152,8 @@ macro_rules! try_parse_one {
sub_properties="animation-name animation-duration
animation-timing-function animation-delay
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_delay, animation_iteration_count, animation_direction};
use properties::longhands::{animation_fill_mode, animation_play_state};

View file

@ -9,26 +9,30 @@ use element_state::*;
use error_reporting::StdoutErrorReporter;
use keyframes::KeyframesAnimation;
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 selector_impl::{ElementExt, TheSelectorImpl, PseudoElement};
use selectors::Element;
use selectors::bloom::BloomFilter;
use selectors::matching::DeclarationBlock as GenericDeclarationBlock;
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
use selectors::matching::{Rule, SelectorMap, StyleRelations};
use selectors::parser::Selector;
use selectors::matching::{StyleRelations, matches_complex_selector};
use selectors::parser::{Selector, SimpleSelector, LocalName, ComplexSelector};
use sink::Push;
use smallvec::VecLike;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt;
use std::hash::BuildHasherDefault;
use std::hash::Hash;
use std::slice;
use std::sync::Arc;
use string_cache::Atom;
use style_traits::viewport::ViewportConstraints;
use stylesheets::{CSSRule, CSSRuleIteratorExt, Origin, Stylesheet};
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
/// 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
/// (depending on the implementation)
pseudos_map: HashMap<PseudoElement,
PerPseudoElementSelectorMap,
BuildHasherDefault<::fnv::FnvHasher>>,
pseudos_map: FnvHashMap<PseudoElement, PerPseudoElementSelectorMap>,
/// 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.
/// These are eagerly computed once, and then used to resolve the new
/// computed values on the fly on layout.
precomputed_pseudo_element_decls: HashMap<PseudoElement,
Vec<DeclarationBlock>,
BuildHasherDefault<::fnv::FnvHasher>>,
precomputed_pseudo_element_decls: FnvHashMap<PseudoElement, Vec<DeclarationBlock>>,
rules_source_order: usize,
@ -96,9 +96,9 @@ impl Stylist {
quirks_mode: false,
element_map: PerPseudoElementSelectorMap::new(),
pseudos_map: HashMap::with_hasher(Default::default()),
animations: HashMap::with_hasher(Default::default()),
precomputed_pseudo_element_decls: HashMap::with_hasher(Default::default()),
pseudos_map: Default::default(),
animations: Default::default(),
precomputed_pseudo_element_decls: Default::default(),
rules_source_order: 0,
state_deps: DependencySet::new(),
@ -122,13 +122,13 @@ impl Stylist {
}
self.element_map = PerPseudoElementSelectorMap::new();
self.pseudos_map = HashMap::with_hasher(Default::default());
self.animations = HashMap::with_hasher(Default::default());
self.pseudos_map = Default::default();
self.animations = Default::default();
TheSelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
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.state_deps.clear();
@ -162,8 +162,8 @@ impl Stylist {
// Take apart the StyleRule into individual Rules and insert
// them into the SelectorMap of that priority.
macro_rules! append(
($style_rule: ident, $priority: ident) => {
if !$style_rule.declarations.$priority.is_empty() {
($style_rule: ident, $priority: ident, $importance: expr, $count: ident) => {
if $style_rule.declarations.$count > 0 {
for selector in &$style_rule.selectors {
let map = if let Some(ref pseudo) = selector.pseudo_element {
self.pseudos_map
@ -178,7 +178,8 @@ impl Stylist {
selector: selector.complex_selector.clone(),
declarations: DeclarationBlock {
specificity: selector.specificity,
declarations: $style_rule.declarations.$priority.clone(),
mixed_declarations: $style_rule.declarations.declarations.clone(),
importance: $importance,
source_order: rules_source_order,
},
});
@ -190,8 +191,8 @@ impl Stylist {
for rule in stylesheet.effective_rules(&self.device) {
match *rule {
CSSRule::Style(ref style_rule) => {
append!(style_rule, normal);
append!(style_rule, important);
append!(style_rule, normal, Importance::Normal, normal_count);
append!(style_rule, important, Importance::Important, important_count);
rules_source_order += 1;
for selector in &style_rule.selectors {
@ -275,6 +276,7 @@ impl Stylist {
parent: &Arc<ComputedValues>)
-> Option<Arc<ComputedValues>>
where E: Element<Impl=TheSelectorImpl> +
fmt::Debug +
PresentationalHintsSynthetizer
{
debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy());
@ -343,6 +345,7 @@ impl Stylist {
pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V) -> StyleRelations
where E: Element<Impl=TheSelectorImpl> +
fmt::Debug +
PresentationalHintsSynthetizer,
V: Push<DeclarationBlock> + VecLike<DeclarationBlock>
{
@ -391,10 +394,14 @@ impl Stylist {
// Step 4: Normal style attributes.
if let Some(ref sa) = style_attribute {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
applicable_declarations,
GenericDeclarationBlock::from_declarations(sa.normal.clone()));
if sa.normal_count > 0 {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
applicable_declarations,
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Normal));
}
}
debug!("style attr: {:?}", relations);
@ -409,9 +416,14 @@ impl Stylist {
// Step 6: `!important` style attributes.
if let Some(ref sa) = style_attribute {
Push::push(
applicable_declarations,
GenericDeclarationBlock::from_declarations(sa.important.clone()));
if sa.important_count > 0 {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
applicable_declarations,
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Important));
}
}
debug!("style attr important: {:?}", relations);
@ -442,7 +454,7 @@ impl Stylist {
}
#[inline]
pub fn animations(&self) -> &HashMap<Atom, KeyframesAnimation> {
pub fn animations(&self) -> &FnvHashMap<Atom, KeyframesAnimation> {
&self.animations
}
@ -525,12 +537,12 @@ impl Stylist {
/// Map that contains the CSS rules for a given origin.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
struct PerOriginSelectorMap {
/// Rules that contains at least one property declararion with
/// Rules that contains at least one property declaration with
/// normal importance.
normal: SelectorMap<Vec<PropertyDeclaration>, TheSelectorImpl>,
/// Rules that contains at least one property declararion with
normal: SelectorMap,
/// Rules that contains at least one property declaration with
/// !important.
important: SelectorMap<Vec<PropertyDeclaration>, TheSelectorImpl>,
important: SelectorMap,
}
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-selection]: https://developer.mozilla.org/en/docs/Web/CSS/::selection
[stylist]: http://doc.servo.org/style/selector_matching/struct.Stylist.html
[selectors-selectormap]: http://doc.servo.org/selectors/matching/struct.SelectorMap.html
[selectors-rule]: http://doc.servo.org/selectors/matching/struct.Rule.html
[selectors-selectormap]: http://doc.servo.org/style/selector_matching/struct.SelectorMap.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-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

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)",
"ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
"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)",

View file

@ -365,6 +365,7 @@ dependencies = [
"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)",
"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)",
"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)",

View file

@ -30,10 +30,10 @@ use gecko_string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use glue::GeckoDeclarationBlock;
use libc::uintptr_t;
use selectors::Element;
use selectors::matching::DeclarationBlock;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use snapshot::GeckoElementSnapshot;
use snapshot_helpers;
use std::fmt;
use std::marker::PhantomData;
use std::ops::BitOr;
use std::ptr;
@ -45,10 +45,11 @@ use style::element_state::ElementState;
use style::error_reporting::StdoutErrorReporter;
use style::gecko_selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PseudoElement};
use style::parser::ParserContextExtraData;
use style::properties::PropertyDeclarationBlock;
use style::properties::{ComputedValues, parse_style_attribute};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
use style::refcell::{Ref, RefCell, RefMut};
use style::selector_impl::ElementExt;
use style::selector_matching::DeclarationBlock;
use style::sink::Push;
use url::Url;
@ -382,6 +383,16 @@ pub struct GeckoElement<'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> {
pub unsafe fn from_raw(el: *mut RawGeckoElement) -> GeckoElement<'le> {
GeckoElement {
@ -462,7 +473,7 @@ impl<'le> PartialEq for GeckoElement<'le> {
impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> {
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.
}

View file

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

View file

@ -5,7 +5,7 @@
pub use cssparser::ToCss;
pub use std::sync::Arc;
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::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent};
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_y::computed_value::T as OverflowYContainer;
let mut normal = Vec::new();
let mut important = Vec::new();
let declarations = vec![
(PropertyDeclaration::Width(
DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(70f32)))),
Importance::Normal),
let length = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(70f32)));
normal.push(PropertyDeclaration::Width(length));
(PropertyDeclaration::MinHeight(
DeclaredValue::Value(LengthOrPercentage::Length(Length::from_px(20f32)))),
Importance::Normal),
let min_height = DeclaredValue::Value(LengthOrPercentage::Length(Length::from_px(20f32)));
normal.push(PropertyDeclaration::MinHeight(min_height));
(PropertyDeclaration::Height(
DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(20f32)))),
Importance::Important),
let value = DeclaredValue::Value(inline_block);
normal.push(PropertyDeclaration::Display(value));
(PropertyDeclaration::Display(
DeclaredValue::Value(inline_block)),
Importance::Normal),
let overflow_x = DeclaredValue::Value(OverflowXValue::auto);
normal.push(PropertyDeclaration::OverflowX(overflow_x));
(PropertyDeclaration::OverflowX(
DeclaredValue::Value(OverflowXValue::auto)),
Importance::Normal),
let overflow_y = DeclaredValue::Value(OverflowYContainer(OverflowXValue::auto));
normal.push(PropertyDeclaration::OverflowY(overflow_y));
(PropertyDeclaration::OverflowY(
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 {
normal: Arc::new(normal),
important: Arc::new(important)
declarations: Arc::new(declarations),
// Incorrect, but not used here:
normal_count: 0,
important_count: 0,
};
let css_string = block.to_css_string();
assert_eq!(
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 {
let block = PropertyDeclarationBlock {
normal: Arc::new(properties),
important: Arc::new(Vec::new())
declarations: Arc::new(properties.into_iter().map(|d| (d, Importance::Normal)).collect()),
// Incorrect, but not used here:
normal_count: 0,
important_count: 0,
};
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::parser::ParserContextExtraData;
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::values::specified::{LengthOrPercentageOrAuto, Percentage};
use url::Url;
@ -22,17 +24,42 @@ fn test_parse_stylesheet() {
let css = r"
@namespace url(http://www.w3.org/1999/xhtml);
/* FIXME: only if scripting is enabled */
input[type=hidden i] { display: none !important; }
html , body /**/ { display: block; }
input[type=hidden i] {
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; }
@keyframes foo {
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 stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
Box::new(CSSErrorReporterTest),
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 {
origin: Origin::UserAgent,
media: None,
@ -71,11 +98,15 @@ fn test_parse_stylesheet() {
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![]),
important: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::Value(
declarations: Arc::new(vec![
(PropertyDeclaration::Display(DeclaredValue::Value(
longhands::display::SpecifiedValue::none)),
Importance::Important),
(PropertyDeclaration::Custom(Atom::from("a"), DeclaredValue::Inherit),
Importance::Important),
]),
normal_count: 0,
important_count: 2,
},
}),
CSSRule::Style(StyleRule {
@ -116,11 +147,13 @@ fn test_parse_stylesheet() {
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::Display(DeclaredValue::Value(
declarations: Arc::new(vec![
(PropertyDeclaration::Display(DeclaredValue::Value(
longhands::display::SpecifiedValue::block)),
Importance::Normal),
]),
important: Arc::new(vec![]),
normal_count: 1,
important_count: 0,
},
}),
CSSRule::Style(StyleRule {
@ -150,24 +183,33 @@ fn test_parse_stylesheet() {
},
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::BackgroundClip(DeclaredValue::Initial),
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(
declarations: Arc::new(vec![
(PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
longhands::background_color::SpecifiedValue {
authored: Some("blue".to_owned()),
parsed: cssparser::Color::RGBA(cssparser::RGBA {
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 {
@ -177,16 +219,22 @@ fn test_parse_stylesheet() {
selector: KeyframeSelector::new_for_unit_testing(
vec![KeyframePercentage::new(0.)]),
declarations: Arc::new(vec![
PropertyDeclaration::Width(DeclaredValue::Value(
(PropertyDeclaration::Width(DeclaredValue::Value(
LengthOrPercentageOrAuto::Percentage(Percentage(0.)))),
Importance::Normal),
]),
},
Keyframe {
selector: KeyframeSelector::new_for_unit_testing(
vec![KeyframePercentage::new(1.)]),
declarations: Arc::new(vec![
PropertyDeclaration::Width(DeclaredValue::Value(
(PropertyDeclaration::Width(DeclaredValue::Value(
LengthOrPercentageOrAuto::Percentage(Percentage(1.)))),
Importance::Normal),
(PropertyDeclaration::AnimationPlayState(DeclaredValue::Value(
animation_play_state::SpecifiedValue(
vec![animation_play_state::SingleSpecifiedValue::running]))),
Importance::Normal),
]),
},
]