mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
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:
commit
f8b2be1ea4
24 changed files with 1022 additions and 356 deletions
|
@ -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,10 +184,12 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
|||
}
|
||||
// Step 3
|
||||
} else {
|
||||
if self.owner.get_important_inline_style_declaration(&property).is_some() {
|
||||
if let Some(decl) = self.owner.get_inline_style_declaration(&property) {
|
||||
if decl.1.important() {
|
||||
return DOMString::from("important");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4
|
||||
DOMString::new()
|
||||
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
if let Some(index) = index {
|
||||
Arc::make_mut(&mut declarations.normal).remove(index);
|
||||
return;
|
||||
let mut importance = None;
|
||||
let index = declarations.declarations.iter().position(|&(ref decl, i)| {
|
||||
let matching = decl.matches(property);
|
||||
if matching {
|
||||
importance = Some(i)
|
||||
}
|
||||
|
||||
let index = declarations.important
|
||||
.iter()
|
||||
.position(|decl| decl.matches(property));
|
||||
matching
|
||||
});
|
||||
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(existing_declarations);
|
||||
let existing_declarations = Arc::make_mut(&mut declaration_block.declarations);
|
||||
|
||||
while let Some(mut incoming_declaration) = declarations.pop() {
|
||||
let mut replaced = false;
|
||||
'outer: for incoming_declaration in declarations {
|
||||
for existing_declaration in &mut *existing_declarations {
|
||||
if existing_declaration.name() == incoming_declaration.name() {
|
||||
mem::swap(existing_declaration, &mut incoming_declaration);
|
||||
replaced = true;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if !replaced {
|
||||
// inserting instead of pushing since the declarations are in reverse order
|
||||
existing_declarations.insert(0, incoming_declaration);
|
||||
existing_declarations.push((incoming_declaration, importance));
|
||||
if importance.important() {
|
||||
declaration_block.important_count += 1;
|
||||
} else {
|
||||
declaration_block.normal_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
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 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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
mem::replace(from, new_from);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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> {}
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
1
components/servo/Cargo.lock
generated
1
components/servo/Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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}() {
|
||||
if importance.important() {
|
||||
if seen_important.get_${property.ident}() {
|
||||
block.important_count -= 1;
|
||||
continue
|
||||
}
|
||||
seen.set_${property.ident}()
|
||||
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}()
|
||||
}
|
||||
% else:
|
||||
unreachable!();
|
||||
% endif
|
||||
},
|
||||
% endfor
|
||||
PropertyDeclaration::Custom(ref name, _) => {
|
||||
if seen_custom.contains(name) {
|
||||
if importance.important() {
|
||||
if seen_custom_important.contains(name) {
|
||||
block.important_count -= 1;
|
||||
continue
|
||||
}
|
||||
seen_custom.push(name.clone())
|
||||
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())
|
||||
}
|
||||
}
|
||||
deduplicated.push(declaration)
|
||||
}
|
||||
deduplicated
|
||||
deduplicated.push((declaration, importance))
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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 {
|
||||
if sa.normal_count > 0 {
|
||||
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
|
||||
Push::push(
|
||||
applicable_declarations,
|
||||
GenericDeclarationBlock::from_declarations(sa.normal.clone()));
|
||||
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 {
|
||||
if sa.important_count > 0 {
|
||||
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
|
||||
Push::push(
|
||||
applicable_declarations,
|
||||
GenericDeclarationBlock::from_declarations(sa.important.clone()));
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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
1
ports/cef/Cargo.lock
generated
|
@ -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)",
|
||||
|
|
1
ports/geckolib/Cargo.lock
generated
1
ports/geckolib/Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ mod logical_geometry;
|
|||
mod media_queries;
|
||||
mod parsing;
|
||||
mod properties;
|
||||
mod selector_matching;
|
||||
mod str;
|
||||
mod stylesheets;
|
||||
mod value;
|
||||
|
|
|
@ -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()
|
||||
|
|
103
tests/unit/style/selector_matching.rs
Normal file
103
tests/unit/style/selector_matching.rs
Normal 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);
|
||||
}
|
|
@ -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,15 +183,8 @@ 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 {
|
||||
|
@ -166,8 +192,24 @@ fn test_parse_stylesheet() {
|
|||
}),
|
||||
}
|
||||
)),
|
||||
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),
|
||||
]),
|
||||
},
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue