<!DOCTYPE html>
<meta charset=utf-8>
<title>Node.cloneNode</title>
<link rel=help href="https://dom.spec.whatwg.org/#dom-node-clonenode">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>
<script>
function assert_equal_node(nodeA, nodeB) {
  assert_equals(nodeB.nodeType, nodeA.nodeType, "nodeType");
  assert_equals(nodeB.nodeName, nodeA.nodeName, "nodeName");

  if (nodeA.nodeType === Node.ELEMENT_NODE) {
    assert_equals(nodeB.prefix, nodeA.prefix, "prefix");
    assert_equals(nodeB.namespaceURI, nodeA.namespaceURI, "namespaceURI");
    assert_equals(nodeB.localName, nodeA.localName, "localName");
    assert_equals(nodeB.tagName, nodeA.tagName, "tagName");
    assert_not_equals(nodeB.attributes != nodeA.attributes, "attributes");
    assert_equals(nodeB.attributes.length, nodeA.attributes.length,
                  "attributes.length");
    for (var i = 0, il = nodeA.attributes.length; i < il; ++i) {
      assert_not_equals(nodeB.attributes[i], nodeA.attributes[i],
                        "attributes[" + i + "]");
      assert_equals(nodeB.attributes[i].name, nodeA.attributes[i].name,
                    "attributes[" + i + "].name");
      assert_equals(nodeB.attributes[i].prefix, nodeA.attributes[i].prefix,
                    "attributes[" + i + "].prefix");
      assert_equals(nodeB.attributes[i].namespaceURI, nodeA.attributes[i].namespaceURI,
                    "attributes[" + i + "].namespaceURI");
      assert_equals(nodeB.attributes[i].value, nodeA.attributes[i].value,
                    "attributes[" + i + "].value");
    }
  }
}

function check_copy(orig, copy, type) {
    assert_not_equals(orig, copy, "Object equality");
    assert_equal_node(orig, copy, "Node equality");
    assert_true(orig instanceof type, "original instanceof " + type);
    assert_true(copy instanceof type, "copy instanceof " + type);
}

function create_element_and_check(localName, typeName) {
  test(function() {
    assert_true(typeName in window, typeName + " is not supported");
    var element = document.createElement(localName);
    var copy = element.cloneNode();
    check_copy(element, copy, window[typeName]);
  }, "createElement(" + localName + ")");
}

