diff options
author | Anthony Ramine <n.oxyde@gmail.com> | 2015-10-25 10:26:54 +0100 |
---|---|---|
committer | Anthony Ramine <n.oxyde@gmail.com> | 2015-12-25 23:35:57 +0100 |
commit | 3c768356159c19a4ce5f0a07684b6dee9b20f2e4 (patch) | |
tree | cd69d31d1e5587b96c4982098c3c8c5a04c56def /components/script/dom/node.rs | |
parent | 1dd7c8cf01b27f63951504a63f5c69cca53ac73a (diff) | |
download | servo-3c768356159c19a4ce5f0a07684b6dee9b20f2e4.tar.gz servo-3c768356159c19a4ce5f0a07684b6dee9b20f2e4.zip |
Properly propagate changes when range or trees are mutated
Diffstat (limited to 'components/script/dom/node.rs')
-rw-r--r-- | components/script/dom/node.rs | 133 |
1 files changed, 103 insertions, 30 deletions
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a47e9340085..be48cc11f93 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -42,6 +42,7 @@ use dom::htmlcollection::HTMLCollection; use dom::htmlelement::HTMLElement; use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; +use dom::range::WeakRangeVec; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::window::Window; @@ -108,6 +109,12 @@ pub struct Node { /// The maximum version of any inclusive descendant of this node. inclusive_descendants_version: Cell<u64>, + /// A vector of weak references to Range instances of which the start + /// or end containers are this node. No range should ever be found + /// twice in this vector, even if both the start and end containers + /// are this node. + ranges: WeakRangeVec, + /// Layout information. Only the layout task may touch this data. /// /// Must be sent back to the layout task to be destroyed when this @@ -296,7 +303,7 @@ impl Node { /// Removes the given child from this node's list of children. /// /// Fails unless `child` is a child of this node. - fn remove_child(&self, child: &Node) { + fn remove_child(&self, child: &Node, cached_index: Option<u32>) { assert!(child.parent_node.get().r() == Some(self)); let prev_sibling = child.GetPreviousSibling(); match prev_sibling { @@ -317,9 +324,7 @@ impl Node { } } - let context = UnbindContext { - tree_in_doc: child.is_in_doc(), - }; + let context = UnbindContext::new(self, prev_sibling.r(), cached_index); child.prev_sibling.set(None); child.next_sibling.set(None); @@ -437,6 +442,10 @@ impl Node { self.children_count.get() } + pub fn ranges(&self) -> &WeakRangeVec { + &self.ranges + } + #[inline] pub fn is_doctype(&self) -> bool { self.type_id() == NodeTypeId::DocumentType @@ -1305,6 +1314,7 @@ impl Node { children_count: Cell::new(0u32), flags: Cell::new(flags), inclusive_descendants_version: Cell::new(0), + ranges: WeakRangeVec::new(), layout_data: LayoutDataRef::new(), @@ -1479,7 +1489,20 @@ impl Node { debug_assert!(&*node.owner_doc() == &*parent.owner_doc()); debug_assert!(child.map_or(true, |child| Some(parent) == child.GetParentNode().r())); - // Steps 1-2: ranges. + // Step 1. + let count = if node.is::<DocumentFragment>() { + node.children_count() + } else { + 1 + }; + // Step 2. + if let Some(child) = child { + if !parent.ranges.is_empty() { + let index = child.index(); + // Steps 2.1-2. + parent.ranges.increase_above(parent, index, count); + } + } let mut new_nodes = RootedVec::new(); let new_nodes = if let NodeTypeId::DocumentFragment = node.type_id() { // Step 3. @@ -1569,14 +1592,27 @@ impl Node { // https://dom.spec.whatwg.org/#concept-node-remove fn remove(node: &Node, parent: &Node, suppress_observers: SuppressObserver) { assert!(node.GetParentNode().map_or(false, |node_parent| node_parent.r() == parent)); - - // Step 1-5: ranges. + let cached_index = { + if parent.ranges.is_empty() { + None + } else { + // Step 1. + let index = node.index(); + // Steps 2-3 are handled in Node::unbind_from_tree. + // Steps 4-5. + parent.ranges.decrease_above(parent, index, 1); + // Parent had ranges, we needed the index, let's keep track of + // it to avoid computing it for other ranges when calling + // unbind_from_tree recursively. + Some(index) + } + }; // Step 6. let old_previous_sibling = node.GetPreviousSibling(); // Steps 7-8: mutation observers. // Step 9. let old_next_sibling = node.GetNextSibling(); - parent.remove_child(node); + parent.remove_child(node, cached_index); if let SuppressObserver::Unsuppressed = suppress_observers { vtable_for(&parent).children_changed( &ChildrenMutation::replace(old_previous_sibling.r(), @@ -2078,28 +2114,26 @@ impl NodeMethods for Node { // https://dom.spec.whatwg.org/#dom-node-normalize fn Normalize(&self) { - let mut prev_text: Option<Root<Text>> = None; - for child in self.children() { - match child.downcast::<Text>() { - Some(text) => { - let characterdata = text.upcast::<CharacterData>(); - if characterdata.Length() == 0 { - Node::remove(&*child, self, SuppressObserver::Unsuppressed); - } else { - match prev_text { - Some(ref text_node) => { - let prev_characterdata = text_node.upcast::<CharacterData>(); - prev_characterdata.append_data(&**characterdata.data()); - Node::remove(&*child, self, SuppressObserver::Unsuppressed); - }, - None => prev_text = Some(Root::from_ref(text)) - } - } - }, - None => { - child.Normalize(); - prev_text = None; + let mut children = self.children().enumerate().peekable(); + while let Some((_, node)) = children.next() { + if let Some(text) = node.downcast::<Text>() { + let cdata = text.upcast::<CharacterData>(); + let mut length = cdata.Length(); + if length == 0 { + Node::remove(&node, self, SuppressObserver::Unsuppressed); + continue; } + while children.peek().map_or(false, |&(_, ref sibling)| sibling.is::<Text>()) { + let (index, sibling) = children.next().unwrap(); + sibling.ranges.drain_to_preceding_text_sibling(&sibling, &node, length); + self.ranges.move_to_text_child_at(self, index as u32, &node, length as u32); + let sibling_cdata = sibling.downcast::<CharacterData>().unwrap(); + length += sibling_cdata.Length(); + cdata.append_data(&sibling_cdata.data()); + Node::remove(&sibling, self, SuppressObserver::Unsuppressed); + } + } else { + node.Normalize(); } } } @@ -2338,6 +2372,13 @@ impl VirtualMethods for Node { list.as_children_list().children_changed(mutation); } } + + // This handles the ranges mentioned in steps 2-3 when removing a node. + // https://dom.spec.whatwg.org/#concept-node-remove + fn unbind_from_tree(&self, context: &UnbindContext) { + self.super_type().unwrap().unbind_from_tree(context); + self.ranges.drain_to_parent(context, self); + } } /// A summary of the changes that happened to a node. @@ -2413,7 +2454,39 @@ impl<'a> ChildrenMutation<'a> { /// The context of the unbinding from a tree of a node when one of its /// inclusive ancestors is removed. -pub struct UnbindContext { +pub struct UnbindContext<'a> { + /// The index of the inclusive ancestor that was removed. + index: Cell<Option<u32>>, + /// The parent of the inclusive ancestor that was removed. + pub parent: &'a Node, + /// The previous sibling of the inclusive ancestor that was removed. + prev_sibling: Option<&'a Node>, /// Whether the tree is in a document. pub tree_in_doc: bool, } + +impl<'a> UnbindContext<'a> { + /// Create a new `UnbindContext` value. + fn new(parent: &'a Node, + prev_sibling: Option<&'a Node>, + cached_index: Option<u32>) -> Self { + UnbindContext { + index: Cell::new(cached_index), + parent: parent, + prev_sibling: prev_sibling, + tree_in_doc: parent.is_in_doc(), + } + } + + /// The index of the inclusive ancestor that was removed from the tree. + #[allow(unsafe_code)] + pub fn index(&self) -> u32 { + if let Some(index) = self.index.get() { + return index; + } + let index = + self.prev_sibling.map(|sibling| sibling.index() + 1).unwrap_or(0); + self.index.set(Some(index)); + index + } +} |