gecko-dev/layout/style/nsStyleSet.cpp
Mantaroh Yoshinaga 90f3d3d4d0 Bug 1062106 part 2 - Remove unused SVG Attribute Animations cascade level. r=birtles,dbaron,xidorn
The previous patch in this series converted all uses of mapped attributes
for animation to be animated as CSS properties (that is, to be treated
as presentation hints in the cascade).

As result, we no longer need the SVG Animation presentation hints level
of the cascade, the corresponding rule processor(SVGAttrAnimationRuleProcessor),
or the corresponding eRestyle_SVGAttrAnimations restyle hint. So this patch
removes these unused rule processor and restyle hint.

MozReview-Commit-ID: Hm8IDaqc3ym

--HG--
extra : rebase_source : 339ad209f37ea84857577001c7385323f2187d46
2017-03-21 15:43:26 +09:00

2711 lines
93 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/*
* the container for the style sheets that apply to a presentation, and
* the internal API that the style system exposes for creating (and
* potentially re-creating) style contexts
*/
#include "nsStyleSet.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EnumeratedRange.h"
#include "mozilla/EventStates.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RuleProcessorCache.h"
#include "mozilla/StyleSheetInlines.h"
#include "nsIDocumentInlines.h"
#include "nsRuleWalker.h"
#include "nsStyleContext.h"
#include "mozilla/css/StyleRule.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSRuleProcessor.h"
#include "nsDataHashtable.h"
#include "nsIContent.h"
#include "nsRuleData.h"
#include "nsRuleProcessorData.h"
#include "nsAnimationManager.h"
#include "nsStyleSheetService.h"
#include "mozilla/dom/Element.h"
#include "GeckoProfiler.h"
#include "nsHTMLCSSStyleSheet.h"
#include "nsHTMLStyleSheet.h"
#include "nsCSSRules.h"
#include "nsPrintfCString.h"
#include "nsIFrame.h"
#include "mozilla/RestyleManager.h"
#include "mozilla/RestyleManagerInlines.h"
#include "nsQueryObject.h"
#include <inttypes.h>
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(nsEmptyStyleRule, nsIStyleRule)
/* virtual */ void
nsEmptyStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
{
}
/* virtual */ bool
nsEmptyStyleRule::MightMapInheritedStyleData()
{
return false;
}
/* virtual */ bool
nsEmptyStyleRule::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue)
{
MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
return false;
}
#ifdef DEBUG
/* virtual */ void
nsEmptyStyleRule::List(FILE* out, int32_t aIndent) const
{
nsAutoCString indentStr;
for (int32_t index = aIndent; --index >= 0; ) {
indentStr.AppendLiteral(" ");
}
fprintf_stderr(out, "%s[empty style rule] {}\n", indentStr.get());
}
#endif
NS_IMPL_ISUPPORTS(nsInitialStyleRule, nsIStyleRule)
/* virtual */ void
nsInitialStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
{
// Iterate over the property groups
for (nsStyleStructID sid = nsStyleStructID(0);
sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) {
if (aRuleData->mSIDs & (1 << sid)) {
// Iterate over nsCSSValues within the property group
nsCSSValue * const value_start =
aRuleData->mValueStorage + aRuleData->mValueOffsets[sid];
for (nsCSSValue *value = value_start,
*value_end = value + nsCSSProps::PropertyCountInStruct(sid);
value != value_end; ++value) {
// If MathML is disabled take care not to set MathML properties (or we
// will trigger assertions in nsRuleNode)
if (sid == eStyleStruct_Font &&
!aRuleData->mPresContext->Document()->GetMathMLEnabled()) {
size_t index = value - value_start;
if (index == nsCSSProps::PropertyIndexInStruct(
eCSSProperty__moz_script_level) ||
index == nsCSSProps::PropertyIndexInStruct(
eCSSProperty__moz_script_size_multiplier) ||
index == nsCSSProps::PropertyIndexInStruct(
eCSSProperty__moz_script_min_size) ||
index == nsCSSProps::PropertyIndexInStruct(
eCSSProperty__moz_math_variant) ||
index == nsCSSProps::PropertyIndexInStruct(
eCSSProperty__moz_math_display)) {
continue;
}
}
if (value->GetUnit() == eCSSUnit_Null) {
value->SetInitialValue();
}
}
}
}
}
/* virtual */ bool
nsInitialStyleRule::MightMapInheritedStyleData()
{
return true;
}
/* virtual */ bool
nsInitialStyleRule::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue)
{
MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
return false;
}
#ifdef DEBUG
/* virtual */ void
nsInitialStyleRule::List(FILE* out, int32_t aIndent) const
{
nsAutoCString indentStr;
for (int32_t index = aIndent; --index >= 0; ) {
indentStr.AppendLiteral(" ");
}
fprintf_stderr(out, "%s[initial style rule] {}\n", indentStr.get());
}
#endif
NS_IMPL_ISUPPORTS(nsDisableTextZoomStyleRule, nsIStyleRule)
/* virtual */ void
nsDisableTextZoomStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
{
if (!(aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Font)))
return;
nsCSSValue* value = aRuleData->ValueForTextZoom();
if (value->GetUnit() == eCSSUnit_Null)
value->SetNoneValue();
}
/* virtual */ bool
nsDisableTextZoomStyleRule::MightMapInheritedStyleData()
{
return true;
}
/* virtual */ bool
nsDisableTextZoomStyleRule::
GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, nsCSSValue* aValue)
{
MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
return false;
}
#ifdef DEBUG
/* virtual */ void
nsDisableTextZoomStyleRule::List(FILE* out, int32_t aIndent) const
{
nsAutoCString indentStr;
for (int32_t index = aIndent; --index >= 0; ) {
indentStr.AppendLiteral(" ");
}
fprintf_stderr(out, "%s[disable text zoom style rule] {}\n", indentStr.get());
}
#endif
static const SheetType gCSSSheetTypes[] = {
// From lowest to highest in cascading order.
SheetType::Agent,
SheetType::User,
SheetType::Doc,
SheetType::ScopedDoc,
SheetType::Override
};
/* static */ bool
nsStyleSet::IsCSSSheetType(SheetType aSheetType)
{
for (SheetType type : gCSSSheetTypes) {
if (type == aSheetType) {
return true;
}
}
return false;
}
nsStyleSet::nsStyleSet()
: mRuleTree(nullptr),
mBatching(0),
mInShutdown(false),
mInGC(false),
mAuthorStyleDisabled(false),
mInReconstruct(false),
mInitFontFeatureValuesLookup(true),
mNeedsRestyleAfterEnsureUniqueInner(false),
mDirty(0),
mRootStyleContextCount(0),
#ifdef DEBUG
mOldRootNode(nullptr),
#endif
mUnusedRuleNodeCount(0)
{
}
nsStyleSet::~nsStyleSet()
{
for (SheetType type : gCSSSheetTypes) {
for (CSSStyleSheet* sheet : mSheets[type]) {
sheet->DropStyleSet(this);
}
}
// drop reference to cached rule processors
nsCSSRuleProcessor* rp;
rp = static_cast<nsCSSRuleProcessor*>(mRuleProcessors[SheetType::Agent].get());
if (rp) {
MOZ_ASSERT(rp->IsShared());
rp->ReleaseStyleSetRef();
}
rp = static_cast<nsCSSRuleProcessor*>(mRuleProcessors[SheetType::User].get());
if (rp) {
MOZ_ASSERT(rp->IsShared());
rp->ReleaseStyleSetRef();
}
}
size_t
nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
for (SheetType type : MakeEnumeratedRange(SheetType::Count)) {
if (mRuleProcessors[type]) {
bool shared = false;
if (type == SheetType::Agent || type == SheetType::User) {
// The only two origins we consider caching rule processors for.
nsCSSRuleProcessor* rp =
static_cast<nsCSSRuleProcessor*>(mRuleProcessors[type].get());
shared = rp->IsShared();
}
if (!shared) {
n += mRuleProcessors[type]->SizeOfIncludingThis(aMallocSizeOf);
}
}
// We don't own the sheets (either the nsLayoutStyleSheetCache singleton
// or our document owns them).
n += mSheets[type].ShallowSizeOfExcludingThis(aMallocSizeOf);
}
for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) {
n += mScopedDocSheetRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf);
}
n += mScopedDocSheetRuleProcessors.ShallowSizeOfExcludingThis(aMallocSizeOf);
return n;
}
void
nsStyleSet::Init(nsPresContext *aPresContext)
{
mFirstLineRule = new nsEmptyStyleRule;
mFirstLetterRule = new nsEmptyStyleRule;
mPlaceholderRule = new nsEmptyStyleRule;
mDisableTextZoomStyleRule = new nsDisableTextZoomStyleRule;
mRuleTree = nsRuleNode::CreateRootNode(aPresContext);
// Make an explicit GatherRuleProcessors call for the levels that
// don't have style sheets. The other levels will have their calls
// triggered by DirtyRuleProcessors.
GatherRuleProcessors(SheetType::PresHint);
GatherRuleProcessors(SheetType::StyleAttr);
GatherRuleProcessors(SheetType::Animation);
GatherRuleProcessors(SheetType::Transition);
}
nsresult
nsStyleSet::BeginReconstruct()
{
NS_ASSERTION(!mInReconstruct, "Unmatched begin/end?");
NS_ASSERTION(mRuleTree, "Reconstructing before first construction?");
mInReconstruct = true;
// Clear any ArenaRefPtr-managed style contexts, as we don't want them
// held on to after the rule tree has been reconstructed.
PresContext()->PresShell()->ClearArenaRefPtrs(eArenaObjectID_nsStyleContext);
// Clear our cached style contexts for non-inheriting anonymous boxes.
ClearNonInheritingStyleContexts();
#ifdef DEBUG
MOZ_ASSERT(!mOldRootNode);
mOldRootNode = mRuleTree;
#endif
// Create a new rule tree root, dropping the reference to our old rule tree.
// After reconstruction, we will re-enable GC, and allow everything to be
// collected.
mRuleTree = nsRuleNode::CreateRootNode(mRuleTree->PresContext());
return NS_OK;
}
void
nsStyleSet::EndReconstruct()
{
NS_ASSERTION(mInReconstruct, "Unmatched begin/end?");
mInReconstruct = false;
GCRuleTrees();
}
typedef nsDataHashtable<nsPtrHashKey<nsINode>, uint32_t> ScopeDepthCache;
// Returns the depth of a style scope element, with 1 being the depth of
// a style scope element that has no ancestor style scope elements. The
// depth does not count intervening non-scope elements.
static uint32_t
GetScopeDepth(nsINode* aScopeElement, ScopeDepthCache& aCache)
{
nsINode* parent = aScopeElement->GetParent();
if (!parent || !parent->IsElementInStyleScope()) {
return 1;
}
uint32_t depth = aCache.Get(aScopeElement);
if (!depth) {
for (nsINode* n = parent; n; n = n->GetParent()) {
if (n->IsScopedStyleRoot()) {
depth = GetScopeDepth(n, aCache) + 1;
aCache.Put(aScopeElement, depth);
break;
}
}
}
return depth;
}
struct ScopedSheetOrder
{
CSSStyleSheet* mSheet;
uint32_t mDepth;
uint32_t mOrder;
bool operator==(const ScopedSheetOrder& aRHS) const
{
return mDepth == aRHS.mDepth &&
mOrder == aRHS.mOrder;
}
bool operator<(const ScopedSheetOrder& aRHS) const
{
if (mDepth != aRHS.mDepth) {
return mDepth < aRHS.mDepth;
}
return mOrder < aRHS.mOrder;
}
};
// Sorts aSheets such that style sheets for ancestor scopes come
// before those for descendant scopes, and with sheets for a single
// scope in document order.
static void
SortStyleSheetsByScope(nsTArray<CSSStyleSheet*>& aSheets)
{
uint32_t n = aSheets.Length();
if (n == 1) {
return;
}
ScopeDepthCache cache;
nsTArray<ScopedSheetOrder> sheets;
sheets.SetLength(n);
// For each sheet, record the depth of its scope element and its original
// document order.
for (uint32_t i = 0; i < n; i++) {
sheets[i].mSheet = aSheets[i];
sheets[i].mDepth = GetScopeDepth(aSheets[i]->GetScopeElement(), cache);
sheets[i].mOrder = i;
}
// Sort by depth first, then document order.
sheets.Sort();
for (uint32_t i = 0; i < n; i++) {
aSheets[i] = sheets[i].mSheet;
}
}
nsresult
nsStyleSet::GatherRuleProcessors(SheetType aType)
{
NS_ENSURE_FALSE(mInShutdown, NS_ERROR_FAILURE);
// We might be in GatherRuleProcessors because we are dropping a sheet,
// resulting in an nsCSSSelector being destroyed. Tell the
// RestyleManager for each document we're used in so that they can
// drop any nsCSSSelector pointers (used for eRestyle_SomeDescendants)
// in their mPendingRestyles.
if (IsCSSSheetType(aType)) {
ClearSelectors();
}
nsCOMPtr<nsIStyleRuleProcessor> oldRuleProcessor(mRuleProcessors[aType]);
nsTArray<nsCOMPtr<nsIStyleRuleProcessor>> oldScopedDocRuleProcessors;
if (aType == SheetType::Agent || aType == SheetType::User) {
// drop reference to cached rule processor
nsCSSRuleProcessor* rp =
static_cast<nsCSSRuleProcessor*>(mRuleProcessors[aType].get());
if (rp) {
MOZ_ASSERT(rp->IsShared());
rp->ReleaseStyleSetRef();
}
}
mRuleProcessors[aType] = nullptr;
if (aType == SheetType::ScopedDoc) {
for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) {
nsIStyleRuleProcessor* processor = mScopedDocSheetRuleProcessors[i].get();
Element* scope =
static_cast<nsCSSRuleProcessor*>(processor)->GetScopeElement();
scope->ClearIsScopedStyleRoot();
}
// Clear mScopedDocSheetRuleProcessors, but save it.
oldScopedDocRuleProcessors.SwapElements(mScopedDocSheetRuleProcessors);
}
if (mAuthorStyleDisabled && (aType == SheetType::Doc ||
aType == SheetType::ScopedDoc ||
aType == SheetType::StyleAttr)) {
// Don't regather if this level is disabled. Note that we gather
// preshint sheets no matter what, but then skip them for some
// elements later if mAuthorStyleDisabled.
return NS_OK;
}
switch (aType) {
// levels that do not contain CSS style sheets
case SheetType::Animation:
MOZ_ASSERT(mSheets[aType].IsEmpty());
mRuleProcessors[aType] = PresContext()->EffectCompositor()->
RuleProcessor(EffectCompositor::CascadeLevel::Animations);
return NS_OK;
case SheetType::Transition:
MOZ_ASSERT(mSheets[aType].IsEmpty());
mRuleProcessors[aType] = PresContext()->EffectCompositor()->
RuleProcessor(EffectCompositor::CascadeLevel::Transitions);
return NS_OK;
case SheetType::StyleAttr:
MOZ_ASSERT(mSheets[aType].IsEmpty());
mRuleProcessors[aType] = PresContext()->Document()->GetInlineStyleSheet();
return NS_OK;
case SheetType::PresHint:
MOZ_ASSERT(mSheets[aType].IsEmpty());
mRuleProcessors[aType] =
PresContext()->Document()->GetAttributeStyleSheet();
return NS_OK;
default:
// keep going
break;
}
MOZ_ASSERT(IsCSSSheetType(aType));
if (aType == SheetType::ScopedDoc) {
// Create a rule processor for each scope.
uint32_t count = mSheets[SheetType::ScopedDoc].Length();
if (count) {
// Gather the scoped style sheets into an array as
// CSSStyleSheets, and mark all of their scope elements
// as scoped style roots.
nsTArray<CSSStyleSheet*> sheets(count);
for (CSSStyleSheet* sheet : mSheets[SheetType::ScopedDoc]) {
sheets.AppendElement(sheet);
Element* scope = sheet->GetScopeElement();
scope->SetIsScopedStyleRoot();
}
// Sort the scoped style sheets so that those for the same scope are
// adjacent and that ancestor scopes come before descendent scopes.
SortStyleSheetsByScope(sheets);
// Put the old scoped rule processors in a hashtable so that we
// can retrieve them efficiently, even in edge cases like the
// simultaneous removal and addition of a large number of elements
// with scoped sheets.
nsDataHashtable<nsPtrHashKey<Element>,
nsCSSRuleProcessor*> oldScopedRuleProcessorHash;
for (size_t i = oldScopedDocRuleProcessors.Length(); i-- != 0; ) {
nsCSSRuleProcessor* oldRP =
static_cast<nsCSSRuleProcessor*>(oldScopedDocRuleProcessors[i].get());
Element* scope = oldRP->GetScopeElement();
MOZ_ASSERT(!oldScopedRuleProcessorHash.Get(scope),
"duplicate rule processors for same scope element?");
oldScopedRuleProcessorHash.Put(scope, oldRP);
}
uint32_t start = 0, end;
do {
// Find the range of style sheets with the same scope.
Element* scope = sheets[start]->GetScopeElement();
end = start + 1;
while (end < count && sheets[end]->GetScopeElement() == scope) {
end++;
}
scope->SetIsScopedStyleRoot();
// Create a rule processor for the scope.
nsTArray<RefPtr<CSSStyleSheet>> sheetsForScope;
sheetsForScope.AppendElements(sheets.Elements() + start, end - start);
nsCSSRuleProcessor* oldRP = oldScopedRuleProcessorHash.Get(scope);
mScopedDocSheetRuleProcessors.AppendElement
(new nsCSSRuleProcessor(Move(sheetsForScope), aType, scope, oldRP));
start = end;
} while (start < count);
}
return NS_OK;
}
if (!mSheets[aType].IsEmpty()) {
switch (aType) {
case SheetType::Agent:
case SheetType::User: {
// levels containing non-scoped CSS style sheets whose rule processors
// we want to re-use
nsTArray<CSSStyleSheet*> sheets(mSheets[aType].Length());
for (CSSStyleSheet* sheet : mSheets[aType]) {
sheets.AppendElement(sheet);
}
nsCSSRuleProcessor* rp =
RuleProcessorCache::GetRuleProcessor(sheets, PresContext());
if (!rp) {
rp = new nsCSSRuleProcessor(mSheets[aType], aType, nullptr,
static_cast<nsCSSRuleProcessor*>(
oldRuleProcessor.get()),
true /* aIsShared */);
nsTArray<css::DocumentRule*> documentRules;
nsDocumentRuleResultCacheKey cacheKey;
rp->TakeDocumentRulesAndCacheKey(PresContext(),
documentRules, cacheKey);
RuleProcessorCache::PutRuleProcessor(sheets,
Move(documentRules),
cacheKey, rp);
}
mRuleProcessors[aType] = rp;
rp->AddStyleSetRef();
break;
}
case SheetType::Doc:
case SheetType::Override: {
// levels containing non-scoped CSS stylesheets whose rule processors
// we don't want to re-use
mRuleProcessors[aType] =
new nsCSSRuleProcessor(mSheets[aType], aType, nullptr,
static_cast<nsCSSRuleProcessor*>(
oldRuleProcessor.get()));
} break;
default:
MOZ_ASSERT_UNREACHABLE("non-CSS sheet types should be handled above");
break;
}
}
return NS_OK;
}
nsresult
nsStyleSet::AppendStyleSheet(SheetType aType, CSSStyleSheet* aSheet)
{
NS_PRECONDITION(aSheet, "null arg");
NS_ASSERTION(aSheet->IsApplicable(),
"Inapplicable sheet being placed in style set");
bool present = mSheets[aType].RemoveElement(aSheet);
mSheets[aType].AppendElement(aSheet);
if (!present && IsCSSSheetType(aType)) {
aSheet->AddStyleSet(this);
}
return DirtyRuleProcessors(aType);
}
nsresult
nsStyleSet::PrependStyleSheet(SheetType aType, CSSStyleSheet* aSheet)
{
NS_PRECONDITION(aSheet, "null arg");
NS_ASSERTION(aSheet->IsApplicable(),
"Inapplicable sheet being placed in style set");
bool present = mSheets[aType].RemoveElement(aSheet);
mSheets[aType].InsertElementAt(0, aSheet);
if (!present && IsCSSSheetType(aType)) {
aSheet->AddStyleSet(this);
}
return DirtyRuleProcessors(aType);
}
nsresult
nsStyleSet::RemoveStyleSheet(SheetType aType, CSSStyleSheet* aSheet)
{
NS_PRECONDITION(aSheet, "null arg");
NS_ASSERTION(aSheet->IsComplete(),
"Incomplete sheet being removed from style set");
if (mSheets[aType].RemoveElement(aSheet)) {
if (IsCSSSheetType(aType)) {
aSheet->DropStyleSet(this);
}
}
return DirtyRuleProcessors(aType);
}
nsresult
nsStyleSet::ReplaceSheets(SheetType aType,
const nsTArray<RefPtr<CSSStyleSheet>>& aNewSheets)
{
bool cssSheetType = IsCSSSheetType(aType);
if (cssSheetType) {
for (CSSStyleSheet* sheet : mSheets[aType]) {
sheet->DropStyleSet(this);
}
}
mSheets[aType].Clear();
mSheets[aType].AppendElements(aNewSheets);
if (cssSheetType) {
for (CSSStyleSheet* sheet : mSheets[aType]) {
sheet->AddStyleSet(this);
}
}
return DirtyRuleProcessors(aType);
}
nsresult
nsStyleSet::InsertStyleSheetBefore(SheetType aType, CSSStyleSheet* aNewSheet,
CSSStyleSheet* aReferenceSheet)
{
NS_PRECONDITION(aNewSheet && aReferenceSheet, "null arg");
NS_ASSERTION(aNewSheet->IsApplicable(),
"Inapplicable sheet being placed in style set");
bool present = mSheets[aType].RemoveElement(aNewSheet);
int32_t idx = mSheets[aType].IndexOf(aReferenceSheet);
if (idx < 0)
return NS_ERROR_INVALID_ARG;
mSheets[aType].InsertElementAt(idx, aNewSheet);
if (!present && IsCSSSheetType(aType)) {
aNewSheet->AddStyleSet(this);
}
return DirtyRuleProcessors(aType);
}
static inline uint32_t
DirtyBit(SheetType aType)
{
return 1 << uint32_t(aType);
}
nsresult
nsStyleSet::DirtyRuleProcessors(SheetType aType)
{
if (!mBatching)
return GatherRuleProcessors(aType);
mDirty |= DirtyBit(aType);
return NS_OK;
}
bool
nsStyleSet::GetAuthorStyleDisabled() const
{
return mAuthorStyleDisabled;
}
nsresult
nsStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
{
if (aStyleDisabled == !mAuthorStyleDisabled) {
mAuthorStyleDisabled = aStyleDisabled;
BeginUpdate();
mDirty |= DirtyBit(SheetType::Doc) |
DirtyBit(SheetType::ScopedDoc) |
DirtyBit(SheetType::StyleAttr);
return EndUpdate();
}
return NS_OK;
}
// -------- Doc Sheets
nsresult
nsStyleSet::AddDocStyleSheet(CSSStyleSheet* aSheet, nsIDocument* aDocument)
{
NS_PRECONDITION(aSheet && aDocument, "null arg");
NS_ASSERTION(aSheet->IsApplicable(),
"Inapplicable sheet being placed in style set");
SheetType type = aSheet->GetScopeElement() ?
SheetType::ScopedDoc :
SheetType::Doc;
nsTArray<RefPtr<CSSStyleSheet>>& sheets = mSheets[type];
bool present = sheets.RemoveElement(aSheet);
size_t index = aDocument->FindDocStyleSheetInsertionPoint(sheets, aSheet);
sheets.InsertElementAt(index, aSheet);
if (!present) {
aSheet->AddStyleSet(this);
}
return DirtyRuleProcessors(type);
}
void
nsStyleSet::AppendAllXBLStyleSheets(nsTArray<mozilla::CSSStyleSheet*>& aArray) const
{
if (mBindingManager) {
// XXXheycam stylo: AppendAllSheets will need to be able to return either
// CSSStyleSheets or ServoStyleSheets, on request (and then here requesting
// CSSStyleSheets).
AutoTArray<StyleSheet*, 32> sheets;
mBindingManager->AppendAllSheets(sheets);
for (StyleSheet* handle : sheets) {
MOZ_ASSERT(handle->IsGecko(), "stylo: AppendAllSheets shouldn't give us "
"ServoStyleSheets yet");
aArray.AppendElement(handle->AsGecko());
}
}
}
nsresult
nsStyleSet::RemoveDocStyleSheet(CSSStyleSheet* aSheet)
{
bool isScoped = aSheet->GetScopeElement();
return RemoveStyleSheet(isScoped ? SheetType::ScopedDoc : SheetType::Doc,
aSheet);
}
// Batching
void
nsStyleSet::BeginUpdate()
{
++mBatching;
}
nsresult
nsStyleSet::EndUpdate()
{
NS_ASSERTION(mBatching > 0, "Unbalanced EndUpdate");
if (--mBatching) {
// We're not completely done yet.
return NS_OK;
}
for (SheetType type : MakeEnumeratedRange(SheetType::Count)) {
if (mDirty & DirtyBit(type)) {
nsresult rv = GatherRuleProcessors(type);
NS_ENSURE_SUCCESS(rv, rv);
}
}
mDirty = 0;
return NS_OK;
}
template<class T>
static bool
EnumRulesMatching(nsIStyleRuleProcessor* aProcessor, void* aData)
{
T* data = static_cast<T*>(aData);
aProcessor->RulesMatching(data);
return true;
}
static inline bool
IsMoreSpecificThanAnimation(nsRuleNode *aRuleNode)
{
return !aRuleNode->IsRoot() &&
(aRuleNode->GetLevel() == SheetType::Transition ||
aRuleNode->IsImportantRule());
}
static nsIStyleRule*
GetAnimationRule(nsRuleNode *aRuleNode)
{
nsRuleNode *n = aRuleNode;
while (IsMoreSpecificThanAnimation(n)) {
n = n->GetParent();
}
if (n->IsRoot() || n->GetLevel() != SheetType::Animation) {
return nullptr;
}
return n->GetRule();
}
static nsRuleNode*
ReplaceAnimationRule(nsRuleNode *aOldRuleNode,
nsIStyleRule *aOldAnimRule,
nsIStyleRule *aNewAnimRule)
{
nsTArray<nsRuleNode*> moreSpecificNodes;
nsRuleNode *n = aOldRuleNode;
while (IsMoreSpecificThanAnimation(n)) {
moreSpecificNodes.AppendElement(n);
n = n->GetParent();
}
if (aOldAnimRule) {
MOZ_ASSERT(n->GetRule() == aOldAnimRule, "wrong rule");
MOZ_ASSERT(n->GetLevel() == SheetType::Animation,
"wrong level");
n = n->GetParent();
}
MOZ_ASSERT(!IsMoreSpecificThanAnimation(n) &&
(n->IsRoot() || n->GetLevel() != SheetType::Animation),
"wrong level");
if (aNewAnimRule) {
n = n->Transition(aNewAnimRule, SheetType::Animation, false);
n->SetIsAnimationRule();
}
for (uint32_t i = moreSpecificNodes.Length(); i-- != 0; ) {
nsRuleNode *oldNode = moreSpecificNodes[i];
n = n->Transition(oldNode->GetRule(), oldNode->GetLevel(),
oldNode->IsImportantRule());
}
return n;
}
/**
* |GetContext| implements sharing of style contexts (not just the data
* on the rule nodes) between siblings and cousins of the same
* generation. (It works for cousins of the same generation since
* |aParentContext| could itself be a shared context.)
*/
already_AddRefed<nsStyleContext>
nsStyleSet::GetContext(nsStyleContext* aParentContext,
nsRuleNode* aRuleNode,
// aVisitedRuleNode may be null; if it is null
// it means that we don't need to force creation
// of a StyleIfVisited. (But if we make one
// because aParentContext has one, then aRuleNode
// should be used.)
nsRuleNode* aVisitedRuleNode,
nsIAtom* aPseudoTag,
CSSPseudoElementType aPseudoType,
Element* aElementForAnimation,
uint32_t aFlags)
{
NS_PRECONDITION((!aPseudoTag &&
aPseudoType ==
CSSPseudoElementType::NotPseudo) ||
(aPseudoTag &&
nsCSSPseudoElements::GetPseudoType(
aPseudoTag, CSSEnabledState::eIgnoreEnabledState) ==
aPseudoType),
"Pseudo mismatch");
if (aVisitedRuleNode == aRuleNode) {
// No need to force creation of a visited style in this case.
aVisitedRuleNode = nullptr;
}
// Ensure |aVisitedRuleNode != nullptr| corresponds to the need to
// create an if-visited style context, and that in that case, we have
// parentIfVisited set correctly.
nsStyleContext *parentIfVisited =
aParentContext ? aParentContext->GetStyleIfVisited() : nullptr;
if (parentIfVisited) {
if (!aVisitedRuleNode) {
aVisitedRuleNode = aRuleNode;
}
} else {
if (aVisitedRuleNode) {
parentIfVisited = aParentContext;
}
}
if (aFlags & eIsLink) {
// If this node is a link, we want its visited's style context's
// parent to be the regular style context of its parent, because
// only the visitedness of the relevant link should influence style.
parentIfVisited = aParentContext;
}
bool relevantLinkVisited = (aFlags & eIsLink) ?
(aFlags & eIsVisitedLink) :
(aParentContext && aParentContext->RelevantLinkVisited());
RefPtr<nsStyleContext> result;
if (aParentContext)
result = aParentContext->FindChildWithRules(aPseudoTag, aRuleNode,
aVisitedRuleNode,
relevantLinkVisited);
if (!result) {
// |aVisitedRuleNode| may have a ref-count of zero since we are yet
// to create the style context that will hold an owning reference to it.
// As a result, we need to make sure it stays alive until that point
// in case something in the first call to NS_NewStyleContext triggers a
// GC sweep of rule nodes.
RefPtr<nsRuleNode> kungFuDeathGrip{aVisitedRuleNode};
result = NS_NewStyleContext(aParentContext, aPseudoTag, aPseudoType,
aRuleNode,
aFlags & eSkipParentDisplayBasedStyleFixup);
if (aVisitedRuleNode) {
RefPtr<nsStyleContext> resultIfVisited =
NS_NewStyleContext(parentIfVisited, aPseudoTag, aPseudoType,
aVisitedRuleNode,
aFlags & eSkipParentDisplayBasedStyleFixup);
resultIfVisited->SetIsStyleIfVisited();
result->SetStyleIfVisited(resultIfVisited.forget());
if (relevantLinkVisited) {
result->AddStyleBit(NS_STYLE_RELEVANT_LINK_VISITED);
}
}
}
else {
NS_ASSERTION(result->GetPseudoType() == aPseudoType, "Unexpected type");
NS_ASSERTION(result->GetPseudo() == aPseudoTag, "Unexpected pseudo");
}
if (aFlags & eDoAnimation) {
nsIStyleRule *oldAnimRule = GetAnimationRule(aRuleNode);
nsIStyleRule *animRule = nullptr;
// Ignore animations for print or print preview, and for elements
// that are not attached to the document tree.
if (PresContext()->IsDynamic() &&
aElementForAnimation->IsInComposedDoc()) {
// Update CSS animations in case the animation-name has just changed.
PresContext()->AnimationManager()->UpdateAnimations(result,
aElementForAnimation);
PresContext()->EffectCompositor()->UpdateEffectProperties(
result, aElementForAnimation, result->GetPseudoType());
animRule = PresContext()->EffectCompositor()->
GetAnimationRule(aElementForAnimation,
result->GetPseudoType(),
EffectCompositor::CascadeLevel::Animations,
result);
}
MOZ_ASSERT(result->RuleNode() == aRuleNode,
"unexpected rule node");
MOZ_ASSERT(!result->GetStyleIfVisited() == !aVisitedRuleNode,
"unexpected visited rule node");
MOZ_ASSERT(!aVisitedRuleNode ||
result->GetStyleIfVisited()->RuleNode() == aVisitedRuleNode,
"unexpected visited rule node");
MOZ_ASSERT(!aVisitedRuleNode ||
oldAnimRule == GetAnimationRule(aVisitedRuleNode),
"animation rule mismatch between rule nodes");
if (oldAnimRule != animRule) {
// FIXME: This should use ResolveStyleWithReplacement instead (and
// we can remove ReplaceAnimationRule), since
// ResolveStyleWithReplacement should now be equally efficient
// (except for the extra code complexity to be more general).
nsRuleNode *ruleNode =
ReplaceAnimationRule(aRuleNode, oldAnimRule, animRule);
nsRuleNode *visitedRuleNode = aVisitedRuleNode
? ReplaceAnimationRule(aVisitedRuleNode, oldAnimRule, animRule)
: nullptr;
MOZ_ASSERT(!visitedRuleNode ||
GetAnimationRule(ruleNode) ==
GetAnimationRule(visitedRuleNode),
"animation rule mismatch between rule nodes");
result = GetContext(aParentContext, ruleNode, visitedRuleNode,
aPseudoTag, aPseudoType, nullptr,
aFlags & ~eDoAnimation);
}
}
if (aElementForAnimation &&
aElementForAnimation->IsHTMLElement(nsGkAtoms::body) &&
aPseudoType == CSSPseudoElementType::NotPseudo &&
PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
nsIDocument* doc = aElementForAnimation->GetUncomposedDoc();
if (doc && doc->GetBodyElement() == aElementForAnimation) {
// Update the prescontext's body color
PresContext()->SetBodyTextColor(result->StyleColor()->mColor);
}
}
return result.forget();
}
void
nsStyleSet::AddImportantRules(nsRuleNode* aCurrLevelNode,
nsRuleNode* aLastPrevLevelNode,
nsRuleWalker* aRuleWalker)
{
NS_ASSERTION(aCurrLevelNode &&
aCurrLevelNode != aLastPrevLevelNode, "How did we get here?");
AutoTArray<nsIStyleRule*, 16> importantRules;
for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
node = node->GetParent()) {
// We guarantee that we never walk the root node here, so no need
// to null-check GetRule(). Furthermore, it must be a CSS rule.
NS_ASSERTION(RefPtr<css::Declaration>(do_QueryObject(node->GetRule())),
"Unexpected non-CSS rule");
nsIStyleRule* impRule =
static_cast<css::Declaration*>(node->GetRule())->GetImportantStyleData();
if (impRule)
importantRules.AppendElement(impRule);
}
NS_ASSERTION(importantRules.Length() != 0,
"Why did we think there were important rules?");
for (uint32_t i = importantRules.Length(); i-- != 0; ) {
aRuleWalker->Forward(importantRules[i]);
}
}
#ifdef DEBUG
void
nsStyleSet::AssertNoImportantRules(nsRuleNode* aCurrLevelNode,
nsRuleNode* aLastPrevLevelNode)
{
if (!aCurrLevelNode)
return;
for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
node = node->GetParent()) {
RefPtr<css::Declaration> declaration(do_QueryObject(node->GetRule()));
NS_ASSERTION(declaration, "Unexpected non-CSS rule");
NS_ASSERTION(!declaration->GetImportantStyleData(),
"Unexpected important style source");
}
}
void
nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode,
nsRuleNode* aLastPrevLevelNode)
{
if (!aCurrLevelNode)
return;
for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
node = node->GetParent()) {
nsIStyleRule *rule = node->GetRule();
RefPtr<css::Declaration> declaration(do_QueryObject(rule));
if (declaration) {
RefPtr<css::StyleRule> cssRule =
do_QueryObject(declaration->GetOwningRule());
NS_ASSERTION(!cssRule || !cssRule->Selector(),
"Unexpected CSS rule");
}
}
}
#endif
// Enumerate the rules in a way that cares about the order of the rules.
void
nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc,
RuleProcessorData* aData, Element* aElement,
nsRuleWalker* aRuleWalker)
{
PROFILER_LABEL("nsStyleSet", "FileRules",
js::ProfileEntry::Category::CSS);
NS_ASSERTION(mBatching == 0, "rule processors out of date");
// Cascading order:
// [least important]
// - UA normal rules = Agent normal
// - User normal rules = User normal
// - Presentation hints = PresHint normal
// - Author normal rules = Document normal
// - Override normal rules = Override normal
// - animation rules = Animation normal
// - Author !important rules = Document !important
// - Override !important rules = Override !important
// - User !important rules = User !important
// - UA !important rules = Agent !important
// - transition rules = Transition normal
// [most important]
// Save off the last rule before we start walking our agent sheets;
// this will be either the root or one of the restriction rules.
nsRuleNode* lastRestrictionRN = aRuleWalker->CurrentNode();
aRuleWalker->SetLevel(SheetType::Agent, false, true);
if (mRuleProcessors[SheetType::Agent])
(*aCollectorFunc)(mRuleProcessors[SheetType::Agent], aData);
nsRuleNode* lastAgentRN = aRuleWalker->CurrentNode();
bool haveImportantUARules = !aRuleWalker->GetCheckForImportantRules();
aRuleWalker->SetLevel(SheetType::User, false, true);
bool skipUserStyles =
aElement && aElement->IsInNativeAnonymousSubtree();
if (!skipUserStyles && mRuleProcessors[SheetType::User]) // NOTE: different
(*aCollectorFunc)(mRuleProcessors[SheetType::User], aData);
nsRuleNode* lastUserRN = aRuleWalker->CurrentNode();
bool haveImportantUserRules = !aRuleWalker->GetCheckForImportantRules();
aRuleWalker->SetLevel(SheetType::PresHint, false, false);
if (mRuleProcessors[SheetType::PresHint])
(*aCollectorFunc)(mRuleProcessors[SheetType::PresHint], aData);
nsRuleNode* lastPresHintRN = aRuleWalker->CurrentNode();
aRuleWalker->SetLevel(SheetType::Doc, false, true);
bool cutOffInheritance = false;
if (mBindingManager && aElement) {
// We can supply additional document-level sheets that should be walked.
mBindingManager->WalkRules(aCollectorFunc,
static_cast<ElementDependentRuleProcessorData*>(aData),
&cutOffInheritance);
}
if (!skipUserStyles && !cutOffInheritance && // NOTE: different
mRuleProcessors[SheetType::Doc])
(*aCollectorFunc)(mRuleProcessors[SheetType::Doc], aData);
nsRuleNode* lastDocRN = aRuleWalker->CurrentNode();
bool haveImportantDocRules = !aRuleWalker->GetCheckForImportantRules();
nsTArray<nsRuleNode*> lastScopedRNs;
nsTArray<bool> haveImportantScopedRules;
bool haveAnyImportantScopedRules = false;
if (!skipUserStyles && !cutOffInheritance &&
aElement && aElement->IsElementInStyleScope()) {
lastScopedRNs.SetLength(mScopedDocSheetRuleProcessors.Length());
haveImportantScopedRules.SetLength(mScopedDocSheetRuleProcessors.Length());
for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) {
aRuleWalker->SetLevel(SheetType::ScopedDoc, false, true);
nsCSSRuleProcessor* processor =
static_cast<nsCSSRuleProcessor*>(mScopedDocSheetRuleProcessors[i].get());
aData->mScope = processor->GetScopeElement();
(*aCollectorFunc)(mScopedDocSheetRuleProcessors[i], aData);
lastScopedRNs[i] = aRuleWalker->CurrentNode();
haveImportantScopedRules[i] = !aRuleWalker->GetCheckForImportantRules();
haveAnyImportantScopedRules = haveAnyImportantScopedRules || haveImportantScopedRules[i];
}
aData->mScope = nullptr;
}
nsRuleNode* lastScopedRN = aRuleWalker->CurrentNode();
aRuleWalker->SetLevel(SheetType::StyleAttr, false, true);
if (mRuleProcessors[SheetType::StyleAttr])
(*aCollectorFunc)(mRuleProcessors[SheetType::StyleAttr], aData);
nsRuleNode* lastStyleAttrRN = aRuleWalker->CurrentNode();
bool haveImportantStyleAttrRules = !aRuleWalker->GetCheckForImportantRules();
aRuleWalker->SetLevel(SheetType::Override, false, true);
if (mRuleProcessors[SheetType::Override])
(*aCollectorFunc)(mRuleProcessors[SheetType::Override], aData);
nsRuleNode* lastOvrRN = aRuleWalker->CurrentNode();
bool haveImportantOverrideRules = !aRuleWalker->GetCheckForImportantRules();
// This needs to match IsMoreSpecificThanAnimation() above.
aRuleWalker->SetLevel(SheetType::Animation, false, false);
(*aCollectorFunc)(mRuleProcessors[SheetType::Animation], aData);
if (haveAnyImportantScopedRules) {
for (uint32_t i = lastScopedRNs.Length(); i-- != 0; ) {
aRuleWalker->SetLevel(SheetType::ScopedDoc, true, false);
nsRuleNode* startRN = lastScopedRNs[i];
nsRuleNode* endRN = i == 0 ? lastDocRN : lastScopedRNs[i - 1];
if (haveImportantScopedRules[i]) {
AddImportantRules(startRN, endRN, aRuleWalker); // scoped
}
#ifdef DEBUG
else {
AssertNoImportantRules(startRN, endRN);
}
#endif
}
}
#ifdef DEBUG
else {
AssertNoImportantRules(lastScopedRN, lastDocRN);
}
#endif
if (haveImportantDocRules) {
aRuleWalker->SetLevel(SheetType::Doc, true, false);
AddImportantRules(lastDocRN, lastPresHintRN, aRuleWalker); // doc
}
#ifdef DEBUG
else {
AssertNoImportantRules(lastDocRN, lastPresHintRN);
}
#endif
if (haveImportantStyleAttrRules) {
aRuleWalker->SetLevel(SheetType::StyleAttr, true, false);
AddImportantRules(lastStyleAttrRN, lastScopedRN, aRuleWalker); // style attr
}
#ifdef DEBUG
else {
AssertNoImportantRules(lastStyleAttrRN, lastScopedRN);
}
#endif
if (haveImportantOverrideRules) {
aRuleWalker->SetLevel(SheetType::Override, true, false);
AddImportantRules(lastOvrRN, lastStyleAttrRN, aRuleWalker); // override
}
#ifdef DEBUG
else {
AssertNoImportantRules(lastOvrRN, lastStyleAttrRN);
}
#endif
#ifdef DEBUG
AssertNoCSSRules(lastPresHintRN, lastUserRN);
#endif
if (haveImportantUserRules) {
aRuleWalker->SetLevel(SheetType::User, true, false);
AddImportantRules(lastUserRN, lastAgentRN, aRuleWalker); //user
}
#ifdef DEBUG
else {
AssertNoImportantRules(lastUserRN, lastAgentRN);
}
#endif
if (haveImportantUARules) {
aRuleWalker->SetLevel(SheetType::Agent, true, false);
AddImportantRules(lastAgentRN, lastRestrictionRN, aRuleWalker); //agent
}
#ifdef DEBUG
else {
AssertNoImportantRules(lastAgentRN, lastRestrictionRN);
}
#endif
#ifdef DEBUG
AssertNoCSSRules(lastRestrictionRN, mRuleTree);
#endif
#ifdef DEBUG
nsRuleNode *lastImportantRN = aRuleWalker->CurrentNode();
#endif
aRuleWalker->SetLevel(SheetType::Transition, false, false);
(*aCollectorFunc)(mRuleProcessors[SheetType::Transition], aData);
#ifdef DEBUG
AssertNoCSSRules(aRuleWalker->CurrentNode(), lastImportantRN);
#endif
}
// Enumerate all the rules in a way that doesn't care about the order
// of the rules and doesn't walk !important-rules.
void
nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc,
ElementDependentRuleProcessorData* aData,
bool aWalkAllXBLStylesheets)
{
NS_ASSERTION(mBatching == 0, "rule processors out of date");
if (mRuleProcessors[SheetType::Agent])
(*aFunc)(mRuleProcessors[SheetType::Agent], aData);
bool skipUserStyles = aData->mElement->IsInNativeAnonymousSubtree();
if (!skipUserStyles && mRuleProcessors[SheetType::User]) // NOTE: different
(*aFunc)(mRuleProcessors[SheetType::User], aData);
if (mRuleProcessors[SheetType::PresHint])
(*aFunc)(mRuleProcessors[SheetType::PresHint], aData);
bool cutOffInheritance = false;
if (mBindingManager) {
// We can supply additional document-level sheets that should be walked.
if (aWalkAllXBLStylesheets) {
mBindingManager->WalkAllRules(aFunc, aData);
} else {
mBindingManager->WalkRules(aFunc, aData, &cutOffInheritance);
}
}
if (!skipUserStyles && !cutOffInheritance) {
if (mRuleProcessors[SheetType::Doc]) // NOTE: different
(*aFunc)(mRuleProcessors[SheetType::Doc], aData);
if (aData->mElement->IsElementInStyleScope()) {
for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++)
(*aFunc)(mScopedDocSheetRuleProcessors[i], aData);
}
}
if (mRuleProcessors[SheetType::StyleAttr])
(*aFunc)(mRuleProcessors[SheetType::StyleAttr], aData);
if (mRuleProcessors[SheetType::Override])
(*aFunc)(mRuleProcessors[SheetType::Override], aData);
(*aFunc)(mRuleProcessors[SheetType::Animation], aData);
(*aFunc)(mRuleProcessors[SheetType::Transition], aData);
}
static void
InitStyleScopes(TreeMatchContext& aTreeContext, Element* aElement)
{
if (aElement->IsElementInStyleScope()) {
aTreeContext.InitStyleScopes(aElement->GetParentElementCrossingShadowRoot());
}
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleFor(Element* aElement,
nsStyleContext* aParentContext)
{
TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
aElement->OwnerDoc());
InitStyleScopes(treeContext, aElement);
return ResolveStyleFor(aElement, aParentContext, treeContext);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleForInternal(Element* aElement,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext,
AnimationFlag aAnimationFlag)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
NS_ASSERTION(aElement, "aElement must not be null");
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
aTreeMatchContext.ResetForUnvisitedMatching();
ElementRuleProcessorData data(PresContext(), aElement, &ruleWalker,
aTreeMatchContext);
WalkDisableTextZoomRule(aElement, &ruleWalker);
FileRules(EnumRulesMatching<ElementRuleProcessorData>, &data, aElement,
&ruleWalker);
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nullptr;
if (aTreeMatchContext.HaveRelevantLink()) {
aTreeMatchContext.ResetForVisitedMatching();
ruleWalker.Reset();
FileRules(EnumRulesMatching<ElementRuleProcessorData>, &data, aElement,
&ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
uint32_t flags = (aAnimationFlag == eWithAnimation) ? eDoAnimation : eNoFlags;
if (nsCSSRuleProcessor::IsLink(aElement)) {
flags |= eIsLink;
}
if (nsCSSRuleProcessor::GetContentState(aElement, aTreeMatchContext).
HasState(NS_EVENT_STATE_VISITED)) {
flags |= eIsVisitedLink;
}
if (aTreeMatchContext.mSkippingParentDisplayBasedStyleFixup) {
flags |= eSkipParentDisplayBasedStyleFixup;
}
return GetContext(aParentContext, ruleNode, visitedRuleNode,
nullptr, CSSPseudoElementType::NotPseudo,
aElement, flags);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleFor(Element* aElement,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext)
{
return ResolveStyleForInternal(aElement,
aParentContext,
aTreeMatchContext,
eWithAnimation);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext,
const nsTArray< nsCOMPtr<nsIStyleRule> > &aRules)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
// FIXME: Perhaps this should be passed in, but it probably doesn't
// matter.
ruleWalker.SetLevel(SheetType::Doc, false, false);
for (uint32_t i = 0; i < aRules.Length(); i++) {
ruleWalker.ForwardOnPossiblyCSSRule(aRules.ElementAt(i));
}
return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr,
nullptr, CSSPseudoElementType::NotPseudo,
nullptr, eNoFlags);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext,
const nsCOMArray<nsIStyleRule> &aRules)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
ruleWalker.SetCurrentNode(aBaseContext->RuleNode());
// This needs to be the transition sheet because that is the highest
// level of the cascade, and thus the only thing that makes sense if
// we are ever going to call ResolveStyleWithReplacement on the
// resulting context. It's also the right thing for the one case (the
// transition manager's cover rule) where we put the result of this
// function in the style context tree.
ruleWalker.SetLevel(SheetType::Transition, false, false);
for (int32_t i = 0; i < aRules.Count(); i++) {
ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i));
}
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nullptr;
if (aBaseContext->GetStyleIfVisited()) {
ruleWalker.SetCurrentNode(aBaseContext->GetStyleIfVisited()->RuleNode());
for (int32_t i = 0; i < aRules.Count(); i++) {
ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i));
}
visitedRuleNode = ruleWalker.CurrentNode();
}
uint32_t flags = eNoFlags;
if (aBaseContext->IsLinkContext()) {
flags |= eIsLink;
// GetContext handles propagating RelevantLinkVisited state from the
// parent in non-link cases; all we need to pass in is if this link
// is visited.
if (aBaseContext->RelevantLinkVisited()) {
flags |= eIsVisitedLink;
}
}
return GetContext(aBaseContext->GetParent(), ruleNode, visitedRuleNode,
aBaseContext->GetPseudo(),
aBaseContext->GetPseudoType(),
nullptr, flags);
}
struct RuleNodeInfo {
nsIStyleRule* mRule;
SheetType mLevel;
bool mIsImportant;
bool mIsAnimationRule;
};
struct CascadeLevel {
SheetType mLevel;
bool mIsImportant;
bool mCheckForImportantRules;
nsRestyleHint mLevelReplacementHint;
};
static const CascadeLevel gCascadeLevels[] = {
{ SheetType::Agent, false, false, nsRestyleHint(0) },
{ SheetType::User, false, false, nsRestyleHint(0) },
{ SheetType::PresHint, false, false, nsRestyleHint(0) },
{ SheetType::Doc, false, false, nsRestyleHint(0) },
{ SheetType::ScopedDoc, false, false, nsRestyleHint(0) },
{ SheetType::StyleAttr, false, true, eRestyle_StyleAttribute |
eRestyle_StyleAttribute_Animations },
{ SheetType::Override, false, false, nsRestyleHint(0) },
{ SheetType::Animation, false, false, eRestyle_CSSAnimations },
{ SheetType::ScopedDoc, true, false, nsRestyleHint(0) },
{ SheetType::Doc, true, false, nsRestyleHint(0) },
{ SheetType::StyleAttr, true, false, eRestyle_StyleAttribute |
eRestyle_StyleAttribute_Animations },
{ SheetType::Override, true, false, nsRestyleHint(0) },
{ SheetType::User, true, false, nsRestyleHint(0) },
{ SheetType::Agent, true, false, nsRestyleHint(0) },
{ SheetType::Transition, false, false, eRestyle_CSSTransitions },
};
nsRuleNode*
nsStyleSet::RuleNodeWithReplacement(Element* aElement,
Element* aPseudoElement,
nsRuleNode* aOldRuleNode,
CSSPseudoElementType aPseudoType,
nsRestyleHint aReplacements)
{
NS_ASSERTION(mBatching == 0, "rule processors out of date");
MOZ_ASSERT(!aPseudoElement ==
(aPseudoType >= CSSPseudoElementType::Count ||
!(nsCSSPseudoElements::PseudoElementSupportsStyleAttribute(aPseudoType) ||
nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudoType))),
"should have aPseudoElement only for certain pseudo elements");
// Remove the Force bits, which we don't need and which could confuse
// the remainingReplacements code below.
aReplacements &= ~(eRestyle_Force | eRestyle_ForceDescendants);
MOZ_ASSERT(!(aReplacements & ~(eRestyle_CSSTransitions |
eRestyle_CSSAnimations |
eRestyle_StyleAttribute |
eRestyle_StyleAttribute_Animations)),
"unexpected replacement bits");
// This array can be hot and often grows to ~20 elements, so inline storage
// is best.
AutoTArray<RuleNodeInfo, 30> rules;
const CascadeLevel* startingLevel = gCascadeLevels;
nsRuleNode* startingNode = mRuleTree;
if (mInReconstruct) {
// Replace the entire path in the rule tree, since the rule tree has
// a new root.
for (nsRuleNode* ruleNode = aOldRuleNode; !ruleNode->IsRoot();
ruleNode = ruleNode->GetParent()) {
RuleNodeInfo* curRule = rules.AppendElement();
curRule->mRule = ruleNode->GetRule();
curRule->mLevel = ruleNode->GetLevel();
curRule->mIsImportant = ruleNode->IsImportantRule();
curRule->mIsAnimationRule = ruleNode->IsAnimationRule();
}
} else {
if (aReplacements == nsRestyleHint(0)) {
// Nothing to do.
return aOldRuleNode;
}
// Walk up the rule tree from aOldNode to figure out the *part* of
// the path in the rule tree that we need to replace.
nsRestyleHint remainingReplacements = aReplacements;
nsRuleNode* ruleNode = aOldRuleNode;
for (const CascadeLevel *level = ArrayEnd(gCascadeLevels);
level-- != gCascadeLevels; ) {
SheetType nodeLevel;
bool nodeIsImportant;
while (!ruleNode->IsRoot() &&
(nodeLevel = ruleNode->GetLevel()) == level->mLevel &&
(nodeIsImportant = ruleNode->IsImportantRule()) ==
level->mIsImportant) {
if (!(level->mLevelReplacementHint & aReplacements)) {
RuleNodeInfo* curRule = rules.AppendElement();
curRule->mRule = ruleNode->GetRule();
curRule->mLevel = nodeLevel;
curRule->mIsImportant = nodeIsImportant;
curRule->mIsAnimationRule = ruleNode->IsAnimationRule();
}
ruleNode = ruleNode->GetParent();
}
if (!level->mIsImportant &&
(level->mLevelReplacementHint & aReplacements)) {
remainingReplacements &= ~level->mLevelReplacementHint;
if (remainingReplacements == nsRestyleHint(0)) {
// We've found the part of the path in the rule tree we
// need to replace.
startingLevel = level;
startingNode = ruleNode;
break;
}
}
}
MOZ_ASSERT(remainingReplacements == nsRestyleHint(0),
"unexpected replacements (but we safely handle this case by "
"falling through to replacing the whole path");
}
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
ruleWalker.SetCurrentNode(startingNode);
auto rulesIndex = rules.Length();
// We need to transfer this information between the non-!important and
// !important phases for the style attribute level.
nsRuleNode* lastScopedRN = nullptr;
nsRuleNode* lastStyleAttrRN = nullptr;
bool haveImportantStyleAttrRules = false;
for (const CascadeLevel *level = startingLevel,
*levelEnd = ArrayEnd(gCascadeLevels);
level != levelEnd; ++level) {
bool doReplace = level->mLevelReplacementHint & aReplacements;
ruleWalker.SetLevel(level->mLevel, level->mIsImportant,
level->mCheckForImportantRules && doReplace);
if (doReplace) {
switch (level->mLevel) {
case SheetType::Animation: {
if (aPseudoType == CSSPseudoElementType::NotPseudo ||
aPseudoType == CSSPseudoElementType::before ||
aPseudoType == CSSPseudoElementType::after) {
nsIStyleRule* rule = PresContext()->EffectCompositor()->
GetAnimationRule(aElement, aPseudoType,
EffectCompositor::CascadeLevel::Animations,
nullptr);
if (rule) {
ruleWalker.ForwardOnPossiblyCSSRule(rule);
ruleWalker.CurrentNode()->SetIsAnimationRule();
}
}
break;
}
case SheetType::Transition: {
if (aPseudoType == CSSPseudoElementType::NotPseudo ||
aPseudoType == CSSPseudoElementType::before ||
aPseudoType == CSSPseudoElementType::after) {
nsIStyleRule* rule = PresContext()->EffectCompositor()->
GetAnimationRule(aElement, aPseudoType,
EffectCompositor::CascadeLevel::Transitions,
nullptr);
if (rule) {
ruleWalker.ForwardOnPossiblyCSSRule(rule);
ruleWalker.CurrentNode()->SetIsAnimationRule();
}
}
break;
}
case SheetType::StyleAttr: {
if (!level->mIsImportant) {
// First time through, we handle the non-!important rule.
nsHTMLCSSStyleSheet* ruleProcessor =
static_cast<nsHTMLCSSStyleSheet*>(
mRuleProcessors[SheetType::StyleAttr].get());
if (ruleProcessor) {
lastScopedRN = ruleWalker.CurrentNode();
if (aPseudoType ==
CSSPseudoElementType::NotPseudo) {
ruleProcessor->ElementRulesMatching(PresContext(),
aElement,
&ruleWalker);
} else if (aPseudoType <
CSSPseudoElementType::Count &&
nsCSSPseudoElements::
PseudoElementSupportsStyleAttribute(aPseudoType)) {
ruleProcessor->PseudoElementRulesMatching(aPseudoElement,
aPseudoType,
&ruleWalker);
}
lastStyleAttrRN = ruleWalker.CurrentNode();
haveImportantStyleAttrRules =
!ruleWalker.GetCheckForImportantRules();
}
} else {
// Second time through, we handle the !important rule(s).
if (haveImportantStyleAttrRules) {
AddImportantRules(lastStyleAttrRN, lastScopedRN, &ruleWalker);
}
}
break;
}
default:
MOZ_ASSERT(false, "unexpected result from gCascadeLevels lookup");
break;
}
}
while (rulesIndex != 0) {
--rulesIndex;
const RuleNodeInfo& ruleInfo = rules[rulesIndex];
if (ruleInfo.mLevel != level->mLevel ||
ruleInfo.mIsImportant != level->mIsImportant) {
++rulesIndex;
break;
}
// When mIsReconstruct is true, we have rules we need to skip in
// the array, so we need to test !doReplace here.
if (!doReplace) {
ruleWalker.ForwardOnPossiblyCSSRule(ruleInfo.mRule);
if (ruleInfo.mIsAnimationRule) {
ruleWalker.CurrentNode()->SetIsAnimationRule();
}
}
}
}
NS_ASSERTION(rulesIndex == 0,
"rules are in incorrect cascading order, "
"which means we replaced them incorrectly");
return ruleWalker.CurrentNode();
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleWithReplacement(Element* aElement,
Element* aPseudoElement,
nsStyleContext* aNewParentContext,
nsStyleContext* aOldStyleContext,
nsRestyleHint aReplacements,
uint32_t aFlags)
{
nsRuleNode* ruleNode =
RuleNodeWithReplacement(aElement, aPseudoElement,
aOldStyleContext->RuleNode(),
aOldStyleContext->GetPseudoType(), aReplacements);
nsRuleNode* visitedRuleNode = nullptr;
nsStyleContext* oldStyleIfVisited = aOldStyleContext->GetStyleIfVisited();
if (oldStyleIfVisited) {
if (oldStyleIfVisited->RuleNode() == aOldStyleContext->RuleNode()) {
visitedRuleNode = ruleNode;
} else {
visitedRuleNode =
RuleNodeWithReplacement(aElement, aPseudoElement,
oldStyleIfVisited->RuleNode(),
oldStyleIfVisited->GetPseudoType(),
aReplacements);
}
}
uint32_t flags = eNoFlags;
if (aOldStyleContext->IsLinkContext()) {
flags |= eIsLink;
// GetContext handles propagating RelevantLinkVisited state from the
// parent in non-link cases; all we need to pass in is if this link
// is visited.
if (aOldStyleContext->RelevantLinkVisited()) {
flags |= eIsVisitedLink;
}
}
CSSPseudoElementType pseudoType = aOldStyleContext->GetPseudoType();
Element* elementForAnimation = nullptr;
if (!(aFlags & eSkipStartingAnimations) &&
(pseudoType == CSSPseudoElementType::NotPseudo ||
pseudoType == CSSPseudoElementType::before ||
pseudoType == CSSPseudoElementType::after)) {
// We want to compute a correct elementForAnimation to pass in
// because at this point the parameter is more than just the element
// for animation; it's also used for the SetBodyTextColor call when
// it's the body element.
// However, we only want to set the flag to call UpdateAnimations
// if we're dealing with a replacement (such as style attribute
// replacement) that could lead to the animation property changing,
// and we explicitly do NOT want to call UpdateAnimations when
// we're trying to do an animation-only update.
if (aReplacements & ~(eRestyle_CSSTransitions | eRestyle_CSSAnimations)) {
flags |= eDoAnimation;
}
elementForAnimation = aElement;
#ifdef DEBUG
{
nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(elementForAnimation);
NS_ASSERTION(pseudoType == CSSPseudoElementType::NotPseudo ||
!styleFrame ||
styleFrame->StyleContext()->GetPseudoType() ==
CSSPseudoElementType::NotPseudo,
"aElement should be the element and not the pseudo-element");
}
#endif
}
if (aElement && aElement->IsRootOfAnonymousSubtree()) {
// For anonymous subtree roots, don't tweak "display" value based on whether
// or not the parent is styled as a flex/grid container. (If the parent
// has anonymous-subtree kids, then we know it's not actually going to get
// a flex/grid container frame, anyway.)
flags |= eSkipParentDisplayBasedStyleFixup;
}
return GetContext(aNewParentContext, ruleNode, visitedRuleNode,
aOldStyleContext->GetPseudo(), pseudoType,
elementForAnimation, flags);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleByRemovingAnimation(dom::Element* aTarget,
nsStyleContext* aStyleContext,
nsRestyleHint aWhichToRemove)
{
#ifdef DEBUG
CSSPseudoElementType pseudoType = aStyleContext->GetPseudoType();
#endif
MOZ_ASSERT(pseudoType == CSSPseudoElementType::NotPseudo ||
pseudoType == CSSPseudoElementType::before ||
pseudoType == CSSPseudoElementType::after,
"unexpected type for animations");
MOZ_ASSERT(PresContext()->RestyleManager()->IsGecko(),
"stylo: the style set and restyle manager must have the same "
"StyleBackendType");
GeckoRestyleManager* restyleManager =
PresContext()->RestyleManager()->AsGecko();
bool oldSkipAnimationRules = restyleManager->SkipAnimationRules();
restyleManager->SetSkipAnimationRules(true);
RefPtr<nsStyleContext> result =
ResolveStyleWithReplacement(aTarget, nullptr, aStyleContext->GetParent(),
aStyleContext, aWhichToRemove,
eSkipStartingAnimations);
restyleManager->SetSkipAnimationRules(oldSkipAnimationRules);
return result.forget();
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleWithoutAnimation(Element* aTarget,
nsStyleContext* aParentContext)
{
GeckoRestyleManager* restyleManager =
PresContext()->RestyleManager()->AsGecko();
TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
aTarget->OwnerDoc());
InitStyleScopes(treeContext, aTarget);
bool oldSkipAnimationRules = restyleManager->SkipAnimationRules();
restyleManager->SetSkipAnimationRules(true);
// Here we can call ResolveStyleForInternal() instead of
// ResolveStyleWithReplacement() since we don't need any animation rules
// (CSS Animations, Transitions and script animations). That's because
// EffectCompositor::GetAnimationRule() skips all of animations rules if
// SkipAnimationRules flag is true.
RefPtr<nsStyleContext> result = ResolveStyleForInternal(aTarget,
aParentContext,
treeContext,
eWithoutAnimation);
restyleManager->SetSkipAnimationRules(oldSkipAnimationRules);
return result.forget();
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleForText(nsIContent* aTextNode,
nsStyleContext* aParentContext)
{
MOZ_ASSERT(aTextNode && aTextNode->IsNodeOfType(nsINode::eTEXT));
return GetContext(aParentContext, mRuleTree, nullptr,
nsCSSAnonBoxes::mozText,
CSSPseudoElementType::InheritingAnonBox, nullptr, eNoFlags);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleForFirstLetterContinuation(nsStyleContext* aParentContext)
{
return GetContext(aParentContext, mRuleTree, nullptr,
nsCSSAnonBoxes::firstLetterContinuation,
CSSPseudoElementType::InheritingAnonBox, nullptr, eNoFlags);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveStyleForPlaceholder()
{
RefPtr<nsStyleContext>& cache =
mNonInheritingStyleContexts[nsCSSAnonBoxes::NonInheriting::oofPlaceholder];
if (cache) {
RefPtr<nsStyleContext> retval = cache;
return retval.forget();
}
RefPtr<nsStyleContext> retval =
GetContext(nullptr, mRuleTree, nullptr,
nsCSSAnonBoxes::oofPlaceholder,
CSSPseudoElementType::NonInheritingAnonBox, nullptr, eNoFlags);
cache = retval;
return retval.forget();
}
void
nsStyleSet::WalkRestrictionRule(CSSPseudoElementType aPseudoType,
nsRuleWalker* aRuleWalker)
{
// This needs to match GetPseudoRestriction in nsRuleNode.cpp.
aRuleWalker->SetLevel(SheetType::Agent, false, false);
if (aPseudoType == CSSPseudoElementType::firstLetter)
aRuleWalker->Forward(mFirstLetterRule);
else if (aPseudoType == CSSPseudoElementType::firstLine)
aRuleWalker->Forward(mFirstLineRule);
else if (aPseudoType == CSSPseudoElementType::placeholder)
aRuleWalker->Forward(mPlaceholderRule);
}
void
nsStyleSet::WalkDisableTextZoomRule(Element* aElement, nsRuleWalker* aRuleWalker)
{
aRuleWalker->SetLevel(SheetType::Agent, false, false);
if (aElement->IsSVGElement(nsGkAtoms::text))
aRuleWalker->Forward(mDisableTextZoomStyleRule);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolvePseudoElementStyleInternal(
Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
Element* aPseudoElement,
AnimationFlag aAnimationFlag)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
NS_ASSERTION(aType < CSSPseudoElementType::Count,
"must have pseudo element type");
NS_ASSERTION(aParentElement, "Must have parent element");
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
aParentElement->OwnerDoc());
InitStyleScopes(treeContext, aParentElement);
PseudoElementRuleProcessorData data(PresContext(), aParentElement,
&ruleWalker, aType, treeContext,
aPseudoElement);
WalkRestrictionRule(aType, &ruleWalker);
FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
aParentElement, &ruleWalker);
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nullptr;
if (treeContext.HaveRelevantLink()) {
treeContext.ResetForVisitedMatching();
ruleWalker.Reset();
WalkRestrictionRule(aType, &ruleWalker);
FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
aParentElement, &ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
// For pseudos, |data.IsLink()| being true means that
// our parent node is a link.
uint32_t flags = eNoFlags;
if (aType == CSSPseudoElementType::before ||
aType == CSSPseudoElementType::after) {
if (aAnimationFlag == eWithAnimation) {
flags |= eDoAnimation;
}
} else {
// Flex and grid containers don't expect to have any pseudo-element children
// aside from ::before and ::after. So if we have such a child, we're not
// actually in a flex/grid container, and we should skip flex/grid item
// style fixup.
flags |= eSkipParentDisplayBasedStyleFixup;
}
return GetContext(aParentContext, ruleNode, visitedRuleNode,
nsCSSPseudoElements::GetPseudoAtom(aType), aType,
aParentElement, flags);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolvePseudoElementStyle(Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
Element* aPseudoElement)
{
return ResolvePseudoElementStyleInternal(aParentElement,
aType,
aParentContext,
aPseudoElement,
eWithAnimation);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolvePseudoElementStyleWithoutAnimation(
Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
Element* aPseudoElement)
{
return ResolvePseudoElementStyleInternal(aParentElement,
aType,
aParentContext,
aPseudoElement,
eWithoutAnimation);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ProbePseudoElementStyle(Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext)
{
TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
aParentElement->OwnerDoc());
InitStyleScopes(treeContext, aParentElement);
return ProbePseudoElementStyle(aParentElement, aType, aParentContext,
treeContext);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ProbePseudoElementStyle(Element* aParentElement,
CSSPseudoElementType aType,
nsStyleContext* aParentContext,
TreeMatchContext& aTreeMatchContext,
Element* aPseudoElement)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
NS_ASSERTION(aType < CSSPseudoElementType::Count,
"must have pseudo element type");
NS_ASSERTION(aParentElement, "aParentElement must not be null");
nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
aTreeMatchContext.ResetForUnvisitedMatching();
PseudoElementRuleProcessorData data(PresContext(), aParentElement,
&ruleWalker, aType, aTreeMatchContext,
aPseudoElement);
WalkRestrictionRule(aType, &ruleWalker);
// not the root if there was a restriction rule
nsRuleNode *adjustedRoot = ruleWalker.CurrentNode();
FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
aParentElement, &ruleWalker);
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
if (ruleNode == adjustedRoot) {
return nullptr;
}
nsRuleNode *visitedRuleNode = nullptr;
if (aTreeMatchContext.HaveRelevantLink()) {
aTreeMatchContext.ResetForVisitedMatching();
ruleWalker.Reset();
WalkRestrictionRule(aType, &ruleWalker);
FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
aParentElement, &ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
// For pseudos, |data.IsLink()| being true means that
// our parent node is a link.
uint32_t flags = eNoFlags;
if (aType == CSSPseudoElementType::before ||
aType == CSSPseudoElementType::after) {
flags |= eDoAnimation;
} else {
// Flex and grid containers don't expect to have any pseudo-element children
// aside from ::before and ::after. So if we have such a child, we're not
// actually in a flex/grid container, and we should skip flex/grid item
// style fixup.
flags |= eSkipParentDisplayBasedStyleFixup;
}
RefPtr<nsStyleContext> result =
GetContext(aParentContext, ruleNode, visitedRuleNode,
pseudoTag, aType,
aParentElement, flags);
// For :before and :after pseudo-elements, having display: none or no
// 'content' property is equivalent to not having the pseudo-element
// at all.
if (result &&
(pseudoTag == nsCSSPseudoElements::before ||
pseudoTag == nsCSSPseudoElements::after)) {
const nsStyleDisplay *display = result->StyleDisplay();
const nsStyleContent *content = result->StyleContent();
// XXXldb What is contentCount for |content: ""|?
if (display->mDisplay == StyleDisplay::None ||
content->ContentCount() == 0) {
result = nullptr;
}
}
return result.forget();
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag,
nsStyleContext* aParentContext,
uint32_t aFlags)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
#ifdef DEBUG
bool isAnonBox = nsCSSAnonBoxes::IsAnonBox(aPseudoTag) &&
!nsCSSAnonBoxes::IsNonInheritingAnonBox(aPseudoTag)
#ifdef MOZ_XUL
&& !nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag)
#endif
;
NS_PRECONDITION(isAnonBox, "Unexpected pseudo");
#endif
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
AnonBoxRuleProcessorData data(PresContext(), aPseudoTag, &ruleWalker);
FileRules(EnumRulesMatching<AnonBoxRuleProcessorData>, &data, nullptr,
&ruleWalker);
if (aPseudoTag == nsCSSAnonBoxes::pageContent) {
// Add any @page rules that are specified.
nsTArray<nsCSSPageRule*> rules;
nsTArray<css::ImportantStyleData*> importantRules;
AppendPageRules(rules);
for (uint32_t i = 0, i_end = rules.Length(); i != i_end; ++i) {
css::Declaration* declaration = rules[i]->Declaration();
declaration->SetImmutable();
ruleWalker.Forward(declaration);
css::ImportantStyleData* importantRule =
declaration->GetImportantStyleData();
if (importantRule) {
importantRules.AppendElement(importantRule);
}
}
for (uint32_t i = 0, i_end = importantRules.Length(); i != i_end; ++i) {
ruleWalker.Forward(importantRules[i]);
}
}
return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr,
aPseudoTag, CSSPseudoElementType::InheritingAnonBox,
nullptr, aFlags);
}
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveNonInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
#ifdef DEBUG
bool isAnonBox = nsCSSAnonBoxes::IsAnonBox(aPseudoTag) &&
nsCSSAnonBoxes::IsNonInheritingAnonBox(aPseudoTag)
#ifdef MOZ_XUL
&& !nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag)
#endif
;
NS_PRECONDITION(isAnonBox, "Unexpected pseudo");
#endif
nsCSSAnonBoxes::NonInheriting type =
nsCSSAnonBoxes::NonInheritingTypeForPseudoTag(aPseudoTag);
RefPtr<nsStyleContext>& cache = mNonInheritingStyleContexts[type];
if (cache) {
RefPtr<nsStyleContext> retval = cache;
return retval.forget();
}
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
AnonBoxRuleProcessorData data(PresContext(), aPseudoTag, &ruleWalker);
FileRules(EnumRulesMatching<AnonBoxRuleProcessorData>, &data, nullptr,
&ruleWalker);
MOZ_ASSERT(aPseudoTag != nsCSSAnonBoxes::pageContent,
"If nsCSSAnonBoxes::pageContent ends up non-inheriting, move the "
"@page handling from ResolveInheritingAnonymousBoxStyle to "
"ResolveNonInheritingAnonymousBoxStyle");
RefPtr<nsStyleContext> retval =
GetContext(nullptr, ruleWalker.CurrentNode(), nullptr,
aPseudoTag, CSSPseudoElementType::NonInheritingAnonBox,
nullptr, eNoFlags);
cache = retval;
return retval.forget();
}
#ifdef MOZ_XUL
already_AddRefed<nsStyleContext>
nsStyleSet::ResolveXULTreePseudoStyle(Element* aParentElement,
nsIAtom* aPseudoTag,
nsStyleContext* aParentContext,
nsICSSPseudoComparator* aComparator)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
NS_ASSERTION(aPseudoTag, "must have pseudo tag");
NS_ASSERTION(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag),
"Unexpected pseudo");
nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
aParentElement->OwnerDoc());
InitStyleScopes(treeContext, aParentElement);
XULTreeRuleProcessorData data(PresContext(), aParentElement, &ruleWalker,
aPseudoTag, aComparator, treeContext);
FileRules(EnumRulesMatching<XULTreeRuleProcessorData>, &data, aParentElement,
&ruleWalker);
nsRuleNode *ruleNode = ruleWalker.CurrentNode();
nsRuleNode *visitedRuleNode = nullptr;
if (treeContext.HaveRelevantLink()) {
treeContext.ResetForVisitedMatching();
ruleWalker.Reset();
FileRules(EnumRulesMatching<XULTreeRuleProcessorData>, &data,
aParentElement, &ruleWalker);
visitedRuleNode = ruleWalker.CurrentNode();
}
return GetContext(aParentContext, ruleNode, visitedRuleNode,
// For pseudos, |data.IsLink()| being true means that
// our parent node is a link.
aPseudoTag, CSSPseudoElementType::XULTree,
nullptr, eNoFlags);
}
#endif
bool
nsStyleSet::AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray)
{
NS_ENSURE_FALSE(mInShutdown, false);
NS_ASSERTION(mBatching == 0, "rule processors out of date");
nsPresContext* presContext = PresContext();
for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
if (gCSSSheetTypes[i] == SheetType::ScopedDoc)
continue;
nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
(mRuleProcessors[gCSSSheetTypes[i]].get());
if (ruleProc && !ruleProc->AppendFontFaceRules(presContext, aArray))
return false;
}
return true;
}
nsCSSKeyframesRule*
nsStyleSet::KeyframesRuleForName(const nsString& aName)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
NS_ASSERTION(mBatching == 0, "rule processors out of date");
nsPresContext* presContext = PresContext();
for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) {
if (gCSSSheetTypes[i] == SheetType::ScopedDoc)
continue;
nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
(mRuleProcessors[gCSSSheetTypes[i]].get());
if (!ruleProc)
continue;
nsCSSKeyframesRule* result =
ruleProc->KeyframesRuleForName(presContext, aName);
if (result)
return result;
}
return nullptr;
}
nsCSSCounterStyleRule*
nsStyleSet::CounterStyleRuleForName(const nsAString& aName)
{
NS_ENSURE_FALSE(mInShutdown, nullptr);
NS_ASSERTION(mBatching == 0, "rule processors out of date");
nsPresContext* presContext = PresContext();
for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) {
if (gCSSSheetTypes[i] == SheetType::ScopedDoc)
continue;
nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
(mRuleProcessors[gCSSSheetTypes[i]].get());
if (!ruleProc)
continue;
nsCSSCounterStyleRule *result =
ruleProc->CounterStyleRuleForName(presContext, aName);
if (result)
return result;
}
return nullptr;
}
bool
nsStyleSet::AppendFontFeatureValuesRules(
nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
{
NS_ENSURE_FALSE(mInShutdown, false);
NS_ASSERTION(mBatching == 0, "rule processors out of date");
nsPresContext* presContext = PresContext();
for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
(mRuleProcessors[gCSSSheetTypes[i]].get());
if (ruleProc &&
!ruleProc->AppendFontFeatureValuesRules(presContext, aArray))
{
return false;
}
}
return true;
}
already_AddRefed<gfxFontFeatureValueSet>
nsStyleSet::GetFontFeatureValuesLookup()
{
if (mInitFontFeatureValuesLookup) {
mInitFontFeatureValuesLookup = false;
nsTArray<nsCSSFontFeatureValuesRule*> rules;
AppendFontFeatureValuesRules(rules);
mFontFeatureValuesLookup = new gfxFontFeatureValueSet();
uint32_t i, numRules = rules.Length();
for (i = 0; i < numRules; i++) {
nsCSSFontFeatureValuesRule *rule = rules[i];
const nsTArray<FontFamilyName>& familyList = rule->GetFamilyList().GetFontlist();
const nsTArray<gfxFontFeatureValueSet::FeatureValues>&
featureValues = rule->GetFeatureValues();
// for each family
size_t f, numFam;
numFam = familyList.Length();
for (f = 0; f < numFam; f++) {
mFontFeatureValuesLookup->AddFontFeatureValues(familyList[f].mName,
featureValues);
}
}
}
RefPtr<gfxFontFeatureValueSet> lookup = mFontFeatureValuesLookup;
return lookup.forget();
}
bool
nsStyleSet::AppendPageRules(nsTArray<nsCSSPageRule*>& aArray)
{
NS_ENSURE_FALSE(mInShutdown, false);
NS_ASSERTION(mBatching == 0, "rule processors out of date");
nsPresContext* presContext = PresContext();
for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
if (gCSSSheetTypes[i] == SheetType::ScopedDoc)
continue;
nsCSSRuleProcessor* ruleProc = static_cast<nsCSSRuleProcessor*>
(mRuleProcessors[gCSSSheetTypes[i]].get());
if (ruleProc && !ruleProc->AppendPageRules(presContext, aArray))
return false;
}
return true;
}
void
nsStyleSet::BeginShutdown()
{
mInShutdown = 1;
}
void
nsStyleSet::Shutdown()
{
// Make sure we drop our cached style contexts before the presshell arena
// starts going away.
ClearNonInheritingStyleContexts();
mRuleTree = nullptr;
GCRuleTrees();
MOZ_ASSERT(mUnusedRuleNodeList.isEmpty());
MOZ_ASSERT(mUnusedRuleNodeCount == 0);
}
void
nsStyleSet::GCRuleTrees()
{
MOZ_ASSERT(!mInReconstruct);
MOZ_ASSERT(!mInGC);
mInGC = true;
while (!mUnusedRuleNodeList.isEmpty()) {
nsRuleNode* node = mUnusedRuleNodeList.popFirst();
#ifdef DEBUG
if (node == mOldRootNode) {
// Flag that we've GCed the old root, if any.
mOldRootNode = nullptr;
}
#endif
node->Destroy();
}
#ifdef DEBUG
NS_ASSERTION(!mOldRootNode, "Should have GCed old root node");
mOldRootNode = nullptr;
#endif
mUnusedRuleNodeCount = 0;
mInGC = false;
}
already_AddRefed<nsStyleContext>
nsStyleSet::ReparentStyleContext(nsStyleContext* aStyleContext,
nsStyleContext* aNewParentContext,
Element* aElement)
{
MOZ_ASSERT(aStyleContext, "aStyleContext must not be null");
// This short-circuit is OK because we don't call TryInitatingTransition
// during style reresolution if the style context pointer hasn't changed.
if (aStyleContext->GetParent() == aNewParentContext) {
RefPtr<nsStyleContext> ret = aStyleContext;
return ret.forget();
}
nsIAtom* pseudoTag = aStyleContext->GetPseudo();
CSSPseudoElementType pseudoType = aStyleContext->GetPseudoType();
nsRuleNode* ruleNode = aStyleContext->RuleNode();
MOZ_ASSERT(PresContext()->RestyleManager()->IsGecko(),
"stylo: the style set and restyle manager must have the same "
"StyleBackendType");
NS_ASSERTION(!PresContext()->RestyleManager()->AsGecko()->SkipAnimationRules(),
"we no longer handle SkipAnimationRules()");
nsRuleNode* visitedRuleNode = nullptr;
nsStyleContext* visitedContext = aStyleContext->GetStyleIfVisited();
// Reparenting a style context just changes where we inherit from,
// not what rules we match or what our DOM looks like. In
// particular, it doesn't change whether this is a style context for
// a link.
if (visitedContext) {
visitedRuleNode = visitedContext->RuleNode();
}
uint32_t flags = eNoFlags;
if (aStyleContext->IsLinkContext()) {
flags |= eIsLink;
// GetContext handles propagating RelevantLinkVisited state from the
// parent in non-link cases; all we need to pass in is if this link
// is visited.
if (aStyleContext->RelevantLinkVisited()) {
flags |= eIsVisitedLink;
}
}
if (pseudoType == CSSPseudoElementType::NotPseudo ||
pseudoType == CSSPseudoElementType::before ||
pseudoType == CSSPseudoElementType::after) {
flags |= eDoAnimation;
}
if (aElement && aElement->IsRootOfAnonymousSubtree()) {
// For anonymous subtree roots, don't tweak "display" value based on whether
// or not the parent is styled as a flex/grid container. (If the parent
// has anonymous-subtree kids, then we know it's not actually going to get
// a flex/grid container frame, anyway.)
flags |= eSkipParentDisplayBasedStyleFixup;
}
return GetContext(aNewParentContext, ruleNode, visitedRuleNode,
pseudoTag, pseudoType,
aElement, flags);
}
struct MOZ_STACK_CLASS StatefulData : public StateRuleProcessorData {
StatefulData(nsPresContext* aPresContext, Element* aElement,
EventStates aStateMask, TreeMatchContext& aTreeMatchContext)
: StateRuleProcessorData(aPresContext, aElement, aStateMask,
aTreeMatchContext),
mHint(nsRestyleHint(0))
{}
nsRestyleHint mHint;
};
struct MOZ_STACK_CLASS StatefulPseudoElementData : public PseudoElementStateRuleProcessorData {
StatefulPseudoElementData(nsPresContext* aPresContext, Element* aElement,
EventStates aStateMask, CSSPseudoElementType aPseudoType,
TreeMatchContext& aTreeMatchContext, Element* aPseudoElement)
: PseudoElementStateRuleProcessorData(aPresContext, aElement, aStateMask,
aPseudoType, aTreeMatchContext,
aPseudoElement),
mHint(nsRestyleHint(0))
{}
nsRestyleHint mHint;
};
static bool SheetHasDocumentStateStyle(nsIStyleRuleProcessor* aProcessor,
void *aData)
{
StatefulData* data = (StatefulData*)aData;
if (aProcessor->HasDocumentStateDependentStyle(data)) {
data->mHint = eRestyle_Self;
return false; // don't continue
}
return true; // continue
}
// Test if style is dependent on a document state.
bool
nsStyleSet::HasDocumentStateDependentStyle(nsIContent* aContent,
EventStates aStateMask)
{
if (!aContent || !aContent->IsElement())
return false;
TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
aContent->OwnerDoc());
InitStyleScopes(treeContext, aContent->AsElement());
StatefulData data(PresContext(), aContent->AsElement(), aStateMask,
treeContext);
WalkRuleProcessors(SheetHasDocumentStateStyle, &data, true);
return data.mHint != 0;
}
static bool SheetHasStatefulStyle(nsIStyleRuleProcessor* aProcessor,
void *aData)
{
StatefulData* data = (StatefulData*)aData;
nsRestyleHint hint = aProcessor->HasStateDependentStyle(data);
data->mHint = nsRestyleHint(data->mHint | hint);
return true; // continue
}
static bool SheetHasStatefulPseudoElementStyle(nsIStyleRuleProcessor* aProcessor,
void *aData)
{
StatefulPseudoElementData* data = (StatefulPseudoElementData*)aData;
nsRestyleHint hint = aProcessor->HasStateDependentStyle(data);
data->mHint = nsRestyleHint(data->mHint | hint);
return true; // continue
}
// Test if style is dependent on content state
nsRestyleHint
nsStyleSet::HasStateDependentStyle(Element* aElement,
EventStates aStateMask)
{
TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
aElement->OwnerDoc());
InitStyleScopes(treeContext, aElement);
StatefulData data(PresContext(), aElement, aStateMask, treeContext);
WalkRuleProcessors(SheetHasStatefulStyle, &data, false);
return data.mHint;
}
nsRestyleHint
nsStyleSet::HasStateDependentStyle(Element* aElement,
CSSPseudoElementType aPseudoType,
Element* aPseudoElement,
EventStates aStateMask)
{
TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
aElement->OwnerDoc());
InitStyleScopes(treeContext, aElement);
StatefulPseudoElementData data(PresContext(), aElement, aStateMask,
aPseudoType, treeContext, aPseudoElement);
WalkRuleProcessors(SheetHasStatefulPseudoElementStyle, &data, false);
return data.mHint;
}
struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData {
AttributeData(nsPresContext* aPresContext, Element* aElement,
int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType,
bool aAttrHasChanged, const nsAttrValue* aOtherValue,
TreeMatchContext& aTreeMatchContext)
: AttributeRuleProcessorData(aPresContext, aElement, aNameSpaceID,
aAttribute, aModType, aAttrHasChanged,
aOtherValue, aTreeMatchContext),
mHint(nsRestyleHint(0))
{}
nsRestyleHint mHint;
RestyleHintData mHintData;
};
static bool
SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData)
{
AttributeData* data = (AttributeData*)aData;
nsRestyleHint hint =
aProcessor->HasAttributeDependentStyle(data, data->mHintData);
data->mHint = nsRestyleHint(data->mHint | hint);
return true; // continue
}
// Test if style is dependent on content state
nsRestyleHint
nsStyleSet::HasAttributeDependentStyle(Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
bool aAttrHasChanged,
const nsAttrValue* aOtherValue,
mozilla::RestyleHintData&
aRestyleHintDataResult)
{
TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
aElement->OwnerDoc());
InitStyleScopes(treeContext, aElement);
AttributeData data(PresContext(), aElement, aNameSpaceID, aAttribute,
aModType, aAttrHasChanged, aOtherValue, treeContext);
WalkRuleProcessors(SheetHasAttributeStyle, &data, false);
if (!(data.mHint & eRestyle_Subtree)) {
// No point keeping the list of selectors around if we are going to
// restyle the whole subtree unconditionally.
aRestyleHintDataResult = Move(data.mHintData);
}
return data.mHint;
}
bool
nsStyleSet::MediumFeaturesChanged()
{
NS_ASSERTION(mBatching == 0, "rule processors out of date");
// We can't use WalkRuleProcessors without a content node.
nsPresContext* presContext = PresContext();
bool stylesChanged = false;
for (nsIStyleRuleProcessor* processor : mRuleProcessors) {
if (!processor) {
continue;
}
bool thisChanged = processor->MediumFeaturesChanged(presContext);
stylesChanged = stylesChanged || thisChanged;
}
for (nsIStyleRuleProcessor* processor : mScopedDocSheetRuleProcessors) {
bool thisChanged = processor->MediumFeaturesChanged(presContext);
stylesChanged = stylesChanged || thisChanged;
}
if (mBindingManager) {
bool thisChanged = false;
mBindingManager->MediumFeaturesChanged(presContext, &thisChanged);
stylesChanged = stylesChanged || thisChanged;
}
return stylesChanged;
}
bool
nsStyleSet::EnsureUniqueInnerOnCSSSheets()
{
AutoTArray<CSSStyleSheet*, 32> queue;
for (SheetType type : gCSSSheetTypes) {
for (CSSStyleSheet* sheet : mSheets[type]) {
queue.AppendElement(sheet);
}
}
if (mBindingManager) {
AutoTArray<StyleSheet*, 32> sheets;
// XXXheycam stylo: AppendAllSheets will need to be able to return either
// CSSStyleSheets or ServoStyleSheets, on request (and then here requesting
// CSSStyleSheets).
mBindingManager->AppendAllSheets(sheets);
for (StyleSheet* sheet : sheets) {
MOZ_ASSERT(sheet->IsGecko(), "stylo: AppendAllSheets shouldn't give us "
"ServoStyleSheets yet");
queue.AppendElement(sheet->AsGecko());
}
}
while (!queue.IsEmpty()) {
uint32_t idx = queue.Length() - 1;
CSSStyleSheet* sheet = queue[idx];
queue.RemoveElementAt(idx);
sheet->EnsureUniqueInner();
// Enqueue all the sheet's children.
sheet->AppendAllChildSheets(queue);
}
bool res = mNeedsRestyleAfterEnsureUniqueInner;
mNeedsRestyleAfterEnsureUniqueInner = false;
return res;
}
nsIStyleRule*
nsStyleSet::InitialStyleRule()
{
if (!mInitialStyleRule) {
mInitialStyleRule = new nsInitialStyleRule;
}
return mInitialStyleRule;
}
bool
nsStyleSet::HasRuleProcessorUsedByMultipleStyleSets(SheetType aSheetType)
{
MOZ_ASSERT(size_t(aSheetType) < ArrayLength(mRuleProcessors));
if (!IsCSSSheetType(aSheetType) || !mRuleProcessors[aSheetType]) {
return false;
}
nsCSSRuleProcessor* rp =
static_cast<nsCSSRuleProcessor*>(mRuleProcessors[aSheetType].get());
return rp->IsUsedByMultipleStyleSets();
}
void
nsStyleSet::ClearSelectors()
{
// We might be called before we've done our first rule tree construction.
if (!mRuleTree) {
return;
}
MOZ_ASSERT(PresContext()->RestyleManager()->IsGecko(),
"stylo: the style set and restyle manager must have the same "
"StyleBackendType");
PresContext()->RestyleManager()->AsGecko()->ClearSelectors();
}
void
nsStyleSet::ClearNonInheritingStyleContexts()
{
for (RefPtr<nsStyleContext>& ptr : mNonInheritingStyleContexts) {
ptr = nullptr;
}
}