Bug 1368782 - Always use rule order for cascading child sheets. r=dbaron
MozReview-Commit-ID: IzEsMvzGqIm
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-import/1368782-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<style>
+@import "data:text/css,*{background:red}";
+@import "data:text/css,*{background:green}";
+</style>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-import/1368782-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<style>
+@import "data:text/css,*{background:green}";
+</style>
+<script>
+document.styleSheets[0].insertRule("@import \"data:text/css,*{background:red}\";", 0);
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-import/1368782-3.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<style>
+@import "data:text/css,*{background:green}";
+@import "data:text/css,*{background:red}";
+</style>
+<script>
+document.styleSheets[0].deleteRule(1);
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-import/green.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<style>
+@import "data:text/css,*{background:green}";
+</style>
--- a/layout/reftests/css-import/reftest.list
+++ b/layout/reftests/css-import/reftest.list
@@ -3,8 +3,11 @@
== 436261-2.html 436261-ref.html
fails-if(styloVsGecko||stylo) == 436261-3.html 436261-ref.html
== 444723-1.html 444723-ref.html
== 444723-2.html 444723-ref.html
fails-if(styloVsGecko||stylo) == 445415-1a.xhtml 445415-1-ref.xhtml
== 445415-1b.xhtml 445415-1-ref.xhtml
== 445415-2a.xhtml 445415-2-ref.xhtml
fails-if(styloVsGecko||stylo) == 445415-2b.xhtml 445415-2-ref.xhtml
+== 1368782-1.html green.html
+== 1368782-2.html green.html
+== 1368782-3.html green.html
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -3630,16 +3630,18 @@ GatherDocRuleEnumFunc(css::Rule* aRule,
* If modifying this function you may need to update
* GatherDocRuleEnumFunc too.
*/
static bool
CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
{
CascadeEnumData* data = (CascadeEnumData*)aData;
int32_t type = aRule->GetType();
+ MOZ_ASSERT(type != css::Rule::IMPORT_RULE,
+ "@import rule must not be handled here");
if (css::Rule::STYLE_RULE == type) {
css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule);
for (nsCSSSelectorList *sel = styleRule->Selector();
sel; sel = sel->mNext) {
int32_t weight = sel->mWeight;
auto entry = static_cast<RuleByWeightEntry*>
@@ -3730,26 +3732,50 @@ CascadeRuleEnumFunc(css::Rule* aRule, vo
}
/* static */ bool
nsCSSRuleProcessor::CascadeSheet(CSSStyleSheet* aSheet, CascadeEnumData* aData)
{
if (aSheet->IsApplicable() &&
aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) &&
aSheet->mInner) {
-
- StyleSheet* child = aSheet->GetFirstChild();
- while (child) {
- CascadeSheet(child->AsGecko(), aData);
-
- child = child->mNext;
+ auto& rules = aSheet->Inner()->mOrderedRules;
+ uint32_t i = 0, len = rules.Length();
+ for (; i < len; i++) {
+ if (rules[i]->GetType() != css::Rule::IMPORT_RULE) {
+ break;
+ }
}
- for (css::Rule* rule : aSheet->Inner()->mOrderedRules) {
- if (!CascadeRuleEnumFunc(rule, aData)) {
+ if (i > 0) {
+ // Collect stylesheets from @import rules. It is done in reverse
+ // order so that we can avoid cascading duplicate sheets.
+ nsTArray<StyleSheet*> childSheets(i);
+ nsTHashtable<nsPtrHashKey<StyleSheet>> childSheetSet(i);
+ for (uint32_t j = i; j > 0; j--) {
+ auto importRule = static_cast<css::ImportRule*>(rules[j - 1]);
+ StyleSheet* sheet = importRule->GetStyleSheet();
+ // There are two cases we want to ignore an import rule:
+ // 1. the import rule does not have stylesheet connected because
+ // it fails in security check or there is a loop involved.
+ // 2. the sheet has been referenced by another import rule at a
+ // later position.
+ if (sheet && !childSheetSet.Contains(sheet)) {
+ childSheets.AppendElement(sheet);
+ childSheetSet.PutEntry(sheet);
+ }
+ }
+ // Now cascade all sheets listed.
+ for (StyleSheet* child : Reversed(childSheets)) {
+ CascadeSheet(child->AsGecko(), aData);
+ }
+ }
+
+ for (; i < len; i++) {
+ if (!CascadeRuleEnumFunc(rules[i], aData)) {
return false;
}
}
}
return true;
}
static int CompareWeightData(const void* aArg1, const void* aArg2,