diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 9b1d923c763..9a70539c78b 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -225,7 +225,8 @@ impl<'ln> LayoutNode<'ln> { } fn debug_str(self) -> String { - format!("{}: dirty={}", self.type_id(), self.is_dirty()) + format!("{}: changed={} dirty={} dirty_descendants={}", + self.type_id(), self.has_changed(), self.is_dirty(), self.has_dirty_descendants()) } pub fn flow_debug_id(self) -> uint { diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 174a43af664..84016cf75fa 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -177,6 +177,7 @@ pub trait DocumentHelpers<'a> { fn set_last_modified(self, value: DOMString); fn set_encoding_name(self, name: DOMString); fn content_changed(self, node: JSRef); + fn content_and_heritage_changed(self, node: JSRef); fn reflow(self); fn wait_until_safe_to_modify_dom(self); fn unregister_named_element(self, to_unregister: JSRef, id: Atom); @@ -227,10 +228,17 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { } fn content_changed(self, node: JSRef) { + debug!("content_changed on {}", node.debug_str()); node.dirty(); self.reflow(); } + fn content_and_heritage_changed(self, node: JSRef) { + debug!("content_and_heritage_changed on {}", node.debug_str()); + node.force_dirty_ancestors(); + self.reflow(); + } + fn reflow(self) { self.window.root().reflow(); } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index ef655cc1bc3..eb9b263dc73 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -288,7 +288,7 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { let parent = self.parent_node().root(); parent.map(|parent| vtable_for(&*parent).child_inserted(self)); - document.content_changed(self); + document.content_and_heritage_changed(self); } // http://dom.spec.whatwg.org/#node-is-removed @@ -465,6 +465,16 @@ pub trait NodeHelpers<'a> { /// descendants as `IS_DIRTY`. fn dirty(self); + /// Similar to `dirty`, but will always walk the ancestors to mark them dirty, + /// too. This is useful when a node is reparented. The node will frequently + /// already be marked as `changed` to skip double-dirties, but the ancestors + /// still need to be marked as `HAS_DIRTY_DESCENDANTS`. + /// + /// See #4170 + fn force_dirty_ancestors(self); + + fn dirty_impl(self, force_ancestors: bool); + fn dump(self); fn dump_indent(self, indent: uint); fn debug_str(self) -> String; @@ -646,11 +656,19 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { self.set_flag(HAS_DIRTY_DESCENDANTS, state) } + fn force_dirty_ancestors(self) { + self.dirty_impl(true) + } + fn dirty(self) { + self.dirty_impl(false) + } + + fn dirty_impl(self, force_ancestors: bool) { // 1. Dirty self. self.set_has_changed(true); - if self.get_is_dirty() { + if self.get_is_dirty() && !force_ancestors { return } @@ -684,7 +702,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { // 4. Dirty ancestors. for ancestor in self.ancestors() { - if ancestor.get_has_dirty_descendants() { break } + if !force_ancestors && ancestor.get_has_dirty_descendants() { break } ancestor.set_has_dirty_descendants(true); } } @@ -1424,7 +1442,6 @@ impl Node { // http://dom.spec.whatwg.org/#concept-node-replace-all fn replace_all(node: Option>, parent: JSRef) { - // Step 1. match node { Some(node) => {