Bug 1354066 - Expand classList testing and port to wpt; r?jgraham draft
authorAryeh Gregor <ayg@aryeh.name>
Tue, 18 Apr 2017 21:25:14 +0300
changeset 565271 72d6aa4c3c117878a4615e14a027433d300ec732
parent 565030 c0ea5ed7f91a6be996a4a3c5ab25e2cdf6b4377e
child 624954 77ced73f1cf5931fd65b8a04be83c2f8f9cc56c8
push id54827
push userayg@aryeh.name
push dateWed, 19 Apr 2017 17:51:03 +0000
reviewersjgraham
bugs1354066
milestone55.0a1
Bug 1354066 - Expand classList testing and port to wpt; r?jgraham Because it tests some Gecko-specific things as well, I'm making two copies, as advised by bz and jgraham. One is to be submitted upstream, and a second one has local changes. This means most of the test is run twice. This overwrites the preexisting Element-classlist.html test upstream. I think I took the useful bits out of it (particularly replace() testing), but there are some things that it had that I didn't think were necessary, including: things that belong in idlharness; .className testing; testing .contains() and stringification and hasAttribute() and such after add/remove/etc. (instead of just testing getAttribute()); CSS class selector matching. MozReview-Commit-ID: JxPK7OyVLXa
dom/base/test/mochitest.ini
dom/base/test/test_classList.html
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/dom/nodes/Element-classlist.html.ini
testing/web-platform/mozilla/meta/MANIFEST.json
testing/web-platform/mozilla/meta/dom/classList.html.ini
testing/web-platform/mozilla/tests/dom/classList.html
testing/web-platform/tests/dom/nodes/Element-classlist.html
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -591,17 +591,16 @@ skip-if = toolkit == 'android'
 [test_bug1281963.html]
 [test_bug1295852.html]
 [test_bug1307730.html]
 [test_bug1308069.html]
 [test_bug1314032.html]
 [test_bug1318303.html]
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
-[test_classList.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_copyimage.html]
 subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
 [test_copypaste.html]
 subsuite = clipboard
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -162608,17 +162608,17 @@
    "e3daa6a1f0c1d87f0b02909affcf190bd29eadf9",
    "testharness"
   ],
   "dom/nodes/Element-children.html": [
    "e3fe31ea198922fe64fbf985ae99d1cd4512567a",
    "testharness"
   ],
   "dom/nodes/Element-classlist.html": [
-   "c6f2331b2479606064cc7d4428912ff275718390",
+   "46a9b0e29b876f32a47675eee1b7c8330dfa5625",
    "testharness"
   ],
   "dom/nodes/Element-closest.html": [
    "4171fb8b70948ba2617e05b118aaf5d9367e916f",
    "testharness"
   ],
   "dom/nodes/Element-firstElementChild-entity-xhtml.xhtml": [
    "d8babfc580fb43cfec2a2600be979ae769087c05",
--- a/testing/web-platform/meta/dom/nodes/Element-classlist.html.ini
+++ b/testing/web-platform/meta/dom/nodes/Element-classlist.html.ini
@@ -1,33 +1,377 @@
 [Element-classlist.html]
   type: testharness
-  [classList must be correct for an element that has classes]
+  [classList.length when set to "a a" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (HTML node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (HTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (HTML node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (HTML node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (HTML node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (HTML node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (HTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (HTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (XHTML node)]
     expected: FAIL
-    bug: 869788, https://github.com/whatwg/dom/issues/105
+
+  [classList.item() when set to "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (XHTML node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (XHTML node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (XHTML node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (XHTML node)]
+    expected: FAIL
 
-  [empty classList should return the empty string since the ordered set parser skip the whitespaces]
+  [classList.toggle("b") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XHTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (XHTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (MathML node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (MathML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (MathML node)]
     expected: FAIL
 
-  [classList.remove must collapse whitespaces around each token and remove duplicates]
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (MathML node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (MathML node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (MathML node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (MathML node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (MathML node)]
     expected: FAIL
-    bug: 869788, https://github.com/whatwg/dom/issues/105
+
+  [classList.toggle("d") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (MathML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (MathML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (XML node with null namespace)]
+    expected: FAIL
 
-  [classList.add must collapse whitespaces and remove duplicates when adding a token that already exists]
+  [classList.length when set to "a a b b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (XML node with null namespace)]
     expected: FAIL
-    bug: 869788, https://github.com/whatwg/dom/issues/105
+
+  [classList.add("c") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XML node with null namespace)]
+    expected: FAIL
 
-  [classList.add should treat \\t as a space]
+  [classList.add("a", "a") with attribute value "a b c " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (XML node with null namespace)]
     expected: FAIL
 
-  [classList.add should treat \\r as a space]
+  [classList.toggle("a") with attribute value "   \\f" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (XML node with null namespace)]
     expected: FAIL
 
-  [classList.add should treat \\n as a space]
+  [classList.length when set to "a a" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (foo node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (foo node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (foo node)]
     expected: FAIL
 
-  [classList.add should treat \\f as a space]
+  [classList.add("a") with attribute value "   \\f" (foo node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (foo node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (foo node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (foo node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (foo node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (foo node)]
     expected: FAIL
 
-  [classList.replace must collapse whitespaces around each token and remove duplicates]
+  [classList.toggle("a") with attribute value " " (foo node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (foo node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (foo node)]
     expected: FAIL
-    bug: 869788, https://github.com/whatwg/dom/issues/105
+
+  [classList.toggle("b") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (foo node)]
+    expected: FAIL
 
+  [classList.replace("b", "c") with attribute value "a b a" (foo node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -345,16 +345,22 @@
    ],
    "wasm/js/unwind.wast.js": [
     [
      {}
     ]
    ]
   },
   "testharness": {
+   "dom/classList.html": [
+    [
+     "/_mozilla/dom/classList.html",
+     {}
+    ]
+   ],
    "fetch/api/redirect/redirect-referrer.https.html": [
     [
      "/_mozilla/fetch/api/redirect/redirect-referrer.https.html",
      {}
     ]
    ],
    "focus/Range_collapse.html": [
     [
@@ -826,16 +832,20 @@
    ]
   }
  },
  "paths": {
   "./placeholder": [
    "74e16eb87ecdfeb2dfc28f36e0c73a584abdf9c2",
    "support"
   ],
+  "dom/classList.html": [
+   "37b0684fa9b869ab3a538cceac4fb5a825f3fff9",
+   "testharness"
+  ],
   "fetch/api/redirect/redirect-referrer-mixed-content.js": [
    "f9d7ec9cf9fa8c847e45664b05482e3f8c191385",
    "support"
   ],
   "fetch/api/redirect/redirect-referrer.https.html": [
    "99cbd16b78771f35e075e4012d8dbc5dce3209c0",
    "testharness"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/dom/classList.html.ini
@@ -0,0 +1,452 @@
+[classList.html]
+  type: testharness
+  [classList.length when set to "a a" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (HTML node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (HTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (HTML node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (HTML node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (HTML node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (HTML node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (HTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (HTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (HTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (XHTML node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XHTML node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (XHTML node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (XHTML node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (XHTML node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XHTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (XHTML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (XHTML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (XUL node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (XUL node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (XUL node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (XUL node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (XUL node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (XUL node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (XUL node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (XUL node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (XUL node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (XUL node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (XUL node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (XUL node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XUL node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (XUL node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (XUL node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (XUL node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (XUL node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (XUL node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (XUL node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (XUL node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (XUL node)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (XUL node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XUL node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (XUL node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (XUL node)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (MathML node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (MathML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (MathML node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (MathML node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (MathML node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (MathML node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (MathML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (MathML node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (MathML node)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (XML node with null namespace)]
+    expected: FAIL
+
+  [classList.length when set to "a a" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "a a a a a a" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "a a b b" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "a b c c b a a b c c" (foo node)]
+    expected: FAIL
+
+  [classList.length when set to "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.item() when set to "aa AA aa" (foo node)]
+    expected: FAIL
+
+  [classList.item() when set to "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "a a a  b" (foo node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value " " (foo node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   \\f" (foo node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.add("a") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.add("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (foo node)]
+    expected: FAIL
+
+  [classList.add("a", "a") with attribute value "a b c " (foo node)]
+    expected: FAIL
+
+  [classList.add() with attribute value "a b c a " (foo node)]
+    expected: FAIL
+
+  [classList.remove("AA") with attribute value "AA BB aa CC AA dd aa" (foo node)]
+    expected: FAIL
+
+  [classList.remove() with attribute value "a a" (foo node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " " (foo node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value "   \\f" (foo node)]
+    expected: FAIL
+
+  [classList.toggle("d") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.toggle("a") with attribute value " A A A " (foo node)]
+    expected: FAIL
+
+  [classList.toggle("b") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
+  [classList.toggle("c") with attribute value "\\t\\n\\f\\r a\\t\\n\\f\\r b\\t\\n\\f\\r " (foo node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "a b a" (foo node)]
+    expected: FAIL
+
+  [classList.replace("b", "c") with attribute value "   a  a b" (foo node)]
+    expected: FAIL
+
rename from dom/base/test/test_classList.html
rename to testing/web-platform/mozilla/tests/dom/classList.html
--- a/dom/base/test/test_classList.html
+++ b/testing/web-platform/mozilla/tests/dom/classList.html
@@ -1,315 +1,340 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=501257
--->
-<head>
-  <title>Test for the classList element attribute</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank" href="http://www.whatwg.org/specs/web-apps/current-work/#dom-classlist">classList DOM attribute</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-<script type="application/javascript">
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for the classList element attribute</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="content"></div>
+<script>
+// This should be the same as dom/nodes/Element-classlist.html in the upstream
+// tests!  We have a couple of bits changed, which are marked with comments
+// "LOCAL MODIFICATION".  Do not change this without changing the upstream test
+// as well!  Merging upstream changes here occasionally might also be nice.
 
-/** Test for Bug 501257 **/
-
+// BEGIN LOCAL MODIFICATION: XUL
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+// END LOCAL MODIFICATION
 const SVG_NS = "http://www.w3.org/2000/svg";
 const XHTML_NS = "http://www.w3.org/1999/xhtml"
 const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
 
+// BEGIN LOCAL MODIFICATION: The spec does not have the onAttrModified event
+// and does not want it, but we still support it.
 var gMutationEvents = [];
 
 function onAttrModified(event) {
-  is(event.attrName, "class", "mutation on unexpected attribute");
+  assert_equals(event.attrName, "class", "mutation on unexpected attribute");
 
   gMutationEvents.push({
     attrChange: event.attrChange,
     prevValue: event.prevValue,
     newValue: event.newValue,
   });
 }
+// END LOCAL MODIFICATION
 
-function checkModification(e, funcName, args, expectedRes, before, after, expectedException) {
+function setClass(e, newVal) {
+  if (newVal === null) {
+    e.removeAttribute("class");
+  } else {
+    e.setAttribute("class", newVal);
+  }
+}
+
+function checkModification(e, funcName, args, expectedRes, before, after,
+                           expectedException, desc) {
   if (!Array.isArray(args)) {
     args = [args];
   }
 
-  var shouldThrow = typeof(expectedException) === "string";
-  if (shouldThrow) {
-    // If an exception is thrown, the class attribute shouldn't change.
-    after = before;
-  }
-  if (before === null)
-    e.removeAttribute("class");
-  else
-    e.setAttribute("class", before);
+  test(function() {
+    var shouldThrow = typeof(expectedException) === "string";
+    if (shouldThrow) {
+      // If an exception is thrown, the class attribute shouldn't change.
+      after = before;
+    }
+    setClass(e, before);
 
-  var contextMsg = "(checkModification: funcName=" + funcName + ",args=" +
-                   JSON.stringify(args) + ",expectedRes=" + expectedRes +
-                   ",before=" + before + ",after=" + after + ")";
+    // BEGIN LOCAL MODIFICATION
+    gMutationEvents = [];
+    e.addEventListener("DOMAttrModified", onAttrModified);
+    // END LOCAL MODIFICATION
+    if (shouldThrow) {
+      assert_throws(expectedException, function() {
+        var list = e.classList;
+        var res = list[funcName].apply(list, args);
+      });
+    } else {
+      var list = e.classList;
+      var res = list[funcName].apply(list, args);
+    }
+    // BEGIN LOCAL MODIFICATION
+    e.removeEventListener("DOMAttrModified", onAttrModified);
+    // END LOCAL MODIFICATION
+    if (!shouldThrow) {
+      assert_equals(res, expectedRes, "wrong return value");
+    }
 
-  gMutationEvents = [];
-  e.addEventListener("DOMAttrModified", onAttrModified);
-  try {
-    var list = e.classList;
-    var res = list[funcName].apply(list, args);
-    if (shouldThrow)
-      ok(false, "classList modification didn't throw " + contextMsg);
-  } catch (e) {
-    if (!shouldThrow)
-      ok(false, "classList modification threw an exception " + contextMsg);
-    is(e.name, expectedException, "wrong exception thrown " + contextMsg);
-  }
-  e.removeEventListener("DOMAttrModified", onAttrModified);
-  if (expectedRes !== null)
-    is(res, expectedRes, "wrong return value from " + funcName +
-       " " + contextMsg);
+    var expectedAfter = after;
+    // BEGIN LOCAL MODIFICATION: XUL returns an empty string when getting a
+    // nonexistent class attribute.
+    if (e.namespaceURI == XUL_NS && expectedAfter === null)
+      expectedAfter = "";
+    // END LOCAL MODIFICATION
 
-  var expectedAfter = after;
-  // XUL returns an empty string when getting a nonexistent class attribute.
-  if (e.namespaceURI == XUL_NS && expectedAfter === null)
-    expectedAfter = "";
-
-  is(e.getAttribute("class"), expectedAfter, "wrong class after modification " +
-     contextMsg);
-  var expectedMutation = before != after;
-  is(gMutationEvents.length, expectedMutation ? 1 : 0,
-     "unexpected mutation event count " + contextMsg);
-  if (expectedMutation && gMutationEvents.length) {
-    is(gMutationEvents[0].attrChange,
-       before == null ? MutationEvent.ADDITION : MutationEvent.MODIFICATION,
-       "wrong type of attribute change " + contextMsg);
-    // If there wasn't any previous attribute, prevValue will return an empty
-    // string.
-    var expectedPrevValue = before === null ? "" : before;
-    is(gMutationEvents[0].prevValue, expectedPrevValue,
-       "wrong previous value " + contextMsg);
-    is(gMutationEvents[0].newValue, after, "wrong new value " + contextMsg);
-  }
+    assert_equals(e.getAttribute("class"), expectedAfter,
+                  "wrong class after modification");
+    // BEGIN LOCAL MODIFICATION
+    var expectedMutation = before != after;
+    assert_equals(gMutationEvents.length, expectedMutation ? 1 : 0,
+                  "unexpected mutation event count");
+    if (expectedMutation && gMutationEvents.length) {
+      assert_equals(gMutationEvents[0].attrChange,
+                    before == null ? MutationEvent.ADDITION
+                                   : MutationEvent.MODIFICATION,
+                    "wrong type of attribute change");
+      // If there wasn't any previous attribute, prevValue will return an empty
+      // string.
+      var expectedPrevValue = before === null ? "" : before;
+      assert_equals(gMutationEvents[0].prevValue, expectedPrevValue,
+                    "wrong previous value");
+      assert_equals(gMutationEvents[0].newValue, after, "wrong new value");
+    }
+    // END LOCAL MODIFICATION
+  }, "classList." + funcName + "(" + args.map(format_value).join(", ") +
+  ") with attribute value " + format_value(before) + desc);
 }
 
 function assignToClassListStrict(e) {
   "use strict";
-  try {
-    e.classList = "foo";
-    ok(true, "assigning to classList didn't throw");
-    e.removeAttribute("class");
-  } catch (e) {
-    ok(false, "assigning to classList threw");
-  }
+  e.classList = "foo";
+  e.removeAttribute("class");
 }
 
 function assignToClassList(e) {
-  try {
-    var expect = e.classList;
-    e.classList = "foo";
-    ok(true, "assigning to classList didn't throw");
-    is(e.classList, expect, "classList should be unchanged after assignment");
-    e.removeAttribute("class");
-  } catch (e) {
-    ok(false, "assigning to classList threw");
-  }
+  var expect = e.classList;
+  e.classList = "foo";
+  assert_equals(e.classList, expect,
+                "classList should be unchanged after assignment");
+  e.removeAttribute("class");
 }
 
-function testClassList(e) {
+function testClassList(e, desc) {
 
-  // basic tests
+  // assignment
 
-  isnot(e.classList, undefined, "no classList attribute");
-  is(typeof(e.classList.contains), "function",
-     "no classList.contains function");
-  is(typeof(e.classList.add), "function", "no classList.add function");
-  is(typeof(e.classList.remove), "function", "no classList.remove function");
-  is(typeof(e.classList.toggle), "function", "no classList.toggle function");
+  test(function() {
+    assignToClassListStrict(e);
+    assignToClassList(e);
+  }, "Assigning to classList" + desc);
 
-  assignToClassListStrict(e);
-  assignToClassList(e);
+  // supports
+  test(function() {
+    assert_throws(TypeError(), function() {
+      e.classList.supports("a");
+    })
+  }, ".supports() must throw TypeError" + desc);
 
   // length attribute
 
-  is(e.classList.length, 0, "wrong classList.length value");
-  e.setAttribute("class", "");
-  is(e.classList.length, 0, "wrong classList.length value");
-  e.setAttribute("class", "   \t  \f");
-  is(e.classList.length, 0, "wrong classList.length value");
-
-  e.setAttribute("class", "a");
-  is(e.classList.length, 1, "wrong classList.length value");
-  e.setAttribute("class", "a A");
-  is(e.classList.length, 2, "wrong classList.length value");
-  e.setAttribute("class", "\r\na\t\f");
-  is(e.classList.length, 1, "wrong classList.length value");
+  function checkLength(value, length) {
+    test(function() {
+      setClass(e, value);
+      assert_equals(e.classList.length, length);
+    }, "classList.length when " +
+    (value === null ? "removed" : "set to " + format_value(value)) + desc);
+  }
 
-  e.setAttribute("class", "a a");
-  is(e.classList.length, 2, "wrong classList.length value");
-
-  e.setAttribute("class", "a a a a a a");
-  is(e.classList.length, 6, "wrong classList.length value");
-
-  e.setAttribute("class", "a a b b");
-  is(e.classList.length, 4, "wrong classList.length value");
-
-  e.setAttribute("class", "a A B b");
-  is(e.classList.length, 4, "wrong classList.length value");
-
-  e.setAttribute("class", "a b c c b a a b c c");
-  is(e.classList.length, 10, "wrong classList.length value");
+  checkLength(null, 0);
+  checkLength("", 0);
+  checkLength("   \t  \f", 0);
+  checkLength("a", 1);
+  checkLength("a A", 2);
+  checkLength("\r\na\t\f", 1);
+  checkLength("a a", 1);
+  checkLength("a a a a a a", 1);
+  checkLength("a a b b", 2);
+  checkLength("a A B b", 4);
+  checkLength("a b c c b a a b c c", 3);
+  checkLength("   a  a b", 2);
+  checkLength("a\tb\nc\fd\re f", 6);
 
   // [Stringifies]
 
-  ok(DOMTokenList.prototype.hasOwnProperty("toString"),
-     "Should have own toString on DOMTokenList")
+  function checkStringifier(value, expected) {
+    test(function() {
+      setClass(e, value);
+      assert_equals(e.classList.toString(), expected);
+    }, "classList.toString() when " +
+    (value === null ? "removed" : "set to " + format_value(value)) + desc);
+  }
 
-  e.removeAttribute("class");
-  is(e.classList.toString(), "", "wrong classList.toString() value");
-  is(e.classList + "", "", "wrong classList string conversion value");
-
-  e.setAttribute("class", "foo");
-  is(e.classList.toString(), "foo", "wrong classList.toString() value");
-  is(e.classList + "", "foo", "wrong classList string conversion value");
+  checkStringifier(null, "");
+  checkStringifier("foo", "foo");
+  checkStringifier("   a  a b", "   a  a b");
 
   // item() method
 
-  e.setAttribute("class", "a");
-  is(e.classList.item(-1), null, "wrong classList.item() result");
-  is(e.classList[-1], undefined, "wrong classList[] result");
-  is(e.classList.item(0), "a", "wrong classList.item() result");
-  is(e.classList[0], "a", "wrong classList[] result");
-  is(e.classList.item(1), null, "wrong classList.item() result");
-  is(e.classList[1], undefined, "wrong classList[] result");
+  function checkItems(attributeValue, expectedValues) {
+    function checkItemFunction(index, expected) {
+      assert_equals(e.classList.item(index), expected,
+                    "classList.item(" + index + ")");
+    }
+
+    function checkItemArray(index, expected) {
+      assert_equals(e.classList[index], expected, "classList[" + index + "]");
+    }
+
+    test(function() {
+      setClass(e, attributeValue);
+
+      checkItemFunction(-1, null);
+      checkItemArray(-1, undefined);
 
-  e.setAttribute("class", "aa AA aa");
-  is(e.classList.item(-1), null, "wrong classList.item() result");
-  is(e.classList[-1], undefined, "wrong classList[] result");
-  is(e.classList.item(0), "aa", "wrong classList.item() result");
-  is(e.classList[0], "aa", "wrong classList[] result");
-  is(e.classList.item(1), "AA", "wrong classList.item() result");
-  is(e.classList[1], "AA", "wrong classList[] result");
-  is(e.classList.item(2), "aa", "wrong classList.item() result");
-  is(e.classList[2], "aa", "wrong classList[] result");
-  is(e.classList.item(3), null, "wrong classList.item() result");
-  is(e.classList[3], undefined, "wrong classList[] result");
-  is(e.classList.item(0xffffffff), null, "wrong classList.item() result");
-  is(e.classList[0xffffffff], undefined, "wrong classList[] result");
-  is(e.classList.item(0xfffffffe), null, "wrong classList.item() result");
-  is(e.classList[0xffffffe], undefined, "wrong classList[] result");
+      var i = 0;
+      while (i < expectedValues.length) {
+        checkItemFunction(i, expectedValues[i]);
+        checkItemArray(i, expectedValues[i]);
+        i++;
+      }
+
+      checkItemFunction(i, null);
+      checkItemArray(i, undefined);
 
-  e.setAttribute("class", "a b");
-  is(e.classList.item(-1), null, "wrong classList.item() result");
-  is(e.classList[-1], undefined, "wrong classList[] result");
-  is(e.classList.item(0), "a", "wrong classList.item() result");
-  is(e.classList[0], "a", "wrong classList[] result");
-  is(e.classList.item(1), "b", "wrong classList.item() result");
-  is(e.classList[1], "b", "wrong classList[] result");
-  is(e.classList.item(2), null, "wrong classList.item() result");
-  is(e.classList[2], undefined, "wrong classList[] result");
+      checkItemFunction(0xffffffff, null);
+      checkItemArray(0xffffffff, undefined);
+
+      checkItemFunction(0xfffffffe, null);
+      checkItemArray(0xfffffffe, undefined);
+    }, "classList.item() when set to " + format_value(attributeValue) + desc);
+  }
+
+  checkItems(null, []);
+  checkItems("a", ["a"]);
+  checkItems("aa AA aa", ["aa", "AA"]);
+  checkItems("a b", ["a", "b"]);
+  checkItems("   a  a b", ["a", "b"]);
+  checkItems("\t\n\f\r a\t\n\f\r b\t\n\f\r ", ["a", "b"]);
 
   // contains() method
 
-  e.removeAttribute("class");
-  is(e.classList.contains("a"), false, "wrong classList.contains() result");
-  try {
-    e.classList.contains("");
-    ok(true, "classList.contains(empty_string) didn't throw");
-  } catch (e) {
-    ok(false, "classList.contains(empty_string) threw");
-  }
-  try {
-    e.classList.contains("  ");
-    ok(true, "classList.contains(string_with_spaces) didn't throw");
-  } catch (e) {
-    ok(false, "classList.contains(string_with_spaces) threw");
-  }
-  try {
-    e.classList.contains("aa ");
-    ok(true, "classList.contains(string_with_spaces) didn't throw");
-  } catch (e) {
-    ok(false, "classList.contains(string_with_spaces) threw");
+  function checkContains(attributeValue, args, expectedRes) {
+    if (!Array.isArray(expectedRes)) {
+      expectedRes = Array(args.length).fill(expectedRes);
+    }
+    setClass(e, attributeValue);
+    for (var i = 0; i < args.length; i++) {
+      test(function() {
+        assert_equals(e.classList.contains(args[i]), expectedRes[i],
+                      "classList.contains(\"" + args[i] + "\")");
+      }, "classList.contains(" + format_value(args[i]) + ") when set to " +
+      format_value(attributeValue) + desc);
+    }
   }
 
-  e.setAttribute("class", "");
-  is(e.classList.contains("a"), false, "wrong classList.contains() result");
+  checkContains(null, ["a", "", "  "], false);
+  checkContains("", ["a"], false);
 
-  e.setAttribute("class", "a");
-  is(e.classList.contains("a"), true, "wrong classList.contains() result");
-  is(e.classList.contains("aa"), false, "wrong classList.contains() result");
-  is(e.classList.contains("b"), false, "wrong classList.contains() result");
-
-  e.setAttribute("class", "aa AA");
-  is(e.classList.contains("aa"), true, "wrong classList.contains() result");
-  is(e.classList.contains("AA"), true, "wrong classList.contains() result");
-  is(e.classList.contains("aA"), false, "wrong classList.contains() result");
+  checkContains("a", ["a"], true);
+  checkContains("a", ["aa", "b", "A", "a.", "a)",, "a'", 'a"', "a$", "a~",
+                      "a?", "a\\"], false);
 
-  e.setAttribute("class", "a a a");
-  is(e.classList.contains("a"), true, "wrong classList.contains() result");
-  is(e.classList.contains("aa"), false, "wrong classList.contains() result");
-  is(e.classList.contains("b"), false, "wrong classList.contains() result");
+  // All "ASCII whitespace" per spec, before and after
+  checkContains("a", ["a\t", "\ta", "a\n", "\na", "a\f", "\fa", "a\r", "\ra",
+                      "a ", " a"], false);
 
-  e.setAttribute("class", "a b c");
-  is(e.classList.contains("a"), true, "wrong classList.contains() result");
-  is(e.classList.contains("b"), true, "wrong classList.contains() result");
+  checkContains("aa AA", ["aa", "AA", "aA"], [true, true, false]);
+  checkContains("a a a", ["a", "aa", "b"], [true, false, false]);
+  checkContains("a b c", ["a", "b"], true);
 
-  // Test for bug 530171
-  e.setAttribute("class", "null undefined");
-  is(e.classList.contains(null), true, "wrong classList.contains() result");
-  is(e.classList.contains(undefined), true, "wrong classList.contains() result");
+  checkContains("null undefined", [null, undefined], true);
+  checkContains("\t\n\f\r a\t\n\f\r b\t\n\f\r ", ["a", "b"], true);
 
   // add() method
 
   function checkAdd(before, argument, after, expectedException) {
-    checkModification(e, "add", argument, null, before, after, expectedException);
+    checkModification(e, "add", argument, undefined, before, after,
+                      expectedException, desc);
+    // Also check force toggle
+    // XXX https://github.com/whatwg/dom/issues/443
+    //if (!Array.isArray(argument)) {
+    //  checkModification(e, "toggle", [argument, true], true, before, after,
+    //                    expectedException);
+    //}
   }
 
   checkAdd(null, "", null, "SyntaxError");
   checkAdd(null, ["a", ""], null, "SyntaxError");
   checkAdd(null, " ", null, "InvalidCharacterError");
+  checkAdd(null, "\ta", null, "InvalidCharacterError");
+  checkAdd(null, "a\t", null, "InvalidCharacterError");
+  checkAdd(null, "\na", null, "InvalidCharacterError");
+  checkAdd(null, "a\n", null, "InvalidCharacterError");
+  checkAdd(null, "\fa", null, "InvalidCharacterError");
+  checkAdd(null, "a\f", null, "InvalidCharacterError");
+  checkAdd(null, "\ra", null, "InvalidCharacterError");
+  checkAdd(null, "a\r", null, "InvalidCharacterError");
+  checkAdd(null, " a", null, "InvalidCharacterError");
+  checkAdd(null, "a ", null, "InvalidCharacterError");
   checkAdd(null, ["a", " "], null, "InvalidCharacterError");
   checkAdd(null, ["a", "aa "], null, "InvalidCharacterError");
 
   checkAdd("a", "a", "a");
   checkAdd("aa", "AA", "aa AA");
   checkAdd("a b c", "a", "a b c");
-  checkAdd("a a a  b", "a", "a a a  b");
+  checkAdd("a a a  b", "a", "a b");
   checkAdd(null, "a", "a");
   checkAdd("", "a", "a");
-  checkAdd(" ", "a", " a");
-  checkAdd("   \f", "a", "   \fa");
+  checkAdd(" ", "a", "a");
+  checkAdd("   \f", "a", "a");
   checkAdd("a", "b", "a b");
   checkAdd("a b c", "d", "a b c d");
   checkAdd("a b c ", "d", "a b c d");
+  checkAdd("   a  a b", "c", "a b c");
+  checkAdd("   a  a b", "a", "a b");
+  checkAdd("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "c", "a b c");
 
   // multiple add
   checkAdd("a b c ", ["d", "e"], "a b c d e");
-  checkAdd("a b c ", ["a", "a"], "a b c ");
+  checkAdd("a b c ", ["a", "a"], "a b c");
   checkAdd("a b c ", ["d", "d"], "a b c d");
-  checkAdd("a b c ", [], "a b c ");
+  checkAdd("a b c a ", [], "a b c");
   checkAdd(null, ["a", "b"], "a b");
   checkAdd("", ["a", "b"], "a b");
 
-  // Test for bug 530171
   checkAdd(null, null, "null");
   checkAdd(null, undefined, "undefined");
 
   // remove() method
 
   function checkRemove(before, argument, after, expectedException) {
-    checkModification(e, "remove", argument, null, before, after, expectedException);
+    checkModification(e, "remove", argument, undefined, before, after,
+                      expectedException, desc);
+    // Also check force toggle
+    // XXX https://github.com/whatwg/dom/issues/443
+    //if (!Array.isArray(argument)) {
+    //  checkModification(e, "toggle", [argument, false], false, before, after,
+    //                    expectedException);
+    //}
   }
 
   checkRemove(null, "", null, "SyntaxError");
   checkRemove(null, " ", null, "InvalidCharacterError");
-  checkRemove(null, "aa ", null, "InvalidCharacterError");
+  checkRemove("\ta", "\ta", "\ta", "InvalidCharacterError");
+  checkRemove("a\t", "a\t", "a\t", "InvalidCharacterError");
+  checkRemove("\na", "\na", "\na", "InvalidCharacterError");
+  checkRemove("a\n", "a\n", "a\n", "InvalidCharacterError");
+  checkRemove("\fa", "\fa", "\fa", "InvalidCharacterError");
+  checkRemove("a\f", "a\f", "a\f", "InvalidCharacterError");
+  checkRemove("\ra", "\ra", "\ra", "InvalidCharacterError");
+  checkRemove("a\r", "a\r", "a\r", "InvalidCharacterError");
+  checkRemove(" a", " a", " a", "InvalidCharacterError");
+  checkRemove("a ", "a ", "a ", "InvalidCharacterError");
+  checkRemove("aa ", "aa ", null, "InvalidCharacterError");
 
   checkRemove(null, "a", null);
   checkRemove("", "a", "");
   checkRemove("a b  c", "d", "a b c");
   checkRemove("a b  c", "A", "a b c");
   checkRemove(" a a a ", "a", "");
   checkRemove("a  b", "a", "b");
   checkRemove("a  b  ", "a", "b");
@@ -322,105 +347,174 @@ function testClassList(e) {
   checkRemove(" a  b  c ", "b", "a c");
   checkRemove("a b b b c", "b", "a c");
 
   checkRemove("a  b  c", "c", "a b");
   checkRemove(" a  b  c ", "c", "a b");
   checkRemove("a b c c c", "c", "a b");
 
   checkRemove("a b a c a d a", "a", "b c d");
-  checkRemove("AA BB aa CC AA dd aa", "AA", "BB aa CC dd aa");
+  checkRemove("AA BB aa CC AA dd aa", "AA", "BB aa CC dd");
 
   checkRemove("\ra\na\ta\f", "a", "");
+  checkRemove("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", "b");
 
   // multiple remove
   checkRemove("a b c ", ["d", "e"], "a b c");
   checkRemove("a b c ", ["a", "b"], "c");
   checkRemove("a b c ", ["a", "c"], "b");
   checkRemove("a b c ", ["a", "a"], "b c");
   checkRemove("a b c ", ["d", "d"], "a b c");
   checkRemove("a b c ", [], "a b c");
   checkRemove(null, ["a", "b"], null);
   checkRemove("", ["a", "b"], "");
+  checkRemove("a a", [], "a");
 
-  // Test for bug 530171
   checkRemove("null", null, "");
   checkRemove("undefined", undefined, "");
 
   // toggle() method
 
   function checkToggle(before, argument, expectedRes, after, expectedException) {
-    checkModification(e, "toggle", argument, expectedRes, before, after, expectedException);
+    checkModification(e, "toggle", argument, expectedRes, before, after,
+                      expectedException, desc);
   }
 
   checkToggle(null, "", null, null, "SyntaxError");
   checkToggle(null, "aa ", null, null, "InvalidCharacterError");
 
   checkToggle(null, "a", true, "a");
   checkToggle("", "a", true, "a");
-  checkToggle(" ", "a", true, " a");
-  checkToggle("   \f", "a", true, "   \fa");
+  checkToggle(" ", "a", true, "a");
+  checkToggle("   \f", "a", true, "a");
   checkToggle("a", "b", true, "a b");
   checkToggle("a", "A", true, "a A");
   checkToggle("a b c", "d", true, "a b c d");
-  checkToggle("a b c", "d", true, "a b c d");
+  checkToggle("   a  a b", "d", true, "a b d");
 
   checkToggle("a", "a", false, "");
   checkToggle(" a a a ", "a", false, "");
-  checkToggle(" A A A ", "a", true, " A A A a");
+  checkToggle(" A A A ", "a", true, "A a");
   checkToggle(" a b c ", "b", false, "a c");
   checkToggle(" a b c b b", "b", false, "a c");
   checkToggle(" a b  c  ", "c", false, "a b");
   checkToggle(" a b c ", "a", false, "b c");
+  checkToggle("   a  a b", "b", false, "a");
+  checkToggle("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", false, "b");
+  checkToggle("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "c", true, "a b c");
 
-  // Test for bug 530171
   checkToggle("null", null, false, "");
   checkToggle("", null, true, "null");
   checkToggle("undefined", undefined, false, "");
   checkToggle("", undefined, true, "undefined");
 
 
   // tests for the force argument handling
+  // XXX Remove these if https://github.com/whatwg/dom/issues/443 is fixed
 
   function checkForceToggle(before, argument, force, expectedRes, after, expectedException) {
-    checkModification(e, "toggle", [argument, force], expectedRes, before, after, expectedException);
+    checkModification(e, "toggle", [argument, force], expectedRes, before,
+                      after, expectedException, desc);
   }
 
   checkForceToggle("", "a", true, true, "a");
   checkForceToggle("a", "a", true, true, "a");
   checkForceToggle("a", "b", true, true, "a b");
   checkForceToggle("a b", "b", true, true, "a b");
   checkForceToggle("", "a", false, false, "");
   checkForceToggle("a", "a", false, false, "");
   checkForceToggle("a", "b", false, false, "a");
   checkForceToggle("a b", "b", false, false, "a");
+
+
+  // replace() method
+  function checkReplace(before, token, newToken, after, expectedException) {
+    checkModification(e, "replace", [token, newToken], undefined, before,
+                      after, expectedException, desc);
+  }
+
+  checkReplace(null, "", "a", null, "SyntaxError");
+  checkReplace(null, "", " ", null, "SyntaxError");
+  checkReplace(null, " ", "a", null, "InvalidCharacterError");
+  checkReplace(null, "\ta", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\t", "b", null, "InvalidCharacterError");
+  checkReplace(null, "\na", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\n", "b", null, "InvalidCharacterError");
+  checkReplace(null, "\fa", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\f", "b", null, "InvalidCharacterError");
+  checkReplace(null, "\ra", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\r", "b", null, "InvalidCharacterError");
+  checkReplace(null, " a", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a ", "b", null, "InvalidCharacterError");
+
+  checkReplace(null, "a", "", null, "SyntaxError");
+  checkReplace(null, " ", "", null, "SyntaxError");
+  checkReplace(null, "a", " ", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\ta", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\t", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\na", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\n", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\fa", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\f", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\ra", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\r", null, "InvalidCharacterError");
+  checkReplace(null, "b", " a", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a ", null, "InvalidCharacterError");
+
+  checkReplace("a", "a", "a", "a");
+  checkReplace("a", "a", "b", "b");
+  checkReplace("a", "A", "b", "a");
+  checkReplace("a b", "b", "A", "a A");
+  checkReplace("a b c", "d", "e", "a b c");
+  // https://github.com/whatwg/dom/issues/443
+  checkReplace("a a a  b", "a", "a", "a b");
+  checkReplace("a a a  b", "c", "d", "a a a  b");
+  checkReplace(null, "a", "b", null);
+  checkReplace("", "a", "b", "");
+  checkReplace(" ", "a", "b", " ");
+  checkReplace(" a  \f", "a", "b", "b");
+  checkReplace("a b c", "b", "d", "a d c");
+  // https://github.com/whatwg/dom/issues/442
+  // Implementations agree on the first one here, so I test it, but disagree on
+  // the second, so no test until the spec decides what to say.
+  checkReplace("a b c", "c", "a", "a b");
+  //checkReplace("c b a", "c", "a", ???);
+  checkReplace("a b a", "a", "c", "c b");
+  checkReplace("a b a", "b", "c", "a c");
+  checkReplace("   a  a b", "a", "c", "c b");
+  checkReplace("   a  a b", "b", "c", "a c");
+  checkReplace("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", "c", "c b");
+  checkReplace("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "b", "c", "a c");
+
+  checkReplace("a null", null, "b", "a b");
+  checkReplace("a b", "a", null, "null b");
+  checkReplace("a undefined", undefined, "b", "a b");
+  checkReplace("a b", "a", undefined, "undefined b");
 }
 
 var content = document.getElementById("content");
 
 var htmlNode = document.createElement("div");
 content.appendChild(htmlNode);
-testClassList(htmlNode);
+testClassList(htmlNode, " (HTML node)");
 
 var xhtmlNode = document.createElementNS(XHTML_NS, "div");
 content.appendChild(xhtmlNode);
-testClassList(xhtmlNode);
+testClassList(xhtmlNode, " (XHTML node)");
 
+// LOCAL MODIFICATION
 var xulNode = document.createElementNS(XUL_NS, "box");
 content.appendChild(xulNode);
-testClassList(xulNode);
+testClassList(xulNode, " (XUL node)");
+// END LOCAL MODIFICATION
 
 var mathMLNode = document.createElementNS(MATHML_NS, "math");
 content.appendChild(mathMLNode);
-testClassList(mathMLNode);
+testClassList(mathMLNode, " (MathML node)");
 
 var xmlNode = document.createElementNS(null, "foo");
 content.appendChild(xmlNode);
-testClassList(xmlNode);
+testClassList(xmlNode, " (XML node with null namespace)");
 
 var fooNode = document.createElementNS("http://example.org/foo", "foo");
 content.appendChild(fooNode);
-testClassList(fooNode);
-
+testClassList(fooNode, " (foo node)");
 </script>
-</pre>
-</body>
-</html>
--- a/testing/web-platform/tests/dom/nodes/Element-classlist.html
+++ b/testing/web-platform/tests/dom/nodes/Element-classlist.html
@@ -1,407 +1,462 @@
 <!doctype html>
-<html>
-  <head class="test test">
-    <title class=" ">Element.classList in case-sensitive documents</title>
-    <link rel="help" href="https://dom.spec.whatwg.org/#concept-class">
-    <script type="text/javascript" src="/resources/testharness.js"></script>
-    <script type="text/javascript" src="/resources/testharnessreport.js"></script>
-    <style type="text/css">
-.foo { font-style: italic; }
-    </style>
-    <script type="text/javascript">
-var elem = document.getElementsByTagName('title')[0], secondelem = document.getElementsByTagName('head')[0];
-test(function () {
-  assert_equals( typeof elem.classList, 'object', 'critical test; ignore any results after this' );
-}, 'Element.classList must exist as an object');
-test(function () {
-  assert_equals( typeof document.documentElement.classList, 'object' );
-}, 'Element.classList must exist as an object even if the element has no class attribute');
-test(function () {
-  assert_true( !!window.DOMTokenList );
-}, 'DOMTokenList should be exposed for prototyping');
-test(function () {
-  DOMTokenList.prototype.customProperty = true;
-  assert_true( elem.classList.customProperty );
-}, 'prototyping DOMTokenList should work');
-test(function () {
-  assert_true( elem.classList instanceof window.DOMTokenList );
-  assert_equals( elem.classList.constructor, window.DOMTokenList );
-}, 'Element.classList must implement DOMTokenList');
-test(function () {
-  assert_not_equals( getComputedStyle(elem,null).fontStyle, 'italic', 'critical test; required by the testsuite' );
-}, 'CSS .foo selectors must not match elements without any class');
-test(function () {
-  assert_equals( secondelem.classList.length, 1, 'duplicates in initial string should be removed per https://dom.spec.whatwg.org/#concept-class' );
-  assert_equals( secondelem.classList.item(0), 'test' );
-  assert_true( secondelem.classList.contains('test') );
-}, 'classList must be correct for an element that has classes');
-test(function () {
-  assert_equals( elem.classList.length, 0 );
-}, 'classList.length must be 0 for an element that has no classes');
-test(function () {
-  assert_false( elem.classList.contains('foo') );
-}, 'classList must not contain an undefined class');
-test(function () {
-  assert_equals( elem.classList.item(0), null );
-}, 'classList.item() must return null for out-of-range index');
-test(function () {
-  assert_equals( elem.classList.item(-1), null );
-}, 'classList.item() must return null for negative index');
-test(function () {
-  /* the normative part of the spec states that:
-  "unless tokens is empty, in which case there are no supported property indices"
-  ...
-  "The term[...] supported property indices [is] used as defined in the WebIDL specification."
-  WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */
-  assert_equals( elem.classList[0], undefined );
-}, 'classList[index] must be undefined for out-of-range index');
-test(function () {
-  assert_equals( elem.classList[-1], undefined );
-}, 'classList[index] must be undefined for negative index');
-test(function () {
-  assert_equals( elem.className, ' ' );
-}, 'className should contain initial markup whitespace');
-test(function () {
-  assert_equals( elem.classList + '', ' ', 'implicit' );
-  assert_equals( elem.classList.toString(), ' ', 'explicit' );
-}, 'classList should contain initial markup whitespace');
-test(function () {
-  assert_false( elem.classList.contains('') );
-}, '.contains(empty_string) must return false');
-test(function () {
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.add(''); } );
-}, '.add(empty_string) must throw a SYNTAX_ERR');
-test(function () {
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.remove(''); } );
-}, '.remove(empty_string) must throw a SYNTAX_ERR');
-test(function () {
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.toggle(''); } );
-}, '.toggle(empty_string) must throw a SYNTAX_ERR');
-test(function () {
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('', 'foo'); } );
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('foo', ''); } );
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('', 'foo bar'); } );
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('foo bar', ''); } );
-  assert_throws( 'SYNTAX_ERR', function () { elem.classList.replace('', ''); } );
-}, '.replace with empty_string must throw a SYNTAX_ERR');
-test(function () {
-  assert_false( elem.classList.contains('a b') );
-}, '.contains(string_with_spaces) must return false');
-test(function () {
-  assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.add('a b'); } );
-}, '.add(string_with_spaces) must throw an INVALID_CHARACTER_ERR');
-test(function () {
-  assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.remove('a b'); } );
-}, '.remove(string_with_spaces) must throw an INVALID_CHARACTER_ERR');
-test(function () {
-  assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.toggle('a b'); } );
-}, '.toggle(string_with_spaces) must throw an INVALID_CHARACTER_ERR');
-test(function () {
-  assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.replace('z', 'a b'); } );
-  assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.replace('a b', 'z'); } );
-  assert_throws( 'INVALID_CHARACTER_ERR', function () { elem.classList.replace('a b', 'b c'); } );
-}, '.replace with string_with_spaces must throw a INVALID_CHARACTER_ERR');
-test(function () {
-  var foo = document.createElement('div');
-  foo.className = 'token1 token2 token3'
-  foo.classList.replace('token1', 'token3');
-  assert_equals( foo.classList.length, 2 );
-  assert_false( foo.classList.contains('token1') );
-  assert_true( foo.classList.contains('token2') );
-  assert_true( foo.classList.contains('token3') );
-}, '.replace with an already existing token')
-elem.className = 'foo';
-test(function () {
-  assert_equals( getComputedStyle(elem,null).fontStyle, 'italic', 'critical test; required by the testsuite' );
-}, 'computed style must update when setting .className');
-test(function () {
-  assert_true( elem.classList.contains('foo') );
-}, 'classList.contains must update when .className is changed');
-test(function () {
-  assert_false( elem.classList.contains('FOO') );
-}, 'classList.contains must be case sensitive');
-test(function () {
-  assert_false( elem.classList.contains('foo.') );
-  assert_false( elem.classList.contains('foo)') );
-  assert_false( elem.classList.contains('foo\'') );
-  assert_false( elem.classList.contains('foo$') );
-  assert_false( elem.classList.contains('foo~') );
-  assert_false( elem.classList.contains('foo?') );
-  assert_false( elem.classList.contains('foo\\') );
-}, 'classList.contains must not match when punctuation characters are added');
-test(function () {
-  elem.classList.add('FOO');
-  assert_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
-}, 'classList.add must not cause the CSS selector to stop matching');
-test(function () {
-  assert_true( elem.classList.contains('foo') );
-}, 'classList.add must not remove existing classes');
-test(function () {
-  assert_true( elem.classList.contains('FOO') );
-}, 'classList.contains case sensitivity must match a case-specific string');
-test(function () {
-  assert_equals( elem.classList.length, 2 );
-}, 'classList.length must correctly reflect the number of tokens');
-test(function () {
-  assert_equals( elem.classList.item(0), 'foo' );
-}, 'classList.item(0) must return the first token');
-test(function () {
-  assert_equals( elem.classList.item(1), 'FOO' );
-}, 'classList.item must return case-sensitive strings and preserve token order');
-test(function () {
-  assert_equals( elem.classList[0], 'foo' );
-}, 'classList[0] must return the first token');
-test(function () {
-  assert_equals( elem.classList[1], 'FOO' );
-}, 'classList[index] must return case-sensitive strings and preserve token order');
-test(function () {
-  /* the normative part of the spec states that:
-  "The object's supported property indices are the numbers in the range zero to the number of tokens in tokens minus one"
-  ...
-  "The term[...] supported property indices [is] used as defined in the WebIDL specification."
-  WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */
-  assert_equals( elem.classList[2], undefined );
-}, 'classList[index] must still be undefined for out-of-range index when earlier indexes exist');
-test(function () {
-  assert_equals( elem.className, 'foo FOO' );
-}, 'className must update correctly when items have been added through classList');
-test(function () {
-  assert_equals( elem.classList + '', 'foo FOO', 'implicit' );
-  assert_equals( elem.classList.toString(), 'foo FOO', 'explicit' );
-}, 'classList must stringify correctly when items have been added');
-test(function () {
-  elem.classList.add('foo');
-  assert_equals( elem.classList.length, 2 );
-  assert_equals( elem.classList + '', 'foo FOO', 'implicit' );
-  assert_equals( elem.classList.toString(), 'foo FOO', 'explicit' );
-}, 'classList.add should not add a token if it already exists');
-test(function () {
-  elem.classList.remove('bar');
-  assert_equals( elem.classList.length, 2 );
-  assert_equals( elem.classList + '', 'foo FOO', 'implicit' );
-  assert_equals( elem.classList.toString(), 'foo FOO', 'explicit' );
-}, 'classList.remove removes arguments passed, if they are present.');
-test(function () {
-  elem.classList.remove('foo');
-  assert_equals( elem.classList.length, 1 );
-  assert_equals( elem.classList + '', 'FOO', 'implicit' );
-  assert_equals( elem.classList.toString(), 'FOO', 'explicit' );
-  assert_false( elem.classList.contains('foo') );
-  assert_true( elem.classList.contains('FOO') );
-}, 'classList.remove must remove existing tokens');
-test(function () {
-  assert_not_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
-}, 'classList.remove must not break case-sensitive CSS selector matching');
-test(function () {
-  secondelem.classList.remove('test');
-  assert_equals( secondelem.classList.length, 0 );
-  assert_false( secondelem.classList.contains('test') );
-}, 'classList.remove must remove duplicated tokens');
-test(function () {
-  secondelem.className = 'token1 token2 token3';
-  secondelem.classList.remove('token2');
-  assert_equals( secondelem.classList + '', 'token1 token3', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token1 token3', 'explicit' );
-}, 'classList.remove must collapse whitespace around removed tokens');
-test(function () {
-  secondelem.className = ' token1 token2  ';
-  secondelem.classList.remove('token2');
-  assert_equals( secondelem.classList + '', 'token1', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token1', 'explicit' );
-}, 'classList.remove must collapse whitespaces around each token');
-test(function () {
-  secondelem.className = '  token1  token2  token1  ';
-  secondelem.classList.remove('token2');
-  assert_equals( secondelem.classList + '', 'token1', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token1', 'explicit' );
-}, 'classList.remove must collapse whitespaces around each token and remove duplicates');
-test(function () {
-  secondelem.className = '  token1  token2  token1  ';
-  secondelem.classList.remove('token1');
-  assert_equals( secondelem.classList + '', 'token2', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token2', 'explicit' );
-}, 'classList.remove must collapse whitespace when removing duplicate tokens');
-test(function () {
-  secondelem.className = '  token1  token1  ';
-  secondelem.classList.add('token1');
-  assert_equals( secondelem.classList + '', 'token1', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token1', 'explicit' );
-}, 'classList.add must collapse whitespaces and remove duplicates when adding a token that already exists');
-test(function () {
-  assert_true(elem.classList.toggle('foo'));
-  assert_equals( elem.classList.length, 2 );
-  assert_true( elem.classList.contains('foo') );
-  assert_true( elem.classList.contains('FOO') );
-}, 'classList.toggle must toggle tokens case-sensitively when adding');
-test(function () {
-  assert_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
-}, 'classList.toggle must not break case-sensitive CSS selector matching');
-test(function () {
-  assert_false(elem.classList.toggle('foo'));
-}, 'classList.toggle must be able to remove tokens');
-test(function () {
-  //will return true if the last test incorrectly removed both
-  assert_false(elem.classList.toggle('FOO'));
-  assert_false( elem.classList.contains('foo') );
-  assert_false( elem.classList.contains('FOO') );
-}, 'classList.toggle must be case-sensitive when removing tokens');
-test(function () {
-  secondelem.className = 'foo FOO'
-  secondelem.classList.replace('bar', 'baz');
-  assert_equals( secondelem.classList.length, 2 );
-  assert_equals( secondelem.classList + '', 'foo FOO', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'foo FOO', 'explicit' );
-}, 'classList.replace replaces arguments passed, if they are present.');
-test(function () {
-  secondelem.classList.replace('foo', 'bar');
-  assert_equals( secondelem.classList.length, 2 );
-  assert_equals( secondelem.classList + '', 'bar FOO', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'bar FOO', 'explicit' );
-  assert_false( secondelem.classList.contains('foo') );
-  assert_true( secondelem.classList.contains('bar') );
-  assert_true( secondelem.classList.contains('FOO') );
-}, 'classList.replace must replace existing tokens');
-test(function () {
-  assert_not_equals( getComputedStyle(secondelem,null).fontStyle, 'italic' );
-}, 'classList.replace must not break case-sensitive CSS selector matching');
-test(function () {
-  secondelem.className = 'token1 token2 token1'
-  secondelem.classList.replace('token1', 'token3');
-  assert_equals( secondelem.classList.length, 2 );
-  assert_false( secondelem.classList.contains('token1') );
-  assert_true( secondelem.classList.contains('token2') );
-  assert_true( secondelem.classList.contains('token3') );
-}, 'classList.replace must replace duplicated tokens');
-test(function () {
-  secondelem.className = 'token1  token2  token3';
-  secondelem.classList.replace('token2', 'token4');
-  assert_equals( secondelem.classList + '', 'token1 token4 token3', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token1 token4 token3', 'explicit' );
-}, 'classList.replace must collapse whitespace around replaced tokens');
-test(function () {
-  secondelem.className = ' token1 token2  ';
-  secondelem.classList.replace('token2', 'token3');
-  assert_equals( secondelem.classList.length, 2 );
-  assert_equals( secondelem.classList + '', 'token1 token3', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token1 token3', 'explicit' );
-}, 'classList.replace must collapse whitespaces around each token');
-test(function () {
-  secondelem.className = '  token1  token2  token1  ';
-  secondelem.classList.replace('token2', 'token3');
-  assert_equals( secondelem.classList + '', 'token1 token3', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token1 token3', 'explicit' );
-}, 'classList.replace must collapse whitespaces around each token and remove duplicates');
-test(function () {
-  secondelem.className = '  token1  token2  token1  ';
-  secondelem.classList.replace('token1', 'token3');
-  assert_equals( secondelem.classList + '', 'token3 token2', 'implicit' );
-  assert_equals( secondelem.classList.toString(), 'token3 token2', 'explicit' );
-}, 'classList.replace must collapse whitespace when replacing duplicate tokens');
-test(function () {
-  assert_not_equals( getComputedStyle(elem,null).fontStyle, 'italic' );
-}, 'CSS class selectors must stop matching when all classes have been removed');
-test(function () {
-  assert_equals( elem.className, '' );
-}, 'className must be empty when all classes have been removed');
-test(function () {
-  assert_equals( elem.classList + '', '', 'implicit' );
-  assert_equals( elem.classList.toString(), '', 'explicit' );
-}, 'classList must stringify to an empty string when all classes have been removed');
-test(function () {
-  assert_equals( elem.classList.item(0), null );
-}, 'classList.item(0) must return null when all classes have been removed');
-test(function () {
-  /* the normative part of the spec states that:
-  "unless the length is zero, in which case there are no supported property indices"
-  ...
-  "The term[...] supported property indices [is] used as defined in the WebIDL specification."
-  WebIDL creates actual OwnProperties and then [] just acts as a normal property lookup */
-  assert_equals( elem.classList[0], undefined );
-}, 'classList[0] must be undefined when all classes have been removed');
-test(function () {
-  var foo = document.createElement('div');
-  foo.classList.add();
-  assert_true( foo.hasAttribute('class') );
-  assert_equals( foo.classList + '', '', 'implicit' );
-  assert_equals( foo.classList.toString(), '', 'explicit' );
-}, 'Invoking add or remove should set the class attribute');
-// The ordered set parser must skip ASCII whitespace (U+0009, U+000A, U+000C, U+000D, and U+0020.)
-test(function () {
-  var foo = document.createElement('div');
-  foo.className = 'a ';
-  foo.classList.add('b');
-  assert_equals(foo.className,'a b');
-}, 'classList.add should treat " " as a space');
-test(function () {
-  var foo = document.createElement('div');
-  foo.className = 'a\t';
-  foo.classList.add('b');
-  assert_equals(foo.className,'a b');
-}, 'classList.add should treat \\t as a space');
-test(function () {
-  var foo = document.createElement('div');
-  foo.className = 'a\r';
-  foo.classList.add('b');
-  assert_equals(foo.className,'a b');
-}, 'classList.add should treat \\r as a space');
-test(function () {
-  var foo = document.createElement('div');
-  foo.className = 'a\n';
-  foo.classList.add('b');
-  assert_equals(foo.className,'a b');
-}, 'classList.add should treat \\n as a space');
-test(function () {
-  var foo = document.createElement('div');
-  foo.className = 'a\f';
-  foo.classList.add('b');
-  assert_equals(foo.className,'a b');
-}, 'classList.add should treat \\f as a space');
-test(function () {
-  //WebIDL and ECMAScript 5 - a readonly property has a getter but not a setter
-  //ES5 makes [[Put]] fail but not throw
-  var failed = false;
-  secondelem.className = 'token1';
-  try {
-    secondelem.classList.length = 0;
-  } catch(e) {
-    failed = e;
+<meta charset=utf-8>
+<title>Test for the classList element attribute</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="content"></div>
+<script>
+const SVG_NS = "http://www.w3.org/2000/svg";
+const XHTML_NS = "http://www.w3.org/1999/xhtml"
+const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
+
+function setClass(e, newVal) {
+  if (newVal === null) {
+    e.removeAttribute("class");
+  } else {
+    e.setAttribute("class", newVal);
+  }
+}
+
+function checkModification(e, funcName, args, expectedRes, before, after,
+                           expectedException, desc) {
+  if (!Array.isArray(args)) {
+    args = [args];
+  }
+
+  test(function() {
+    var shouldThrow = typeof(expectedException) === "string";
+    if (shouldThrow) {
+      // If an exception is thrown, the class attribute shouldn't change.
+      after = before;
+    }
+    setClass(e, before);
+
+    if (shouldThrow) {
+      assert_throws(expectedException, function() {
+        var list = e.classList;
+        var res = list[funcName].apply(list, args);
+      });
+    } else {
+      var list = e.classList;
+      var res = list[funcName].apply(list, args);
+    }
+    if (!shouldThrow) {
+      assert_equals(res, expectedRes, "wrong return value");
+    }
+
+    var expectedAfter = after;
+
+    assert_equals(e.getAttribute("class"), expectedAfter,
+                  "wrong class after modification");
+  }, "classList." + funcName + "(" + args.map(format_value).join(", ") +
+  ") with attribute value " + format_value(before) + desc);
+}
+
+function assignToClassListStrict(e) {
+  "use strict";
+  e.classList = "foo";
+  e.removeAttribute("class");
+}
+
+function assignToClassList(e) {
+  var expect = e.classList;
+  e.classList = "foo";
+  assert_equals(e.classList, expect,
+                "classList should be unchanged after assignment");
+  e.removeAttribute("class");
+}
+
+function testClassList(e, desc) {
+
+  // assignment
+
+  test(function() {
+    assignToClassListStrict(e);
+    assignToClassList(e);
+  }, "Assigning to classList" + desc);
+
+  // supports
+  test(function() {
+    assert_throws(TypeError(), function() {
+      e.classList.supports("a");
+    })
+  }, ".supports() must throw TypeError" + desc);
+
+  // length attribute
+
+  function checkLength(value, length) {
+    test(function() {
+      setClass(e, value);
+      assert_equals(e.classList.length, length);
+    }, "classList.length when " +
+    (value === null ? "removed" : "set to " + format_value(value)) + desc);
+  }
+
+  checkLength(null, 0);
+  checkLength("", 0);
+  checkLength("   \t  \f", 0);
+  checkLength("a", 1);
+  checkLength("a A", 2);
+  checkLength("\r\na\t\f", 1);
+  checkLength("a a", 1);
+  checkLength("a a a a a a", 1);
+  checkLength("a a b b", 2);
+  checkLength("a A B b", 4);
+  checkLength("a b c c b a a b c c", 3);
+  checkLength("   a  a b", 2);
+  checkLength("a\tb\nc\fd\re f", 6);
+
+  // [Stringifies]
+
+  function checkStringifier(value, expected) {
+    test(function() {
+      setClass(e, value);
+      assert_equals(e.classList.toString(), expected);
+    }, "classList.toString() when " +
+    (value === null ? "removed" : "set to " + format_value(value)) + desc);
+  }
+
+  checkStringifier(null, "");
+  checkStringifier("foo", "foo");
+  checkStringifier("   a  a b", "   a  a b");
+
+  // item() method
+
+  function checkItems(attributeValue, expectedValues) {
+    function checkItemFunction(index, expected) {
+      assert_equals(e.classList.item(index), expected,
+                    "classList.item(" + index + ")");
+    }
+
+    function checkItemArray(index, expected) {
+      assert_equals(e.classList[index], expected, "classList[" + index + "]");
+    }
+
+    test(function() {
+      setClass(e, attributeValue);
+
+      checkItemFunction(-1, null);
+      checkItemArray(-1, undefined);
+
+      var i = 0;
+      while (i < expectedValues.length) {
+        checkItemFunction(i, expectedValues[i]);
+        checkItemArray(i, expectedValues[i]);
+        i++;
+      }
+
+      checkItemFunction(i, null);
+      checkItemArray(i, undefined);
+
+      checkItemFunction(0xffffffff, null);
+      checkItemArray(0xffffffff, undefined);
+
+      checkItemFunction(0xfffffffe, null);
+      checkItemArray(0xfffffffe, undefined);
+    }, "classList.item() when set to " + format_value(attributeValue) + desc);
+  }
+
+  checkItems(null, []);
+  checkItems("a", ["a"]);
+  checkItems("aa AA aa", ["aa", "AA"]);
+  checkItems("a b", ["a", "b"]);
+  checkItems("   a  a b", ["a", "b"]);
+  checkItems("\t\n\f\r a\t\n\f\r b\t\n\f\r ", ["a", "b"]);
+
+  // contains() method
+
+  function checkContains(attributeValue, args, expectedRes) {
+    if (!Array.isArray(expectedRes)) {
+      expectedRes = Array(args.length).fill(expectedRes);
+    }
+    setClass(e, attributeValue);
+    for (var i = 0; i < args.length; i++) {
+      test(function() {
+        assert_equals(e.classList.contains(args[i]), expectedRes[i],
+                      "classList.contains(\"" + args[i] + "\")");
+      }, "classList.contains(" + format_value(args[i]) + ") when set to " +
+      format_value(attributeValue) + desc);
+    }
+  }
+
+  checkContains(null, ["a", "", "  "], false);
+  checkContains("", ["a"], false);
+
+  checkContains("a", ["a"], true);
+  checkContains("a", ["aa", "b", "A", "a.", "a)",, "a'", 'a"', "a$", "a~",
+                      "a?", "a\\"], false);
+
+  // All "ASCII whitespace" per spec, before and after
+  checkContains("a", ["a\t", "\ta", "a\n", "\na", "a\f", "\fa", "a\r", "\ra",
+                      "a ", " a"], false);
+
+  checkContains("aa AA", ["aa", "AA", "aA"], [true, true, false]);
+  checkContains("a a a", ["a", "aa", "b"], [true, false, false]);
+  checkContains("a b c", ["a", "b"], true);
+
+  checkContains("null undefined", [null, undefined], true);
+  checkContains("\t\n\f\r a\t\n\f\r b\t\n\f\r ", ["a", "b"], true);
+
+  // add() method
+
+  function checkAdd(before, argument, after, expectedException) {
+    checkModification(e, "add", argument, undefined, before, after,
+                      expectedException, desc);
+    // Also check force toggle
+    // XXX https://github.com/whatwg/dom/issues/443
+    //if (!Array.isArray(argument)) {
+    //  checkModification(e, "toggle", [argument, true], true, before, after,
+    //                    expectedException);
+    //}
   }
-  assert_equals(secondelem.classList.length,1);
-  assert_false(failed,'an error was thrown');
-}, 'classList.length must be read-only');
-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');
+
+  checkAdd(null, "", null, "SyntaxError");
+  checkAdd(null, ["a", ""], null, "SyntaxError");
+  checkAdd(null, " ", null, "InvalidCharacterError");
+  checkAdd(null, "\ta", null, "InvalidCharacterError");
+  checkAdd(null, "a\t", null, "InvalidCharacterError");
+  checkAdd(null, "\na", null, "InvalidCharacterError");
+  checkAdd(null, "a\n", null, "InvalidCharacterError");
+  checkAdd(null, "\fa", null, "InvalidCharacterError");
+  checkAdd(null, "a\f", null, "InvalidCharacterError");
+  checkAdd(null, "\ra", null, "InvalidCharacterError");
+  checkAdd(null, "a\r", null, "InvalidCharacterError");
+  checkAdd(null, " a", null, "InvalidCharacterError");
+  checkAdd(null, "a ", null, "InvalidCharacterError");
+  checkAdd(null, ["a", " "], null, "InvalidCharacterError");
+  checkAdd(null, ["a", "aa "], null, "InvalidCharacterError");
+
+  checkAdd("a", "a", "a");
+  checkAdd("aa", "AA", "aa AA");
+  checkAdd("a b c", "a", "a b c");
+  checkAdd("a a a  b", "a", "a b");
+  checkAdd(null, "a", "a");
+  checkAdd("", "a", "a");
+  checkAdd(" ", "a", "a");
+  checkAdd("   \f", "a", "a");
+  checkAdd("a", "b", "a b");
+  checkAdd("a b c", "d", "a b c d");
+  checkAdd("a b c ", "d", "a b c d");
+  checkAdd("   a  a b", "c", "a b c");
+  checkAdd("   a  a b", "a", "a b");
+  checkAdd("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "c", "a b c");
+
+  // multiple add
+  checkAdd("a b c ", ["d", "e"], "a b c d e");
+  checkAdd("a b c ", ["a", "a"], "a b c");
+  checkAdd("a b c ", ["d", "d"], "a b c d");
+  checkAdd("a b c a ", [], "a b c");
+  checkAdd(null, ["a", "b"], "a b");
+  checkAdd("", ["a", "b"], "a b");
+
+  checkAdd(null, null, "null");
+  checkAdd(null, undefined, "undefined");
+
+  // remove() method
+
+  function checkRemove(before, argument, after, expectedException) {
+    checkModification(e, "remove", argument, undefined, before, after,
+                      expectedException, desc);
+    // Also check force toggle
+    // XXX https://github.com/whatwg/dom/issues/443
+    //if (!Array.isArray(argument)) {
+    //  checkModification(e, "toggle", [argument, false], false, before, after,
+    //                    expectedException);
+    //}
+  }
 
-  assert_equals(foo.className, 'a');
+  checkRemove(null, "", null, "SyntaxError");
+  checkRemove(null, " ", null, "InvalidCharacterError");
+  checkRemove("\ta", "\ta", "\ta", "InvalidCharacterError");
+  checkRemove("a\t", "a\t", "a\t", "InvalidCharacterError");
+  checkRemove("\na", "\na", "\na", "InvalidCharacterError");
+  checkRemove("a\n", "a\n", "a\n", "InvalidCharacterError");
+  checkRemove("\fa", "\fa", "\fa", "InvalidCharacterError");
+  checkRemove("a\f", "a\f", "a\f", "InvalidCharacterError");
+  checkRemove("\ra", "\ra", "\ra", "InvalidCharacterError");
+  checkRemove("a\r", "a\r", "a\r", "InvalidCharacterError");
+  checkRemove(" a", " a", " a", "InvalidCharacterError");
+  checkRemove("a ", "a ", "a ", "InvalidCharacterError");
+  checkRemove("aa ", "aa ", null, "InvalidCharacterError");
+
+  checkRemove(null, "a", null);
+  checkRemove("", "a", "");
+  checkRemove("a b  c", "d", "a b c");
+  checkRemove("a b  c", "A", "a b c");
+  checkRemove(" a a a ", "a", "");
+  checkRemove("a  b", "a", "b");
+  checkRemove("a  b  ", "a", "b");
+  checkRemove("a a b", "a", "b");
+  checkRemove("aa aa bb", "aa", "bb");
+  checkRemove("a a b a a c a a", "a", "b c");
+
+  checkRemove("a  b  c", "b", "a c");
+  checkRemove("aaa  bbb  ccc", "bbb", "aaa ccc");
+  checkRemove(" a  b  c ", "b", "a c");
+  checkRemove("a b b b c", "b", "a c");
+
+  checkRemove("a  b  c", "c", "a b");
+  checkRemove(" a  b  c ", "c", "a b");
+  checkRemove("a b c c c", "c", "a b");
+
+  checkRemove("a b a c a d a", "a", "b c d");
+  checkRemove("AA BB aa CC AA dd aa", "AA", "BB aa CC dd");
+
+  checkRemove("\ra\na\ta\f", "a", "");
+  checkRemove("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", "b");
+
+  // multiple remove
+  checkRemove("a b c ", ["d", "e"], "a b c");
+  checkRemove("a b c ", ["a", "b"], "c");
+  checkRemove("a b c ", ["a", "c"], "b");
+  checkRemove("a b c ", ["a", "a"], "b c");
+  checkRemove("a b c ", ["d", "d"], "a b c");
+  checkRemove("a b c ", [], "a b c");
+  checkRemove(null, ["a", "b"], null);
+  checkRemove("", ["a", "b"], "");
+  checkRemove("a a", [], "a");
+
+  checkRemove("null", null, "");
+  checkRemove("undefined", undefined, "");
+
+  // toggle() method
+
+  function checkToggle(before, argument, expectedRes, after, expectedException) {
+    checkModification(e, "toggle", argument, expectedRes, before, after,
+                      expectedException, desc);
+  }
+
+  checkToggle(null, "", null, null, "SyntaxError");
+  checkToggle(null, "aa ", null, null, "InvalidCharacterError");
 
-  foo.classList.replace('a', 'b');
-  assert_equals(foo.className, 'b');
+  checkToggle(null, "a", true, "a");
+  checkToggle("", "a", true, "a");
+  checkToggle(" ", "a", true, "a");
+  checkToggle("   \f", "a", true, "a");
+  checkToggle("a", "b", true, "a b");
+  checkToggle("a", "A", true, "a A");
+  checkToggle("a b c", "d", true, "a b c d");
+  checkToggle("   a  a b", "d", true, "a b d");
+
+  checkToggle("a", "a", false, "");
+  checkToggle(" a a a ", "a", false, "");
+  checkToggle(" A A A ", "a", true, "A a");
+  checkToggle(" a b c ", "b", false, "a c");
+  checkToggle(" a b c b b", "b", false, "a c");
+  checkToggle(" a b  c  ", "c", false, "a b");
+  checkToggle(" a b c ", "a", false, "b c");
+  checkToggle("   a  a b", "b", false, "a");
+  checkToggle("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", false, "b");
+  checkToggle("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "c", true, "a b c");
+
+  checkToggle("null", null, false, "");
+  checkToggle("", null, true, "null");
+  checkToggle("undefined", undefined, false, "");
+  checkToggle("", undefined, true, "undefined");
+
+
+  // tests for the force argument handling
+  // XXX Remove these if https://github.com/whatwg/dom/issues/443 is fixed
 
-  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');
+  function checkForceToggle(before, argument, force, expectedRes, after, expectedException) {
+    checkModification(e, "toggle", [argument, force], expectedRes, before,
+                      after, expectedException, desc);
+  }
+
+  checkForceToggle("", "a", true, true, "a");
+  checkForceToggle("a", "a", true, true, "a");
+  checkForceToggle("a", "b", true, true, "a b");
+  checkForceToggle("a b", "b", true, true, "a b");
+  checkForceToggle("", "a", false, false, "");
+  checkForceToggle("a", "a", false, false, "");
+  checkForceToggle("a", "b", false, false, "a");
+  checkForceToggle("a b", "b", false, false, "a");
+
+
+  // replace() method
+  function checkReplace(before, token, newToken, after, expectedException) {
+    checkModification(e, "replace", [token, newToken], undefined, before,
+                      after, expectedException, desc);
+  }
+
+  checkReplace(null, "", "a", null, "SyntaxError");
+  checkReplace(null, "", " ", null, "SyntaxError");
+  checkReplace(null, " ", "a", null, "InvalidCharacterError");
+  checkReplace(null, "\ta", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\t", "b", null, "InvalidCharacterError");
+  checkReplace(null, "\na", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\n", "b", null, "InvalidCharacterError");
+  checkReplace(null, "\fa", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\f", "b", null, "InvalidCharacterError");
+  checkReplace(null, "\ra", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a\r", "b", null, "InvalidCharacterError");
+  checkReplace(null, " a", "b", null, "InvalidCharacterError");
+  checkReplace(null, "a ", "b", null, "InvalidCharacterError");
 
-test(function() {
-  var foo = document.createElement('div');
-  assert_throws(new TypeError(),
-                function() { foo.classList.supports('hello') });
-}, 'classList.supports should throw');
-    </script>
-  </head>
-  <body>
+  checkReplace(null, "a", "", null, "SyntaxError");
+  checkReplace(null, " ", "", null, "SyntaxError");
+  checkReplace(null, "a", " ", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\ta", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\t", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\na", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\n", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\fa", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\f", null, "InvalidCharacterError");
+  checkReplace(null, "b", "\ra", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a\r", null, "InvalidCharacterError");
+  checkReplace(null, "b", " a", null, "InvalidCharacterError");
+  checkReplace(null, "b", "a ", null, "InvalidCharacterError");
 
-    <div id="log"></div>
+  checkReplace("a", "a", "a", "a");
+  checkReplace("a", "a", "b", "b");
+  checkReplace("a", "A", "b", "a");
+  checkReplace("a b", "b", "A", "a A");
+  checkReplace("a b c", "d", "e", "a b c");
+  // https://github.com/whatwg/dom/issues/443
+  checkReplace("a a a  b", "a", "a", "a b");
+  checkReplace("a a a  b", "c", "d", "a a a  b");
+  checkReplace(null, "a", "b", null);
+  checkReplace("", "a", "b", "");
+  checkReplace(" ", "a", "b", " ");
+  checkReplace(" a  \f", "a", "b", "b");
+  checkReplace("a b c", "b", "d", "a d c");
+  // https://github.com/whatwg/dom/issues/442
+  // Implementations agree on the first one here, so I test it, but disagree on
+  // the second, so no test until the spec decides what to say.
+  checkReplace("a b c", "c", "a", "a b");
+  //checkReplace("c b a", "c", "a", ???);
+  checkReplace("a b a", "a", "c", "c b");
+  checkReplace("a b a", "b", "c", "a c");
+  checkReplace("   a  a b", "a", "c", "c b");
+  checkReplace("   a  a b", "b", "c", "a c");
+  checkReplace("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "a", "c", "c b");
+  checkReplace("\t\n\f\r a\t\n\f\r b\t\n\f\r ", "b", "c", "a c");
 
-  </body>
-</html>
+  checkReplace("a null", null, "b", "a b");
+  checkReplace("a b", "a", null, "null b");
+  checkReplace("a undefined", undefined, "b", "a b");
+  checkReplace("a b", "a", undefined, "undefined b");
+}
+
+var content = document.getElementById("content");
+
+var htmlNode = document.createElement("div");
+content.appendChild(htmlNode);
+testClassList(htmlNode, " (HTML node)");
+
+var xhtmlNode = document.createElementNS(XHTML_NS, "div");
+content.appendChild(xhtmlNode);
+testClassList(xhtmlNode, " (XHTML node)");
+
+var mathMLNode = document.createElementNS(MATHML_NS, "math");
+content.appendChild(mathMLNode);
+testClassList(mathMLNode, " (MathML node)");
+
+var xmlNode = document.createElementNS(null, "foo");
+content.appendChild(xmlNode);
+testClassList(xmlNode, " (XML node with null namespace)");
+
+var fooNode = document.createElementNS("http://example.org/foo", "foo");
+content.appendChild(fooNode);
+testClassList(fooNode, " (foo node)");
+</script>