Bug 1224186: Implement DOMTokenlist.replace r=baku,Ms2ger
authorEmilio Cobos Álvarez <ecoal95@gmail.com>
Thu, 28 Apr 2016 11:20:25 +0200
changeset 334248 030b17d4947a2dd3d01e6f74710e92a372d290bd
parent 334247 dc4b21ccd9c09735c23abe26850315ef3829eb26
child 334249 d928d2fde8c71f629774efb5771c2a3084af5d69
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, Ms2ger
bugs1224186
milestone49.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 1224186: Implement DOMTokenlist.replace r=baku,Ms2ger
dom/base/nsDOMTokenList.cpp
dom/base/nsDOMTokenList.h
dom/webidl/DOMTokenList.webidl
testing/web-platform/meta/dom/interfaces.html.ini
testing/web-platform/meta/dom/nodes/Element-classlist.html.ini
testing/web-platform/tests/dom/nodes/Element-classlist.html
--- a/dom/base/nsDOMTokenList.cpp
+++ b/dom/base/nsDOMTokenList.cpp
@@ -69,17 +69,17 @@ nsDOMTokenList::IndexedGetter(uint32_t a
     aFound = true;
     attr->AtomAt(aIndex)->ToString(aResult);
   } else {
     aFound = false;
   }
 }
 
 void
-nsDOMTokenList::SetValue(const nsAString& aValue, mozilla::ErrorResult& rv)
+nsDOMTokenList::SetValue(const nsAString& aValue, ErrorResult& rv)
 {
   if (!mElement) {
     return;
   }
 
   rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true);
 }
 
@@ -177,17 +177,17 @@ nsDOMTokenList::Add(const nsTArray<nsStr
     return;
   }
 
   const nsAttrValue* attr = GetParsedAttr();
   AddInternal(attr, aTokens);
 }
 
 void
-nsDOMTokenList::Add(const nsAString& aToken, mozilla::ErrorResult& aError)
+nsDOMTokenList::Add(const nsAString& aToken, ErrorResult& aError)
 {
   AutoTArray<nsString, 1> tokens;
   tokens.AppendElement(aToken);
   Add(tokens, aError);
 }
 
 void
 nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
@@ -263,17 +263,17 @@ nsDOMTokenList::Remove(const nsTArray<ns
   if (!attr) {
     return;
   }
 
   RemoveInternal(attr, aTokens);
 }
 
 void
-nsDOMTokenList::Remove(const nsAString& aToken, mozilla::ErrorResult& aError)
+nsDOMTokenList::Remove(const nsAString& aToken, ErrorResult& aError)
 {
   AutoTArray<nsString, 1> tokens;
   tokens.AppendElement(aToken);
   Remove(tokens, aError);
 }
 
 bool
 nsDOMTokenList::Toggle(const nsAString& aToken,
@@ -304,16 +304,53 @@ nsDOMTokenList::Toggle(const nsAString& 
       isPresent = true;
     }
   }
 
   return isPresent;
 }
 
 void
+nsDOMTokenList::Replace(const nsAString& aToken,
+                        const nsAString& aNewToken,
+                        ErrorResult& aError)
+{
+  // Doing this here instead of using `CheckToken` because if aToken had invalid
+  // characters, and aNewToken is empty, the returned error should be a
+  // SyntaxError, not an InvalidCharacterError.
+  if (aNewToken.IsEmpty()) {
+    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
+
+  aError = CheckToken(aToken);
+  if (aError.Failed()) {
+    return;
+  }
+
+  aError = CheckToken(aNewToken);
+  if (aError.Failed()) {
+    return;
+  }
+
+  const nsAttrValue* attr = GetParsedAttr();
+  if (!attr || !attr->Contains(aToken)) {
+    return;
+  }
+
+  AutoTArray<nsString, 1> tokens;
+
+  tokens.AppendElement(aToken);
+  RemoveInternal(attr, tokens);
+
+  tokens[0] = aNewToken;
+  AddInternal(attr, tokens);
+}
+
+void
 nsDOMTokenList::Stringify(nsAString& aResult)
 {
   if (!mElement) {
     aResult.Truncate();
     return;
   }
 
   mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult);
--- a/dom/base/nsDOMTokenList.h
+++ b/dom/base/nsDOMTokenList.h
@@ -58,20 +58,23 @@ public:
   void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aResult);
   bool Contains(const nsAString& aToken, mozilla::ErrorResult& aError);
   void Add(const nsAString& aToken, mozilla::ErrorResult& aError);
   void Add(const nsTArray<nsString>& aTokens,
            mozilla::ErrorResult& aError);
   void Remove(const nsAString& aToken, mozilla::ErrorResult& aError);
   void Remove(const nsTArray<nsString>& aTokens,
               mozilla::ErrorResult& aError);
