[SCM] WebKit Debian packaging branch, debian/experimental, updated. upstream/1.3.3-9427-gc2be6fc

arv at chromium.org arv at chromium.org
Wed Dec 22 13:49:30 UTC 2010


The following commit has been merged in the debian/experimental branch:
commit e4f1cb58af44e3cf3e3eff5e378ab4cc360da08f
Author: arv at chromium.org <arv at chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Mon Sep 27 23:12:11 2010 +0000

    2010-09-27  Erik Arvidsson  <arv at chromium.org>
    
            Reviewed by Darin Adler.
    
            Implement HTML 5's HTMLElement.classList property
            https://bugs.webkit.org/show_bug.cgi?id=20709
    
            * fast/dom/HTMLElement/class-list-expected.txt: Added.
            * fast/dom/HTMLElement/class-list-gc-expected.txt: Added.
            * fast/dom/HTMLElement/class-list-gc.html: Added.
            * fast/dom/HTMLElement/class-list-quirks-expected.txt: Added.
            * fast/dom/HTMLElement/class-list-quirks.html: Added.
            * fast/dom/HTMLElement/class-list.html: Added.
            * fast/dom/HTMLElement/script-tests/class-list-gc.js: Added.
            (gc):
            * fast/dom/HTMLElement/script-tests/class-list.js: Added.
            (createElement):
            * fast/dom/Window/window-properties-expected.txt:
            * fast/dom/Window/window-property-descriptors-expected.txt:
            * fast/dom/prototype-inheritance-2-expected.txt:
            * fast/dom/prototype-inheritance-expected.txt:
            * fast/js/global-constructors-expected.txt:
            * perf/class-list-remove-expected.txt: Added.
            * perf/class-list-remove.html: Added.
    2010-09-27  Erik Arvidsson  <arv at chromium.org>
    
            Reviewed by Darin Adler.
    
            Implement HTML 5's HTMLElement.classList property
            https://bugs.webkit.org/show_bug.cgi?id=20709
    
            This adds a DOMTokenList class that is used for the classList property. DOMTokenList uses a SpaceSplitString for fast
            contains check. In standards mode the existing classNames is used but in quirks mode we use an internal SpaceSplitString
            because classList is always case sensitive.
    
            Tests: fast/dom/HTMLElement/class-list-gc.html
                   fast/dom/HTMLElement/class-list-quirks.html
                   fast/dom/HTMLElement/class-list.html
                   perf/class-list-remove.html
    
            * Android.derived.jscbindings.mk:
            * Android.derived.v8bindings.mk:
            * Android.jscbindings.mk:
            * CMakeLists.txt:
            * DerivedSources.cpp:
            * DerivedSources.make:
            * GNUmakefile.am:
            * WebCore.gypi:
            * WebCore.pri:
            * WebCore.pro:
            * WebCore.vcproj/WebCore.vcproj:
            * WebCore.xcodeproj/project.pbxproj:
            * bindings/gobject/GNUmakefile.am:
            * bindings/js/JSElementCustom.cpp:
            (WebCore::JSElement::markChildren): Make sure that we keep the wrapper while the element is alive.
            * bindings/scripts/CodeGeneratorJS.pm: DOMTokenList has a string indexed getter.
            * bindings/scripts/CodeGeneratorV8.pm:
            * bindings/v8/custom/V8DOMTokenListCustom.cpp: Added.
            (WebCore::toV8):
            * dom/Element.cpp:
            (WebCore::Element::classList):
            (WebCore::Element::optionalClassList):
            * dom/Element.h:
            * dom/ElementRareData.h: This now has an OwnPtr to a ClassList if the Element::classList is ever called.
            * dom/SpaceSplitString.h:
            (WebCore::SpaceSplitString::isNull):
            * dom/StyledElement.cpp:
            (WebCore::StyledElement::classAttributeChanged): Update the classList if it exists.
            * dom/StyledElement.h:
            * html/DOMTokenList.cpp: Added.
            (WebCore::validateToken):
            (WebCore::DOMTokenList::DOMTokenList):
            (WebCore::DOMTokenList::ref):
            (WebCore::DOMTokenList::deref):
            (WebCore::DOMTokenList::length):
            (WebCore::DOMTokenList::item):
            (WebCore::DOMTokenList::contains):
            (WebCore::DOMTokenList::containsInternal): The internal methods do no validation of the token.
            (WebCore::DOMTokenList::add):
            (WebCore::DOMTokenList::addInternal):
            (WebCore::DOMTokenList::remove):
            (WebCore::DOMTokenList::removeInternal):
            (WebCore::DOMTokenList::toggle):
            (WebCore::DOMTokenList::toString):
            (WebCore::DOMTokenList::reset):
            (WebCore::DOMTokenList::classNames):
            * html/DOMTokenList.h: Added.
            (WebCore::DOMTokenList::create):
            (WebCore::DOMTokenList::element):
            * html/DOMTokenList.idl: Added.
            * html/HTMLElement.idl:
            * page/DOMWindow.cpp:
            * page/DOMWindow.idl:
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@68440 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 3c691c4..1f36135 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,28 @@
+2010-09-27  Erik Arvidsson  <arv at chromium.org>
+
+        Reviewed by Darin Adler.
+
+        Implement HTML 5's HTMLElement.classList property
+        https://bugs.webkit.org/show_bug.cgi?id=20709
+
+        * fast/dom/HTMLElement/class-list-expected.txt: Added.
+        * fast/dom/HTMLElement/class-list-gc-expected.txt: Added.
+        * fast/dom/HTMLElement/class-list-gc.html: Added.
+        * fast/dom/HTMLElement/class-list-quirks-expected.txt: Added.
+        * fast/dom/HTMLElement/class-list-quirks.html: Added.
+        * fast/dom/HTMLElement/class-list.html: Added.
+        * fast/dom/HTMLElement/script-tests/class-list-gc.js: Added.
+        (gc):
+        * fast/dom/HTMLElement/script-tests/class-list.js: Added.
+        (createElement):
+        * fast/dom/Window/window-properties-expected.txt:
+        * fast/dom/Window/window-property-descriptors-expected.txt:
+        * fast/dom/prototype-inheritance-2-expected.txt:
+        * fast/dom/prototype-inheritance-expected.txt:
+        * fast/js/global-constructors-expected.txt:
+        * perf/class-list-remove-expected.txt: Added.
+        * perf/class-list-remove.html: Added.
+
 2010-09-27  David Hyatt  <hyatt at apple.com>
 
         Reviewed by Sam Weinig.
