This commit is contained in:
Tim Ledbetter 2024-11-21 14:29:44 +01:00 committed by GitHub
commit 175fdefde5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 992 additions and 9 deletions

View file

@ -148,18 +148,16 @@ Attr const* NamedNodeMap::get_attribute(FlyString const& qualified_name, size_t*
*item_index = 0;
// 1. If element is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase.
// FIXME: Handle the second condition, assume it is an HTML document for now.
bool compare_as_lowercase = associated_element().namespace_uri() == Namespace::HTML;
auto const& name_to_compare = [&]() {
if (associated_element().namespace_uri() == Namespace::HTML && associated_element().document().is_html_document())
return qualified_name.to_ascii_lowercase();
return qualified_name;
}();
// 2. Return the first attribute in elements attribute list whose qualified name is qualifiedName; otherwise null.
for (auto const& attribute : m_attributes) {
if (compare_as_lowercase) {
if (attribute->name().equals_ignoring_ascii_case(qualified_name))
return attribute;
} else {
if (attribute->name() == qualified_name)
return attribute;
}
if (attribute->name() == name_to_compare)
return attribute;
if (item_index)
++(*item_index);

View file

@ -0,0 +1,81 @@
Summary
Harness status: OK
Rerun
Found 70 tests
69 Pass
1 Fail
Details
Result Test Name MessagePass When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown. (toggleAttribute)
Pass 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)
Pass toggleAttribute should lowercase its name argument (upper case attribute)
Pass toggleAttribute should lowercase its name argument (mixed case attribute)
Pass toggleAttribute should not throw even when qualifiedName starts with 'xmlns'
Pass Basic functionality should be intact. (toggleAttribute)
Pass toggleAttribute should not change the order of previously set attributes.
Pass toggleAttribute should set the first attribute with the given name
Pass toggleAttribute should set the attribute with the given qualified name
Pass Toggling element with inline style should make inline style disappear
Pass When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown. (setAttribute)
Pass 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)
Pass setAttribute should lowercase its name argument (upper case attribute)
Pass setAttribute should lowercase its name argument (mixed case attribute)
Pass setAttribute should not throw even when qualifiedName starts with 'xmlns'
Pass Basic functionality should be intact.
Pass setAttribute should not change the order of previously set attributes.
Pass setAttribute should set the first attribute with the given name
Pass setAttribute should set the attribute with the given qualified name
Pass When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown. (setAttributeNS)
Pass 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)
Pass When qualifiedName does not match the QName production, an INVALID_CHARACTER_ERR exception is to be thrown.
Pass null and the empty string should result in a null namespace.
Pass A namespace is required to use a prefix.
Pass The xml prefix should not be allowed for arbitrary namespaces
Pass XML-namespaced attributes don't need an xml prefix
Pass The xmlns prefix should not be allowed for arbitrary namespaces
Pass The xmlns qualified name should not be allowed for arbitrary namespaces
Pass xmlns should be allowed as local name
Pass The XMLNS namespace should require xmlns as prefix or qualified name
Pass xmlns should be allowed as prefix in the XMLNS namespace
Pass xmlns should be allowed as qualified name in the XMLNS namespace
Pass Setting the same attribute with another prefix should not change the prefix
Pass setAttribute should not throw even if a load is not allowed
Pass Attributes should work in document fragments.
Pass Attribute values should not be parsed.
Pass Specified attributes should be accessible.
Pass Entities in attributes should have been expanded while parsing.
Pass Unset attributes return null
Pass First set attribute is returned by getAttribute
Pass Style attributes are not normalized
Pass Only lowercase attributes are returned on HTML elements (upper case attribute)
Pass Only lowercase attributes are returned on HTML elements (mixed case attribute)
Pass First set attribute is returned with mapped attribute set first
Pass First set attribute is returned with mapped attribute set later
Pass Non-HTML element with upper-case attribute
Pass Attribute with prefix in local name
Pass Attribute loses its owner when removed
Pass Basic functionality of getAttributeNode/getAttributeNodeNS
Pass Basic functionality of setAttributeNode
Pass setAttributeNode should distinguish attributes with same local name and different namespaces
Pass setAttributeNode doesn't have case-insensitivity even with an HTMLElement 1
Pass setAttributeNode doesn't have case-insensitivity even with an HTMLElement 2
Pass setAttributeNode doesn't have case-insensitivity even with an HTMLElement 3
Pass Basic functionality of setAttributeNodeNS
Pass If attrs element is neither null nor element, throw an InUseAttributeError.
Pass Replacing an attr by itself
Pass Basic functionality of removeAttributeNode
Pass setAttributeNode on bound attribute should throw InUseAttributeError
Pass setAttributeNode, if it fires mutation events, should fire one with the new node when resetting an existing attribute
Pass setAttributeNode, if it fires mutation events, should fire one with the new node when resetting an existing attribute (outer shell)
Pass setAttributeNode called with an Attr that has the same name as an existing one should not change attribute order
Pass getAttributeNames tests
Pass Own property correctness with basic attributes
Pass Own property correctness with non-namespaced attribute before same-name namespaced one
Pass Own property correctness with namespaced attribute before same-name non-namespaced one
Pass Own property correctness with two namespaced attributes with the same name-with-prefix
Pass Own property names should only include all-lowercase qualified names for an HTML element in an HTML document
Pass Own property names should include all qualified names for a non-HTML element in an HTML document
Fail Own property names should include all qualified names for an HTML element in a non-HTML document

