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