diff --git a/LayoutTests/fast/dom/HTMLElement/class-list-expected.txt b/LayoutTests/fast/dom/HTMLElement/class-list-expected.txt
new file mode 100644
index 0000000..bd0e8a4
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/class-list-expected.txt
@@ -0,0 +1,68 @@
+Tests the classList attribute and its properties.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Tests from http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/
+PASS String(element.classList) is "x"
+PASS element.classList.length is 0
+PASS element.classList.length is 1
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.className is "x"
+PASS element.className is "x"
+PASS element.className is "x  x"
+PASS element.className is "y x"
+PASS element.className is ""
+PASS element.className is ""
+PASS element.className is " y y "
+PASS element.className is "y"
+Testing add in presence of trailing white spaces.
+PASS element.className is "x y"
+PASS element.className is "x	 y"
+PASS element.className is " y"
+Test invalid tokens
+PASS element.classList.contains('') threw expected DOMException with code 12
+PASS element.classList.contains('x y') threw expected DOMException with code 5
+PASS element.classList.add('') threw expected DOMException with code 12
+PASS element.classList.add('x y') threw expected DOMException with code 5
+PASS element.classList.remove('') threw expected DOMException with code 12
+PASS element.classList.remove('x y') threw expected DOMException with code 5
+PASS element.classList.toggle('') threw expected DOMException with code 12
+PASS element.classList.toggle('x y') threw expected DOMException with code 5
+Indexing
+PASS element.classList[0] is "x"
+PASS element.classList.item(0) is "x"
+PASS element.classList[1] is "x"
+PASS element.classList.item(1) is "x"
+PASS element.classList[1] is "y"
+PASS element.classList.item(1) is "y"
+PASS element.classList[0] is null
+PASS element.classList.item(0) is null
+PASS element.classList[4] is null
+PASS element.classList.item(4) is null
+PASS element.classList[-1] is undefined.
+PASS element.classList.item(-1) is null
+Test case since DOMTokenList is case sensitive
+PASS element.classList.contains('x') is true
+PASS element.classList.contains('X') is false
+PASS element.classList[0] is "x"
+PASS element.classList.contains('X') is true
+PASS element.classList.contains('x') is false
+PASS element.classList[0] is "X"
+Testing whitespace
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+DOMTokenList presence and type
+PASS 'undefined' != typeof DOMTokenList is true
+PASS typeof DOMTokenList.prototype is "object"
+PASS typeof element.classList is "object"
+PASS element.classList.constructor is DOMTokenList
+PASS element.classList === element.classList is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/HTMLElement/class-list-gc-expected.txt b/LayoutTests/fast/dom/HTMLElement/class-list-gc-expected.txt
new file mode 100644
index 0000000..e1c25fb
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/class-list-gc-expected.txt
@@ -0,0 +1,11 @@
+This tests that properties on the classList persists GC.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS d.classList.life is 42
+PASS d.classList.life is 42
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/HTMLElement/class-list-gc.html b/LayoutTests/fast/dom/HTMLElement/class-list-gc.html
new file mode 100644
index 0000000..4c5efad
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/class-list-gc.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/class-list-gc.js"></script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/HTMLElement/class-list-quirks-expected.txt b/LayoutTests/fast/dom/HTMLElement/class-list-quirks-expected.txt
new file mode 100644
index 0000000..bd0e8a4
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/class-list-quirks-expected.txt
@@ -0,0 +1,68 @@
+Tests the classList attribute and its properties.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Tests from http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/
+PASS String(element.classList) is "x"
+PASS element.classList.length is 0
+PASS element.classList.length is 1
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.className is "x"
+PASS element.className is "x"
+PASS element.className is "x  x"
+PASS element.className is "y x"
+PASS element.className is ""
+PASS element.className is ""
+PASS element.className is " y y "
+PASS element.className is "y"
+Testing add in presence of trailing white spaces.
+PASS element.className is "x y"
+PASS element.className is "x	 y"
+PASS element.className is " y"
+Test invalid tokens
+PASS element.classList.contains('') threw expected DOMException with code 12
+PASS element.classList.contains('x y') threw expected DOMException with code 5
+PASS element.classList.add('') threw expected DOMException with code 12
+PASS element.classList.add('x y') threw expected DOMException with code 5
+PASS element.classList.remove('') threw expected DOMException with code 12
+PASS element.classList.remove('x y') threw expected DOMException with code 5
+PASS element.classList.toggle('') threw expected DOMException with code 12
+PASS element.classList.toggle('x y') threw expected DOMException with code 5
+Indexing
+PASS element.classList[0] is "x"
+PASS element.classList.item(0) is "x"
+PASS element.classList[1] is "x"
+PASS element.classList.item(1) is "x"
+PASS element.classList[1] is "y"
+PASS element.classList.item(1) is "y"
+PASS element.classList[0] is null
+PASS element.classList.item(0) is null
+PASS element.classList[4] is null
+PASS element.classList.item(4) is null
+PASS element.classList[-1] is undefined.
+PASS element.classList.item(-1) is null
+Test case since DOMTokenList is case sensitive
+PASS element.classList.contains('x') is true
+PASS element.classList.contains('X') is false
+PASS element.classList[0] is "x"
+PASS element.classList.contains('X') is true
+PASS element.classList.contains('x') is false
+PASS element.classList[0] is "X"
+Testing whitespace
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+PASS element.classList.length is 2
+DOMTokenList presence and type
+PASS 'undefined' != typeof DOMTokenList is true
+PASS typeof DOMTokenList.prototype is "object"
+PASS typeof element.classList is "object"
+PASS element.classList.constructor is DOMTokenList
+PASS element.classList === element.classList is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/HTMLElement/class-list-quirks.html b/LayoutTests/fast/dom/HTMLElement/class-list-quirks.html
new file mode 100644
index 0000000..fd876a1
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/class-list-quirks.html
@@ -0,0 +1,13 @@
+<!-- Quirks Mode -->
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/class-list.js"></script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/HTMLElement/class-list.html b/LayoutTests/fast/dom/HTMLElement/class-list.html
new file mode 100644
index 0000000..61df243
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/class-list.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css">
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/class-list.js"></script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/HTMLElement/script-tests/class-list-gc.js b/LayoutTests/fast/dom/HTMLElement/script-tests/class-list-gc.js
new file mode 100644
index 0000000..0e3161a
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/script-tests/class-list-gc.js
@@ -0,0 +1,30 @@
+description('This tests that properties on the classList persists GC.');
+
+function gc()
+{
+    if (window.GCController)
+        return GCController.collect();
+
+    for (var i = 0; i < 10000; i++) {
+        var s = new String;
+    }
+}
+
+var d = document.createElement('div');
+
+// Ensure the classList is created.
+var classList = d.classList;
+
+// Set a custom property.
+d.classList.life = 42;
+shouldEvaluateTo('d.classList.life', 42);
+
+// Null out reference to the dataset.
+classList = null;
+
+gc();
+
+// Test that the classList wrapper persisted the GC and still has the custom property.
+shouldEvaluateTo('d.classList.life', 42);
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/dom/HTMLElement/script-tests/class-list.js b/LayoutTests/fast/dom/HTMLElement/script-tests/class-list.js
new file mode 100644
index 0000000..2a34dfa
--- /dev/null
+++ b/LayoutTests/fast/dom/HTMLElement/script-tests/class-list.js
@@ -0,0 +1,238 @@
+description('Tests the classList attribute and its properties.');
+
+var element;
+
+function createElement(className)
+{
+    element = document.createElement('p');
+    element.className = className;
+}
+
+debug('Tests from http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/setting/001.htm
+// Firefox throws here but WebKit does not throw on setting readonly idl
+// attributes.
+createElement('x');
+try {
+    element.classList = 'y';
+    shouldBeEqualToString('String(element.classList)', 'x');
+} catch (ex) {
+    testPassed('Throwing on set is acceptable');
+}
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/001.htm
+createElement('');
+shouldEvaluateTo('element.classList.length', 0);
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/002.htm
+createElement('x');
+shouldEvaluateTo('element.classList.length', 1);
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/003.htm
+createElement('x x');
+shouldEvaluateTo('element.classList.length', 2);
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/004.htm
+createElement('x y');
+shouldEvaluateTo('element.classList.length', 2);
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/005.htm
+createElement('');
+element.classList.add('x');
+shouldBeEqualToString('element.className', 'x');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/006.htm
+createElement('x');
+element.classList.add('x');
+shouldBeEqualToString('element.className', 'x');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/007.htm
+createElement('x  x');
+element.classList.add('x');
+shouldBeEqualToString('element.className', 'x  x');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/008.htm
+createElement('y');
+element.classList.add('x');
+shouldBeEqualToString('element.className', 'y x');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/009.htm
+createElement('');
+element.classList.remove('x');
+shouldBeEqualToString('element.className', '');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/010.htm
+createElement('x');
+element.classList.remove('x');
+shouldBeEqualToString('element.className', '');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/011.htm
+createElement(' y x  y ');
+element.classList.remove('x');
+shouldBeEqualToString('element.className', ' y y ');
+
+// http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/012.htm
+createElement(' x y  x ');
+element.classList.remove('x');
+shouldBeEqualToString('element.className', 'y');
+
+debug('Testing add in presence of trailing white spaces.');
+
+createElement('x ');
+element.classList.add('y');
+shouldBeEqualToString('element.className', 'x y');
+
+createElement('x\t');
+element.classList.add('y');
+shouldBeEqualToString('element.className', 'x\t y');
+
+createElement(' ');
+element.classList.add('y');
+shouldBeEqualToString('element.className', ' y');
+
+
+debug('Test invalid tokens');
+
+// Testing exception due to invalid token
+
+// shouldThrow from js-test-pre.js is not sufficient.
+function shouldThrowDOMException(f, ec)
+{
+    try {
+        f();
+        testFailed('Expected an exception');
+    } catch (ex) {
+        if (!(ex instanceof DOMException)) {
+            testFailed('Exception is not an instance of DOMException, found: ' +
+                       Object.toString.call(ex));
+            return;
+        }
+        if (ec !== ex.code) {
+            testFailed('Wrong exception code: ' + ex.code);
+            return;
+        }
+    }
+    var formattedFunction = String(f).replace(/^function.+\{\s*/m, '').
+        replace(/;?\s+\}/m, '');
+    testPassed(formattedFunction + ' threw expected DOMException with code ' + ec);
+}
+
+createElement('x');
+shouldThrowDOMException(function() {
+    element.classList.contains('');
+}, DOMException.SYNTAX_ERR);
+
+createElement('x y');
+shouldThrowDOMException(function() {
+    element.classList.contains('x y');
+}, DOMException.INVALID_CHARACTER_ERR);
+
+createElement('');
+shouldThrowDOMException(function() {
+    element.classList.add('');
+}, DOMException.SYNTAX_ERR);
+
+createElement('');
+shouldThrowDOMException(function() {
+    element.classList.add('x y');
+}, DOMException.INVALID_CHARACTER_ERR);
+
+createElement('');
+shouldThrowDOMException(function() {
+    element.classList.remove('');
+}, DOMException.SYNTAX_ERR);
+
+createElement('');
+shouldThrowDOMException(function() {
+    element.classList.remove('x y');
+}, DOMException.INVALID_CHARACTER_ERR);
+
+createElement('');
+shouldThrowDOMException(function() {
+    element.classList.toggle('');
+}, DOMException.SYNTAX_ERR);
+
+createElement('x y');
+shouldThrowDOMException(function() {
+    element.classList.toggle('x y');
+}, DOMException.INVALID_CHARACTER_ERR);
+
+
+debug('Indexing');
+
+createElement('x');
+shouldBeEqualToString('element.classList[0]', 'x');
+shouldBeEqualToString('element.classList.item(0)', 'x');
+
+createElement('x x');
+shouldBeEqualToString('element.classList[1]', 'x');
+shouldBeEqualToString('element.classList.item(1)', 'x');
+
+createElement('x y');
+shouldBeEqualToString('element.classList[1]', 'y');
+shouldBeEqualToString('element.classList.item(1)', 'y');
+
+createElement('');
+shouldBeNull('element.classList[0]');
+shouldBeNull('element.classList.item(0)');
+
+createElement('x y z');
+shouldBeNull('element.classList[4]');
+shouldBeNull('element.classList.item(4)');
+shouldBeUndefined('element.classList[-1]');  // Not a valid index so should not trigger item().
+shouldBeNull('element.classList.item(-1)');
+
+debug('Test case since DOMTokenList is case sensitive');
+
+createElement('x');
+shouldBeTrue('element.classList.contains(\'x\')');
+shouldBeFalse('element.classList.contains(\'X\')');
+shouldBeEqualToString('element.classList[0]', 'x');
+
+createElement('X');
+shouldBeTrue('element.classList.contains(\'X\')');
+shouldBeFalse('element.classList.contains(\'x\')');
+shouldBeEqualToString('element.classList[0]', 'X');
+
+
+debug('Testing whitespace');
+// U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF),
+// U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR)
+
+createElement('x\u0020y');
+shouldEvaluateTo('element.classList.length', 2);
+
+createElement('x\u0009y');
+shouldEvaluateTo('element.classList.length', 2);
+
+createElement('x\u000Ay');
+shouldEvaluateTo('element.classList.length', 2);
+
+createElement('x\u000Cy');
+shouldEvaluateTo('element.classList.length', 2);
+
+createElement('x\u000Dy');
+shouldEvaluateTo('element.classList.length', 2);
+
+
+debug('DOMTokenList presence and type');
+
+
+// Safari returns object
+// Firefox returns object
+// IE8 returns object
+// Chrome returns function
+// assertEquals('object', typeof DOMTokenList);
+shouldBeTrue('\'undefined\' != typeof DOMTokenList');
+
+shouldBeEqualToString('typeof DOMTokenList.prototype', 'object');
+
+createElement('x');
+shouldBeEqualToString('typeof element.classList', 'object');
+
+shouldEvaluateTo('element.classList.constructor', 'DOMTokenList');
+
+shouldBeTrue('element.classList === element.classList');
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/dom/Window/window-properties-expected.txt b/LayoutTests/fast/dom/Window/window-properties-expected.txt
index b5455de..cc1d0be 100644
--- a/LayoutTests/fast/dom/Window/window-properties-expected.txt
+++ b/LayoutTests/fast/dom/Window/window-properties-expected.txt
@@ -561,6 +561,14 @@ window.DOMStringList.prototype.contains [function]
 window.DOMStringList.prototype.item [function]
 window.DOMStringMap [object DOMStringMapConstructor]
 window.DOMStringMap.prototype [object DOMStringMapPrototype]
+window.DOMTokenList [object DOMTokenListConstructor]
+window.DOMTokenList.prototype [object DOMTokenListPrototype]
+window.DOMTokenList.prototype.add [function]
+window.DOMTokenList.prototype.contains [function]
+window.DOMTokenList.prototype.item [function]
+window.DOMTokenList.prototype.remove [function]
+window.DOMTokenList.prototype.toString [function]
+window.DOMTokenList.prototype.toggle [function]
 window.Date [function]
 window.Document [object DocumentConstructor]
 window.Document.prototype [object DocumentPrototype]
diff --git a/LayoutTests/fast/dom/Window/window-property-descriptors-expected.txt b/LayoutTests/fast/dom/Window/window-property-descriptors-expected.txt
index 08d6664..f5e763f 100644
--- a/LayoutTests/fast/dom/Window/window-property-descriptors-expected.txt
+++ b/LayoutTests/fast/dom/Window/window-property-descriptors-expected.txt
@@ -40,6 +40,7 @@ PASS typeof Object.getOwnPropertyDescriptor(window, 'DOMImplementation') is 'obj
 PASS typeof Object.getOwnPropertyDescriptor(window, 'DOMParser') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'DOMStringList') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'DOMStringMap') is 'object'
