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 : #include "nsCSSDataBlock.h"
13 :
14 : #include "CSSVariableImageTable.h"
15 : #include "mozilla/css/Declaration.h"
16 : #include "mozilla/css/ImageLoader.h"
17 : #include "mozilla/MemoryReporting.h"
18 : #include "mozilla/WritingModes.h"
19 : #include "nsAutoPtr.h"
20 : #include "nsIDocument.h"
21 : #include "nsRuleData.h"
22 : #include "nsStyleContext.h"
23 : #include "nsStyleSet.h"
24 :
25 : using namespace mozilla;
26 : using namespace mozilla::css;
27 :
28 : /**
29 : * Does a fast move of aSource to aDest. The previous value in
30 : * aDest is cleanly destroyed, and aSource is cleared. Returns
31 : * true if, before the copy, the value at aSource compared unequal
32 : * to the value at aDest; false otherwise.
33 : */
34 : static bool
35 12996 : MoveValue(nsCSSValue* aSource, nsCSSValue* aDest)
36 : {
37 12996 : bool changed = (*aSource != *aDest);
38 12996 : aDest->~nsCSSValue();
39 12996 : memcpy(aDest, aSource, sizeof(nsCSSValue));
40 12996 : new (aSource) nsCSSValue();
41 12996 : return changed;
42 : }
43 :
44 : static bool
45 815 : ShouldIgnoreColors(nsRuleData *aRuleData)
46 : {
47 1450 : return aRuleData->mLevel != SheetType::Agent &&
48 1450 : aRuleData->mLevel != SheetType::User &&
49 1450 : !aRuleData->mPresContext->UseDocumentColors();
50 : }
51 :
52 : /**
53 : * Tries to call |nsCSSValue::StartImageLoad()| on an image source.
54 : * Image sources are specified by |url()| or |-moz-image-rect()| function.
55 : */
56 : static void
57 254 : TryToStartImageLoadOnValue(const nsCSSValue& aValue, nsIDocument* aDocument,
58 : nsStyleContext* aContext, nsCSSPropertyID aProperty,
59 : bool aForTokenStream)
60 : {
61 254 : MOZ_ASSERT(aDocument);
62 :
63 254 : if (aValue.GetUnit() == eCSSUnit_URL) {
64 : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
65 : // The 'mask-image' property accepts local reference URIs.
66 : // For example,
67 : // mask-image: url(#mask_id); // refer to a SVG mask element, whose id is
68 : // // "mask_id", in the current document.
69 : // For such 'mask-image' values (pointing to an in-document element),
70 : // there is no need to trigger image download.
71 41 : if (aProperty == eCSSProperty_mask_image) {
72 : // Filter out all fragment URLs.
73 : // Since nsCSSValue::GetURLStructValue runs much faster than
74 : // nsIURI::EqualsExceptRef bellow, we get performance gain by this
75 : // early return.
76 1 : URLValue* urlValue = aValue.GetURLStructValue();
77 1 : if (urlValue->IsLocalRef()) {
78 1 : return;
79 : }
80 :
81 : // Even though urlValue is not a fragment URL, it might still refer to
82 : // an internal resource.
83 : // For example, aDocument base URL is "http://foo/index.html" and
84 : // intentionally references a mask-image at
85 : // url(http://foo/index.html#mask) which still refers to a resource in
86 : // aDocument.
87 0 : nsIURI* imageURI = aValue.GetURLValue();
88 0 : if (imageURI) {
89 0 : nsIURI* docURI = aDocument->GetDocumentURI();
90 0 : bool isEqualExceptRef = false;
91 0 : nsresult rv = imageURI->EqualsExceptRef(docURI, &isEqualExceptRef);
92 0 : if (NS_SUCCEEDED(rv) && isEqualExceptRef) {
93 0 : return;
94 : }
95 : }
96 : }
97 : #endif
98 40 : aValue.StartImageLoad(aDocument);
99 40 : if (aForTokenStream && aContext) {
100 0 : CSSVariableImageTable::Add(aContext, aProperty,
101 0 : aValue.GetImageStructValue());
102 : }
103 : }
104 213 : else if (aValue.GetUnit() == eCSSUnit_Image) {
105 : // If we already have a request, see if this document needs to clone it.
106 101 : imgIRequest* request = aValue.GetImageValue(nullptr);
107 :
108 101 : if (request) {
109 101 : ImageValue* imageValue = aValue.GetImageStructValue();
110 101 : aDocument->StyleImageLoader()->MaybeRegisterCSSImage(imageValue);
111 101 : if (aForTokenStream && aContext) {
112 0 : CSSVariableImageTable::Add(aContext, aProperty, imageValue);
113 : }
114 : }
115 : }
116 112 : else if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) {
117 2 : nsCSSValue::Array* arguments = aValue.GetArrayValue();
118 2 : MOZ_ASSERT(arguments->Count() == 6, "unexpected num of arguments");
119 :
120 2 : const nsCSSValue& image = arguments->Item(1);
121 2 : TryToStartImageLoadOnValue(image, aDocument, aContext, aProperty,
122 2 : aForTokenStream);
123 : }
124 : }
125 :
126 : static void
127 749 : TryToStartImageLoad(const nsCSSValue& aValue, nsIDocument* aDocument,
128 : nsStyleContext* aContext, nsCSSPropertyID aProperty,
129 : bool aForTokenStream)
130 : {
131 749 : if (aValue.GetUnit() == eCSSUnit_List) {
132 552 : for (const nsCSSValueList* l = aValue.GetListValue(); l; l = l->mNext) {
133 283 : TryToStartImageLoad(l->mValue, aDocument, aContext, aProperty,
134 283 : aForTokenStream);
135 : }
136 480 : } else if (nsCSSProps::PropHasFlags(aProperty,
137 : CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0)) {
138 228 : if (aValue.GetUnit() == eCSSUnit_Array) {
139 0 : TryToStartImageLoadOnValue(aValue.GetArrayValue()->Item(0), aDocument,
140 0 : aContext, aProperty, aForTokenStream);
141 : }
142 : } else {
143 252 : TryToStartImageLoadOnValue(aValue, aDocument, aContext, aProperty,
144 252 : aForTokenStream);
145 : }
146 749 : }
147 :
148 : static inline bool
149 9948 : ShouldStartImageLoads(nsRuleData *aRuleData, nsCSSPropertyID aProperty)
150 : {
151 : // Don't initiate image loads for if-visited styles. This is
152 : // important because:
153 : // (1) it's a waste of CPU and bandwidth
154 : // (2) in some cases we'd start the image load on a style change
155 : // where we wouldn't have started the load initially, which makes
156 : // which links are visited detectable to Web pages (see bug
157 : // 557287)
158 19896 : return !aRuleData->mStyleContext->IsStyleIfVisited() &&
159 19896 : nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_START_IMAGE_LOADS);
160 : }
161 :
162 : static void
163 9948 : MapSinglePropertyInto(nsCSSPropertyID aTargetProp,
164 : const nsCSSValue* aSrcValue,
165 : nsCSSValue* aTargetValue,
166 : nsRuleData* aRuleData)
167 : {
168 9948 : MOZ_ASSERT(!nsCSSProps::PropHasFlags(aTargetProp, CSS_PROPERTY_LOGICAL),
169 : "Can't map into a logical property");
170 9948 : MOZ_ASSERT(aSrcValue->GetUnit() != eCSSUnit_Null, "oops");
171 :
172 : // Although aTargetValue is the nsCSSValue we are going to write into,
173 : // we also look at its value before writing into it. This is done
174 : // when aTargetValue is a token stream value, which is the case when we
175 : // have just re-parsed a property that had a variable reference (in
176 : // nsCSSParser::ParsePropertyWithVariableReferences). TryToStartImageLoad
177 : // then records any resulting ImageValue objects in the
178 : // CSSVariableImageTable, to give them the appropriate lifetime.
179 9948 : MOZ_ASSERT(aTargetValue->GetUnit() == eCSSUnit_TokenStream ||
180 : aTargetValue->GetUnit() == eCSSUnit_Null,
181 : "aTargetValue must only be a token stream (when re-parsing "
182 : "properties with variable references) or null");
183 :
184 9948 : if (ShouldStartImageLoads(aRuleData, aTargetProp)) {
185 466 : nsIDocument* doc = aRuleData->mPresContext->Document();
186 466 : TryToStartImageLoad(*aSrcValue, doc, aRuleData->mStyleContext,
187 : aTargetProp,
188 932 : aTargetValue->GetUnit() == eCSSUnit_TokenStream);
189 : }
190 9948 : *aTargetValue = *aSrcValue;
191 19896 : if (nsCSSProps::PropHasFlags(aTargetProp,
192 10763 : CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED) &&
193 815 : ShouldIgnoreColors(aRuleData))
194 : {
195 0 : if (aTargetProp == eCSSProperty_background_color) {
196 : // Force non-'transparent' background
197 : // colors to the user's default.
198 0 : if (aTargetValue->IsNonTransparentColor()) {
199 0 : aTargetValue->SetColorValue(aRuleData->mPresContext->
200 0 : DefaultBackgroundColor());
201 : }
202 : } else {
203 : // Ignore 'color', 'border-*-color', etc.
204 0 : *aTargetValue = nsCSSValue();
205 : }
206 : }
207 9948 : }
208 :
209 : /**
210 : * If aProperty is a logical property, converts it to the equivalent physical
211 : * property based on writing mode information obtained from aRuleData's
212 : * style context.
213 : */
214 : static inline void
215 202 : EnsurePhysicalProperty(nsCSSPropertyID& aProperty, nsRuleData* aRuleData)
216 : {
217 : bool isAxisProperty =
218 202 : nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_LOGICAL_AXIS);
219 : bool isBlock =
220 202 : nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_LOGICAL_BLOCK_AXIS);
221 :
222 : int index;
223 :
224 202 : if (isAxisProperty) {
225 0 : LogicalAxis logicalAxis = isBlock ? eLogicalAxisBlock : eLogicalAxisInline;
226 0 : uint8_t wm = aRuleData->mStyleContext->StyleVisibility()->mWritingMode;
227 : PhysicalAxis axis =
228 0 : WritingMode::PhysicalAxisForLogicalAxis(wm, logicalAxis);
229 :
230 : // We rely on physical axis constants values matching the order of the
231 : // physical properties in the logical group array.
232 : static_assert(eAxisVertical == 0 && eAxisHorizontal == 1,
233 : "unexpected axis constant values");
234 0 : index = axis;
235 : } else {
236 : bool isEnd =
237 202 : nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_LOGICAL_END_EDGE);
238 :
239 202 : LogicalEdge edge = isEnd ? eLogicalEdgeEnd : eLogicalEdgeStart;
240 :
241 : // We handle block axis logical properties separately to save a bit of
242 : // work that the WritingMode constructor does that is unnecessary
243 : // unless we have an inline axis property.
244 : mozilla::Side side;
245 202 : if (isBlock) {
246 0 : uint8_t wm = aRuleData->mStyleContext->StyleVisibility()->mWritingMode;
247 0 : side = WritingMode::PhysicalSideForBlockAxis(wm, edge);
248 : } else {
249 202 : WritingMode wm(aRuleData->mStyleContext);
250 202 : side = wm.PhysicalSideForInlineAxis(edge);
251 : }
252 :
253 : // We rely on the physical side constant values matching the order of
254 : // the physical properties in the logical group array.
255 : static_assert(eSideTop == 0 && eSideRight == 1 &&
256 : eSideBottom == 2 && eSideLeft == 3,
257 : "unexpected side constant values");
258 202 : index = side;
259 : }
260 :
261 202 : const nsCSSPropertyID* props = nsCSSProps::LogicalGroup(aProperty);
262 202 : size_t len = isAxisProperty ? 2 : 4;
263 : #ifdef DEBUG
264 1010 : for (size_t i = 0; i < len; i++) {
265 808 : MOZ_ASSERT(props[i] != eCSSProperty_UNKNOWN,
266 : "unexpected logical group length");
267 : }
268 202 : MOZ_ASSERT(props[len] == eCSSProperty_UNKNOWN,
269 : "unexpected logical group length");
270 : #endif
271 :
272 247 : for (size_t i = 0; i < len; i++) {
273 239 : if (aRuleData->ValueFor(props[i])->GetUnit() == eCSSUnit_Null) {
274 : // A declaration of one of the logical properties in this logical
275 : // group (but maybe not aProperty) would be the winning
276 : // declaration in the cascade. This means that it's reasonably
277 : // likely that this logical property could be the winning
278 : // declaration in the cascade for some values of writing-mode,
279 : // direction, and text-orientation. (It doesn't mean that for
280 : // sure, though. For example, if this is a block-start logical
281 : // property, and all but the bottom physical property were set.
282 : // But the common case we want to hit here is logical declarations
283 : // that are completely overridden by a shorthand.)
284 : //
285 : // If this logical property could be the winning declaration in
286 : // the cascade for some values of writing-mode, direction, and
287 : // text-orientation, then we have to fault the resulting style
288 : // struct out of the rule tree. We can't cache anything on the
289 : // rule tree if it depends on data from the style context, since
290 : // data cached in the rule tree could be used with a style context
291 : // with a different value of the depended-upon data.
292 194 : uint8_t wm = WritingMode(aRuleData->mStyleContext).GetBits();
293 194 : aRuleData->mConditions.SetWritingModeDependency(wm);
294 194 : break;
295 : }
296 : }
297 :
298 202 : aProperty = props[index];
299 202 : }
300 :
301 : void
302 31904 : nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const
303 : {
304 : // If we have no data for these structs, then return immediately.
305 : // This optimization should make us return most of the time, so we
306 : // have to worry much less (although still some) about the speed of
307 : // the rest of the function.
308 31904 : if (!(aRuleData->mSIDs & mStyleBits))
309 26610 : return;
310 :
311 : // We process these in reverse order so that we end up mapping the
312 : // right property when one can be expressed using both logical and
313 : // physical property names.
314 40709 : for (uint32_t i = mNumProps; i-- > 0; ) {
315 35415 : nsCSSPropertyID iProp = PropertyAtIndex(i);
316 70830 : if (nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]) &
317 35415 : aRuleData->mSIDs) {
318 10161 : if (nsCSSProps::PropHasFlags(iProp, CSS_PROPERTY_LOGICAL)) {
319 153 : EnsurePhysicalProperty(iProp, aRuleData);
320 : }
321 10161 : nsCSSValue* target = aRuleData->ValueFor(iProp);
322 10161 : if (target->GetUnit() == eCSSUnit_Null) {
323 9065 : const nsCSSValue *val = ValueAtIndex(i);
324 : // In order for variable resolution to have the right information
325 : // about the stylesheet level of a value, that level needs to be
326 : // stored on the token stream. We can't do that at creation time
327 : // because the CSS parser (which creates the object) has no idea
328 : // about the stylesheet level, so we do it here instead, where
329 : // the rule walking will have just updated aRuleData.
330 9065 : if (val->GetUnit() == eCSSUnit_TokenStream) {
331 883 : val->GetTokenStreamValue()->mLevel = aRuleData->mLevel;
332 : }
333 9065 : MapSinglePropertyInto(iProp, val, target, aRuleData);
334 : }
335 : }
336 : }
337 : }
338 :
339 : const nsCSSValue*
340 102 : nsCSSCompressedDataBlock::ValueFor(nsCSSPropertyID aProperty) const
341 : {
342 102 : MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
343 : "Don't call for shorthands");
344 :
345 : // If we have no data for this struct, then return immediately.
346 : // This optimization should make us return most of the time, so we
347 : // have to worry much less (although still some) about the speed of
348 : // the rest of the function.
349 204 : if (!(nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]) &
350 102 : mStyleBits))
351 69 : return nullptr;
352 :
353 63 : for (uint32_t i = 0; i < mNumProps; i++) {
354 34 : if (PropertyAtIndex(i) == aProperty) {
355 4 : return ValueAtIndex(i);
356 : }
357 : }
358 :
359 29 : return nullptr;
360 : }
361 :
362 : bool
363 102 : nsCSSCompressedDataBlock::TryReplaceValue(nsCSSPropertyID aProperty,
364 : nsCSSExpandedDataBlock& aFromBlock,
365 : bool *aChanged)
366 : {
367 102 : nsCSSValue* newValue = aFromBlock.PropertyAt(aProperty);
368 102 : MOZ_ASSERT(newValue && newValue->GetUnit() != eCSSUnit_Null,
369 : "cannot replace with empty value");
370 :
371 102 : const nsCSSValue* oldValue = ValueFor(aProperty);
372 102 : if (!oldValue) {
373 98 : *aChanged = false;
374 98 : return false;
375 : }
376 :
377 4 : *aChanged = MoveValue(newValue, const_cast<nsCSSValue*>(oldValue));
378 4 : aFromBlock.ClearPropertyBit(aProperty);
379 4 : return true;
380 : }
381 :
382 : nsCSSCompressedDataBlock*
383 15 : nsCSSCompressedDataBlock::Clone() const
384 : {
385 : nsAutoPtr<nsCSSCompressedDataBlock>
386 30 : result(new(mNumProps) nsCSSCompressedDataBlock(mNumProps));
387 :
388 15 : result->mStyleBits = mStyleBits;
389 :
390 63 : for (uint32_t i = 0; i < mNumProps; i++) {
391 48 : result->SetPropertyAtIndex(i, PropertyAtIndex(i));
392 48 : result->CopyValueToIndex(i, ValueAtIndex(i));
393 : }
394 :
395 30 : return result.forget();
396 : }
397 :
398 282 : nsCSSCompressedDataBlock::~nsCSSCompressedDataBlock()
399 : {
400 167 : for (uint32_t i = 0; i < mNumProps; i++) {
401 : #ifdef DEBUG
402 26 : (void)PropertyAtIndex(i); // this checks the property is in range
403 : #endif
404 26 : const nsCSSValue* val = ValueAtIndex(i);
405 26 : MOZ_ASSERT(val->GetUnit() != eCSSUnit_Null, "oops");
406 26 : val->~nsCSSValue();
407 : }
408 141 : }
409 :
410 : /* static */ nsCSSCompressedDataBlock*
411 84 : nsCSSCompressedDataBlock::CreateEmptyBlock()
412 : {
413 84 : nsCSSCompressedDataBlock *result = new(0) nsCSSCompressedDataBlock(0);
414 84 : return result;
415 : }
416 :
417 : size_t
418 14 : nsCSSCompressedDataBlock::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
419 : {
420 14 : size_t n = aMallocSizeOf(this);
421 41 : for (uint32_t i = 0; i < mNumProps; i++) {
422 27 : n += ValueAtIndex(i)->SizeOfExcludingThis(aMallocSizeOf);
423 : }
424 14 : return n;
425 : }
426 :
427 : bool
428 0 : nsCSSCompressedDataBlock::HasDefaultBorderImageSlice() const
429 : {
430 : const nsCSSValueList *slice =
431 0 : ValueFor(eCSSProperty_border_image_slice)->GetListValue();
432 0 : return !slice->mNext &&
433 0 : slice->mValue.GetRectValue().AllSidesEqualTo(
434 0 : nsCSSValue(1.0f, eCSSUnit_Percent));
435 : }
436 :
437 : bool
438 0 : nsCSSCompressedDataBlock::HasDefaultBorderImageWidth() const
439 : {
440 : const nsCSSRect &width =
441 0 : ValueFor(eCSSProperty_border_image_width)->GetRectValue();
442 0 : return width.AllSidesEqualTo(nsCSSValue(1.0f, eCSSUnit_Number));
443 : }
444 :
445 : bool
446 0 : nsCSSCompressedDataBlock::HasDefaultBorderImageOutset() const
447 : {
448 : const nsCSSRect &outset =
449 0 : ValueFor(eCSSProperty_border_image_outset)->GetRectValue();
450 0 : return outset.AllSidesEqualTo(nsCSSValue(0.0f, eCSSUnit_Number));
451 : }
452 :
453 : bool
454 0 : nsCSSCompressedDataBlock::HasDefaultBorderImageRepeat() const
455 : {
456 : const nsCSSValuePair &repeat =
457 0 : ValueFor(eCSSProperty_border_image_repeat)->GetPairValue();
458 : return repeat.BothValuesEqualTo(
459 0 : nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, eCSSUnit_Enumerated));
460 : }
461 :
462 : /*****************************************************************************/
463 :
464 25 : nsCSSExpandedDataBlock::nsCSSExpandedDataBlock()
465 : {
466 25 : AssertInitialState();
467 25 : }
468 :
469 34 : nsCSSExpandedDataBlock::~nsCSSExpandedDataBlock()
470 : {
471 17 : AssertInitialState();
472 17 : }
473 :
474 : void
475 116 : nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
476 : bool aImportant)
477 : {
478 : /*
479 : * Save needless copying and allocation by copying the memory
480 : * corresponding to the stored data in the compressed block.
481 : */
482 150 : for (uint32_t i = 0; i < aBlock->mNumProps; i++) {
483 34 : nsCSSPropertyID iProp = aBlock->PropertyAtIndex(i);
484 34 : MOZ_ASSERT(!nsCSSProps::IsShorthand(iProp), "out of range");
485 34 : MOZ_ASSERT(!HasPropertyBit(iProp),
486 : "compressed block has property multiple times");
487 34 : SetPropertyBit(iProp);
488 34 : if (aImportant)
489 0 : SetImportantBit(iProp);
490 :
491 34 : const nsCSSValue* val = aBlock->ValueAtIndex(i);
492 34 : nsCSSValue* dest = PropertyAt(iProp);
493 34 : MOZ_ASSERT(val->GetUnit() != eCSSUnit_Null, "oops");
494 34 : MOZ_ASSERT(dest->GetUnit() == eCSSUnit_Null,
495 : "expanding into non-empty block");
496 : #ifdef NS_BUILD_REFCNT_LOGGING
497 34 : dest->~nsCSSValue();
498 : #endif
499 34 : memcpy(dest, val, sizeof(nsCSSValue));
500 : }
501 :
502 : // Set the number of properties to zero so that we don't destroy the
503 : // remnants of what we just copied.
504 116 : aBlock->SetNumPropsToZero();
505 116 : delete aBlock;
506 116 : }
507 :
508 : void
509 116 : nsCSSExpandedDataBlock::Expand(nsCSSCompressedDataBlock *aNormalBlock,
510 : nsCSSCompressedDataBlock *aImportantBlock)
511 : {
512 116 : MOZ_ASSERT(aNormalBlock, "unexpected null block");
513 116 : AssertInitialState();
514 :
515 116 : DoExpand(aNormalBlock, false);
516 116 : if (aImportantBlock) {
517 0 : DoExpand(aImportantBlock, true);
518 : }
519 116 : }
520 :
521 : void
522 3118 : nsCSSExpandedDataBlock::ComputeNumProps(uint32_t* aNumPropsNormal,
523 : uint32_t* aNumPropsImportant)
524 : {
525 3118 : *aNumPropsNormal = *aNumPropsImportant = 0;
526 21826 : for (size_t iHigh = 0; iHigh < nsCSSPropertyIDSet::kChunkCount; ++iHigh) {
527 18708 : if (!mPropertiesSet.HasPropertyInChunk(iHigh))
528 14045 : continue;
529 303095 : for (size_t iLow = 0; iLow < nsCSSPropertyIDSet::kBitsInChunk; ++iLow) {
530 298432 : if (!mPropertiesSet.HasPropertyAt(iHigh, iLow))
531 285498 : continue;
532 : #ifdef DEBUG
533 12934 : nsCSSPropertyID iProp = nsCSSPropertyIDSet::CSSPropertyAt(iHigh, iLow);
534 : #endif
535 12934 : MOZ_ASSERT(!nsCSSProps::IsShorthand(iProp), "out of range");
536 12934 : MOZ_ASSERT(PropertyAt(iProp)->GetUnit() != eCSSUnit_Null,
537 : "null value while computing size");
538 12934 : if (mPropertiesImportant.HasPropertyAt(iHigh, iLow))
539 1031 : (*aNumPropsImportant)++;
540 : else
541 11903 : (*aNumPropsNormal)++;
542 : }
543 : }
544 3118 : }
545 :
546 : void
547 3118 : nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock,
548 : nsCSSCompressedDataBlock **aImportantBlock,
549 : const nsTArray<uint32_t>& aOrder)
550 : {
551 6236 : nsAutoPtr<nsCSSCompressedDataBlock> result_normal, result_important;
552 3118 : uint32_t i_normal = 0, i_important = 0;
553 :
554 : uint32_t numPropsNormal, numPropsImportant;
555 3118 : ComputeNumProps(&numPropsNormal, &numPropsImportant);
556 :
557 : result_normal =
558 3118 : new(numPropsNormal) nsCSSCompressedDataBlock(numPropsNormal);
559 :
560 3118 : if (numPropsImportant != 0) {
561 : result_important =
562 303 : new(numPropsImportant) nsCSSCompressedDataBlock(numPropsImportant);
563 : } else {
564 2815 : result_important = nullptr;
565 : }
566 :
567 : /*
568 : * Save needless copying and allocation by copying the memory
569 : * corresponding to the stored data in the expanded block, and then
570 : * clearing the data in the expanded block.
571 : */
572 16126 : for (size_t i = 0; i < aOrder.Length(); i++) {
573 13008 : nsCSSPropertyID iProp = static_cast<nsCSSPropertyID>(aOrder[i]);
574 13008 : if (iProp >= eCSSProperty_COUNT) {
575 : // a custom property
576 74 : continue;
577 : }
578 12934 : MOZ_ASSERT(mPropertiesSet.HasProperty(iProp),
579 : "aOrder identifies a property not in the expanded "
580 : "data block");
581 12934 : MOZ_ASSERT(!nsCSSProps::IsShorthand(iProp), "out of range");
582 12934 : bool important = mPropertiesImportant.HasProperty(iProp);
583 : nsCSSCompressedDataBlock *result =
584 12934 : important ? result_important : result_normal;
585 12934 : uint32_t* ip = important ? &i_important : &i_normal;
586 12934 : nsCSSValue* val = PropertyAt(iProp);
587 12934 : MOZ_ASSERT(val->GetUnit() != eCSSUnit_Null,
588 : "Null value while compressing");
589 12934 : result->SetPropertyAtIndex(*ip, iProp);
590 12934 : result->RawCopyValueToIndex(*ip, val);
591 12934 : new (val) nsCSSValue();
592 12934 : (*ip)++;
593 12934 : result->mStyleBits |=
594 12934 : nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]);
595 : }
596 :
597 3118 : MOZ_ASSERT(numPropsNormal == i_normal, "bad numProps");
598 :
599 3118 : if (result_important) {
600 303 : MOZ_ASSERT(numPropsImportant == i_important, "bad numProps");
601 : }
602 :
603 : #ifdef DEBUG
604 : {
605 : // assert that we didn't have any other properties on this expanded data
606 : // block that we didn't find in aOrder
607 3118 : uint32_t numPropsInSet = 0;
608 21826 : for (size_t iHigh = 0; iHigh < nsCSSPropertyIDSet::kChunkCount; iHigh++) {
609 18708 : if (!mPropertiesSet.HasPropertyInChunk(iHigh)) {
610 14045 : continue;
611 : }
612 303095 : for (size_t iLow = 0; iLow < nsCSSPropertyIDSet::kBitsInChunk; iLow++) {
613 298432 : if (mPropertiesSet.HasPropertyAt(iHigh, iLow)) {
614 12934 : numPropsInSet++;
615 : }
616 : }
617 : }
618 3118 : MOZ_ASSERT(numPropsNormal + numPropsImportant == numPropsInSet,
619 : "aOrder missing properties from the expanded data block");
620 : }
621 : #endif
622 :
623 3118 : ClearSets();
624 3118 : AssertInitialState();
625 3118 : *aNormalBlock = result_normal.forget();
626 3118 : *aImportantBlock = result_important.forget();
627 3118 : }
628 :
629 : void
630 17626 : nsCSSExpandedDataBlock::AddLonghandProperty(nsCSSPropertyID aProperty,
631 : const nsCSSValue& aValue)
632 : {
633 17626 : MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
634 : "property out of range");
635 17626 : nsCSSValue& storage = *static_cast<nsCSSValue*>(PropertyAt(aProperty));
636 17626 : storage = aValue;
637 17626 : SetPropertyBit(aProperty);
638 17626 : }
639 :
640 : void
641 0 : nsCSSExpandedDataBlock::Clear()
642 : {
643 0 : for (size_t iHigh = 0; iHigh < nsCSSPropertyIDSet::kChunkCount; ++iHigh) {
644 0 : if (!mPropertiesSet.HasPropertyInChunk(iHigh))
645 0 : continue;
646 0 : for (size_t iLow = 0; iLow < nsCSSPropertyIDSet::kBitsInChunk; ++iLow) {
647 0 : if (!mPropertiesSet.HasPropertyAt(iHigh, iLow))
648 0 : continue;
649 0 : nsCSSPropertyID iProp = nsCSSPropertyIDSet::CSSPropertyAt(iHigh, iLow);
650 0 : ClearLonghandProperty(iProp);
651 : }
652 : }
653 :
654 0 : AssertInitialState();
655 0 : }
656 :
657 : void
658 883 : nsCSSExpandedDataBlock::ClearProperty(nsCSSPropertyID aPropID)
659 : {
660 883 : if (nsCSSProps::IsShorthand(aPropID)) {
661 22991 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(
662 : p, aPropID, CSSEnabledState::eIgnoreEnabledState) {
663 4222 : ClearLonghandProperty(*p);
664 : }
665 : } else {
666 177 : ClearLonghandProperty(aPropID);
667 : }
668 883 : }
669 :
670 : void
671 4400 : nsCSSExpandedDataBlock::ClearLonghandProperty(nsCSSPropertyID aPropID)
672 : {
673 4400 : MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID), "out of range");
674 :
675 4400 : ClearPropertyBit(aPropID);
676 4400 : ClearImportantBit(aPropID);
677 4400 : PropertyAt(aPropID)->Reset();
678 4400 : }
679 :
680 : bool
681 6571 : nsCSSExpandedDataBlock::TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock,
682 : nsCSSPropertyID aPropID,
683 : CSSEnabledState aEnabledState,
684 : bool aIsImportant,
685 : bool aOverrideImportant,
686 : bool aMustCallValueAppended,
687 : css::Declaration* aDeclaration,
688 : nsIDocument* aSheetDocument)
689 : {
690 6571 : if (!nsCSSProps::IsShorthand(aPropID)) {
691 5446 : return DoTransferFromBlock(aFromBlock, aPropID,
692 : aIsImportant, aOverrideImportant,
693 : aMustCallValueAppended, aDeclaration,
694 5446 : aSheetDocument);
695 : }
696 :
697 : // We can pass CSSEnabledState::eIgnore (here, and in ClearProperty
698 : // above) rather than a value corresponding to whether we're parsing
699 : // a UA style sheet or certified app because we assert in nsCSSProps::
700 : // AddRefTable that shorthand properties available in these contexts
701 : // also have all of their subproperties available in these contexts.
702 1125 : bool changed = false;
703 8671 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID, aEnabledState) {
704 7546 : changed |= DoTransferFromBlock(aFromBlock, *p,
705 : aIsImportant, aOverrideImportant,
706 : aMustCallValueAppended, aDeclaration,
707 : aSheetDocument);
708 : }
709 1125 : return changed;
710 : }
711 :
712 : bool
713 12992 : nsCSSExpandedDataBlock::DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock,
714 : nsCSSPropertyID aPropID,
715 : bool aIsImportant,
716 : bool aOverrideImportant,
717 : bool aMustCallValueAppended,
718 : css::Declaration* aDeclaration,
719 : nsIDocument* aSheetDocument)
720 : {
721 12992 : bool changed = false;
722 12992 : MOZ_ASSERT(aFromBlock.HasPropertyBit(aPropID), "oops");
723 12992 : if (aIsImportant) {
724 1031 : if (!HasImportantBit(aPropID))
725 1031 : changed = true;
726 1031 : SetImportantBit(aPropID);
727 : } else {
728 11961 : if (HasImportantBit(aPropID)) {
729 : // When parsing a declaration block, an !important declaration
730 : // is not overwritten by an ordinary declaration of the same
731 : // property later in the block. However, CSSOM manipulations
732 : // come through here too, and in that case we do want to
733 : // overwrite the property.
734 0 : if (!aOverrideImportant) {
735 0 : aFromBlock.ClearLonghandProperty(aPropID);
736 0 : return false;
737 : }
738 0 : changed = true;
739 0 : ClearImportantBit(aPropID);
740 : }
741 : }
742 :
743 12992 : if (aMustCallValueAppended || !HasPropertyBit(aPropID)) {
744 12992 : aDeclaration->ValueAppended(aPropID);
745 : }
746 :
747 12992 : if (aSheetDocument) {
748 6202 : UseCounter useCounter = nsCSSProps::UseCounterFor(aPropID);
749 6202 : if (useCounter != eUseCounter_UNKNOWN) {
750 71 : aSheetDocument->SetDocumentAndPageUseCounter(useCounter);
751 : }
752 : }
753 :
754 12992 : SetPropertyBit(aPropID);
755 12992 : aFromBlock.ClearPropertyBit(aPropID);
756 :
757 : /*
758 : * Save needless copying and allocation by calling the destructor in
759 : * the destination, copying memory directly, and then using placement
760 : * new.
761 : */
762 12992 : changed |= MoveValue(aFromBlock.PropertyAt(aPropID), PropertyAt(aPropID));
763 12992 : return changed;
764 : }
765 :
766 : void
767 883 : nsCSSExpandedDataBlock::MapRuleInfoInto(nsCSSPropertyID aPropID,
768 : nsRuleData* aRuleData) const
769 : {
770 883 : MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID));
771 :
772 883 : const nsCSSValue* src = PropertyAt(aPropID);
773 883 : MOZ_ASSERT(src->GetUnit() != eCSSUnit_Null);
774 :
775 883 : nsCSSPropertyID physicalProp = aPropID;
776 883 : if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_LOGICAL)) {
777 49 : EnsurePhysicalProperty(physicalProp, aRuleData);
778 : }
779 :
780 883 : nsCSSValue* dest = aRuleData->ValueFor(physicalProp);
781 883 : MOZ_ASSERT(dest->GetUnit() == eCSSUnit_TokenStream &&
782 : dest->GetTokenStreamValue()->mPropertyID == aPropID);
783 :
784 1766 : CSSVariableImageTable::ReplaceAll(aRuleData->mStyleContext, aPropID, [=] {
785 883 : MapSinglePropertyInto(physicalProp, src, dest, aRuleData);
786 2649 : });
787 883 : }
788 :
789 : #ifdef DEBUG
790 : void
791 21135 : nsCSSExpandedDataBlock::DoAssertInitialState()
792 : {
793 21135 : mPropertiesSet.AssertIsEmpty("not initial state");
794 21135 : mPropertiesImportant.AssertIsEmpty("not initial state");
795 :
796 6826605 : for (uint32_t i = 0; i < eCSSProperty_COUNT_no_shorthands; ++i) {
797 6805470 : nsCSSPropertyID prop = nsCSSPropertyID(i);
798 6805470 : MOZ_ASSERT(PropertyAt(prop)->GetUnit() == eCSSUnit_Null,
799 : "not initial state");
800 : }
801 21135 : }
802 : #endif
|