Bug 1391198 - Make the order of rules in DevTools be the specificity order. draft
authorKuoE0 <kuoe0.tw@gmail.com>
Fri, 01 Sep 2017 10:48:29 +0800
changeset 657087 60890d26aaaf70339aec10f045092acaf9df4868
parent 657086 a1aea8e3ee5f796bc0bc0983201c6a82c66da1f1
child 729344 a4d2d17d5ae1f5ee06c653cc2dabb906df95f75b
push id77435
push userbmo:kuoe0@mozilla.com
push dateFri, 01 Sep 2017 02:56:46 +0000
bugs1391198
milestone57.0a1
Bug 1391198 - Make the order of rules in DevTools be the specificity order. We insert rules with any important declaration into rule tree twice, one for normal level and another for important level. And when we fetch them from rule tree, we skip the important one to make the order be MozReview-Commit-ID: HewZG6jYVvv
servo/components/style/rule_tree/mod.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -194,46 +194,53 @@ impl RuleTree {
         let mut important_author = SmallVec::<[StyleSource; 4]>::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");
             debug_assert!(!level.is_important(), "Important levels handled internally");
-            let (any_normal, any_important) = {
+            let any_important = {
                 let pdb = source.read(level.guard(guards));
-                (pdb.any_normal(), pdb.any_important())
+                pdb.any_important()
             };
+
             if any_important {
                 found_important = true;
                 match level {
                     AuthorNormal => important_author.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());
                     },
                     _ => {},
                 };
             }
-            // We really want to ensure empty rule nodes appear in the rule tree for
-            // devtools, this condition ensures that if we find an empty rule node, we
-            // insert it at the normal level.
-            if any_normal || !any_important {
-                if matches!(level, Transitions) && found_important {
-                    // There can be at most one transition, and it will come at
-                    // the end of the iterator. Stash it and apply it after
-                    // !important rules.
-                    debug_assert!(transition.is_none());
-                    transition = Some(source);
-                } else {
-                    current = current.ensure_child(self.root.downgrade(), source, level);
-                }
+
+            // We don't optimize out empty rules, even though we could.
+            //
+            // Inspector relies on every rule being inserted in the normal level
+            // at least once, in order to return the rules with the correct
+            // specificity order.
+            //
+            // TODO(emilio): If we want to apply these optimizations without
+            // breaking inspector's expectations, we'd need to run
+            // selector-matching again at the inspector's request. That may or
+            // may not be a better trade-off.
+            if matches!(level, Transitions) && found_important {
+                // There can be at most one transition, and it will come at
+                // the end of the iterator. Stash it and apply it after
+                // !important rules.
+                debug_assert!(transition.is_none());
+                transition = Some(source);
+            } else {
+                current = current.ensure_child(self.root.downgrade(), source, level);
             }
             last_level = level;
         }
 
         // Early-return in the common case of no !important declarations.
         if !found_important {
             return current;
         }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1914,34 +1914,30 @@ pub extern "C" fn Servo_ComputedValues_E
 #[no_mangle]
 pub extern "C" fn Servo_ComputedValues_GetStyleRuleList(values: ServoStyleContextBorrowed,
                                                         rules: RawGeckoServoStyleRuleListBorrowedMut) {
     let rule_node = match values.rules {
         Some(ref r) => r,
         None => return,
     };
 
-    let global_style_data = &*GLOBAL_STYLE_DATA;
-    let guard = global_style_data.shared_lock.read();
-
     // TODO(emilio): Will benefit from SmallVec.
     let mut result = vec![];
     for node in rule_node.self_and_ancestors() {
         let style_rule = match *node.style_source() {
             StyleSource::Style(ref rule) => rule,
             _ => continue,
         };
 
+        // For the rules with any important declaration, we insert them into
+        // rule tree twice, one for normal level and another for important
+        // level. So, we skip the important one to keep the specificity order of
+        // rules.
         if node.importance().important() {
-            let block = style_rule.read_with(&guard).block.read_with(&guard);
-            if block.any_normal() {
-                // We'll append it when we find the normal rules in our
-                // parent chain.
-                continue;
-            }
+            continue;
         }
 
         result.push(style_rule);
     }
 
     unsafe { rules.set_len(result.len() as u32) };
     for (ref src, ref mut dest) in result.into_iter().zip(rules.iter_mut()) {
         src.with_raw_offset_arc(|arc| {