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