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