Auto merge of #6568 - dzbarsky:delete_range, r=jdm

Implement Range#insertNode

Gecko doesn't really follow the spec but it seems to throw a HierarchyRequest error when parent is null.
Any ideas who I should talk to about fixing the spec to account for the null checks?

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/6568)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2015-07-16 10:56:17 -06:00
commit acf47a02cf
5 changed files with 172 additions and 3319 deletions

View file

@ -1495,9 +1495,10 @@ impl Node {
// If node is an element, it is _affected by a base URL change_. // If node is an element, it is _affected by a base URL change_.
} }
// https://dom.spec.whatwg.org/#concept-node-pre-insert // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
fn pre_insert(node: &Node, parent: &Node, child: Option<&Node>) pub fn ensure_pre_insertion_validity(node: &Node,
-> Fallible<Root<Node>> { parent: &Node,
child: Option<&Node>) -> ErrorResult {
// Step 1. // Step 1.
match parent.type_id() { match parent.type_id() {
NodeTypeId::Document | NodeTypeId::Document |
@ -1538,78 +1539,83 @@ impl Node {
} }
// Step 6. // Step 6.
match parent.type_id() { if parent.type_id() == NodeTypeId::Document {
NodeTypeId::Document => { match node.type_id() {
match node.type_id() { // Step 6.1
// Step 6.1 NodeTypeId::DocumentFragment => {
NodeTypeId::DocumentFragment => { // Step 6.1.1(b)
// Step 6.1.1(b) if node.children()
if node.children() .any(|c| c.r().is_text())
.any(|c| c.r().is_text()) {
{ return Err(HierarchyRequest);
return Err(HierarchyRequest); }
} match node.child_elements().count() {
match node.child_elements().count() { 0 => (),
0 => (), // Step 6.1.2
// Step 6.1.2 1 => {
1 => { if !parent.child_elements().is_empty() {
if !parent.child_elements().is_empty() { return Err(HierarchyRequest);
return Err(HierarchyRequest);
}
if let Some(child) = child {
if child.inclusively_following_siblings()
.any(|child| child.r().is_doctype()) {
return Err(HierarchyRequest);
}
}
},
// Step 6.1.1(a)
_ => return Err(HierarchyRequest),
}
},
// Step 6.2
NodeTypeId::Element(_) => {
if !parent.child_elements().is_empty() {
return Err(HierarchyRequest);
}
if let Some(ref child) = child {
if child.inclusively_following_siblings()
.any(|child| child.r().is_doctype()) {
return Err(HierarchyRequest);
} }
} if let Some(child) = child {
}, if child.inclusively_following_siblings()
// Step 6.3 .any(|child| child.r().is_doctype()) {
NodeTypeId::DocumentType => { return Err(HierarchyRequest);
if parent.children()
.any(|c| c.r().is_doctype())
{
return Err(HierarchyRequest);
}
match child {
Some(child) => {
if parent.children()
.take_while(|c| c.r() != child)
.any(|c| c.r().is_element())
{
return Err(HierarchyRequest);
} }
}, }
None => { },
if !parent.child_elements().is_empty() { // Step 6.1.1(a)
return Err(HierarchyRequest); _ => return Err(HierarchyRequest),
} }
}, },
// Step 6.2
NodeTypeId::Element(_) => {
if !parent.child_elements().is_empty() {
return Err(HierarchyRequest);
}
if let Some(ref child) = child {
if child.inclusively_following_siblings()
.any(|child| child.r().is_doctype()) {
return Err(HierarchyRequest);
} }
}, }
NodeTypeId::CharacterData(_) => (), },
NodeTypeId::Document => unreachable!(), // Step 6.3
} NodeTypeId::DocumentType => {
}, if parent.children()
_ => (), .any(|c| c.r().is_doctype())
{
return Err(HierarchyRequest);
}
match child {
Some(child) => {
if parent.children()
.take_while(|c| c.r() != child)
.any(|c| c.r().is_element())
{
return Err(HierarchyRequest);
}
},
None => {
if !parent.child_elements().is_empty() {
return Err(HierarchyRequest);
}
},
}
},
NodeTypeId::CharacterData(_) => (),
NodeTypeId::Document => unreachable!(),
}
} }
Ok(())
}
// Step 7-8. // https://dom.spec.whatwg.org/#concept-node-pre-insert
pub fn pre_insert(node: &Node, parent: &Node, child: Option<&Node>)
-> Fallible<Root<Node>> {
// Step 1.
try!(Node::ensure_pre_insertion_validity(node, parent, child));
// Steps 2-3.
let reference_child_root; let reference_child_root;
let reference_child = match child { let reference_child = match child {
Some(child) if child == node => { Some(child) if child == node => {
@ -1619,14 +1625,14 @@ impl Node {
_ => child _ => child
}; };
// Step 9. // Step 4.
let document = document_from_node(parent); let document = document_from_node(parent);
Node::adopt(node, document.r()); Node::adopt(node, document.r());
// Step 10. // Step 5.
Node::insert(node, parent, reference_child, SuppressObserver::Unsuppressed); Node::insert(node, parent, reference_child, SuppressObserver::Unsuppressed);
// Step 11. // Step 6.
return Ok(Root::from_ref(node)) return Ok(Root::from_ref(node))
} }

View file

@ -4,17 +4,20 @@
use dom::bindings::codegen::Bindings::NodeBinding::NodeConstants; use dom::bindings::codegen::Bindings::NodeBinding::NodeConstants;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use dom::bindings::codegen::Bindings::RangeBinding::{self, RangeConstants}; use dom::bindings::codegen::Bindings::RangeBinding::{self, RangeConstants};
use dom::bindings::codegen::Bindings::RangeBinding::RangeMethods; use dom::bindings::codegen::Bindings::RangeBinding::RangeMethods;
use dom::bindings::codegen::Bindings::TextBinding::TextMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::InheritTypes::NodeCast; use dom::bindings::codegen::InheritTypes::{NodeCast, TextCast};
use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::error::Error::HierarchyRequest;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root}; use dom::bindings::js::{JS, Root, RootedReference};
use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::characterdata::CharacterDataTypeId;
use dom::document::{Document, DocumentHelpers}; use dom::document::{Document, DocumentHelpers};
use dom::node::{Node, NodeHelpers}; use dom::node::{Node, NodeHelpers, NodeTypeId};
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::rc::Rc; use std::rc::Rc;
@ -288,6 +291,90 @@ impl<'a> RangeMethods for &'a Range {
fn Detach(self) { fn Detach(self) {
// This method intentionally left blank. // This method intentionally left blank.
} }
// https://dom.spec.whatwg.org/#dom-range-insertnode
// https://dom.spec.whatwg.org/#concept-range-insert
fn InsertNode(self, node: &Node) -> ErrorResult {
let (start_node, start_offset) = {
let inner = self.inner().borrow();
let start = &inner.start;
(start.node(), start.offset())
};
// Step 1.
match start_node.type_id() {
// Handled under step 2.
NodeTypeId::CharacterData(CharacterDataTypeId::Text) => (),
NodeTypeId::CharacterData(_) => return Err(HierarchyRequest),
_ => ()
}
// Step 2.
let (reference_node, parent) =
if start_node.type_id() == NodeTypeId::CharacterData(CharacterDataTypeId::Text) {
// Step 3.
let parent = match start_node.GetParentNode() {
Some(parent) => parent,
// Step 1.
None => return Err(HierarchyRequest)
};
// Step 5.
(Some(Root::from_ref(start_node.r())), parent)
} else {
// Steps 4-5.
let child = start_node.ChildNodes().Item(start_offset);
(child, Root::from_ref(start_node.r()))
};
// Step 6.
try!(Node::ensure_pre_insertion_validity(node,
parent.r(),
reference_node.r()));
// Step 7.
let split_text;
let reference_node =
match TextCast::to_ref(start_node.r()) {
Some(text) => {
split_text = try!(text.SplitText(start_offset));
let new_reference = NodeCast::from_root(split_text);
assert!(new_reference.GetParentNode().r() == Some(parent.r()));
Some(new_reference)
},
_ => reference_node
};
// Step 8.
let reference_node = if Some(node) == reference_node.r() {
node.GetNextSibling()
} else {
reference_node
};
// Step 9.
node.remove_self();
// Step 10.
let new_offset =
reference_node.r().map_or(parent.len(), |node| node.index());
// Step 11
let new_offset = new_offset + if node.type_id() == NodeTypeId::DocumentFragment {
node.len()
} else {
1
};
// Step 12.
try!(Node::pre_insert(node, parent.r(), reference_node.r()));
// Step 13.
if self.Collapsed() {
self.inner().borrow_mut().set_end(parent.r(), new_offset);
}
Ok(())
}
} }
#[derive(JSTraceable)] #[derive(JSTraceable)]

