Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / dom / DOMTokenList.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
23  */
24
25 #include "config.h"
26 #include "core/dom/DOMTokenList.h"
27
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"
32
33 namespace blink {
34
35 bool DOMTokenList::validateToken(const String& token, ExceptionState& exceptionState)
36 {
37     if (token.isEmpty()) {
38         exceptionState.throwDOMException(SyntaxError, "The token provided must not be empty.");
39         return false;
40     }
41
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.");
46             return false;
47         }
48     }
49
50     return true;
51 }
52
53 bool DOMTokenList::validateTokens(const Vector<String>& tokens, ExceptionState& exceptionState)
54 {
55     for (size_t i = 0; i < tokens.size(); ++i) {
56         if (!validateToken(tokens[i], exceptionState))
57             return false;
58     }
59
60     return true;
61 }
62
63 bool DOMTokenList::contains(const AtomicString& token, ExceptionState& exceptionState) const
64 {
65     if (!validateToken(token, exceptionState))
66         return false;
67     return containsInternal(token);
68 }
69
70 void DOMTokenList::add(const AtomicString& token, ExceptionState& exceptionState)
71 {
72     Vector<String> tokens;
73     tokens.append(token.string());
74     add(tokens, exceptionState);
75 }
76
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)
80 {
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))
85             return;
86         if (containsInternal(AtomicString(tokens[i])))
87             continue;
88         if (filteredTokens.contains(tokens[i]))
89             continue;
90         filteredTokens.append(tokens[i]);
91     }
92
93     if (filteredTokens.isEmpty())
94         return;
95
96     setValue(addTokens(value(), filteredTokens));
97 }
98
99 void DOMTokenList::remove(const AtomicString& token, ExceptionState& exceptionState)
100 {
101     Vector<String> tokens;
102     tokens.append(token.string());
103     remove(tokens, exceptionState);
104 }
105
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)
109 {
110     if (!validateTokens(tokens, exceptionState))
111         return;
112
113     // Check using containsInternal first since it is a lot faster than going
114     // through the string character by character.
115     bool found = false;
116     for (size_t i = 0; i < tokens.size(); ++i) {
117         if (containsInternal(AtomicString(tokens[i]))) {
118             found = true;
119             break;
120         }
121     }
122
123     if (found)
124         setValue(removeTokens(value(), tokens));
125 }
126
127 bool DOMTokenList::toggle(const AtomicString& token, ExceptionState& exceptionState)
128 {
129     if (!validateToken(token, exceptionState))
130         return false;
131
132     if (containsInternal(token)) {
133         removeInternal(token);
134         return false;
135     }
136     addInternal(token);
137     return true;
138 }
139
140 bool DOMTokenList::toggle(const AtomicString& token, bool force, ExceptionState& exceptionState)
141 {
142     if (!validateToken(token, exceptionState))
143         return false;
144
145     if (force)
146         addInternal(token);
147     else
148         removeInternal(token);
149
150     return force;
151 }
152
153 void DOMTokenList::addInternal(const AtomicString& token)
154 {
155     if (!containsInternal(token))
156         setValue(addToken(value(), token));
157 }
158
159 void DOMTokenList::removeInternal(const AtomicString& token)
160 {
161     // Check using contains first since it uses AtomicString comparisons instead
162     // of character by character testing.
163     if (!containsInternal(token))
164         return;
165     setValue(removeToken(value(), token));
166 }
167
168 AtomicString DOMTokenList::addToken(const AtomicString& input, const AtomicString& token)
169 {
170     Vector<String> tokens;
171     tokens.append(token.string());
172     return addTokens(input, tokens);
173 }
174
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)
178 {
179     bool needsSpace = false;
180
181     StringBuilder builder;
182     if (!input.isEmpty()) {
183         builder.append(input);
184         needsSpace = !isHTMLSpace<UChar>(input[input.length() - 1]);
185     }
186
187     for (size_t i = 0; i < tokens.size(); ++i) {
188         if (needsSpace)
189             builder.append(' ');
190         builder.append(tokens[i]);
191         needsSpace = true;
192     }
193
194     return builder.toAtomicString();
195 }
196
197 AtomicString DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token)
198 {
199     Vector<String> tokens;
200     tokens.append(token.string());
201     return removeTokens(input, tokens);
202 }
203
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)
207 {
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
210
211     unsigned inputLength = input.length();
212     StringBuilder output; // 3
213     output.reserveCapacity(inputLength);
214     unsigned position = 0; // 4
215
216     // Step 5
217     while (position < inputLength) {
218         if (isHTMLSpace<UChar>(input[position])) { // 6
219             output.append(input[position++]); // 6.1, 6.2
220             continue; // 6.3
221         }
222
223         // Step 7
224         StringBuilder tokenBuilder;
225         while (position < inputLength && isNotHTMLSpace<UChar>(input[position]))
226             tokenBuilder.append(input[position++]);
227
228         // Step 8
229         String token = tokenBuilder.toString();
230         if (tokens.contains(token)) {
231             // Step 8.1
232             while (position < inputLength && isHTMLSpace<UChar>(input[position]))
233                 ++position;
234
235             // Step 8.2
236             size_t j = output.length();
237             while (j > 0 && isHTMLSpace<UChar>(output[j - 1]))
238                 --j;
239             output.resize(j);
240
241             // Step 8.3
242             if (position < inputLength && !output.isEmpty())
243                 output.append(' ');
244         } else {
245             output.append(token); // Step 9
246         }
247     }
248
249     return output.toAtomicString();
250 }
251
252 } // namespace blink