diff -r 000000000000 -r 6474c204b198 layout/style/nsStyleSet.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/style/nsStyleSet.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,2077 @@ +/* -*- 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 "mozilla/ArrayUtils.h" +#include "mozilla/EventStates.h" +#include "mozilla/MemoryReporting.h" + +#include "nsStyleSet.h" +#include "nsCSSStyleSheet.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 "nsTransitionManager.h" +#include "nsAnimationManager.h" +#include "nsStyleSheetService.h" +#include "mozilla/dom/Element.h" +#include "GeckoProfiler.h" +#include "nsHTMLCSSStyleSheet.h" +#include "nsHTMLStyleSheet.h" +#include "nsCSSRules.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS(nsEmptyStyleRule, nsIStyleRule) + +/* virtual */ void +nsEmptyStyleRule::MapRuleInfoInto(nsRuleData* aRuleData) +{ +} + +#ifdef DEBUG +/* virtual */ void +nsEmptyStyleRule::List(FILE* out, int32_t aIndent) const +{ + for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); + fputs("[empty style rule] {}\n", out); +} +#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_script_level) || + index == nsCSSProps::PropertyIndexInStruct( + eCSSProperty_script_size_multiplier) || + index == nsCSSProps::PropertyIndexInStruct( + eCSSProperty_script_min_size) || + index == nsCSSProps::PropertyIndexInStruct( + eCSSProperty_math_variant) || + index == nsCSSProps::PropertyIndexInStruct( + eCSSProperty_math_display)) { + continue; + } + } + if (value->GetUnit() == eCSSUnit_Null) { + value->SetInitialValue(); + } + } + } + } +} + +#ifdef DEBUG +/* virtual */ void +nsInitialStyleRule::List(FILE* out, int32_t aIndent) const +{ + for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); + fputs("[initial style rule] {}\n", out); +} +#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(); +} + +#ifdef DEBUG +/* virtual */ void +nsDisableTextZoomStyleRule::List(FILE* out, int32_t aIndent) const +{ + for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); + fputs("[disable text zoom style rule] {}\n", out); +} +#endif + +static const nsStyleSet::sheetType gCSSSheetTypes[] = { + // From lowest to highest in cascading order. + nsStyleSet::eAgentSheet, + nsStyleSet::eUserSheet, + nsStyleSet::eDocSheet, + nsStyleSet::eScopedDocSheet, + nsStyleSet::eOverrideSheet +}; + +nsStyleSet::nsStyleSet() + : mRuleTree(nullptr), + mBatching(0), + mInShutdown(false), + mAuthorStyleDisabled(false), + mInReconstruct(false), + mInitFontFeatureValuesLookup(true), + mDirty(0), + mUnusedRuleNodeCount(0) +{ +} + +size_t +nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + + for (int i = 0; i < eSheetTypeCount; i++) { + if (mRuleProcessors[i]) { + n += mRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf); + } + n += mSheets[i].SizeOfExcludingThis(nullptr, aMallocSizeOf); + } + + for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { + n += mScopedDocSheetRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf); + } + n += mScopedDocSheetRuleProcessors.SizeOfExcludingThis(aMallocSizeOf); + + n += mRoots.SizeOfExcludingThis(aMallocSizeOf); + n += mOldRuleTrees.SizeOfExcludingThis(aMallocSizeOf); + + return n; +} + +void +nsStyleSet::Init(nsPresContext *aPresContext) +{ + mFirstLineRule = new nsEmptyStyleRule; + mFirstLetterRule = new nsEmptyStyleRule; + mPlaceholderRule = new nsEmptyStyleRule; + mDisableTextZoomStyleRule = new nsDisableTextZoomStyleRule; + + mRuleTree = nsRuleNode::CreateRootNode(aPresContext); + + GatherRuleProcessors(eAnimationSheet); + GatherRuleProcessors(eTransitionSheet); +} + +nsresult +nsStyleSet::BeginReconstruct() +{ + NS_ASSERTION(!mInReconstruct, "Unmatched begin/end?"); + NS_ASSERTION(mRuleTree, "Reconstructing before first construction?"); + + // Create a new rule tree root + nsRuleNode* newTree = + nsRuleNode::CreateRootNode(mRuleTree->PresContext()); + if (!newTree) + return NS_ERROR_OUT_OF_MEMORY; + + // Save the old rule tree so we can destroy it later + if (!mOldRuleTrees.AppendElement(mRuleTree)) { + newTree->Destroy(); + return NS_ERROR_OUT_OF_MEMORY; + } + + // We need to keep mRoots so that the rule tree GC will only free the + // rule trees that really aren't referenced anymore (which should be + // all of them, if there are no bugs in reresolution code). + + mInReconstruct = true; + mRuleTree = newTree; + + return NS_OK; +} + +void +nsStyleSet::EndReconstruct() +{ + NS_ASSERTION(mInReconstruct, "Unmatched begin/end?"); + mInReconstruct = false; +#ifdef DEBUG + for (int32_t i = mRoots.Length() - 1; i >= 0; --i) { + nsRuleNode *n = mRoots[i]->RuleNode(); + while (n->GetParent()) { + n = n->GetParent(); + } + // Since nsStyleContext's mParent and mRuleNode are immutable, and + // style contexts own their parents, and nsStyleContext asserts in + // its constructor that the style context and its parent are in the + // same rule tree, we don't need to check any of the children of + // mRoots; we only need to check the rule nodes of mRoots + // themselves. + + NS_ASSERTION(n == mRuleTree, "style context has old rule node"); + } +#endif + // This *should* destroy the only element of mOldRuleTrees, but in + // case of some bugs (which would trigger the above assertions), it + // won't. + GCRuleTrees(); +} + +void +nsStyleSet::SetQuirkStyleSheet(nsIStyleSheet* aQuirkStyleSheet) +{ + NS_ASSERTION(aQuirkStyleSheet, "Must have quirk sheet if this is called"); + NS_ASSERTION(!mQuirkStyleSheet, "Multiple calls to SetQuirkStyleSheet?"); + NS_ASSERTION(mSheets[eAgentSheet].IndexOf(aQuirkStyleSheet) != -1, + "Quirk style sheet not one of our agent sheets?"); + mQuirkStyleSheet = aQuirkStyleSheet; +} + +typedef nsDataHashtable, 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 +{ + nsCSSStyleSheet* 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& aSheets) +{ + uint32_t n = aSheets.Length(); + if (n == 1) { + return; + } + + ScopeDepthCache cache; + + nsTArray 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) +{ + mRuleProcessors[aType] = nullptr; + if (aType == eScopedDocSheet) { + for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { + nsIStyleRuleProcessor* processor = mScopedDocSheetRuleProcessors[i].get(); + Element* scope = + static_cast(processor)->GetScopeElement(); + scope->ClearIsScopedStyleRoot(); + } + mScopedDocSheetRuleProcessors.Clear(); + } + if (mAuthorStyleDisabled && (aType == eDocSheet || + aType == eScopedDocSheet || + aType == eStyleAttrSheet)) { + // 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) { + // handle the types for which have a rule processor that does not + // implement the style sheet interface. + case eAnimationSheet: + MOZ_ASSERT(mSheets[aType].Count() == 0); + mRuleProcessors[aType] = PresContext()->AnimationManager(); + return NS_OK; + case eTransitionSheet: + MOZ_ASSERT(mSheets[aType].Count() == 0); + mRuleProcessors[aType] = PresContext()->TransitionManager(); + return NS_OK; + case eStyleAttrSheet: + MOZ_ASSERT(mSheets[aType].Count() == 0); + mRuleProcessors[aType] = PresContext()->Document()->GetInlineStyleSheet(); + return NS_OK; + case ePresHintSheet: + MOZ_ASSERT(mSheets[aType].Count() == 0); + mRuleProcessors[aType] = PresContext()->Document()->GetAttributeStyleSheet(); + return NS_OK; + default: + // keep going + break; + } + if (aType == eScopedDocSheet) { + // Create a rule processor for each scope. + uint32_t count = mSheets[eScopedDocSheet].Count(); + if (count) { + // Gather the scoped style sheets into an array as + // nsCSSStyleSheets, and mark all of their scope elements + // as scoped style roots. + nsTArray sheets(count); + for (uint32_t i = 0; i < count; i++) { + nsRefPtr sheet = + do_QueryObject(mSheets[eScopedDocSheet].ObjectAt(i)); + 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); + + 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< nsRefPtr > sheetsForScope; + sheetsForScope.AppendElements(sheets.Elements() + start, end - start); + mScopedDocSheetRuleProcessors.AppendElement + (new nsCSSRuleProcessor(sheetsForScope, uint8_t(aType), scope)); + + start = end; + } while (start < count); + } + return NS_OK; + } + if (mSheets[aType].Count()) { + switch (aType) { + case eAgentSheet: + case eUserSheet: + case eDocSheet: + case eOverrideSheet: { + // levels containing CSS stylesheets (apart from eScopedDocSheet) + nsCOMArray& sheets = mSheets[aType]; + nsTArray > cssSheets(sheets.Count()); + for (int32_t i = 0, i_end = sheets.Count(); i < i_end; ++i) { + nsRefPtr cssSheet = do_QueryObject(sheets[i]); + NS_ASSERTION(cssSheet, "not a CSS sheet"); + cssSheets.AppendElement(cssSheet); + } + mRuleProcessors[aType] = + new nsCSSRuleProcessor(cssSheets, uint8_t(aType), nullptr); + } break; + + default: + // levels containing non-CSS stylesheets + NS_ASSERTION(mSheets[aType].Count() == 1, "only one sheet per level"); + mRuleProcessors[aType] = do_QueryInterface(mSheets[aType][0]); + break; + } + } + + return NS_OK; +} + +static bool +IsScopedStyleSheet(nsIStyleSheet* aSheet) +{ + nsRefPtr cssSheet = do_QueryObject(aSheet); + NS_ASSERTION(cssSheet, "expected aSheet to be an nsCSSStyleSheet"); + + return cssSheet->GetScopeElement(); +} + +nsresult +nsStyleSet::AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet) +{ + NS_PRECONDITION(aSheet, "null arg"); + NS_ASSERTION(aSheet->IsApplicable(), + "Inapplicable sheet being placed in style set"); + mSheets[aType].RemoveObject(aSheet); + if (!mSheets[aType].AppendObject(aSheet)) + return NS_ERROR_OUT_OF_MEMORY; + + return DirtyRuleProcessors(aType); +} + +nsresult +nsStyleSet::PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet) +{ + NS_PRECONDITION(aSheet, "null arg"); + NS_ASSERTION(aSheet->IsApplicable(), + "Inapplicable sheet being placed in style set"); + mSheets[aType].RemoveObject(aSheet); + if (!mSheets[aType].InsertObjectAt(aSheet, 0)) + return NS_ERROR_OUT_OF_MEMORY; + + return DirtyRuleProcessors(aType); +} + +nsresult +nsStyleSet::RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet) +{ + NS_PRECONDITION(aSheet, "null arg"); + NS_ASSERTION(aSheet->IsComplete(), + "Incomplete sheet being removed from style set"); + mSheets[aType].RemoveObject(aSheet); + + return DirtyRuleProcessors(aType); +} + +nsresult +nsStyleSet::ReplaceSheets(sheetType aType, + const nsCOMArray &aNewSheets) +{ + mSheets[aType].Clear(); + if (!mSheets[aType].AppendObjects(aNewSheets)) + return NS_ERROR_OUT_OF_MEMORY; + + return DirtyRuleProcessors(aType); +} + +nsresult +nsStyleSet::InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet, + nsIStyleSheet *aReferenceSheet) +{ + NS_PRECONDITION(aNewSheet && aReferenceSheet, "null arg"); + NS_ASSERTION(aNewSheet->IsApplicable(), + "Inapplicable sheet being placed in style set"); + + mSheets[aType].RemoveObject(aNewSheet); + int32_t idx = mSheets[aType].IndexOf(aReferenceSheet); + if (idx < 0) + return NS_ERROR_INVALID_ARG; + + if (!mSheets[aType].InsertObjectAt(aNewSheet, idx)) + return NS_ERROR_OUT_OF_MEMORY; + + return DirtyRuleProcessors(aType); +} + +nsresult +nsStyleSet::DirtyRuleProcessors(sheetType aType) +{ + if (!mBatching) + return GatherRuleProcessors(aType); + + mDirty |= 1 << aType; + return NS_OK; +} + +bool +nsStyleSet::GetAuthorStyleDisabled() +{ + return mAuthorStyleDisabled; +} + +nsresult +nsStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled) +{ + if (aStyleDisabled == !mAuthorStyleDisabled) { + mAuthorStyleDisabled = aStyleDisabled; + BeginUpdate(); + mDirty |= 1 << eDocSheet | + 1 << eScopedDocSheet | + 1 << eStyleAttrSheet; + return EndUpdate(); + } + return NS_OK; +} + +// -------- Doc Sheets + +nsresult +nsStyleSet::AddDocStyleSheet(nsIStyleSheet* aSheet, nsIDocument* aDocument) +{ + NS_PRECONDITION(aSheet && aDocument, "null arg"); + NS_ASSERTION(aSheet->IsApplicable(), + "Inapplicable sheet being placed in style set"); + + sheetType type = IsScopedStyleSheet(aSheet) ? + eScopedDocSheet : + eDocSheet; + nsCOMArray& sheets = mSheets[type]; + + sheets.RemoveObject(aSheet); + nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); + + // lowest index first + int32_t newDocIndex = aDocument->GetIndexOfStyleSheet(aSheet); + + int32_t count = sheets.Count(); + int32_t index; + for (index = 0; index < count; index++) { + nsIStyleSheet* sheet = sheets.ObjectAt(index); + int32_t sheetDocIndex = aDocument->GetIndexOfStyleSheet(sheet); + if (sheetDocIndex > newDocIndex) + break; + + // If the sheet is not owned by the document it can be an author + // sheet registered at nsStyleSheetService or an additional author + // sheet on the document, which means the new + // doc sheet should end up before it. + if (sheetDocIndex < 0 && + ((sheetService && + sheetService->AuthorStyleSheets()->IndexOf(sheet) >= 0) || + sheet == aDocument->FirstAdditionalAuthorSheet())) + break; + } + if (!sheets.InsertObjectAt(aSheet, index)) + return NS_ERROR_OUT_OF_MEMORY; + + return DirtyRuleProcessors(type); +} + +nsresult +nsStyleSet::RemoveDocStyleSheet(nsIStyleSheet *aSheet) +{ + nsRefPtr cssSheet = do_QueryObject(aSheet); + bool isScoped = cssSheet && cssSheet->GetScopeElement(); + return RemoveStyleSheet(isScoped ? eScopedDocSheet : eDocSheet, 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 (int i = 0; i < eSheetTypeCount; ++i) { + if (mDirty & (1 << i)) { + nsresult rv = GatherRuleProcessors(sheetType(i)); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + mDirty = 0; + return NS_OK; +} + +void +nsStyleSet::EnableQuirkStyleSheet(bool aEnable) +{ +#ifdef DEBUG + bool oldEnabled; + { + nsCOMPtr domSheet = + do_QueryInterface(mQuirkStyleSheet); + domSheet->GetDisabled(&oldEnabled); + oldEnabled = !oldEnabled; + } +#endif + mQuirkStyleSheet->SetEnabled(aEnable); +#ifdef DEBUG + // This should always be OK, since SetEnabled should call + // ClearRuleCascades. + // Note that we can hit this codepath multiple times when document.open() + // (potentially implied) happens multiple times. + if (mRuleProcessors[eAgentSheet] && aEnable != oldEnabled) { + static_cast(static_cast( + mRuleProcessors[eAgentSheet]))->AssertQuirksChangeOK(); + } +#endif +} + +template +static bool +EnumRulesMatching(nsIStyleRuleProcessor* aProcessor, void* aData) +{ + T* data = static_cast(aData); + aProcessor->RulesMatching(data); + return true; +} + +static inline bool +IsMoreSpecificThanAnimation(nsRuleNode *aRuleNode) +{ + return !aRuleNode->IsRoot() && + (aRuleNode->GetLevel() == nsStyleSet::eTransitionSheet || + aRuleNode->IsImportantRule()); +} + +static nsIStyleRule* +GetAnimationRule(nsRuleNode *aRuleNode) +{ + nsRuleNode *n = aRuleNode; + while (IsMoreSpecificThanAnimation(n)) { + n = n->GetParent(); + } + + if (n->IsRoot() || n->GetLevel() != nsStyleSet::eAnimationSheet) { + return nullptr; + } + + return n->GetRule(); +} + +static nsRuleNode* +ReplaceAnimationRule(nsRuleNode *aOldRuleNode, + nsIStyleRule *aOldAnimRule, + nsIStyleRule *aNewAnimRule) +{ + nsTArray moreSpecificNodes; + + nsRuleNode *n = aOldRuleNode; + while (IsMoreSpecificThanAnimation(n)) { + moreSpecificNodes.AppendElement(n); + n = n->GetParent(); + } + + if (aOldAnimRule) { + NS_ABORT_IF_FALSE(n->GetRule() == aOldAnimRule, "wrong rule"); + NS_ABORT_IF_FALSE(n->GetLevel() == nsStyleSet::eAnimationSheet, + "wrong level"); + n = n->GetParent(); + } + + NS_ABORT_IF_FALSE(!IsMoreSpecificThanAnimation(n) && + (n->IsRoot() || + n->GetLevel() != nsStyleSet::eAnimationSheet), + "wrong level"); + + if (aNewAnimRule) { + n = n->Transition(aNewAnimRule, nsStyleSet::eAnimationSheet, false); + } + + 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 +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, + nsCSSPseudoElements::Type aPseudoType, + Element* aElementForAnimation, + uint32_t aFlags) +{ + NS_PRECONDITION((!aPseudoTag && + aPseudoType == + nsCSSPseudoElements::ePseudo_NotPseudoElement) || + (aPseudoTag && + nsCSSPseudoElements::GetPseudoType(aPseudoTag) == + 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; + } + + nsRefPtr result; + if (aParentContext) + result = aParentContext->FindChildWithRules(aPseudoTag, aRuleNode, + aVisitedRuleNode, + aFlags & eIsVisitedLink); + +#ifdef NOISY_DEBUG + if (result) + fprintf(stdout, "--- SharedSC %d ---\n", ++gSharedCount); + else + fprintf(stdout, "+++ NewSC %d +++\n", ++gNewCount); +#endif + + if (!result) { + result = NS_NewStyleContext(aParentContext, aPseudoTag, aPseudoType, + aRuleNode, aFlags & eSkipFlexItemStyleFixup); + if (aVisitedRuleNode) { + nsRefPtr resultIfVisited = + NS_NewStyleContext(parentIfVisited, aPseudoTag, aPseudoType, + aVisitedRuleNode, + aFlags & eSkipFlexItemStyleFixup); + if (!parentIfVisited) { + mRoots.AppendElement(resultIfVisited); + } + resultIfVisited->SetIsStyleIfVisited(); + result->SetStyleIfVisited(resultIfVisited.forget()); + + bool relevantLinkVisited = (aFlags & eIsLink) ? + (aFlags & eIsVisitedLink) : + (aParentContext && aParentContext->RelevantLinkVisited()); + + if (relevantLinkVisited) { + result->AddStyleBit(NS_STYLE_RELEVANT_LINK_VISITED); + } + } + if (!aParentContext) { + mRoots.AppendElement(result); + } + } + else { + NS_ASSERTION(result->GetPseudoType() == aPseudoType, "Unexpected type"); + NS_ASSERTION(result->GetPseudo() == aPseudoTag, "Unexpected pseudo"); + } + + if (aFlags & eDoAnimation) { + // Normally the animation manager has already added the correct + // style rule. However, if the animation-name just changed, it + // might have been wrong. So ask it to double-check based on the + // resulting style context. + nsIStyleRule *oldAnimRule = GetAnimationRule(aRuleNode); + nsIStyleRule *animRule = PresContext()->AnimationManager()-> + CheckAnimationRule(result, aElementForAnimation); + NS_ABORT_IF_FALSE(result->RuleNode() == aRuleNode, + "unexpected rule node"); + NS_ABORT_IF_FALSE(!result->GetStyleIfVisited() == !aVisitedRuleNode, + "unexpected visited rule node"); + NS_ABORT_IF_FALSE(!aVisitedRuleNode || + result->GetStyleIfVisited()->RuleNode() == + aVisitedRuleNode, + "unexpected visited rule node"); + NS_ABORT_IF_FALSE(!aVisitedRuleNode || + oldAnimRule == GetAnimationRule(aVisitedRuleNode), + "animation rule mismatch between rule nodes"); + if (oldAnimRule != animRule) { + nsRuleNode *ruleNode = + ReplaceAnimationRule(aRuleNode, oldAnimRule, animRule); + nsRuleNode *visitedRuleNode = aVisitedRuleNode + ? ReplaceAnimationRule(aVisitedRuleNode, oldAnimRule, animRule) + : nullptr; + NS_ABORT_IF_FALSE(!visitedRuleNode || + GetAnimationRule(ruleNode) == + GetAnimationRule(visitedRuleNode), + "animation rule mismatch between rule nodes"); + result = GetContext(aParentContext, ruleNode, visitedRuleNode, + aPseudoTag, aPseudoType, nullptr, + aFlags & ~eDoAnimation); + } + } + + if (aElementForAnimation && aElementForAnimation->IsHTML(nsGkAtoms::body) && + aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement && + PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) { + nsIDocument* doc = aElementForAnimation->GetCurrentDoc(); + 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?"); + + nsAutoTArray 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(nsRefPtr(do_QueryObject(node->GetRule())), + "Unexpected non-CSS rule"); + + nsIStyleRule* impRule = + static_cast(node->GetRule())->GetImportantRule(); + 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()) { + nsRefPtr rule(do_QueryObject(node->GetRule())); + NS_ASSERTION(rule, "Unexpected non-CSS rule"); + + NS_ASSERTION(!rule->GetImportantRule(), "Unexpected important rule"); + } +} + +void +nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode, + nsRuleNode* aLastPrevLevelNode) +{ + if (!aCurrLevelNode) + return; + + for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode; + node = node->GetParent()) { + nsIStyleRule *rule = node->GetRule(); + nsRefPtr cssRule(do_QueryObject(rule)); + 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"); + + // 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(eAgentSheet, false, true); + if (mRuleProcessors[eAgentSheet]) + (*aCollectorFunc)(mRuleProcessors[eAgentSheet], aData); + nsRuleNode* lastAgentRN = aRuleWalker->CurrentNode(); + bool haveImportantUARules = !aRuleWalker->GetCheckForImportantRules(); + + aRuleWalker->SetLevel(eUserSheet, false, true); + bool skipUserStyles = + aElement && aElement->IsInNativeAnonymousSubtree(); + if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different + (*aCollectorFunc)(mRuleProcessors[eUserSheet], aData); + nsRuleNode* lastUserRN = aRuleWalker->CurrentNode(); + bool haveImportantUserRules = !aRuleWalker->GetCheckForImportantRules(); + + aRuleWalker->SetLevel(ePresHintSheet, false, false); + if (mRuleProcessors[ePresHintSheet]) + (*aCollectorFunc)(mRuleProcessors[ePresHintSheet], aData); + nsRuleNode* lastPresHintRN = aRuleWalker->CurrentNode(); + + aRuleWalker->SetLevel(eDocSheet, false, true); + bool cutOffInheritance = false; + if (mBindingManager && aElement) { + // We can supply additional document-level sheets that should be walked. + mBindingManager->WalkRules(aCollectorFunc, + static_cast(aData), + &cutOffInheritance); + } + if (!skipUserStyles && !cutOffInheritance && // NOTE: different + mRuleProcessors[eDocSheet]) + (*aCollectorFunc)(mRuleProcessors[eDocSheet], aData); + nsRuleNode* lastDocRN = aRuleWalker->CurrentNode(); + bool haveImportantDocRules = !aRuleWalker->GetCheckForImportantRules(); + nsTArray lastScopedRNs; + nsTArray 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(eScopedDocSheet, false, true); + nsCSSRuleProcessor* processor = + static_cast(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(eStyleAttrSheet, false, true); + if (mRuleProcessors[eStyleAttrSheet]) + (*aCollectorFunc)(mRuleProcessors[eStyleAttrSheet], aData); + nsRuleNode* lastStyleAttrRN = aRuleWalker->CurrentNode(); + bool haveImportantStyleAttrRules = !aRuleWalker->GetCheckForImportantRules(); + + aRuleWalker->SetLevel(eOverrideSheet, false, true); + if (mRuleProcessors[eOverrideSheet]) + (*aCollectorFunc)(mRuleProcessors[eOverrideSheet], aData); + nsRuleNode* lastOvrRN = aRuleWalker->CurrentNode(); + bool haveImportantOverrideRules = !aRuleWalker->GetCheckForImportantRules(); + + // This needs to match IsMoreSpecificThanAnimation() above. + aRuleWalker->SetLevel(eAnimationSheet, false, false); + (*aCollectorFunc)(mRuleProcessors[eAnimationSheet], aData); + + if (haveAnyImportantScopedRules) { + for (uint32_t i = lastScopedRNs.Length(); i-- != 0; ) { + aRuleWalker->SetLevel(eScopedDocSheet, 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(eDocSheet, true, false); + AddImportantRules(lastDocRN, lastPresHintRN, aRuleWalker); // doc + } +#ifdef DEBUG + else { + AssertNoImportantRules(lastDocRN, lastPresHintRN); + } +#endif + + if (haveImportantStyleAttrRules) { + aRuleWalker->SetLevel(eStyleAttrSheet, true, false); + AddImportantRules(lastStyleAttrRN, lastScopedRN, aRuleWalker); // style attr + } +#ifdef DEBUG + else { + AssertNoImportantRules(lastStyleAttrRN, lastScopedRN); + } +#endif + + if (haveImportantOverrideRules) { + aRuleWalker->SetLevel(eOverrideSheet, true, false); + AddImportantRules(lastOvrRN, lastStyleAttrRN, aRuleWalker); // override + } +#ifdef DEBUG + else { + AssertNoImportantRules(lastOvrRN, lastStyleAttrRN); + } +#endif + +#ifdef DEBUG + AssertNoCSSRules(lastPresHintRN, lastUserRN); +#endif + + if (haveImportantUserRules) { + aRuleWalker->SetLevel(eUserSheet, true, false); + AddImportantRules(lastUserRN, lastAgentRN, aRuleWalker); //user + } +#ifdef DEBUG + else { + AssertNoImportantRules(lastUserRN, lastAgentRN); + } +#endif + + if (haveImportantUARules) { + aRuleWalker->SetLevel(eAgentSheet, 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(eTransitionSheet, false, false); + (*aCollectorFunc)(mRuleProcessors[eTransitionSheet], 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) +{ + if (mRuleProcessors[eAgentSheet]) + (*aFunc)(mRuleProcessors[eAgentSheet], aData); + + bool skipUserStyles = aData->mElement->IsInNativeAnonymousSubtree(); + if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different + (*aFunc)(mRuleProcessors[eUserSheet], aData); + + if (mRuleProcessors[ePresHintSheet]) + (*aFunc)(mRuleProcessors[ePresHintSheet], 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[eDocSheet]) // NOTE: different + (*aFunc)(mRuleProcessors[eDocSheet], aData); + if (aData->mElement->IsElementInStyleScope()) { + for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) + (*aFunc)(mScopedDocSheetRuleProcessors[i], aData); + } + } + if (mRuleProcessors[eStyleAttrSheet]) + (*aFunc)(mRuleProcessors[eStyleAttrSheet], aData); + if (mRuleProcessors[eOverrideSheet]) + (*aFunc)(mRuleProcessors[eOverrideSheet], aData); + (*aFunc)(mRuleProcessors[eAnimationSheet], aData); + (*aFunc)(mRuleProcessors[eTransitionSheet], aData); +} + +static void +InitStyleScopes(TreeMatchContext& aTreeContext, Element* aElement) +{ + if (aElement->IsElementInStyleScope()) { + aTreeContext.InitStyleScopes(aElement->GetParentElement()); + } +} + +already_AddRefed +nsStyleSet::ResolveStyleFor(Element* aElement, + nsStyleContext* aParentContext) +{ + TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, + aElement->OwnerDoc()); + InitStyleScopes(treeContext, aElement); + return ResolveStyleFor(aElement, aParentContext, treeContext); +} + +already_AddRefed +nsStyleSet::ResolveStyleFor(Element* aElement, + nsStyleContext* aParentContext, + TreeMatchContext& aTreeMatchContext) +{ + 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, &data, aElement, + &ruleWalker); + + nsRuleNode *ruleNode = ruleWalker.CurrentNode(); + nsRuleNode *visitedRuleNode = nullptr; + + if (aTreeMatchContext.HaveRelevantLink()) { + aTreeMatchContext.ResetForVisitedMatching(); + ruleWalker.Reset(); + FileRules(EnumRulesMatching, &data, aElement, + &ruleWalker); + visitedRuleNode = ruleWalker.CurrentNode(); + } + + uint32_t flags = eDoAnimation; + if (nsCSSRuleProcessor::IsLink(aElement)) { + flags |= eIsLink; + } + if (nsCSSRuleProcessor::GetContentState(aElement, aTreeMatchContext). + HasState(NS_EVENT_STATE_VISITED)) { + flags |= eIsVisitedLink; + } + if (aTreeMatchContext.mSkippingFlexItemStyleFixup) { + flags |= eSkipFlexItemStyleFixup; + } + + return GetContext(aParentContext, ruleNode, visitedRuleNode, + nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement, + aElement, flags); +} + +already_AddRefed +nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext, + const nsTArray< nsCOMPtr > &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(eDocSheet, false, false); + for (uint32_t i = 0; i < aRules.Length(); i++) { + ruleWalker.ForwardOnPossiblyCSSRule(aRules.ElementAt(i)); + } + + return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr, + nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement, + nullptr, eNoFlags); +} + +already_AddRefed +nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext, + nsStyleContext* aOldStyle, + const nsTArray& aRules) +{ + nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); + for (int32_t i = aRules.Length() - 1; i >= 0; --i) { + ruleWalker.SetLevel(aRules[i].mLevel, false, false); + ruleWalker.ForwardOnPossiblyCSSRule(aRules[i].mRule); + } + + uint32_t flags = eNoFlags; + if (aOldStyle->IsLinkContext()) { + flags |= eIsLink; + } + if (aOldStyle->RelevantLinkVisited()) { + flags |= eIsVisitedLink; + } + + return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr, + nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement, + nullptr, flags); +} + +already_AddRefed +nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext, + const nsCOMArray &aRules) +{ + NS_ENSURE_FALSE(mInShutdown, nullptr); + + nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); + ruleWalker.SetCurrentNode(aBaseContext->RuleNode()); + // FIXME: Perhaps this should be passed in, but it probably doesn't + // matter. + ruleWalker.SetLevel(eDocSheet, 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; + } + if (aBaseContext->RelevantLinkVisited()) { + flags |= eIsVisitedLink; + } + return GetContext(aBaseContext->GetParent(), ruleNode, visitedRuleNode, + aBaseContext->GetPseudo(), + aBaseContext->GetPseudoType(), + nullptr, flags); +} + +already_AddRefed +nsStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext) +{ + return GetContext(aParentContext, mRuleTree, nullptr, + nsCSSAnonBoxes::mozNonElement, + nsCSSPseudoElements::ePseudo_AnonBox, nullptr, + eNoFlags); +} + +void +nsStyleSet::WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType, + nsRuleWalker* aRuleWalker) +{ + // This needs to match GetPseudoRestriction in nsRuleNode.cpp. + aRuleWalker->SetLevel(eAgentSheet, false, false); + if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter) + aRuleWalker->Forward(mFirstLetterRule); + else if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLine) + aRuleWalker->Forward(mFirstLineRule); + else if (aPseudoType == nsCSSPseudoElements::ePseudo_mozPlaceholder) + aRuleWalker->Forward(mPlaceholderRule); +} + +void +nsStyleSet::WalkDisableTextZoomRule(Element* aElement, nsRuleWalker* aRuleWalker) +{ + aRuleWalker->SetLevel(eAgentSheet, false, false); + if (aElement->IsSVG(nsGkAtoms::text)) + aRuleWalker->Forward(mDisableTextZoomStyleRule); +} + +already_AddRefed +nsStyleSet::ResolvePseudoElementStyle(Element* aParentElement, + nsCSSPseudoElements::Type aType, + nsStyleContext* aParentContext, + Element* aPseudoElement) +{ + NS_ENSURE_FALSE(mInShutdown, nullptr); + + NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount, + "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, &data, + aParentElement, &ruleWalker); + + nsRuleNode *ruleNode = ruleWalker.CurrentNode(); + nsRuleNode *visitedRuleNode = nullptr; + + if (treeContext.HaveRelevantLink()) { + treeContext.ResetForVisitedMatching(); + ruleWalker.Reset(); + WalkRestrictionRule(aType, &ruleWalker); + FileRules(EnumRulesMatching, &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 == nsCSSPseudoElements::ePseudo_before || + aType == nsCSSPseudoElements::ePseudo_after) { + flags |= eDoAnimation; + } else { + // Flex 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 container, and we should skip flex-item style fixup. + flags |= eSkipFlexItemStyleFixup; + } + + return GetContext(aParentContext, ruleNode, visitedRuleNode, + nsCSSPseudoElements::GetPseudoAtom(aType), aType, + aParentElement, flags); +} + +already_AddRefed +nsStyleSet::ProbePseudoElementStyle(Element* aParentElement, + nsCSSPseudoElements::Type aType, + nsStyleContext* aParentContext) +{ + TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, + aParentElement->OwnerDoc()); + InitStyleScopes(treeContext, aParentElement); + return ProbePseudoElementStyle(aParentElement, aType, aParentContext, + treeContext); +} + +already_AddRefed +nsStyleSet::ProbePseudoElementStyle(Element* aParentElement, + nsCSSPseudoElements::Type aType, + nsStyleContext* aParentContext, + TreeMatchContext& aTreeMatchContext, + Element* aPseudoElement) +{ + NS_ENSURE_FALSE(mInShutdown, nullptr); + + NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount, + "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, &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, &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 == nsCSSPseudoElements::ePseudo_before || + aType == nsCSSPseudoElements::ePseudo_after) { + flags |= eDoAnimation; + } else { + // Flex 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 container, and we should skip flex-item style fixup. + flags |= eSkipFlexItemStyleFixup; + } + + nsRefPtr 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 == NS_STYLE_DISPLAY_NONE || + content->ContentCount() == 0) { + result = nullptr; + } + } + + return result.forget(); +} + +already_AddRefed +nsStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, + nsStyleContext* aParentContext) +{ + NS_ENSURE_FALSE(mInShutdown, nullptr); + +#ifdef DEBUG + bool isAnonBox = nsCSSAnonBoxes::IsAnonBox(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, &data, nullptr, + &ruleWalker); + + if (aPseudoTag == nsCSSAnonBoxes::pageContent) { + // Add any @page rules that are specified. + nsTArray rules; + nsTArray importantRules; + nsPresContext* presContext = PresContext(); + presContext->StyleSet()->AppendPageRules(presContext, rules); + for (uint32_t i = 0, i_end = rules.Length(); i != i_end; ++i) { + ruleWalker.Forward(rules[i]); + css::ImportantRule* importantRule = rules[i]->GetImportantRule(); + 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, nsCSSPseudoElements::ePseudo_AnonBox, + nullptr, eNoFlags); +} + +#ifdef MOZ_XUL +already_AddRefed +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, &data, aParentElement, + &ruleWalker); + + nsRuleNode *ruleNode = ruleWalker.CurrentNode(); + nsRuleNode *visitedRuleNode = nullptr; + + if (treeContext.HaveRelevantLink()) { + treeContext.ResetForVisitedMatching(); + ruleWalker.Reset(); + FileRules(EnumRulesMatching, &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, nsCSSPseudoElements::ePseudo_XULTree, + nullptr, eNoFlags); +} +#endif + +bool +nsStyleSet::AppendFontFaceRules(nsPresContext* aPresContext, + nsTArray& aArray) +{ + NS_ENSURE_FALSE(mInShutdown, false); + + for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { + if (gCSSSheetTypes[i] == eScopedDocSheet) + continue; + nsCSSRuleProcessor *ruleProc = static_cast + (mRuleProcessors[gCSSSheetTypes[i]].get()); + if (ruleProc && !ruleProc->AppendFontFaceRules(aPresContext, aArray)) + return false; + } + return true; +} + +nsCSSKeyframesRule* +nsStyleSet::KeyframesRuleForName(nsPresContext* aPresContext, + const nsString& aName) +{ + NS_ENSURE_FALSE(mInShutdown, nullptr); + + for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) { + if (gCSSSheetTypes[i] == eScopedDocSheet) + continue; + nsCSSRuleProcessor *ruleProc = static_cast + (mRuleProcessors[gCSSSheetTypes[i]].get()); + if (!ruleProc) + continue; + nsCSSKeyframesRule* result = + ruleProc->KeyframesRuleForName(aPresContext, aName); + if (result) + return result; + } + return nullptr; +} + +bool +nsStyleSet::AppendFontFeatureValuesRules(nsPresContext* aPresContext, + nsTArray& aArray) +{ + NS_ENSURE_FALSE(mInShutdown, false); + + for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { + nsCSSRuleProcessor *ruleProc = static_cast + (mRuleProcessors[gCSSSheetTypes[i]].get()); + if (ruleProc && + !ruleProc->AppendFontFeatureValuesRules(aPresContext, aArray)) + { + return false; + } + } + return true; +} + +already_AddRefed +nsStyleSet::GetFontFeatureValuesLookup() +{ + if (mInitFontFeatureValuesLookup) { + mInitFontFeatureValuesLookup = false; + + nsTArray rules; + AppendFontFeatureValuesRules(PresContext(), rules); + + mFontFeatureValuesLookup = new gfxFontFeatureValueSet(); + + uint32_t i, numRules = rules.Length(); + for (i = 0; i < numRules; i++) { + nsCSSFontFeatureValuesRule *rule = rules[i]; + + const nsTArray& familyList = rule->GetFamilyList(); + const nsTArray& + featureValues = rule->GetFeatureValues(); + + // for each family + uint32_t f, numFam; + + numFam = familyList.Length(); + for (f = 0; f < numFam; f++) { + const nsString& family = familyList.ElementAt(f); + nsAutoString silly(family); + mFontFeatureValuesLookup->AddFontFeatureValues(silly, featureValues); + } + } + } + + nsRefPtr lookup = mFontFeatureValuesLookup; + return lookup.forget(); +} + +bool +nsStyleSet::AppendPageRules(nsPresContext* aPresContext, + nsTArray& aArray) +{ + NS_ENSURE_FALSE(mInShutdown, false); + + for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { + if (gCSSSheetTypes[i] == eScopedDocSheet) + continue; + nsCSSRuleProcessor* ruleProc = static_cast + (mRuleProcessors[gCSSSheetTypes[i]].get()); + if (ruleProc && !ruleProc->AppendPageRules(aPresContext, aArray)) + return false; + } + return true; +} + +void +nsStyleSet::BeginShutdown(nsPresContext* aPresContext) +{ + mInShutdown = 1; + mRoots.Clear(); // no longer valid, since we won't keep it up to date +} + +void +nsStyleSet::Shutdown(nsPresContext* aPresContext) +{ + mRuleTree->Destroy(); + mRuleTree = nullptr; + + // We can have old rule trees either because: + // (1) we failed the assertions in EndReconstruct, or + // (2) we're shutting down within a reconstruct (see bug 462392) + for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) { + --i; + mOldRuleTrees[i]->Destroy(); + } + mOldRuleTrees.Clear(); +} + +static const uint32_t kGCInterval = 300; + +void +nsStyleSet::NotifyStyleContextDestroyed(nsPresContext* aPresContext, + nsStyleContext* aStyleContext) +{ + if (mInShutdown) + return; + + // Remove style contexts from mRoots even if mOldRuleTree is non-null. This + // could be a style context from the new ruletree! + if (!aStyleContext->GetParent()) { + mRoots.RemoveElement(aStyleContext); + } + + if (mInReconstruct) + return; + + if (mUnusedRuleNodeCount >= kGCInterval) { + GCRuleTrees(); + } +} + +void +nsStyleSet::GCRuleTrees() +{ + mUnusedRuleNodeCount = 0; + + // Mark the style context tree by marking all style contexts which + // have no parent, which will mark all descendants. This will reach + // style contexts in the undisplayed map and "additional style + // contexts" since they are descendants of the roots. + for (int32_t i = mRoots.Length() - 1; i >= 0; --i) { + mRoots[i]->Mark(); + } + + // Sweep the rule tree. +#ifdef DEBUG + bool deleted = +#endif + mRuleTree->Sweep(); + NS_ASSERTION(!deleted, "Root node must not be gc'd"); + + // Sweep the old rule trees. + for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) { + --i; + if (mOldRuleTrees[i]->Sweep()) { + // It was deleted, as it should be. + mOldRuleTrees.RemoveElementAt(i); + } else { + NS_NOTREACHED("old rule tree still referenced"); + } + } +} + +/** + * Return an equivalent to aRuleNode with both animation and transition + * rules removed, and post a restyle if needed. + */ +static inline nsRuleNode* +SkipAnimationRules(nsRuleNode* aRuleNode, Element* aElement, bool isPseudo) +{ + nsRuleNode* ruleNode = aRuleNode; + // The transition rule must be at the top of the cascade. + if (!ruleNode->IsRoot() && + ruleNode->GetLevel() == nsStyleSet::eTransitionSheet) { + ruleNode = ruleNode->GetParent(); + } + NS_ABORT_IF_FALSE(ruleNode->IsRoot() || + ruleNode->GetLevel() != nsStyleSet::eTransitionSheet, + "can't have more than one transition rule"); + + // Use our existing ReplaceAnimationRule function to replace the + // animation rule, if present. + nsIStyleRule* animationRule = GetAnimationRule(ruleNode); + if (animationRule) { + ruleNode = ReplaceAnimationRule(ruleNode, animationRule, nullptr); + } + + if (ruleNode != aRuleNode) { + NS_ASSERTION(aElement, "How can we have transition rules but no element?"); + // Need to do an animation restyle, just like + // nsTransitionManager::WalkTransitionRule and + // nsAnimationManager::GetAnimationRule would. + nsRestyleHint hint = isPseudo ? eRestyle_Subtree : eRestyle_Self; + aRuleNode->PresContext()->PresShell()->RestyleForAnimation(aElement, hint); + } + return ruleNode; +} + +already_AddRefed +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 TryStartingTransition + // during style reresolution if the style context pointer hasn't changed. + if (aStyleContext->GetParent() == aNewParentContext) { + nsRefPtr ret = aStyleContext; + return ret.forget(); + } + + nsIAtom* pseudoTag = aStyleContext->GetPseudo(); + nsCSSPseudoElements::Type pseudoType = aStyleContext->GetPseudoType(); + nsRuleNode* ruleNode = aStyleContext->RuleNode(); + + // Skip transition rules as needed just like + // nsTransitionManager::WalkTransitionRule would. + bool skipAnimationRules = PresContext()->IsProcessingRestyles() && + !PresContext()->IsProcessingAnimationStyleChange(); + if (skipAnimationRules) { + // Make sure that we're not using transition rules or animation rules for + // our new style context. If we need them, an animation restyle will + // provide. + ruleNode = + SkipAnimationRules(ruleNode, aElement, + pseudoType != + nsCSSPseudoElements::ePseudo_NotPseudoElement); + } + + 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(); + // Again, skip transition rules as needed + if (skipAnimationRules) { + // FIXME do something here for animations? + visitedRuleNode = + SkipAnimationRules(visitedRuleNode, aElement, + pseudoType != + nsCSSPseudoElements::ePseudo_NotPseudoElement); + } + } + + uint32_t flags = eNoFlags; + if (aStyleContext->IsLinkContext()) { + flags |= eIsLink; + } + + // If we're a style context for a link, then we already know whether + // our relevant link is visited, since that does not depend on our + // parent. Otherwise, we need to match aNewParentContext. + bool relevantLinkVisited = aStyleContext->IsLinkContext() ? + aStyleContext->RelevantLinkVisited() : + aNewParentContext->RelevantLinkVisited(); + + if (relevantLinkVisited) { + flags |= eIsVisitedLink; + } + + if (pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement || + pseudoType == nsCSSPseudoElements::ePseudo_before || + pseudoType == nsCSSPseudoElements::ePseudo_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 container. (If the parent + // has anonymous-subtree kids, then we know it's not actually going to get + // a flex container frame, anyway.) + flags |= eSkipFlexItemStyleFixup; + } + + 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, nsCSSPseudoElements::Type 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(nsPresContext* aPresContext, + nsIContent* aContent, + EventStates aStateMask) +{ + if (!aContent || !aContent->IsElement()) + return false; + + TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, + aContent->OwnerDoc()); + InitStyleScopes(treeContext, aContent->AsElement()); + StatefulData data(aPresContext, 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(nsPresContext* aPresContext, + Element* aElement, + EventStates aStateMask) +{ + TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, + aElement->OwnerDoc()); + InitStyleScopes(treeContext, aElement); + StatefulData data(aPresContext, aElement, aStateMask, treeContext); + WalkRuleProcessors(SheetHasStatefulStyle, &data, false); + return data.mHint; +} + +nsRestyleHint +nsStyleSet::HasStateDependentStyle(nsPresContext* aPresContext, + Element* aElement, + nsCSSPseudoElements::Type aPseudoType, + Element* aPseudoElement, + EventStates aStateMask) +{ + TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, + aElement->OwnerDoc()); + InitStyleScopes(treeContext, aElement); + StatefulPseudoElementData data(aPresContext, aElement, aStateMask, + aPseudoType, treeContext, aPseudoElement); + WalkRuleProcessors(SheetHasStatefulPseudoElementStyle, &data, false); + return data.mHint; +} + +struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData { + AttributeData(nsPresContext* aPresContext, + Element* aElement, nsIAtom* aAttribute, int32_t aModType, + bool aAttrHasChanged, TreeMatchContext& aTreeMatchContext) + : AttributeRuleProcessorData(aPresContext, aElement, aAttribute, aModType, + aAttrHasChanged, aTreeMatchContext), + mHint(nsRestyleHint(0)) + {} + nsRestyleHint mHint; +}; + +static bool +SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData) +{ + AttributeData* data = (AttributeData*)aData; + nsRestyleHint hint = aProcessor->HasAttributeDependentStyle(data); + data->mHint = nsRestyleHint(data->mHint | hint); + return true; // continue +} + +// Test if style is dependent on content state +nsRestyleHint +nsStyleSet::HasAttributeDependentStyle(nsPresContext* aPresContext, + Element* aElement, + nsIAtom* aAttribute, + int32_t aModType, + bool aAttrHasChanged) +{ + TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, + aElement->OwnerDoc()); + InitStyleScopes(treeContext, aElement); + AttributeData data(aPresContext, aElement, aAttribute, + aModType, aAttrHasChanged, treeContext); + WalkRuleProcessors(SheetHasAttributeStyle, &data, false); + return data.mHint; +} + +bool +nsStyleSet::MediumFeaturesChanged(nsPresContext* aPresContext) +{ + // We can't use WalkRuleProcessors without a content node. + bool stylesChanged = false; + for (uint32_t i = 0; i < ArrayLength(mRuleProcessors); ++i) { + nsIStyleRuleProcessor *processor = mRuleProcessors[i]; + if (!processor) { + continue; + } + bool thisChanged = processor->MediumFeaturesChanged(aPresContext); + stylesChanged = stylesChanged || thisChanged; + } + for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); ++i) { + nsIStyleRuleProcessor *processor = mScopedDocSheetRuleProcessors[i]; + bool thisChanged = processor->MediumFeaturesChanged(aPresContext); + stylesChanged = stylesChanged || thisChanged; + } + + if (mBindingManager) { + bool thisChanged = false; + mBindingManager->MediumFeaturesChanged(aPresContext, &thisChanged); + stylesChanged = stylesChanged || thisChanged; + } + + return stylesChanged; +} + +nsCSSStyleSheet::EnsureUniqueInnerResult +nsStyleSet::EnsureUniqueInnerOnCSSSheets() +{ + nsAutoTArray queue; + for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { + nsCOMArray &sheets = mSheets[gCSSSheetTypes[i]]; + for (uint32_t j = 0, j_end = sheets.Count(); j < j_end; ++j) { + nsCSSStyleSheet *sheet = static_cast(sheets[j]); + queue.AppendElement(sheet); + } + } + + if (mBindingManager) { + mBindingManager->AppendAllSheets(queue); + } + + nsCSSStyleSheet::EnsureUniqueInnerResult res = + nsCSSStyleSheet::eUniqueInner_AlreadyUnique; + while (!queue.IsEmpty()) { + uint32_t idx = queue.Length() - 1; + nsCSSStyleSheet *sheet = queue[idx]; + queue.RemoveElementAt(idx); + + nsCSSStyleSheet::EnsureUniqueInnerResult sheetRes = + sheet->EnsureUniqueInner(); + if (sheetRes == nsCSSStyleSheet::eUniqueInner_ClonedInner) { + res = sheetRes; + } + + // Enqueue all the sheet's children. + sheet->AppendAllChildSheets(queue); + } + return res; +} + +nsIStyleRule* +nsStyleSet::InitialStyleRule() +{ + if (!mInitialStyleRule) { + mInitialStyleRule = new nsInitialStyleRule; + } + return mInitialStyleRule; +}