Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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 |