2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "DOMTokenList.h"
29 #include "ExceptionCode.h"
30 #include "HTMLParserIdioms.h"
31 #include "SpaceSplitString.h"
32 #include <wtf/HashSet.h>
33 #include <wtf/text/AtomicStringHash.h>
34 #include <wtf/text/StringBuilder.h>
38 bool DOMTokenList::validateToken(const String& token, ExceptionCode& ec)
40 if (token.isEmpty()) {
45 unsigned length = token.length();
46 for (unsigned i = 0; i < length; ++i) {
47 if (isHTMLSpace(token[i])) {
48 ec = INVALID_CHARACTER_ERR;
56 bool DOMTokenList::validateTokens(const String* tokens, size_t length, ExceptionCode& ec)
58 for (size_t i = 0; i < length; ++i) {
59 if (!validateToken(tokens[i], ec))
65 bool DOMTokenList::contains(const AtomicString& token) const
67 return m_tokens.contains(token);
70 inline void DOMTokenList::addInternal(const String* tokens, size_t length, ExceptionCode& ec)
72 // This is usually called with a single token.
73 Vector<AtomicString, 1> uniqueTokens;
74 uniqueTokens.reserveInitialCapacity(length);
76 for (size_t i = 0; i < length; ++i) {
77 if (!validateToken(tokens[i], ec))
79 if (!m_tokens.contains(tokens[i]) && !uniqueTokens.contains(tokens[i]))
80 uniqueTokens.uncheckedAppend(tokens[i]);
83 if (!uniqueTokens.isEmpty())
84 m_tokens.appendVector(uniqueTokens);
86 updateAfterTokenChange();
89 void DOMTokenList::add(const Vector<String>& tokens, ExceptionCode& ec)
91 addInternal(tokens.data(), tokens.size(), ec);
94 void DOMTokenList::add(const WTF::AtomicString& token, ExceptionCode& ec)
96 addInternal(&token.string(), 1, ec);
99 inline void DOMTokenList::removeInternal(const String* tokens, size_t length, ExceptionCode& ec)
101 if (!validateTokens(tokens, length, ec))
104 for (size_t i = 0; i < length; ++i)
105 m_tokens.removeFirst(tokens[i]);
107 updateAfterTokenChange();
110 void DOMTokenList::remove(const Vector<String>& tokens, ExceptionCode& ec)
112 removeInternal(tokens.data(), tokens.size(), ec);
115 void DOMTokenList::remove(const WTF::AtomicString& token, ExceptionCode& ec)
117 removeInternal(&token.string(), 1, ec);
120 bool DOMTokenList::toggle(const AtomicString& token, Optional<bool> force, ExceptionCode& ec)
122 if (!validateToken(token, ec))
125 if (m_tokens.contains(token)) {
126 if (!force.valueOr(false)) {
127 m_tokens.removeFirst(token);
128 updateAfterTokenChange();
134 if (force && !force.value())
137 m_tokens.append(token);
138 updateAfterTokenChange();
142 const AtomicString& DOMTokenList::value() const
144 if (m_cachedValue.isNull()) {
145 // https://dom.spec.whatwg.org/#concept-ordered-set-serializer
146 StringBuilder builder;
147 for (auto& token : m_tokens) {
148 if (!builder.isEmpty())
150 builder.append(token);
152 m_cachedValue = builder.toAtomicString();
153 ASSERT(!m_cachedValue.isNull());
155 return m_cachedValue;
158 void DOMTokenList::setValue(const String& value)
160 setValueInternal(value);
161 updateAfterTokenChange();
164 void DOMTokenList::setValueInternal(const WTF::String& value)
166 // Clear tokens but not capacity.
169 HashSet<AtomicString> addedTokens;
170 // https://dom.spec.whatwg.org/#ordered%20sets
171 for (unsigned start = 0; ; ) {
172 while (start < value.length() && isHTMLSpace(value[start]))
174 if (start >= value.length())
176 unsigned end = start + 1;
177 while (end < value.length() && !isHTMLSpace(value[end]))
180 AtomicString token = value.substring(start, end - start);
181 if (!addedTokens.contains(token)) {
182 m_tokens.append(token);
183 addedTokens.add(token);
189 m_tokens.shrinkToFit();
190 m_cachedValue = nullAtom;
193 } // namespace WebCore