aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/layout/context.rs3
-rw-r--r--components/layout_thread/lib.rs10
-rw-r--r--components/script/dom/element.rs10
-rw-r--r--components/script_layout_interface/wrapper_traits.rs11
-rw-r--r--components/style/animation.rs32
-rw-r--r--components/style/context.rs4
-rw-r--r--components/style/data.rs14
-rw-r--r--components/style/gecko/context.rs3
-rw-r--r--components/style/gecko/wrapper.rs2
-rw-r--r--components/style/lib.rs1
-rw-r--r--components/style/matching.rs288
-rw-r--r--components/style/parallel.rs6
-rw-r--r--components/style/properties/properties.mako.rs318
-rw-r--r--components/style/rule_tree/mod.rs575
-rw-r--r--components/style/selector_matching.rs120
-rw-r--r--components/style/thread_state.rs4
-rw-r--r--components/style/traversal.rs4
-rw-r--r--components/style/values/specified/mod.rs4
-rw-r--r--components/util/opts.rs9
19 files changed, 893 insertions, 525 deletions
diff --git a/components/layout/context.rs b/components/layout/context.rs
index 308295f4196..98e1ffe4092 100644
--- a/components/layout/context.rs
+++ b/components/layout/context.rs
@@ -53,9 +53,6 @@ fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext)
LOCAL_CONTEXT_KEY.with(|r| {
let mut r = r.borrow_mut();
if let Some(context) = r.clone() {
- if shared_layout_context.style_context.screen_size_changed {
- context.style_context.applicable_declarations_cache.borrow_mut().evict_all();
- }
context
} else {
let font_cache_thread = shared_layout_context.font_cache_thread.lock().unwrap().clone();
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index 537d5935596..60956b00150 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -1170,6 +1170,16 @@ impl LayoutThread {
node.dump_style();
}
+ if opts::get().dump_rule_tree {
+ shared_layout_context.style_context.stylist.rule_tree.dump_stdout();
+ }
+
+ // GC The rule tree.
+ //
+ // FIXME(emilio): The whole point of the free list is not always freeing
+ // the list, find a good heuristic here for that.
+ unsafe { shared_layout_context.style_context.stylist.rule_tree.gc() }
+
// Perform post-style recalculation layout passes.
self.perform_post_style_recalc_layout_passes(&data.reflow_info,
Some(&data.query_type),
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 2217651f778..15a4ef816c8 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -328,10 +328,10 @@ impl LayoutElementHelpers for LayoutJS<Element> {
where V: Push<ApplicableDeclarationBlock>
{
#[inline]
- fn from_declaration(rule: PropertyDeclaration) -> ApplicableDeclarationBlock {
+ fn from_declaration(declaration: PropertyDeclaration) -> ApplicableDeclarationBlock {
ApplicableDeclarationBlock::from_declarations(
Arc::new(RwLock::new(PropertyDeclarationBlock {
- declarations: vec![(rule, Importance::Normal)],
+ declarations: vec![(declaration, Importance::Normal)],
important_count: 0,
})),
Importance::Normal)
@@ -814,10 +814,7 @@ impl Element {
}
None
}
-}
-
-impl Element {
pub fn is_focusable_area(&self) -> bool {
if self.is_actually_disabled() {
return false;
@@ -856,10 +853,7 @@ impl Element {
_ => false,
}
}
-}
-
-impl Element {
pub fn push_new_attribute(&self,
local_name: LocalName,
value: AttrValue,
diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs
index a241ae12099..7bd91bdb0d0 100644
--- a/components/script_layout_interface/wrapper_traits.rs
+++ b/components/script_layout_interface/wrapper_traits.rs
@@ -375,13 +375,13 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
.borrow()
.current_styles().pseudos.contains_key(&style_pseudo) {
let mut data = self.get_style_data().unwrap().borrow_mut();
- let new_style =
+ let new_style_and_rule_node =
context.stylist.precomputed_values_for_pseudo(
&style_pseudo,
Some(&data.current_styles().primary),
false);
data.current_pseudos_mut()
- .insert(style_pseudo.clone(), new_style.unwrap());
+ .insert(style_pseudo.clone(), new_style_and_rule_node.unwrap());
}
}
PseudoElementCascadeType::Lazy => {
@@ -404,7 +404,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
self.get_style_data().unwrap().borrow()
.current_styles().pseudos.get(&style_pseudo)
- .unwrap().clone()
+ .unwrap().0.clone()
}
}
}
@@ -413,7 +413,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
fn selected_style(&self) -> Arc<ServoComputedValues> {
let data = self.get_style_data().unwrap().borrow();
data.current_styles().pseudos
- .get(&PseudoElement::Selection)
+ .get(&PseudoElement::Selection).map(|s| &s.0)
.unwrap_or(&data.current_styles().primary)
.clone()
}
@@ -432,7 +432,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
PseudoElementType::Normal
=> data.current_styles().primary.clone(),
other
- => data.current_styles().pseudos.get(&other.style_pseudo_element()).unwrap().clone(),
+ => data.current_styles().pseudos
+ .get(&other.style_pseudo_element()).unwrap().0.clone(),
}
}
diff --git a/components/style/animation.rs b/components/style/animation.rs
index b106db1812e..0e00cbca0ca 100644
--- a/components/style/animation.rs
+++ b/components/style/animation.rs
@@ -17,7 +17,6 @@ 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 selector_matching::ApplicableDeclarationBlock;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use timer::Timer;
@@ -385,23 +384,26 @@ fn compute_style_for_animation_step(context: &SharedStyleContext,
style_from_cascade: &ComputedValues)
-> ComputedValues {
match step.value {
- // TODO: avoiding this spurious clone might involve having to create
- // an Arc in the below (more common case).
KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
KeyframesStepValue::Declarations { block: ref declarations } => {
- let declaration_block = ApplicableDeclarationBlock {
- mixed_declarations: declarations.clone(),
- importance: Importance::Normal,
- source_order: 0,
- specificity: ::std::u32::MAX,
+ let guard = declarations.read();
+
+ // No !important in keyframes.
+ debug_assert!(guard.declarations.iter()
+ .all(|&(_, importance)| importance == Importance::Normal));
+
+ let iter = || {
+ guard.declarations.iter().rev().map(|&(ref decl, _importance)| decl)
};
- let (computed, _) = properties::cascade(context.viewport_size,
- &[declaration_block],
- Some(previous_style),
- None,
- None,
- context.error_reporter.clone(),
- CascadeFlags::empty());
+
+ let computed =
+ properties::apply_declarations(context.viewport_size,
+ /* is_root = */ false,
+ iter,
+ previous_style,
+ /* cascade_info = */ None,
+ context.error_reporter.clone(),
+ CascadeFlags::empty());
computed
}
}
diff --git a/components/style/context.rs b/components/style/context.rs
index fc3724452a7..2707a799713 100644
--- a/components/style/context.rs
+++ b/components/style/context.rs
@@ -9,7 +9,7 @@ use app_units::Au;
use dom::OpaqueNode;
use error_reporting::ParseErrorReporter;
use euclid::Size2D;
-use matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
+use matching::StyleSharingCandidateCache;
use parking_lot::RwLock;
use selector_matching::Stylist;
use std::cell::RefCell;
@@ -66,7 +66,6 @@ pub struct SharedStyleContext {
}
pub struct LocalStyleContext {
- pub applicable_declarations_cache: RefCell<ApplicableDeclarationsCache>,
pub style_sharing_candidate_cache: RefCell<StyleSharingCandidateCache>,
/// A channel on which new animations that have been triggered by style
/// recalculation can be sent.
@@ -76,7 +75,6 @@ pub struct LocalStyleContext {
impl LocalStyleContext {
pub fn new(local_context_creation_data: &LocalStyleContextCreationInfo) -> Self {
LocalStyleContext {
- applicable_declarations_cache: RefCell::new(ApplicableDeclarationsCache::new()),
style_sharing_candidate_cache: RefCell::new(StyleSharingCandidateCache::new()),
new_animations_sender: local_context_creation_data.new_animations_sender.clone(),
}
diff --git a/components/style/data.rs b/components/style/data.rs
index f6011c81fcb..6f4a34e90ca 100644
--- a/components/style/data.rs
+++ b/components/style/data.rs
@@ -5,6 +5,7 @@
//! Per-node data used in style calculation.
use properties::ComputedValues;
+use rule_tree::StrongRuleNode;
use selector_impl::PseudoElement;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
@@ -12,7 +13,7 @@ use std::mem;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
-type PseudoStylesInner = HashMap<PseudoElement, Arc<ComputedValues>,
+type PseudoStylesInner = HashMap<PseudoElement, (Arc<ComputedValues>, StrongRuleNode),
BuildHasherDefault<::fnv::FnvHasher>>;
#[derive(Clone, Debug)]
pub struct PseudoStyles(PseudoStylesInner);
@@ -39,14 +40,18 @@ pub struct ElementStyles {
/// The results of CSS styling for this node.
pub primary: Arc<ComputedValues>,
+ /// The rule node representing the last rule matched for this node.
+ pub rule_node: StrongRuleNode,
+
/// The results of CSS styling for each pseudo-element (if any).
pub pseudos: PseudoStyles,
}
impl ElementStyles {
- pub fn new(primary: Arc<ComputedValues>) -> Self {
+ pub fn new(primary: Arc<ComputedValues>, rule_node: StrongRuleNode) -> Self {
ElementStyles {
primary: primary,
+ rule_node: rule_node,
pseudos: PseudoStyles::empty(),
}
}
@@ -185,11 +190,6 @@ impl ElementData {
}
}
- pub fn style_text_node(&mut self, style: Arc<ComputedValues>) {
- self.styles = ElementDataStyles::Current(ElementStyles::new(style));
- self.restyle_data = None;
- }
-
pub fn finish_styling(&mut self, styles: ElementStyles) {
debug_assert!(self.styles.is_previous());
self.styles = ElementDataStyles::Current(styles);
diff --git a/components/style/gecko/context.rs b/components/style/gecko/context.rs
index 068b81560d8..4cfb3152b16 100644
--- a/components/style/gecko/context.rs
+++ b/components/style/gecko/context.rs
@@ -13,9 +13,6 @@ fn create_or_get_local_context(shared: &SharedStyleContext) -> Rc<LocalStyleCont
LOCAL_CONTEXT_KEY.with(|r| {
let mut r = r.borrow_mut();
if let Some(context) = r.clone() {
- if shared.screen_size_changed {
- context.applicable_declarations_cache.borrow_mut().evict_all();
- }
context
} else {
let context = Rc::new(LocalStyleContext::new(&shared.local_context_creation_data.lock().unwrap()));
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index d57d4ecd4a0..3723045bae7 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -334,7 +334,7 @@ impl<'le> GeckoElement<'le> {
pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> {
self.borrow_data().and_then(|data| data.current_styles().pseudos
- .get(pseudo).map(|c| c.clone()))
+ .get(pseudo).map(|c| c.0.clone()))
}
pub fn ensure_data(&self) -> &AtomicRefCell<ElementData> {
diff --git a/components/style/lib.rs b/components/style/lib.rs
index 687a54ca644..a398760bd68 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -114,6 +114,7 @@ pub mod parallel;
pub mod parser;
pub mod refcell;
pub mod restyle_hints;
+pub mod rule_tree;
pub mod selector_impl;
pub mod selector_matching;
pub mod sequential;
diff --git a/components/style/matching.rs b/components/style/matching.rs
index 0887ebc823a..ac4bdb0b59a 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -8,26 +8,24 @@
use {Atom, LocalName};
use animation;
-use arc_ptr_eq;
use atomic_refcell::AtomicRefMut;
-use cache::{LRUCache, SimpleHashCache};
+use cache::LRUCache;
use cascade_info::CascadeInfo;
use context::{SharedStyleContext, StyleContext};
use data::{ElementData, ElementStyles, PseudoStyles};
use dom::{TElement, TNode, TRestyleDamage, UnsafeNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, cascade};
use properties::longhands::display::computed_value as display;
-use selector_impl::{PseudoElement, TheSelectorImpl};
+use rule_tree::StrongRuleNode;
+use selector_impl::{TheSelectorImpl, PseudoElement};
use selector_matching::{ApplicableDeclarationBlock, Stylist};
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_PSEUDO_ELEMENTS, MatchingReason, StyleRelations};
use sink::ForgetfulSink;
-use smallvec::SmallVec;
use std::collections::HashMap;
-use std::hash::{BuildHasherDefault, Hash, Hasher};
+use std::hash::BuildHasherDefault;
use std::mem;
-use std::ops::Deref;
use std::slice::IterMut;
use std::sync::Arc;
use util::opts;
@@ -53,7 +51,7 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
}
pub struct ApplicableDeclarations {
- pub normal: SmallVec<[ApplicableDeclarationBlock; 16]>,
+ pub normal: Vec<ApplicableDeclarationBlock>,
pub per_pseudo: HashMap<PseudoElement,
Vec<ApplicableDeclarationBlock>,
BuildHasherDefault<::fnv::FnvHasher>>,
@@ -65,7 +63,7 @@ pub struct ApplicableDeclarations {
impl ApplicableDeclarations {
pub fn new() -> Self {
let mut applicable_declarations = ApplicableDeclarations {
- normal: SmallVec::new(),
+ normal: Vec::with_capacity(16),
per_pseudo: HashMap::with_hasher(Default::default()),
normal_shareable: false,
};
@@ -78,105 +76,6 @@ impl ApplicableDeclarations {
}
}
-#[derive(Clone)]
-pub struct ApplicableDeclarationsCacheEntry {
- pub declarations: Vec<ApplicableDeclarationBlock>,
-}
-
-impl ApplicableDeclarationsCacheEntry {
- fn new(declarations: Vec<ApplicableDeclarationBlock>) -> ApplicableDeclarationsCacheEntry {
- ApplicableDeclarationsCacheEntry {
- declarations: declarations,
- }
- }
-}
-
-impl PartialEq for ApplicableDeclarationsCacheEntry {
- fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
- let this_as_query = ApplicableDeclarationsCacheQuery::new(&*self.declarations);
- this_as_query.eq(other)
- }
-}
-impl Eq for ApplicableDeclarationsCacheEntry {}
-
-impl Hash for ApplicableDeclarationsCacheEntry {
- fn hash<H: Hasher>(&self, state: &mut H) {
- let tmp = ApplicableDeclarationsCacheQuery::new(&*self.declarations);
- tmp.hash(state);
- }
-}
-
-struct ApplicableDeclarationsCacheQuery<'a> {
- declarations: &'a [ApplicableDeclarationBlock],
-}
-
-impl<'a> ApplicableDeclarationsCacheQuery<'a> {
- fn new(declarations: &'a [ApplicableDeclarationBlock]) -> ApplicableDeclarationsCacheQuery<'a> {
- ApplicableDeclarationsCacheQuery {
- declarations: declarations,
- }
- }
-}
-
-impl<'a> PartialEq for ApplicableDeclarationsCacheQuery<'a> {
- fn eq(&self, other: &ApplicableDeclarationsCacheQuery<'a>) -> bool {
- 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> {}
-
-impl<'a> PartialEq<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
- fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
- let other_as_query = ApplicableDeclarationsCacheQuery::new(&other.declarations);
- self.eq(&other_as_query)
- }
-}
-
-impl<'a> Hash for ApplicableDeclarationsCacheQuery<'a> {
- fn hash<H: Hasher>(&self, state: &mut H) {
- for declaration in self.declarations {
- // Each declaration contians an Arc, which is a stable
- // pointer; we use that for hashing and equality.
- let ptr: *const _ = Arc::deref(&declaration.mixed_declarations);
- ptr.hash(state);
- declaration.importance.hash(state);
- }
- }
-}
-
-static APPLICABLE_DECLARATIONS_CACHE_SIZE: usize = 32;
-
-pub struct ApplicableDeclarationsCache {
- cache: SimpleHashCache<ApplicableDeclarationsCacheEntry, Arc<ComputedValues>>,
-}
-
-impl ApplicableDeclarationsCache {
- pub fn new() -> Self {
- ApplicableDeclarationsCache {
- cache: SimpleHashCache::new(APPLICABLE_DECLARATIONS_CACHE_SIZE),
- }
- }
-
- pub fn find(&self, declarations: &[ApplicableDeclarationBlock]) -> Option<Arc<ComputedValues>> {
- match self.cache.find(&ApplicableDeclarationsCacheQuery::new(declarations)) {
- None => None,
- Some(ref values) => Some((*values).clone()),
- }
- }
-
- pub fn insert(&mut self, declarations: Vec<ApplicableDeclarationBlock>, style: Arc<ComputedValues>) {
- self.cache.insert(ApplicableDeclarationsCacheEntry::new(declarations), style)
- }
-
- pub fn evict_all(&mut self) {
- self.cache.evict_all();
- }
-}
-
/// Information regarding a candidate.
///
/// TODO: We can stick a lot more info here.
@@ -184,8 +83,6 @@ impl ApplicableDeclarationsCache {
struct StyleSharingCandidate {
/// The node, guaranteed to be an element.
node: UnsafeNode,
- /// The cached computed style, here for convenience.
- style: Arc<ComputedValues>,
/// The cached common style affecting attribute info.
common_style_affecting_attributes: Option<CommonStyleAffectingAttributes>,
/// the cached class names.
@@ -195,7 +92,6 @@ struct StyleSharingCandidate {
impl PartialEq<StyleSharingCandidate> for StyleSharingCandidate {
fn eq(&self, other: &Self) -> bool {
self.node == other.node &&
- arc_ptr_eq(&self.style, &other.style) &&
self.common_style_affecting_attributes == other.common_style_affecting_attributes
}
}
@@ -234,7 +130,7 @@ fn element_matches_candidate<E: TElement>(element: &E,
candidate: &mut StyleSharingCandidate,
candidate_element: &E,
shared_context: &SharedStyleContext)
- -> Result<Arc<ComputedValues>, CacheMiss> {
+ -> Result<(Arc<ComputedValues>, StrongRuleNode), CacheMiss> {
macro_rules! miss {
($miss: ident) => {
return Err(CacheMiss::$miss);
@@ -295,7 +191,11 @@ fn element_matches_candidate<E: TElement>(element: &E,
miss!(NonCommonAttrRules)
}
- Ok(candidate.style.clone())
+ let data = candidate_element.borrow_data().unwrap();
+ let current_styles = data.get_current_styles().unwrap();
+
+ Ok((current_styles.primary.clone(),
+ current_styles.rule_node.clone()))
}
fn have_same_common_style_affecting_attributes<E: TElement>(element: &E,
@@ -458,7 +358,6 @@ impl StyleSharingCandidateCache {
self.cache.insert(StyleSharingCandidate {
node: element.as_node().to_unsafe(),
- style: style.clone(),
common_style_affecting_attributes: None,
class_attributes: None,
}, ());
@@ -490,7 +389,6 @@ pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps?
struct CascadeBooleans {
shareable: bool,
- cacheable: bool,
animate: bool,
}
@@ -503,23 +401,16 @@ trait PrivateMatchMethods: TElement {
context: &Ctx,
parent_style: Option<&Arc<ComputedValues>>,
old_style: Option<&Arc<ComputedValues>>,
- applicable_declarations: &[ApplicableDeclarationBlock],
- applicable_declarations_cache:
- &mut ApplicableDeclarationsCache,
+ applicable_declarations: &mut Vec<ApplicableDeclarationBlock>,
booleans: CascadeBooleans)
- -> Arc<ComputedValues>
+ -> (Arc<ComputedValues>, StrongRuleNode)
where Ctx: StyleContext<'a>
{
- let mut cacheable = booleans.cacheable;
let shared_context = context.shared_context();
-
- // Don’t cache applicable declarations for elements with a style attribute.
- // Since the style attribute contributes to that set, no other element would have the same set
- // and the cache would not be effective anyway.
- // This also works around the test failures at
- // https://github.com/servo/servo/pull/13459#issuecomment-250717584
- let has_style_attribute = self.style_attribute().is_some();
- cacheable = cacheable && !has_style_attribute;
+ let rule_node =
+ shared_context.stylist.rule_tree
+ .insert_ordered_rules(
+ applicable_declarations.drain(..).map(|d| (d.source, d.importance)));
let mut cascade_info = CascadeInfo::new();
let mut cascade_flags = CascadeFlags::empty();
@@ -527,26 +418,18 @@ trait PrivateMatchMethods: TElement {
cascade_flags.insert(SHAREABLE)
}
- let (this_style, is_cacheable) = match parent_style {
+ let this_style = match parent_style {
Some(ref parent_style) => {
- let cache_entry = applicable_declarations_cache.find(applicable_declarations);
- let cached_computed_values = match cache_entry {
- Some(ref style) => Some(&**style),
- None => None,
- };
-
cascade(shared_context.viewport_size,
- applicable_declarations,
+ &rule_node,
Some(&***parent_style),
- cached_computed_values,
Some(&mut cascade_info),
shared_context.error_reporter.clone(),
cascade_flags)
}
None => {
cascade(shared_context.viewport_size,
- applicable_declarations,
- None,
+ &rule_node,
None,
Some(&mut cascade_info),
shared_context.error_reporter.clone(),
@@ -555,43 +438,30 @@ trait PrivateMatchMethods: TElement {
};
cascade_info.finish(&self.as_node());
- cacheable = cacheable && is_cacheable;
-
let mut this_style = Arc::new(this_style);
if booleans.animate {
let new_animations_sender = &context.local_context().new_animations_sender;
let this_opaque = self.as_node().opaque();
// Trigger any present animations if necessary.
- let mut animations_started = animation::maybe_start_animations(
- &shared_context,
- new_animations_sender,
- this_opaque,
- &this_style);
+ animation::maybe_start_animations(&shared_context,
+ new_animations_sender,
+ this_opaque, &this_style);
// Trigger transitions if necessary. This will reset `this_style` back
// to its old value if it did trigger a transition.
if let Some(ref style) = old_style {
- animations_started |=
- animation::start_transitions_if_applicable(
- new_animations_sender,
- this_opaque,
- self.as_node().to_unsafe(),
- &**style,
- &mut this_style,
- &shared_context.timer);
+ animation::start_transitions_if_applicable(
+ new_animations_sender,
+ this_opaque,
+ self.as_node().to_unsafe(),
+ &**style,
+ &mut this_style,
+ &shared_context.timer);
}
-
- cacheable = cacheable && !animations_started
- }
-
- // Cache the resolved style if it was cacheable.
- if cacheable {
- applicable_declarations_cache.insert(applicable_declarations.to_vec(),
- this_style.clone());
}
- this_style
+ (this_style, rule_node)
}
fn update_animations_for_cascade(&self,
@@ -636,7 +506,7 @@ trait PrivateMatchMethods: TElement {
fn share_style_with_candidate_if_possible(&self,
shared_context: &SharedStyleContext,
candidate: &mut StyleSharingCandidate)
- -> Result<Arc<ComputedValues>, CacheMiss> {
+ -> Result<(Arc<ComputedValues>, StrongRuleNode), CacheMiss> {
let candidate_element = unsafe {
Self::ConcreteNode::from_unsafe(&candidate.node).as_element().unwrap()
};
@@ -652,9 +522,10 @@ pub trait MatchMethods : TElement {
fn match_element(&self,
stylist: &Stylist,
parent_bf: Option<&BloomFilter>,
- applicable_declarations: &mut ApplicableDeclarations)
+ mut applicable_declarations: &mut ApplicableDeclarations)
-> StyleRelations {
use traversal::relations_are_shareable;
+
let style_attribute = self.style_attribute();
let mut relations =
@@ -711,7 +582,7 @@ pub trait MatchMethods : TElement {
for (i, &mut (ref mut candidate, ())) in style_sharing_candidate_cache.iter_mut().enumerate() {
let sharing_result = self.share_style_with_candidate_if_possible(shared_context, candidate);
match sharing_result {
- Ok(shared_style) => {
+ Ok((shared_style, rule_node)) => {
// Yay, cache hit. Share the style.
// TODO: add the display: none optimisation here too! Even
@@ -728,7 +599,7 @@ pub trait MatchMethods : TElement {
}
};
- data.finish_styling(ElementStyles::new(shared_style));
+ data.finish_styling(ElementStyles::new(shared_style, rule_node));
return StyleSharingResult::StyleWasShared(i, damage)
}
@@ -848,7 +719,7 @@ pub trait MatchMethods : TElement {
context: &Ctx,
mut data: AtomicRefMut<ElementData>,
parent: Option<Self>,
- applicable_declarations: &ApplicableDeclarations)
+ mut applicable_declarations: ApplicableDeclarations)
where Ctx: StyleContext<'a>
{
// Get our parent's style.
@@ -857,40 +728,39 @@ pub trait MatchMethods : TElement {
let mut new_styles;
- let mut applicable_declarations_cache =
- context.local_context().applicable_declarations_cache.borrow_mut();
-
let damage = {
- // Update animations before the cascade. This may modify the value of the old primary
- // style.
- let cacheable = data.previous_styles_mut().map_or(true,
- |x| !self.update_animations_for_cascade(context.shared_context(), &mut x.primary));
let shareable = applicable_declarations.normal_shareable;
+
let (old_primary, old_pseudos) = match data.previous_styles_mut() {
None => (None, None),
- Some(x) => (Some(&x.primary), Some(&mut x.pseudos)),
+ Some(previous) => {
+ // Update animations before the cascade. This may modify the
+ // value of the old primary style.
+ self.update_animations_for_cascade(context.shared_context(),
+ &mut previous.primary);
+ (Some(&previous.primary), Some(&mut previous.pseudos))
+ }
};
-
- new_styles = ElementStyles::new(
+ let (new_style, rule_node) =
self.cascade_node_pseudo_element(context,
- parent_style.clone(),
+ parent_style,
old_primary,
- &applicable_declarations.normal,
- &mut applicable_declarations_cache,
+ &mut applicable_declarations.normal,
CascadeBooleans {
shareable: shareable,
- cacheable: cacheable,
animate: true,
- }));
+ });
+
+ new_styles = ElementStyles::new(new_style, rule_node);
let damage =
self.compute_damage_and_cascade_pseudos(old_primary,
old_pseudos,
&new_styles.primary,
&mut new_styles.pseudos,
- context, applicable_declarations,
- &mut applicable_declarations_cache);
+ context,
+ &mut applicable_declarations);
self.as_node().set_can_be_fragmented(parent.map_or(false, |p| {
p.as_node().can_be_fragmented() ||
@@ -912,8 +782,7 @@ pub trait MatchMethods : TElement {
new_primary: &Arc<ComputedValues>,
new_pseudos: &mut PseudoStyles,
context: &Ctx,
- applicable_declarations: &ApplicableDeclarations,
- mut applicable_declarations_cache: &mut ApplicableDeclarationsCache)
+ applicable_declarations: &mut ApplicableDeclarations)
-> Self::ConcreteRestyleDamage
where Ctx: StyleContext<'a>
{
@@ -953,58 +822,55 @@ pub trait MatchMethods : TElement {
let rebuild_and_reflow =
Self::ConcreteRestyleDamage::rebuild_and_reflow();
- let no_damage = Self::ConcreteRestyleDamage::empty();
debug_assert!(new_pseudos.is_empty());
<Self as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
- let applicable_declarations_for_this_pseudo =
- applicable_declarations.per_pseudo.get(&pseudo).unwrap();
+ let mut applicable_declarations_for_this_pseudo =
+ applicable_declarations.per_pseudo.get_mut(&pseudo).unwrap();
let has_declarations =
!applicable_declarations_for_this_pseudo.is_empty();
// Grab the old pseudo style for analysis.
- let mut old_pseudo_style = old_pseudos.as_mut().and_then(|x| x.remove(&pseudo));
+ let mut maybe_old_pseudo_style_and_rule_node =
+ old_pseudos.as_mut().and_then(|x| x.remove(&pseudo));
if has_declarations {
// We have declarations, so we need to cascade. Compute parameters.
let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
- let cacheable = if animate && old_pseudo_style.is_some() {
- // Update animations before the cascade. This may modify
- // the value of old_pseudo_style.
- !self.update_animations_for_cascade(context.shared_context(),
- old_pseudo_style.as_mut().unwrap())
- } else {
- true
- };
-
- let new_pseudo_style =
+ if animate {
+ if let Some((ref mut old_pseudo_style, _)) = maybe_old_pseudo_style_and_rule_node {
+ // Update animations before the cascade. This may modify
+ // the value of old_pseudo_style.
+ self.update_animations_for_cascade(context.shared_context(),
+ old_pseudo_style);
+ }
+ }
+
+ let (new_pseudo_style, new_rule_node) =
self.cascade_node_pseudo_element(context, Some(new_primary),
- old_pseudo_style.as_ref(),
- &*applicable_declarations_for_this_pseudo,
- &mut applicable_declarations_cache,
+ maybe_old_pseudo_style_and_rule_node.as_ref().map(|s| &s.0),
+ &mut applicable_declarations_for_this_pseudo,
CascadeBooleans {
shareable: false,
- cacheable: cacheable,
animate: animate,
});
// Compute restyle damage unless we've already maxed it out.
if damage != rebuild_and_reflow {
- damage = damage | match old_pseudo_style {
+ damage = damage | match maybe_old_pseudo_style_and_rule_node {
None => rebuild_and_reflow,
- Some(ref old) => self.compute_restyle_damage(Some(old), &new_pseudo_style,
- Some(&pseudo)),
+ Some((ref old, _)) => self.compute_restyle_damage(Some(old), &new_pseudo_style,
+ Some(&pseudo)),
};
}
// Insert the new entry into the map.
- let existing = new_pseudos.insert(pseudo, new_pseudo_style);
+ let existing = new_pseudos.insert(pseudo, (new_pseudo_style, new_rule_node));
debug_assert!(existing.is_none());
} else {
- damage = damage | match old_pseudo_style {
- Some(_) => rebuild_and_reflow,
- None => no_damage,
+ if maybe_old_pseudo_style_and_rule_node.is_some() {
+ damage = rebuild_and_reflow;
}
}
});
diff --git a/components/style/parallel.rs b/components/style/parallel.rs
index 8c30868224d..6740c4f379c 100644
--- a/components/style/parallel.rs
+++ b/components/style/parallel.rs
@@ -33,7 +33,8 @@ pub fn run_queue_with_custom_work_data_type<To, F, SharedContext: Sync>(
queue: &mut WorkQueue<SharedContext, WorkQueueData>,
callback: F,
shared: &SharedContext)
- where To: 'static + Send, F: FnOnce(&mut WorkQueue<SharedContext, To>) {
+ where To: 'static + Send, F: FnOnce(&mut WorkQueue<SharedContext, To>)
+{
let queue: &mut WorkQueue<SharedContext, To> = unsafe {
mem::transmute(queue)
};
@@ -73,7 +74,8 @@ pub fn traverse_dom<N, C>(root: N,
#[inline(always)]
fn top_down_dom<N, C>(unsafe_nodes: UnsafeNodeList,
proxy: &mut WorkerProxy<C::SharedContext, UnsafeNodeList>)
- where N: TNode, C: DomTraversalContext<N> {
+ where N: TNode, C: DomTraversalContext<N>
+{
let context = C::new(proxy.user_data(), unsafe_nodes.1);
let mut discovered_child_nodes = vec![];
diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs
index c0543d7ab3f..9ae5a2f8e46 100644
--- a/components/style/properties/properties.mako.rs
+++ b/components/style/properties/properties.mako.rs
@@ -28,12 +28,12 @@ use computed_values;
#[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide};
use logical_geometry::WritingMode;
use parser::{ParserContext, ParserContextExtraData};
-use selector_matching::{ApplicableDeclarationBlock, ApplicableDeclarationBlockReadGuard};
use stylesheets::Origin;
#[cfg(feature = "servo")] use values::LocalToCss;
use values::HasViewportPercentage;
-use values::computed::{self, ToComputedValue};
+use values::computed;
use cascade_info::CascadeInfo;
+use rule_tree::StrongRuleNode;
#[cfg(feature = "servo")] use values::specified::BorderStyle;
use self::property_bit_field::PropertyBitField;
@@ -1382,120 +1382,6 @@ mod lazy_static_module {
}
}
-/// Fast path for the function below. Only computes new inherited styles.
-#[allow(unused_mut, unused_imports)]
-fn cascade_with_cached_declarations(
- viewport_size: Size2D<Au>,
- applicable_declarations: &[ApplicableDeclarationBlockReadGuard],
- shareable: bool,
- parent_style: &ComputedValues,
- cached_style: &ComputedValues,
- custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
- mut cascade_info: Option<<&mut CascadeInfo>,
- mut error_reporter: StdBox<ParseErrorReporter + Send>)
- -> ComputedValues {
- let mut context = computed::Context {
- is_root_element: false,
- viewport_size: viewport_size,
- inherited_style: parent_style,
- style: ComputedValues::new(
- custom_properties,
- shareable,
- WritingMode::empty(),
- parent_style.root_font_size(),
- % for style_struct in data.active_style_structs():
- % if style_struct.inherited:
- parent_style
- % else:
- cached_style
- % endif
- .clone_${style_struct.name_lower}(),
- % endfor
- ),
- };
- let mut seen = PropertyBitField::new();
- // Declaration blocks are stored in increasing precedence order,
- // we want them in decreasing order here.
- for block in applicable_declarations.iter().rev() {
- for declaration in block.iter().rev() {
- match *declaration {
- % for style_struct in data.active_style_structs():
- % for property in style_struct.longhands:
- % if not property.derived_from:
- PropertyDeclaration::${property.camel_case}(ref
- ${'_' if not style_struct.inherited else ''}declared_value)
- => {
- % if style_struct.inherited:
- if seen.get_${property.ident}() {
- continue
- }
- seen.set_${property.ident}();
- let custom_props = context.style().custom_properties();
- substitute_variables_${property.ident}(
- declared_value, &custom_props,
- |value| {
- if let Some(ref mut cascade_info) = cascade_info {
- cascade_info.on_cascade_property(&declaration,
- &value);
- }
- match *value {
- DeclaredValue::Value(ref specified_value)
- => {
- let computed = specified_value.to_computed_value(&context);
- context.mutate_style().mutate_${style_struct.name_lower}()
- .set_${property.ident}(computed);
- },
- DeclaredValue::Initial
- => {
- // FIXME(bholley): We may want set_X_to_initial_value() here.
- let initial = longhands::${property.ident}::get_initial_value();
- context.mutate_style().mutate_${style_struct.name_lower}()
- .set_${property.ident}(initial);
- },
- DeclaredValue::Inherit => {
- // This is a bit slow, but this is rare so it shouldn't
- // matter.
- //
- // FIXME: is it still?
- let inherited_struct = parent_style.get_${style_struct.ident}();
- context.mutate_style().mutate_${style_struct.name_lower}()
- .copy_${property.ident}_from(inherited_struct);
- }
- DeclaredValue::WithVariables { .. } => unreachable!()
- }
- }, &mut error_reporter);
- % endif
-
- % if property.name in data.derived_longhands:
- % for derived in data.derived_longhands[property.name]:
- longhands::${derived.ident}
- ::derive_from_${property.ident}(&mut context);
- % endfor
- % endif
- }
- % else:
- PropertyDeclaration::${property.camel_case}(_) => {
- // Do not allow stylesheets to set derived properties.
- }
- % endif
- % endfor
- % endfor
- PropertyDeclaration::Custom(..) => {}
- }
- }
- }
-
- if seen.get_font_style() || seen.get_font_weight() || seen.get_font_stretch() ||
- seen.get_font_family() {
- context.mutate_style().mutate_font().compute_font_hash();
- }
-
- let mode = get_writing_mode(context.style.get_inheritedbox());
- context.style.set_writing_mode(mode);
-
- context.style
-}
-
pub type CascadePropertyFn =
extern "Rust" fn(declaration: &PropertyDeclaration,
inherited_style: &ComputedValues,
@@ -1523,71 +1409,90 @@ bitflags! {
}
}
-/// Performs the CSS cascade, computing new styles for an element from its parent style and
-/// optionally a cached related style. The arguments are:
+/// Performs the CSS cascade, computing new styles for an element from its parent style.
+///
+/// The arguments are:
///
/// * `viewport_size`: The size of the initial viewport.
///
-/// * `applicable_declarations`: The list of CSS rules that matched.
+/// * `rule_node`: The rule node in the tree that represent the CSS rules that
+/// matched.
///
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
///
-/// * `cached_style`: If present, cascading is short-circuited for everything but inherited
-/// values and these values are used instead. Obviously, you must be careful when supplying
-/// this that it is safe to only provide inherited declarations. If `parent_style` is `None`,
-/// this is ignored.
-///
+/// Returns the computed values.
/// * `flags`: Various flags.
///
-/// Returns the computed values and a boolean indicating whether the result is cacheable.
pub fn cascade(viewport_size: Size2D<Au>,
- applicable_declarations: &[ApplicableDeclarationBlock],
+ rule_node: &StrongRuleNode,
parent_style: Option<<&ComputedValues>,
- cached_style: Option<<&ComputedValues>,
- mut cascade_info: Option<<&mut CascadeInfo>,
- mut error_reporter: StdBox<ParseErrorReporter + Send>,
+ cascade_info: Option<<&mut CascadeInfo>,
+ error_reporter: StdBox<ParseErrorReporter + Send>,
flags: CascadeFlags)
- -> (ComputedValues, bool) {
- let initial_values = ComputedValues::initial_values();
+ -> ComputedValues {
let (is_root_element, inherited_style) = match parent_style {
Some(parent_style) => (false, parent_style),
- None => (true, initial_values),
+ None => (true, ComputedValues::initial_values()),
};
+ // Hold locks until after the apply_declarations() call returns.
+ // Use filter_map because the root node has no style source.
+ let lock_guards = rule_node.self_and_ancestors().filter_map(|node| {
+ node.style_source().map(|source| (source.read(), node.importance()))
+ }).collect::<Vec<_>>();
+ let iter_declarations = || {
+ lock_guards.iter().flat_map(|&(ref source, source_importance)| {
+ source.declarations.iter()
+ // Yield declarations later in source order (with more precedence) first.
+ .rev()
+ .filter_map(move |&(ref declaration, declaration_importance)| {
+ if declaration_importance == source_importance {
+ Some(declaration)
+ } else {
+ None
+ }
+ })
+ })
+ };
+ apply_declarations(viewport_size,
+ is_root_element,
+ iter_declarations,
+ inherited_style,
+ cascade_info,
+ error_reporter,
+ flags)
+}
- // Aquire locks for at least the lifetime of `specified_custom_properties`.
- let applicable_declarations = applicable_declarations.iter()
- .map(|block| block.read())
- .collect::<Vec<_>>();
-
+/// NOTE: This function expects the declaration with more priority to appear
+/// first.
+pub fn apply_declarations<'a, F, I>(viewport_size: Size2D<Au>,
+ is_root_element: bool,
+ iter_declarations: F,
+ inherited_style: &ComputedValues,
+ mut cascade_info: Option<<&mut CascadeInfo>,
+ mut error_reporter: StdBox<ParseErrorReporter + Send>,
+ flags: CascadeFlags)
+ -> ComputedValues
+ where F: Fn() -> I, I: Iterator<Item = &'a PropertyDeclaration>
+{
let inherited_custom_properties = inherited_style.custom_properties();
- let mut specified_custom_properties = None;
+ let mut custom_properties = None;
let mut seen_custom = HashSet::new();
- for block in applicable_declarations.iter().rev() {
- for declaration in block.iter().rev() {
- match *declaration {
- PropertyDeclaration::Custom(ref name, ref value) => {
- ::custom_properties::cascade(
- &mut specified_custom_properties, &inherited_custom_properties,
- &mut seen_custom, name, value)
- }
- _ => {}
+ for declaration in iter_declarations() {
+ match *declaration {
+ PropertyDeclaration::Custom(ref name, ref value) => {
+ ::custom_properties::cascade(
+ &mut custom_properties, &inherited_custom_properties,
+ &mut seen_custom, name, value)
}
+ _ => {}
}
}
- let custom_properties = ::custom_properties::finish_cascade(
- specified_custom_properties, &inherited_custom_properties);
- if let (Some(cached_style), Some(parent_style)) = (cached_style, parent_style) {
- let style = cascade_with_cached_declarations(viewport_size,
- &applicable_declarations,
- flags.contains(SHAREABLE),
- parent_style,
- cached_style,
- custom_properties,
- cascade_info,
- error_reporter);
- return (style, false)
- }
+ let custom_properties =
+ ::custom_properties::finish_cascade(
+ custom_properties, &inherited_custom_properties);
+
+ let initial_values = ComputedValues::initial_values();
let starting_style = if !flags.contains(INHERIT_ALL) {
ComputedValues::new(custom_properties,
@@ -1620,54 +1525,61 @@ pub fn cascade(viewport_size: Size2D<Au>,
style: starting_style,
};
- // Set computed values, overwriting earlier declarations for the same property.
+ // Set computed values, overwriting earlier declarations for the same
+ // property.
let mut cacheable = true;
let mut seen = PropertyBitField::new();
- // Declaration blocks are stored in increasing precedence order, we want them in decreasing
- // order here.
+
+ // Declaration blocks are stored in increasing precedence order, we want
+ // them in decreasing order here.
//
- // We could (and used to) use a pattern match here, but that bloats this function to over 100K
- // of compiled code! To improve i-cache behavior, we outline the individual functions and use
+ // We could (and used to) use a pattern match here, but that bloats this
+ // function to over 100K of compiled code!
+ //
+ // To improve i-cache behavior, we outline the individual functions and use
// virtual dispatch instead.
ComputedValues::do_cascade_property(|cascade_property| {
% for category_to_cascade_now in ["early", "other"]:
- for block in applicable_declarations.iter().rev() {
- for declaration in block.iter().rev() {
- if let PropertyDeclaration::Custom(..) = *declaration {
- continue
- }
- // The computed value of some properties depends on the (sometimes computed)
- // value of *other* properties.
- // So we classify properties into "early" and "other",
- // such that the only dependencies can be from "other" to "early".
- // We iterate applicable_declarations twice, first cascading "early" properties
- // then "other".
- // Unfortunately, it’s not easy to check that this classification is correct.
- let is_early_property = matches!(*declaration,
- PropertyDeclaration::FontSize(_) |
- PropertyDeclaration::Color(_) |
- PropertyDeclaration::Position(_) |
- PropertyDeclaration::Float(_) |
- PropertyDeclaration::TextDecoration${'' if product == 'servo' else 'Line'}(_) |
- PropertyDeclaration::WritingMode(_)
- );
- if
- % if category_to_cascade_now == "early":
- !
- % endif
- is_early_property
- {
- continue
- }
- let discriminant = declaration.discriminant_value();
- (cascade_property[discriminant])(declaration,
- inherited_style,
- &mut context,
- &mut seen,
- &mut cacheable,
- &mut cascade_info,
- &mut error_reporter);
+ for declaration in iter_declarations() {
+ if let PropertyDeclaration::Custom(..) = *declaration {
+ continue
+ }
+ // The computed value of some properties depends on the
+ // (sometimes computed) value of *other* properties.
+ //
+ // So we classify properties into "early" and "other", such that
+ // the only dependencies can be from "other" to "early".
+ //
+ // We iterate applicable_declarations twice, first cascading
+ // "early" properties then "other".
+ //
+ // Unfortunately, it’s not easy to check that this
+ // classification is correct.
+ let is_early_property = matches!(*declaration,
+ PropertyDeclaration::FontSize(_) |
+ PropertyDeclaration::Color(_) |
+ PropertyDeclaration::Position(_) |
+ PropertyDeclaration::Float(_) |
+ PropertyDeclaration::TextDecoration${'' if product == 'servo' else 'Line'}(_) |
+ PropertyDeclaration::WritingMode(_)
+ );
+ if
+ % if category_to_cascade_now == "early":
+ !
+ % endif
+ is_early_property
+ {
+ continue
}
+
+ let discriminant = declaration.discriminant_value();
+ (cascade_property[discriminant])(declaration,
+ inherited_style,
+ &mut context,
+ &mut seen,
+ &mut cacheable,
+ &mut cascade_info,
+ &mut error_reporter);
}
% endfor
});
@@ -1753,6 +1665,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
}
% endfor
+
% if product == "gecko":
style.mutate_background().fill_arrays();
style.mutate_svg().fill_arrays();
@@ -1776,7 +1689,7 @@ pub fn cascade(viewport_size: Size2D<Au>,
let mode = get_writing_mode(style.get_inheritedbox());
style.set_writing_mode(mode);
- (style, cacheable)
+ style
}
#[cfg(feature = "servo")]
@@ -1972,7 +1885,6 @@ pub fn modify_style_for_inline_absolute_hypothetical_fragment(style: &mut Arc<Co
}
}
-
// FIXME: https://github.com/w3c/csswg-drafts/issues/580
pub fn is_supported_property(property: &str) -> bool {
match_ignore_ascii_case! { property,
diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs
new file mode 100644
index 00000000000..5534d76323a
--- /dev/null
+++ b/components/style/rule_tree/mod.rs
@@ -0,0 +1,575 @@
+/* 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/. */
+
+#![allow(unsafe_code)]
+
+use arc_ptr_eq;
+#[cfg(feature = "servo")]
+use heapsize::HeapSizeOf;
+use owning_handle::OwningHandle;
+use parking_lot::{RwLock, RwLockReadGuard};
+use properties::{Importance, PropertyDeclarationBlock};
+use std::io::{self, Write};
+use std::ptr;
+use std::sync::Arc;
+use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
+use stylesheets::StyleRule;
+use thread_state;
+
+#[derive(Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct RuleTree {
+ root: StrongRuleNode,
+}
+
+#[derive(Debug, Clone)]
+pub enum StyleSource {
+ Style(Arc<RwLock<StyleRule>>),
+ Declarations(Arc<RwLock<PropertyDeclarationBlock>>),
+}
+
+type StyleSourceGuardHandle<'a> =
+ OwningHandle<
+ RwLockReadGuard<'a, StyleRule>,
+ RwLockReadGuard<'a, PropertyDeclarationBlock>>;
+
+pub enum StyleSourceGuard<'a> {
+ Style(StyleSourceGuardHandle<'a>),
+ Declarations(RwLockReadGuard<'a, PropertyDeclarationBlock>),
+}
+
+impl<'a> ::std::ops::Deref for StyleSourceGuard<'a> {
+ type Target = PropertyDeclarationBlock;
+
+ fn deref(&self) -> &Self::Target {
+ match *self {
+ StyleSourceGuard::Declarations(ref block) => &*block,
+ StyleSourceGuard::Style(ref handle) => &*handle,
+ }
+ }
+}
+
+impl StyleSource {
+ #[inline]
+ fn ptr_equals(&self, other: &Self) -> bool {
+ use self::StyleSource::*;
+ match (self, other) {
+ (&Style(ref one), &Style(ref other)) => arc_ptr_eq(one, other),
+ (&Declarations(ref one), &Declarations(ref other)) => arc_ptr_eq(one, other),
+ _ => false,
+ }
+ }
+
+ fn dump<W: Write>(&self, writer: &mut W) {
+ use self::StyleSource::*;
+
+ if let Style(ref rule) = *self {
+ let _ = write!(writer, "{:?}", rule.read().selectors);
+ }
+
+ let _ = write!(writer, " -> {:?}", self.read().declarations);
+ }
+
+ #[inline]
+ pub fn read<'a>(&'a self) -> StyleSourceGuard<'a> {
+ use self::StyleSource::*;
+ match *self {
+ Style(ref rule) => {
+ let owning_ref = OwningHandle::new(rule.read(), |r| unsafe { &*r }.block.read());
+ StyleSourceGuard::Style(owning_ref)
+ }
+ Declarations(ref block) => StyleSourceGuard::Declarations(block.read()),
+ }
+ }
+}
+
+/// This value exists here so a node that pushes itself to the list can know
+/// that is in the free list by looking at is next pointer, and comparing it
+/// with null.
+const FREE_LIST_SENTINEL: *mut RuleNode = 0x01 as *mut RuleNode;
+
+impl RuleTree {
+ pub fn new() -> Self {
+ RuleTree {
+ root: StrongRuleNode::new(Box::new(RuleNode::root())),
+ }
+ }
+
+ pub fn root(&self) -> StrongRuleNode {
+ self.root.clone()
+ }
+
+ fn dump<W: Write>(&self, writer: &mut W) {
+ let _ = writeln!(writer, " + RuleTree");
+ self.root.get().dump(writer, 0);
+ }
+
+ pub fn dump_stdout(&self) {
+ let mut stdout = io::stdout();
+ self.dump(&mut stdout);
+ }
+
+ pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
+ where I: Iterator<Item=(StyleSource, Importance)>
+ {
+ let mut current = self.root.clone();
+ for (source, importance) in iter {
+ current = current.ensure_child(self.root.downgrade(), source, importance);
+ }
+ current
+ }
+
+ /// This can only be called when no other threads is accessing this tree.
+ pub unsafe fn gc(&self) {
+ self.root.gc();
+ }
+}
+
+struct RuleNode {
+ /// The root node. Only the root has no root pointer, for obvious reasons.
+ root: Option<WeakRuleNode>,
+
+ /// The parent rule node. Only the root has no parent.
+ parent: Option<StrongRuleNode>,
+
+ /// The actual style source, either coming from a selector in a StyleRule,
+ /// or a raw property declaration block (like the style attribute).
+ source: Option<StyleSource>,
+
+ /// The importance of the declarations relevant in the style rule,
+ /// meaningless in the root node.
+ importance: Importance,
+
+ refcount: AtomicUsize,
+ first_child: AtomicPtr<RuleNode>,
+ next_sibling: AtomicPtr<RuleNode>,
+ prev_sibling: AtomicPtr<RuleNode>,
+
+ /// The next item in the rule tree free list, that starts on the root node.
+ next_free: AtomicPtr<RuleNode>,
+}
+
+unsafe impl Sync for RuleTree {}
+unsafe impl Send for RuleTree {}
+
+impl RuleNode {
+ fn new(root: WeakRuleNode,
+ parent: StrongRuleNode,
+ source: StyleSource,
+ importance: Importance) -> Self {
+ debug_assert!(root.upgrade().parent().is_none());
+ RuleNode {
+ root: Some(root),
+ parent: Some(parent),
+ source: Some(source),
+ importance: importance,
+ refcount: AtomicUsize::new(1),
+ first_child: AtomicPtr::new(ptr::null_mut()),
+ next_sibling: AtomicPtr::new(ptr::null_mut()),
+ prev_sibling: AtomicPtr::new(ptr::null_mut()),
+ next_free: AtomicPtr::new(ptr::null_mut()),
+ }
+ }
+
+ fn root() -> Self {
+ RuleNode {
+ root: None,
+ parent: None,
+ source: None,
+ importance: Importance::Normal,
+ refcount: AtomicUsize::new(1),
+ first_child: AtomicPtr::new(ptr::null_mut()),
+ next_sibling: AtomicPtr::new(ptr::null_mut()),
+ prev_sibling: AtomicPtr::new(ptr::null_mut()),
+ next_free: AtomicPtr::new(FREE_LIST_SENTINEL),
+ }
+ }
+
+ fn is_root(&self) -> bool {
+ self.parent.is_none()
+ }
+
+ /// Remove this rule node from the child list.
+ ///
+ /// This method doesn't use proper synchronization, and it's expected to be
+ /// called in a single-threaded fashion, thus the unsafety.
+ ///
+ /// This is expected to be called before freeing the node from the free
+ /// list.
+ unsafe fn remove_from_child_list(&self) {
+ debug!("Remove from child list: {:?}, parent: {:?}",
+ self as *const RuleNode, self.parent.as_ref().map(|p| p.ptr()));
+ let prev_sibling = self.prev_sibling.swap(ptr::null_mut(), Ordering::SeqCst);
+ let next_sibling = self.next_sibling.swap(ptr::null_mut(), Ordering::SeqCst);
+
+ if prev_sibling != ptr::null_mut() {
+ let really_previous = WeakRuleNode { ptr: prev_sibling };
+ really_previous.upgrade()
+ .get().next_sibling.store(next_sibling, Ordering::SeqCst);
+ } else {
+ self.parent.as_ref().unwrap()
+ .get().first_child.store(ptr::null_mut(), Ordering::SeqCst);
+ }
+
+ if next_sibling != ptr::null_mut() {
+ let really_next = WeakRuleNode { ptr: next_sibling };
+ really_next.upgrade().get().prev_sibling.store(prev_sibling, Ordering::SeqCst);
+ }
+ }
+
+ fn dump<W: Write>(&self, writer: &mut W, indent: usize) {
+ const INDENT_INCREMENT: usize = 4;
+
+ for _ in 0..indent {
+ let _ = write!(writer, " ");
+ }
+
+ let _ = writeln!(writer, " - {:?} (ref: {:?}, parent: {:?})",
+ self as *const _, self.refcount.load(Ordering::SeqCst),
+ self.parent.as_ref().map(|p| p.ptr()));
+
+ for _ in 0..indent {
+ let _ = write!(writer, " ");
+ }
+
+ match self.source {
+ Some(ref source) => {
+ source.dump(writer);
+ }
+ None => {
+ if indent != 0 {
+ error!("How has this happened?");
+ }
+ let _ = write!(writer, "(root)");
+ }
+ }
+
+ let _ = write!(writer, "\n");
+ for child in self.iter_children() {
+ child.get().dump(writer, indent + INDENT_INCREMENT);
+ }
+ }
+
+ fn iter_children(&self) -> RuleChildrenListIter {
+ // FIXME(emilio): Fiddle with memory orderings.
+ let first_child = self.first_child.load(Ordering::SeqCst);
+ RuleChildrenListIter {
+ current: if first_child.is_null() {
+ None
+ } else {
+ Some(WeakRuleNode { ptr: first_child })
+ }
+ }
+ }
+}
+
+#[derive(Clone)]
+struct WeakRuleNode {
+ ptr: *mut RuleNode,
+}
+
+#[derive(Debug)]
+pub struct StrongRuleNode {
+ ptr: *mut RuleNode,
+}
+
+#[cfg(feature = "servo")]
+impl HeapSizeOf for StrongRuleNode {
+ fn heap_size_of_children(&self) -> usize { 0 }
+}
+
+
+impl StrongRuleNode {
+ fn new(n: Box<RuleNode>) -> Self {
+ debug_assert!(n.parent.is_none() == n.source.is_none());
+
+ let ptr = Box::into_raw(n);
+
+ debug!("Creating rule node: {:p}", ptr);
+
+ StrongRuleNode {
+ ptr: ptr,
+ }
+ }
+
+ fn downgrade(&self) -> WeakRuleNode {
+ WeakRuleNode {
+ ptr: self.ptr,
+ }
+ }
+
+ fn next_sibling(&self) -> Option<WeakRuleNode> {
+ // FIXME(emilio): Investigate what ordering can we achieve without
+ // messing things up.
+ let ptr = self.get().next_sibling.load(Ordering::SeqCst);
+ if ptr.is_null() {
+ None
+ } else {
+ Some(WeakRuleNode {
+ ptr: ptr
+ })
+ }
+ }
+
+ fn parent(&self) -> Option<&StrongRuleNode> {
+ self.get().parent.as_ref()
+ }
+
+ fn ensure_child(&self,
+ root: WeakRuleNode,
+ source: StyleSource,
+ importance: Importance) -> StrongRuleNode {
+ let mut last = None;
+ for child in self.get().iter_children() {
+ if child .get().importance == importance &&
+ child.get().source.as_ref().unwrap().ptr_equals(&source) {
+ return child;
+ }
+ last = Some(child);
+ }
+
+ let mut node = Box::new(RuleNode::new(root,
+ self.clone(),
+ source.clone(),
+ importance));
+ let new_ptr: *mut RuleNode = &mut *node;
+
+ loop {
+ let strong;
+
+ {
+ let next_sibling_ptr = match last {
+ Some(ref l) => &l.get().next_sibling,
+ None => &self.get().first_child,
+ };
+
+ let existing =
+ next_sibling_ptr.compare_and_swap(ptr::null_mut(),
+ new_ptr,
+ Ordering::SeqCst);
+
+ if existing == ptr::null_mut() {
+ // Now we know we're in the correct position in the child list,
+ // we can set the back pointer, knowing that this will only be
+ // accessed again in a single-threaded manner when we're
+ // sweeping possibly dead nodes.
+ if let Some(ref l) = last {
+ node.prev_sibling.store(l.ptr(), Ordering::Relaxed);
+ }
+
+ return StrongRuleNode::new(node);
+ }
+
+ // Existing is not null: some thread insert a child node since we accessed `last`.
+ strong = WeakRuleNode { ptr: existing }.upgrade();
+
+ if strong.get().source.as_ref().unwrap().ptr_equals(&source) {
+ // That node happens to be for the same style source, use that.
+ return strong;
+ }
+ }
+
+ // Try again inserting after the new last child.
+ last = Some(strong);
+ }
+ }
+
+ fn ptr(&self) -> *mut RuleNode {
+ self.ptr
+ }
+
+ fn get(&self) -> &RuleNode {
+ if cfg!(debug_assertions) {
+ let node = unsafe { &*self.ptr };
+ assert!(node.refcount.load(Ordering::SeqCst) > 0);
+ }
+ unsafe { &*self.ptr }
+ }
+
+ pub fn style_source(&self) -> Option<&StyleSource> {
+ self.get().source.as_ref()
+ }
+
+ pub fn importance(&self) -> Importance {
+ self.get().importance
+ }
+
+ pub fn self_and_ancestors(&self) -> SelfAndAncestors {
+ SelfAndAncestors {
+ current: Some(self)
+ }
+ }
+
+ unsafe fn pop_from_free_list(&self) -> Option<WeakRuleNode> {
+ debug_assert!(thread_state::get().is_layout() &&
+ !thread_state::get().is_worker());
+
+ let me = &*self.ptr;
+ debug_assert!(me.is_root());
+
+ let current = self.get().next_free.load(Ordering::SeqCst);
+ if current == FREE_LIST_SENTINEL {
+ return None;
+ }
+
+ let current = WeakRuleNode { ptr: current };
+
+ let node = &*current.ptr();
+ let next = node.next_free.swap(ptr::null_mut(), Ordering::SeqCst);
+ me.next_free.store(next, Ordering::SeqCst);
+
+ debug!("Popping from free list: cur: {:?}, next: {:?}", current.ptr(), next);
+
+ Some(current)
+ }
+
+ unsafe fn gc(&self) {
+ // NB: This can run from the root node destructor, so we can't use
+ // `get()`, since it asserts the refcount is bigger than zero.
+ let me = &*self.ptr;
+
+ debug_assert!(me.is_root(), "Can't call GC on a non-root node!");
+
+ while let Some(weak) = self.pop_from_free_list() {
+ let needs_drop = {
+ let node = &*weak.ptr();
+ if node.refcount.load(Ordering::SeqCst) == 0 {
+ node.remove_from_child_list();
+ true
+ } else {
+ false
+ }
+ };
+
+ debug!("GC'ing {:?}: {}", weak.ptr(), needs_drop);
+ if needs_drop {
+ let _ = Box::from_raw(weak.ptr());
+ }
+ }
+
+ debug_assert!(me.next_free.load(Ordering::SeqCst) == FREE_LIST_SENTINEL);
+ }
+}
+
+#[derive(Clone)]
+pub struct SelfAndAncestors<'a> {
+ current: Option<&'a StrongRuleNode>,
+}
+
+impl<'a> Iterator for SelfAndAncestors<'a> {
+ type Item = &'a StrongRuleNode;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.current.map(|node| {
+ self.current = node.parent();
+ node
+ })
+ }
+}
+
+impl Clone for StrongRuleNode {
+ fn clone(&self) -> Self {
+ debug!("{:?}: {:?}+", self.ptr(), self.get().refcount.load(Ordering::SeqCst));
+ debug_assert!(self.get().refcount.load(Ordering::SeqCst) > 0);
+ self.get().refcount.fetch_add(1, Ordering::SeqCst);
+ StrongRuleNode {
+ ptr: self.ptr,
+ }
+ }
+}
+
+impl Drop for StrongRuleNode {
+ fn drop(&mut self) {
+ let node = unsafe { &*self.ptr };
+
+ debug!("{:?}: {:?}-", self.ptr(), node.refcount.load(Ordering::SeqCst));
+ debug!("Dropping node: {:?}, root: {:?}, parent: {:?}",
+ self.ptr,
+ node.root.as_ref().map(|r| r.ptr()),
+ node.parent.as_ref().map(|p| p.ptr()));
+ let should_drop = {
+ debug_assert!(node.refcount.load(Ordering::SeqCst) > 0);
+ node.refcount.fetch_sub(1, Ordering::SeqCst) == 1
+ };
+
+ if !should_drop {
+ return
+ }
+
+ debug_assert_eq!(node.first_child.load(Ordering::SeqCst),
+ ptr::null_mut());
+ if node.parent.is_none() {
+ debug!("Dropping root node!");
+ // NOTE: Calling this is fine, because the rule tree root
+ // destructor needs to happen from the layout thread, where the
+ // stylist, and hence, the rule tree, is held.
+ unsafe { self.gc() };
+ let _ = unsafe { Box::from_raw(self.ptr()) };
+ return;
+ }
+
+ // The node is already in the free list, so do nothing.
+ if node.next_free.load(Ordering::SeqCst) != ptr::null_mut() {
+ return;
+ }
+
+ let free_list =
+ &unsafe { &*node.root.as_ref().unwrap().ptr() }.next_free;
+ loop {
+ let next_free = free_list.load(Ordering::SeqCst);
+ debug_assert!(!next_free.is_null());
+
+ node.next_free.store(next_free, Ordering::SeqCst);
+
+ let existing =
+ free_list.compare_and_swap(next_free,
+ self.ptr(),
+ Ordering::SeqCst);
+ if existing == next_free {
+ // Successfully inserted, yay! Otherwise try again.
+ break;
+ }
+ }
+ }
+}
+
+impl<'a> From<&'a StrongRuleNode> for WeakRuleNode {
+ fn from(node: &'a StrongRuleNode) -> Self {
+ WeakRuleNode {
+ ptr: node.ptr(),
+ }
+ }
+}
+
+impl WeakRuleNode {
+ fn upgrade(&self) -> StrongRuleNode {
+ debug!("Upgrading weak node: {:p}", self.ptr());
+
+ let node = unsafe { &*self.ptr };
+ node.refcount.fetch_add(1, Ordering::SeqCst);
+ StrongRuleNode {
+ ptr: self.ptr,
+ }
+ }
+
+ fn ptr(&self) -> *mut RuleNode {
+ self.ptr
+ }
+}
+
+struct RuleChildrenListIter {
+ current: Option<WeakRuleNode>,
+}
+
+impl Iterator for RuleChildrenListIter {
+ type Item = StrongRuleNode;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.current.take().map(|current| {
+ let current = current.upgrade();
+ self.current = current.next_sibling();
+ current
+ })
+ }
+}
diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs
index b97fcf9bb68..1a0a07b32e4 100644
--- a/components/style/selector_matching.rs
+++ b/components/style/selector_matching.rs
@@ -10,11 +10,12 @@ use element_state::*;
use error_reporting::StdoutErrorReporter;
use keyframes::KeyframesAnimation;
use media_queries::{Device, MediaType};
-use parking_lot::{RwLock, RwLockReadGuard};
+use parking_lot::RwLock;
use properties::{self, CascadeFlags, ComputedValues, INHERIT_ALL, Importance};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use quickersort::sort_by;
use restyle_hints::{RestyleHint, DependencySet};
+use rule_tree::{RuleTree, StrongRuleNode, StyleSource};
use selector_impl::{ElementExt, TheSelectorImpl, PseudoElement};
use selectors::Element;
use selectors::bloom::BloomFilter;
@@ -31,7 +32,7 @@ use std::hash::Hash;
use std::slice;
use std::sync::Arc;
use style_traits::viewport::ViewportConstraints;
-use stylesheets::{CSSRule, Origin, Stylesheet, UserAgentStylesheets};
+use stylesheets::{CSSRule, Origin, StyleRule, Stylesheet, UserAgentStylesheets};
use viewport::{self, MaybeNew, ViewportRule};
pub type FnvHashMap<K, V> = HashMap<K, V, BuildHasherDefault<::fnv::FnvHasher>>;
@@ -62,6 +63,11 @@ pub struct Stylist {
/// rules against the current device.
element_map: PerPseudoElementSelectorMap,
+ /// The rule tree, that stores the results of selector matching.
+ ///
+ /// FIXME(emilio): Not `pub`!
+ pub rule_tree: RuleTree,
+
/// The selector maps corresponding to a given pseudo-element
/// (depending on the implementation)
pseudos_map: FnvHashMap<PseudoElement, PerPseudoElementSelectorMap>,
@@ -72,6 +78,8 @@ pub struct Stylist {
/// 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.
+ ///
+ /// FIXME(emilio): Use the rule tree!
precomputed_pseudo_element_decls: FnvHashMap<PseudoElement, Vec<ApplicableDeclarationBlock>>,
rules_source_order: usize,
@@ -103,6 +111,7 @@ impl Stylist {
animations: Default::default(),
precomputed_pseudo_element_decls: Default::default(),
rules_source_order: 0,
+ rule_tree: RuleTree::new(),
state_deps: DependencySet::new(),
// XXX remember resetting them!
@@ -179,8 +188,8 @@ impl Stylist {
stylesheet.effective_rules(&self.device, |rule| {
match *rule {
CSSRule::Style(ref style_rule) => {
- let style_rule = style_rule.read();
- for selector in &style_rule.selectors {
+ let guard = style_rule.read();
+ for selector in &guard.selectors {
let map = if let Some(ref pseudo) = selector.pseudo_element {
pseudos_map
.entry(pseudo.clone())
@@ -192,14 +201,14 @@ impl Stylist {
map.insert(Rule {
selector: selector.complex_selector.clone(),
- declarations: style_rule.block.clone(),
+ style_rule: style_rule.clone(),
specificity: selector.specificity,
source_order: *rules_source_order,
});
}
*rules_source_order += 1;
- for selector in &style_rule.selectors {
+ for selector in &guard.selectors {
state_deps.note_selector(&selector.complex_selector);
if selector.affects_siblings() {
sibling_affecting_selectors.push(selector.clone());
@@ -261,25 +270,30 @@ impl Stylist {
pseudo: &PseudoElement,
parent: Option<&Arc<ComputedValues>>,
inherit_all: bool)
- -> Option<Arc<ComputedValues>> {
+ -> Option<(Arc<ComputedValues>, StrongRuleNode)> {
debug_assert!(TheSelectorImpl::pseudo_element_cascade_type(pseudo).is_precomputed());
if let Some(declarations) = self.precomputed_pseudo_element_decls.get(pseudo) {
+ // FIXME(emilio): When we've taken rid of the cascade we can just
+ // use into_iter.
+ let rule_node =
+ self.rule_tree.insert_ordered_rules(
+ declarations.iter().map(|a| (a.source.clone(), a.importance)));
+
let mut flags = CascadeFlags::empty();
if inherit_all {
flags.insert(INHERIT_ALL)
}
- let (computed, _) =
+ let computed =
properties::cascade(self.device.au_viewport_size(),
- &declarations,
+ &rule_node,
parent.map(|p| &**p),
None,
- None,
Box::new(StdoutErrorReporter),
flags);
- Some(Arc::new(computed))
+ Some((Arc::new(computed), rule_node))
} else {
- parent.map(|p| p.clone())
+ parent.map(|p| (p.clone(), self.rule_tree.root()))
}
}
@@ -308,13 +322,14 @@ impl Stylist {
};
self.precomputed_values_for_pseudo(&pseudo, Some(parent_style), inherit_all)
.expect("style_for_anonymous_box(): No precomputed values for that pseudo!")
+ .0
}
pub fn lazily_compute_pseudo_element_style<E>(&self,
element: &E,
pseudo: &PseudoElement,
parent: &Arc<ComputedValues>)
- -> Option<Arc<ComputedValues>>
+ -> Option<(Arc<ComputedValues>, StrongRuleNode)>
where E: Element<Impl=TheSelectorImpl> +
fmt::Debug +
PresentationalHintsSynthetizer
@@ -326,8 +341,6 @@ impl Stylist {
let mut declarations = vec![];
- // NB: This being cached could be worth it, maybe allow an optional
- // ApplicableDeclarationsCache?.
self.push_applicable_declarations(element,
None,
None,
@@ -335,16 +348,18 @@ impl Stylist {
&mut declarations,
MatchingReason::ForStyling);
- let (computed, _) =
+ let rule_node =
+ self.rule_tree.insert_ordered_rules(declarations.into_iter().map(|a| (a.source.clone(), a.importance)));
+
+ let computed =
properties::cascade(self.device.au_viewport_size(),
- &declarations,
+ &rule_node,
Some(&**parent),
None,
- None,
Box::new(StdoutErrorReporter),
CascadeFlags::empty());
- Some(Arc::new(computed))
+ Some((Arc::new(computed), rule_node))
}
pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc<Stylesheet>]) {
@@ -408,9 +423,9 @@ impl Stylist {
PresentationalHintsSynthetizer,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>
{
- assert!(!self.is_device_dirty);
- assert!(style_attribute.is_none() || pseudo_element.is_none(),
- "Style attributes do not apply to pseudo-elements");
+ debug_assert!(!self.is_device_dirty);
+ debug_assert!(style_attribute.is_none() || pseudo_element.is_none(),
+ "Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.is_none() ||
!TheSelectorImpl::pseudo_element_cascade_type(pseudo_element.as_ref().unwrap())
.is_precomputed());
@@ -557,6 +572,11 @@ impl Stylist {
true
}
+ #[inline]
+ pub fn rule_tree_root(&self) -> StrongRuleNode {
+ self.rule_tree.root()
+ }
+
pub fn match_same_sibling_affecting_rules<E>(&self,
element: &E,
candidate: &E) -> bool
@@ -754,7 +774,7 @@ impl SelectorMap {
// Sort only the rules we just added.
sort_by_key(&mut matching_rules_list[init_len..],
- |rule| (rule.specificity, rule.source_order));
+ |block| (block.specificity, block.source_order));
}
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
@@ -773,7 +793,8 @@ impl SelectorMap {
for rule in self.other_rules.iter() {
if rule.selector.compound_selector.is_empty() &&
rule.selector.next.is_none() {
- let block = rule.declarations.read();
+ let guard = rule.style_rule.read();
+ let block = guard.block.read();
if block.any_normal() {
matching_rules_list.push(
rule.to_applicable_declaration_block(Importance::Normal));
@@ -786,7 +807,7 @@ impl SelectorMap {
}
sort_by_key(&mut matching_rules_list[init_len..],
- |rule| (rule.specificity, rule.source_order));
+ |block| (block.specificity, block.source_order));
}
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector>(
@@ -827,7 +848,8 @@ impl SelectorMap {
V: VecLike<ApplicableDeclarationBlock>
{
for rule in rules.iter() {
- let block = rule.declarations.read();
+ let guard = rule.style_rule.read();
+ let block = guard.block.read();
let any_declaration_for_importance = if importance.important() {
block.any_important()
} else {
@@ -836,7 +858,8 @@ impl SelectorMap {
if any_declaration_for_importance &&
matches_complex_selector(&*rule.selector, element, parent_bf,
relations, reason) {
- matching_rules.push(rule.to_applicable_declaration_block(importance));
+ matching_rules.push(
+ rule.to_applicable_declaration_block(importance));
}
}
}
@@ -907,7 +930,7 @@ impl SelectorMap {
}
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone)]
+#[derive(Clone, Debug)]
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
@@ -915,16 +938,15 @@ pub struct Rule {
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub selector: Arc<ComplexSelector<TheSelectorImpl>>,
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
- pub declarations: Arc<RwLock<PropertyDeclarationBlock>>,
+ pub style_rule: Arc<RwLock<StyleRule>>,
pub source_order: usize,
pub specificity: u32,
}
impl Rule {
- fn to_applicable_declaration_block(&self, importance: Importance)
- -> ApplicableDeclarationBlock {
+ fn to_applicable_declaration_block(&self, importance: Importance) -> ApplicableDeclarationBlock {
ApplicableDeclarationBlock {
- mixed_declarations: self.declarations.clone(),
+ source: StyleSource::Style(self.style_rule.clone()),
importance: importance,
source_order: self.source_order,
specificity: self.specificity,
@@ -937,10 +959,8 @@ impl Rule {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug, Clone)]
pub struct ApplicableDeclarationBlock {
- /// Contains declarations of either importance, but only those of self.importance are relevant.
- /// Use ApplicableDeclarationBlock::iter
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
- pub mixed_declarations: Arc<RwLock<PropertyDeclarationBlock>>,
+ pub source: StyleSource,
pub importance: Importance,
pub source_order: usize,
pub specificity: u32,
@@ -952,32 +972,10 @@ impl ApplicableDeclarationBlock {
importance: Importance)
-> Self {
ApplicableDeclarationBlock {
- mixed_declarations: declarations,
- importance: importance,
+ source: StyleSource::Declarations(declarations),
source_order: 0,
specificity: 0,
- }
- }
-
- pub fn read(&self) -> ApplicableDeclarationBlockReadGuard {
- ApplicableDeclarationBlockReadGuard {
- guard: self.mixed_declarations.read(),
- importance: self.importance,
- }
- }
-
-}
-
-pub struct ApplicableDeclarationBlockReadGuard<'a> {
- guard: RwLockReadGuard<'a, PropertyDeclarationBlock>,
- importance: Importance,
-}
-
-impl<'a> ApplicableDeclarationBlockReadGuard<'a> {
- pub fn iter(&self) -> ApplicableDeclarationBlockIter {
- ApplicableDeclarationBlockIter {
- iter: self.guard.declarations.iter(),
- importance: self.importance,
+ importance: importance,
}
}
}
@@ -1013,6 +1011,8 @@ impl<'a> DoubleEndedIterator for ApplicableDeclarationBlockIter<'a> {
}
}
-fn find_push<Str: Eq + Hash>(map: &mut FnvHashMap<Str, Vec<Rule>>, key: Str, value: Rule) {
+#[inline]
+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)
}
diff --git a/components/style/thread_state.rs b/components/style/thread_state.rs
index 25e77ecbb45..12e52425f55 100644
--- a/components/style/thread_state.rs
+++ b/components/style/thread_state.rs
@@ -24,6 +24,10 @@ bitflags! {
macro_rules! thread_types ( ( $( $fun:ident = $flag:ident ; )* ) => (
impl ThreadState {
+ pub fn is_worker(self) -> bool {
+ self.contains(IN_WORKER)
+ }
+
$(
#[cfg(debug_assertions)]
pub fn $fun(self) -> bool {
diff --git a/components/style/traversal.rs b/components/style/traversal.rs
index be69690f471..4dff1d0c967 100644
--- a/components/style/traversal.rs
+++ b/components/style/traversal.rs
@@ -294,7 +294,7 @@ fn ensure_element_styled_internal<'a, E, C>(element: E,
&mut applicable_declarations);
unsafe {
- element.cascade_node(context, data, parent, &applicable_declarations);
+ element.cascade_node(context, data, parent, applicable_declarations);
}
}
@@ -357,7 +357,7 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
// Perform the CSS cascade.
unsafe {
element.cascade_node(context, data, element.parent_element(),
- &applicable_declarations);
+ applicable_declarations);
}
// Add ourselves to the LRU cache.
diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs
index aad327ca1d3..124acadad5a 100644
--- a/components/style/values/specified/mod.rs
+++ b/components/style/values/specified/mod.rs
@@ -215,8 +215,8 @@ impl ToCss for Length {
Length::FontRelative(length) => length.to_css(dest),
Length::ViewportPercentage(length) => length.to_css(dest),
Length::Calc(ref calc, _) => calc.to_css(dest),
- Length::ServoCharacterWidth(_)
- => panic!("internal CSS values should never be serialized"),
+ /* This should only be reached from style dumping code */
+ Length::ServoCharacterWidth(CharacterWidth(i)) => write!(dest, "CharWidth({})", i),
}
}
}
diff --git a/components/util/opts.rs b/components/util/opts.rs
index 242cd569bec..d82c9f5a5d7 100644
--- a/components/util/opts.rs
+++ b/components/util/opts.rs
@@ -159,6 +159,9 @@ pub struct Opts {
/// Dumps the DOM after restyle.
pub dump_style_tree: bool,
+ /// Dumps the rule tree.
+ pub dump_rule_tree: bool,
+
/// Dumps the flow tree after a layout.
pub dump_flow_tree: bool,
@@ -244,6 +247,9 @@ pub struct DebugOptions {
/// Print the DOM after each restyle.
pub dump_style_tree: bool,
+ /// Dumps the rule tree.
+ pub dump_rule_tree: bool,
+
/// Print the flow tree after each layout.
pub dump_flow_tree: bool,
@@ -339,6 +345,7 @@ impl DebugOptions {
"disable-text-aa" => debug_options.disable_text_aa = true,
"disable-canvas-aa" => debug_options.disable_text_aa = true,
"dump-style-tree" => debug_options.dump_style_tree = true,
+ "dump-rule-tree" => debug_options.dump_rule_tree = true,
"dump-flow-tree" => debug_options.dump_flow_tree = true,
"dump-display-list" => debug_options.dump_display_list = true,
"dump-display-list-json" => debug_options.dump_display_list_json = true,
@@ -521,6 +528,7 @@ pub fn default_opts() -> Opts {
random_pipeline_closure_seed: None,
sandbox: false,
dump_style_tree: false,
+ dump_rule_tree: false,
dump_flow_tree: false,
dump_display_list: false,
dump_display_list_json: false,
@@ -824,6 +832,7 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
enable_text_antialiasing: !debug_options.disable_text_aa,
enable_canvas_antialiasing: !debug_options.disable_canvas_aa,
dump_style_tree: debug_options.dump_style_tree,
+ dump_rule_tree: debug_options.dump_rule_tree,
dump_flow_tree: debug_options.dump_flow_tree,
dump_display_list: debug_options.dump_display_list,
dump_display_list_json: debug_options.dump_display_list_json,