Skip to content

Commit

Permalink
Bug 1325734 - Explicitly track whether we're performing the initial s…
Browse files Browse the repository at this point in the history
…tyle. r=emilio
  • Loading branch information
bholley committed Jan 9, 2017
1 parent 92b9d70 commit 4558efb
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 6 deletions.
52 changes: 50 additions & 2 deletions components/style/context.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -89,6 +90,18 @@ pub struct SharedStyleContext {
pub default_computed_values: Arc<ComputedValues>,
}

/// 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
Expand All @@ -102,17 +115,52 @@ pub struct ThreadLocalStyleContext<E: TElement> {
/// A channel on which new animations that have been triggered by style
/// recalculation can be sent.
pub new_animations_sender: Sender<Animation>,
/// Information related to the current element, non-None during processing.
current_element_info: Option<CurrentElementInfo>,
}

impl<E: TElement> ThreadLocalStyleContext<E> {
/// 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<E: TElement> Drop for ThreadLocalStyleContext<E> {
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
Expand Down
12 changes: 8 additions & 4 deletions components/style/traversal.rs
Expand Up @@ -195,7 +195,7 @@ pub trait DomTraversal<E: TElement> : 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<E>,
thread_local: &mut ThreadLocalStyleContext<E>,
parent: E,
parent_data: &ElementData,
log: LogBehavior) -> bool
Expand Down Expand Up @@ -230,7 +230,7 @@ pub trait DomTraversal<E: TElement> : 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;
Expand All @@ -246,8 +246,11 @@ pub trait DomTraversal<E: TElement> : 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;
}

Expand Down Expand Up @@ -376,6 +379,7 @@ pub fn recalc_style_at<E, D>(traversal: &D,
where E: TElement,
D: DomTraversal<E>
{
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");

Expand Down

0 comments on commit 4558efb

Please sign in to comment.