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 (const InternalAttr
& attr
: aCopy
.Attrs()) {
60 new (&mBuffer
[i
++]) InternalAttr(attr
);
64 nsMappedAttributes::~nsMappedAttributes() {
66 mSheet
->DropMappedAttributes(this);
69 for (InternalAttr
& attr
: Attrs()) {
74 nsMappedAttributes
* nsMappedAttributes::Clone(bool aWillAddAttr
) {
75 uint32_t extra
= aWillAddAttr
? 1 : 0;
77 // This will call the overridden operator new
78 return new (mAttrCount
+ extra
) nsMappedAttributes(*this);
81 void* nsMappedAttributes::operator new(size_t aSize
,
82 uint32_t aAttrCount
) noexcept(true) {
83 size_t size
= aSize
+ aAttrCount
* sizeof(InternalAttr
);
85 if (sCachedMappedAttributeAllocations
) {
86 void* cached
= sCachedMappedAttributeAllocations
->SafeElementAt(aAttrCount
);
88 (*sCachedMappedAttributeAllocations
)[aAttrCount
] = nullptr;
93 void* newAttrs
= ::operator new(size
);
96 static_cast<nsMappedAttributes
*>(newAttrs
)->mBufferSize
= aAttrCount
;
101 void nsMappedAttributes::LastRelease() {
102 if (!sShuttingDown
) {
103 if (!sCachedMappedAttributeAllocations
) {
104 sCachedMappedAttributeAllocations
= new nsTArray
<void*>();
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);
117 if (!(*sCachedMappedAttributeAllocations
)[mAttrCount
]) {
118 void* memoryToCache
= this;
119 this->~nsMappedAttributes();
120 (*sCachedMappedAttributeAllocations
)[mAttrCount
] = memoryToCache
;
128 void nsMappedAttributes::SetAndSwapAttr(nsAtom
* aAttrName
, nsAttrValue
& aValue
,
129 bool* aValueWasSet
) {
130 MOZ_ASSERT(aAttrName
, "null name");
131 *aValueWasSet
= false;
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;
141 MOZ_ASSERT(mBufferSize
>= mAttrCount
+ 1, "can't fit attributes");
143 if (mAttrCount
!= i
) {
144 memmove(&mBuffer
[i
+ 1], &mBuffer
[i
],
145 (mAttrCount
- i
) * sizeof(InternalAttr
));
148 new (&mBuffer
[i
].mName
) nsAttrName(aAttrName
);
149 new (&mBuffer
[i
].mValue
) nsAttrValue();
150 mBuffer
[i
].mValue
.SwapValueWith(aValue
);
154 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
)) {
164 const nsAttrValue
* nsMappedAttributes::GetAttr(
165 const nsAString
& aAttrName
) const {
166 for (const InternalAttr
& attr
: Attrs()) {
167 if (attr
.mName
.Atom()->Equals(aAttrName
)) {
174 bool nsMappedAttributes::Equals(const nsMappedAttributes
* aOther
) const {
175 if (this == aOther
) {
179 if (mRuleMapper
!= aOther
->mRuleMapper
|| mAttrCount
!= aOther
->mAttrCount
) {
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
)) {
194 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());
202 void nsMappedAttributes::SetStyleSheet(nsHTMLStyleSheet
* aSheet
) {
204 "Should either drop the sheet reference manually, "
205 "or drop the mapped attributes");
206 mSheet
= aSheet
; // not ref counted
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
));
217 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
)) {
225 if (attr
.mName
.NodeInfo()->QualifiedNameEquals(aName
)) {
234 int32_t nsMappedAttributes::IndexOfAttr(const nsAtom
* aLocalName
) const {
235 for (uint32_t i
= 0; i
< mAttrCount
; ++i
) {
236 if (mBuffer
[i
].mName
.Equals(aLocalName
)) {
243 size_t nsMappedAttributes::SizeOfIncludingThis(
244 MallocSizeOf aMallocSizeOf
) const {
245 MOZ_ASSERT(mBufferSize
>= mAttrCount
, "can't fit attributes");
247 size_t n
= aMallocSizeOf(this);
248 for (const InternalAttr
& attr
: Attrs()) {
249 n
+= attr
.mValue
.SizeOfExcludingThis(aMallocSizeOf
);
254 void nsMappedAttributes::LazilyResolveServoDeclaration(dom::Document
* aDoc
) {
255 MOZ_ASSERT(!mServoStyle
,
256 "LazilyResolveServoDeclaration should not be called if "
257 "mServoStyle is already set");
259 MappedDeclarations
declarations(
260 aDoc
, Servo_DeclarationBlock_CreateEmpty().Consume());
261 (*mRuleMapper
)(this, declarations
);
262 mServoStyle
= declarations
.TakeDeclarationBlock();