// test1: createElement
create_element_and_check("a",         "HTMLAnchorElement");
create_element_and_check("abbr",      "HTMLElement");
create_element_and_check("acronym",   "HTMLElement");
create_element_and_check("address",   "HTMLElement");
create_element_and_check("area",      "HTMLAreaElement");
create_element_and_check("article",   "HTMLElement");
create_element_and_check("aside",     "HTMLElement");
create_element_and_check("audio",     "HTMLAudioElement");
create_element_and_check("b",         "HTMLElement");
create_element_and_check("base",      "HTMLBaseElement");
create_element_and_check("bdi",       "HTMLElement");
create_element_and_check("bdo",       "HTMLElement");
create_element_and_check("bgsound",   "HTMLElement");
create_element_and_check("big",       "HTMLElement");
create_element_and_check("blockquote","HTMLElement");
create_element_and_check("body",      "HTMLBodyElement");
create_element_and_check("br",        "HTMLBRElement");
create_element_and_check("button",    "HTMLButtonElement");
create_element_and_check("canvas",    "HTMLCanvasElement");
create_element_and_check("caption",   "HTMLTableCaptionElement");
create_element_and_check("center",    "HTMLElement");
create_element_and_check("cite",      "HTMLElement");
create_element_and_check("code",      "HTMLElement");
create_element_and_check("col",       "HTMLTableColElement");
create_element_and_check("colgroup",  "HTMLTableColElement");
create_element_and_check("data",      "HTMLDataElement");
create_element_and_check("datalist",  "HTMLDataListElement");
create_element_and_check("dialog",    "HTMLDialogElement");
create_element_and_check("dd",        "HTMLElement");
create_element_and_check("del",       "HTMLModElement");
create_element_and_check("details",   "HTMLElement");
create_element_and_check("dfn",       "HTMLElement");
create_element_and_check("dir",       "HTMLDirectoryElement");
create_element_and_check("div",       "HTMLDivElement");
create_element_and_check("dl",        "HTMLDListElement");
create_element_and_check("dt",        "HTMLElement");
create_element_and_check("embed",     "HTMLEmbedElement");
create_element_and_check("fieldset",  "HTMLFieldSetElement");
create_element_and_check("figcaption","HTMLElement");
create_element_and_check("figure",    "HTMLElement");
create_element_and_check("font",      "HTMLFontElement");
create_element_and_check("footer",    "HTMLElement");
create_element_and_check("form",      "HTMLFormElement");
create_element_and_check("frame",     "HTMLFrameElement");
create_element_and_check("frameset",  "HTMLFrameSetElement");
create_element_and_check("h1",        "HTMLHeadingElement");
create_element_and_check("h2",        "HTMLHeadingElement");
create_element_and_check("h3",        "HTMLHeadingElement");
create_element_and_check("h4",        "HTMLHeadingElement");
create_element_and_check("h5",        "HTMLHeadingElement");
create_element_and_check("h6",        "HTMLHeadingElement");
create_element_and_check("head",      "HTMLHeadElement");
create_element_and_check("header",    "HTMLElement");
create_element_and_check("hgroup",    "HTMLElement");
create_element_and_check("hr",        "HTMLHRElement");
create_element_and_check("html",      "HTMLHtmlElement");
create_element_and_check("i",         "HTMLElement");
create_element_and_check("iframe",    "HTMLIFrameElement");
create_element_and_check("img",       "HTMLImageElement");
create_element_and_check("input",     "HTMLInputElement");
create_element_and_check("ins",       "HTMLModElement");
create_element_and_check("isindex",   "HTMLElement");
create_element_and_check("kbd",       "HTMLElement");
create_element_and_check("label",     "HTMLLabelElement");
create_element_and_check("legend",    "HTMLLegendElement");
create_element_and_check("li",        "HTMLLIElement");
create_element_and_check("link",      "HTMLLinkElement");
create_element_and_check("main",      "HTMLElement");
create_element_and_check("map",       "HTMLMapElement");
create_element_and_check("mark",      "HTMLElement");
create_element_and_check("marquee",   "HTMLElement");
create_element_and_check("meta",      "HTMLMetaElement");
create_element_and_check("meter",     "HTMLMeterElement");
create_element_and_check("nav",       "HTMLElement");
create_element_and_check("nobr",      "HTMLElement");
create_element_and_check("noframes",  "HTMLElement");
create_element_and_check("noscript",  "HTMLElement");
create_element_and_check("object",    "HTMLObjectElement");
create_element_and_check("ol",        "HTMLOListElement");
create_element_and_check("optgroup",  "HTMLOptGroupElement");
create_element_and_check("option",    "HTMLOptionElement");
create_element_and_check("output",    "HTMLOutputElement");
create_element_and_check("p",         "HTMLParagraphElement");
create_element_and_check("param",     "HTMLParamElement");
create_element_and_check("pre",       "HTMLPreElement");
create_element_and_check("progress",  "HTMLProgressElement");
create_element_and_check("q",         "HTMLQuoteElement");
create_element_and_check("rp",        "HTMLElement");
create_element_and_check("rt",        "HTMLElement");
create_element_and_check("ruby",      "HTMLElement");
create_element_and_check("s",         "HTMLElement");
create_element_and_check("samp",      "HTMLElement");
create_element_and_check("script",    "HTMLScriptElement");
create_element_and_check("section",   "HTMLElement");
create_element_and_check("select",    "HTMLSelectElement");
create_element_and_check("small",     "HTMLElement");
create_element_and_check("source",    "HTMLSourceElement");
create_element_and_check("spacer",    "HTMLElement");
create_element_and_check("span",      "HTMLSpanElement");
create_element_and_check("strike",    "HTMLElement");
create_element_and_check("style",     "HTMLStyleElement");
create_element_and_check("sub",       "HTMLElement");
create_element_and_check("summary",   "HTMLElement");
create_element_and_check("sup",       "HTMLElement");
create_element_and_check("table",     "HTMLTableElement");
create_element_and_check("tbody",     "HTMLTableSectionElement");
create_element_and_check("td",        "HTMLTableCellElement");
create_element_and_check("template",  "HTMLTemplateElement");
create_element_and_check("textarea",  "HTMLTextAreaElement");
create_element_and_check("th",        "HTMLTableCellElement");
create_element_and_check("time",      "HTMLTimeElement");
create_element_and_check("title",     "HTMLTitleElement");
create_element_and_check("tr",        "HTMLTableRowElement");
create_element_and_check("tt",        "HTMLElement");
create_element_and_check("track",     "HTMLTrackElement");
create_element_and_check("u",         "HTMLElement");
create_element_and_check("ul",        "HTMLUListElement");
create_element_and_check("var",       "HTMLElement");
create_element_and_check("video",     "HTMLVideoElement");
create_element_and_check("unknown",   "HTMLUnknownElement");
create_element_and_check("wbr",       "HTMLElement");

