servo: Merge #16111 - Add functions for after-change style of CSS Transition (from hiikezoe:after-change-style); r=emilio
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Thu, 23 Mar 2017 19:05:03 -0700
changeset 349320 6b9bbd396faca96f2e4549dab53b82557fe22403
parent 349319 1147dd18f1d91034757ea50a01281fae6e43eda6
child 349321 56e2fa280d2a9f00e37238c3c2532caa01c3b2a5
push id31550
push usercbook@mozilla.com
push dateFri, 24 Mar 2017 13:22:27 +0000
treeherdermozilla-central@473e0b201761 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
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
servo: Merge #16111 - Add functions for after-change style of CSS Transition (from hiikezoe:after-change-style); r=emilio Add functions for after-change style of CSS Transition <!-- Please describe your changes on the following line: --> This is a PR of https://bugzilla.mozilla.org/show_bug.cgi?id=1346663 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors <!-- Either: --> - [X] These changes do not require tests because it's for stylo <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 089c71b5872839c5a6c32d1988ce4d42a73860e4
servo/components/style/matching.rs
servo/components/style/rule_tree/mod.rs
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -464,34 +464,25 @@ trait PrivateMatchMethods: TElement {
                 current.borrow_data().unwrap().styles().primary.values().is_display_contents();
 
             if !is_display_contents {
                 return current;
             }
         }
     }
 
