/src/mozilla-central/dom/base/nsMappedAttributes.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 | | /* |
8 | | * A unique per-element set of attributes that is used as an |
9 | | * nsIStyleRule; used to implement presentational attributes. |
10 | | */ |
11 | | |
12 | | #include "nsMappedAttributes.h" |
13 | | #include "nsHTMLStyleSheet.h" |
14 | | #include "mozilla/DeclarationBlock.h" |
15 | | #include "mozilla/HashFunctions.h" |
16 | | #include "mozilla/MappedDeclarations.h" |
17 | | #include "mozilla/MemoryReporting.h" |
18 | | |
19 | | using namespace mozilla; |
20 | | |
21 | | bool |
22 | | nsMappedAttributes::sShuttingDown = false; |
23 | | nsTArray<void*>* |
24 | | nsMappedAttributes::sCachedMappedAttributeAllocations = nullptr; |
25 | | |
26 | | void |
27 | | nsMappedAttributes::Shutdown() |
28 | 0 | { |
29 | 0 | sShuttingDown = true; |
30 | 0 | if (sCachedMappedAttributeAllocations) { |
31 | 0 | for (uint32_t i = 0; i < sCachedMappedAttributeAllocations->Length(); ++i) { |
32 | 0 | void* cachedValue = (*sCachedMappedAttributeAllocations)[i]; |
33 | 0 | ::operator delete(cachedValue); |
34 | 0 | } |
35 | 0 | } |
36 | 0 |
|
37 | 0 | delete sCachedMappedAttributeAllocations; |
38 | 0 | sCachedMappedAttributeAllocations = nullptr; |
39 | 0 | } |
40 | | |
41 | | nsMappedAttributes::nsMappedAttributes(nsHTMLStyleSheet* aSheet, |
42 | | nsMapRuleToAttributesFunc aMapRuleFunc) |
43 | | : mAttrCount(0), |
44 | | mSheet(aSheet), |
45 | | mRuleMapper(aMapRuleFunc), |
46 | | mServoStyle(nullptr) |
47 | 0 | { |
48 | 0 | MOZ_ASSERT(mRefCnt == 0); // Ensure caching works as expected. |
49 | 0 | } |
50 | | |
51 | | nsMappedAttributes::nsMappedAttributes(const nsMappedAttributes& aCopy) |
52 | | : mAttrCount(aCopy.mAttrCount), |
53 | | mSheet(aCopy.mSheet), |
54 | | mRuleMapper(aCopy.mRuleMapper), |
55 | | // This is only called by ::Clone, which is used to create independent |
56 | | // nsMappedAttributes objects which should not share a DeclarationBlock |
57 | | mServoStyle(nullptr) |
58 | 0 | { |
59 | 0 | NS_ASSERTION(mBufferSize >= aCopy.mAttrCount, "can't fit attributes"); |
60 | 0 | MOZ_ASSERT(mRefCnt == 0); // Ensure caching works as expected. |
61 | 0 |
|
62 | 0 | uint32_t i; |
63 | 0 | for (i = 0; i < mAttrCount; ++i) { |
64 | 0 | new (&Attrs()[i]) InternalAttr(aCopy.Attrs()[i]); |
65 | 0 | } |
66 | 0 | } |
67 | | |
68 | | nsMappedAttributes::~nsMappedAttributes() |
69 | 0 | { |
70 | 0 | if (mSheet) { |
71 | 0 | mSheet->DropMappedAttributes(this); |
72 | 0 | } |
73 | 0 |
|
74 | 0 | uint32_t i; |
75 | 0 | for (i = 0; i < mAttrCount; ++i) { |
76 | 0 | Attrs()[i].~InternalAttr(); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | |
81 | | nsMappedAttributes* |
82 | | nsMappedAttributes::Clone(bool aWillAddAttr) |
83 | 0 | { |
84 | 0 | uint32_t extra = aWillAddAttr ? 1 : 0; |
85 | 0 |
|
86 | 0 | // This will call the overridden operator new |
87 | 0 | return new (mAttrCount + extra) nsMappedAttributes(*this); |
88 | 0 | } |
89 | | |
90 | | void* nsMappedAttributes::operator new(size_t aSize, uint32_t aAttrCount) CPP_THROW_NEW |
91 | 0 | { |
92 | 0 |
|
93 | 0 | size_t size = aSize + aAttrCount * sizeof(InternalAttr); |
94 | 0 |
|
95 | 0 | // aSize will include the mAttrs buffer so subtract that. |
96 | 0 | // We don't want to under-allocate, however, so do not subtract |
97 | 0 | // if we have zero attributes. The zero attribute case only happens |
98 | 0 | // for <body>'s mapped attributes |
99 | 0 | if (aAttrCount != 0) { |
100 | 0 | size -= sizeof(void*[1]); |
101 | 0 | } |
102 | 0 |
|
103 | 0 | if (sCachedMappedAttributeAllocations) { |
104 | 0 | void* cached = |
105 | 0 | sCachedMappedAttributeAllocations->SafeElementAt(aAttrCount); |
106 | 0 | if (cached) { |
107 | 0 | (*sCachedMappedAttributeAllocations)[aAttrCount] = nullptr; |
108 | 0 | return cached; |
109 | 0 | } |
110 | 0 | } |
111 | 0 | |
112 | 0 | void* newAttrs = ::operator new(size); |
113 | 0 |
|
114 | | #ifdef DEBUG |
115 | | static_cast<nsMappedAttributes*>(newAttrs)->mBufferSize = aAttrCount; |
116 | | #endif |
117 | | return newAttrs; |
118 | 0 | } |
119 | | |
120 | | void |
121 | | nsMappedAttributes::LastRelease() |
122 | 0 | { |
123 | 0 | if (!sShuttingDown) { |
124 | 0 | if (!sCachedMappedAttributeAllocations) { |
125 | 0 | sCachedMappedAttributeAllocations = new nsTArray<void*>(); |
126 | 0 | } |
127 | 0 |
|
128 | 0 | // Ensure the cache array is at least mAttrCount + 1 long and |
129 | 0 | // that each item is either null or pointing to a cached item. |
130 | 0 | // The size of the array is capped because mapped attributes are defined |
131 | 0 | // statically in element implementations. |
132 | 0 | sCachedMappedAttributeAllocations->SetCapacity(mAttrCount + 1); |
133 | 0 | for (uint32_t i = sCachedMappedAttributeAllocations->Length(); |
134 | 0 | i < (uint32_t(mAttrCount) + 1); ++i) { |
135 | 0 | sCachedMappedAttributeAllocations->AppendElement(nullptr); |
136 | 0 | } |
137 | 0 |
|
138 | 0 | if (!(*sCachedMappedAttributeAllocations)[mAttrCount]) { |
139 | 0 | void* memoryToCache = this; |
140 | 0 | this->~nsMappedAttributes(); |
141 | 0 | (*sCachedMappedAttributeAllocations)[mAttrCount] = memoryToCache; |
142 | 0 | return; |
143 | 0 | } |
144 | 0 | } |
145 | 0 | |
146 | 0 | delete this; |
147 | 0 | } |
148 | | |
149 | | |
150 | | void |
151 | | nsMappedAttributes::SetAndSwapAttr(nsAtom* aAttrName, nsAttrValue& aValue, |
152 | | bool* aValueWasSet) |
153 | 0 | { |
154 | 0 | MOZ_ASSERT(aAttrName, "null name"); |
155 | 0 | *aValueWasSet = false; |
156 | 0 | uint32_t i; |
157 | 0 | for (i = 0; i < mAttrCount && !Attrs()[i].mName.IsSmaller(aAttrName); ++i) { |
158 | 0 | if (Attrs()[i].mName.Equals(aAttrName)) { |
159 | 0 | Attrs()[i].mValue.SwapValueWith(aValue); |
160 | 0 | *aValueWasSet = true; |
161 | 0 | return; |
162 | 0 | } |
163 | 0 | } |
164 | 0 |
|
165 | 0 | NS_ASSERTION(mBufferSize >= mAttrCount + 1, "can't fit attributes"); |
166 | 0 |
|
167 | 0 | if (mAttrCount != i) { |
168 | 0 | memmove(&Attrs()[i + 1], &Attrs()[i], (mAttrCount - i) * sizeof(InternalAttr)); |
169 | 0 | } |
170 | 0 |
|
171 | 0 | new (&Attrs()[i].mName) nsAttrName(aAttrName); |
172 | 0 | new (&Attrs()[i].mValue) nsAttrValue(); |
173 | 0 | Attrs()[i].mValue.SwapValueWith(aValue); |
174 | 0 | ++mAttrCount; |
175 | 0 | } |
176 | | |
177 | | const nsAttrValue* |
178 | | nsMappedAttributes::GetAttr(nsAtom* aAttrName) const |
179 | 0 | { |
180 | 0 | MOZ_ASSERT(aAttrName, "null name"); |
181 | 0 |
|
182 | 0 | for (uint32_t i = 0; i < mAttrCount; ++i) { |
183 | 0 | if (Attrs()[i].mName.Equals(aAttrName)) { |
184 | 0 | return &Attrs()[i].mValue; |
185 | 0 | } |
186 | 0 | } |
187 | 0 |
|
188 | 0 | return nullptr; |
189 | 0 | } |
190 | | |
191 | | const nsAttrValue* |
192 | | nsMappedAttributes::GetAttr(const nsAString& aAttrName) const |
193 | 0 | { |
194 | 0 | for (uint32_t i = 0; i < mAttrCount; ++i) { |
195 | 0 | if (Attrs()[i].mName.Atom()->Equals(aAttrName)) { |
196 | 0 | return &Attrs()[i].mValue; |
197 | 0 | } |
198 | 0 | } |
199 | 0 |
|
200 | 0 | return nullptr; |
201 | 0 | } |
202 | | |
203 | | bool |
204 | | nsMappedAttributes::Equals(const nsMappedAttributes* aOther) const |
205 | 0 | { |
206 | 0 | if (this == aOther) { |
207 | 0 | return true; |
208 | 0 | } |
209 | 0 | |
210 | 0 | if (mRuleMapper != aOther->mRuleMapper || mAttrCount != aOther->mAttrCount) { |
211 | 0 | return false; |
212 | 0 | } |
213 | 0 | |
214 | 0 | uint32_t i; |
215 | 0 | for (i = 0; i < mAttrCount; ++i) { |
216 | 0 | if (!Attrs()[i].mName.Equals(aOther->Attrs()[i].mName) || |
217 | 0 | !Attrs()[i].mValue.Equals(aOther->Attrs()[i].mValue)) { |
218 | 0 | return false; |
219 | 0 | } |
220 | 0 | } |
221 | 0 |
|
222 | 0 | return true; |
223 | 0 | } |
224 | | |
225 | | PLDHashNumber |
226 | | nsMappedAttributes::HashValue() const |
227 | 0 | { |
228 | 0 | PLDHashNumber hash = HashGeneric(mRuleMapper); |
229 | 0 |
|
230 | 0 | uint32_t i; |
231 | 0 | for (i = 0; i < mAttrCount; ++i) { |
232 | 0 | hash = AddToHash(hash, |
233 | 0 | Attrs()[i].mName.HashValue(), |
234 | 0 | Attrs()[i].mValue.HashValue()); |
235 | 0 | } |
236 | 0 |
|
237 | 0 | return hash; |
238 | 0 | } |
239 | | |
240 | | void |
241 | | nsMappedAttributes::SetStyleSheet(nsHTMLStyleSheet* aSheet) |
242 | 0 | { |
243 | 0 | if (mSheet) { |
244 | 0 | mSheet->DropMappedAttributes(this); |
245 | 0 | } |
246 | 0 | mSheet = aSheet; // not ref counted |
247 | 0 | } |
248 | | |
249 | | |
250 | | void |
251 | | nsMappedAttributes::RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue) |
252 | 0 | { |
253 | 0 | Attrs()[aPos].mValue.SwapValueWith(aValue); |
254 | 0 | Attrs()[aPos].~InternalAttr(); |
255 | 0 | memmove(&Attrs()[aPos], &Attrs()[aPos + 1], |
256 | 0 | (mAttrCount - aPos - 1) * sizeof(InternalAttr)); |
257 | 0 | mAttrCount--; |
258 | 0 | } |
259 | | |
260 | | const nsAttrName* |
261 | | nsMappedAttributes::GetExistingAttrNameFromQName(const nsAString& aName) const |
262 | 0 | { |
263 | 0 | uint32_t i; |
264 | 0 | for (i = 0; i < mAttrCount; ++i) { |
265 | 0 | if (Attrs()[i].mName.IsAtom()) { |
266 | 0 | if (Attrs()[i].mName.Atom()->Equals(aName)) { |
267 | 0 | return &Attrs()[i].mName; |
268 | 0 | } |
269 | 0 | } |
270 | 0 | else { |
271 | 0 | if (Attrs()[i].mName.NodeInfo()->QualifiedNameEquals(aName)) { |
272 | 0 | return &Attrs()[i].mName; |
273 | 0 | } |
274 | 0 | } |
275 | 0 | } |
276 | 0 |
|
277 | 0 | return nullptr; |
278 | 0 | } |
279 | | |
280 | | int32_t |
281 | | nsMappedAttributes::IndexOfAttr(nsAtom* aLocalName) const |
282 | 0 | { |
283 | 0 | uint32_t i; |
284 | 0 | for (i = 0; i < mAttrCount; ++i) { |
285 | 0 | if (Attrs()[i].mName.Equals(aLocalName)) { |
286 | 0 | return i; |
287 | 0 | } |
288 | 0 | } |
289 | 0 |
|
290 | 0 | return -1; |
291 | 0 | } |
292 | | |
293 | | size_t |
294 | | nsMappedAttributes::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
295 | 0 | { |
296 | 0 | NS_ASSERTION(mAttrCount == mBufferSize, |
297 | 0 | "mBufferSize and mAttrCount are expected to be the same."); |
298 | 0 |
|
299 | 0 | size_t n = aMallocSizeOf(this); |
300 | 0 | for (uint16_t i = 0; i < mAttrCount; ++i) { |
301 | 0 | n += Attrs()[i].mValue.SizeOfExcludingThis(aMallocSizeOf); |
302 | 0 | } |
303 | 0 | return n; |
304 | 0 | } |
305 | | |
306 | | void |
307 | | nsMappedAttributes::LazilyResolveServoDeclaration(nsIDocument* aDoc) |
308 | 0 | { |
309 | 0 |
|
310 | 0 | MOZ_ASSERT(!mServoStyle, |
311 | 0 | "LazilyResolveServoDeclaration should not be called if mServoStyle is already set"); |
312 | 0 | if (mRuleMapper) { |
313 | 0 | MappedDeclarations declarations( |
314 | 0 | aDoc, Servo_DeclarationBlock_CreateEmpty().Consume()); |
315 | 0 | (*mRuleMapper)(this, declarations); |
316 | 0 | mServoStyle = declarations.TakeDeclarationBlock(); |
317 | 0 | } |
318 | 0 | } |