Bug 1237580. NamedNodeMap should only claim to support a name if that name has no uppercase ASCII chars, for HTML elements in HTML documents. r=bkelly
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 15 Jan 2016 13:29:58 -0500
changeset 280223 31d158e26ea5d10dcf25b042ee7620ed79513b5f
parent 280222 3e2c9a354c87a393d4037fa188a3b9754afb4c3e
child 280224 b20e5de65e51400701e5ab8ba7308df37e13ebeb
push id29906
push userryanvm@gmail.com
push dateSun, 17 Jan 2016 19:40:11 +0000
treeherdermozilla-central@b92f5032b4fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1237580
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1237580. NamedNodeMap should only claim to support a name if that name has no uppercase ASCII chars, for HTML elements in HTML documents. r=bkelly
dom/base/nsDOMAttributeMap.cpp
testing/web-platform/tests/dom/nodes/attributes.html
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -191,27 +191,42 @@ nsDOMAttributeMap::NameIsEnumerable(cons
 void
 nsDOMAttributeMap::GetSupportedNames(unsigned aFlags,
                                      nsTArray<nsString>& aNames)
 {
   if (!(aFlags & JSITER_HIDDEN)) {
     return;
   }
 
+  // For HTML elements in HTML documents, only include names that are still the
+  // same after ASCII-lowercasing, since our named getter will end up
+  // ASCII-lowercasing the given string.
+  bool lowercaseNamesOnly =
+    mContent->IsHTMLElement() && mContent->IsInHTMLDocument();
+
   const uint32_t count = mContent->GetAttrCount();
   bool seenNonAtomName = false;
   for (uint32_t i = 0; i < count; i++) {
     const nsAttrName* name = mContent->GetAttrNameAt(i);
     seenNonAtomName = seenNonAtomName || !name->IsAtom();
     nsString qualifiedName;
     name->GetQualifiedName(qualifiedName);
 
+    if (lowercaseNamesOnly &&
+        nsContentUtils::StringContainsASCIIUpper(qualifiedName)) {
+      continue;
+    }
+
+    // Omit duplicates.  We only need to do this check if we've seen a non-atom
+    // name, because that's the only way we can have two identical qualified
+    // names.
     if (seenNonAtomName && aNames.Contains(qualifiedName)) {
       continue;
     }
+
     aNames.AppendElement(qualifiedName);
   }
 }
 
 Attr*
 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName)
 {
   bool dummy;
--- a/testing/web-platform/tests/dom/nodes/attributes.html
+++ b/testing/web-platform/tests/dom/nodes/attributes.html
@@ -581,36 +581,98 @@ test(function() {
   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>