-    fn cascade_internal(&self,
-                        context: &StyleContext<Self>,
-                        primary_style: &ComputedStyle,
-                        pseudo_style: &mut Option<(&PseudoElement, &mut ComputedStyle)>,
-                        booleans: &CascadeBooleans)
-                        -> Arc<ComputedValues> {
+    fn cascade_with_rules(&self,
+                          context: &StyleContext<Self>,
+                          rule_node: &StrongRuleNode,
+                          primary_style: &ComputedStyle,
+                          pseudo_style: &Option<(&PseudoElement, &mut ComputedStyle)>,
+                          cascade_flags: CascadeFlags)
+                          -> Arc<ComputedValues> {
         let shared_context = context.shared;
         let mut cascade_info = CascadeInfo::new();
-        let mut cascade_flags = CascadeFlags::empty();
-        if booleans.shareable {
-            cascade_flags.insert(SHAREABLE)
-        }
-        if self.skip_root_and_item_based_display_fixup() {
-            cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
-        }
-
-        // Grab the rule node.
-        let rule_node = &pseudo_style.as_ref().map_or(primary_style, |p| &*p.1).rules;
 
         // Grab the inherited values.
         let parent_el;
         let parent_data;
         let inherited_values_ = if pseudo_style.is_none() {
             parent_el = self.parent_element();
             parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
             let parent_values = parent_data.as_ref().map(|d| {
@@ -547,16 +538,35 @@ trait PrivateMatchMethods: TElement {
                              Some(&mut cascade_info),
                              &*shared_context.error_reporter,
                              cascade_flags));
 
         cascade_info.finish(&self.as_node());
         values
     }
 
+    fn cascade_internal(&self,
+                        context: &StyleContext<Self>,
+                        primary_style: &ComputedStyle,
+                        pseudo_style: &Option<(&PseudoElement, &mut ComputedStyle)>,
+                        booleans: &CascadeBooleans)
+                        -> Arc<ComputedValues> {
+        let mut cascade_flags = CascadeFlags::empty();
+        if booleans.shareable {
+            cascade_flags.insert(SHAREABLE)
+        }
+        if self.skip_root_and_item_based_display_fixup() {
+            cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
+        }
+
+        // Grab the rule node.
+        let rule_node = &pseudo_style.as_ref().map_or(primary_style, |p| &*p.1).rules;
+        self.cascade_with_rules(context, rule_node, primary_style, pseudo_style, cascade_flags)
+    }
+
     /// Computes values and damage for the primary or pseudo style of an element,
     /// setting them on the ElementData.
     fn cascade_primary_or_pseudo<'a>(&self,
                                      context: &mut StyleContext<Self>,
                                      data: &mut ElementData,
                                      pseudo: Option<&PseudoElement>,
                                      possibly_expired_animations: &mut Vec<PropertyAnimation>,
                                      booleans: CascadeBooleans) {
@@ -565,17 +575,17 @@ trait PrivateMatchMethods: TElement {
         let mut primary_style = &mut styles.primary;
         let pseudos = &mut styles.pseudos;
         let mut pseudo_style = pseudo.map(|p| (p, pseudos.get_mut(p).unwrap()));
         let mut old_values =
             pseudo_style.as_mut().map_or_else(|| primary_style.values.take(), |p| p.1.values.take());
 
         // Compute the new values.
         let mut new_values = self.cascade_internal(context, primary_style,
-                                                   &mut pseudo_style, &booleans);
+                                                   &pseudo_style, &booleans);
 
         // Handle animations.
         if booleans.animate {
             self.process_animations(context,
                                     &mut old_values,
                                     &mut new_values,
                                     pseudo,
                                     possibly_expired_animations);
@@ -590,16 +600,43 @@ trait PrivateMatchMethods: TElement {
         if let Some((_, ref mut style)) = pseudo_style {
             style.values = Some(new_values);
         } else {
             primary_style.values = Some(new_values);
         }
     }
 
     #[cfg(feature = "gecko")]
+    fn get_after_change_style(&self,
+                              context: &mut StyleContext<Self>,
+                              primary_style: &ComputedStyle,
+                              pseudo_style: &Option<(&PseudoElement, &mut ComputedStyle)>)
+                              -> Arc<ComputedValues> {
+        let style = &pseudo_style.as_ref().map_or(primary_style, |p| &*p.1);
+        let rule_node = &style.rules;
+        let without_transition_rules =
+            context.shared.stylist.rule_tree.remove_transition_rule_if_applicable(rule_node);
+        if without_transition_rules == *rule_node {
+            // Note that unwrapping here is fine, because the style is
+            // only incomplete during the styling process.
+            return style.values.as_ref().unwrap().clone();
+        }
+
+        let mut cascade_flags = CascadeFlags::empty();
+        if self.skip_root_and_item_based_display_fixup() {
+            cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
+        }
+        self.cascade_with_rules(context,
+                                &without_transition_rules,
+                                primary_style,
+                                &pseudo_style,
+                                cascade_flags)
+    }
+
+    #[cfg(feature = "gecko")]
     fn process_animations(&self,
                           context: &mut StyleContext<Self>,
                           old_values: &mut Option<Arc<ComputedValues>>,
                           new_values: &mut Arc<ComputedValues>,
                           pseudo: Option<&PseudoElement>,
                           _possibly_expired_animations: &mut Vec<PropertyAnimation>) {
         let ref new_box_style = new_values.get_box();
         let has_new_animation_style = new_box_style.animation_name_count() >= 1 &&
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -234,16 +234,47 @@ impl RuleTree {
                 }
             }
         }
 
         // Now the rule is in the relevant place, push the children as
         // necessary.
         Some(self.insert_ordered_rules_from(current, children.into_iter().rev()))
     }
+
+    /// Returns new rule nodes without Transitions level rule.
+    pub fn remove_transition_rule_if_applicable(&self, path: &StrongRuleNode) -> StrongRuleNode {
+        // Return a clone if there is no transition level.
+        if path.cascade_level() != CascadeLevel::Transitions {
+            return path.clone();
+        }
+
+        path.parent().unwrap().clone()
+    }
+
+    /// Returns new rule node without Animations and Transitions level rules.
+    pub fn remove_animation_and_transition_rules(&self, path: &StrongRuleNode) -> StrongRuleNode {
+        // Return a clone if there is neither animation nor transition level.
+        if !path.has_animation_or_transition_rules() {
+            return path.clone();
+        }
+
+        let iter = path.self_and_ancestors().take_while(|node| node.cascade_level() >= CascadeLevel::Animations);
+        let mut last = path;
+        let mut children = vec![];
+        for node in iter {
+            if node.cascade_level() != CascadeLevel::Animations &&
+               node.cascade_level() != CascadeLevel::Transitions {
+                children.push((node.get().source.clone().unwrap(), node.cascade_level()));
+            }
+            last = node;
+        }
+
+        self.insert_ordered_rules_from(last.parent().unwrap().clone(), children.into_iter().rev())
+    }
 }
 
 /// The number of RuleNodes added to the free list before we will consider
 /// doing a GC when calling maybe_gc().  (The value is copied from Gecko,
 /// where it likely did not result from a rigorous performance analysis.)
 const RULE_TREE_GC_INTERVAL: usize = 300;
 
 /// The cascade level these rules are relevant at, as per[1].
@@ -723,16 +754,23 @@ impl StrongRuleNode {
     }
 
     unsafe fn maybe_gc(&self) {
         debug_assert!(self.get().is_root(), "Can't call GC on a non-root node!");
         if self.get().free_count.load(Ordering::SeqCst) > RULE_TREE_GC_INTERVAL {
             self.gc();
         }
     }
+
+    /// Returns true if there is either animation or transition level rule.
+    pub fn has_animation_or_transition_rules(&self) -> bool {
+        self.self_and_ancestors()
+            .take_while(|node| node.cascade_level() >= CascadeLevel::Animations)
+            .any(|node| matches!(node.cascade_level(), CascadeLevel::Animations | CascadeLevel::Transitions))
+    }
 }
 
 /// An iterator over a rule node and its ancestors.
 #[derive(Clone)]
 pub struct SelfAndAncestors<'a> {
     current: Option<&'a StrongRuleNode>,
 }