aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout_thread/lib.rs26
-rw-r--r--components/script/dom/document.rs6
-rw-r--r--components/script/dom/element.rs17
-rw-r--r--components/script/dom/node.rs34
-rw-r--r--components/script/layout_wrapper.rs29
5 files changed, 68 insertions, 44 deletions
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index a1a8825791f..d7b659be472 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -1084,11 +1084,8 @@ impl LayoutThread {
// NB: The dirty bit is propagated down the tree.
unsafe { node.set_dirty(); }
- let mut current = node.parent_node().and_then(|n| n.as_element());
- while let Some(el) = current {
- if el.has_dirty_descendants() { break; }
- unsafe { el.set_dirty_descendants(); }
- current = el.parent_element();
+ if let Some(p) = node.parent_node().and_then(|n| n.as_element()) {
+ unsafe { p.note_dirty_descendant() };
}
next = iter.next_skipping_children();
@@ -1120,8 +1117,16 @@ impl LayoutThread {
let restyles = document.drain_pending_restyles();
if !needs_dirtying {
for (el, restyle) in restyles {
+ // Propagate the descendant bit up the ancestors. Do this before
+ // the restyle calculation so that we can also do it for new
+ // unstyled nodes, which the descendants bit helps us find.
+ if let Some(parent) = el.parent_element() {
+ unsafe { parent.note_dirty_descendant() };
+ }
+
if el.get_data().is_none() {
- // If we haven't styled this node yet, we can ignore the restyle.
+ // If we haven't styled this node yet, we don't need to track
+ // a restyle.
continue;
}
@@ -1143,15 +1148,6 @@ impl LayoutThread {
let mut d = el.mutate_layout_data().unwrap();
d.base.restyle_damage |= restyle.damage;
}
-
- // Propagate the descendant bit up the ancestors.
- if !hint.is_empty() || !restyle.damage.is_empty() {
- let curr = el;
- while let Some(curr) = curr.parent_element() {
- if curr.has_dirty_descendants() { break }
- unsafe { curr.set_dirty_descendants(); }
- }
- }
}
}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 829b1d98192..b3be9b8ec14 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -477,7 +477,7 @@ impl Document {
}
pub fn content_and_heritage_changed(&self, node: &Node, damage: NodeDamage) {
- node.force_dirty_ancestors(damage);
+ node.dirty(damage);
}
/// Reflows and disarms the timer if the reflow timer has expired.
@@ -1990,12 +1990,12 @@ impl Document {
self.id_map.borrow().get(&id).map(|ref elements| Root::from_ref(&*(*elements)[0]))
}
- fn ensure_pending_restyle(&self, el: &Element) -> RefMut<PendingRestyle> {
+ pub fn ensure_pending_restyle(&self, el: &Element) -> RefMut<PendingRestyle> {
let map = self.pending_restyles.borrow_mut();
RefMut::map(map, |m| m.entry(JS::from_ref(el)).or_insert_with(PendingRestyle::new))
}
- fn ensure_snapshot(&self, el: &Element) -> RefMut<Snapshot> {
+ pub fn ensure_snapshot(&self, el: &Element) -> RefMut<Snapshot> {
let mut entry = self.ensure_pending_restyle(el);
if entry.snapshot.is_none() {
entry.snapshot = Some(Snapshot::new(el.html_element_in_html_document()));
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index ac65c01e24c..b348220c0fb 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -84,13 +84,15 @@ use std::fmt;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
+use style::dom::TRestyleDamage;
use style::element_state::*;
use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
use style::parser::ParserContextExtraData;
use style::properties::{DeclaredValue, Importance};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
use style::properties::longhands::{background_image, border_spacing, font_family, font_size, overflow_x};
-use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl};
+use style::restyle_hints::RESTYLE_SELF;
+use style::selector_impl::{NonTSPseudoClass, RestyleDamage, ServoSelectorImpl};
use style::selector_matching::ApplicableDeclarationBlock;
use style::sink::Push;
use style::values::CSSFloat;
@@ -201,6 +203,19 @@ impl Element {
ElementBinding::Wrap)
}
+ pub fn restyle(&self, damage: NodeDamage) {
+ let doc = self.node.owner_doc();
+ let mut restyle = doc.ensure_pending_restyle(self);
+
+ // FIXME(bholley): I think we should probably only do this for
+ // NodeStyleDamaged, but I'm preserving existing behavior.
+ restyle.hint |= RESTYLE_SELF;
+
+ if damage == NodeDamage::OtherNodeDamage {
+ restyle.damage = RestyleDamage::rebuild_and_reflow();
+ }
+ }
+
// https://drafts.csswg.org/cssom-view/#css-layout-box
// Elements that have a computed value of the display property
// that is table-column or table-column-group
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 0f510719640..5ef7a79ea48 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -446,10 +446,6 @@ impl Node {
self.set_flag(HAS_DIRTY_DESCENDANTS, state)
}
- pub fn force_dirty_ancestors(&self, damage: NodeDamage) {
- self.dirty_impl(damage, true)
- }
-
pub fn rev_version(&self) {
// The new version counter is 1 plus the max of the node's current version counter,
// its descendants version, and the document's version. Normally, this will just be
@@ -464,30 +460,18 @@ impl Node {
}
pub fn dirty(&self, damage: NodeDamage) {
- self.dirty_impl(damage, false)
- }
-
- pub fn dirty_impl(&self, damage: NodeDamage, force_ancestors: bool) {
- // 0. Set version counter
self.rev_version();
-
- // 1. Dirty self.
- match damage {
- NodeDamage::NodeStyleDamaged => {}
- NodeDamage::OtherNodeDamage => self.set_has_changed(true),
- }
-
- if self.is_dirty() && !force_ancestors {
- return
+ if !self.is_in_doc() {
+ return;
}
- self.set_flag(IS_DIRTY, true);
-
- // 4. Dirty ancestors.
- for ancestor in self.ancestors() {
- if !force_ancestors && ancestor.has_dirty_descendants() { break }
- ancestor.set_has_dirty_descendants(true);
- }
+ match self.type_id() {
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text) =>
+ self.parent_node.get().unwrap().downcast::<Element>().unwrap().restyle(damage),
+ NodeTypeId::Element(_) =>
+ self.downcast::<Element>().unwrap().restyle(damage),
+ _ => {},
+ };
}
/// The maximum version number of this node's descendants, including itself
diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs
index 3d61a810111..9a46b65d92a 100644
--- a/components/script/layout_wrapper.rs
+++ b/components/script/layout_wrapper.rs
@@ -529,6 +529,35 @@ impl<'le> ServoLayoutElement<'le> {
self.get_style_and_layout_data().map(|d| &**d.ptr)
}
}
+
+ pub unsafe fn note_dirty_descendant(&self) {
+ use ::selectors::Element;
+
+ let mut current = Some(*self);
+ while let Some(el) = current {
+ // FIXME(bholley): Ideally we'd have the invariant that any element
+ // with has_dirty_descendants also has the bit set on all its ancestors.
+ // However, there are currently some corner-cases where we get that wrong.
+ // I have in-flight patches to fix all this stuff up, so we just always
+ // propagate this bit for now.
+ el.set_dirty_descendants();
+ current = el.parent_element();
+ }
+
+ debug_assert!(self.dirty_descendants_bit_is_propagated());
+ }
+
+ fn dirty_descendants_bit_is_propagated(&self) -> bool {
+ use ::selectors::Element;
+
+ let mut current = Some(*self);
+ while let Some(el) = current {
+ if !el.has_dirty_descendants() { return false; }
+ current = el.parent_element();
+ }
+
+ true
+ }
}
fn as_element<'le>(node: LayoutJS<Node>) -> Option<ServoLayoutElement<'le>> {