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 "DOMTokenList.h" 27 28#include "ExceptionCode.h" 29#include "HTMLParserIdioms.h" 30#include <wtf/text/StringBuilder.h> 31 32namespace WebCore { 33 34bool DOMTokenList::validateToken(const AtomicString& token, ExceptionCode& ec) 35{ 36 if (token.isEmpty()) { 37 ec = SYNTAX_ERR; 38 return false; 39 } 40 41 unsigned length = token.length(); 42 for (unsigned i = 0; i < length; ++i) { 43 if (isHTMLSpace(token[i])) { 44 ec = INVALID_CHARACTER_ERR; 45 return false; 46 } 47 } 48 49 return true; 50} 51 52bool DOMTokenList::validateTokens(const Vector<String>& tokens, ExceptionCode& ec) 53{ 54 for (size_t i = 0; i < tokens.size(); ++i) { 55 if (!validateToken(tokens[i], ec)) 56 return false; 57 } 58 59 return true; 60} 61 62bool DOMTokenList::contains(const AtomicString& token, ExceptionCode& ec) const 63{ 64 if (!validateToken(token, ec)) 65 return false; 66 return containsInternal(token); 67} 68 69void DOMTokenList::add(const AtomicString& token, ExceptionCode& ec) 70{ 71 Vector<String> tokens; 72 tokens.append(token.string()); 73 add(tokens, ec); 74} 75 76void DOMTokenList::add(const Vector<String>& tokens, ExceptionCode& ec) 77{ 78 Vector<String> filteredTokens; 79 for (size_t i = 0; i < tokens.size(); ++i) { 80 if (!validateToken(tokens[i], ec)) 81 return; 82 if (!containsInternal(tokens[i])) 83 filteredTokens.append(tokens[i]); 84 } 85 86 if (filteredTokens.isEmpty()) 87 return; 88 89 setValue(addTokens(value(), filteredTokens)); 90} 91 92void DOMTokenList::remove(const AtomicString& token, ExceptionCode& ec) 93{ 94 Vector<String> tokens; 95 tokens.append(token.string()); 96 remove(tokens, ec); 97} 98 99void DOMTokenList::remove(const Vector<String>& tokens, ExceptionCode& ec) 100{ 101 if (!validateTokens(tokens, ec)) 102 return; 103 104 // Check using containsInternal first since it is a lot faster than going 105 // through the string character by character. 106 bool found = false; 107 for (size_t i = 0; i < tokens.size(); ++i) { 108 if (containsInternal(tokens[i])) { 109 found = true; 110 break; 111 } 112 } 113 114 if (found) 115 setValue(removeTokens(value(), tokens)); 116} 117 118bool DOMTokenList::toggle(const AtomicString& token, ExceptionCode& ec) 119{ 120 if (!validateToken(token, ec)) 121 return false; 122 123 if (containsInternal(token)) { 124 removeInternal(token); 125 return false; 126 } 127 addInternal(token); 128 return true; 129} 130 131bool DOMTokenList::toggle(const AtomicString& token, bool force, ExceptionCode& ec) 132{ 133 if (!validateToken(token, ec)) 134 return false; 135 136 if (force) 137 addInternal(token); 138 else 139 removeInternal(token); 140 141 return force; 142} 143 144void DOMTokenList::addInternal(const AtomicString& token) 145{ 146 if (!containsInternal(token)) 147 setValue(addToken(value(), token)); 148} 149 150void DOMTokenList::removeInternal(const AtomicString& token) 151{ 152 // Check using contains first since it uses AtomicString comparisons instead 153 // of character by character testing. 154 if (!containsInternal(token)) 155 return; 156 setValue(removeToken(value(), token)); 157} 158 159String DOMTokenList::addToken(const AtomicString& input, const AtomicString& token) 160{ 161 Vector<String> tokens; 162 tokens.append(token.string()); 163 return addTokens(input, tokens); 164} 165 166String DOMTokenList::addTokens(const AtomicString& input, const Vector<String>& tokens) 167{ 168 bool needsSpace = false; 169 170 StringBuilder builder; 171 if (!input.isEmpty()) { 172 builder.append(input); 173 needsSpace = !isHTMLSpace(input[input.length() - 1]); 174 } 175 176 for (size_t i = 0; i < tokens.size(); ++i) { 177 if (needsSpace) 178 builder.append(' '); 179 builder.append(tokens[i]); 180 needsSpace = true; 181 } 182 183 return builder.toString(); 184} 185 186String DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token) 187{ 188 Vector<String> tokens; 189 tokens.append(token.string()); 190 return removeTokens(input, tokens); 191} 192 193String DOMTokenList::removeTokens(const AtomicString& input, const Vector<String>& tokens) 194{ 195 // Algorithm defined at http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string 196 // New spec is at http://dom.spec.whatwg.org/#remove-a-token-from-a-string 197 198 unsigned inputLength = input.length(); 199 StringBuilder output; // 3 200 output.reserveCapacity(inputLength); 201 unsigned position = 0; // 4 202 203 // Step 5 204 while (position < inputLength) { 205 if (isHTMLSpace(input[position])) { // 6 206 output.append(input[position++]); // 6.1, 6.2 207 continue; // 6.3 208 } 209 210 // Step 7 211 StringBuilder s; 212 while (position < inputLength && isNotHTMLSpace(input[position])) 213 s.append(input[position++]); 214 215 // Step 8 216 if (tokens.contains(s.toStringPreserveCapacity())) { 217 // Step 8.1 218 while (position < inputLength && isHTMLSpace(input[position])) 219 ++position; 220 221 // Step 8.2 222 size_t j = output.length(); 223 while (j > 0 && isHTMLSpace(output[j - 1])) 224 --j; 225 output.resize(j); 226 227 // Step 8.3 228 if (position < inputLength && !output.isEmpty()) 229 output.append(' '); 230 } else 231 output.append(s.toStringPreserveCapacity()); // Step 9 232 } 233 234 return output.toString(); 235} 236 237} // namespace WebCore 238