Bug 1454162: Fix cascade order of !important in Shadow DOM. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 18 Apr 2018 09:56:33 +0200
changeset 784238 4cfe48d4242495971ffd8529527f9d10cd076c09
parent 784237 0c5c210f9f5dc066f3dcb223458948de4ac858d7
push id106886
push userbmo:emilio@crisal.io
push dateWed, 18 Apr 2018 10:10:34 +0000
reviewersheycam
bugs1454162
milestone61.0a1
Bug 1454162: Fix cascade order of !important in Shadow DOM. r?heycam No cleaner ideas right now that carrying that counter around... Maybe a custom type may be cleaner? This makes ApplicableDeclarationBlock a bit bigger. I could probably try to make the counter a 4 / 5-bit number or something and pack the counter there in the SourceOrderAndCascadeLevel somehow... But doesn't seem really worth the churn, and can be done as a followup in any case. Let me know if you want to block on that. MozReview-Commit-ID: 1LdW9S4xA6f
servo/components/style/applicable_declarations.rs
servo/components/style/rule_tree/mod.rs
servo/components/style/selector_map.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
servo/ports/geckolib/tests/size_of.rs
testing/web-platform/meta/css/css-scoping/shadow-cascade-order-001.html.ini
--- a/servo/components/style/applicable_declarations.rs
+++ b/servo/components/style/applicable_declarations.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Applicable declarations management.
 
 use properties::PropertyDeclarationBlock;
-use rule_tree::{CascadeLevel, StyleSource};
+use rule_tree::{CascadeLevel, ShadowCascadeOrder, StyleSource};
 use servo_arc::Arc;
 use shared_lock::Locked;
 use smallvec::SmallVec;
 use std::fmt::{self, Debug};
 use std::mem;
 
 /// List of applicable declarations. This is a transient structure that shuttles
 /// declarations between selector matching and inserting into the rule tree, and