test(function() {
    var fragment = document.createDocumentFragment();
    var copy = fragment.cloneNode();
    check_copy(fragment, copy, DocumentFragment);
}, "createDocumentFragment");

test(function() {
    var text = document.createTextNode("hello world");
    var copy = text.cloneNode();
    check_copy(text, copy, Text);
    assert_equals(text.data, copy.data);
    assert_equals(text.wholeText, copy.wholeText);
}, "createTextNode");

test(function() {
    var comment = document.createComment("a comment");
    var copy = comment.cloneNode();
    check_copy(comment, copy, Comment);
    assert_equals(comment.data, copy.data);
}, "createComment");

test(function() {
  var el = document.createElement("foo");
  el.setAttribute("a", "b");
  el.setAttribute("c", "d");
  var c = el.cloneNode();
  check_copy(el, c, Element);
}, "createElement with attributes")

test(function() {
  var el = document.createElementNS("http://www.w3.org/1999/xhtml", "foo:div");
  var c = el.cloneNode();
  check_copy(el, c, HTMLDivElement);
}, "createElementNS HTML")

test(function() {
  var el = document.createElementNS("http://www.example.com/", "foo:div");
  var c = el.cloneNode();
  check_copy(el, c, Element);
}, "createElementNS non-HTML")

test(function() {
    var pi = document.createProcessingInstruction("target", "data");
    var copy = pi.cloneNode();
    check_copy(pi, copy, ProcessingInstruction);
    assert_equals(pi.data, copy.data, "data");
    assert_equals(pi.target, pi.target, "target");
}, "createProcessingInstruction");

test(function() {
    var attr = document.createAttribute("class");
    var copy = attr.cloneNode();
    check_copy(attr, copy, Attr);
    assert_equals(attr.namespaceURI, copy.namespaceURI);
    assert_equals(attr.prefix, copy.prefix);
    assert_equals(attr.localName, copy.localName);
    assert_equals(attr.value, copy.value);

    attr.value = "abc";
    assert_equals(attr.namespaceURI, copy.namespaceURI);
    assert_equals(attr.prefix, copy.prefix);
    assert_equals(attr.localName, copy.localName);
    assert_not_equals(attr.value, copy.value);

    var copy2 = attr.cloneNode();
    check_copy(attr, copy2, Attr);
    assert_equals(attr.namespaceURI, copy.namespaceURI);
    assert_equals(attr.prefix, copy.prefix);
    assert_equals(attr.localName, copy2.localName);
    assert_equals(attr.value, copy2.value);
}, "createAttribute");

