gecko-dev/layout/style/ServoStyleSet.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1576 строки
56 KiB
C++
Исходник Обычный вид История

/* -*- 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/DocumentStyleRootIterator.h"
#include "mozilla/AttributeStyles.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/CSSBinding.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 "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)
: mSetInServoTraversal(aSet) {
MOZ_ASSERT(!aSet->StylistNeedsUpdate());
}
private:
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::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): These two 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();
UpdateStylistIfNeeded();
mDocument->ResolveScheduledPresAttrs();
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();
Bug 1725940 - patch 2 - Move nsFontCache from the device context to the prescontext. r=emilio To look up/instantiate platform fonts based on CSS font properties, we create a gfxFontGroup from an nsFont and other attributes; this is currently cached in an nsFontCache attached to the nsDeviceContext. However, this assumes that the mapping to platform fonts will be the same for all documents using the given device context. In a world where visibility of platform fonts to the page may be restricted, and may depend on the individual document (e.g. if the user disables tracking protection for a particular site), the mapping represented by nsFontCache may vary, and determining how to resolve a given font request will need access to the requesting document in order to know what visibility it is allowed. To support this, this patch moves the nsFontCache from nsDeviceContext to nsPresContext. In itself, this should cause no visible change in behavior, but it provides a basis for the patches that will follow in bug 1715501. It's likely that this will have some effects on individual performance tests, depending on the exact content and sequencing of page loads, because of changed caching behavior. E.g. having a per-presContext cache may sometimes mean that we no longer take advantage of a cached gfxFontGroup that a previously-loaded page created; but on the other hand the caches will tend to be smaller and have faster lookups. My testing so far suggests that we will see some apparent regressions, alongside some improvements, but that overall there should be little change. I'd like to get this change landed separately, before any of the actual font-visibility behavior changes, so that we can more clearly see and isolate any unexpected effects. Differential Revision: https://phabricator.services.mozilla.com/D122715
2021-08-16 16:58:03 +03:00
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,
nsAtom* aFunctionalPseudoParameter, 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));
// caching is done using `aType` only, therefore results would be wrong for
// pseudos with functional parameters (e.g. `::highlight(foo)`).
const bool cacheable =
!aFunctionalPseudoParameter &&
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, aFunctionalPseudoParameter,
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::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, const StylePagePseudoClassFlags& aPseudo) {
// 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 when we are doing a lookup for page styles without a
// page-name or any pseudo classes.
const bool useCache = !aPageName && !aPseudo;
RefPtr<ComputedStyle>& cache =
mNonInheritingComputedStyles[nsCSSAnonBoxes::NonInheriting::pageContent];
if (useCache && cache) {
RefPtr<ComputedStyle> retval = cache;
return retval.forget();
}
UpdateStylistIfNeeded();
RefPtr<ComputedStyle> computedValues =
Servo_ComputedValues_GetForPageContent(mRawData.get(), aPageName, aPseudo)
.Consume();
MOZ_ASSERT(computedValues);
if (useCache) {
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);
NS_ASSERTION(!StylistNeedsUpdate(),
"Stylesheets modified when resolving XUL tree pseudo");
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, StylePagePseudoClassFlags::FIRST);
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) ||
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)) {
DocumentStyleRootIterator iter(mDocument->GetServoRestyleRoot());
while (Element* root = iter.GetNextStyleRoot()) {
postTraversalRequired |=
Servo_TraverseSubtree(root, mRawData.get(), &snapshots, aFlags) ||
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::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<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,
nsAtom* aFunctionalPseudoParameter, 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,
aFunctionalPseudoParameter, 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());
}
void ServoStyleSet::MaybeInvalidateRelativeSelectorIDDependency(
const Element& aElement, nsAtom* aOldID, nsAtom* aNewID,
const ServoElementSnapshotTable& aSnapshots) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorIDDependency(
mRawData.get(), &aElement, aOldID, aNewID, &aSnapshots);
}
void ServoStyleSet::MaybeInvalidateRelativeSelectorClassDependency(
const Element& aElement, const ServoElementSnapshotTable& aSnapshots) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorClassDependency(
mRawData.get(), &aElement, &aSnapshots);
}
void ServoStyleSet::MaybeInvalidateRelativeSelectorAttributeDependency(
const Element& aElement, nsAtom* aAttribute,
const ServoElementSnapshotTable& aSnapshots) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorAttributeDependency(
mRawData.get(), &aElement, aAttribute, &aSnapshots);
}
void ServoStyleSet::MaybeInvalidateRelativeSelectorStateDependency(
const Element& aElement, ElementState aState,
const ServoElementSnapshotTable& aSnapshots) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorStateDependency(
mRawData.get(), &aElement, aState.GetInternalValue(), &aSnapshots);
}
void ServoStyleSet::MaybeInvalidateRelativeSelectorForEmptyDependency(
const Element& aElement) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorEmptyDependency(mRawData.get(),
&aElement);
}
void ServoStyleSet::MaybeInvalidateRelativeSelectorForNthEdgeDependency(
const Element& aElement) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorNthEdgeDependency(
mRawData.get(), &aElement);
}
void ServoStyleSet::MaybeInvalidateRelativeSelectorForNthDependencyFromSibling(
const Element* aFromSibling) {
if (aFromSibling == nullptr) {
return;
}
Servo_StyleSet_MaybeInvalidateRelativeSelectorNthDependencyFromSibling(
mRawData.get(), aFromSibling);
}
void ServoStyleSet::MaybeInvalidateForElementInsertion(
const Element& aElement) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorForInsertion(mRawData.get(),
&aElement);
}
void ServoStyleSet::MaybeInvalidateForElementAppend(const Element& aElement) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorForAppend(mRawData.get(),
&aElement);
}
void ServoStyleSet::MaybeInvalidateForElementRemove(
const Element& aElement, const Element* aPrevSibling,
const Element* aNextSibling) {
Servo_StyleSet_MaybeInvalidateRelativeSelectorForRemoval(
mRawData.get(), &aElement, aPrevSibling, aNextSibling);
}
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());
}
void ServoStyleSet::RestyleSiblingsForNthOf(const Element& aElement,
uint32_t aFlags) const {
Servo_StyleSet_RestyleSiblingsForNthOf(&aElement, aFlags);
}
bool ServoStyleSet::HasDocumentStateDependency(
dom::DocumentState aState) const {
return Servo_StyleSet_HasDocumentStateDependency(mRawData.get(),
aState.GetInternalValue());
}
already_AddRefed<ComputedStyle> ServoStyleSet::ReparentComputedStyle(
ComputedStyle* aComputedStyle, ComputedStyle* aNewParent,
Bug 1841128 - Simplify first-line reparenting a bit. r=TYLin The TLDR is that our first-line implementation is complete madness, but I'm not fixing bug 1465474 yet. The guarantee that first-line style reparenting relies on is that reparenting shouldn't change reset properties (that's why we had all this "style to inherit ignoring first-line" to inherit reset properties). Bug 1839223 broke this guarantee in the case the mapped attribute declarations are reused / mutated (which is something we want to do for performance). What's going on in the test-case is that the old style's rule-node declarations are mutated by the mapped attributes code. Re-cascading the old styles yields a different `float` property value. When a style change that causes a reframe like this one happens, we don't update the frame tree styles with the new styles: it'd be wrong and also useless work. First-line reparenting happens after we've recomputed the styles, but before we've reframed as needed. In this intermediate state, the frame remains with the old (floated) style, but the ::first-line reparenting code isn't aware that this frame is going away, so it happily re-cascades and updates the style anyways, creating a floated frame that has float: none in its computed style information. This causes the frame destruction code to crash. This patch restores that guarantee that ::first-line doesn't change reset properties by re-using the "reset properties cache" code-path. This is also simpler since it avoids us having to compute the "parent ignoring ::first-line" style. The code is also more explicit with the new enum rather than just an Option. Differential Revision: https://phabricator.services.mozilla.com/D182769
2023-07-07 19:46:52 +03:00
ComputedStyle* aNewLayoutParent, Element* aElement) {
return Servo_ReparentStyle(aComputedStyle, aNewParent, 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);
}
void ServoStyleSet::RegisterProperty(const PropertyDefinition& aDefinition,
ErrorResult& aRv) {
using Result = StyleRegisterCustomPropertyResult;
auto result = Servo_RegisterCustomProperty(
RawData(), mDocument->DefaultStyleAttrURLData(), &aDefinition.mName,
&aDefinition.mSyntax, aDefinition.mInherits,
aDefinition.mInitialValue.WasPassed() ? &aDefinition.mInitialValue.Value()
: nullptr);
switch (result) {
case Result::SuccessfullyRegistered:
if (Element* root = mDocument->GetRootElement()) {
if (nsPresContext* pc = GetPresContext()) {
pc->RestyleManager()->PostRestyleEvent(
root, RestyleHint::RecascadeSubtree(), nsChangeHint(0));
}
}
break;
case Result::InvalidName:
return aRv.ThrowSyntaxError("Invalid name");
case Result::InvalidSyntax:
return aRv.ThrowSyntaxError("Invalid syntax descriptor");
case Result::InvalidInitialValue:
return aRv.ThrowSyntaxError("Invalid initial value syntax");
case Result::NoInitialValue:
return aRv.ThrowSyntaxError(
"Initial value is required when syntax is not universal");
case Result::InitialValueNotComputationallyIndependent:
return aRv.ThrowSyntaxError(
"Initial value is required when syntax is not universal");
case Result::AlreadyRegistered:
return aRv.ThrowInvalidModificationError("Property already registered");
}
}
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