aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/document.rs10
-rw-r--r--components/script/dom/node.rs15
-rw-r--r--components/style/context.rs63
-rw-r--r--components/style/data.rs44
-rw-r--r--components/style/matching.rs20
-rw-r--r--components/style/traversal.rs94
6 files changed, 67 insertions, 179 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index c9130ad5e44..66ef28c8e93 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -573,8 +573,14 @@ impl Document {
self.encoding.set(encoding);
}
- pub fn content_and_heritage_changed(&self, node: &Node, damage: NodeDamage) {
- node.dirty(damage);
+ pub fn content_and_heritage_changed(&self, node: &Node) {
+ if node.is_in_doc() {
+ node.note_dirty_descendants();
+ }
+
+ // FIXME(emilio): This is very inefficient, ideally the flag above would
+ // be enough and incremental layout could figure out from there.
+ node.dirty(NodeDamage::OtherNodeDamage);
}
/// Reflows and disarms the timer if the reflow timer has expired.
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index ff69e2ae2b3..b0f85b1ccfa 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -483,6 +483,19 @@ impl Node {
self.flags.set(flags);
}
+ // FIXME(emilio): This and the function below should move to Element.
+ pub fn note_dirty_descendants(&self) {
+ debug_assert!(self.is_in_doc());
+
+ for ancestor in self.inclusive_ancestors() {
+ if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
+ return;
+ }
+
+ ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
+ }
+ }
+
pub fn has_dirty_descendants(&self) -> bool {
self.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
}
@@ -2499,7 +2512,7 @@ impl VirtualMethods for Node {
if let Some(list) = self.child_list.get() {
list.as_children_list().children_changed(mutation);
}
- self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage);
+ self.owner_doc().content_and_heritage_changed(self);
}
// This handles the ranges mentioned in steps 2-3 when removing a node.
diff --git a/components/style/context.rs b/components/style/context.rs
index 38af94f7584..a39ab809b5b 100644
--- a/components/style/context.rs
+++ b/components/style/context.rs
@@ -5,11 +5,11 @@
//! The context within which style is calculated.
#[cfg(feature = "servo")] use animation::Animation;
-#[cfg(feature = "servo")] use animation::PropertyAnimation;
use app_units::Au;
use bloom::StyleBloom;
use data::{EagerPseudoStyles, ElementData};
-use dom::{OpaqueNode, TNode, TElement, SendElement};
+use dom::{TElement, SendElement};
+#[cfg(feature = "servo")] use dom::OpaqueNode;
use euclid::ScaleFactor;
use euclid::Size2D;
use fnv::FnvHashMap;
@@ -285,23 +285,6 @@ impl ElementCascadeInputs {
}
}
-/// 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.
-pub 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 Vec of possibly expired animations. Used only by Servo.
- #[allow(dead_code)]
- #[cfg(feature = "servo")]
- pub possibly_expired_animations: Vec<PropertyAnimation>,
-}
-
/// Statistics gathered during the traversal. We gather statistics on each
/// thread and then combine them after the threads join via the Add
/// implementation below.
@@ -712,8 +695,6 @@ pub struct ThreadLocalStyleContext<E: TElement> {
pub selector_flags: SelectorFlagsMap<E>,
/// Statistics about the traversal.
pub statistics: TraversalStatistics,
- /// Information related to the current element, non-None during processing.
- pub current_element_info: Option<CurrentElementInfo>,
/// The struct used to compute and cache font metrics from style
/// for evaluation of the font-relative em/ch units and font-size
pub font_metrics_provider: E::FontMetricsProvider,
@@ -736,7 +717,6 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
tasks: SequentialTaskList(Vec::new()),
selector_flags: SelectorFlagsMap::new(),
statistics: TraversalStatistics::default(),
- current_element_info: None,
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
stack_limit_checker: StackLimitChecker::new(
(STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024),
@@ -754,55 +734,16 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
tasks: SequentialTaskList(Vec::new()),
selector_flags: SelectorFlagsMap::new(),
statistics: TraversalStatistics::default(),
- current_element_info: None,
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
stack_limit_checker: StackLimitChecker::new(
(STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024),
nth_index_cache: NthIndexCache::default(),
}
}
-
- #[cfg(feature = "gecko")]
- /// 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.has_styles(),
- });
- }
-
- #[cfg(feature = "servo")]
- /// 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.has_styles(),
- possibly_expired_animations: Vec::new(),
- });
- }
-
- /// 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
- }
}
impl<E: TElement> Drop for ThreadLocalStyleContext<E> {
fn drop(&mut self) {
- debug_assert!(self.current_element_info.is_none());
debug_assert!(thread_state::get() == ThreadState::LAYOUT);
// Apply any slow selector flags that need to be set on parents.
diff --git a/components/style/data.rs b/components/style/data.rs
index f498aa556e3..6c02f54305d 100644
--- a/components/style/data.rs
+++ b/components/style/data.rs
@@ -38,15 +38,16 @@ bitflags! {
/// traversed, so each traversal simply updates it with the appropriate
/// value.
const TRAVERSED_WITHOUT_STYLING = 1 << 1;
- /// Whether we reframed/reconstructed any ancestor or self.
- const ANCESTOR_WAS_RECONSTRUCTED = 1 << 2;
- /// Whether the primary style of this element data was reused from another
- /// element via a rule node comparison. This allows us to differentiate
- /// between elements that shared styles because they met all the criteria
- /// of the style sharing cache, compared to elements that reused style
- /// structs via rule node identity. The former gives us stronger transitive
- /// guarantees that allows us to apply the style sharing cache to cousins.
- const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 3;
+
+ /// Whether the primary style of this element data was reused from
+ /// another element via a rule node comparison. This allows us to
+ /// differentiate between elements that shared styles because they met
+ /// all the criteria of the style sharing cache, compared to elements
+ /// that reused style structs via rule node identity.
+ ///
+ /// The former gives us stronger transitive guarantees that allows us to
+ /// apply the style sharing cache to cousins.
+ const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 2;
}
}
@@ -405,13 +406,7 @@ impl ElementData {
#[inline]
pub fn clear_restyle_flags_and_damage(&mut self) {
self.damage = RestyleDamage::empty();
- self.flags.remove(ElementDataFlags::WAS_RESTYLED | ElementDataFlags::ANCESTOR_WAS_RECONSTRUCTED)
- }
-
- /// Returns whether this element or any ancestor is going to be
- /// reconstructed.
- pub fn reconstructed_self_or_ancestor(&self) -> bool {
- self.reconstructed_ancestor() || self.reconstructed_self()
+ self.flags.remove(ElementDataFlags::WAS_RESTYLED);
}
/// Returns whether this element is going to be reconstructed.
@@ -419,23 +414,6 @@ impl ElementData {
self.damage.contains(RestyleDamage::reconstruct())
}
- /// Returns whether any ancestor of this element is going to be
- /// reconstructed.
- fn reconstructed_ancestor(&self) -> bool {
- self.flags.contains(ElementDataFlags::ANCESTOR_WAS_RECONSTRUCTED)
- }
-
- /// Sets the flag that tells us whether we've reconstructed an ancestor.
- pub fn set_reconstructed_ancestor(&mut self, reconstructed: bool) {
- if reconstructed {
- // If it weren't for animation-only traversals, we could assert
- // `!self.reconstructed_ancestor()` here.
- self.flags.insert(ElementDataFlags::ANCESTOR_WAS_RECONSTRUCTED);
- } else {
- self.flags.remove(ElementDataFlags::ANCESTOR_WAS_RECONSTRUCTED);
- }
- }
-
/// Mark this element as restyled, which is useful to know whether we need
/// to do a post-traversal.
pub fn set_restyled(&mut self) {
diff --git a/components/style/matching.rs b/components/style/matching.rs
index fbf872908e2..103cf24da25 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -13,6 +13,7 @@ use data::ElementData;
use dom::TElement;
use invalidation::element::restyle_hints::RestyleHint;
use properties::ComputedValues;
+use properties::longhands::display::computed_value as display;
use rule_tree::{CascadeLevel, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage};
use selectors::matching::ElementSelectorFlags;
@@ -142,8 +143,6 @@ trait PrivateMatchMethods: TElement {
old_values: Option<&Arc<ComputedValues>>,
new_values: &ComputedValues,
) -> bool {
- use properties::longhands::display::computed_value as display;
-
let new_box_style = new_values.get_box();
let has_new_animation_style = new_box_style.specifies_animations();
let has_animations = self.has_css_animations();
@@ -182,7 +181,6 @@ trait PrivateMatchMethods: TElement {
restyle_hints: RestyleHint
) {
use context::PostAnimationTasks;
- use properties::longhands::display::computed_value as display;
if !restyle_hints.intersects(RestyleHint::RESTYLE_SMIL) {
return;
@@ -298,13 +296,11 @@ trait PrivateMatchMethods: TElement {
use animation;
use dom::TNode;
- let possibly_expired_animations =
- &mut context.thread_local.current_element_info.as_mut().unwrap()
- .possibly_expired_animations;
+ let mut possibly_expired_animations = vec![];
let shared_context = context.shared;
if let Some(ref mut old) = *old_values {
self.update_animations_for_cascade(shared_context, old,
- possibly_expired_animations,
+ &mut possibly_expired_animations,
&context.thread_local.font_metrics_provider);
}
@@ -367,6 +363,16 @@ trait PrivateMatchMethods: TElement {
let old_display = old_values.get_box().clone_display();
let new_display = new_values.get_box().clone_display();
+ // If we used to be a display: none element, and no longer are,
+ // our children need to be restyled because they're unstyled.
+ //
+ // NOTE(emilio): Gecko has the special-case of -moz-binding, but
+ // that gets handled on the frame constructor when processing
+ // the reframe, so no need to handle that here.
+ if old_display == display::T::none && old_display != new_display {
+ return ChildCascadeRequirement::MustCascadeChildren
+ }
+
// Blockification of children may depend on our display value,
// so we need to actually do the recascade. We could potentially
// do better, but it doesn't seem worth it.
diff --git a/components/style/traversal.rs b/components/style/traversal.rs
index 8b52c3bd4b9..88c02f51b3e 100644
--- a/components/style/traversal.rs
+++ b/components/style/traversal.rs
@@ -159,13 +159,6 @@ pub trait DomTraversal<E: TElement> : Sync {
let parent_data = parent.as_ref().and_then(|p| p.borrow_data());
if let Some(ref mut data) = data {
- // Make sure we don't have any stale RECONSTRUCTED_ANCESTOR bits
- // from the last traversal (at a potentially-higher root).
- //
- // From the perspective of this traversal, the root cannot have
- // reconstructed ancestors.
- data.set_reconstructed_ancestor(false);
-
if !traversal_flags.for_animation_only() {
// Invalidate our style, and that of our siblings and
// descendants as needed.
@@ -247,48 +240,6 @@ pub trait DomTraversal<E: TElement> : Sync {
_ => return true,
};
- // If the element is native-anonymous and an ancestor frame will be
- // reconstructed, the child and all its descendants will be destroyed.
- // In that case, we wouldn't need to traverse the subtree...
- //
- // Except if there could be transitions of pseudo-elements, in which
- // case we still need to process them, unfortunately.
- //
- // We need to conservatively continue the traversal to style the
- // pseudo-element in order to properly process potentially-new
- // transitions that we won't see otherwise.
- //
- // But it may be that we no longer match, so detect that case and act
- // appropriately here.
- if el.is_native_anonymous() {
- if let Some(parent_data) = parent_data {
- let going_to_reframe =
- parent_data.reconstructed_self_or_ancestor();
-
- let mut is_before_or_after_pseudo = false;
- if let Some(pseudo) = el.implemented_pseudo_element() {
- if pseudo.is_before_or_after() {
- is_before_or_after_pseudo = true;
- let still_match =
- parent_data.styles.pseudos.get(&pseudo).is_some();
-
- if !still_match {
- debug_assert!(going_to_reframe,
- "We're removing a pseudo, so we \
- should reframe!");
- return false;
- }
- }
- }
-
- if going_to_reframe && !is_before_or_after_pseudo {
- debug!("Element {:?} is in doomed NAC subtree, \
- culling traversal", el);
- return false;
- }
- }
- }
-
// If the dirty descendants bit is set, we need to traverse no matter
// what. Skip examining the ElementData.
if el.has_dirty_descendants() {
@@ -322,6 +273,7 @@ pub trait DomTraversal<E: TElement> : Sync {
&self,
context: &mut StyleContext<E>,
parent: E,
+ is_initial_style: bool,
parent_data: &ElementData,
) -> bool {
debug_assert!(cfg!(feature = "gecko") ||
@@ -353,7 +305,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") && context.thread_local.is_initial_style() &&
+ if cfg!(feature = "gecko") && is_initial_style &&
parent_data.styles.primary().has_moz_binding()
{
debug!("Parent {:?} has XBL binding, deferring traversal", parent);
@@ -476,7 +428,8 @@ where
use traversal_flags::TraversalFlags;
let flags = context.shared.traversal_flags;
- context.thread_local.begin_element(element, data);
+ let is_initial_style = !data.has_styles();
+
context.thread_local.statistics.elements_traversed += 1;
debug_assert!(flags.intersects(TraversalFlags::AnimationOnly | TraversalFlags::UnstyledOnly) ||
!element.has_snapshot() || element.handled_snapshot(),
@@ -555,26 +508,24 @@ where
// Before examining each child individually, try to prove that our children
// don't need style processing. They need processing if any of the following
// conditions hold:
- // * We have the dirty descendants bit.
- // * We're propagating a hint.
- // * This is the initial style.
- // * We generated a reconstruct hint on self (which could mean that we
- // switched from display:none to something else, which means the children
- // need initial styling).
- // * This is a servo non-incremental traversal.
+ //
+ // * We have the dirty descendants bit.
+ // * We're propagating a restyle hint.
+ // * We can't skip the cascade.
+ // * This is a servo non-incremental traversal.
//
// Additionally, there are a few scenarios where we avoid traversing the
// subtree even if descendant styles are out of date. These cases are
// enumerated in should_cull_subtree().
- let mut traverse_children = has_dirty_descendants_for_this_restyle ||
- !propagated_hint.is_empty() ||
- !child_cascade_requirement.can_skip_cascade() ||
- context.thread_local.is_initial_style() ||
- data.reconstructed_self() ||
- is_servo_nonincremental_layout();
+ let mut traverse_children =
+ has_dirty_descendants_for_this_restyle ||
+ !propagated_hint.is_empty() ||
+ !child_cascade_requirement.can_skip_cascade() ||
+ is_servo_nonincremental_layout();
- traverse_children = traverse_children &&
- !traversal.should_cull_subtree(context, element, &data);
+ traverse_children =
+ traverse_children &&
+ !traversal.should_cull_subtree(context, element, is_initial_style, &data);
// Examine our children, and enqueue the appropriate ones for traversal.
if traverse_children {
@@ -584,7 +535,7 @@ where
data,
propagated_hint,
child_cascade_requirement,
- data.reconstructed_self_or_ancestor(),
+ is_initial_style,
note_child
);
}
@@ -600,8 +551,6 @@ where
!element.has_animation_only_dirty_descendants(),
"Should have cleared animation bits already");
clear_state_after_traversing(element, data, flags);
-
- context.thread_local.end_element(element);
}
fn clear_state_after_traversing<E>(
@@ -826,7 +775,7 @@ fn note_children<E, D, F>(
data: &ElementData,
propagated_hint: RestyleHint,
cascade_requirement: ChildCascadeRequirement,
- reconstructed_ancestor: bool,
+ is_initial_style: bool,
mut note_child: F,
)
where
@@ -836,7 +785,6 @@ where
{
trace!("note_children: {:?}", element);
let flags = context.shared.traversal_flags;
- let is_initial_style = context.thread_local.is_initial_style();
// Loop over all the traversal children.
for child_node in element.traversal_children() {
@@ -866,10 +814,6 @@ where
}
if let Some(ref mut child_data) = child_data {
- // Propagate the parent restyle hint, that may make us restyle the whole
- // subtree.
- child_data.set_reconstructed_ancestor(reconstructed_ancestor);
-
let mut child_hint = propagated_hint;
match cascade_requirement {
ChildCascadeRequirement::CanSkipCascade => {}