Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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)
24 {
25 // We don't add a reference to our element. If it goes away,
26 // we'll be told to drop our reference
27 SetIsDOMBinding();
28 }
30 nsDOMTokenList::~nsDOMTokenList() { }
32 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMTokenList, mElement)
34 NS_INTERFACE_MAP_BEGIN(nsDOMTokenList)
35 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
36 NS_INTERFACE_MAP_ENTRY(nsISupports)
37 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMTokenList)
38 NS_INTERFACE_MAP_END
40 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTokenList)
41 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTokenList)
43 const nsAttrValue*
44 nsDOMTokenList::GetParsedAttr()
45 {
46 if (!mElement) {
47 return nullptr;
48 }
49 return mElement->GetAttrInfo(kNameSpaceID_None, mAttrAtom).mValue;
50 }
52 uint32_t
53 nsDOMTokenList::Length()
54 {
55 const nsAttrValue* attr = GetParsedAttr();
56 if (!attr) {
57 return 0;
58 }
60 return attr->GetAtomCount();
61 }
63 void
64 nsDOMTokenList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aResult)
65 {
66 const nsAttrValue* attr = GetParsedAttr();
68 if (attr && aIndex < static_cast<uint32_t>(attr->GetAtomCount())) {
69 aFound = true;
70 attr->AtomAt(aIndex)->ToString(aResult);
71 } else {
72 aFound = false;
73 }
74 }
76 nsresult
77 nsDOMTokenList::CheckToken(const nsAString& aStr)
78 {
79 if (aStr.IsEmpty()) {
80 return NS_ERROR_DOM_SYNTAX_ERR;
81 }
83 nsAString::const_iterator iter, end;
84 aStr.BeginReading(iter);
85 aStr.EndReading(end);
87 while (iter != end) {
88 if (nsContentUtils::IsHTMLWhitespace(*iter))
89 return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
90 ++iter;
91 }
93 return NS_OK;
94 }
96 nsresult
97 nsDOMTokenList::CheckTokens(const nsTArray<nsString>& aTokens)
98 {
99 for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
100 nsresult rv = CheckToken(aTokens[i]);
101 if (NS_FAILED(rv)) {
102 return rv;
103 }
104 }
106 return NS_OK;
107 }
109 bool
110 nsDOMTokenList::Contains(const nsAString& aToken, ErrorResult& aError)
111 {
112 aError = CheckToken(aToken);
113 if (aError.Failed()) {
114 return false;
115 }
117 const nsAttrValue* attr = GetParsedAttr();
118 return attr && attr->Contains(aToken);
119 }
121 void
122 nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
123 const nsTArray<nsString>& aTokens)
124 {
125 if (!mElement) {
126 return;
127 }
129 nsAutoString resultStr;
131 if (aAttr) {
132 aAttr->ToString(resultStr);
133 }
135 bool oneWasAdded = false;
136 nsAutoTArray<nsString, 10> addedClasses;
138 for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
139 const nsString& aToken = aTokens[i];
141 if ((aAttr && aAttr->Contains(aToken)) ||
142 addedClasses.Contains(aToken)) {
143 continue;
144 }
146 if (oneWasAdded ||
147 (!resultStr.IsEmpty() &&
148 !nsContentUtils::IsHTMLWhitespace(resultStr.Last()))) {
149 resultStr.Append(NS_LITERAL_STRING(" ") + aToken);
150 } else {
151 resultStr.Append(aToken);
152 }
154 oneWasAdded = true;
155 addedClasses.AppendElement(aToken);
156 }
158 mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
159 }
161 void
162 nsDOMTokenList::Add(const nsTArray<nsString>& aTokens, ErrorResult& aError)
163 {
164 aError = CheckTokens(aTokens);
165 if (aError.Failed()) {
166 return;
167 }
169 const nsAttrValue* attr = GetParsedAttr();
170 AddInternal(attr, aTokens);
171 }
173 void
174 nsDOMTokenList::Add(const nsAString& aToken, mozilla::ErrorResult& aError)
175 {
176 nsAutoTArray<nsString, 1> tokens;
177 tokens.AppendElement(aToken);
178 Add(tokens, aError);
179 }
181 void
182 nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
183 const nsTArray<nsString>& aTokens)
184 {
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;
202 }
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;
211 }
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;
223 }
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(' '));
233 }
234 lastTokenRemoved = false;
235 output.Append(Substring(copyStart, iter));
236 copyStart = iter;
237 }
238 }
240 mElement->SetAttr(kNameSpaceID_None, mAttrAtom, output, true);
241 }
243 void
244 nsDOMTokenList::Remove(const nsTArray<nsString>& aTokens, ErrorResult& aError)
245 {
246 aError = CheckTokens(aTokens);
247 if (aError.Failed()) {
248 return;
249 }
251 const nsAttrValue* attr = GetParsedAttr();
252 if (!attr) {
253 return;
254 }
256 RemoveInternal(attr, aTokens);
257 }
259 void
260 nsDOMTokenList::Remove(const nsAString& aToken, mozilla::ErrorResult& aError)
261 {
262 nsAutoTArray<nsString, 1> tokens;
263 tokens.AppendElement(aToken);
264 Remove(tokens, aError);
265 }
267 bool
268 nsDOMTokenList::Toggle(const nsAString& aToken,
269 const Optional<bool>& aForce,
270 ErrorResult& aError)
271 {
272 aError = CheckToken(aToken);
273 if (aError.Failed()) {
274 return false;
275 }
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;
289 }
290 } else {
291 if (!forceOff) {
292 AddInternal(attr, tokens);
293 isPresent = true;
294 }
295 }
297 return isPresent;
298 }
300 void
301 nsDOMTokenList::Stringify(nsAString& aResult)
302 {
303 if (!mElement) {
304 aResult.Truncate();
305 return;
306 }
308 mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult);
309 }
311 JSObject*
312 nsDOMTokenList::WrapObject(JSContext *cx)
313 {
314 return DOMTokenListBinding::Wrap(cx, this);
315 }