+PASS typeof Object.getOwnPropertyDescriptor(window, 'DOMTokenList') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'Date') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'Document') is 'object'
 PASS typeof Object.getOwnPropertyDescriptor(window, 'DocumentFragment') is 'object'
diff --git a/LayoutTests/fast/dom/prototype-inheritance-2-expected.txt b/LayoutTests/fast/dom/prototype-inheritance-2-expected.txt
index 1d1f5c5..3451d16 100644
--- a/LayoutTests/fast/dom/prototype-inheritance-2-expected.txt
+++ b/LayoutTests/fast/dom/prototype-inheritance-2-expected.txt
@@ -77,6 +77,9 @@ PASS DOMImplementationPrototype from inner.document.forms.testForm.0.ownerDocume
 PASS DOMStringMap from inner.document.forms.testForm.0.dataset
 PASS DOMStringMapConstructor from inner.document.forms.testForm.0.dataset.constructor
 PASS DOMStringMapPrototype from inner.document.forms.testForm.0.dataset.__proto__
+PASS DOMTokenList from inner.document.forms.testForm.0.0.classList
+PASS DOMTokenListConstructor from inner.document.forms.testForm.0.classList.constructor
+PASS DOMTokenListPrototype from inner.document.forms.testForm.0.0.classList.__proto__
 PASS DOMWindow from inner
 PASS DOMWindowPrototype from inner.document.forms.testForm.0.ownerDocument.defaultView.__proto__
 PASS DocumentPrototype from inner.document.forms.testForm.0.ownerDocument.__proto__.__proto__
diff --git a/LayoutTests/fast/dom/prototype-inheritance-expected.txt b/LayoutTests/fast/dom/prototype-inheritance-expected.txt
index 3b45ce8..fa6476a 100644
--- a/LayoutTests/fast/dom/prototype-inheritance-expected.txt
+++ b/LayoutTests/fast/dom/prototype-inheritance-expected.txt
@@ -77,6 +77,8 @@ PASS inner.DOMStringList.isInner is true
 PASS inner.DOMStringList.constructor.isInner is true
 PASS inner.DOMStringMap.isInner is true
 PASS inner.DOMStringMap.constructor.isInner is true
+PASS inner.DOMTokenList.isInner is true
+PASS inner.DOMTokenList.constructor.isInner is true
 PASS inner.Date.isInner is true
 PASS inner.Date.constructor.isInner is true
 PASS inner.Document.isInner is true
diff --git a/LayoutTests/fast/js/global-constructors-expected.txt b/LayoutTests/fast/js/global-constructors-expected.txt
index 97893da..2143b86 100644
--- a/LayoutTests/fast/js/global-constructors-expected.txt
+++ b/LayoutTests/fast/js/global-constructors-expected.txt
@@ -38,6 +38,7 @@ PASS DOMImplementation.toString() is '[object DOMImplementationConstructor]'
 PASS DOMParser.toString() is '[object DOMParserConstructor]'
 PASS DOMStringList.toString() is '[object DOMStringListConstructor]'
 PASS DOMStringMap.toString() is '[object DOMStringMapConstructor]'
+PASS DOMTokenList.toString() is '[object DOMTokenListConstructor]'
 PASS Document.toString() is '[object DocumentConstructor]'
 PASS DocumentFragment.toString() is '[object DocumentFragmentConstructor]'
 PASS DocumentType.toString() is '[object DocumentTypeConstructor]'
diff --git a/LayoutTests/perf/class-list-remove-expected.txt b/LayoutTests/perf/class-list-remove-expected.txt
new file mode 100644
index 0000000..2e38133
--- /dev/null
+++ b/LayoutTests/perf/class-list-remove-expected.txt
@@ -0,0 +1,4 @@
+Tests that classList remove is linear.
+PASS
+PASS
+
diff --git a/LayoutTests/perf/class-list-remove.html b/LayoutTests/perf/class-list-remove.html
new file mode 100644
index 0000000..f03e9ba
--- /dev/null
+++ b/LayoutTests/perf/class-list-remove.html
@@ -0,0 +1,39 @@
+<script src="../resources/magnitude-perf.js"></script>
+<script>
+
+var element, s;
+
+// Test 1 tests that remove is linear when there are N class names.
+
+function setupFunction1(magnitude)
+{
+    s = 'b';
+    element = document.createElement('div');
+    element.className = Array(magnitude).join('a ') + s;
+}
+
+function test1(magnitude)
+{
+    element.classList.remove(s);
+}
+
+// Test 2 tests that remove is linear when the length of the class name is N.
+
+function setupFunction2(magnitude)
+{
+    element = document.createElement('div');
+}
+
+function test2(magnitude)
+{
+    var s = Array(magnitude + 1).join('a');
+    element.className = s;
+    element.classList.remove(s);
+}
+
+Magnitude.description('Tests that classList remove is linear.');
+Magnitude.run(setupFunction1, test1, Magnitude.LINEAR);
+Magnitude.run(setupFunction2, test2, Magnitude.LINEAR);
+
+</script>
+</body>
diff --git a/WebCore/Android.derived.jscbindings.mk b/WebCore/Android.derived.jscbindings.mk
index 486d8cd..a927f11 100644
--- a/WebCore/Android.derived.jscbindings.mk
+++ b/WebCore/Android.derived.jscbindings.mk
@@ -159,6 +159,7 @@ GEN := \
     $(intermediates)/html/JSBlob.h \
     $(intermediates)/html/JSBlobBuilder.h \
     $(intermediates)/html/JSDOMFormData.h \
