Bug 1222079. Fix the behavior of Object.getOwnPropertyNames for nsDOMAttributeMap. r=bzbarsky
authorSalah <meftahs@uni.coventry.ac.uk>
Mon, 07 Dec 2015 18:42:36 -0500
changeset 314606 87caa5721cd900ab113ba5991e0d7616b84bd076
parent 314605 ae19fb16cb392f557466d5eb527040adb959d2be
child 314607 803db6db0f680e066a2e3830cdd22959b07a0459
push id8237
push userjbeich@vfemail.net
push dateTue, 08 Dec 2015 12:55:06 +0000
reviewersbzbarsky
bugs1222079
milestone45.0a1
Bug 1222079. Fix the behavior of Object.getOwnPropertyNames for nsDOMAttributeMap. r=bzbarsky
dom/base/nsDOMAttributeMap.cpp
dom/base/nsDOMAttributeMap.h
testing/web-platform/tests/dom/nodes/attributes.html
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -207,17 +207,40 @@ nsDOMAttributeMap::NamedGetter(const nsA
 
   aFound = true;
   return GetAttribute(ni, false);
 }
 
 bool
 nsDOMAttributeMap::NameIsEnumerable(const nsAString& aName)
 {
-  return true;
+  return false;
+}
+
+void
+nsDOMAttributeMap::GetSupportedNames(unsigned aFlags,
+                                     nsTArray<nsString>& aNames)
+{
+  if (!(aFlags & JSITER_HIDDEN)) {
+    return;
+  }
+
+  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 (seenNonAtomName && aNames.Contains(qualifiedName)) {
+      continue;
+    }
+    aNames.AppendElement(qualifiedName);
+  }
 }
 
 Attr*
 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName)
 {
   bool dummy;
   return NamedGetter(aAttrName, dummy);
 }
--- a/dom/base/nsDOMAttributeMap.h
+++ b/dom/base/nsDOMAttributeMap.h
@@ -153,20 +153,18 @@ public:
   GetNamedItemNS(const nsAString& aNamespaceURI,
                  const nsAString& aLocalName);
   already_AddRefed<Attr>
   SetNamedItemNS(Attr& aNode, ErrorResult& aError);
   already_AddRefed<Attr>
   RemoveNamedItemNS(const nsAString& aNamespaceURI, const nsAString& aLocalName,
                     ErrorResult& aError);
 
-  void GetSupportedNames(unsigned, nsTArray<nsString>& aNames)
-  {
-    // No supported names we want to show up in iteration.
-  }
+  void
+  GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames);
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
   virtual ~nsDOMAttributeMap();
 
 private:
   nsCOMPtr<Element> mContent;
--- a/testing/web-platform/tests/dom/nodes/attributes.html
+++ b/testing/web-platform/tests/dom/nodes/attributes.html
@@ -488,9 +488,75 @@ test(function() {
   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(
+    (name) => 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"])
+}, "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"])
+}, "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"])
+}, "Own property correctness with two namespaced attributes with the same name-with-prefix");
 </script>