style: Factor out per-origin data storage. r?emilio draft
authorCameron McCormack <cam@mcc.id.au>
Sat, 12 Aug 2017 13:40:44 +0800
changeset 645501 fb7152a4cffafd5487d341310d90554eba6dc0c6
parent 645500 048f81b21e6a9bd3992cbfed5e0fca05c8bc871a
child 645502 54c50d92422855bf9d16156f3e82b4daf3a9b8bf
push id73769
push userbmo:cam@mcc.id.au
push dateSun, 13 Aug 2017 04:04:30 +0000
reviewersemilio
milestone57.0a1
style: Factor out per-origin data storage. r?emilio Also add an iter_mut_origins() function. MozReview-Commit-ID: Ddh3uKb0CWS
servo/components/style/gecko/data.rs
servo/components/style/stylesheets/mod.rs
servo/components/style/stylesheets/origin.rs
servo/components/style/stylist.rs
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -12,17 +12,17 @@ use gecko_bindings::structs::RawGeckoPre
 use gecko_bindings::structs::nsIDocument;
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 use media_queries::{Device, MediaList};
 use properties::ComputedValues;
 use servo_arc::Arc;
 use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
 use stylesheet_set::StylesheetSet;
-use stylesheets::{StylesheetContents, StylesheetInDocument};
+use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument};
 use stylist::{ExtraStyleData, Stylist};
 
 /// Little wrapper to a Gecko style sheet.
 #[derive(PartialEq, Eq, Debug)]
 pub struct GeckoStyleSheet(*const ServoStyleSheet);
 
 impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
     fn to_media_list_key(&self) -> MediaListKey {
@@ -113,17 +113,17 @@ impl StylesheetInDocument for GeckoStyle
 pub struct PerDocumentStyleDataImpl {
     /// Rule processor.
     pub stylist: Stylist,
 
     /// List of stylesheets, mirrored from Gecko.
     pub stylesheets: StylesheetSet<GeckoStyleSheet>,
 
     /// List of effective @font-face and @counter-style rules.
-    pub extra_style_data: ExtraStyleData,
+    pub extra_style_data: PerOrigin<ExtraStyleData>,
 }
 
 /// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
 /// and unexpected races while trying to mutate it.
 pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);
 
 impl PerDocumentStyleData {
     /// Create a dummy `PerDocumentStyleData`.
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -39,17 +39,17 @@ pub use self::font_feature_values_rule::
 pub use self::import_rule::ImportRule;
 pub use self::keyframes_rule::KeyframesRule;
 pub use self::loader::StylesheetLoader;
 pub use self::media_rule::MediaRule;
 pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard};
 #[cfg(feature = "gecko")]
 pub use self::memory::{MallocSizeOfWithRepeats, SizeOfState};
 pub use self::namespace_rule::NamespaceRule;
-pub use self::origin::Origin;
+pub use self::origin::{Origin, PerOrigin, PerOriginClear};
 pub use self::page_rule::PageRule;
 pub use self::rule_parser::{State, TopLevelRuleParser};
 pub use self::rule_list::{CssRules, CssRulesHelpers};
 pub use self::rules_iterator::{AllRules, EffectiveRules, NestedRuleIterationCondition, RulesIterator};
 pub use self::stylesheet::{Namespaces, Stylesheet, StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
 pub use self::style_rule::StyleRule;
 pub use self::supports_rule::SupportsRule;
 pub use self::viewport_rule::ViewportRule;
--- a/servo/components/style/stylesheets/origin.rs
+++ b/servo/components/style/stylesheets/origin.rs
@@ -1,22 +1,146 @@
 /* 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/. */
 
 ///! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins).
 
+use std::marker::PhantomData;
+use std::mem::transmute;
+
 /// Each style rule has an origin, which determines where it enters the cascade.
 ///
 /// https://drafts.csswg.org/css-cascade/#cascading-origins
 #[derive(Clone, PartialEq, Eq, Copy, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Origin {
     /// https://drafts.csswg.org/css-cascade/#cascade-origin-us
     UserAgent,
 
     /// https://drafts.csswg.org/css-cascade/#cascade-origin-author
     Author,
 
     /// https://drafts.csswg.org/css-cascade/#cascade-origin-user
     User,
 }
 
