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: #ifndef nsCSSDataBlock_h__ michael@0: #define nsCSSDataBlock_h__ michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "nsCSSProps.h" michael@0: #include "nsCSSPropertySet.h" michael@0: #include "nsCSSValue.h" michael@0: #include "imgRequestProxy.h" michael@0: michael@0: struct nsRuleData; michael@0: class nsCSSExpandedDataBlock; michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: class Declaration; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * An |nsCSSCompressedDataBlock| holds a usually-immutable chunk of michael@0: * property-value data for a CSS declaration block (which we misname a michael@0: * |css::Declaration|). Mutation is accomplished through michael@0: * |nsCSSExpandedDataBlock| or in some cases via direct slot access. michael@0: */ michael@0: class nsCSSCompressedDataBlock { michael@0: private: michael@0: friend class nsCSSExpandedDataBlock; michael@0: michael@0: // Only this class (via |CreateEmptyBlock|) or nsCSSExpandedDataBlock michael@0: // (in |Compress|) can create compressed data blocks. michael@0: nsCSSCompressedDataBlock(uint32_t aNumProps) michael@0: : mStyleBits(0), mNumProps(aNumProps) michael@0: {} michael@0: michael@0: public: michael@0: ~nsCSSCompressedDataBlock(); michael@0: michael@0: /** michael@0: * Do what |nsIStyleRule::MapRuleInfoInto| needs to do for a style michael@0: * rule using this block for storage. michael@0: */ michael@0: void MapRuleInfoInto(nsRuleData *aRuleData) const; michael@0: michael@0: /** michael@0: * Return the location at which the *value* for the property is michael@0: * stored, or null if the block does not contain a value for the michael@0: * property. michael@0: * michael@0: * Inefficient (by design). michael@0: * michael@0: * Must not be called for shorthands. michael@0: */ michael@0: const nsCSSValue* ValueFor(nsCSSProperty aProperty) const; michael@0: michael@0: /** michael@0: * Attempt to replace the value for |aProperty| stored in this block michael@0: * with the matching value stored in |aFromBlock|. michael@0: * This method will fail (returning false) if |aProperty| is not michael@0: * already in this block. It will set |aChanged| to true if it michael@0: * actually made a change to the block, but regardless, if it michael@0: * returns true, the value in |aFromBlock| was erased. michael@0: */ michael@0: bool TryReplaceValue(nsCSSProperty aProperty, michael@0: nsCSSExpandedDataBlock& aFromBlock, michael@0: bool* aChanged); michael@0: michael@0: /** michael@0: * Clone this block, or return null on out-of-memory. michael@0: */ michael@0: nsCSSCompressedDataBlock* Clone() const; michael@0: michael@0: /** michael@0: * Create a new nsCSSCompressedDataBlock holding no declarations. michael@0: */ michael@0: static nsCSSCompressedDataBlock* CreateEmptyBlock(); michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: bool HasDefaultBorderImageSlice() const; michael@0: bool HasDefaultBorderImageWidth() const; michael@0: bool HasDefaultBorderImageOutset() const; michael@0: bool HasDefaultBorderImageRepeat() const; michael@0: michael@0: private: michael@0: void* operator new(size_t aBaseSize, uint32_t aNumProps) { michael@0: NS_ABORT_IF_FALSE(aBaseSize == sizeof(nsCSSCompressedDataBlock), michael@0: "unexpected size for nsCSSCompressedDataBlock"); michael@0: return ::operator new(aBaseSize + DataSize(aNumProps)); michael@0: } michael@0: michael@0: public: michael@0: // Ideally, |nsCSSProperty| would be |enum nsCSSProperty : int16_t|. But michael@0: // not all of the compilers we use are modern enough to support small michael@0: // enums. So we manually squeeze nsCSSProperty into 16 bits ourselves. michael@0: // The static assertion below ensures it fits. michael@0: typedef int16_t CompressedCSSProperty; michael@0: static const size_t MaxCompressedCSSProperty = INT16_MAX; michael@0: michael@0: private: michael@0: static size_t DataSize(uint32_t aNumProps) { michael@0: return size_t(aNumProps) * michael@0: (sizeof(nsCSSValue) + sizeof(CompressedCSSProperty)); michael@0: } michael@0: michael@0: int32_t mStyleBits; // the structs for which we have data, according to michael@0: // |nsCachedStyleData::GetBitForSID|. michael@0: uint32_t mNumProps; michael@0: // nsCSSValue elements are stored after these fields, and michael@0: // nsCSSProperty elements are stored -- each one compressed as a michael@0: // CompressedCSSProperty -- after the nsCSSValue elements. Space for them michael@0: // is allocated in |operator new| above. The static assertions following michael@0: // this class make sure that the value and property elements are aligned michael@0: // appropriately. michael@0: michael@0: nsCSSValue* Values() const { michael@0: return (nsCSSValue*)(this + 1); michael@0: } michael@0: michael@0: CompressedCSSProperty* CompressedProperties() const { michael@0: return (CompressedCSSProperty*)(Values() + mNumProps); michael@0: } michael@0: michael@0: nsCSSValue* ValueAtIndex(uint32_t i) const { michael@0: NS_ABORT_IF_FALSE(i < mNumProps, "value index out of range"); michael@0: return Values() + i; michael@0: } michael@0: michael@0: nsCSSProperty PropertyAtIndex(uint32_t i) const { michael@0: NS_ABORT_IF_FALSE(i < mNumProps, "property index out of range"); michael@0: nsCSSProperty prop = (nsCSSProperty)CompressedProperties()[i]; michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(prop), "out of range"); michael@0: return prop; michael@0: } michael@0: michael@0: void CopyValueToIndex(uint32_t i, nsCSSValue* aValue) { michael@0: new (ValueAtIndex(i)) nsCSSValue(*aValue); michael@0: } michael@0: michael@0: void RawCopyValueToIndex(uint32_t i, nsCSSValue* aValue) { michael@0: memcpy(ValueAtIndex(i), aValue, sizeof(nsCSSValue)); michael@0: } michael@0: michael@0: void SetPropertyAtIndex(uint32_t i, nsCSSProperty aProperty) { michael@0: NS_ABORT_IF_FALSE(i < mNumProps, "set property index out of range"); michael@0: CompressedProperties()[i] = (CompressedCSSProperty)aProperty; michael@0: } michael@0: michael@0: void SetNumPropsToZero() { michael@0: mNumProps = 0; michael@0: } michael@0: }; michael@0: michael@0: // Make sure the values and properties are aligned appropriately. (These michael@0: // assertions are stronger than necessary to keep them simple.) michael@0: static_assert(sizeof(nsCSSCompressedDataBlock) == 8, michael@0: "nsCSSCompressedDataBlock's size has changed"); michael@0: static_assert(NS_ALIGNMENT_OF(nsCSSValue) == 4 || NS_ALIGNMENT_OF(nsCSSValue) == 8, michael@0: "nsCSSValue doesn't align with nsCSSCompressedDataBlock"); michael@0: static_assert(NS_ALIGNMENT_OF(nsCSSCompressedDataBlock::CompressedCSSProperty) == 2, michael@0: "CompressedCSSProperty doesn't align with nsCSSValue"); michael@0: michael@0: // Make sure that sizeof(CompressedCSSProperty) is big enough. michael@0: static_assert(eCSSProperty_COUNT_no_shorthands <= michael@0: nsCSSCompressedDataBlock::MaxCompressedCSSProperty, michael@0: "nsCSSProperty doesn't fit in StoredSizeOfCSSProperty"); michael@0: michael@0: class nsCSSExpandedDataBlock { michael@0: friend class nsCSSCompressedDataBlock; michael@0: michael@0: public: michael@0: nsCSSExpandedDataBlock(); michael@0: ~nsCSSExpandedDataBlock(); michael@0: michael@0: private: michael@0: /* Property storage may not be accessed directly; use AddLonghandProperty michael@0: * and friends. michael@0: */ michael@0: nsCSSValue mValues[eCSSProperty_COUNT_no_shorthands]; michael@0: michael@0: public: michael@0: /** michael@0: * Transfer all of the state from a pair of compressed data blocks michael@0: * to this expanded block. This expanded block must be clear michael@0: * beforehand. michael@0: * michael@0: * This method DELETES both of the compressed data blocks it is michael@0: * passed. (This is necessary because ownership of sub-objects michael@0: * is transferred to the expanded block.) michael@0: */ michael@0: void Expand(nsCSSCompressedDataBlock *aNormalBlock, michael@0: nsCSSCompressedDataBlock *aImportantBlock); michael@0: michael@0: /** michael@0: * Allocate new compressed blocks and transfer all of the state michael@0: * from this expanded block to the new blocks, clearing this michael@0: * expanded block. A normal block will always be allocated, but michael@0: * an important block will only be allocated if there are michael@0: * !important properties in the expanded block; otherwise michael@0: * |*aImportantBlock| will be set to null. michael@0: */ michael@0: void Compress(nsCSSCompressedDataBlock **aNormalBlock, michael@0: nsCSSCompressedDataBlock **aImportantBlock); michael@0: michael@0: /** michael@0: * Copy a value into this expanded block. This does NOT destroy michael@0: * the source value object. |aProperty| cannot be a shorthand. michael@0: */ michael@0: void AddLonghandProperty(nsCSSProperty aProperty, const nsCSSValue& aValue); michael@0: michael@0: /** michael@0: * Clear the state of this expanded block. michael@0: */ michael@0: void Clear(); michael@0: michael@0: /** michael@0: * Clear the data for the given property (including the set and michael@0: * important bits). Can be used with shorthand properties. michael@0: */ michael@0: void ClearProperty(nsCSSProperty aPropID); michael@0: michael@0: /** michael@0: * Same as ClearProperty, but faster and cannot be used with shorthands. michael@0: */ michael@0: void ClearLonghandProperty(nsCSSProperty aPropID); michael@0: michael@0: /** michael@0: * Transfer the state for |aPropID| (which may be a shorthand) michael@0: * from |aFromBlock| to this block. The property being transferred michael@0: * is !important if |aIsImportant| is true, and should replace an michael@0: * existing !important property regardless of its own importance michael@0: * if |aOverrideImportant| is true. michael@0: * michael@0: * Returns true if something changed, false otherwise. Calls michael@0: * |ValueAppended| on |aDeclaration| if the property was not michael@0: * previously set, or in any case if |aMustCallValueAppended| is true. michael@0: */ michael@0: bool TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, michael@0: nsCSSProperty aPropID, michael@0: bool aIsImportant, michael@0: bool aOverrideImportant, michael@0: bool aMustCallValueAppended, michael@0: mozilla::css::Declaration* aDeclaration); michael@0: michael@0: /** michael@0: * Copies the values for aPropID into the specified aRuleData object. michael@0: * michael@0: * This is used for copying parsed-at-computed-value-time properties michael@0: * that had variable references. aPropID must be a longhand property. michael@0: */ michael@0: void MapRuleInfoInto(nsCSSProperty aPropID, nsRuleData* aRuleData) const; michael@0: michael@0: void AssertInitialState() { michael@0: #ifdef DEBUG michael@0: DoAssertInitialState(); michael@0: #endif michael@0: } michael@0: michael@0: private: michael@0: /** michael@0: * Compute the number of properties that will be present in the michael@0: * result of |Compress|. michael@0: */ michael@0: void ComputeNumProps(uint32_t* aNumPropsNormal, michael@0: uint32_t* aNumPropsImportant); michael@0: michael@0: void DoExpand(nsCSSCompressedDataBlock *aBlock, bool aImportant); michael@0: michael@0: /** michael@0: * Worker for TransferFromBlock; cannot be used with shorthands. michael@0: */ michael@0: bool DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, michael@0: nsCSSProperty aPropID, michael@0: bool aIsImportant, michael@0: bool aOverrideImportant, michael@0: bool aMustCallValueAppended, michael@0: mozilla::css::Declaration* aDeclaration); michael@0: michael@0: #ifdef DEBUG michael@0: void DoAssertInitialState(); michael@0: #endif michael@0: michael@0: /* michael@0: * mPropertiesSet stores a bit for every property that is present, michael@0: * to optimize compression of blocks with small numbers of michael@0: * properties (the norm) and to allow quickly checking whether a michael@0: * property is set in this block. michael@0: */ michael@0: nsCSSPropertySet mPropertiesSet; michael@0: /* michael@0: * mPropertiesImportant indicates which properties are '!important'. michael@0: */ michael@0: nsCSSPropertySet mPropertiesImportant; michael@0: michael@0: /* michael@0: * Return the storage location within |this| of the value of the michael@0: * property |aProperty|. michael@0: */ michael@0: nsCSSValue* PropertyAt(nsCSSProperty aProperty) { michael@0: NS_ABORT_IF_FALSE(0 <= aProperty && michael@0: aProperty < eCSSProperty_COUNT_no_shorthands, michael@0: "property out of range"); michael@0: return &mValues[aProperty]; michael@0: } michael@0: const nsCSSValue* PropertyAt(nsCSSProperty aProperty) const { michael@0: NS_ABORT_IF_FALSE(0 <= aProperty && michael@0: aProperty < eCSSProperty_COUNT_no_shorthands, michael@0: "property out of range"); michael@0: return &mValues[aProperty]; michael@0: } michael@0: michael@0: void SetPropertyBit(nsCSSProperty aProperty) { michael@0: mPropertiesSet.AddProperty(aProperty); michael@0: } michael@0: michael@0: void ClearPropertyBit(nsCSSProperty aProperty) { michael@0: mPropertiesSet.RemoveProperty(aProperty); michael@0: } michael@0: michael@0: bool HasPropertyBit(nsCSSProperty aProperty) { michael@0: return mPropertiesSet.HasProperty(aProperty); michael@0: } michael@0: michael@0: void SetImportantBit(nsCSSProperty aProperty) { michael@0: mPropertiesImportant.AddProperty(aProperty); michael@0: } michael@0: michael@0: void ClearImportantBit(nsCSSProperty aProperty) { michael@0: mPropertiesImportant.RemoveProperty(aProperty); michael@0: } michael@0: michael@0: bool HasImportantBit(nsCSSProperty aProperty) { michael@0: return mPropertiesImportant.HasProperty(aProperty); michael@0: } michael@0: michael@0: void ClearSets() { michael@0: mPropertiesSet.Empty(); michael@0: mPropertiesImportant.Empty(); michael@0: } michael@0: }; michael@0: michael@0: #endif /* !defined(nsCSSDataBlock_h__) */