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 "mozilla/Assertions.h"
14 #include "nsHTMLStyleSheet.h"
15 #include "mozilla/DeclarationBlock.h"
16 #include "mozilla/HashFunctions.h"
17 #include "mozilla/MappedDeclarations.h"
18 #include "mozilla/MemoryReporting.h"
19
20 using namespace mozilla;
21
22 bool nsMappedAttributes::sShuttingDown = false;
23 nsTArray<void*>* nsMappedAttributes::sCachedMappedAttributeAllocations =
24 nullptr;
25
Shutdown()26 void nsMappedAttributes::Shutdown() {
27 sShuttingDown = true;
28 if (sCachedMappedAttributeAllocations) {
29 for (uint32_t i = 0; i < sCachedMappedAttributeAllocations->Length(); ++i) {
30 void* cachedValue = (*sCachedMappedAttributeAllocations)[i];
31 ::operator delete(cachedValue);
32 }
33 }
34
35 delete sCachedMappedAttributeAllocations;
36 sCachedMappedAttributeAllocations = nullptr;
37 }
38
nsMappedAttributes(nsHTMLStyleSheet * aSheet,nsMapRuleToAttributesFunc aMapRuleFunc)39 nsMappedAttributes::nsMappedAttributes(nsHTMLStyleSheet* aSheet,
40 nsMapRuleToAttributesFunc aMapRuleFunc)
41 : mAttrCount(0),
42 mSheet(aSheet),
43 mRuleMapper(aMapRuleFunc),
44 mServoStyle(nullptr) {
45 MOZ_ASSERT(mRefCnt == 0); // Ensure caching works as expected.
46 }
47
nsMappedAttributes(const nsMappedAttributes & aCopy)48 nsMappedAttributes::nsMappedAttributes(const nsMappedAttributes& aCopy)
49 : mAttrCount(aCopy.mAttrCount),
50 mSheet(aCopy.mSheet),
51 mRuleMapper(aCopy.mRuleMapper),
52 // This is only called by ::Clone, which is used to create independent
53 // nsMappedAttributes objects which should not share a DeclarationBlock
54 mServoStyle(nullptr) {
55 MOZ_ASSERT(mBufferSize >= aCopy.mAttrCount, "can't fit attributes");
56 MOZ_ASSERT(mRefCnt == 0); // Ensure caching works as expected.
57
58 uint32_t i = 0;
59 for (const InternalAttr& attr : aCopy.Attrs()) {
60 new (&mBuffer[i++]) InternalAttr(attr);
61 }
62 }
63
~nsMappedAttributes()64 nsMappedAttributes::~nsMappedAttributes() {
65 if (mSheet) {
66 mSheet->DropMappedAttributes(this);
67 }
68
69 for (InternalAttr& attr : Attrs()) {
70 attr.~InternalAttr();
71 }
72 }
73
Clone(bool aWillAddAttr)74 nsMappedAttributes* nsMappedAttributes::Clone(bool aWillAddAttr) {
75 uint32_t extra = aWillAddAttr ? 1 : 0;
76
77 // This will call the overridden operator new
78 return new (mAttrCount + extra) nsMappedAttributes(*this);
79 }
80
operator new(size_t aSize,uint32_t aAttrCount)81 void* nsMappedAttributes::operator new(size_t aSize,
82 uint32_t aAttrCount) noexcept(true) {
83 size_t size = aSize + aAttrCount * sizeof(InternalAttr);
84
85 if (sCachedMappedAttributeAllocations) {
86 void* cached = sCachedMappedAttributeAllocations->SafeElementAt(aAttrCount);
87 if (cached) {
88 (*sCachedMappedAttributeAllocations)[aAttrCount] = nullptr;
89 return cached;
90 }
91 }
92
93 void* newAttrs = ::operator new(size);
94
95 #ifdef DEBUG
96 static_cast<nsMappedAttributes*>(newAttrs)->mBufferSize = aAttrCount;
97 #endif
98 return newAttrs;
99 }
100
LastRelease()101 void nsMappedAttributes::LastRelease() {
102 if (!sShuttingDown) {
103 if (!sCachedMappedAttributeAllocations) {
104 sCachedMappedAttributeAllocations = new nsTArray<void*>();
105 }
106
107 // Ensure the cache array is at least mAttrCount + 1 long and
108 // that each item is either null or pointing to a cached item.
109 // The size of the array is capped because mapped attributes are defined
110 // statically in element implementations.
111 sCachedMappedAttributeAllocations->SetCapacity(mAttrCount + 1);
112 for (uint32_t i = sCachedMappedAttributeAllocations->Length();
113 i < (uint32_t(mAttrCount) + 1); ++i) {
114 sCachedMappedAttributeAllocations->AppendElement(nullptr);
115 }
116
117 if (!(*sCachedMappedAttributeAllocations)[mAttrCount]) {
118 void* memoryToCache = this;
119 this->~nsMappedAttributes();
120 (*sCachedMappedAttributeAllocations)[mAttrCount] = memoryToCache;
121 return;
122 }
123 }
124
125 delete this;
126 }
127
SetAndSwapAttr(nsAtom * aAttrName,nsAttrValue & aValue,bool * aValueWasSet)128 void nsMappedAttributes::SetAndSwapAttr(nsAtom* aAttrName, nsAttrValue& aValue,
129 bool* aValueWasSet) {
130 MOZ_ASSERT(aAttrName, "null name");
131 *aValueWasSet = false;
132 uint32_t i;
133 for (i = 0; i < mAttrCount && !mBuffer[i].mName.IsSmaller(aAttrName); ++i) {
134 if (mBuffer[i].mName.Equals(aAttrName)) {
135 mBuffer[i].mValue.SwapValueWith(aValue);
136 *aValueWasSet = true;
137 return;
138 }
139 }
140
141 MOZ_ASSERT(mBufferSize >= mAttrCount + 1, "can't fit attributes");
142
143 if (mAttrCount != i) {
144 memmove(&mBuffer[i + 1], &mBuffer[i],
145 (mAttrCount - i) * sizeof(InternalAttr));
146 }
147
148 new (&mBuffer[i].mName) nsAttrName(aAttrName);
149 new (&mBuffer[i].mValue) nsAttrValue();
150 mBuffer[i].mValue.SwapValueWith(aValue);
151 ++mAttrCount;
152 }
153
GetAttr(const nsAtom * aAttrName) const154 const nsAttrValue* nsMappedAttributes::GetAttr(const nsAtom* aAttrName) const {
155 MOZ_ASSERT(aAttrName, "null name");
156 for (const InternalAttr& attr : Attrs()) {
157 if (attr.mName.Equals(aAttrName)) {
158 return &attr.mValue;
159 }
160 }
161 return nullptr;
162 }
163
GetAttr(const nsAString & aAttrName) const164 const nsAttrValue* nsMappedAttributes::GetAttr(
165 const nsAString& aAttrName) const {
166 for (const InternalAttr& attr : Attrs()) {
167 if (attr.mName.Atom()->Equals(aAttrName)) {
168 return &attr.mValue;
169 }
170 }
171 return nullptr;
172 }
173
Equals(const nsMappedAttributes * aOther) const174 bool nsMappedAttributes::Equals(const nsMappedAttributes* aOther) const {
175 if (this == aOther) {
176 return true;
177 }
178
179 if (mRuleMapper != aOther->mRuleMapper || mAttrCount != aOther->mAttrCount) {
180 return false;
181 }
182
183 uint32_t i;
184 for (i = 0; i < mAttrCount; ++i) {
185 if (!mBuffer[i].mName.Equals(aOther->mBuffer[i].mName) ||
186 !mBuffer[i].mValue.Equals(aOther->mBuffer[i].mValue)) {
187 return false;
188 }
189 }
190
191 return true;
192 }
193
HashValue() const194 PLDHashNumber nsMappedAttributes::HashValue() const {
195 PLDHashNumber hash = HashGeneric(mRuleMapper);
196 for (const InternalAttr& attr : Attrs()) {
197 hash = AddToHash(hash, attr.mName.HashValue(), attr.mValue.HashValue());
198 }
199 return hash;
200 }
201
SetStyleSheet(nsHTMLStyleSheet * aSheet)202 void nsMappedAttributes::SetStyleSheet(nsHTMLStyleSheet* aSheet) {
203 MOZ_ASSERT(!mSheet,
204 "Should either drop the sheet reference manually, "
205 "or drop the mapped attributes");
206 mSheet = aSheet; // not ref counted
207 }
208
RemoveAttrAt(uint32_t aPos,nsAttrValue & aValue)209 void nsMappedAttributes::RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue) {
210 mBuffer[aPos].mValue.SwapValueWith(aValue);
211 mBuffer[aPos].~InternalAttr();
212 memmove(&mBuffer[aPos], &mBuffer[aPos + 1],
213 (mAttrCount - aPos - 1) * sizeof(InternalAttr));
214 mAttrCount--;
215 }
216
GetExistingAttrNameFromQName(const nsAString & aName) const217 const nsAttrName* nsMappedAttributes::GetExistingAttrNameFromQName(
218 const nsAString& aName) const {
219 for (const InternalAttr& attr : Attrs()) {
220 if (attr.mName.IsAtom()) {
221 if (attr.mName.Atom()->Equals(aName)) {
222 return &attr.mName;
223 }
224 } else {
225 if (attr.mName.NodeInfo()->QualifiedNameEquals(aName)) {
226 return &attr.mName;
227 }
228 }
229 }
230
231 return nullptr;
232 }
233
IndexOfAttr(const nsAtom * aLocalName) const234 int32_t nsMappedAttributes::IndexOfAttr(const nsAtom* aLocalName) const {
235 for (uint32_t i = 0; i < mAttrCount; ++i) {
236 if (mBuffer[i].mName.Equals(aLocalName)) {
237 return i;
238 }
239 }
240 return -1;
241 }
242
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const243 size_t nsMappedAttributes::SizeOfIncludingThis(
244 MallocSizeOf aMallocSizeOf) const {
245 MOZ_ASSERT(mBufferSize >= mAttrCount, "can't fit attributes");
246
247 size_t n = aMallocSizeOf(this);
248 for (const InternalAttr& attr : Attrs()) {
249 n += attr.mValue.SizeOfExcludingThis(aMallocSizeOf);
250 }
251 return n;
252 }
253
LazilyResolveServoDeclaration(dom::Document * aDoc)254 void nsMappedAttributes::LazilyResolveServoDeclaration(dom::Document* aDoc) {
255 MOZ_ASSERT(!mServoStyle,
256 "LazilyResolveServoDeclaration should not be called if "
257 "mServoStyle is already set");
258 if (mRuleMapper) {
259 MappedDeclarations declarations(
260 aDoc, Servo_DeclarationBlock_CreateEmpty().Consume());
261 (*mRuleMapper)(this, declarations);
262 mServoStyle = declarations.TakeDeclarationBlock();
263 }
264 }
265