2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "core/dom/DOMTokenList.h"
28 #include "bindings/core/v8/ExceptionState.h"
29 #include "core/dom/ExceptionCode.h"
30 #include "core/html/parser/HTMLParserIdioms.h"
31 #include "wtf/text/StringBuilder.h"
35 bool DOMTokenList::validateToken(const String& token, ExceptionState& exceptionState)
37 if (token.isEmpty()) {
38 exceptionState.throwDOMException(SyntaxError, "The token provided must not be empty.");
42 unsigned length = token.length();
43 for (unsigned i = 0; i < length; ++i) {
44 if (isHTMLSpace<UChar>(token[i])) {
45 exceptionState.throwDOMException(InvalidCharacterError, "The token provided ('" + token + "') contains HTML space characters, which are not valid in tokens.");
53 bool DOMTokenList::validateTokens(const Vector<String>& tokens, ExceptionState& exceptionState)
55 for (size_t i = 0; i < tokens.size(); ++i) {
56 if (!validateToken(tokens[i], exceptionState))
63 bool DOMTokenList::contains(const AtomicString& token, ExceptionState& exceptionState) const
65 if (!validateToken(token, exceptionState))
67 return containsInternal(token);
70 void DOMTokenList::add(const AtomicString& token, ExceptionState& exceptionState)
72 Vector<String> tokens;
73 tokens.append(token.string());
74 add(tokens, exceptionState);
77 // Optimally, this should take a Vector<AtomicString> const ref in argument but the
78 // bindings generator does not handle that.
79 void DOMTokenList::add(const Vector<String>& tokens, ExceptionState& exceptionState)
81 Vector<String> filteredTokens;
82 filteredTokens.reserveCapacity(tokens.size());
83 for (size_t i = 0; i < tokens.size(); ++i) {
84 if (!validateToken(tokens[i], exceptionState))
86 if (containsInternal(AtomicString(tokens[i])))
88 if (filteredTokens.contains(tokens[i]))
90 filteredTokens.append(tokens[i]);
93 if (filteredTokens.isEmpty())
96 setValue(addTokens(value(), filteredTokens));
99 void DOMTokenList::remove(const AtomicString& token, ExceptionState& exceptionState)
101 Vector<String> tokens;
102 tokens.append(token.string());
103 remove(tokens, exceptionState);
106 // Optimally, this should take a Vector<AtomicString> const ref in argument but the
107 // bindings generator does not handle that.
108 void DOMTokenList::remove(const Vector<String>& tokens, ExceptionState& exceptionState)
110 if (!validateTokens(tokens, exceptionState))
113 // Check using containsInternal first since it is a lot faster than going
114 // through the string character by character.
116 for (size_t i = 0; i < tokens.size(); ++i) {
117 if (containsInternal(AtomicString(tokens[i]))) {
124 setValue(removeTokens(value(), tokens));
127 bool DOMTokenList::toggle(const AtomicString& token, ExceptionState& exceptionState)
129 if (!validateToken(token, exceptionState))
132 if (containsInternal(token)) {
133 removeInternal(token);
140 bool DOMTokenList::toggle(const AtomicString& token, bool force, ExceptionState& exceptionState)
142 if (!validateToken(token, exceptionState))
148 removeInternal(token);
153 void DOMTokenList::addInternal(const AtomicString& token)
155 if (!containsInternal(token))
156 setValue(addToken(value(), token));
159 void DOMTokenList::removeInternal(const AtomicString& token)
161 // Check using contains first since it uses AtomicString comparisons instead
162 // of character by character testing.
163 if (!containsInternal(token))
165 setValue(removeToken(value(), token));
168 AtomicString DOMTokenList::addToken(const AtomicString& input, const AtomicString& token)
170 Vector<String> tokens;
171 tokens.append(token.string());
172 return addTokens(input, tokens);
175 // This returns an AtomicString because it is always passed as argument to setValue() and setValue()
176 // takes an AtomicString in argument.
177 AtomicString DOMTokenList::addTokens(const AtomicString& input, const Vector<String>& tokens)
179 bool needsSpace = false;
181 StringBuilder builder;
182 if (!input.isEmpty()) {
183 builder.append(input);
184 needsSpace = !isHTMLSpace<UChar>(input[input.length() - 1]);
187 for (size_t i = 0; i < tokens.size(); ++i) {
190 builder.append(tokens[i]);
194 return builder.toAtomicString();
197 AtomicString DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token)
199 Vector<String> tokens;
200 tokens.append(token.string());
201 return removeTokens(input, tokens);
204 // This returns an AtomicString because it is always passed as argument to setValue() and setValue()
205 // takes an AtomicString in argument.
206 AtomicString DOMTokenList::removeTokens(const AtomicString& input, const Vector<String>& tokens)
208 // Algorithm defined at http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string
209 // New spec is at http://dom.spec.whatwg.org/#remove-a-token-from-a-string
211 unsigned inputLength = input.length();
212 StringBuilder output; // 3
213 output.reserveCapacity(inputLength);
214 unsigned position = 0; // 4
217 while (position < inputLength) {
218 if (isHTMLSpace<UChar>(input[position])) { // 6
219 output.append(input[position++]); // 6.1, 6.2
224 StringBuilder tokenBuilder;
225 while (position < inputLength && isNotHTMLSpace<UChar>(input[position]))
226 tokenBuilder.append(input[position++]);
229 String token = tokenBuilder.toString();
230 if (tokens.contains(token)) {
232 while (position < inputLength && isHTMLSpace<UChar>(input[position]))
236 size_t j = output.length();
237 while (j > 0 && isHTMLSpace<UChar>(output[j - 1]))
242 if (position < inputLength && !output.isEmpty())
245 output.append(token); // Step 9
249 return output.toAtomicString();