+/// An object that stores a `T` for each origin of the CSS cascade.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Debug, Default)]
+pub struct PerOrigin<T> {
+    /// Data for `Origin::UserAgent`.
+    pub user_agent: T,
+
+    /// Data for `Origin::Author`.
+    pub author: T,
+
+    /// Data for `Origin::User`.
+    pub user: T,
+}
+
+impl<T> PerOrigin<T> {
+    /// Returns a reference to the per-origin data for the specified origin.
+    #[inline]
+    pub fn borrow_for_origin(&self, origin: &Origin) -> &T {
+        match *origin {
+            Origin::UserAgent => &self.user_agent,
+            Origin::Author => &self.author,
+            Origin::User => &self.user,
+        }
+    }
+
+    /// Returns a mutable reference to the per-origin data for the specified
+    /// origin.
+    #[inline]
+    pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T {
+        match *origin {
+            Origin::UserAgent => &mut self.user_agent,
+            Origin::Author => &mut self.author,
+            Origin::User => &mut self.user,
+        }
+    }
+
+    /// Iterates over references to per-origin extra style data, from highest
+    /// level (user) to lowest (user agent).
+    pub fn iter_origins(&self) -> PerOriginIter<T> {
+        PerOriginIter {
+            data: &self,
+            cur: 0,
+        }
+    }
+
+    /// Iterates over mutable references to per-origin extra style data, from
+    /// highest level (user) to lowest (user agent).
+    pub fn iter_mut_origins(&mut self) -> PerOriginIterMut<T> {
+        PerOriginIterMut {
+            data: self,
+            cur: 0,
+            _marker: PhantomData,
+        }
+    }
+}
+
+/// An object that can be cleared.
+pub trait PerOriginClear {
+    /// Clears the object.
+    fn clear(&mut self);
+}
+
+impl<T> PerOriginClear for PerOrigin<T> where T : PerOriginClear {
+    fn clear(&mut self) {
+        self.user_agent.clear();
+        self.author.clear();
+        self.user.clear();
+    }
+}
+
+/// Iterator over `PerOrigin<T>`, from highest level (user) to lowest
+/// (user agent).
+///
+/// We rely on this specific order for correctly looking up @font-face,
+/// @counter-style and @keyframes rules.
+pub struct PerOriginIter<'a, T: 'a> {
+    data: &'a PerOrigin<T>,
+    cur: usize,
+}
+
+impl<'a, T> Iterator for PerOriginIter<'a, T> where T: 'a {
+    type Item = (&'a T, Origin);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let result = match self.cur {
+            0 => (&self.data.user, Origin::User),
+            1 => (&self.data.author, Origin::Author),
+            2 => (&self.data.user_agent, Origin::UserAgent),
+            _ => return None,
+        };
+        self.cur += 1;
+        Some(result)
+    }
+}
+
+/// Like `PerOriginIter<T>`, but iterates over mutable references to the
+/// per-origin data.
+///
+/// We must use unsafe code here since it's not possible for the borrow
+/// checker to know that we are safely returning a different reference
+/// each time from `next()`.
+pub struct PerOriginIterMut<'a, T: 'a> {
+    data: *mut PerOrigin<T>,
+    cur: usize,
+    _marker: PhantomData<&'a mut PerOrigin<T>>,
+}
+
+impl<'a, T> Iterator for PerOriginIterMut<'a, T> where T: 'a {
+    type Item = (&'a mut T, Origin);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let result = match self.cur {
+            0 => (unsafe { transmute(&mut (*self.data).user) }, Origin::User),
+            1 => (unsafe { transmute(&mut (*self.data).author) }, Origin::Author),
+            2 => (unsafe { transmute(&mut (*self.data).user_agent) }, Origin::UserAgent),
+            _ => return None,
+        };
+        self.cur += 1;
+        Some(result)
+    }
+}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -35,17 +35,18 @@ use selectors::visitor::SelectorVisitor;
 use servo_arc::{Arc, ArcBorrow};
 use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
 use smallvec::VecLike;
 use std::fmt::Debug;
 use style_traits::viewport::ViewportConstraints;
 #[cfg(feature = "gecko")]
 use stylesheets::{CounterStyleRule, FontFaceRule};
 use stylesheets::{CssRule, StyleRule};
