Skip to content

Commit

Permalink
servo: Merge #1852 - Implement Node.cloneNode (from brunoabinader:nod…
Browse files Browse the repository at this point in the history
…e-clonenode); r=Ms2ger

Spec:
http://dom.spec.whatwg.org/#dom-node-clonenode

This is a sub-task for #1655. Closes #1240.

Source-Repo: https://github.com/servo/servo
Source-Revision: 71f4fd0478183692ba114351841b44c58691e665
  • Loading branch information
brunoabinader committed Mar 13, 2014
1 parent 318a35f commit fefe0b3
Show file tree
Hide file tree
Showing 15 changed files with 354 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ DOMInterfaces = {
'needsAbstract': [
'appendChild',
'childNodes',
'cloneNode',
'compareDocumentPosition',
'contains',
'insertBefore',
Expand Down
4 changes: 4 additions & 0 deletions servo/src/components/script/dom/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ impl Document {
}
}

pub fn quirks_mode(&self) -> QuirksMode {
self.extra.quirks_mode
}

pub fn set_quirks_mode(&mut self, mode: QuirksMode) {
self.extra.quirks_mode = mode;
}
Expand Down
141 changes: 136 additions & 5 deletions servo/src/components/script/dom/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@

//! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, ElementCast, TextCast, NodeCast};
use dom::attr::Attr;
use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived};
use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast;
use dom::bindings::js::JS;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::error::{ErrorResult, Fallible, NotFound, HierarchyRequest};
use dom::bindings::utils;
use dom::characterdata::CharacterData;
use dom::document::Document;
use dom::comment::Comment;
use dom::document::{Document, HTMLDocument, NonHTMLDocument};
use dom::documentfragment::DocumentFragment;
use dom::documenttype::DocumentType;
use dom::element::{Element, ElementTypeId, HTMLAnchorElementTypeId, IElement};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::nodelist::{NodeList};
use dom::text::Text;
use dom::processinginstruction::ProcessingInstruction;
use dom::window::Window;
use html::hubbub_html_parser::build_element_from_tag;
use layout_interface::{LayoutChan, ReapLayoutDataMsg, UntrustedNodeAddress};
use layout_interface::TrustedNodeAddress;
use servo_util::str::{DOMString, null_str_as_empty};
Expand Down Expand Up @@ -722,6 +727,12 @@ fn gather_abstract_nodes(cur: &JS<Node>, refs: &mut ~[JS<Node>], postorder: bool
}
}

/// Specifies whether children must be recursively cloned or not.
enum CloneChildrenFlag {
CloneChildren,
DoNotCloneChildren
}

