Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: third_party/WebKit/Source/core/dom/DOMTokenList.cpp

Issue 2895903002: DOMTokenList: Update serialization algorithm on add()/remove() (Closed)
Patch Set: Apply review comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved. 2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 13 matching lines...) Expand all
24 24
25 #include "core/dom/DOMTokenList.h" 25 #include "core/dom/DOMTokenList.h"
26 26
27 #include "bindings/core/v8/ExceptionState.h" 27 #include "bindings/core/v8/ExceptionState.h"
28 #include "core/dom/ExceptionCode.h" 28 #include "core/dom/ExceptionCode.h"
29 #include "core/html/parser/HTMLParserIdioms.h" 29 #include "core/html/parser/HTMLParserIdioms.h"
30 #include "platform/wtf/text/StringBuilder.h" 30 #include "platform/wtf/text/StringBuilder.h"
31 31
32 namespace blink { 32 namespace blink {
33 33
34 // This implements the common part of the following operations:
35 // https://dom.spec.whatwg.org/#dom-domtokenlist-add
36 // https://dom.spec.whatwg.org/#dom-domtokenlist-remove
37 // https://dom.spec.whatwg.org/#dom-domtokenlist-toggle
38 // https://dom.spec.whatwg.org/#dom-domtokenlist-replace
34 bool DOMTokenList::ValidateToken(const String& token, 39 bool DOMTokenList::ValidateToken(const String& token,
35 ExceptionState& exception_state) const { 40 ExceptionState& exception_state) const {
41 // 1. If token is the empty string, then throw a SyntaxError.
36 if (token.IsEmpty()) { 42 if (token.IsEmpty()) {
37 exception_state.ThrowDOMException(kSyntaxError, 43 exception_state.ThrowDOMException(kSyntaxError,
38 "The token provided must not be empty."); 44 "The token provided must not be empty.");
39 return false; 45 return false;
40 } 46 }
41 47
48 // 2. If token contains any ASCII whitespace, then throw an
49 // InvalidCharacterError.
42 if (token.Find(IsHTMLSpace) != kNotFound) { 50 if (token.Find(IsHTMLSpace) != kNotFound) {
43 exception_state.ThrowDOMException(kInvalidCharacterError, 51 exception_state.ThrowDOMException(kInvalidCharacterError,
44 "The token provided ('" + token + 52 "The token provided ('" + token +
45 "') contains HTML space characters, " 53 "') contains HTML space characters, "
46 "which are not valid in tokens."); 54 "which are not valid in tokens.");
47 return false; 55 return false;
48 } 56 }
49 57
50 return true; 58 return true;
51 } 59 }
(...skipping 20 matching lines...) Expand all
72 return ContainsInternal(token); 80 return ContainsInternal(token);
73 } 81 }
74 82
75 void DOMTokenList::add(const AtomicString& token, 83 void DOMTokenList::add(const AtomicString& token,
76 ExceptionState& exception_state) { 84 ExceptionState& exception_state) {
77 Vector<String> tokens; 85 Vector<String> tokens;
78 tokens.push_back(token.GetString()); 86 tokens.push_back(token.GetString());
79 add(tokens, exception_state); 87 add(tokens, exception_state);
80 } 88 }
81 89
90 // https://dom.spec.whatwg.org/#dom-domtokenlist-add
82 // Optimally, this should take a Vector<AtomicString> const ref in argument but 91 // Optimally, this should take a Vector<AtomicString> const ref in argument but
83 // the bindings generator does not handle that. 92 // the bindings generator does not handle that.
84 void DOMTokenList::add(const Vector<String>& tokens, 93 void DOMTokenList::add(const Vector<String>& tokens,
85 ExceptionState& exception_state) { 94 ExceptionState& exception_state) {
86 Vector<String> filtered_tokens; 95 if (!ValidateTokens(tokens, exception_state))
87 filtered_tokens.ReserveCapacity(tokens.size()); 96 return;
88 for (const auto& token : tokens) {
89 if (!ValidateToken(token, exception_state))
90 return;
91 if (ContainsInternal(AtomicString(token)))
92 continue;
93 if (filtered_tokens.Contains(token))
94 continue;
95 filtered_tokens.push_back(token);
96 }
97 97
98 if (!filtered_tokens.IsEmpty()) 98 setValue(AddTokens(tokens));
99 setValue(AddTokens(value(), filtered_tokens));
100 } 99 }
101 100
102 void DOMTokenList::remove(const AtomicString& token, 101 void DOMTokenList::remove(const AtomicString& token,
103 ExceptionState& exception_state) { 102 ExceptionState& exception_state) {
104 Vector<String> tokens; 103 Vector<String> tokens;
105 tokens.push_back(token.GetString()); 104 tokens.push_back(token.GetString());
106 remove(tokens, exception_state); 105 remove(tokens, exception_state);
107 } 106 }
108 107
108 // https://dom.spec.whatwg.org/#dom-domtokenlist-remove
109 // Optimally, this should take a Vector<AtomicString> const ref in argument but 109 // Optimally, this should take a Vector<AtomicString> const ref in argument but
110 // the bindings generator does not handle that. 110 // the bindings generator does not handle that.
111 void DOMTokenList::remove(const Vector<String>& tokens, 111 void DOMTokenList::remove(const Vector<String>& tokens,
112 ExceptionState& exception_state) { 112 ExceptionState& exception_state) {
113 if (!ValidateTokens(tokens, exception_state)) 113 if (!ValidateTokens(tokens, exception_state))
114 return; 114 return;
115 115
116 // Check using containsInternal first since it is a lot faster than going 116 // TODO(tkent): This null check doesn't conform to the DOM specification.
117 // through the string character by character. 117 // See https://github.com/whatwg/dom/issues/462
118 bool found = false; 118 if (value().IsNull())
119 for (const auto& token : tokens) { 119 return;
120 if (ContainsInternal(AtomicString(token))) { 120 setValue(RemoveTokens(tokens));
121 found = true;
122 break;
123 }
124 }
125
126 setValue(found ? RemoveTokens(value(), tokens) : value());
127 } 121 }
128 122
129 bool DOMTokenList::toggle(const AtomicString& token, 123 bool DOMTokenList::toggle(const AtomicString& token,
130 ExceptionState& exception_state) { 124 ExceptionState& exception_state) {
131 if (!ValidateToken(token, exception_state)) 125 if (!ValidateToken(token, exception_state))
132 return false; 126 return false;
133 127
134 if (ContainsInternal(token)) { 128 if (ContainsInternal(token)) {
135 RemoveInternal(token); 129 RemoveInternal(token);
136 return false; 130 return false;
(...skipping 16 matching lines...) Expand all
153 return force; 147 return force;
154 } 148 }
155 149
156 bool DOMTokenList::supports(const AtomicString& token, 150 bool DOMTokenList::supports(const AtomicString& token,
157 ExceptionState& exception_state) { 151 ExceptionState& exception_state) {
158 return ValidateTokenValue(token, exception_state); 152 return ValidateTokenValue(token, exception_state);
159 } 153 }
160 154
161 void DOMTokenList::AddInternal(const AtomicString& token) { 155 void DOMTokenList::AddInternal(const AtomicString& token) {
162 if (!ContainsInternal(token)) 156 if (!ContainsInternal(token))
163 setValue(AddToken(value(), token)); 157 setValue(AddToken(token));
164 } 158 }
165 159
166 void DOMTokenList::RemoveInternal(const AtomicString& token) { 160 void DOMTokenList::RemoveInternal(const AtomicString& token) {
167 // Check using contains first since it uses AtomicString comparisons instead 161 // Check using contains first since it uses AtomicString comparisons instead
168 // of character by character testing. 162 // of character by character testing.
169 if (!ContainsInternal(token)) 163 if (!ContainsInternal(token))
170 return; 164 return;
171 setValue(RemoveToken(value(), token)); 165 setValue(RemoveToken(token));
172 } 166 }
173 167
174 AtomicString DOMTokenList::AddToken(const AtomicString& input, 168 AtomicString DOMTokenList::AddToken(const AtomicString& token) {
175 const AtomicString& token) {
176 Vector<String> tokens; 169 Vector<String> tokens;
177 tokens.push_back(token.GetString()); 170 tokens.push_back(token.GetString());
178 return AddTokens(input, tokens); 171 return AddTokens(tokens);
179 } 172 }
180 173
174 // https://dom.spec.whatwg.org/#dom-domtokenlist-add
181 // This returns an AtomicString because it is always passed as argument to 175 // This returns an AtomicString because it is always passed as argument to
182 // setValue() and setValue() takes an AtomicString in argument. 176 // setValue() and setValue() takes an AtomicString in argument.
183 AtomicString DOMTokenList::AddTokens(const AtomicString& input, 177 AtomicString DOMTokenList::AddTokens(const Vector<String>& tokens) {
184 const Vector<String>& tokens) { 178 SpaceSplitString& token_set = MutableSet();
185 bool needs_space = false; 179 // 2. For each token in tokens, append token to context object’s token set.
180 for (const auto& token : tokens)
181 token_set.Add(AtomicString(token));
182 // 3. Run the update steps.
183 return SerializeSet(token_set);
184 }
186 185
186 AtomicString DOMTokenList::RemoveToken(const AtomicString& token) {
187 Vector<String> tokens;
188 tokens.push_back(token.GetString());
189 return RemoveTokens(tokens);
190 }
191
192 // https://dom.spec.whatwg.org/#dom-domtokenlist-remove
193 // This returns an AtomicString because it is always passed as argument to
194 // setValue() and setValue() takes an AtomicString in argument.
195 AtomicString DOMTokenList::RemoveTokens(const Vector<String>& tokens) {
196 SpaceSplitString& token_set = MutableSet();
197 // 2. For each token in tokens, remove token from context object’s token set.
198 for (const auto& token : tokens)
199 token_set.Remove(AtomicString(token));
200 // 3. Run the update steps.
201 return SerializeSet(token_set);
202 }
203
204 // https://dom.spec.whatwg.org/#concept-ordered-set-serializer
205 // The ordered set serializer takes a set and returns the concatenation of the
206 // strings in set, separated from each other by U+0020, if set is non-empty, and
207 // the empty string otherwise.
208 AtomicString DOMTokenList::SerializeSet(const SpaceSplitString& token_set) {
209 size_t size = token_set.size();
210 if (size == 0)
211 return g_empty_atom;
212 if (size == 1)
213 return token_set[0];
187 StringBuilder builder; 214 StringBuilder builder;
188 if (!input.IsEmpty()) { 215 builder.Append(token_set[0]);
189 builder.Append(input); 216 for (size_t i = 1; i < size; ++i) {
190 needs_space = !IsHTMLSpace<UChar>(input[input.length() - 1]); 217 builder.Append(' ');
218 builder.Append(token_set[i]);
191 } 219 }
192
193 for (const auto& token : tokens) {
194 if (needs_space)
195 builder.Append(' ');
196 builder.Append(token);
197 needs_space = true;
198 }
199
200 return builder.ToAtomicString(); 220 return builder.ToAtomicString();
201 } 221 }
202 222
203 AtomicString DOMTokenList::RemoveToken(const AtomicString& input,
204 const AtomicString& token) {
205 Vector<String> tokens;
206 tokens.push_back(token.GetString());
207 return RemoveTokens(input, tokens);
208 }
209
210 // This returns an AtomicString because it is always passed as argument to
211 // setValue() and setValue() takes an AtomicString in argument.
212 AtomicString DOMTokenList::RemoveTokens(const AtomicString& input,
213 const Vector<String>& tokens) {
214 // Algorithm defined at
215 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyn taxes.html#remove-a-token-from-a-string
216 // New spec is at https://dom.spec.whatwg.org/#remove-a-token-from-a-string
217
218 unsigned input_length = input.length();
219 StringBuilder output; // 3
220 output.ReserveCapacity(input_length);
221 unsigned position = 0; // 4
222
223 // Step 5
224 while (position < input_length) {
225 if (IsHTMLSpace<UChar>(input[position])) { // 6
226 position++;
227 continue; // 6.3
228 }
229
230 // Step 7
231 StringBuilder token_builder;
232 while (position < input_length && IsNotHTMLSpace<UChar>(input[position]))
233 token_builder.Append(input[position++]);
234
235 // Step 8
236 String token = token_builder.ToString();
237 if (tokens.Contains(token)) {
238 // Step 8.1
239 while (position < input_length && IsHTMLSpace<UChar>(input[position]))
240 ++position;
241
242 // Step 8.2
243 size_t j = output.length();
244 while (j > 0 && IsHTMLSpace<UChar>(output[j - 1]))
245 --j;
246 output.Resize(j);
247 } else {
248 output.Append(token); // Step 9
249 }
250
251 if (position < input_length && !output.IsEmpty())
252 output.Append(' ');
253 }
254
255 size_t j = output.length();
256 if (j > 0 && IsHTMLSpace<UChar>(output[j - 1]))
257 output.Resize(j - 1);
258
259 return output.ToAtomicString();
260 }
261
262 void DOMTokenList::setValue(const AtomicString& value) { 223 void DOMTokenList::setValue(const AtomicString& value) {
263 bool value_changed = value_ != value; 224 bool value_changed = value_ != value;
264 value_ = value; 225 value_ = value;
265 if (value_changed) 226 if (value_changed)
266 tokens_.Set(value, SpaceSplitString::kShouldNotFoldCase); 227 tokens_.Set(value, SpaceSplitString::kShouldNotFoldCase);
267 if (observer_) 228 if (observer_)
268 observer_->ValueWasSet(); 229 observer_->ValueWasSet();
269 } 230 }
270 231
271 bool DOMTokenList::ContainsInternal(const AtomicString& token) const { 232 bool DOMTokenList::ContainsInternal(const AtomicString& token) const {
272 return tokens_.Contains(token); 233 return tokens_.Contains(token);
273 } 234 }
274 235
275 const AtomicString DOMTokenList::item(unsigned index) const { 236 const AtomicString DOMTokenList::item(unsigned index) const {
276 if (index >= length()) 237 if (index >= length())
277 return AtomicString(); 238 return AtomicString();
278 return tokens_[index]; 239 return tokens_[index];
279 } 240 }
280 241
281 } // namespace blink 242 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/dom/DOMTokenList.h ('k') | third_party/WebKit/Source/core/dom/SpaceSplitString.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698