Bug 1880216 - Migrate Fenix docs into Sphinx. r=owlish,geckoview-reviewers,android...
[gecko.git] / dom / html / nsDOMStringMap.cpp
blobf8975cc02ea1bafb74c0a3fc601631ee50503e76
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 #include "nsDOMStringMap.h"
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"
16 using namespace mozilla;
17 using namespace mozilla::dom;
19 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsDOMStringMap)
21 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStringMap)
22 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
23 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
25 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStringMap)
26 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
27 // Check that mElement exists in case the unlink code is run more than once.
28 if (tmp->mElement) {
29 // Call back to element to null out weak reference to this object.
30 tmp->mElement->ClearDataset();
31 tmp->mElement->RemoveMutationObserver(tmp);
32 tmp->mElement = nullptr;
34 tmp->mExpandoAndGeneration.OwnerUnlinked();
35 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
37 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStringMap)
38 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
39 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
40 NS_INTERFACE_MAP_ENTRY(nsISupports)
41 NS_INTERFACE_MAP_END
43 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStringMap)
44 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStringMap)
46 nsDOMStringMap::nsDOMStringMap(Element* aElement)
47 : mElement(aElement), mRemovingProp(false) {
48 mElement->AddMutationObserver(this);
51 nsDOMStringMap::~nsDOMStringMap() {
52 // Check if element still exists, may have been unlinked by cycle collector.
53 if (mElement) {
54 // Call back to element to null out weak reference to this object.
55 mElement->ClearDataset();
56 mElement->RemoveMutationObserver(this);
60 DocGroup* nsDOMStringMap::GetDocGroup() const {
61 return mElement ? mElement->GetDocGroup() : nullptr;
64 /* virtual */
65 JSObject* nsDOMStringMap::WrapObject(JSContext* cx,
66 JS::Handle<JSObject*> aGivenProto) {
67 return DOMStringMap_Binding::Wrap(cx, this, aGivenProto);
70 void nsDOMStringMap::NamedGetter(const nsAString& aProp, bool& found,
71 DOMString& aResult) const {
72 nsAutoString attr;
74 if (!DataPropToAttr(aProp, attr)) {
75 found = false;
76 return;
79 found = mElement->GetAttr(attr, aResult);
82 void nsDOMStringMap::NamedSetter(const nsAString& aProp,
83 const nsAString& aValue, ErrorResult& rv) {
84 nsAutoString attr;
85 if (!DataPropToAttr(aProp, attr)) {
86 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
87 return;
90 nsresult res = nsContentUtils::CheckQName(attr, false);
91 if (NS_FAILED(res)) {
92 rv.Throw(res);
93 return;
96 RefPtr<nsAtom> attrAtom = NS_Atomize(attr);
97 MOZ_ASSERT(attrAtom, "Should be infallible");
99 res = mElement->SetAttr(kNameSpaceID_None, attrAtom, aValue, true);
100 if (NS_FAILED(res)) {
101 rv.Throw(res);
105 void nsDOMStringMap::NamedDeleter(const nsAString& aProp, bool& found) {
106 // Currently removing property, attribute is already removed.
107 if (mRemovingProp) {
108 found = false;
109 return;
112 nsAutoString attr;
113 if (!DataPropToAttr(aProp, attr)) {
114 found = false;
115 return;
118 RefPtr<nsAtom> attrAtom = NS_Atomize(attr);
119 MOZ_ASSERT(attrAtom, "Should be infallible");
121 found = mElement->HasAttr(attrAtom);
123 if (found) {
124 mRemovingProp = true;
125 mElement->UnsetAttr(kNameSpaceID_None, attrAtom, true);
126 mRemovingProp = false;
130 void nsDOMStringMap::GetSupportedNames(nsTArray<nsString>& aNames) {
131 uint32_t attrCount = mElement->GetAttrCount();
133 // Iterate through all the attributes and add property
134 // names corresponding to data attributes to return array.
135 for (uint32_t i = 0; i < attrCount; ++i) {
136 const nsAttrName* attrName = mElement->GetAttrNameAt(i);
137 // Skip the ones that are not in the null namespace
138 if (attrName->NamespaceID() != kNameSpaceID_None) {
139 continue;
142 nsAutoString prop;
143 if (!AttrToDataProp(nsDependentAtomString(attrName->LocalName()), prop)) {
144 continue;
147 aNames.AppendElement(prop);
152 * Converts a dataset property name to the corresponding data attribute name.
153 * (ex. aBigFish to data-a-big-fish).
155 bool nsDOMStringMap::DataPropToAttr(const nsAString& aProp,
156 nsAutoString& aResult) {
157 // aResult is an autostring, so don't worry about setting its capacity:
158 // SetCapacity is slow even when it's a no-op and we already have enough
159 // storage there for most cases, probably.
160 aResult.AppendLiteral("data-");
162 // Iterate property by character to form attribute name.
163 // Return syntax error if there is a sequence of "-" followed by a character
164 // in the range "a" to "z".
165 // Replace capital characters with "-" followed by lower case character.
166 // Otherwise, simply append character to attribute name.
167 const char16_t* start = aProp.BeginReading();
168 const char16_t* end = aProp.EndReading();
169 const char16_t* cur = start;
170 for (; cur < end; ++cur) {
171 const char16_t* next = cur + 1;
172 if (char16_t('-') == *cur && next < end && char16_t('a') <= *next &&
173 *next <= char16_t('z')) {
174 // Syntax error if character following "-" is in range "a" to "z".
175 return false;
178 if (char16_t('A') <= *cur && *cur <= char16_t('Z')) {
179 // Append the characters in the range [start, cur)
180 aResult.Append(start, cur - start);
181 // Uncamel-case characters in the range of "A" to "Z".
182 aResult.Append(char16_t('-'));
183 aResult.Append(*cur - 'A' + 'a');
184 start = next; // We've already appended the thing at *cur
188 aResult.Append(start, cur - start);
190 return true;
194 * Converts a data attribute name to the corresponding dataset property name.
195 * (ex. data-a-big-fish to aBigFish).
197 bool nsDOMStringMap::AttrToDataProp(const nsAString& aAttr,
198 nsAutoString& aResult) {
199 // If the attribute name does not begin with "data-" then it can not be
200 // a data attribute.
201 if (!StringBeginsWith(aAttr, u"data-"_ns)) {
202 return false;
205 // Start reading attribute from first character after "data-".
206 const char16_t* cur = aAttr.BeginReading() + 5;
207 const char16_t* end = aAttr.EndReading();
209 // Don't try to mess with aResult's capacity: the probably-no-op SetCapacity()
210 // call is not that fast.
212 // Iterate through attrName by character to form property name.
213 // If there is a sequence of "-" followed by a character in the range "a" to
214 // "z" then replace with upper case letter.
215 // Otherwise append character to property name.
216 for (; cur < end; ++cur) {
217 const char16_t* next = cur + 1;
218 if (char16_t('-') == *cur && next < end && char16_t('a') <= *next &&
219 *next <= char16_t('z')) {
220 // Upper case the lower case letters that follow a "-".
221 aResult.Append(*next - 'a' + 'A');
222 // Consume character to account for "-" character.
223 ++cur;
224 } else {
225 // Simply append character if camel case is not necessary.
226 aResult.Append(*cur);
230 return true;
233 void nsDOMStringMap::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
234 nsAtom* aAttribute, int32_t aModType,
235 const nsAttrValue* aOldValue) {
236 if ((aModType == MutationEvent_Binding::ADDITION ||
237 aModType == MutationEvent_Binding::REMOVAL) &&
238 aNameSpaceID == kNameSpaceID_None &&
239 StringBeginsWith(nsDependentAtomString(aAttribute), u"data-"_ns)) {
240 ++mExpandoAndGeneration.generation;