View file

@ -48,8 +48,8 @@ interface Range {
// DocumentFragment extractContents(); // DocumentFragment extractContents();
// [NewObject, Throws] // [NewObject, Throws]
// DocumentFragment cloneContents(); // DocumentFragment cloneContents();
// [Throws] [Throws]
// void insertNode(Node node); void insertNode(Node node);
// [Throws] // [Throws]
// void surroundContents(Node newParent); // void surroundContents(Node newParent);

View file

@ -252,9 +252,6 @@
[Range interface: operation cloneContents()] [Range interface: operation cloneContents()]
expected: FAIL expected: FAIL
[Range interface: operation insertNode(Node)]
expected: FAIL
[Range interface: operation surroundContents(Node)] [Range interface: operation surroundContents(Node)]
expected: FAIL expected: FAIL
@ -270,12 +267,6 @@
[Range interface: document.createRange() must inherit property "cloneContents" with the proper type (22)] [Range interface: document.createRange() must inherit property "cloneContents" with the proper type (22)]
expected: FAIL expected: FAIL
[Range interface: document.createRange() must inherit property "insertNode" with the proper type (23)]
expected: FAIL
[Range interface: calling insertNode(Node) on document.createRange() with too few arguments must throw TypeError]
expected: FAIL
[Range interface: document.createRange() must inherit property "surroundContents" with the proper type (24)] [Range interface: document.createRange() must inherit property "surroundContents" with the proper type (24)]
expected: FAIL expected: FAIL
@ -291,12 +282,6 @@
[Range interface: detachedRange must inherit property "cloneContents" with the proper type (22)] [Range interface: detachedRange must inherit property "cloneContents" with the proper type (22)]
expected: FAIL expected: FAIL
[Range interface: detachedRange must inherit property "insertNode" with the proper type (23)]
expected: FAIL
[Range interface: calling insertNode(Node) on detachedRange with too few arguments must throw TypeError]
expected: FAIL
[Range interface: detachedRange must inherit property "surroundContents" with the proper type (24)] [Range interface: detachedRange must inherit property "surroundContents" with the proper type (24)]
expected: FAIL expected: FAIL

File diff suppressed because it is too large Load diff