/src/mozilla-central/dom/html/nsDOMStringMap.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "nsDOMStringMap.h" |
8 | | |
9 | | #include "jsapi.h" |
10 | | #include "nsError.h" |
11 | | #include "nsGenericHTMLElement.h" |
12 | | #include "nsContentUtils.h" |
13 | | #include "mozilla/dom/DOMStringMapBinding.h" |
14 | | #include "mozilla/dom/MutationEventBinding.h" |
15 | | |
16 | | using namespace mozilla; |
17 | | using namespace mozilla::dom; |
18 | | |
19 | | NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStringMap) |
20 | | |
21 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStringMap) |
22 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement) |
23 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
24 | | |
25 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStringMap) |
26 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
27 | 0 | // Check that mElement exists in case the unlink code is run more than once. |
28 | 0 | if (tmp->mElement) { |
29 | 0 | // Call back to element to null out weak reference to this object. |
30 | 0 | tmp->mElement->ClearDataset(); |
31 | 0 | tmp->mElement->RemoveMutationObserver(tmp); |
32 | 0 | tmp->mElement = nullptr; |
33 | 0 | } |
34 | 0 | tmp->mExpandoAndGeneration.OwnerUnlinked(); |
35 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
36 | | |
37 | | NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMStringMap) |
38 | | |
39 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStringMap) |
40 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
41 | 0 | NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) |
42 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
43 | 0 | NS_INTERFACE_MAP_END |
44 | | |
45 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStringMap) |
46 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStringMap) |
47 | | |
48 | | nsDOMStringMap::nsDOMStringMap(Element* aElement) |
49 | | : mElement(aElement), |
50 | | mRemovingProp(false) |
51 | 0 | { |
52 | 0 | mElement->AddMutationObserver(this); |
53 | 0 | } |
54 | | |
55 | | nsDOMStringMap::~nsDOMStringMap() |
56 | 0 | { |
57 | 0 | // Check if element still exists, may have been unlinked by cycle collector. |
58 | 0 | if (mElement) { |
59 | 0 | // Call back to element to null out weak reference to this object. |
60 | 0 | mElement->ClearDataset(); |
61 | 0 | mElement->RemoveMutationObserver(this); |
62 | 0 | } |
63 | 0 | } |
64 | | |
65 | | DocGroup* |
66 | | nsDOMStringMap::GetDocGroup() const |
67 | 0 | { |
68 | 0 | return mElement ? mElement->GetDocGroup() : nullptr; |
69 | 0 | } |
70 | | |
71 | | /* virtual */ |
72 | | JSObject* |
73 | | nsDOMStringMap::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) |
74 | 0 | { |
75 | 0 | return DOMStringMap_Binding::Wrap(cx, this, aGivenProto); |
76 | 0 | } |
77 | | |
78 | | void |
79 | | nsDOMStringMap::NamedGetter(const nsAString& aProp, bool& found, |
80 | | DOMString& aResult) const |
81 | 0 | { |
82 | 0 | nsAutoString attr; |
83 | 0 |
|
84 | 0 | if (!DataPropToAttr(aProp, attr)) { |
85 | 0 | found = false; |
86 | 0 | return; |
87 | 0 | } |
88 | 0 | |
89 | 0 | found = mElement->GetAttr(attr, aResult); |
90 | 0 | } |
91 | | |
92 | | void |
93 | | nsDOMStringMap::NamedSetter(const nsAString& aProp, |
94 | | const nsAString& aValue, |
95 | | ErrorResult& rv) |
96 | 0 | { |
97 | 0 | nsAutoString attr; |
98 | 0 | if (!DataPropToAttr(aProp, attr)) { |
99 | 0 | rv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
100 | 0 | return; |
101 | 0 | } |
102 | 0 | |
103 | 0 | nsresult res = nsContentUtils::CheckQName(attr, false); |
104 | 0 | if (NS_FAILED(res)) { |
105 | 0 | rv.Throw(res); |
106 | 0 | return; |
107 | 0 | } |
108 | 0 | |
109 | 0 | RefPtr<nsAtom> attrAtom = NS_Atomize(attr); |
110 | 0 | MOZ_ASSERT(attrAtom, "Should be infallible"); |
111 | 0 |
|
112 | 0 | res = mElement->SetAttr(kNameSpaceID_None, attrAtom, aValue, true); |
113 | 0 | if (NS_FAILED(res)) { |
114 | 0 | rv.Throw(res); |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | | void |
119 | | nsDOMStringMap::NamedDeleter(const nsAString& aProp, bool& found) |
120 | 0 | { |
121 | 0 | // Currently removing property, attribute is already removed. |
122 | 0 | if (mRemovingProp) { |
123 | 0 | found = false; |
124 | 0 | return; |
125 | 0 | } |
126 | 0 | |
127 | 0 | nsAutoString attr; |
128 | 0 | if (!DataPropToAttr(aProp, attr)) { |
129 | 0 | found = false; |
130 | 0 | return; |
131 | 0 | } |
132 | 0 | |
133 | 0 | RefPtr<nsAtom> attrAtom = NS_Atomize(attr); |
134 | 0 | MOZ_ASSERT(attrAtom, "Should be infallible"); |
135 | 0 |
|
136 | 0 | found = mElement->HasAttr(kNameSpaceID_None, attrAtom); |
137 | 0 |
|
138 | 0 | if (found) { |
139 | 0 | mRemovingProp = true; |
140 | 0 | mElement->UnsetAttr(kNameSpaceID_None, attrAtom, true); |
141 | 0 | mRemovingProp = false; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | void |
146 | | nsDOMStringMap::GetSupportedNames(nsTArray<nsString>& aNames) |
147 | 0 | { |
148 | 0 | uint32_t attrCount = mElement->GetAttrCount(); |
149 | 0 |
|
150 | 0 | // Iterate through all the attributes and add property |
151 | 0 | // names corresponding to data attributes to return array. |
152 | 0 | for (uint32_t i = 0; i < attrCount; ++i) { |
153 | 0 | const nsAttrName* attrName = mElement->GetAttrNameAt(i); |
154 | 0 | // Skip the ones that are not in the null namespace |
155 | 0 | if (attrName->NamespaceID() != kNameSpaceID_None) { |
156 | 0 | continue; |
157 | 0 | } |
158 | 0 | |
159 | 0 | nsAutoString prop; |
160 | 0 | if (!AttrToDataProp(nsDependentAtomString(attrName->LocalName()), |
161 | 0 | prop)) { |
162 | 0 | continue; |
163 | 0 | } |
164 | 0 | |
165 | 0 | aNames.AppendElement(prop); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | /** |
170 | | * Converts a dataset property name to the corresponding data attribute name. |
171 | | * (ex. aBigFish to data-a-big-fish). |
172 | | */ |
173 | | bool nsDOMStringMap::DataPropToAttr(const nsAString& aProp, |
174 | | nsAutoString& aResult) |
175 | 0 | { |
176 | 0 | // aResult is an autostring, so don't worry about setting its capacity: |
177 | 0 | // SetCapacity is slow even when it's a no-op and we already have enough |
178 | 0 | // storage there for most cases, probably. |
179 | 0 | aResult.AppendLiteral("data-"); |
180 | 0 |
|
181 | 0 | // Iterate property by character to form attribute name. |
182 | 0 | // Return syntax error if there is a sequence of "-" followed by a character |
183 | 0 | // in the range "a" to "z". |
184 | 0 | // Replace capital characters with "-" followed by lower case character. |
185 | 0 | // Otherwise, simply append character to attribute name. |
186 | 0 | const char16_t* start = aProp.BeginReading(); |
187 | 0 | const char16_t* end = aProp.EndReading(); |
188 | 0 | const char16_t* cur = start; |
189 | 0 | for (; cur < end; ++cur) { |
190 | 0 | const char16_t* next = cur + 1; |
191 | 0 | if (char16_t('-') == *cur && next < end && |
192 | 0 | char16_t('a') <= *next && *next <= char16_t('z')) { |
193 | 0 | // Syntax error if character following "-" is in range "a" to "z". |
194 | 0 | return false; |
195 | 0 | } |
196 | 0 | |
197 | 0 | if (char16_t('A') <= *cur && *cur <= char16_t('Z')) { |
198 | 0 | // Append the characters in the range [start, cur) |
199 | 0 | aResult.Append(start, cur - start); |
200 | 0 | // Uncamel-case characters in the range of "A" to "Z". |
201 | 0 | aResult.Append(char16_t('-')); |
202 | 0 | aResult.Append(*cur - 'A' + 'a'); |
203 | 0 | start = next; // We've already appended the thing at *cur |
204 | 0 | } |
205 | 0 | } |
206 | 0 |
|
207 | 0 | aResult.Append(start, cur - start); |
208 | 0 |
|
209 | 0 | return true; |
210 | 0 | } |
211 | | |
212 | | /** |
213 | | * Converts a data attribute name to the corresponding dataset property name. |
214 | | * (ex. data-a-big-fish to aBigFish). |
215 | | */ |
216 | | bool nsDOMStringMap::AttrToDataProp(const nsAString& aAttr, |
217 | | nsAutoString& aResult) |
218 | 0 | { |
219 | 0 | // If the attribute name does not begin with "data-" then it can not be |
220 | 0 | // a data attribute. |
221 | 0 | if (!StringBeginsWith(aAttr, NS_LITERAL_STRING("data-"))) { |
222 | 0 | return false; |
223 | 0 | } |
224 | 0 | |
225 | 0 | // Start reading attribute from first character after "data-". |
226 | 0 | const char16_t* cur = aAttr.BeginReading() + 5; |
227 | 0 | const char16_t* end = aAttr.EndReading(); |
228 | 0 |
|
229 | 0 | // Don't try to mess with aResult's capacity: the probably-no-op SetCapacity() |
230 | 0 | // call is not that fast. |
231 | 0 |
|
232 | 0 | // Iterate through attrName by character to form property name. |
233 | 0 | // If there is a sequence of "-" followed by a character in the range "a" to |
234 | 0 | // "z" then replace with upper case letter. |
235 | 0 | // Otherwise append character to property name. |
236 | 0 | for (; cur < end; ++cur) { |
237 | 0 | const char16_t* next = cur + 1; |
238 | 0 | if (char16_t('-') == *cur && next < end && |
239 | 0 | char16_t('a') <= *next && *next <= char16_t('z')) { |
240 | 0 | // Upper case the lower case letters that follow a "-". |
241 | 0 | aResult.Append(*next - 'a' + 'A'); |
242 | 0 | // Consume character to account for "-" character. |
243 | 0 | ++cur; |
244 | 0 | } else { |
245 | 0 | // Simply append character if camel case is not necessary. |
246 | 0 | aResult.Append(*cur); |
247 | 0 | } |
248 | 0 | } |
249 | 0 |
|
250 | 0 | return true; |
251 | 0 | } |
252 | | |
253 | | void |
254 | | nsDOMStringMap::AttributeChanged(Element* aElement, |
255 | | int32_t aNameSpaceID, |
256 | | nsAtom* aAttribute, |
257 | | int32_t aModType, |
258 | | const nsAttrValue* aOldValue) |
259 | 0 | { |
260 | 0 | if ((aModType == MutationEvent_Binding::ADDITION || |
261 | 0 | aModType == MutationEvent_Binding::REMOVAL) && |
262 | 0 | aNameSpaceID == kNameSpaceID_None && |
263 | 0 | StringBeginsWith(nsDependentAtomString(aAttribute), |
264 | 0 | NS_LITERAL_STRING("data-"))) { |
265 | 0 | ++mExpandoAndGeneration.generation; |
266 | 0 | } |
267 | 0 | } |