зеркало из https://github.com/mozilla/gecko-dev.git
1576 строки
56 KiB
C++
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();
|
|
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,
|
|
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
|