servo: Merge #17128 - stylo: Basic support for first-line/letter dynamic changes (from emilio:first-line-letter); r=bholley
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 01 Jun 2017 12:13:43 -0700
changeset 361882 e3e4beb7042b4d8eca079f0a62f3fdf5098729a2
parent 361881 b20f3d4ad0fb15b220045c0d4b2aefd6c80ad6d1
child 361883 cf869515f9be63a3665cbe5b207e436c6dbb6da1
push id31947
push userkwierso@gmail.com
push dateFri, 02 Jun 2017 00:13:09 +0000
treeherdermozilla-central@fec3a4b50acd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs17128
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 #17128 - stylo: Basic support for first-line/letter dynamic changes (from emilio:first-line-letter); r=bholley Source-Repo: https://github.com/servo/servo Source-Revision: 17e69af0c9323927f724ff412867b24a0364efa1
servo/components/style/data.rs
servo/components/style/dom.rs
servo/components/style/gecko/generated/pseudo_element_definition.rs
servo/components/style/gecko/pseudo_element_definition.mako.rs
servo/components/style/matching.rs
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -1,14 +1,15 @@
 /* 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/. */
 
 //! Per-node data used in style calculation.
 
+use arrayvec::ArrayVec;
 use context::SharedStyleContext;
 use dom::TElement;
 use properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock};
 use properties::longhands::display::computed_value as display;
 use restyle_hints::{HintComputationContext, RestyleReplacements, RestyleHint};
 use rule_tree::StrongRuleNode;
 use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
 use selectors::matching::VisitedHandlingMode;
@@ -188,18 +189,18 @@ impl EagerPseudoStyles {
         let empty = self.0.as_ref().unwrap().iter().all(|x| x.is_none());
         if empty {
             self.0 = None;
         }
         result
     }
 
     /// Returns a list of the pseudo-elements.