+    $(intermediates)/html/JSDOMTokenList.h \
     $(intermediates)/html/JSDataGridColumn.h \
     $(intermediates)/html/JSDataGridColumnList.h \
     $(intermediates)/html/JSFile.h \
diff --git a/WebCore/Android.derived.v8bindings.mk b/WebCore/Android.derived.v8bindings.mk
index 9b8f22c..a73157d 100644
--- a/WebCore/Android.derived.v8bindings.mk
+++ b/WebCore/Android.derived.v8bindings.mk
@@ -140,6 +140,7 @@ $(patsubst %.h,%.cpp,$(GEN)): $(intermediates)/bindings/%.cpp : $(intermediates)
 GEN := \
     $(intermediates)/bindings/V8Blob.h \
     $(intermediates)/bindings/V8BlobBuilder.h \
+    $(intermediates)/bindings/V8DOMTokenList.h \
     $(intermediates)/bindings/V8DataGridColumn.h \
     $(intermediates)/bindings/V8DataGridColumnList.h \
     $(intermediates)/bindings/V8File.h \
diff --git a/WebCore/Android.jscbindings.mk b/WebCore/Android.jscbindings.mk
index e07ed1e..0816113 100644
--- a/WebCore/Android.jscbindings.mk
+++ b/WebCore/Android.jscbindings.mk
@@ -83,6 +83,7 @@ LOCAL_SRC_FILES += \
 	bindings/js/JSDOMBinding.cpp \
 	bindings/js/JSDOMFormDataCustom.cpp \
 	bindings/js/JSDOMGlobalObject.cpp \
+	bindings/js/JSDOMTokenList.cpp \
 	bindings/js/JSDOMWindowBase.cpp \
 	bindings/js/JSDOMWindowCustom.cpp \
 	bindings/js/JSDOMWindowShell.cpp \
diff --git a/WebCore/CMakeLists.txt b/WebCore/CMakeLists.txt
index faed8ee..50f64cf 100644
--- a/WebCore/CMakeLists.txt
+++ b/WebCore/CMakeLists.txt
@@ -205,6 +205,7 @@ SET(WebCore_IDL_FILES
     html/DataGridColumn.idl
     html/DataGridColumnList.idl
     html/DOMFormData.idl
+    html/DOMTokenList.idl
     html/HTMLAllCollection.idl
     html/HTMLAnchorElement.idl
     html/HTMLAppletElement.idl
@@ -942,6 +943,7 @@ SET(WebCore_SOURCES
     html/CollectionCache.cpp
     html/DOMDataGridDataSource.cpp
     html/DOMFormData.cpp
+    html/DOMTokenList.cpp
     html/DataGridColumn.cpp
     html/DataGridColumnList.cpp
     html/DateComponents.cpp
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index 26eb4b5..ac75c44 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,73 @@
+2010-09-27  Erik Arvidsson  <arv at chromium.org>
+
+        Reviewed by Darin Adler.
+
+        Implement HTML 5's HTMLElement.classList property
+        https://bugs.webkit.org/show_bug.cgi?id=20709
+
+        This adds a DOMTokenList class that is used for the classList property. DOMTokenList uses a SpaceSplitString for fast
+        contains check. In standards mode the existing classNames is used but in quirks mode we use an internal SpaceSplitString
+        because classList is always case sensitive.
+
+        Tests: fast/dom/HTMLElement/class-list-gc.html
+               fast/dom/HTMLElement/class-list-quirks.html
+               fast/dom/HTMLElement/class-list.html
+               perf/class-list-remove.html
+
+        * Android.derived.jscbindings.mk:
+        * Android.derived.v8bindings.mk:
+        * Android.jscbindings.mk:
+        * CMakeLists.txt:
+        * DerivedSources.cpp:
+        * DerivedSources.make:
+        * GNUmakefile.am:
+        * WebCore.gypi:
+        * WebCore.pri:
+        * WebCore.pro:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/gobject/GNUmakefile.am:
+        * bindings/js/JSElementCustom.cpp:
+        (WebCore::JSElement::markChildren): Make sure that we keep the wrapper while the element is alive.
+        * bindings/scripts/CodeGeneratorJS.pm: DOMTokenList has a string indexed getter.
+        * bindings/scripts/CodeGeneratorV8.pm:
+        * bindings/v8/custom/V8DOMTokenListCustom.cpp: Added.
+        (WebCore::toV8):
+        * dom/Element.cpp:
+        (WebCore::Element::classList):
+        (WebCore::Element::optionalClassList):
+        * dom/Element.h:
+        * dom/ElementRareData.h: This now has an OwnPtr to a ClassList if the Element::classList is ever called.
+        * dom/SpaceSplitString.h:
+        (WebCore::SpaceSplitString::isNull):
+        * dom/StyledElement.cpp:
+        (WebCore::StyledElement::classAttributeChanged): Update the classList if it exists.
+        * dom/StyledElement.h:
+        * html/DOMTokenList.cpp: Added.
+        (WebCore::validateToken):
+        (WebCore::DOMTokenList::DOMTokenList):
+        (WebCore::DOMTokenList::ref):
+        (WebCore::DOMTokenList::deref):
+        (WebCore::DOMTokenList::length):
+        (WebCore::DOMTokenList::item):
+        (WebCore::DOMTokenList::contains):
+        (WebCore::DOMTokenList::containsInternal): The internal methods do no validation of the token.
+        (WebCore::DOMTokenList::add):
+        (WebCore::DOMTokenList::addInternal):
+        (WebCore::DOMTokenList::remove):
+        (WebCore::DOMTokenList::removeInternal):
+        (WebCore::DOMTokenList::toggle):
+        (WebCore::DOMTokenList::toString):
+        (WebCore::DOMTokenList::reset):
+        (WebCore::DOMTokenList::classNames):
+        * html/DOMTokenList.h: Added.
+        (WebCore::DOMTokenList::create):
+        (WebCore::DOMTokenList::element):
+        * html/DOMTokenList.idl: Added.
+        * html/HTMLElement.idl:
+        * page/DOMWindow.cpp:
+        * page/DOMWindow.idl:
+
 2010-09-27  Kenneth Russell  <kbr at google.com>
 
         Reviewed by James Robinson.
diff --git a/WebCore/DerivedSources.cpp b/WebCore/DerivedSources.cpp
index bab8109..898da5c 100644
--- a/WebCore/DerivedSources.cpp
+++ b/WebCore/DerivedSources.cpp
@@ -91,6 +91,7 @@
 #include "JSDOMSelection.cpp"
 #include "JSDOMStringList.cpp"
 #include "JSDOMStringMap.cpp"
+#include "JSDOMTokenList.cpp"
 #include "JSDOMWindow.cpp"
 #include "JSElement.cpp"
 #include "JSEntity.cpp"
diff --git a/WebCore/DerivedSources.make b/WebCore/DerivedSources.make
index adbd30c..2fda5e9 100644
--- a/WebCore/DerivedSources.make
+++ b/WebCore/DerivedSources.make
@@ -124,6 +124,7 @@ DOM_CLASSES = \
     DOMSelection \
     DOMStringList \
     DOMStringMap \
+    DOMTokenList \
     DOMWindow \
     Database \
     DatabaseCallback \
diff --git a/WebCore/GNUmakefile.am b/WebCore/GNUmakefile.am
index 44c71b5..882ca7c 100644
--- a/WebCore/GNUmakefile.am
+++ b/WebCore/GNUmakefile.am
@@ -216,6 +216,8 @@ webcore_built_sources += \
 	DerivedSources/WebCore/JSDOMStringList.h \
 	DerivedSources/WebCore/JSDOMStringMap.cpp \
 	DerivedSources/WebCore/JSDOMStringMap.h \
+	DerivedSources/WebCore/JSDOMTokenList.cpp \
+	DerivedSources/WebCore/JSDOMTokenList.h \
 	DerivedSources/WebCore/JSDOMWindow.cpp \
 	DerivedSources/WebCore/JSDOMWindow.h \
 	DerivedSources/WebCore/JSElement.cpp \
@@ -1433,6 +1435,8 @@ webcore_sources += \
 	WebCore/html/canvas/CanvasRenderingContext.h \
 	WebCore/html/canvas/CanvasStyle.cpp \
 	WebCore/html/canvas/CanvasStyle.h \
+	WebCore/html/DOMTokenList.cpp \
+	WebCore/html/DOMTokenList.h \
 	WebCore/html/CollectionCache.cpp \
 	WebCore/html/CollectionCache.h \
 	WebCore/html/CollectionType.h \
diff --git a/WebCore/WebCore.gypi b/WebCore/WebCore.gypi
index ca655d5..ee65f47 100644
--- a/WebCore/WebCore.gypi
+++ b/WebCore/WebCore.gypi
@@ -111,6 +111,7 @@
             'fileapi/Metadata.idl',
             'fileapi/MetadataCallback.idl',
             'html/DOMFormData.idl',
+            'html/DOMTokenList.idl',
             'html/DataGridColumn.idl',
             'html/DataGridColumnList.idl',
             'html/HTMLAllCollection.idl',
@@ -766,6 +767,7 @@
             'bindings/v8/custom/V8DocumentLocationCustom.cpp',
             'bindings/v8/custom/V8DOMFormDataCustom.cpp',
             'bindings/v8/custom/V8DOMStringMapCustom.cpp',
+            'bindings/v8/custom/V8DOMTokenListCustom.cpp',
             'bindings/v8/custom/V8DOMWindowCustom.cpp',
             'bindings/v8/custom/V8DocumentCustom.cpp',
             'bindings/v8/custom/V8ElementCustom.cpp',
@@ -1517,6 +1519,8 @@
             'html/DOMDataGridDataSource.h',
             'html/DOMFormData.cpp',
             'html/DOMFormData.h',
+            'html/DOMTokenList.cpp',
+            'html/DOMTokenList.h',
             'html/DataGridColumn.cpp',
             'html/DataGridColumn.h',
             'html/DataGridColumnList.cpp',
diff --git a/WebCore/WebCore.pri b/WebCore/WebCore.pri
index 67b5fdb..d15c343 100644
--- a/WebCore/WebCore.pri
+++ b/WebCore/WebCore.pri
@@ -201,6 +201,7 @@ IDL_BINDINGS += \
     html/DataGridColumn.idl \
     html/DataGridColumnList.idl \
     html/DOMFormData.idl \
+    html/DOMTokenList.idl \
     html/HTMLAllCollection.idl \
     html/HTMLAudioElement.idl \
     html/HTMLAnchorElement.idl \
diff --git a/WebCore/WebCore.pro b/WebCore/WebCore.pro
index 93076d6..337d227 100644
--- a/WebCore/WebCore.pro
+++ b/WebCore/WebCore.pro
@@ -403,7 +403,8 @@ v8 {
         bindings/v8/custom/V8InjectedScriptHostCustom.cpp \
         bindings/v8/custom/V8InspectorFrontendHostCustom.cpp \
         bindings/v8/custom/V8CustomEventListener.cpp \
-        bindings/v8/custom/V8DOMStringMapCustom.cpp
+        bindings/v8/custom/V8DOMStringMapCustom.cpp \
+        bindings/v8/custom/V8DOMTokenListCustom.cpp
 
     SOURCES += \
         bindings/v8/custom/V8CustomSQLStatementErrorCallback.cpp \
@@ -838,6 +839,7 @@ SOURCES += \
     html/CollectionCache.cpp \
     html/DOMDataGridDataSource.cpp \
     html/DOMFormData.cpp \
+    html/DOMTokenList.cpp \
     html/DataGridColumn.cpp \
     html/DataGridColumnList.cpp \
     html/DateComponents.cpp \
@@ -1716,6 +1718,7 @@ HEADERS += \
     html/DateComponents.h \
     html/DOMDataGridDataSource.h \
     html/DOMFormData.h \
+    html/DOMTokenList.h \
     html/FormDataList.h \
     html/FTPDirectoryDocument.h \
     html/HTMLAllCollection.h \
diff --git a/WebCore/WebCore.vcproj/WebCore.vcproj b/WebCore/WebCore.vcproj/WebCore.vcproj
index 6e84f7c..83ca957 100644
--- a/WebCore/WebCore.vcproj/WebCore.vcproj
+++ b/WebCore/WebCore.vcproj/WebCore.vcproj
@@ -4033,6 +4033,14 @@
 				>
 			</File>
 			<File
+				RelativePath="$(WebKitOutputDir)\obj\$(ProjectName)\DerivedSources\JSDOMTokenList.cpp"
+				>
+			</File>
+			<File
+				RelativePath="$(WebKitOutputDir)\obj\$(ProjectName)\DerivedSources\JSDOMTokenList.h"
+				>
+			</File>
+			<File
 				RelativePath="$(WebKitOutputDir)\obj\$(ProjectName)\DerivedSources\JSDOMWindow.cpp"
 				>
 				<FileConfiguration
@@ -37845,6 +37853,18 @@
 				>
 			</File>
 			<File
+				RelativePath="..\html\DOMTokenList.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\html\DOMTokenList.h"
+				>
+			</File>
+			<File
+				RelativePath="..\html\FormDataList.cpp"
+				>
+			</File>
+			<File
 				RelativePath="..\html\FTPDirectoryDocument.h"
 				>
 			</File>
diff --git a/WebCore/WebCore.xcodeproj/project.pbxproj b/WebCore/WebCore.xcodeproj/project.pbxproj
index dd4a8b6..5dbf13e 100644
--- a/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -1412,12 +1412,18 @@
 		7693BAD3106C2DCA007B0823 /* PluginHalter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7693BACF106C2DCA007B0823 /* PluginHalter.cpp */; };
 		7693BAD4106C2DCA007B0823 /* PluginHalter.h in Headers */ = {isa = PBXBuildFile; fileRef = 7693BAD0106C2DCA007B0823 /* PluginHalter.h */; };
 		7693BAD5106C2DCA007B0823 /* PluginHalterClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 7693BAD1106C2DCA007B0823 /* PluginHalterClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		7694563C1214D97C0007CBAE /* JSDOMTokenList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7694563A1214D97C0007CBAE /* JSDOMTokenList.cpp */; };
+		7694563D1214D97C0007CBAE /* JSDOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 7694563B1214D97C0007CBAE /* JSDOMTokenList.h */; };
+		7694565B1214DB630007CBAE /* DOMDOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 769456591214DB630007CBAE /* DOMDOMTokenList.h */; };
+		7694565C1214DB630007CBAE /* DOMDOMTokenList.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7694565A1214DB630007CBAE /* DOMDOMTokenList.mm */; };
 		76CDD2F21103DA6600680521 /* AccessibilityMenuList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76CDD2EC1103DA6600680521 /* AccessibilityMenuList.cpp */; };
 		76CDD2F31103DA6600680521 /* AccessibilityMenuList.h in Headers */ = {isa = PBXBuildFile; fileRef = 76CDD2ED1103DA6600680521 /* AccessibilityMenuList.h */; };
 		76CDD2F41103DA6600680521 /* AccessibilityMenuListPopup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76CDD2EE1103DA6600680521 /* AccessibilityMenuListPopup.cpp */; };
 		76CDD2F51103DA6600680521 /* AccessibilityMenuListPopup.h in Headers */ = {isa = PBXBuildFile; fileRef = 76CDD2EF1103DA6600680521 /* AccessibilityMenuListPopup.h */; };
 		76CDD2F61103DA6600680521 /* AccessibilityMenuListOption.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76CDD2F01103DA6600680521 /* AccessibilityMenuListOption.cpp */; };
 		76CDD2F71103DA6600680521 /* AccessibilityMenuListOption.h in Headers */ = {isa = PBXBuildFile; fileRef = 76CDD2F11103DA6600680521 /* AccessibilityMenuListOption.h */; };
