michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * the container for the style sheets that apply to a presentation, and michael@0: * the internal API that the style system exposes for creating (and michael@0: * potentially re-creating) style contexts michael@0: */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "nsStyleSet.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: #include "nsIDocumentInlines.h" michael@0: #include "nsRuleWalker.h" michael@0: #include "nsStyleContext.h" michael@0: #include "mozilla/css/StyleRule.h" michael@0: #include "nsCSSAnonBoxes.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "nsCSSRuleProcessor.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "nsIContent.h" michael@0: #include "nsRuleData.h" michael@0: #include "nsRuleProcessorData.h" michael@0: #include "nsTransitionManager.h" michael@0: #include "nsAnimationManager.h" michael@0: #include "nsStyleSheetService.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "GeckoProfiler.h" michael@0: #include "nsHTMLCSSStyleSheet.h" michael@0: #include "nsHTMLStyleSheet.h" michael@0: #include "nsCSSRules.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsEmptyStyleRule, nsIStyleRule) michael@0: michael@0: /* virtual */ void michael@0: nsEmptyStyleRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: nsEmptyStyleRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: fputs("[empty style rule] {}\n", out); michael@0: } michael@0: #endif michael@0: michael@0: NS_IMPL_ISUPPORTS(nsInitialStyleRule, nsIStyleRule) michael@0: michael@0: /* virtual */ void michael@0: nsInitialStyleRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: // Iterate over the property groups michael@0: for (nsStyleStructID sid = nsStyleStructID(0); michael@0: sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) { michael@0: if (aRuleData->mSIDs & (1 << sid)) { michael@0: // Iterate over nsCSSValues within the property group michael@0: nsCSSValue * const value_start = michael@0: aRuleData->mValueStorage + aRuleData->mValueOffsets[sid]; michael@0: for (nsCSSValue *value = value_start, michael@0: *value_end = value + nsCSSProps::PropertyCountInStruct(sid); michael@0: value != value_end; ++value) { michael@0: // If MathML is disabled take care not to set MathML properties (or we michael@0: // will trigger assertions in nsRuleNode) michael@0: if (sid == eStyleStruct_Font && michael@0: !aRuleData->mPresContext->Document()->GetMathMLEnabled()) { michael@0: size_t index = value - value_start; michael@0: if (index == nsCSSProps::PropertyIndexInStruct( michael@0: eCSSProperty_script_level) || michael@0: index == nsCSSProps::PropertyIndexInStruct( michael@0: eCSSProperty_script_size_multiplier) || michael@0: index == nsCSSProps::PropertyIndexInStruct( michael@0: eCSSProperty_script_min_size) || michael@0: index == nsCSSProps::PropertyIndexInStruct( michael@0: eCSSProperty_math_variant) || michael@0: index == nsCSSProps::PropertyIndexInStruct( michael@0: eCSSProperty_math_display)) { michael@0: continue; michael@0: } michael@0: } michael@0: if (value->GetUnit() == eCSSUnit_Null) { michael@0: value->SetInitialValue(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: nsInitialStyleRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: fputs("[initial style rule] {}\n", out); michael@0: } michael@0: #endif michael@0: michael@0: NS_IMPL_ISUPPORTS(nsDisableTextZoomStyleRule, nsIStyleRule) michael@0: michael@0: /* virtual */ void michael@0: nsDisableTextZoomStyleRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: if (!(aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Font))) michael@0: return; michael@0: michael@0: nsCSSValue* value = aRuleData->ValueForTextZoom(); michael@0: if (value->GetUnit() == eCSSUnit_Null) michael@0: value->SetNoneValue(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: nsDisableTextZoomStyleRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: fputs("[disable text zoom style rule] {}\n", out); michael@0: } michael@0: #endif michael@0: michael@0: static const nsStyleSet::sheetType gCSSSheetTypes[] = { michael@0: // From lowest to highest in cascading order. michael@0: nsStyleSet::eAgentSheet, michael@0: nsStyleSet::eUserSheet, michael@0: nsStyleSet::eDocSheet, michael@0: nsStyleSet::eScopedDocSheet, michael@0: nsStyleSet::eOverrideSheet michael@0: }; michael@0: michael@0: nsStyleSet::nsStyleSet() michael@0: : mRuleTree(nullptr), michael@0: mBatching(0), michael@0: mInShutdown(false), michael@0: mAuthorStyleDisabled(false), michael@0: mInReconstruct(false), michael@0: mInitFontFeatureValuesLookup(true), michael@0: mDirty(0), michael@0: mUnusedRuleNodeCount(0) michael@0: { michael@0: } michael@0: michael@0: size_t michael@0: nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: michael@0: for (int i = 0; i < eSheetTypeCount; i++) { michael@0: if (mRuleProcessors[i]) { michael@0: n += mRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: n += mSheets[i].SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { michael@0: n += mScopedDocSheetRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: n += mScopedDocSheetRuleProcessors.SizeOfExcludingThis(aMallocSizeOf); michael@0: michael@0: n += mRoots.SizeOfExcludingThis(aMallocSizeOf); michael@0: n += mOldRuleTrees.SizeOfExcludingThis(aMallocSizeOf); michael@0: michael@0: return n; michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::Init(nsPresContext *aPresContext) michael@0: { michael@0: mFirstLineRule = new nsEmptyStyleRule; michael@0: mFirstLetterRule = new nsEmptyStyleRule; michael@0: mPlaceholderRule = new nsEmptyStyleRule; michael@0: mDisableTextZoomStyleRule = new nsDisableTextZoomStyleRule; michael@0: michael@0: mRuleTree = nsRuleNode::CreateRootNode(aPresContext); michael@0: michael@0: GatherRuleProcessors(eAnimationSheet); michael@0: GatherRuleProcessors(eTransitionSheet); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::BeginReconstruct() michael@0: { michael@0: NS_ASSERTION(!mInReconstruct, "Unmatched begin/end?"); michael@0: NS_ASSERTION(mRuleTree, "Reconstructing before first construction?"); michael@0: michael@0: // Create a new rule tree root michael@0: nsRuleNode* newTree = michael@0: nsRuleNode::CreateRootNode(mRuleTree->PresContext()); michael@0: if (!newTree) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // Save the old rule tree so we can destroy it later michael@0: if (!mOldRuleTrees.AppendElement(mRuleTree)) { michael@0: newTree->Destroy(); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: // We need to keep mRoots so that the rule tree GC will only free the michael@0: // rule trees that really aren't referenced anymore (which should be michael@0: // all of them, if there are no bugs in reresolution code). michael@0: michael@0: mInReconstruct = true; michael@0: mRuleTree = newTree; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::EndReconstruct() michael@0: { michael@0: NS_ASSERTION(mInReconstruct, "Unmatched begin/end?"); michael@0: mInReconstruct = false; michael@0: #ifdef DEBUG michael@0: for (int32_t i = mRoots.Length() - 1; i >= 0; --i) { michael@0: nsRuleNode *n = mRoots[i]->RuleNode(); michael@0: while (n->GetParent()) { michael@0: n = n->GetParent(); michael@0: } michael@0: // Since nsStyleContext's mParent and mRuleNode are immutable, and michael@0: // style contexts own their parents, and nsStyleContext asserts in michael@0: // its constructor that the style context and its parent are in the michael@0: // same rule tree, we don't need to check any of the children of michael@0: // mRoots; we only need to check the rule nodes of mRoots michael@0: // themselves. michael@0: michael@0: NS_ASSERTION(n == mRuleTree, "style context has old rule node"); michael@0: } michael@0: #endif michael@0: // This *should* destroy the only element of mOldRuleTrees, but in michael@0: // case of some bugs (which would trigger the above assertions), it michael@0: // won't. michael@0: GCRuleTrees(); michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::SetQuirkStyleSheet(nsIStyleSheet* aQuirkStyleSheet) michael@0: { michael@0: NS_ASSERTION(aQuirkStyleSheet, "Must have quirk sheet if this is called"); michael@0: NS_ASSERTION(!mQuirkStyleSheet, "Multiple calls to SetQuirkStyleSheet?"); michael@0: NS_ASSERTION(mSheets[eAgentSheet].IndexOf(aQuirkStyleSheet) != -1, michael@0: "Quirk style sheet not one of our agent sheets?"); michael@0: mQuirkStyleSheet = aQuirkStyleSheet; michael@0: } michael@0: michael@0: typedef nsDataHashtable, uint32_t> ScopeDepthCache; michael@0: michael@0: // Returns the depth of a style scope element, with 1 being the depth of michael@0: // a style scope element that has no ancestor style scope elements. The michael@0: // depth does not count intervening non-scope elements. michael@0: static uint32_t michael@0: GetScopeDepth(nsINode* aScopeElement, ScopeDepthCache& aCache) michael@0: { michael@0: nsINode* parent = aScopeElement->GetParent(); michael@0: if (!parent || !parent->IsElementInStyleScope()) { michael@0: return 1; michael@0: } michael@0: michael@0: uint32_t depth = aCache.Get(aScopeElement); michael@0: if (!depth) { michael@0: for (nsINode* n = parent; n; n = n->GetParent()) { michael@0: if (n->IsScopedStyleRoot()) { michael@0: depth = GetScopeDepth(n, aCache) + 1; michael@0: aCache.Put(aScopeElement, depth); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: return depth; michael@0: } michael@0: michael@0: struct ScopedSheetOrder michael@0: { michael@0: nsCSSStyleSheet* mSheet; michael@0: uint32_t mDepth; michael@0: uint32_t mOrder; michael@0: michael@0: bool operator==(const ScopedSheetOrder& aRHS) const michael@0: { michael@0: return mDepth == aRHS.mDepth && michael@0: mOrder == aRHS.mOrder; michael@0: } michael@0: michael@0: bool operator<(const ScopedSheetOrder& aRHS) const michael@0: { michael@0: if (mDepth != aRHS.mDepth) { michael@0: return mDepth < aRHS.mDepth; michael@0: } michael@0: return mOrder < aRHS.mOrder; michael@0: } michael@0: }; michael@0: michael@0: // Sorts aSheets such that style sheets for ancestor scopes come michael@0: // before those for descendant scopes, and with sheets for a single michael@0: // scope in document order. michael@0: static void michael@0: SortStyleSheetsByScope(nsTArray& aSheets) michael@0: { michael@0: uint32_t n = aSheets.Length(); michael@0: if (n == 1) { michael@0: return; michael@0: } michael@0: michael@0: ScopeDepthCache cache; michael@0: michael@0: nsTArray sheets; michael@0: sheets.SetLength(n); michael@0: michael@0: // For each sheet, record the depth of its scope element and its original michael@0: // document order. michael@0: for (uint32_t i = 0; i < n; i++) { michael@0: sheets[i].mSheet = aSheets[i]; michael@0: sheets[i].mDepth = GetScopeDepth(aSheets[i]->GetScopeElement(), cache); michael@0: sheets[i].mOrder = i; michael@0: } michael@0: michael@0: // Sort by depth first, then document order. michael@0: sheets.Sort(); michael@0: michael@0: for (uint32_t i = 0; i < n; i++) { michael@0: aSheets[i] = sheets[i].mSheet; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::GatherRuleProcessors(sheetType aType) michael@0: { michael@0: mRuleProcessors[aType] = nullptr; michael@0: if (aType == eScopedDocSheet) { michael@0: for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { michael@0: nsIStyleRuleProcessor* processor = mScopedDocSheetRuleProcessors[i].get(); michael@0: Element* scope = michael@0: static_cast(processor)->GetScopeElement(); michael@0: scope->ClearIsScopedStyleRoot(); michael@0: } michael@0: mScopedDocSheetRuleProcessors.Clear(); michael@0: } michael@0: if (mAuthorStyleDisabled && (aType == eDocSheet || michael@0: aType == eScopedDocSheet || michael@0: aType == eStyleAttrSheet)) { michael@0: // Don't regather if this level is disabled. Note that we gather michael@0: // preshint sheets no matter what, but then skip them for some michael@0: // elements later if mAuthorStyleDisabled. michael@0: return NS_OK; michael@0: } michael@0: switch (aType) { michael@0: // handle the types for which have a rule processor that does not michael@0: // implement the style sheet interface. michael@0: case eAnimationSheet: michael@0: MOZ_ASSERT(mSheets[aType].Count() == 0); michael@0: mRuleProcessors[aType] = PresContext()->AnimationManager(); michael@0: return NS_OK; michael@0: case eTransitionSheet: michael@0: MOZ_ASSERT(mSheets[aType].Count() == 0); michael@0: mRuleProcessors[aType] = PresContext()->TransitionManager(); michael@0: return NS_OK; michael@0: case eStyleAttrSheet: michael@0: MOZ_ASSERT(mSheets[aType].Count() == 0); michael@0: mRuleProcessors[aType] = PresContext()->Document()->GetInlineStyleSheet(); michael@0: return NS_OK; michael@0: case ePresHintSheet: michael@0: MOZ_ASSERT(mSheets[aType].Count() == 0); michael@0: mRuleProcessors[aType] = PresContext()->Document()->GetAttributeStyleSheet(); michael@0: return NS_OK; michael@0: default: michael@0: // keep going michael@0: break; michael@0: } michael@0: if (aType == eScopedDocSheet) { michael@0: // Create a rule processor for each scope. michael@0: uint32_t count = mSheets[eScopedDocSheet].Count(); michael@0: if (count) { michael@0: // Gather the scoped style sheets into an array as michael@0: // nsCSSStyleSheets, and mark all of their scope elements michael@0: // as scoped style roots. michael@0: nsTArray sheets(count); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: nsRefPtr sheet = michael@0: do_QueryObject(mSheets[eScopedDocSheet].ObjectAt(i)); michael@0: sheets.AppendElement(sheet); michael@0: michael@0: Element* scope = sheet->GetScopeElement(); michael@0: scope->SetIsScopedStyleRoot(); michael@0: } michael@0: michael@0: // Sort the scoped style sheets so that those for the same scope are michael@0: // adjacent and that ancestor scopes come before descendent scopes. michael@0: SortStyleSheetsByScope(sheets); michael@0: michael@0: uint32_t start = 0, end; michael@0: do { michael@0: // Find the range of style sheets with the same scope. michael@0: Element* scope = sheets[start]->GetScopeElement(); michael@0: end = start + 1; michael@0: while (end < count && sheets[end]->GetScopeElement() == scope) { michael@0: end++; michael@0: } michael@0: michael@0: scope->SetIsScopedStyleRoot(); michael@0: michael@0: // Create a rule processor for the scope. michael@0: nsTArray< nsRefPtr > sheetsForScope; michael@0: sheetsForScope.AppendElements(sheets.Elements() + start, end - start); michael@0: mScopedDocSheetRuleProcessors.AppendElement michael@0: (new nsCSSRuleProcessor(sheetsForScope, uint8_t(aType), scope)); michael@0: michael@0: start = end; michael@0: } while (start < count); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: if (mSheets[aType].Count()) { michael@0: switch (aType) { michael@0: case eAgentSheet: michael@0: case eUserSheet: michael@0: case eDocSheet: michael@0: case eOverrideSheet: { michael@0: // levels containing CSS stylesheets (apart from eScopedDocSheet) michael@0: nsCOMArray& sheets = mSheets[aType]; michael@0: nsTArray > cssSheets(sheets.Count()); michael@0: for (int32_t i = 0, i_end = sheets.Count(); i < i_end; ++i) { michael@0: nsRefPtr cssSheet = do_QueryObject(sheets[i]); michael@0: NS_ASSERTION(cssSheet, "not a CSS sheet"); michael@0: cssSheets.AppendElement(cssSheet); michael@0: } michael@0: mRuleProcessors[aType] = michael@0: new nsCSSRuleProcessor(cssSheets, uint8_t(aType), nullptr); michael@0: } break; michael@0: michael@0: default: michael@0: // levels containing non-CSS stylesheets michael@0: NS_ASSERTION(mSheets[aType].Count() == 1, "only one sheet per level"); michael@0: mRuleProcessors[aType] = do_QueryInterface(mSheets[aType][0]); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool michael@0: IsScopedStyleSheet(nsIStyleSheet* aSheet) michael@0: { michael@0: nsRefPtr cssSheet = do_QueryObject(aSheet); michael@0: NS_ASSERTION(cssSheet, "expected aSheet to be an nsCSSStyleSheet"); michael@0: michael@0: return cssSheet->GetScopeElement(); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet) michael@0: { michael@0: NS_PRECONDITION(aSheet, "null arg"); michael@0: NS_ASSERTION(aSheet->IsApplicable(), michael@0: "Inapplicable sheet being placed in style set"); michael@0: mSheets[aType].RemoveObject(aSheet); michael@0: if (!mSheets[aType].AppendObject(aSheet)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return DirtyRuleProcessors(aType); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet) michael@0: { michael@0: NS_PRECONDITION(aSheet, "null arg"); michael@0: NS_ASSERTION(aSheet->IsApplicable(), michael@0: "Inapplicable sheet being placed in style set"); michael@0: mSheets[aType].RemoveObject(aSheet); michael@0: if (!mSheets[aType].InsertObjectAt(aSheet, 0)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return DirtyRuleProcessors(aType); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet) michael@0: { michael@0: NS_PRECONDITION(aSheet, "null arg"); michael@0: NS_ASSERTION(aSheet->IsComplete(), michael@0: "Incomplete sheet being removed from style set"); michael@0: mSheets[aType].RemoveObject(aSheet); michael@0: michael@0: return DirtyRuleProcessors(aType); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::ReplaceSheets(sheetType aType, michael@0: const nsCOMArray &aNewSheets) michael@0: { michael@0: mSheets[aType].Clear(); michael@0: if (!mSheets[aType].AppendObjects(aNewSheets)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return DirtyRuleProcessors(aType); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet, michael@0: nsIStyleSheet *aReferenceSheet) michael@0: { michael@0: NS_PRECONDITION(aNewSheet && aReferenceSheet, "null arg"); michael@0: NS_ASSERTION(aNewSheet->IsApplicable(), michael@0: "Inapplicable sheet being placed in style set"); michael@0: michael@0: mSheets[aType].RemoveObject(aNewSheet); michael@0: int32_t idx = mSheets[aType].IndexOf(aReferenceSheet); michael@0: if (idx < 0) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: if (!mSheets[aType].InsertObjectAt(aNewSheet, idx)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return DirtyRuleProcessors(aType); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::DirtyRuleProcessors(sheetType aType) michael@0: { michael@0: if (!mBatching) michael@0: return GatherRuleProcessors(aType); michael@0: michael@0: mDirty |= 1 << aType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsStyleSet::GetAuthorStyleDisabled() michael@0: { michael@0: return mAuthorStyleDisabled; michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled) michael@0: { michael@0: if (aStyleDisabled == !mAuthorStyleDisabled) { michael@0: mAuthorStyleDisabled = aStyleDisabled; michael@0: BeginUpdate(); michael@0: mDirty |= 1 << eDocSheet | michael@0: 1 << eScopedDocSheet | michael@0: 1 << eStyleAttrSheet; michael@0: return EndUpdate(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // -------- Doc Sheets michael@0: michael@0: nsresult michael@0: nsStyleSet::AddDocStyleSheet(nsIStyleSheet* aSheet, nsIDocument* aDocument) michael@0: { michael@0: NS_PRECONDITION(aSheet && aDocument, "null arg"); michael@0: NS_ASSERTION(aSheet->IsApplicable(), michael@0: "Inapplicable sheet being placed in style set"); michael@0: michael@0: sheetType type = IsScopedStyleSheet(aSheet) ? michael@0: eScopedDocSheet : michael@0: eDocSheet; michael@0: nsCOMArray& sheets = mSheets[type]; michael@0: michael@0: sheets.RemoveObject(aSheet); michael@0: nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); michael@0: michael@0: // lowest index first michael@0: int32_t newDocIndex = aDocument->GetIndexOfStyleSheet(aSheet); michael@0: michael@0: int32_t count = sheets.Count(); michael@0: int32_t index; michael@0: for (index = 0; index < count; index++) { michael@0: nsIStyleSheet* sheet = sheets.ObjectAt(index); michael@0: int32_t sheetDocIndex = aDocument->GetIndexOfStyleSheet(sheet); michael@0: if (sheetDocIndex > newDocIndex) michael@0: break; michael@0: michael@0: // If the sheet is not owned by the document it can be an author michael@0: // sheet registered at nsStyleSheetService or an additional author michael@0: // sheet on the document, which means the new michael@0: // doc sheet should end up before it. michael@0: if (sheetDocIndex < 0 && michael@0: ((sheetService && michael@0: sheetService->AuthorStyleSheets()->IndexOf(sheet) >= 0) || michael@0: sheet == aDocument->FirstAdditionalAuthorSheet())) michael@0: break; michael@0: } michael@0: if (!sheets.InsertObjectAt(aSheet, index)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return DirtyRuleProcessors(type); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::RemoveDocStyleSheet(nsIStyleSheet *aSheet) michael@0: { michael@0: nsRefPtr cssSheet = do_QueryObject(aSheet); michael@0: bool isScoped = cssSheet && cssSheet->GetScopeElement(); michael@0: return RemoveStyleSheet(isScoped ? eScopedDocSheet : eDocSheet, aSheet); michael@0: } michael@0: michael@0: // Batching michael@0: void michael@0: nsStyleSet::BeginUpdate() michael@0: { michael@0: ++mBatching; michael@0: } michael@0: michael@0: nsresult michael@0: nsStyleSet::EndUpdate() michael@0: { michael@0: NS_ASSERTION(mBatching > 0, "Unbalanced EndUpdate"); michael@0: if (--mBatching) { michael@0: // We're not completely done yet. michael@0: return NS_OK; michael@0: } michael@0: michael@0: for (int i = 0; i < eSheetTypeCount; ++i) { michael@0: if (mDirty & (1 << i)) { michael@0: nsresult rv = GatherRuleProcessors(sheetType(i)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: mDirty = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::EnableQuirkStyleSheet(bool aEnable) michael@0: { michael@0: #ifdef DEBUG michael@0: bool oldEnabled; michael@0: { michael@0: nsCOMPtr domSheet = michael@0: do_QueryInterface(mQuirkStyleSheet); michael@0: domSheet->GetDisabled(&oldEnabled); michael@0: oldEnabled = !oldEnabled; michael@0: } michael@0: #endif michael@0: mQuirkStyleSheet->SetEnabled(aEnable); michael@0: #ifdef DEBUG michael@0: // This should always be OK, since SetEnabled should call michael@0: // ClearRuleCascades. michael@0: // Note that we can hit this codepath multiple times when document.open() michael@0: // (potentially implied) happens multiple times. michael@0: if (mRuleProcessors[eAgentSheet] && aEnable != oldEnabled) { michael@0: static_cast(static_cast( michael@0: mRuleProcessors[eAgentSheet]))->AssertQuirksChangeOK(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: EnumRulesMatching(nsIStyleRuleProcessor* aProcessor, void* aData) michael@0: { michael@0: T* data = static_cast(aData); michael@0: aProcessor->RulesMatching(data); michael@0: return true; michael@0: } michael@0: michael@0: static inline bool michael@0: IsMoreSpecificThanAnimation(nsRuleNode *aRuleNode) michael@0: { michael@0: return !aRuleNode->IsRoot() && michael@0: (aRuleNode->GetLevel() == nsStyleSet::eTransitionSheet || michael@0: aRuleNode->IsImportantRule()); michael@0: } michael@0: michael@0: static nsIStyleRule* michael@0: GetAnimationRule(nsRuleNode *aRuleNode) michael@0: { michael@0: nsRuleNode *n = aRuleNode; michael@0: while (IsMoreSpecificThanAnimation(n)) { michael@0: n = n->GetParent(); michael@0: } michael@0: michael@0: if (n->IsRoot() || n->GetLevel() != nsStyleSet::eAnimationSheet) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return n->GetRule(); michael@0: } michael@0: michael@0: static nsRuleNode* michael@0: ReplaceAnimationRule(nsRuleNode *aOldRuleNode, michael@0: nsIStyleRule *aOldAnimRule, michael@0: nsIStyleRule *aNewAnimRule) michael@0: { michael@0: nsTArray moreSpecificNodes; michael@0: michael@0: nsRuleNode *n = aOldRuleNode; michael@0: while (IsMoreSpecificThanAnimation(n)) { michael@0: moreSpecificNodes.AppendElement(n); michael@0: n = n->GetParent(); michael@0: } michael@0: michael@0: if (aOldAnimRule) { michael@0: NS_ABORT_IF_FALSE(n->GetRule() == aOldAnimRule, "wrong rule"); michael@0: NS_ABORT_IF_FALSE(n->GetLevel() == nsStyleSet::eAnimationSheet, michael@0: "wrong level"); michael@0: n = n->GetParent(); michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(!IsMoreSpecificThanAnimation(n) && michael@0: (n->IsRoot() || michael@0: n->GetLevel() != nsStyleSet::eAnimationSheet), michael@0: "wrong level"); michael@0: michael@0: if (aNewAnimRule) { michael@0: n = n->Transition(aNewAnimRule, nsStyleSet::eAnimationSheet, false); michael@0: } michael@0: michael@0: for (uint32_t i = moreSpecificNodes.Length(); i-- != 0; ) { michael@0: nsRuleNode *oldNode = moreSpecificNodes[i]; michael@0: n = n->Transition(oldNode->GetRule(), oldNode->GetLevel(), michael@0: oldNode->IsImportantRule()); michael@0: } michael@0: michael@0: return n; michael@0: } michael@0: michael@0: /** michael@0: * |GetContext| implements sharing of style contexts (not just the data michael@0: * on the rule nodes) between siblings and cousins of the same michael@0: * generation. (It works for cousins of the same generation since michael@0: * |aParentContext| could itself be a shared context.) michael@0: */ michael@0: already_AddRefed michael@0: nsStyleSet::GetContext(nsStyleContext* aParentContext, michael@0: nsRuleNode* aRuleNode, michael@0: // aVisitedRuleNode may be null; if it is null michael@0: // it means that we don't need to force creation michael@0: // of a StyleIfVisited. (But if we make one michael@0: // because aParentContext has one, then aRuleNode michael@0: // should be used.) michael@0: nsRuleNode* aVisitedRuleNode, michael@0: nsIAtom* aPseudoTag, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: Element* aElementForAnimation, michael@0: uint32_t aFlags) michael@0: { michael@0: NS_PRECONDITION((!aPseudoTag && michael@0: aPseudoType == michael@0: nsCSSPseudoElements::ePseudo_NotPseudoElement) || michael@0: (aPseudoTag && michael@0: nsCSSPseudoElements::GetPseudoType(aPseudoTag) == michael@0: aPseudoType), michael@0: "Pseudo mismatch"); michael@0: michael@0: if (aVisitedRuleNode == aRuleNode) { michael@0: // No need to force creation of a visited style in this case. michael@0: aVisitedRuleNode = nullptr; michael@0: } michael@0: michael@0: // Ensure |aVisitedRuleNode != nullptr| corresponds to the need to michael@0: // create an if-visited style context, and that in that case, we have michael@0: // parentIfVisited set correctly. michael@0: nsStyleContext *parentIfVisited = michael@0: aParentContext ? aParentContext->GetStyleIfVisited() : nullptr; michael@0: if (parentIfVisited) { michael@0: if (!aVisitedRuleNode) { michael@0: aVisitedRuleNode = aRuleNode; michael@0: } michael@0: } else { michael@0: if (aVisitedRuleNode) { michael@0: parentIfVisited = aParentContext; michael@0: } michael@0: } michael@0: michael@0: if (aFlags & eIsLink) { michael@0: // If this node is a link, we want its visited's style context's michael@0: // parent to be the regular style context of its parent, because michael@0: // only the visitedness of the relevant link should influence style. michael@0: parentIfVisited = aParentContext; michael@0: } michael@0: michael@0: nsRefPtr result; michael@0: if (aParentContext) michael@0: result = aParentContext->FindChildWithRules(aPseudoTag, aRuleNode, michael@0: aVisitedRuleNode, michael@0: aFlags & eIsVisitedLink); michael@0: michael@0: #ifdef NOISY_DEBUG michael@0: if (result) michael@0: fprintf(stdout, "--- SharedSC %d ---\n", ++gSharedCount); michael@0: else michael@0: fprintf(stdout, "+++ NewSC %d +++\n", ++gNewCount); michael@0: #endif michael@0: michael@0: if (!result) { michael@0: result = NS_NewStyleContext(aParentContext, aPseudoTag, aPseudoType, michael@0: aRuleNode, aFlags & eSkipFlexItemStyleFixup); michael@0: if (aVisitedRuleNode) { michael@0: nsRefPtr resultIfVisited = michael@0: NS_NewStyleContext(parentIfVisited, aPseudoTag, aPseudoType, michael@0: aVisitedRuleNode, michael@0: aFlags & eSkipFlexItemStyleFixup); michael@0: if (!parentIfVisited) { michael@0: mRoots.AppendElement(resultIfVisited); michael@0: } michael@0: resultIfVisited->SetIsStyleIfVisited(); michael@0: result->SetStyleIfVisited(resultIfVisited.forget()); michael@0: michael@0: bool relevantLinkVisited = (aFlags & eIsLink) ? michael@0: (aFlags & eIsVisitedLink) : michael@0: (aParentContext && aParentContext->RelevantLinkVisited()); michael@0: michael@0: if (relevantLinkVisited) { michael@0: result->AddStyleBit(NS_STYLE_RELEVANT_LINK_VISITED); michael@0: } michael@0: } michael@0: if (!aParentContext) { michael@0: mRoots.AppendElement(result); michael@0: } michael@0: } michael@0: else { michael@0: NS_ASSERTION(result->GetPseudoType() == aPseudoType, "Unexpected type"); michael@0: NS_ASSERTION(result->GetPseudo() == aPseudoTag, "Unexpected pseudo"); michael@0: } michael@0: michael@0: if (aFlags & eDoAnimation) { michael@0: // Normally the animation manager has already added the correct michael@0: // style rule. However, if the animation-name just changed, it michael@0: // might have been wrong. So ask it to double-check based on the michael@0: // resulting style context. michael@0: nsIStyleRule *oldAnimRule = GetAnimationRule(aRuleNode); michael@0: nsIStyleRule *animRule = PresContext()->AnimationManager()-> michael@0: CheckAnimationRule(result, aElementForAnimation); michael@0: NS_ABORT_IF_FALSE(result->RuleNode() == aRuleNode, michael@0: "unexpected rule node"); michael@0: NS_ABORT_IF_FALSE(!result->GetStyleIfVisited() == !aVisitedRuleNode, michael@0: "unexpected visited rule node"); michael@0: NS_ABORT_IF_FALSE(!aVisitedRuleNode || michael@0: result->GetStyleIfVisited()->RuleNode() == michael@0: aVisitedRuleNode, michael@0: "unexpected visited rule node"); michael@0: NS_ABORT_IF_FALSE(!aVisitedRuleNode || michael@0: oldAnimRule == GetAnimationRule(aVisitedRuleNode), michael@0: "animation rule mismatch between rule nodes"); michael@0: if (oldAnimRule != animRule) { michael@0: nsRuleNode *ruleNode = michael@0: ReplaceAnimationRule(aRuleNode, oldAnimRule, animRule); michael@0: nsRuleNode *visitedRuleNode = aVisitedRuleNode michael@0: ? ReplaceAnimationRule(aVisitedRuleNode, oldAnimRule, animRule) michael@0: : nullptr; michael@0: NS_ABORT_IF_FALSE(!visitedRuleNode || michael@0: GetAnimationRule(ruleNode) == michael@0: GetAnimationRule(visitedRuleNode), michael@0: "animation rule mismatch between rule nodes"); michael@0: result = GetContext(aParentContext, ruleNode, visitedRuleNode, michael@0: aPseudoTag, aPseudoType, nullptr, michael@0: aFlags & ~eDoAnimation); michael@0: } michael@0: } michael@0: michael@0: if (aElementForAnimation && aElementForAnimation->IsHTML(nsGkAtoms::body) && michael@0: aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement && michael@0: PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) { michael@0: nsIDocument* doc = aElementForAnimation->GetCurrentDoc(); michael@0: if (doc && doc->GetBodyElement() == aElementForAnimation) { michael@0: // Update the prescontext's body color michael@0: PresContext()->SetBodyTextColor(result->StyleColor()->mColor); michael@0: } michael@0: } michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::AddImportantRules(nsRuleNode* aCurrLevelNode, michael@0: nsRuleNode* aLastPrevLevelNode, michael@0: nsRuleWalker* aRuleWalker) michael@0: { michael@0: NS_ASSERTION(aCurrLevelNode && michael@0: aCurrLevelNode != aLastPrevLevelNode, "How did we get here?"); michael@0: michael@0: nsAutoTArray importantRules; michael@0: for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode; michael@0: node = node->GetParent()) { michael@0: // We guarantee that we never walk the root node here, so no need michael@0: // to null-check GetRule(). Furthermore, it must be a CSS rule. michael@0: NS_ASSERTION(nsRefPtr(do_QueryObject(node->GetRule())), michael@0: "Unexpected non-CSS rule"); michael@0: michael@0: nsIStyleRule* impRule = michael@0: static_cast(node->GetRule())->GetImportantRule(); michael@0: if (impRule) michael@0: importantRules.AppendElement(impRule); michael@0: } michael@0: michael@0: NS_ASSERTION(importantRules.Length() != 0, michael@0: "Why did we think there were important rules?"); michael@0: michael@0: for (uint32_t i = importantRules.Length(); i-- != 0; ) { michael@0: aRuleWalker->Forward(importantRules[i]); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsStyleSet::AssertNoImportantRules(nsRuleNode* aCurrLevelNode, michael@0: nsRuleNode* aLastPrevLevelNode) michael@0: { michael@0: if (!aCurrLevelNode) michael@0: return; michael@0: michael@0: for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode; michael@0: node = node->GetParent()) { michael@0: nsRefPtr rule(do_QueryObject(node->GetRule())); michael@0: NS_ASSERTION(rule, "Unexpected non-CSS rule"); michael@0: michael@0: NS_ASSERTION(!rule->GetImportantRule(), "Unexpected important rule"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode, michael@0: nsRuleNode* aLastPrevLevelNode) michael@0: { michael@0: if (!aCurrLevelNode) michael@0: return; michael@0: michael@0: for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode; michael@0: node = node->GetParent()) { michael@0: nsIStyleRule *rule = node->GetRule(); michael@0: nsRefPtr cssRule(do_QueryObject(rule)); michael@0: NS_ASSERTION(!cssRule || !cssRule->Selector(), "Unexpected CSS rule"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Enumerate the rules in a way that cares about the order of the rules. michael@0: void michael@0: nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, michael@0: RuleProcessorData* aData, Element* aElement, michael@0: nsRuleWalker* aRuleWalker) michael@0: { michael@0: PROFILER_LABEL("nsStyleSet", "FileRules"); michael@0: michael@0: // Cascading order: michael@0: // [least important] michael@0: // - UA normal rules = Agent normal michael@0: // - User normal rules = User normal michael@0: // - Presentation hints = PresHint normal michael@0: // - Author normal rules = Document normal michael@0: // - Override normal rules = Override normal michael@0: // - animation rules = Animation normal michael@0: // - Author !important rules = Document !important michael@0: // - Override !important rules = Override !important michael@0: // - User !important rules = User !important michael@0: // - UA !important rules = Agent !important michael@0: // - transition rules = Transition normal michael@0: // [most important] michael@0: michael@0: // Save off the last rule before we start walking our agent sheets; michael@0: // this will be either the root or one of the restriction rules. michael@0: nsRuleNode* lastRestrictionRN = aRuleWalker->CurrentNode(); michael@0: michael@0: aRuleWalker->SetLevel(eAgentSheet, false, true); michael@0: if (mRuleProcessors[eAgentSheet]) michael@0: (*aCollectorFunc)(mRuleProcessors[eAgentSheet], aData); michael@0: nsRuleNode* lastAgentRN = aRuleWalker->CurrentNode(); michael@0: bool haveImportantUARules = !aRuleWalker->GetCheckForImportantRules(); michael@0: michael@0: aRuleWalker->SetLevel(eUserSheet, false, true); michael@0: bool skipUserStyles = michael@0: aElement && aElement->IsInNativeAnonymousSubtree(); michael@0: if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different michael@0: (*aCollectorFunc)(mRuleProcessors[eUserSheet], aData); michael@0: nsRuleNode* lastUserRN = aRuleWalker->CurrentNode(); michael@0: bool haveImportantUserRules = !aRuleWalker->GetCheckForImportantRules(); michael@0: michael@0: aRuleWalker->SetLevel(ePresHintSheet, false, false); michael@0: if (mRuleProcessors[ePresHintSheet]) michael@0: (*aCollectorFunc)(mRuleProcessors[ePresHintSheet], aData); michael@0: nsRuleNode* lastPresHintRN = aRuleWalker->CurrentNode(); michael@0: michael@0: aRuleWalker->SetLevel(eDocSheet, false, true); michael@0: bool cutOffInheritance = false; michael@0: if (mBindingManager && aElement) { michael@0: // We can supply additional document-level sheets that should be walked. michael@0: mBindingManager->WalkRules(aCollectorFunc, michael@0: static_cast(aData), michael@0: &cutOffInheritance); michael@0: } michael@0: if (!skipUserStyles && !cutOffInheritance && // NOTE: different michael@0: mRuleProcessors[eDocSheet]) michael@0: (*aCollectorFunc)(mRuleProcessors[eDocSheet], aData); michael@0: nsRuleNode* lastDocRN = aRuleWalker->CurrentNode(); michael@0: bool haveImportantDocRules = !aRuleWalker->GetCheckForImportantRules(); michael@0: nsTArray lastScopedRNs; michael@0: nsTArray haveImportantScopedRules; michael@0: bool haveAnyImportantScopedRules = false; michael@0: if (!skipUserStyles && !cutOffInheritance && michael@0: aElement && aElement->IsElementInStyleScope()) { michael@0: lastScopedRNs.SetLength(mScopedDocSheetRuleProcessors.Length()); michael@0: haveImportantScopedRules.SetLength(mScopedDocSheetRuleProcessors.Length()); michael@0: for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { michael@0: aRuleWalker->SetLevel(eScopedDocSheet, false, true); michael@0: nsCSSRuleProcessor* processor = michael@0: static_cast(mScopedDocSheetRuleProcessors[i].get()); michael@0: aData->mScope = processor->GetScopeElement(); michael@0: (*aCollectorFunc)(mScopedDocSheetRuleProcessors[i], aData); michael@0: lastScopedRNs[i] = aRuleWalker->CurrentNode(); michael@0: haveImportantScopedRules[i] = !aRuleWalker->GetCheckForImportantRules(); michael@0: haveAnyImportantScopedRules = haveAnyImportantScopedRules || haveImportantScopedRules[i]; michael@0: } michael@0: aData->mScope = nullptr; michael@0: } michael@0: nsRuleNode* lastScopedRN = aRuleWalker->CurrentNode(); michael@0: aRuleWalker->SetLevel(eStyleAttrSheet, false, true); michael@0: if (mRuleProcessors[eStyleAttrSheet]) michael@0: (*aCollectorFunc)(mRuleProcessors[eStyleAttrSheet], aData); michael@0: nsRuleNode* lastStyleAttrRN = aRuleWalker->CurrentNode(); michael@0: bool haveImportantStyleAttrRules = !aRuleWalker->GetCheckForImportantRules(); michael@0: michael@0: aRuleWalker->SetLevel(eOverrideSheet, false, true); michael@0: if (mRuleProcessors[eOverrideSheet]) michael@0: (*aCollectorFunc)(mRuleProcessors[eOverrideSheet], aData); michael@0: nsRuleNode* lastOvrRN = aRuleWalker->CurrentNode(); michael@0: bool haveImportantOverrideRules = !aRuleWalker->GetCheckForImportantRules(); michael@0: michael@0: // This needs to match IsMoreSpecificThanAnimation() above. michael@0: aRuleWalker->SetLevel(eAnimationSheet, false, false); michael@0: (*aCollectorFunc)(mRuleProcessors[eAnimationSheet], aData); michael@0: michael@0: if (haveAnyImportantScopedRules) { michael@0: for (uint32_t i = lastScopedRNs.Length(); i-- != 0; ) { michael@0: aRuleWalker->SetLevel(eScopedDocSheet, true, false); michael@0: nsRuleNode* startRN = lastScopedRNs[i]; michael@0: nsRuleNode* endRN = i == 0 ? lastDocRN : lastScopedRNs[i - 1]; michael@0: if (haveImportantScopedRules[i]) { michael@0: AddImportantRules(startRN, endRN, aRuleWalker); // scoped michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: AssertNoImportantRules(startRN, endRN); michael@0: } michael@0: #endif michael@0: } michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: AssertNoImportantRules(lastScopedRN, lastDocRN); michael@0: } michael@0: #endif michael@0: michael@0: if (haveImportantDocRules) { michael@0: aRuleWalker->SetLevel(eDocSheet, true, false); michael@0: AddImportantRules(lastDocRN, lastPresHintRN, aRuleWalker); // doc michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: AssertNoImportantRules(lastDocRN, lastPresHintRN); michael@0: } michael@0: #endif michael@0: michael@0: if (haveImportantStyleAttrRules) { michael@0: aRuleWalker->SetLevel(eStyleAttrSheet, true, false); michael@0: AddImportantRules(lastStyleAttrRN, lastScopedRN, aRuleWalker); // style attr michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: AssertNoImportantRules(lastStyleAttrRN, lastScopedRN); michael@0: } michael@0: #endif michael@0: michael@0: if (haveImportantOverrideRules) { michael@0: aRuleWalker->SetLevel(eOverrideSheet, true, false); michael@0: AddImportantRules(lastOvrRN, lastStyleAttrRN, aRuleWalker); // override michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: AssertNoImportantRules(lastOvrRN, lastStyleAttrRN); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: AssertNoCSSRules(lastPresHintRN, lastUserRN); michael@0: #endif michael@0: michael@0: if (haveImportantUserRules) { michael@0: aRuleWalker->SetLevel(eUserSheet, true, false); michael@0: AddImportantRules(lastUserRN, lastAgentRN, aRuleWalker); //user michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: AssertNoImportantRules(lastUserRN, lastAgentRN); michael@0: } michael@0: #endif michael@0: michael@0: if (haveImportantUARules) { michael@0: aRuleWalker->SetLevel(eAgentSheet, true, false); michael@0: AddImportantRules(lastAgentRN, lastRestrictionRN, aRuleWalker); //agent michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: AssertNoImportantRules(lastAgentRN, lastRestrictionRN); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: AssertNoCSSRules(lastRestrictionRN, mRuleTree); michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: nsRuleNode *lastImportantRN = aRuleWalker->CurrentNode(); michael@0: #endif michael@0: aRuleWalker->SetLevel(eTransitionSheet, false, false); michael@0: (*aCollectorFunc)(mRuleProcessors[eTransitionSheet], aData); michael@0: #ifdef DEBUG michael@0: AssertNoCSSRules(aRuleWalker->CurrentNode(), lastImportantRN); michael@0: #endif michael@0: michael@0: } michael@0: michael@0: // Enumerate all the rules in a way that doesn't care about the order michael@0: // of the rules and doesn't walk !important-rules. michael@0: void michael@0: nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc, michael@0: ElementDependentRuleProcessorData* aData, michael@0: bool aWalkAllXBLStylesheets) michael@0: { michael@0: if (mRuleProcessors[eAgentSheet]) michael@0: (*aFunc)(mRuleProcessors[eAgentSheet], aData); michael@0: michael@0: bool skipUserStyles = aData->mElement->IsInNativeAnonymousSubtree(); michael@0: if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different michael@0: (*aFunc)(mRuleProcessors[eUserSheet], aData); michael@0: michael@0: if (mRuleProcessors[ePresHintSheet]) michael@0: (*aFunc)(mRuleProcessors[ePresHintSheet], aData); michael@0: michael@0: bool cutOffInheritance = false; michael@0: if (mBindingManager) { michael@0: // We can supply additional document-level sheets that should be walked. michael@0: if (aWalkAllXBLStylesheets) { michael@0: mBindingManager->WalkAllRules(aFunc, aData); michael@0: } else { michael@0: mBindingManager->WalkRules(aFunc, aData, &cutOffInheritance); michael@0: } michael@0: } michael@0: if (!skipUserStyles && !cutOffInheritance) { michael@0: if (mRuleProcessors[eDocSheet]) // NOTE: different michael@0: (*aFunc)(mRuleProcessors[eDocSheet], aData); michael@0: if (aData->mElement->IsElementInStyleScope()) { michael@0: for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) michael@0: (*aFunc)(mScopedDocSheetRuleProcessors[i], aData); michael@0: } michael@0: } michael@0: if (mRuleProcessors[eStyleAttrSheet]) michael@0: (*aFunc)(mRuleProcessors[eStyleAttrSheet], aData); michael@0: if (mRuleProcessors[eOverrideSheet]) michael@0: (*aFunc)(mRuleProcessors[eOverrideSheet], aData); michael@0: (*aFunc)(mRuleProcessors[eAnimationSheet], aData); michael@0: (*aFunc)(mRuleProcessors[eTransitionSheet], aData); michael@0: } michael@0: michael@0: static void michael@0: InitStyleScopes(TreeMatchContext& aTreeContext, Element* aElement) michael@0: { michael@0: if (aElement->IsElementInStyleScope()) { michael@0: aTreeContext.InitStyleScopes(aElement->GetParentElement()); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolveStyleFor(Element* aElement, michael@0: nsStyleContext* aParentContext) michael@0: { michael@0: TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, michael@0: aElement->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aElement); michael@0: return ResolveStyleFor(aElement, aParentContext, treeContext); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolveStyleFor(Element* aElement, michael@0: nsStyleContext* aParentContext, michael@0: TreeMatchContext& aTreeMatchContext) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: NS_ASSERTION(aElement, "aElement must not be null"); michael@0: michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: aTreeMatchContext.ResetForUnvisitedMatching(); michael@0: ElementRuleProcessorData data(PresContext(), aElement, &ruleWalker, michael@0: aTreeMatchContext); michael@0: WalkDisableTextZoomRule(aElement, &ruleWalker); michael@0: FileRules(EnumRulesMatching, &data, aElement, michael@0: &ruleWalker); michael@0: michael@0: nsRuleNode *ruleNode = ruleWalker.CurrentNode(); michael@0: nsRuleNode *visitedRuleNode = nullptr; michael@0: michael@0: if (aTreeMatchContext.HaveRelevantLink()) { michael@0: aTreeMatchContext.ResetForVisitedMatching(); michael@0: ruleWalker.Reset(); michael@0: FileRules(EnumRulesMatching, &data, aElement, michael@0: &ruleWalker); michael@0: visitedRuleNode = ruleWalker.CurrentNode(); michael@0: } michael@0: michael@0: uint32_t flags = eDoAnimation; michael@0: if (nsCSSRuleProcessor::IsLink(aElement)) { michael@0: flags |= eIsLink; michael@0: } michael@0: if (nsCSSRuleProcessor::GetContentState(aElement, aTreeMatchContext). michael@0: HasState(NS_EVENT_STATE_VISITED)) { michael@0: flags |= eIsVisitedLink; michael@0: } michael@0: if (aTreeMatchContext.mSkippingFlexItemStyleFixup) { michael@0: flags |= eSkipFlexItemStyleFixup; michael@0: } michael@0: michael@0: return GetContext(aParentContext, ruleNode, visitedRuleNode, michael@0: nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement, michael@0: aElement, flags); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext, michael@0: const nsTArray< nsCOMPtr > &aRules) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: // FIXME: Perhaps this should be passed in, but it probably doesn't michael@0: // matter. michael@0: ruleWalker.SetLevel(eDocSheet, false, false); michael@0: for (uint32_t i = 0; i < aRules.Length(); i++) { michael@0: ruleWalker.ForwardOnPossiblyCSSRule(aRules.ElementAt(i)); michael@0: } michael@0: michael@0: return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr, michael@0: nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement, michael@0: nullptr, eNoFlags); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext, michael@0: nsStyleContext* aOldStyle, michael@0: const nsTArray& aRules) michael@0: { michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: for (int32_t i = aRules.Length() - 1; i >= 0; --i) { michael@0: ruleWalker.SetLevel(aRules[i].mLevel, false, false); michael@0: ruleWalker.ForwardOnPossiblyCSSRule(aRules[i].mRule); michael@0: } michael@0: michael@0: uint32_t flags = eNoFlags; michael@0: if (aOldStyle->IsLinkContext()) { michael@0: flags |= eIsLink; michael@0: } michael@0: if (aOldStyle->RelevantLinkVisited()) { michael@0: flags |= eIsVisitedLink; michael@0: } michael@0: michael@0: return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr, michael@0: nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement, michael@0: nullptr, flags); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext, michael@0: const nsCOMArray &aRules) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: ruleWalker.SetCurrentNode(aBaseContext->RuleNode()); michael@0: // FIXME: Perhaps this should be passed in, but it probably doesn't michael@0: // matter. michael@0: ruleWalker.SetLevel(eDocSheet, false, false); michael@0: for (int32_t i = 0; i < aRules.Count(); i++) { michael@0: ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i)); michael@0: } michael@0: michael@0: nsRuleNode *ruleNode = ruleWalker.CurrentNode(); michael@0: nsRuleNode *visitedRuleNode = nullptr; michael@0: michael@0: if (aBaseContext->GetStyleIfVisited()) { michael@0: ruleWalker.SetCurrentNode(aBaseContext->GetStyleIfVisited()->RuleNode()); michael@0: for (int32_t i = 0; i < aRules.Count(); i++) { michael@0: ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i)); michael@0: } michael@0: visitedRuleNode = ruleWalker.CurrentNode(); michael@0: } michael@0: michael@0: uint32_t flags = eNoFlags; michael@0: if (aBaseContext->IsLinkContext()) { michael@0: flags |= eIsLink; michael@0: } michael@0: if (aBaseContext->RelevantLinkVisited()) { michael@0: flags |= eIsVisitedLink; michael@0: } michael@0: return GetContext(aBaseContext->GetParent(), ruleNode, visitedRuleNode, michael@0: aBaseContext->GetPseudo(), michael@0: aBaseContext->GetPseudoType(), michael@0: nullptr, flags); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext) michael@0: { michael@0: return GetContext(aParentContext, mRuleTree, nullptr, michael@0: nsCSSAnonBoxes::mozNonElement, michael@0: nsCSSPseudoElements::ePseudo_AnonBox, nullptr, michael@0: eNoFlags); michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType, michael@0: nsRuleWalker* aRuleWalker) michael@0: { michael@0: // This needs to match GetPseudoRestriction in nsRuleNode.cpp. michael@0: aRuleWalker->SetLevel(eAgentSheet, false, false); michael@0: if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter) michael@0: aRuleWalker->Forward(mFirstLetterRule); michael@0: else if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLine) michael@0: aRuleWalker->Forward(mFirstLineRule); michael@0: else if (aPseudoType == nsCSSPseudoElements::ePseudo_mozPlaceholder) michael@0: aRuleWalker->Forward(mPlaceholderRule); michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::WalkDisableTextZoomRule(Element* aElement, nsRuleWalker* aRuleWalker) michael@0: { michael@0: aRuleWalker->SetLevel(eAgentSheet, false, false); michael@0: if (aElement->IsSVG(nsGkAtoms::text)) michael@0: aRuleWalker->Forward(mDisableTextZoomStyleRule); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolvePseudoElementStyle(Element* aParentElement, michael@0: nsCSSPseudoElements::Type aType, michael@0: nsStyleContext* aParentContext, michael@0: Element* aPseudoElement) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: michael@0: NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount, michael@0: "must have pseudo element type"); michael@0: NS_ASSERTION(aParentElement, "Must have parent element"); michael@0: michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, michael@0: aParentElement->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aParentElement); michael@0: PseudoElementRuleProcessorData data(PresContext(), aParentElement, michael@0: &ruleWalker, aType, treeContext, michael@0: aPseudoElement); michael@0: WalkRestrictionRule(aType, &ruleWalker); michael@0: FileRules(EnumRulesMatching, &data, michael@0: aParentElement, &ruleWalker); michael@0: michael@0: nsRuleNode *ruleNode = ruleWalker.CurrentNode(); michael@0: nsRuleNode *visitedRuleNode = nullptr; michael@0: michael@0: if (treeContext.HaveRelevantLink()) { michael@0: treeContext.ResetForVisitedMatching(); michael@0: ruleWalker.Reset(); michael@0: WalkRestrictionRule(aType, &ruleWalker); michael@0: FileRules(EnumRulesMatching, &data, michael@0: aParentElement, &ruleWalker); michael@0: visitedRuleNode = ruleWalker.CurrentNode(); michael@0: } michael@0: michael@0: // For pseudos, |data.IsLink()| being true means that michael@0: // our parent node is a link. michael@0: uint32_t flags = eNoFlags; michael@0: if (aType == nsCSSPseudoElements::ePseudo_before || michael@0: aType == nsCSSPseudoElements::ePseudo_after) { michael@0: flags |= eDoAnimation; michael@0: } else { michael@0: // Flex containers don't expect to have any pseudo-element children aside michael@0: // from ::before and ::after. So if we have such a child, we're not michael@0: // actually in a flex container, and we should skip flex-item style fixup. michael@0: flags |= eSkipFlexItemStyleFixup; michael@0: } michael@0: michael@0: return GetContext(aParentContext, ruleNode, visitedRuleNode, michael@0: nsCSSPseudoElements::GetPseudoAtom(aType), aType, michael@0: aParentElement, flags); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ProbePseudoElementStyle(Element* aParentElement, michael@0: nsCSSPseudoElements::Type aType, michael@0: nsStyleContext* aParentContext) michael@0: { michael@0: TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, michael@0: aParentElement->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aParentElement); michael@0: return ProbePseudoElementStyle(aParentElement, aType, aParentContext, michael@0: treeContext); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ProbePseudoElementStyle(Element* aParentElement, michael@0: nsCSSPseudoElements::Type aType, michael@0: nsStyleContext* aParentContext, michael@0: TreeMatchContext& aTreeMatchContext, michael@0: Element* aPseudoElement) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: michael@0: NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount, michael@0: "must have pseudo element type"); michael@0: NS_ASSERTION(aParentElement, "aParentElement must not be null"); michael@0: michael@0: nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType); michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: aTreeMatchContext.ResetForUnvisitedMatching(); michael@0: PseudoElementRuleProcessorData data(PresContext(), aParentElement, michael@0: &ruleWalker, aType, aTreeMatchContext, michael@0: aPseudoElement); michael@0: WalkRestrictionRule(aType, &ruleWalker); michael@0: // not the root if there was a restriction rule michael@0: nsRuleNode *adjustedRoot = ruleWalker.CurrentNode(); michael@0: FileRules(EnumRulesMatching, &data, michael@0: aParentElement, &ruleWalker); michael@0: michael@0: nsRuleNode *ruleNode = ruleWalker.CurrentNode(); michael@0: if (ruleNode == adjustedRoot) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRuleNode *visitedRuleNode = nullptr; michael@0: michael@0: if (aTreeMatchContext.HaveRelevantLink()) { michael@0: aTreeMatchContext.ResetForVisitedMatching(); michael@0: ruleWalker.Reset(); michael@0: WalkRestrictionRule(aType, &ruleWalker); michael@0: FileRules(EnumRulesMatching, &data, michael@0: aParentElement, &ruleWalker); michael@0: visitedRuleNode = ruleWalker.CurrentNode(); michael@0: } michael@0: michael@0: // For pseudos, |data.IsLink()| being true means that michael@0: // our parent node is a link. michael@0: uint32_t flags = eNoFlags; michael@0: if (aType == nsCSSPseudoElements::ePseudo_before || michael@0: aType == nsCSSPseudoElements::ePseudo_after) { michael@0: flags |= eDoAnimation; michael@0: } else { michael@0: // Flex containers don't expect to have any pseudo-element children aside michael@0: // from ::before and ::after. So if we have such a child, we're not michael@0: // actually in a flex container, and we should skip flex-item style fixup. michael@0: flags |= eSkipFlexItemStyleFixup; michael@0: } michael@0: michael@0: nsRefPtr result = michael@0: GetContext(aParentContext, ruleNode, visitedRuleNode, michael@0: pseudoTag, aType, michael@0: aParentElement, flags); michael@0: michael@0: // For :before and :after pseudo-elements, having display: none or no michael@0: // 'content' property is equivalent to not having the pseudo-element michael@0: // at all. michael@0: if (result && michael@0: (pseudoTag == nsCSSPseudoElements::before || michael@0: pseudoTag == nsCSSPseudoElements::after)) { michael@0: const nsStyleDisplay *display = result->StyleDisplay(); michael@0: const nsStyleContent *content = result->StyleContent(); michael@0: // XXXldb What is contentCount for |content: ""|? michael@0: if (display->mDisplay == NS_STYLE_DISPLAY_NONE || michael@0: content->ContentCount() == 0) { michael@0: result = nullptr; michael@0: } michael@0: } michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, michael@0: nsStyleContext* aParentContext) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: michael@0: #ifdef DEBUG michael@0: bool isAnonBox = nsCSSAnonBoxes::IsAnonBox(aPseudoTag) michael@0: #ifdef MOZ_XUL michael@0: && !nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag) michael@0: #endif michael@0: ; michael@0: NS_PRECONDITION(isAnonBox, "Unexpected pseudo"); michael@0: #endif michael@0: michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: AnonBoxRuleProcessorData data(PresContext(), aPseudoTag, &ruleWalker); michael@0: FileRules(EnumRulesMatching, &data, nullptr, michael@0: &ruleWalker); michael@0: michael@0: if (aPseudoTag == nsCSSAnonBoxes::pageContent) { michael@0: // Add any @page rules that are specified. michael@0: nsTArray rules; michael@0: nsTArray importantRules; michael@0: nsPresContext* presContext = PresContext(); michael@0: presContext->StyleSet()->AppendPageRules(presContext, rules); michael@0: for (uint32_t i = 0, i_end = rules.Length(); i != i_end; ++i) { michael@0: ruleWalker.Forward(rules[i]); michael@0: css::ImportantRule* importantRule = rules[i]->GetImportantRule(); michael@0: if (importantRule) { michael@0: importantRules.AppendElement(importantRule); michael@0: } michael@0: } michael@0: for (uint32_t i = 0, i_end = importantRules.Length(); i != i_end; ++i) { michael@0: ruleWalker.Forward(importantRules[i]); michael@0: } michael@0: } michael@0: michael@0: return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr, michael@0: aPseudoTag, nsCSSPseudoElements::ePseudo_AnonBox, michael@0: nullptr, eNoFlags); michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: already_AddRefed michael@0: nsStyleSet::ResolveXULTreePseudoStyle(Element* aParentElement, michael@0: nsIAtom* aPseudoTag, michael@0: nsStyleContext* aParentContext, michael@0: nsICSSPseudoComparator* aComparator) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: michael@0: NS_ASSERTION(aPseudoTag, "must have pseudo tag"); michael@0: NS_ASSERTION(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag), michael@0: "Unexpected pseudo"); michael@0: michael@0: nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); michael@0: TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited, michael@0: aParentElement->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aParentElement); michael@0: XULTreeRuleProcessorData data(PresContext(), aParentElement, &ruleWalker, michael@0: aPseudoTag, aComparator, treeContext); michael@0: FileRules(EnumRulesMatching, &data, aParentElement, michael@0: &ruleWalker); michael@0: michael@0: nsRuleNode *ruleNode = ruleWalker.CurrentNode(); michael@0: nsRuleNode *visitedRuleNode = nullptr; michael@0: michael@0: if (treeContext.HaveRelevantLink()) { michael@0: treeContext.ResetForVisitedMatching(); michael@0: ruleWalker.Reset(); michael@0: FileRules(EnumRulesMatching, &data, michael@0: aParentElement, &ruleWalker); michael@0: visitedRuleNode = ruleWalker.CurrentNode(); michael@0: } michael@0: michael@0: return GetContext(aParentContext, ruleNode, visitedRuleNode, michael@0: // For pseudos, |data.IsLink()| being true means that michael@0: // our parent node is a link. michael@0: aPseudoTag, nsCSSPseudoElements::ePseudo_XULTree, michael@0: nullptr, eNoFlags); michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: nsStyleSet::AppendFontFaceRules(nsPresContext* aPresContext, michael@0: nsTArray& aArray) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, false); michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { michael@0: if (gCSSSheetTypes[i] == eScopedDocSheet) michael@0: continue; michael@0: nsCSSRuleProcessor *ruleProc = static_cast michael@0: (mRuleProcessors[gCSSSheetTypes[i]].get()); michael@0: if (ruleProc && !ruleProc->AppendFontFaceRules(aPresContext, aArray)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: nsCSSKeyframesRule* michael@0: nsStyleSet::KeyframesRuleForName(nsPresContext* aPresContext, michael@0: const nsString& aName) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, nullptr); michael@0: michael@0: for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) { michael@0: if (gCSSSheetTypes[i] == eScopedDocSheet) michael@0: continue; michael@0: nsCSSRuleProcessor *ruleProc = static_cast michael@0: (mRuleProcessors[gCSSSheetTypes[i]].get()); michael@0: if (!ruleProc) michael@0: continue; michael@0: nsCSSKeyframesRule* result = michael@0: ruleProc->KeyframesRuleForName(aPresContext, aName); michael@0: if (result) michael@0: return result; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsStyleSet::AppendFontFeatureValuesRules(nsPresContext* aPresContext, michael@0: nsTArray& aArray) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, false); michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { michael@0: nsCSSRuleProcessor *ruleProc = static_cast michael@0: (mRuleProcessors[gCSSSheetTypes[i]].get()); michael@0: if (ruleProc && michael@0: !ruleProc->AppendFontFeatureValuesRules(aPresContext, aArray)) michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::GetFontFeatureValuesLookup() michael@0: { michael@0: if (mInitFontFeatureValuesLookup) { michael@0: mInitFontFeatureValuesLookup = false; michael@0: michael@0: nsTArray rules; michael@0: AppendFontFeatureValuesRules(PresContext(), rules); michael@0: michael@0: mFontFeatureValuesLookup = new gfxFontFeatureValueSet(); michael@0: michael@0: uint32_t i, numRules = rules.Length(); michael@0: for (i = 0; i < numRules; i++) { michael@0: nsCSSFontFeatureValuesRule *rule = rules[i]; michael@0: michael@0: const nsTArray& familyList = rule->GetFamilyList(); michael@0: const nsTArray& michael@0: featureValues = rule->GetFeatureValues(); michael@0: michael@0: // for each family michael@0: uint32_t f, numFam; michael@0: michael@0: numFam = familyList.Length(); michael@0: for (f = 0; f < numFam; f++) { michael@0: const nsString& family = familyList.ElementAt(f); michael@0: nsAutoString silly(family); michael@0: mFontFeatureValuesLookup->AddFontFeatureValues(silly, featureValues); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsRefPtr lookup = mFontFeatureValuesLookup; michael@0: return lookup.forget(); michael@0: } michael@0: michael@0: bool michael@0: nsStyleSet::AppendPageRules(nsPresContext* aPresContext, michael@0: nsTArray& aArray) michael@0: { michael@0: NS_ENSURE_FALSE(mInShutdown, false); michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { michael@0: if (gCSSSheetTypes[i] == eScopedDocSheet) michael@0: continue; michael@0: nsCSSRuleProcessor* ruleProc = static_cast michael@0: (mRuleProcessors[gCSSSheetTypes[i]].get()); michael@0: if (ruleProc && !ruleProc->AppendPageRules(aPresContext, aArray)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::BeginShutdown(nsPresContext* aPresContext) michael@0: { michael@0: mInShutdown = 1; michael@0: mRoots.Clear(); // no longer valid, since we won't keep it up to date michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::Shutdown(nsPresContext* aPresContext) michael@0: { michael@0: mRuleTree->Destroy(); michael@0: mRuleTree = nullptr; michael@0: michael@0: // We can have old rule trees either because: michael@0: // (1) we failed the assertions in EndReconstruct, or michael@0: // (2) we're shutting down within a reconstruct (see bug 462392) michael@0: for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) { michael@0: --i; michael@0: mOldRuleTrees[i]->Destroy(); michael@0: } michael@0: mOldRuleTrees.Clear(); michael@0: } michael@0: michael@0: static const uint32_t kGCInterval = 300; michael@0: michael@0: void michael@0: nsStyleSet::NotifyStyleContextDestroyed(nsPresContext* aPresContext, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: if (mInShutdown) michael@0: return; michael@0: michael@0: // Remove style contexts from mRoots even if mOldRuleTree is non-null. This michael@0: // could be a style context from the new ruletree! michael@0: if (!aStyleContext->GetParent()) { michael@0: mRoots.RemoveElement(aStyleContext); michael@0: } michael@0: michael@0: if (mInReconstruct) michael@0: return; michael@0: michael@0: if (mUnusedRuleNodeCount >= kGCInterval) { michael@0: GCRuleTrees(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsStyleSet::GCRuleTrees() michael@0: { michael@0: mUnusedRuleNodeCount = 0; michael@0: michael@0: // Mark the style context tree by marking all style contexts which michael@0: // have no parent, which will mark all descendants. This will reach michael@0: // style contexts in the undisplayed map and "additional style michael@0: // contexts" since they are descendants of the roots. michael@0: for (int32_t i = mRoots.Length() - 1; i >= 0; --i) { michael@0: mRoots[i]->Mark(); michael@0: } michael@0: michael@0: // Sweep the rule tree. michael@0: #ifdef DEBUG michael@0: bool deleted = michael@0: #endif michael@0: mRuleTree->Sweep(); michael@0: NS_ASSERTION(!deleted, "Root node must not be gc'd"); michael@0: michael@0: // Sweep the old rule trees. michael@0: for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) { michael@0: --i; michael@0: if (mOldRuleTrees[i]->Sweep()) { michael@0: // It was deleted, as it should be. michael@0: mOldRuleTrees.RemoveElementAt(i); michael@0: } else { michael@0: NS_NOTREACHED("old rule tree still referenced"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Return an equivalent to aRuleNode with both animation and transition michael@0: * rules removed, and post a restyle if needed. michael@0: */ michael@0: static inline nsRuleNode* michael@0: SkipAnimationRules(nsRuleNode* aRuleNode, Element* aElement, bool isPseudo) michael@0: { michael@0: nsRuleNode* ruleNode = aRuleNode; michael@0: // The transition rule must be at the top of the cascade. michael@0: if (!ruleNode->IsRoot() && michael@0: ruleNode->GetLevel() == nsStyleSet::eTransitionSheet) { michael@0: ruleNode = ruleNode->GetParent(); michael@0: } michael@0: NS_ABORT_IF_FALSE(ruleNode->IsRoot() || michael@0: ruleNode->GetLevel() != nsStyleSet::eTransitionSheet, michael@0: "can't have more than one transition rule"); michael@0: michael@0: // Use our existing ReplaceAnimationRule function to replace the michael@0: // animation rule, if present. michael@0: nsIStyleRule* animationRule = GetAnimationRule(ruleNode); michael@0: if (animationRule) { michael@0: ruleNode = ReplaceAnimationRule(ruleNode, animationRule, nullptr); michael@0: } michael@0: michael@0: if (ruleNode != aRuleNode) { michael@0: NS_ASSERTION(aElement, "How can we have transition rules but no element?"); michael@0: // Need to do an animation restyle, just like michael@0: // nsTransitionManager::WalkTransitionRule and michael@0: // nsAnimationManager::GetAnimationRule would. michael@0: nsRestyleHint hint = isPseudo ? eRestyle_Subtree : eRestyle_Self; michael@0: aRuleNode->PresContext()->PresShell()->RestyleForAnimation(aElement, hint); michael@0: } michael@0: return ruleNode; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsStyleSet::ReparentStyleContext(nsStyleContext* aStyleContext, michael@0: nsStyleContext* aNewParentContext, michael@0: Element* aElement) michael@0: { michael@0: MOZ_ASSERT(aStyleContext, "aStyleContext must not be null"); michael@0: michael@0: // This short-circuit is OK because we don't call TryStartingTransition michael@0: // during style reresolution if the style context pointer hasn't changed. michael@0: if (aStyleContext->GetParent() == aNewParentContext) { michael@0: nsRefPtr ret = aStyleContext; michael@0: return ret.forget(); michael@0: } michael@0: michael@0: nsIAtom* pseudoTag = aStyleContext->GetPseudo(); michael@0: nsCSSPseudoElements::Type pseudoType = aStyleContext->GetPseudoType(); michael@0: nsRuleNode* ruleNode = aStyleContext->RuleNode(); michael@0: michael@0: // Skip transition rules as needed just like michael@0: // nsTransitionManager::WalkTransitionRule would. michael@0: bool skipAnimationRules = PresContext()->IsProcessingRestyles() && michael@0: !PresContext()->IsProcessingAnimationStyleChange(); michael@0: if (skipAnimationRules) { michael@0: // Make sure that we're not using transition rules or animation rules for michael@0: // our new style context. If we need them, an animation restyle will michael@0: // provide. michael@0: ruleNode = michael@0: SkipAnimationRules(ruleNode, aElement, michael@0: pseudoType != michael@0: nsCSSPseudoElements::ePseudo_NotPseudoElement); michael@0: } michael@0: michael@0: nsRuleNode* visitedRuleNode = nullptr; michael@0: nsStyleContext* visitedContext = aStyleContext->GetStyleIfVisited(); michael@0: // Reparenting a style context just changes where we inherit from, michael@0: // not what rules we match or what our DOM looks like. In michael@0: // particular, it doesn't change whether this is a style context for michael@0: // a link. michael@0: if (visitedContext) { michael@0: visitedRuleNode = visitedContext->RuleNode(); michael@0: // Again, skip transition rules as needed michael@0: if (skipAnimationRules) { michael@0: // FIXME do something here for animations? michael@0: visitedRuleNode = michael@0: SkipAnimationRules(visitedRuleNode, aElement, michael@0: pseudoType != michael@0: nsCSSPseudoElements::ePseudo_NotPseudoElement); michael@0: } michael@0: } michael@0: michael@0: uint32_t flags = eNoFlags; michael@0: if (aStyleContext->IsLinkContext()) { michael@0: flags |= eIsLink; michael@0: } michael@0: michael@0: // If we're a style context for a link, then we already know whether michael@0: // our relevant link is visited, since that does not depend on our michael@0: // parent. Otherwise, we need to match aNewParentContext. michael@0: bool relevantLinkVisited = aStyleContext->IsLinkContext() ? michael@0: aStyleContext->RelevantLinkVisited() : michael@0: aNewParentContext->RelevantLinkVisited(); michael@0: michael@0: if (relevantLinkVisited) { michael@0: flags |= eIsVisitedLink; michael@0: } michael@0: michael@0: if (pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement || michael@0: pseudoType == nsCSSPseudoElements::ePseudo_before || michael@0: pseudoType == nsCSSPseudoElements::ePseudo_after) { michael@0: flags |= eDoAnimation; michael@0: } michael@0: michael@0: if (aElement && aElement->IsRootOfAnonymousSubtree()) { michael@0: // For anonymous subtree roots, don't tweak "display" value based on michael@0: // whether or not the parent is styled as a flex container. (If the parent michael@0: // has anonymous-subtree kids, then we know it's not actually going to get michael@0: // a flex container frame, anyway.) michael@0: flags |= eSkipFlexItemStyleFixup; michael@0: } michael@0: michael@0: return GetContext(aNewParentContext, ruleNode, visitedRuleNode, michael@0: pseudoTag, pseudoType, michael@0: aElement, flags); michael@0: } michael@0: michael@0: struct MOZ_STACK_CLASS StatefulData : public StateRuleProcessorData { michael@0: StatefulData(nsPresContext* aPresContext, Element* aElement, michael@0: EventStates aStateMask, TreeMatchContext& aTreeMatchContext) michael@0: : StateRuleProcessorData(aPresContext, aElement, aStateMask, michael@0: aTreeMatchContext), michael@0: mHint(nsRestyleHint(0)) michael@0: {} michael@0: nsRestyleHint mHint; michael@0: }; michael@0: michael@0: struct MOZ_STACK_CLASS StatefulPseudoElementData : public PseudoElementStateRuleProcessorData { michael@0: StatefulPseudoElementData(nsPresContext* aPresContext, Element* aElement, michael@0: EventStates aStateMask, nsCSSPseudoElements::Type aPseudoType, michael@0: TreeMatchContext& aTreeMatchContext, Element* aPseudoElement) michael@0: : PseudoElementStateRuleProcessorData(aPresContext, aElement, aStateMask, michael@0: aPseudoType, aTreeMatchContext, michael@0: aPseudoElement), michael@0: mHint(nsRestyleHint(0)) michael@0: {} michael@0: nsRestyleHint mHint; michael@0: }; michael@0: michael@0: static bool SheetHasDocumentStateStyle(nsIStyleRuleProcessor* aProcessor, michael@0: void *aData) michael@0: { michael@0: StatefulData* data = (StatefulData*)aData; michael@0: if (aProcessor->HasDocumentStateDependentStyle(data)) { michael@0: data->mHint = eRestyle_Self; michael@0: return false; // don't continue michael@0: } michael@0: return true; // continue michael@0: } michael@0: michael@0: // Test if style is dependent on a document state. michael@0: bool michael@0: nsStyleSet::HasDocumentStateDependentStyle(nsPresContext* aPresContext, michael@0: nsIContent* aContent, michael@0: EventStates aStateMask) michael@0: { michael@0: if (!aContent || !aContent->IsElement()) michael@0: return false; michael@0: michael@0: TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, michael@0: aContent->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aContent->AsElement()); michael@0: StatefulData data(aPresContext, aContent->AsElement(), aStateMask, michael@0: treeContext); michael@0: WalkRuleProcessors(SheetHasDocumentStateStyle, &data, true); michael@0: return data.mHint != 0; michael@0: } michael@0: michael@0: static bool SheetHasStatefulStyle(nsIStyleRuleProcessor* aProcessor, michael@0: void *aData) michael@0: { michael@0: StatefulData* data = (StatefulData*)aData; michael@0: nsRestyleHint hint = aProcessor->HasStateDependentStyle(data); michael@0: data->mHint = nsRestyleHint(data->mHint | hint); michael@0: return true; // continue michael@0: } michael@0: michael@0: static bool SheetHasStatefulPseudoElementStyle(nsIStyleRuleProcessor* aProcessor, michael@0: void *aData) michael@0: { michael@0: StatefulPseudoElementData* data = (StatefulPseudoElementData*)aData; michael@0: nsRestyleHint hint = aProcessor->HasStateDependentStyle(data); michael@0: data->mHint = nsRestyleHint(data->mHint | hint); michael@0: return true; // continue michael@0: } michael@0: michael@0: // Test if style is dependent on content state michael@0: nsRestyleHint michael@0: nsStyleSet::HasStateDependentStyle(nsPresContext* aPresContext, michael@0: Element* aElement, michael@0: EventStates aStateMask) michael@0: { michael@0: TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, michael@0: aElement->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aElement); michael@0: StatefulData data(aPresContext, aElement, aStateMask, treeContext); michael@0: WalkRuleProcessors(SheetHasStatefulStyle, &data, false); michael@0: return data.mHint; michael@0: } michael@0: michael@0: nsRestyleHint michael@0: nsStyleSet::HasStateDependentStyle(nsPresContext* aPresContext, michael@0: Element* aElement, michael@0: nsCSSPseudoElements::Type aPseudoType, michael@0: Element* aPseudoElement, michael@0: EventStates aStateMask) michael@0: { michael@0: TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, michael@0: aElement->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aElement); michael@0: StatefulPseudoElementData data(aPresContext, aElement, aStateMask, michael@0: aPseudoType, treeContext, aPseudoElement); michael@0: WalkRuleProcessors(SheetHasStatefulPseudoElementStyle, &data, false); michael@0: return data.mHint; michael@0: } michael@0: michael@0: struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData { michael@0: AttributeData(nsPresContext* aPresContext, michael@0: Element* aElement, nsIAtom* aAttribute, int32_t aModType, michael@0: bool aAttrHasChanged, TreeMatchContext& aTreeMatchContext) michael@0: : AttributeRuleProcessorData(aPresContext, aElement, aAttribute, aModType, michael@0: aAttrHasChanged, aTreeMatchContext), michael@0: mHint(nsRestyleHint(0)) michael@0: {} michael@0: nsRestyleHint mHint; michael@0: }; michael@0: michael@0: static bool michael@0: SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData) michael@0: { michael@0: AttributeData* data = (AttributeData*)aData; michael@0: nsRestyleHint hint = aProcessor->HasAttributeDependentStyle(data); michael@0: data->mHint = nsRestyleHint(data->mHint | hint); michael@0: return true; // continue michael@0: } michael@0: michael@0: // Test if style is dependent on content state michael@0: nsRestyleHint michael@0: nsStyleSet::HasAttributeDependentStyle(nsPresContext* aPresContext, michael@0: Element* aElement, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType, michael@0: bool aAttrHasChanged) michael@0: { michael@0: TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, michael@0: aElement->OwnerDoc()); michael@0: InitStyleScopes(treeContext, aElement); michael@0: AttributeData data(aPresContext, aElement, aAttribute, michael@0: aModType, aAttrHasChanged, treeContext); michael@0: WalkRuleProcessors(SheetHasAttributeStyle, &data, false); michael@0: return data.mHint; michael@0: } michael@0: michael@0: bool michael@0: nsStyleSet::MediumFeaturesChanged(nsPresContext* aPresContext) michael@0: { michael@0: // We can't use WalkRuleProcessors without a content node. michael@0: bool stylesChanged = false; michael@0: for (uint32_t i = 0; i < ArrayLength(mRuleProcessors); ++i) { michael@0: nsIStyleRuleProcessor *processor = mRuleProcessors[i]; michael@0: if (!processor) { michael@0: continue; michael@0: } michael@0: bool thisChanged = processor->MediumFeaturesChanged(aPresContext); michael@0: stylesChanged = stylesChanged || thisChanged; michael@0: } michael@0: for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); ++i) { michael@0: nsIStyleRuleProcessor *processor = mScopedDocSheetRuleProcessors[i]; michael@0: bool thisChanged = processor->MediumFeaturesChanged(aPresContext); michael@0: stylesChanged = stylesChanged || thisChanged; michael@0: } michael@0: michael@0: if (mBindingManager) { michael@0: bool thisChanged = false; michael@0: mBindingManager->MediumFeaturesChanged(aPresContext, &thisChanged); michael@0: stylesChanged = stylesChanged || thisChanged; michael@0: } michael@0: michael@0: return stylesChanged; michael@0: } michael@0: michael@0: nsCSSStyleSheet::EnsureUniqueInnerResult michael@0: nsStyleSet::EnsureUniqueInnerOnCSSSheets() michael@0: { michael@0: nsAutoTArray queue; michael@0: for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { michael@0: nsCOMArray &sheets = mSheets[gCSSSheetTypes[i]]; michael@0: for (uint32_t j = 0, j_end = sheets.Count(); j < j_end; ++j) { michael@0: nsCSSStyleSheet *sheet = static_cast(sheets[j]); michael@0: queue.AppendElement(sheet); michael@0: } michael@0: } michael@0: michael@0: if (mBindingManager) { michael@0: mBindingManager->AppendAllSheets(queue); michael@0: } michael@0: michael@0: nsCSSStyleSheet::EnsureUniqueInnerResult res = michael@0: nsCSSStyleSheet::eUniqueInner_AlreadyUnique; michael@0: while (!queue.IsEmpty()) { michael@0: uint32_t idx = queue.Length() - 1; michael@0: nsCSSStyleSheet *sheet = queue[idx]; michael@0: queue.RemoveElementAt(idx); michael@0: michael@0: nsCSSStyleSheet::EnsureUniqueInnerResult sheetRes = michael@0: sheet->EnsureUniqueInner(); michael@0: if (sheetRes == nsCSSStyleSheet::eUniqueInner_ClonedInner) { michael@0: res = sheetRes; michael@0: } michael@0: michael@0: // Enqueue all the sheet's children. michael@0: sheet->AppendAllChildSheets(queue); michael@0: } michael@0: return res; michael@0: } michael@0: michael@0: nsIStyleRule* michael@0: nsStyleSet::InitialStyleRule() michael@0: { michael@0: if (!mInitialStyleRule) { michael@0: mInitialStyleRule = new nsInitialStyleRule; michael@0: } michael@0: return mInitialStyleRule; michael@0: }