test(function() {
    var attr = document.createAttributeNS("http://www.w3.org/1999/xhtml", "foo:class");
    var copy = attr.cloneNode();
    check_copy(attr, copy, Attr);
    assert_equals(attr.namespaceURI, copy.namespaceURI);
    assert_equals(attr.prefix, copy.prefix);
    assert_equals(attr.localName, copy.localName);
    assert_equals(attr.value, copy.value);

    attr.value = "abc";
    assert_equals(attr.namespaceURI, copy.namespaceURI);
    assert_equals(attr.prefix, copy.prefix);
    assert_equals(attr.localName, copy.localName);
    assert_not_equals(attr.value, copy.value);

    var copy2 = attr.cloneNode();
    check_copy(attr, copy2, Attr);
    assert_equals(attr.namespaceURI, copy.namespaceURI);
    assert_equals(attr.prefix, copy.prefix);
    assert_equals(attr.localName, copy2.localName);
    assert_equals(attr.value, copy2.value);
}, "createAttributeNS");

test(function() {
    var doctype = document.implementation.createDocumentType("html", "public", "system");
    var copy = doctype.cloneNode();
    check_copy(doctype, copy, DocumentType);
    assert_equals(doctype.name, copy.name, "name");
    assert_equals(doctype.publicId, copy.publicId, "publicId");
    assert_equals(doctype.systemId, copy.systemId, "systemId");
}, "implementation.createDocumentType");

test(function() {
    var doc = document.implementation.createDocument(null, null);
    var copy = doc.cloneNode();
    check_copy(doc, copy, Document);
    assert_equals(doc.charset, "UTF-8", "charset value");
    assert_equals(doc.charset, copy.charset, "charset equality");
    assert_equals(doc.contentType, "application/xml", "contentType value");
    assert_equals(doc.contentType, copy.contentType, "contentType equality");
    assert_equals(doc.URL, "about:blank", "URL value")
    assert_equals(doc.URL, copy.URL, "URL equality");
    assert_equals(doc.compatMode, "CSS1Compat", "compatMode value");
    assert_equals(doc.compatMode, copy.compatMode, "compatMode equality");
}, "implementation.createDocument");

test(function() {
    var html = document.implementation.createHTMLDocument("title");
    var copy = html.cloneNode();
    check_copy(html, copy, Document);
    assert_equals(copy.title, "", "title value");
}, "implementation.createHTMLDocument");

test(function() {
    var parent = document.createElement("div");
    var child1 = document.createElement("div");
    var child2 = document.createElement("div");
    var grandChild = document.createElement("div");

    child2.appendChild(grandChild);
    parent.appendChild(child1);
    parent.appendChild(child2);

    var deep = true;
    var copy = parent.cloneNode(deep);

    check_copy(parent, copy, HTMLDivElement);
    assert_equals(copy.childNodes.length, 2,
                  "copy.childNodes.length with deep copy");

    check_copy(child1, copy.childNodes[0], HTMLDivElement);
    assert_equals(copy.childNodes[0].childNodes.length, 0,
                  "copy.childNodes[0].childNodes.length");

    check_copy(child2, copy.childNodes[1], HTMLDivElement);
    assert_equals(copy.childNodes[1].childNodes.length, 1,
                  "copy.childNodes[1].childNodes.length");
    check_copy(grandChild, copy.childNodes[1].childNodes[0], HTMLDivElement);

    deep = false;
    copy = parent.cloneNode(deep);

    check_copy(parent, copy, HTMLDivElement);
    assert_equals(copy.childNodes.length, 0,
                  "copy.childNodes.length with non-deep copy");
}, "node with children");

test(() => {
  const proto = Object.create(HTMLElement.prototype),
        node = document.createElement("hi");
  Object.setPrototypeOf(node, proto);
  assert_true(proto.isPrototypeOf(node));
  const clone = node.cloneNode();
  assert_false(proto.isPrototypeOf(clone));
  assert_true(HTMLUnknownElement.prototype.isPrototypeOf(clone));
  const deepClone = node.cloneNode(true);
  assert_false(proto.isPrototypeOf(deepClone));
  assert_true(HTMLUnknownElement.prototype.isPrototypeOf(deepClone));
}, "Node with custom prototype")
</script>
