diff options
Diffstat (limited to 'layout/style/ServoStyleSet.h')
-rw-r--r-- | layout/style/ServoStyleSet.h | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h new file mode 100644 index 0000000000..fb051002c8 --- /dev/null +++ b/layout/style/ServoStyleSet.h @@ -0,0 +1,652 @@ +/* -*- 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/. */ + +#ifndef mozilla_ServoStyleSet_h +#define mozilla_ServoStyleSet_h + +#include "mozilla/AnonymousContentKey.h" +#include "mozilla/AtomArray.h" +#include "mozilla/EnumeratedArray.h" +#include "mozilla/Maybe.h" +#include "mozilla/PostTraversalTask.h" +#include "mozilla/ServoBindingTypes.h" +#include "mozilla/ServoUtils.h" +#include "mozilla/dom/RustTypes.h" +#include "mozilla/UniquePtr.h" +#include "MainThreadUtils.h" +#include "nsCSSPseudoElements.h" +#include "nsCSSAnonBoxes.h" +#include "nsChangeHint.h" +#include "nsCoord.h" +#include "nsAtom.h" +#include "nsIMemoryReporter.h" +#include "nsTArray.h" +#include "nsIMemoryReporter.h" + +namespace mozilla { +enum class MediaFeatureChangeReason : uint16_t; +enum class StylePageSizeOrientation : uint8_t; +enum class StyleRuleChangeKind : uint32_t; + +template <typename Integer, typename Number, typename LinearStops> +struct StyleTimingFunction; +struct StylePiecewiseLinearFunction; +using StyleComputedTimingFunction = + StyleTimingFunction<int32_t, float, StylePiecewiseLinearFunction>; + +namespace css { +class Rule; +} // namespace css +namespace dom { +class CSSImportRule; +class Element; +class ShadowRoot; +} // namespace dom +namespace gfx { +class FontPaletteValueSet; +} // namespace gfx +class StyleSheet; +struct Keyframe; +class ServoElementSnapshotTable; +class ComputedStyle; +class ServoStyleRuleMap; +class StyleSheet; +} // namespace mozilla +class gfxFontFeatureValueSet; +class nsIContent; + +class nsPresContext; +class nsWindowSizes; +struct TreeMatchContext; + +namespace mozilla { + +// A few flags used to track which kind of stylist state we may need to +// update. +enum class StylistState : uint8_t { + // The stylist is not dirty, we should do nothing. + NotDirty = 0, + + // The style sheets have changed, so we need to update the style data. + StyleSheetsDirty = 1 << 0, + + // Some of the style sheets of the shadow trees in the document have + // changed. + ShadowDOMStyleSheetsDirty = 1 << 1, +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StylistState) + +enum class StyleOrigin : uint8_t; + +// Bitfield type to represent Servo stylesheet origins. +enum class OriginFlags : uint8_t { + UserAgent = 0x01, + User = 0x02, + Author = 0x04, + All = 0x07, +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OriginFlags) + +/** + * The set of style sheets that apply to a document, backed by a Servo + * Stylist. A ServoStyleSet contains StyleSheets. + */ +class ServoStyleSet { + friend class RestyleManager; + using SnapshotTable = ServoElementSnapshotTable; + using Origin = StyleOrigin; + + // We assert that these match the Servo ones in the definition of this array. + static constexpr Origin kOrigins[] = { + Origin(static_cast<uint8_t>(OriginFlags::UserAgent)), + Origin(static_cast<uint8_t>(OriginFlags::User)), + Origin(static_cast<uint8_t>(OriginFlags::Author)), + }; + + public: + static bool IsInServoTraversal() { return mozilla::IsInServoTraversal(); } + +#ifdef DEBUG + // Used for debug assertions. We make this debug-only to prevent callers from + // accidentally using it instead of IsInServoTraversal, which is cheaper. We + // can change this if a use-case arises. + static bool IsCurrentThreadInServoTraversal(); +#endif + + static ServoStyleSet* Current() { return sInServoTraversal; } + + explicit ServoStyleSet(dom::Document&); + ~ServoStyleSet(); + + void ShellDetachedFromDocument(); + + // Called when a rules in a stylesheet in this set, or a child sheet of that, + // are mutated from CSSOM. + void RuleAdded(StyleSheet&, css::Rule&); + void RuleRemoved(StyleSheet&, css::Rule&); + void RuleChanged(StyleSheet&, css::Rule*, StyleRuleChangeKind); + void SheetCloned(StyleSheet&); + void ImportRuleLoaded(dom::CSSImportRule&, StyleSheet&); + + // Runs style invalidation due to document state changes. + void InvalidateStyleForDocumentStateChanges( + dom::DocumentState aStatesChanged); + + void RecordShadowStyleChange(dom::ShadowRoot&); + + bool StyleSheetsHaveChanged() const { return StylistNeedsUpdate(); } + + RestyleHint MediumFeaturesChanged(MediaFeatureChangeReason); + + // Evaluates a given SourceSizeList, returning the optimal viewport width in + // app units. + // + // The SourceSizeList parameter can be null, in which case it will return + // 100vw. + inline nscoord EvaluateSourceSizeList( + const RawServoSourceSizeList* aSourceSizeList) const; + + void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const; + const RawServoStyleSet* RawSet() const { return mRawSet.get(); } + + bool GetAuthorStyleDisabled() const { return mAuthorStyleDisabled; } + + bool UsesFontMetrics() const; + + void SetAuthorStyleDisabled(bool aStyleDisabled); + + // Get a CopmutedStyle for a text node (which no rules will match). + // + // The returned ComputedStyle 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<ComputedStyle> ResolveStyleForText( + nsIContent* aTextNode, ComputedStyle* aParentStyle); + + // Get a ComputedStyle for a first-letter continuation (which no rules will + // match). + // + // The returned ComputedStyle will have + // nsCSSAnonBoxes::firstLetterContinuation() as its pseudo. + // + // (Perhaps nsCSSAnonBoxes::firstLetterContinuation() should go away and we + // shouldn't even create ComputedStyles for such frames. However, not doing + // any rule matching for them is a first step. And right now we do use this + // ComputedStyle for some things) + already_AddRefed<ComputedStyle> ResolveStyleForFirstLetterContinuation( + ComputedStyle* aParentStyle); + + // Get a ComputedStyle for a placeholder frame (which no rules will match). + // + // The returned ComputedStyle will have nsCSSAnonBoxes::oofPlaceholder() as + // its pseudo. + // + // (Perhaps nsCSSAnonBoxes::oofPaceholder() should go away and we shouldn't + // even create ComputedStyle for placeholders. However, not doing any rule + // matching for them is a first step.) + already_AddRefed<ComputedStyle> ResolveStyleForPlaceholder(); + + // Returns whether a given pseudo-element should exist or not. + static bool GeneratedContentPseudoExists(const ComputedStyle& aParentStyle, + const ComputedStyle& aPseudoStyle); + + enum class IsProbe { + No, + Yes, + }; + + // Get a style for a pseudo-element. + // + // If IsProbe is Yes, then no style is returned if there are no rules matching + // for the pseudo-element, or GeneratedContentPseudoExists returns false. + // + // If IsProbe is No, then the style is guaranteed to be non-null. + already_AddRefed<ComputedStyle> ResolvePseudoElementStyle( + const dom::Element& aOriginatingElement, PseudoStyleType, + ComputedStyle* aParentStyle, IsProbe = IsProbe::No); + + already_AddRefed<ComputedStyle> ProbePseudoElementStyle( + const dom::Element& aOriginatingElement, PseudoStyleType aType, + ComputedStyle* aParentStyle) { + return ResolvePseudoElementStyle(aOriginatingElement, aType, aParentStyle, + IsProbe::Yes); + } + + // Resolves style for a (possibly-pseudo) Element without assuming that the + // style has been resolved. If the element was unstyled and a new style + // was resolved, it is not stored in the DOM. (That is, the element remains + // unstyled.) + already_AddRefed<ComputedStyle> ResolveStyleLazily( + const dom::Element&, PseudoStyleType = PseudoStyleType::NotPseudo, + StyleRuleInclusion = StyleRuleInclusion::All); + + // Get a ComputedStyle for an anonymous box. The pseudo type must be an + // inheriting anon box. + already_AddRefed<ComputedStyle> ResolveInheritingAnonymousBoxStyle( + PseudoStyleType, ComputedStyle* aParentStyle); + + // Get a ComputedStyle for an anonymous box. The pseudo type must be + // a non-inheriting anon box. + already_AddRefed<ComputedStyle> ResolveNonInheritingAnonymousBoxStyle( + PseudoStyleType aType) { + return ResolveNonInheritingAnonymousBoxStyle(aType, nullptr); + } + + already_AddRefed<ComputedStyle> ResolvePageContentStyle( + const nsAtom* aPageName) { + return ResolveNonInheritingAnonymousBoxStyle(PseudoStyleType::pageContent, + aPageName); + } + + already_AddRefed<ComputedStyle> ResolveXULTreePseudoStyle( + dom::Element* aParentElement, nsCSSAnonBoxPseudoStaticAtom* aPseudoTag, + ComputedStyle* aParentStyle, const AtomArray& aInputWord); + + size_t SheetCount(Origin) const; + StyleSheet* SheetAt(Origin, size_t aIndex) const; + + // Gets the default orientation of CSS pages. + // This will return portrait or landscape both for a landscape/portrait + // value to page-size, as well as for an explicit size or paper name which + // is not square. + // If the value is auto or square, then returns nothing. + Maybe<StylePageSizeOrientation> GetDefaultPageSizeOrientation( + const nsAtom* aFirstPageName); + + void AppendAllNonDocumentAuthorSheets(nsTArray<StyleSheet*>& aArray) const; + + // Manage the set of style sheets in the style set + void AppendStyleSheet(StyleSheet&); + void InsertStyleSheetBefore(StyleSheet&, StyleSheet& aReferenceSheet); + void RemoveStyleSheet(StyleSheet&); + void AddDocStyleSheet(StyleSheet&); + + /** + * Performs a Servo traversal to compute style for all dirty nodes in the + * document. + * + * This will traverse all of the document's style roots (that is, its document + * element, and the roots of the document-level native anonymous content). + * + * We specify |ForCSSRuleChanges| to try to update all CSS animations + * when we call this function due to CSS rule changes since @keyframes rules + * may have changed. + * + * Returns true if a post-traversal is required. + */ + bool StyleDocument(ServoTraversalFlags aFlags); + + /** + * Eagerly styles a subtree of unstyled nodes that was just appended to the + * tree. This is used in situations where we need the style immediately and + * cannot wait for a future batch restyle. + */ + void StyleNewSubtree(dom::Element* aRoot); + + /** + * Helper for correctly calling UpdateStylist without paying the cost of an + * extra function call in the common no-rebuild-needed case. + */ + void UpdateStylistIfNeeded() { + if (StylistNeedsUpdate()) { + UpdateStylist(); + } + } + + /** + * Checks whether the rule tree has crossed its threshold for unused nodes, + * and if so, frees them. + */ + void MaybeGCRuleTree(); + + /** + * Returns true if the given element may be used as the root of a style + * traversal. Reasons for false include having an unstyled parent, or having + * a parent that is display:none. + * + * Most traversal callsites don't need to check this, but some do. + */ + static bool MayTraverseFrom(const dom::Element* aElement); + +#ifdef DEBUG + void AssertTreeIsClean(); +#else + void AssertTreeIsClean() {} +#endif + + /** + * Clears any cached style data that may depend on all sorts of computed + * values. + * + * Right now this clears the non-inheriting ComputedStyle cache, resets the + * default computed values, and clears cached anonymous content style. + * + * This does _not_, however, clear the stylist. + */ + void ClearCachedStyleData(); + + /** + * Notifies the Servo stylesheet that the document's compatibility mode has + * changed. + */ + void CompatibilityModeChanged(); + + template <typename T> + void EnumerateStyleSheets(T aCb) { + for (auto origin : kOrigins) { + for (size_t i = 0, count = SheetCount(origin); i < count; ++i) { + aCb(*SheetAt(origin, i)); + } + } + } + + /** + * Resolve style for the given element, and return it as a + * ComputedStyle. + * + * FIXME(emilio): Is there a point in this after bug 1367904? + */ + static inline already_AddRefed<ComputedStyle> ResolveServoStyle( + const dom::Element&); + + bool GetKeyframesForName(const dom::Element&, const ComputedStyle&, + nsAtom* aName, + const StyleComputedTimingFunction& aTimingFunction, + nsTArray<Keyframe>& aKeyframes); + + nsTArray<ComputedKeyframeValues> GetComputedKeyframeValuesFor( + const nsTArray<Keyframe>& aKeyframes, dom::Element* aElement, + PseudoStyleType aPseudoType, const ComputedStyle* aStyle); + + void GetAnimationValues( + RawServoDeclarationBlock* aDeclarations, dom::Element* aElement, + const mozilla::ComputedStyle* aStyle, + nsTArray<RefPtr<RawServoAnimationValue>>& aAnimationValues); + + void AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray); + + const RawServoCounterStyleRule* CounterStyleRuleForName(nsAtom* aName); + + // Get all the currently-active font feature values set. + already_AddRefed<gfxFontFeatureValueSet> BuildFontFeatureValueSet(); + + // Get the set of all currently-active font-palette-values. + already_AddRefed<gfx::FontPaletteValueSet> BuildFontPaletteValueSet(); + + already_AddRefed<ComputedStyle> GetBaseContextForElement( + dom::Element* aElement, const ComputedStyle* aStyle); + + // Get a ComputedStyle that represents |aStyle|, but as though it additionally + // matched the rules of the newly added |aAnimaitonaValue|. + // + // We use this function to temporarily generate a ComputedStyle for + // calculating the cumulative change hints. + // + // This must hold: + // The additional rules must be appropriate for the transition + // 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.) + // Note: |aElement| should be the generated element if it is pseudo. + already_AddRefed<ComputedStyle> ResolveServoStyleByAddingAnimation( + dom::Element* aElement, const ComputedStyle* aStyle, + RawServoAnimationValue* aAnimationValue); + /** + * Resolve style for a given declaration block with/without the parent style. + * If the parent style is not specified, the document default computed values + * is used. + */ + already_AddRefed<ComputedStyle> ResolveForDeclarations( + const ComputedStyle* aParentOrNull, + const RawServoDeclarationBlock* aDeclarations); + + already_AddRefed<RawServoAnimationValue> ComputeAnimationValue( + dom::Element* aElement, RawServoDeclarationBlock* aDeclaration, + const mozilla::ComputedStyle* aStyle); + + void AppendTask(PostTraversalTask aTask) { + MOZ_ASSERT(IsInServoTraversal()); + + // We currently only use PostTraversalTasks while the Servo font metrics + // mutex is locked. If we need to use them in other situations during + // a traversal, we should assert that we've taken appropriate + // synchronization measures. + AssertIsMainThreadOrServoFontMetricsLocked(); + + mPostTraversalTasks.AppendElement(aTask); + } + + // Returns true if a restyle of the document is needed due to cloning + // sheet inners. + bool EnsureUniqueInnerOnCSSSheets(); + + // Returns the style rule map. + ServoStyleRuleMap* StyleRuleMap(); + + /** + * Returns true if a modification to an an attribute with the specified + * local name might require us to restyle the element. + * + * This function allows us to skip taking a an attribute snapshot when + * the modified attribute doesn't appear in an attribute selector in + * a style sheet. + */ + bool MightHaveAttributeDependency(const dom::Element&, + nsAtom* aAttribute) const; + + /** + * Returns true if a change in event state on an element might require + * us to restyle the element. + * + * This function allows us to skip taking a state snapshot when + * the changed state isn't depended upon by any pseudo-class selectors + * in a style sheet. + */ + bool HasStateDependency(const dom::Element&, dom::ElementState) const; + + /** + * Returns true if a change in document state might require us to restyle the + * document. + */ + bool HasDocumentStateDependency(dom::DocumentState) const; + + /** + * Get a new ComputedStyle that uses the same rules as the given ComputedStyle + * but has a different parent. + * + * aElement is non-null if this is a ComputedStyle for a frame whose mContent + * is an element and which has no pseudo on its ComputedStyle (so it's the + * actual style for the element being passed). + */ + already_AddRefed<ComputedStyle> ReparentComputedStyle( + ComputedStyle* aComputedStyle, ComputedStyle* aNewParent, + ComputedStyle* aNewParentIgnoringFirstLine, + ComputedStyle* aNewLayoutParent, dom::Element* aElement); + + /** + * Invalidate styles where there's any viewport units dependent style. + */ + enum class OnlyDynamic : bool { No, Yes }; + void InvalidateForViewportUnits(OnlyDynamic); + + private: + friend class AutoSetInServoTraversal; + friend class AutoPrepareTraversal; + friend class PostTraversalTask; + + bool ShouldTraverseInParallel() const; + + void RuleChangedInternal(StyleSheet&, css::Rule&, StyleRuleChangeKind); + + /** + * Forces all the ShadowRoot styles to be dirty. + * + * Only to be used for: + * + * * Devtools (dealing with sheet cloning). + * * Compatibility-mode changes. + * + * Try to do something more incremental for other callers that are exposed to + * the web. + */ + void ForceDirtyAllShadowStyles(); + + /** + * Gets the pending snapshots to handle from the restyle manager. + */ + const SnapshotTable& Snapshots(); + + /** + * Resolve all DeclarationBlocks attached to mapped + * presentation attributes cached on the document. + * + * Call this before jumping into Servo's style system. + */ + void ResolveMappedAttrDeclarationBlocks(); + + /** + * Clear our cached mNonInheritingComputedStyles. + * + * We do this when we want to make sure those ComputedStyles won't live too + * long (e.g. when rebuilding all style data or when shutting down the style + * set). + */ + void ClearNonInheritingComputedStyles(); + + /** + * Perform processes that we should do before traversing. + * + * When aRoot is null, the entire document is pre-traversed. Otherwise, + * only the subtree rooted at aRoot is pre-traversed. + */ + void PreTraverse(ServoTraversalFlags aFlags, dom::Element* aRoot = nullptr); + + // Subset of the pre-traverse steps that involve syncing up data + void PreTraverseSync(); + + /** + * Records that the contents of style sheets at the specified origin have + * changed since the last. Calling this will ensure that the Stylist + * rebuilds its selector maps. + */ + void MarkOriginsDirty(OriginFlags aChangedOrigins); + + /** + * Note that the stylist needs a style flush due to style sheet changes. + */ + void SetStylistStyleSheetsDirty(); + + void SetStylistShadowDOMStyleSheetsDirty(); + + bool StylistNeedsUpdate() const { + return mStylistState != StylistState::NotDirty; + } + + /** + * Update the stylist as needed to ensure style data is up-to-date. + * + * This should only be called if StylistNeedsUpdate returns true. + */ + void UpdateStylist(); + + void RunPostTraversalTasks(); + + void PrependSheetOfType(Origin, StyleSheet*); + void AppendSheetOfType(Origin, StyleSheet*); + void InsertSheetOfType(Origin, StyleSheet*, StyleSheet* aBeforeSheet); + void RemoveSheetOfType(Origin, StyleSheet*); + + const nsPresContext* GetPresContext() const { + return const_cast<ServoStyleSet*>(this)->GetPresContext(); + } + + /** + * Return the associated pres context if we're the master style set and we + * have an associated pres shell. + */ + nsPresContext* GetPresContext(); + + // The owner document of this style set. Never null, and always outlives the + // StyleSet. + dom::Document* mDocument; + UniquePtr<RawServoStyleSet> mRawSet; + + // Map from raw Servo style rule to Gecko's wrapper object. + // Constructed lazily when requested by devtools. + UniquePtr<ServoStyleRuleMap> mStyleRuleMap; + uint64_t mUserFontSetUpdateGeneration = 0; + + // Tasks to perform after a traversal, back on the main thread. + // + // These are similar to Servo's SequentialTasks, except that they are + // posted by C++ code running on style worker threads. + nsTArray<PostTraversalTask> mPostTraversalTasks; + + // Stores pointers to our cached ComputedStyles for non-inheriting anonymous + // boxes. + EnumeratedArray<nsCSSAnonBoxes::NonInheriting, + nsCSSAnonBoxes::NonInheriting::_Count, RefPtr<ComputedStyle>> + mNonInheritingComputedStyles; + + already_AddRefed<ComputedStyle> ResolveNonInheritingAnonymousBoxStyle( + PseudoStyleType aType, const nsAtom* aPageName); + + public: + void PutCachedAnonymousContentStyles( + AnonymousContentKey aKey, nsTArray<RefPtr<ComputedStyle>>&& aStyles) { + auto index = static_cast<size_t>(aKey); + + MOZ_ASSERT(mCachedAnonymousContentStyles.Length() + aStyles.Length() < 256, + "(index, length) pairs must be bigger"); + MOZ_ASSERT(mCachedAnonymousContentStyleIndexes[index].second == 0, + "shouldn't need to overwrite existing cached styles"); + MOZ_ASSERT(!aStyles.IsEmpty(), "should have some styles to cache"); + + mCachedAnonymousContentStyleIndexes[index] = std::make_pair( + mCachedAnonymousContentStyles.Length(), aStyles.Length()); + mCachedAnonymousContentStyles.AppendElements(std::move(aStyles)); + } + + void GetCachedAnonymousContentStyles( + AnonymousContentKey aKey, nsTArray<RefPtr<ComputedStyle>>& aStyles) { + auto index = static_cast<size_t>(aKey); + auto loc = mCachedAnonymousContentStyleIndexes[index]; + aStyles.AppendElements(mCachedAnonymousContentStyles.Elements() + loc.first, + loc.second); + } + + private: + // Map of AnonymousContentKey values to an (index, length) pair pointing into + // mCachedAnonymousContentStyles. + // + // We assert that the index and length values fit into uint8_ts. + Array<std::pair<uint8_t, uint8_t>, 1 << sizeof(AnonymousContentKey) * 8> + mCachedAnonymousContentStyleIndexes; + + // Stores cached ComputedStyles for certain native anonymous content. + nsTArray<RefPtr<ComputedStyle>> mCachedAnonymousContentStyles; + + StylistState mStylistState = StylistState::NotDirty; + bool mAuthorStyleDisabled = false; + bool mNeedsRestyleAfterEnsureUniqueInner = false; +}; + +class UACacheReporter final : public nsIMemoryReporter { + NS_DECL_ISUPPORTS + NS_DECL_NSIMEMORYREPORTER + + private: + ~UACacheReporter() = default; +}; + +} // namespace mozilla + +#endif // mozilla_ServoStyleSet_h |