michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: * representation of a declaration block (or style attribute) in a CSS michael@0: * stylesheet michael@0: */ michael@0: michael@0: #ifndef mozilla_css_Declaration_h michael@0: #define mozilla_css_Declaration_h michael@0: michael@0: // This header is in EXPORTS because it's used in several places in content/, michael@0: // but it's not really a public interface. michael@0: #ifndef MOZILLA_INTERNAL_API michael@0: #error "This file should only be included within libxul" michael@0: #endif michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "CSSVariableDeclarations.h" michael@0: #include "nsCSSDataBlock.h" michael@0: #include "nsCSSProperty.h" michael@0: #include "nsCSSProps.h" michael@0: #include "nsStringFwd.h" michael@0: #include "nsTArray.h" michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: michael@0: // Declaration objects have unusual lifetime rules. Every declaration michael@0: // begins life in an invalid state which ends when InitializeEmpty or michael@0: // CompressFrom is called upon it. After that, it can be attached to michael@0: // exactly one style rule, and will be destroyed when that style rule michael@0: // is destroyed. A declaration becomes immutable when its style rule's michael@0: // |RuleMatched| method is called; after that, it must be copied before michael@0: // it can be modified, which is taken care of by |EnsureMutable|. michael@0: michael@0: class Declaration { michael@0: public: michael@0: /** michael@0: * Construct an |Declaration| that is in an invalid state (null michael@0: * |mData|) and cannot be used until its |CompressFrom| method or michael@0: * |InitializeEmpty| method is called. michael@0: */ michael@0: Declaration(); michael@0: michael@0: Declaration(const Declaration& aCopy); michael@0: michael@0: ~Declaration(); michael@0: michael@0: /** michael@0: * |ValueAppended| must be called to maintain this declaration's michael@0: * |mOrder| whenever a property is parsed into an expanded data block michael@0: * for this declaration. aProperty must not be a shorthand. michael@0: */ michael@0: void ValueAppended(nsCSSProperty aProperty); michael@0: michael@0: void RemoveProperty(nsCSSProperty aProperty); michael@0: michael@0: bool HasProperty(nsCSSProperty aProperty) const; michael@0: michael@0: void GetValue(nsCSSProperty aProperty, nsAString& aValue) const; michael@0: void GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const; michael@0: michael@0: bool HasImportantData() const { michael@0: return mImportantData || mImportantVariables; michael@0: } michael@0: bool GetValueIsImportant(nsCSSProperty aProperty) const; michael@0: bool GetValueIsImportant(const nsAString& aProperty) const; michael@0: michael@0: /** michael@0: * Adds a custom property declaration to this object. michael@0: * michael@0: * @param aName The variable name (i.e., without the "--" prefix). michael@0: * @param aType The type of value the variable has. michael@0: * @param aValue The value of the variable, if aType is michael@0: * CSSVariableDeclarations::eTokenStream. michael@0: * @param aIsImportant Whether the declaration is !important. michael@0: * @param aOverrideImportant When aIsImportant is false, whether an michael@0: * existing !important declaration will be overridden. michael@0: */ michael@0: void AddVariableDeclaration(const nsAString& aName, michael@0: CSSVariableDeclarations::Type aType, michael@0: const nsString& aValue, michael@0: bool aIsImportant, michael@0: bool aOverrideImportant); michael@0: michael@0: /** michael@0: * Removes a custom property declaration from this object. michael@0: * michael@0: * @param aName The variable name (i.e., without the "--" prefix). michael@0: */ michael@0: void RemoveVariableDeclaration(const nsAString& aName); michael@0: michael@0: /** michael@0: * Returns whether a custom property declaration for a variable with michael@0: * a given name exists on this object. michael@0: * michael@0: * @param aName The variable name (i.e., without the "--" prefix). michael@0: */ michael@0: bool HasVariableDeclaration(const nsAString& aName) const; michael@0: michael@0: /** michael@0: * Gets the string value for a custom property declaration of a variable michael@0: * with a given name. michael@0: * michael@0: * @param aName The variable name (i.e., without the "--" prefix). michael@0: * @param aValue Out parameter into which the variable's value will be michael@0: * stored. If the value is 'initial' or 'inherit', that exact string michael@0: * will be stored in aValue. michael@0: */ michael@0: void GetVariableDeclaration(const nsAString& aName, nsAString& aValue) const; michael@0: michael@0: /** michael@0: * Returns whether the custom property declaration for a variable with michael@0: * the given name was !important. michael@0: */ michael@0: bool GetVariableValueIsImportant(const nsAString& aName) const; michael@0: michael@0: uint32_t Count() const { michael@0: return mOrder.Length(); michael@0: } michael@0: michael@0: // Returns whether we actually had a property at aIndex michael@0: bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const; michael@0: michael@0: void ToString(nsAString& aString) const; michael@0: michael@0: nsCSSCompressedDataBlock* GetNormalBlock() const { return mData; } michael@0: nsCSSCompressedDataBlock* GetImportantBlock() const { return mImportantData; } michael@0: michael@0: /** michael@0: * Initialize this declaration as holding no data. Cannot fail. michael@0: */ michael@0: void InitializeEmpty(); michael@0: michael@0: /** michael@0: * Transfer all of the state from |aExpandedData| into this declaration. michael@0: * After calling, |aExpandedData| should be in its initial state. michael@0: * Callers must make sure mOrder is updated as necessary. michael@0: */ michael@0: void CompressFrom(nsCSSExpandedDataBlock *aExpandedData) { michael@0: NS_ABORT_IF_FALSE(!mData, "oops"); michael@0: NS_ABORT_IF_FALSE(!mImportantData, "oops"); michael@0: aExpandedData->Compress(getter_Transfers(mData), michael@0: getter_Transfers(mImportantData)); michael@0: aExpandedData->AssertInitialState(); michael@0: } michael@0: michael@0: /** michael@0: * Transfer all of the state from this declaration into michael@0: * |aExpandedData| and put this declaration temporarily into an michael@0: * invalid state (ended by |CompressFrom| or |InitializeEmpty|) that michael@0: * should last only during parsing. During this time only michael@0: * |ValueAppended| should be called. michael@0: */ michael@0: void ExpandTo(nsCSSExpandedDataBlock *aExpandedData) { michael@0: AssertMutable(); michael@0: aExpandedData->AssertInitialState(); michael@0: michael@0: NS_ABORT_IF_FALSE(mData, "oops"); michael@0: aExpandedData->Expand(mData.forget(), mImportantData.forget()); michael@0: } michael@0: michael@0: /** michael@0: * Do what |nsIStyleRule::MapRuleInfoInto| needs to do for a style michael@0: * rule using this declaration for storage. michael@0: */ michael@0: void MapNormalRuleInfoInto(nsRuleData *aRuleData) const { michael@0: NS_ABORT_IF_FALSE(mData, "called while expanded"); michael@0: mData->MapRuleInfoInto(aRuleData); michael@0: if (mVariables) { michael@0: mVariables->MapRuleInfoInto(aRuleData); michael@0: } michael@0: } michael@0: void MapImportantRuleInfoInto(nsRuleData *aRuleData) const { michael@0: NS_ABORT_IF_FALSE(mData, "called while expanded"); michael@0: NS_ABORT_IF_FALSE(mImportantData || mImportantVariables, michael@0: "must have important data or variables"); michael@0: if (mImportantData) { michael@0: mImportantData->MapRuleInfoInto(aRuleData); michael@0: } michael@0: if (mImportantVariables) { michael@0: mImportantVariables->MapRuleInfoInto(aRuleData); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Attempt to replace the value for |aProperty| stored in this michael@0: * declaration with the matching value from |aFromBlock|. michael@0: * This method may only be called on a mutable declaration. michael@0: * It will fail (returning false) if |aProperty| is shorthand, michael@0: * is not already in this declaration, or does not have the indicated michael@0: * importance level. If it returns true, it erases the value in michael@0: * |aFromBlock|. |aChanged| is set to true if the declaration michael@0: * changed as a result of the call, and to false otherwise. michael@0: */ michael@0: bool TryReplaceValue(nsCSSProperty aProperty, bool aIsImportant, michael@0: nsCSSExpandedDataBlock& aFromBlock, michael@0: bool* aChanged) michael@0: { michael@0: AssertMutable(); michael@0: NS_ABORT_IF_FALSE(mData, "called while expanded"); michael@0: michael@0: if (nsCSSProps::IsShorthand(aProperty)) { michael@0: *aChanged = false; michael@0: return false; michael@0: } michael@0: nsCSSCompressedDataBlock *block = aIsImportant ? mImportantData : mData; michael@0: // mImportantData might be null michael@0: if (!block) { michael@0: *aChanged = false; michael@0: return false; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: { michael@0: nsCSSCompressedDataBlock *other = aIsImportant ? mData : mImportantData; michael@0: NS_ABORT_IF_FALSE(!other || !other->ValueFor(aProperty) || michael@0: !block->ValueFor(aProperty), michael@0: "Property both important and not?"); michael@0: } michael@0: #endif michael@0: return block->TryReplaceValue(aProperty, aFromBlock, aChanged); michael@0: } michael@0: michael@0: bool HasNonImportantValueFor(nsCSSProperty aProperty) const { michael@0: NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty), "must be longhand"); michael@0: return !!mData->ValueFor(aProperty); michael@0: } michael@0: michael@0: /** michael@0: * Return whether |this| may be modified. michael@0: */ michael@0: bool IsMutable() const { michael@0: return !mImmutable; michael@0: } michael@0: michael@0: /** michael@0: * Copy |this|, if necessary to ensure that it can be modified. michael@0: */ michael@0: Declaration* EnsureMutable(); michael@0: michael@0: /** michael@0: * Crash if |this| cannot be modified. michael@0: */ michael@0: void AssertMutable() const { michael@0: NS_ABORT_IF_FALSE(IsMutable(), "someone forgot to call EnsureMutable"); michael@0: } michael@0: michael@0: /** michael@0: * Mark this declaration as unmodifiable. It's 'const' so it can michael@0: * be called from ToString. michael@0: */ michael@0: void SetImmutable() const { mImmutable = true; } michael@0: michael@0: /** michael@0: * Clear the data, in preparation for its replacement with entirely michael@0: * new data by a call to |CompressFrom|. michael@0: */ michael@0: void ClearData() { michael@0: AssertMutable(); michael@0: mData = nullptr; michael@0: mImportantData = nullptr; michael@0: mVariables = nullptr; michael@0: mImportantVariables = nullptr; michael@0: mOrder.Clear(); michael@0: mVariableOrder.Clear(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void List(FILE* out = stdout, int32_t aIndent = 0) const; michael@0: #endif michael@0: michael@0: private: michael@0: Declaration& operator=(const Declaration& aCopy) MOZ_DELETE; michael@0: bool operator==(const Declaration& aCopy) const MOZ_DELETE; michael@0: michael@0: void GetValue(nsCSSProperty aProperty, nsAString& aValue, michael@0: nsCSSValue::Serialization aValueSerialization) const; michael@0: michael@0: static void AppendImportanceToString(bool aIsImportant, nsAString& aString); michael@0: // return whether there was a value in |aValue| (i.e., it had a non-null unit) michael@0: bool AppendValueToString(nsCSSProperty aProperty, nsAString& aResult) const; michael@0: bool AppendValueToString(nsCSSProperty aProperty, nsAString& aResult, michael@0: nsCSSValue::Serialization aValueSerialization) const; michael@0: // Helper for ToString with strange semantics regarding aValue. michael@0: void AppendPropertyAndValueToString(nsCSSProperty aProperty, michael@0: nsAutoString& aValue, michael@0: nsAString& aResult) const; michael@0: // helper for ToString that serializes a custom property declaration for michael@0: // a variable with the specified name michael@0: void AppendVariableAndValueToString(const nsAString& aName, michael@0: nsAString& aResult) const; michael@0: michael@0: public: michael@0: /** michael@0: * Returns the property at the given index in the ordered list of michael@0: * declarations. For custom properties, eCSSPropertyExtra_variable michael@0: * is returned. michael@0: */ michael@0: nsCSSProperty GetPropertyAt(uint32_t aIndex) const { michael@0: uint32_t value = mOrder[aIndex]; michael@0: if (value >= eCSSProperty_COUNT) { michael@0: return eCSSPropertyExtra_variable; michael@0: } michael@0: return nsCSSProperty(value); michael@0: } michael@0: michael@0: /** michael@0: * Gets the name of the custom property at the given index in the ordered michael@0: * list of declarations. michael@0: */ michael@0: void GetCustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const { michael@0: MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT); michael@0: uint32_t variableIndex = mOrder[aIndex] - eCSSProperty_COUNT; michael@0: aResult.Truncate(); michael@0: aResult.AppendLiteral("--"); michael@0: aResult.Append(mVariableOrder[variableIndex]); michael@0: } michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: private: michael@0: // The order of properties in this declaration. Longhand properties are michael@0: // represented by their nsCSSProperty value, and each custom property (--*) michael@0: // is represented by a value that begins at eCSSProperty_COUNT. michael@0: // michael@0: // Subtracting eCSSProperty_COUNT from those values that represent custom michael@0: // properties results in an index into mVariableOrder, which identifies the michael@0: // specific variable the custom property declaration is for. michael@0: nsAutoTArray mOrder; michael@0: michael@0: // variable names of custom properties found in mOrder michael@0: nsTArray mVariableOrder; michael@0: michael@0: // never null, except while expanded, or before the first call to michael@0: // InitializeEmpty or CompressFrom. michael@0: nsAutoPtr mData; michael@0: michael@0: // may be null michael@0: nsAutoPtr mImportantData; michael@0: michael@0: // may be null michael@0: nsAutoPtr mVariables; michael@0: michael@0: // may be null michael@0: nsAutoPtr mImportantVariables; michael@0: michael@0: // set by style rules when |RuleMatched| is called; michael@0: // also by ToString (hence the 'mutable'). michael@0: mutable bool mImmutable; michael@0: }; michael@0: michael@0: } // namespace css michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_css_Declaration_h */