diff options
Diffstat (limited to 'components/script/dom/node.rs')
-rw-r--r-- | components/script/dom/node.rs | 223 |
1 files changed, 142 insertions, 81 deletions
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index c54d85e8b28..a2a39979ad2 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -33,7 +33,6 @@ use dom::bindings::trace::JSTraceable; use dom::bindings::trace::RootedVec; use dom::bindings::xmlname::namespace_from_domstring; use dom::characterdata::CharacterData; -use dom::comment::Comment; use dom::document::{Document, DocumentSource, IsHTMLDocument}; use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; @@ -43,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; @@ -109,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 @@ -283,6 +289,7 @@ impl Node { } new_child.parent_node.set(Some(self)); + self.children_count.set(self.children_count.get() + 1); let parent_in_doc = self.is_in_doc(); for node in new_child.traverse_preorder() { @@ -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,14 +324,16 @@ impl Node { } } + let context = UnbindContext::new(self, prev_sibling.r(), cached_index); + child.prev_sibling.set(None); child.next_sibling.set(None); child.parent_node.set(None); + self.children_count.set(self.children_count.get() - 1); - let parent_in_doc = self.is_in_doc(); for node in child.traverse_preorder() { node.set_flag(IS_IN_DOC, false); - vtable_for(&&*node).unbind_from_tree(parent_in_doc); + vtable_for(&&*node).unbind_from_tree(&context); node.layout_data.dispose(&node); } @@ -433,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 @@ -1307,6 +1320,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(), @@ -1481,7 +1495,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. @@ -1571,18 +1598,31 @@ 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(), - &node, &[], + &Some(&node), &[], old_next_sibling.r())); } } @@ -1611,10 +1651,9 @@ impl Node { let doc_fragment = DocumentFragment::new(document.r()); Root::upcast::<Node>(doc_fragment) }, - NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => { + NodeTypeId::CharacterData(_) => { let cdata = node.downcast::<CharacterData>().unwrap(); - let comment = Comment::new(cdata.Data(), document.r()); - Root::upcast::<Node>(comment) + cdata.clone_with_data(cdata.Data(), &document) }, NodeTypeId::Document(_) => { let document = node.downcast::<Document>().unwrap(); @@ -1640,17 +1679,6 @@ impl Node { document.r(), ElementCreator::ScriptCreated); Root::upcast::<Node>(element) }, - NodeTypeId::CharacterData(CharacterDataTypeId::Text) => { - let cdata = node.downcast::<CharacterData>().unwrap(); - let text = Text::new(cdata.Data(), document.r()); - Root::upcast::<Node>(text) - }, - NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => { - let pi = node.downcast::<ProcessingInstruction>().unwrap(); - let pi = ProcessingInstruction::new(pi.Target(), - pi.upcast::<CharacterData>().Data(), document.r()); - Root::upcast::<Node>(pi) - }, }; // Step 3. @@ -2038,11 +2066,6 @@ impl NodeMethods for Node { } } - // Ok if not caught by previous error checks. - if node == child { - return Ok(Root::from_ref(child)); - } - // Step 7-8. let child_next_sibling = child.GetNextSibling(); let node_next_sibling = node.GetNextSibling(); @@ -2053,14 +2076,19 @@ impl NodeMethods for Node { }; // Step 9. - let document = document_from_node(self); - Node::adopt(node, document.r()); + let previous_sibling = child.GetPreviousSibling(); // Step 10. - let previous_sibling = child.GetPreviousSibling(); + let document = document_from_node(self); + Node::adopt(node, document.r()); - // Step 11. - Node::remove(child, self, SuppressObserver::Suppressed); + let removed_child = if node != child { + // Step 11. + Node::remove(child, self, SuppressObserver::Suppressed); + Some(child) + } else { + None + }; // Step 12. let mut nodes = RootedVec::new(); @@ -2077,7 +2105,7 @@ impl NodeMethods for Node { // Step 14. vtable_for(&self).children_changed( &ChildrenMutation::replace(previous_sibling.r(), - &child, nodes, + &removed_child, nodes, reference_child)); // Step 15. @@ -2092,28 +2120,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(); } } } @@ -2348,25 +2374,17 @@ impl VirtualMethods for Node { if let Some(ref s) = self.super_type() { s.children_changed(mutation); } - match *mutation { - ChildrenMutation::Append { added, .. } | - ChildrenMutation::Insert { added, .. } | - ChildrenMutation::Prepend { added, .. } => { - self.children_count.set( - self.children_count.get() + added.len() as u32); - }, - ChildrenMutation::Replace { added, .. } => { - self.children_count.set( - self.children_count.get() - 1u32 + added.len() as u32); - }, - ChildrenMutation::ReplaceAll { added, .. } => { - self.children_count.set(added.len() as u32); - }, - } if let Some(list) = self.child_list.get() { 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. @@ -2411,22 +2429,26 @@ impl<'a> ChildrenMutation<'a> { } fn replace(prev: Option<&'a Node>, - removed: &'a &'a Node, + removed: &'a Option<&'a Node>, added: &'a [&'a Node], next: Option<&'a Node>) -> ChildrenMutation<'a> { - if let (None, None) = (prev, next) { - ChildrenMutation::ReplaceAll { - removed: ref_slice(removed), - added: added, + if let Some(ref removed) = *removed { + if let (None, None) = (prev, next) { + ChildrenMutation::ReplaceAll { + removed: ref_slice(removed), + added: added, + } + } else { + ChildrenMutation::Replace { + prev: prev, + removed: *removed, + added: added, + next: next, + } } } else { - ChildrenMutation::Replace { - prev: prev, - removed: *removed, - added: added, - next: next, - } + ChildrenMutation::insert(prev, added, next) } } @@ -2435,3 +2457,42 @@ impl<'a> ChildrenMutation<'a> { ChildrenMutation::ReplaceAll { removed: removed, added: added } } } + +/// The context of the unbinding from a tree of a node when one of its +/// inclusive ancestors is removed. +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 + } +} |