/* -*- 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/. */ /* * representation of CSS style rules (selectors+declaration) and CSS * selectors */ #ifndef mozilla_css_StyleRule_h__ #define mozilla_css_StyleRule_h__ #include "mozilla/Attributes.h" #include "mozilla/BindingStyleRule.h" #include "mozilla/MemoryReporting.h" #include "mozilla/StyleSetHandle.h" #include "mozilla/UniquePtr.h" #include "nsString.h" #include "nsCOMPtr.h" #include "nsCSSPseudoElements.h" #include "nsIStyleRule.h" class nsAtom; struct nsCSSSelectorList; namespace mozilla { enum class CSSPseudoClassType : uint8_t; class CSSStyleSheet; } // namespace mozilla struct nsAtomList { public: explicit nsAtomList(nsAtom* aAtom); explicit nsAtomList(const nsString& aAtomValue); ~nsAtomList(void); /** Do a deep clone. Should be used only on the first in the linked list. */ nsAtomList* Clone() const { return Clone(true); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; RefPtr mAtom; nsAtomList* mNext; private: nsAtomList* Clone(bool aDeep) const; nsAtomList(const nsAtomList& aCopy) = delete; nsAtomList& operator=(const nsAtomList& aCopy) = delete; }; struct nsPseudoClassList { public: typedef mozilla::CSSPseudoClassType CSSPseudoClassType; explicit nsPseudoClassList(CSSPseudoClassType aType); nsPseudoClassList(CSSPseudoClassType aType, const char16_t *aString); nsPseudoClassList(CSSPseudoClassType aType, const int32_t *aIntPair); nsPseudoClassList(CSSPseudoClassType aType, nsCSSSelectorList *aSelectorList /* takes ownership */); ~nsPseudoClassList(void); /** Do a deep clone. Should be used only on the first in the linked list. */ nsPseudoClassList* Clone() const { return Clone(true); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; union { // For a given value of mType, we have either: // a. no value, which means mMemory is always null // (if none of the conditions for (b), (c), or (d) is true) // b. a string value, which means mString/mMemory is non-null // (if nsCSSPseudoClasses::HasStringArg(mType)) // c. an integer pair value, which means mNumbers/mMemory is non-null // (if nsCSSPseudoClasses::HasNthPairArg(mType)) // d. a selector list, which means mSelectors is non-null // (if nsCSSPseudoClasses::HasSelectorListArg(mType)) void* mMemory; // mString and mNumbers use moz_xmalloc/free char16_t* mString; int32_t* mNumbers; nsCSSSelectorList* mSelectors; } u; CSSPseudoClassType mType; nsPseudoClassList* mNext; private: nsPseudoClassList* Clone(bool aDeep) const; nsPseudoClassList(const nsPseudoClassList& aCopy) = delete; nsPseudoClassList& operator=(const nsPseudoClassList& aCopy) = delete; }; #define NS_ATTR_FUNC_SET 0 // [attr] #define NS_ATTR_FUNC_EQUALS 1 // [attr=value] #define NS_ATTR_FUNC_INCLUDES 2 // [attr~=value] (space separated) #define NS_ATTR_FUNC_DASHMATCH 3 // [attr|=value] ('-' truncated) #define NS_ATTR_FUNC_BEGINSMATCH 4 // [attr^=value] (begins with) #define NS_ATTR_FUNC_ENDSMATCH 5 // [attr$=value] (ends with) #define NS_ATTR_FUNC_CONTAINSMATCH 6 // [attr*=value] (contains substring) struct nsAttrSelector { public: enum class ValueCaseSensitivity : uint8_t { CaseSensitive, CaseInsensitive, CaseInsensitiveInHTML }; nsAttrSelector(int32_t aNameSpace, const nsString& aAttr); nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction, const nsString& aValue, ValueCaseSensitivity aValueCaseSensitivity); nsAttrSelector(int32_t aNameSpace, nsAtom* aLowercaseAttr, nsAtom* aCasedAttr, uint8_t aFunction, const nsString& aValue, ValueCaseSensitivity aValueCaseSensitivity); ~nsAttrSelector(void); /** Do a deep clone. Should be used only on the first in the linked list. */ nsAttrSelector* Clone() const { return Clone(true); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; bool IsValueCaseSensitive(bool aInHTML) const { return mValueCaseSensitivity == ValueCaseSensitivity::CaseSensitive || (!aInHTML && mValueCaseSensitivity == ValueCaseSensitivity::CaseInsensitiveInHTML); } nsString mValue; nsAttrSelector* mNext; RefPtr mLowercaseAttr; RefPtr mCasedAttr; int32_t mNameSpace; uint8_t mFunction; ValueCaseSensitivity mValueCaseSensitivity; private: nsAttrSelector* Clone(bool aDeep) const; nsAttrSelector(const nsAttrSelector& aCopy) = delete; nsAttrSelector& operator=(const nsAttrSelector& aCopy) = delete; }; struct nsCSSSelector { public: typedef mozilla::CSSPseudoClassType CSSPseudoClassType; nsCSSSelector(void); ~nsCSSSelector(void); /** Do a deep clone. Should be used only on the first in the linked list. */ nsCSSSelector* Clone() const { return Clone(true, true); } void Reset(void); void SetNameSpace(int32_t aNameSpace); void SetTag(const nsString& aTag); void AddID(const nsString& aID); void AddClass(const nsString& aClass); void AddPseudoClass(CSSPseudoClassType aType); void AddPseudoClass(CSSPseudoClassType aType, const char16_t* aString); void AddPseudoClass(CSSPseudoClassType aType, const int32_t* aIntPair); // takes ownership of aSelectorList void AddPseudoClass(CSSPseudoClassType aType, nsCSSSelectorList* aSelectorList); void AddAttribute(int32_t aNameSpace, const nsString& aAttr); void AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc, const nsString& aValue, nsAttrSelector::ValueCaseSensitivity aValueCaseSensitivity); void SetOperator(char16_t aOperator); inline bool HasTagSelector() const { return !!mCasedTag; } inline bool IsPseudoElement() const { return mLowercaseTag && !mCasedTag; } // Calculate the specificity of this selector (not including its mNext!). int32_t CalcWeight() const; void ToString(nsAString& aString, mozilla::CSSStyleSheet* aSheet, bool aAppend = false) const; bool IsRestrictedSelector() const { return PseudoType() == mozilla::CSSPseudoElementType::NotPseudo; } #ifdef DEBUG nsCString RestrictedSelectorToString() const; #endif private: void AddPseudoClassInternal(nsPseudoClassList *aPseudoClass); nsCSSSelector* Clone(bool aDeepNext, bool aDeepNegations) const; void AppendToStringWithoutCombinators( nsAString& aString, mozilla::CSSStyleSheet* aSheet, bool aUseStandardNamespacePrefixes) const; void AppendToStringWithoutCombinatorsOrNegations( nsAString& aString, mozilla::CSSStyleSheet* aSheet, bool aIsNegated, bool aUseStandardNamespacePrefixes) const; // Returns true if this selector can have a namespace specified (which // happens if and only if the default namespace would apply to this // selector). bool CanBeNamespaced(bool aIsNegated) const; // Calculate the specificity of this selector (not including its mNext // or its mNegations). int32_t CalcWeightWithoutNegations() const; public: // Get and set the selector's pseudo type mozilla::CSSPseudoElementType PseudoType() const { return mPseudoType; } void SetPseudoType(mozilla::CSSPseudoElementType aType) { mPseudoType = aType; } size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; // For case-sensitive documents, mLowercaseTag is the same as mCasedTag, // but in case-insensitive documents (HTML) mLowercaseTag is lowercase. // Also, for pseudo-elements mCasedTag will be null but mLowercaseTag // contains their name. RefPtr mLowercaseTag; RefPtr mCasedTag; nsAtomList* mIDList; nsAtomList* mClassList; nsPseudoClassList* mPseudoClassList; // atom for the pseudo, string for // the argument to functional pseudos nsAttrSelector* mAttrList; nsCSSSelector* mNegations; nsCSSSelector* mNext; int32_t mNameSpace; char16_t mOperator; private: // The underlying type of CSSPseudoElementType is uint8_t and // it packs well with mOperator. (char16_t + uint8_t is less than 32bits.) mozilla::CSSPseudoElementType mPseudoType; nsCSSSelector(const nsCSSSelector& aCopy) = delete; nsCSSSelector& operator=(const nsCSSSelector& aCopy) = delete; }; /** * A selector list is the unit of selectors that each style rule has. * For example, "P B, H1 B { ... }" would be a selector list with two * items (where each |nsCSSSelectorList| object's |mSelectors| has * an |mNext| for the P or H1). We represent them as linked lists. */ namespace mozilla { namespace css { class StyleRule; } // namespace css } // namespace mozilla struct nsCSSSelectorList { nsCSSSelectorList(void); ~nsCSSSelectorList(void); /** * Create a new selector and push it onto the beginning of |mSelectors|, * setting its |mNext| to the current value of |mSelectors|. If there is an * earlier selector, set its |mOperator| to |aOperator|; else |aOperator| * must be char16_t(0). * Returns the new selector. * The list owns the new selector. * The caller is responsible for updating |mWeight|. */ nsCSSSelector* AddSelector(char16_t aOperator); /** * Point |mSelectors| to its |mNext|, and delete the first node in the old * |mSelectors|. * Should only be used on a list with more than one selector in it. */ void RemoveRightmostSelector(); /** * Should be used only on the first in the list */ void ToString(nsAString& aResult, mozilla::CSSStyleSheet* aSheet); /** * Do a deep clone. Should be used only on the first in the list. */ nsCSSSelectorList* Clone() const { return Clone(true); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; nsCSSSelector* mSelectors; int32_t mWeight; nsCSSSelectorList* mNext; protected: friend class mozilla::css::StyleRule; nsCSSSelectorList* Clone(bool aDeep) const; private: nsCSSSelectorList(const nsCSSSelectorList& aCopy) = delete; nsCSSSelectorList& operator=(const nsCSSSelectorList& aCopy) = delete; }; // 464bab7a-2fce-4f30-ab44-b7a5f3aae57d #define NS_CSS_STYLE_RULE_IMPL_CID \ { 0x464bab7a, 0x2fce, 0x4f30, \ { 0xab, 0x44, 0xb7, 0xa5, 0xf3, 0xaa, 0xe5, 0x7d } } class DOMCSSDeclarationImpl; namespace mozilla { namespace css { class Declaration; class StyleRule final : public BindingStyleRule { public: StyleRule(nsCSSSelectorList* aSelector, Declaration *aDeclaration, uint32_t aLineNumber, uint32_t aColumnNumber); private: // for |Clone| StyleRule(const StyleRule& aCopy); public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_STYLE_RULE_IMPL_CID) NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(StyleRule, Rule) bool IsCCLeaf() const override; uint32_t GetSelectorCount() override; nsresult GetSelectorText(uint32_t aSelectorIndex, nsAString& aText) override; nsresult GetSpecificity(uint32_t aSelectorIndex, uint64_t* aSpecificity) override; nsresult SelectorMatchesElement(dom::Element* aElement, uint32_t aSelectorIndex, const nsAString& aPseudo, bool* aMatches) override; mozilla::NotNull GetDeclarationBlock() const override; // WebIDL interface uint16_t Type() const override; void GetCssText(nsAString& aCssText) const override; void GetSelectorText(nsAString& aSelectorText) final override; void SetSelectorText(const nsAString& aSelectorText) final override; nsICSSDeclaration* Style() override; // null for style attribute nsCSSSelectorList* Selector() { return mSelector; } Declaration* GetDeclaration() const { return mDeclaration; } void SetDeclaration(Declaration* aDecl); int32_t GetType() const override; #ifdef MOZ_OLD_STYLE CSSStyleSheet* GetStyleSheet() const { StyleSheet* sheet = Rule::GetStyleSheet(); return sheet ? sheet->AsGecko() : nullptr; } #endif already_AddRefed Clone() const override; #ifdef DEBUG void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override; private: ~StyleRule(); // Drop our references to mDeclaration and mRule, and let them know we're // doing that. void DropReferences(); nsCSSSelectorList* GetSelectorAtIndex(uint32_t aIndex, ErrorResult& rv); private: nsCSSSelectorList* mSelector; // null for style attribute RefPtr mDeclaration; // We own it, and it aggregates its refcount with us. UniquePtr mDOMDeclaration; private: StyleRule& operator=(const StyleRule& aCopy) = delete; }; NS_DEFINE_STATIC_IID_ACCESSOR(StyleRule, NS_CSS_STYLE_RULE_IMPL_CID) } // namespace css } // namespace mozilla #endif /* mozilla_css_StyleRule_h__ */