diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index a002e893823..34deb138af2 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -82,6 +82,7 @@ DOMInterfaces = { 'needsAbstract': [ 'appendChild', 'childNodes', + 'cloneNode', 'compareDocumentPosition', 'contains', 'insertBefore', diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index 27e209731e3..c39c82449c0 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -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; } diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 13d941308a7..000a19be847 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -4,7 +4,9 @@ //! 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; @@ -12,7 +14,9 @@ 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}; @@ -20,6 +24,7 @@ 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}; @@ -722,6 +727,12 @@ fn gather_abstract_nodes(cur: &JS, refs: &mut ~[JS], postorder: bool } } +/// Specifies whether children must be recursively cloned or not. +enum CloneChildrenFlag { + CloneChildren, + DoNotCloneChildren +} + impl Node { pub fn ancestors(&self) -> AncestorIterator { AncestorIterator { @@ -1271,6 +1282,124 @@ impl Node { } } + // http://dom.spec.whatwg.org/#concept-node-clone + fn clone(node: &JS, maybe_doc: Option<&JS>, clone_children: CloneChildrenFlag) + -> JS { + fn clone_recursively(node: &JS, copy: &mut JS, doc: &JS) { + 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 = match node.type_id() { + DoctypeNodeTypeId => { + let doctype: JS = 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 = CommentCast::to(node); + let comment = comment.get(); + let comment = Comment::new(comment.characterdata.data.clone(), &document); + NodeCast::from(&comment) + }, + DocumentNodeTypeId => { + let document: JS = 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 = 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 = TextCast::to(node); + let text = text.get(); + let text = Text::new(text.characterdata.data.clone(), &document); + NodeCast::from(&text) + }, + ProcessingInstructionNodeTypeId => { + let pi: JS = 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(©); + } + 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 = DocumentCast::to(node); + let node_doc = node_doc.get(); + let mut copy_doc: JS = DocumentCast::to(©); + 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 = ElementCast::to(node); + let node_elem = node_elem.get(); + let mut copy_elem: JS = ElementCast::to(©); + 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: &mut JS, child: Option>) -> Fallible> { @@ -1428,9 +1557,11 @@ impl Node { } // http://dom.spec.whatwg.org/#dom-node-clonenode - pub fn CloneNode(&self, _deep: bool) -> Fallible> { - // FIXME: stub - https://github.com/mozilla/servo/issues/1240 - fail!("stub") + pub fn CloneNode(&self, abstract_self: &mut JS, deep: bool) -> JS { + match deep { + true => Node::clone(abstract_self, None, CloneChildren), + false => Node::clone(abstract_self, None, DoNotCloneChildren) + } } // http://dom.spec.whatwg.org/#dom-node-isequalnode diff --git a/src/components/script/dom/webidls/Node.webidl b/src/components/script/dom/webidls/Node.webidl index 0670e6eb3f6..2a13d6ab381 100644 --- a/src/components/script/dom/webidls/Node.webidl +++ b/src/components/script/dom/webidls/Node.webidl @@ -52,7 +52,6 @@ interface Node : EventTarget { attribute DOMString? textContent; void normalize(); - [Throws] Node cloneNode(optional boolean deep = true); boolean isEqualNode(Node? node); diff --git a/src/test/content/harness.js b/src/test/content/harness.js index 72aa2712469..76540b2a1a5 100644 --- a/src/test/content/harness.js +++ b/src/test/content/harness.js @@ -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; }); diff --git a/src/test/content/test_collections.html b/src/test/content/test_collections.html index 323ee450938..6878f6b6c73 100644 --- a/src/test/content/test_collections.html +++ b/src/test/content/test_collections.html @@ -16,7 +16,7 @@ function check_collection(obj, num, classes, name) { classes = [Element, HTMLElement].concat(classes); for (var i=0; i // 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"); } @@ -14,7 +14,7 @@ // 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"); } @@ -22,7 +22,7 @@ // 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"); } @@ -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"); @@ -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"); @@ -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"); diff --git a/src/test/content/test_document_createProcessingInstruction.html b/src/test/content/test_document_createProcessingInstruction.html index c313f34a4de..e6d8384e144 100644 --- a/src/test/content/test_document_createProcessingInstruction.html +++ b/src/test/content/test_document_createProcessingInstruction.html @@ -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"); diff --git a/src/test/content/test_document_doctype.html b/src/test/content/test_document_doctype.html index 0f16cccbff7..bf9f1999243 100644 --- a/src/test/content/test_document_doctype.html +++ b/src/test/content/test_document_doctype.html @@ -6,8 +6,8 @@ + + + diff --git a/src/test/content/test_node_replaceChild.html b/src/test/content/test_node_replaceChild.html index a9a4703cc9c..2deec080198 100644 --- a/src/test/content/test_node_replaceChild.html +++ b/src/test/content/test_node_replaceChild.html @@ -31,7 +31,7 @@ var doc_doctype = document.doctype; var new_doctype = document.implementation.createDocumentType("html", null, null); - isnot(doc_doctype, new_doctype, "test2-0, doctype"); + is_not(doc_doctype, new_doctype, "test2-0, doctype"); is(document.replaceChild(new_doctype, doc_doctype), doc_doctype, "test2-1, doctype"); is(document.doctype, new_doctype, "test2-2, doctype"); } @@ -41,7 +41,7 @@ var doc_elem = document.documentElement; var new_elem = document.createElement("html"); - isnot(doc_elem, new_elem, "test3-0, documentElement"); + is_not(doc_elem, new_elem, "test3-0, documentElement"); is(document.replaceChild(new_elem, doc_elem), doc_elem, "test3-1, documentElement"); is(document.documentElement, new_elem, "test3-2, documentElement"); }