+  void Replace(const nsAString& aToken,
+               const nsAString& aNewToken,
+               mozilla::ErrorResult& aError);
   bool Toggle(const nsAString& aToken,
               const mozilla::dom::Optional<bool>& force,
               mozilla::ErrorResult& aError);
-  
+
   void GetValue(nsAString& aResult) { Stringify(aResult); }
   void SetValue(const nsAString& aValue, mozilla::ErrorResult& rv);
   void Stringify(nsAString& aResult);
 
 protected:
   virtual ~nsDOMTokenList();
 
   nsresult CheckToken(const nsAString& aStr);
--- a/dom/webidl/DOMTokenList.webidl
+++ b/dom/webidl/DOMTokenList.webidl
@@ -15,13 +15,15 @@ interface DOMTokenList {
   getter DOMString? item(unsigned long index);
   [Throws]
   boolean contains(DOMString token);
   [Throws]
   void add(DOMString... tokens);
   [Throws]
   void remove(DOMString... tokens);
   [Throws]
+  void replace(DOMString token, DOMString newToken);
+  [Throws]
   boolean toggle(DOMString token, optional boolean force);
   [SetterThrows]
   attribute DOMString value;
   stringifier DOMString ();
 };
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -82,28 +82,19 @@
     expected: FAIL
 
   [Document interface: xmlDoc must inherit property "query" with the proper type (35)]
     expected: FAIL
 
   [Document interface: xmlDoc must inherit property "queryAll" with the proper type (36)]
     expected: FAIL
 
-  [DOMTokenList interface: operation replace(DOMString,DOMString)]
-    expected: FAIL
-
   [DOMTokenList interface: operation supports(DOMString)]
     expected: FAIL
 
-  [DOMTokenList interface: document.body.classList must inherit property "replace" with the proper type (6)]
-    expected: FAIL
-
-  [DOMTokenList interface: calling replace(DOMString,DOMString) on document.body.classList with too few arguments must throw TypeError]
-    expected: FAIL
-
   [DOMTokenList interface: document.body.classList must inherit property "supports" with the proper type (7)]
     expected: FAIL
 
   [DOMTokenList interface: calling supports(DOMString) on document.body.classList with too few arguments must throw TypeError]
     expected: FAIL
 
   [Element interface: element must inherit property "query" with the proper type (36)]
     expected: FAIL
--- a/testing/web-platform/meta/dom/nodes/Element-classlist.html.ini
+++ b/testing/web-platform/meta/dom/nodes/Element-classlist.html.ini
@@ -22,37 +22,19 @@
     expected: FAIL
 
   [classList.add should treat \\n as a space]
     expected: FAIL
 
   [classList.add should treat \\f as a space]
     expected: FAIL
 
-  [.replace with empty_string must throw a SYNTAX_ERR]
-    expected: FAIL
-
-  [.replace with string_with_spaces must throw a INVALID_CHARACTER_ERR]
-    expected: FAIL
-
-  [.replace with an already existing token]
-    expected: FAIL
-
-  [classList.replace replaces arguments passed, if they are present.]
-    expected: FAIL
-
   [classList.replace must replace existing tokens]
     expected: FAIL
 
-  [classList.replace must not break case-sensitive CSS selector matching]
-    expected: FAIL
-
-  [classList.replace must replace duplicated tokens]
-    expected: FAIL
-
   [classList.replace must collapse whitespace around replaced tokens]
     expected: FAIL
 
   [classList.replace must collapse whitespaces around each token]
     expected: FAIL
 
   [classList.replace must collapse whitespaces around each token and remove duplicates]
     expected: FAIL
--- a/testing/web-platform/tests/dom/nodes/Element-classlist.html
+++ b/testing/web-platform/tests/dom/nodes/Element-classlist.html
@@ -372,16 +372,30 @@ test(function () {
 test(function () {
   var realList = secondelem.classList;
   secondelem.classList = 'foo bar';
   assert_equals(secondelem.classList,realList);
   assert_equals(secondelem.classList.length,2);
   assert_equals(secondelem.classList[0],'foo');
   assert_equals(secondelem.classList[1],'bar');
 }, 'classList must have [PutForwards=value]');
+test(function () {
+  var foo = document.createElement('div');
+  foo.className = 'a';
+  foo.classList.replace('token1', 'token2');
+
+  assert_equals(foo.className, 'a');
+
+  foo.classList.replace('a', 'b');
+  assert_equals(foo.className, 'b');
+
+  assert_throws('SYNTAX_ERR', function () { foo.classList.replace('t with space', '') });
+  assert_throws('INVALID_CHARACTER_ERR', function () { foo.classList.replace('t with space', 'foo') });
+  assert_throws('SYNTAX_ERR', function () { foo.classList.replace('', 'foo') });
+}, 'classList.replace should work');
     </script>
   </head>
   <body>
 
     <div id="log"></div>
 
   </body>
 </html>