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