WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
148589
DOMTokenList update steps for classList don't follow the spec
https://bugs.webkit.org/show_bug.cgi?id=148589
Summary
DOMTokenList update steps for classList don't follow the spec
Michał Gołębiowski-Owczarek
Reported
2015-08-28 14:14:46 PDT
Steps to reproduce the problem: ``` var div = document.createElement('div'); div.className = ' a b c '; div.classList.remove('b'); console.log(div.className); // " a c " ``` `DOMTokenList` [update steps](
https://dom.spec.whatwg.org/#domtokenlist
)' second point is: "Set an attribute value for the associated element using associated attribute’s local name and the result of running the ordered set serializer for tokens." For `classList` the "associated attribute's local name" is `class`. The ["ordered set serializer" algorithm](
https://dom.spec.whatwg.org/#concept-ordered-set-serializer
) is: "The ordered set serializer takes a set and returns the concatenation of the strings in set, separated from each other by U+0020". This means that after updating the context object's classes via the classList API, className value (that reflects the "class" attribute value) should be "a c", not " a c ". Edge is the only browser that returns "a c" correctly for the above test case.
https://code.google.com/p/chromium/issues/detail?id=526282
Attachments
Patch
(86.76 KB, patch)
2015-09-10 16:13 PDT
,
Chris Dumez
no flags
Details
Formatted Diff
Diff
Patch
(92.74 KB, patch)
2015-09-10 16:24 PDT
,
Chris Dumez
no flags
Details
Formatted Diff
Diff
Patch
(92.82 KB, patch)
2015-09-10 16:45 PDT
,
Chris Dumez
no flags
Details
Formatted Diff
Diff
Patch
(94.01 KB, patch)
2015-09-11 13:22 PDT
,
Chris Dumez
no flags
Details
Formatted Diff
Diff
Show Obsolete
(3)
View All
Add attachment
proposed patch, testcase, etc.
Michał Gołębiowski-Owczarek
Comment 1
2015-08-28 14:17:50 PDT
Firefox issue:
https://bugzilla.mozilla.org/show_bug.cgi?id=869788
Chris Dumez
Comment 2
2015-09-02 15:28:43 PDT
22547443
Chris Dumez
Comment 3
2015-09-02 15:29:02 PDT
rdar://problem/22547443
Chris Dumez
Comment 4
2015-09-10 16:13:52 PDT
Created
attachment 260961
[details]
Patch
WebKit Commit Bot
Comment 5
2015-09-10 16:17:17 PDT
Attachment 260961
[details]
did not pass style-queue: ERROR: Source/WebCore/html/DOMTokenList.h:53: Missing space before { [whitespace/braces] [5] ERROR: Source/WebCore/html/DOMTokenList.h:55: Missing space before { [whitespace/braces] [5] Total errors found: 2 in 27 files If any of these errors are false positives, please file a bug against check-webkit-style.
Chris Dumez
Comment 6
2015-09-10 16:24:18 PDT
Created
attachment 260963
[details]
Patch
WebKit Commit Bot
Comment 7
2015-09-10 16:27:19 PDT
Attachment 260963
[details]
did not pass style-queue: ERROR: Source/WebCore/html/DOMTokenList.h:53: Missing space before { [whitespace/braces] [5] ERROR: Source/WebCore/html/DOMTokenList.h:55: Missing space before { [whitespace/braces] [5] Total errors found: 2 in 30 files If any of these errors are false positives, please file a bug against check-webkit-style.
Chris Dumez
Comment 8
2015-09-10 16:45:15 PDT
Created
attachment 260969
[details]
Patch
WebKit Commit Bot
Comment 9
2015-09-10 16:46:36 PDT
Attachment 260969
[details]
did not pass style-queue: ERROR: Source/WebCore/html/DOMTokenList.h:53: Missing space before { [whitespace/braces] [5] ERROR: Source/WebCore/html/DOMTokenList.h:55: Missing space before { [whitespace/braces] [5] Total errors found: 2 in 30 files If any of these errors are false positives, please file a bug against check-webkit-style.
Darin Adler
Comment 10
2015-09-11 09:36:07 PDT
Comment on
attachment 260969
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=260969&action=review
> Source/WebCore/html/AttributeDOMTokenList.h:42 > + virtual void ref() override { m_element.ref(); } > + virtual void deref() override { m_element.deref(); } > + > + virtual Element* element() const override { return &m_element; }
Can any of these be private?
> Source/WebCore/html/AttributeDOMTokenList.h:48 > + const WebCore::QualifiedName m_attributeName;
Could consider making this a reference; no need to churn the reference counts on the global immortal attribute names.
> Source/WebCore/html/DOMTokenList.cpp:73 > + Vector<AtomicString> uniqueTokens;
This is a temporary that’s only used on the stack. I suggest some inline capacity to get rid of one heap allocation.
> Source/WebCore/html/DOMTokenList.cpp:136 > + for (size_t i = 0; i < m_tokens.size(); ++i) { > + builder.append(m_tokens[i]); > + if (i != m_tokens.size() - 1) > + builder.append(' '); > + }
I liked the old way with a boolean because then we could use a modern for loop. Also might be nice to teach StringBuilder how to join and append, since this seems like a common idiom.
> Source/WebCore/html/DOMTokenList.cpp:162 > + // We do a simple linear search as there are usually not many tokens. > + if (!m_tokens.contains(token)) > + m_tokens.append(token);
Seems a little dangerous to assume there’s never a giant one of these.
> Source/WebCore/html/DOMTokenList.h:53 > + void add(const AtomicString& token, ExceptionCode& ec) { add(Vector<String>{token.string()}, ec); }
Seems wasteful to allocate a vector just to do this. There are multiple ways to avoid that; one is to have the worker function that takes a “view” on a vector, just a pointer plus a length. I also think we are going too far in putting function bodies in the class. I’d like to see interface and implementation a bit more separate by putting the inline function bodies after the class.
> Source/WebCore/html/DOMTokenList.h:72 > protected: > - virtual AtomicString value() const = 0; > - virtual void setValue(const AtomicString&) = 0; > + const AtomicString& value() const; > + void setValue(const String&); > + virtual void updateAfterTokenChange() { m_cachedValue = nullAtom; } > > - void addInternal(const AtomicString&); > - virtual bool containsInternal(const AtomicString&) const = 0; > - void removeInternal(const AtomicString&); > - > - static bool validateToken(const AtomicString&, ExceptionCode&); > + static bool validateToken(const String&, 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>&); > + > + Vector<AtomicString> m_tokens; > + mutable AtomicString m_cachedValue;
Do derived classes need access to all of these? I think at least some of this should be private rather than protected.
Chris Dumez
Comment 11
2015-09-11 11:02:12 PDT
Comment on
attachment 260969
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=260969&action=review
>> Source/WebCore/html/AttributeDOMTokenList.h:42 >> + virtual Element* element() const override { return &m_element; } > > Can any of these be private?
I believe so, will fix.
>> Source/WebCore/html/AttributeDOMTokenList.h:48 >> + const WebCore::QualifiedName m_attributeName; > > Could consider making this a reference; no need to churn the reference counts on the global immortal attribute names.
Ok.
>> Source/WebCore/html/DOMTokenList.cpp:73 >> + Vector<AtomicString> uniqueTokens; > > This is a temporary that’s only used on the stack. I suggest some inline capacity to get rid of one heap allocation.
Ok, I will browse a little bit to determine a decent inline capacity.
>> Source/WebCore/html/DOMTokenList.cpp:136 >> + } > > I liked the old way with a boolean because then we could use a modern for loop. Also might be nice to teach StringBuilder how to join and append, since this seems like a common idiom.
Ok.
>> Source/WebCore/html/DOMTokenList.cpp:162 >> + m_tokens.append(token); > > Seems a little dangerous to assume there’s never a giant one of these.
Hmm. I will think a bit about this. We could allocate a HashMap on the stack to make this faster.
>> Source/WebCore/html/DOMTokenList.h:53 >> + void add(const AtomicString& token, ExceptionCode& ec) { add(Vector<String>{token.string()}, ec); } > > Seems wasteful to allocate a vector just to do this. There are multiple ways to avoid that; one is to have the worker function that takes a “view” on a vector, just a pointer plus a length. > > I also think we are going too far in putting function bodies in the class. I’d like to see interface and implementation a bit more separate by putting the inline function bodies after the class.
Hmm. Agreed. I did not bother because the existing code was doing this and I am pretty sure this is only used by legacy ObjC bindings. However, I will look into the change you're suggesting.
>> Source/WebCore/html/DOMTokenList.h:72 >> + mutable AtomicString m_cachedValue; > > Do derived classes need access to all of these? I think at least some of this should be private rather than protected.
Ok.
Chris Dumez
Comment 12
2015-09-11 13:22:57 PDT
Created
attachment 261012
[details]
Patch
Chris Dumez
Comment 13
2015-09-11 13:26:47 PDT
It looks like this may be performance-positive on Dromaeo Selector tests:
http://dromaeo.com/?id=241285,241294
WebKit Commit Bot
Comment 14
2015-09-11 14:33:28 PDT
Comment on
attachment 261012
[details]
Patch Clearing flags on attachment: 261012 Committed
r189632
: <
http://trac.webkit.org/changeset/189632
>
WebKit Commit Bot
Comment 15
2015-09-11 14:33:34 PDT
All reviewed patches have been landed. Closing bug.
Michał Gołębiowski-Owczarek
Comment 16
2015-09-11 14:53:08 PDT
Thanks! \o/
Michał Gołębiowski-Owczarek
Comment 17
2016-04-07 08:43:49 PDT
It seems the algorithm used in Safari Technology Preview serializes too aggressively; see the spec issue at
https://github.com/whatwg/dom/issues/105
; the Tech Preview follows the old behavior, the current Safari 9 follows the new one. Edge 13 which implemented DOMTokenList update steps properly applies them on add/remove/toggle but not just on a regular stringification if those are not involved (so it follows the current spec correctly, I think).
Chris Dumez
Comment 18
2016-04-07 09:24:54 PDT
(In reply to
comment #17
)
> It seems the algorithm used in Safari Technology Preview serializes too > aggressively; see the spec issue at >
https://github.com/whatwg/dom/issues/105
; the Tech Preview follows the old > behavior, the current Safari 9 follows the new one. > > Edge 13 which implemented DOMTokenList update steps properly applies them on > add/remove/toggle but not just on a regular stringification if those are not > involved (so it follows the current spec correctly, I think).
Would you mind filing a new bug? I can probably fix this soon.
Chris Dumez
Comment 19
2016-04-07 10:33:19 PDT
(In reply to
comment #18
)
> (In reply to
comment #17
) > > It seems the algorithm used in Safari Technology Preview serializes too > > aggressively; see the spec issue at > >
https://github.com/whatwg/dom/issues/105
; the Tech Preview follows the old > > behavior, the current Safari 9 follows the new one. > > > > Edge 13 which implemented DOMTokenList update steps properly applies them on > > add/remove/toggle but not just on a regular stringification if those are not > > involved (so it follows the current spec correctly, I think). > > Would you mind filing a new bug? I can probably fix this soon.
Although, since we were matching the spec until they changed it recently and considering that our new behavior is nicer, I think we should try and push back on the spec.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug