Line data Source code
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 : * compact representation of the property-value pairs within a CSS
9 : * declaration, and the code for expanding and compacting it
10 : */
11 :
12 : #ifndef nsCSSDataBlock_h__
13 : #define nsCSSDataBlock_h__
14 :
15 : #include "mozilla/MemoryReporting.h"
16 : #include "nsCSSProps.h"
17 : #include "nsCSSPropertyIDSet.h"
18 : #include "nsCSSValue.h"
19 : #include "nsStyleStruct.h"
20 : #include "imgRequestProxy.h"
21 :
22 : struct nsRuleData;
23 : class nsCSSExpandedDataBlock;
24 : class nsIDocument;
25 :
26 : namespace mozilla {
27 : namespace css {
28 : class Declaration;
29 : } // namespace css
30 : } // namespace mozilla
31 :
32 : /**
33 : * An |nsCSSCompressedDataBlock| holds a usually-immutable chunk of
34 : * property-value data for a CSS declaration block (which we misname a
35 : * |css::Declaration|). Mutation is accomplished through
36 : * |nsCSSExpandedDataBlock| or in some cases via direct slot access.
37 : */
38 : class nsCSSCompressedDataBlock
39 : {
40 : private:
41 : friend class nsCSSExpandedDataBlock;
42 :
43 : // Only this class (via |CreateEmptyBlock|) or nsCSSExpandedDataBlock
44 : // (in |Compress|) can create compressed data blocks.
45 3520 : explicit nsCSSCompressedDataBlock(uint32_t aNumProps)
46 3520 : : mStyleBits(0), mNumProps(aNumProps)
47 3520 : {}
48 :
49 : public:
50 : ~nsCSSCompressedDataBlock();
51 :
52 : /**
53 : * Do what |nsIStyleRule::MapRuleInfoInto| needs to do for a style
54 : * rule using this block for storage.
55 : */
56 : void MapRuleInfoInto(nsRuleData *aRuleData) const;
57 :
58 : /**
59 : * Return the location at which the *value* for the property is
60 : * stored, or null if the block does not contain a value for the
61 : * property.
62 : *
63 : * Inefficient (by design).
64 : *
65 : * Must not be called for shorthands.
66 : */
67 : const nsCSSValue* ValueFor(nsCSSPropertyID aProperty) const;
68 :
69 : /**
70 : * Attempt to replace the value for |aProperty| stored in this block
71 : * with the matching value stored in |aFromBlock|.
72 : * This method will fail (returning false) if |aProperty| is not
73 : * already in this block. It will set |aChanged| to true if it
74 : * actually made a change to the block, but regardless, if it
75 : * returns true, the value in |aFromBlock| was erased.
76 : */
77 : bool TryReplaceValue(nsCSSPropertyID aProperty,
78 : nsCSSExpandedDataBlock& aFromBlock,
79 : bool* aChanged);
80 :
81 : /**
82 : * Clone this block, or return null on out-of-memory.
83 : */
84 : nsCSSCompressedDataBlock* Clone() const;
85 :
86 : /**
87 : * Create a new nsCSSCompressedDataBlock holding no declarations.
88 : */
89 : static nsCSSCompressedDataBlock* CreateEmptyBlock();
90 :
91 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
92 :
93 : bool HasDefaultBorderImageSlice() const;
94 : bool HasDefaultBorderImageWidth() const;
95 : bool HasDefaultBorderImageOutset() const;
96 : bool HasDefaultBorderImageRepeat() const;
97 :
98 316 : bool HasInheritedStyleData() const
99 : {
100 316 : return mStyleBits & NS_STYLE_INHERITED_STRUCT_MASK;
101 : }
102 :
103 : private:
104 3520 : void* operator new(size_t aBaseSize, uint32_t aNumProps) {
105 3520 : MOZ_ASSERT(aBaseSize == sizeof(nsCSSCompressedDataBlock),
106 : "unexpected size for nsCSSCompressedDataBlock");
107 7040 : return ::operator new(aBaseSize + DataSize(aNumProps));
108 : }
109 :
110 : public:
111 : // Ideally, |nsCSSPropertyID| would be |enum nsCSSPropertyID : int16_t|. But
112 : // not all of the compilers we use are modern enough to support small
113 : // enums. So we manually squeeze nsCSSPropertyID into 16 bits ourselves.
114 : // The static assertion below ensures it fits.
115 : typedef int16_t CompressedCSSProperty;
116 : static const size_t MaxCompressedCSSProperty = INT16_MAX;
117 :
118 : private:
119 3520 : static size_t DataSize(uint32_t aNumProps) {
120 3520 : return size_t(aNumProps) *
121 3520 : (sizeof(nsCSSValue) + sizeof(CompressedCSSProperty));
122 : }
123 :
124 : int32_t mStyleBits; // the structs for which we have data, according to
125 : // |nsCachedStyleData::GetBitForSID|.
126 : uint32_t mNumProps;
127 : // nsCSSValue elements are stored after these fields, and
128 : // nsCSSPropertyID elements are stored -- each one compressed as a
129 : // CompressedCSSProperty -- after the nsCSSValue elements. Space for them
130 : // is allocated in |operator new| above. The static assertions following
131 : // this class make sure that the value and property elements are aligned
132 : // appropriately.
133 :
134 70725 : nsCSSValue* Values() const {
135 70725 : return (nsCSSValue*)(this + 1);
136 : }
137 :
138 48539 : CompressedCSSProperty* CompressedProperties() const {
139 48539 : return (CompressedCSSProperty*)(Values() + mNumProps);
140 : }
141 :
142 22186 : nsCSSValue* ValueAtIndex(uint32_t i) const {
143 22186 : MOZ_ASSERT(i < mNumProps, "value index out of range");
144 22186 : return Values() + i;
145 : }
146 :
147 35557 : nsCSSPropertyID PropertyAtIndex(uint32_t i) const {
148 35557 : MOZ_ASSERT(i < mNumProps, "property index out of range");
149 35557 : nsCSSPropertyID prop = (nsCSSPropertyID)CompressedProperties()[i];
150 35557 : MOZ_ASSERT(!nsCSSProps::IsShorthand(prop), "out of range");
151 35557 : return prop;
152 : }
153 :
154 48 : void CopyValueToIndex(uint32_t i, nsCSSValue* aValue) {
155 48 : new (ValueAtIndex(i)) nsCSSValue(*aValue);
156 48 : }
157 :
158 12934 : void RawCopyValueToIndex(uint32_t i, nsCSSValue* aValue) {
159 12934 : memcpy(ValueAtIndex(i), aValue, sizeof(nsCSSValue));
160 12934 : }
161 :
162 12982 : void SetPropertyAtIndex(uint32_t i, nsCSSPropertyID aProperty) {
163 12982 : MOZ_ASSERT(i < mNumProps, "set property index out of range");
164 12982 : CompressedProperties()[i] = (CompressedCSSProperty)aProperty;
165 12982 : }
166 :
167 116 : void SetNumPropsToZero() {
168 116 : mNumProps = 0;
169 116 : }
170 : };
171 :
172 : // Make sure the values and properties are aligned appropriately. (These
173 : // assertions are stronger than necessary to keep them simple.)
174 : static_assert(sizeof(nsCSSCompressedDataBlock) == 8,
175 : "nsCSSCompressedDataBlock's size has changed");
176 : static_assert(NS_ALIGNMENT_OF(nsCSSValue) == 4 || NS_ALIGNMENT_OF(nsCSSValue) == 8,
177 : "nsCSSValue doesn't align with nsCSSCompressedDataBlock");
178 : static_assert(NS_ALIGNMENT_OF(nsCSSCompressedDataBlock::CompressedCSSProperty) == 2,
179 : "CompressedCSSProperty doesn't align with nsCSSValue");
180 :
181 : // Make sure that sizeof(CompressedCSSProperty) is big enough.
182 : static_assert(eCSSProperty_COUNT_no_shorthands <=
183 : nsCSSCompressedDataBlock::MaxCompressedCSSProperty,
184 : "nsCSSPropertyID doesn't fit in StoredSizeOfCSSProperty");
185 :
186 : class nsCSSExpandedDataBlock
187 : {
188 : friend class nsCSSCompressedDataBlock;
189 :
190 : public:
191 : nsCSSExpandedDataBlock();
192 : ~nsCSSExpandedDataBlock();
193 :
194 : private:
195 : /* Property storage may not be accessed directly; use AddLonghandProperty
196 : * and friends.
197 : */
198 : nsCSSValue mValues[eCSSProperty_COUNT_no_shorthands];
199 :
200 : public:
201 : /**
202 : * Transfer all of the state from a pair of compressed data blocks
203 : * to this expanded block. This expanded block must be clear
204 : * beforehand.
205 : *
206 : * This method DELETES both of the compressed data blocks it is
207 : * passed. (This is necessary because ownership of sub-objects
208 : * is transferred to the expanded block.)
209 : */
210 : void Expand(nsCSSCompressedDataBlock *aNormalBlock,
211 : nsCSSCompressedDataBlock *aImportantBlock);
212 :
213 : /**
214 : * Allocate new compressed blocks and transfer all of the state
215 : * from this expanded block to the new blocks, clearing this
216 : * expanded block. A normal block will always be allocated, but
217 : * an important block will only be allocated if there are
218 : * !important properties in the expanded block; otherwise
219 : * |*aImportantBlock| will be set to null.
220 : *
221 : * aOrder is an array of nsCSSPropertyID values specifying the order
222 : * to store values in the two data blocks.
223 : */
224 : void Compress(nsCSSCompressedDataBlock **aNormalBlock,
225 : nsCSSCompressedDataBlock **aImportantBlock,
226 : const nsTArray<uint32_t>& aOrder);
227 :
228 : /**
229 : * Copy a value into this expanded block. This does NOT destroy
230 : * the source value object. |aProperty| cannot be a shorthand.
231 : */
232 : void AddLonghandProperty(nsCSSPropertyID aProperty, const nsCSSValue& aValue);
233 :
234 : /**
235 : * Clear the state of this expanded block.
236 : */
237 : void Clear();
238 :
239 : /**
240 : * Clear the data for the given property (including the set and
241 : * important bits). Can be used with shorthand properties.
242 : */
243 : void ClearProperty(nsCSSPropertyID aPropID);
244 :
245 : /**
246 : * Same as ClearProperty, but faster and cannot be used with shorthands.
247 : */
248 : void ClearLonghandProperty(nsCSSPropertyID aPropID);
249 :
250 : /**
251 : * Transfer the state for |aPropID| (which may be a shorthand)
252 : * from |aFromBlock| to this block. The property being transferred
253 : * is !important if |aIsImportant| is true, and should replace an
254 : * existing !important property regardless of its own importance
255 : * if |aOverrideImportant| is true. |aEnabledState| is used to
256 : * determine which longhand components of |aPropID| (if it is a
257 : * shorthand) to transfer.
258 : *
259 : * Returns true if something changed, false otherwise. Calls
260 : * |ValueAppended| on |aDeclaration| if the property was not
261 : * previously set, or in any case if |aMustCallValueAppended| is true.
262 : * Calls |SetDocumentAndPageUseCounter| on |aSheetDocument| if it is
263 : * non-null and |aPropID| has a use counter.
264 : */
265 : bool TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock,
266 : nsCSSPropertyID aPropID,
267 : mozilla::CSSEnabledState aEnabledState,
268 : bool aIsImportant,
269 : bool aOverrideImportant,
270 : bool aMustCallValueAppended,
271 : mozilla::css::Declaration* aDeclaration,
272 : nsIDocument* aSheetDocument);
273 :
274 : /**
275 : * Copies the values for aPropID into the specified aRuleData object.
276 : *
277 : * This is used for copying parsed-at-computed-value-time properties
278 : * that had variable references. aPropID must be a longhand property.
279 : */
280 : void MapRuleInfoInto(nsCSSPropertyID aPropID, nsRuleData* aRuleData) const;
281 :
282 21135 : void AssertInitialState() {
283 : #ifdef DEBUG
284 21135 : DoAssertInitialState();
285 : #endif
286 21135 : }
287 :
288 : private:
289 : /**
290 : * Compute the number of properties that will be present in the
291 : * result of |Compress|.
292 : */
293 : void ComputeNumProps(uint32_t* aNumPropsNormal,
294 : uint32_t* aNumPropsImportant);
295 :
296 : void DoExpand(nsCSSCompressedDataBlock *aBlock, bool aImportant);
297 :
298 : /**
299 : * Worker for TransferFromBlock; cannot be used with shorthands.
300 : */
301 : bool DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock,
302 : nsCSSPropertyID aPropID,
303 : bool aIsImportant,
304 : bool aOverrideImportant,
305 : bool aMustCallValueAppended,
306 : mozilla::css::Declaration* aDeclaration,
307 : nsIDocument* aSheetDocument);
308 :
309 : #ifdef DEBUG
310 : void DoAssertInitialState();
311 : #endif
312 :
313 : /*
314 : * mPropertiesSet stores a bit for every property that is present,
315 : * to optimize compression of blocks with small numbers of
316 : * properties (the norm) and to allow quickly checking whether a
317 : * property is set in this block.
318 : */
319 : nsCSSPropertyIDSet mPropertiesSet;
320 : /*
321 : * mPropertiesImportant indicates which properties are '!important'.
322 : */
323 : nsCSSPropertyIDSet mPropertiesImportant;
324 :
325 : /*
326 : * Return the storage location within |this| of the value of the
327 : * property |aProperty|.
328 : */
329 6879484 : nsCSSValue* PropertyAt(nsCSSPropertyID aProperty) {
330 6879484 : MOZ_ASSERT(0 <= aProperty &&
331 : aProperty < eCSSProperty_COUNT_no_shorthands,
332 : "property out of range");
333 6879484 : return &mValues[aProperty];
334 : }
335 883 : const nsCSSValue* PropertyAt(nsCSSPropertyID aProperty) const {
336 883 : MOZ_ASSERT(0 <= aProperty &&
337 : aProperty < eCSSProperty_COUNT_no_shorthands,
338 : "property out of range");
339 883 : return &mValues[aProperty];
340 : }
341 :
342 30652 : void SetPropertyBit(nsCSSPropertyID aProperty) {
343 30652 : mPropertiesSet.AddProperty(aProperty);
344 30652 : }
345 :
346 17396 : void ClearPropertyBit(nsCSSPropertyID aProperty) {
347 17396 : mPropertiesSet.RemoveProperty(aProperty);
348 17396 : }
349 :
350 13133 : bool HasPropertyBit(nsCSSPropertyID aProperty) {
351 13133 : return mPropertiesSet.HasProperty(aProperty);
352 : }
353 :
354 1031 : void SetImportantBit(nsCSSPropertyID aProperty) {
355 1031 : mPropertiesImportant.AddProperty(aProperty);
356 1031 : }
357 :
358 4400 : void ClearImportantBit(nsCSSPropertyID aProperty) {
359 4400 : mPropertiesImportant.RemoveProperty(aProperty);
360 4400 : }
361 :
362 12992 : bool HasImportantBit(nsCSSPropertyID aProperty) {
363 12992 : return mPropertiesImportant.HasProperty(aProperty);
364 : }
365 :
366 3118 : void ClearSets() {
367 3118 : mPropertiesSet.Empty();
368 3118 : mPropertiesImportant.Empty();
369 3118 : }
370 : };
371 :
372 : #endif /* !defined(nsCSSDataBlock_h__) */
|