From ec57be653abace4d9f20d8b6cb2e8b73e09d39de Mon Sep 17 00:00:00 2001 From: Francis Dominic Fajardo Date: Sat, 19 Jul 2025 22:14:29 +0800 Subject: [PATCH] Issue #2828 - Part 7: Walk child rule processors separately and ensure inverted cascade layer order is enforced for important rules Excluding @layers declared in scoped styles because those aren't specced and are in an entirely separate cascade origin. --- layout/style/nsStyleSet.cpp | 168 ++++++++++++++++++++++++++++++------ 1 file changed, 142 insertions(+), 26 deletions(-) diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 17330a0c51..11e7f18e22 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1086,6 +1086,43 @@ nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode, } #endif +static MOZ_ALWAYS_INLINE void +FileRulesFromAllChildProcessors( + nsCOMPtr aParentProcessor, + nsIStyleRuleProcessor::EnumFunc aCollectorFunc, + RuleProcessorData* aData, + nsRuleWalker* aRuleWalker, + mozilla::SheetType aLevel, + nsRuleNode*& aLastRN, + nsTArray& aLastRNs, + nsTArray& aHaveImportantOriginRules, + bool& aHaveAnyImportantOriginRules) +{ + if (!aParentProcessor) { + return; + } + + nsTArray>* processors = + aParentProcessor->GetChildRuleProcessors(); + if (!processors) { + (*aCollectorFunc)(aParentProcessor, aData); + return; + } + + for (nsCOMPtr processor : *processors) { + aRuleWalker->SetLevel(aLevel, false, true); + (*aCollectorFunc)(processor, aData); + + aLastRN = aRuleWalker->CurrentNode(); + aLastRNs.AppendElement(aLastRN); + + bool haveImportantRules = !aRuleWalker->GetCheckForImportantRules(); + aHaveImportantOriginRules.AppendElement(haveImportantRules); + aHaveAnyImportantOriginRules = + aHaveAnyImportantOriginRules || haveImportantRules; + } +} + // Enumerate the rules in a way that cares about the order of the rules. void nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, @@ -1097,6 +1134,9 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, NS_ASSERTION(mBatching == 0, "rule processors out of date"); + bool skipUserStyles = + aElement && aElement->IsInNativeAnonymousSubtree(); + // Cascading order: // [least important] // - UA normal rules = Agent normal @@ -1117,19 +1157,41 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, // this will be either the root or one of the restriction rules. nsRuleNode* lastRestrictionRN = aRuleWalker->CurrentNode(); - aRuleWalker->SetLevel(SheetType::Agent, false, true); - if (mRuleProcessors[SheetType::Agent]) - (*aCollectorFunc)(mRuleProcessors[SheetType::Agent], aData); - nsRuleNode* lastAgentRN = aRuleWalker->CurrentNode(); - bool haveImportantUARules = !aRuleWalker->GetCheckForImportantRules(); + nsRuleNode* lastAgentRN = nullptr; + nsTArray lastAgentRNs; + nsTArray haveImportantAgentRules; + bool haveAnyImportantAgentRules = false; + FileRulesFromAllChildProcessors(mRuleProcessors[SheetType::Agent], + aCollectorFunc, + aData, + aRuleWalker, + SheetType::Agent, + lastAgentRN, + lastAgentRNs, + haveImportantAgentRules, + haveAnyImportantAgentRules); + if (!lastAgentRN) { + lastAgentRN = aRuleWalker->CurrentNode(); + } - aRuleWalker->SetLevel(SheetType::User, false, true); - bool skipUserStyles = - aElement && aElement->IsInNativeAnonymousSubtree(); - if (!skipUserStyles && mRuleProcessors[SheetType::User]) // NOTE: different - (*aCollectorFunc)(mRuleProcessors[SheetType::User], aData); - nsRuleNode* lastUserRN = aRuleWalker->CurrentNode(); - bool haveImportantUserRules = !aRuleWalker->GetCheckForImportantRules(); + nsRuleNode* lastUserRN = nullptr; + nsTArray lastUserRNs; + nsTArray haveImportantUserRules; + bool haveAnyImportantUserRules = false; + if (!skipUserStyles) { // NOTE: different + FileRulesFromAllChildProcessors(mRuleProcessors[SheetType::User], + aCollectorFunc, + aData, + aRuleWalker, + SheetType::User, + lastUserRN, + lastUserRNs, + haveImportantUserRules, + haveAnyImportantUserRules); + } + if (!lastUserRN) { + lastUserRN = aRuleWalker->CurrentNode(); + } aRuleWalker->SetLevel(SheetType::PresHint, false, false); if (mRuleProcessors[SheetType::PresHint]) @@ -1148,16 +1210,35 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, static_cast(aData), &cutOffInheritance); } - if (!skipUserStyles && !cutOffInheritance && // NOTE: different - mRuleProcessors[SheetType::Doc]) - (*aCollectorFunc)(mRuleProcessors[SheetType::Doc], aData); - nsRuleNode* lastDocRN = aRuleWalker->CurrentNode(); - bool haveImportantDocRules = !aRuleWalker->GetCheckForImportantRules(); + bool haveImportantNACRules = !aRuleWalker->GetCheckForImportantRules(); + + nsRuleNode* lastDocRN = nullptr; + nsTArray lastDocRNs; + nsTArray haveImportantDocRules; + bool haveAnyImportantDocRules = haveImportantNACRules; + if (!skipUserStyles && !cutOffInheritance) { // NOTE: different + FileRulesFromAllChildProcessors(mRuleProcessors[SheetType::Doc], + aCollectorFunc, + aData, + aRuleWalker, + SheetType::Doc, + lastDocRN, + lastDocRNs, + haveImportantDocRules, + haveAnyImportantDocRules); + } + if (!lastDocRN) { + lastDocRN = aRuleWalker->CurrentNode(); + } + nsTArray lastScopedRNs; nsTArray haveImportantScopedRules; bool haveAnyImportantScopedRules = false; if (!skipUserStyles && !cutOffInheritance && aElement && aElement->IsElementInStyleScope()) { + // XXX: Scoped style sheets will add important rules from cascade + // layers in reverse order. Behavior for layers inside this isn't + // specced and isn't worth the additional complexity. lastScopedRNs.SetLength(mScopedDocSheetRuleProcessors.Length()); haveImportantScopedRules.SetLength(mScopedDocSheetRuleProcessors.Length()); for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { @@ -1173,6 +1254,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, aData->mScope = nullptr; } nsRuleNode* lastScopedRN = aRuleWalker->CurrentNode(); + aRuleWalker->SetLevel(SheetType::StyleAttr, false, true); if (mRuleProcessors[SheetType::StyleAttr]) (*aCollectorFunc)(mRuleProcessors[SheetType::StyleAttr], aData); @@ -1210,9 +1292,21 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, } #endif - if (haveImportantDocRules) { - aRuleWalker->SetLevel(SheetType::Doc, true, false); - AddImportantRules(lastDocRN, lastSVGAttrAnimationRN, aRuleWalker); // doc + if (haveAnyImportantDocRules) { + for (uint32_t i = lastDocRNs.Length(); i-- != 0; ) { + aRuleWalker->SetLevel(SheetType::Doc, true, false); + nsRuleNode* startRN = lastDocRNs[i]; + nsRuleNode* endRN = i == 0 ? lastSVGAttrAnimationRN : lastDocRNs[i - 1]; + bool isNearestToNACWithImportantRules = i == 0 && haveImportantNACRules; + if (haveImportantDocRules[i] || isNearestToNACWithImportantRules) { + AddImportantRules(startRN, endRN, aRuleWalker); // doc + } +#ifdef DEBUG + else { + AssertNoImportantRules(startRN, endRN); + } +#endif + } } #ifdef DEBUG else { @@ -1244,9 +1338,20 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, AssertNoCSSRules(lastSVGAttrAnimationRN, lastUserRN); #endif - if (haveImportantUserRules) { - aRuleWalker->SetLevel(SheetType::User, true, false); - AddImportantRules(lastUserRN, lastAgentRN, aRuleWalker); //user + if (haveAnyImportantUserRules) { + for (uint32_t i = lastUserRNs.Length(); i-- != 0;) { + aRuleWalker->SetLevel(SheetType::User, true, false); + nsRuleNode* startRN = lastUserRNs[i]; + nsRuleNode* endRN = i == 0 ? lastAgentRN : lastUserRNs[i - 1]; + if (haveImportantUserRules[i]) { + AddImportantRules(startRN, endRN, aRuleWalker); // user + } +#ifdef DEBUG + else { + AssertNoImportantRules(startRN, endRN); + } +#endif + } } #ifdef DEBUG else { @@ -1254,9 +1359,20 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, } #endif - if (haveImportantUARules) { - aRuleWalker->SetLevel(SheetType::Agent, true, false); - AddImportantRules(lastAgentRN, lastRestrictionRN, aRuleWalker); //agent + if (haveAnyImportantAgentRules) { + for (uint32_t i = lastAgentRNs.Length(); i-- != 0;) { + aRuleWalker->SetLevel(SheetType::Agent, true, false); + nsRuleNode* startRN = lastAgentRNs[i]; + nsRuleNode* endRN = i == 0 ? lastRestrictionRN : lastAgentRNs[i - 1]; + if (haveImportantAgentRules[i]) { + AddImportantRules(startRN, endRN, aRuleWalker); // agent + } +#ifdef DEBUG + else { + AssertNoImportantRules(startRN, endRN); + } +#endif + } } #ifdef DEBUG else {