-use stylesheets::{StylesheetInDocument, Origin, UserAgentStylesheets};
+use stylesheets::{StylesheetInDocument, Origin, PerOrigin, PerOriginClear};
+use stylesheets::UserAgentStylesheets;
 use stylesheets::keyframes_rule::KeyframesAnimation;
 use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
 use thread_state;
 
 pub use ::fnv::FnvHashMap;
 
 /// This structure holds all the selectors and device characteristics
 /// for a given document. The selectors are converted into `Rule`s
@@ -85,17 +86,17 @@ pub struct Stylist {
 
     /// If true, the stylist is in a cleared state (e.g. just-constructed, or
     /// had clear() called on it with no following rebuild()).
     is_cleared: bool,
 
     /// Selector maps for all of the style sheets in the stylist, after
     /// evalutaing media rules against the current device, split out per
     /// cascade level.
-    cascade_data: CascadeData,
+    cascade_data: PerOrigin<CascadeData>,
 
     /// The rule tree, that stores the results of selector matching.
     rule_tree: RuleTree,
 
     /// Applicable declarations for a given non-eagerly cascaded pseudo-element.
     /// These are eagerly computed once, and then used to resolve the new
     /// computed values on the fly on layout.
     ///
@@ -105,100 +106,16 @@ pub struct Stylist {
     /// A monotonically increasing counter to represent the order on which a
     /// style rule appears in a stylesheet, needed to sort them by source order.
     rules_source_order: u32,
 
     /// The total number of times the stylist has been rebuilt.
     num_rebuilds: usize,
 }
 
-/// This struct holds data which users of Stylist may want to extract
-/// from stylesheets which can be done at the same time as updating.
-#[cfg(feature = "gecko")]
-#[derive(Default)]
-pub struct ExtraStyleData {
-    /// Extra data from user agent stylesheets
-    user_agent: PerOriginExtraStyleData,
-    /// Extra data from author stylesheets
-    author: PerOriginExtraStyleData,
-    /// Extra data from user stylesheets
-    user: PerOriginExtraStyleData,
-}
-
-/// This struct holds data which users of Stylist may want to extract
-/// from stylesheets which can be done at the same time as updating.
-#[cfg(feature = "gecko")]
-#[derive(Default)]
-pub struct PerOriginExtraStyleData {
-    /// A list of effective font-face rules and their origin.
-    pub font_faces: Vec<Arc<Locked<FontFaceRule>>>,
-    /// A map of effective counter-style rules.
-    pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
-}
-
-#[cfg(feature = "gecko")]
-impl ExtraStyleData {
-    /// Clear the internal data.
-    pub fn clear(&mut self) {
-        self.user_agent.clear();
-        self.author.clear();
-        self.user.clear();
-    }
-
-    /// Returns a reference to the per-origin extra style data for
-    /// the specified origin.
-    #[inline]
-    pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut PerOriginExtraStyleData {
-        match *origin {
-            Origin::UserAgent => &mut self.user_agent,
-            Origin::Author => &mut self.author,
-            Origin::User => &mut self.user,
-        }
-    }
-
-    /// Iterates over the per-origin extra style data, from highest level (user)
-    /// to lowest (user agent).
-    pub fn iter_origins(&self) -> ExtraStyleDataIter {
-        ExtraStyleDataIter {
-            extra_style_data: &self,
-            cur: 0,
-        }
-    }
-}
-
-#[cfg(feature = "gecko")]
-impl PerOriginExtraStyleData {
-    /// Clears the stored @font-face and @counter-style rules.
-    fn clear(&mut self) {
-        self.font_faces.clear();
-        self.counter_styles.clear();
-    }
-
-    /// Add the given @font-face rule.
-    fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
-        self.font_faces.push(rule.clone());
-    }
-
-    /// Add the given @counter-style rule.
-    fn add_counter_style(&mut self, guard: &SharedRwLockReadGuard,
-                         rule: &Arc<Locked<CounterStyleRule>>) {
-        let name = rule.read_with(guard).mName.raw::<nsIAtom>().into();
-        self.counter_styles.insert(name, rule.clone());
-    }
-}
-
-#[allow(missing_docs)]
-#[cfg(feature = "servo")]
-pub struct ExtraStyleData;
-
-#[cfg(feature = "servo")]
-impl ExtraStyleData {
-    fn clear(&mut self) {}
-}
-
 /// What cascade levels to include when styling elements.
 #[derive(Copy, Clone, PartialEq)]
 pub enum RuleInclusion {
     /// Include rules for style sheets at all cascade levels.  This is the
     /// normal rule inclusion mode.
     All,
     /// Only include rules from UA and user level sheets.  Used to implement
     /// `getDefaultComputedStyle`.
@@ -224,63 +141,63 @@ impl Stylist {
         Stylist {
             viewport_constraints: None,
             device: device,
             is_device_dirty: true,
             is_cleared: true,
             quirks_mode: quirks_mode,
             effective_media_query_results: EffectiveMediaQueryResults::new(),
 
-            cascade_data: CascadeData::new(),
+            cascade_data: Default::default(),
             precomputed_pseudo_element_decls: PerPseudoElementMap::default(),
             rules_source_order: 0,
             rule_tree: RuleTree::new(),
             num_rebuilds: 0,
         }
 
         // FIXME: Add iso-8859-9.css when the document’s encoding is ISO-8859-8.
     }
 
     /// Returns the number of selectors.
     pub fn num_selectors(&self) -> usize {
-        self.cascade_data.iter_origins().map(|d| d.num_selectors).sum()
+        self.cascade_data.iter_origins().map(|(d, _)| d.num_selectors).sum()
     }
 
     /// Returns the number of declarations.
     pub fn num_declarations(&self) -> usize {
-        self.cascade_data.iter_origins().map(|d| d.num_declarations).sum()
+        self.cascade_data.iter_origins().map(|(d, _)| d.num_declarations).sum()
     }
 
     /// Returns the number of times the stylist has been rebuilt.
     pub fn num_rebuilds(&self) -> usize {
         self.num_rebuilds
     }
 
     /// Returns the number of revalidation_selectors.
     pub fn num_revalidation_selectors(&self) -> usize {
         self.cascade_data.iter_origins()
-            .map(|d| d.selectors_for_cache_revalidation.len()).sum()
+            .map(|(d, _)| d.selectors_for_cache_revalidation.len()).sum()
     }
 
     /// Returns the number of entries in invalidation maps.
     pub fn num_invalidations(&self) -> usize {
         self.cascade_data.iter_origins()
-            .map(|d| d.invalidation_map.len()).sum()
+            .map(|(d, _)| d.invalidation_map.len()).sum()
     }
 
     /// Invokes `f` with the `InvalidationMap` for each origin.
     ///
     /// NOTE(heycam) This might be better as an `iter_invalidation_maps`, once
     /// we have `impl trait` and can return that easily without bothering to
     /// create a whole new iterator type.
     pub fn each_invalidation_map<F>(&self, mut f: F)
         where F: FnMut(&InvalidationMap)
     {
-        for origin_cascade_data in self.cascade_data.iter_origins() {
-            f(&origin_cascade_data.invalidation_map)
+        for (data, _) in self.cascade_data.iter_origins() {
+            f(&data.invalidation_map)
         }
     }
 
     /// Clear the stylist's state, effectively resetting it to more or less
     /// the state Stylist::new creates.
     ///
     /// We preserve the state of the following members:
     ///   device: Someone might have set this on us.
@@ -319,17 +236,17 @@ impl Stylist {
     /// device is dirty, which means we need to re-evaluate media queries.
     pub fn rebuild<'a, I, S>(
         &mut self,
         doc_stylesheets: I,
         guards: &StylesheetGuards,
         ua_stylesheets: Option<&UserAgentStylesheets>,
         stylesheets_changed: bool,
         author_style_disabled: bool,
-        extra_data: &mut ExtraStyleData
+        extra_data: &mut PerOrigin<ExtraStyleData>
     ) -> bool
     where
         I: Iterator<Item = &'a S> + Clone,
         S: StylesheetInDocument + ToMediaListKey + 'static,
     {
         debug_assert!(!self.is_cleared || self.is_device_dirty);
 
         self.is_cleared = false;
@@ -399,17 +316,17 @@ impl Stylist {
     /// either clear() or rebuild(), with the latter done lazily, instead.
     pub fn update<'a, I, S>(
         &mut self,
         doc_stylesheets: I,
         guards: &StylesheetGuards,
         ua_stylesheets: Option<&UserAgentStylesheets>,
         stylesheets_changed: bool,
         author_style_disabled: bool,
-        extra_data: &mut ExtraStyleData
+        extra_data: &mut PerOrigin<ExtraStyleData>
     ) -> bool
     where
         I: Iterator<Item = &'a S> + Clone,
         S: StylesheetInDocument + ToMediaListKey + 'static,
     {
         debug_assert!(!self.is_cleared || self.is_device_dirty);
 
         // We have to do a dirtiness check before clearing, because if
@@ -421,17 +338,17 @@ impl Stylist {
         self.rebuild(doc_stylesheets, guards, ua_stylesheets, stylesheets_changed,
                      author_style_disabled, extra_data)
     }
 
     fn add_stylesheet<S>(
         &mut self,
         stylesheet: &S,
         guard: &SharedRwLockReadGuard,
-        _extra_data: &mut ExtraStyleData
+        _extra_data: &mut PerOrigin<ExtraStyleData>
     )
     where
         S: StylesheetInDocument + ToMediaListKey + 'static,
     {
         if !stylesheet.enabled() ||
            !stylesheet.is_effective_for_device(&self.device, guard) {
             return;
         }
@@ -569,21 +486,21 @@ impl Stylist {
                                            -> bool {
         if self.is_cleared || self.is_device_dirty {
             // We can't tell what attributes are in our style rules until
             // we rebuild.
             true
         } else if *local_name == local_name!("style") {
             self.cascade_data
                 .iter_origins()
-                .any(|d| d.style_attribute_dependency)
+                .any(|(d, _)| d.style_attribute_dependency)
         } else {
             self.cascade_data
                 .iter_origins()
-                .any(|d| {
+                .any(|(d, _)| {
                     d.attribute_dependencies
                         .might_contain_hash(local_name.get_hash())
                 })
         }
     }
 
     /// Returns whether the given ElementState bit might be relied upon by a
     /// selector of some rule in the stylist.
@@ -597,17 +514,17 @@ impl Stylist {
         }
     }
 
     /// Returns whether the given ElementState bit is relied upon by a selector
     /// of some rule in the stylist.
     pub fn has_state_dependency(&self, state: ElementState) -> bool {
         self.cascade_data
             .iter_origins()
-            .any(|d| d.state_dependencies.intersects(state))
+            .any(|(d, _)| d.state_dependencies.intersects(state))
     }
 
     /// Computes the style for a given "precomputed" pseudo-element, taking the
     /// universal rules and applying them.
     ///
     /// If `inherit_all` is true, then all properties are inherited from the
     /// parent; otherwise, non-inherited properties are reset to their initial
     /// values. The flow constructor uses this flag when constructing anonymous
@@ -847,33 +764,39 @@ impl Stylist {
                             Some(layout_parent_style),
                             visited_values,
                             None,
                             font_metrics,
                             cascade_flags,
                             self.quirks_mode)
     }
 
+    fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
+        self.cascade_data
+            .iter_origins()
+            .any(|(d, _)| d.has_rules_for_pseudo(pseudo))
+    }
+
     /// Computes the cascade inputs for a lazily-cascaded pseudo-element.
     ///
     /// See the documentation on lazy pseudo-elements in
     /// docs/components/style.md
     pub fn lazy_pseudo_rules<E>(&self,
                                 guards: &StylesheetGuards,
                                 element: &E,
                                 pseudo: &PseudoElement,
                                 is_probe: bool,
                                 rule_inclusion: RuleInclusion)
                                 -> CascadeInputs
         where E: TElement
     {
         let pseudo = pseudo.canonical();
         debug_assert!(pseudo.is_lazy());
 
-        if !self.cascade_data.has_rules_for_pseudo(&pseudo) {
+        if !self.has_rules_for_pseudo(&pseudo) {
             return CascadeInputs::default()
         }
 
         // Apply the selector flags. We should be in sequential mode
         // already, so we can directly apply the parent flags.
         let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
             if cfg!(feature = "servo") {
                 // Servo calls this function from the worker, but only for internal
@@ -1319,32 +1242,32 @@ impl Stylist {
     }
 
     /// Given an id, returns whether there might be any rules for that id in any
     /// of our rule maps.
     #[inline]
     pub fn may_have_rules_for_id(&self, id: &Atom) -> bool {
         self.cascade_data
             .iter_origins()
-            .any(|d| d.mapped_ids.might_contain_hash(id.get_hash()))
+            .any(|(d, _)| d.mapped_ids.might_contain_hash(id.get_hash()))
     }
 
     /// Return whether the device is dirty, that is, whether the screen size or
     /// media type have changed (for now).
     #[inline]
     pub fn is_device_dirty(&self) -> bool {
         self.is_device_dirty
     }
 
     /// Returns the registered `@keyframes` animation for the specified name.
     #[inline]
     pub fn get_animation(&self, name: &Atom) -> Option<&KeyframesAnimation> {
         self.cascade_data
             .iter_origins()
-            .filter_map(|d| d.animations.get(name))
+            .filter_map(|(d, _)| d.animations.get(name))
             .next()
     }
 
     /// Computes the match results of a given element against the set of
     /// revalidation selectors.
     pub fn match_revalidation_selectors<E, F>(&self,
                                               element: &E,
                                               bloom: Option<&BloomFilter>,
@@ -1359,18 +1282,18 @@ impl Stylist {
             MatchingContext::new(MatchingMode::Normal, bloom, self.quirks_mode);
 
         // Note that, by the time we're revalidating, we're guaranteed that the
         // candidate and the entry have the same id, classes, and local name.
         // This means we're guaranteed to get the same rulehash buckets for all
         // the lookups, which means that the bitvecs are comparable. We verify
         // this in the caller by asserting that the bitvecs are same-length.
         let mut results = BitVec::new();
-        for origin_cascade_data in self.cascade_data.iter_origins() {
-            origin_cascade_data.selectors_for_cache_revalidation.lookup(
+        for (data, _) in self.cascade_data.iter_origins() {
+            data.selectors_for_cache_revalidation.lookup(
                 *element, self.quirks_mode, &mut |selector_and_hashes| {
                     results.push(matches_selector(&selector_and_hashes.selector,
                                                   selector_and_hashes.selector_offset,
                                                   Some(&selector_and_hashes.hashes),
                                                   element,
                                                   &mut matching_context,
                                                   flags_setter));
                     true
@@ -1426,16 +1349,54 @@ impl Stylist {
     }
 
     /// Accessor for a shared reference to the rule tree.
     pub fn rule_tree(&self) -> &RuleTree {
         &self.rule_tree
     }
 }
 
+/// This struct holds data which users of Stylist may want to extract
+/// from stylesheets which can be done at the same time as updating.
+#[derive(Default)]
+pub struct ExtraStyleData {
+    /// A list of effective font-face rules and their origin.
+    #[cfg(feature = "gecko")]
+    pub font_faces: Vec<Arc<Locked<FontFaceRule>>>,
+
+    /// A map of effective counter-style rules.
+    #[cfg(feature = "gecko")]
+    pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
+}
+
+#[cfg(feature = "gecko")]
+impl ExtraStyleData {
+    /// Add the given @font-face rule.
+    fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
+        self.font_faces.push(rule.clone());
+    }
+
+    /// Add the given @counter-style rule.
+    fn add_counter_style(&mut self, guard: &SharedRwLockReadGuard,
+                         rule: &Arc<Locked<CounterStyleRule>>) {
+        let name = rule.read_with(guard).mName.raw::<nsIAtom>().into();
+        self.counter_styles.insert(name, rule.clone());
+    }
+}
+
+impl PerOriginClear for ExtraStyleData {
+    fn clear(&mut self) {
+        #[cfg(feature = "gecko")]
+        {
+            self.font_faces.clear();
+            self.counter_styles.clear();
+        }
+    }
+}
+
 /// SelectorMapEntry implementation for use in our revalidation selector map.
 #[derive(Clone, Debug)]
 struct RevalidationSelectorAndHashes {
     selector: Selector<SelectorImpl>,
     selector_offset: usize,
     hashes: AncestorHashes,
 }
 
@@ -1594,121 +1555,21 @@ impl<'a> SelectorVisitor for StylistSele
             }
             _ => {},
         }
 
         true
     }
 }
 
-/// Data resulting from performing the CSS cascade.
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Debug)]
-struct CascadeData {
-    /// Rules from user agent stylesheets
-    user_agent: PerOriginCascadeData,
-    /// Rules from author stylesheets
-    author: PerOriginCascadeData,
-    /// Rules from user stylesheets
-    user: PerOriginCascadeData,
-}
-
-impl CascadeData {
-    fn new() -> Self {
-        CascadeData {
-            user_agent: PerOriginCascadeData::new(),
-            author: PerOriginCascadeData::new(),
-            user: PerOriginCascadeData::new(),
-        }
-    }
-
-    #[inline]
-    fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut PerOriginCascadeData {
-        match *origin {
-            Origin::UserAgent => &mut self.user_agent,
-            Origin::Author => &mut self.author,
-            Origin::User => &mut self.user,
-        }
-    }
-
-    fn clear(&mut self) {
-        self.user_agent.clear();
-        self.author.clear();
-        self.user.clear();
-    }
-
-    fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
-        self.iter_origins().any(|d| d.has_rules_for_pseudo(pseudo))
-    }
-
-    fn iter_origins(&self) -> CascadeDataIter {
-        CascadeDataIter {
-            cascade_data: &self,
-            cur: 0,
-        }
-    }
-}
-
-/// Iterator over `PerOriginCascadeData`, from highest level (user) to lowest
-/// (user agent).
-///
-/// We rely on this specific order for correctly looking up animations
-/// (prioritizing rules at higher cascade levels), among other things.
-struct CascadeDataIter<'a> {
-    cascade_data: &'a CascadeData,
-    cur: usize,
-}
-
-impl<'a> Iterator for CascadeDataIter<'a> {
-    type Item = &'a PerOriginCascadeData;
-
-    fn next(&mut self) -> Option<&'a PerOriginCascadeData> {
-        let result = match self.cur {
-            0 => &self.cascade_data.user,
-            1 => &self.cascade_data.author,
-            2 => &self.cascade_data.user_agent,
-            _ => return None,
-        };
-        self.cur += 1;
-        Some(result)
-    }
-}
-
-/// Iterator over `PerOriginExtraStyleData`, from highest level (user) to lowest
-/// (user agent).
-///
-/// We rely on this specific order for correctly looking up the @font-face
-/// and @counter-style rules.
-#[cfg(feature = "gecko")]
-pub struct ExtraStyleDataIter<'a> {
-    extra_style_data: &'a ExtraStyleData,
-    cur: usize,
-}
-
-#[cfg(feature = "gecko")]
-impl<'a> Iterator for ExtraStyleDataIter<'a> {
-    type Item = (&'a PerOriginExtraStyleData, Origin);
-
-    fn next(&mut self) -> Option<(&'a PerOriginExtraStyleData, Origin)> {
-        let result = match self.cur {
-            0 => (&self.extra_style_data.user, Origin::User),
-            1 => (&self.extra_style_data.author, Origin::Author),
-            2 => (&self.extra_style_data.user_agent, Origin::UserAgent),
-            _ => return None,
-        };
-        self.cur += 1;
-        Some(result)
-    }
-}
-
 /// Data resulting from performing the CSS cascade that is specific to a given
 /// origin.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Debug)]
-struct PerOriginCascadeData {
+struct CascadeData {
     /// Rules from stylesheets at this `CascadeData`'s origin.
     element_map: SelectorMap<Rule>,
 
     /// Rules from stylesheets at this `CascadeData`'s origin that correspond
     /// to a given pseudo-element.
     pseudos_map: PerPseudoElementMap<SelectorMap<Rule>>,
 
     /// A map with all the animations at this `CascadeData`'s origin, indexed
@@ -1753,17 +1614,17 @@ struct PerOriginCascadeData {
 
     /// The total number of selectors.
     num_selectors: usize,
 
     /// The total number of declarations.
     num_declarations: usize,
 }
 
-impl PerOriginCascadeData {
+impl CascadeData {
     fn new() -> Self {
         Self {
             element_map: SelectorMap::new(),
             pseudos_map: PerPseudoElementMap::default(),
             animations: Default::default(),
             invalidation_map: InvalidationMap::new(),
             attribute_dependencies: NonCountingBloomFilter::new(),
             style_attribute_dependency: false,
@@ -1778,32 +1639,40 @@ impl PerOriginCascadeData {
     #[inline]
     fn borrow_for_pseudo(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
         match pseudo {
             Some(pseudo) => self.pseudos_map.get(&pseudo.canonical()),
             None => Some(&self.element_map),
         }
     }
 
+    fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
+        self.pseudos_map.get(pseudo).is_some()
+    }
+}
+
+impl PerOriginClear for CascadeData {
     fn clear(&mut self) {
         self.element_map = SelectorMap::new();
         self.pseudos_map = Default::default();
         self.animations = Default::default();
         self.invalidation_map.clear();
         self.attribute_dependencies.clear();
         self.style_attribute_dependency = false;
         self.state_dependencies = ElementState::empty();
         self.mapped_ids.clear();
         self.selectors_for_cache_revalidation = SelectorMap::new();
         self.num_selectors = 0;
         self.num_declarations = 0;
     }
+}
 
-    fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
-        self.pseudos_map.get(pseudo).is_some()
+impl Default for CascadeData {
+    fn default() -> Self {
+        CascadeData::new()
     }
 }
 
 /// A rule, that wraps a style rule, but represents a single selector of the
 /// rule.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Debug)]
 pub struct Rule {