Record effective @font-face rules when updating stylist. r=Manishearth
draft
Record effective @font-face rules when updating stylist. r=Manishearth
MozReview-Commit-ID: Do979S1331D
--- 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