diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/html/DOMTokenList.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/html/DOMTokenList.cpp')
-rw-r--r-- | Source/WebCore/html/DOMTokenList.cpp | 306 |
1 files changed, 168 insertions, 138 deletions
diff --git a/Source/WebCore/html/DOMTokenList.cpp b/Source/WebCore/html/DOMTokenList.cpp index dbd4d2639..0395a2e0f 100644 --- a/Source/WebCore/html/DOMTokenList.cpp +++ b/Source/WebCore/html/DOMTokenList.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,211 +28,240 @@ #include "ExceptionCode.h" #include "HTMLParserIdioms.h" +#include "SpaceSplitString.h" +#include <wtf/HashSet.h> +#include <wtf/SetForScope.h> +#include <wtf/text/AtomicStringHash.h> #include <wtf/text/StringBuilder.h> namespace WebCore { -bool DOMTokenList::validateToken(const AtomicString& token, ExceptionCode& ec) +DOMTokenList::DOMTokenList(Element& element, const QualifiedName& attributeName, WTF::Function<bool(StringView)>&& isSupportedToken) + : m_element(element) + , m_attributeName(attributeName) + , m_isSupportedToken(WTFMove(isSupportedToken)) { - if (token.isEmpty()) { - ec = SYNTAX_ERR; - return false; - } +} - unsigned length = token.length(); - for (unsigned i = 0; i < length; ++i) { - if (isHTMLSpace(token[i])) { - ec = INVALID_CHARACTER_ERR; - return false; - } +static inline bool tokenContainsHTMLSpace(const String& token) +{ + return token.find(isHTMLSpace) != notFound; +} + +ExceptionOr<void> DOMTokenList::validateToken(const String& token) +{ + if (token.isEmpty()) + return Exception { SYNTAX_ERR }; + + if (tokenContainsHTMLSpace(token)) + return Exception { INVALID_CHARACTER_ERR }; + + return { }; +} + +ExceptionOr<void> DOMTokenList::validateTokens(const String* tokens, size_t length) +{ + for (size_t i = 0; i < length; ++i) { + auto result = validateToken(tokens[i]); + if (result.hasException()) + return result; } + return { }; +} - return true; +bool DOMTokenList::contains(const AtomicString& token) const +{ + return tokens().contains(token); } -bool DOMTokenList::validateTokens(const Vector<String>& tokens, ExceptionCode& ec) +inline ExceptionOr<void> DOMTokenList::addInternal(const String* newTokens, size_t length) { - for (size_t i = 0; i < tokens.size(); ++i) { - if (!validateToken(tokens[i], ec)) - return false; + // This is usually called with a single token. + Vector<AtomicString, 1> uniqueNewTokens; + uniqueNewTokens.reserveInitialCapacity(length); + + auto& tokens = this->tokens(); + + for (size_t i = 0; i < length; ++i) { + auto result = validateToken(newTokens[i]); + if (result.hasException()) + return result; + if (!tokens.contains(newTokens[i]) && !uniqueNewTokens.contains(newTokens[i])) + uniqueNewTokens.uncheckedAppend(newTokens[i]); } - return true; + if (!uniqueNewTokens.isEmpty()) + tokens.appendVector(uniqueNewTokens); + + updateAssociatedAttributeFromTokens(); + + return { }; } -bool DOMTokenList::contains(const AtomicString& token, ExceptionCode& ec) const +ExceptionOr<void> DOMTokenList::add(const Vector<String>& tokens) { - if (!validateToken(token, ec)) - return false; - return containsInternal(token); + return addInternal(tokens.data(), tokens.size()); } -void DOMTokenList::add(const AtomicString& token, ExceptionCode& ec) +ExceptionOr<void> DOMTokenList::add(const AtomicString& token) { - Vector<String> tokens; - tokens.append(token.string()); - add(tokens, ec); + return addInternal(&token.string(), 1); } -void DOMTokenList::add(const Vector<String>& tokens, ExceptionCode& ec) +inline ExceptionOr<void> DOMTokenList::removeInternal(const String* tokensToRemove, size_t length) { - Vector<String> filteredTokens; - for (size_t i = 0; i < tokens.size(); ++i) { - if (!validateToken(tokens[i], ec)) - return; - if (!containsInternal(tokens[i]) && !filteredTokens.contains(tokens[i])) - filteredTokens.append(tokens[i]); - } + auto result = validateTokens(tokensToRemove, length); + if (result.hasException()) + return result; - if (filteredTokens.isEmpty()) - return; + auto& tokens = this->tokens(); + for (size_t i = 0; i < length; ++i) + tokens.removeFirst(tokensToRemove[i]); - setValue(addTokens(value(), filteredTokens)); + updateAssociatedAttributeFromTokens(); + + return { }; } -void DOMTokenList::remove(const AtomicString& token, ExceptionCode& ec) +ExceptionOr<void> DOMTokenList::remove(const Vector<String>& tokens) { - Vector<String> tokens; - tokens.append(token.string()); - remove(tokens, ec); + return removeInternal(tokens.data(), tokens.size()); } -void DOMTokenList::remove(const Vector<String>& tokens, ExceptionCode& ec) +ExceptionOr<void> DOMTokenList::remove(const AtomicString& token) { - if (!validateTokens(tokens, ec)) - return; + return removeInternal(&token.string(), 1); +} - // 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; +ExceptionOr<bool> DOMTokenList::toggle(const AtomicString& token, std::optional<bool> force) +{ + auto result = validateToken(token); + if (result.hasException()) + return result.releaseException(); + + auto& tokens = this->tokens(); + + if (tokens.contains(token)) { + if (!force.value_or(false)) { + tokens.removeFirst(token); + updateAssociatedAttributeFromTokens(); + return false; } + return true; } - if (found) - setValue(removeTokens(value(), tokens)); -} - -bool DOMTokenList::toggle(const AtomicString& token, ExceptionCode& ec) -{ - if (!validateToken(token, ec)) + if (force && !force.value()) return false; - if (containsInternal(token)) { - removeInternal(token); - return false; - } - addInternal(token); + tokens.append(token); + updateAssociatedAttributeFromTokens(); return true; } -bool DOMTokenList::toggle(const AtomicString& token, bool force, ExceptionCode& ec) +ExceptionOr<void> DOMTokenList::replace(const AtomicString& token, const AtomicString& newToken) { - if (!validateToken(token, ec)) - return false; + if (token.isEmpty() || newToken.isEmpty()) + return Exception { SYNTAX_ERR }; + + if (tokenContainsHTMLSpace(token) || tokenContainsHTMLSpace(newToken)) + return Exception { INVALID_CHARACTER_ERR }; - if (force) - addInternal(token); + auto& tokens = this->tokens(); + size_t index = tokens.find(token); + if (index == notFound) + return { }; + + if (tokens.find(newToken) != notFound) + tokens.remove(index); else - removeInternal(token); + tokens[index] = newToken; + + updateAssociatedAttributeFromTokens(); - return force; + return { }; } -void DOMTokenList::addInternal(const AtomicString& token) +// https://dom.spec.whatwg.org/#concept-domtokenlist-validation +ExceptionOr<bool> DOMTokenList::supports(StringView token) { - if (!containsInternal(token)) - setValue(addToken(value(), token)); + if (!m_isSupportedToken) + return Exception { TypeError }; + return m_isSupportedToken(token); } -void DOMTokenList::removeInternal(const AtomicString& token) +// https://dom.spec.whatwg.org/#dom-domtokenlist-value +const AtomicString& DOMTokenList::value() const { - // Check using contains first since it uses AtomicString comparisons instead - // of character by character testing. - if (!containsInternal(token)) - return; - setValue(removeToken(value(), token)); + return m_element.getAttribute(m_attributeName); } -String DOMTokenList::addToken(const AtomicString& input, const AtomicString& token) +void DOMTokenList::setValue(const String& value) { - Vector<String> tokens; - tokens.append(token.string()); - return addTokens(input, tokens); + m_element.setAttribute(m_attributeName, value); } -String DOMTokenList::addTokens(const AtomicString& input, const Vector<String>& tokens) +void DOMTokenList::updateTokensFromAttributeValue(const String& value) { - bool needsSpace = false; - - StringBuilder builder; - if (!input.isEmpty()) { - builder.append(input); - needsSpace = !isHTMLSpace(input[input.length() - 1]); - } + // 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); + } - for (size_t i = 0; i < tokens.size(); ++i) { - if (needsSpace) - builder.append(' '); - builder.append(tokens[i]); - needsSpace = true; + start = end + 1; } - return builder.toString(); + m_tokens.shrinkToFit(); + m_tokensNeedUpdating = false; } -String DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token) +void DOMTokenList::associatedAttributeValueChanged(const AtomicString&) { - Vector<String> tokens; - tokens.append(token.string()); - return removeTokens(input, tokens); + // Do not reset the DOMTokenList value if the attribute value was changed by us. + if (m_inUpdateAssociatedAttributeFromTokens) + return; + + m_tokensNeedUpdating = true; } -String DOMTokenList::removeTokens(const AtomicString& input, const Vector<String>& tokens) +// https://dom.spec.whatwg.org/#concept-dtl-update +void DOMTokenList::updateAssociatedAttributeFromTokens() { - // 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 + ASSERT(!m_tokensNeedUpdating); - // Step 5 - while (position < inputLength) { - if (isHTMLSpace(input[position])) { // 6 - output.append(input[position++]); // 6.1, 6.2 - continue; // 6.3 - } - - // 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 + // https://dom.spec.whatwg.org/#concept-ordered-set-serializer + StringBuilder builder; + for (auto& token : tokens()) { + if (!builder.isEmpty()) + builder.append(' '); + builder.append(token); } + AtomicString serializedValue = builder.toAtomicString(); - return output.toString(); + SetForScope<bool> inAttributeUpdate(m_inUpdateAssociatedAttributeFromTokens, true); + m_element.setAttribute(m_attributeName, serializedValue); +} + +Vector<AtomicString>& DOMTokenList::tokens() +{ + if (m_tokensNeedUpdating) + updateTokensFromAttributeValue(m_element.getAttribute(m_attributeName)); + ASSERT(!m_tokensNeedUpdating); + return m_tokens; } } // namespace WebCore |