1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsDOMTokenList.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,316 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * Implementation of DOMTokenList specified by HTML5. 1.10 + */ 1.11 + 1.12 +#include "nsDOMTokenList.h" 1.13 + 1.14 +#include "nsAttrValue.h" 1.15 +#include "nsContentUtils.h" 1.16 +#include "nsError.h" 1.17 +#include "mozilla/dom/Element.h" 1.18 +#include "mozilla/dom/DOMTokenListBinding.h" 1.19 +#include "mozilla/ErrorResult.h" 1.20 + 1.21 +using namespace mozilla; 1.22 +using namespace mozilla::dom; 1.23 + 1.24 +nsDOMTokenList::nsDOMTokenList(Element* aElement, nsIAtom* aAttrAtom) 1.25 + : mElement(aElement), 1.26 + mAttrAtom(aAttrAtom) 1.27 +{ 1.28 + // We don't add a reference to our element. If it goes away, 1.29 + // we'll be told to drop our reference 1.30 + SetIsDOMBinding(); 1.31 +} 1.32 + 1.33 +nsDOMTokenList::~nsDOMTokenList() { } 1.34 + 1.35 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMTokenList, mElement) 1.36 + 1.37 +NS_INTERFACE_MAP_BEGIN(nsDOMTokenList) 1.38 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.39 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.40 + NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMTokenList) 1.41 +NS_INTERFACE_MAP_END 1.42 + 1.43 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTokenList) 1.44 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTokenList) 1.45 + 1.46 +const nsAttrValue* 1.47 +nsDOMTokenList::GetParsedAttr() 1.48 +{ 1.49 + if (!mElement) { 1.50 + return nullptr; 1.51 + } 1.52 + return mElement->GetAttrInfo(kNameSpaceID_None, mAttrAtom).mValue; 1.53 +} 1.54 + 1.55 +uint32_t 1.56 +nsDOMTokenList::Length() 1.57 +{ 1.58 + const nsAttrValue* attr = GetParsedAttr(); 1.59 + if (!attr) { 1.60 + return 0; 1.61 + } 1.62 + 1.63 + return attr->GetAtomCount(); 1.64 +} 1.65 + 1.66 +void 1.67 +nsDOMTokenList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aResult) 1.68 +{ 1.69 + const nsAttrValue* attr = GetParsedAttr(); 1.70 + 1.71 + if (attr && aIndex < static_cast<uint32_t>(attr->GetAtomCount())) { 1.72 + aFound = true; 1.73 + attr->AtomAt(aIndex)->ToString(aResult); 1.74 + } else { 1.75 + aFound = false; 1.76 + } 1.77 +} 1.78 + 1.79 +nsresult 1.80 +nsDOMTokenList::CheckToken(const nsAString& aStr) 1.81 +{ 1.82 + if (aStr.IsEmpty()) { 1.83 + return NS_ERROR_DOM_SYNTAX_ERR; 1.84 + } 1.85 + 1.86 + nsAString::const_iterator iter, end; 1.87 + aStr.BeginReading(iter); 1.88 + aStr.EndReading(end); 1.89 + 1.90 + while (iter != end) { 1.91 + if (nsContentUtils::IsHTMLWhitespace(*iter)) 1.92 + return NS_ERROR_DOM_INVALID_CHARACTER_ERR; 1.93 + ++iter; 1.94 + } 1.95 + 1.96 + return NS_OK; 1.97 +} 1.98 + 1.99 +nsresult 1.100 +nsDOMTokenList::CheckTokens(const nsTArray<nsString>& aTokens) 1.101 +{ 1.102 + for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) { 1.103 + nsresult rv = CheckToken(aTokens[i]); 1.104 + if (NS_FAILED(rv)) { 1.105 + return rv; 1.106 + } 1.107 + } 1.108 + 1.109 + return NS_OK; 1.110 +} 1.111 + 1.112 +bool 1.113 +nsDOMTokenList::Contains(const nsAString& aToken, ErrorResult& aError) 1.114 +{ 1.115 + aError = CheckToken(aToken); 1.116 + if (aError.Failed()) { 1.117 + return false; 1.118 + } 1.119 + 1.120 + const nsAttrValue* attr = GetParsedAttr(); 1.121 + return attr && attr->Contains(aToken); 1.122 +} 1.123 + 1.124 +void 1.125 +nsDOMTokenList::AddInternal(const nsAttrValue* aAttr, 1.126 + const nsTArray<nsString>& aTokens) 1.127 +{ 1.128 + if (!mElement) { 1.129 + return; 1.130 + } 1.131 + 1.132 + nsAutoString resultStr; 1.133 + 1.134 + if (aAttr) { 1.135 + aAttr->ToString(resultStr); 1.136 + } 1.137 + 1.138 + bool oneWasAdded = false; 1.139 + nsAutoTArray<nsString, 10> addedClasses; 1.140 + 1.141 + for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) { 1.142 + const nsString& aToken = aTokens[i]; 1.143 + 1.144 + if ((aAttr && aAttr->Contains(aToken)) || 1.145 + addedClasses.Contains(aToken)) { 1.146 + continue; 1.147 + } 1.148 + 1.149 + if (oneWasAdded || 1.150 + (!resultStr.IsEmpty() && 1.151 + !nsContentUtils::IsHTMLWhitespace(resultStr.Last()))) { 1.152 + resultStr.Append(NS_LITERAL_STRING(" ") + aToken); 1.153 + } else { 1.154 + resultStr.Append(aToken); 1.155 + } 1.156 + 1.157 + oneWasAdded = true; 1.158 + addedClasses.AppendElement(aToken); 1.159 + } 1.160 + 1.161 + mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true); 1.162 +} 1.163 + 1.164 +void 1.165 +nsDOMTokenList::Add(const nsTArray<nsString>& aTokens, ErrorResult& aError) 1.166 +{ 1.167 + aError = CheckTokens(aTokens); 1.168 + if (aError.Failed()) { 1.169 + return; 1.170 + } 1.171 + 1.172 + const nsAttrValue* attr = GetParsedAttr(); 1.173 + AddInternal(attr, aTokens); 1.174 +} 1.175 + 1.176 +void 1.177 +nsDOMTokenList::Add(const nsAString& aToken, mozilla::ErrorResult& aError) 1.178 +{ 1.179 + nsAutoTArray<nsString, 1> tokens; 1.180 + tokens.AppendElement(aToken); 1.181 + Add(tokens, aError); 1.182 +} 1.183 + 1.184 +void 1.185 +nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr, 1.186 + const nsTArray<nsString>& aTokens) 1.187 +{ 1.188 + NS_ABORT_IF_FALSE(aAttr, "Need an attribute"); 1.189 + 1.190 + nsAutoString input; 1.191 + aAttr->ToString(input); 1.192 + 1.193 + nsAString::const_iterator copyStart, tokenStart, iter, end; 1.194 + input.BeginReading(iter); 1.195 + input.EndReading(end); 1.196 + copyStart = iter; 1.197 + 1.198 + nsAutoString output; 1.199 + bool lastTokenRemoved = false; 1.200 + 1.201 + while (iter != end) { 1.202 + // skip whitespace. 1.203 + while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) { 1.204 + ++iter; 1.205 + } 1.206 + 1.207 + if (iter == end) { 1.208 + // At this point we're sure the last seen token (if any) wasn't to be 1.209 + // removed. So the trailing spaces will need to be kept. 1.210 + NS_ABORT_IF_FALSE(!lastTokenRemoved, "How did this happen?"); 1.211 + 1.212 + output.Append(Substring(copyStart, end)); 1.213 + break; 1.214 + } 1.215 + 1.216 + tokenStart = iter; 1.217 + do { 1.218 + ++iter; 1.219 + } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter)); 1.220 + 1.221 + if (aTokens.Contains(Substring(tokenStart, iter))) { 1.222 + 1.223 + // Skip whitespace after the token, it will be collapsed. 1.224 + while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) { 1.225 + ++iter; 1.226 + } 1.227 + copyStart = iter; 1.228 + lastTokenRemoved = true; 1.229 + 1.230 + } else { 1.231 + 1.232 + if (lastTokenRemoved && !output.IsEmpty()) { 1.233 + NS_ABORT_IF_FALSE(!nsContentUtils::IsHTMLWhitespace( 1.234 + output.Last()), "Invalid last output token"); 1.235 + output.Append(char16_t(' ')); 1.236 + } 1.237 + lastTokenRemoved = false; 1.238 + output.Append(Substring(copyStart, iter)); 1.239 + copyStart = iter; 1.240 + } 1.241 + } 1.242 + 1.243 + mElement->SetAttr(kNameSpaceID_None, mAttrAtom, output, true); 1.244 +} 1.245 + 1.246 +void 1.247 +nsDOMTokenList::Remove(const nsTArray<nsString>& aTokens, ErrorResult& aError) 1.248 +{ 1.249 + aError = CheckTokens(aTokens); 1.250 + if (aError.Failed()) { 1.251 + return; 1.252 + } 1.253 + 1.254 + const nsAttrValue* attr = GetParsedAttr(); 1.255 + if (!attr) { 1.256 + return; 1.257 + } 1.258 + 1.259 + RemoveInternal(attr, aTokens); 1.260 +} 1.261 + 1.262 +void 1.263 +nsDOMTokenList::Remove(const nsAString& aToken, mozilla::ErrorResult& aError) 1.264 +{ 1.265 + nsAutoTArray<nsString, 1> tokens; 1.266 + tokens.AppendElement(aToken); 1.267 + Remove(tokens, aError); 1.268 +} 1.269 + 1.270 +bool 1.271 +nsDOMTokenList::Toggle(const nsAString& aToken, 1.272 + const Optional<bool>& aForce, 1.273 + ErrorResult& aError) 1.274 +{ 1.275 + aError = CheckToken(aToken); 1.276 + if (aError.Failed()) { 1.277 + return false; 1.278 + } 1.279 + 1.280 + const nsAttrValue* attr = GetParsedAttr(); 1.281 + const bool forceOn = aForce.WasPassed() && aForce.Value(); 1.282 + const bool forceOff = aForce.WasPassed() && !aForce.Value(); 1.283 + 1.284 + bool isPresent = attr && attr->Contains(aToken); 1.285 + nsAutoTArray<nsString, 1> tokens; 1.286 + (*tokens.AppendElement()).Rebind(aToken.Data(), aToken.Length()); 1.287 + 1.288 + if (isPresent) { 1.289 + if (!forceOn) { 1.290 + RemoveInternal(attr, tokens); 1.291 + isPresent = false; 1.292 + } 1.293 + } else { 1.294 + if (!forceOff) { 1.295 + AddInternal(attr, tokens); 1.296 + isPresent = true; 1.297 + } 1.298 + } 1.299 + 1.300 + return isPresent; 1.301 +} 1.302 + 1.303 +void 1.304 +nsDOMTokenList::Stringify(nsAString& aResult) 1.305 +{ 1.306 + if (!mElement) { 1.307 + aResult.Truncate(); 1.308 + return; 1.309 + } 1.310 + 1.311 + mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult); 1.312 +} 1.313 + 1.314 +JSObject* 1.315 +nsDOMTokenList::WrapObject(JSContext *cx) 1.316 +{ 1.317 + return DOMTokenListBinding::Wrap(cx, this); 1.318 +} 1.319 +