From 4558efbca5c1ad3fe0b6b074415174c42ae1f75f Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Sun, 8 Jan 2017 12:38:44 -0800 Subject: [PATCH] Bug 1325734 - Explicitly track whether we're performing the initial style. r=emilio --- components/style/context.rs | 52 +++++++++++++++++++++++++++++++++-- components/style/traversal.rs | 12 +++++--- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/components/style/context.rs b/components/style/context.rs index ab5899e142f0..66310c752c76 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -8,7 +8,8 @@ use animation::Animation; use app_units::Au; use bloom::StyleBloom; -use dom::{OpaqueNode, TElement}; +use data::ElementData; +use dom::{OpaqueNode, TNode, TElement}; use error_reporting::ParseErrorReporter; use euclid::Size2D; use matching::StyleSharingCandidateCache; @@ -89,6 +90,18 @@ pub struct SharedStyleContext { pub default_computed_values: Arc, } +/// Information about the current element being processed. We group this together +/// into a single struct within ThreadLocalStyleContext so that we can instantiate +/// and destroy it easily at the beginning and end of element processing. +struct CurrentElementInfo { + /// The element being processed. Currently we use an OpaqueNode since we only + /// use this for identity checks, but we could use SendElement if there were + /// a good reason to. + element: OpaqueNode, + /// Whether the element is being styled for the first time. + is_initial_style: bool, +} + /// A thread-local style context. /// /// This context contains data that needs to be used during restyling, but is @@ -102,17 +115,52 @@ pub struct ThreadLocalStyleContext { /// A channel on which new animations that have been triggered by style /// recalculation can be sent. pub new_animations_sender: Sender, + /// Information related to the current element, non-None during processing. + current_element_info: Option, } impl ThreadLocalStyleContext { - /// Create a new `ThreadLocalStyleContext` from a shared one. + /// Creates a new `ThreadLocalStyleContext` from a shared one. pub fn new(shared: &SharedStyleContext) -> Self { ThreadLocalStyleContext { style_sharing_candidate_cache: StyleSharingCandidateCache::new(), bloom_filter: StyleBloom::new(), new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(), + current_element_info: None, } } + + /// Notes when the style system starts traversing an element. + pub fn begin_element(&mut self, element: E, data: &ElementData) { + debug_assert!(self.current_element_info.is_none()); + self.current_element_info = Some(CurrentElementInfo { + element: element.as_node().opaque(), + is_initial_style: data.is_unstyled_initial(), + }); + } + + /// Notes when the style system finishes traversing an element. + pub fn end_element(&mut self, element: E) { + debug_assert!(self.current_element_info.is_some()); + debug_assert!(self.current_element_info.as_ref().unwrap().element == + element.as_node().opaque()); + self.current_element_info = None; + } + + /// Returns true if the current element being traversed is being styled for + /// the first time. + /// + /// Panics if called while no element is being traversed. + pub fn is_initial_style(&self) -> bool { + self.current_element_info.as_ref().unwrap().is_initial_style + } +} + +#[cfg(debug_assertions)] +impl Drop for ThreadLocalStyleContext { + fn drop(&mut self) { + debug_assert!(self.current_element_info.is_none()); + } } /// A `StyleContext` is just a simple container for a immutable reference to a diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 8dd541fbd5b2..8e7bf4656826 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -195,7 +195,7 @@ pub trait DomTraversal : Sync { /// This may be called multiple times when processing an element, so we pass /// a parameter to keep the logs tidy. fn should_traverse_children(&self, - _thread_local: &mut ThreadLocalStyleContext, + thread_local: &mut ThreadLocalStyleContext, parent: E, parent_data: &ElementData, log: LogBehavior) -> bool @@ -230,7 +230,7 @@ pub trait DomTraversal : Sync { // happens, we may just end up doing wasted work, since Gecko // recursively drops Servo ElementData when the XBL insertion parent of // an Element is changed. - if cfg!(feature = "gecko") && parent_data.is_styled_initial() && + if cfg!(feature = "gecko") && thread_local.is_initial_style() && parent_data.styles().primary.values.has_moz_binding() { if log.allow() { debug!("Parent {:?} has XBL binding, deferring traversal", parent); } return false; @@ -246,8 +246,11 @@ pub trait DomTraversal : Sync { where F: FnMut(&mut Self::ThreadLocalContext, E::ConcreteNode) { // Check if we're allowed to traverse past this element. - if !self.should_traverse_children(thread_local.borrow_mut(), parent, - &parent.borrow_data().unwrap(), MayLog) { + let should_traverse = + self.should_traverse_children(thread_local.borrow_mut(), parent, + &parent.borrow_data().unwrap(), MayLog); + thread_local.borrow_mut().end_element(parent); + if !should_traverse { return; } @@ -376,6 +379,7 @@ pub fn recalc_style_at(traversal: &D, where E: TElement, D: DomTraversal { + context.thread_local.begin_element(element, &data); debug_assert!(data.as_restyle().map_or(true, |r| r.snapshot.is_none()), "Snapshots should be expanded by the caller");