mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Bug 1325734 - Explicitly track whether we're performing the initial style. r=emilio
This commit is contained in:
parent
92b9d70c3a
commit
4558efbca5
2 changed files with 58 additions and 6 deletions
|
@ -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<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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue