summaryrefslogtreecommitdiffstats
path: root/layout/style/ServoStyleSet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/ServoStyleSet.cpp')
-rw-r--r--layout/style/ServoStyleSet.cpp1502
1 files changed, 1502 insertions, 0 deletions
diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp
new file mode 100644
index 0000000000..88d67f8049
--- /dev/null
+++ b/layout/style/ServoStyleSet.cpp
@@ -0,0 +1,1502 @@
+/* -*- 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/. */
+
+#include "mozilla/ServoStyleSet.h"
+#include "mozilla/ServoStyleSetInlines.h"
+
+#include "gfxPlatformFontList.h"
+#include "mozilla/AutoRestyleTimelineMarker.h"
+#include "mozilla/DocumentStyleRootIterator.h"
+#include "mozilla/EffectCompositor.h"
+#include "mozilla/IntegerRange.h"
+#include "mozilla/Keyframe.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ServoBindings.h"
+#include "mozilla/RestyleManager.h"
+#include "mozilla/ServoStyleRuleMap.h"
+#include "mozilla/ServoTypes.h"
+#include "mozilla/SMILAnimationController.h"
+#include "mozilla/MediaFeatureChange.h"
+#include "mozilla/StyleAnimationValue.h"
+#include "mozilla/css/Loader.h"
+#include "mozilla/dom/AnonymousContent.h"
+#include "mozilla/dom/CSSCounterStyleRule.h"
+#include "mozilla/dom/CSSFontFaceRule.h"
+#include "mozilla/dom/CSSFontFeatureValuesRule.h"
+#include "mozilla/dom/CSSFontPaletteValuesRule.h"
+#include "mozilla/dom/CSSImportRule.h"
+#include "mozilla/dom/CSSContainerRule.h"
+#include "mozilla/dom/CSSLayerBlockRule.h"
+#include "mozilla/dom/CSSLayerStatementRule.h"
+#include "mozilla/dom/CSSMediaRule.h"
+#include "mozilla/dom/CSSMozDocumentRule.h"
+#include "mozilla/dom/CSSKeyframesRule.h"
+#include "mozilla/dom/CSSKeyframeRule.h"
+#include "mozilla/dom/CSSNamespaceRule.h"
+#include "mozilla/dom/CSSPageRule.h"
+#include "mozilla/dom/CSSPropertyRule.h"
+#include "mozilla/dom/CSSSupportsRule.h"
+#include "mozilla/dom/FontFaceSet.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ElementInlines.h"
+#include "nsCSSAnonBoxes.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsCSSPseudoElements.h"
+#include "nsDeviceContext.h"
+#include "nsHTMLStyleSheet.h"
+#include "nsIAnonymousContentCreator.h"
+#include "nsLayoutUtils.h"
+#include "mozilla/dom/DocumentInlines.h"
+#include "nsPrintfCString.h"
+#include "gfxUserFontSet.h"
+#include "nsWindowSizes.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+#ifdef DEBUG
+bool ServoStyleSet::IsCurrentThreadInServoTraversal() {
+ return sInServoTraversal && (NS_IsMainThread() || Servo_IsWorkerThread());
+}
+#endif
+
+// The definition of kOrigins relies on this.
+static_assert(static_cast<uint8_t>(StyleOrigin::UserAgent) ==
+ static_cast<uint8_t>(OriginFlags::UserAgent));
+static_assert(static_cast<uint8_t>(StyleOrigin::User) ==
+ static_cast<uint8_t>(OriginFlags::User));
+static_assert(static_cast<uint8_t>(StyleOrigin::Author) ==
+ static_cast<uint8_t>(OriginFlags::Author));
+
+constexpr const StyleOrigin ServoStyleSet::kOrigins[];
+
+ServoStyleSet* sInServoTraversal = nullptr;
+
+// On construction, sets sInServoTraversal to the given ServoStyleSet.
+// On destruction, clears sInServoTraversal and calls RunPostTraversalTasks.
+class MOZ_RAII AutoSetInServoTraversal {
+ public:
+ explicit AutoSetInServoTraversal(ServoStyleSet* aSet) : mSet(aSet) {
+ MOZ_ASSERT(!sInServoTraversal);
+ MOZ_ASSERT(aSet);
+ sInServoTraversal = aSet;
+ }
+
+ ~AutoSetInServoTraversal() {
+ MOZ_ASSERT(sInServoTraversal);
+ sInServoTraversal = nullptr;
+ mSet->RunPostTraversalTasks();
+ }
+
+ private:
+ ServoStyleSet* mSet;
+};
+
+// Sets up for one or more calls to Servo_TraverseSubtree.
+class MOZ_RAII AutoPrepareTraversal {
+ public:
+ explicit AutoPrepareTraversal(ServoStyleSet* aSet)
+ // For markers for animations, we have already set the markers in
+ // RestyleManager::PostRestyleEventForAnimations so that we don't need
+ // to care about animation restyles here.
+ : mTimelineMarker(aSet->mDocument->GetDocShell(), false),
+ mSetInServoTraversal(aSet) {
+ MOZ_ASSERT(!aSet->StylistNeedsUpdate());
+ }
+
+ private:
+ AutoRestyleTimelineMarker mTimelineMarker;
+ AutoSetInServoTraversal mSetInServoTraversal;
+};
+
+ServoStyleSet::ServoStyleSet(Document& aDocument) : mDocument(&aDocument) {
+ PreferenceSheet::EnsureInitialized();
+ PodArrayZero(mCachedAnonymousContentStyleIndexes);
+ mRawData.reset(Servo_StyleSet_Init(&aDocument));
+}
+
+ServoStyleSet::~ServoStyleSet() {
+ MOZ_ASSERT(!IsInServoTraversal());
+ EnumerateStyleSheets([&](StyleSheet& aSheet) { aSheet.DropStyleSet(this); });
+}
+
+nsPresContext* ServoStyleSet::GetPresContext() {
+ return mDocument->GetPresContext();
+}
+
+template <typename Functor>
+static void EnumerateShadowRoots(const Document& aDoc, const Functor& aCb) {
+ const Document::ShadowRootSet& shadowRoots = aDoc.ComposedShadowRoots();
+ for (ShadowRoot* root : shadowRoots) {
+ MOZ_ASSERT(root);
+ MOZ_DIAGNOSTIC_ASSERT(root->IsInComposedDoc());
+ aCb(*root);
+ }
+}
+
+void ServoStyleSet::ShellDetachedFromDocument() {
+ ClearNonInheritingComputedStyles();
+ mCachedAnonymousContentStyles.Clear();
+ PodArrayZero(mCachedAnonymousContentStyleIndexes);
+ mStyleRuleMap = nullptr;
+
+ // Remove all our stylesheets...
+ for (auto origin : kOrigins) {
+ for (size_t count = SheetCount(origin); count--;) {
+ RemoveStyleSheet(*SheetAt(origin, count));
+ }
+ }
+
+ // And remove all the CascadeDatas from memory.
+ UpdateStylistIfNeeded();
+
+ // Also GC the ruletree if it got big now that the DOM no longer has
+ // references to styles around anymore.
+ MaybeGCRuleTree();
+}
+
+void ServoStyleSet::RecordShadowStyleChange(ShadowRoot& aShadowRoot) {
+ // TODO(emilio): We could keep track of the actual shadow roots that need
+ // their styles recomputed.
+ SetStylistShadowDOMStyleSheetsDirty();
+
+ // FIXME(emilio): This should be done using stylesheet invalidation instead.
+ if (nsPresContext* pc = GetPresContext()) {
+ pc->RestyleManager()->PostRestyleEvent(
+ aShadowRoot.Host(), RestyleHint::RestyleSubtree(), nsChangeHint(0));
+ }
+}
+
+void ServoStyleSet::InvalidateStyleForDocumentStateChanges(
+ DocumentState aStatesChanged) {
+ MOZ_ASSERT(mDocument);
+ MOZ_ASSERT(!aStatesChanged.IsEmpty());
+
+ nsPresContext* pc = GetPresContext();
+ if (!pc) {
+ return;
+ }
+
+ Element* root = mDocument->GetRootElement();
+ if (!root) {
+ return;
+ }
+
+ // TODO(emilio): It may be nicer to just invalidate stuff in a given subtree
+ // for Shadow DOM. Consider just enumerating shadow roots instead and run
+ // invalidation individually, passing mRawData for the UA / User sheets.
+ AutoTArray<const StyleAuthorStyles*, 20> nonDocumentStyles;
+
+ EnumerateShadowRoots(*mDocument, [&](ShadowRoot& aShadowRoot) {
+ if (auto* authorStyles = aShadowRoot.GetServoStyles()) {
+ nonDocumentStyles.AppendElement(authorStyles);
+ }
+ });
+
+ Servo_InvalidateStyleForDocStateChanges(root, mRawData.get(),
+ &nonDocumentStyles,
+ aStatesChanged.GetInternalValue());
+}
+
+static const MediaFeatureChangeReason kMediaFeaturesAffectingDefaultStyle =
+ // Zoom changes change the meaning of em units.
+ MediaFeatureChangeReason::ZoomChange |
+ // A resolution change changes the app-units-per-dev-pixels ratio, which
+ // some structs (Border, Outline, Column) store for clamping. We should
+ // arguably not do that, maybe doing it on layout directly, to try to avoid
+ // relying on the pres context (bug 1418159).
+ MediaFeatureChangeReason::ResolutionChange;
+
+RestyleHint ServoStyleSet::MediumFeaturesChanged(
+ MediaFeatureChangeReason aReason) {
+ AutoTArray<StyleAuthorStyles*, 20> nonDocumentStyles;
+
+ EnumerateShadowRoots(*mDocument, [&](ShadowRoot& aShadowRoot) {
+ if (auto* authorStyles = aShadowRoot.GetServoStyles()) {
+ nonDocumentStyles.AppendElement(authorStyles);
+ }
+ });
+
+ const bool mayAffectDefaultStyle =
+ bool(aReason & kMediaFeaturesAffectingDefaultStyle);
+ const MediumFeaturesChangedResult result =
+ Servo_StyleSet_MediumFeaturesChanged(mRawData.get(), &nonDocumentStyles,
+ mayAffectDefaultStyle);
+
+ const bool viewportChanged =
+ bool(aReason & MediaFeatureChangeReason::ViewportChange);
+ if (viewportChanged) {
+ InvalidateForViewportUnits(OnlyDynamic::No);
+ }
+
+ const bool rulesChanged =
+ result.mAffectsDocumentRules || result.mAffectsNonDocumentRules;
+
+ if (result.mAffectsDocumentRules) {
+ SetStylistStyleSheetsDirty();
+ }
+
+ if (result.mAffectsNonDocumentRules) {
+ SetStylistShadowDOMStyleSheetsDirty();
+ }
+
+ if (rulesChanged) {
+ // TODO(emilio): This could be more granular.
+ return RestyleHint::RestyleSubtree();
+ }
+
+ return RestyleHint{0};
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSetMallocSizeOf)
+MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSetMallocEnclosingSizeOf)
+
+void ServoStyleSet::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const {
+ MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
+
+ aSizes.mLayoutStyleSetsOther += mallocSizeOf(this);
+
+ if (mRawData) {
+ aSizes.mLayoutStyleSetsOther += mallocSizeOf(mRawData.get());
+ ServoStyleSetSizes sizes;
+ // Measure mRawData. We use ServoStyleSetMallocSizeOf rather than
+ // aMallocSizeOf to distinguish in DMD's output the memory measured within
+ // Servo code.
+ Servo_StyleSet_AddSizeOfExcludingThis(ServoStyleSetMallocSizeOf,
+ ServoStyleSetMallocEnclosingSizeOf,
+ &sizes, mRawData.get());
+
+ // The StyleSet does not contain precomputed pseudos; they are in the UA
+ // cache.
+ MOZ_RELEASE_ASSERT(sizes.mPrecomputedPseudos == 0);
+
+ aSizes.mLayoutStyleSetsStylistRuleTree += sizes.mRuleTree;
+ aSizes.mLayoutStyleSetsStylistElementAndPseudosMaps +=
+ sizes.mElementAndPseudosMaps;
+ aSizes.mLayoutStyleSetsStylistInvalidationMap += sizes.mInvalidationMap;
+ aSizes.mLayoutStyleSetsStylistRevalidationSelectors +=
+ sizes.mRevalidationSelectors;
+ aSizes.mLayoutStyleSetsStylistOther += sizes.mOther;
+ }
+
+ if (mStyleRuleMap) {
+ aSizes.mLayoutStyleSetsOther +=
+ mStyleRuleMap->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
+ }
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mSheets
+ // - mNonInheritingComputedStyles
+ //
+ // The following members are not measured:
+ // - mDocument, because it a non-owning pointer
+}
+
+void ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled) {
+ if (mAuthorStyleDisabled == aStyleDisabled) {
+ return;
+ }
+
+ mAuthorStyleDisabled = aStyleDisabled;
+ if (Element* root = mDocument->GetRootElement()) {
+ if (nsPresContext* pc = GetPresContext()) {
+ pc->RestyleManager()->PostRestyleEvent(
+ root, RestyleHint::RestyleSubtree(), nsChangeHint(0));
+ }
+ }
+ Servo_StyleSet_SetAuthorStyleDisabled(mRawData.get(), mAuthorStyleDisabled);
+ // XXX Workaround for bug 1437785.
+ SetStylistStyleSheetsDirty();
+}
+
+const ServoElementSnapshotTable& ServoStyleSet::Snapshots() {
+ MOZ_ASSERT(GetPresContext(), "Styling a document without a shell?");
+ return GetPresContext()->RestyleManager()->Snapshots();
+}
+
+void ServoStyleSet::ResolveMappedAttrDeclarationBlocks() {
+ if (nsHTMLStyleSheet* sheet = mDocument->GetAttributeStyleSheet()) {
+ sheet->CalculateMappedServoDeclarations();
+ }
+
+ mDocument->ResolveScheduledSVGPresAttrs();
+}
+
+void ServoStyleSet::PreTraverseSync() {
+ // Get the Document's root element to ensure that the cache is valid before
+ // calling into the (potentially-parallel) Servo traversal, where a cache hit
+ // is necessary to avoid a data race when updating the cache.
+ Unused << mDocument->GetRootElement();
+
+ // FIXME(emilio): This shouldn't be needed in theory, the call to the same
+ // function in PresShell should do the work, but as it turns out we
+ // ProcessPendingRestyles() twice, and runnables from frames just constructed
+ // can end up doing editing stuff, which adds stylesheets etc...
+ mDocument->FlushUserFontSet();
+
+ ResolveMappedAttrDeclarationBlocks();
+
+ LookAndFeel::NativeInit();
+
+ mDocument->CacheAllKnownLangPrefs();
+
+ if (gfxUserFontSet* userFontSet = mDocument->GetUserFontSet()) {
+ nsPresContext* presContext = GetPresContext();
+ MOZ_ASSERT(presContext,
+ "For now, we don't call into here without a pres context");
+
+ // Ensure that the @font-face data is not stale
+ uint64_t generation = userFontSet->GetGeneration();
+ if (generation != mUserFontSetUpdateGeneration) {
+ mDocument->GetFonts()->CacheFontLoadability();
+ presContext->UpdateFontCacheUserFonts(userFontSet);
+ mUserFontSetUpdateGeneration = generation;
+ }
+ }
+
+ MOZ_ASSERT(!StylistNeedsUpdate());
+}
+
+void ServoStyleSet::PreTraverse(ServoTraversalFlags aFlags, Element* aRoot) {
+ PreTraverseSync();
+
+ // Process animation stuff that we should avoid doing during the parallel
+ // traversal.
+ SMILAnimationController* smilController =
+ mDocument->HasAnimationController() ? mDocument->GetAnimationController()
+ : nullptr;
+
+ MOZ_ASSERT(GetPresContext());
+ if (aRoot) {
+ GetPresContext()->EffectCompositor()->PreTraverseInSubtree(aFlags, aRoot);
+ if (smilController) {
+ smilController->PreTraverseInSubtree(aRoot);
+ }
+ } else {
+ GetPresContext()->EffectCompositor()->PreTraverse(aFlags);
+ if (smilController) {
+ smilController->PreTraverse();
+ }
+ }
+}
+
+static inline already_AddRefed<ComputedStyle>
+ResolveStyleForTextOrFirstLetterContinuation(
+ const StylePerDocumentStyleData* aRawData, ComputedStyle& aParent,
+ PseudoStyleType aType) {
+ MOZ_ASSERT(aType == PseudoStyleType::mozText ||
+ aType == PseudoStyleType::firstLetterContinuation);
+ auto inheritTarget = aType == PseudoStyleType::mozText
+ ? InheritTarget::Text
+ : InheritTarget::FirstLetterContinuation;
+
+ RefPtr<ComputedStyle> style = aParent.GetCachedInheritingAnonBoxStyle(aType);
+ if (!style) {
+ style =
+ Servo_ComputedValues_Inherit(aRawData, aType, &aParent, inheritTarget)
+ .Consume();
+ MOZ_ASSERT(style);
+ aParent.SetCachedInheritedAnonBoxStyle(style);
+ }
+
+ return style.forget();
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ResolveStyleForText(
+ nsIContent* aTextNode, ComputedStyle* aParentStyle) {
+ MOZ_ASSERT(aTextNode && aTextNode->IsText());
+ MOZ_ASSERT(aTextNode->GetParent());
+ MOZ_ASSERT(aParentStyle);
+
+ return ResolveStyleForTextOrFirstLetterContinuation(
+ mRawData.get(), *aParentStyle, PseudoStyleType::mozText);
+}
+
+already_AddRefed<ComputedStyle>
+ServoStyleSet::ResolveStyleForFirstLetterContinuation(
+ ComputedStyle* aParentStyle) {
+ MOZ_ASSERT(aParentStyle);
+
+ return ResolveStyleForTextOrFirstLetterContinuation(
+ mRawData.get(), *aParentStyle, PseudoStyleType::firstLetterContinuation);
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ResolveStyleForPlaceholder() {
+ RefPtr<ComputedStyle>& cache = mNonInheritingComputedStyles
+ [nsCSSAnonBoxes::NonInheriting::oofPlaceholder];
+ if (cache) {
+ RefPtr<ComputedStyle> retval = cache;
+ return retval.forget();
+ }
+
+ RefPtr<ComputedStyle> computedValues =
+ Servo_ComputedValues_Inherit(mRawData.get(),
+ PseudoStyleType::oofPlaceholder, nullptr,
+ InheritTarget::PlaceholderFrame)
+ .Consume();
+ MOZ_ASSERT(computedValues);
+
+ cache = computedValues;
+ return computedValues.forget();
+}
+
+static inline bool LazyPseudoIsCacheable(PseudoStyleType aType,
+ const Element& aOriginatingElement,
+ ComputedStyle* aParentStyle) {
+ return aParentStyle &&
+ !nsCSSPseudoElements::IsEagerlyCascadedInServo(aType) &&
+ aOriginatingElement.HasServoData() &&
+ !Servo_Element_IsPrimaryStyleReusedViaRuleNode(&aOriginatingElement);
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ResolvePseudoElementStyle(
+ const Element& aOriginatingElement, PseudoStyleType aType,
+ ComputedStyle* aParentStyle, IsProbe aIsProbe) {
+ // Runs from frame construction, this should have clean styles already, except
+ // with non-lazy FC...
+ UpdateStylistIfNeeded();
+ MOZ_ASSERT(PseudoStyle::IsPseudoElement(aType));
+
+ const bool cacheable =
+ LazyPseudoIsCacheable(aType, aOriginatingElement, aParentStyle);
+ RefPtr<ComputedStyle> style =
+ cacheable ? aParentStyle->GetCachedLazyPseudoStyle(aType) : nullptr;
+
+ const bool isProbe = aIsProbe == IsProbe::Yes;
+
+ if (!style) {
+ // FIXME(emilio): Why passing null for probing as the parent style?
+ //
+ // There are callers which do pass the wrong parent style and it would
+ // assert (like ComputeSelectionStyle()). That's messy!
+ style = Servo_ResolvePseudoStyle(&aOriginatingElement, aType, isProbe,
+ isProbe ? nullptr : aParentStyle,
+ mRawData.get())
+ .Consume();
+ if (!style) {
+ MOZ_ASSERT(isProbe);
+ return nullptr;
+ }
+ if (cacheable) {
+ aParentStyle->SetCachedLazyPseudoStyle(style);
+ }
+ }
+
+ MOZ_ASSERT(style);
+
+ if (isProbe && !GeneratedContentPseudoExists(*aParentStyle, *style)) {
+ return nullptr;
+ }
+
+ return style.forget();
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ProbeHighlightPseudoElementStyle(
+ const dom::Element& aOriginatingElement, const nsAtom* aHighlightName,
+ ComputedStyle* aParentStyle) {
+ MOZ_ASSERT(!StylistNeedsUpdate());
+ MOZ_ASSERT(aHighlightName);
+
+ RefPtr<ComputedStyle> style =
+ Servo_ComputedValues_ResolveHighlightPseudoStyle(
+ &aOriginatingElement, aHighlightName, mRawData.get())
+ .Consume();
+
+ return style ? style.forget() : nullptr;
+}
+
+already_AddRefed<ComputedStyle>
+ServoStyleSet::ResolveInheritingAnonymousBoxStyle(PseudoStyleType aType,
+ ComputedStyle* aParentStyle) {
+ MOZ_ASSERT(PseudoStyle::IsInheritingAnonBox(aType));
+ MOZ_ASSERT_IF(aParentStyle, !StylistNeedsUpdate());
+
+ UpdateStylistIfNeeded();
+
+ RefPtr<ComputedStyle> style = nullptr;
+
+ if (aParentStyle) {
+ style = aParentStyle->GetCachedInheritingAnonBoxStyle(aType);
+ }
+
+ if (!style) {
+ style = Servo_ComputedValues_GetForAnonymousBox(aParentStyle, aType,
+ mRawData.get())
+ .Consume();
+ MOZ_ASSERT(style);
+ if (aParentStyle) {
+ aParentStyle->SetCachedInheritedAnonBoxStyle(style);
+ }
+ }
+
+ return style.forget();
+}
+
+already_AddRefed<ComputedStyle>
+ServoStyleSet::ResolveNonInheritingAnonymousBoxStyle(PseudoStyleType aType) {
+ MOZ_ASSERT(aType != PseudoStyleType::pageContent,
+ "Use ResolvePageContentStyle for page content");
+ MOZ_ASSERT(PseudoStyle::IsNonInheritingAnonBox(aType));
+
+ nsCSSAnonBoxes::NonInheriting type =
+ nsCSSAnonBoxes::NonInheritingTypeForPseudoType(aType);
+ RefPtr<ComputedStyle>& cache = mNonInheritingComputedStyles[type];
+ if (cache) {
+ RefPtr<ComputedStyle> retval = cache;
+ return retval.forget();
+ }
+
+ UpdateStylistIfNeeded();
+
+ // We always want to skip parent-based display fixup here. It never makes
+ // sense for non-inheriting anonymous boxes. (Static assertions in
+ // nsCSSAnonBoxes.cpp ensure that all non-inheriting non-anonymous boxes
+ // are indeed annotated as skipping this fixup.)
+ MOZ_ASSERT(!PseudoStyle::IsNonInheritingAnonBox(PseudoStyleType::viewport),
+ "viewport needs fixup to handle blockifying it");
+
+ RefPtr<ComputedStyle> computedValues =
+ Servo_ComputedValues_GetForAnonymousBox(nullptr, aType, mRawData.get())
+ .Consume();
+ MOZ_ASSERT(computedValues);
+
+ cache = computedValues;
+ return computedValues.forget();
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ResolvePageContentStyle(
+ const nsAtom* aPageName) {
+ // The empty atom is used to indicate no specified page name, and is not
+ // usable as a page-rule selector. Changing this to null is a slight
+ // optimization to avoid the Servo code from doing an unnecessary hashtable
+ // lookup, and still use the style cache in this case.
+ if (aPageName == nsGkAtoms::_empty) {
+ aPageName = nullptr;
+ }
+ // Only use the cache if we are not doing a lookup for a named page style.
+ RefPtr<ComputedStyle>& cache =
+ mNonInheritingComputedStyles[nsCSSAnonBoxes::NonInheriting::pageContent];
+ if (!aPageName && cache) {
+ RefPtr<ComputedStyle> retval = cache;
+ return retval.forget();
+ }
+
+ UpdateStylistIfNeeded();
+
+ RefPtr<ComputedStyle> computedValues =
+ Servo_ComputedValues_GetForPageContent(mRawData.get(), aPageName)
+ .Consume();
+ MOZ_ASSERT(computedValues);
+
+ if (!aPageName) {
+ cache = computedValues;
+ }
+ return computedValues.forget();
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ResolveXULTreePseudoStyle(
+ dom::Element* aParentElement, nsCSSAnonBoxPseudoStaticAtom* aPseudoTag,
+ ComputedStyle* aParentStyle, const AtomArray& aInputWord) {
+ MOZ_ASSERT(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag));
+ MOZ_ASSERT(aParentStyle);
+ MOZ_ASSERT(!StylistNeedsUpdate());
+
+ return Servo_ComputedValues_ResolveXULTreePseudoStyle(
+ aParentElement, aPseudoTag, aParentStyle, &aInputWord,
+ mRawData.get())
+ .Consume();
+}
+
+// manage the set of style sheets in the style set
+void ServoStyleSet::AppendStyleSheet(StyleSheet& aSheet) {
+ MOZ_ASSERT(aSheet.IsApplicable());
+ MOZ_ASSERT(aSheet.RawContents(),
+ "Raw sheet should be in place before insertion.");
+
+ aSheet.AddStyleSet(this);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ // Servo will remove aSheet from its original position as part of the call
+ // to Servo_StyleSet_AppendStyleSheet.
+ Servo_StyleSet_AppendStyleSheet(mRawData.get(), &aSheet);
+ SetStylistStyleSheetsDirty();
+
+ if (mStyleRuleMap) {
+ mStyleRuleMap->SheetAdded(aSheet);
+ }
+}
+
+void ServoStyleSet::RemoveStyleSheet(StyleSheet& aSheet) {
+ aSheet.DropStyleSet(this);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_RemoveStyleSheet(mRawData.get(), &aSheet);
+ SetStylistStyleSheetsDirty();
+
+ if (mStyleRuleMap) {
+ mStyleRuleMap->SheetRemoved(aSheet);
+ }
+}
+
+void ServoStyleSet::InsertStyleSheetBefore(StyleSheet& aNewSheet,
+ StyleSheet& aReferenceSheet) {
+ MOZ_ASSERT(aNewSheet.IsApplicable());
+ MOZ_ASSERT(aReferenceSheet.IsApplicable());
+ MOZ_ASSERT(&aNewSheet != &aReferenceSheet,
+ "Can't place sheet before itself.");
+ MOZ_ASSERT(aNewSheet.GetOrigin() == aReferenceSheet.GetOrigin(),
+ "Sheets should be in the same origin");
+ MOZ_ASSERT(aNewSheet.RawContents(),
+ "Raw sheet should be in place before insertion.");
+ MOZ_ASSERT(aReferenceSheet.RawContents(),
+ "Reference sheet should have a raw sheet.");
+
+ // Servo will remove aNewSheet from its original position as part of the
+ // call to Servo_StyleSet_InsertStyleSheetBefore.
+ aNewSheet.AddStyleSet(this);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_InsertStyleSheetBefore(mRawData.get(), &aNewSheet,
+ &aReferenceSheet);
+ SetStylistStyleSheetsDirty();
+
+ if (mStyleRuleMap) {
+ mStyleRuleMap->SheetAdded(aNewSheet);
+ }
+}
+
+size_t ServoStyleSet::SheetCount(Origin aOrigin) const {
+ return Servo_StyleSet_GetSheetCount(mRawData.get(), aOrigin);
+}
+
+StyleSheet* ServoStyleSet::SheetAt(Origin aOrigin, size_t aIndex) const {
+ return const_cast<StyleSheet*>(
+ Servo_StyleSet_GetSheetAt(mRawData.get(), aOrigin, aIndex));
+}
+
+ServoStyleSet::FirstPageSizeAndOrientation
+ServoStyleSet::GetFirstPageSizeAndOrientation(const nsAtom* aFirstPageName) {
+ FirstPageSizeAndOrientation retval;
+ const RefPtr<ComputedStyle> style = ResolvePageContentStyle(aFirstPageName);
+ const StylePageSize& pageSize = style->StylePage()->mSize;
+
+ if (pageSize.IsSize()) {
+ const nscoord w = pageSize.AsSize().width.ToAppUnits();
+ const nscoord h = pageSize.AsSize().height.ToAppUnits();
+ // Ignoring sizes that include a zero width or height.
+ // These are also ignored in nsPageFrame::ComputePageSize()
+ // when calculating the scaling for a page size.
+ // In bug 1807985, we might add similar handling for @page margin/size
+ // combinations that produce a zero-sized page-content box.
+ if (w > 0 && h > 0) {
+ retval.size.emplace(w, h);
+ if (w > h) {
+ retval.orientation.emplace(StylePageSizeOrientation::Landscape);
+ } else if (w < h) {
+ retval.orientation.emplace(StylePageSizeOrientation::Portrait);
+ }
+ }
+ } else if (pageSize.IsOrientation()) {
+ retval.orientation.emplace(pageSize.AsOrientation());
+ } else {
+ MOZ_ASSERT(pageSize.IsAuto(), "Impossible page size");
+ }
+ return retval;
+}
+
+void ServoStyleSet::AppendAllNonDocumentAuthorSheets(
+ nsTArray<StyleSheet*>& aArray) const {
+ EnumerateShadowRoots(*mDocument, [&](ShadowRoot& aShadowRoot) {
+ for (auto index : IntegerRange(aShadowRoot.SheetCount())) {
+ aArray.AppendElement(aShadowRoot.SheetAt(index));
+ }
+ aArray.AppendElements(aShadowRoot.AdoptedStyleSheets());
+ });
+}
+
+void ServoStyleSet::AddDocStyleSheet(StyleSheet& aSheet) {
+ MOZ_ASSERT(aSheet.IsApplicable());
+ MOZ_ASSERT(aSheet.RawContents(),
+ "Raw sheet should be in place by this point.");
+
+ size_t index = mDocument->FindDocStyleSheetInsertionPoint(aSheet);
+ aSheet.AddStyleSet(this);
+
+ if (index < SheetCount(Origin::Author)) {
+ // This case is insert before.
+ StyleSheet* beforeSheet = SheetAt(Origin::Author, index);
+
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_InsertStyleSheetBefore(mRawData.get(), &aSheet, beforeSheet);
+ SetStylistStyleSheetsDirty();
+ } else {
+ // Maintain a mirrored list of sheets on the servo side.
+ Servo_StyleSet_AppendStyleSheet(mRawData.get(), &aSheet);
+ SetStylistStyleSheetsDirty();
+ }
+
+ if (mStyleRuleMap) {
+ mStyleRuleMap->SheetAdded(aSheet);
+ }
+}
+
+bool ServoStyleSet::GeneratedContentPseudoExists(
+ const ComputedStyle& aParentStyle, const ComputedStyle& aPseudoStyle) {
+ auto type = aPseudoStyle.GetPseudoType();
+ MOZ_ASSERT(type != PseudoStyleType::NotPseudo);
+
+ if (type == PseudoStyleType::marker) {
+ // ::marker only exist for list items (for now).
+ if (!aParentStyle.StyleDisplay()->IsListItem()) {
+ return false;
+ }
+ const auto& content = aPseudoStyle.StyleContent()->mContent;
+ // ::marker does not exist if 'content' is 'none' (this trumps
+ // any 'list-style-type' or 'list-style-image' values).
+ if (content.IsNone()) {
+ return false;
+ }
+ // ::marker only exist if we have 'content' or at least one of
+ // 'list-style-type' or 'list-style-image'.
+ if (aPseudoStyle.StyleList()->mCounterStyle.IsNone() &&
+ aPseudoStyle.StyleList()->mListStyleImage.IsNone() &&
+ content.IsNormal()) {
+ return false;
+ }
+ // display:none is equivalent to not having a pseudo at all.
+ if (aPseudoStyle.StyleDisplay()->mDisplay == StyleDisplay::None) {
+ return false;
+ }
+ }
+
+ // For ::before and ::after pseudo-elements, no 'content' items is
+ // equivalent to not having the pseudo-element at all.
+ if (type == PseudoStyleType::before || type == PseudoStyleType::after) {
+ if (!aPseudoStyle.StyleContent()->mContent.IsItems()) {
+ return false;
+ }
+ MOZ_ASSERT(aPseudoStyle.StyleContent()->ContentCount() > 0,
+ "IsItems() implies we have at least one item");
+ // display:none is equivalent to not having a pseudo at all.
+ if (aPseudoStyle.StyleDisplay()->mDisplay == StyleDisplay::None) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ServoStyleSet::StyleDocument(ServoTraversalFlags aFlags) {
+ AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(LAYOUT_StyleComputation);
+ MOZ_ASSERT(GetPresContext(), "Styling a document without a shell?");
+
+ if (!mDocument->GetServoRestyleRoot()) {
+ return false;
+ }
+
+ Element* rootElement = mDocument->GetRootElement();
+ if (rootElement && MOZ_UNLIKELY(!rootElement->HasServoData())) {
+ StyleNewSubtree(rootElement);
+ return true;
+ }
+
+ PreTraverse(aFlags);
+ AutoPrepareTraversal guard(this);
+ const SnapshotTable& snapshots = Snapshots();
+
+ // Restyle the document from the root element and each of the document level
+ // NAC subtree roots.
+ bool postTraversalRequired = false;
+
+ if (ShouldTraverseInParallel()) {
+ aFlags |= ServoTraversalFlags::ParallelTraversal;
+ }
+
+ // Do the first traversal.
+ DocumentStyleRootIterator iter(mDocument->GetServoRestyleRoot());
+ while (Element* root = iter.GetNextStyleRoot()) {
+ MOZ_ASSERT(MayTraverseFrom(root));
+
+ Element* parent = root->GetFlattenedTreeParentElementForStyle();
+ MOZ_ASSERT_IF(parent,
+ !parent->HasAnyOfFlags(Element::kAllServoDescendantBits));
+
+ postTraversalRequired |=
+ Servo_TraverseSubtree(root, mRawData.get(), &snapshots, aFlags);
+ postTraversalRequired |= root->HasAnyOfFlags(
+ Element::kAllServoDescendantBits | NODE_NEEDS_FRAME);
+
+ {
+ uint32_t existingBits = mDocument->GetServoRestyleRootDirtyBits();
+ Element* newRoot = nullptr;
+ while (parent && parent->HasDirtyDescendantsForServo()) {
+ MOZ_ASSERT(root == mDocument->GetServoRestyleRoot(),
+ "Restyle root shouldn't have magically changed");
+ // If any style invalidation was triggered in our siblings, then we may
+ // need to post-traverse them, even if the root wasn't restyled after
+ // all.
+ // We need to propagate the existing bits to the ancestor.
+ parent->SetFlags(existingBits);
+ newRoot = parent;
+ parent = parent->GetFlattenedTreeParentElementForStyle();
+ }
+
+ if (newRoot) {
+ mDocument->SetServoRestyleRoot(
+ newRoot, existingBits | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+ postTraversalRequired = true;
+ }
+ }
+ }
+
+ // If there are still animation restyles needed, trigger a second traversal to
+ // update CSS animations or transitions' styles.
+ //
+ // Note that we need to check the style root again, because doing another
+ // PreTraverse on the EffectCompositor might alter the style root. But we
+ // don't need to worry about NAC, since document-level NAC shouldn't have
+ // animations.
+ //
+ // We don't need to do this for SMIL since SMIL only updates its animation
+ // values once at the begin of a tick. As a result, even if the previous
+ // traversal caused, for example, the font-size to change, the SMIL style
+ // won't be updated until the next tick anyway.
+ if (GetPresContext()->EffectCompositor()->PreTraverse(aFlags)) {
+ nsINode* styleRoot = mDocument->GetServoRestyleRoot();
+ Element* root =
+ styleRoot->IsElement() ? styleRoot->AsElement() : rootElement;
+
+ postTraversalRequired |=
+ Servo_TraverseSubtree(root, mRawData.get(), &snapshots, aFlags);
+ postTraversalRequired |= root->HasAnyOfFlags(
+ Element::kAllServoDescendantBits | NODE_NEEDS_FRAME);
+ }
+
+ return postTraversalRequired;
+}
+
+void ServoStyleSet::StyleNewSubtree(Element* aRoot) {
+ MOZ_ASSERT(GetPresContext());
+ MOZ_ASSERT(!aRoot->HasServoData());
+ MOZ_ASSERT(aRoot->GetFlattenedTreeParentNodeForStyle(),
+ "Not in the flat tree? Fishy!");
+ PreTraverseSync();
+ AutoPrepareTraversal guard(this);
+
+ // Do the traversal. The snapshots will not be used.
+ const SnapshotTable& snapshots = Snapshots();
+ auto flags = ServoTraversalFlags::Empty;
+ if (ShouldTraverseInParallel()) {
+ flags |= ServoTraversalFlags::ParallelTraversal;
+ }
+
+ DebugOnly<bool> postTraversalRequired =
+ Servo_TraverseSubtree(aRoot, mRawData.get(), &snapshots, flags);
+ MOZ_ASSERT(!postTraversalRequired);
+
+ // Annoyingly, the newly-styled content may have animations that need
+ // starting, which requires traversing them again. Mark the elements
+ // that need animation processing, then do a forgetful traversal to
+ // update the styles and clear the animation bits.
+ if (GetPresContext()->EffectCompositor()->PreTraverseInSubtree(flags,
+ aRoot)) {
+ postTraversalRequired =
+ Servo_TraverseSubtree(aRoot, mRawData.get(), &snapshots,
+ ServoTraversalFlags::AnimationOnly |
+ ServoTraversalFlags::FinalAnimationTraversal);
+ MOZ_ASSERT(!postTraversalRequired);
+ }
+}
+
+void ServoStyleSet::MarkOriginsDirty(OriginFlags aChangedOrigins) {
+ SetStylistStyleSheetsDirty();
+ Servo_StyleSet_NoteStyleSheetsChanged(mRawData.get(), aChangedOrigins);
+}
+
+void ServoStyleSet::SetStylistStyleSheetsDirty() {
+ mStylistState |= StylistState::StyleSheetsDirty;
+
+ // We need to invalidate cached style in getComputedStyle for undisplayed
+ // elements, since we don't know if any of the style sheet change that we do
+ // would affect undisplayed elements.
+ //
+ // We don't allow to call getComputedStyle in elements without a pres shell
+ // yet, so it is fine if there's no pres context here.
+ if (nsPresContext* presContext = GetPresContext()) {
+ presContext->RestyleManager()->IncrementUndisplayedRestyleGeneration();
+ }
+}
+
+void ServoStyleSet::SetStylistShadowDOMStyleSheetsDirty() {
+ mStylistState |= StylistState::ShadowDOMStyleSheetsDirty;
+ if (nsPresContext* presContext = GetPresContext()) {
+ presContext->RestyleManager()->IncrementUndisplayedRestyleGeneration();
+ }
+}
+
+static OriginFlags ToOriginFlags(StyleOrigin aOrigin) {
+ switch (aOrigin) {
+ case StyleOrigin::UserAgent:
+ return OriginFlags::UserAgent;
+ case StyleOrigin::User:
+ return OriginFlags::User;
+ default:
+ MOZ_FALLTHROUGH_ASSERT("Unknown origin?");
+ case StyleOrigin::Author:
+ return OriginFlags::Author;
+ }
+}
+
+void ServoStyleSet::ImportRuleLoaded(dom::CSSImportRule&, StyleSheet& aSheet) {
+ if (mStyleRuleMap) {
+ mStyleRuleMap->SheetAdded(aSheet);
+ }
+
+ // TODO: Should probably consider ancestor sheets too.
+ if (!aSheet.IsApplicable()) {
+ return;
+ }
+
+ // TODO(emilio): Could handle it better given we know it is an insertion, and
+ // use the style invalidation machinery stuff that we do for regular sheet
+ // insertions.
+ MarkOriginsDirty(ToOriginFlags(aSheet.GetOrigin()));
+}
+
+void ServoStyleSet::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
+ if (mStyleRuleMap) {
+ mStyleRuleMap->RuleAdded(aSheet, aRule);
+ }
+
+ if (!aSheet.IsApplicable() || aRule.IsIncompleteImportRule()) {
+ return;
+ }
+
+ RuleChangedInternal(aSheet, aRule, StyleRuleChangeKind::Insertion);
+}
+
+void ServoStyleSet::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
+ if (mStyleRuleMap) {
+ mStyleRuleMap->RuleRemoved(aSheet, aRule);
+ }
+
+ if (!aSheet.IsApplicable()) {
+ return;
+ }
+
+ RuleChangedInternal(aSheet, aRule, StyleRuleChangeKind::Removal);
+}
+
+void ServoStyleSet::RuleChangedInternal(StyleSheet& aSheet, css::Rule& aRule,
+ StyleRuleChangeKind aKind) {
+ MOZ_ASSERT(aSheet.IsApplicable());
+ SetStylistStyleSheetsDirty();
+
+#define CASE_FOR(constant_, type_) \
+ case StyleCssRuleType::constant_: \
+ return Servo_StyleSet_##constant_##RuleChanged( \
+ mRawData.get(), static_cast<dom::CSS##type_##Rule&>(aRule).Raw(), \
+ &aSheet, aKind);
+
+ switch (aRule.Type()) {
+ CASE_FOR(CounterStyle, CounterStyle)
+ CASE_FOR(Style, Style)
+ CASE_FOR(Import, Import)
+ CASE_FOR(Media, Media)
+ CASE_FOR(Keyframes, Keyframes)
+ CASE_FOR(FontFeatureValues, FontFeatureValues)
+ CASE_FOR(FontPaletteValues, FontPaletteValues)
+ CASE_FOR(FontFace, FontFace)
+ CASE_FOR(Page, Page)
+ CASE_FOR(Property, Property)
+ CASE_FOR(Document, MozDocument)
+ CASE_FOR(Supports, Supports)
+ CASE_FOR(LayerBlock, LayerBlock)
+ CASE_FOR(LayerStatement, LayerStatement)
+ CASE_FOR(Container, Container)
+ // @namespace can only be inserted / removed when there are only other
+ // @namespace and @import rules, and can't be mutated.
+ case StyleCssRuleType::Namespace:
+ break;
+ case StyleCssRuleType::Viewport:
+ MOZ_ASSERT_UNREACHABLE("Gecko doesn't implement @viewport");
+ break;
+ case StyleCssRuleType::Keyframe:
+ // FIXME: We should probably just forward to the parent @keyframes rule? I
+ // think that'd do the right thing, but meanwhile...
+ return MarkOriginsDirty(ToOriginFlags(aSheet.GetOrigin()));
+ }
+
+#undef CASE_FOR
+}
+
+void ServoStyleSet::RuleChanged(StyleSheet& aSheet, css::Rule* aRule,
+ StyleRuleChangeKind aKind) {
+ if (!aSheet.IsApplicable()) {
+ return;
+ }
+
+ if (!aRule) {
+ // FIXME: This is done for StyleSheet.media attribute changes and such
+ MarkOriginsDirty(ToOriginFlags(aSheet.GetOrigin()));
+ } else {
+ RuleChangedInternal(aSheet, *aRule, aKind);
+ }
+}
+
+void ServoStyleSet::SheetCloned(StyleSheet& aSheet) {
+ mNeedsRestyleAfterEnsureUniqueInner = true;
+ if (mStyleRuleMap) {
+ mStyleRuleMap->SheetCloned(aSheet);
+ }
+}
+
+#ifdef DEBUG
+void ServoStyleSet::AssertTreeIsClean() {
+ DocumentStyleRootIterator iter(mDocument);
+ while (Element* root = iter.GetNextStyleRoot()) {
+ Servo_AssertTreeIsClean(root);
+ }
+}
+#endif
+
+bool ServoStyleSet::GetKeyframesForName(
+ const Element& aElement, const ComputedStyle& aStyle, nsAtom* aName,
+ const StyleComputedTimingFunction& aTimingFunction,
+ nsTArray<Keyframe>& aKeyframes) {
+ MOZ_ASSERT(!StylistNeedsUpdate());
+ return Servo_StyleSet_GetKeyframesForName(
+ mRawData.get(), &aElement, &aStyle, aName, &aTimingFunction, &aKeyframes);
+}
+
+nsTArray<ComputedKeyframeValues> ServoStyleSet::GetComputedKeyframeValuesFor(
+ const nsTArray<Keyframe>& aKeyframes, Element* aElement,
+ PseudoStyleType aPseudoType, const ComputedStyle* aStyle) {
+ nsTArray<ComputedKeyframeValues> result(aKeyframes.Length());
+
+ // Construct each nsTArray<PropertyStyleAnimationValuePair> here.
+ result.AppendElements(aKeyframes.Length());
+
+ Servo_GetComputedKeyframeValues(&aKeyframes, aElement, aPseudoType, aStyle,
+ mRawData.get(), &result);
+ return result;
+}
+
+void ServoStyleSet::GetAnimationValues(
+ StyleLockedDeclarationBlock* aDeclarations, Element* aElement,
+ const ComputedStyle* aComputedStyle,
+ nsTArray<RefPtr<StyleAnimationValue>>& aAnimationValues) {
+ // Servo_GetAnimationValues below won't handle ignoring existing element
+ // data for bfcached documents. (See comment in ResolveStyleLazily
+ // about these bfcache issues.)
+ Servo_GetAnimationValues(aDeclarations, aElement, aComputedStyle,
+ mRawData.get(), &aAnimationValues);
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::GetBaseContextForElement(
+ Element* aElement, const ComputedStyle* aStyle) {
+ return Servo_StyleSet_GetBaseComputedValuesForElement(
+ mRawData.get(), aElement, aStyle, &Snapshots())
+ .Consume();
+}
+
+already_AddRefed<ComputedStyle>
+ServoStyleSet::ResolveServoStyleByAddingAnimation(
+ Element* aElement, const ComputedStyle* aStyle,
+ StyleAnimationValue* aAnimationValue) {
+ return Servo_StyleSet_GetComputedValuesByAddingAnimation(
+ mRawData.get(), aElement, aStyle, &Snapshots(), aAnimationValue)
+ .Consume();
+}
+
+already_AddRefed<StyleAnimationValue> ServoStyleSet::ComputeAnimationValue(
+ Element* aElement, StyleLockedDeclarationBlock* aDeclarations,
+ const ComputedStyle* aStyle) {
+ return Servo_AnimationValue_Compute(aElement, aDeclarations, aStyle,
+ mRawData.get())
+ .Consume();
+}
+
+bool ServoStyleSet::UsesFontMetrics() const {
+ return Servo_StyleSet_UsesFontMetrics(mRawData.get());
+}
+
+bool ServoStyleSet::EnsureUniqueInnerOnCSSSheets() {
+ using SheetOwner = Variant<ServoStyleSet*, ShadowRoot*>;
+
+ AutoTArray<std::pair<StyleSheet*, SheetOwner>, 32> queue;
+ EnumerateStyleSheets([&](StyleSheet& aSheet) {
+ queue.AppendElement(std::make_pair(&aSheet, SheetOwner{this}));
+ });
+
+ EnumerateShadowRoots(*mDocument, [&](ShadowRoot& aShadowRoot) {
+ for (auto index : IntegerRange(aShadowRoot.SheetCount())) {
+ queue.AppendElement(
+ std::make_pair(aShadowRoot.SheetAt(index), SheetOwner{&aShadowRoot}));
+ }
+ for (const auto& adopted : aShadowRoot.AdoptedStyleSheets()) {
+ queue.AppendElement(
+ std::make_pair(adopted.get(), SheetOwner{&aShadowRoot}));
+ }
+ });
+
+ while (!queue.IsEmpty()) {
+ auto [sheet, owner] = queue.PopLastElement();
+
+ if (sheet->HasForcedUniqueInner()) {
+ // We already processed this sheet and its children.
+ // Normally we don't hit this but adopted stylesheets can have dupes so we
+ // can save some work here.
+ continue;
+ }
+
+ // Only call EnsureUniqueInner for complete sheets. If we do call it on
+ // incomplete sheets, we'll cause problems when the sheet is actually
+ // loaded. We don't care about incomplete sheets here anyway, because this
+ // method is only invoked by nsPresContext::EnsureSafeToHandOutCSSRules.
+ // The CSSRule objects we are handing out won't contain any rules derived
+ // from incomplete sheets (because they aren't yet applied in styling).
+ if (sheet->IsComplete()) {
+ sheet->EnsureUniqueInner();
+ }
+
+ // Enqueue all the sheet's children.
+ for (StyleSheet* child : sheet->ChildSheets()) {
+ queue.AppendElement(std::make_pair(child, owner));
+ }
+ }
+
+ if (mNeedsRestyleAfterEnsureUniqueInner) {
+ // TODO(emilio): We could make this faster if needed tracking the specific
+ // origins and sheets that have been cloned. But the only caller of this
+ // doesn't seem to really care about perf.
+ MarkOriginsDirty(OriginFlags::All);
+ ForceDirtyAllShadowStyles();
+ }
+ bool res = mNeedsRestyleAfterEnsureUniqueInner;
+ mNeedsRestyleAfterEnsureUniqueInner = false;
+ return res;
+}
+
+void ServoStyleSet::ClearCachedStyleData() {
+ ClearNonInheritingComputedStyles();
+ Servo_StyleSet_RebuildCachedData(mRawData.get());
+ mCachedAnonymousContentStyles.Clear();
+ PodArrayZero(mCachedAnonymousContentStyleIndexes);
+}
+
+void ServoStyleSet::ForceDirtyAllShadowStyles() {
+ bool anyShadow = false;
+ EnumerateShadowRoots(*mDocument, [&](ShadowRoot& aShadowRoot) {
+ if (auto* authorStyles = aShadowRoot.GetServoStyles()) {
+ anyShadow = true;
+ Servo_AuthorStyles_ForceDirty(authorStyles);
+ }
+ });
+ if (anyShadow) {
+ SetStylistShadowDOMStyleSheetsDirty();
+ }
+}
+
+void ServoStyleSet::CompatibilityModeChanged() {
+ Servo_StyleSet_CompatModeChanged(mRawData.get());
+ SetStylistStyleSheetsDirty();
+ ForceDirtyAllShadowStyles();
+}
+
+void ServoStyleSet::ClearNonInheritingComputedStyles() {
+ for (RefPtr<ComputedStyle>& ptr : mNonInheritingComputedStyles) {
+ ptr = nullptr;
+ }
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ResolveStyleLazily(
+ const Element& aElement, PseudoStyleType aPseudoType,
+ StyleRuleInclusion aRuleInclusion) {
+ PreTraverseSync();
+ MOZ_ASSERT(!StylistNeedsUpdate());
+
+ AutoSetInServoTraversal guard(this);
+
+ /**
+ * NB: This is needed because we process animations and transitions on the
+ * pseudo-elements themselves, not on the parent's EagerPseudoStyles.
+ *
+ * That means that that style doesn't account for animations, and we can't do
+ * that easily from the traversal without doing wasted work.
+ *
+ * As such, we just lie here a bit, which is the entrypoint of
+ * getComputedStyle, the only API where this can be observed, to look at the
+ * style of the pseudo-element if it exists instead.
+ */
+ const Element* elementForStyleResolution = &aElement;
+ PseudoStyleType pseudoTypeForStyleResolution = aPseudoType;
+ if (aPseudoType == PseudoStyleType::before) {
+ if (Element* pseudo = nsLayoutUtils::GetBeforePseudo(&aElement)) {
+ elementForStyleResolution = pseudo;
+ pseudoTypeForStyleResolution = PseudoStyleType::NotPseudo;
+ }
+ } else if (aPseudoType == PseudoStyleType::after) {
+ if (Element* pseudo = nsLayoutUtils::GetAfterPseudo(&aElement)) {
+ elementForStyleResolution = pseudo;
+ pseudoTypeForStyleResolution = PseudoStyleType::NotPseudo;
+ }
+ } else if (aPseudoType == PseudoStyleType::marker) {
+ if (Element* pseudo = nsLayoutUtils::GetMarkerPseudo(&aElement)) {
+ elementForStyleResolution = pseudo;
+ pseudoTypeForStyleResolution = PseudoStyleType::NotPseudo;
+ }
+ }
+
+ nsPresContext* pc = GetPresContext();
+ MOZ_ASSERT(pc, "For now, no style resolution without a pres context");
+ auto* restyleManager = pc->RestyleManager();
+ const bool canUseCache = aRuleInclusion == StyleRuleInclusion::All &&
+ aElement.OwnerDoc() == mDocument &&
+ pc->PresShell()->DidInitialize();
+ return Servo_ResolveStyleLazily(
+ elementForStyleResolution, pseudoTypeForStyleResolution,
+ aRuleInclusion, &restyleManager->Snapshots(),
+ restyleManager->GetUndisplayedRestyleGeneration(), canUseCache,
+ mRawData.get())
+ .Consume();
+}
+
+void ServoStyleSet::AppendFontFaceRules(
+ nsTArray<nsFontFaceRuleContainer>& aArray) {
+ // TODO(emilio): Can we make this so this asserts instead?
+ UpdateStylistIfNeeded();
+ Servo_StyleSet_GetFontFaceRules(mRawData.get(), &aArray);
+}
+
+const StyleLockedCounterStyleRule* ServoStyleSet::CounterStyleRuleForName(
+ nsAtom* aName) {
+ MOZ_ASSERT(!StylistNeedsUpdate());
+ return Servo_StyleSet_GetCounterStyleRule(mRawData.get(), aName);
+}
+
+already_AddRefed<gfxFontFeatureValueSet>
+ServoStyleSet::BuildFontFeatureValueSet() {
+ MOZ_ASSERT(!StylistNeedsUpdate());
+ RefPtr<gfxFontFeatureValueSet> set =
+ Servo_StyleSet_BuildFontFeatureValueSet(mRawData.get());
+ return set.forget();
+}
+
+already_AddRefed<gfx::FontPaletteValueSet>
+ServoStyleSet::BuildFontPaletteValueSet() {
+ MOZ_ASSERT(!StylistNeedsUpdate());
+ RefPtr<gfx::FontPaletteValueSet> set =
+ Servo_StyleSet_BuildFontPaletteValueSet(mRawData.get());
+ return set.forget();
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ResolveForDeclarations(
+ const ComputedStyle* aParentOrNull,
+ const StyleLockedDeclarationBlock* aDeclarations) {
+ // No need to update the stylist, we're only cascading aDeclarations.
+ return Servo_StyleSet_ResolveForDeclarations(mRawData.get(), aParentOrNull,
+ aDeclarations)
+ .Consume();
+}
+
+void ServoStyleSet::UpdateStylist() {
+ AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Update stylesheet information", LAYOUT);
+ MOZ_ASSERT(StylistNeedsUpdate());
+
+ if (mStylistState & StylistState::StyleSheetsDirty) {
+ Element* root = mDocument->GetRootElement();
+ const ServoElementSnapshotTable* snapshots = nullptr;
+ if (nsPresContext* pc = GetPresContext()) {
+ snapshots = &pc->RestyleManager()->Snapshots();
+ }
+ Servo_StyleSet_FlushStyleSheets(mRawData.get(), root, snapshots);
+ }
+
+ if (MOZ_UNLIKELY(mStylistState & StylistState::ShadowDOMStyleSheetsDirty)) {
+ EnumerateShadowRoots(*mDocument, [&](ShadowRoot& aShadowRoot) {
+ if (auto* authorStyles = aShadowRoot.GetServoStyles()) {
+ Servo_AuthorStyles_Flush(authorStyles, mRawData.get());
+ }
+ });
+ Servo_StyleSet_RemoveUniqueEntriesFromAuthorStylesCache(mRawData.get());
+ }
+
+ mStylistState = StylistState::NotDirty;
+}
+
+void ServoStyleSet::MaybeGCRuleTree() {
+ MOZ_ASSERT(NS_IsMainThread());
+ Servo_MaybeGCRuleTree(mRawData.get());
+}
+
+/* static */
+bool ServoStyleSet::MayTraverseFrom(const Element* aElement) {
+ MOZ_ASSERT(aElement->IsInComposedDoc());
+ nsINode* parent = aElement->GetFlattenedTreeParentNodeForStyle();
+ if (!parent) {
+ return false;
+ }
+
+ if (!parent->IsElement()) {
+ MOZ_ASSERT(parent->IsDocument());
+ return true;
+ }
+
+ if (!parent->AsElement()->HasServoData()) {
+ return false;
+ }
+
+ return !Servo_Element_IsDisplayNone(parent->AsElement());
+}
+
+bool ServoStyleSet::ShouldTraverseInParallel() const {
+ MOZ_ASSERT(mDocument->GetPresShell(), "Styling a document without a shell?");
+ if (!mDocument->GetPresShell()->IsActive()) {
+ return false;
+ }
+ if (profiler_feature_active(ProfilerFeature::SequentialStyle)) {
+ return false;
+ }
+ return true;
+}
+
+void ServoStyleSet::RunPostTraversalTasks() {
+ MOZ_ASSERT(!IsInServoTraversal());
+
+ if (mPostTraversalTasks.IsEmpty()) {
+ return;
+ }
+
+ nsTArray<PostTraversalTask> tasks = std::move(mPostTraversalTasks);
+
+ for (auto& task : tasks) {
+ task.Run();
+ }
+}
+
+ServoStyleRuleMap* ServoStyleSet::StyleRuleMap() {
+ if (!mStyleRuleMap) {
+ mStyleRuleMap = MakeUnique<ServoStyleRuleMap>();
+ }
+ mStyleRuleMap->EnsureTable(*this);
+ return mStyleRuleMap.get();
+}
+
+bool ServoStyleSet::MightHaveAttributeDependency(const Element& aElement,
+ nsAtom* aAttribute) const {
+ return Servo_StyleSet_MightHaveAttributeDependency(mRawData.get(), &aElement,
+ aAttribute);
+}
+
+bool ServoStyleSet::MightHaveNthOfIDDependency(const Element& aElement,
+ nsAtom* aOldID,
+ nsAtom* aNewID) const {
+ return Servo_StyleSet_MightHaveNthOfIDDependency(mRawData.get(), &aElement,
+ aOldID, aNewID);
+}
+
+bool ServoStyleSet::MightHaveNthOfClassDependency(const Element& aElement) {
+ return Servo_StyleSet_MightHaveNthOfClassDependency(mRawData.get(), &aElement,
+ &Snapshots());
+}
+
+bool ServoStyleSet::MightHaveNthOfAttributeDependency(
+ const Element& aElement, nsAtom* aAttribute) const {
+ return Servo_StyleSet_MightHaveNthOfAttributeDependency(
+ mRawData.get(), &aElement, aAttribute);
+}
+
+bool ServoStyleSet::HasStateDependency(const Element& aElement,
+ dom::ElementState aState) const {
+ return Servo_StyleSet_HasStateDependency(mRawData.get(), &aElement,
+ aState.GetInternalValue());
+}
+
+bool ServoStyleSet::HasNthOfStateDependency(const Element& aElement,
+ dom::ElementState aState) const {
+ return Servo_StyleSet_HasNthOfStateDependency(mRawData.get(), &aElement,
+ aState.GetInternalValue());
+}
+
+bool ServoStyleSet::HasDocumentStateDependency(
+ dom::DocumentState aState) const {
+ return Servo_StyleSet_HasDocumentStateDependency(mRawData.get(),
+ aState.GetInternalValue());
+}
+
+already_AddRefed<ComputedStyle> ServoStyleSet::ReparentComputedStyle(
+ ComputedStyle* aComputedStyle, ComputedStyle* aNewParent,
+ ComputedStyle* aNewParentIgnoringFirstLine, ComputedStyle* aNewLayoutParent,
+ Element* aElement) {
+ return Servo_ReparentStyle(aComputedStyle, aNewParent,
+ aNewParentIgnoringFirstLine, aNewLayoutParent,
+ aElement, mRawData.get())
+ .Consume();
+}
+
+void ServoStyleSet::InvalidateForViewportUnits(OnlyDynamic aOnlyDynamic) {
+ dom::Element* root = mDocument->GetRootElement();
+ if (!root) {
+ return;
+ }
+
+ Servo_InvalidateForViewportUnits(mRawData.get(), root,
+ aOnlyDynamic == OnlyDynamic::Yes);
+}
+
+NS_IMPL_ISUPPORTS(UACacheReporter, nsIMemoryReporter)
+
+MOZ_DEFINE_MALLOC_SIZE_OF(ServoUACacheMallocSizeOf)
+MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoUACacheMallocEnclosingSizeOf)
+
+NS_IMETHODIMP
+UACacheReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) {
+ ServoStyleSetSizes sizes;
+ Servo_UACache_AddSizeOf(ServoUACacheMallocSizeOf,
+ ServoUACacheMallocEnclosingSizeOf, &sizes);
+
+#define REPORT(_path, _amount, _desc) \
+ do { \
+ size_t __amount = _amount; /* evaluate _amount only once */ \
+ if (__amount > 0) { \
+ MOZ_COLLECT_REPORT(_path, KIND_HEAP, UNITS_BYTES, __amount, _desc); \
+ } \
+ } while (0)
+
+ // The UA cache does not contain the rule tree; that's in the StyleSet.
+ MOZ_RELEASE_ASSERT(sizes.mRuleTree == 0);
+
+ REPORT("explicit/layout/servo-ua-cache/precomputed-pseudos",
+ sizes.mPrecomputedPseudos,
+ "Memory used by precomputed pseudo-element declarations within the "
+ "UA cache.");
+
+ REPORT("explicit/layout/servo-ua-cache/element-and-pseudos-maps",
+ sizes.mElementAndPseudosMaps,
+ "Memory used by element and pseudos maps within the UA cache.");
+
+ REPORT("explicit/layout/servo-ua-cache/invalidation-map",
+ sizes.mInvalidationMap,
+ "Memory used by invalidation maps within the UA cache.");
+
+ REPORT("explicit/layout/servo-ua-cache/revalidation-selectors",
+ sizes.mRevalidationSelectors,
+ "Memory used by selectors for cache revalidation within the UA "
+ "cache.");
+
+ REPORT("explicit/layout/servo-ua-cache/other", sizes.mOther,
+ "Memory used by other data within the UA cache");
+
+ return NS_OK;
+}
+
+} // namespace mozilla