gecko-dev/layout/style/ServoStyleSet.cpp

1622 строки
52 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 "gfxPlatformFontList.h"
#include "mozilla/AutoRestyleTimelineMarker.h"
#include "mozilla/DocumentStyleRootIterator.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/RestyleManagerInlines.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/ServoStyleRuleMap.h"
#include "mozilla/ServoTypes.h"
#include "mozilla/css/Loader.h"
#include "mozilla/dom/AnonymousContent.h"
#include "mozilla/dom/ChildIterator.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 "nsCSSRuleProcessor.h"
#include "nsDeviceContext.h"
#include "nsHTMLStyleSheet.h"
#include "nsIAnonymousContentCreator.h"
#include "nsIDocumentInlines.h"
#include "nsPrintfCString.h"
#include "nsSMILAnimationController.h"
#include "nsStyleContext.h"
#include "nsStyleSet.h"
#include "gfxUserFontSet.h"
using namespace mozilla;
using namespace mozilla::dom;
ServoStyleSet* ServoStyleSet::sInServoTraversal = nullptr;
#ifdef DEBUG
bool
ServoStyleSet::IsCurrentThreadInServoTraversal()
{
return sInServoTraversal && (NS_IsMainThread() || Servo_IsWorkerThread());
}
#endif
namespace mozilla {
// 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(!ServoStyleSet::sInServoTraversal);
MOZ_ASSERT(aSet);
ServoStyleSet::sInServoTraversal = aSet;
}
~AutoSetInServoTraversal()
{
MOZ_ASSERT(ServoStyleSet::sInServoTraversal);
ServoStyleSet::sInServoTraversal = nullptr;
mSet->RunPostTraversalTasks();
}
private:
ServoStyleSet* mSet;
};
// Sets up for one or more calls to Servo_TraverseSubtree.
class MOZ_RAII AutoPrepareTraversal
{
public:
explicit AutoPrepareTraversal(ServoStyleSet* aSet)
// For markers for animations, we have already set the markers in
// ServoRestyleManager::PostRestyleEventForAnimations so that we don't need
// to care about animation restyles here.
: mTimelineMarker(aSet->mPresContext->GetDocShell(), false)
, mSetInServoTraversal(aSet)
{
MOZ_ASSERT(!aSet->StylistNeedsUpdate());
}
private:
AutoRestyleTimelineMarker mTimelineMarker;
AutoSetInServoTraversal mSetInServoTraversal;
};
} // namespace mozilla
ServoStyleSet::ServoStyleSet(Kind aKind)
: mKind(aKind)
, mPresContext(nullptr)
, mAuthorStyleDisabled(false)
, mStylistState(StylistState::NotDirty)
, mUserFontSetUpdateGeneration(0)
, mUserFontCacheUpdateGeneration(0)
, mNeedsRestyleAfterEnsureUniqueInner(false)
{
}
ServoStyleSet::~ServoStyleSet()
{
for (auto& sheetArray : mSheets) {
for (auto& sheet : sheetArray) {
sheet->DropStyleSet(this);
}
}
}
UniquePtr<ServoStyleSet>
ServoStyleSet::CreateXBLServoStyleSet(
nsPresContext* aPresContext,
const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets)
{
auto set = MakeUnique<ServoStyleSet>(Kind::ForXBL);
set->Init(aPresContext, nullptr);
// The XBL style sheets aren't document level sheets, but we need to
// decide a particular SheetType to add them to style set. This type
// doesn't affect the place where we pull those rules from
// stylist::push_applicable_declarations_as_xbl_only_stylist().
set->ReplaceSheets(SheetType::Doc, aNewSheets);
// Update stylist immediately.
set->UpdateStylist();
// The PresContext of the bound document could be destroyed anytime later,
// which shouldn't be used for XBL styleset, so we clear it here to avoid
// dangling pointer.
set->mPresContext = nullptr;
return set;
}
void
ServoStyleSet::Init(nsPresContext* aPresContext, nsBindingManager* aBindingManager)
{
mPresContext = aPresContext;
mLastPresContextUsesXBLStyleSet = aPresContext;
mRawSet.reset(Servo_StyleSet_Init(aPresContext));
mBindingManager = aBindingManager;
mPresContext->DeviceContext()->InitFontCache();
// Now that we have an mRawSet, go ahead and notify about whatever stylesheets
// we have so far.
for (auto& sheetArray : mSheets) {
for (auto& sheet : sheetArray) {
// There's no guarantee this will create a list on the servo side whose
// ordering matches the list that would have been created had all those
// sheets been appended/prepended/etc after we had mRawSet. That's okay
// because Servo only needs to maintain relative ordering within a sheet
// type, which this preserves.
MOZ_ASSERT(sheet->RawContents(),
"We should only append non-null raw sheets.");
Servo_StyleSet_AppendStyleSheet(mRawSet.get(), sheet);
}
}
// We added prefilled stylesheets into mRawSet, so the stylist is dirty.
// The Stylist should be updated later when necessary.
SetStylistStyleSheetsDirty();
}
void
ServoStyleSet::BeginShutdown()
{
nsIDocument* doc = mPresContext->Document();
// Remove the style rule map from document's observer and drop it.
if (mStyleRuleMap) {
doc->RemoveObserver(mStyleRuleMap);
doc->CSSLoader()->RemoveObserver(mStyleRuleMap);
mStyleRuleMap = nullptr;
}
}
void
ServoStyleSet::Shutdown()
{
// Make sure we drop our cached style contexts before the presshell arena
// starts going away.
ClearNonInheritingStyleContexts();
mRawSet = nullptr;
}
void
ServoStyleSet::InvalidateStyleForCSSRuleChanges()
{
MOZ_ASSERT(StylistNeedsUpdate());
mPresContext->RestyleManager()->AsServo()->PostRestyleEventForCSSRuleChanges();
}
bool
ServoStyleSet::SetPresContext(nsPresContext* aPresContext)
{
MOZ_ASSERT(IsForXBL(), "Only XBL styleset can set PresContext!");
mLastPresContextUsesXBLStyleSet = aPresContext;
const OriginFlags rulesChanged = static_cast<OriginFlags>(
Servo_StyleSet_SetDevice(mRawSet.get(), aPresContext));
if (rulesChanged != OriginFlags(0)) {
MarkOriginsDirty(rulesChanged);
return true;
}
return false;
}
nsRestyleHint
ServoStyleSet::MediumFeaturesChanged(bool aViewportChanged)
{
bool viewportUnitsUsed = false;
bool rulesChanged = MediumFeaturesChangedRules(&viewportUnitsUsed);
if (mBindingManager &&
mBindingManager->MediumFeaturesChanged(mPresContext)) {
SetStylistXBLStyleSheetsDirty();
rulesChanged = true;
}
if (rulesChanged) {
return eRestyle_Subtree;
}
if (viewportUnitsUsed && aViewportChanged) {
return eRestyle_ForceDescendants;
}
return nsRestyleHint(0);
}
bool
ServoStyleSet::MediumFeaturesChangedRules(bool* aViewportUnitsUsed)
{
MOZ_ASSERT(aViewportUnitsUsed);
const OriginFlags rulesChanged = static_cast<OriginFlags>(
Servo_StyleSet_MediumFeaturesChanged(mRawSet.get(), aViewportUnitsUsed));
if (rulesChanged != OriginFlags(0)) {
MarkOriginsDirty(rulesChanged);
return true;
}
return false;
}
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.mLayoutServoStyleSetsOther += mallocSizeOf(this);
if (mRawSet) {
aSizes.mLayoutServoStyleSetsOther += mallocSizeOf(mRawSet.get());
ServoStyleSetSizes sizes;
// Measure mRawSet. We use ServoStyleSetMallocSizeOf rather than
// aMallocSizeOf to distinguish in DMD's output the memory measured within
// Servo code.
Servo_StyleSet_AddSizeOfExcludingThis(ServoStyleSetMallocSizeOf,
ServoStyleSetMallocEnclosingSizeOf,
&sizes, mRawSet.get());
// The StyleSet does not contain precomputed pseudos; they are in the UA
// cache.
MOZ_RELEASE_ASSERT(sizes.mPrecomputedPseudos == 0);
aSizes.mLayoutServoStyleSetsStylistRuleTree += sizes.mRuleTree;
aSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps +=
sizes.mElementAndPseudosMaps;
aSizes.mLayoutServoStyleSetsStylistInvalidationMap +=
sizes.mInvalidationMap;
aSizes.mLayoutServoStyleSetsStylistRevalidationSelectors +=
sizes.mRevalidationSelectors;
aSizes.mLayoutServoStyleSetsStylistOther += sizes.mOther;
}
if (mStyleRuleMap) {
aSizes.mLayoutServoStyleSetsOther +=
mStyleRuleMap->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
}
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mSheets
// - mNonInheritingStyleContexts
//
// The following members are not measured:
// - mPresContext, because it a non-owning pointer
}
bool
ServoStyleSet::GetAuthorStyleDisabled() const
{
return mAuthorStyleDisabled;
}
nsresult
ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
{
if (mAuthorStyleDisabled == aStyleDisabled) {
return NS_OK;
}
mAuthorStyleDisabled = aStyleDisabled;
MarkOriginsDirty(OriginFlags::Author);
return NS_OK;
}
void
ServoStyleSet::BeginUpdate()
{
}
nsresult
ServoStyleSet::EndUpdate()
{
return NS_OK;
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveStyleFor(Element* aElement,
ServoStyleContext* aParentContext,
LazyComputeBehavior aMayCompute)
{
RefPtr<ServoStyleContext> computedValues;
if (aMayCompute == LazyComputeBehavior::Allow) {
PreTraverseSync();
return ResolveStyleLazilyInternal(
aElement, CSSPseudoElementType::NotPseudo, nullptr, aParentContext);
}
return ResolveServoStyle(aElement);
}
/**
* Clears any stale Servo element data that might existing in the specified
* element's document. Upon destruction, asserts that the element and all
* its ancestors still have no element data, if the document has no pres shell.
*/
class MOZ_STACK_CLASS AutoClearStaleData
{
public:
explicit AutoClearStaleData(Element* aElement)
#ifdef DEBUG
: mElement(aElement)
#endif
{
aElement->OwnerDoc()->ClearStaleServoDataFromDocument();
}
~AutoClearStaleData()
{
#ifdef DEBUG
// Assert that the element and its ancestors are all unstyled, if the
// document has no pres shell.
if (mElement->OwnerDoc()->HasShellOrBFCacheEntry()) {
// We must check whether we're in the bfcache because its presence
// means we have a "hidden" pres shell with up-to-date data in the
// tree.
return;
}
for (Element* e = mElement; e; e = e->GetParentElement()) {
MOZ_ASSERT(!e->HasServoData(), "expected element to be unstyled");
}
#endif
}
private:
#ifdef DEBUG
Element* mElement;
#endif
};
const ServoElementSnapshotTable&
ServoStyleSet::Snapshots()
{
return mPresContext->RestyleManager()->AsServo()->Snapshots();
}
void
ServoStyleSet::ResolveMappedAttrDeclarationBlocks()
{
if (nsHTMLStyleSheet* sheet = mPresContext->Document()->GetAttributeStyleSheet()) {
sheet->CalculateMappedServoDeclarations(mPresContext);
}
mPresContext->Document()->ResolveScheduledSVGPresAttrs();
}
void
ServoStyleSet::PreTraverseSync()
{
// Get the Document's root element to ensure that the cache is valid before
// calling into the (potentially-parallel) Servo traversal, where a cache hit
// is necessary to avoid a data race when updating the cache.
mozilla::Unused << mPresContext->Document()->GetRootElement();
ResolveMappedAttrDeclarationBlocks();
nsCSSRuleProcessor::InitSystemMetrics();
LookAndFeel::NativeInit();
// This is lazily computed and pseudo matching needs to access
// it so force computation early.
mPresContext->Document()->GetDocumentState();
if (gfxUserFontSet* userFontSet = mPresContext->Document()->GetUserFontSet()) {
// Ensure that the @font-face data is not stale
uint64_t generation = userFontSet->GetGeneration();
if (generation != mUserFontSetUpdateGeneration) {
mPresContext->DeviceContext()->UpdateFontCacheUserFonts(userFontSet);
mUserFontSetUpdateGeneration = generation;
}
// Ensure that the FontFaceSet's cached document principal is up to date.
FontFaceSet* fontFaceSet =
static_cast<FontFaceSet::UserFontSet*>(userFontSet)->GetFontFaceSet();
fontFaceSet->UpdateStandardFontLoadPrincipal();
bool principalChanged = fontFaceSet->HasStandardFontLoadPrincipalChanged();
// Ensure that the user font cache holds up-to-date data on whether
// our font set is allowed to re-use fonts from the cache.
uint32_t cacheGeneration = gfxUserFontSet::UserFontCache::Generation();
if (principalChanged) {
gfxUserFontSet::UserFontCache::ClearAllowedFontSets(userFontSet);
}
if (cacheGeneration != mUserFontCacheUpdateGeneration || principalChanged) {
gfxUserFontSet::UserFontCache::UpdateAllowedFontSets(userFontSet);
mUserFontCacheUpdateGeneration = cacheGeneration;
}
}
UpdateStylistIfNeeded();
mPresContext->CacheAllLangs();
}
void
ServoStyleSet::PreTraverse(ServoTraversalFlags aFlags, Element* aRoot)
{
PreTraverseSync();
// Process animation stuff that we should avoid doing during the parallel
// traversal.
nsSMILAnimationController* smilController =
mPresContext->Document()->HasAnimationController()
? mPresContext->Document()->GetAnimationController()
: nullptr;
if (aRoot) {
mPresContext->EffectCompositor()
->PreTraverseInSubtree(aFlags, aRoot);
if (smilController) {
smilController->PreTraverseInSubtree(aRoot);
}
} else {
mPresContext->EffectCompositor()->PreTraverse(aFlags);
if (smilController) {
smilController->PreTraverse();
}
}
}
static inline already_AddRefed<ServoStyleContext>
ResolveStyleForTextOrFirstLetterContinuation(
RawServoStyleSetBorrowed aStyleSet,
ServoStyleContext& aParent,
nsIAtom* aAnonBox)
{
MOZ_ASSERT(aAnonBox == nsCSSAnonBoxes::mozText ||
aAnonBox == nsCSSAnonBoxes::firstLetterContinuation);
auto inheritTarget = aAnonBox == nsCSSAnonBoxes::mozText
? InheritTarget::Text
: InheritTarget::FirstLetterContinuation;
RefPtr<ServoStyleContext> style =
aParent.GetCachedInheritingAnonBoxStyle(aAnonBox);
if (!style) {
style = Servo_ComputedValues_Inherit(aStyleSet,
aAnonBox,
&aParent,
inheritTarget).Consume();
MOZ_ASSERT(style);
aParent.SetCachedInheritedAnonBoxStyle(aAnonBox, style);
}
return style.forget();
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveStyleForText(nsIContent* aTextNode,
ServoStyleContext* aParentContext)
{
MOZ_ASSERT(aTextNode && aTextNode->IsNodeOfType(nsINode::eTEXT));
MOZ_ASSERT(aTextNode->GetParent());
MOZ_ASSERT(aParentContext);
return ResolveStyleForTextOrFirstLetterContinuation(
mRawSet.get(), *aParentContext, nsCSSAnonBoxes::mozText);
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveStyleForFirstLetterContinuation(ServoStyleContext* aParentContext)
{
MOZ_ASSERT(aParentContext);
return ResolveStyleForTextOrFirstLetterContinuation(
mRawSet.get(), *aParentContext, nsCSSAnonBoxes::firstLetterContinuation);
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveStyleForPlaceholder()
{
RefPtr<ServoStyleContext>& cache =
mNonInheritingStyleContexts[nsCSSAnonBoxes::NonInheriting::oofPlaceholder];
if (cache) {
RefPtr<ServoStyleContext> retval = cache;
return retval.forget();
}
RefPtr<ServoStyleContext> computedValues =
Servo_ComputedValues_Inherit(mRawSet.get(),
nsCSSAnonBoxes::oofPlaceholder,
nullptr,
InheritTarget::PlaceholderFrame)
.Consume();
MOZ_ASSERT(computedValues);
cache = computedValues;
return computedValues.forget();
}
static inline bool
LazyPseudoIsCacheable(CSSPseudoElementType aType,
Element* aOriginatingElement,
ServoStyleContext* aParentContext)
{
return aParentContext &&
!nsCSSPseudoElements::IsEagerlyCascadedInServo(aType) &&
aOriginatingElement->HasServoData() &&
!Servo_Element_IsPrimaryStyleReusedViaRuleNode(aOriginatingElement);
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolvePseudoElementStyle(Element* aOriginatingElement,
CSSPseudoElementType aType,
ServoStyleContext* aParentContext,
Element* aPseudoElement)
{
UpdateStylistIfNeeded();
MOZ_ASSERT(aType < CSSPseudoElementType::Count);
RefPtr<ServoStyleContext> computedValues;
if (aPseudoElement) {
MOZ_ASSERT(aType == aPseudoElement->GetPseudoElementType());
computedValues =
Servo_ResolveStyle(aPseudoElement, mRawSet.get()).Consume();
} else {
bool cacheable =
LazyPseudoIsCacheable(aType, aOriginatingElement, aParentContext);
computedValues =
cacheable ? aParentContext->GetCachedLazyPseudoStyle(aType) : nullptr;
if (!computedValues) {
computedValues = Servo_ResolvePseudoStyle(aOriginatingElement,
aType,
/* is_probe = */ false,
aParentContext,
mRawSet.get()).Consume();
if (cacheable) {
aParentContext->SetCachedLazyPseudoStyle(computedValues);
}
}
}
MOZ_ASSERT(computedValues);
return computedValues.forget();
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveStyleLazily(Element* aElement,
CSSPseudoElementType aPseudoType,
nsIAtom* aPseudoTag,
StyleRuleInclusion aRuleInclusion)
{
// Lazy style computation avoids storing any new data in the tree.
// If the tree has stale data in it, then the AutoClearStaleData below
// will ensure it's cleared so we don't use it. But if the document is
// in the bfcache, then we will have valid, usable data in the tree,
// but we don't want to use it. Instead we want to pretend as if the
// document has no pres shell and no styles.
//
// If we don't do this, then we can very easily mix styles from different
// style sets in the tree. For example, calling getComputedStyle on an
// element in a display:none iframe (which has no pres shell) will use the
// caller's style set for any styling. If we allowed this to re-use any
// existing styles in the DOM, then we would do selector matching on the
// undisplayed element with the caller's style set's rules, but inherit from
// values that were computed with the style set from the target element's
// hidden-by-the-bfcache-entry pres shell.
bool ignoreExistingStyles = aElement->OwnerDoc()->GetBFCacheEntry();
AutoClearStaleData guard(aElement);
PreTraverseSync();
return ResolveStyleLazilyInternal(aElement, aPseudoType, aPseudoTag,
nullptr, aRuleInclusion,
ignoreExistingStyles);
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag,
ServoStyleContext* aParentContext)
{
MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudoTag) &&
!nsCSSAnonBoxes::IsNonInheritingAnonBox(aPseudoTag));
MOZ_ASSERT_IF(aParentContext, !StylistNeedsUpdate());
UpdateStylistIfNeeded();
RefPtr<ServoStyleContext> style = nullptr;
if (aParentContext) {
style = aParentContext->GetCachedInheritingAnonBoxStyle(aPseudoTag);
}
if (!style) {
style =
Servo_ComputedValues_GetForAnonymousBox(aParentContext,
aPseudoTag,
mRawSet.get()).Consume();
MOZ_ASSERT(style);
if (aParentContext) {
aParentContext->SetCachedInheritedAnonBoxStyle(aPseudoTag, style);
}
}
return style.forget();
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveNonInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag)
{
MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudoTag) &&
nsCSSAnonBoxes::IsNonInheritingAnonBox(aPseudoTag));
MOZ_ASSERT(aPseudoTag != nsCSSAnonBoxes::pageContent,
"If nsCSSAnonBoxes::pageContent ends up non-inheriting, check "
"whether we need to do anything to move the "
"@page handling from ResolveInheritingAnonymousBoxStyle to "
"ResolveNonInheritingAnonymousBoxStyle");
nsCSSAnonBoxes::NonInheriting type =
nsCSSAnonBoxes::NonInheritingTypeForPseudoTag(aPseudoTag);
RefPtr<ServoStyleContext>& cache = mNonInheritingStyleContexts[type];
if (cache) {
RefPtr<ServoStyleContext> 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(!nsCSSAnonBoxes::IsNonInheritingAnonBox(nsCSSAnonBoxes::viewport),
"viewport needs fixup to handle blockifying it");
RefPtr<ServoStyleContext> computedValues =
Servo_ComputedValues_GetForAnonymousBox(nullptr,
aPseudoTag,
mRawSet.get()).Consume();
#ifdef DEBUG
if (!computedValues) {
nsString pseudo;
aPseudoTag->ToString(pseudo);
NS_ERROR(nsPrintfCString("stylo: could not get anon-box: %s",
NS_ConvertUTF16toUTF8(pseudo).get()).get());
MOZ_CRASH();
}
#endif
cache = computedValues;
return computedValues.forget();
}
// manage the set of style sheets in the style set
nsresult
ServoStyleSet::AppendStyleSheet(SheetType aType,
ServoStyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
MOZ_ASSERT(aSheet->IsApplicable());
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
MOZ_ASSERT(aSheet->RawContents(), "Raw sheet should be in place before insertion.");
RemoveSheetOfType(aType, aSheet);
AppendSheetOfType(aType, aSheet);
if (mRawSet) {
// 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(mRawSet.get(), aSheet);
SetStylistStyleSheetsDirty();
}
return NS_OK;
}
nsresult
ServoStyleSet::PrependStyleSheet(SheetType aType,
ServoStyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
MOZ_ASSERT(aSheet->IsApplicable());
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
MOZ_ASSERT(aSheet->RawContents(),
"Raw sheet should be in place before insertion.");
RemoveSheetOfType(aType, aSheet);
PrependSheetOfType(aType, aSheet);
if (mRawSet) {
// 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_PrependStyleSheet.
Servo_StyleSet_PrependStyleSheet(mRawSet.get(), aSheet);
SetStylistStyleSheetsDirty();
}
return NS_OK;
}
nsresult
ServoStyleSet::RemoveStyleSheet(SheetType aType,
ServoStyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
RemoveSheetOfType(aType, aSheet);
if (mRawSet) {
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), aSheet);
SetStylistStyleSheetsDirty();
}
return NS_OK;
}
nsresult
ServoStyleSet::ReplaceSheets(SheetType aType,
const nsTArray<RefPtr<ServoStyleSheet>>& aNewSheets)
{
// Gecko uses a two-dimensional array keyed by sheet type, whereas Servo
// stores a flattened list. This makes ReplaceSheets a pretty clunky thing
// to express. If the need ever arises, we can easily make this more efficent,
// probably by aligning the representations better between engines.
SetStylistStyleSheetsDirty();
// Remove all the existing sheets first.
for (const auto& sheet : mSheets[aType]) {
sheet->DropStyleSet(this);
if (mRawSet) {
Servo_StyleSet_RemoveStyleSheet(mRawSet.get(), sheet);
}
}
mSheets[aType].Clear();
// Add in all the new sheets.
for (auto& sheet : aNewSheets) {
AppendSheetOfType(aType, sheet);
if (mRawSet) {
MOZ_ASSERT(sheet->RawContents(), "Raw sheet should be in place before replacement.");
Servo_StyleSet_AppendStyleSheet(mRawSet.get(), sheet);
}
}
return NS_OK;
}
nsresult
ServoStyleSet::InsertStyleSheetBefore(SheetType aType,
ServoStyleSheet* aNewSheet,
ServoStyleSheet* aReferenceSheet)
{
MOZ_ASSERT(aNewSheet);
MOZ_ASSERT(aReferenceSheet);
MOZ_ASSERT(aNewSheet->IsApplicable());
MOZ_ASSERT(aNewSheet != aReferenceSheet, "Can't place sheet before itself.");
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.
RemoveSheetOfType(aType, aNewSheet);
InsertSheetOfType(aType, aNewSheet, aReferenceSheet);
if (mRawSet) {
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_InsertStyleSheetBefore(
mRawSet.get(), aNewSheet, aReferenceSheet);
SetStylistStyleSheetsDirty();
}
return NS_OK;
}
int32_t
ServoStyleSet::SheetCount(SheetType aType) const
{
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
return mSheets[aType].Length();
}
ServoStyleSheet*
ServoStyleSet::StyleSheetAt(SheetType aType, int32_t aIndex) const
{
MOZ_ASSERT(nsStyleSet::IsCSSSheetType(aType));
return mSheets[aType][aIndex];
}
void
ServoStyleSet::AppendAllXBLStyleSheets(nsTArray<StyleSheet*>& aArray) const
{
if (mBindingManager) {
mBindingManager->AppendAllSheets(aArray);
}
}
nsresult
ServoStyleSet::RemoveDocStyleSheet(ServoStyleSheet* aSheet)
{
return RemoveStyleSheet(SheetType::Doc, aSheet);
}
nsresult
ServoStyleSet::AddDocStyleSheet(ServoStyleSheet* aSheet,
nsIDocument* aDocument)
{
MOZ_ASSERT(aSheet->IsApplicable());
MOZ_ASSERT(aSheet->RawContents(), "Raw sheet should be in place by this point.");
RefPtr<StyleSheet> strong(aSheet);
RemoveSheetOfType(SheetType::Doc, aSheet);
size_t index =
aDocument->FindDocStyleSheetInsertionPoint(mSheets[SheetType::Doc], aSheet);
if (index < mSheets[SheetType::Doc].Length()) {
// This case is insert before.
ServoStyleSheet *beforeSheet = mSheets[SheetType::Doc][index];
InsertSheetOfType(SheetType::Doc, aSheet, beforeSheet);
if (mRawSet) {
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_InsertStyleSheetBefore(mRawSet.get(), aSheet, beforeSheet);
SetStylistStyleSheetsDirty();
}
} else {
// This case is append.
AppendSheetOfType(SheetType::Doc, aSheet);
if (mRawSet) {
// Maintain a mirrored list of sheets on the servo side.
Servo_StyleSet_AppendStyleSheet(mRawSet.get(), aSheet);
SetStylistStyleSheetsDirty();
}
}
return NS_OK;
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ProbePseudoElementStyle(Element* aOriginatingElement,
CSSPseudoElementType aType,
ServoStyleContext* aParentContext)
{
UpdateStylistIfNeeded();
// NB: We ignore aParentContext, because in some cases
// (first-line/first-letter on anonymous box blocks) Gecko passes something
// nonsensical there. In all other cases we want to inherit directly from
// aOriginatingElement's styles anyway.
MOZ_ASSERT(aType < CSSPseudoElementType::Count);
bool cacheable =
LazyPseudoIsCacheable(aType, aOriginatingElement, aParentContext);
RefPtr<ServoStyleContext> computedValues =
cacheable ? aParentContext->GetCachedLazyPseudoStyle(aType) : nullptr;
if (!computedValues) {
computedValues = Servo_ResolvePseudoStyle(aOriginatingElement, aType,
/* is_probe = */ true,
nullptr,
mRawSet.get()).Consume();
if (!computedValues) {
return nullptr;
}
if (cacheable) {
// NB: We don't need to worry about the before/after handling below
// because those are eager and thus not |cacheable| anyway.
aParentContext->SetCachedLazyPseudoStyle(computedValues);
}
}
// For :before and :after pseudo-elements, having display: none or no
// 'content' property is equivalent to not having the pseudo-element
// at all.
bool isBeforeOrAfter = aType == CSSPseudoElementType::before ||
aType == CSSPseudoElementType::after;
if (isBeforeOrAfter) {
const nsStyleDisplay* display = computedValues->ComputedData()->GetStyleDisplay();
const nsStyleContent* content = computedValues->ComputedData()->GetStyleContent();
if (display->mDisplay == StyleDisplay::None ||
content->ContentCount() == 0) {
return nullptr;
}
}
return computedValues.forget();
}
bool
ServoStyleSet::StyleDocument(ServoTraversalFlags aFlags)
{
nsIDocument* doc = mPresContext->Document();
if (!doc->GetServoRestyleRoot()) {
return false;
}
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;
Element* rootElement = doc->GetRootElement();
MOZ_ASSERT_IF(rootElement, rootElement->HasServoData());
if (ShouldTraverseInParallel()) {
aFlags |= ServoTraversalFlags::ParallelTraversal;
}
// Do the first traversal.
DocumentStyleRootIterator iter(doc->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, mRawSet.get(), &snapshots, aFlags);
postTraversalRequired |=
root->HasAnyOfFlags(Element::kAllServoDescendantBits | NODE_NEEDS_FRAME);
if (parent) {
MOZ_ASSERT(root == doc->GetServoRestyleRoot());
if (parent->HasDirtyDescendantsForServo()) {
// 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.
doc->SetServoRestyleRoot(
parent,
doc->GetServoRestyleRootDirtyBits() |
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 (mPresContext->EffectCompositor()->PreTraverse(aFlags)) {
nsINode* styleRoot = doc->GetServoRestyleRoot();
Element* root = styleRoot->IsElement() ? styleRoot->AsElement() : rootElement;
postTraversalRequired |=
Servo_TraverseSubtree(root, mRawSet.get(), &snapshots, aFlags);
postTraversalRequired |=
root->HasAnyOfFlags(Element::kAllServoDescendantBits | NODE_NEEDS_FRAME);
}
return postTraversalRequired;
}
void
ServoStyleSet::StyleNewSubtree(Element* aRoot)
{
MOZ_ASSERT(!aRoot->HasServoData(), "Should have called StyleNewChildren");
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, mRawSet.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 (mPresContext->EffectCompositor()->PreTraverseInSubtree(flags, aRoot)) {
postTraversalRequired =
Servo_TraverseSubtree(aRoot, mRawSet.get(), &snapshots,
ServoTraversalFlags::AnimationOnly |
ServoTraversalFlags::Forgetful |
ServoTraversalFlags::ClearAnimationOnlyDirtyDescendants);
MOZ_ASSERT(!postTraversalRequired);
}
}
void
ServoStyleSet::StyleNewChildren(Element* aParent)
{
MOZ_ASSERT(aParent->HasServoData(), "Should have called StyleNewSubtree");
if (Servo_Element_IsDisplayNone(aParent)) {
return;
}
PreTraverseSync();
AutoPrepareTraversal guard(this);
// Implementing StyleNewChildren correctly is very annoying, for two reasons:
// (1) We have to tiptoe around existing invalidations in the tree. In rare
// cases Gecko calls into the frame constructor with pending invalidations,
// and in other rare cases the frame constructor needs to perform
// synchronous styling rather than using the normal lazy frame
// construction mechanism. If both of these cases happen together, then we
// get an |aParent| with dirty style and/or dirty descendants, which we
// can't process right now because we're not set up to update the frames
// and process the change hints. We handle this case by passing the
// UnstyledOnly flag to servo.
// (2) We don't have a good way to handle animations. When styling unstyled
// content, a followup animation traversal may be required (for example
// to change the transition style from the after-change style we used in
// the animation cascade to the timeline-correct style). But once we do
// the initial styling, we don't have a good way to distinguish the new
// content and scope our animation processing to that. We should handle
// this somehow, but for now we just don't do the followup animation
// traversal, which is buggy.
// Set ourselves up to find the children by marking the parent as having
// dirty descendants.
bool hadDirtyDescendants = aParent->HasDirtyDescendantsForServo();
aParent->SetHasDirtyDescendantsForServo();
auto flags = ServoTraversalFlags::UnstyledOnly;
if (ShouldTraverseInParallel()) {
flags |= ServoTraversalFlags::ParallelTraversal;
}
// Do the traversal. The snapshots will be ignored.
const SnapshotTable& snapshots = Snapshots();
Servo_TraverseSubtree(aParent, mRawSet.get(), &snapshots, flags);
// Restore the old state of the dirty descendants bit.
if (!hadDirtyDescendants) {
aParent->UnsetHasDirtyDescendantsForServo();
}
}
void
ServoStyleSet::StyleNewlyBoundElement(Element* aElement)
{
// In general the element is always styled by the time we're applying XBL
// bindings, because we need to style the element to know what the binding
// URI is. However, programmatic consumers of the XBL service (like the
// XML pretty printer) _can_ apply bindings without having styled the bound
// element. We could assert against this and require the callers manually
// resolve the style first, but it's easy enough to just handle here.
if (MOZ_LIKELY(aElement->HasServoData())) {
StyleNewChildren(aElement);
} else {
StyleNewSubtree(aElement);
}
}
void
ServoStyleSet::MarkOriginsDirty(OriginFlags aChangedOrigins)
{
SetStylistStyleSheetsDirty();
Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get(),
mAuthorStyleDisabled,
aChangedOrigins);
}
void
ServoStyleSet::RecordStyleSheetChange(
ServoStyleSheet* aSheet,
StyleSheet::ChangeType aChangeType)
{
switch (aChangeType) {
case StyleSheet::ChangeType::RuleAdded:
case StyleSheet::ChangeType::RuleRemoved:
case StyleSheet::ChangeType::RuleChanged:
// FIXME(emilio): We can presumably do better in a bunch of these.
return MarkOriginsDirty(aSheet->GetOrigin());
case StyleSheet::ChangeType::ApplicableStateChanged:
case StyleSheet::ChangeType::Added:
case StyleSheet::ChangeType::Removed:
// Do nothing, we've already recorded the change in the
// Append/Remove/Replace methods, etc, and will act consequently.
return;
}
}
#ifdef DEBUG
void
ServoStyleSet::AssertTreeIsClean()
{
DocumentStyleRootIterator iter(mPresContext->Document());
while (Element* root = iter.GetNextStyleRoot()) {
Servo_AssertTreeIsClean(root);
}
}
#endif
bool
ServoStyleSet::GetKeyframesForName(const nsString& aName,
const nsTimingFunction& aTimingFunction,
nsTArray<Keyframe>& aKeyframes)
{
UpdateStylistIfNeeded();
NS_ConvertUTF16toUTF8 name(aName);
return Servo_StyleSet_GetKeyframesForName(mRawSet.get(),
&name,
&aTimingFunction,
&aKeyframes);
}
nsTArray<ComputedKeyframeValues>
ServoStyleSet::GetComputedKeyframeValuesFor(
const nsTArray<Keyframe>& aKeyframes,
Element* aElement,
const ServoStyleContext* aContext)
{
// Servo_GetComputedKeyframeValues below won't handle ignoring existing
// element data for bfcached documents. (See comment in ResolveStyleLazily
// about these bfcache issues.)
MOZ_RELEASE_ASSERT(!aElement->OwnerDoc()->GetBFCacheEntry());
AutoClearStaleData guard(aElement);
nsTArray<ComputedKeyframeValues> result(aKeyframes.Length());
// Construct each nsTArray<PropertyStyleAnimationValuePair> here.
result.AppendElements(aKeyframes.Length());
Servo_GetComputedKeyframeValues(&aKeyframes,
aElement,
aContext,
mRawSet.get(),
&result);
return result;
}
void
ServoStyleSet::GetAnimationValues(
RawServoDeclarationBlock* aDeclarations,
Element* aElement,
const ServoStyleContext* aStyleContext,
nsTArray<RefPtr<RawServoAnimationValue>>& aAnimationValues)
{
// Servo_GetAnimationValues below won't handle ignoring existing element
// data for bfcached documents. (See comment in ResolveStyleLazily
// about these bfcache issues.)
MOZ_RELEASE_ASSERT(!aElement->OwnerDoc()->GetBFCacheEntry());
AutoClearStaleData guard(aElement);
Servo_GetAnimationValues(aDeclarations,
aElement,
aStyleContext,
mRawSet.get(),
&aAnimationValues);
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::GetBaseContextForElement(
Element* aElement,
ServoStyleContext* aParentContext,
nsPresContext* aPresContext,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType,
const ServoStyleContext* aStyle)
{
// Servo_StyleSet_GetBaseComputedValuesForElement below won't handle ignoring
// existing element data for bfcached documents. (See comment in
// ResolveStyleLazily about these bfcache issues.)
MOZ_RELEASE_ASSERT(!aElement->OwnerDoc()->GetBFCacheEntry(),
"GetBaseContextForElement does not support documents in the "
"bfcache");
AutoClearStaleData guard(aElement);
return Servo_StyleSet_GetBaseComputedValuesForElement(mRawSet.get(),
aElement,
aStyle,
&Snapshots(),
aPseudoType).Consume();
}
already_AddRefed<RawServoAnimationValue>
ServoStyleSet::ComputeAnimationValue(
Element* aElement,
RawServoDeclarationBlock* aDeclarations,
const ServoStyleContext* aContext)
{
// Servo_AnimationValue_Compute below won't handle ignoring existing element
// data for bfcached documents. (See comment in ResolveStyleLazily about
// these bfcache issues.)
MOZ_RELEASE_ASSERT(!aElement->OwnerDoc()->GetBFCacheEntry());
AutoClearStaleData guard(aElement);
return Servo_AnimationValue_Compute(aElement,
aDeclarations,
aContext,
mRawSet.get()).Consume();
}
bool
ServoStyleSet::EnsureUniqueInnerOnCSSSheets()
{
AutoTArray<StyleSheet*, 32> queue;
for (auto& entryArray : mSheets) {
for (auto& sheet : entryArray) {
queue.AppendElement(sheet);
}
}
// This is a stub until more of the functionality of nsStyleSet is
// replicated for Servo here.
// Bug 1290276 will replicate the nsStyleSet work of checking
// a nsBindingManager
while (!queue.IsEmpty()) {
uint32_t idx = queue.Length() - 1;
StyleSheet* sheet = queue[idx];
queue.RemoveElementAt(idx);
sheet->EnsureUniqueInner();
// Enqueue all the sheet's children.
sheet->AppendAllChildSheets(queue);
}
if (mNeedsRestyleAfterEnsureUniqueInner) {
// TODO(emilio): We could make this faster if needed tracking the specific
// origins and all that, but the only caller of this doesn't seem to really
// care about perf.
MarkOriginsDirty(OriginFlags::All);
}
bool res = mNeedsRestyleAfterEnsureUniqueInner;
mNeedsRestyleAfterEnsureUniqueInner = false;
return res;
}
void
ServoStyleSet::ClearCachedStyleData()
{
ClearNonInheritingStyleContexts();
Servo_StyleSet_RebuildCachedData(mRawSet.get());
}
void
ServoStyleSet::CompatibilityModeChanged()
{
Servo_StyleSet_CompatModeChanged(mRawSet.get());
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveServoStyle(Element* aElement)
{
RefPtr<ServoStyleContext> result =
Servo_ResolveStyle(aElement, mRawSet.get()).Consume();
return result.forget();
}
void
ServoStyleSet::ClearNonInheritingStyleContexts()
{
for (RefPtr<ServoStyleContext>& ptr : mNonInheritingStyleContexts) {
ptr = nullptr;
}
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveStyleLazilyInternal(Element* aElement,
CSSPseudoElementType aPseudoType,
nsIAtom* aPseudoTag,
const ServoStyleContext* aParentContext,
StyleRuleInclusion aRuleInclusion,
bool aIgnoreExistingStyles)
{
mPresContext->EffectCompositor()->PreTraverse(aElement, aPseudoType);
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.
*/
Element* elementForStyleResolution = aElement;
CSSPseudoElementType pseudoTypeForStyleResolution = aPseudoType;
if (aPseudoType == CSSPseudoElementType::before) {
if (Element* pseudo = nsLayoutUtils::GetBeforePseudo(aElement)) {
elementForStyleResolution = pseudo;
pseudoTypeForStyleResolution = CSSPseudoElementType::NotPseudo;
}
} else if (aPseudoType == CSSPseudoElementType::after) {
if (Element* pseudo = nsLayoutUtils::GetAfterPseudo(aElement)) {
elementForStyleResolution = pseudo;
pseudoTypeForStyleResolution = CSSPseudoElementType::NotPseudo;
}
}
RefPtr<ServoStyleContext> computedValues =
Servo_ResolveStyleLazily(elementForStyleResolution,
pseudoTypeForStyleResolution,
aRuleInclusion,
&Snapshots(),
mRawSet.get(),
aIgnoreExistingStyles).Consume();
if (mPresContext->EffectCompositor()->PreTraverse(aElement, aPseudoType)) {
computedValues =
Servo_ResolveStyleLazily(elementForStyleResolution,
pseudoTypeForStyleResolution,
aRuleInclusion,
&Snapshots(),
mRawSet.get(),
aIgnoreExistingStyles).Consume();
}
return computedValues.forget();
}
bool
ServoStyleSet::AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray)
{
UpdateStylistIfNeeded();
Servo_StyleSet_GetFontFaceRules(mRawSet.get(), &aArray);
return true;
}
nsCSSCounterStyleRule*
ServoStyleSet::CounterStyleRuleForName(nsIAtom* aName)
{
return Servo_StyleSet_GetCounterStyleRule(mRawSet.get(), aName);
}
already_AddRefed<gfxFontFeatureValueSet>
ServoStyleSet::BuildFontFeatureValueSet()
{
UpdateStylistIfNeeded();
RefPtr<gfxFontFeatureValueSet> set =
Servo_StyleSet_BuildFontFeatureValueSet(mRawSet.get());
return set.forget();
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ResolveForDeclarations(
const ServoStyleContext* aParentOrNull,
RawServoDeclarationBlockBorrowed aDeclarations)
{
UpdateStylistIfNeeded();
return Servo_StyleSet_ResolveForDeclarations(mRawSet.get(),
aParentOrNull,
aDeclarations).Consume();
}
void
ServoStyleSet::UpdateStylist()
{
MOZ_ASSERT(StylistNeedsUpdate());
if (mStylistState & StylistState::StyleSheetsDirty) {
// There's no need to compute invalidations and such for an XBL styleset,
// since they are loaded and unloaded synchronously, and they don't have to
// deal with dynamic content changes.
Element* root =
IsMaster() ? mPresContext->Document()->GetDocumentElement() : nullptr;
Servo_StyleSet_FlushStyleSheets(mRawSet.get(), root);
}
if (MOZ_UNLIKELY(mStylistState & StylistState::XBLStyleSheetsDirty)) {
MOZ_ASSERT(IsMaster(), "Only master styleset can mark XBL stylesets dirty!");
mBindingManager->UpdateBoundContentBindingsForServo(mPresContext);
}
mStylistState = StylistState::NotDirty;
}
void
ServoStyleSet::MaybeGCRuleTree()
{
MOZ_ASSERT(NS_IsMainThread());
Servo_MaybeGCRuleTree(mRawSet.get());
}
/* static */ bool
ServoStyleSet::MayTraverseFrom(const Element* aElement)
{
MOZ_ASSERT(aElement->IsInComposedDoc());
Element* parent = aElement->GetFlattenedTreeParentElementForStyle();
if (!parent) {
return true;
}
if (!parent->HasServoData()) {
return false;
}
return !Servo_Element_IsDisplayNone(parent);
}
bool
ServoStyleSet::ShouldTraverseInParallel() const
{
return mPresContext->PresShell()->IsActive();
}
void
ServoStyleSet::PrependSheetOfType(SheetType aType,
ServoStyleSheet* aSheet)
{
aSheet->AddStyleSet(this);
mSheets[aType].InsertElementAt(0, aSheet);
}
void
ServoStyleSet::AppendSheetOfType(SheetType aType,
ServoStyleSheet* aSheet)
{
aSheet->AddStyleSet(this);
mSheets[aType].AppendElement(aSheet);
}
void
ServoStyleSet::InsertSheetOfType(SheetType aType,
ServoStyleSheet* aSheet,
ServoStyleSheet* aBeforeSheet)
{
for (uint32_t i = 0; i < mSheets[aType].Length(); ++i) {
if (mSheets[aType][i] == aBeforeSheet) {
aSheet->AddStyleSet(this);
mSheets[aType].InsertElementAt(i, aSheet);
return;
}
}
}
void
ServoStyleSet::RemoveSheetOfType(SheetType aType,
ServoStyleSheet* aSheet)
{
for (uint32_t i = 0; i < mSheets[aType].Length(); ++i) {
if (mSheets[aType][i] == aSheet) {
aSheet->DropStyleSet(this);
mSheets[aType].RemoveElementAt(i);
}
}
}
void
ServoStyleSet::RunPostTraversalTasks()
{
MOZ_ASSERT(!IsInServoTraversal());
if (mPostTraversalTasks.IsEmpty()) {
return;
}
nsTArray<PostTraversalTask> tasks;
tasks.SwapElements(mPostTraversalTasks);
for (auto& task : tasks) {
task.Run();
}
}
ServoStyleRuleMap*
ServoStyleSet::StyleRuleMap()
{
if (!mStyleRuleMap) {
mStyleRuleMap = new ServoStyleRuleMap(this);
if (mPresContext) {
nsIDocument* doc = mPresContext->Document();
doc->AddObserver(mStyleRuleMap);
doc->CSSLoader()->AddObserver(mStyleRuleMap);
}
}
return mStyleRuleMap;
}
bool
ServoStyleSet::MightHaveAttributeDependency(const Element& aElement,
nsIAtom* aAttribute) const
{
return Servo_StyleSet_MightHaveAttributeDependency(
mRawSet.get(), &aElement, aAttribute);
}
bool
ServoStyleSet::HasStateDependency(const Element& aElement,
EventStates aState) const
{
return Servo_StyleSet_HasStateDependency(
mRawSet.get(), &aElement, aState.ServoValue());
}
already_AddRefed<ServoStyleContext>
ServoStyleSet::ReparentStyleContext(ServoStyleContext* aStyleContext,
ServoStyleContext* aNewParent,
ServoStyleContext* aNewParentIgnoringFirstLine,
ServoStyleContext* aNewLayoutParent,
Element* aElement)
{
return Servo_ReparentStyle(aStyleContext, aNewParent,
aNewParentIgnoringFirstLine, aNewLayoutParent,
aElement, mRawSet.get()).Consume();
}
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;
}