aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2015-12-26 04:08:15 +0530
committerbors-servo <lbergstrom+bors@mozilla.com>2015-12-26 04:08:15 +0530
commit89ab368258eb827b0dcc8d6e6deecd3ed3c1de71 (patch)
treecf25fa3290e5c6c9b60ddeef014c54c334a28201 /components/script
parent7db6ce41d2a0748285d2a3c3f4141e8bd30ee8ef (diff)
parent3c768356159c19a4ce5f0a07684b6dee9b20f2e4 (diff)
downloadservo-89ab368258eb827b0dcc8d6e6deecd3ed3c1de71.tar.gz
servo-89ab368258eb827b0dcc8d6e6deecd3ed3c1de71.zip
Auto merge of #8506 - nox:finish-ranges, r=dzbarsky
Properly propagate changes when range or trees are mutated Does the same thing as #6817, but storing Range instances directly in their start and end containers. Cc @dzbarsky <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8506) <!-- Reviewable:end -->
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf4
-rw-r--r--components/script/dom/bindings/weakref.rs99
-rw-r--r--components/script/dom/characterdata.rs66
-rw-r--r--components/script/dom/element.rs10
-rw-r--r--components/script/dom/htmlbaseelement.rs8
-rw-r--r--components/script/dom/htmlbuttonelement.rs8
-rw-r--r--components/script/dom/htmliframeelement.rs8
-rw-r--r--components/script/dom/htmlinputelement.rs8
-rw-r--r--components/script/dom/htmloptionelement.rs8
-rw-r--r--components/script/dom/htmlselectelement.rs8
-rw-r--r--components/script/dom/htmltextareaelement.rs8
-rw-r--r--components/script/dom/node.rs223
-rw-r--r--components/script/dom/range.rs392
-rw-r--r--components/script/dom/text.rs9
-rw-r--r--components/script/dom/virtualmethods.rs6
-rw-r--r--components/script/lib.rs1
16 files changed, 662 insertions, 204 deletions
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index 00ccea7fb63..dfb31685fd1 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -18,6 +18,10 @@ DOMInterfaces = {
'outerObjectHook': 'Some(bindings::utils::outerize_global)',
},
+'Range': {
+ 'weakReferenceable': True,
+},
+
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
'TestBinding': {},
diff --git a/components/script/dom/bindings/weakref.rs b/components/script/dom/bindings/weakref.rs
index 248944d9962..e311d4a40bb 100644
--- a/components/script/dom/bindings/weakref.rs
+++ b/components/script/dom/bindings/weakref.rs
@@ -19,7 +19,9 @@ use js::jsapi::{JSTracer, JS_GetReservedSlot, JS_SetReservedSlot};
use js::jsval::PrivateValue;
use libc::c_void;
use std::cell::{Cell, UnsafeCell};
+use std::iter::Iterator;
use std::mem;
+use std::ops::{Deref, DerefMut, Drop};
use util::mem::HeapSizeOf;
/// The index of the slot wherein a pointer to the weak holder cell is
@@ -113,6 +115,25 @@ impl<T: WeakReferenceable> HeapSizeOf for WeakRef<T> {
}
}
+impl<T: WeakReferenceable> PartialEq for WeakRef<T> {
+ fn eq(&self, other: &Self) -> bool {
+ unsafe {
+ (**self.ptr).value.get() == (**other.ptr).value.get()
+ }
+ }
+}
+
+impl<T: WeakReferenceable> PartialEq<T> for WeakRef<T> {
+ fn eq(&self, other: &T) -> bool {
+ unsafe {
+ match (**self.ptr).value.get() {
+ Some(ptr) => *ptr == other,
+ None => false,
+ }
+ }
+ }
+}
+
no_jsmanaged_fields!(WeakRef<T: WeakReferenceable>);
impl<T: WeakReferenceable> Drop for WeakRef<T> {
@@ -182,3 +203,81 @@ impl<T: WeakReferenceable> JSTraceable for MutableWeakRef<T> {
}
}
}
+
+/// A vector of weak references. On tracing, the vector retains
+/// only references which still point to live objects.
+#[allow_unrooted_interior]
+#[derive(HeapSizeOf)]
+pub struct WeakRefVec<T: WeakReferenceable> {
+ vec: Vec<WeakRef<T>>,
+}
+
+impl<T: WeakReferenceable> WeakRefVec<T> {
+ /// Create a new vector of weak references.
+ pub fn new() -> Self {
+ WeakRefVec { vec: vec![] }
+ }
+
+ /// Calls a function on each reference which still points to a
+ /// live object. The order of the references isn't preserved.
+ pub fn update<F: FnMut(WeakRefEntry<T>)>(&mut self, mut f: F) {
+ let mut i = 0;
+ while i < self.vec.len() {
+ if self.vec[i].is_alive() {
+ f(WeakRefEntry { vec: self, index: &mut i });
+ } else {
+ self.vec.swap_remove(i);
+ }
+ }
+ }
+
+ /// Clears the vector of its dead references.
+ pub fn retain_alive(&mut self) {
+ self.update(|_| ());
+ }
+}
+
+impl<T: WeakReferenceable> Deref for WeakRefVec<T> {
+ type Target = Vec<WeakRef<T>>;
+
+ fn deref(&self) -> &Vec<WeakRef<T>> {
+ &self.vec
+ }
+}
+
+impl<T: WeakReferenceable> DerefMut for WeakRefVec<T> {
+ fn deref_mut(&mut self) -> &mut Vec<WeakRef<T>> {
+ &mut self.vec
+ }
+}
+
+/// An entry of a vector of weak references. Passed to the closure
+/// given to `WeakRefVec::update`.
+#[allow_unrooted_interior]
+pub struct WeakRefEntry<'a, T: WeakReferenceable + 'a> {
+ vec: &'a mut WeakRefVec<T>,
+ index: &'a mut usize,
+}
+
+impl<'a, T: WeakReferenceable + 'a> WeakRefEntry<'a, T> {
+ /// Remove the entry from the underlying vector of weak references.
+ pub fn remove(self) -> WeakRef<T> {
+ let ref_ = self.vec.swap_remove(*self.index);
+ mem::forget(self);
+ ref_
+ }
+}
+
+impl<'a, T: WeakReferenceable + 'a> Deref for WeakRefEntry<'a, T> {
+ type Target = WeakRef<T>;
+
+ fn deref(&self) -> &WeakRef<T> {
+ &self.vec[*self.index]
+ }
+}
+
+impl<'a, T: WeakReferenceable + 'a> Drop for WeakRefEntry<'a, T> {
+ fn drop(&mut self) {
+ *self.index += 1;
+ }
+}
diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs
index 8ed455c507a..d9798f76a11 100644
--- a/components/script/dom/characterdata.rs
+++ b/components/script/dom/characterdata.rs
@@ -6,13 +6,18 @@
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
+use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
+use dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId};
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
+use dom::comment::Comment;
use dom::document::Document;
use dom::element::Element;
use dom::node::{Node, NodeDamage};
+use dom::processinginstruction::ProcessingInstruction;
+use dom::text::Text;
use std::cell::Ref;
use util::str::DOMString;
@@ -30,6 +35,38 @@ impl CharacterData {
data: DOMRefCell::new(data),
}
}
+
+ pub fn clone_with_data(&self, data: DOMString, document: &Document) -> Root<Node> {
+ match self.upcast::<Node>().type_id() {
+ NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => {
+ Root::upcast(Comment::new(data, &document))
+ }
+ NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
+ let pi = self.downcast::<ProcessingInstruction>().unwrap();
+ Root::upcast(ProcessingInstruction::new(pi.Target(), data, &document))
+ },
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text) => {
+ Root::upcast(Text::new(data, &document))
+ },
+ _ => unreachable!(),
+ }
+ }
+
+ #[inline]
+ pub fn data(&self) -> Ref<DOMString> {
+ self.data.borrow()
+ }
+
+ #[inline]
+ pub fn append_data(&self, data: &str) {
+ self.data.borrow_mut().push_str(data);
+ self.content_changed();
+ }
+
+ fn content_changed(&self) {
+ let node = self.upcast::<Node>();
+ node.owner_doc().content_changed(node, NodeDamage::OtherNodeDamage);
+ }
}
impl CharacterDataMethods for CharacterData {
@@ -40,13 +77,17 @@ impl CharacterDataMethods for CharacterData {
// https://dom.spec.whatwg.org/#dom-characterdata-data
fn SetData(&self, data: DOMString) {
+ let old_length = self.Length();
+ let new_length = data.utf16_units().count() as u32;
*self.data.borrow_mut() = data;
self.content_changed();
+ let node = self.upcast::<Node>();
+ node.ranges().replace_code_units(node, 0, old_length, new_length);
}
// https://dom.spec.whatwg.org/#dom-characterdata-length
fn Length(&self) -> u32 {
- self.data.borrow().chars().map(|c| c.len_utf16()).sum::<usize>() as u32
+ self.data.borrow().utf16_units().count() as u32
}
// https://dom.spec.whatwg.org/#dom-characterdata-substringdata
@@ -107,7 +148,10 @@ impl CharacterDataMethods for CharacterData {
};
*self.data.borrow_mut() = DOMString::from(new_data);
self.content_changed();
- // FIXME: Once we have `Range`, we should implement step 8 to step 11
+ // Steps 8-11.
+ let node = self.upcast::<Node>();
+ node.ranges().replace_code_units(
+ node, offset, count, arg.utf16_units().count() as u32);
Ok(())
}
@@ -143,24 +187,6 @@ impl CharacterDataMethods for CharacterData {
}
}
-impl CharacterData {
- #[inline]
- pub fn data(&self) -> Ref<DOMString> {
- self.data.borrow()
- }
- #[inline]
- pub fn append_data(&self, data: &str) {
- // FIXME(ajeffrey): Efficient append on DOMStrings?
- self.data.borrow_mut().push_str(data);
- self.content_changed();
- }
-
- fn content_changed(&self) {
- let node = self.upcast::<Node>();
- node.owner_doc().content_changed(node, NodeDamage::OtherNodeDamage);
- }
-}
-
#[allow(unsafe_code)]
pub trait LayoutCharacterDataHelpers {
unsafe fn data_for_layout(&self) -> &str;
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 0293281892d..6f766ea961c 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -53,7 +53,7 @@ use dom::htmltemplateelement::HTMLTemplateElement;
use dom::htmltextareaelement::{HTMLTextAreaElement, RawLayoutHTMLTextAreaElementHelpers};
use dom::namednodemap::NamedNodeMap;
use dom::node::{CLICK_IN_PROGRESS, LayoutNodeHelpers, Node};
-use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE};
+use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE, UnbindContext};
use dom::node::{document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::text::Text;
@@ -1615,12 +1615,10 @@ impl VirtualMethods for Element {
}
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
- }
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
- if !tree_in_doc {
+ if !context.tree_in_doc {
return;
}
diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs
index c281d1269ad..7f1ce7641d3 100644
--- a/components/script/dom/htmlbaseelement.rs
+++ b/components/script/dom/htmlbaseelement.rs
@@ -9,7 +9,7 @@ use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::{AttributeMutation, Element};
use dom::htmlelement::HTMLElement;
-use dom::node::{Node, document_from_node};
+use dom::node::{Node, UnbindContext, document_from_node};
use dom::virtualmethods::VirtualMethods;
use string_cache::Atom;
use url::Url;
@@ -77,8 +77,8 @@ impl VirtualMethods for HTMLBaseElement {
self.bind_unbind(tree_in_doc);
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- self.super_type().unwrap().unbind_from_tree(tree_in_doc);
- self.bind_unbind(tree_in_doc);
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
+ self.bind_unbind(context.tree_in_doc);
}
}
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs
index 07c508bd03d..ad60d8fb38c 100644
--- a/components/script/dom/htmlbuttonelement.rs
+++ b/components/script/dom/htmlbuttonelement.rs
@@ -16,7 +16,7 @@ use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, FormSubmitter};
use dom::htmlformelement::{SubmittedFrom, HTMLFormElement};
-use dom::node::{Node, document_from_node, window_from_node};
+use dom::node::{Node, UnbindContext, document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
@@ -174,10 +174,8 @@ impl VirtualMethods for HTMLButtonElement {
self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
- }
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index c51e98d8f92..12a432f4c91 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -20,7 +20,7 @@ use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
-use dom::node::{Node, window_from_node};
+use dom::node::{Node, UnbindContext, window_from_node};
use dom::urlhelper::UrlHelper;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
@@ -483,10 +483,8 @@ impl VirtualMethods for HTMLIFrameElement {
}
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
- }
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
// https://html.spec.whatwg.org/multipage/#a-browsing-context-is-discarded
if let Some(pipeline_id) = self.pipeline_id.get() {
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index a7143133b05..9413699ede7 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -23,7 +23,7 @@ use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, FormDatum, FormSubmitter, HTMLFormElement};
use dom::htmlformelement::{ResetFrom, SubmittedFrom};
use dom::keyboardevent::KeyboardEvent;
-use dom::node::{Node, NodeDamage};
+use dom::node::{Node, NodeDamage, UnbindContext};
use dom::node::{document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::virtualmethods::VirtualMethods;
@@ -645,10 +645,8 @@ impl VirtualMethods for HTMLInputElement {
self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
- }
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs
index 67e58b3d46e..3aff70c70f7 100644
--- a/components/script/dom/htmloptionelement.rs
+++ b/components/script/dom/htmloptionelement.rs
@@ -15,7 +15,7 @@ use dom::element::{AttributeMutation, Element};
use dom::htmlelement::HTMLElement;
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlselectelement::HTMLSelectElement;
-use dom::node::Node;
+use dom::node::{Node, UnbindContext};
use dom::text::Text;
use dom::virtualmethods::VirtualMethods;
use selectors::states::*;
@@ -207,10 +207,8 @@ impl VirtualMethods for HTMLOptionElement {
self.pick_if_selected_and_reset();
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
- }
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index a9310997851..daf4d6f9ba5 100644
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -16,7 +16,7 @@ use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::htmloptionelement::HTMLOptionElement;
-use dom::node::{Node, window_from_node};
+use dom::node::{Node, UnbindContext, window_from_node};
use dom::nodelist::NodeList;
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
@@ -196,10 +196,8 @@ impl VirtualMethods for HTMLSelectElement {
self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
- }
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
index 386d52f591d..8d856ff6b80 100644
--- a/components/script/dom/htmltextareaelement.rs
+++ b/components/script/dom/htmltextareaelement.rs
@@ -20,7 +20,7 @@ use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::keyboardevent::KeyboardEvent;
-use dom::node::{ChildrenMutation, Node, NodeDamage};
+use dom::node::{ChildrenMutation, Node, NodeDamage, UnbindContext};
use dom::node::{document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::virtualmethods::VirtualMethods;
@@ -300,10 +300,8 @@ impl VirtualMethods for HTMLTextAreaElement {
}
}
- fn unbind_from_tree(&self, tree_in_doc: bool) {
- if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
- }
+ fn unbind_from_tree(&self, context: &UnbindContext) {
+ self.super_type().unwrap().unbind_from_tree(context);
let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
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
+ }
+}
diff --git a/components/script/dom/range.rs b/components/script/dom/range.rs
index 58200ee35f2..03db51ba0d8 100644
--- a/components/script/dom/range.rs
+++ b/components/script/dom/range.rs
@@ -16,14 +16,17 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::inheritance::{CharacterDataTypeId, NodeTypeId};
use dom::bindings::js::{JS, MutHeap, Root, RootedReference};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
-use dom::bindings::trace::RootedVec;
+use dom::bindings::trace::{JSTraceable, RootedVec};
+use dom::bindings::weakref::{WeakRef, WeakRefVec};
use dom::characterdata::CharacterData;
use dom::document::Document;
use dom::documentfragment::DocumentFragment;
-use dom::node::Node;
+use dom::node::{Node, UnbindContext};
use dom::text::Text;
-use std::cell::Cell;
+use js::jsapi::JSTracer;
+use std::cell::{Cell, UnsafeCell};
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
+use util::mem::HeapSizeOf;
use util::str::DOMString;
#[dom_struct]
@@ -52,10 +55,15 @@ impl Range {
start_container: &Node, start_offset: u32,
end_container: &Node, end_offset: u32)
-> Root<Range> {
- reflect_dom_object(box Range::new_inherited(start_container, start_offset,
- end_container, end_offset),
- GlobalRef::Window(document.window()),
- RangeBinding::Wrap)
+ let range = reflect_dom_object(box Range::new_inherited(start_container, start_offset,
+ end_container, end_offset),
+ GlobalRef::Window(document.window()),
+ RangeBinding::Wrap);
+ start_container.ranges().push(WeakRef::new(&range));
+ if start_container != end_container {
+ end_container.ranges().push(WeakRef::new(&range));
+ }
+ range
}
// https://dom.spec.whatwg.org/#dom-range
@@ -121,19 +129,31 @@ impl Range {
}
// https://dom.spec.whatwg.org/#concept-range-bp-set
- pub fn set_start(&self, node: &Node, offset: u32) {
- self.start.set(node, offset);
- if !(self.start <= self.end) {
- self.end.set(node, offset);
+ fn set_start(&self, node: &Node, offset: u32) {
+ if &self.start.node != node {
+ if self.start.node == self.end.node {
+ node.ranges().push(WeakRef::new(&self));
+ } else if &self.end.node == node {
+ self.StartContainer().ranges().remove(self);
+ } else {
+ node.ranges().push(self.StartContainer().ranges().remove(self));
+ }
}
+ self.start.set(node, offset);
}
// https://dom.spec.whatwg.org/#concept-range-bp-set
- pub fn set_end(&self, node: &Node, offset: u32) {
- self.end.set(node, offset);
- if !(self.end >= self.start) {
- self.start.set(node, offset);
+ fn set_end(&self, node: &Node, offset: u32) {
+ if &self.end.node != node {
+ if self.end.node == self.start.node {
+ node.ranges().push(WeakRef::new(&self));
+ } else if &self.start.node == node {
+ self.EndContainer().ranges().remove(self);
+ } else {
+ node.ranges().push(self.EndContainer().ranges().remove(self));
+ }
}
+ self.end.set(node, offset);
}
// https://dom.spec.whatwg.org/#dom-range-comparepointnode-offset
@@ -206,7 +226,7 @@ impl RangeMethods for Range {
unreachable!();
}
- // https://dom.spec.whatwg.org/#dom-range-setstartnode-offset
+ // https://dom.spec.whatwg.org/#dom-range-setstart
fn SetStart(&self, node: &Node, offset: u32) -> ErrorResult {
if node.is_doctype() {
// Step 1.
@@ -215,13 +235,17 @@ impl RangeMethods for Range {
// Step 2.
Err(Error::IndexSize)
} else {
- // Step 3-4.
+ // Step 3.
self.set_start(node, offset);
+ if !(self.start <= self.end) {
+ // Step 4.
+ self.set_end(node, offset);
+ }
Ok(())
}
}
- // https://dom.spec.whatwg.org/#dom-range-setendnode-offset
+ // https://dom.spec.whatwg.org/#dom-range-setend
fn SetEnd(&self, node: &Node, offset: u32) -> ErrorResult {
if node.is_doctype() {
// Step 1.
@@ -230,59 +254,63 @@ impl RangeMethods for Range {
// Step 2.
Err(Error::IndexSize)
} else {
- // Step 3-4.
+ // Step 3.
self.set_end(node, offset);
+ if !(self.end >= self.start) {
+ // Step 4.
+ self.set_start(node, offset);
+ }
Ok(())
}
}
- // https://dom.spec.whatwg.org/#dom-range-setstartbeforenode
+ // https://dom.spec.whatwg.org/#dom-range-setstartbefore
fn SetStartBefore(&self, node: &Node) -> ErrorResult {
let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType));
self.SetStart(parent.r(), node.index())
}
- // https://dom.spec.whatwg.org/#dom-range-setstartafternode
+ // https://dom.spec.whatwg.org/#dom-range-setstartafter
fn SetStartAfter(&self, node: &Node) -> ErrorResult {
let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType));
self.SetStart(parent.r(), node.index() + 1)
}
- // https://dom.spec.whatwg.org/#dom-range-setendbeforenode
+ // https://dom.spec.whatwg.org/#dom-range-setendbefore
fn SetEndBefore(&self, node: &Node) -> ErrorResult {
let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType));
self.SetEnd(parent.r(), node.index())
}
- // https://dom.spec.whatwg.org/#dom-range-setendafternode
+ // https://dom.spec.whatwg.org/#dom-range-setendafter
fn SetEndAfter(&self, node: &Node) -> ErrorResult {
let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType));
self.SetEnd(parent.r(), node.index() + 1)
}
- // https://dom.spec.whatwg.org/#dom-range-collapsetostart
+ // https://dom.spec.whatwg.org/#dom-range-collapse
fn Collapse(&self, to_start: bool) {
if to_start {
- self.end.set(&self.StartContainer(), self.StartOffset());
+ self.set_end(&self.StartContainer(), self.StartOffset());
} else {
- self.start.set(&self.EndContainer(), self.EndOffset());
+ self.set_start(&self.EndContainer(), self.EndOffset());
}
}
- // https://dom.spec.whatwg.org/#dom-range-selectnodenode
+ // https://dom.spec.whatwg.org/#dom-range-selectnode
fn SelectNode(&self, node: &Node) -> ErrorResult {
// Steps 1, 2.
let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType));
// Step 3.
let index = node.index();
// Step 4.
- self.start.set(&parent, index);
+ self.set_start(&parent, index);
// Step 5.
- self.end.set(&parent, index + 1);
+ self.set_end(&parent, index + 1);
Ok(())
}
- // https://dom.spec.whatwg.org/#dom-range-selectnodecontentsnode
+ // https://dom.spec.whatwg.org/#dom-range-selectnodecontents
fn SelectNodeContents(&self, node: &Node) -> ErrorResult {
if node.is_doctype() {
// Step 1.
@@ -291,13 +319,13 @@ impl RangeMethods for Range {
// Step 2.
let length = node.len();
// Step 3.
- self.start.set(node, 0);
+ self.set_start(node, 0);
// Step 4.
- self.end.set(node, length);
+ self.set_end(node, length);
Ok(())
}
- // https://dom.spec.whatwg.org/#dom-range-compareboundarypointshow-sourcerange
+ // https://dom.spec.whatwg.org/#dom-range-compareboundarypoints
fn CompareBoundaryPoints(&self, how: u16, other: &Range)
-> Fallible<i16> {
if how > RangeConstants::END_TO_START {
@@ -342,7 +370,7 @@ impl RangeMethods for Range {
&self.EndContainer(), self.EndOffset())
}
- // https://dom.spec.whatwg.org/#dom-range-ispointinrangenode-offset
+ // https://dom.spec.whatwg.org/#dom-range-ispointinrange
fn IsPointInRange(&self, node: &Node, offset: u32) -> Fallible<bool> {
match self.compare_point(node, offset) {
Ok(Ordering::Less) => Ok(false),
@@ -356,7 +384,7 @@ impl RangeMethods for Range {
}
}
- // https://dom.spec.whatwg.org/#dom-range-comparepointnode-offset
+ // https://dom.spec.whatwg.org/#dom-range-comparepoint
fn ComparePoint(&self, node: &Node, offset: u32) -> Fallible<i16> {
self.compare_point(node, offset).map(|order| {
match order {
@@ -410,12 +438,10 @@ impl RangeMethods for Range {
}
if end_node == start_node {
- if let Some(text) = start_node.downcast::<CharacterData>() {
- // Step 4.1.
- let clone = start_node.CloneNode(true);
- // Step 4.2.
- let text = text.SubstringData(start_offset, end_offset - start_offset);
- clone.downcast::<CharacterData>().unwrap().SetData(text.unwrap());
+ if let Some(cdata) = start_node.downcast::<CharacterData>() {
+ // Steps 4.1-2.
+ let data = cdata.SubstringData(start_offset, end_offset - start_offset).unwrap();
+ let clone = cdata.clone_with_data(data, &start_node.owner_doc());
// Step 4.3.
try!(fragment.upcast::<Node>().AppendChild(&clone));
// Step 4.4
@@ -429,13 +455,11 @@ impl RangeMethods for Range {
if let Some(child) = first_contained_child {
// Step 13.
- if let Some(text) = child.downcast::<CharacterData>() {
+ if let Some(cdata) = child.downcast::<CharacterData>() {
assert!(child == start_node);
- // Step 13.1.
- let clone = start_node.CloneNode(true); // CharacterData has no children.
- // Step 13.2
- let text = text.SubstringData(start_offset, start_node.len() - start_offset);
- clone.downcast::<CharacterData>().unwrap().SetData(text.unwrap());
+ // Steps 13.1-2.
+ let data = cdata.SubstringData(start_offset, start_node.len() - start_offset).unwrap();
+ let clone = cdata.clone_with_data(data, &start_node.owner_doc());
// Step 13.3.
try!(fragment.upcast::<Node>().AppendChild(&clone));
} else {
@@ -466,13 +490,11 @@ impl RangeMethods for Range {
if let Some(child) = last_contained_child {
// Step 16.
- if let Some(text) = child.downcast::<CharacterData>() {
+ if let Some(cdata) = child.downcast::<CharacterData>() {
assert!(child == end_node);
- // Step 16.1.
- let clone = end_node.CloneNode(true); // CharacterData has no children.
- // Step 16.2.
- let text = text.SubstringData(0, end_offset);
- clone.downcast::<CharacterData>().unwrap().SetData(text.unwrap());
+ // Steps 16.1-2.
+ let data = cdata.SubstringData(0, end_offset).unwrap();
+ let clone = cdata.clone_with_data(data, &start_node.owner_doc());
// Step 16.3.
try!(fragment.upcast::<Node>().AppendChild(&clone));
} else {
@@ -839,10 +861,12 @@ impl BoundaryPoint {
}
}
- fn set(&self, node: &Node, offset: u32) {
- debug_assert!(!node.is_doctype());
- debug_assert!(offset <= node.len());
+ pub fn set(&self, node: &Node, offset: u32) {
self.node.set(node);
+ self.set_offset(offset);
+ }
+
+ pub fn set_offset(&self, offset: u32) {
self.offset.set(offset);
}
}
@@ -900,3 +924,259 @@ fn bp_position(a_node: &Node, a_offset: u32,
Some(Ordering::Less)
}
}
+
+pub struct WeakRangeVec {
+ cell: UnsafeCell<WeakRefVec<Range>>,
+}
+
+#[allow(unsafe_code)]
+impl WeakRangeVec {
+ /// Create a new vector of weak references.
+ pub fn new() -> Self {
+ WeakRangeVec { cell: UnsafeCell::new(WeakRefVec::new()) }
+ }
+
+ /// Whether that vector of ranges is empty.
+ pub fn is_empty(&self) -> bool {
+ unsafe { (*self.cell.get()).is_empty() }
+ }
+
+ /// Used for steps 2.1-2. when inserting a node.
+ /// https://dom.spec.whatwg.org/#concept-node-insert
+ pub fn increase_above(&self, node: &Node, offset: u32, delta: u32) {
+ self.map_offset_above(node, offset, |offset| offset + delta);
+ }
+
+ /// Used for steps 4-5. when removing a node.
+ /// https://dom.spec.whatwg.org/#concept-node-remove
+ pub fn decrease_above(&self, node: &Node, offset: u32, delta: u32) {
+ self.map_offset_above(node, offset, |offset| offset - delta);
+ }
+
+ /// Used for steps 2-3. when removing a node.
+ /// https://dom.spec.whatwg.org/#concept-node-remove
+ pub fn drain_to_parent(&self, context: &UnbindContext, child: &Node) {
+ if self.is_empty() {
+ return;
+ }
+
+ let offset = context.index();
+ let parent = context.parent;
+ unsafe {
+ let mut ranges = &mut *self.cell.get();
+
+ ranges.update(|entry| {
+ let range = entry.root().unwrap();
+ if &range.start.node == parent || &range.end.node == parent {
+ entry.remove();
+ }
+ if &range.start.node == child {
+ range.start.set(context.parent, offset);
+ }
+ if &range.end.node == child {
+ range.end.set(context.parent, offset);
+ }
+ });
+
+ (*context.parent.ranges().cell.get()).extend(ranges.drain(..));
+ }
+ }
+
+ /// Used for steps 7.1-2. when normalizing a node.
+ /// https://dom.spec.whatwg.org/#dom-node-normalize
+ pub fn drain_to_preceding_text_sibling(&self, node: &Node, sibling: &Node, length: u32) {
+ if self.is_empty() {
+ return;
+ }
+
+ unsafe {
+ let mut ranges = &mut *self.cell.get();
+
+ ranges.update(|entry| {
+ let range = entry.root().unwrap();
+ if &range.start.node == sibling || &range.end.node == sibling {
+ entry.remove();
+ }
+ if &range.start.node == node {
+ range.start.set(sibling, range.StartOffset() + length);
+ }
+ if &range.end.node == node {
+ range.end.set(sibling, range.EndOffset() + length);
+ }
+ });
+
+ (*sibling.ranges().cell.get()).extend(ranges.drain(..));
+ }
+ }
+
+ /// Used for steps 7.3-4. when normalizing a node.
+ /// https://dom.spec.whatwg.org/#dom-node-normalize
+ pub fn move_to_text_child_at(&self,
+ node: &Node, offset: u32,
+ child: &Node, new_offset: u32) {
+ unsafe {
+ let child_ranges = &mut *child.ranges().cell.get();
+
+ (*self.cell.get()).update(|entry| {
+ let range = entry.root().unwrap();
+
+ let node_is_start = &range.start.node == node;
+ let node_is_end = &range.end.node == node;
+
+ let move_start = node_is_start && range.StartOffset() == offset;
+ let move_end = node_is_end && range.EndOffset() == offset;
+
+ let remove_from_node = move_start && move_end ||
+ move_start && !node_is_end ||
+ move_end && !node_is_start;
+
+ let already_in_child = &range.start.node == child || &range.end.node == child;
+ let push_to_child = !already_in_child && (move_start || move_end);
+
+ if remove_from_node {
+ let ref_ = entry.remove();
+ if push_to_child {
+ child_ranges.push(ref_);
+ }
+ } else if push_to_child {
+ child_ranges.push(WeakRef::new(&range));
+ }
+
+ if move_start {
+ range.start.set(child, new_offset);
+ }
+ if move_end {
+ range.end.set(child, new_offset);
+ }
+ });
+ }
+ }
+
+ /// Used for steps 8-11. when replacing character data.
+ /// https://dom.spec.whatwg.org/#concept-cd-replace
+ pub fn replace_code_units(&self,
+ node: &Node, offset: u32,
+ removed_code_units: u32, added_code_units: u32) {
+ self.map_offset_above(node, offset, |range_offset| {
+ if range_offset <= offset + removed_code_units {
+ offset
+ } else {
+ range_offset + added_code_units - removed_code_units
+ }
+ });
+ }
+
+ /// Used for steps 7.2-3. when splitting a text node.
+ /// https://dom.spec.whatwg.org/#concept-text-split
+ pub fn move_to_following_text_sibling_above(&self,
+ node: &Node, offset: u32,
+ sibling: &Node) {
+ unsafe {
+ let sibling_ranges = &mut *sibling.ranges().cell.get();
+
+ (*self.cell.get()).update(|entry| {
+ let range = entry.root().unwrap();
+ let start_offset = range.StartOffset();
+ let end_offset = range.EndOffset();
+
+ let node_is_start = &range.start.node == node;
+ let node_is_end = &range.end.node == node;
+
+ let move_start = node_is_start && start_offset > offset;
+ let move_end = node_is_end && end_offset > offset;
+
+ let remove_from_node = move_start && move_end ||
+ move_start && !node_is_end ||
+ move_end && !node_is_start;
+
+ let already_in_sibling =
+ &range.start.node == sibling || &range.end.node == sibling;
+ let push_to_sibling = !already_in_sibling && (move_start || move_end);
+
+ if remove_from_node {
+ let ref_ = entry.remove();
+ if push_to_sibling {
+ sibling_ranges.push(ref_);
+ }
+ } else if push_to_sibling {
+ sibling_ranges.push(WeakRef::new(&range));
+ }
+
+ if move_start {
+ range.start.set(sibling, start_offset - offset);
+ }
+ if move_end {
+ range.end.set(sibling, end_offset - offset);
+ }
+ });
+ }
+ }
+
+ /// Used for steps 7.4-5. when splitting a text node.
+ /// https://dom.spec.whatwg.org/#concept-text-split
+ pub fn increment_at(&self, node: &Node, offset: u32) {
+ unsafe {
+ (*self.cell.get()).update(|entry| {
+ let range = entry.root().unwrap();
+ if &range.start.node == node && offset == range.StartOffset() {
+ range.start.set_offset(offset + 1);
+ }
+ if &range.end.node == node && offset == range.EndOffset() {
+ range.end.set_offset(offset + 1);
+ }
+ });
+ }
+ }
+
+ /// Used for steps 9.1-2. when splitting a text node.
+ /// https://dom.spec.whatwg.org/#concept-text-split
+ pub fn clamp_above(&self, node: &Node, offset: u32) {
+ self.map_offset_above(node, offset, |_| offset);
+ }
+
+ fn map_offset_above<F: FnMut(u32) -> u32>(&self, node: &Node, offset: u32, mut f: F) {
+ unsafe {
+ (*self.cell.get()).update(|entry| {
+ let range = entry.root().unwrap();
+ let start_offset = range.StartOffset();
+ if &range.start.node == node && start_offset > offset {
+ range.start.set_offset(f(start_offset));
+ }
+ let end_offset = range.EndOffset();
+ if &range.end.node == node && end_offset > offset {
+ range.end.set_offset(f(end_offset));
+ }
+ });
+ }
+ }
+
+ fn push(&self, ref_: WeakRef<Range>) {
+ unsafe {
+ (*self.cell.get()).push(ref_);
+ }
+ }
+
+ fn remove(&self, range: &Range) -> WeakRef<Range> {
+ unsafe {
+ let ranges = &mut *self.cell.get();
+ let position = ranges.iter().position(|ref_| {
+ ref_ == range
+ }).unwrap();
+ ranges.swap_remove(position)
+ }
+ }
+}
+
+#[allow(unsafe_code)]
+impl HeapSizeOf for WeakRangeVec {
+ fn heap_size_of_children(&self) -> usize {
+ unsafe { (*self.cell.get()).heap_size_of_children() }
+ }
+}
+
+#[allow(unsafe_code)]
+impl JSTraceable for WeakRangeVec {
+ fn trace(&self, _: *mut JSTracer) {
+ unsafe { (*self.cell.get()).retain_alive() }
+ }
+}
diff --git a/components/script/dom/text.rs b/components/script/dom/text.rs
index ee92332752c..1c834f563cf 100644
--- a/components/script/dom/text.rs
+++ b/components/script/dom/text.rs
@@ -62,15 +62,18 @@ impl TextMethods for Text {
// Step 6.
let parent = node.GetParentNode();
if let Some(ref parent) = parent {
- // Step 7.
+ // Step 7.1.
parent.InsertBefore(new_node.upcast(), node.GetNextSibling().r()).unwrap();
- // TODO: Ranges.
+ // Steps 7.2-3.
+ node.ranges().move_to_following_text_sibling_above(node, offset, new_node.upcast());
+ // Steps 7.4-5.
+ parent.ranges().increment_at(&parent, node.index() + 1);
}
// Step 8.
cdata.DeleteData(offset, count).unwrap();
if parent.is_none() {
// Step 9.
- // TODO: Ranges
+ node.ranges().clamp_above(&node, offset);
}
// Step 10.
Ok(new_node)
diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs
index d2a436606d7..3540fbddf0f 100644
--- a/components/script/dom/virtualmethods.rs
+++ b/components/script/dom/virtualmethods.rs
@@ -42,7 +42,7 @@ use dom::htmltablesectionelement::HTMLTableSectionElement;
use dom::htmltemplateelement::HTMLTemplateElement;
use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::htmltitleelement::HTMLTitleElement;
-use dom::node::{ChildrenMutation, CloneChildrenFlag, Node};
+use dom::node::{ChildrenMutation, CloneChildrenFlag, Node, UnbindContext};
use string_cache::Atom;
use util::str::DOMString;
@@ -82,9 +82,9 @@ pub trait VirtualMethods {
/// Called when a Node is removed from a tree, where 'tree_in_doc'
/// indicates whether the tree is part of a Document.
- fn unbind_from_tree(&self, tree_in_doc: bool) {
+ fn unbind_from_tree(&self, context: &UnbindContext) {
if let Some(ref s) = self.super_type() {
- s.unbind_from_tree(tree_in_doc);
+ s.unbind_from_tree(context);
}
}
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 743b56be20f..ff07264362e 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -13,7 +13,6 @@
#![feature(custom_derive)]
#![feature(fnbox)]
#![feature(hashmap_hasher)]
-#![feature(iter_arith)]
#![feature(mpsc_select)]
#![feature(nonzero)]
#![feature(on_unimplemented)]