@@ -78,55 +78,65 @@ impl Debug for SourceOrderAndCascadeLeve
 pub struct ApplicableDeclarationBlock {
     /// The style source, either a style rule, or a property declaration block.
     #[ignore_malloc_size_of = "Arc"]
     pub source: StyleSource,
     /// The source order of the block, and the cascade level it belongs to.
     order_and_level: SourceOrderAndCascadeLevel,
     /// The specificity of the selector this block is represented by.
     pub specificity: u32,
+    /// The order in the tree of trees we carry on.
+    pub shadow_cascade_order: ShadowCascadeOrder,
 }
 
 impl ApplicableDeclarationBlock {
     /// Constructs an applicable declaration block from a given property
     /// declaration block and importance.
     #[inline]
     pub fn from_declarations(
         declarations: Arc<Locked<PropertyDeclarationBlock>>,
         level: CascadeLevel,
     ) -> Self {
         ApplicableDeclarationBlock {
             source: StyleSource::Declarations(declarations),
             order_and_level: SourceOrderAndCascadeLevel::new(0, level),
             specificity: 0,
+            shadow_cascade_order: 0,
         }
     }
 
     /// Constructs an applicable declaration block from the given components
     #[inline]
-    pub fn new(source: StyleSource, order: u32, level: CascadeLevel, specificity: u32) -> Self {
+    pub fn new(
+        source: StyleSource,
+        order: u32,
+        level: CascadeLevel,
+        specificity: u32,
+        shadow_cascade_order: u32,
+    ) -> Self {
         ApplicableDeclarationBlock {
-            source: source,
+            source,
             order_and_level: SourceOrderAndCascadeLevel::new(order, level),
-            specificity: specificity,
+            specificity,
+            shadow_cascade_order,
         }
     }
 
     /// Returns the source order of the block.
     #[inline]
     pub fn source_order(&self) -> u32 {
         self.order_and_level.order()
     }
 
     /// Returns the cascade level of the block.
     #[inline]
     pub fn level(&self) -> CascadeLevel {
         self.order_and_level.level()
     }
 
-    /// Convenience method to consume self and return the source alongside the
-    /// level.
+    /// Convenience method to consume self and return the right thing for the
+    /// rule tree to iterate over.
     #[inline]
-    pub fn order_and_level(self) -> (StyleSource, CascadeLevel) {
+    pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel, ShadowCascadeOrder) {
         let level = self.level();
-        (self.source, level)
+        (self.source, level, self.shadow_cascade_order)
     }
 }
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -157,16 +157,26 @@ impl StyleSource {
 ///
 /// The root node doesn't have a null pointer in the free list, but this value.
 const FREE_LIST_SENTINEL: *mut RuleNode = 0x01 as *mut RuleNode;
 
 /// A second sentinel value for the free list, indicating that it's locked (i.e.
 /// another thread is currently adding an entry). We spin if we find this value.
 const FREE_LIST_LOCKED: *mut RuleNode = 0x02 as *mut RuleNode;
 
+/// A counter to track how many inner shadow roots rules deep we are.
+///
+/// This is used to handle:
+///
+/// https://drafts.csswg.org/css-scoping/#shadow-cascading
+///
+/// In particular, it'd be `0` for the innermost shadow host, `1` for the next,
+/// and so on.
+pub type ShadowCascadeOrder = u32;
+
 impl RuleTree {
     /// Construct a new rule tree.
     pub fn new() -> Self {
         RuleTree {
             root: StrongRuleNode::new(Box::new(RuleNode::root())),
         }
     }
 
@@ -193,41 +203,61 @@ impl RuleTree {
     /// in the rule tree. This allows selector matching to ignore importance,
     /// while still maintaining the appropriate cascade order in the rule tree.
     pub fn insert_ordered_rules_with_important<'a, I>(
         &self,
         iter: I,
         guards: &StylesheetGuards,
     ) -> StrongRuleNode
     where
-        I: Iterator<Item = (StyleSource, CascadeLevel)>,
+        I: Iterator<Item = (StyleSource, CascadeLevel, ShadowCascadeOrder)>,
     {
         use self::CascadeLevel::*;
         let mut current = self.root.clone();
         let mut last_level = current.get().level;
 
         let mut found_important = false;
         let mut important_style_attr = None;
-        let mut important_author = SmallVec::<[StyleSource; 4]>::new();
+
+        let mut important_same_tree = SmallVec::<[StyleSource; 4]>::new();
+        let mut important_inner_shadow = SmallVec::<[SmallVec<[StyleSource; 4]>; 4]>::new();
+        important_inner_shadow.push(SmallVec::new());
+
         let mut important_user = SmallVec::<[StyleSource; 4]>::new();
         let mut important_ua = SmallVec::<[StyleSource; 4]>::new();
         let mut transition = None;
 
-        for (source, level) in iter {
-            debug_assert!(last_level <= level, "Not really ordered");
+        let mut last_cascade_order = 0;
+        for (source, level, shadow_cascade_order) in iter {
+            debug_assert!(level >= last_level, "Not really ordered");
             debug_assert!(!level.is_important(), "Important levels handled internally");
             let any_important = {
                 let pdb = source.read(level.guard(guards));
                 pdb.any_important()
             };
 
             if any_important {
                 found_important = true;
                 match level {
-                    AuthorNormal => important_author.push(source.clone()),
+                    InnerShadowNormal => {
+                        debug_assert!(
+                            shadow_cascade_order >= last_cascade_order,
+                            "Not really ordered"
+                        );
+                        if shadow_cascade_order > last_cascade_order &&
+                            !important_inner_shadow.last().unwrap().is_empty()
+                        {
+                            last_cascade_order = shadow_cascade_order;
+                            important_inner_shadow.push(SmallVec::new());
+                        }
+                        important_inner_shadow.last_mut().unwrap().push(source.clone())
+                    }
+                    SameTreeAuthorNormal => {
+                        important_same_tree.push(source.clone())
+                    },
                     UANormal => important_ua.push(source.clone()),
                     UserNormal => important_user.push(source.clone()),
                     StyleAttributeNormal => {
                         debug_assert!(important_style_attr.is_none());
                         important_style_attr = Some(source.clone());
                     },
                     _ => {},
                 };
@@ -260,24 +290,30 @@ impl RuleTree {
             return current;
         }
 
         //
         // Insert important declarations, in order of increasing importance,
         // followed by any transition rule.
         //
 
-        for source in important_author.drain() {
-            current = current.ensure_child(self.root.downgrade(), source, AuthorImportant);
+        for source in important_same_tree.drain() {
+            current = current.ensure_child(self.root.downgrade(), source, SameTreeAuthorImportant);
         }
 
         if let Some(source) = important_style_attr {
             current = current.ensure_child(self.root.downgrade(), source, StyleAttributeImportant);
         }
 
+        for mut list in important_inner_shadow.drain().rev() {
+            for source in list.drain() {
+                current = current.ensure_child(self.root.downgrade(), source, InnerShadowImportant);
+            }
+        }
+
         for source in important_user.drain() {
             current = current.ensure_child(self.root.downgrade(), source, UserImportant);
         }
 
         for source in important_ua.drain() {
             current = current.ensure_child(self.root.downgrade(), source, UAImportant);
         }
 
@@ -290,19 +326,20 @@ impl RuleTree {
 
     /// Given a list of applicable declarations, insert the rules and return the
     /// corresponding rule node.
     pub fn compute_rule_node(
         &self,
         applicable_declarations: &mut ApplicableDeclarationList,
         guards: &StylesheetGuards,
     ) -> StrongRuleNode {
-        let rules = applicable_declarations.drain().map(|d| d.order_and_level());
-        let rule_node = self.insert_ordered_rules_with_important(rules, guards);
-        rule_node
+        self.insert_ordered_rules_with_important(
+            applicable_declarations.drain().map(|d| d.for_rule_tree()),
+            guards,
+        )
     }
 
     /// Insert the given rules, that must be in proper order by specifity, and
     /// return the corresponding rule node representing the last inserted one.
     pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
     where
         I: Iterator<Item = (StyleSource, CascadeLevel)>,
     {
@@ -376,18 +413,18 @@ impl RuleTree {
                 // same as `pdb`, we're done, and `path` is still valid.
                 //
                 // TODO(emilio): Another potential optimization is the one where
                 // we can just replace the rule at that level for `pdb`, and
                 // then we don't need to re-create the children, and `path` is
                 // also equally valid. This is less likely, and would require an
                 // in-place mutation of the source, which is, at best, fiddly,
                 // so let's skip it for now.
-                let is_here_already = match &current.get().source {
-                    &StyleSource::Declarations(ref already_here) => {
+                let is_here_already = match current.get().source {
+                    StyleSource::Declarations(ref already_here) => {
                         pdb.with_arc(|arc| Arc::ptr_eq(arc, already_here))
                     },
                     _ => unreachable!("Replacing non-declarations style?"),
                 };
 
                 if is_here_already {
                     debug!("Picking the fast path in rule replacement");
                     return None;
@@ -495,41 +532,63 @@ const RULE_TREE_GC_INTERVAL: usize = 300
 ///
 /// Presentational hints for SVG and HTML are in the "author-level
 /// zero-specificity" level, that is, right after user rules, and before author
 /// rules.
 ///
 /// The order of variants declared here is significant, and must be in
 /// _ascending_ order of precedence.
 ///
+/// See also [4] for the Shadow DOM bits. We rely on the invariant that rules
+/// from outside the tree the element is in can't affect the element.
+///
+/// The opposite is not true (i.e., :host and ::slotted) from an "inner" shadow
+/// tree may affect an element connected to the document or an "outer" shadow
+/// tree.
+///
+/// We need to differentiate between rules from the same tree and "inner" shadow
+/// trees in order to be able to find the right position for the style attribute
+/// easily. Otherwise we wouldn't be able to avoid selector-matching when a
+/// style attribute is added or removed.
+///
 /// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
 /// [2]: https://drafts.csswg.org/css-cascade/#preshint
 /// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
+/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
 #[repr(u8)]
 #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 pub enum CascadeLevel {
     /// Normal User-Agent rules.
     UANormal = 0,
     /// User normal rules.
     UserNormal,
     /// Presentational hints.
     PresHints,
-    /// Author normal rules.
-    AuthorNormal,
+    /// Shadow DOM styles from "inner" shadow trees.
+    ///
+    /// See above for why this is needed instead of merging InnerShadowNormal,
+    /// SameTreeAuthorNormal and StyleAttributeNormal inside something like
+    /// AuthorNormal.
+    InnerShadowNormal,
+    /// Author normal rules from the same tree the element is in.
+    SameTreeAuthorNormal,
     /// Style attribute normal rules.
     StyleAttributeNormal,
     /// SVG SMIL animations.
     SMILOverride,
     /// CSS animations and script-generated animations.
     Animations,
-    /// Author-supplied important rules.
-    AuthorImportant,
+    /// Author-supplied important rules from the same tree the element came
+    /// from.
+    SameTreeAuthorImportant,
     /// Style attribute important rules.
     StyleAttributeImportant,
+    /// Shadow DOM important rules.
+    InnerShadowImportant,
     /// User important rules.
     UserImportant,
     /// User-agent important rules.
     UAImportant,
     /// Transitions
     ///
     /// NB: If this changes from being last, change from_byte below.
     Transitions,
@@ -566,17 +625,18 @@ impl CascadeLevel {
         }
     }
 
     /// Returns whether this cascade level represents important rules of some
     /// sort.
     #[inline]
     pub fn is_important(&self) -> bool {
         match *self {
-            CascadeLevel::AuthorImportant |
+            CascadeLevel::SameTreeAuthorImportant |
+            CascadeLevel::InnerShadowImportant |
             CascadeLevel::StyleAttributeImportant |
             CascadeLevel::UserImportant |
             CascadeLevel::UAImportant => true,
             _ => false,
         }
     }
 
     /// Returns the importance relevant for this rule. Pretty similar to
@@ -1297,21 +1357,23 @@ impl StrongRuleNode {
                                     have_explicit_ua_inherit = true;
                                     inherited_properties.insert(id);
                                 }
                             }
                         }
                     },
                     // Author rules:
                     CascadeLevel::PresHints |
-                    CascadeLevel::AuthorNormal |
+                    CascadeLevel::SameTreeAuthorNormal |
+                    CascadeLevel::InnerShadowNormal |
                     CascadeLevel::StyleAttributeNormal |
                     CascadeLevel::SMILOverride |
                     CascadeLevel::Animations |
-                    CascadeLevel::AuthorImportant |
+                    CascadeLevel::SameTreeAuthorImportant |
+                    CascadeLevel::InnerShadowImportant |
                     CascadeLevel::StyleAttributeImportant |
                     CascadeLevel::Transitions => {
                         for (id, declaration) in longhands {
                             if properties.contains(id) {
                                 if !author_colors_allowed {
                                     if let PropertyDeclaration::BackgroundColor(ref color) =
                                         *declaration
                                     {
--- a/servo/components/style/selector_map.rs
+++ b/servo/components/style/selector_map.rs
@@ -9,17 +9,17 @@ use {Atom, LocalName, WeakAtom};
 use applicable_declarations::ApplicableDeclarationList;
 use context::QuirksMode;
 use dom::TElement;
 use fallible::FallibleVec;
 use hash::{HashMap, HashSet};
 use hash::map as hash_map;
 use hashglobe::FailedAllocationError;
 use precomputed_hash::PrecomputedHash;
-use rule_tree::CascadeLevel;
+use rule_tree::{CascadeLevel, ShadowCascadeOrder};
 use selector_parser::SelectorImpl;
 use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext};
 use selectors::parser::{Combinator, Component, SelectorIter};
 use smallvec::SmallVec;
 use std::hash::{BuildHasherDefault, Hash, Hasher};
 use stylist::Rule;
 
 /// A hasher implementation that doesn't hash anything, because it expects its
@@ -158,16 +158,17 @@ impl SelectorMap<Rule> {
     pub fn get_all_matching_rules<E, F>(
         &self,
         element: E,
         rule_hash_target: E,
         matching_rules_list: &mut ApplicableDeclarationList,
         context: &mut MatchingContext<E::Impl>,
         flags_setter: &mut F,
         cascade_level: CascadeLevel,
+        shadow_cascade_order: ShadowCascadeOrder,
     ) where
         E: TElement,
         F: FnMut(&E, ElementSelectorFlags),
     {
         if self.is_empty() {
             return;
         }
 
@@ -180,80 +181,85 @@ impl SelectorMap<Rule> {
             if let Some(rules) = self.id_hash.get(id, quirks_mode) {
                 SelectorMap::get_matching_rules(
                     element,
                     rules,
                     matching_rules_list,
                     context,
                     flags_setter,
                     cascade_level,
+                    shadow_cascade_order,
                 )
             }
         }
 
         rule_hash_target.each_class(|class| {
             if let Some(rules) = self.class_hash.get(&class, quirks_mode) {
                 SelectorMap::get_matching_rules(
                     element,
                     rules,
                     matching_rules_list,
                     context,
                     flags_setter,
                     cascade_level,
+                    shadow_cascade_order,
                 )
             }
         });
 
         if let Some(rules) = self.local_name_hash.get(rule_hash_target.local_name()) {
             SelectorMap::get_matching_rules(
                 element,
                 rules,
                 matching_rules_list,
                 context,
                 flags_setter,
                 cascade_level,
+                shadow_cascade_order,
             )
         }
 
         SelectorMap::get_matching_rules(
             element,
             &self.other,
             matching_rules_list,
             context,
             flags_setter,
             cascade_level,
+            shadow_cascade_order,
         );
 
         // Sort only the rules we just added.
         matching_rules_list[init_len..]
             .sort_unstable_by_key(|block| (block.specificity, block.source_order()));
     }
 
     /// Adds rules in `rules` that match `element` to the `matching_rules` list.
     fn get_matching_rules<E, F>(
         element: E,
         rules: &[Rule],
         matching_rules: &mut ApplicableDeclarationList,
         context: &mut MatchingContext<E::Impl>,
         flags_setter: &mut F,
         cascade_level: CascadeLevel,
+        shadow_cascade_order: ShadowCascadeOrder,
     ) where
         E: TElement,
         F: FnMut(&E, ElementSelectorFlags),
     {
         for rule in rules {
             if matches_selector(
                 &rule.selector,
                 0,
                 Some(&rule.hashes),
                 &element,
                 context,
                 flags_setter,
             ) {
-                matching_rules.push(rule.to_applicable_declaration_block(cascade_level));
+                matching_rules.push(rule.to_applicable_declaration_block(cascade_level, shadow_cascade_order));
             }
         }
     }
 }
 
 impl<T: SelectorMapEntry> SelectorMap<T> {
     /// Inserts into the correct hash, trying id, class, and localname.
     pub fn insert(
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -18,17 +18,17 @@ use invalidation::media_queries::{Effect
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
 #[cfg(feature = "gecko")]
 use malloc_size_of::MallocUnconditionalShallowSizeOf;
 use media_queries::Device;
 use properties::{self, CascadeFlags, ComputedValues};
 use properties::{AnimationRules, PropertyDeclarationBlock};
 use rule_cache::{RuleCache, RuleCacheConditions};
-use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
+use rule_tree::{CascadeLevel, RuleTree, ShadowCascadeOrder, StrongRuleNode, StyleSource};
 use selector_map::{PrecomputedHashMap, SelectorMap, SelectorMapEntry};
 use selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap};
 use selectors::NthIndexCache;
 use selectors::attr::{CaseSensitivity, NamespaceConstraint};
 use selectors::bloom::{BloomFilter, NonCountingBloomFilter};
 use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext, MatchingMode};
 use selectors::matching::VisitedHandlingMode;
 use selectors::parser::{AncestorHashes, Combinator, Component, Selector};
@@ -688,17 +688,17 @@ impl Stylist {
                 },
                 None => Some(declarations),
             },
             None => extra_declarations.as_ref(),
         };
 
         match declarations {
             Some(decls) => self.rule_tree.insert_ordered_rules_with_important(
-                decls.into_iter().map(|a| (a.source.clone(), a.level())),
+                decls.into_iter().map(|a| a.clone().for_rule_tree()),
                 guards,
             ),
             None => self.rule_tree.root().clone(),
         }
     }
 
     /// Returns the style for an anonymous box of the given type.
     ///
@@ -1015,17 +1015,17 @@ impl Stylist {
                 AnimationRules(None, None),
                 rule_inclusion,
                 &mut declarations,
                 &mut matching_context,
                 &mut set_selector_flags,
             );
             if !declarations.is_empty() {
                 let rule_node = self.rule_tree.insert_ordered_rules_with_important(
-                    declarations.drain().map(|a| a.order_and_level()),
+                    declarations.drain().map(|a| a.for_rule_tree()),
                     guards,
                 );
                 if rule_node != *self.rule_tree.root() {
                     visited_rules = Some(rule_node);
                 }
             }
         }
 
@@ -1182,16 +1182,17 @@ impl Stylist {
         {
             map.get_all_matching_rules(
                 element,
                 rule_hash_target,
                 applicable_declarations,
                 context,
                 flags_setter,
                 CascadeLevel::UANormal,
+                0,
             );
         }
 
         // NB: the following condition, although it may look somewhat
         // inaccurate, would be equivalent to something like:
         //
         //     element.matches_user_and_author_rules() ||
         //     (is_implemented_pseudo &&
@@ -1203,16 +1204,17 @@ impl Stylist {
             if let Some(map) = self.cascade_data.user.normal_rules(pseudo_element) {
                 map.get_all_matching_rules(
                     element,
                     rule_hash_target,
                     applicable_declarations,
                     context,
                     flags_setter,
                     CascadeLevel::UserNormal,
+                    0,
                 );
             }
         }
 
         if pseudo_element.is_none() && !only_default_rules {
             // Presentational hints.
             //
             // These go before author rules, but after user rules, see:
@@ -1227,16 +1229,17 @@ impl Stylist {
                     for declaration in &applicable_declarations[length_before_preshints..] {
                         assert_eq!(declaration.level(), CascadeLevel::PresHints);
                     }
                 }
             }
         }
 
         let mut match_document_author_rules = matches_author_rules;
+        let mut shadow_cascade_order = 0;
 
         // XBL / Shadow DOM rules, which are author rules too.
         //
         // TODO(emilio): Cascade order here is wrong for Shadow DOM. In
         // particular, normally document rules override ::slotted() rules, but
         // for !important it should be the other way around. So probably we need
         // to add some sort of AuthorScoped cascade level or something.
         if matches_author_rules && !only_default_rules {
@@ -1244,19 +1247,21 @@ impl Stylist {
                 if let Some(map) = shadow.style_data().host_rules(pseudo_element) {
                     context.with_shadow_host(Some(rule_hash_target), |context| {
                         map.get_all_matching_rules(
                             element,
                             rule_hash_target,
                             applicable_declarations,
                             context,
                             flags_setter,
-                            CascadeLevel::AuthorNormal,
+                            CascadeLevel::InnerShadowNormal,
+                            shadow_cascade_order,
                         );
                     });
+                    shadow_cascade_order += 1;
                 }
             }
 
             // Match slotted rules in reverse order, so that the outer slotted
             // rules come before the inner rules (and thus have less priority).
             let mut slots = SmallVec::<[_; 3]>::new();
             let mut current = rule_hash_target.assigned_slot();
             while let Some(slot) = current {
@@ -1270,86 +1275,90 @@ impl Stylist {
                 if let Some(map) = styles.slotted_rules(pseudo_element) {
                     context.with_shadow_host(Some(shadow.host()), |context| {
                         map.get_all_matching_rules(
                             element,
                             rule_hash_target,
                             applicable_declarations,
                             context,
                             flags_setter,
-                            CascadeLevel::AuthorNormal,
+                            CascadeLevel::InnerShadowNormal,
+                            shadow_cascade_order,
                         );
                     });
+                    shadow_cascade_order += 1;
                 }
             }
 
             if let Some(containing_shadow) = rule_hash_target.containing_shadow() {
                 let cascade_data = containing_shadow.style_data();
                 if let Some(map) = cascade_data.normal_rules(pseudo_element) {
                     context.with_shadow_host(Some(containing_shadow.host()), |context| {
                         map.get_all_matching_rules(
                             element,
                             rule_hash_target,
                             applicable_declarations,
                             context,
                             flags_setter,
-                            CascadeLevel::AuthorNormal,
+                            CascadeLevel::SameTreeAuthorNormal,
+                            shadow_cascade_order,
                         );
                     });
+                    shadow_cascade_order += 1;
                 }
 
                 match_document_author_rules = false;
             }
         }
 
         // FIXME(emilio): It looks very wrong to match XBL rules even for
         // getDefaultComputedStyle!
         //
         // Also, this doesn't account for the author_styles_enabled stuff.
         let cut_xbl_binding_inheritance =
             element.each_xbl_cascade_data(|cascade_data, quirks_mode| {
                 if let Some(map) = cascade_data.normal_rules(pseudo_element) {
                     // NOTE(emilio): This is needed because the XBL stylist may
                     // think it has a different quirks mode than the document.
-                    //
-                    // FIXME(emilio): this should use the same VisitedMatchingMode
-                    // as `context`, write a test-case of :visited not working on
-                    // Shadow DOM and fix it!
                     let mut matching_context = MatchingContext::new(
                         context.matching_mode(),
                         context.bloom_filter,
                         context.nth_index_cache.as_mut().map(|s| &mut **s),
                         quirks_mode,
                     );
                     matching_context.pseudo_element_matching_fn =
                         context.pseudo_element_matching_fn;
 
+                    // SameTreeAuthorNormal instead of InnerShadowNormal to
+                    // preserve behavior, though that's kinda fishy...
                     map.get_all_matching_rules(
                         element,
                         rule_hash_target,
                         applicable_declarations,
                         &mut matching_context,
                         flags_setter,
-                        CascadeLevel::AuthorNormal,
+                        CascadeLevel::SameTreeAuthorNormal,
+                        shadow_cascade_order,
                     );
                 }
             });
 
         match_document_author_rules &= !cut_xbl_binding_inheritance;
 
         if match_document_author_rules && !only_default_rules {
             // Author normal rules.
             if let Some(map) = self.cascade_data.author.normal_rules(pseudo_element) {
                 map.get_all_matching_rules(
                     element,
                     rule_hash_target,
                     applicable_declarations,
                     context,
                     flags_setter,
-                    CascadeLevel::AuthorNormal,
+                    CascadeLevel::SameTreeAuthorNormal,
+                    shadow_cascade_order,
                 );
             }
         }
 
         if !only_default_rules {
             // Style attribute ("Normal override declarations").
             if let Some(sa) = style_attribute {
                 applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
@@ -2167,16 +2176,17 @@ impl CascadeData {
                                     .as_mut()
                                     .expect("Expected precomputed declarations for the UA level")
                                     .get_or_insert_with(&pseudo.canonical(), Vec::new)
                                     .push(ApplicableDeclarationBlock::new(
                                         StyleSource::Style(locked.clone()),
                                         self.rules_source_order,
                                         CascadeLevel::UANormal,
                                         selector.specificity(),
+                                        0,
                                     ));
                                 continue;
                             }
                         }
 
                         let hashes = AncestorHashes::new(&selector, quirks_mode);
 
                         let rule = Rule::new(
@@ -2463,19 +2473,20 @@ impl Rule {
         self.selector.specificity()
     }
 
     /// Turns this rule into an `ApplicableDeclarationBlock` for the given
     /// cascade level.
     pub fn to_applicable_declaration_block(
         &self,
         level: CascadeLevel,
+        shadow_cascade_order: ShadowCascadeOrder,
     ) -> ApplicableDeclarationBlock {
         let source = StyleSource::Style(self.style_rule.clone());
-        ApplicableDeclarationBlock::new(source, self.source_order, level, self.specificity())
+        ApplicableDeclarationBlock::new(source, self.source_order, level, self.specificity(), shadow_cascade_order)
     }
 
     /// Creates a new Rule.
     pub fn new(
         selector: Selector<SelectorImpl>,
         hashes: AncestorHashes,
         style_rule: Arc<Locked<StyleRule>>,
         source_order: u32,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -2704,17 +2704,17 @@ pub unsafe extern "C" fn Servo_ComputedV
     let page_decls = match pseudo {
         PseudoElement::PageContent => {
             let mut declarations = vec![];
             let iter = data.stylist.iter_extra_data_origins_rev();
             for (data, origin) in iter {
                 let level = match origin {
                     Origin::UserAgent => CascadeLevel::UANormal,
                     Origin::User => CascadeLevel::UserNormal,
-                    Origin::Author => CascadeLevel::AuthorNormal,
+                    Origin::Author => CascadeLevel::SameTreeAuthorNormal,
                 };
                 for rule in data.pages.iter() {
                     declarations.push(ApplicableDeclarationBlock::from_declarations(
                         rule.read_with(level.guard(&guards)).block.clone(),
                         level
                     ));
                 }
             }
--- a/servo/ports/geckolib/tests/size_of.rs
+++ b/servo/ports/geckolib/tests/size_of.rs
@@ -30,17 +30,17 @@ size_of_test!(test_size_of_cv, ComputedV
 size_of_test!(test_size_of_option_arc_cv, Option<Arc<ComputedValues>>, 8);
 size_of_test!(test_size_of_option_rule_node, Option<StrongRuleNode>, 8);
 
 size_of_test!(test_size_of_element_styles, ElementStyles, 16);
 size_of_test!(test_size_of_element_data, ElementData, 24);
 
 size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);
 
-size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 24);
+size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 28);
 size_of_test!(test_size_of_rule_node, RuleNode, 80);
 
 // This is huge, but we allocate it on the stack and then never move it,
 // we only pass `&mut SourcePropertyDeclaration` references around.
 size_of_test!(test_size_of_parsed_declaration, style::properties::SourcePropertyDeclaration, 608);
 
 size_of_test!(test_size_of_computed_image, computed::image::Image, 32);
 size_of_test!(test_size_of_specified_image, specified::image::Image, 32);
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-scoping/shadow-cascade-order-001.html.ini
+++ /dev/null
@@ -1,43 +0,0 @@
-[shadow-cascade-order-001.html]
-  [D1. document vs ::slotted both with !important, ::slotted rule should win for open mode.]
-    expected: FAIL
-
-  [D2. document vs :host both with !important, :host rule should win for open mode.]
-    expected: FAIL
-
-  [D4. ::slotted vs :host both with !important, later in tree-of-trees rule should win for open mode.]
-    expected: FAIL
-
-  [D5. ::slotted vs inline both with !important, ::slotted rule should win for open mode.]
-    expected: FAIL
-
-  [D6. :host vs inline both with !important, :host rule should win for open mode.]
-    expected: FAIL
-
-  [E2. all styles with !important applied, rule in the last tree-of-trees should win for open mode.]
-    expected: FAIL
-
-  [F6. all rules with !important, the last rule in tree-of-trees should win for open mode.]
-    expected: FAIL
-
-  [D1. document vs ::slotted both with !important, ::slotted rule should win for closed mode.]
-    expected: FAIL
-
-  [D2. document vs :host both with !important, :host rule should win for closed mode.]
-    expected: FAIL
-
-  [D4. ::slotted vs :host both with !important, later in tree-of-trees rule should win for closed mode.]
-    expected: FAIL
-
-  [D5. ::slotted vs inline both with !important, ::slotted rule should win for closed mode.]
-    expected: FAIL
-
-  [D6. :host vs inline both with !important, :host rule should win for closed mode.]
-    expected: FAIL
-
-  [E2. all styles with !important applied, rule in the last tree-of-trees should win for closed mode.]
-    expected: FAIL
-
-  [F6. all rules with !important, the last rule in tree-of-trees should win for closed mode.]
-    expected: FAIL
-