Line data Source code
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 "nsRuleData.h"
15 : #include "nsRuleWalker.h"
16 : #include "mozilla/GenericSpecifiedValues.h"
17 : #include "mozilla/HashFunctions.h"
18 : #include "mozilla/MemoryReporting.h"
19 : #include "mozilla/ServoDeclarationBlock.h"
20 : #include "mozilla/ServoSpecifiedValues.h"
21 :
22 : using namespace mozilla;
23 :
24 : bool
25 : nsMappedAttributes::sShuttingDown = false;
26 : nsTArray<void*>*
27 : nsMappedAttributes::sCachedMappedAttributeAllocations = nullptr;
28 :
29 : void
30 0 : nsMappedAttributes::Shutdown()
31 : {
32 0 : sShuttingDown = true;
33 0 : if (sCachedMappedAttributeAllocations) {
34 0 : for (uint32_t i = 0; i < sCachedMappedAttributeAllocations->Length(); ++i) {
35 0 : void* cachedValue = (*sCachedMappedAttributeAllocations)[i];
36 : ::operator delete(cachedValue);
37 : }
38 : }
39 :
40 0 : delete sCachedMappedAttributeAllocations;
41 0 : sCachedMappedAttributeAllocations = nullptr;
42 0 : }
43 :
44 33 : nsMappedAttributes::nsMappedAttributes(nsHTMLStyleSheet* aSheet,
45 33 : nsMapRuleToAttributesFunc aMapRuleFunc)
46 : : mAttrCount(0),
47 : mSheet(aSheet),
48 : mRuleMapper(aMapRuleFunc),
49 33 : mServoStyle(nullptr)
50 : {
51 33 : MOZ_ASSERT(mRefCnt == 0); // Ensure caching works as expected.
52 33 : }
53 :
54 0 : nsMappedAttributes::nsMappedAttributes(const nsMappedAttributes& aCopy)
55 0 : : mAttrCount(aCopy.mAttrCount),
56 0 : mSheet(aCopy.mSheet),
57 0 : mRuleMapper(aCopy.mRuleMapper),
58 : // This is only called by ::Clone, which is used to create independent
59 : // nsMappedAttributes objects which should not share a ServoDeclarationBlock
60 0 : mServoStyle(nullptr)
61 : {
62 0 : NS_ASSERTION(mBufferSize >= aCopy.mAttrCount, "can't fit attributes");
63 0 : MOZ_ASSERT(mRefCnt == 0); // Ensure caching works as expected.
64 :
65 : uint32_t i;
66 0 : for (i = 0; i < mAttrCount; ++i) {
67 0 : new (&Attrs()[i]) InternalAttr(aCopy.Attrs()[i]);
68 : }
69 0 : }
70 :
71 2 : nsMappedAttributes::~nsMappedAttributes()
72 : {
73 1 : if (mSheet) {
74 0 : mSheet->DropMappedAttributes(this);
75 : }
76 :
77 : uint32_t i;
78 2 : for (i = 0; i < mAttrCount; ++i) {
79 1 : Attrs()[i].~InternalAttr();
80 : }
81 1 : }
82 :
83 :
84 : nsMappedAttributes*
85 0 : nsMappedAttributes::Clone(bool aWillAddAttr)
86 : {
87 0 : uint32_t extra = aWillAddAttr ? 1 : 0;
88 :
89 : // This will call the overridden operator new
90 0 : return new (mAttrCount + extra) nsMappedAttributes(*this);
91 : }
92 :
93 33 : void* nsMappedAttributes::operator new(size_t aSize, uint32_t aAttrCount) CPP_THROW_NEW
94 : {
95 :
96 33 : size_t size = aSize + aAttrCount * sizeof(InternalAttr);
97 :
98 : // aSize will include the mAttrs buffer so subtract that.
99 : // We don't want to under-allocate, however, so do not subtract
100 : // if we have zero attributes. The zero attribute case only happens
101 : // for <body>'s mapped attributes
102 33 : if (aAttrCount != 0) {
103 26 : size -= sizeof(void*[1]);
104 : }
105 :
106 33 : if (sCachedMappedAttributeAllocations) {
107 : void* cached =
108 26 : sCachedMappedAttributeAllocations->SafeElementAt(aAttrCount);
109 26 : if (cached) {
110 1 : (*sCachedMappedAttributeAllocations)[aAttrCount] = nullptr;
111 1 : return cached;
112 : }
113 : }
114 :
115 32 : void* newAttrs = ::operator new(size);
116 :
117 : #ifdef DEBUG
118 32 : static_cast<nsMappedAttributes*>(newAttrs)->mBufferSize = aAttrCount;
119 : #endif
120 32 : return newAttrs;
121 : }
122 :
123 : void
124 1 : nsMappedAttributes::LastRelease()
125 : {
126 1 : if (!sShuttingDown) {
127 1 : if (!sCachedMappedAttributeAllocations) {
128 1 : sCachedMappedAttributeAllocations = new nsTArray<void*>();
129 : }
130 :
131 : // Ensure the cache array is at least mAttrCount + 1 long and
132 : // that each item is either null or pointing to a cached item.
133 : // The size of the array is capped because mapped attributes are defined
134 : // statically in element implementations.
135 1 : sCachedMappedAttributeAllocations->SetCapacity(mAttrCount + 1);
136 3 : for (uint32_t i = sCachedMappedAttributeAllocations->Length();
137 3 : i < (uint32_t(mAttrCount) + 1); ++i) {
138 2 : sCachedMappedAttributeAllocations->AppendElement(nullptr);
139 : }
140 :
141 1 : if (!(*sCachedMappedAttributeAllocations)[mAttrCount]) {
142 1 : void* memoryToCache = this;
143 1 : this->~nsMappedAttributes();
144 1 : (*sCachedMappedAttributeAllocations)[mAttrCount] = memoryToCache;
145 1 : return;
146 : }
147 : }
148 :
149 0 : delete this;
150 : }
151 :
152 132 : NS_IMPL_ADDREF(nsMappedAttributes)
153 70 : NS_IMPL_RELEASE_WITH_DESTROY(nsMappedAttributes, LastRelease())
154 :
155 173 : NS_IMPL_QUERY_INTERFACE(nsMappedAttributes,
156 : nsIStyleRule)
157 :
158 : void
159 26 : nsMappedAttributes::SetAndSwapAttr(nsIAtom* aAttrName, nsAttrValue& aValue,
160 : bool* aValueWasSet)
161 : {
162 26 : NS_PRECONDITION(aAttrName, "null name");
163 26 : *aValueWasSet = false;
164 : uint32_t i;
165 26 : for (i = 0; i < mAttrCount && !Attrs()[i].mName.IsSmaller(aAttrName); ++i) {
166 0 : if (Attrs()[i].mName.Equals(aAttrName)) {
167 0 : Attrs()[i].mValue.SwapValueWith(aValue);
168 0 : *aValueWasSet = true;
169 0 : return;
170 : }
171 : }
172 :
173 26 : NS_ASSERTION(mBufferSize >= mAttrCount + 1, "can't fit attributes");
174 :
175 26 : if (mAttrCount != i) {
176 0 : memmove(&Attrs()[i + 1], &Attrs()[i], (mAttrCount - i) * sizeof(InternalAttr));
177 : }
178 :
179 26 : new (&Attrs()[i].mName) nsAttrName(aAttrName);
180 26 : new (&Attrs()[i].mValue) nsAttrValue();
181 26 : Attrs()[i].mValue.SwapValueWith(aValue);
182 26 : ++mAttrCount;
183 : }
184 :
185 : const nsAttrValue*
186 402 : nsMappedAttributes::GetAttr(nsIAtom* aAttrName) const
187 : {
188 402 : NS_PRECONDITION(aAttrName, "null name");
189 :
190 634 : for (uint32_t i = 0; i < mAttrCount; ++i) {
191 357 : if (Attrs()[i].mName.Equals(aAttrName)) {
192 125 : return &Attrs()[i].mValue;
193 : }
194 : }
195 :
196 277 : return nullptr;
197 : }
198 :
199 : const nsAttrValue*
200 0 : nsMappedAttributes::GetAttr(const nsAString& aAttrName) const
201 : {
202 0 : for (uint32_t i = 0; i < mAttrCount; ++i) {
203 0 : if (Attrs()[i].mName.Atom()->Equals(aAttrName)) {
204 0 : return &Attrs()[i].mValue;
205 : }
206 : }
207 :
208 0 : return nullptr;
209 : }
210 :
211 : bool
212 1 : nsMappedAttributes::Equals(const nsMappedAttributes* aOther) const
213 : {
214 1 : if (this == aOther) {
215 0 : return true;
216 : }
217 :
218 1 : if (mRuleMapper != aOther->mRuleMapper || mAttrCount != aOther->mAttrCount) {
219 0 : return false;
220 : }
221 :
222 : uint32_t i;
223 2 : for (i = 0; i < mAttrCount; ++i) {
224 2 : if (!Attrs()[i].mName.Equals(aOther->Attrs()[i].mName) ||
225 1 : !Attrs()[i].mValue.Equals(aOther->Attrs()[i].mValue)) {
226 0 : return false;
227 : }
228 : }
229 :
230 1 : return true;
231 : }
232 :
233 : PLDHashNumber
234 33 : nsMappedAttributes::HashValue() const
235 : {
236 33 : PLDHashNumber hash = HashGeneric(mRuleMapper);
237 :
238 : uint32_t i;
239 59 : for (i = 0; i < mAttrCount; ++i) {
240 52 : hash = AddToHash(hash,
241 26 : Attrs()[i].mName.HashValue(),
242 26 : Attrs()[i].mValue.HashValue());
243 : }
244 :
245 33 : return hash;
246 : }
247 :
248 : void
249 0 : nsMappedAttributes::SetStyleSheet(nsHTMLStyleSheet* aSheet)
250 : {
251 0 : if (mSheet) {
252 0 : mSheet->DropMappedAttributes(this);
253 : }
254 0 : mSheet = aSheet; // not ref counted
255 0 : }
256 :
257 : /* virtual */ void
258 172 : nsMappedAttributes::MapRuleInfoInto(nsRuleData* aRuleData)
259 : {
260 172 : if (mRuleMapper) {
261 172 : (*mRuleMapper)(this, aRuleData);
262 : }
263 172 : }
264 :
265 : /* virtual */ bool
266 0 : nsMappedAttributes::MightMapInheritedStyleData()
267 : {
268 : // Just assume that we do, rather than adding checks to all of the different
269 : // kinds of attribute mapping functions we have.
270 0 : return true;
271 : }
272 :
273 : /* virtual */ bool
274 0 : nsMappedAttributes::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
275 : nsCSSValue* aValue)
276 : {
277 0 : MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
278 : return false;
279 : }
280 :
281 : #ifdef DEBUG
282 : /* virtual */ void
283 0 : nsMappedAttributes::List(FILE* out, int32_t aIndent) const
284 : {
285 0 : nsAutoCString str;
286 0 : nsAutoString tmp;
287 : uint32_t i;
288 :
289 0 : for (i = 0; i < mAttrCount; ++i) {
290 : int32_t indent;
291 0 : for (indent = aIndent; indent > 0; --indent) {
292 0 : str.AppendLiteral(" ");
293 : }
294 :
295 0 : Attrs()[i].mName.GetQualifiedName(tmp);
296 0 : LossyAppendUTF16toASCII(tmp, str);
297 :
298 0 : Attrs()[i].mValue.ToString(tmp);
299 0 : LossyAppendUTF16toASCII(tmp, str);
300 0 : str.Append('\n');
301 0 : fprintf_stderr(out, "%s", str.get());
302 : }
303 0 : }
304 : #endif
305 :
306 : void
307 0 : nsMappedAttributes::RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue)
308 : {
309 0 : Attrs()[aPos].mValue.SwapValueWith(aValue);
310 0 : Attrs()[aPos].~InternalAttr();
311 0 : memmove(&Attrs()[aPos], &Attrs()[aPos + 1],
312 0 : (mAttrCount - aPos - 1) * sizeof(InternalAttr));
313 0 : mAttrCount--;
314 0 : }
315 :
316 : const nsAttrName*
317 0 : nsMappedAttributes::GetExistingAttrNameFromQName(const nsAString& aName) const
318 : {
319 : uint32_t i;
320 0 : for (i = 0; i < mAttrCount; ++i) {
321 0 : if (Attrs()[i].mName.IsAtom()) {
322 0 : if (Attrs()[i].mName.Atom()->Equals(aName)) {
323 0 : return &Attrs()[i].mName;
324 : }
325 : }
326 : else {
327 0 : if (Attrs()[i].mName.NodeInfo()->QualifiedNameEquals(aName)) {
328 0 : return &Attrs()[i].mName;
329 : }
330 : }
331 : }
332 :
333 0 : return nullptr;
334 : }
335 :
336 : int32_t
337 135 : nsMappedAttributes::IndexOfAttr(nsIAtom* aLocalName) const
338 : {
339 : uint32_t i;
340 231 : for (i = 0; i < mAttrCount; ++i) {
341 132 : if (Attrs()[i].mName.Equals(aLocalName)) {
342 36 : return i;
343 : }
344 : }
345 :
346 99 : return -1;
347 : }
348 :
349 : size_t
350 21 : nsMappedAttributes::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
351 : {
352 21 : NS_ASSERTION(mAttrCount == mBufferSize,
353 : "mBufferSize and mAttrCount are expected to be the same.");
354 :
355 21 : size_t n = aMallocSizeOf(this);
356 42 : for (uint16_t i = 0; i < mAttrCount; ++i) {
357 21 : n += Attrs()[i].mValue.SizeOfExcludingThis(aMallocSizeOf);
358 : }
359 21 : return n;
360 : }
361 :
362 : void
363 0 : nsMappedAttributes::LazilyResolveServoDeclaration(nsPresContext* aContext)
364 : {
365 :
366 0 : MOZ_ASSERT(!mServoStyle,
367 : "LazilyResolveServoDeclaration should not be called if mServoStyle is already set");
368 0 : if (mRuleMapper) {
369 0 : mServoStyle = Servo_DeclarationBlock_CreateEmpty().Consume();
370 0 : ServoSpecifiedValues servo = ServoSpecifiedValues(aContext, mServoStyle.get());
371 0 : (*mRuleMapper)(this, &servo);
372 : }
373 9 : }
|