View file

@ -0,0 +1,883 @@
<!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="&amp;&lt;&gt;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&lt;"
attr_is(attr, "Y&lt;", "x", null, null, "x")
assert_equals(el.getAttribute("x"), "Y&lt;")
}, "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 1")
test(function() {
var el = document.createElement("div");
var attr1 = document.createAttributeNS("ns1", "FOOBAR");
var attr2 = document.createAttributeNS("ns1", "FOOBAR");
assert_equals(el.setAttributeNode(attr1), null);
assert_equals(attr1.ownerElement, el);
assert_equals(attr2.ownerElement, null);
var oldAttr = el.setAttributeNode(attr2);
assert_equals(oldAttr, attr1);
assert_equals(attr1.ownerElement, null);
assert_equals(attr2.ownerElement, el);
}, "setAttributeNode doesn't have case-insensitivity even with an HTMLElement 2")
test(function() {
var el = document.createElement("div");
var attr1 = document.createAttributeNS("ns1", "foobar");
var attr2 = document.createAttributeNS("ns1", "FOOBAR");
assert_equals(el.setAttributeNode(attr1), null);
assert_equals(attr1.ownerElement, el);
assert_equals(attr2.ownerElement, null);
assert_equals(el.setAttributeNode(attr2), null);
assert_equals(attr1.ownerElement, el);
assert_equals(attr2.ownerElement, el);
}, "setAttributeNode doesn't have case-insensitivity even with an HTMLElement 3")
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 attrs 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>

View file

@ -0,0 +1,18 @@
function attr_is(attr, v, ln, ns, p, n) {
assert_equals(attr.value, v)
assert_equals(attr.nodeValue, v)
assert_equals(attr.textContent, v)
assert_equals(attr.localName, ln)
assert_equals(attr.namespaceURI, ns)
assert_equals(attr.prefix, p)
assert_equals(attr.name, n)
assert_equals(attr.nodeName, n);
assert_equals(attr.specified, true)
}
function attributes_are(el, l) {
for (var i = 0, il = l.length; i < il; i++) {
attr_is(el.attributes[i], l[i][1], l[i][0], (l[i].length < 3) ? null : l[i][2], null, l[i][0])
assert_equals(el.attributes[i].ownerElement, el)
}
}

View file

@ -0,0 +1,3 @@
var invalid_names = ["", "invalid^Name", "\\", "'", '"', "0", "0:a"] // XXX
var valid_names = ["x", "X", ":", "a:0"]
var invalid_qnames = [":a", "b:", "x:y:z"] // XXX