1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_ServoStyleSet_h
8 #define mozilla_ServoStyleSet_h
10 #include "mozilla/AnonymousContentKey.h"
11 #include "mozilla/AtomArray.h"
12 #include "mozilla/EnumeratedArray.h"
13 #include "mozilla/EventStates.h"
14 #include "mozilla/PostTraversalTask.h"
15 #include "mozilla/ServoBindingTypes.h"
16 #include "mozilla/ServoUtils.h"
17 #include "mozilla/UniquePtr.h"
18 #include "MainThreadUtils.h"
19 #include "nsCSSPseudoElements.h"
20 #include "nsCSSAnonBoxes.h"
21 #include "nsChangeHint.h"
24 #include "nsIMemoryReporter.h"
26 #include "nsIMemoryReporter.h"
29 enum class MediaFeatureChangeReason
: uint8_t;
40 class ServoElementSnapshotTable
;
42 class ServoStyleRuleMap
;
44 } // namespace mozilla
45 class gfxFontFeatureValueSet
;
50 struct nsTimingFunction
;
51 struct TreeMatchContext
;
55 // A few flags used to track which kind of stylist state we may need to
57 enum class StylistState
: uint8_t {
58 // The stylist is not dirty, we should do nothing.
61 // The style sheets have changed, so we need to update the style data.
62 StyleSheetsDirty
= 1 << 0,
64 // Some of the style sheets of the shadow trees in the document have
66 ShadowDOMStyleSheetsDirty
= 1 << 1,
69 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StylistState
)
71 enum class StyleOrigin
: uint8_t;
73 // Bitfield type to represent Servo stylesheet origins.
74 enum class OriginFlags
: uint8_t {
81 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OriginFlags
)
84 * The set of style sheets that apply to a document, backed by a Servo
85 * Stylist. A ServoStyleSet contains StyleSheets.
88 friend class RestyleManager
;
89 using SnapshotTable
= ServoElementSnapshotTable
;
90 using Origin
= StyleOrigin
;
92 // We assert that these match the Servo ones in the definition of this array.
93 static constexpr Origin kOrigins
[] = {
94 Origin(static_cast<uint8_t>(OriginFlags::UserAgent
)),
95 Origin(static_cast<uint8_t>(OriginFlags::User
)),
96 Origin(static_cast<uint8_t>(OriginFlags::Author
)),
100 static bool IsInServoTraversal() { return mozilla::IsInServoTraversal(); }
103 // Used for debug assertions. We make this debug-only to prevent callers from
104 // accidentally using it instead of IsInServoTraversal, which is cheaper. We
105 // can change this if a use-case arises.
106 static bool IsCurrentThreadInServoTraversal();
109 static ServoStyleSet
* Current() { return sInServoTraversal
; }
111 explicit ServoStyleSet(dom::Document
&);
114 void ShellDetachedFromDocument();
116 // Called when a rules in a stylesheet in this set, or a child sheet of that,
117 // are mutated from CSSOM.
118 void RuleAdded(StyleSheet
&, css::Rule
&);
119 void RuleRemoved(StyleSheet
&, css::Rule
&);
120 void RuleChanged(StyleSheet
&, css::Rule
* aRule
);
121 void SheetCloned(StyleSheet
&) { mNeedsRestyleAfterEnsureUniqueInner
= true; }
122 void ImportRuleLoaded(dom::CSSImportRule
&, StyleSheet
&);
124 // Runs style invalidation due to document state changes.
125 void InvalidateStyleForDocumentStateChanges(EventStates aStatesChanged
);
127 void RecordShadowStyleChange(dom::ShadowRoot
&);
129 bool StyleSheetsHaveChanged() const { return StylistNeedsUpdate(); }
131 RestyleHint
MediumFeaturesChanged(MediaFeatureChangeReason
);
133 // Evaluates a given SourceSizeList, returning the optimal viewport width in
136 // The SourceSizeList parameter can be null, in which case it will return
138 inline nscoord
EvaluateSourceSizeList(
139 const RawServoSourceSizeList
* aSourceSizeList
) const;
141 void AddSizeOfIncludingThis(nsWindowSizes
& aSizes
) const;
142 const RawServoStyleSet
* RawSet() const { return mRawSet
.get(); }
144 bool GetAuthorStyleDisabled() const { return mAuthorStyleDisabled
; }
146 void SetAuthorStyleDisabled(bool aStyleDisabled
);
148 // Get a CopmutedStyle for a text node (which no rules will match).
150 // The returned ComputedStyle will have nsCSSAnonBoxes::mozText() as its
153 // (Perhaps mozText should go away and we shouldn't even create style
154 // contexts for such content nodes, when text-combine-upright is not
155 // present. However, not doing any rule matching for them is a first step.)
156 already_AddRefed
<ComputedStyle
> ResolveStyleForText(
157 nsIContent
* aTextNode
, ComputedStyle
* aParentStyle
);
159 // Get a ComputedStyle for a first-letter continuation (which no rules will
162 // The returned ComputedStyle will have
163 // nsCSSAnonBoxes::firstLetterContinuation() as its pseudo.
165 // (Perhaps nsCSSAnonBoxes::firstLetterContinuation() should go away and we
166 // shouldn't even create ComputedStyles for such frames. However, not doing
167 // any rule matching for them is a first step. And right now we do use this
168 // ComputedStyle for some things)
169 already_AddRefed
<ComputedStyle
> ResolveStyleForFirstLetterContinuation(
170 ComputedStyle
* aParentStyle
);
172 // Get a ComputedStyle for a placeholder frame (which no rules will match).
174 // The returned ComputedStyle will have nsCSSAnonBoxes::oofPlaceholder() as
177 // (Perhaps nsCSSAnonBoxes::oofPaceholder() should go away and we shouldn't
178 // even create ComputedStyle for placeholders. However, not doing any rule
179 // matching for them is a first step.)
180 already_AddRefed
<ComputedStyle
> ResolveStyleForPlaceholder();
182 // Returns whether a given pseudo-element should exist or not.
183 static bool GeneratedContentPseudoExists(const ComputedStyle
& aParentStyle
,
184 const ComputedStyle
& aPseudoStyle
);
191 // Get a style for a pseudo-element.
193 // If IsProbe is Yes, then no style is returned if there are no rules matching
194 // for the pseudo-element, or GeneratedContentPseudoExists returns false.
196 // If IsProbe is No, then the style is guaranteed to be non-null.
197 already_AddRefed
<ComputedStyle
> ResolvePseudoElementStyle(
198 const dom::Element
& aOriginatingElement
, PseudoStyleType
,
199 ComputedStyle
* aParentStyle
, IsProbe
= IsProbe::No
);
201 already_AddRefed
<ComputedStyle
> ProbePseudoElementStyle(
202 const dom::Element
& aOriginatingElement
, PseudoStyleType aType
,
203 ComputedStyle
* aParentStyle
) {
204 return ResolvePseudoElementStyle(aOriginatingElement
, aType
, aParentStyle
,
208 // Resolves style for a (possibly-pseudo) Element without assuming that the
209 // style has been resolved. If the element was unstyled and a new style
210 // was resolved, it is not stored in the DOM. (That is, the element remains
213 // TODO(emilio): Element argument should be `const`.
214 already_AddRefed
<ComputedStyle
> ResolveStyleLazily(
215 dom::Element
&, PseudoStyleType
= PseudoStyleType::NotPseudo
,
216 StyleRuleInclusion
= StyleRuleInclusion::All
);
218 // Get a ComputedStyle for an anonymous box. The pseudo type must be an
219 // inheriting anon box.
220 already_AddRefed
<ComputedStyle
> ResolveInheritingAnonymousBoxStyle(
221 PseudoStyleType
, ComputedStyle
* aParentStyle
);
223 // Get a ComputedStyle for an anonymous box. The pseudo type must be
224 // a non-inheriting anon box.
225 already_AddRefed
<ComputedStyle
> ResolveNonInheritingAnonymousBoxStyle(
229 already_AddRefed
<ComputedStyle
> ResolveXULTreePseudoStyle(
230 dom::Element
* aParentElement
, nsCSSAnonBoxPseudoStaticAtom
* aPseudoTag
,
231 ComputedStyle
* aParentStyle
, const AtomArray
& aInputWord
);
234 size_t SheetCount(Origin
) const;
235 StyleSheet
* SheetAt(Origin
, size_t aIndex
) const;
237 void AppendAllNonDocumentAuthorSheets(nsTArray
<StyleSheet
*>& aArray
) const;
239 // Manage the set of style sheets in the style set
240 void AppendStyleSheet(StyleSheet
&);
241 void InsertStyleSheetBefore(StyleSheet
&, StyleSheet
& aReferenceSheet
);
242 void RemoveStyleSheet(StyleSheet
&);
243 void AddDocStyleSheet(StyleSheet
&);
246 * Performs a Servo traversal to compute style for all dirty nodes in the
249 * This will traverse all of the document's style roots (that is, its document
250 * element, and the roots of the document-level native anonymous content).
252 * We specify |ForCSSRuleChanges| to try to update all CSS animations
253 * when we call this function due to CSS rule changes since @keyframes rules
256 * Returns true if a post-traversal is required.
258 bool StyleDocument(ServoTraversalFlags aFlags
);
261 * Eagerly styles a subtree of unstyled nodes that was just appended to the
262 * tree. This is used in situations where we need the style immediately and
263 * cannot wait for a future batch restyle.
265 void StyleNewSubtree(dom::Element
* aRoot
);
268 * Helper for correctly calling UpdateStylist without paying the cost of an
269 * extra function call in the common no-rebuild-needed case.
271 void UpdateStylistIfNeeded() {
272 if (StylistNeedsUpdate()) {
278 * Checks whether the rule tree has crossed its threshold for unused nodes,
279 * and if so, frees them.
281 void MaybeGCRuleTree();
284 * Returns true if the given element may be used as the root of a style
285 * traversal. Reasons for false include having an unstyled parent, or having
286 * a parent that is display:none.
288 * Most traversal callsites don't need to check this, but some do.
290 static bool MayTraverseFrom(const dom::Element
* aElement
);
293 void AssertTreeIsClean();
295 void AssertTreeIsClean() {}
299 * Clears any cached style data that may depend on all sorts of computed
302 * Right now this clears the non-inheriting ComputedStyle cache, resets the
303 * default computed values, and clears cached anonymous content style.
305 * This does _not_, however, clear the stylist.
307 void ClearCachedStyleData();
310 * Notifies the Servo stylesheet that the document's compatibility mode has
313 void CompatibilityModeChanged();
315 template <typename T
>
316 void EnumerateStyleSheets(T aCb
) {
317 for (auto origin
: kOrigins
) {
318 for (size_t i
= 0, count
= SheetCount(origin
); i
< count
; ++i
) {
319 aCb(*SheetAt(origin
, i
));
325 * Resolve style for the given element, and return it as a
328 * FIXME(emilio): Is there a point in this after bug 1367904?
330 static inline already_AddRefed
<ComputedStyle
> ResolveServoStyle(
331 const dom::Element
&);
333 bool GetKeyframesForName(const dom::Element
&, const ComputedStyle
&,
335 const nsTimingFunction
& aTimingFunction
,
336 nsTArray
<Keyframe
>& aKeyframes
);
338 nsTArray
<ComputedKeyframeValues
> GetComputedKeyframeValuesFor(
339 const nsTArray
<Keyframe
>& aKeyframes
, dom::Element
* aElement
,
340 const ComputedStyle
* aStyle
);
342 void GetAnimationValues(
343 RawServoDeclarationBlock
* aDeclarations
, dom::Element
* aElement
,
344 const mozilla::ComputedStyle
* aStyle
,
345 nsTArray
<RefPtr
<RawServoAnimationValue
>>& aAnimationValues
);
347 void AppendFontFaceRules(nsTArray
<nsFontFaceRuleContainer
>& aArray
);
349 const RawServoCounterStyleRule
* CounterStyleRuleForName(nsAtom
* aName
);
351 // Get all the currently-active font feature values set.
352 already_AddRefed
<gfxFontFeatureValueSet
> BuildFontFeatureValueSet();
354 already_AddRefed
<ComputedStyle
> GetBaseContextForElement(
355 dom::Element
* aElement
, const ComputedStyle
* aStyle
);
357 // Get a ComputedStyle that represents |aStyle|, but as though it additionally
358 // matched the rules of the newly added |aAnimaitonaValue|.
360 // We use this function to temporarily generate a ComputedStyle for
361 // calculating the cumulative change hints.
364 // The additional rules must be appropriate for the transition
365 // level of the cascade, which is the highest level of the cascade.
366 // (This is the case for one current caller, the cover rule used
367 // for CSS transitions.)
368 // Note: |aElement| should be the generated element if it is pseudo.
369 already_AddRefed
<ComputedStyle
> ResolveServoStyleByAddingAnimation(
370 dom::Element
* aElement
, const ComputedStyle
* aStyle
,
371 RawServoAnimationValue
* aAnimationValue
);
373 * Resolve style for a given declaration block with/without the parent style.
374 * If the parent style is not specified, the document default computed values
377 already_AddRefed
<ComputedStyle
> ResolveForDeclarations(
378 const ComputedStyle
* aParentOrNull
,
379 const RawServoDeclarationBlock
* aDeclarations
);
381 already_AddRefed
<RawServoAnimationValue
> ComputeAnimationValue(
382 dom::Element
* aElement
, RawServoDeclarationBlock
* aDeclaration
,
383 const mozilla::ComputedStyle
* aStyle
);
385 void AppendTask(PostTraversalTask aTask
) {
386 MOZ_ASSERT(IsInServoTraversal());
388 // We currently only use PostTraversalTasks while the Servo font metrics
389 // mutex is locked. If we need to use them in other situations during
390 // a traversal, we should assert that we've taken appropriate
391 // synchronization measures.
392 AssertIsMainThreadOrServoFontMetricsLocked();
394 mPostTraversalTasks
.AppendElement(aTask
);
397 // Returns true if a restyle of the document is needed due to cloning
399 bool EnsureUniqueInnerOnCSSSheets();
401 // Returns the style rule map.
402 ServoStyleRuleMap
* StyleRuleMap();
405 * Returns true if a modification to an an attribute with the specified
406 * local name might require us to restyle the element.
408 * This function allows us to skip taking a an attribute snapshot when
409 * the modified attribute doesn't appear in an attribute selector in
412 bool MightHaveAttributeDependency(const dom::Element
&,
413 nsAtom
* aAttribute
) const;
416 * Returns true if a change in event state on an element might require
417 * us to restyle the element.
419 * This function allows us to skip taking a state snapshot when
420 * the changed state isn't depended upon by any pseudo-class selectors
423 bool HasStateDependency(const dom::Element
&, EventStates
) const;
426 * Returns true if a change in document state might require us to restyle the
429 bool HasDocumentStateDependency(EventStates aState
) const;
432 * Get a new ComputedStyle that uses the same rules as the given ComputedStyle
433 * but has a different parent.
435 * aElement is non-null if this is a ComputedStyle for a frame whose mContent
436 * is an element and which has no pseudo on its ComputedStyle (so it's the
437 * actual style for the element being passed).
439 already_AddRefed
<ComputedStyle
> ReparentComputedStyle(
440 ComputedStyle
* aComputedStyle
, ComputedStyle
* aNewParent
,
441 ComputedStyle
* aNewParentIgnoringFirstLine
,
442 ComputedStyle
* aNewLayoutParent
, dom::Element
* aElement
);
445 friend class AutoSetInServoTraversal
;
446 friend class AutoPrepareTraversal
;
447 friend class PostTraversalTask
;
449 bool ShouldTraverseInParallel() const;
452 * Forces all the ShadowRoot styles to be dirty.
454 * Only to be used for:
456 * * Devtools (dealing with sheet cloning).
457 * * Compatibility-mode changes.
459 * Try to do something more incremental for other callers that are exposed to
462 void ForceDirtyAllShadowStyles();
465 * Gets the pending snapshots to handle from the restyle manager.
467 const SnapshotTable
& Snapshots();
470 * Resolve all DeclarationBlocks attached to mapped
471 * presentation attributes cached on the document.
473 * Call this before jumping into Servo's style system.
475 void ResolveMappedAttrDeclarationBlocks();
478 * Clear our cached mNonInheritingComputedStyles.
480 * We do this when we want to make sure those ComputedStyles won't live too
481 * long (e.g. when rebuilding all style data or when shutting down the style
484 void ClearNonInheritingComputedStyles();
487 * Perform processes that we should do before traversing.
489 * When aRoot is null, the entire document is pre-traversed. Otherwise,
490 * only the subtree rooted at aRoot is pre-traversed.
492 void PreTraverse(ServoTraversalFlags aFlags
, dom::Element
* aRoot
= nullptr);
494 // Subset of the pre-traverse steps that involve syncing up data
495 void PreTraverseSync();
498 * Records that the contents of style sheets at the specified origin have
499 * changed since the last. Calling this will ensure that the Stylist
500 * rebuilds its selector maps.
502 void MarkOriginsDirty(OriginFlags aChangedOrigins
);
505 * Note that the stylist needs a style flush due to style sheet changes.
507 void SetStylistStyleSheetsDirty();
509 void SetStylistShadowDOMStyleSheetsDirty();
511 bool StylistNeedsUpdate() const {
512 return mStylistState
!= StylistState::NotDirty
;
516 * Update the stylist as needed to ensure style data is up-to-date.
518 * This should only be called if StylistNeedsUpdate returns true.
520 void UpdateStylist();
522 void RunPostTraversalTasks();
524 void PrependSheetOfType(Origin
, StyleSheet
*);
525 void AppendSheetOfType(Origin
, StyleSheet
*);
526 void InsertSheetOfType(Origin
, StyleSheet
*, StyleSheet
* aBeforeSheet
);
527 void RemoveSheetOfType(Origin
, StyleSheet
*);
529 const nsPresContext
* GetPresContext() const {
530 return const_cast<ServoStyleSet
*>(this)->GetPresContext();
534 * Return the associated pres context if we're the master style set and we
535 * have an associated pres shell.
537 nsPresContext
* GetPresContext();
539 // The owner document of this style set. Never null, and always outlives the
541 dom::Document
* mDocument
;
542 UniquePtr
<RawServoStyleSet
> mRawSet
;
544 // Map from raw Servo style rule to Gecko's wrapper object.
545 // Constructed lazily when requested by devtools.
546 UniquePtr
<ServoStyleRuleMap
> mStyleRuleMap
;
547 uint64_t mUserFontSetUpdateGeneration
= 0;
549 // Tasks to perform after a traversal, back on the main thread.
551 // These are similar to Servo's SequentialTasks, except that they are
552 // posted by C++ code running on style worker threads.
553 nsTArray
<PostTraversalTask
> mPostTraversalTasks
;
555 // Stores pointers to our cached ComputedStyles for non-inheriting anonymous
557 EnumeratedArray
<nsCSSAnonBoxes::NonInheriting
,
558 nsCSSAnonBoxes::NonInheriting::_Count
, RefPtr
<ComputedStyle
>>
559 mNonInheritingComputedStyles
;
562 void PutCachedAnonymousContentStyles(
563 AnonymousContentKey aKey
, nsTArray
<RefPtr
<ComputedStyle
>>&& aStyles
) {
564 auto index
= static_cast<size_t>(aKey
);
566 MOZ_ASSERT(mCachedAnonymousContentStyles
.Length() + aStyles
.Length() < 256,
567 "(index, length) pairs must be bigger");
568 MOZ_ASSERT(mCachedAnonymousContentStyleIndexes
[index
].second
== 0,
569 "shouldn't need to overwrite existing cached styles");
570 MOZ_ASSERT(!aStyles
.IsEmpty(), "should have some styles to cache");
572 mCachedAnonymousContentStyleIndexes
[index
] = std::make_pair(
573 mCachedAnonymousContentStyles
.Length(), aStyles
.Length());
574 mCachedAnonymousContentStyles
.AppendElements(std::move(aStyles
));
577 void GetCachedAnonymousContentStyles(
578 AnonymousContentKey aKey
, nsTArray
<RefPtr
<ComputedStyle
>>& aStyles
) {
579 auto index
= static_cast<size_t>(aKey
);
580 auto loc
= mCachedAnonymousContentStyleIndexes
[index
];
581 aStyles
.AppendElements(mCachedAnonymousContentStyles
.Elements() + loc
.first
,
586 // Map of AnonymousContentKey values to an (index, length) pair pointing into
587 // mCachedAnonymousContentStyles.
589 // We assert that the index and length values fit into uint8_ts.
590 Array
<std::pair
<uint8_t, uint8_t>, 1 << sizeof(AnonymousContentKey
) * 8>
591 mCachedAnonymousContentStyleIndexes
;
593 // Stores cached ComputedStyles for certain native anonymous content.
594 nsTArray
<RefPtr
<ComputedStyle
>> mCachedAnonymousContentStyles
;
596 StylistState mStylistState
= StylistState::NotDirty
;
597 bool mAuthorStyleDisabled
= false;
598 bool mNeedsRestyleAfterEnsureUniqueInner
= false;
601 class UACacheReporter final
: public nsIMemoryReporter
{
603 NS_DECL_NSIMEMORYREPORTER
606 ~UACacheReporter() = default;
609 } // namespace mozilla
611 #endif // mozilla_ServoStyleSet_h