-    pub fn keys(&self) -> Vec<PseudoElement> {
-        let mut v = Vec::new();
+    pub fn keys(&self) -> ArrayVec<[PseudoElement; EAGER_PSEUDO_COUNT]> {
+        let mut v = ArrayVec::new();
         if let Some(ref arr) = self.0 {
             for i in 0..EAGER_PSEUDO_COUNT {
                 if arr[i].is_some() {
                     v.push(PseudoElement::from_eager_index(i));
                 }
             }
         }
         v
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -377,16 +377,30 @@ pub trait TElement : Eq + PartialEq + De
     /// XXX(emilio): It's a bit unfortunate we need to pass the current computed
     /// values as an argument here, but otherwise Servo would crash due to
     /// double borrows to return it.
     fn existing_style_for_restyle_damage<'a>(&'a self,
                                              current_computed_values: &'a ComputedValues,
                                              pseudo: Option<&PseudoElement>)
                                              -> Option<&'a PreExistingComputedValues>;
 
+    /// Whether a given element may generate a pseudo-element.
+    ///
+    /// This is useful to avoid computing, for example, pseudo styles for
+    /// `::-first-line` or `::-first-letter`, when we know it won't affect us.
+    ///
+    /// TODO(emilio, bz): actually implement the logic for it.
+    fn may_generate_pseudo(
+        &self,
+        _pseudo: &PseudoElement,
+        _primary_style: &ComputedValues,
+    ) -> bool {
+        true
+    }
+
     /// Returns true if this element may have a descendant needing style processing.
     ///
     /// Note that we cannot guarantee the existence of such an element, because
     /// it may have been removed from the DOM between marking it for restyle and
     /// the actual restyle traversal.
     fn has_dirty_descendants(&self) -> bool;
 
     /// Returns whether state or attributes that may change style have changed
@@ -439,17 +453,18 @@ pub trait TElement : Eq + PartialEq + De
     unsafe fn unset_dirty_descendants(&self);
 
     /// Similar to the dirty_descendants but for representing a descendant of
     /// the element needs to be updated in animation-only traversal.
     fn has_animation_only_dirty_descendants(&self) -> bool {
         false
     }
 
-    /// Flag that this element has a descendant for animation-only restyle processing.
+    /// Flag that this element has a descendant for animation-only restyle
+    /// processing.
     ///
     /// Only safe to call with exclusive access to the element.
     unsafe fn set_animation_only_dirty_descendants(&self) {
     }
 
     /// Flag that this element has no descendant for animation-only restyle processing.
     ///
     /// Only safe to call with exclusive access to the element.
--- a/servo/components/style/gecko/generated/pseudo_element_definition.rs
+++ b/servo/components/style/gecko/generated/pseudo_element_definition.rs
@@ -171,22 +171,24 @@ pub enum PseudoElement {
         MozSVGForeignContent,
         /// :-moz-svg-text
         MozSVGText,
 }
 
 
 
 /// The number of eager pseudo-elements.
-pub const EAGER_PSEUDO_COUNT: usize = 2;
+pub const EAGER_PSEUDO_COUNT: usize = 4;
 
 /// The list of eager pseudos.
 pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
     PseudoElement::Before,
     PseudoElement::After,
+    PseudoElement::FirstLine,
+    PseudoElement::FirstLetter,
 ];
 
 impl PseudoElement {
     /// Executes a closure with each pseudo-element as an argument.
     pub fn each<F>(mut fun: F)
         where F: FnMut(Self),
     {
             fun(PseudoElement::After);
@@ -453,17 +455,17 @@ impl PseudoElement {
                 PseudoElement::MozSVGText => true,
         }
     }
 
     /// Whether this pseudo-element is eagerly-cascaded.
     #[inline]
     pub fn is_eager(&self) -> bool {
         matches!(*self,
-                 PseudoElement::Before | PseudoElement::After)
+                 PseudoElement::Before | PseudoElement::After | PseudoElement::FirstLine | PseudoElement::FirstLetter)
     }
 
     /// Gets the flags associated to this pseudo-element, or 0 if it's an
     /// anonymous box.
     pub fn flags(&self) -> u32 {
         match *self {
                 PseudoElement::After => {
                         structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_after
--- a/servo/components/style/gecko/pseudo_element_definition.mako.rs
+++ b/servo/components/style/gecko/pseudo_element_definition.mako.rs
@@ -6,17 +6,17 @@
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub enum PseudoElement {
     % for pseudo in PSEUDOS:
         /// ${pseudo.value}
         ${pseudo.capitalized()},
     % endfor
 }
 
-<% EAGER_PSEUDOS = ["Before", "After"] %>
+<% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %>
 
 /// The number of eager pseudo-elements.
 pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};
 
 /// The list of eager pseudos.
 pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
     % for eager_pseudo_name in EAGER_PSEUDOS:
     PseudoElement::${eager_pseudo_name},
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -352,16 +352,17 @@ trait PrivateMatchMethods: TElement {
             // pseudo-elements, it doesn't seem worth the effort at a glance.
             //
             // For the same reason as described in match_primary, if we are
             // computing default styles, we aren't guaranteed the parent
             // will have eagerly computed our styles, so we just handled it
             // below like a lazy pseudo.
             let only_default_rules = context.shared.traversal_flags.for_default_styles();
             if pseudo.is_eager() && !only_default_rules {
+                debug_assert!(pseudo.is_before_or_after());
                 let parent = self.parent_element().unwrap();
                 if !parent.may_have_animations() ||
                    primary_style.rules.get_animation_rules().is_empty() {
                     let parent_data = parent.borrow_data().unwrap();
                     let pseudo_style =
                         parent_data.styles().pseudos.get(&pseudo).unwrap();
                     let values = cascade_visited.values(pseudo_style);
                     return values.clone()
@@ -1101,16 +1102,20 @@ pub trait MatchMethods : TElement {
         SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
             // For pseudo-elements, we only try to match visited rules if there
             // are also unvisited rules.  (This matches Gecko's behavior.)
             if visited_handling == VisitedHandlingMode::RelevantLinkVisited &&
                !data.styles().pseudos.has(&pseudo) {
                 return
             }
 
+            if !self.may_generate_pseudo(&pseudo, data.styles().primary.values()) {
+                return;
+            }
+
             debug_assert!(applicable_declarations.is_empty());
             // NB: We handle animation rules for ::before and ::after when
             // traversing them.
             stylist.push_applicable_declarations(self,
                                                  Some(&pseudo),
                                                  None,
                                                  None,
                                                  AnimationRules(None, None),