Bug 1368782 - Always use rule order for cascading child sheets. r=dbaron
authorXidorn Quan <me@upsuper.org>
Wed, 31 May 2017 10:51:25 +1000
changeset 409570 b19afe698a9e1580908f386d267c4e3d1a26c8b4
parent 409569 0ab2933e41c3be1825a99247a184c2dbde7fcc76
child 409571 73abfac22f012c8f6fcfe7c732b203660f0e7ca9
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1368782
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,