impl Node {
pub fn ancestors(&self) -> AncestorIterator {
AncestorIterator {
Expand Down Expand Up @@ -1271,6 +1282,124 @@ impl Node {
}
}

// http://dom.spec.whatwg.org/#concept-node-clone
fn clone(node: &JS<Node>, maybe_doc: Option<&JS<Document>>, clone_children: CloneChildrenFlag)
-> JS<Node> {
fn clone_recursively(node: &JS<Node>, copy: &mut JS<Node>, doc: &JS<Document>) {
for ref child in node.get().children() {
let mut cloned = Node::clone(child, Some(doc), DoNotCloneChildren);
match Node::pre_insert(&mut cloned, copy, None) {
Ok(ref mut appended) => clone_recursively(child, appended, doc),
Err(..) => fail!("an error occurred while appending children")
}
}
}

// Step 1.
let mut document = match maybe_doc {
Some(doc) => doc.clone(),
None => node.get().owner_doc().clone()
};

// Step 2.
// XXXabinader: clone() for each node as trait?
let mut copy: JS<Node> = match node.type_id() {
DoctypeNodeTypeId => {
let doctype: JS<DocumentType> = DocumentTypeCast::to(node);
let doctype = doctype.get();
let doctype = DocumentType::new(doctype.name.clone(),
Some(doctype.public_id.clone()),
Some(doctype.system_id.clone()), &document);
NodeCast::from(&doctype)
},
DocumentFragmentNodeTypeId => {
let doc_fragment = DocumentFragment::new(&document);
NodeCast::from(&doc_fragment)
},
CommentNodeTypeId => {
let comment: JS<Comment> = CommentCast::to(node);
let comment = comment.get();
let comment = Comment::new(comment.characterdata.data.clone(), &document);
NodeCast::from(&comment)
},
DocumentNodeTypeId => {
let document: JS<Document> = DocumentCast::to(node);
let document = document.get();
let is_html_doc = match document.is_html_document {
true => HTMLDocument,
false => NonHTMLDocument
};
let document = Document::new(&document.window, Some(document.url().clone()),
is_html_doc, None);
NodeCast::from(&document)
},
ElementNodeTypeId(..) => {
let element: JS<Element> = ElementCast::to(node);
let element = element.get();
let element = build_element_from_tag(element.tag_name.clone(), &document);
NodeCast::from(&element)
},
TextNodeTypeId => {
let text: JS<Text> = TextCast::to(node);
let text = text.get();
let text = Text::new(text.characterdata.data.clone(), &document);
NodeCast::from(&text)
},
ProcessingInstructionNodeTypeId => {
let pi: JS<ProcessingInstruction> = ProcessingInstructionCast::to(node);
let pi = pi.get();
let pi = ProcessingInstruction::new(pi.target.clone(),
pi.characterdata.data.clone(), &document);
NodeCast::from(&pi)
},
};

// Step 3.
if copy.is_document() {
document = DocumentCast::to(&copy);
}
assert_eq!(copy.get().owner_doc(), &document);

// Step 4 (some data already copied in step 2).
match node.type_id() {
DocumentNodeTypeId => {
let node_doc: JS<Document> = DocumentCast::to(node);
let node_doc = node_doc.get();
let mut copy_doc: JS<Document> = DocumentCast::to(&copy);
let copy_doc = copy_doc.get_mut();
copy_doc.set_encoding_name(node_doc.encoding_name.clone());
copy_doc.set_quirks_mode(node_doc.quirks_mode());
},
ElementNodeTypeId(..) => {
let node_elem: JS<Element> = ElementCast::to(node);
let node_elem = node_elem.get();
let mut copy_elem: JS<Element> = ElementCast::to(&copy);
let copy_elem = copy_elem.get_mut();
// FIXME: https://github.com/mozilla/servo/issues/1737
copy_elem.namespace = node_elem.namespace.clone();
for attr in node_elem.attrs.iter() {
let attr = attr.get();
copy_elem.attrs.push(Attr::new_ns(&document.get().window,
attr.local_name.clone(), attr.value.clone(),
attr.name.clone(), attr.namespace.clone(),
attr.prefix.clone()));
}
},
_ => ()
}

// Step 5: cloning steps.

// Step 6.
match clone_children {
CloneChildren => clone_recursively(node, &mut copy, &document),
DoNotCloneChildren => ()
}

// Step 7.
copy
}

// http://dom.spec.whatwg.org/#dom-node-insertbefore
pub fn InsertBefore(&self, abstract_self: &mut JS<Node>, node: &mut JS<Node>, child: Option<JS<Node>>)
-> Fallible<JS<Node>> {
Expand Down Expand Up @@ -1428,9 +1557,11 @@ impl Node {
}

// http://dom.spec.whatwg.org/#dom-node-clonenode
pub fn CloneNode(&self, _deep: bool) -> Fallible<JS<Node>> {
// FIXME: stub - https://github.com/mozilla/servo/issues/1240
fail!("stub")
pub fn CloneNode(&self, abstract_self: &mut JS<Node>, deep: bool) -> JS<Node> {
match deep {
true => Node::clone(abstract_self, None, CloneChildren),
false => Node::clone(abstract_self, None, DoNotCloneChildren)
}
}

// http://dom.spec.whatwg.org/#dom-node-isequalnode
Expand Down
1 change: 0 additions & 1 deletion servo/src/components/script/dom/webidls/Node.webidl
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ interface Node : EventTarget {
attribute DOMString? textContent;
void normalize();

[Throws]
Node cloneNode(optional boolean deep = true);
boolean isEqualNode(Node? node);

Expand Down
2 changes: 1 addition & 1 deletion servo/src/test/content/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ function _printer(opstr, op) {
}

var is = _printer("==", function (a,b) { return a == b; });
var is_not = _printer("!=", function (a,b) { return a != b; });
var is_a = _printer("is a", function (a,b) { return a instanceof b; });
var is_not_a = _printer("is not a", function (a,b) { return !(a instanceof b); });
var is_in = _printer("is in", function (a,b) { return a in b; });
var is_not_in = _printer("is not in", function (a,b) { return !(a in b); });
var as_str_is = _printer("as string is", function (a,b) { return String(a) == b; });
var isnot = _printer("!=", function (a,b) { return a != b; });
var lt = _printer("<", function (a,b) { return a < b; });
var gt = _printer(">", function (a,b) { return a > b; });
var leq = _printer("<=", function (a,b) { return a <= b; });
Expand Down
2 changes: 1 addition & 1 deletion servo/src/test/content/test_collections.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
classes = [Element, HTMLElement].concat(classes);

for (var i=0; i<obj.length; i++) {
isnot(obj[i], null);
is_not(obj[i], null);
is(obj[i].tagName, name);
for (var j=0; j<classes.length; j++) {
is_a(obj[i], classes[j]);
Expand Down
12 changes: 6 additions & 6 deletions servo/src/test/content/test_document_body.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
<script>
// test1: existing document's body
{
isnot(document.body, null, "test1-0, existing document's body");
is_not(document.body, null, "test1-0, existing document's body");
is_a(document.body, HTMLBodyElement, "test1-1, exising document's body");
is(document.body && document.body.tagName, "BODY", "test1-2, existing document's body");
}

// test2: replace document's body with new body
{
let new_body = document.createElement("body");
isnot(new_body, null, "test2-0, replace document's body with new body");
is_not(new_body, null, "test2-0, replace document's body with new body");
document.body = new_body;
is(new_body, document.body, "test2-1, replace document's body with new body");
}

// test3: replace document's body with new frameset
{
let new_frameset = document.createElement("frameset");
isnot(new_frameset, null, "test2-0, replace document's body with new frameset");
is_not(new_frameset, null, "test2-0, replace document's body with new frameset");
document.body = new_frameset;
is(new_frameset, document.body, "test2-1, replace document's body with new frameset");
}
Expand All @@ -33,7 +33,7 @@
new_document.appendChild(new_document.createElement("html"));
let new_div = new_document.createElement("div");

isnot(new_div, null, "test4-0, append an invalid element to a new document");
is_not(new_div, null, "test4-0, append an invalid element to a new document");

new_document.body = new_div;
is(new_document.body, null, "test4-1, append an invalid element to a new document");
Expand All @@ -45,7 +45,7 @@
new_document.appendChild(new_document.createElement("html"));
let new_body = new_document.createElement("body");

isnot(new_body, null, "test5-0, append body to a new document");
is_not(new_body, null, "test5-0, append body to a new document");
is_a(new_body, HTMLBodyElement, "test5-1, append body to a new document");
is(new_body && new_body.tagName, "BODY", "test5-2, append body to a new document");

Expand All @@ -59,7 +59,7 @@
new_document.appendChild(new_document.createElement("html"));
let new_frameset = new_document.createElement("frameset");

isnot(new_frameset, null, "test6-0, append frameset to a new document");
is_not(new_frameset, null, "test6-0, append frameset to a new document");
is_a(new_frameset, HTMLFrameSetElement, "test6-1, append frameset to a new document");
is(new_frameset && new_frameset.tagName, "FRAMESET", "test6-2, append frameset to a new document");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// test1: createProcessingInstruction
{
var pi = document.createProcessingInstruction("xml-stylesheet", "href=\"mycss.css\" type=\"text/css\"");
isnot(pi, null, "test1-0: createProcessingInstruction");
is_not(pi, null, "test1-0: createProcessingInstruction");
is_a(pi, ProcessingInstruction, "test1-1: createProcessingInstruction");
is(pi.target, "xml-stylesheet", "test1-2: createProcessingInstruction");
is(pi.data, "href=\"mycss.css\" type=\"text/css\"", "test1-3: createProcessingInstruction");
Expand Down
4 changes: 2 additions & 2 deletions servo/src/test/content/test_document_doctype.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<script>
// test1: document with doctype
{
isnot(document.doctype, document.firstChild, "test1-0, document with doctype");
isnot(document.doctype, null, "test1-1, document with doctype");
is_not(document.doctype, document.firstChild, "test1-0, document with doctype");
is_not(document.doctype, null, "test1-1, document with doctype");
is_a(document.doctype, DocumentType, "test1-2, document with doctype");
}

Expand Down
6 changes: 3 additions & 3 deletions servo/src/test/content/test_document_getElementById.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
// test1: on static page
{
let foo = document.getElementById("foo");
isnot(foo, null, "test-1-0, on static page");
is_not(foo, null, "test-1-0, on static page");
is(foo && foo.tagName, "HEAD", "test1-1, on static page");
is_a(foo, HTMLHeadElement, "test1-2, on static page");

let bar = document.getElementById("bar");
isnot(bar, null, "test1-3, on static page");
is_not(bar, null, "test1-3, on static page");
is(bar && bar.tagName, "DIV", "test1-4, on static page");
is_a(bar, HTMLDivElement, "test1-5, on static page");
}
Expand All @@ -30,7 +30,7 @@

// test: appended element
let appended = document.getElementById(TEST_ID);
isnot(appended, null, "test2-0, appended element");
is_not(appended, null, "test2-0, appended element");
is(appended && appended.tagName, "DIV", "test2-1, appended element");
is_a(appended, HTMLDivElement, "test2-2, appended element");

Expand Down
4 changes: 2 additions & 2 deletions servo/src/test/content/test_document_head.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<script>
// test1: existing document's head
{
isnot(document.head, null, "test1-0, existing document's head");
is_not(document.head, null, "test1-0, existing document's head");
is_a(document.head, HTMLHeadElement, "test1-1, exising document's head");
is(document.head && document.head.tagName, "HEAD", "test1-2, existing document's head");
}
Expand All @@ -17,7 +17,7 @@
new_document.appendChild(new_document.createElement("html"));
let new_head = new_document.createElement("head");

isnot(new_head, null, "test2-0, append head to a new document");
is_not(new_head, null, "test2-0, append head to a new document");
is_a(new_head, HTMLHeadElement, "test2-1, append head to a new document");
is(new_head && new_head.tagName, "HEAD", "test2-2, append head to a new document");

Expand Down
4 changes: 2 additions & 2 deletions servo/src/test/content/test_document_implementation.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<script>
// test1: basic test
{
isnot(document.implementation, null, "test1-0, basic test");
is_not(document.implementation, null, "test1-0, basic test");
is_a(document.implementation, DOMImplementation, "test1-1, basic test");

var implementation = document.implementation;
Expand All @@ -25,7 +25,7 @@
// test3: createHTMLDocument
{
var htmldoc = document.implementation.createHTMLDocument("example title");
isnot(htmldoc, null, "test3-0, createHTMLDocument");
is_not(htmldoc, null, "test3-0, createHTMLDocument");
is_a(htmldoc, Document, "test3-1, createHTMLDocument");
is(htmldoc.childNodes.length, 2, "test3-3, createHTMLDocument");

Expand Down
4 changes: 2 additions & 2 deletions servo/src/test/content/test_document_url.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<script>
// test1: URL & documentURI
{
isnot(document.URL, null, "test1-0, URL & documentURI");
isnot(document.documentURI, null, "test1-1, URL & documentURI");
is_not(document.URL, null, "test1-0, URL & documentURI");
is_not(document.documentURI, null, "test1-1, URL & documentURI");
is(document.URL, document.documentURI, "test1-2, URL & documentURI");
}

Expand Down
Loading

0 comments on commit fefe0b3

Please sign in to comment.