+		76FC2B0B12370DA0006A991A /* DOMTokenList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76FC2B0812370DA0006A991A /* DOMTokenList.cpp */; };
+		76FC2B0C12370DA0006A991A /* DOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 76FC2B0912370DA0006A991A /* DOMTokenList.h */; };
 		76FF17E311235673001D61B5 /* PluginViewNone.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76FF17E211235673001D61B5 /* PluginViewNone.cpp */; };
 		79AC9218109945C80021266E /* JSCompositionEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79AC9216109945C80021266E /* JSCompositionEvent.cpp */; };
 		79AC9219109945C80021266E /* JSCompositionEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 79AC9217109945C80021266E /* JSCompositionEvent.h */; };
@@ -2777,7 +2783,6 @@
 		973E325610883B7C005BC493 /* ResourceLoadNotifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 973E325410883B7C005BC493 /* ResourceLoadNotifier.cpp */; };
 		973E325710883B7C005BC493 /* ResourceLoadNotifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 973E325510883B7C005BC493 /* ResourceLoadNotifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		976D6C77122B8A3D001FD1F7 /* AsyncFileWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 976D6C58122B8A3D001FD1F7 /* AsyncFileWriter.h */; };
-		976D6C93122B8A3D001FD1F7 /* AsyncFileWriterClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 976D6C74122B8A3D001FD1F7 /* AsyncFileWriterClient.h */; };
 		976D6C78122B8A3D001FD1F7 /* Blob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 976D6C59122B8A3D001FD1F7 /* Blob.cpp */; };
 		976D6C79122B8A3D001FD1F7 /* Blob.h in Headers */ = {isa = PBXBuildFile; fileRef = 976D6C5A122B8A3D001FD1F7 /* Blob.h */; };
 		976D6C7B122B8A3D001FD1F7 /* BlobBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 976D6C5C122B8A3D001FD1F7 /* BlobBuilder.cpp */; };
@@ -2798,6 +2803,7 @@
 		976D6C8F122B8A3D001FD1F7 /* FileThreadTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 976D6C70122B8A3D001FD1F7 /* FileThreadTask.h */; };
 		976D6C90122B8A3D001FD1F7 /* FileWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 976D6C71122B8A3D001FD1F7 /* FileWriter.cpp */; };
 		976D6C91122B8A3D001FD1F7 /* FileWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 976D6C72122B8A3D001FD1F7 /* FileWriter.h */; };
+		976D6C93122B8A3D001FD1F7 /* AsyncFileWriterClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 976D6C74122B8A3D001FD1F7 /* AsyncFileWriterClient.h */; };
 		976D6C94122B8A3D001FD1F7 /* ThreadableBlobRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 976D6C75122B8A3D001FD1F7 /* ThreadableBlobRegistry.cpp */; };
 		976D6C95122B8A3D001FD1F7 /* ThreadableBlobRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 976D6C76122B8A3D001FD1F7 /* ThreadableBlobRegistry.h */; };
 		977B37231228721700B81FF8 /* HTMLElementStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 977B371F1228721700B81FF8 /* HTMLElementStack.cpp */; };
