| Index: third_party/WebKit/Source/core/dom/DOMTokenList.cpp
|
| diff --git a/third_party/WebKit/Source/core/dom/DOMTokenList.cpp b/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
|
| index 1446659eac750382785f954e679268238a4d7bc6..47b0cc1e4cf920cf5312b47834f280ddac4bc45c 100644
|
| --- a/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
|
| +++ b/third_party/WebKit/Source/core/dom/DOMTokenList.cpp
|
| @@ -31,14 +31,22 @@
|
|
|
| namespace blink {
|
|
|
| +// This implements the common part of the following operations:
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-add
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-remove
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-toggle
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-replace
|
| bool DOMTokenList::ValidateToken(const String& token,
|
| ExceptionState& exception_state) const {
|
| + // 1. If token is the empty string, then throw a SyntaxError.
|
| if (token.IsEmpty()) {
|
| exception_state.ThrowDOMException(kSyntaxError,
|
| "The token provided must not be empty.");
|
| return false;
|
| }
|
|
|
| + // 2. If token contains any ASCII whitespace, then throw an
|
| + // InvalidCharacterError.
|
| if (token.Find(IsHTMLSpace) != kNotFound) {
|
| exception_state.ThrowDOMException(kInvalidCharacterError,
|
| "The token provided ('" + token +
|
| @@ -79,24 +87,15 @@ void DOMTokenList::add(const AtomicString& token,
|
| add(tokens, exception_state);
|
| }
|
|
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-add
|
| // Optimally, this should take a Vector<AtomicString> const ref in argument but
|
| // the bindings generator does not handle that.
|
| void DOMTokenList::add(const Vector<String>& tokens,
|
| ExceptionState& exception_state) {
|
| - Vector<String> filtered_tokens;
|
| - filtered_tokens.ReserveCapacity(tokens.size());
|
| - for (const auto& token : tokens) {
|
| - if (!ValidateToken(token, exception_state))
|
| - return;
|
| - if (ContainsInternal(AtomicString(token)))
|
| - continue;
|
| - if (filtered_tokens.Contains(token))
|
| - continue;
|
| - filtered_tokens.push_back(token);
|
| - }
|
| + if (!ValidateTokens(tokens, exception_state))
|
| + return;
|
|
|
| - if (!filtered_tokens.IsEmpty())
|
| - setValue(AddTokens(value(), filtered_tokens));
|
| + setValue(AddTokens(tokens));
|
| }
|
|
|
| void DOMTokenList::remove(const AtomicString& token,
|
| @@ -106,6 +105,7 @@ void DOMTokenList::remove(const AtomicString& token,
|
| remove(tokens, exception_state);
|
| }
|
|
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-remove
|
| // Optimally, this should take a Vector<AtomicString> const ref in argument but
|
| // the bindings generator does not handle that.
|
| void DOMTokenList::remove(const Vector<String>& tokens,
|
| @@ -113,17 +113,11 @@ void DOMTokenList::remove(const Vector<String>& tokens,
|
| if (!ValidateTokens(tokens, exception_state))
|
| return;
|
|
|
| - // Check using containsInternal first since it is a lot faster than going
|
| - // through the string character by character.
|
| - bool found = false;
|
| - for (const auto& token : tokens) {
|
| - if (ContainsInternal(AtomicString(token))) {
|
| - found = true;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - setValue(found ? RemoveTokens(value(), tokens) : value());
|
| + // TODO(tkent): This null check doesn't conform to the DOM specification.
|
| + // See https://github.com/whatwg/dom/issues/462
|
| + if (value().IsNull())
|
| + return;
|
| + setValue(RemoveTokens(tokens));
|
| }
|
|
|
| bool DOMTokenList::toggle(const AtomicString& token,
|
| @@ -160,7 +154,7 @@ bool DOMTokenList::supports(const AtomicString& token,
|
|
|
| void DOMTokenList::AddInternal(const AtomicString& token) {
|
| if (!ContainsInternal(token))
|
| - setValue(AddToken(value(), token));
|
| + setValue(AddToken(token));
|
| }
|
|
|
| void DOMTokenList::RemoveInternal(const AtomicString& token) {
|
| @@ -168,95 +162,62 @@ void DOMTokenList::RemoveInternal(const AtomicString& token) {
|
| // of character by character testing.
|
| if (!ContainsInternal(token))
|
| return;
|
| - setValue(RemoveToken(value(), token));
|
| + setValue(RemoveToken(token));
|
| }
|
|
|
| -AtomicString DOMTokenList::AddToken(const AtomicString& input,
|
| - const AtomicString& token) {
|
| +AtomicString DOMTokenList::AddToken(const AtomicString& token) {
|
| Vector<String> tokens;
|
| tokens.push_back(token.GetString());
|
| - return AddTokens(input, tokens);
|
| + return AddTokens(tokens);
|
| }
|
|
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-add
|
| // This returns an AtomicString because it is always passed as argument to
|
| // setValue() and setValue() takes an AtomicString in argument.
|
| -AtomicString DOMTokenList::AddTokens(const AtomicString& input,
|
| - const Vector<String>& tokens) {
|
| - bool needs_space = false;
|
| -
|
| - StringBuilder builder;
|
| - if (!input.IsEmpty()) {
|
| - builder.Append(input);
|
| - needs_space = !IsHTMLSpace<UChar>(input[input.length() - 1]);
|
| - }
|
| -
|
| - for (const auto& token : tokens) {
|
| - if (needs_space)
|
| - builder.Append(' ');
|
| - builder.Append(token);
|
| - needs_space = true;
|
| - }
|
| -
|
| - return builder.ToAtomicString();
|
| +AtomicString DOMTokenList::AddTokens(const Vector<String>& tokens) {
|
| + SpaceSplitString& token_set = MutableSet();
|
| + // 2. For each token in tokens, append token to context object’s token set.
|
| + for (const auto& token : tokens)
|
| + token_set.Add(AtomicString(token));
|
| + // 3. Run the update steps.
|
| + return SerializeSet(token_set);
|
| }
|
|
|
| -AtomicString DOMTokenList::RemoveToken(const AtomicString& input,
|
| - const AtomicString& token) {
|
| +AtomicString DOMTokenList::RemoveToken(const AtomicString& token) {
|
| Vector<String> tokens;
|
| tokens.push_back(token.GetString());
|
| - return RemoveTokens(input, tokens);
|
| + return RemoveTokens(tokens);
|
| }
|
|
|
| +// https://dom.spec.whatwg.org/#dom-domtokenlist-remove
|
| // This returns an AtomicString because it is always passed as argument to
|
| // setValue() and setValue() takes an AtomicString in argument.
|
| -AtomicString DOMTokenList::RemoveTokens(const AtomicString& input,
|
| - const Vector<String>& tokens) {
|
| - // 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 https://dom.spec.whatwg.org/#remove-a-token-from-a-string
|
| -
|
| - unsigned input_length = input.length();
|
| - StringBuilder output; // 3
|
| - output.ReserveCapacity(input_length);
|
| - unsigned position = 0; // 4
|
| -
|
| - // Step 5
|
| - while (position < input_length) {
|
| - if (IsHTMLSpace<UChar>(input[position])) { // 6
|
| - position++;
|
| - continue; // 6.3
|
| - }
|
| -
|
| - // Step 7
|
| - StringBuilder token_builder;
|
| - while (position < input_length && IsNotHTMLSpace<UChar>(input[position]))
|
| - token_builder.Append(input[position++]);
|
| -
|
| - // Step 8
|
| - String token = token_builder.ToString();
|
| - if (tokens.Contains(token)) {
|
| - // Step 8.1
|
| - while (position < input_length && IsHTMLSpace<UChar>(input[position]))
|
| - ++position;
|
| -
|
| - // Step 8.2
|
| - size_t j = output.length();
|
| - while (j > 0 && IsHTMLSpace<UChar>(output[j - 1]))
|
| - --j;
|
| - output.Resize(j);
|
| - } else {
|
| - output.Append(token); // Step 9
|
| - }
|
| -
|
| - if (position < input_length && !output.IsEmpty())
|
| - output.Append(' ');
|
| +AtomicString DOMTokenList::RemoveTokens(const Vector<String>& tokens) {
|
| + SpaceSplitString& token_set = MutableSet();
|
| + // 2. For each token in tokens, remove token from context object’s token set.
|
| + for (const auto& token : tokens)
|
| + token_set.Remove(AtomicString(token));
|
| + // 3. Run the update steps.
|
| + return SerializeSet(token_set);
|
| +}
|
| +
|
| +// https://dom.spec.whatwg.org/#concept-ordered-set-serializer
|
| +// The ordered set serializer takes a set and returns the concatenation of the
|
| +// strings in set, separated from each other by U+0020, if set is non-empty, and
|
| +// the empty string otherwise.
|
| +AtomicString DOMTokenList::SerializeSet(const SpaceSplitString& token_set) {
|
| + size_t size = token_set.size();
|
| + if (size == 0)
|
| + return g_empty_atom;
|
| + if (size == 1)
|
| + return token_set[0];
|
| + StringBuilder builder;
|
| + builder.Append(token_set[0]);
|
| + for (size_t i = 1; i < size; ++i) {
|
| + builder.Append(' ');
|
| + builder.Append(token_set[i]);
|
| }
|
| -
|
| - size_t j = output.length();
|
| - if (j > 0 && IsHTMLSpace<UChar>(output[j - 1]))
|
| - output.Resize(j - 1);
|
| -
|
| - return output.ToAtomicString();
|
| + return builder.ToAtomicString();
|
| }
|
|
|
| void DOMTokenList::setValue(const AtomicString& value) {
|
|
|