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/. */
8 * A unique per-element set of attributes that is used as an
9 * nsIStyleRule; used to implement presentational attributes.
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"
20 using namespace mozilla
;
22 bool nsMappedAttributes::sShuttingDown
= false;
23 nsTArray
<void*>* nsMappedAttributes::sCachedMappedAttributeAllocations
=
26 void nsMappedAttributes::Shutdown() {
28 if (sCachedMappedAttributeAllocations
) {
29 for (uint32_t i
= 0; i
< sCachedMappedAttributeAllocations
->Length(); ++i
) {
30 void* cachedValue
= (*sCachedMappedAttributeAllocations
)[i
];
31 ::operator delete(cachedValue
);
35 delete sCachedMappedAttributeAllocations
;
36 sCachedMappedAttributeAllocations
= nullptr;
39 nsMappedAttributes::nsMappedAttributes(nsHTMLStyleSheet
* aSheet
,
40 nsMapRuleToAttributesFunc aMapRuleFunc
)
43 mRuleMapper(aMapRuleFunc
),
44 mServoStyle(nullptr) {
45 MOZ_ASSERT(mRefCnt
== 0); // Ensure caching works as expected.
48 nsMappedAttributes::nsMappedAttributes(const nsMappedAttributes
& aCopy
)
49 : mAttrCount(aCopy
.mAttrCount
),
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.
59 for (i
= 0; i
< mAttrCount
; ++i
) {
60 new (&Attrs()[i
]) InternalAttr(aCopy
.Attrs()[i
]);
64 nsMappedAttributes::~nsMappedAttributes() {
66 mSheet
->DropMappedAttributes(this);
70 for (i
= 0; i
< mAttrCount
; ++i
) {
71 Attrs()[i
].~InternalAttr();
75 nsMappedAttributes
* nsMappedAttributes::Clone(bool aWillAddAttr
) {
76 uint32_t extra
= aWillAddAttr
? 1 : 0;
78 // This will call the overridden operator new
79 return new (mAttrCount
+ extra
) nsMappedAttributes(*this);
82 void* nsMappedAttributes::operator new(size_t aSize
,
83 uint32_t aAttrCount
) noexcept(true) {
84 size_t size
= aSize
+ aAttrCount
* sizeof(InternalAttr
);
86 // aSize will include the mAttrs buffer so subtract that.
87 // We don't want to under-allocate, however, so do not subtract
88 // if we have zero attributes. The zero attribute case only happens
89 // for <body>'s mapped attributes
90 if (aAttrCount
!= 0) {
91 size
-= sizeof(void* [1]);
94 if (sCachedMappedAttributeAllocations
) {
95 void* cached
= sCachedMappedAttributeAllocations
->SafeElementAt(aAttrCount
);
97 (*sCachedMappedAttributeAllocations
)[aAttrCount
] = nullptr;
102 void* newAttrs
= ::operator new(size
);
105 static_cast<nsMappedAttributes
*>(newAttrs
)->mBufferSize
= aAttrCount
;
110 void nsMappedAttributes::LastRelease() {
111 if (!sShuttingDown
) {
112 if (!sCachedMappedAttributeAllocations
) {
113 sCachedMappedAttributeAllocations
= new nsTArray
<void*>();
116 // Ensure the cache array is at least mAttrCount + 1 long and
117 // that each item is either null or pointing to a cached item.
118 // The size of the array is capped because mapped attributes are defined
119 // statically in element implementations.
120 sCachedMappedAttributeAllocations
->SetCapacity(mAttrCount
+ 1);
121 for (uint32_t i
= sCachedMappedAttributeAllocations
->Length();
122 i
< (uint32_t(mAttrCount
) + 1); ++i
) {
123 sCachedMappedAttributeAllocations
->AppendElement(nullptr);
126 if (!(*sCachedMappedAttributeAllocations
)[mAttrCount
]) {
127 void* memoryToCache
= this;
128 this->~nsMappedAttributes();
129 (*sCachedMappedAttributeAllocations
)[mAttrCount
] = memoryToCache
;
137 void nsMappedAttributes::SetAndSwapAttr(nsAtom
* aAttrName
, nsAttrValue
& aValue
,
138 bool* aValueWasSet
) {
139 MOZ_ASSERT(aAttrName
, "null name");
140 *aValueWasSet
= false;
142 for (i
= 0; i
< mAttrCount
&& !Attrs()[i
].mName
.IsSmaller(aAttrName
); ++i
) {
143 if (Attrs()[i
].mName
.Equals(aAttrName
)) {
144 Attrs()[i
].mValue
.SwapValueWith(aValue
);
145 *aValueWasSet
= true;
150 MOZ_ASSERT(mBufferSize
>= mAttrCount
+ 1, "can't fit attributes");
152 if (mAttrCount
!= i
) {
153 memmove(&Attrs()[i
+ 1], &Attrs()[i
],
154 (mAttrCount
- i
) * sizeof(InternalAttr
));
157 new (&Attrs()[i
].mName
) nsAttrName(aAttrName
);
158 new (&Attrs()[i
].mValue
) nsAttrValue();
159 Attrs()[i
].mValue
.SwapValueWith(aValue
);
163 const nsAttrValue
* nsMappedAttributes::GetAttr(const nsAtom
* aAttrName
) const {
164 MOZ_ASSERT(aAttrName
, "null name");
166 for (uint32_t i
= 0; i
< mAttrCount
; ++i
) {
167 if (Attrs()[i
].mName
.Equals(aAttrName
)) {
168 return &Attrs()[i
].mValue
;
175 const nsAttrValue
* nsMappedAttributes::GetAttr(
176 const nsAString
& aAttrName
) const {
177 for (uint32_t i
= 0; i
< mAttrCount
; ++i
) {
178 if (Attrs()[i
].mName
.Atom()->Equals(aAttrName
)) {
179 return &Attrs()[i
].mValue
;
186 bool nsMappedAttributes::Equals(const nsMappedAttributes
* aOther
) const {
187 if (this == aOther
) {
191 if (mRuleMapper
!= aOther
->mRuleMapper
|| mAttrCount
!= aOther
->mAttrCount
) {
196 for (i
= 0; i
< mAttrCount
; ++i
) {
197 if (!Attrs()[i
].mName
.Equals(aOther
->Attrs()[i
].mName
) ||
198 !Attrs()[i
].mValue
.Equals(aOther
->Attrs()[i
].mValue
)) {
206 PLDHashNumber
nsMappedAttributes::HashValue() const {
207 PLDHashNumber hash
= HashGeneric(mRuleMapper
);
210 for (i
= 0; i
< mAttrCount
; ++i
) {
211 hash
= AddToHash(hash
, Attrs()[i
].mName
.HashValue(),
212 Attrs()[i
].mValue
.HashValue());
218 void nsMappedAttributes::SetStyleSheet(nsHTMLStyleSheet
* aSheet
) {
220 "Should either drop the sheet reference manually, "
221 "or drop the mapped attributes");
222 mSheet
= aSheet
; // not ref counted
225 void nsMappedAttributes::RemoveAttrAt(uint32_t aPos
, nsAttrValue
& aValue
) {
226 Attrs()[aPos
].mValue
.SwapValueWith(aValue
);
227 Attrs()[aPos
].~InternalAttr();
228 memmove(&Attrs()[aPos
], &Attrs()[aPos
+ 1],
229 (mAttrCount
- aPos
- 1) * sizeof(InternalAttr
));
233 const nsAttrName
* nsMappedAttributes::GetExistingAttrNameFromQName(
234 const nsAString
& aName
) const {
236 for (i
= 0; i
< mAttrCount
; ++i
) {
237 if (Attrs()[i
].mName
.IsAtom()) {
238 if (Attrs()[i
].mName
.Atom()->Equals(aName
)) {
239 return &Attrs()[i
].mName
;
242 if (Attrs()[i
].mName
.NodeInfo()->QualifiedNameEquals(aName
)) {
243 return &Attrs()[i
].mName
;
251 int32_t nsMappedAttributes::IndexOfAttr(const nsAtom
* aLocalName
) const {
253 for (i
= 0; i
< mAttrCount
; ++i
) {
254 if (Attrs()[i
].mName
.Equals(aLocalName
)) {
262 size_t nsMappedAttributes::SizeOfIncludingThis(
263 MallocSizeOf aMallocSizeOf
) const {
264 MOZ_ASSERT(mBufferSize
>= mAttrCount
, "can't fit attributes");
266 size_t n
= aMallocSizeOf(this);
267 for (uint16_t i
= 0; i
< mAttrCount
; ++i
) {
268 n
+= Attrs()[i
].mValue
.SizeOfExcludingThis(aMallocSizeOf
);
273 void nsMappedAttributes::LazilyResolveServoDeclaration(dom::Document
* aDoc
) {
274 MOZ_ASSERT(!mServoStyle
,
275 "LazilyResolveServoDeclaration should not be called if "
276 "mServoStyle is already set");
278 MappedDeclarations
declarations(
279 aDoc
, Servo_DeclarationBlock_CreateEmpty().Consume());
280 (*mRuleMapper
)(this, declarations
);
281 mServoStyle
= declarations
.TakeDeclarationBlock();