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: * the container for the style sheets that apply to a presentation, and michael@0: * the internal API that the style system exposes for creating (and michael@0: * potentially re-creating) style contexts michael@0: */ michael@0: michael@0: #ifndef nsStyleSet_h_ michael@0: #define nsStyleSet_h_ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "nsIStyleRuleProcessor.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: #include "nsBindingManager.h" michael@0: #include "nsRuleNode.h" michael@0: #include "nsTArray.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIStyleRule.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "gfxFontFeatures.h" michael@0: michael@0: class nsCSSFontFaceRule; michael@0: class nsCSSKeyframesRule; michael@0: class nsCSSFontFeatureValuesRule; michael@0: class nsCSSPageRule; michael@0: class nsRuleWalker; michael@0: struct ElementDependentRuleProcessorData; michael@0: struct TreeMatchContext; michael@0: michael@0: namespace mozilla { michael@0: class EventStates; michael@0: } // namespace mozilla michael@0: michael@0: class nsEmptyStyleRule MOZ_FINAL : public nsIStyleRule michael@0: { michael@0: NS_DECL_ISUPPORTS michael@0: virtual void MapRuleInfoInto(nsRuleData* aRuleData) MOZ_OVERRIDE; michael@0: #ifdef DEBUG michael@0: virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE; michael@0: #endif michael@0: }; michael@0: michael@0: class nsInitialStyleRule MOZ_FINAL : public nsIStyleRule michael@0: { michael@0: NS_DECL_ISUPPORTS michael@0: virtual void MapRuleInfoInto(nsRuleData* aRuleData) MOZ_OVERRIDE; michael@0: #ifdef DEBUG michael@0: virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE; michael@0: #endif michael@0: }; michael@0: michael@0: class nsDisableTextZoomStyleRule MOZ_FINAL : public nsIStyleRule michael@0: { michael@0: NS_DECL_ISUPPORTS michael@0: virtual void MapRuleInfoInto(nsRuleData* aRuleData) MOZ_OVERRIDE; michael@0: #ifdef DEBUG michael@0: virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE; michael@0: #endif michael@0: }; michael@0: michael@0: // The style set object is created by the document viewer and ownership is michael@0: // then handed off to the PresShell. Only the PresShell should delete a michael@0: // style set. michael@0: michael@0: class nsStyleSet michael@0: { michael@0: public: michael@0: nsStyleSet(); michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: void Init(nsPresContext *aPresContext); michael@0: michael@0: nsRuleNode* GetRuleTree() { return mRuleTree; } michael@0: michael@0: // enable / disable the Quirk style sheet michael@0: void EnableQuirkStyleSheet(bool aEnable); michael@0: michael@0: // get a style context for a non-pseudo frame. michael@0: already_AddRefed michael@0: ResolveStyleFor(mozilla::dom::Element* aElement, michael@0: nsStyleContext* aParentContext); michael@0: michael@0: already_AddRefed michael@0: ResolveStyleFor(mozilla::dom::Element* aElement, michael@0: nsStyleContext* aParentContext, michael@0: TreeMatchContext& aTreeMatchContext); michael@0: michael@0: // Get a style context (with the given parent) for the michael@0: // sequence of style rules in the |aRules| array. michael@0: already_AddRefed michael@0: ResolveStyleForRules(nsStyleContext* aParentContext, michael@0: const nsTArray< nsCOMPtr > &aRules); michael@0: michael@0: // used in ResolveStyleForRules below michael@0: struct RuleAndLevel michael@0: { michael@0: nsIStyleRule* mRule; michael@0: uint8_t mLevel; michael@0: }; michael@0: michael@0: // Get a new style context for aElement for the rules in aRules michael@0: // aRules is an array of rules and their levels in reverse order, michael@0: // that is from the leaf-most to the root-most rule in the rule tree. michael@0: already_AddRefed michael@0: ResolveStyleForRules(nsStyleContext* aParentContext, michael@0: nsStyleContext* aOldStyle, michael@0: const nsTArray& aRules); michael@0: michael@0: // Get a style context that represents aBaseContext, but as though michael@0: // it additionally matched the rules in the aRules array (in that michael@0: // order, as more specific than any other rules). michael@0: already_AddRefed michael@0: ResolveStyleByAddingRules(nsStyleContext* aBaseContext, michael@0: const nsCOMArray &aRules); michael@0: michael@0: // Get a style context for a non-element (which no rules will match), michael@0: // such as text nodes, placeholder frames, and the nsFirstLetterFrame michael@0: // for everything after the first letter. michael@0: // michael@0: // Perhaps this should go away and we shouldn't even create style michael@0: // contexts for such content nodes. However, not doing any rule michael@0: // matching for them is a first step. michael@0: already_AddRefed michael@0: ResolveStyleForNonElement(nsStyleContext* aParentContext); michael@0: michael@0: // Get a style context for a pseudo-element. aParentElement must be michael@0: // non-null. aPseudoID is the nsCSSPseudoElements::Type for the michael@0: // pseudo-element. aPseudoElement must be non-null if the pseudo-element michael@0: // type is one that allows user action pseudo-classes after it; otherwise, michael@0: // it is ignored. michael@0: already_AddRefed michael@0: ResolvePseudoElementStyle(mozilla::dom::Element* aParentElement, michael@0: nsCSSPseudoElements::Type aType, michael@0: nsStyleContext* aParentContext, michael@0: mozilla::dom::Element* aPseudoElement); michael@0: michael@0: // This functions just like ResolvePseudoElementStyle except that it will michael@0: // return nullptr if there are no explicit style rules for that michael@0: // pseudo element. michael@0: already_AddRefed michael@0: ProbePseudoElementStyle(mozilla::dom::Element* aParentElement, michael@0: nsCSSPseudoElements::Type aType, michael@0: nsStyleContext* aParentContext); michael@0: already_AddRefed michael@0: ProbePseudoElementStyle(mozilla::dom::Element* aParentElement, michael@0: nsCSSPseudoElements::Type aType, michael@0: nsStyleContext* aParentContext, michael@0: TreeMatchContext& aTreeMatchContext, michael@0: mozilla::dom::Element* aPseudoElement = nullptr); michael@0: michael@0: // Get a style context for an anonymous box. aPseudoTag is the michael@0: // pseudo-tag to use and must be non-null. michael@0: already_AddRefed michael@0: ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, nsStyleContext* aParentContext); michael@0: michael@0: #ifdef MOZ_XUL michael@0: // Get a style context for a XUL tree pseudo. aPseudoTag is the michael@0: // pseudo-tag to use and must be non-null. aParentContent must be michael@0: // non-null. aComparator must be non-null. michael@0: already_AddRefed michael@0: ResolveXULTreePseudoStyle(mozilla::dom::Element* aParentElement, michael@0: nsIAtom* aPseudoTag, michael@0: nsStyleContext* aParentContext, michael@0: nsICSSPseudoComparator* aComparator); michael@0: #endif michael@0: michael@0: // Append all the currently-active font face rules to aArray. Return michael@0: // true for success and false for failure. michael@0: bool AppendFontFaceRules(nsPresContext* aPresContext, michael@0: nsTArray& aArray); michael@0: michael@0: // Return the winning (in the cascade) @keyframes rule for the given name. michael@0: nsCSSKeyframesRule* KeyframesRuleForName(nsPresContext* aPresContext, michael@0: const nsString& aName); michael@0: michael@0: // Fetch object for looking up font feature values michael@0: already_AddRefed GetFontFeatureValuesLookup(); michael@0: michael@0: // Append all the currently-active font feature values rules to aArray. michael@0: // Return true for success and false for failure. michael@0: bool AppendFontFeatureValuesRules(nsPresContext* aPresContext, michael@0: nsTArray& aArray); michael@0: michael@0: // Append all the currently-active page rules to aArray. Return michael@0: // true for success and false for failure. michael@0: bool AppendPageRules(nsPresContext* aPresContext, michael@0: nsTArray& aArray); michael@0: michael@0: // Begin ignoring style context destruction, to avoid lots of unnecessary michael@0: // work on document teardown. michael@0: void BeginShutdown(nsPresContext* aPresContext); michael@0: michael@0: // Free all of the data associated with this style set. michael@0: void Shutdown(nsPresContext* aPresContext); michael@0: michael@0: // Notification that a style context is being destroyed. michael@0: void NotifyStyleContextDestroyed(nsPresContext* aPresContext, michael@0: nsStyleContext* aStyleContext); michael@0: michael@0: // Get a new style context that lives in a different parent michael@0: // The new context will be the same as the old if the new parent is the michael@0: // same as the old parent. michael@0: // aElement should be non-null if this is a style context for an michael@0: // element or pseudo-element; in the latter case it should be the michael@0: // real element the pseudo-element is for. michael@0: already_AddRefed michael@0: ReparentStyleContext(nsStyleContext* aStyleContext, michael@0: nsStyleContext* aNewParentContext, michael@0: mozilla::dom::Element* aElement); michael@0: michael@0: // Test if style is dependent on a document state. michael@0: bool HasDocumentStateDependentStyle(nsPresContext* aPresContext, michael@0: nsIContent* aContent, michael@0: mozilla::EventStates aStateMask); michael@0: michael@0: // Test if style is dependent on content state michael@0: nsRestyleHint HasStateDependentStyle(nsPresContext* aPresContext, michael@0: mozilla::dom::Element* aElement, michael@0: mozilla::EventStates aStateMask); michael@0: nsRestyleHint HasStateDependentStyle(nsPresContext* aPresContext, michael@0: mozilla::dom::Element* aElement, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: mozilla::dom::Element* aPseudoElement, michael@0: mozilla::EventStates aStateMask); michael@0: michael@0: // Test if style is dependent on the presence of an attribute. michael@0: nsRestyleHint HasAttributeDependentStyle(nsPresContext* aPresContext, michael@0: mozilla::dom::Element* aElement, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType, michael@0: bool aAttrHasChanged); michael@0: michael@0: /* michael@0: * Do any processing that needs to happen as a result of a change in michael@0: * the characteristics of the medium, and return whether style rules michael@0: * may have changed as a result. michael@0: */ michael@0: bool MediumFeaturesChanged(nsPresContext* aPresContext); michael@0: michael@0: // APIs for registering objects that can supply additional michael@0: // rules during processing. michael@0: void SetBindingManager(nsBindingManager* aBindingManager) michael@0: { michael@0: mBindingManager = aBindingManager; michael@0: } michael@0: michael@0: // The "origins" of the CSS cascade, from lowest precedence to michael@0: // highest (for non-!important rules). michael@0: enum sheetType { michael@0: eAgentSheet, // CSS michael@0: eUserSheet, // CSS michael@0: ePresHintSheet, michael@0: eDocSheet, // CSS michael@0: eScopedDocSheet, michael@0: eStyleAttrSheet, michael@0: eOverrideSheet, // CSS michael@0: eAnimationSheet, michael@0: eTransitionSheet, michael@0: eSheetTypeCount michael@0: // be sure to keep the number of bits in |mDirty| below and in michael@0: // NS_RULE_NODE_LEVEL_MASK updated when changing the number of sheet michael@0: // types michael@0: }; michael@0: michael@0: // APIs to manipulate the style sheet lists. The sheets in each michael@0: // list are stored with the most significant sheet last. michael@0: nsresult AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet); michael@0: nsresult PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet); michael@0: nsresult RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet); michael@0: nsresult ReplaceSheets(sheetType aType, michael@0: const nsCOMArray &aNewSheets); michael@0: nsresult InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet, michael@0: nsIStyleSheet *aReferenceSheet); michael@0: michael@0: nsresult DirtyRuleProcessors(sheetType aType); michael@0: michael@0: // Enable/Disable entire author style level (Doc, ScopedDoc & PresHint levels) michael@0: bool GetAuthorStyleDisabled(); michael@0: nsresult SetAuthorStyleDisabled(bool aStyleDisabled); michael@0: michael@0: int32_t SheetCount(sheetType aType) const { michael@0: return mSheets[aType].Count(); michael@0: } michael@0: michael@0: nsIStyleSheet* StyleSheetAt(sheetType aType, int32_t aIndex) const { michael@0: return mSheets[aType].ObjectAt(aIndex); michael@0: } michael@0: michael@0: nsresult RemoveDocStyleSheet(nsIStyleSheet* aSheet); michael@0: nsresult AddDocStyleSheet(nsIStyleSheet* aSheet, nsIDocument* aDocument); michael@0: michael@0: void BeginUpdate(); michael@0: nsresult EndUpdate(); michael@0: michael@0: // Methods for reconstructing the tree; BeginReconstruct basically moves the michael@0: // old rule tree root and style context roots out of the way, michael@0: // and EndReconstruct destroys the old rule tree when we're done michael@0: nsresult BeginReconstruct(); michael@0: // Note: EndReconstruct should not be called if BeginReconstruct fails michael@0: void EndReconstruct(); michael@0: michael@0: // Let the style set know that a particular sheet is the quirks sheet. This michael@0: // sheet must already have been added to the UA sheets. The pointer must not michael@0: // be null. This should only be called once for a given style set. michael@0: void SetQuirkStyleSheet(nsIStyleSheet* aQuirkStyleSheet); michael@0: michael@0: // Return whether the rule tree has cached data such that we need to michael@0: // do dynamic change handling for changes that change the results of michael@0: // media queries or require rebuilding all style data. michael@0: // We don't care whether we have cached rule processors or whether michael@0: // they have cached rule cascades; getting the rule cascades again in michael@0: // order to do rule matching will get the correct rule cascade. michael@0: bool HasCachedStyleData() const { michael@0: return (mRuleTree && mRuleTree->TreeHasCachedData()) || !mRoots.IsEmpty(); michael@0: } michael@0: michael@0: // Notify the style set that a rulenode is no longer in use, or was michael@0: // just created and is not in use yet. michael@0: void RuleNodeUnused() { michael@0: ++mUnusedRuleNodeCount; michael@0: } michael@0: michael@0: // Notify the style set that a rulenode that wasn't in use now is michael@0: void RuleNodeInUse() { michael@0: --mUnusedRuleNodeCount; michael@0: } michael@0: michael@0: nsCSSStyleSheet::EnsureUniqueInnerResult EnsureUniqueInnerOnCSSSheets(); michael@0: michael@0: nsIStyleRule* InitialStyleRule(); michael@0: michael@0: private: michael@0: nsStyleSet(const nsStyleSet& aCopy) MOZ_DELETE; michael@0: nsStyleSet& operator=(const nsStyleSet& aCopy) MOZ_DELETE; michael@0: michael@0: // Run mark-and-sweep GC on mRuleTree and mOldRuleTrees, based on mRoots. michael@0: void GCRuleTrees(); michael@0: michael@0: // Update the rule processor list after a change to the style sheet list. michael@0: nsresult GatherRuleProcessors(sheetType aType); michael@0: michael@0: void AddImportantRules(nsRuleNode* aCurrLevelNode, michael@0: nsRuleNode* aLastPrevLevelNode, michael@0: nsRuleWalker* aRuleWalker); michael@0: michael@0: // Move aRuleWalker forward by the appropriate rule if we need to add michael@0: // a rule due to property restrictions on pseudo-elements. michael@0: void WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType, michael@0: nsRuleWalker* aRuleWalker); michael@0: michael@0: void WalkDisableTextZoomRule(mozilla::dom::Element* aElement, michael@0: nsRuleWalker* aRuleWalker); michael@0: michael@0: #ifdef DEBUG michael@0: // Just like AddImportantRules except it doesn't actually add anything; it michael@0: // just asserts that there are no important rules between aCurrLevelNode and michael@0: // aLastPrevLevelNode. michael@0: void AssertNoImportantRules(nsRuleNode* aCurrLevelNode, michael@0: nsRuleNode* aLastPrevLevelNode); michael@0: michael@0: // Just like AddImportantRules except it doesn't actually add anything; it michael@0: // just asserts that there are no CSS rules between aCurrLevelNode and michael@0: // aLastPrevLevelNode. Mostly useful for the preshint level. michael@0: void AssertNoCSSRules(nsRuleNode* aCurrLevelNode, michael@0: nsRuleNode* aLastPrevLevelNode); michael@0: #endif michael@0: michael@0: // Enumerate the rules in a way that cares about the order of the michael@0: // rules. michael@0: // aElement is the element the rules are for. It might be null. aData michael@0: // is the closure to pass to aCollectorFunc. If aContent is not null, michael@0: // aData must be a RuleProcessorData* michael@0: void FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, michael@0: RuleProcessorData* aData, mozilla::dom::Element* aElement, michael@0: nsRuleWalker* aRuleWalker); michael@0: michael@0: // Enumerate all the rules in a way that doesn't care about the order michael@0: // of the rules and break out if the enumeration is halted. michael@0: void WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc, michael@0: ElementDependentRuleProcessorData* aData, michael@0: bool aWalkAllXBLStylesheets); michael@0: michael@0: /** michael@0: * Bit-flags that can be passed to GetContext() in its parameter 'aFlags'. michael@0: */ michael@0: enum { michael@0: eNoFlags = 0, michael@0: eIsLink = 1 << 0, michael@0: eIsVisitedLink = 1 << 1, michael@0: eDoAnimation = 1 << 2, michael@0: michael@0: // Indicates that we should skip the flex-item-specific chunk of michael@0: // ApplyStyleFixups(). This is useful if our parent has "display: flex" michael@0: // but we can tell it's not going to actually be a flex container (e.g. if michael@0: // it's the outer frame of a button widget, and we're the inline frame for michael@0: // the button's label). michael@0: eSkipFlexItemStyleFixup = 1 << 3 michael@0: }; michael@0: michael@0: already_AddRefed michael@0: GetContext(nsStyleContext* aParentContext, michael@0: nsRuleNode* aRuleNode, michael@0: nsRuleNode* aVisitedRuleNode, michael@0: nsIAtom* aPseudoTag, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: mozilla::dom::Element* aElementForAnimation, michael@0: uint32_t aFlags); michael@0: michael@0: nsPresContext* PresContext() { return mRuleTree->PresContext(); } michael@0: michael@0: // The sheets in each array in mSheets are stored with the most significant michael@0: // sheet last. michael@0: // The arrays for ePresHintSheet, eStyleAttrSheet, eTransitionSheet, michael@0: // and eAnimationSheet are always empty. (FIXME: We should reduce michael@0: // the storage needed for them.) michael@0: nsCOMArray mSheets[eSheetTypeCount]; michael@0: michael@0: // mRuleProcessors[eScopedDocSheet] is always null; rule processors michael@0: // for scoped style sheets are stored in mScopedDocSheetRuleProcessors. michael@0: nsCOMPtr mRuleProcessors[eSheetTypeCount]; michael@0: michael@0: // Rule processors for HTML5 scoped style sheets, one per scope. michael@0: nsTArray > mScopedDocSheetRuleProcessors; michael@0: michael@0: // cached instance for enabling/disabling michael@0: nsCOMPtr mQuirkStyleSheet; michael@0: michael@0: nsRefPtr mBindingManager; michael@0: michael@0: nsRuleNode* mRuleTree; // This is the root of our rule tree. It is a michael@0: // lexicographic tree of matched rules that style michael@0: // contexts use to look up properties. michael@0: michael@0: uint16_t mBatching; michael@0: michael@0: unsigned mInShutdown : 1; michael@0: unsigned mAuthorStyleDisabled: 1; michael@0: unsigned mInReconstruct : 1; michael@0: unsigned mInitFontFeatureValuesLookup : 1; michael@0: unsigned mDirty : 9; // one dirty bit is used per sheet type michael@0: michael@0: uint32_t mUnusedRuleNodeCount; // used to batch rule node GC michael@0: nsTArray mRoots; // style contexts with no parent michael@0: michael@0: // Empty style rules to force things that restrict which properties michael@0: // apply into different branches of the rule tree. michael@0: nsRefPtr mFirstLineRule, mFirstLetterRule, mPlaceholderRule; michael@0: michael@0: // Style rule which sets all properties to their initial values for michael@0: // determining when context-sensitive values are in use. michael@0: nsRefPtr mInitialStyleRule; michael@0: michael@0: // Style rule that sets the internal -x-text-zoom property on michael@0: // elements to disable the effect of text zooming. michael@0: nsRefPtr mDisableTextZoomStyleRule; michael@0: michael@0: // Old rule trees, which should only be non-empty between michael@0: // BeginReconstruct and EndReconstruct, but in case of bugs that cause michael@0: // style contexts to exist too long, may last longer. michael@0: nsTArray mOldRuleTrees; michael@0: michael@0: // whether font feature values lookup object needs initialization michael@0: nsRefPtr mFontFeatureValuesLookup; michael@0: }; michael@0: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: inline michael@0: void nsRuleNode::AddRef() michael@0: { michael@0: if (mRefCnt++ == 0 && !IsRoot()) { michael@0: mPresContext->StyleSet()->RuleNodeInUse(); michael@0: } michael@0: } michael@0: michael@0: inline michael@0: void nsRuleNode::Release() michael@0: { michael@0: if (--mRefCnt == 0 && !IsRoot()) { michael@0: mPresContext->StyleSet()->RuleNodeUnused(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #endif