@@ -7370,12 +7376,19 @@
 		7693BACF106C2DCA007B0823 /* PluginHalter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginHalter.cpp; sourceTree = "<group>"; };
 		7693BAD0106C2DCA007B0823 /* PluginHalter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginHalter.h; sourceTree = "<group>"; };
 		7693BAD1106C2DCA007B0823 /* PluginHalterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginHalterClient.h; sourceTree = "<group>"; };
+		7694563A1214D97C0007CBAE /* JSDOMTokenList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMTokenList.cpp; sourceTree = "<group>"; };
+		7694563B1214D97C0007CBAE /* JSDOMTokenList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDOMTokenList.h; sourceTree = "<group>"; };
+		769456591214DB630007CBAE /* DOMDOMTokenList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMDOMTokenList.h; sourceTree = "<group>"; };
+		7694565A1214DB630007CBAE /* DOMDOMTokenList.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DOMDOMTokenList.mm; sourceTree = "<group>"; };
 		76CDD2EC1103DA6600680521 /* AccessibilityMenuList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilityMenuList.cpp; sourceTree = "<group>"; };
 		76CDD2ED1103DA6600680521 /* AccessibilityMenuList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilityMenuList.h; sourceTree = "<group>"; };
 		76CDD2EE1103DA6600680521 /* AccessibilityMenuListPopup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilityMenuListPopup.cpp; sourceTree = "<group>"; };
 		76CDD2EF1103DA6600680521 /* AccessibilityMenuListPopup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilityMenuListPopup.h; sourceTree = "<group>"; };
 		76CDD2F01103DA6600680521 /* AccessibilityMenuListOption.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilityMenuListOption.cpp; sourceTree = "<group>"; };
 		76CDD2F11103DA6600680521 /* AccessibilityMenuListOption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilityMenuListOption.h; sourceTree = "<group>"; };
+		76FC2B0812370DA0006A991A /* DOMTokenList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMTokenList.cpp; sourceTree = "<group>"; };
+		76FC2B0912370DA0006A991A /* DOMTokenList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMTokenList.h; sourceTree = "<group>"; };
+		76FC2B0A12370DA0006A991A /* DOMTokenList.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DOMTokenList.idl; sourceTree = "<group>"; };
 		76FF17E211235673001D61B5 /* PluginViewNone.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginViewNone.cpp; sourceTree = "<group>"; };
 		79AC9216109945C80021266E /* JSCompositionEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCompositionEvent.cpp; sourceTree = "<group>"; };
 		79AC9217109945C80021266E /* JSCompositionEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCompositionEvent.h; sourceTree = "<group>"; };
@@ -8677,7 +8690,6 @@
 		973E325410883B7C005BC493 /* ResourceLoadNotifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourceLoadNotifier.cpp; sourceTree = "<group>"; };
 		973E325510883B7C005BC493 /* ResourceLoadNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceLoadNotifier.h; sourceTree = "<group>"; };
 		976D6C58122B8A3D001FD1F7 /* AsyncFileWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncFileWriter.h; path = fileapi/AsyncFileWriter.h; sourceTree = "<group>"; };
-		976D6C74122B8A3D001FD1F7 /* AsyncFileWriterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncFileWriterClient.h; path = fileapi/AsyncFileWriterClient.h; sourceTree = "<group>"; };
 		976D6C59122B8A3D001FD1F7 /* Blob.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Blob.cpp; path = fileapi/Blob.cpp; sourceTree = "<group>"; };
 		976D6C5A122B8A3D001FD1F7 /* Blob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Blob.h; path = fileapi/Blob.h; sourceTree = "<group>"; };
 		976D6C5C122B8A3D001FD1F7 /* BlobBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BlobBuilder.cpp; path = fileapi/BlobBuilder.cpp; sourceTree = "<group>"; };
@@ -8698,6 +8710,7 @@
 		976D6C70122B8A3D001FD1F7 /* FileThreadTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileThreadTask.h; path = fileapi/FileThreadTask.h; sourceTree = "<group>"; };
 		976D6C71122B8A3D001FD1F7 /* FileWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileWriter.cpp; path = fileapi/FileWriter.cpp; sourceTree = "<group>"; };
 		976D6C72122B8A3D001FD1F7 /* FileWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileWriter.h; path = fileapi/FileWriter.h; sourceTree = "<group>"; };
+		976D6C74122B8A3D001FD1F7 /* AsyncFileWriterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncFileWriterClient.h; path = fileapi/AsyncFileWriterClient.h; sourceTree = "<group>"; };
 		976D6C75122B8A3D001FD1F7 /* ThreadableBlobRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadableBlobRegistry.cpp; path = fileapi/ThreadableBlobRegistry.cpp; sourceTree = "<group>"; };
 		976D6C76122B8A3D001FD1F7 /* ThreadableBlobRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadableBlobRegistry.h; path = fileapi/ThreadableBlobRegistry.h; sourceTree = "<group>"; };
 		977B371F1228721700B81FF8 /* HTMLElementStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HTMLElementStack.cpp; path = parser/HTMLElementStack.cpp; sourceTree = "<group>"; };
@@ -13520,6 +13533,8 @@
 			children = (
 				2E2D99E510E2BC1C00496337 /* DOMBlob.h */,
 				2E2D99E610E2BC1C00496337 /* DOMBlob.mm */,
+				769456591214DB630007CBAE /* DOMDOMTokenList.h */,
+				7694565A1214DB630007CBAE /* DOMDOMTokenList.mm */,
 				BC00EFFE0E0A185500FD04E3 /* DOMFile.h */,
 				BC00EFFF0E0A185500FD04E3 /* DOMFile.mm */,
 				2E3BC106117D479800B9409A /* DOMFileError.h */,
@@ -14143,6 +14158,9 @@
 				2ED609BA1145B07100C8684E /* DOMFormData.cpp */,
 				2ED609BB1145B07100C8684E /* DOMFormData.h */,
 				2E0888C3114883A900AF4265 /* DOMFormData.idl */,
+				76FC2B0812370DA0006A991A /* DOMTokenList.cpp */,
+				76FC2B0912370DA0006A991A /* DOMTokenList.h */,
+				76FC2B0A12370DA0006A991A /* DOMTokenList.idl */,
 				A8136D370973A8E700D74463 /* FormDataList.cpp */,
 				A8136D360973A8E700D74463 /* FormDataList.h */,
 				97205AAD123928CA00B17380 /* FTPDirectoryDocument.cpp */,
@@ -14634,6 +14652,8 @@
 				BC77D1680FF19F550070887B /* JSDataGridColumnList.h */,
 				2E0888D21148848A00AF4265 /* JSDOMFormData.cpp */,
 				2E0888D31148848A00AF4265 /* JSDOMFormData.h */,
+				7694563A1214D97C0007CBAE /* JSDOMTokenList.cpp */,
+				7694563B1214D97C0007CBAE /* JSDOMTokenList.h */,
 				49EECEF4105070C400099FAB /* JSFloat32Array.cpp */,
 				49EECEF5105070C400099FAB /* JSFloat32Array.h */,
 				BC97E410109154FA0010D361 /* JSHTMLAllCollection.cpp */,
@@ -20582,6 +20602,9 @@
 				84730D911248F0B300D3A9C9 /* LightSource.h in Headers */,
 				84730D921248F0B300D3A9C9 /* PointLightSource.h in Headers */,
 				84730D931248F0B300D3A9C9 /* SpotLightSource.h in Headers */,
+				7694563D1214D97C0007CBAE /* JSDOMTokenList.h in Headers */,
+				7694565B1214DB630007CBAE /* DOMDOMTokenList.h in Headers */,
+				76FC2B0C12370DA0006A991A /* DOMTokenList.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -23061,6 +23084,9 @@
 				84730D8A1248F0B300D3A9C9 /* FETile.cpp in Sources */,
 				84730D8C1248F0B300D3A9C9 /* FETurbulence.cpp in Sources */,
 				84730D901248F0B300D3A9C9 /* LightSource.cpp in Sources */,
