Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / nsDOMTokenList.cpp
blob856718566f5e6f96f517213a9fe0b4e2e743310b
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /*
6 * Implementation of DOMTokenList specified by HTML5.
7 */
9 #include "nsDOMTokenList.h"
11 #include "nsAttrValue.h"
12 #include "nsContentUtils.h"
13 #include "nsError.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/DOMTokenListBinding.h"
16 #include "mozilla/ErrorResult.h"
18 using namespace mozilla;
19 using namespace mozilla::dom;
21 nsDOMTokenList::nsDOMTokenList(Element* aElement, nsIAtom* aAttrAtom)
22 : mElement(aElement),
23 mAttrAtom(aAttrAtom)
25 // We don't add a reference to our element. If it goes away,
26 // we'll be told to drop our reference
29 nsDOMTokenList::~nsDOMTokenList() { }
31 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMTokenList, mElement)
33 NS_INTERFACE_MAP_BEGIN(nsDOMTokenList)
34 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
35 NS_INTERFACE_MAP_ENTRY(nsISupports)
36 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMTokenList)
37 NS_INTERFACE_MAP_END
39 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTokenList)
40 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTokenList)
42 const nsAttrValue*
43 nsDOMTokenList::GetParsedAttr()
45 if (!mElement) {
46 return nullptr;
48 return mElement->GetAttrInfo(kNameSpaceID_None, mAttrAtom).mValue;
51 uint32_t
52 nsDOMTokenList::Length()
54 const nsAttrValue* attr = GetParsedAttr();
55 if (!attr) {
56 return 0;
59 return attr->GetAtomCount();
62 void
63 nsDOMTokenList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aResult)
65 const nsAttrValue* attr = GetParsedAttr();
67 if (attr && aIndex < static_cast<uint32_t>(attr->GetAtomCount())) {
68 aFound = true;
69 attr->AtomAt(aIndex)->ToString(aResult);
70 } else {
71 aFound = false;
75 nsresult
76 nsDOMTokenList::CheckToken(const nsAString& aStr)
78 if (aStr.IsEmpty()) {
79 return NS_ERROR_DOM_SYNTAX_ERR;
82 nsAString::const_iterator iter, end;
83 aStr.BeginReading(iter);
84 aStr.EndReading(end);
86 while (iter != end) {
87 if (nsContentUtils::IsHTMLWhitespace(*iter))
88 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
89 ++iter;
92 return NS_OK;
95 nsresult
96 nsDOMTokenList::CheckTokens(const nsTArray<nsString>& aTokens)
98 for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
99 nsresult rv = CheckToken(aTokens[i]);
100 if (NS_FAILED(rv)) {
101 return rv;
105 return NS_OK;
108 bool
109 nsDOMTokenList::Contains(const nsAString& aToken, ErrorResult& aError)
111 aError = CheckToken(aToken);
112 if (aError.Failed()) {
113 return false;
116 const nsAttrValue* attr = GetParsedAttr();
117 return attr && attr->Contains(aToken);
120 void
121 nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
122 const nsTArray<nsString>& aTokens)
124 if (!mElement) {
125 return;
128 nsAutoString resultStr;
130 if (aAttr) {
131 aAttr->ToString(resultStr);
134 bool oneWasAdded = false;
135 nsAutoTArray<nsString, 10> addedClasses;
137 for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
138 const nsString& aToken = aTokens[i];
140 if ((aAttr && aAttr->Contains(aToken)) ||
141 addedClasses.Contains(aToken)) {
142 continue;
145 if (oneWasAdded ||
146 (!resultStr.IsEmpty() &&
147 !nsContentUtils::IsHTMLWhitespace(resultStr.Last()))) {
148 resultStr.Append(' ');
149 resultStr.Append(aToken);
150 } else {
151 resultStr.Append(aToken);
154 oneWasAdded = true;
155 addedClasses.AppendElement(aToken);
158 mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
161 void
162 nsDOMTokenList::Add(const nsTArray<nsString>& aTokens, ErrorResult& aError)
164 aError = CheckTokens(aTokens);
165 if (aError.Failed()) {
166 return;
169 const nsAttrValue* attr = GetParsedAttr();
170 AddInternal(attr, aTokens);
173 void
174 nsDOMTokenList::Add(const nsAString& aToken, mozilla::ErrorResult& aError)
176 nsAutoTArray<nsString, 1> tokens;
177 tokens.AppendElement(aToken);
178 Add(tokens, aError);
181 void
182 nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
183 const nsTArray<nsString>& aTokens)
185 NS_ABORT_IF_FALSE(aAttr, "Need an attribute");
187 nsAutoString input;
188 aAttr->ToString(input);
190 nsAString::const_iterator copyStart, tokenStart, iter, end;
191 input.BeginReading(iter);
192 input.EndReading(end);
193 copyStart = iter;
195 nsAutoString output;
196 bool lastTokenRemoved = false;
198 while (iter != end) {
199 // skip whitespace.
200 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
201 ++iter;
204 if (iter == end) {
205 // At this point we're sure the last seen token (if any) wasn't to be
206 // removed. So the trailing spaces will need to be kept.
207 NS_ABORT_IF_FALSE(!lastTokenRemoved, "How did this happen?");
209 output.Append(Substring(copyStart, end));
210 break;
213 tokenStart = iter;
214 do {
215 ++iter;
216 } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
218 if (aTokens.Contains(Substring(tokenStart, iter))) {
220 // Skip whitespace after the token, it will be collapsed.
221 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
222 ++iter;
224 copyStart = iter;
225 lastTokenRemoved = true;
227 } else {
229 if (lastTokenRemoved && !output.IsEmpty()) {
230 NS_ABORT_IF_FALSE(!nsContentUtils::IsHTMLWhitespace(
231 output.Last()), "Invalid last output token");
232 output.Append(char16_t(' '));
234 lastTokenRemoved = false;
235 output.Append(Substring(copyStart, iter));
236 copyStart = iter;
240 mElement->SetAttr(kNameSpaceID_None, mAttrAtom, output, true);
243 void
244 nsDOMTokenList::Remove(const nsTArray<nsString>& aTokens, ErrorResult& aError)
246 aError = CheckTokens(aTokens);
247 if (aError.Failed()) {
248 return;
251 const nsAttrValue* attr = GetParsedAttr();
252 if (!attr) {
253 return;
256 RemoveInternal(attr, aTokens);
259 void
260 nsDOMTokenList::Remove(const nsAString& aToken, mozilla::ErrorResult& aError)
262 nsAutoTArray<nsString, 1> tokens;
263 tokens.AppendElement(aToken);
264 Remove(tokens, aError);
267 bool
268 nsDOMTokenList::Toggle(const nsAString& aToken,
269 const Optional<bool>& aForce,
270 ErrorResult& aError)
272 aError = CheckToken(aToken);
273 if (aError.Failed()) {
274 return false;
277 const nsAttrValue* attr = GetParsedAttr();
278 const bool forceOn = aForce.WasPassed() && aForce.Value();
279 const bool forceOff = aForce.WasPassed() && !aForce.Value();
281 bool isPresent = attr && attr->Contains(aToken);
282 nsAutoTArray<nsString, 1> tokens;
283 (*tokens.AppendElement()).Rebind(aToken.Data(), aToken.Length());
285 if (isPresent) {
286 if (!forceOn) {
287 RemoveInternal(attr, tokens);
288 isPresent = false;
290 } else {
291 if (!forceOff) {
292 AddInternal(attr, tokens);
293 isPresent = true;
297 return isPresent;
300 void
301 nsDOMTokenList::Stringify(nsAString& aResult)
303 if (!mElement) {
304 aResult.Truncate();
305 return;
308 mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult);
311 JSObject*
312 nsDOMTokenList::WrapObject(JSContext *cx)
314 return DOMTokenListBinding::Wrap(cx, this);