Bug 1368782 - Always use rule order for cascading child sheets. r=dbaron draft
authorXidorn Quan <me@upsuper.org>
Wed, 31 May 2017 10:51:25 +1000
changeset 586841 a3880b2a16b5346593e40ed868f58e0ec605b5ff
parent 586744 40d071be0200ea0673fc467fb4045ea5f71fe7ef
child 631119 e15df3b04bb4521d92bfddad2e8b742c70f8c8d5
push id61544
push userxquan@mozilla.com
push dateWed, 31 May 2017 06:09:01 +0000
reviewersdbaron
bugs1368782
milestone55.0a1
Bug 1368782 - Always use rule order for cascading child sheets. r=dbaron MozReview-Commit-ID: IzEsMvzGqIm
layout/reftests/css-import/1368782-1.html
layout/reftests/css-import/1368782-2.html
layout/reftests/css-import/1368782-3.html
layout/reftests/css-import/green.html
layout/reftests/css-import/reftest.list
layout/style/nsCSSRuleProcessor.cpp
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,