michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * compact representation of the property-value pairs within a CSS michael@0: * declaration, and the code for expanding and compacting it michael@0: */ michael@0: michael@0: #include "nsCSSDataBlock.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/css/Declaration.h" michael@0: #include "mozilla/css/ImageLoader.h" michael@0: #include "nsRuleData.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsIDocument.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /** michael@0: * Does a fast move of aSource to aDest. The previous value in michael@0: * aDest is cleanly destroyed, and aSource is cleared. Returns michael@0: * true if, before the copy, the value at aSource compared unequal michael@0: * to the value at aDest; false otherwise. michael@0: */ michael@0: static bool michael@0: MoveValue(nsCSSValue* aSource, nsCSSValue* aDest) michael@0: { michael@0: bool changed = (*aSource != *aDest); michael@0: aDest->~nsCSSValue(); michael@0: memcpy(aDest, aSource, sizeof(nsCSSValue)); michael@0: new (aSource) nsCSSValue(); michael@0: return changed; michael@0: } michael@0: michael@0: static bool michael@0: ShouldIgnoreColors(nsRuleData *aRuleData) michael@0: { michael@0: return aRuleData->mLevel != nsStyleSet::eAgentSheet && michael@0: aRuleData->mLevel != nsStyleSet::eUserSheet && michael@0: !aRuleData->mPresContext->UseDocumentColors(); michael@0: } michael@0: michael@0: /** michael@0: * Tries to call |nsCSSValue::StartImageLoad()| on an image source. michael@0: * Image sources are specified by |url()| or |-moz-image-rect()| function. michael@0: */ michael@0: static void michael@0: TryToStartImageLoadOnValue(const nsCSSValue& aValue, nsIDocument* aDocument, michael@0: nsCSSValueTokenStream* aTokenStream) michael@0: { michael@0: MOZ_ASSERT(aDocument); michael@0: michael@0: if (aValue.GetUnit() == eCSSUnit_URL) { michael@0: aValue.StartImageLoad(aDocument); michael@0: if (aTokenStream) { michael@0: aTokenStream->mImageValues.PutEntry(aValue.GetImageStructValue()); michael@0: } michael@0: } michael@0: else if (aValue.GetUnit() == eCSSUnit_Image) { michael@0: // If we already have a request, see if this document needs to clone it. michael@0: imgIRequest* request = aValue.GetImageValue(nullptr); michael@0: michael@0: if (request) { michael@0: mozilla::css::ImageValue* imageValue = aValue.GetImageStructValue(); michael@0: aDocument->StyleImageLoader()->MaybeRegisterCSSImage(imageValue); michael@0: if (aTokenStream) { michael@0: aTokenStream->mImageValues.PutEntry(imageValue); michael@0: } michael@0: } michael@0: } michael@0: else if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) { michael@0: nsCSSValue::Array* arguments = aValue.GetArrayValue(); michael@0: NS_ABORT_IF_FALSE(arguments->Count() == 6, "unexpected num of arguments"); michael@0: michael@0: const nsCSSValue& image = arguments->Item(1); michael@0: TryToStartImageLoadOnValue(image, aDocument, aTokenStream); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: TryToStartImageLoad(const nsCSSValue& aValue, nsIDocument* aDocument, michael@0: nsCSSProperty aProperty, michael@0: nsCSSValueTokenStream* aTokenStream) michael@0: { michael@0: if (aValue.GetUnit() == eCSSUnit_List) { michael@0: for (const nsCSSValueList* l = aValue.GetListValue(); l; l = l->mNext) { michael@0: TryToStartImageLoad(l->mValue, aDocument, aProperty, aTokenStream); michael@0: } michael@0: } else if (nsCSSProps::PropHasFlags(aProperty, michael@0: CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0)) { michael@0: if (aValue.GetUnit() == eCSSUnit_Array) { michael@0: TryToStartImageLoadOnValue(aValue.GetArrayValue()->Item(0), aDocument, michael@0: aTokenStream); michael@0: } michael@0: } else { michael@0: TryToStartImageLoadOnValue(aValue, aDocument, aTokenStream); michael@0: } michael@0: } michael@0: michael@0: static inline bool michael@0: ShouldStartImageLoads(nsRuleData *aRuleData, nsCSSProperty aProperty) michael@0: { michael@0: // Don't initiate image loads for if-visited styles. This is michael@0: // important because: michael@0: // (1) it's a waste of CPU and bandwidth michael@0: // (2) in some cases we'd start the image load on a style change michael@0: // where we wouldn't have started the load initially, which makes michael@0: // which links are visited detectable to Web pages (see bug michael@0: // 557287) michael@0: return !aRuleData->mStyleContext->IsStyleIfVisited() && michael@0: nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_START_IMAGE_LOADS); michael@0: } michael@0: michael@0: static void michael@0: MapSinglePropertyInto(nsCSSProperty aProp, michael@0: const nsCSSValue* aValue, michael@0: nsCSSValue* aTarget, michael@0: nsRuleData* aRuleData) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue->GetUnit() != eCSSUnit_Null, "oops"); michael@0: michael@0: // Although aTarget is the nsCSSValue we are going to write into, michael@0: // we also look at its value before writing into it. This is done michael@0: // when aTarget is a token stream value, which is the case when we michael@0: // have just re-parsed a property that had a variable reference (in michael@0: // nsCSSParser::ParsePropertyWithVariableReferences). TryToStartImageLoad michael@0: // then records any resulting ImageValue objects on the michael@0: // nsCSSValueTokenStream object we found on aTarget. See the comment michael@0: // above nsCSSValueTokenStream::mImageValues for why. michael@0: NS_ABORT_IF_FALSE(aTarget->GetUnit() == eCSSUnit_TokenStream || michael@0: aTarget->GetUnit() == eCSSUnit_Null, michael@0: "aTarget must only be a token stream (when re-parsing " michael@0: "properties with variable references) or null"); michael@0: michael@0: nsCSSValueTokenStream* tokenStream = michael@0: aTarget->GetUnit() == eCSSUnit_TokenStream ? michael@0: aTarget->GetTokenStreamValue() : michael@0: nullptr; michael@0: michael@0: if (ShouldStartImageLoads(aRuleData, aProp)) { michael@0: nsIDocument* doc = aRuleData->mPresContext->Document(); michael@0: TryToStartImageLoad(*aValue, doc, aProp, tokenStream); michael@0: } michael@0: *aTarget = *aValue; michael@0: if (nsCSSProps::PropHasFlags(aProp, michael@0: CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED) && michael@0: ShouldIgnoreColors(aRuleData)) michael@0: { michael@0: if (aProp == eCSSProperty_background_color) { michael@0: // Force non-'transparent' background michael@0: // colors to the user's default. michael@0: if (aTarget->IsNonTransparentColor()) { michael@0: aTarget->SetColorValue(aRuleData->mPresContext-> michael@0: DefaultBackgroundColor()); michael@0: } michael@0: } else { michael@0: // Ignore 'color', 'border-*-color', etc. michael@0: *aTarget = nsCSSValue(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const michael@0: { michael@0: // If we have no data for these structs, then return immediately. michael@0: // This optimization should make us return most of the time, so we michael@0: // have to worry much less (although still some) about the speed of michael@0: // the rest of the function. michael@0: if (!(aRuleData->mSIDs & mStyleBits)) michael@0: return; michael@0: michael@0: for (uint32_t i = 0; i < mNumProps; i++) { michael@0: nsCSSProperty iProp = PropertyAtIndex(i); michael@0: if (nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]) & michael@0: aRuleData->mSIDs) { michael@0: nsCSSValue* target = aRuleData->ValueFor(iProp); michael@0: if (target->GetUnit() == eCSSUnit_Null) { michael@0: const nsCSSValue *val = ValueAtIndex(i); michael@0: MapSinglePropertyInto(iProp, val, target, aRuleData); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: const nsCSSValue* michael@0: nsCSSCompressedDataBlock::ValueFor(nsCSSProperty aProperty) const michael@0: { michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty), michael@0: "Don't call for shorthands"); michael@0: michael@0: // If we have no data for this struct, then return immediately. michael@0: // This optimization should make us return most of the time, so we michael@0: // have to worry much less (although still some) about the speed of michael@0: // the rest of the function. michael@0: if (!(nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]) & michael@0: mStyleBits)) michael@0: return nullptr; michael@0: michael@0: for (uint32_t i = 0; i < mNumProps; i++) { michael@0: if (PropertyAtIndex(i) == aProperty) { michael@0: return ValueAtIndex(i); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsCSSCompressedDataBlock::TryReplaceValue(nsCSSProperty aProperty, michael@0: nsCSSExpandedDataBlock& aFromBlock, michael@0: bool *aChanged) michael@0: { michael@0: nsCSSValue* newValue = aFromBlock.PropertyAt(aProperty); michael@0: NS_ABORT_IF_FALSE(newValue && newValue->GetUnit() != eCSSUnit_Null, michael@0: "cannot replace with empty value"); michael@0: michael@0: const nsCSSValue* oldValue = ValueFor(aProperty); michael@0: if (!oldValue) { michael@0: *aChanged = false; michael@0: return false; michael@0: } michael@0: michael@0: *aChanged = MoveValue(newValue, const_cast(oldValue)); michael@0: aFromBlock.ClearPropertyBit(aProperty); michael@0: return true; michael@0: } michael@0: michael@0: nsCSSCompressedDataBlock* michael@0: nsCSSCompressedDataBlock::Clone() const michael@0: { michael@0: nsAutoPtr michael@0: result(new(mNumProps) nsCSSCompressedDataBlock(mNumProps)); michael@0: michael@0: result->mStyleBits = mStyleBits; michael@0: michael@0: for (uint32_t i = 0; i < mNumProps; i++) { michael@0: result->SetPropertyAtIndex(i, PropertyAtIndex(i)); michael@0: result->CopyValueToIndex(i, ValueAtIndex(i)); michael@0: } michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: nsCSSCompressedDataBlock::~nsCSSCompressedDataBlock() michael@0: { michael@0: for (uint32_t i = 0; i < mNumProps; i++) { michael@0: #ifdef DEBUG michael@0: (void)PropertyAtIndex(i); // this checks the property is in range michael@0: #endif michael@0: const nsCSSValue* val = ValueAtIndex(i); michael@0: NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops"); michael@0: val->~nsCSSValue(); michael@0: } michael@0: } michael@0: michael@0: /* static */ nsCSSCompressedDataBlock* michael@0: nsCSSCompressedDataBlock::CreateEmptyBlock() michael@0: { michael@0: nsCSSCompressedDataBlock *result = new(0) nsCSSCompressedDataBlock(0); michael@0: return result; michael@0: } michael@0: michael@0: size_t michael@0: nsCSSCompressedDataBlock::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: for (uint32_t i = 0; i < mNumProps; i++) { michael@0: n += ValueAtIndex(i)->SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: bool michael@0: nsCSSCompressedDataBlock::HasDefaultBorderImageSlice() const michael@0: { michael@0: const nsCSSValueList *slice = michael@0: ValueFor(eCSSProperty_border_image_slice)->GetListValue(); michael@0: return !slice->mNext && michael@0: slice->mValue.GetRectValue().AllSidesEqualTo( michael@0: nsCSSValue(1.0f, eCSSUnit_Percent)); michael@0: } michael@0: michael@0: bool michael@0: nsCSSCompressedDataBlock::HasDefaultBorderImageWidth() const michael@0: { michael@0: const nsCSSRect &width = michael@0: ValueFor(eCSSProperty_border_image_width)->GetRectValue(); michael@0: return width.AllSidesEqualTo(nsCSSValue(1.0f, eCSSUnit_Number)); michael@0: } michael@0: michael@0: bool michael@0: nsCSSCompressedDataBlock::HasDefaultBorderImageOutset() const michael@0: { michael@0: const nsCSSRect &outset = michael@0: ValueFor(eCSSProperty_border_image_outset)->GetRectValue(); michael@0: return outset.AllSidesEqualTo(nsCSSValue(0.0f, eCSSUnit_Number)); michael@0: } michael@0: michael@0: bool michael@0: nsCSSCompressedDataBlock::HasDefaultBorderImageRepeat() const michael@0: { michael@0: const nsCSSValuePair &repeat = michael@0: ValueFor(eCSSProperty_border_image_repeat)->GetPairValue(); michael@0: return repeat.BothValuesEqualTo( michael@0: nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, eCSSUnit_Enumerated)); michael@0: } michael@0: michael@0: /*****************************************************************************/ michael@0: michael@0: nsCSSExpandedDataBlock::nsCSSExpandedDataBlock() michael@0: { michael@0: AssertInitialState(); michael@0: } michael@0: michael@0: nsCSSExpandedDataBlock::~nsCSSExpandedDataBlock() michael@0: { michael@0: AssertInitialState(); michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock, michael@0: bool aImportant) michael@0: { michael@0: /* michael@0: * Save needless copying and allocation by copying the memory michael@0: * corresponding to the stored data in the compressed block. michael@0: */ michael@0: for (uint32_t i = 0; i < aBlock->mNumProps; i++) { michael@0: nsCSSProperty iProp = aBlock->PropertyAtIndex(i); michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range"); michael@0: NS_ABORT_IF_FALSE(!HasPropertyBit(iProp), michael@0: "compressed block has property multiple times"); michael@0: SetPropertyBit(iProp); michael@0: if (aImportant) michael@0: SetImportantBit(iProp); michael@0: michael@0: const nsCSSValue* val = aBlock->ValueAtIndex(i); michael@0: nsCSSValue* dest = PropertyAt(iProp); michael@0: NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, "oops"); michael@0: NS_ABORT_IF_FALSE(dest->GetUnit() == eCSSUnit_Null, michael@0: "expanding into non-empty block"); michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: dest->~nsCSSValue(); michael@0: #endif michael@0: memcpy(dest, val, sizeof(nsCSSValue)); michael@0: } michael@0: michael@0: // Set the number of properties to zero so that we don't destroy the michael@0: // remnants of what we just copied. michael@0: aBlock->SetNumPropsToZero(); michael@0: delete aBlock; michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::Expand(nsCSSCompressedDataBlock *aNormalBlock, michael@0: nsCSSCompressedDataBlock *aImportantBlock) michael@0: { michael@0: NS_ABORT_IF_FALSE(aNormalBlock, "unexpected null block"); michael@0: AssertInitialState(); michael@0: michael@0: DoExpand(aNormalBlock, false); michael@0: if (aImportantBlock) { michael@0: DoExpand(aImportantBlock, true); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::ComputeNumProps(uint32_t* aNumPropsNormal, michael@0: uint32_t* aNumPropsImportant) michael@0: { michael@0: *aNumPropsNormal = *aNumPropsImportant = 0; michael@0: for (size_t iHigh = 0; iHigh < nsCSSPropertySet::kChunkCount; ++iHigh) { michael@0: if (!mPropertiesSet.HasPropertyInChunk(iHigh)) michael@0: continue; michael@0: for (size_t iLow = 0; iLow < nsCSSPropertySet::kBitsInChunk; ++iLow) { michael@0: if (!mPropertiesSet.HasPropertyAt(iHigh, iLow)) michael@0: continue; michael@0: #ifdef DEBUG michael@0: nsCSSProperty iProp = nsCSSPropertySet::CSSPropertyAt(iHigh, iLow); michael@0: #endif michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range"); michael@0: NS_ABORT_IF_FALSE(PropertyAt(iProp)->GetUnit() != eCSSUnit_Null, michael@0: "null value while computing size"); michael@0: if (mPropertiesImportant.HasPropertyAt(iHigh, iLow)) michael@0: (*aNumPropsImportant)++; michael@0: else michael@0: (*aNumPropsNormal)++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock, michael@0: nsCSSCompressedDataBlock **aImportantBlock) michael@0: { michael@0: nsAutoPtr result_normal, result_important; michael@0: uint32_t i_normal = 0, i_important = 0; michael@0: michael@0: uint32_t numPropsNormal, numPropsImportant; michael@0: ComputeNumProps(&numPropsNormal, &numPropsImportant); michael@0: michael@0: result_normal = michael@0: new(numPropsNormal) nsCSSCompressedDataBlock(numPropsNormal); michael@0: michael@0: if (numPropsImportant != 0) { michael@0: result_important = michael@0: new(numPropsImportant) nsCSSCompressedDataBlock(numPropsImportant); michael@0: } else { michael@0: result_important = nullptr; michael@0: } michael@0: michael@0: /* michael@0: * Save needless copying and allocation by copying the memory michael@0: * corresponding to the stored data in the expanded block, and then michael@0: * clearing the data in the expanded block. michael@0: */ michael@0: for (size_t iHigh = 0; iHigh < nsCSSPropertySet::kChunkCount; ++iHigh) { michael@0: if (!mPropertiesSet.HasPropertyInChunk(iHigh)) michael@0: continue; michael@0: for (size_t iLow = 0; iLow < nsCSSPropertySet::kBitsInChunk; ++iLow) { michael@0: if (!mPropertiesSet.HasPropertyAt(iHigh, iLow)) michael@0: continue; michael@0: nsCSSProperty iProp = nsCSSPropertySet::CSSPropertyAt(iHigh, iLow); michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(iProp), "out of range"); michael@0: bool important = michael@0: mPropertiesImportant.HasPropertyAt(iHigh, iLow); michael@0: nsCSSCompressedDataBlock *result = michael@0: important ? result_important : result_normal; michael@0: uint32_t* ip = important ? &i_important : &i_normal; michael@0: nsCSSValue* val = PropertyAt(iProp); michael@0: NS_ABORT_IF_FALSE(val->GetUnit() != eCSSUnit_Null, michael@0: "Null value while compressing"); michael@0: result->SetPropertyAtIndex(*ip, iProp); michael@0: result->RawCopyValueToIndex(*ip, val); michael@0: new (val) nsCSSValue(); michael@0: (*ip)++; michael@0: result->mStyleBits |= michael@0: nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]); michael@0: } michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(numPropsNormal == i_normal, "bad numProps"); michael@0: michael@0: if (result_important) { michael@0: NS_ABORT_IF_FALSE(numPropsImportant == i_important, "bad numProps"); michael@0: } michael@0: michael@0: ClearSets(); michael@0: AssertInitialState(); michael@0: *aNormalBlock = result_normal.forget(); michael@0: *aImportantBlock = result_important.forget(); michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::AddLonghandProperty(nsCSSProperty aProperty, michael@0: const nsCSSValue& aValue) michael@0: { michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty), michael@0: "property out of range"); michael@0: nsCSSValue& storage = *static_cast(PropertyAt(aProperty)); michael@0: storage = aValue; michael@0: SetPropertyBit(aProperty); michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::Clear() michael@0: { michael@0: for (size_t iHigh = 0; iHigh < nsCSSPropertySet::kChunkCount; ++iHigh) { michael@0: if (!mPropertiesSet.HasPropertyInChunk(iHigh)) michael@0: continue; michael@0: for (size_t iLow = 0; iLow < nsCSSPropertySet::kBitsInChunk; ++iLow) { michael@0: if (!mPropertiesSet.HasPropertyAt(iHigh, iLow)) michael@0: continue; michael@0: nsCSSProperty iProp = nsCSSPropertySet::CSSPropertyAt(iHigh, iLow); michael@0: ClearLonghandProperty(iProp); michael@0: } michael@0: } michael@0: michael@0: AssertInitialState(); michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::ClearProperty(nsCSSProperty aPropID) michael@0: { michael@0: if (nsCSSProps::IsShorthand(aPropID)) { michael@0: CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) { michael@0: ClearLonghandProperty(*p); michael@0: } michael@0: } else { michael@0: ClearLonghandProperty(aPropID); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::ClearLonghandProperty(nsCSSProperty aPropID) michael@0: { michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aPropID), "out of range"); michael@0: michael@0: ClearPropertyBit(aPropID); michael@0: ClearImportantBit(aPropID); michael@0: PropertyAt(aPropID)->Reset(); michael@0: } michael@0: michael@0: bool michael@0: nsCSSExpandedDataBlock::TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, michael@0: nsCSSProperty aPropID, michael@0: bool aIsImportant, michael@0: bool aOverrideImportant, michael@0: bool aMustCallValueAppended, michael@0: css::Declaration* aDeclaration) michael@0: { michael@0: if (!nsCSSProps::IsShorthand(aPropID)) { michael@0: return DoTransferFromBlock(aFromBlock, aPropID, michael@0: aIsImportant, aOverrideImportant, michael@0: aMustCallValueAppended, aDeclaration); michael@0: } michael@0: michael@0: bool changed = false; michael@0: CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID) { michael@0: changed |= DoTransferFromBlock(aFromBlock, *p, michael@0: aIsImportant, aOverrideImportant, michael@0: aMustCallValueAppended, aDeclaration); michael@0: } michael@0: return changed; michael@0: } michael@0: michael@0: bool michael@0: nsCSSExpandedDataBlock::DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, michael@0: nsCSSProperty aPropID, michael@0: bool aIsImportant, michael@0: bool aOverrideImportant, michael@0: bool aMustCallValueAppended, michael@0: css::Declaration* aDeclaration) michael@0: { michael@0: bool changed = false; michael@0: NS_ABORT_IF_FALSE(aFromBlock.HasPropertyBit(aPropID), "oops"); michael@0: if (aIsImportant) { michael@0: if (!HasImportantBit(aPropID)) michael@0: changed = true; michael@0: SetImportantBit(aPropID); michael@0: } else { michael@0: if (HasImportantBit(aPropID)) { michael@0: // When parsing a declaration block, an !important declaration michael@0: // is not overwritten by an ordinary declaration of the same michael@0: // property later in the block. However, CSSOM manipulations michael@0: // come through here too, and in that case we do want to michael@0: // overwrite the property. michael@0: if (!aOverrideImportant) { michael@0: aFromBlock.ClearLonghandProperty(aPropID); michael@0: return false; michael@0: } michael@0: changed = true; michael@0: ClearImportantBit(aPropID); michael@0: } michael@0: } michael@0: michael@0: if (aMustCallValueAppended || !HasPropertyBit(aPropID)) { michael@0: aDeclaration->ValueAppended(aPropID); michael@0: } michael@0: michael@0: SetPropertyBit(aPropID); michael@0: aFromBlock.ClearPropertyBit(aPropID); michael@0: michael@0: /* michael@0: * Save needless copying and allocation by calling the destructor in michael@0: * the destination, copying memory directly, and then using placement michael@0: * new. michael@0: */ michael@0: changed |= MoveValue(aFromBlock.PropertyAt(aPropID), PropertyAt(aPropID)); michael@0: return changed; michael@0: } michael@0: michael@0: void michael@0: nsCSSExpandedDataBlock::MapRuleInfoInto(nsCSSProperty aPropID, michael@0: nsRuleData* aRuleData) const michael@0: { michael@0: MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID)); michael@0: michael@0: const nsCSSValue* src = PropertyAt(aPropID); michael@0: MOZ_ASSERT(src->GetUnit() != eCSSUnit_Null); michael@0: michael@0: nsCSSValue* dest = aRuleData->ValueFor(aPropID); michael@0: MOZ_ASSERT(dest->GetUnit() == eCSSUnit_TokenStream && michael@0: dest->GetTokenStreamValue()->mPropertyID == aPropID); michael@0: michael@0: MapSinglePropertyInto(aPropID, src, dest, aRuleData); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsCSSExpandedDataBlock::DoAssertInitialState() michael@0: { michael@0: mPropertiesSet.AssertIsEmpty("not initial state"); michael@0: mPropertiesImportant.AssertIsEmpty("not initial state"); michael@0: michael@0: for (uint32_t i = 0; i < eCSSProperty_COUNT_no_shorthands; ++i) { michael@0: nsCSSProperty prop = nsCSSProperty(i); michael@0: NS_ABORT_IF_FALSE(PropertyAt(prop)->GetUnit() == eCSSUnit_Null, michael@0: "not initial state"); michael@0: } michael@0: } michael@0: #endif