1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is nsCSSDataBlock.cpp.
16 : *
17 : * The Initial Developer of the Original Code is L. David Baron.
18 : * Portions created by the Initial Developer are Copyright (C) 2003
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * L. David Baron <dbaron@dbaron.org> (original author)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : * compact representation of the property-value pairs within a CSS
40 : * declaration, and the code for expanding and compacting it
41 : */
42 :
43 : #include "nsCSSDataBlock.h"
44 : #include "mozilla/css/Declaration.h"
45 : #include "nsRuleData.h"
46 : #include "nsStyleSet.h"
47 : #include "nsStyleContext.h"
48 :
49 : namespace css = mozilla::css;
50 :
51 : enum {
52 : CDBValueStorage_advance = sizeof(CDBValueStorage)
53 : };
54 :
55 : /*
56 : * Define a bunch of utility functions for getting the property or any
57 : * of the value types when the cursor is at the beginning of the storage
58 : * for the property-value pair. The versions taking a non-const cursor
59 : * argument return a reference so that the caller can assign into the
60 : * result.
61 : */
62 :
63 0 : inline nsCSSProperty& PropertyAtCursor(char *aCursor) {
64 0 : return *reinterpret_cast<nsCSSProperty*>(aCursor);
65 : }
66 :
67 0 : inline nsCSSProperty PropertyAtCursor(const char *aCursor) {
68 0 : return *reinterpret_cast<const nsCSSProperty*>(aCursor);
69 : }
70 :
71 0 : inline nsCSSValue* ValueAtCursor(char *aCursor) {
72 0 : return & reinterpret_cast<CDBValueStorage*>(aCursor)->value;
73 : }
74 :
75 0 : inline const nsCSSValue* ValueAtCursor(const char *aCursor) {
76 0 : return & reinterpret_cast<const CDBValueStorage*>(aCursor)->value;
77 : }
78 :
79 : /**
80 : * Does a fast move of aSource to aDest. The previous value in
81 : * aDest is cleanly destroyed, and aSource is cleared. Returns
82 : * true if, before the copy, the value at aSource compared unequal
83 : * to the value at aDest; false otherwise.
84 : */
85 : static bool
86 0 : MoveValue(nsCSSValue* aSource, nsCSSValue* aDest)
87 : {
88 0 : bool changed = (*aSource != *aDest);
89 0 : aDest->~nsCSSValue();
90 0 : memcpy(aDest, aSource, sizeof(nsCSSValue));
91 0 : new (aSource) nsCSSValue();
92 0 : return changed;
93 : }
94 :
95 : static bool
96 0 : ShouldIgnoreColors(nsRuleData *aRuleData)
97 : {
98 : return aRuleData->mLevel != nsStyleSet::eAgentSheet &&
99 : aRuleData->mLevel != nsStyleSet::eUserSheet &&
100 0 : !aRuleData->mPresContext->UseDocumentColors();
101 : }
102 :
103 : /**
104 : * Tries to call |nsCSSValue::StartImageLoad()| on an image source.
105 : * Image sources are specified by |url()| or |-moz-image-rect()| function.
106 : */
107 : static void
108 0 : TryToStartImageLoadOnValue(const nsCSSValue& aValue, nsIDocument* aDocument)
109 : {
110 0 : if (aValue.GetUnit() == eCSSUnit_URL) {
111 0 : aValue.StartImageLoad(aDocument);
112 : }
113 0 : else if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) {
114 0 : nsCSSValue::Array* arguments = aValue.GetArrayValue();
115 0 : NS_ABORT_IF_FALSE(arguments->Count() == 6, "unexpected num of arguments");
116 :
117 0 : const nsCSSValue& image = arguments->Item(1);
118 0 : if (image.GetUnit() == eCSSUnit_URL)
119 0 : image.StartImageLoad(aDocument);
120 : }
121 0 : }
122 :
123 : static void
124 0 : TryToStartImageLoad(const nsCSSValue& aValue, nsIDocument* aDocument,
125 : nsCSSProperty aProperty)
126 : {
127 0 : if (aValue.GetUnit() == eCSSUnit_List) {
128 0 : for (const nsCSSValueList* l = aValue.GetListValue(); l; l = l->mNext) {
129 0 : TryToStartImageLoad(l->mValue, aDocument, aProperty);
130 : }
131 0 : } else if (nsCSSProps::PropHasFlags(aProperty,
132 : CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0)) {
133 0 : if (aValue.GetUnit() == eCSSUnit_Array) {
134 0 : TryToStartImageLoadOnValue(aValue.GetArrayValue()->Item(0), aDocument);
135 : }
136 : } else {
137 0 : TryToStartImageLoadOnValue(aValue, aDocument);
138 : }
139 0 : }
140 :
141 : static inline bool
142 0 : ShouldStartImageLoads(nsRuleData *aRuleData, nsCSSProperty aProperty)
143 : {
144 : // Don't initiate image loads for if-visited styles. This is
145 : // important because:
146 : // (1) it's a waste of CPU and bandwidth
147 : // (2) in some cases we'd start the image load on a style change
148 : // where we wouldn't have started the load initially, which makes
149 : // which links are visited detectable to Web pages (see bug
150 : // 557287)
151 0 : return !aRuleData->mStyleContext->IsStyleIfVisited() &&
152 0 : nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_START_IMAGE_LOADS);
153 : }
154 :
155 : void
156 0 : nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const
157 : {
158 : // If we have no data for these structs, then return immediately.
159 : // This optimization should make us return most of the time, so we
160 : // have to worry much less (although still some) about the speed of
161 : // the rest of the function.
162 0 : if (!(aRuleData->mSIDs & mStyleBits))
163 0 : return;
164 :
165 0 : nsIDocument* doc = aRuleData->mPresContext->Document();
166 :
167 0 : const char* cursor = Block();
168 0 : const char* cursor_end = BlockEnd();
169 0 : while (cursor < cursor_end) {
170 0 : nsCSSProperty iProp = PropertyAtCursor(cursor);
171 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
172 0 : if (nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]) &
173 : aRuleData->mSIDs) {
174 0 : nsCSSValue* target = aRuleData->ValueFor(iProp);
175 0 : if (target->GetUnit() == eCSSUnit_Null) {
176 0 : const nsCSSValue *val = ValueAtCursor(cursor);
177 0 : NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops");
178 0 : if (ShouldStartImageLoads(aRuleData, iProp)) {
179 0 : TryToStartImageLoad(*val, doc, iProp);
180 : }
181 0 : *target = *val;
182 0 : if (nsCSSProps::PropHasFlags(iProp,
183 0 : CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED) &&
184 0 : ShouldIgnoreColors(aRuleData))
185 : {
186 0 : if (iProp == eCSSProperty_background_color) {
187 : // Force non-'transparent' background
188 : // colors to the user's default.
189 0 : if (target->IsNonTransparentColor()) {
190 : target->SetColorValue(aRuleData->mPresContext->
191 0 : DefaultBackgroundColor());
192 : }
193 : } else {
194 : // Ignore 'color', 'border-*-color', etc.
195 0 : *target = nsCSSValue();
196 : }
197 : }
198 : }
199 : }
200 0 : cursor += CDBValueStorage_advance;
201 : }
202 0 : NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");
203 : }
204 :
205 : const nsCSSValue*
206 0 : nsCSSCompressedDataBlock::ValueFor(nsCSSProperty aProperty) const
207 : {
208 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
209 : "Don't call for shorthands");
210 :
211 : // If we have no data for this struct, then return immediately.
212 : // This optimization should make us return most of the time, so we
213 : // have to worry much less (although still some) about the speed of
214 : // the rest of the function.
215 0 : if (!(nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]) &
216 0 : mStyleBits))
217 0 : return nsnull;
218 :
219 0 : const char* cursor = Block();
220 0 : const char* cursor_end = BlockEnd();
221 0 : while (cursor < cursor_end) {
222 0 : nsCSSProperty iProp = PropertyAtCursor(cursor);
223 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
224 0 : if (iProp == aProperty) {
225 0 : return ValueAtCursor(cursor);
226 : }
227 0 : cursor += CDBValueStorage_advance;
228 : }
229 0 : NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");
230 :
231 0 : return nsnull;
232 : }
233 :
234 : bool
235 0 : nsCSSCompressedDataBlock::TryReplaceValue(nsCSSProperty aProperty,
236 : nsCSSExpandedDataBlock& aFromBlock,
237 : bool *aChanged)
238 : {
239 0 : nsCSSValue* newValue = aFromBlock.PropertyAt(aProperty);
240 0 : NS_ABORT_IF_FALSE(newValue && newValue->GetUnit() != eCSSUnit_Null,
241 : "cannot replace with empty value");
242 :
243 0 : const nsCSSValue* oldValue = ValueFor(aProperty);
244 0 : if (!oldValue) {
245 0 : *aChanged = false;
246 0 : return false;
247 : }
248 :
249 0 : *aChanged = MoveValue(newValue, const_cast<nsCSSValue*>(oldValue));
250 0 : aFromBlock.ClearPropertyBit(aProperty);
251 0 : return true;
252 : }
253 :
254 : nsCSSCompressedDataBlock*
255 0 : nsCSSCompressedDataBlock::Clone() const
256 : {
257 0 : const char *cursor = Block(), *cursor_end = BlockEnd();
258 : char *result_cursor;
259 :
260 : nsAutoPtr<nsCSSCompressedDataBlock> result
261 0 : (new(cursor_end - cursor) nsCSSCompressedDataBlock());
262 0 : if (!result)
263 0 : return nsnull;
264 0 : result_cursor = result->Block();
265 :
266 0 : while (cursor < cursor_end) {
267 0 : nsCSSProperty iProp = PropertyAtCursor(cursor);
268 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
269 0 : PropertyAtCursor(result_cursor) = iProp;
270 :
271 0 : const nsCSSValue* val = ValueAtCursor(cursor);
272 0 : nsCSSValue *result_val = ValueAtCursor(result_cursor);
273 0 : new (result_val) nsCSSValue(*val);
274 0 : cursor += CDBValueStorage_advance;
275 0 : result_cursor += CDBValueStorage_advance;
276 : }
277 0 : NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");
278 :
279 0 : result->SetBlockEnd(result_cursor);
280 0 : result->mStyleBits = mStyleBits;
281 0 : NS_ABORT_IF_FALSE(result->DataSize() == DataSize(), "wrong size");
282 :
283 0 : return result.forget();
284 : }
285 :
286 0 : nsCSSCompressedDataBlock::~nsCSSCompressedDataBlock()
287 : {
288 0 : const char* cursor = Block();
289 0 : const char* cursor_end = BlockEnd();
290 0 : while (cursor < cursor_end) {
291 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(PropertyAtCursor(cursor)),
292 : "out of range");
293 :
294 0 : const nsCSSValue* val = ValueAtCursor(cursor);
295 0 : NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops");
296 0 : val->~nsCSSValue();
297 0 : cursor += CDBValueStorage_advance;
298 : }
299 0 : NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");
300 0 : }
301 :
302 : /* static */ nsCSSCompressedDataBlock*
303 0 : nsCSSCompressedDataBlock::CreateEmptyBlock()
304 : {
305 0 : nsCSSCompressedDataBlock *result = new(0) nsCSSCompressedDataBlock();
306 0 : result->SetBlockEnd(result->Block());
307 0 : return result;
308 : }
309 :
310 : size_t
311 0 : nsCSSCompressedDataBlock::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
312 : {
313 0 : size_t n = aMallocSizeOf(this);
314 :
315 0 : const char* cursor = Block();
316 0 : const char* cursor_end = BlockEnd();
317 0 : while (cursor < cursor_end) {
318 0 : n += ValueAtCursor(cursor)->SizeOfExcludingThis(aMallocSizeOf);
319 0 : cursor += CDBValueStorage_advance;
320 : }
321 0 : return n;
322 : }
323 :
324 : /*****************************************************************************/
325 :
326 24 : nsCSSExpandedDataBlock::nsCSSExpandedDataBlock()
327 : {
328 24 : AssertInitialState();
329 24 : }
330 :
331 5688 : nsCSSExpandedDataBlock::~nsCSSExpandedDataBlock()
332 : {
333 24 : AssertInitialState();
334 5664 : }
335 :
336 : void
337 0 : nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
338 : bool aImportant)
339 : {
340 : /*
341 : * Save needless copying and allocation by copying the memory
342 : * corresponding to the stored data in the compressed block.
343 : */
344 0 : const char* cursor = aBlock->Block();
345 0 : const char* cursor_end = aBlock->BlockEnd();
346 0 : while (cursor < cursor_end) {
347 0 : nsCSSProperty iProp = PropertyAtCursor(cursor);
348 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
349 0 : NS_ABORT_IF_FALSE(!HasPropertyBit(iProp),
350 : "compressed block has property multiple times");
351 0 : SetPropertyBit(iProp);
352 0 : if (aImportant)
353 0 : SetImportantBit(iProp);
354 :
355 0 : const nsCSSValue* val = ValueAtCursor(cursor);
356 0 : nsCSSValue* dest = PropertyAt(iProp);
357 0 : NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops");
358 0 : NS_ABORT_IF_FALSE(dest->GetUnit() == eCSSUnit_Null,
359 : "expanding into non-empty block");
360 : #ifdef NS_BUILD_REFCNT_LOGGING
361 0 : dest->~nsCSSValue();
362 : #endif
363 0 : memcpy(dest, val, sizeof(nsCSSValue));
364 0 : cursor += CDBValueStorage_advance;
365 : }
366 0 : NS_ABORT_IF_FALSE(cursor == cursor_end, "inconsistent data");
367 :
368 : // Don't destroy remnants of what we just copied
369 0 : aBlock->SetBlockEnd(aBlock->Block());
370 0 : delete aBlock;
371 0 : }
372 :
373 : void
374 0 : nsCSSExpandedDataBlock::Expand(nsCSSCompressedDataBlock *aNormalBlock,
375 : nsCSSCompressedDataBlock *aImportantBlock)
376 : {
377 0 : NS_ABORT_IF_FALSE(aNormalBlock, "unexpected null block");
378 0 : AssertInitialState();
379 :
380 0 : DoExpand(aNormalBlock, false);
381 0 : if (aImportantBlock) {
382 0 : DoExpand(aImportantBlock, true);
383 : }
384 0 : }
385 :
386 : nsCSSExpandedDataBlock::ComputeSizeResult
387 0 : nsCSSExpandedDataBlock::ComputeSize()
388 : {
389 0 : ComputeSizeResult result = {0, 0};
390 0 : for (size_t iHigh = 0; iHigh < nsCSSPropertySet::kChunkCount; ++iHigh) {
391 0 : if (!mPropertiesSet.HasPropertyInChunk(iHigh))
392 0 : continue;
393 0 : for (size_t iLow = 0; iLow < nsCSSPropertySet::kBitsInChunk; ++iLow) {
394 0 : if (!mPropertiesSet.HasPropertyAt(iHigh, iLow))
395 0 : continue;
396 : #ifdef DEBUG
397 0 : nsCSSProperty iProp = nsCSSPropertySet::CSSPropertyAt(iHigh, iLow);
398 : #endif
399 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
400 0 : NS_ABORT_IF_FALSE(PropertyAt(iProp)->GetUnit() != eCSSUnit_Null,
401 : "null value while computing size");
402 0 : if (mPropertiesImportant.HasPropertyAt(iHigh, iLow))
403 0 : result.important += CDBValueStorage_advance;
404 : else
405 0 : result.normal += CDBValueStorage_advance;
406 : }
407 : }
408 : return result;
409 : }
410 :
411 : void
412 0 : nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock,
413 : nsCSSCompressedDataBlock **aImportantBlock)
414 : {
415 0 : nsAutoPtr<nsCSSCompressedDataBlock> result_normal, result_important;
416 : char *cursor_normal, *cursor_important;
417 :
418 0 : ComputeSizeResult size = ComputeSize();
419 :
420 0 : result_normal = new(size.normal) nsCSSCompressedDataBlock();
421 0 : cursor_normal = result_normal->Block();
422 :
423 0 : if (size.important != 0) {
424 0 : result_important = new(size.important) nsCSSCompressedDataBlock();
425 0 : cursor_important = result_important->Block();
426 : } else {
427 0 : result_important = nsnull;
428 0 : cursor_important = nsnull;
429 : }
430 :
431 : /*
432 : * Save needless copying and allocation by copying the memory
433 : * corresponding to the stored data in the expanded block, and then
434 : * clearing the data in the expanded block.
435 : */
436 0 : for (size_t iHigh = 0; iHigh < nsCSSPropertySet::kChunkCount; ++iHigh) {
437 0 : if (!mPropertiesSet.HasPropertyInChunk(iHigh))
438 0 : continue;
439 0 : for (size_t iLow = 0; iLow < nsCSSPropertySet::kBitsInChunk; ++iLow) {
440 0 : if (!mPropertiesSet.HasPropertyAt(iHigh, iLow))
441 0 : continue;
442 0 : nsCSSProperty iProp = nsCSSPropertySet::CSSPropertyAt(iHigh, iLow);
443 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range");
444 : bool important =
445 0 : mPropertiesImportant.HasPropertyAt(iHigh, iLow);
446 0 : char *&cursor = important ? cursor_important : cursor_normal;
447 : nsCSSCompressedDataBlock *result =
448 0 : important ? result_important : result_normal;
449 0 : nsCSSValue* val = PropertyAt(iProp);
450 0 : NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null,
451 : "Null value while compressing");
452 : CDBValueStorage *storage =
453 0 : reinterpret_cast<CDBValueStorage*>(cursor);
454 0 : storage->property = iProp;
455 0 : memcpy(&storage->value, val, sizeof(nsCSSValue));
456 0 : new (val) nsCSSValue();
457 0 : cursor += CDBValueStorage_advance;
458 : result->mStyleBits |=
459 0 : nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]);
460 : }
461 : }
462 :
463 0 : result_normal->SetBlockEnd(cursor_normal);
464 0 : NS_ABORT_IF_FALSE(result_normal->DataSize() == ptrdiff_t(size.normal),
465 : "size miscalculation");
466 :
467 0 : if (result_important) {
468 0 : result_important->SetBlockEnd(cursor_important);
469 0 : NS_ABORT_IF_FALSE(result_important->DataSize() ==
470 : ptrdiff_t(size.important),
471 : "size miscalculation");
472 : }
473 :
474 0 : ClearSets();
475 0 : AssertInitialState();
476 0 : *aNormalBlock = result_normal.forget();
477 0 : *aImportantBlock = result_important.forget();
478 0 : }
479 :
480 : void
481 0 : nsCSSExpandedDataBlock::AddLonghandProperty(nsCSSProperty aProperty,
482 : const nsCSSValue& aValue)
483 : {
484 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
485 : "property out of range");
486 0 : nsCSSValue& storage = *static_cast<nsCSSValue*>(PropertyAt(aProperty));
487 0 : storage = aValue;
488 0 : SetPropertyBit(aProperty);
489 0 : }
490 :
491 : void
492 0 : nsCSSExpandedDataBlock::Clear()
493 : {
494 0 : for (size_t iHigh = 0; iHigh < nsCSSPropertySet::kChunkCount; ++iHigh) {
495 0 : if (!mPropertiesSet.HasPropertyInChunk(iHigh))
496 0 : continue;
497 0 : for (size_t iLow = 0; iLow < nsCSSPropertySet::kBitsInChunk; ++iLow) {
498 0 : if (!mPropertiesSet.HasPropertyAt(iHigh, iLow))
499 0 : continue;
500 0 : nsCSSProperty iProp = nsCSSPropertySet::CSSPropertyAt(iHigh, iLow);
501 0 : ClearLonghandProperty(iProp);
502 : }
503 : }
504 :
505 0 : AssertInitialState();
506 0 : }
507 :
508 : void
509 0 : nsCSSExpandedDataBlock::ClearProperty(nsCSSProperty aPropID)
510 : {
511 0 : if (nsCSSProps::IsShorthand(aPropID)) {
512 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
513 0 : ClearLonghandProperty(*p);
514 : }
515 : } else {
516 0 : ClearLonghandProperty(aPropID);
517 : }
518 0 : }
519 :
520 : void
521 0 : nsCSSExpandedDataBlock::ClearLonghandProperty(nsCSSProperty aPropID)
522 : {
523 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aPropID), "out of range");
524 :
525 0 : ClearPropertyBit(aPropID);
526 0 : ClearImportantBit(aPropID);
527 0 : PropertyAt(aPropID)->Reset();
528 0 : }
529 :
530 : bool
531 0 : nsCSSExpandedDataBlock::TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock,
532 : nsCSSProperty aPropID,
533 : bool aIsImportant,
534 : bool aOverrideImportant,
535 : bool aMustCallValueAppended,
536 : css::Declaration* aDeclaration)
537 : {
538 0 : if (!nsCSSProps::IsShorthand(aPropID)) {
539 : return DoTransferFromBlock(aFromBlock, aPropID,
540 : aIsImportant, aOverrideImportant,
541 0 : aMustCallValueAppended, aDeclaration);
542 : }
543 :
544 0 : bool changed = false;
545 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) {
546 : changed |= DoTransferFromBlock(aFromBlock, *p,
547 : aIsImportant, aOverrideImportant,
548 0 : aMustCallValueAppended, aDeclaration);
549 : }
550 0 : return changed;
551 : }
552 :
553 : bool
554 0 : nsCSSExpandedDataBlock::DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock,
555 : nsCSSProperty aPropID,
556 : bool aIsImportant,
557 : bool aOverrideImportant,
558 : bool aMustCallValueAppended,
559 : css::Declaration* aDeclaration)
560 : {
561 0 : bool changed = false;
562 0 : NS_ABORT_IF_FALSE(aFromBlock.HasPropertyBit(aPropID), "oops");
563 0 : if (aIsImportant) {
564 0 : if (!HasImportantBit(aPropID))
565 0 : changed = true;
566 0 : SetImportantBit(aPropID);
567 : } else {
568 0 : if (HasImportantBit(aPropID)) {
569 : // When parsing a declaration block, an !important declaration
570 : // is not overwritten by an ordinary declaration of the same
571 : // property later in the block. However, CSSOM manipulations
572 : // come through here too, and in that case we do want to
573 : // overwrite the property.
574 0 : if (!aOverrideImportant) {
575 0 : aFromBlock.ClearLonghandProperty(aPropID);
576 0 : return false;
577 : }
578 0 : changed = true;
579 0 : ClearImportantBit(aPropID);
580 : }
581 : }
582 :
583 0 : if (aMustCallValueAppended || !HasPropertyBit(aPropID)) {
584 0 : aDeclaration->ValueAppended(aPropID);
585 : }
586 :
587 0 : SetPropertyBit(aPropID);
588 0 : aFromBlock.ClearPropertyBit(aPropID);
589 :
590 : /*
591 : * Save needless copying and allocation by calling the destructor in
592 : * the destination, copying memory directly, and then using placement
593 : * new.
594 : */
595 0 : changed |= MoveValue(aFromBlock.PropertyAt(aPropID), PropertyAt(aPropID));
596 0 : return changed;
597 : }
598 :
599 : #ifdef DEBUG
600 : void
601 72 : nsCSSExpandedDataBlock::DoAssertInitialState()
602 : {
603 72 : mPropertiesSet.AssertIsEmpty("not initial state");
604 72 : mPropertiesImportant.AssertIsEmpty("not initial state");
605 :
606 16920 : for (PRUint32 i = 0; i < eCSSProperty_COUNT_no_shorthands; ++i) {
607 16848 : nsCSSProperty prop = nsCSSProperty(i);
608 16848 : NS_ABORT_IF_FALSE(PropertyAt(prop)->GetUnit() == eCSSUnit_Null,
609 : "not initial state");
610 : }
611 72 : }
612 : #endif
|