layout/style/nsCSSDataBlock.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial