diff options
Diffstat (limited to 'layout/style/nsStyleSet.h')
-rw-r--r-- | layout/style/nsStyleSet.h | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h new file mode 100644 index 000000000..879d0add3 --- /dev/null +++ b/layout/style/nsStyleSet.h @@ -0,0 +1,600 @@ +/* -*- 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/. */ + +/* + * the container for the style sheets that apply to a presentation, and + * the internal API that the style system exposes for creating (and + * potentially re-creating) style contexts + */ + +#ifndef nsStyleSet_h_ +#define nsStyleSet_h_ + +#include "mozilla/Attributes.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/EnumeratedArray.h" +#include "mozilla/LinkedList.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/SheetType.h" + +#include "nsIStyleRuleProcessor.h" +#include "nsBindingManager.h" +#include "nsRuleNode.h" +#include "nsTArray.h" +#include "nsCOMArray.h" +#include "nsIStyleRule.h" + +class gfxFontFeatureValueSet; +class nsCSSKeyframesRule; +class nsCSSFontFeatureValuesRule; +class nsCSSPageRule; +class nsCSSCounterStyleRule; +class nsICSSPseudoComparator; +class nsRuleWalker; +struct ElementDependentRuleProcessorData; +struct nsFontFaceRuleContainer; +struct TreeMatchContext; + +namespace mozilla { +class CSSStyleSheet; +class EventStates; +enum class CSSPseudoElementType : uint8_t; +} // namespace mozilla + +class nsEmptyStyleRule final : public nsIStyleRule +{ +private: + ~nsEmptyStyleRule() {} + +public: + NS_DECL_ISUPPORTS + virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; + virtual bool MightMapInheritedStyleData() override; + virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, + nsCSSValue* aValue) override; +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif +}; + +class nsInitialStyleRule final : public nsIStyleRule +{ +private: + ~nsInitialStyleRule() {} + +public: + NS_DECL_ISUPPORTS + virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; + virtual bool MightMapInheritedStyleData() override; + virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, + nsCSSValue* aValue) override; +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif +}; + +class nsDisableTextZoomStyleRule final : public nsIStyleRule +{ +private: + ~nsDisableTextZoomStyleRule() {} + +public: + NS_DECL_ISUPPORTS + virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; + virtual bool MightMapInheritedStyleData() override; + virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, + nsCSSValue* aValue) override; +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif +}; + +// The style set object is created by the document viewer and ownership is +// then handed off to the PresShell. Only the PresShell should delete a +// style set. + +class nsStyleSet final +{ + public: + nsStyleSet(); + ~nsStyleSet(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + + void Init(nsPresContext *aPresContext); + + nsRuleNode* GetRuleTree() { return mRuleTree; } + + // get a style context for a non-pseudo frame. + already_AddRefed<nsStyleContext> + ResolveStyleFor(mozilla::dom::Element* aElement, + nsStyleContext* aParentContext); + + already_AddRefed<nsStyleContext> + ResolveStyleFor(mozilla::dom::Element* aElement, + nsStyleContext* aParentContext, + TreeMatchContext& aTreeMatchContext); + + // Get a style context (with the given parent) for the + // sequence of style rules in the |aRules| array. + already_AddRefed<nsStyleContext> + ResolveStyleForRules(nsStyleContext* aParentContext, + const nsTArray< nsCOMPtr<nsIStyleRule> > &aRules); + + // Get a style context that represents aBaseContext, but as though + // it additionally matched the rules in the aRules array (in that + // order, as more specific than any other rules). + // + // One of the following must hold: + // 1. The resulting style context must be used only on a temporary + // basis, and it must never be put into the style context tree + // (and, in particular, we must never call + // ResolveStyleWithReplacement with it as the old context, which + // might happen if it is put in the style context tree), or + // 2. The additional rules must be appropriate for the transitions + // level of the cascade, which is the highest level of the cascade. + // (This is the case for one current caller, the cover rule used + // for CSS transitions.) + already_AddRefed<nsStyleContext> + ResolveStyleByAddingRules(nsStyleContext* aBaseContext, + const nsCOMArray<nsIStyleRule> &aRules); + + // Resolve style by making replacements in the list of style rules as + // described by aReplacements, but otherwise maintaining the status + // quo. + // aPseudoElement must follow the same rules as for + // ResolvePseudoElementStyle, and be null for non-pseudo-element cases + enum { // flags for aFlags + // Skip starting CSS animations that result from the style. + eSkipStartingAnimations = (1<<0), + }; + already_AddRefed<nsStyleContext> + ResolveStyleWithReplacement(mozilla::dom::Element* aElement, + mozilla::dom::Element* aPseudoElement, + nsStyleContext* aNewParentContext, + nsStyleContext* aOldStyleContext, + nsRestyleHint aReplacements, + uint32_t aFlags = 0); + + // Resolve style by returning a style context with the specified + // animation data removed. It is allowable to remove all animation + // data with eRestyle_AllHintsWithAnimations, or by using any other + // hints that are allowed by ResolveStyleWithReplacement. + already_AddRefed<nsStyleContext> + ResolveStyleWithoutAnimation(mozilla::dom::Element* aElement, + nsStyleContext* aStyleContext, + nsRestyleHint aWhichToRemove); + + // Get a style context for a text node (which no rules will match). + // + // The returned style context will have nsCSSAnonBoxes::mozText as its pseudo. + // + // (Perhaps mozText should go away and we shouldn't even create style + // contexts for such content nodes, when text-combine-upright is not + // present. However, not doing any rule matching for them is a first step.) + already_AddRefed<nsStyleContext> + ResolveStyleForText(nsIContent* aTextNode, nsStyleContext* aParentContext); + + // Get a style context for a non-element (which no rules will match) + // other than a text node, such as placeholder frames, and the + // nsFirstLetterFrame for everything after the first letter. + // + // The returned style context will have nsCSSAnonBoxes::mozOtherNonElement as + // its pseudo. + // + // (Perhaps mozOtherNonElement should go away and we shouldn't even + // create style contexts for such content nodes. However, not doing + // any rule matching for them is a first step.) + already_AddRefed<nsStyleContext> + ResolveStyleForOtherNonElement(nsStyleContext* aParentContext); + + // Get a style context for a pseudo-element. aParentElement must be + // non-null. aPseudoID is the CSSPseudoElementType for the + // pseudo-element. aPseudoElement must be non-null if the pseudo-element + // type is one that allows user action pseudo-classes after it or allows + // style attributes; otherwise, it is ignored. + already_AddRefed<nsStyleContext> + ResolvePseudoElementStyle(mozilla::dom::Element* aParentElement, + mozilla::CSSPseudoElementType aType, + nsStyleContext* aParentContext, + mozilla::dom::Element* aPseudoElement); + + // This functions just like ResolvePseudoElementStyle except that it will + // return nullptr if there are no explicit style rules for that + // pseudo element. + already_AddRefed<nsStyleContext> + ProbePseudoElementStyle(mozilla::dom::Element* aParentElement, + mozilla::CSSPseudoElementType aType, + nsStyleContext* aParentContext); + already_AddRefed<nsStyleContext> + ProbePseudoElementStyle(mozilla::dom::Element* aParentElement, + mozilla::CSSPseudoElementType aType, + nsStyleContext* aParentContext, + TreeMatchContext& aTreeMatchContext, + mozilla::dom::Element* aPseudoElement = nullptr); + + /** + * Bit-flags that can be passed to ResolveAnonymousBoxStyle and GetContext + * in their parameter 'aFlags'. + */ + enum { + eNoFlags = 0, + eIsLink = 1 << 0, + eIsVisitedLink = 1 << 1, + eDoAnimation = 1 << 2, + + // Indicates that we should skip the flex/grid item specific chunk of + // ApplyStyleFixups(). This is useful if our parent has "display: flex" + // or "display: grid" but we can tell we're not going to honor that (e.g. if + // it's the outer frame of a button widget, and we're the inline frame for + // the button's label). + eSkipParentDisplayBasedStyleFixup = 1 << 3 + }; + + // Get a style context for an anonymous box. aPseudoTag is the + // pseudo-tag to use and must be non-null. aFlags will be forwarded + // to a GetContext call internally. + already_AddRefed<nsStyleContext> + ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, nsStyleContext* aParentContext, + uint32_t aFlags = eNoFlags); + +#ifdef MOZ_XUL + // Get a style context for a XUL tree pseudo. aPseudoTag is the + // pseudo-tag to use and must be non-null. aParentContent must be + // non-null. aComparator must be non-null. + already_AddRefed<nsStyleContext> + ResolveXULTreePseudoStyle(mozilla::dom::Element* aParentElement, + nsIAtom* aPseudoTag, + nsStyleContext* aParentContext, + nsICSSPseudoComparator* aComparator); +#endif + + // Append all the currently-active font face rules to aArray. Return + // true for success and false for failure. + bool AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray); + + // Return the winning (in the cascade) @keyframes rule for the given name. + nsCSSKeyframesRule* KeyframesRuleForName(const nsString& aName); + + // Return the winning (in the cascade) @counter-style rule for the given name. + nsCSSCounterStyleRule* CounterStyleRuleForName(const nsAString& aName); + + // Fetch object for looking up font feature values + already_AddRefed<gfxFontFeatureValueSet> GetFontFeatureValuesLookup(); + + // Append all the currently-active font feature values rules to aArray. + // Return true for success and false for failure. + bool AppendFontFeatureValuesRules( + nsTArray<nsCSSFontFeatureValuesRule*>& aArray); + + // Append all the currently-active page rules to aArray. Return + // true for success and false for failure. + bool AppendPageRules(nsTArray<nsCSSPageRule*>& aArray); + + // Begin ignoring style context destruction, to avoid lots of unnecessary + // work on document teardown. + void BeginShutdown(); + + // Free all of the data associated with this style set. + void Shutdown(); + + // Get a new style context that lives in a different parent + // The new context will be the same as the old if the new parent is the + // same as the old parent. + // aElement should be non-null if this is a style context for an + // element or pseudo-element; in the latter case it should be the + // real element the pseudo-element is for. + already_AddRefed<nsStyleContext> + ReparentStyleContext(nsStyleContext* aStyleContext, + nsStyleContext* aNewParentContext, + mozilla::dom::Element* aElement); + + // Test if style is dependent on a document state. + bool HasDocumentStateDependentStyle(nsIContent* aContent, + mozilla::EventStates aStateMask); + + // Test if style is dependent on content state + nsRestyleHint HasStateDependentStyle(mozilla::dom::Element* aElement, + mozilla::EventStates aStateMask); + nsRestyleHint HasStateDependentStyle(mozilla::dom::Element* aElement, + mozilla::CSSPseudoElementType aPseudoType, + mozilla::dom::Element* aPseudoElement, + mozilla::EventStates aStateMask); + + // Test if style is dependent on the presence of an attribute. + nsRestyleHint HasAttributeDependentStyle(mozilla::dom::Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + bool aAttrHasChanged, + const nsAttrValue* aOtherValue, + mozilla::RestyleHintData& + aRestyleHintDataResult); + + /* + * Do any processing that needs to happen as a result of a change in + * the characteristics of the medium, and return whether style rules + * may have changed as a result. + */ + bool MediumFeaturesChanged(); + + // APIs for registering objects that can supply additional + // rules during processing. + void SetBindingManager(nsBindingManager* aBindingManager) + { + mBindingManager = aBindingManager; + } + + // APIs to manipulate the style sheet lists. The sheets in each + // list are stored with the most significant sheet last. + nsresult AppendStyleSheet(mozilla::SheetType aType, + mozilla::CSSStyleSheet* aSheet); + nsresult PrependStyleSheet(mozilla::SheetType aType, + mozilla::CSSStyleSheet* aSheet); + nsresult RemoveStyleSheet(mozilla::SheetType aType, + mozilla::CSSStyleSheet* aSheet); + nsresult ReplaceSheets(mozilla::SheetType aType, + const nsTArray<RefPtr<mozilla::CSSStyleSheet>>& aNewSheets); + nsresult InsertStyleSheetBefore(mozilla::SheetType aType, + mozilla::CSSStyleSheet* aNewSheet, + mozilla::CSSStyleSheet* aReferenceSheet); + + // Enable/Disable entire author style level (Doc, ScopedDoc & PresHint levels) + bool GetAuthorStyleDisabled() const; + nsresult SetAuthorStyleDisabled(bool aStyleDisabled); + + int32_t SheetCount(mozilla::SheetType aType) const { + return mSheets[aType].Length(); + } + + mozilla::CSSStyleSheet* StyleSheetAt(mozilla::SheetType aType, + int32_t aIndex) const { + return mSheets[aType][aIndex]; + } + + void AppendAllXBLStyleSheets(nsTArray<mozilla::CSSStyleSheet*>& aArray) const; + + nsresult RemoveDocStyleSheet(mozilla::CSSStyleSheet* aSheet); + nsresult AddDocStyleSheet(mozilla::CSSStyleSheet* aSheet, + nsIDocument* aDocument); + + void BeginUpdate(); + nsresult EndUpdate(); + + // Methods for reconstructing the tree; BeginReconstruct basically moves the + // old rule tree root and style context roots out of the way, + // and EndReconstruct destroys the old rule tree when we're done + nsresult BeginReconstruct(); + // Note: EndReconstruct should not be called if BeginReconstruct fails + void EndReconstruct(); + + bool IsInRuleTreeReconstruct() const { + return mInReconstruct; + } + + void RootStyleContextAdded() { + ++mRootStyleContextCount; + } + void RootStyleContextRemoved() { + MOZ_ASSERT(mRootStyleContextCount > 0); + --mRootStyleContextCount; + } + + // Return whether the rule tree has cached data such that we need to + // do dynamic change handling for changes that change the results of + // media queries or require rebuilding all style data. + // We don't care whether we have cached rule processors or whether + // they have cached rule cascades; getting the rule cascades again in + // order to do rule matching will get the correct rule cascade. + bool HasCachedStyleData() const { + return (mRuleTree && mRuleTree->TreeHasCachedData()) || mRootStyleContextCount > 0; + } + + // Notify the style set that a rulenode is no longer in use, or was + // just created and is not in use yet. + static const uint32_t kGCInterval = 300; + void RuleNodeUnused(nsRuleNode* aNode, bool aMayGC) { + ++mUnusedRuleNodeCount; + mUnusedRuleNodeList.insertBack(aNode); + if (aMayGC && mUnusedRuleNodeCount >= kGCInterval && !mInGC && !mInReconstruct) { + GCRuleTrees(); + } + } + + // Notify the style set that a rulenode that wasn't in use now is + void RuleNodeInUse(nsRuleNode* aNode) { + MOZ_ASSERT(mUnusedRuleNodeCount > 0); + --mUnusedRuleNodeCount; + aNode->removeFrom(mUnusedRuleNodeList); + } + + // Returns true if a restyle of the document is needed due to cloning + // sheet inners. + bool EnsureUniqueInnerOnCSSSheets(); + + // Called by CSSStyleSheet::EnsureUniqueInner to let us know it cloned + // its inner. + void SetNeedsRestyleAfterEnsureUniqueInner() { + mNeedsRestyleAfterEnsureUniqueInner = true; + } + + nsIStyleRule* InitialStyleRule(); + + bool HasRuleProcessorUsedByMultipleStyleSets(mozilla::SheetType aSheetType); + + // Tells the RestyleManager for the document using this style set + // to drop any nsCSSSelector pointers it has. + void ClearSelectors(); + + // Returns whether aSheetType represents a level of the cascade that uses + // CSSStyleSheets. See gCSSSheetTypes in nsStyleSet.cpp for the list + // of CSS sheet types. + static bool IsCSSSheetType(mozilla::SheetType aSheetType); + +private: + nsStyleSet(const nsStyleSet& aCopy) = delete; + nsStyleSet& operator=(const nsStyleSet& aCopy) = delete; + + // Free all the rules with reference-count zero. This continues iterating + // over the free list until it is empty, which allows immediate collection + // of nodes whose reference-count drops to zero during the destruction of + // a child node. This allows the collection of entire trees at once, since + // children hold their parents alive. + void GCRuleTrees(); + + nsresult DirtyRuleProcessors(mozilla::SheetType aType); + + // Update the rule processor list after a change to the style sheet list. + nsresult GatherRuleProcessors(mozilla::SheetType aType); + + void AddImportantRules(nsRuleNode* aCurrLevelNode, + nsRuleNode* aLastPrevLevelNode, + nsRuleWalker* aRuleWalker); + + // Move aRuleWalker forward by the appropriate rule if we need to add + // a rule due to property restrictions on pseudo-elements. + void WalkRestrictionRule(mozilla::CSSPseudoElementType aPseudoType, + nsRuleWalker* aRuleWalker); + + void WalkDisableTextZoomRule(mozilla::dom::Element* aElement, + nsRuleWalker* aRuleWalker); + +#ifdef DEBUG + // Just like AddImportantRules except it doesn't actually add anything; it + // just asserts that there are no important rules between aCurrLevelNode and + // aLastPrevLevelNode. + void AssertNoImportantRules(nsRuleNode* aCurrLevelNode, + nsRuleNode* aLastPrevLevelNode); + + // Just like AddImportantRules except it doesn't actually add anything; it + // just asserts that there are no CSS rules between aCurrLevelNode and + // aLastPrevLevelNode. Mostly useful for the preshint level. + void AssertNoCSSRules(nsRuleNode* aCurrLevelNode, + nsRuleNode* aLastPrevLevelNode); +#endif + + // Enumerate the rules in a way that cares about the order of the + // rules. + // aElement is the element the rules are for. It might be null. aData + // is the closure to pass to aCollectorFunc. If aContent is not null, + // aData must be a RuleProcessorData* + void FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, + RuleProcessorData* aData, mozilla::dom::Element* aElement, + nsRuleWalker* aRuleWalker); + + // Enumerate all the rules in a way that doesn't care about the order + // of the rules and break out if the enumeration is halted. + void WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc, + ElementDependentRuleProcessorData* aData, + bool aWalkAllXBLStylesheets); + + // Helper for ResolveStyleWithReplacement + // aPseudoElement must follow the same rules as for + // ResolvePseudoElementStyle, and be null for non-pseudo-element cases + nsRuleNode* RuleNodeWithReplacement(mozilla::dom::Element* aElement, + mozilla::dom::Element* aPseudoElement, + nsRuleNode* aOldRuleNode, + mozilla::CSSPseudoElementType aPseudoType, + nsRestyleHint aReplacements); + + already_AddRefed<nsStyleContext> + GetContext(nsStyleContext* aParentContext, + nsRuleNode* aRuleNode, + nsRuleNode* aVisitedRuleNode, + nsIAtom* aPseudoTag, + mozilla::CSSPseudoElementType aPseudoType, + mozilla::dom::Element* aElementForAnimation, + uint32_t aFlags); + + nsPresContext* PresContext() { return mRuleTree->PresContext(); } + + // The sheets in each array in mSheets are stored with the most significant + // sheet last. + // The arrays for ePresHintSheet, eStyleAttrSheet, eTransitionSheet, + // eAnimationSheet and eSVGAttrAnimationSheet are always empty. + // (FIXME: We should reduce the storage needed for them.) + mozilla::EnumeratedArray<mozilla::SheetType, mozilla::SheetType::Count, + nsTArray<RefPtr<mozilla::CSSStyleSheet>>> mSheets; + + // mRuleProcessors[eScopedDocSheet] is always null; rule processors + // for scoped style sheets are stored in mScopedDocSheetRuleProcessors. + mozilla::EnumeratedArray<mozilla::SheetType, mozilla::SheetType::Count, + nsCOMPtr<nsIStyleRuleProcessor>> mRuleProcessors; + + // Rule processors for HTML5 scoped style sheets, one per scope. + nsTArray<nsCOMPtr<nsIStyleRuleProcessor> > mScopedDocSheetRuleProcessors; + + RefPtr<nsBindingManager> mBindingManager; + + RefPtr<nsRuleNode> mRuleTree; // This is the root of our rule tree. It is a + // lexicographic tree of matched rules that style + // contexts use to look up properties. + + uint16_t mBatching; + + unsigned mInShutdown : 1; + unsigned mInGC : 1; + unsigned mAuthorStyleDisabled: 1; + unsigned mInReconstruct : 1; + unsigned mInitFontFeatureValuesLookup : 1; + unsigned mNeedsRestyleAfterEnsureUniqueInner : 1; + unsigned mDirty : int(mozilla::SheetType::Count); // one bit per sheet type + + uint32_t mRootStyleContextCount; + +#ifdef DEBUG + // In debug builds, we stash a weak pointer here to the old root during + // reconstruction. During GC, we check for this pointer, and null it out + // when we encounter it. This allows us to assert that the old root (and + // thus all of its subtree) was GCed after reconstruction, which implies + // that there are no style contexts holding on to old rule nodes. + nsRuleNode* mOldRootNode; +#endif + + // Track our rule nodes with zero refcount. When this hits a threshold, we + // sweep and free. Keeping unused rule nodes around for a bit allows us to + // reuse them in many cases. + mozilla::LinkedList<nsRuleNode> mUnusedRuleNodeList; + uint32_t mUnusedRuleNodeCount; + + // Empty style rules to force things that restrict which properties + // apply into different branches of the rule tree. + RefPtr<nsEmptyStyleRule> mFirstLineRule, mFirstLetterRule, mPlaceholderRule; + + // Style rule which sets all properties to their initial values for + // determining when context-sensitive values are in use. + RefPtr<nsInitialStyleRule> mInitialStyleRule; + + // Style rule that sets the internal -x-text-zoom property on + // <svg:text> elements to disable the effect of text zooming. + RefPtr<nsDisableTextZoomStyleRule> mDisableTextZoomStyleRule; + + // whether font feature values lookup object needs initialization + RefPtr<gfxFontFeatureValueSet> mFontFeatureValuesLookup; +}; + +#ifdef MOZILLA_INTERNAL_API +inline +void nsRuleNode::AddRef() +{ + if (mRefCnt++ == 0) { + MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(), + "ServoStyleSets should not have rule nodes"); + mPresContext->StyleSet()->AsGecko()->RuleNodeInUse(this); + } +} + +inline +void nsRuleNode::Release() +{ + if (--mRefCnt == 0) { + MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(), + "ServoStyleSets should not have rule nodes"); + mPresContext->StyleSet()->AsGecko()->RuleNodeUnused(this, /* aMayGC = */ true); + } +} +#endif + +#endif |