DOMTokenList update steps for classList don't follow the spec
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Sep 2015 21:33:16 +0000 (21:33 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Sep 2015 21:33:16 +0000 (21:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148589
<rdar://problem/22547443>

Reviewed by Ryosuke Niwa and Darin Adler.

LayoutTests/imported/w3c:

Rebaseline W3C tests for DOMTokenList now that more checks are passing.

* web-platform-tests/dom/lists/DOMTokenList-stringifier-expected.txt:
* web-platform-tests/dom/nodes/Element-classlist-expected.txt:

Source/WebCore:

Update our DOMTokenList implementation to behave according to
the latest DOM specification:
https://dom.spec.whatwg.org/#interface-domtokenlist

In particular, the following changes were made:
- The stringifier now returns the result of the ordered set serializer
  for tokens. This means that we drop duplicate spaces and extra spaces
  [1][2][3].
- DOMSettableTokenList.value now returns the result of the ordered set
  serializer for tokens [4] as well.
- When the DOMTokenList's tokens are updated and there is an associated
  Element attribute, we set the attribute value to be the the ordered
  set serializer for tokens. This is according to the DOMTokenList
  update steps in the specification [6]. Chrome does not match the
  specification either.

Edge browser behaves according to the specification already. Firefox was
implementing this via [7] but no recent progress. This makes the JS API
nicer to use and interest was shown by jQuery Team.

The following refactoring was done:
- Merge ClassList / RelList into a single AttributeDOMTokenList class
  as their code is mostly duplicated and the only thing that changes
  is which attribute is associated (class vs rel). AttributeDOMTokenList
  now keeps the attribute name as a member so it could be used for any
  attribute. AttributeDOMTokenList overrides DOMTokenList's
  updateAfterTokenChange() to do update the attribute's value as per
  [6].
- We no longer use a SpaceSplitString for the internal representation
  as we need to sanitize the tokens (drop duplicates and extra spaces).
  DOMTokenList now has an internal Vector<AtomicString> containing the
  tokens that is constructed from the algorithm in [2]. As a result,
  most of the logic is now in DOMTokenList instead of its subclasses
  which means that most methods are no longer virtual. We only have
  one virtual function named updateAfterTokenChange() to do the update
  steps as AttributeDOMTokenList needs to update the associated
  attribute.

This change does not seem to impact Dromaeo.

[1] https://dom.spec.whatwg.org/#concept-ordered-set-serializer
[2] https://dom.spec.whatwg.org/#ordered%20sets
[3] https://dom.spec.whatwg.org/#stringification-behavior
[4] https://dom.spec.whatwg.org/#dom-domsettabletokenlist-value
[5] https://dom.spec.whatwg.org/#dom-domtokenlist-contains (step 2)
[6] https://dom.spec.whatwg.org/#concept-DTL-update
[7] https://bugzilla.mozilla.org/show_bug.cgi?id=869788

No new tests, already covered by existing tests.

* dom/Element.cpp:
(WebCore::Element::classAttributeChanged):
When the class attribute changes, make sure to update the associated
classList if there is one. We could do this lazily if it turns out
to be a performance problem. However, chances are this is not as
classList is rarely used and we only need to update the classList if
it was ever accessed by JS for this Element.

(WebCore::Element::insertedInto):
Drop call to clearClassListValueForQuirksMode() as we no longer need
to maintain a separate SpaceSplitString for classes when in quirks
mode. This is because AttributeDOMTokenList now has its own Vector
of classes in their original cases. It therefore no longer relies on
Element::classNames() which has its case folded when in quirks mode.

(WebCore::Element::classList):
Return a AttributeDOMTokenList instead of a ClassList.

* html/AttributeDOMTokenList.h: Added.
* html/AttributeDOMTokenList.cpp: Added.
(WebCore::AttributeDOMTokenList::AttributeDOMTokenList):
Call DOMTokenList::setValue() using the attribute's value so that
DOMTokenList can initialize its token Vector.

(WebCore::AttributeDOMTokenList::attributeValueChanged):
If the attribute value was changed by somebody else that the
AttributeDOMTokenList, call DOMTokenList::setValue() so that it
can update its token Vector.

(WebCore::AttributeDOMTokenList::updateAfterTokenChange):
This is called whenever the token Vector is changed via JS. In
this case, we update the associated attribute's value.

* html/ClassList.cpp: Removed.
* html/ClassList.h: Removed.
Now merged into AttributeDOMTokenList.

* html/DOMSettableTokenList.cpp:
* html/DOMSettableTokenList.h:
Get rid of most of the code as most of the logic is now in
DOMTokenList parent class.

* html/DOMTokenList.cpp:
(WebCore::DOMTokenList::validateToken):
Use a String parameter instead of an AtomicString as this method does
not need the input the be an AtomicString. This avoid atomizing
String unnecessarily.

(WebCore::DOMTokenList::validateTokens):
Use a modern loop.

(WebCore::DOMTokenList::contains):
No longer use containsInternal() virtual function. We can now check
the internal token Vector.

(WebCore::DOMTokenList::add):
Now update the internal Vector. Use a modern loop and try to minimize
Vector capacity reallocation.

(WebCore::DOMTokenList::remove):
Now update the internal Vector.

(WebCore::DOMTokenList::toggle):
Now update the internal Vector and refactor the code so that it is
structured exactly as the algorithm in the specification for
clarity.

(WebCore::DOMTokenList::value):
Now return the result of the ordered set serializer for tokens. This
method is used for:
- The DOMSettableTokenList.value() getter
- The DOMTokenList stringifier
- As attribute value when updating the associated attribute in
  AttributeDOMTokenList.

(WebCore::DOMTokenList::setValue):
Update the internal Vector using the algorithm in [2].

* html/DOMTokenList.h:
(WebCore::DOMTokenList::length):
No longer virtual, now returns the size of the internal token Vector.

(WebCore::DOMTokenList::item):
No longer virtual, now returns the token at the given index in the
internal Vector.

* html/HTMLAnchorElement.cpp:
(WebCore::HTMLAnchorElement::relList):
Now return a AttributeDOMTokenList.

* html/HTMLLinkElement.cpp:
(WebCore::HTMLLinkElement::relList):
Now return a AttributeDOMTokenList.

* html/RelList.cpp: Removed.
* html/RelList.h: Removed.
Now merged into AttributeDOMTokenList.

LayoutTests:

Update / rebaseline existing tests as our behavior changed.

* fast/dom/HTMLElement/class-list-expected.txt:
* fast/dom/HTMLElement/class-list-quirks-expected.txt:
* fast/dom/HTMLElement/script-tests/class-list.js:
* fast/dom/HTMLOutputElement/dom-settable-token-list-expected.txt:
* fast/dom/HTMLOutputElement/htmloutputelement-expected.txt:
* fast/dom/HTMLOutputElement/htmloutputelement.html:
* fast/dom/HTMLOutputElement/script-tests/dom-settable-token-list.js:
* fast/dom/rel-list-expected.txt:
* fast/dom/rel-list.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189632 268f45cc-cd09-0410-ab3c-d52691b4dbfc

34 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/HTMLElement/class-list-expected.txt
LayoutTests/fast/dom/HTMLElement/class-list-quirks-expected.txt
LayoutTests/fast/dom/HTMLElement/script-tests/class-list.js
LayoutTests/fast/dom/HTMLOutputElement/dom-settable-token-list-expected.txt
LayoutTests/fast/dom/HTMLOutputElement/htmloutputelement-expected.txt
LayoutTests/fast/dom/HTMLOutputElement/htmloutputelement.html
LayoutTests/fast/dom/HTMLOutputElement/script-tests/dom-settable-token-list.js
LayoutTests/fast/dom/rel-list-expected.txt
LayoutTests/fast/dom/rel-list.html
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/dom/lists/DOMTokenList-stringifier-expected.txt
LayoutTests/imported/w3c/web-platform-tests/dom/nodes/Element-classlist-expected.txt
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/ElementRareData.h
Source/WebCore/html/AttributeDOMTokenList.cpp [new file with mode: 0644]
Source/WebCore/html/AttributeDOMTokenList.h [new file with mode: 0644]
Source/WebCore/html/ClassList.cpp [deleted file]
Source/WebCore/html/ClassList.h [deleted file]
Source/WebCore/html/DOMSettableTokenList.cpp
Source/WebCore/html/DOMSettableTokenList.h
Source/WebCore/html/DOMTokenList.cpp
Source/WebCore/html/DOMTokenList.h
Source/WebCore/html/HTMLAnchorElement.cpp
Source/WebCore/html/HTMLAnchorElement.h
Source/WebCore/html/HTMLLinkElement.cpp
Source/WebCore/html/HTMLLinkElement.h
Source/WebCore/html/RelList.cpp [deleted file]
Source/WebCore/html/RelList.h [deleted file]

index 46db5f6..20651d1 100644 (file)
@@ -1,3 +1,23 @@
+2015-09-11  Chris Dumez  <cdumez@apple.com>
+
+        DOMTokenList update steps for classList don't follow the spec
+        https://bugs.webkit.org/show_bug.cgi?id=148589
+        <rdar://problem/22547443>
+
+        Reviewed by Ryosuke Niwa and Darin Adler.
+
+        Update / rebaseline existing tests as our behavior changed.
+
+        * fast/dom/HTMLElement/class-list-expected.txt:
+        * fast/dom/HTMLElement/class-list-quirks-expected.txt:
+        * fast/dom/HTMLElement/script-tests/class-list.js:
+        * fast/dom/HTMLOutputElement/dom-settable-token-list-expected.txt:
+        * fast/dom/HTMLOutputElement/htmloutputelement-expected.txt:
+        * fast/dom/HTMLOutputElement/htmloutputelement.html:
+        * fast/dom/HTMLOutputElement/script-tests/dom-settable-token-list.js:
+        * fast/dom/rel-list-expected.txt:
+        * fast/dom/rel-list.html:
+
 2015-09-11  Alex Christensen  <achristensen@webkit.org>
 
         Unreviewed gardening to make Windows bot green.
index d81edad..3dbcf8d 100644 (file)
@@ -7,7 +7,7 @@ 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 1
 PASS element.classList.length is 2
 PASS element.className is "x"
 PASS element.className is "x"
@@ -15,7 +15,7 @@ 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"
 PASS element.className is "y"
 Ensure that we can handle empty class name correctly
 PASS element.classList.toggle('x') is true
@@ -37,8 +37,8 @@ PASS element.classList.toggle('', true) threw expected DOMException with code 12
 PASS element.classList.toggle('x y', false) threw expected DOMException with code 5
 Testing add in presence of trailing white spaces.
 PASS element.className is "x y"
-PASS element.className is "x\ty"
-PASS element.className is " 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
@@ -52,8 +52,8 @@ PASS element.classList.toggle() threw exception TypeError: Not enough arguments.
 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 undefined.
+PASS element.classList.item(1) is null
 PASS element.classList[1] is "y"
 PASS element.classList.item(1) is "y"
 PASS element.classList[0] is undefined.
@@ -98,8 +98,8 @@ PASS element.classList.add("a", {toString: function() { throw new Error("user er
 PASS element.className is ""
 PASS element.classList.add() did not throw exception.
 PASS observer.takeRecords().length is 1
-PASS element.className is "b d  "
-PASS element.className is "d  "
+PASS element.className is "b d"
+PASS element.className is "d"
 PASS element.className is "a b c"
 PASS element.classList.remove('a', 'b', '') threw expected DOMException with code 12
 PASS element.className is "a b"
index d81edad..3dbcf8d 100644 (file)
@@ -7,7 +7,7 @@ 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 1
 PASS element.classList.length is 2
 PASS element.className is "x"
 PASS element.className is "x"
@@ -15,7 +15,7 @@ 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"
 PASS element.className is "y"
 Ensure that we can handle empty class name correctly
 PASS element.classList.toggle('x') is true
@@ -37,8 +37,8 @@ PASS element.classList.toggle('', true) threw expected DOMException with code 12
 PASS element.classList.toggle('x y', false) threw expected DOMException with code 5
 Testing add in presence of trailing white spaces.
 PASS element.className is "x y"
-PASS element.className is "x\ty"
-PASS element.className is " 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
@@ -52,8 +52,8 @@ PASS element.classList.toggle() threw exception TypeError: Not enough arguments.
 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 undefined.
+PASS element.classList.item(1) is null
 PASS element.classList[1] is "y"
 PASS element.classList.item(1) is "y"
 PASS element.classList[0] is undefined.
@@ -98,8 +98,8 @@ PASS element.classList.add("a", {toString: function() { throw new Error("user er
 PASS element.className is ""
 PASS element.classList.add() did not throw exception.
 PASS observer.takeRecords().length is 1
-PASS element.className is "b d  "
-PASS element.className is "d  "
+PASS element.className is "b d"
+PASS element.className is "d"
 PASS element.className is "a b c"
 PASS element.classList.remove('a', 'b', '') threw expected DOMException with code 12
 PASS element.className is "a b"
index 911d244..7dc4876 100644 (file)
@@ -31,7 +31,7 @@ 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);
+shouldEvaluateTo('element.classList.length', 1);
 
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/004.htm
 createElement('x y');
@@ -70,7 +70,7 @@ 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 ');
+shouldBeEqualToString('element.className', 'y');
 
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/012.htm
 createElement(' x y  x ');
@@ -121,11 +121,11 @@ shouldBeEqualToString('element.className', 'x y');
 
 createElement('x\t');
 element.classList.add('y');
-shouldBeEqualToString('element.className', 'x\ty');
+shouldBeEqualToString('element.className', 'x y');
 
 createElement(' ');
 element.classList.add('y');
-shouldBeEqualToString('element.className', ' y');
+shouldBeEqualToString('element.className', 'y');
 
 
 debug('Test invalid tokens');
@@ -206,8 +206,8 @@ 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');
+shouldBeUndefined('element.classList[1]');
+shouldBeNull('element.classList.item(1)');
 
 createElement('x y');
 shouldBeEqualToString('element.classList[1]', 'y');
@@ -327,10 +327,10 @@ shouldBe('observer.takeRecords().length', '1');
 
 createElement('a b c d  ');
 element.classList.remove('a', 'c');
-shouldBeEqualToString('element.className', 'b d  ');
+shouldBeEqualToString('element.className', 'b d');
 
 element.classList.remove('b', 'b');
-shouldBeEqualToString('element.className', 'd  ');
+shouldBeEqualToString('element.className', 'd');
 
 createElement('a b c null d undefined 0 false');
 element.classList.remove(null, {toString: function() { return 'd' }}, undefined, 0, false);
index 8ec624c..b3b29f7 100644 (file)
@@ -7,15 +7,15 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 PASS String(element.htmlFor) is "x"
 PASS element.htmlFor.length is 0
 PASS element.htmlFor.length is 1
-PASS element.htmlFor.length is 2
+PASS element.htmlFor.length is 1
 PASS element.htmlFor.length is 2
 PASS element.htmlFor.toString() is "x"
 PASS element.htmlFor.toString() is "x"
-PASS element.htmlFor.toString() is "x  x"
+PASS element.htmlFor.toString() is "x"
 PASS element.htmlFor.toString() is "y x"
 PASS element.htmlFor.toString() is ""
 PASS element.htmlFor.toString() is ""
-PASS element.htmlFor.toString() is " y y "
+PASS element.htmlFor.toString() is "y"
 PASS element.htmlFor.toString() is "y"
 - Ensure that we can handle empty form attribute correctly
 PASS list.value is "x"
@@ -26,8 +26,8 @@ PASS element.htmlFor.contains('x') is true
 PASS element.htmlFor[1] is undefined.
 - Testing add in presence of trailing white spaces.
 PASS element.htmlFor.toString() is "x y"
-PASS element.htmlFor.toString() is "x\ty"
-PASS element.htmlFor.toString() is " y"
+PASS element.htmlFor.toString() is "x y"
+PASS element.htmlFor.toString() is "y"
 - Test invalid tokens
 PASS element.htmlFor.contains('') threw expected DOMException with code 12
 PASS element.htmlFor.contains('x y') threw expected DOMException with code 5
@@ -40,8 +40,8 @@ PASS element.htmlFor.toggle('x y') threw expected DOMException with code 5
 - Indexing
 PASS element.htmlFor[0] is "x"
 PASS element.htmlFor.item(0) is "x"
-PASS element.htmlFor[1] is "x"
-PASS element.htmlFor.item(1) is "x"
+PASS element.htmlFor[1] is undefined.
+PASS element.htmlFor.item(1) is null
 PASS element.htmlFor[1] is "y"
 PASS element.htmlFor.item(1) is "y"
 PASS element.htmlFor[0] is undefined.
index 48b85dc..225f08a 100644 (file)
@@ -12,7 +12,7 @@ PASS output2.htmlFor.value is "for-target1"
 PASS output3.htmlFor.length is 2
 PASS output3.htmlFor[0] is "for-target1"
 PASS output3.htmlFor[1] is "for-target2"
-PASS output3.htmlFor.value is " for-target1 for-target2 "
+PASS output3.htmlFor.value is "for-target1 for-target2"
 PASS successfullyParsed is true
 
 TEST COMPLETE
index df83cea..368576a 100644 (file)
@@ -28,7 +28,7 @@ shouldBeEqualToString('output2.htmlFor.value', 'for-target1');
 shouldEvaluateTo('output3.htmlFor.length', 2);
 shouldBeEqualToString('output3.htmlFor[0]', 'for-target1');
 shouldBeEqualToString('output3.htmlFor[1]', 'for-target2');
-shouldBeEqualToString('output3.htmlFor.value', ' for-target1 for-target2 ');
+shouldBeEqualToString('output3.htmlFor.value', 'for-target1 for-target2');
 </script>
 <script src="../../../resources/js-test-post.js"></script>
 </body>
index 5cf0439..44dc678 100644 (file)
@@ -29,7 +29,7 @@ shouldEvaluateTo('element.htmlFor.length', 1);
 
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/003.htm
 createElement('x x');
-shouldEvaluateTo('element.htmlFor.length', 2);
+shouldEvaluateTo('element.htmlFor.length', 1);
 
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/004.htm
 createElement('x y');
@@ -48,7 +48,7 @@ shouldBeEqualToString('element.htmlFor.toString()', 'x');
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/007.htm
 createElement('x  x');
 element.htmlFor.add('x');
-shouldBeEqualToString('element.htmlFor.toString()', 'x  x');
+shouldBeEqualToString('element.htmlFor.toString()', 'x');
 
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/008.htm
 createElement('y');
@@ -68,7 +68,7 @@ shouldBeEqualToString('element.htmlFor.toString()', '');
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/011.htm
 createElement(' y x  y ');
 element.htmlFor.remove('x');
-shouldBeEqualToString('element.htmlFor.toString()', ' y y ');
+shouldBeEqualToString('element.htmlFor.toString()', 'y');
 
 // http://simon.html5.org/test/html/dom/reflecting/DOMTokenList/getting/012.htm
 createElement(' x y  x ');
@@ -101,11 +101,11 @@ shouldBeEqualToString('element.htmlFor.toString()', 'x y');
 
 createElement('x\t');
 element.htmlFor.add('y');
-shouldBeEqualToString('element.htmlFor.toString()', 'x\ty');
+shouldBeEqualToString('element.htmlFor.toString()', 'x y');
 
 createElement(' ');
 element.htmlFor.add('y');
-shouldBeEqualToString('element.htmlFor.toString()', ' y');
+shouldBeEqualToString('element.htmlFor.toString()', 'y');
 
 
 debug('- Test invalid tokens');
@@ -182,8 +182,8 @@ shouldBeEqualToString('element.htmlFor[0]', 'x');
 shouldBeEqualToString('element.htmlFor.item(0)', 'x');
 
 createElement('x x');
-shouldBeEqualToString('element.htmlFor[1]', 'x');
-shouldBeEqualToString('element.htmlFor.item(1)', 'x');
+shouldBeUndefined('element.htmlFor[1]');
+shouldBeNull('element.htmlFor.item(1)');
 
 createElement('x y');
 shouldBeEqualToString('element.htmlFor[1]', 'y');
index d10e907..56f263a 100644 (file)
@@ -26,7 +26,7 @@ PASS element.relList.contains("orange") is false
 PASS String(element.relList) is "x"
 PASS element.relList.length is 0
 PASS element.relList.length is 1
-PASS element.relList.length is 2
+PASS element.relList.length is 1
 PASS element.relList.length is 2
 PASS element.rel is "x"
 PASS element.rel is "x"
@@ -34,7 +34,7 @@ PASS element.rel is "x  x"
 PASS element.rel is "y x"
 PASS element.rel is ""
 PASS element.rel is ""
-PASS element.rel is " y y "
+PASS element.rel is "y"
 PASS element.rel is "y"
 Ensure that we can handle empty rel correctly
 PASS element.relList.toggle('x') is true
@@ -58,8 +58,8 @@ PASS element.relList.toggle("", true) threw exception Error: SyntaxError: DOM Ex
 PASS element.relList.toggle("x y", false) threw exception Error: InvalidCharacterError: DOM Exception 5.
 Testing add in presence of trailing white spaces.
 PASS element.rel is "x y"
-PASS element.rel is "x\ty"
-PASS element.rel is " y"
+PASS element.rel is "x y"
+PASS element.rel is "y"
 Test invalid tokens
 PASS element.relList.contains("") threw exception Error: SyntaxError: DOM Exception 12.
 PASS element.relList.contains("x y") threw exception Error: InvalidCharacterError: DOM Exception 5.
@@ -71,8 +71,8 @@ PASS element.relList.toggle("") threw exception Error: SyntaxError: DOM Exceptio
 Indexing
 PASS element.relList[0] is "x"
 PASS element.relList.item(0) is "x"
-PASS element.relList[1] is "x"
-PASS element.relList.item(1) is "x"
+PASS element.relList[1] is undefined.
+PASS element.relList.item(1) is null
 PASS element.relList[1] is "y"
 PASS element.relList.item(1) is "y"
 PASS element.relList[0] is undefined.
@@ -115,8 +115,8 @@ PASS element.relList.add("a", {toString: function() { throw new Error("user erro
 PASS element.rel is ""
 PASS element.relList.add() did not throw exception.
 PASS observer.takeRecords().length is 1
-PASS element.rel is "b d  "
-PASS element.rel is "d  "
+PASS element.rel is "b d"
+PASS element.rel is "d"
 PASS element.rel is "a b c"
 PASS element.relList.remove("a", "b", "") threw exception Error: SyntaxError: DOM Exception 12.
 PASS element.rel is "a b"
@@ -149,7 +149,7 @@ PASS element.relList.contains("orange") is false
 PASS String(element.relList) is "x"
 PASS element.relList.length is 0
 PASS element.relList.length is 1
-PASS element.relList.length is 2
+PASS element.relList.length is 1
 PASS element.relList.length is 2
 PASS element.rel is "x"
 PASS element.rel is "x"
@@ -157,7 +157,7 @@ PASS element.rel is "x  x"
 PASS element.rel is "y x"
 PASS element.rel is ""
 PASS element.rel is ""
-PASS element.rel is " y y "
+PASS element.rel is "y"
 PASS element.rel is "y"
 Ensure that we can handle empty rel correctly
 PASS element.relList.toggle('x') is true
@@ -181,8 +181,8 @@ PASS element.relList.toggle("", true) threw exception Error: SyntaxError: DOM Ex
 PASS element.relList.toggle("x y", false) threw exception Error: InvalidCharacterError: DOM Exception 5.
 Testing add in presence of trailing white spaces.
 PASS element.rel is "x y"
-PASS element.rel is "x\ty"
-PASS element.rel is " y"
+PASS element.rel is "x y"
+PASS element.rel is "y"
 Test invalid tokens
 PASS element.relList.contains("") threw exception Error: SyntaxError: DOM Exception 12.
 PASS element.relList.contains("x y") threw exception Error: InvalidCharacterError: DOM Exception 5.
@@ -194,8 +194,8 @@ PASS element.relList.toggle("") threw exception Error: SyntaxError: DOM Exceptio
 Indexing
 PASS element.relList[0] is "x"
 PASS element.relList.item(0) is "x"
-PASS element.relList[1] is "x"
-PASS element.relList.item(1) is "x"
+PASS element.relList[1] is undefined.
+PASS element.relList.item(1) is null
 PASS element.relList[1] is "y"
 PASS element.relList.item(1) is "y"
 PASS element.relList[0] is undefined.
@@ -238,8 +238,8 @@ PASS element.relList.add("a", {toString: function() { throw new Error("user erro
 PASS element.rel is ""
 PASS element.relList.add() did not throw exception.
 PASS observer.takeRecords().length is 1
-PASS element.rel is "b d  "
-PASS element.rel is "d  "
+PASS element.rel is "b d"
+PASS element.rel is "d"
 PASS element.rel is "a b c"
 PASS element.relList.remove("a", "b", "") threw exception Error: SyntaxError: DOM Exception 12.
 PASS element.rel is "a b"
@@ -272,7 +272,7 @@ PASS element.relList.contains("orange") is false
 PASS String(element.relList) is "x"
 PASS element.relList.length is 0
 PASS element.relList.length is 1
-PASS element.relList.length is 2
+PASS element.relList.length is 1
 PASS element.relList.length is 2
 PASS element.rel is "x"
 PASS element.rel is "x"
@@ -280,7 +280,7 @@ PASS element.rel is "x  x"
 PASS element.rel is "y x"
 PASS element.rel is ""
 PASS element.rel is ""
-PASS element.rel is " y y "
+PASS element.rel is "y"
 PASS element.rel is "y"
 Ensure that we can handle empty rel correctly
 PASS element.relList.toggle('x') is true
@@ -304,8 +304,8 @@ PASS element.relList.toggle("", true) threw exception Error: SyntaxError: DOM Ex
 PASS element.relList.toggle("x y", false) threw exception Error: InvalidCharacterError: DOM Exception 5.
 Testing add in presence of trailing white spaces.
 PASS element.rel is "x y"
-PASS element.rel is "x\ty"
-PASS element.rel is " y"
+PASS element.rel is "x y"
+PASS element.rel is "y"
 Test invalid tokens
 PASS element.relList.contains("") threw exception Error: SyntaxError: DOM Exception 12.
 PASS element.relList.contains("x y") threw exception Error: InvalidCharacterError: DOM Exception 5.
@@ -317,8 +317,8 @@ PASS element.relList.toggle("") threw exception Error: SyntaxError: DOM Exceptio
 Indexing
 PASS element.relList[0] is "x"
 PASS element.relList.item(0) is "x"
-PASS element.relList[1] is "x"
-PASS element.relList.item(1) is "x"
+PASS element.relList[1] is undefined.
+PASS element.relList.item(1) is null
 PASS element.relList[1] is "y"
 PASS element.relList.item(1) is "y"
 PASS element.relList[0] is undefined.
@@ -361,8 +361,8 @@ PASS element.relList.add("a", {toString: function() { throw new Error("user erro
 PASS element.rel is ""
 PASS element.relList.add() did not throw exception.
 PASS observer.takeRecords().length is 1
-PASS element.rel is "b d  "
-PASS element.rel is "d  "
+PASS element.rel is "b d"
+PASS element.rel is "d"
 PASS element.rel is "a b c"
 PASS element.relList.remove("a", "b", "") threw exception Error: SyntaxError: DOM Exception 12.
 PASS element.rel is "a b"
index cbc2c4a..093aaaa 100644 (file)
@@ -62,7 +62,7 @@ function test()
     shouldEvaluateTo('element.relList.length', 1);
 
     createElement('x x');
-    shouldEvaluateTo('element.relList.length', 2);
+    shouldEvaluateTo('element.relList.length', 1);
 
     createElement('x y');
     shouldEvaluateTo('element.relList.length', 2);
@@ -93,7 +93,7 @@ function test()
 
     createElement(' y x  y ');
     element.relList.remove('x');
-    shouldBeEqualToString('element.rel', ' y y ');
+    shouldBeEqualToString('element.rel', 'y');
 
     createElement(' x y  x ');
     element.relList.remove('x');
@@ -134,11 +134,11 @@ function test()
 
     createElement('x\t');
     element.relList.add('y');
-    shouldBeEqualToString('element.rel', 'x\ty');
+    shouldBeEqualToString('element.rel', 'x y');
 
     createElement(' ');
     element.relList.add('y');
-    shouldBeEqualToString('element.rel', ' y');
+    shouldBeEqualToString('element.rel', 'y');
 
     debug('Test invalid tokens');
 
@@ -169,8 +169,8 @@ function test()
     shouldBeEqualToString('element.relList.item(0)', 'x');
 
     createElement('x x');
-    shouldBeEqualToString('element.relList[1]', 'x');
-    shouldBeEqualToString('element.relList.item(1)', 'x');
+    shouldBeUndefined('element.relList[1]');
+    shouldBeNull('element.relList.item(1)');
 
     createElement('x y');
     shouldBeEqualToString('element.relList[1]', 'y');
@@ -260,10 +260,10 @@ function test()
 
     createElement('a b c d  ');
     element.relList.remove('a', 'c');
-    shouldBeEqualToString('element.rel', 'b d  ');
+    shouldBeEqualToString('element.rel', 'b d');
 
     element.relList.remove('b', 'b');
-    shouldBeEqualToString('element.rel', 'd  ');
+    shouldBeEqualToString('element.rel', 'd');
 
     createElement('a b c null d undefined 0 false');
     element.relList.remove(null, {toString: function() { return 'd' }}, undefined, 0, false);
@@ -300,4 +300,4 @@ test();
 </script>
 <script src="../../resources/js-test-post.js"></script>
 </body>
-</html>
\ No newline at end of file
+</html>
index 09a0e29..79097e4 100644 (file)
@@ -1,5 +1,18 @@
 2015-09-11  Chris Dumez  <cdumez@apple.com>
 
+        DOMTokenList update steps for classList don't follow the spec
+        https://bugs.webkit.org/show_bug.cgi?id=148589
+        <rdar://problem/22547443>
+
+        Reviewed by Ryosuke Niwa and Darin Adler.
+
+        Rebaseline W3C tests for DOMTokenList now that more checks are passing.
+
+        * web-platform-tests/dom/lists/DOMTokenList-stringifier-expected.txt:
+        * web-platform-tests/dom/nodes/Element-classlist-expected.txt:
+
+2015-09-11  Chris Dumez  <cdumez@apple.com>
+
         Element.tagName should be upper-case for HTML elements in HTML documents
         https://bugs.webkit.org/show_bug.cgi?id=148843
         <rdar://problem/22559081>
index 7af8e90..1dadc7f 100644 (file)
@@ -5,7 +5,7 @@ PASS DOMTokenList should be exposed for prototyping
 PASS prototyping DOMTokenList should work 
 PASS Element.classList must implement DOMTokenList 
 PASS CSS .foo selectors must not match elements without any class 
-FAIL classList must be correct for an element that has classes assert_equals: duplicates in initial string should be removed per https://dom.spec.whatwg.org/#concept-class expected 1 but got 2
+PASS classList must be correct for an element that has classes 
 PASS classList.length must be 0 for an element that has no classes 
 PASS classList must not contain an undefined class 
 PASS classList.item() must return null for out-of-range index 
@@ -13,7 +13,7 @@ PASS classList.item() must return null for negative index
 PASS classList[index] must be undefined for out-of-range index 
 PASS classList[index] must be undefined for negative index 
 PASS className should contain initial markup whitespace 
-FAIL empty classList should return the empty string since the ordered set parser skip the whitespaces assert_equals: implicit expected "" but got " "
+PASS empty classList should return the empty string since the ordered set parser skip the whitespaces 
 PASS .contains(empty_string) must throw a SYNTAX_ERR 
 PASS .add(empty_string) must throw a SYNTAX_ERR 
 PASS .remove(empty_string) must throw a SYNTAX_ERR 
@@ -43,10 +43,10 @@ PASS classList.remove must remove existing tokens
 PASS classList.remove must not break case-sensitive CSS selector matching 
 PASS classList.remove must remove duplicated tokens 
 PASS classList.remove must collapse whitespace around removed tokens 
-FAIL classList.remove must collapse whitespaces around each token assert_equals: implicit expected "token1" but got " token1"
-FAIL classList.remove must collapse whitespaces around each token and remove duplicates assert_equals: implicit expected "token1" but got "  token1 token1  "
+PASS classList.remove must collapse whitespaces around each token 
+PASS classList.remove must collapse whitespaces around each token and remove duplicates 
 PASS classList.remove must collapse whitespace when removing duplicate tokens 
-FAIL classList.add must collapse whitespaces and remove duplicates when adding a token that already exists assert_equals: implicit expected "token1" but got "  token1  token1  "
+PASS classList.add must collapse whitespaces and remove duplicates when adding a token that already exists 
 PASS classList.toggle must toggle tokens case-sensitively when adding 
 PASS classList.toggle must not break case-sensitive CSS selector matching 
 PASS classList.toggle must be able to remove tokens 
@@ -57,10 +57,10 @@ PASS classList must stringify to an empty string when all classes have been remo
 PASS classList.item(0) must return null when all classes have been removed 
 PASS classList[0] must be undefined when all classes have been removed 
 PASS classList.add should treat " " as a space 
-FAIL classList.add should treat \t as a space assert_equals: expected "a b" but got "a\tb"
-FAIL classList.add should treat \r as a space assert_equals: expected "a b" but got "a\rb"
-FAIL classList.add should treat \n as a space assert_equals: expected "a b" but got "a\nb"
-FAIL classList.add should treat \f as a space assert_equals: expected "a b" but got "a\fb"
+PASS classList.add should treat \t as a space 
+PASS classList.add should treat \r as a space 
+PASS classList.add should treat \n as a space 
+PASS classList.add should treat \f as a space 
 PASS classList.length must be read-only 
 PASS classList must be read-only 
 
index 108484b..0f31451 100644 (file)
@@ -1573,6 +1573,7 @@ set(WebCore_SOURCES
     history/HistoryItem.cpp
     history/PageCache.cpp
 
+    html/AttributeDOMTokenList.cpp
     html/BaseButtonInputType.cpp
     html/BaseCheckableInputType.cpp
     html/BaseChooserOnlyDateAndTimeInputType.cpp
@@ -1581,7 +1582,6 @@ set(WebCore_SOURCES
     html/BaseTextInputType.cpp
     html/ButtonInputType.cpp
     html/CheckboxInputType.cpp
-    html/ClassList.cpp
     html/ColorInputType.cpp
     html/DOMFormData.cpp
     html/DOMSettableTokenList.cpp
@@ -1705,7 +1705,6 @@ set(WebCore_SOURCES
     html/RadioInputType.cpp
     html/RadioNodeList.cpp
     html/RangeInputType.cpp
-    html/RelList.cpp
     html/ResetInputType.cpp
     html/RubyElement.cpp
     html/RubyTextElement.cpp
index 0367c9c..6d24f9b 100644 (file)
@@ -1,5 +1,161 @@
 2015-09-11  Chris Dumez  <cdumez@apple.com>
 
+        DOMTokenList update steps for classList don't follow the spec
+        https://bugs.webkit.org/show_bug.cgi?id=148589
+        <rdar://problem/22547443>
+
+        Reviewed by Ryosuke Niwa and Darin Adler.
+
+        Update our DOMTokenList implementation to behave according to
+        the latest DOM specification:
+        https://dom.spec.whatwg.org/#interface-domtokenlist
+
+        In particular, the following changes were made:
+        - The stringifier now returns the result of the ordered set serializer
+          for tokens. This means that we drop duplicate spaces and extra spaces
+          [1][2][3].
+        - DOMSettableTokenList.value now returns the result of the ordered set
+          serializer for tokens [4] as well.
+        - When the DOMTokenList's tokens are updated and there is an associated
+          Element attribute, we set the attribute value to be the the ordered
+          set serializer for tokens. This is according to the DOMTokenList
+          update steps in the specification [6]. Chrome does not match the
+          specification either.
+
+        Edge browser behaves according to the specification already. Firefox was
+        implementing this via [7] but no recent progress. This makes the JS API
+        nicer to use and interest was shown by jQuery Team.
+
+        The following refactoring was done:
+        - Merge ClassList / RelList into a single AttributeDOMTokenList class
+          as their code is mostly duplicated and the only thing that changes
+          is which attribute is associated (class vs rel). AttributeDOMTokenList
+          now keeps the attribute name as a member so it could be used for any
+          attribute. AttributeDOMTokenList overrides DOMTokenList's
+          updateAfterTokenChange() to do update the attribute's value as per
+          [6].
+        - We no longer use a SpaceSplitString for the internal representation
+          as we need to sanitize the tokens (drop duplicates and extra spaces).
+          DOMTokenList now has an internal Vector<AtomicString> containing the
+          tokens that is constructed from the algorithm in [2]. As a result,
+          most of the logic is now in DOMTokenList instead of its subclasses
+          which means that most methods are no longer virtual. We only have
+          one virtual function named updateAfterTokenChange() to do the update
+          steps as AttributeDOMTokenList needs to update the associated
+          attribute.
+
+        This change does not seem to impact Dromaeo.
+
+        [1] https://dom.spec.whatwg.org/#concept-ordered-set-serializer
+        [2] https://dom.spec.whatwg.org/#ordered%20sets
+        [3] https://dom.spec.whatwg.org/#stringification-behavior
+        [4] https://dom.spec.whatwg.org/#dom-domsettabletokenlist-value
+        [5] https://dom.spec.whatwg.org/#dom-domtokenlist-contains (step 2)
+        [6] https://dom.spec.whatwg.org/#concept-DTL-update
+        [7] https://bugzilla.mozilla.org/show_bug.cgi?id=869788
+
+        No new tests, already covered by existing tests.
+
+        * dom/Element.cpp:
+        (WebCore::Element::classAttributeChanged):
+        When the class attribute changes, make sure to update the associated
+        classList if there is one. We could do this lazily if it turns out
+        to be a performance problem. However, chances are this is not as
+        classList is rarely used and we only need to update the classList if
+        it was ever accessed by JS for this Element.
+
+        (WebCore::Element::insertedInto):
+        Drop call to clearClassListValueForQuirksMode() as we no longer need
+        to maintain a separate SpaceSplitString for classes when in quirks
+        mode. This is because AttributeDOMTokenList now has its own Vector
+        of classes in their original cases. It therefore no longer relies on
+        Element::classNames() which has its case folded when in quirks mode.
+
+        (WebCore::Element::classList):
+        Return a AttributeDOMTokenList instead of a ClassList.
+
+        * html/AttributeDOMTokenList.h: Added.
+        * html/AttributeDOMTokenList.cpp: Added.
+        (WebCore::AttributeDOMTokenList::AttributeDOMTokenList):
+        Call DOMTokenList::setValue() using the attribute's value so that
+        DOMTokenList can initialize its token Vector.
+
+        (WebCore::AttributeDOMTokenList::attributeValueChanged):
+        If the attribute value was changed by somebody else that the
+        AttributeDOMTokenList, call DOMTokenList::setValue() so that it
+        can update its token Vector.
+
+        (WebCore::AttributeDOMTokenList::updateAfterTokenChange):
+        This is called whenever the token Vector is changed via JS. In
+        this case, we update the associated attribute's value.
+
+        * html/ClassList.cpp: Removed.
+        * html/ClassList.h: Removed.
+        Now merged into AttributeDOMTokenList.
+
+        * html/DOMSettableTokenList.cpp:
+        * html/DOMSettableTokenList.h:
+        Get rid of most of the code as most of the logic is now in
+        DOMTokenList parent class.
+
+        * html/DOMTokenList.cpp:
+        (WebCore::DOMTokenList::validateToken):
+        Use a String parameter instead of an AtomicString as this method does
+        not need the input the be an AtomicString. This avoid atomizing
+        String unnecessarily.
+
+        (WebCore::DOMTokenList::validateTokens):
+        Use a modern loop.
+
+        (WebCore::DOMTokenList::contains):
+        No longer use containsInternal() virtual function. We can now check
+        the internal token Vector.
+
+        (WebCore::DOMTokenList::add):
+        Now update the internal Vector. Use a modern loop and try to minimize
+        Vector capacity reallocation.
+
+        (WebCore::DOMTokenList::remove):
+        Now update the internal Vector.
+
+        (WebCore::DOMTokenList::toggle):
+        Now update the internal Vector and refactor the code so that it is
+        structured exactly as the algorithm in the specification for
+        clarity.
+
+        (WebCore::DOMTokenList::value):
+        Now return the result of the ordered set serializer for tokens. This
+        method is used for:
+        - The DOMSettableTokenList.value() getter
+        - The DOMTokenList stringifier
+        - As attribute value when updating the associated attribute in
+          AttributeDOMTokenList.
+
+        (WebCore::DOMTokenList::setValue):
+        Update the internal Vector using the algorithm in [2].
+
+        * html/DOMTokenList.h:
+        (WebCore::DOMTokenList::length):
+        No longer virtual, now returns the size of the internal token Vector.
+
+        (WebCore::DOMTokenList::item):
+        No longer virtual, now returns the token at the given index in the
+        internal Vector.
+
+        * html/HTMLAnchorElement.cpp:
+        (WebCore::HTMLAnchorElement::relList):
+        Now return a AttributeDOMTokenList.
+
+        * html/HTMLLinkElement.cpp:
+        (WebCore::HTMLLinkElement::relList):
+        Now return a AttributeDOMTokenList.
+
+        * html/RelList.cpp: Removed.
+        * html/RelList.h: Removed.
+        Now merged into AttributeDOMTokenList.
+
+2015-09-11  Chris Dumez  <cdumez@apple.com>
+
         Element.tagName should be upper-case for HTML elements in HTML documents
         https://bugs.webkit.org/show_bug.cgi?id=148843
         <rdar://problem/22559081>
index d5f0119..61bacd0 100644 (file)
     </ClCompile>
     <ClCompile Include="..\editing\win\EditorWin.cpp" />
     <ClCompile Include="..\fileapi\AsyncFileStream.cpp" />
+    <ClCompile Include="..\html\AttributeDOMTokenList.cpp" />
     <ClCompile Include="..\html\BaseButtonInputType.cpp" />
     <ClCompile Include="..\html\BaseCheckableInputType.cpp" />
     <ClCompile Include="..\html\BaseClickableWithKeyInputType.cpp" />
     <ClCompile Include="..\html\canvas\CanvasRenderingContext2D.cpp" />
     <ClCompile Include="..\html\canvas\CanvasStyle.cpp" />
     <ClCompile Include="..\html\CheckboxInputType.cpp" />
-    <ClCompile Include="..\html\ClassList.cpp" />
     <ClCompile Include="..\html\ColorInputType.cpp" />
     <ClCompile Include="..\html\DateInputType.cpp" />
     <ClCompile Include="..\html\DateTimeInputType.cpp" />
     <ClCompile Include="..\html\RadioInputType.cpp" />
     <ClCompile Include="..\html\RadioNodeList.cpp" />
     <ClCompile Include="..\html\RangeInputType.cpp" />
-    <ClCompile Include="..\html\RelList.cpp" />
     <ClCompile Include="..\html\ResetInputType.cpp" />
     <ClCompile Include="..\html\RubyElement.cpp" />
     <ClCompile Include="..\html\RubyTextElement.cpp" />
     <ClInclude Include="..\editing\VisibleUnits.h" />
     <ClInclude Include="..\editing\WrapContentsInDummySpanCommand.h" />
     <ClInclude Include="..\fileapi\AsyncFileStream.h" />
+    <ClInclude Include="..\html\AttributeDOMTokenList.h" />
     <ClInclude Include="..\html\BaseButtonInputType.h" />
     <ClInclude Include="..\html\BaseCheckableInputType.h" />
     <ClInclude Include="..\html\BaseClickableWithKeyInputType.h" />
     <ClInclude Include="..\html\canvas\CanvasRenderingContext2D.h" />
     <ClInclude Include="..\html\canvas\CanvasStyle.h" />
     <ClInclude Include="..\html\CheckboxInputType.h" />
-    <ClInclude Include="..\html\ClassList.h" />
     <ClInclude Include="..\html\CollectionType.h" />
     <ClInclude Include="..\html\ColorInputType.h" />
     <ClInclude Include="..\html\DateInputType.h" />
     <ClInclude Include="..\html\RadioInputType.h" />
     <ClInclude Include="..\html\RadioNodeList.h" />
     <ClInclude Include="..\html\RangeInputType.h" />
-    <ClInclude Include="..\html\RelList.h" />
     <ClInclude Include="..\html\ResetInputType.h" />
     <ClInclude Include="..\html\RubyElement.h" />
     <ClInclude Include="..\html\RubyTextElement.h" />
index d9e33c6..175a939 100644 (file)
     <ClCompile Include="..\fileapi\AsyncFileStream.cpp">
       <Filter>html</Filter>
     </ClCompile>
+    <ClCompile Include="..\html\AttributeDOMTokenList.cpp">
+      <Filter>html</Filter>
+    </ClCompile>
     <ClCompile Include="..\html\BaseButtonInputType.cpp">
       <Filter>html</Filter>
     </ClCompile>
     <ClCompile Include="..\html\CheckboxInputType.cpp">
       <Filter>html</Filter>
     </ClCompile>
-    <ClCompile Include="..\html\ClassList.cpp">
-      <Filter>html</Filter>
-    </ClCompile>
     <ClCompile Include="..\html\ColorInputType.cpp">
       <Filter>html</Filter>
     </ClCompile>
     <ClCompile Include="..\html\RangeInputType.cpp">
       <Filter>html</Filter>
     </ClCompile>
-    <ClCompile Include="..\html\RelList.cpp">
-      <Filter>html</Filter>
-    </ClCompile>
     <ClCompile Include="..\html\ResetInputType.cpp">
       <Filter>html</Filter>
     </ClCompile>
     <ClInclude Include="..\fileapi\AsyncFileStream.h">
       <Filter>html</Filter>
     </ClInclude>
+    <ClInclude Include="..\html\AttributeDOMTokenList.h">
+      <Filter>html</Filter>
+    </ClInclude>
     <ClInclude Include="..\html\BaseButtonInputType.h">
       <Filter>html</Filter>
     </ClInclude>
     <ClInclude Include="..\html\CheckboxInputType.h">
       <Filter>html</Filter>
     </ClInclude>
-    <ClInclude Include="..\html\ClassList.h">
-      <Filter>html</Filter>
-    </ClInclude>
     <ClInclude Include="..\html\CollectionType.h">
       <Filter>html</Filter>
     </ClInclude>
     <ClInclude Include="..\html\RangeInputType.h">
       <Filter>html</Filter>
     </ClInclude>
-    <ClInclude Include="..\html\RelList.h">
-      <Filter>html</Filter>
-    </ClInclude>
     <ClInclude Include="..\html\ResetInputType.h">
       <Filter>html</Filter>
     </ClInclude>
index 52a8957..c2f758f 100644 (file)
                45BAC2B01360BBAB005DA258 /* IconURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 45BAC2AF1360BBAB005DA258 /* IconURL.h */; settings = {ATTRIBUTES = (Private, ); }; };
                45FEA5CF156DDE8C00654101 /* Decimal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45FEA5CD156DDE8C00654101 /* Decimal.cpp */; };
                45FEA5D0156DDE8C00654101 /* Decimal.h in Headers */ = {isa = PBXBuildFile; fileRef = 45FEA5CE156DDE8C00654101 /* Decimal.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               46233EEE1BA0F187000EBEBB /* AttributeDOMTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 46233EED1BA0F17A000EBEBB /* AttributeDOMTokenList.h */; };
+               46233EEF1BA0F18B000EBEBB /* AttributeDOMTokenList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46233EEC1BA0F17A000EBEBB /* AttributeDOMTokenList.cpp */; };
                4634592C1AC2271000ECB71C /* PowerObserverMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4634592B1AC2271000ECB71C /* PowerObserverMac.cpp */; };
                4634592E1AC2273C00ECB71C /* SharedTimerCF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4634592D1AC2273C00ECB71C /* SharedTimerCF.cpp */; };
                463EB6221B8789E00096ED51 /* TagCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 463EB6201B8789CB0096ED51 /* TagCollection.cpp */; };
                4A9CC81816BB9AC600EC645A /* CSSDefaultStyleSheets.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A9CC81616BB9AC600EC645A /* CSSDefaultStyleSheets.h */; };
                4A9CC82016BF9BB400EC645A /* InspectorCSSOMWrappers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A9CC81E16BF9BB400EC645A /* InspectorCSSOMWrappers.cpp */; };
                4A9CC82116BF9BB400EC645A /* InspectorCSSOMWrappers.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A9CC81F16BF9BB400EC645A /* InspectorCSSOMWrappers.h */; };
-               4ACBC0BE12713CBD0094F9B2 /* ClassList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4ACBC0BC12713CBD0094F9B2 /* ClassList.cpp */; };
-               4ACBC0BF12713CBD0094F9B2 /* ClassList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ACBC0BD12713CBD0094F9B2 /* ClassList.h */; };
                4ACBC0C312713CCA0094F9B2 /* DOMSettableTokenList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4ACBC0C012713CCA0094F9B2 /* DOMSettableTokenList.cpp */; };
                4ACBC0C412713CCA0094F9B2 /* DOMSettableTokenList.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ACBC0C112713CCA0094F9B2 /* DOMSettableTokenList.h */; };
                4ACBC0CA12713D0A0094F9B2 /* JSDOMSettableTokenList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4ACBC0C812713D0A0094F9B2 /* JSDOMSettableTokenList.cpp */; };
                B2ED97710B1F55CE00257D0F /* GraphicsContextCG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2ED97700B1F55CE00257D0F /* GraphicsContextCG.cpp */; };
                B2F34FE60E82F81400F627CD /* DNS.h in Headers */ = {isa = PBXBuildFile; fileRef = B2F34FE50E82F81400F627CD /* DNS.h */; settings = {ATTRIBUTES = (Private, ); }; };
                B2F34FE90E82F82700F627CD /* DNSCFNet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2F34FE80E82F82700F627CD /* DNSCFNet.cpp */; };
-               B2F78CFD19F2F02D0049696C /* RelList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2F78CFB19F2F02D0049696C /* RelList.cpp */; };
-               B2F78CFE19F2F02D0049696C /* RelList.h in Headers */ = {isa = PBXBuildFile; fileRef = B2F78CFC19F2F02D0049696C /* RelList.h */; };
                B2FA3D360AB75A6F000E5AC4 /* JSSVGAnimateColorElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2FA3C4E0AB75A6E000E5AC4 /* JSSVGAnimateColorElement.cpp */; };
                B2FA3D370AB75A6F000E5AC4 /* JSSVGAnimateColorElement.h in Headers */ = {isa = PBXBuildFile; fileRef = B2FA3C4F0AB75A6E000E5AC4 /* JSSVGAnimateColorElement.h */; };
                B2FA3D380AB75A6F000E5AC4 /* JSSVGAnimatedAngle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2FA3C500AB75A6E000E5AC4 /* JSSVGAnimatedAngle.cpp */; };
                45BAC2AF1360BBAB005DA258 /* IconURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconURL.h; sourceTree = "<group>"; };
                45FEA5CD156DDE8C00654101 /* Decimal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decimal.cpp; sourceTree = "<group>"; };
                45FEA5CE156DDE8C00654101 /* Decimal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decimal.h; sourceTree = "<group>"; };
+               46233EEC1BA0F17A000EBEBB /* AttributeDOMTokenList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AttributeDOMTokenList.cpp; path = ../AttributeDOMTokenList.cpp; sourceTree = "<group>"; };
+               46233EED1BA0F17A000EBEBB /* AttributeDOMTokenList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AttributeDOMTokenList.h; path = ../AttributeDOMTokenList.h; sourceTree = "<group>"; };
                4634592B1AC2271000ECB71C /* PowerObserverMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PowerObserverMac.cpp; sourceTree = "<group>"; };
                4634592D1AC2273C00ECB71C /* SharedTimerCF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SharedTimerCF.cpp; sourceTree = "<group>"; };
                463EB6201B8789CB0096ED51 /* TagCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TagCollection.cpp; sourceTree = "<group>"; };
                4A9CC81616BB9AC600EC645A /* CSSDefaultStyleSheets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSDefaultStyleSheets.h; sourceTree = "<group>"; };
                4A9CC81E16BF9BB400EC645A /* InspectorCSSOMWrappers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorCSSOMWrappers.cpp; sourceTree = "<group>"; };
                4A9CC81F16BF9BB400EC645A /* InspectorCSSOMWrappers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorCSSOMWrappers.h; sourceTree = "<group>"; };
-               4ACBC0BC12713CBD0094F9B2 /* ClassList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClassList.cpp; sourceTree = "<group>"; };
-               4ACBC0BD12713CBD0094F9B2 /* ClassList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClassList.h; sourceTree = "<group>"; };
                4ACBC0C012713CCA0094F9B2 /* DOMSettableTokenList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMSettableTokenList.cpp; sourceTree = "<group>"; };
                4ACBC0C112713CCA0094F9B2 /* DOMSettableTokenList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMSettableTokenList.h; sourceTree = "<group>"; };
                4ACBC0C212713CCA0094F9B2 /* DOMSettableTokenList.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DOMSettableTokenList.idl; sourceTree = "<group>"; };
                B2ED97700B1F55CE00257D0F /* GraphicsContextCG.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = GraphicsContextCG.cpp; sourceTree = "<group>"; };
                B2F34FE50E82F81400F627CD /* DNS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNS.h; sourceTree = "<group>"; };
                B2F34FE80E82F82700F627CD /* DNSCFNet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNSCFNet.cpp; sourceTree = "<group>"; };
-               B2F78CFB19F2F02D0049696C /* RelList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RelList.cpp; sourceTree = "<group>"; };
-               B2F78CFC19F2F02D0049696C /* RelList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RelList.h; sourceTree = "<group>"; };
                B2FA3C4E0AB75A6E000E5AC4 /* JSSVGAnimateColorElement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSSVGAnimateColorElement.cpp; sourceTree = "<group>"; };
                B2FA3C4F0AB75A6E000E5AC4 /* JSSVGAnimateColorElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSSVGAnimateColorElement.h; sourceTree = "<group>"; };
                B2FA3C500AB75A6E000E5AC4 /* JSSVGAnimatedAngle.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSSVGAnimatedAngle.cpp; sourceTree = "<group>"; };
                                830030F71B7D398800ED3AAC /* CachedHTMLCollection.h */,
                                F55B3D7D1251F12D003EF269 /* CheckboxInputType.cpp */,
                                F55B3D7E1251F12D003EF269 /* CheckboxInputType.h */,
-                               4ACBC0BC12713CBD0094F9B2 /* ClassList.cpp */,
-                               4ACBC0BD12713CBD0094F9B2 /* ClassList.h */,
                                46EBEA011B7D4D5D00BE4941 /* CollectionTraversal.h */,
                                93C441FF0F813AE100C1A634 /* CollectionType.h */,
                                BC29935C17A1DD5800BCE880 /* ColorInputType.cpp */,
                                B658FFA41522EFAA00DD5595 /* RadioNodeList.h */,
                                F55B3D991251F12D003EF269 /* RangeInputType.cpp */,
                                F55B3D9A1251F12D003EF269 /* RangeInputType.h */,
-                               B2F78CFB19F2F02D0049696C /* RelList.cpp */,
-                               B2F78CFC19F2F02D0049696C /* RelList.h */,
                                F55B3D9B1251F12D003EF269 /* ResetInputType.cpp */,
                                F55B3D9C1251F12D003EF269 /* ResetInputType.h */,
                                5824ABA01AE81116009074B7 /* RubyElement.cpp */,
                B1AD4E7713A12A7200846B27 /* track */ = {
                        isa = PBXGroup;
                        children = (
+                               46233EEC1BA0F17A000EBEBB /* AttributeDOMTokenList.cpp */,
+                               46233EED1BA0F17A000EBEBB /* AttributeDOMTokenList.h */,
                                BE88E0CC1715D2A200658D98 /* AudioTrack.cpp */,
                                BE88E0CD1715D2A200658D98 /* AudioTrack.h */,
                                BE88E0CE1715D2A200658D98 /* AudioTrack.idl */,
                                A81872200977D3C0005826D9 /* ChildNodeList.h in Headers */,
                                14D823520AF92A790004F057 /* Chrome.h in Headers */,
                                14D824080AF93AEB0004F057 /* ChromeClient.h in Headers */,
-                               4ACBC0BF12713CBD0094F9B2 /* ClassList.h in Headers */,
                                BCC0657E0F3CE1B700CD2D87 /* ClientRect.h in Headers */,
                                BCC065810F3CE1B700CD2D87 /* ClientRectList.h in Headers */,
                                85031B400A44EFC700F992E0 /* ClipboardEvent.h in Headers */,
                                97059978107D975200A50A7C /* PolicyCallback.h in Headers */,
                                9705997A107D975200A50A7C /* PolicyChecker.h in Headers */,
                                FD45A957175D414C00C21EC8 /* PolygonShape.h in Headers */,
+                               46233EEE1BA0F187000EBEBB /* AttributeDOMTokenList.h in Headers */,
                                5174E20A10A1F44F00F95E6F /* PopStateEvent.h in Headers */,
                                0668E18B0ADD9624004128E0 /* PopupMenu.h in Headers */,
                                ABC128770B33AA6D00C693D5 /* PopupMenuClient.h in Headers */,
                                6CDDE8D01770BB220016E072 /* RegionOversetState.h in Headers */,
                                26B9998F1803AE7200D01121 /* RegisterAllocator.h in Headers */,
                                85031B4C0A44EFC700F992E0 /* RegisteredEventListener.h in Headers */,
-                               B2F78CFE19F2F02D0049696C /* RelList.h in Headers */,
                                CDFC360618CA61C20026E56F /* RemoteCommandListener.h in Headers */,
                                93309E01099E64920056E581 /* RemoveCSSPropertyCommand.h in Headers */,
                                D06C0D8F0CFD11460065F43F /* RemoveFormatCommand.h in Headers */,
                                A81872250977D3C0005826D9 /* ChildNodeList.cpp in Sources */,
                                14D8238B0AF92DF60004F057 /* Chrome.cpp in Sources */,
                                ABAF22080C03B1C700B0BCF0 /* ChromeMac.mm in Sources */,
-                               4ACBC0BE12713CBD0094F9B2 /* ClassList.cpp in Sources */,
                                BCC0657D0F3CE1B700CD2D87 /* ClientRect.cpp in Sources */,
                                BCC065800F3CE1B700CD2D87 /* ClientRectList.cpp in Sources */,
                                85031B3F0A44EFC700F992E0 /* ClipboardEvent.cpp in Sources */,
                                B27535600B053814002CE64F /* IntPointCG.cpp in Sources */,
                                B275357C0B053814002CE64F /* IntPointMac.mm in Sources */,
                                B27535730B053814002CE64F /* IntRect.cpp in Sources */,
+                               46233EEF1BA0F18B000EBEBB /* AttributeDOMTokenList.cpp in Sources */,
                                B27535610B053814002CE64F /* IntRectCG.cpp in Sources */,
                                B275357D0B053814002CE64F /* IntRectMac.mm in Sources */,
                                2D46F05017B96FD2005647F0 /* IntSize.cpp in Sources */,
                                FD45A95A175D417100C21EC8 /* RectangleShape.cpp in Sources */,
                                BCAB418113E356E800D8AAF3 /* Region.cpp in Sources */,
                                85031B4B0A44EFC700F992E0 /* RegisteredEventListener.cpp in Sources */,
-                               B2F78CFD19F2F02D0049696C /* RelList.cpp in Sources */,
                                CDFC360518CA61C20026E56F /* RemoteCommandListener.cpp in Sources */,
                                CDFC360718CA696C0026E56F /* RemoteCommandListenerIOS.mm in Sources */,
                                93309E00099E64920056E581 /* RemoveCSSPropertyCommand.cpp in Sources */,
index 3a89061..258737d 100644 (file)
@@ -1346,8 +1346,10 @@ void Element::classAttributeChanged(const AtomicString& newClassString)
         elementData()->clearClass();
     }
 
-    if (hasRareData())
-        elementRareData()->clearClassListValueForQuirksMode();
+    if (hasRareData()) {
+        if (auto* classList = elementRareData()->classList())
+            classList->attributeValueChanged(newClassString);
+    }
 
     if (shouldInvalidateStyle)
         setNeedsStyleRecalc();
@@ -1515,9 +1517,6 @@ Node::InsertionNotificationRequest Element::insertedInto(ContainerNode& insertio
     if (!insertionPoint.isInTreeScope())
         return InsertionDone;
 
-    if (hasRareData())
-        elementRareData()->clearClassListValueForQuirksMode();
-
     TreeScope* newScope = &insertionPoint.treeScope();
     HTMLDocument* newDocument = !wasInDocument && inDocument() && is<HTMLDocument>(newScope->documentScope()) ? &downcast<HTMLDocument>(newScope->documentScope()) : nullptr;
     if (newScope != &treeScope())
@@ -2654,7 +2653,7 @@ DOMTokenList& Element::classList()
 {
     ElementRareData& data = ensureElementRareData();
     if (!data.classList())
-        data.setClassList(std::make_unique<ClassList>(*this));
+        data.setClassList(std::make_unique<AttributeDOMTokenList>(*this, HTMLNames::classAttr));
     return *data.classList();
 }
 
index 31c83de..5981cf4 100644 (file)
@@ -22,7 +22,7 @@
 #ifndef ElementRareData_h
 #define ElementRareData_h
 
-#include "ClassList.h"
+#include "AttributeDOMTokenList.h"
 #include "DatasetDOMStringMap.h"
 #include "NamedNodeMap.h"
 #include "NodeRareData.h"
@@ -92,14 +92,8 @@ public:
     RenderStyle* computedStyle() const { return m_computedStyle.get(); }
     void setComputedStyle(Ref<RenderStyle>&& computedStyle) { m_computedStyle = WTF::move(computedStyle); }
 
-    ClassList* classList() const { return m_classList.get(); }
-    void setClassList(std::unique_ptr<ClassList> classList) { m_classList = WTF::move(classList); }
-    void clearClassListValueForQuirksMode()
-    {
-        if (!m_classList)
-            return;
-        m_classList->clearValueForQuirksMode();
-    }
+    AttributeDOMTokenList* classList() const { return m_classList.get(); }
+    void setClassList(std::unique_ptr<AttributeDOMTokenList> classList) { m_classList = WTF::move(classList); }
 
     DatasetDOMStringMap* dataset() const { return m_dataset.get(); }
     void setDataset(std::unique_ptr<DatasetDOMStringMap> dataset) { m_dataset = WTF::move(dataset); }
@@ -140,7 +134,7 @@ private:
     RefPtr<RenderStyle> m_computedStyle;
 
     std::unique_ptr<DatasetDOMStringMap> m_dataset;
-    std::unique_ptr<ClassList> m_classList;
+    std::unique_ptr<AttributeDOMTokenList> m_classList;
     RefPtr<ShadowRoot> m_shadowRoot;
     std::unique_ptr<NamedNodeMap> m_attributeMap;
 
diff --git a/Source/WebCore/html/AttributeDOMTokenList.cpp b/Source/WebCore/html/AttributeDOMTokenList.cpp
new file mode 100644 (file)
index 0000000..88ec6f4
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Apple 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 "AttributeDOMTokenList.h"
+
+#include <wtf/TemporaryChange.h>
+
+namespace WebCore {
+
+AttributeDOMTokenList::AttributeDOMTokenList(Element& element, const QualifiedName& attributeName)
+    : m_element(element)
+    , m_attributeName(attributeName)
+{
+    setValue(m_element.getAttribute(m_attributeName));
+}
+
+void AttributeDOMTokenList::attributeValueChanged(const AtomicString& newValue)
+{
+    // Do not reset the DOMTokenList value if the attribute value was changed by us.
+    if (m_isUpdatingAttributeValue)
+        return;
+
+    DOMTokenList::setValue(newValue);
+}
+
+void AttributeDOMTokenList::updateAfterTokenChange()
+{
+    DOMTokenList::updateAfterTokenChange();
+
+    TemporaryChange<bool> inAttributeUpdate(m_isUpdatingAttributeValue, true);
+    m_element.setAttribute(m_attributeName, value());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/html/AttributeDOMTokenList.h b/Source/WebCore/html/AttributeDOMTokenList.h
new file mode 100644 (file)
index 0000000..34130fd
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 Apple 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 AttributeDOMTokenList_h
+#define AttributeDOMTokenList_h
+
+#include "DOMTokenList.h"
+#include "Element.h"
+
+namespace WebCore {
+
+class AttributeDOMTokenList final : public DOMTokenList {
+public:
+    AttributeDOMTokenList(Element&, const QualifiedName& attributeName);
+    void attributeValueChanged(const AtomicString&);
+
+private:
+    virtual void ref() override { m_element.ref(); }
+    virtual void deref() override { m_element.deref(); }
+
+    virtual Element* element() const override { return &m_element; }
+    virtual void updateAfterTokenChange() override;
+
+    Element& m_element;
+    const WebCore::QualifiedName& m_attributeName;
+    bool m_isUpdatingAttributeValue { false };
+};
+
+} // namespace WebCore
+
+#endif // AttributeDOMTokenList_h
diff --git a/Source/WebCore/html/ClassList.cpp b/Source/WebCore/html/ClassList.cpp
deleted file mode 100644 (file)
index 7e332d4..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple 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 "ClassList.h"
-
-#include "Element.h"
-#include "HTMLNames.h"
-#include "HTMLParserIdioms.h"
-
-namespace WebCore {
-
-void ClassList::ref()
-{
-    m_element.ref();
-}
-
-void ClassList::deref()
-{
-    m_element.deref();
-}
-
-unsigned ClassList::length() const
-{
-    return m_element.hasClass() ? classNames().size() : 0;
-}
-
-const AtomicString ClassList::item(unsigned index) const
-{
-    if (index >= length())
-        return AtomicString();
-    return classNames()[index];
-}
-
-Element* ClassList::element() const
-{
-    return &m_element;
-}
-
-bool ClassList::containsInternal(const AtomicString& token) const
-{
-    return m_element.hasClass() && classNames().contains(token);
-}
-
-AtomicString ClassList::value() const
-{
-    return m_element.getAttribute(HTMLNames::classAttr);
-}
-
-void ClassList::setValue(const AtomicString& value)
-{
-    m_element.setAttribute(HTMLNames::classAttr, value);
-}
-
-const SpaceSplitString& ClassList::classNames() const
-{
-    ASSERT(m_element.hasClass());
-    if (m_element.document().inQuirksMode()) {
-        if (m_classNamesForQuirksMode.isEmpty())
-            m_classNamesForQuirksMode.set(value(), false);
-        return m_classNamesForQuirksMode;
-    }
-    return m_element.elementData()->classNames();
-}
-
-} // namespace WebCore
diff --git a/Source/WebCore/html/ClassList.h b/Source/WebCore/html/ClassList.h
deleted file mode 100644 (file)
index 8aaa4c7..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple 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 ClassList_h
-#define ClassList_h
-
-#include "DOMTokenList.h"
-#include "SpaceSplitString.h"
-
-namespace WebCore {
-
-class Element;
-
-class ClassList final : public DOMTokenList {
-public:
-    ClassList(Element& element)
-        : m_element(element)
-    {
-    }
-
-    virtual void ref() override;
-    virtual void deref() override;
-
-    virtual unsigned length() const override;
-    virtual const AtomicString item(unsigned index) const override;
-
-    virtual Element* element() const override;
-
-    void clearValueForQuirksMode() { m_classNamesForQuirksMode.clear(); }
-
-private:
-    virtual bool containsInternal(const AtomicString&) const override;
-    virtual AtomicString value() const override;
-    virtual void setValue(const AtomicString&) override;
-
-    const SpaceSplitString& classNames() const;
-
-    Element& m_element;
-    mutable SpaceSplitString m_classNamesForQuirksMode;
-};
-
-} // namespace WebCore
-
-#endif // ClassList_h
index 3f3b011..45d3b39 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,32 +38,4 @@ void DOMSettableTokenList::deref()
     RefCounted<DOMSettableTokenList>::deref();
 }
 
-unsigned DOMSettableTokenList::length() const
-{
-    return m_tokens.size();
-}
-
-const AtomicString DOMSettableTokenList::item(unsigned index) const
-{
-    if (index >= length())
-        return AtomicString();
-    return m_tokens[index];
-}
-
-bool DOMSettableTokenList::containsInternal(const AtomicString& token) const
-{
-    return m_tokens.contains(token);
-}
-
-AtomicString DOMSettableTokenList::value() const
-{
-    return m_value;
-}
-
-void DOMSettableTokenList::setValue(const AtomicString& value)
-{
-    m_value = value;
-    m_tokens.set(value, false);
-}
-
 } // namespace WebCore
index 577d1f8..159eb51 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,20 +43,12 @@ public:
         return adoptRef(*new DOMSettableTokenList);
     }
 
+    // Make public.
+    using DOMTokenList::value;
+    using DOMTokenList::setValue;
+
     virtual void ref() override;
     virtual void deref() override;
-
-    virtual unsigned length() const override;
-    virtual const AtomicString item(unsigned index) const override;
-
-    virtual AtomicString value() const override;
-    virtual void setValue(const AtomicString&) override;
-
-private:
-    virtual bool containsInternal(const AtomicString&) const override;
-
-    AtomicString m_value;
-    SpaceSplitString m_tokens;
 };
 
 } // namespace WebCore
index dbd4d26..685e821 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #include "ExceptionCode.h"
 #include "HTMLParserIdioms.h"
+#include "SpaceSplitString.h"
+#include <wtf/HashSet.h>
+#include <wtf/text/AtomicStringHash.h>
 #include <wtf/text/StringBuilder.h>
 
 namespace WebCore {
 
-bool DOMTokenList::validateToken(const AtomicString& token, ExceptionCode& ec)
+bool DOMTokenList::validateToken(const String& token, ExceptionCode& ec)
 {
     if (token.isEmpty()) {
         ec = SYNTAX_ERR;
@@ -49,13 +53,12 @@ bool DOMTokenList::validateToken(const AtomicString& token, ExceptionCode& ec)
     return true;
 }
 
-bool DOMTokenList::validateTokens(const Vector<String>& tokens, ExceptionCode& ec)
+bool DOMTokenList::validateTokens(const String* tokens, size_t length, ExceptionCode& ec)
 {
-    for (size_t i = 0; i < tokens.size(); ++i) {
+    for (size_t i = 0; i < length; ++i) {
         if (!validateToken(tokens[i], ec))
             return false;
     }
-
     return true;
 }
 
@@ -63,175 +66,130 @@ bool DOMTokenList::contains(const AtomicString& token, ExceptionCode& ec) const
 {
     if (!validateToken(token, ec))
         return false;
-    return containsInternal(token);
-}
 
-void DOMTokenList::add(const AtomicString& token, ExceptionCode& ec)
-{
-    Vector<String> tokens;
-    tokens.append(token.string());
-    add(tokens, ec);
+    return m_tokens.contains(token);
 }
 
-void DOMTokenList::add(const Vector<String>& tokens, ExceptionCode& ec)
+inline void DOMTokenList::addInternal(const String* tokens, size_t length, ExceptionCode& ec)
 {
-    Vector<String> filteredTokens;
-    for (size_t i = 0; i < tokens.size(); ++i) {
+    // This is usually called with a single token.
+    Vector<AtomicString, 1> uniqueTokens;
+    uniqueTokens.reserveInitialCapacity(length);
+
+    for (size_t i = 0; i < length; ++i) {
         if (!validateToken(tokens[i], ec))
             return;
-        if (!containsInternal(tokens[i]) && !filteredTokens.contains(tokens[i]))
-            filteredTokens.append(tokens[i]);
+        if (!m_tokens.contains(tokens[i]) && !uniqueTokens.contains(tokens[i]))
+            uniqueTokens.uncheckedAppend(tokens[i]);
     }
 
-    if (filteredTokens.isEmpty())
+    if (uniqueTokens.isEmpty())
         return;
 
-    setValue(addTokens(value(), filteredTokens));
+    m_tokens.appendVector(uniqueTokens);
+    updateAfterTokenChange();
 }
 
-void DOMTokenList::remove(const AtomicString& token, ExceptionCode& ec)
+void DOMTokenList::add(const Vector<String>& tokens, ExceptionCode& ec)
 {
-    Vector<String> tokens;
-    tokens.append(token.string());
-    remove(tokens, ec);
+    addInternal(tokens.data(), tokens.size(), ec);
 }
 
-void DOMTokenList::remove(const Vector<String>& tokens, ExceptionCode& ec)
+void DOMTokenList::add(const WTF::AtomicString& token, ExceptionCode& ec)
 {
-    if (!validateTokens(tokens, ec))
-        return;
-
-    // Check using containsInternal first since it is a lot faster than going
-    // through the string character by character.
-    bool found = false;
-    for (size_t i = 0; i < tokens.size(); ++i) {
-        if (containsInternal(tokens[i])) {
-            found = true;
-            break;
-        }
-    }
-
-    if (found)
-        setValue(removeTokens(value(), tokens));
+    addInternal(&token.string(), 1, ec);
 }
 
-bool DOMTokenList::toggle(const AtomicString& token, ExceptionCode& ec)
+inline void DOMTokenList::removeInternal(const String* tokens, size_t length, ExceptionCode& ec)
 {
-    if (!validateToken(token, ec))
-        return false;
+    if (!validateTokens(tokens, length, ec))
+        return;
 
-    if (containsInternal(token)) {
-        removeInternal(token);
-        return false;
+    bool didRemoveTokens = false;
+    for (size_t i = 0; i < length; ++i) {
+        if (m_tokens.removeFirst(tokens[i]))
+            didRemoveTokens = true;
     }
-    addInternal(token);
-    return true;
-}
 
-bool DOMTokenList::toggle(const AtomicString& token, bool force, ExceptionCode& ec)
-{
-    if (!validateToken(token, ec))
-        return false;
-
-    if (force)
-        addInternal(token);
-    else
-        removeInternal(token);
-
-    return force;
-}
-
-void DOMTokenList::addInternal(const AtomicString& token)
-{
-    if (!containsInternal(token))
-        setValue(addToken(value(), token));
+    if (didRemoveTokens)
+        updateAfterTokenChange();
 }
 
-void DOMTokenList::removeInternal(const AtomicString& token)
+void DOMTokenList::remove(const Vector<String>& tokens, ExceptionCode& ec)
 {
-    // Check using contains first since it uses AtomicString comparisons instead
-    // of character by character testing.
-    if (!containsInternal(token))
-        return;
-    setValue(removeToken(value(), token));
+    removeInternal(tokens.data(), tokens.size(), ec);
 }
 
-String DOMTokenList::addToken(const AtomicString& input, const AtomicString& token)
+void DOMTokenList::remove(const WTF::AtomicString& token, ExceptionCode& ec)
 {
-    Vector<String> tokens;
-    tokens.append(token.string());
-    return addTokens(input, tokens);
+    removeInternal(&token.string(), 1, ec);
 }
 
-String DOMTokenList::addTokens(const AtomicString& input, const Vector<String>& tokens)
+bool DOMTokenList::toggle(const AtomicString& token, Optional<bool> force, ExceptionCode& ec)
 {
-    bool needsSpace = false;
+    if (!validateToken(token, ec))
+        return false;
 
-    StringBuilder builder;
-    if (!input.isEmpty()) {
-        builder.append(input);
-        needsSpace = !isHTMLSpace(input[input.length() - 1]);
+    if (m_tokens.contains(token)) {
+        if (!force.valueOr(false)) {
+            m_tokens.removeFirst(token);
+            updateAfterTokenChange();
+            return false;
+        }
+        return true;
     }
 
-    for (size_t i = 0; i < tokens.size(); ++i) {
-        if (needsSpace)
-            builder.append(' ');
-        builder.append(tokens[i]);
-        needsSpace = true;
-    }
+    if (force && !force.value())
+        return false;
 
-    return builder.toString();
+    m_tokens.append(token);
+    updateAfterTokenChange();
+    return true;
 }
 
-String DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token)
+const AtomicString& DOMTokenList::value() const
 {
-    Vector<String> tokens;
-    tokens.append(token.string());
-    return removeTokens(input, tokens);
+    if (m_cachedValue.isNull()) {
+        // https://dom.spec.whatwg.org/#concept-ordered-set-serializer
+        StringBuilder builder;
+        for (auto& token : m_tokens) {
+            if (!builder.isEmpty())
+                builder.append(' ');
+            builder.append(token);
+        }
+        m_cachedValue = builder.toAtomicString();
+        ASSERT(!m_cachedValue.isNull());
+    }
+    return m_cachedValue;
 }
 
-String DOMTokenList::removeTokens(const AtomicString& input, const Vector<String>& tokens)
+void DOMTokenList::setValue(const String& value)
 {
-    // Algorithm defined at http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string
-    // New spec is at http://dom.spec.whatwg.org/#remove-a-token-from-a-string
-
-    unsigned inputLength = input.length();
-    StringBuilder output; // 3
-    output.reserveCapacity(inputLength);
-    unsigned position = 0; // 4
-
-    // Step 5
-    while (position < inputLength) {
-        if (isHTMLSpace(input[position])) { // 6
-            output.append(input[position++]); // 6.1, 6.2
-            continue; // 6.3
+    // Clear tokens but not capacity.
+    m_tokens.shrink(0);
+
+    HashSet<AtomicString> addedTokens;
+    // https://dom.spec.whatwg.org/#ordered%20sets
+    for (unsigned start = 0; ; ) {
+        while (start < value.length() && isHTMLSpace(value[start]))
+            ++start;
+        if (start >= value.length())
+            break;
+        unsigned end = start + 1;
+        while (end < value.length() && !isHTMLSpace(value[end]))
+            ++end;
+
+        AtomicString token = value.substring(start, end - start);
+        if (!addedTokens.contains(token)) {
+            m_tokens.append(token);
+            addedTokens.add(token);
         }
 
-        // Step 7
-        StringBuilder s;
-        while (position < inputLength && isNotHTMLSpace(input[position]))
-            s.append(input[position++]);
-
-        // Step 8
-        if (tokens.contains(s.toStringPreserveCapacity())) {
-            // Step 8.1
-            while (position < inputLength && isHTMLSpace(input[position]))
-                ++position;
-
-            // Step 8.2
-            size_t j = output.length();
-            while (j > 0 && isHTMLSpace(output[j - 1]))
-                --j;
-            output.resize(j);
-
-            // Step 8.3
-            if (position < inputLength && !output.isEmpty())
-                output.append(' ');
-        } else
-            output.append(s.toStringPreserveCapacity()); // Step 9
+        start = end + 1;
     }
 
-    return output.toString();
+    m_tokens.shrinkToFit();
+    m_cachedValue = nullAtom;
 }
 
 } // namespace WebCore
index 7ca3efa..e1f5232 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -25,8 +26,9 @@
 #ifndef DOMTokenList_h
 #define DOMTokenList_h
 
-#include <wtf/text/AtomicString.h>
+#include <wtf/Optional.h>
 #include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
 
 namespace WebCore {
 
@@ -37,14 +39,13 @@ typedef int ExceptionCode;
 class DOMTokenList {
     WTF_MAKE_NONCOPYABLE(DOMTokenList); WTF_MAKE_FAST_ALLOCATED;
 public:
-    DOMTokenList() { }
-    virtual ~DOMTokenList() {};
+    virtual ~DOMTokenList() { }
 
     virtual void ref() = 0;
     virtual void deref() = 0;
 
-    virtual unsigned length() const = 0;
-    virtual const AtomicString item(unsigned index) const = 0;
+    unsigned length() const;
+    const AtomicString& item(unsigned index) const;
 
     bool contains(const AtomicString&, ExceptionCode&) const;
     void add(const Vector<String>&, ExceptionCode&);
@@ -52,28 +53,44 @@ public:
     void remove(const Vector<String>&, ExceptionCode&);
     void remove(const AtomicString&, ExceptionCode&);
     bool toggle(const AtomicString&, ExceptionCode&);
-    bool toggle(const AtomicString&, bool force, ExceptionCode&);
+    bool toggle(const AtomicString&, Optional<bool> force, ExceptionCode&);
 
-    AtomicString toString() const { return value(); }
+    const AtomicString& toString() const { return value(); }
 
-    virtual Element* element() const { return 0; }
+    virtual Element* element() const { return nullptr; }
 
 protected:
-    virtual AtomicString value() const = 0;
-    virtual void setValue(const AtomicString&) = 0;
-
-    void addInternal(const AtomicString&);
-    virtual bool containsInternal(const AtomicString&) const = 0;
-    void removeInternal(const AtomicString&);
-
-    static bool validateToken(const AtomicString&, ExceptionCode&);
-    static bool validateTokens(const Vector<String>&, ExceptionCode&);
-    static String addToken(const AtomicString&, const AtomicString&);
-    static String addTokens(const AtomicString&, const Vector<String>&);
-    static String removeToken(const AtomicString&, const AtomicString&);
-    static String removeTokens(const AtomicString&, const Vector<String>&);
+    DOMTokenList() = default;
+    const AtomicString& value() const;
+    void setValue(const String&);
+
+    virtual void updateAfterTokenChange() { m_cachedValue = nullAtom; }
+
+private:
+    static bool validateToken(const String&, ExceptionCode&);
+    static bool validateTokens(const String* tokens, size_t length, ExceptionCode&);
+    void addInternal(const String* tokens, size_t length, ExceptionCode&);
+    void removeInternal(const String* tokens, size_t length, ExceptionCode&);
+
+    Vector<AtomicString> m_tokens;
+    mutable AtomicString m_cachedValue;
 };
 
+inline unsigned DOMTokenList::length() const
+{
+    return m_tokens.size();
+}
+
+inline const AtomicString& DOMTokenList::item(unsigned index) const
+{
+    return index < m_tokens.size() ? m_tokens[index] : nullAtom;
+}
+
+inline bool DOMTokenList::toggle(const AtomicString& token, ExceptionCode& ec)
+{
+    return toggle(token, Nullopt, ec);
+}
+
 } // namespace WebCore
 
 #endif // DOMTokenList_h
index b4ffdb2..053ec45 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "HTMLAnchorElement.h"
 
+#include "AttributeDOMTokenList.h"
 #include "ElementIterator.h"
 #include "EventHandler.h"
 #include "EventNames.h"
@@ -39,7 +40,6 @@
 #include "MouseEvent.h"
 #include "PingLoader.h"
 #include "PlatformMouseEvent.h"
-#include "RelList.h"
 #include "RenderImage.h"
 #include "ResourceRequest.h"
 #include "SVGImage.h"
@@ -264,7 +264,7 @@ void HTMLAnchorElement::parseAttribute(const QualifiedName& name, const AtomicSt
         if (SpaceSplitString::spaceSplitStringContainsValue(value, "noreferrer", true))
             m_linkRelations |= RelationNoReferrer;
         if (m_relList)
-            m_relList->updateRelAttribute(value);
+            m_relList->attributeValueChanged(value);
     }
     else
         HTMLElement::parseAttribute(name, value);
@@ -316,7 +316,7 @@ bool HTMLAnchorElement::hasRel(uint32_t relation) const
 DOMTokenList& HTMLAnchorElement::relList()
 {
     if (!m_relList) 
-        m_relList = std::make_unique<RelList>(*this);
+        m_relList = std::make_unique<AttributeDOMTokenList>(*this, HTMLNames::relAttr);
     return *m_relList;
 }
 
index 4b66a01..fd9d966 100644 (file)
@@ -30,7 +30,7 @@
 
 namespace WebCore {
 
-class RelList;
+class AttributeDOMTokenList;
 
 // Link relation bitmask values.
 // FIXME: Uncomment as the various link relations are implemented.
@@ -145,7 +145,7 @@ private:
     uint32_t m_linkRelations : 30;
     mutable LinkHash m_cachedVisitedLinkHash;
 
-    std::unique_ptr<RelList> m_relList;
+    std::unique_ptr<AttributeDOMTokenList> m_relList;
 };
 
 inline LinkHash HTMLAnchorElement::visitedLinkHash() const
index 04139cb..333432f 100644 (file)
@@ -26,6 +26,7 @@
 #include "HTMLLinkElement.h"
 
 #include "Attribute.h"
+#include "AttributeDOMTokenList.h"
 #include "CachedCSSStyleSheet.h"
 #include "CachedResource.h"
 #include "CachedResourceLoader.h"
@@ -46,7 +47,6 @@
 #include "MediaQueryEvaluator.h"
 #include "MouseEvent.h"
 #include "Page.h"
-#include "RelList.h"
 #include "RenderStyle.h"
 #include "SecurityOrigin.h"
 #include "Settings.h"
@@ -141,7 +141,7 @@ void HTMLLinkElement::parseAttribute(const QualifiedName& name, const AtomicStri
     if (name == relAttr) {
         m_relAttribute = LinkRelAttribute(value);
         if (m_relList)
-            m_relList->updateRelAttribute(value);
+            m_relList->attributeValueChanged(value);
         process();
         return;
     }
@@ -403,7 +403,7 @@ void HTMLLinkElement::dispatchPendingEvent(LinkEventSender* eventSender)
 DOMTokenList& HTMLLinkElement::relList()
 {
     if (!m_relList) 
-        m_relList = std::make_unique<RelList>(*this);
+        m_relList = std::make_unique<AttributeDOMTokenList>(*this, HTMLNames::relAttr);
     return *m_relList;
 }
 
index a667352..beb171e 100644 (file)
@@ -35,8 +35,8 @@
 
 namespace WebCore {
 
+class AttributeDOMTokenList;
 class HTMLLinkElement;
-class RelList;
 class URL;
 
 template<typename T> class EventSender;
@@ -140,7 +140,7 @@ private:
 
     PendingSheetType m_pendingSheetType;
 
-    std::unique_ptr<RelList> m_relList;
+    std::unique_ptr<AttributeDOMTokenList> m_relList;
 };
 
 } //namespace
diff --git a/Source/WebCore/html/RelList.cpp b/Source/WebCore/html/RelList.cpp
deleted file mode 100644 (file)
index eb3f4bb..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 Dhi Aurrahman <diorahman@rockybars.com>
- *
- * 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 "RelList.h"
-
-#include "Element.h"
-#include "HTMLNames.h"
-
-namespace WebCore {
-
-RelList::RelList(Element& element) 
-    : m_element(element)
-    , m_relAttributeValue(SpaceSplitString(element.fastGetAttribute(HTMLNames::relAttr), false))
-{
-}
-
-void RelList::ref()
-{
-    m_element.ref();
-}
-
-void RelList::deref()
-{
-    m_element.deref();
-}
-
-unsigned RelList::length() const
-{
-    return m_relAttributeValue.size();
-}
-
-const AtomicString RelList::item(unsigned index) const
-{
-    if (index >= length())
-        return nullAtom;
-    return m_relAttributeValue[index];
-}
-
-Element* RelList::element() const
-{
-    return &m_element;
-}
-
-void RelList::updateRelAttribute(const AtomicString& value)
-{
-    m_relAttributeValue.set(value, false);
-}
-
-bool RelList::containsInternal(const AtomicString& token) const
-{
-    return m_relAttributeValue.contains(token);
-}
-
-AtomicString RelList::value() const
-{
-    return m_element.fastGetAttribute(HTMLNames::relAttr);
-}
-
-void RelList::setValue(const AtomicString& value)
-{
-    m_element.setAttribute(HTMLNames::relAttr, value);
-}
-
-} // namespace WebCore
-
diff --git a/Source/WebCore/html/RelList.h b/Source/WebCore/html/RelList.h
deleted file mode 100644 (file)
index 9743ccf..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2014 Dhi Aurrahman <diorahman@rockybars.com>
- *
- * 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 RelList_h
-#define RelList_h
-
-#include "DOMTokenList.h"
-#include "SpaceSplitString.h"
-
-namespace WebCore {
-
-class Element;
-
-class RelList final : public DOMTokenList {
-public:
-    RelList(Element&);
-    void updateRelAttribute(const AtomicString&);
-
-private:
-    virtual void ref() override;
-    virtual void deref() override;
-    virtual unsigned length() const override;
-    virtual const AtomicString item(unsigned index) const override;
-    virtual Element* element() const override;
-    virtual bool containsInternal(const AtomicString&) const override;
-    virtual AtomicString value() const override;
-    virtual void setValue(const AtomicString&) override;
-    
-    Element& m_element;
-    mutable SpaceSplitString m_relAttributeValue;
-};
-
-} // namespace WebCore
-
-#endif // RelList_h
-