+				7694563C1214D97C0007CBAE /* JSDOMTokenList.cpp in Sources */,
+				7694565C1214DB630007CBAE /* DOMDOMTokenList.mm in Sources */,
+				76FC2B0B12370DA0006A991A /* DOMTokenList.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/WebCore/bindings/gobject/GNUmakefile.am b/WebCore/bindings/gobject/GNUmakefile.am
index 12835cc..d808e18 100644
--- a/WebCore/bindings/gobject/GNUmakefile.am
+++ b/WebCore/bindings/gobject/GNUmakefile.am
@@ -49,6 +49,8 @@ webkitgtk_gdom_built_sources += \
 	DerivedSources/webkit/WebKitDOMDOMStringListPrivate.h \
 	DerivedSources/webkit/WebKitDOMDOMStringMap.cpp \
 	DerivedSources/webkit/WebKitDOMDOMStringMapPrivate.h \
+	DerivedSources/webkit/WebKitDOMDOMTokenList.cpp \
+	DerivedSources/webkit/WebKitDOMDOMTokenListPrivate.h \
 	DerivedSources/webkit/WebKitDOMDOMWindow.cpp \
 	DerivedSources/webkit/WebKitDOMDOMWindowPrivate.h \
 	DerivedSources/webkit/WebKitDOMElement.cpp \
@@ -263,6 +265,7 @@ webkitgtk_built_h_api += \
 	DerivedSources/webkit/WebKitDOMDOMImplementation.h \
 	DerivedSources/webkit/WebKitDOMDOMStringList.h \
 	DerivedSources/webkit/WebKitDOMDOMStringMap.h \
+	DerivedSources/webkit/WebKitDOMDOMTokenList.h \
 	DerivedSources/webkit/WebKitDOMElement.h \
 	DerivedSources/webkit/WebKitDOMEntityReference.h \
 	DerivedSources/webkit/WebKitDOMEvent.h \
diff --git a/WebCore/bindings/js/JSElementCustom.cpp b/WebCore/bindings/js/JSElementCustom.cpp
index 8a1df5c..f691620 100644
--- a/WebCore/bindings/js/JSElementCustom.cpp
+++ b/WebCore/bindings/js/JSElementCustom.cpp
@@ -60,6 +60,7 @@ void JSElement::markChildren(MarkStack& markStack)
     JSGlobalData& globalData = *Heap::heap(this)->globalData();
 
     markDOMObjectWrapper(markStack, globalData, element->attributeMap());
+    markDOMObjectWrapper(markStack, globalData, element->optionalClassList());
     markDOMObjectWrapper(markStack, globalData, element->optionalDataset());
 
     if (element->isStyledElement())
diff --git a/WebCore/bindings/scripts/CodeGeneratorJS.pm b/WebCore/bindings/scripts/CodeGeneratorJS.pm
index 1a114a4..803542e 100644
--- a/WebCore/bindings/scripts/CodeGeneratorJS.pm
+++ b/WebCore/bindings/scripts/CodeGeneratorJS.pm
@@ -240,7 +240,7 @@ sub IndexGetterReturnsStrings
 {
     my $type = shift;
 
-    return 1 if $type eq "CSSStyleDeclaration" or $type eq "MediaList" or $type eq "CSSVariablesDeclaration" or $type eq "DOMStringList";
+    return 1 if $type eq "CSSStyleDeclaration" or $type eq "MediaList" or $type eq "CSSVariablesDeclaration" or $type eq "DOMStringList" or $type eq "DOMTokenList";
     return 0;
 }
 
diff --git a/WebCore/bindings/scripts/CodeGeneratorV8.pm b/WebCore/bindings/scripts/CodeGeneratorV8.pm
index f74f2b1..3353f0f 100644
--- a/WebCore/bindings/scripts/CodeGeneratorV8.pm
+++ b/WebCore/bindings/scripts/CodeGeneratorV8.pm
@@ -2510,6 +2510,7 @@ sub HasCustomToV8Implementation {
     return 1 if $interfaceName eq "CanvasPixelArray";
     return 1 if $interfaceName eq "DOMStringMap";
     return 1 if $interfaceName eq "DOMWindow";
+    return 1 if $interfaceName eq "DOMTokenList";
     return 1 if $interfaceName eq "Element";
     return 1 if $interfaceName eq "HTMLDocument";
     return 1 if $interfaceName eq "HTMLElement";
diff --git a/WebCore/bindings/v8/custom/V8DOMTokenListCustom.cpp b/WebCore/bindings/v8/custom/V8DOMTokenListCustom.cpp
new file mode 100644
index 0000000..171ff5c
--- /dev/null
+++ b/WebCore/bindings/v8/custom/V8DOMTokenListCustom.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "V8DOMTokenList.h"
+
+#include "DOMTokenList.h"
+#include "V8Binding.h"
+#include "V8DOMWrapper.h"
+#include "V8Element.h"
+
+namespace WebCore {
+
+v8::Handle<v8::Value> toV8(DOMTokenList* impl)
+{
+    if (!impl)
+        return v8::Null();
+    v8::Handle<v8::Object> wrapper = V8DOMTokenList::wrap(impl);
+    // Add a hidden reference from the element to the DOMTokenList.
+    Element* element = impl->element();
+    if (!wrapper.IsEmpty() && element) {
+        v8::Handle<v8::Value> elementValue = toV8(element);
+        if (!elementValue.IsEmpty() && elementValue->IsObject())
+            V8DOMWrapper::setHiddenReference(elementValue.As<v8::Object>(), wrapper);
+    }
+    return wrapper;
+}
+
+} // namespace WebCore
diff --git a/WebCore/dom/Element.cpp b/WebCore/dom/Element.cpp
index 5c4e82b..c408790 100644
--- a/WebCore/dom/Element.cpp
+++ b/WebCore/dom/Element.cpp
@@ -33,6 +33,7 @@
 #include "CSSStyleSelector.h"
 #include "ClientRect.h"
 #include "ClientRectList.h"
+#include "DOMTokenList.h"
 #include "DatasetDOMStringMap.h"
 #include "Document.h"
 #include "DocumentFragment.h"
@@ -1573,6 +1574,21 @@ bool Element::webkitMatchesSelector(const String& selector, ExceptionCode& ec)
     return false;
 }
 
+DOMTokenList* Element::classList()
+{
+    ElementRareData* data = ensureRareData();
+    if (!data->m_classList)
+        data->m_classList = DOMTokenList::create(this);
+    return data->m_classList.get();
+}
+
+DOMTokenList* Element::optionalClassList() const
+{
+    if (!hasRareData())
+        return 0;
+    return rareData()->m_classList.get();
+}
+
 DOMStringMap* Element::dataset()
 {
     ElementRareData* data = ensureRareData();
diff --git a/WebCore/dom/Element.h b/WebCore/dom/Element.h
index d9a5085..b51e750 100644
--- a/WebCore/dom/Element.h
+++ b/WebCore/dom/Element.h
@@ -36,6 +36,7 @@ class Attribute;
 class ClientRect;
 class ClientRectList;
 class DOMStringMap;
+class DOMTokenList;
 class ElementRareData;
 class IntSize;
 
@@ -269,6 +270,9 @@ public:
 
     bool webkitMatchesSelector(const String& selectors, ExceptionCode&);
 
+    DOMTokenList* classList();
+    DOMTokenList* optionalClassList() const;
+
     DOMStringMap* dataset();
     DOMStringMap* optionalDataset() const;
 
diff --git a/WebCore/dom/ElementRareData.h b/WebCore/dom/ElementRareData.h
index 8e338ab..f1e6334 100644
--- a/WebCore/dom/ElementRareData.h
+++ b/WebCore/dom/ElementRareData.h
@@ -22,6 +22,7 @@
 #ifndef ElementRareData_h
 #define ElementRareData_h
 
+#include "DOMTokenList.h"
 #include "DatasetDOMStringMap.h"
 #include "Element.h"
 #include "NodeRareData.h"
@@ -42,6 +43,7 @@ public:
     RefPtr<RenderStyle> m_computedStyle;
 
     OwnPtr<DatasetDOMStringMap> m_datasetDOMStringMap;
+    OwnPtr<DOMTokenList> m_classList;
 };
 
 inline IntSize defaultMinimumSizeForResizing()
diff --git a/WebCore/dom/SpaceSplitString.h b/WebCore/dom/SpaceSplitString.h
index f49670b..0d3650e 100644
--- a/WebCore/dom/SpaceSplitString.h
+++ b/WebCore/dom/SpaceSplitString.h
@@ -74,6 +74,7 @@ namespace WebCore {
         bool containsAll(const SpaceSplitString& names) const { return !names.m_data || (m_data && m_data->containsAll(*names.m_data)); }
 
         size_t size() const { return m_data ? m_data->size() : 0; }
+        bool isNull() const { return !m_data; }
         const AtomicString& operator[](size_t i) const { ASSERT(i < size()); return (*m_data)[i]; }
 
     private:
diff --git a/WebCore/dom/StyledElement.cpp b/WebCore/dom/StyledElement.cpp
index a158e2e..f07fda9 100644
--- a/WebCore/dom/StyledElement.cpp
+++ b/WebCore/dom/StyledElement.cpp
@@ -28,6 +28,7 @@
 #include "CSSStyleSelector.h"
 #include "CSSStyleSheet.h"
 #include "CSSValueKeywords.h"
+#include "DOMTokenList.h"
 #include "Document.h"
 #include "HTMLNames.h"
 #include <wtf/HashFunctions.h>
@@ -217,12 +218,12 @@ void StyledElement::classAttributeChanged(const AtomicString& newClassString)
     }
     bool hasClass = i < length;
     setHasClass(hasClass);
-    if (hasClass)
+    if (hasClass) {
         attributes()->setClass(newClassString);
-    else {
-        if (attributeMap())    
-            attributeMap()->clearClass();
-    }
+        if (DOMTokenList* classList = optionalClassList())
+            classList->reset(newClassString);
+    } else if (attributeMap())
+        attributeMap()->clearClass();
     setNeedsStyleRecalc();
     dispatchSubtreeModifiedEvent();
 }
diff --git a/WebCore/dom/StyledElement.h b/WebCore/dom/StyledElement.h
index 8040dbf..4b62388 100644
--- a/WebCore/dom/StyledElement.h
+++ b/WebCore/dom/StyledElement.h
@@ -32,6 +32,7 @@
 namespace WebCore {
 
 class Attribute;
+class ClassList;
 class CSSMappedAttributeDeclaration;
 
 class StyledElement : public Element {
diff --git a/WebCore/html/DOMTokenList.cpp b/WebCore/html/DOMTokenList.cpp
new file mode 100644
index 0000000..8ee45a2
--- /dev/null
+++ b/WebCore/html/DOMTokenList.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2010, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DOMTokenList.h"
+
+#include "Element.h"
+#include "HTMLNames.h"
+#include "SpaceSplitString.h"
+#include "StringBuilder.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static bool validateToken(const AtomicString& token, ExceptionCode& ec)
+{
+    if (token.isEmpty()) {
+        ec = SYNTAX_ERR;
+        return false;
+    }
+
+    unsigned length = token.length();
+    for (unsigned i = 0; i < length; ++i) {
+        if (isClassWhitespace(token[i])) {
+            ec = INVALID_CHARACTER_ERR;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+DOMTokenList::DOMTokenList(Element* element)
+    : m_element(element)
+{
+    if (m_element->document()->inQuirksMode())
+        m_classNamesForQuirksMode.set(m_element->fastGetAttribute(classAttr), false);
+}
+
+void DOMTokenList::ref()
+{
+    m_element->ref();
+}
+
+void DOMTokenList::deref()
+{
+    m_element->deref();
+}
+
+unsigned DOMTokenList::length() const
+{
+    return classNames().size();
+}
+
+const AtomicString DOMTokenList::item(unsigned index) const
+{
+    if (index >= length())
+        return AtomicString();
+    return classNames()[index];
+}
+
+bool DOMTokenList::contains(const AtomicString& token, ExceptionCode& ec) const
+{
+    if (!validateToken(token, ec))
+        return false;
+    return containsInternal(token);
+}
+
+bool DOMTokenList::containsInternal(const AtomicString& token) const
+{
+    return classNames().contains(token);
+}
+
+void DOMTokenList::add(const AtomicString& token, ExceptionCode& ec)
+{
+    if (!validateToken(token, ec))
+        return;
+    addInternal(token);
+}
+
+void DOMTokenList::addInternal(const AtomicString& token) const
+{
+    const AtomicString& oldClassName(m_element->fastGetAttribute(classAttr));
+    if (oldClassName.isEmpty())
+        m_element->setAttribute(classAttr, token);
+    else if (!containsInternal(token)) {
+        StringBuilder builder;
+        builder.append(oldClassName);
+        if (oldClassName[oldClassName.length() - 1] != ' ')
+            builder.append(' ');
+        builder.append(token);
+        m_element->setAttribute(classAttr, builder.toString());
+    }
+}
+
+void DOMTokenList::remove(const AtomicString& token, ExceptionCode& ec)
+{
+    if (!validateToken(token, ec))
+        return;
+    removeInternal(token);
+}
+
+void DOMTokenList::removeInternal(const AtomicString& token) const
+{
+    // Check using contains first since it uses AtomicString comparisons instead
+    // of character by character testing.
+    if (!containsInternal(token))
+        return;
+
+    // Algorithm defined at http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string
+
+    const AtomicString& input = m_element->fastGetAttribute(classAttr);
+    unsigned inputLength = input.length();
+    Vector<UChar> output; // 3
+    output.reserveCapacity(inputLength);
+    unsigned position = 0; // 4
+
+    // Step 5
+    while (position < inputLength) {
+        if (isClassWhitespace(input[position])) { // 6
+            output.append(input[position++]); // 6.1, 6.2
+            continue; // 6.3
+        }
+
+        // Step 7
+        Vector<UChar> s;
+        while (position < inputLength && !isClassWhitespace(input[position]))
+            s.append(input[position++]);
+
+        // Step 8
+        if (s == token) {
+            // Step 8.1
+            while (position < inputLength && isClassWhitespace(input[position]))
+                ++position;
+
+            // Step 8.2
+            size_t j = output.size();
+            while (j > 0 && isClassWhitespace(output[j - 1]))
+                --j;
+            output.resize(j);
+
+            // Step 8.3
+            if (position < inputLength && !output.isEmpty())
+                output.append(' ');
+        } else
+            output.append(s); // Step 9
+    }
+
+    output.shrinkToFit();
+    m_element->setAttribute(classAttr, String::adopt(output));
+}
+
+bool DOMTokenList::toggle(const AtomicString& token, ExceptionCode& ec)
+{
+    if (!validateToken(token, ec))
+        return false;
+
+    if (containsInternal(token)) {
+        removeInternal(token);
+        return false;
+    }
+    addInternal(token);
+    return true;
+}
+
+String DOMTokenList::toString() const
+{
+    return m_element->fastGetAttribute(classAttr);
+}
+
+void DOMTokenList::reset(const String& newClassName)
+{
+    if (!m_classNamesForQuirksMode.isNull())
+        m_classNamesForQuirksMode.set(newClassName, false);
+}
+
+const SpaceSplitString& DOMTokenList::classNames() const
+{
+    if (!m_classNamesForQuirksMode.isNull())
+        return m_classNamesForQuirksMode;
+    return m_element->attributeMap()->classNames();
+}
+
+} // namespace WebCore
diff --git a/WebCore/html/DOMTokenList.h b/WebCore/html/DOMTokenList.h
new file mode 100644
index 0000000..fad69ca
--- /dev/null
+++ b/WebCore/html/DOMTokenList.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DOMTokenList_h
+#define DOMTokenList_h
+
+#include "ExceptionCode.h"
+#include "SpaceSplitString.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class Element;
+
+class DOMTokenList : public Noncopyable {
+public:
+    static PassOwnPtr<DOMTokenList> create(Element* element)
+    {
+        return adoptPtr(new DOMTokenList(element));
+    }
+
+    void ref();
+    void deref();
+
+    unsigned length() const;
+    const AtomicString item(unsigned index) const;
+    bool contains(const AtomicString&, ExceptionCode&) const;
+    void add(const AtomicString&, ExceptionCode&);
+    void remove(const AtomicString&, ExceptionCode&);
+    bool toggle(const AtomicString&, ExceptionCode&);
+    String toString() const;
+
+    void reset(const String&);
+
+    Element* element() { return m_element; }
+
+private:
+    DOMTokenList(Element*);
+
+    void addInternal(const AtomicString&) const;
+    bool containsInternal(const AtomicString&) const;
+    void removeInternal(const AtomicString&) const;
+
+    const SpaceSplitString& classNames() const;
+
+    Element* m_element;
+    SpaceSplitString m_classNamesForQuirksMode;
+};
+
+} // namespace WebCore
+
+#endif // DOMTokenList_h
diff --git a/WebCore/html/DOMTokenList.idl b/WebCore/html/DOMTokenList.idl
new file mode 100644
index 0000000..a46900e
--- /dev/null
+++ b/WebCore/html/DOMTokenList.idl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module core {
+
+    interface [
+        GenerateConstructor,
+        HasIndexGetter
+    ] DOMTokenList {
+        readonly attribute unsigned long length;
+        [ConvertNullStringTo=Null] DOMString item(in unsigned long index);
+        boolean contains(in DOMString token) raises(DOMException);
+        void add(in DOMString token) raises(DOMException);
+        void remove(in DOMString token) raises(DOMException);
+        boolean toggle(in DOMString token) raises(DOMException);
+
+#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
+        [DontEnum] DOMString toString();
+#endif
+    };
+
+}
diff --git a/WebCore/html/HTMLElement.idl b/WebCore/html/HTMLElement.idl
index 863f28c..b127c51 100644
--- a/WebCore/html/HTMLElement.idl
+++ b/WebCore/html/HTMLElement.idl
@@ -31,6 +31,7 @@ module html {
                  attribute [Reflect] DOMString lang;
                  attribute [Reflect] DOMString dir;
                  attribute [Reflect=class] DOMString className;
+                 readonly attribute DOMTokenList classList;
 
                  attribute long              tabIndex;
                  attribute boolean           draggable;
diff --git a/WebCore/page/DOMWindow.cpp b/WebCore/page/DOMWindow.cpp
index 5efaea7..343ca2c 100644
--- a/WebCore/page/DOMWindow.cpp
+++ b/WebCore/page/DOMWindow.cpp
@@ -40,6 +40,7 @@
 #include "DOMSelection.h"
 #include "DOMStringList.h"
 #include "DOMTimer.h"
+#include "DOMTokenList.h"
 #include "Database.h"
 #include "DatabaseCallback.h"
 #include "DeviceMotionController.h"
diff --git a/WebCore/page/DOMWindow.idl b/WebCore/page/DOMWindow.idl
index 72ca36a..8cac879 100644
--- a/WebCore/page/DOMWindow.idl
+++ b/WebCore/page/DOMWindow.idl
@@ -379,6 +379,7 @@ module window {
 //        attribute DOMImplementationListConstructor DOMImplementationList;
 //        attribute DOMImplementationSourceConstructor DOMImplementationSource;
         attribute DOMImplementationConstructor DOMImplementation;
+        attribute DOMTokenListConstructor DOMTokenList;
         attribute DocumentFragmentConstructor DocumentFragment;
         attribute DocumentConstructor Document;
         attribute NodeConstructor Node;

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list