Bug 1700051: part 35) Reduce accessibility of `mSoftText.mDOMMapping` to `private...
[gecko.git] / dom / base / nsDOMTokenList.cpp
blob7905324f2b191ed71297caa060709ff975999f52
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Implementation of DOMTokenList specified by HTML5.
9 */
11 #include "nsDOMTokenList.h"
12 #include "nsAttrValue.h"
13 #include "nsAttrValueInlines.h"
14 #include "nsTHashMap.h"
15 #include "nsError.h"
16 #include "nsHashKeys.h"
17 #include "mozilla/dom/Document.h"
18 #include "mozilla/dom/Element.h"
19 #include "mozilla/dom/DOMTokenListBinding.h"
20 #include "mozilla/BloomFilter.h"
21 #include "mozilla/ErrorResult.h"
23 using namespace mozilla;
24 using namespace mozilla::dom;
26 nsDOMTokenList::nsDOMTokenList(
27 Element* aElement, nsAtom* aAttrAtom,
28 const DOMTokenListSupportedTokenArray aSupportedTokens)
29 : mElement(aElement),
30 mAttrAtom(aAttrAtom),
31 mSupportedTokens(aSupportedTokens) {
32 // We don't add a reference to our element. If it goes away,
33 // we'll be told to drop our reference
36 nsDOMTokenList::~nsDOMTokenList() = default;
38 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMTokenList, mElement)
40 NS_INTERFACE_MAP_BEGIN(nsDOMTokenList)
41 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
42 NS_INTERFACE_MAP_ENTRY(nsISupports)
43 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMTokenList)
44 NS_INTERFACE_MAP_END
46 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTokenList)
47 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTokenList)
49 const nsAttrValue* nsDOMTokenList::GetParsedAttr() {
50 if (!mElement) {
51 return nullptr;
53 return mElement->GetAttrInfo(kNameSpaceID_None, mAttrAtom).mValue;
56 static void RemoveDuplicatesInternal(AtomArray* aArray, uint32_t aStart) {
57 nsTHashMap<nsPtrHashKey<nsAtom>, bool> tokens;
59 for (uint32_t i = 0; i < aArray->Length(); i++) {
60 nsAtom* atom = aArray->ElementAt(i);
61 // No need to check the hashtable below aStart
62 if (i >= aStart && tokens.Get(atom)) {
63 aArray->RemoveElementAt(i);
64 i--;
65 } else {
66 tokens.InsertOrUpdate(atom, true);
71 void nsDOMTokenList::RemoveDuplicates(const nsAttrValue* aAttr) {
72 if (!aAttr || aAttr->Type() != nsAttrValue::eAtomArray) {
73 return;
76 BitBloomFilter<8, nsAtom> filter;
77 AtomArray* array = aAttr->GetAtomArrayValue();
78 for (uint32_t i = 0; i < array->Length(); i++) {
79 nsAtom* atom = array->ElementAt(i);
80 if (filter.mightContain(atom)) {
81 // Start again, with a hashtable
82 RemoveDuplicatesInternal(array, i);
83 return;
84 } else {
85 filter.add(atom);
90 uint32_t nsDOMTokenList::Length() {
91 const nsAttrValue* attr = GetParsedAttr();
92 if (!attr) {
93 return 0;
96 RemoveDuplicates(attr);
97 return attr->GetAtomCount();
100 void nsDOMTokenList::IndexedGetter(uint32_t aIndex, bool& aFound,
101 nsAString& aResult) {
102 const nsAttrValue* attr = GetParsedAttr();
104 if (!attr || aIndex >= static_cast<uint32_t>(attr->GetAtomCount())) {
105 aFound = false;
106 return;
109 RemoveDuplicates(attr);
111 if (attr && aIndex < static_cast<uint32_t>(attr->GetAtomCount())) {
112 aFound = true;
113 attr->AtomAt(aIndex)->ToString(aResult);
114 } else {
115 aFound = false;
119 void nsDOMTokenList::GetValue(nsAString& aResult) {
120 if (!mElement) {
121 aResult.Truncate();
122 return;
125 mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult);
128 void nsDOMTokenList::SetValue(const nsAString& aValue, ErrorResult& rv) {
129 if (!mElement) {
130 return;
133 rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true);
136 nsresult nsDOMTokenList::CheckToken(const nsAString& aStr) {
137 if (aStr.IsEmpty()) {
138 return NS_ERROR_DOM_SYNTAX_ERR;
141 nsAString::const_iterator iter, end;
142 aStr.BeginReading(iter);
143 aStr.EndReading(end);
145 while (iter != end) {
146 if (nsContentUtils::IsHTMLWhitespace(*iter))
147 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
148 ++iter;
151 return NS_OK;
154 nsresult nsDOMTokenList::CheckTokens(const nsTArray<nsString>& aTokens) {
155 for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
156 nsresult rv = CheckToken(aTokens[i]);
157 if (NS_FAILED(rv)) {
158 return rv;
162 return NS_OK;
165 bool nsDOMTokenList::Contains(const nsAString& aToken) {
166 const nsAttrValue* attr = GetParsedAttr();
167 return attr && attr->Contains(aToken);
170 void nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
171 const nsTArray<nsString>& aTokens) {
172 if (!mElement) {
173 return;
176 nsAutoString resultStr;
178 if (aAttr) {
179 RemoveDuplicates(aAttr);
180 for (uint32_t i = 0; i < aAttr->GetAtomCount(); i++) {
181 if (i != 0) {
182 resultStr.AppendLiteral(" ");
184 resultStr.Append(nsDependentAtomString(aAttr->AtomAt(i)));
188 AutoTArray<nsString, 10> addedClasses;
190 for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
191 const nsString& aToken = aTokens[i];
193 if ((aAttr && aAttr->Contains(aToken)) || addedClasses.Contains(aToken)) {
194 continue;
197 if (!resultStr.IsEmpty()) {
198 resultStr.Append(' ');
200 resultStr.Append(aToken);
202 addedClasses.AppendElement(aToken);
205 mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
208 void nsDOMTokenList::Add(const nsTArray<nsString>& aTokens,
209 ErrorResult& aError) {
210 aError = CheckTokens(aTokens);
211 if (aError.Failed()) {
212 return;
215 const nsAttrValue* attr = GetParsedAttr();
216 AddInternal(attr, aTokens);
219 void nsDOMTokenList::Add(const nsAString& aToken, ErrorResult& aError) {
220 AutoTArray<nsString, 1> tokens;
221 tokens.AppendElement(aToken);
222 Add(tokens, aError);
225 void nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
226 const nsTArray<nsString>& aTokens) {
227 MOZ_ASSERT(aAttr, "Need an attribute");
229 RemoveDuplicates(aAttr);
231 nsAutoString resultStr;
232 for (uint32_t i = 0; i < aAttr->GetAtomCount(); i++) {
233 if (aTokens.Contains(nsDependentAtomString(aAttr->AtomAt(i)))) {
234 continue;
236 if (!resultStr.IsEmpty()) {
237 resultStr.AppendLiteral(" ");
239 resultStr.Append(nsDependentAtomString(aAttr->AtomAt(i)));
242 mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
245 void nsDOMTokenList::Remove(const nsTArray<nsString>& aTokens,
246 ErrorResult& aError) {
247 aError = CheckTokens(aTokens);
248 if (aError.Failed()) {
249 return;
252 const nsAttrValue* attr = GetParsedAttr();
253 if (!attr) {
254 return;
257 RemoveInternal(attr, aTokens);
260 void nsDOMTokenList::Remove(const nsAString& aToken, ErrorResult& aError) {
261 AutoTArray<nsString, 1> tokens;
262 tokens.AppendElement(aToken);
263 Remove(tokens, aError);
266 bool nsDOMTokenList::Toggle(const nsAString& aToken,
267 const Optional<bool>& aForce, ErrorResult& aError) {
268 aError = CheckToken(aToken);
269 if (aError.Failed()) {
270 return false;
273 const nsAttrValue* attr = GetParsedAttr();
274 const bool forceOn = aForce.WasPassed() && aForce.Value();
275 const bool forceOff = aForce.WasPassed() && !aForce.Value();
277 bool isPresent = attr && attr->Contains(aToken);
278 AutoTArray<nsString, 1> tokens;
279 (*tokens.AppendElement()).Rebind(aToken.Data(), aToken.Length());
281 if (isPresent) {
282 if (!forceOn) {
283 RemoveInternal(attr, tokens);
284 isPresent = false;
286 } else {
287 if (!forceOff) {
288 AddInternal(attr, tokens);
289 isPresent = true;
293 return isPresent;
296 bool nsDOMTokenList::Replace(const nsAString& aToken,
297 const nsAString& aNewToken, ErrorResult& aError) {
298 // Doing this here instead of using `CheckToken` because if aToken had invalid
299 // characters, and aNewToken is empty, the returned error should be a
300 // SyntaxError, not an InvalidCharacterError.
301 if (aNewToken.IsEmpty()) {
302 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
303 return false;
306 aError = CheckToken(aToken);
307 if (aError.Failed()) {
308 return false;
311 aError = CheckToken(aNewToken);
312 if (aError.Failed()) {
313 return false;
316 const nsAttrValue* attr = GetParsedAttr();
317 if (!attr) {
318 return false;
321 return ReplaceInternal(attr, aToken, aNewToken);
324 bool nsDOMTokenList::ReplaceInternal(const nsAttrValue* aAttr,
325 const nsAString& aToken,
326 const nsAString& aNewToken) {
327 RemoveDuplicates(aAttr);
329 // Trying to do a single pass here leads to really complicated code. Just do
330 // the simple thing.
331 bool haveOld = false;
332 for (uint32_t i = 0; i < aAttr->GetAtomCount(); ++i) {
333 if (aAttr->AtomAt(i)->Equals(aToken)) {
334 haveOld = true;
335 break;
338 if (!haveOld) {
339 // Make sure to not touch the attribute value in this case.
340 return false;
343 bool sawIt = false;
344 nsAutoString resultStr;
345 for (uint32_t i = 0; i < aAttr->GetAtomCount(); i++) {
346 if (aAttr->AtomAt(i)->Equals(aToken) ||
347 aAttr->AtomAt(i)->Equals(aNewToken)) {
348 if (sawIt) {
349 // We keep only the first
350 continue;
352 sawIt = true;
353 if (!resultStr.IsEmpty()) {
354 resultStr.AppendLiteral(" ");
356 resultStr.Append(aNewToken);
357 continue;
359 if (!resultStr.IsEmpty()) {
360 resultStr.AppendLiteral(" ");
362 resultStr.Append(nsDependentAtomString(aAttr->AtomAt(i)));
365 MOZ_ASSERT(sawIt, "How could we not have found our token this time?");
366 mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
367 return true;
370 bool nsDOMTokenList::Supports(const nsAString& aToken, ErrorResult& aError) {
371 if (!mSupportedTokens) {
372 aError.ThrowTypeError<MSG_TOKENLIST_NO_SUPPORTED_TOKENS>(
373 NS_ConvertUTF16toUTF8(mElement->LocalName()),
374 NS_ConvertUTF16toUTF8(nsDependentAtomString(mAttrAtom)));
375 return false;
378 for (DOMTokenListSupportedToken* supportedToken = mSupportedTokens;
379 *supportedToken; ++supportedToken) {
380 if (aToken.LowerCaseEqualsASCII(*supportedToken)) {
381 return true;
385 return false;
388 DocGroup* nsDOMTokenList::GetDocGroup() const {
389 return mElement ? mElement->OwnerDoc()->GetDocGroup() : nullptr;
392 JSObject* nsDOMTokenList::WrapObject(JSContext* cx,
393 JS::Handle<JSObject*> aGivenProto) {
394 return DOMTokenList_Binding::Wrap(cx, this, aGivenProto);