Record effective @font-face rules when updating stylist. r=Manishearth draft
authorXidorn Quan <me@upsuper.org>
Mon, 27 Mar 2017 20:55:08 +1100
changeset 552765 27462deeffb8f9356ec98896c9b468650baac120
parent 552764 7a88bca00684e7b5be0903de7773691d0cb2c25e
child 552766 65e1c9752abc2140bc2094244bd9357d00bb7929
push id51449
push userxquan@mozilla.com
push dateTue, 28 Mar 2017 23:45:05 +0000
reviewersManishearth
milestone55.0a1
Record effective @font-face rules when updating stylist. r=Manishearth MozReview-Commit-ID: Do979S1331D
servo/components/layout_thread/lib.rs
servo/components/style/gecko/data.rs
servo/components/style/stylist.rs
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -94,16 +94,17 @@ use selectors::Element;
 use servo_config::opts;
 use servo_config::prefs::PREFS;
 use servo_config::resource_files::read_resource_file;
 use servo_geometry::max_rect;
 use servo_url::ServoUrl;
 use std::borrow::ToOwned;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
+use std::marker::PhantomData;
 use std::mem as std_mem;
 use std::ops::{Deref, DerefMut};
 use std::process;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::mpsc::{Receiver, Sender, channel};
 use std::thread;
 use style::animation::Animation;
@@ -112,17 +113,17 @@ use style::data::StoredRestyleHint;
 use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
 use style::error_reporting::StdoutErrorReporter;
 use style::logical_geometry::LogicalPoint;
 use style::media_queries::{Device, MediaType};
 use style::parser::ParserContextExtraData;
 use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
 use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
 use style::stylesheets::{Origin, Stylesheet, UserAgentStylesheets};
-use style::stylist::Stylist;
+use style::stylist::{ExtraStyleData, Stylist};
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::{DomTraversal, TraversalDriver, TraversalFlags};
 
 /// Information needed by the layout thread.
 pub struct LayoutThread {
     /// The ID of the pipeline that we belong to.
     id: PipelineId,
@@ -1074,21 +1075,25 @@ impl LayoutThread {
 
         // If the entire flow tree is invalid, then it will be reflowed anyhow.
         let ua_stylesheets = &*UA_STYLESHEETS;
         let ua_or_user_guard = ua_stylesheets.shared_lock.read();
         let guards = StylesheetGuards {
             author: &author_guard,
             ua_or_user: &ua_or_user_guard,
         };
+        let mut extra_data = ExtraStyleData {
+            marker: PhantomData,
+        };
         let needs_dirtying = Arc::get_mut(&mut rw_data.stylist).unwrap().update(
             &data.document_stylesheets,
             &guards,
             Some(ua_stylesheets),
-            data.stylesheets_changed);
+            data.stylesheets_changed,
+            &mut extra_data);
         let needs_reflow = viewport_size_changed && !needs_dirtying;
         if needs_dirtying {
             if let Some(mut d) = element.mutate_data() {
                 if d.has_styles() {
                     d.ensure_restyle().hint.insert(&StoredRestyleHint::subtree());
                 }
             }
         }
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -8,22 +8,22 @@ use animation::Animation;
 use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
 use dom::OpaqueNode;
 use gecko_bindings::bindings::RawServoStyleSet;
 use gecko_bindings::structs::RawGeckoPresContextOwned;
 use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
 use media_queries::Device;
 use parking_lot::RwLock;
 use properties::ComputedValues;
-use shared_lock::{StylesheetGuards, SharedRwLockReadGuard};
+use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
 use std::collections::HashMap;
 use std::sync::Arc;
 use std::sync::mpsc::{Receiver, Sender, channel};
-use stylesheets::Stylesheet;
-use stylist::Stylist;
+use stylesheets::{FontFaceRule, Origin, Stylesheet};
+use stylist::{ExtraStyleData, Stylist};
 
 /// The container for data that a Servo-backed Gecko document needs to style
 /// itself.
 pub struct PerDocumentStyleDataImpl {
     /// Rule processor.
     pub stylist: Arc<Stylist>,
 
     /// List of stylesheets, mirrored from Gecko.
@@ -40,16 +40,19 @@ pub struct PerDocumentStyleDataImpl {
     /// animations properly.
     pub new_animations_receiver: Receiver<Animation>,
     /// Unused. Will go away when we actually implement transitions and
     /// animations properly.
     pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
     /// Unused. Will go away when we actually implement transitions and
     /// animations properly.
     pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
+
+    /// List of effective font face rules.
+    pub font_faces: Vec<(Arc<Locked<FontFaceRule>>, Origin)>,
 }
 
 /// 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`.
@@ -61,16 +64,17 @@ impl PerDocumentStyleData {
         PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
             stylist: Arc::new(Stylist::new(device)),
             stylesheets: vec![],
             stylesheets_changed: true,
             new_animations_sender: new_anims_sender,
             new_animations_receiver: new_anims_receiver,
             running_animations: Arc::new(RwLock::new(HashMap::new())),
             expired_animations: Arc::new(RwLock::new(HashMap::new())),
+            font_faces: vec![],
         }))
     }
 
     /// Get an immutable reference to this style data.
     pub fn borrow(&self) -> AtomicRef<PerDocumentStyleDataImpl> {
         self.0.borrow()
     }
 
