| <!DOCTYPE html> | |
| <meta charset=utf-8> | |
| <title>Attributes tests</title> | |
| <link rel=help href="https://dom.spec.whatwg.org/#attr"> | |
| <link rel=help href="https://dom.spec.whatwg.org/#dom-element-setattribute"> | |
| <link rel=help href="https://dom.spec.whatwg.org/#dom-element-setattributens"> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script src="attributes.js"></script> | |
| <script src="productions.js"></script> | |
| <div id="log"></div> | |
| <span id="test1"></span> | |
| <span class="&<>foo"></span> | |
| <span id="test2"> | |
| <span ~=""></span> | |
| <span ~></span> | |
| <span></span> | |
| </span> | |
| <script> | |
| var XML = "http://www.w3.org/XML/1998/namespace" | |
| var XMLNS = "http://www.w3.org/2000/xmlns/" | |
| // toggleAttribute exhaustive tests | |
| // Step 1 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| for (var i = 0; i < invalid_names.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { el.toggleAttribute(invalid_names[i], true) }) | |
| } | |
| for (var i = 0; i < invalid_names.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { el.toggleAttribute(invalid_names[i]) }) | |
| } | |
| for (var i = 0; i < invalid_names.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { el.toggleAttribute(invalid_names[i], false) }) | |
| } | |
| }, "When qualifiedName does not match the Name production, an " + | |
| "INVALID_CHARACTER_ERR exception is to be thrown. (toggleAttribute)") | |
| test(function() { | |
| var el = document.getElementById("test2") | |
| for (var i = 0; i < el.children.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { | |
| el.children[i].toggleAttribute("~", false) | |
| }) | |
| } | |
| for (var i = 0; i < el.children.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { | |
| el.children[i].toggleAttribute("~") | |
| }) | |
| } | |
| for (var i = 0; i < el.children.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { | |
| el.children[i].toggleAttribute("~", true) | |
| }) | |
| } | |
| }, "When qualifiedName does not match the Name production, an " + | |
| "INVALID_CHARACTER_ERR exception is to be thrown, even if the attribute " + | |
| "is already present. (toggleAttribute)") | |
| // Step 2 | |
| test(function() { | |
| var el = document.createElement("div") | |
| assert_true(el.toggleAttribute("ALIGN")) | |
| assert_true(!el.hasAttributeNS("", "ALIGN")) | |
| assert_true(el.hasAttributeNS("", "align")) | |
| assert_true(el.hasAttribute("align")) | |
| assert_true(!el.toggleAttribute("ALIGN")) | |
| assert_true(!el.hasAttributeNS("", "ALIGN")) | |
| assert_true(!el.hasAttributeNS("", "align")) | |
| assert_true(!el.hasAttribute("align")) | |
| }, "toggleAttribute should lowercase its name argument (upper case attribute)") | |
| test(function() { | |
| var el = document.createElement("div") | |
| assert_true(el.toggleAttribute("CHEEseCaKe")) | |
| assert_true(!el.hasAttributeNS("", "CHEEseCaKe")) | |
| assert_true(el.hasAttributeNS("", "cheesecake")) | |
| assert_true(el.hasAttribute("cheesecake")) | |
| }, "toggleAttribute should lowercase its name argument (mixed case attribute)") | |
| // Step 3 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| var tests = ["xmlns", "xmlns:a", "xmlnsx", "xmlns0"] | |
| for (var i = 0; i < tests.length; i++) { | |
| assert_true(el.toggleAttribute(tests[i])); | |
| assert_true(el.hasAttribute(tests[i])); | |
| } | |
| }, "toggleAttribute should not throw even when qualifiedName starts with 'xmlns'") | |
| // Step 4 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| for (var i = 0; i < valid_names.length; i++) { | |
| assert_true(el.toggleAttribute(valid_names[i])) | |
| assert_true(el.hasAttribute(valid_names[i])) | |
| assert_true(!el.toggleAttribute(valid_names[i])) | |
| assert_true(!el.hasAttribute(valid_names[i])) | |
| // Check using force attr | |
| assert_true(el.toggleAttribute(valid_names[i], true)) | |
| assert_true(el.hasAttribute(valid_names[i])) | |
| assert_true(el.toggleAttribute(valid_names[i], true)) | |
| assert_true(el.hasAttribute(valid_names[i])) | |
| assert_true(!el.toggleAttribute(valid_names[i], false)) | |
| assert_true(!el.hasAttribute(valid_names[i])) | |
| } | |
| }, "Basic functionality should be intact. (toggleAttribute)") | |
| // Step 5 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.toggleAttribute("a") | |
| el.toggleAttribute("b") | |
| el.setAttribute("a", "thing") | |
| el.toggleAttribute("c") | |
| attributes_are(el, [["a", "thing"], | |
| ["b", ""], | |
| ["c", ""]]) | |
| }, "toggleAttribute should not change the order of previously set attributes.") | |
| test(function() { | |
| var el = document.createElement("baz") | |
| el.setAttributeNS("ab", "attr", "fail") | |
| el.setAttributeNS("kl", "attr", "pass") | |
| el.toggleAttribute("attr") | |
| attributes_are(el, [["attr", "pass", "kl"]]) | |
| }, "toggleAttribute should set the first attribute with the given name") | |
| test(function() { | |
| // Based on a test by David Flanagan. | |
| var el = document.createElement("baz") | |
| el.setAttributeNS("foo", "foo:bar", "1"); | |
| el.setAttributeNS("foo", "foo:bat", "2"); | |
| assert_equals(el.getAttribute("foo:bar"), "1") | |
| assert_equals(el.getAttribute("foo:bat"), "2") | |
| attr_is(el.attributes[0], "1", "bar", "foo", "foo", "foo:bar") | |
| attr_is(el.attributes[1], "2", "bat", "foo", "foo", "foo:bat") | |
| el.toggleAttribute("foo:bar"); | |
| assert_true(!el.hasAttribute("foo:bar")) | |
| attr_is(el.attributes[0], "2", "bat", "foo", "foo", "foo:bat") | |
| }, "toggleAttribute should set the attribute with the given qualified name") | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.style = "color: red; background-color: green" | |
| assert_equals(el.toggleAttribute("style"), false) | |
| }, "Toggling element with inline style should make inline style disappear") | |
| // setAttribute exhaustive tests | |
| // Step 1 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| for (var i = 0; i < invalid_names.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { el.setAttribute(invalid_names[i], "test") }) | |
| } | |
| }, "When qualifiedName does not match the Name production, an " + | |
| "INVALID_CHARACTER_ERR exception is to be thrown. (setAttribute)") | |
| test(function() { | |
| var el = document.getElementById("test2") | |
| for (var i = 0; i < el.children.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { | |
| el.children[i].setAttribute("~", "test") | |
| }) | |
| } | |
| }, "When qualifiedName does not match the Name production, an " + | |
| "INVALID_CHARACTER_ERR exception is to be thrown, even if the attribute " + | |
| "is already present. (setAttribute)") | |
| // Step 2 | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("ALIGN", "left") | |
| assert_equals(el.getAttributeNS("", "ALIGN"), null) | |
| assert_equals(el.getAttributeNS("", "align"), "left") | |
| assert_equals(el.getAttribute("align"), "left") | |
| }, "setAttribute should lowercase its name argument (upper case attribute)") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("CHEEseCaKe", "tasty") | |
| assert_equals(el.getAttributeNS("", "CHEEseCaKe"), null) | |
| assert_equals(el.getAttributeNS("", "cheesecake"), "tasty") | |
| assert_equals(el.getAttribute("cheesecake"), "tasty") | |
| }, "setAttribute should lowercase its name argument (mixed case attribute)") | |
| // Step 3 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| var tests = ["xmlns", "xmlns:a", "xmlnsx", "xmlns0"] | |
| for (var i = 0; i < tests.length; i++) { | |
| el.setAttribute(tests[i], "success"); | |
| } | |
| }, "setAttribute should not throw even when qualifiedName starts with 'xmlns'") | |
| // Step 4 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| for (var i = 0; i < valid_names.length; i++) { | |
| el.setAttribute(valid_names[i], "test") | |
| assert_equals(el.getAttribute(valid_names[i]), "test") | |
| } | |
| }, "Basic functionality should be intact.") | |
| // Step 5 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttribute("a", "1") | |
| el.setAttribute("b", "2") | |
| el.setAttribute("a", "3") | |
| el.setAttribute("c", "4") | |
| attributes_are(el, [["a", "3"], | |
| ["b", "2"], | |
| ["c", "4"]]) | |
| }, "setAttribute should not change the order of previously set attributes.") | |
| test(function() { | |
| var el = document.createElement("baz") | |
| el.setAttributeNS("ab", "attr", "fail") | |
| el.setAttributeNS("kl", "attr", "pass") | |
| el.setAttribute("attr", "pass") | |
| attributes_are(el, [["attr", "pass", "ab"], | |
| ["attr", "pass", "kl"]]) | |
| }, "setAttribute should set the first attribute with the given name") | |
| test(function() { | |
| // Based on a test by David Flanagan. | |
| var el = document.createElement("baz") | |
| el.setAttributeNS("foo", "foo:bar", "1"); | |
| assert_equals(el.getAttribute("foo:bar"), "1") | |
| attr_is(el.attributes[0], "1", "bar", "foo", "foo", "foo:bar") | |
| el.setAttribute("foo:bar", "2"); | |
| assert_equals(el.getAttribute("foo:bar"), "2") | |
| attr_is(el.attributes[0], "2", "bar", "foo", "foo", "foo:bar") | |
| }, "setAttribute should set the attribute with the given qualified name") | |
| // setAttributeNS exhaustive tests | |
| // Step 1 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| for (var i = 0, il = invalid_names.length; i < il; ++i) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", | |
| function() { el.setAttributeNS("a", invalid_names[i], "fail") }) | |
| } | |
| }, "When qualifiedName does not match the Name production, an " + | |
| "INVALID_CHARACTER_ERR exception is to be thrown. (setAttributeNS)") | |
| test(function() { | |
| var el = document.getElementById("test2") | |
| for (var i = 0; i < el.children.length; i++) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", function() { | |
| el.children[i].setAttributeNS(null, "~", "test") | |
| }) | |
| } | |
| }, "When qualifiedName does not match the Name production, an " + | |
| "INVALID_CHARACTER_ERR exception is to be thrown, even if the attribute " + | |
| "is already present. (setAttributeNS)") | |
| // Step 2 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| for (var i = 0, il = invalid_qnames.length; i < il; ++i) { | |
| assert_throws_dom("INVALID_CHARACTER_ERR", | |
| function() { el.setAttributeNS("a", invalid_qnames[i], "fail") }, | |
| "Expected exception for " + invalid_qnames[i] + ".") | |
| } | |
| }, "When qualifiedName does not match the QName production, an " + | |
| "INVALID_CHARACTER_ERR exception is to be thrown.") | |
| // Step 3 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttributeNS(null, "aa", "bb") | |
| el.setAttributeNS("", "xx", "bb") | |
| attributes_are(el, [["aa", "bb"], | |
| ["xx", "bb"]]) | |
| }, "null and the empty string should result in a null namespace.") | |
| // Step 4 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| assert_throws_dom("NAMESPACE_ERR", | |
| function() { el.setAttributeNS("", "aa:bb", "fail") }) | |
| assert_throws_dom("NAMESPACE_ERR", | |
| function() { el.setAttributeNS(null, "aa:bb", "fail") }) | |
| }, "A namespace is required to use a prefix.") | |
| // Step 5 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| assert_throws_dom("NAMESPACE_ERR", | |
| function() { el.setAttributeNS("a", "xml:bb", "fail") }) | |
| }, "The xml prefix should not be allowed for arbitrary namespaces") | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttributeNS(XML, "a:bb", "pass") | |
| assert_equals(el.attributes.length, 1) | |
| attr_is(el.attributes[0], "pass", "bb", XML, "a", "a:bb") | |
| }, "XML-namespaced attributes don't need an xml prefix") | |
| // Step 6 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| assert_throws_dom("NAMESPACE_ERR", | |
| function() { el.setAttributeNS("a", "xmlns:bb", "fail") }) | |
| }, "The xmlns prefix should not be allowed for arbitrary namespaces") | |
| test(function() { | |
| var el = document.createElement("foo") | |
| assert_throws_dom("NAMESPACE_ERR", | |
| function() { el.setAttributeNS("a", "xmlns", "fail") }) | |
| }, "The xmlns qualified name should not be allowed for arbitrary namespaces") | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttributeNS("ns", "a:xmlns", "pass") | |
| assert_equals(el.attributes.length, 1) | |
| attr_is(el.attributes[0], "pass", "xmlns", "ns", "a", "a:xmlns") | |
| }, "xmlns should be allowed as local name") | |
| // Step 7 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| assert_throws_dom("NAMESPACE_ERR", | |
| function() { el.setAttributeNS(XMLNS, "a:xmlns", "fail") }) | |
| assert_throws_dom("NAMESPACE_ERR", | |
| function() { el.setAttributeNS(XMLNS, "b:foo", "fail") }) | |
| }, "The XMLNS namespace should require xmlns as prefix or qualified name") | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttributeNS(XMLNS, "xmlns:a", "pass") | |
| assert_equals(el.attributes.length, 1) | |
| attr_is(el.attributes[0], "pass", "a", XMLNS, "xmlns", "xmlns:a") | |
| }, "xmlns should be allowed as prefix in the XMLNS namespace") | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttributeNS(XMLNS, "xmlns", "pass") | |
| assert_equals(el.attributes.length, 1) | |
| attr_is(el.attributes[0], "pass", "xmlns", XMLNS, null, "xmlns") | |
| }, "xmlns should be allowed as qualified name in the XMLNS namespace") | |
| // Step 8-9 | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttributeNS("a", "foo:bar", "X") | |
| assert_equals(el.attributes.length, 1) | |
| attr_is(el.attributes[0], "X", "bar", "a", "foo", "foo:bar") | |
| el.setAttributeNS("a", "quux:bar", "Y") | |
| assert_equals(el.attributes.length, 1) | |
| attr_is(el.attributes[0], "Y", "bar", "a", "foo", "foo:bar") | |
| el.removeAttributeNS("a", "bar") | |
| }, "Setting the same attribute with another prefix should not change the prefix") | |
| // Miscellaneous tests | |
| test(function() { | |
| var el = document.createElement("iframe") | |
| el.setAttribute("src", "file:///home") | |
| assert_equals(el.getAttribute("src"), "file:///home") | |
| }, "setAttribute should not throw even if a load is not allowed") | |
| test(function() { | |
| var docFragment = document.createDocumentFragment() | |
| var newOne = document.createElement("newElement") | |
| newOne.setAttribute("newdomestic", "Yes") | |
| docFragment.appendChild(newOne) | |
| var domesticNode = docFragment.firstChild | |
| var attr = domesticNode.attributes.item(0) | |
| attr_is(attr, "Yes", "newdomestic", null, null, "newdomestic") | |
| }, "Attributes should work in document fragments.") | |
| test(function() { | |
| var el = document.createElement("foo") | |
| el.setAttribute("x", "y") | |
| var attr = el.attributes[0] | |
| attr.value = "Y<" | |
| attr_is(attr, "Y<", "x", null, null, "x") | |
| assert_equals(el.getAttribute("x"), "Y<") | |
| }, "Attribute values should not be parsed.") | |
| test(function() { | |
| var el = document.getElementsByTagName("span")[0] | |
| attr_is(el.attributes[0], "test1", "id", null, null, "id") | |
| }, "Specified attributes should be accessible.") | |
| test(function() { | |
| var el = document.getElementsByTagName("span")[1] | |
| attr_is(el.attributes[0], "&<>foo", "class", null, null, "class") | |
| }, "Entities in attributes should have been expanded while parsing.") | |
| test(function() { | |
| var el = document.createElement("div") | |
| assert_equals(el.hasAttribute("bar"), false) | |
| assert_equals(el.hasAttributeNS(null, "bar"), false) | |
| assert_equals(el.hasAttributeNS("", "bar"), false) | |
| assert_equals(el.getAttribute("bar"), null) | |
| assert_equals(el.getAttributeNS(null, "bar"), null) | |
| assert_equals(el.getAttributeNS("", "bar"), null) | |
| }, "Unset attributes return null") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttributeNS("ab", "attr", "t1") | |
| el.setAttributeNS("kl", "attr", "t2") | |
| assert_equals(el.hasAttribute("attr"), true) | |
| assert_equals(el.hasAttributeNS("ab", "attr"), true) | |
| assert_equals(el.hasAttributeNS("kl", "attr"), true) | |
| assert_equals(el.getAttribute("attr"), "t1") | |
| assert_equals(el.getAttributeNS("ab", "attr"), "t1") | |
| assert_equals(el.getAttributeNS("kl", "attr"), "t2") | |
| assert_equals(el.getAttributeNS(null, "attr"), null) | |
| assert_equals(el.getAttributeNS("", "attr"), null) | |
| }, "First set attribute is returned by getAttribute") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("style", "color:#fff;") | |
| assert_equals(el.hasAttribute("style"), true) | |
| assert_equals(el.hasAttributeNS(null, "style"), true) | |
| assert_equals(el.hasAttributeNS("", "style"), true) | |
| assert_equals(el.getAttribute("style"), "color:#fff;") | |
| assert_equals(el.getAttributeNS(null, "style"), "color:#fff;") | |
| assert_equals(el.getAttributeNS("", "style"), "color:#fff;") | |
| }, "Style attributes are not normalized") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttributeNS("", "ALIGN", "left") | |
| assert_equals(el.hasAttribute("ALIGN"), false) | |
| assert_equals(el.hasAttribute("align"), false) | |
| assert_equals(el.hasAttributeNS(null, "ALIGN"), true) | |
| assert_equals(el.hasAttributeNS(null, "align"), false) | |
| assert_equals(el.hasAttributeNS("", "ALIGN"), true) | |
| assert_equals(el.hasAttributeNS("", "align"), false) | |
| assert_equals(el.getAttribute("ALIGN"), null) | |
| assert_equals(el.getAttribute("align"), null) | |
| assert_equals(el.getAttributeNS(null, "ALIGN"), "left") | |
| assert_equals(el.getAttributeNS("", "ALIGN"), "left") | |
| assert_equals(el.getAttributeNS(null, "align"), null) | |
| assert_equals(el.getAttributeNS("", "align"), null) | |
| el.removeAttributeNS("", "ALIGN") | |
| }, "Only lowercase attributes are returned on HTML elements (upper case attribute)") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttributeNS("", "CHEEseCaKe", "tasty") | |
| assert_equals(el.hasAttribute("CHEESECAKE"), false) | |
| assert_equals(el.hasAttribute("CHEEseCaKe"), false) | |
| assert_equals(el.hasAttribute("cheesecake"), false) | |
| assert_equals(el.hasAttributeNS("", "CHEESECAKE"), false) | |
| assert_equals(el.hasAttributeNS("", "CHEEseCaKe"), true) | |
| assert_equals(el.hasAttributeNS("", "cheesecake"), false) | |
| assert_equals(el.hasAttributeNS(null, "CHEESECAKE"), false) | |
| assert_equals(el.hasAttributeNS(null, "CHEEseCaKe"), true) | |
| assert_equals(el.hasAttributeNS(null, "cheesecake"), false) | |
| assert_equals(el.getAttribute("CHEESECAKE"), null) | |
| assert_equals(el.getAttribute("CHEEseCaKe"), null) | |
| assert_equals(el.getAttribute("cheesecake"), null) | |
| assert_equals(el.getAttributeNS(null, "CHEESECAKE"), null) | |
| assert_equals(el.getAttributeNS("", "CHEESECAKE"), null) | |
| assert_equals(el.getAttributeNS(null, "CHEEseCaKe"), "tasty") | |
| assert_equals(el.getAttributeNS("", "CHEEseCaKe"), "tasty") | |
| assert_equals(el.getAttributeNS(null, "cheesecake"), null) | |
| assert_equals(el.getAttributeNS("", "cheesecake"), null) | |
| el.removeAttributeNS("", "CHEEseCaKe") | |
| }, "Only lowercase attributes are returned on HTML elements (mixed case attribute)") | |
| test(function() { | |
| var el = document.createElement("div") | |
| document.body.appendChild(el) | |
| el.setAttributeNS("", "align", "left") | |
| el.setAttributeNS("xx", "align", "right") | |
| el.setAttributeNS("", "foo", "left") | |
| el.setAttributeNS("xx", "foo", "right") | |
| assert_equals(el.hasAttribute("align"), true) | |
| assert_equals(el.hasAttribute("foo"), true) | |
| assert_equals(el.hasAttributeNS("xx", "align"), true) | |
| assert_equals(el.hasAttributeNS(null, "foo"), true) | |
| assert_equals(el.getAttribute("align"), "left") | |
| assert_equals(el.getAttribute("foo"), "left") | |
| assert_equals(el.getAttributeNS("xx", "align"), "right") | |
| assert_equals(el.getAttributeNS(null, "foo"), "left") | |
| assert_equals(el.getAttributeNS("", "foo"), "left") | |
| el.removeAttributeNS("", "align") | |
| el.removeAttributeNS("xx", "align") | |
| el.removeAttributeNS("", "foo") | |
| el.removeAttributeNS("xx", "foo") | |
| document.body.removeChild(el) | |
| }, "First set attribute is returned with mapped attribute set first") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttributeNS("xx", "align", "right") | |
| el.setAttributeNS("", "align", "left") | |
| el.setAttributeNS("xx", "foo", "right") | |
| el.setAttributeNS("", "foo", "left") | |
| assert_equals(el.hasAttribute("align"), true) | |
| assert_equals(el.hasAttribute("foo"), true) | |
| assert_equals(el.hasAttributeNS("xx", "align"), true) | |
| assert_equals(el.hasAttributeNS(null, "foo"), true) | |
| assert_equals(el.getAttribute("align"), "right") | |
| assert_equals(el.getAttribute("foo"), "right") | |
| assert_equals(el.getAttributeNS("xx", "align"), "right") | |
| assert_equals(el.getAttributeNS(null, "foo"), "left") | |
| assert_equals(el.getAttributeNS("", "foo"), "left") | |
| el.removeAttributeNS("", "align") | |
| el.removeAttributeNS("xx", "align") | |
| el.removeAttributeNS("", "foo") | |
| el.removeAttributeNS("xx", "foo") | |
| }, "First set attribute is returned with mapped attribute set later") | |
| test(function() { | |
| var el = document.createElementNS("http://www.example.com", "foo") | |
| el.setAttribute("A", "test") | |
| assert_equals(el.hasAttribute("A"), true, "hasAttribute()") | |
| assert_equals(el.hasAttributeNS("", "A"), true, "el.hasAttributeNS(\"\")") | |
| assert_equals(el.hasAttributeNS(null, "A"), true, "el.hasAttributeNS(null)") | |
| assert_equals(el.hasAttributeNS(undefined, "A"), true, "el.hasAttributeNS(undefined)") | |
| assert_equals(el.hasAttributeNS("foo", "A"), false, "el.hasAttributeNS(\"foo\")") | |
| assert_equals(el.getAttribute("A"), "test", "getAttribute()") | |
| assert_equals(el.getAttributeNS("", "A"), "test", "el.getAttributeNS(\"\")") | |
| assert_equals(el.getAttributeNS(null, "A"), "test", "el.getAttributeNS(null)") | |
| assert_equals(el.getAttributeNS(undefined, "A"), "test", "el.getAttributeNS(undefined)") | |
| assert_equals(el.getAttributeNS("foo", "A"), null, "el.getAttributeNS(\"foo\")") | |
| }, "Non-HTML element with upper-case attribute") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("pre:fix", "value 1") | |
| el.setAttribute("fix", "value 2") | |
| var prefixed = el.attributes[0] | |
| assert_equals(prefixed.localName, "pre:fix", "prefixed local name") | |
| assert_equals(prefixed.namespaceURI, null, "prefixed namespace") | |
| var unprefixed = el.attributes[1] | |
| assert_equals(unprefixed.localName, "fix", "unprefixed local name") | |
| assert_equals(unprefixed.namespaceURI, null, "unprefixed namespace") | |
| el.removeAttributeNS(null, "pre:fix") | |
| assert_equals(el.attributes[0], unprefixed) | |
| }, "Attribute with prefix in local name") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("foo", "bar") | |
| var attr = el.attributes[0] | |
| assert_equals(attr.ownerElement, el) | |
| el.removeAttribute("foo") | |
| assert_equals(attr.ownerElement, null) | |
| }, "Attribute loses its owner when removed") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("foo", "bar") | |
| var attr = el.attributes[0] | |
| var attrNode = el.getAttributeNode("foo"); | |
| var attrNodeNS = el.getAttributeNodeNS("", "foo"); | |
| assert_equals(attr, attrNode); | |
| assert_equals(attr, attrNodeNS); | |
| el.setAttributeNS("x", "foo2", "bar2"); | |
| var attr2 = el.attributes[1]; | |
| var attrNodeNS2 = el.getAttributeNodeNS("x", "foo2"); | |
| assert_equals(attr2, attrNodeNS2); | |
| }, "Basic functionality of getAttributeNode/getAttributeNodeNS") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("foo", "bar") | |
| var attrNode = el.getAttributeNode("foo"); | |
| var attrNodeNS = el.getAttributeNodeNS("", "foo"); | |
| assert_equals(attrNode, attrNodeNS); | |
| el.removeAttribute("foo"); | |
| var el2 = document.createElement("div"); | |
| el2.setAttributeNode(attrNode); | |
| assert_equals(attrNode, el2.getAttributeNode("foo")); | |
| assert_equals(attrNode, el2.attributes[0]); | |
| assert_equals(attrNode.ownerElement, el2); | |
| assert_equals(attrNode.value, "bar"); | |
| var el3 = document.createElement("div"); | |
| el2.removeAttribute("foo"); | |
| el3.setAttribute("foo", "baz"); | |
| el3.setAttributeNode(attrNode); | |
| assert_equals(el3.getAttribute("foo"), "bar"); | |
| }, "Basic functionality of setAttributeNode") | |
| test(function() { | |
| var el = document.createElement("div"); | |
| var attr1 = document.createAttributeNS("ns1", "p1:name"); | |
| attr1.value = "value1"; | |
| var attr2 = document.createAttributeNS("ns2", "p2:name"); | |
| attr2.value = "value2"; | |
| el.setAttributeNode(attr1); | |
| el.setAttributeNode(attr2); | |
| assert_equals(el.getAttributeNodeNS("ns1", "name").value, "value1"); | |
| assert_equals(el.getAttributeNodeNS("ns2", "name").value, "value2"); | |
| }, "setAttributeNode should distinguish attributes with same local name and different namespaces") | |
| test(function() { | |
| var el = document.createElement("div"); | |
| var attr1 = document.createAttributeNS("ns1", "p1:name"); | |
| attr1.value = "value1"; | |
| var attr2 = document.createAttributeNS("ns1", "p1:NAME"); | |
| attr2.value = "VALUE2"; | |
| el.setAttributeNode(attr1); | |
| el.setAttributeNode(attr2); | |
| assert_equals(el.getAttributeNodeNS("ns1", "name").value, "value1"); | |
| assert_equals(el.getAttributeNodeNS("ns1", "NAME").value, "VALUE2"); | |
| }, "setAttributeNode doesn't have case-insensitivity even with an HTMLElement") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttributeNS("x", "foo", "bar") | |
| var attrNode = el.getAttributeNodeNS("x", "foo"); | |
| el.removeAttribute("foo"); | |
| var el2 = document.createElement("div"); | |
| el2.setAttributeNS("x", "foo", "baz"); | |
| el2.setAttributeNodeNS(attrNode); | |
| assert_equals(el2.getAttributeNS("x", "foo"), "bar"); | |
| }, "Basic functionality of setAttributeNodeNS") | |
| test(function() { | |
| var el = document.createElement("div"); | |
| var other = document.createElement("div"); | |
| var attr = document.createAttribute("foo"); | |
| assert_equals(el.setAttributeNode(attr), null); | |
| assert_equals(attr.ownerElement, el); | |
| assert_throws_dom("INUSE_ATTRIBUTE_ERR", | |
| function() { other.setAttributeNode(attr) }, | |
| "Attribute already associated with el") | |
| }, "If attr’s element is neither null nor element, throw an InUseAttributeError."); | |
| test(function() { | |
| var el = document.createElement("div"); | |
| var attr = document.createAttribute("foo"); | |
| assert_equals(el.setAttributeNode(attr), null); | |
| el.setAttribute("bar", "qux"); | |
| assert_equals(el.setAttributeNode(attr), attr); | |
| assert_equals(el.attributes[0], attr); | |
| }, "Replacing an attr by itself"); | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("foo", "bar") | |
| var attrNode = el.getAttributeNode("foo"); | |
| el.removeAttributeNode(attrNode); | |
| var el2 = document.createElement("div"); | |
| el2.setAttributeNode(attrNode); | |
| assert_equals(el2.attributes[0], attrNode); | |
| assert_equals(el.attributes.length, 0); | |
| }, "Basic functionality of removeAttributeNode") | |
| test(function() { | |
| var el = document.createElement("div") | |
| el.setAttribute("foo", "bar") | |
| var attrNode = el.getAttributeNode("foo"); | |
| var el2 = document.createElement("div"); | |
| assert_throws_dom("INUSE_ATTRIBUTE_ERR", function(){el2.setAttributeNode(attrNode)}); | |
| }, "setAttributeNode on bound attribute should throw InUseAttributeError") | |
| // Have to use an async_test to see what a DOMAttrModified listener sees, | |
| // because otherwise the event dispatch code will swallow our exceptions. And | |
| // we want to make sure this test always happens, even when no mutation events | |
| // run. | |
| var setAttributeNode_mutation_test = async_test("setAttributeNode, if it fires mutation events, should fire one with the new node when resetting an existing attribute"); | |
| test(function(){ | |
| var el = document.createElement("div") | |
| var attrNode1 = document.createAttribute("foo"); | |
| attrNode1.value = "bar"; | |
| el.setAttributeNode(attrNode1); | |
| var attrNode2 = document.createAttribute("foo"); | |
| attrNode2.value = "baz"; | |
| el.addEventListener("DOMAttrModified", function(e) { | |
| // If this never gets called, that's OK, I guess. But if it gets called, it | |
| // better represent a single modification with attrNode2 as the relatedNode. | |
| // We have to do an inner test() call here, because otherwise the exceptions | |
| // our asserts trigger will get swallowed by the event dispatch code. | |
| setAttributeNode_mutation_test.step(function() { | |
| assert_equals(e.attrName, "foo"); | |
| assert_equals(e.attrChange, MutationEvent.MODIFICATION); | |
| assert_equals(e.prevValue, "bar"); | |
| assert_equals(e.newValue, "baz"); | |
| assert_equals(e.relatedNode, attrNode2); | |
| }); | |
| }); | |
| var oldNode = el.setAttributeNode(attrNode2); | |
| assert_equals(oldNode, attrNode1, | |
| "Must return the old attr node from a setAttributeNode call"); | |
| }, "setAttributeNode, if it fires mutation events, should fire one with the new node when resetting an existing attribute (outer shell)"); | |
| setAttributeNode_mutation_test.done(); | |
| test(function(){ | |
| var el = document.createElement("div") | |
| el.setAttribute("a", "b"); | |
| el.setAttribute("c", "d"); | |
| assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.name }), | |
| ["a", "c"]); | |
| assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.value }), | |
| ["b", "d"]); | |
| var attrNode = document.createAttribute("a"); | |
| attrNode.value = "e"; | |
| el.setAttributeNode(attrNode); | |
| assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.name }), | |
| ["a", "c"]); | |
| assert_array_equals(Array.prototype.map.call(el.attributes, function(a) { return a.value }), | |
| ["e", "d"]); | |
| }, "setAttributeNode called with an Attr that has the same name as an existing one should not change attribute order"); | |
| test(function() { | |
| var el = document.createElement("div"); | |
| el.setAttribute("foo", "bar"); | |
| assert_equals(el.getAttributeNames().length, 1); | |
| assert_equals(el.getAttributeNames()[0], el.attributes[0].name); | |
| assert_equals(el.getAttributeNames()[0], "foo"); | |
| el.removeAttribute("foo"); | |
| assert_equals(el.getAttributeNames().length, 0); | |
| el.setAttribute("foo", "bar"); | |
| el.setAttributeNS("", "FOO", "bar"); | |
| el.setAttributeNS("dummy1", "foo", "bar"); | |
| el.setAttributeNS("dummy2", "dummy:foo", "bar"); | |
| assert_equals(el.getAttributeNames().length, 4); | |
| assert_equals(el.getAttributeNames()[0], "foo"); | |
| assert_equals(el.getAttributeNames()[1], "FOO"); | |
| assert_equals(el.getAttributeNames()[2], "foo"); | |
| assert_equals(el.getAttributeNames()[3], "dummy:foo"); | |
| assert_equals(el.getAttributeNames()[0], el.attributes[0].name); | |
| assert_equals(el.getAttributeNames()[1], el.attributes[1].name); | |
| assert_equals(el.getAttributeNames()[2], el.attributes[2].name); | |
| assert_equals(el.getAttributeNames()[3], el.attributes[3].name); | |
| el.removeAttributeNS("", "FOO"); | |
| assert_equals(el.getAttributeNames().length, 3); | |
| assert_equals(el.getAttributeNames()[0], "foo"); | |
| assert_equals(el.getAttributeNames()[1], "foo"); | |
| assert_equals(el.getAttributeNames()[2], "dummy:foo"); | |
| assert_equals(el.getAttributeNames()[0], el.attributes[0].name); | |
| assert_equals(el.getAttributeNames()[1], el.attributes[1].name); | |
| assert_equals(el.getAttributeNames()[2], el.attributes[2].name); | |
| }, "getAttributeNames tests"); | |
| function getEnumerableOwnProps1(obj) { | |
| var arr = []; | |
| for (var prop in obj) { | |
| if (obj.hasOwnProperty(prop)) { | |
| arr.push(prop); | |
| } | |
| } | |
| return arr; | |
| } | |
| function getEnumerableOwnProps2(obj) { | |
| return Object.getOwnPropertyNames(obj).filter( | |
| function (name) { return Object.getOwnPropertyDescriptor(obj, name).enumerable; }) | |
| } | |
| test(function() { | |
| var el = document.createElement("div"); | |
| el.setAttribute("a", ""); | |
| el.setAttribute("b", ""); | |
| assert_array_equals(getEnumerableOwnProps1(el.attributes), | |
| ["0", "1"]) | |
| assert_array_equals(getEnumerableOwnProps2(el.attributes), | |
| ["0", "1"]) | |
| assert_array_equals(Object.getOwnPropertyNames(el.attributes), | |
| ["0", "1", "a", "b"]) | |
| }, "Own property correctness with basic attributes"); | |
| test(function() { | |
| var el = document.createElement("div"); | |
| el.setAttributeNS("", "a", ""); | |
| el.setAttribute("b", ""); | |
| el.setAttributeNS("foo", "a", ""); | |
| assert_array_equals(getEnumerableOwnProps1(el.attributes), | |
| ["0", "1", "2"]) | |
| assert_array_equals(getEnumerableOwnProps2(el.attributes), | |
| ["0", "1", "2"]) | |
| assert_array_equals(Object.getOwnPropertyNames(el.attributes), | |
| ["0", "1", "2", "a", "b"]) | |
| for (var propName of Object.getOwnPropertyNames(el.attributes)) { | |
| assert_true(el.attributes[propName] instanceof Attr, | |
| "el.attributes has an Attr for property name " + propName); | |
| } | |
| }, "Own property correctness with non-namespaced attribute before same-name namespaced one"); | |
| test(function() { | |
| var el = document.createElement("div"); | |
| el.setAttributeNS("foo", "a", ""); | |
| el.setAttribute("b", ""); | |
| el.setAttributeNS("", "a", ""); | |
| assert_array_equals(getEnumerableOwnProps1(el.attributes), | |
| ["0", "1", "2"]) | |
| assert_array_equals(getEnumerableOwnProps2(el.attributes), | |
| ["0", "1", "2"]) | |
| assert_array_equals(Object.getOwnPropertyNames(el.attributes), | |
| ["0", "1", "2", "a", "b"]) | |
| for (var propName of Object.getOwnPropertyNames(el.attributes)) { | |
| assert_true(el.attributes[propName] instanceof Attr, | |
| "el.attributes has an Attr for property name " + propName); | |
| } | |
| }, "Own property correctness with namespaced attribute before same-name non-namespaced one"); | |
| test(function() { | |
| var el = document.createElement("div"); | |
| el.setAttributeNS("foo", "a:b", ""); | |
| el.setAttributeNS("foo", "c:d", ""); | |
| el.setAttributeNS("bar", "a:b", ""); | |
| assert_array_equals(getEnumerableOwnProps1(el.attributes), | |
| ["0", "1", "2"]) | |
| assert_array_equals(getEnumerableOwnProps2(el.attributes), | |
| ["0", "1", "2"]) | |
| assert_array_equals(Object.getOwnPropertyNames(el.attributes), | |
| ["0", "1", "2", "a:b", "c:d"]) | |
| for (var propName of Object.getOwnPropertyNames(el.attributes)) { | |
| assert_true(el.attributes[propName] instanceof Attr, | |
| "el.attributes has an Attr for property name " + propName); | |
| } | |
| }, "Own property correctness with two namespaced attributes with the same name-with-prefix"); | |
| test(function() { | |
| var el = document.createElement("div"); | |
| el.setAttributeNS("foo", "A:B", ""); | |
| el.setAttributeNS("bar", "c:D", ""); | |
| el.setAttributeNS("baz", "e:F", ""); | |
| el.setAttributeNS("qux", "g:h", ""); | |
| el.setAttributeNS("", "I", ""); | |
| el.setAttributeNS("", "j", ""); | |
| assert_array_equals(Object.getOwnPropertyNames(el.attributes), | |
| ["0", "1", "2", "3", "4", "5", "g:h", "j"]) | |
| for (var propName of Object.getOwnPropertyNames(el.attributes)) { | |
| assert_true(el.attributes[propName] instanceof Attr, | |
| "el.attributes has an Attr for property name " + propName); | |
| } | |
| }, "Own property names should only include all-lowercase qualified names for an HTML element in an HTML document"); | |
| test(function() { | |
| var el = document.createElementNS("", "div"); | |
| el.setAttributeNS("foo", "A:B", ""); | |
| el.setAttributeNS("bar", "c:D", ""); | |
| el.setAttributeNS("baz", "e:F", ""); | |
| el.setAttributeNS("qux", "g:h", ""); | |
| el.setAttributeNS("", "I", ""); | |
| el.setAttributeNS("", "j", ""); | |
| assert_array_equals(Object.getOwnPropertyNames(el.attributes), | |
| ["0", "1", "2", "3", "4", "5", "A:B", "c:D", "e:F", "g:h", "I", "j"]) | |
| for (var propName of Object.getOwnPropertyNames(el.attributes)) { | |
| assert_true(el.attributes[propName] instanceof Attr, | |
| "el.attributes has an Attr for property name " + propName); | |
| } | |
| }, "Own property names should include all qualified names for a non-HTML element in an HTML document"); | |
| test(function() { | |
| var doc = document.implementation.createDocument(null, ""); | |
| assert_equals(doc.contentType, "application/xml"); | |
| var el = doc.createElementNS("http://www.w3.org/1999/xhtml", "div"); | |
| el.setAttributeNS("foo", "A:B", ""); | |
| el.setAttributeNS("bar", "c:D", ""); | |
| el.setAttributeNS("baz", "e:F", ""); | |
| el.setAttributeNS("qux", "g:h", ""); | |
| el.setAttributeNS("", "I", ""); | |
| el.setAttributeNS("", "j", ""); | |
| assert_array_equals(Object.getOwnPropertyNames(el.attributes), | |
| ["0", "1", "2", "3", "4", "5", "A:B", "c:D", "e:F", "g:h", "I", "j"]) | |
| for (var propName of Object.getOwnPropertyNames(el.attributes)) { | |
| assert_true(el.attributes[propName] instanceof Attr, | |
| "el.attributes has an Attr for property name " + propName); | |
| } | |
| }, "Own property names should include all qualified names for an HTML element in a non-HTML document"); | |
| </script> |