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