mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +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 animation::Animation;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use bloom::StyleBloom;
|
use bloom::StyleBloom;
|
||||||
use dom::{OpaqueNode, TElement};
|
use data::ElementData;
|
||||||
|
use dom::{OpaqueNode, TNode, TElement};
|
||||||
use error_reporting::ParseErrorReporter;
|
use error_reporting::ParseErrorReporter;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use matching::StyleSharingCandidateCache;
|
use matching::StyleSharingCandidateCache;
|
||||||
|
@ -89,6 +90,18 @@ pub struct SharedStyleContext {
|
||||||
pub default_computed_values: Arc<ComputedValues>,
|
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.
|
/// A thread-local style context.
|
||||||
///
|
///
|
||||||
/// This context contains data that needs to be used during restyling, but is
|
/// 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
|
/// A channel on which new animations that have been triggered by style
|
||||||
/// recalculation can be sent.
|
/// recalculation can be sent.
|
||||||
pub new_animations_sender: Sender<Animation>,
|
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> {
|
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 {
|
pub fn new(shared: &SharedStyleContext) -> Self {
|
||||||
ThreadLocalStyleContext {
|
ThreadLocalStyleContext {
|
||||||
style_sharing_candidate_cache: StyleSharingCandidateCache::new(),
|
style_sharing_candidate_cache: StyleSharingCandidateCache::new(),
|
||||||
bloom_filter: StyleBloom::new(),
|
bloom_filter: StyleBloom::new(),
|
||||||
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
|
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
|
/// 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
|
/// This may be called multiple times when processing an element, so we pass
|
||||||
/// a parameter to keep the logs tidy.
|
/// a parameter to keep the logs tidy.
|
||||||
fn should_traverse_children(&self,
|
fn should_traverse_children(&self,
|
||||||
_thread_local: &mut ThreadLocalStyleContext<E>,
|
thread_local: &mut ThreadLocalStyleContext<E>,
|
||||||
parent: E,
|
parent: E,
|
||||||
parent_data: &ElementData,
|
parent_data: &ElementData,
|
||||||
log: LogBehavior) -> bool
|
log: LogBehavior) -> bool
|
||||||
|
@ -230,7 +230,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
// happens, we may just end up doing wasted work, since Gecko
|
// happens, we may just end up doing wasted work, since Gecko
|
||||||
// recursively drops Servo ElementData when the XBL insertion parent of
|
// recursively drops Servo ElementData when the XBL insertion parent of
|
||||||
// an Element is changed.
|
// 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() {
|
parent_data.styles().primary.values.has_moz_binding() {
|
||||||
if log.allow() { debug!("Parent {:?} has XBL binding, deferring traversal", parent); }
|
if log.allow() { debug!("Parent {:?} has XBL binding, deferring traversal", parent); }
|
||||||
return false;
|
return false;
|
||||||
|
@ -246,8 +246,11 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
where F: FnMut(&mut Self::ThreadLocalContext, E::ConcreteNode)
|
where F: FnMut(&mut Self::ThreadLocalContext, E::ConcreteNode)
|
||||||
{
|
{
|
||||||
// Check if we're allowed to traverse past this element.
|
// Check if we're allowed to traverse past this element.
|
||||||
if !self.should_traverse_children(thread_local.borrow_mut(), parent,
|
let should_traverse =
|
||||||
&parent.borrow_data().unwrap(), MayLog) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,6 +379,7 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
D: DomTraversal<E>
|
D: DomTraversal<E>
|
||||||
{
|
{
|
||||||
|
context.thread_local.begin_element(element, &data);
|
||||||
debug_assert!(data.as_restyle().map_or(true, |r| r.snapshot.is_none()),
|
debug_assert!(data.as_restyle().map_or(true, |r| r.snapshot.is_none()),
|
||||||
"Snapshots should be expanded by the caller");
|
"Snapshots should be expanded by the caller");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue