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/. */
6 * Implementation of DOMTokenList specified by HTML5.
9 #include "nsDOMTokenList.h"
11 #include "nsAttrValue.h"
12 #include "nsContentUtils.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
)
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
)
39 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTokenList
)
40 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTokenList
)
43 nsDOMTokenList::GetParsedAttr()
48 return mElement
->GetAttrInfo(kNameSpaceID_None
, mAttrAtom
).mValue
;
52 nsDOMTokenList::Length()
54 const nsAttrValue
* attr
= GetParsedAttr();
59 return attr
->GetAtomCount();
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())) {
69 attr
->AtomAt(aIndex
)->ToString(aResult
);
76 nsDOMTokenList::CheckToken(const nsAString
& aStr
)
79 return NS_ERROR_DOM_SYNTAX_ERR
;
82 nsAString::const_iterator iter
, end
;
83 aStr
.BeginReading(iter
);
87 if (nsContentUtils::IsHTMLWhitespace(*iter
))
88 return NS_ERROR_DOM_INVALID_CHARACTER_ERR
;
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
]);
109 nsDOMTokenList::Contains(const nsAString
& aToken
, ErrorResult
& aError
)
111 aError
= CheckToken(aToken
);
112 if (aError
.Failed()) {
116 const nsAttrValue
* attr
= GetParsedAttr();
117 return attr
&& attr
->Contains(aToken
);
121 nsDOMTokenList::AddInternal(const nsAttrValue
* aAttr
,
122 const nsTArray
<nsString
>& aTokens
)
128 nsAutoString resultStr
;
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
)) {
146 (!resultStr
.IsEmpty() &&
147 !nsContentUtils::IsHTMLWhitespace(resultStr
.Last()))) {
148 resultStr
.Append(' ');
149 resultStr
.Append(aToken
);
151 resultStr
.Append(aToken
);
155 addedClasses
.AppendElement(aToken
);
158 mElement
->SetAttr(kNameSpaceID_None
, mAttrAtom
, resultStr
, true);
162 nsDOMTokenList::Add(const nsTArray
<nsString
>& aTokens
, ErrorResult
& aError
)
164 aError
= CheckTokens(aTokens
);
165 if (aError
.Failed()) {
169 const nsAttrValue
* attr
= GetParsedAttr();
170 AddInternal(attr
, aTokens
);
174 nsDOMTokenList::Add(const nsAString
& aToken
, mozilla::ErrorResult
& aError
)
176 nsAutoTArray
<nsString
, 1> tokens
;
177 tokens
.AppendElement(aToken
);
182 nsDOMTokenList::RemoveInternal(const nsAttrValue
* aAttr
,
183 const nsTArray
<nsString
>& aTokens
)
185 NS_ABORT_IF_FALSE(aAttr
, "Need an attribute");
188 aAttr
->ToString(input
);
190 nsAString::const_iterator copyStart
, tokenStart
, iter
, end
;
191 input
.BeginReading(iter
);
192 input
.EndReading(end
);
196 bool lastTokenRemoved
= false;
198 while (iter
!= end
) {
200 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
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
));
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
)) {
225 lastTokenRemoved
= true;
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
));
240 mElement
->SetAttr(kNameSpaceID_None
, mAttrAtom
, output
, true);
244 nsDOMTokenList::Remove(const nsTArray
<nsString
>& aTokens
, ErrorResult
& aError
)
246 aError
= CheckTokens(aTokens
);
247 if (aError
.Failed()) {
251 const nsAttrValue
* attr
= GetParsedAttr();
256 RemoveInternal(attr
, aTokens
);
260 nsDOMTokenList::Remove(const nsAString
& aToken
, mozilla::ErrorResult
& aError
)
262 nsAutoTArray
<nsString
, 1> tokens
;
263 tokens
.AppendElement(aToken
);
264 Remove(tokens
, aError
);
268 nsDOMTokenList::Toggle(const nsAString
& aToken
,
269 const Optional
<bool>& aForce
,
272 aError
= CheckToken(aToken
);
273 if (aError
.Failed()) {
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());
287 RemoveInternal(attr
, tokens
);
292 AddInternal(attr
, tokens
);
301 nsDOMTokenList::Stringify(nsAString
& aResult
)
308 mElement
->GetAttr(kNameSpaceID_None
, mAttrAtom
, aResult
);
312 nsDOMTokenList::WrapObject(JSContext
*cx
)
314 return DOMTokenListBinding::Wrap(cx
, this);