@@ -92,17 +96,21 @@ impl PerDocumentStyleDataImpl {
         self.stylesheets_changed = true;
         self.flush_stylesheets(guard);
     }
 
     /// Recreate the style data if the stylesheets have changed.
     pub fn flush_stylesheets(&mut self, guard: &SharedRwLockReadGuard) {
         if self.stylesheets_changed {
             let mut stylist = Arc::get_mut(&mut self.stylist).unwrap();
-            stylist.update(&self.stylesheets, &StylesheetGuards::same(guard), None, true);
+            let mut extra_data = ExtraStyleData {
+                font_faces: &mut self.font_faces,
+            };
+            stylist.update(&self.stylesheets, &StylesheetGuards::same(guard),
+                           None, true, &mut extra_data);
             self.stylesheets_changed = false;
         }
     }
 
     /// Get the default computed values for this document.
     pub fn default_computed_values(&self) -> &Arc<ComputedValues> {
         self.stylist.device.default_computed_values_arc()
     }
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -29,19 +29,21 @@ use selectors::parser::{Selector, Simple
 use selectors::parser::SelectorMethods;
 use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
 use sink::Push;
 use smallvec::VecLike;
 use std::borrow::Borrow;
 use std::collections::HashMap;
 use std::fmt;
 use std::hash::Hash;
+#[cfg(feature = "servo")]
+use std::marker::PhantomData;
 use std::sync::Arc;
 use style_traits::viewport::ViewportConstraints;
-use stylesheets::{CssRule, Origin, StyleRule, Stylesheet, UserAgentStylesheets};
+use stylesheets::{CssRule, FontFaceRule, Origin, StyleRule, Stylesheet, UserAgentStylesheets};
 use thread_state;
 use viewport::{self, MaybeNew, ViewportRule};
 
 pub use ::fnv::FnvHashMap;
 
 /// This structure holds all the selectors and device characteristics
 /// for a given document. The selectors are converted into `Rule`s
 /// (defined in rust-selectors), and introduced in a `SelectorMap`
@@ -113,16 +115,47 @@ pub struct Stylist {
     sibling_affecting_selectors: Vec<Selector<SelectorImpl>>,
 
     /// Selectors in the page matching elements with non-common style-affecting
     /// attributes.
     #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
     non_common_style_affecting_attributes_selectors: Vec<Selector<SelectorImpl>>,
 }
 
+/// This struct holds data which user of Stylist may want to extract
+/// from stylesheets which can be done at the same time as updating.
+pub struct ExtraStyleData<'a> {
+    /// A list of effective font-face rules and their origin.
+    #[cfg(feature = "gecko")]
+    pub font_faces: &'a mut Vec<(Arc<Locked<FontFaceRule>>, Origin)>,
+
+    #[allow(missing_docs)]
+    #[cfg(feature = "servo")]
+    pub marker: PhantomData<&'a usize>,
+}
+
+#[cfg(feature = "gecko")]
+impl<'a> ExtraStyleData<'a> {
+    /// Clear the internal @font-face rule list.
+    fn clear_font_faces(&mut self) {
+        self.font_faces.clear();
+    }
+
+    /// Add the given @font-face rule.
+    fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>, origin: Origin) {
+        self.font_faces.push((rule.clone(), origin));
+    }
+}
+
+#[cfg(feature = "servo")]
+impl<'a> ExtraStyleData<'a> {
+    fn clear_font_faces(&mut self) {}
+    fn add_font_face(&mut self, _: &Arc<Locked<FontFaceRule>>, _: Origin) {}
+}
+
 impl Stylist {
     /// Construct a new `Stylist`, using a given `Device`.
     #[inline]
     pub fn new(device: Device) -> Self {
         let mut stylist = Stylist {
             viewport_constraints: None,
             device: Arc::new(device),
             is_device_dirty: true,
@@ -151,21 +184,22 @@ impl Stylist {
     }
 
     /// Update the stylist for the given document stylesheets, and optionally
     /// with a set of user agent stylesheets.
     ///
     /// This method resets all the style data each time the stylesheets change
     /// (which is indicated by the `stylesheets_changed` parameter), or the
     /// device is dirty, which means we need to re-evaluate media queries.
-    pub fn update(&mut self,
-                  doc_stylesheets: &[Arc<Stylesheet>],
-                  guards: &StylesheetGuards,
-                  ua_stylesheets: Option<&UserAgentStylesheets>,
-                  stylesheets_changed: bool) -> bool {
+    pub fn update<'a>(&mut self,
+                      doc_stylesheets: &[Arc<Stylesheet>],
+                      guards: &StylesheetGuards,
+                      ua_stylesheets: Option<&UserAgentStylesheets>,
+                      stylesheets_changed: bool,
+                      extra_data: &mut ExtraStyleData<'a>) -> bool {
         if !(self.is_device_dirty || stylesheets_changed) {
             return false;
         }
 
         let cascaded_rule = ViewportRule {
             declarations: viewport::Cascade::from_stylesheets(
                 doc_stylesheets, guards.author, &self.device
             ).finish(),
@@ -189,28 +223,31 @@ impl Stylist {
         self.precomputed_pseudo_element_decls = Default::default();
         self.rules_source_order = 0;
         self.state_deps.clear();
         self.animations.clear();
 
         self.sibling_affecting_selectors.clear();
         self.non_common_style_affecting_attributes_selectors.clear();
 
+        extra_data.clear_font_faces();
+
         if let Some(ua_stylesheets) = ua_stylesheets {
             for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
-                self.add_stylesheet(&stylesheet, guards.ua_or_user);
+                self.add_stylesheet(&stylesheet, guards.ua_or_user, extra_data);
             }
 
             if self.quirks_mode {
-                self.add_stylesheet(&ua_stylesheets.quirks_mode_stylesheet, guards.ua_or_user);
+                self.add_stylesheet(&ua_stylesheets.quirks_mode_stylesheet,
+                                    guards.ua_or_user, extra_data);
             }
         }
 
         for ref stylesheet in doc_stylesheets.iter() {
-            self.add_stylesheet(stylesheet, guards.author);
+            self.add_stylesheet(stylesheet, guards.author, extra_data);
         }
 
         debug!("Stylist stats:");
         debug!(" - Got {} sibling-affecting selectors",
                self.sibling_affecting_selectors.len());
         debug!(" - Got {} non-common-style-attribute-affecting selectors",
                self.non_common_style_affecting_attributes_selectors.len());
         debug!(" - Got {} deps for style-hint calculation",
@@ -225,17 +262,18 @@ impl Stylist {
                 self.precomputed_pseudo_element_decls.insert(pseudo, declarations);
             }
         });
 
         self.is_device_dirty = false;
         true
     }
 
-    fn add_stylesheet(&mut self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard) {
+    fn add_stylesheet<'a>(&mut self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard,
+                          extra_data: &mut ExtraStyleData<'a>) {
         if stylesheet.disabled() || !stylesheet.is_effective_for_device(&self.device, guard) {
             return;
         }
 
         // Cheap `Arc` clone so that the closure below can borrow `&mut Stylist`.
         let device = self.device.clone();
 
         stylesheet.effective_rules(&device, guard, |rule| {
@@ -269,26 +307,29 @@ impl Stylist {
 
                         if selector.matches_non_common_style_affecting_attribute() {
                             self.non_common_style_affecting_attributes_selectors.push(selector.clone());
                         }
                     }
                 }
                 CssRule::Import(ref import) => {
                     let import = import.read_with(guard);
-                    self.add_stylesheet(&import.stylesheet, guard)
+                    self.add_stylesheet(&import.stylesheet, guard, extra_data)
                 }
                 CssRule::Keyframes(ref keyframes_rule) => {
                     let keyframes_rule = keyframes_rule.read_with(guard);
                     debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
                     let animation = KeyframesAnimation::from_keyframes(
                         &keyframes_rule.keyframes, guard);
                     debug!("Found valid keyframe animation: {:?}", animation);
                     self.animations.insert(keyframes_rule.name.clone(), animation);
                 }
+                CssRule::FontFace(ref rule) => {
+                    extra_data.add_font_face(&rule, stylesheet.origin);
+                }
                 // We don't care about any other rule.
                 _ => {}
             }
         });
     }
 
 
     /// Computes the style for a given "precomputed" pseudo-element, taking the