style: Document the stylist module.

This commit is contained in:
Emilio Cobos Álvarez 2017-01-02 01:10:16 +01:00
parent 082866aba6
commit c5f2142d8f
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
2 changed files with 108 additions and 30 deletions

View file

@ -1054,27 +1054,24 @@ impl LayoutThread {
let device = Device::new(MediaType::Screen, initial_viewport); let device = Device::new(MediaType::Screen, initial_viewport);
Arc::get_mut(&mut rw_data.stylist).unwrap().set_device(device, &data.document_stylesheets); Arc::get_mut(&mut rw_data.stylist).unwrap().set_device(device, &data.document_stylesheets);
let constraints = rw_data.stylist.viewport_constraints().clone(); self.viewport_size =
self.viewport_size = match constraints { rw_data.stylist.viewport_constraints().map_or(current_screen_size, |constraints| {
Some(ref constraints) => {
debug!("Viewport constraints: {:?}", constraints); debug!("Viewport constraints: {:?}", constraints);
// other rules are evaluated against the actual viewport // other rules are evaluated against the actual viewport
Size2D::new(Au::from_f32_px(constraints.size.width), Size2D::new(Au::from_f32_px(constraints.size.width),
Au::from_f32_px(constraints.size.height)) Au::from_f32_px(constraints.size.height))
} });
None => current_screen_size,
};
// Handle conditions where the entire flow tree is invalid. // Handle conditions where the entire flow tree is invalid.
let mut needs_dirtying = false; let mut needs_dirtying = false;
let viewport_size_changed = self.viewport_size != old_viewport_size; let viewport_size_changed = self.viewport_size != old_viewport_size;
if viewport_size_changed { if viewport_size_changed {
if let Some(constraints) = constraints { if let Some(constraints) = rw_data.stylist.viewport_constraints() {
// let the constellation know about the viewport constraints // let the constellation know about the viewport constraints
rw_data.constellation_chan rw_data.constellation_chan
.send(ConstellationMsg::ViewportConstrained(self.id, constraints)) .send(ConstellationMsg::ViewportConstrained(self.id, constraints.clone()))
.unwrap(); .unwrap();
} }
if data.document_stylesheets.iter().any(|sheet| sheet.dirty_on_viewport_size_change()) { if data.document_stylesheets.iter().any(|sheet| sheet.dirty_on_viewport_size_change()) {

View file

@ -4,6 +4,8 @@
//! Selector matching. //! Selector matching.
#![deny(missing_docs)]
use {Atom, LocalName}; use {Atom, LocalName};
use data::ComputedStyle; use data::ComputedStyle;
use dom::PresentationalHintsSynthetizer; use dom::PresentationalHintsSynthetizer;
@ -28,7 +30,6 @@ use smallvec::VecLike;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::hash::BuildHasherDefault;
use std::hash::Hash; use std::hash::Hash;
use std::slice; use std::slice;
use std::sync::Arc; use std::sync::Arc;
@ -83,6 +84,8 @@ pub struct Stylist {
/// FIXME(emilio): Use the rule tree! /// FIXME(emilio): Use the rule tree!
precomputed_pseudo_element_decls: FnvHashMap<PseudoElement, Vec<ApplicableDeclarationBlock>>, precomputed_pseudo_element_decls: FnvHashMap<PseudoElement, Vec<ApplicableDeclarationBlock>>,
/// 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: usize, rules_source_order: usize,
/// Selector dependencies used to compute restyle hints. /// Selector dependencies used to compute restyle hints.
@ -99,6 +102,7 @@ pub struct Stylist {
} }
impl Stylist { impl Stylist {
/// Construct a new `Stylist`, using a given `Device`.
#[inline] #[inline]
pub fn new(device: Device) -> Self { pub fn new(device: Device) -> Self {
let mut stylist = Stylist { let mut stylist = Stylist {
@ -129,6 +133,12 @@ impl Stylist {
stylist 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, pub fn update(&mut self,
doc_stylesheets: &[Arc<Stylesheet>], doc_stylesheets: &[Arc<Stylesheet>],
ua_stylesheets: Option<&UserAgentStylesheets>, ua_stylesheets: Option<&UserAgentStylesheets>,
@ -258,9 +268,10 @@ impl Stylist {
/// Computes the style for a given "precomputed" pseudo-element, taking the /// Computes the style for a given "precomputed" pseudo-element, taking the
/// universal rules and applying them. /// universal rules and applying them.
/// ///
/// If `inherit_all` is true, then all properties are inherited from the parent; otherwise, /// If `inherit_all` is true, then all properties are inherited from the
/// non-inherited properties are reset to their initial values. The flow constructor uses this /// parent; otherwise, non-inherited properties are reset to their initial
/// flag when constructing anonymous flows. /// values. The flow constructor uses this flag when constructing anonymous
/// flows.
pub fn precomputed_values_for_pseudo(&self, pub fn precomputed_values_for_pseudo(&self,
pseudo: &PseudoElement, pseudo: &PseudoElement,
parent: Option<&Arc<ComputedValues>>, parent: Option<&Arc<ComputedValues>>,
@ -272,7 +283,7 @@ impl Stylist {
// use into_iter. // use into_iter.
let rule_node = let rule_node =
self.rule_tree.insert_ordered_rules( self.rule_tree.insert_ordered_rules(
declarations.iter().map(|a| (a.source.clone(), a.importance))); declarations.into_iter().map(|a| (a.source.clone(), a.importance)));
let mut flags = CascadeFlags::empty(); let mut flags = CascadeFlags::empty();
if inherit_all { if inherit_all {
@ -320,6 +331,13 @@ impl Stylist {
.values .values
} }
/// Computes a pseudo-element style lazily during layout.
///
/// This can only be done for a certain set of pseudo-elements, like
/// :selection.
///
/// Check the documentation on lazy pseudo-elements in
/// docs/components/style.md
pub fn lazily_compute_pseudo_element_style<E>(&self, pub fn lazily_compute_pseudo_element_style<E>(&self,
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
@ -344,7 +362,8 @@ impl Stylist {
MatchingReason::ForStyling); MatchingReason::ForStyling);
let rule_node = let rule_node =
self.rule_tree.insert_ordered_rules(declarations.into_iter().map(|a| (a.source.clone(), a.importance))); self.rule_tree.insert_ordered_rules(
declarations.into_iter().map(|a| (a.source, a.importance)));
let computed = let computed =
properties::cascade(self.device.au_viewport_size(), properties::cascade(self.device.au_viewport_size(),
@ -357,6 +376,11 @@ impl Stylist {
Some(ComputedStyle::new(rule_node, Arc::new(computed))) Some(ComputedStyle::new(rule_node, Arc::new(computed)))
} }
/// Set a given device, which may change the styles that apply to the
/// document.
///
/// This means that we may need to rebuild style data even if the
/// stylesheets haven't changed.
pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc<Stylesheet>]) { pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc<Stylesheet>]) {
let cascaded_rule = ViewportRule { let cascaded_rule = ViewportRule {
declarations: viewport::Cascade::from_stylesheets(stylesheets, &device).finish(), declarations: viewport::Cascade::from_stylesheets(stylesheets, &device).finish(),
@ -364,6 +388,9 @@ impl Stylist {
self.viewport_constraints = ViewportConstraints::maybe_new(device.viewport_size, &cascaded_rule); self.viewport_constraints = ViewportConstraints::maybe_new(device.viewport_size, &cascaded_rule);
if let Some(ref constraints) = self.viewport_constraints { if let Some(ref constraints) = self.viewport_constraints {
// FIXME(emilio): creating a device here works, but is not really
// appropriate. I should get rid of this while doing the stylo media
// query work.
device = Device::new(MediaType::Screen, constraints.size); device = Device::new(MediaType::Screen, constraints.size);
} }
@ -389,21 +416,29 @@ impl Stylist {
self.device = Arc::new(device); self.device = Arc::new(device);
} }
pub fn viewport_constraints(&self) -> &Option<ViewportConstraints> { /// Returns the viewport constraints that apply to this document because of
&self.viewport_constraints /// a @viewport rule.
pub fn viewport_constraints(&self) -> Option<&ViewportConstraints> {
self.viewport_constraints.as_ref()
} }
/// Sets the quirks mode of the document.
pub fn set_quirks_mode(&mut self, enabled: bool) { pub fn set_quirks_mode(&mut self, enabled: bool) {
// FIXME(emilio): We don't seem to change the quirks mode dynamically
// during multiple layout passes, but this is totally bogus, in the
// sense that it's updated asynchronously.
//
// This should probably be an argument to `update`, and use the quirks
// mode info in the `SharedLayoutContext`.
self.quirks_mode = enabled; self.quirks_mode = enabled;
} }
/// Returns the applicable CSS declarations for the given element. /// Returns the applicable CSS declarations for the given element.
///
/// This corresponds to `ElementRuleCollector` in WebKit. /// This corresponds to `ElementRuleCollector` in WebKit.
/// ///
/// The returned boolean indicates whether the style is *shareable*; /// The returned `StyleRelations` indicate hints about which kind of rules
/// that is, whether the matched selectors are simple enough to allow the /// have matched.
/// matching logic to be reduced to the logic in
/// `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn push_applicable_declarations<E, V>( pub fn push_applicable_declarations<E, V>(
&self, &self,
@ -530,20 +565,28 @@ impl Stylist {
relations relations
} }
/// Return whether the device is dirty, that is, whether the screen size or
/// media type have changed (for now).
#[inline] #[inline]
pub fn is_device_dirty(&self) -> bool { pub fn is_device_dirty(&self) -> bool {
self.is_device_dirty self.is_device_dirty
} }
/// Returns the map of registered `@keyframes` animations.
#[inline] #[inline]
pub fn animations(&self) -> &FnvHashMap<Atom, KeyframesAnimation> { pub fn animations(&self) -> &FnvHashMap<Atom, KeyframesAnimation> {
&self.animations &self.animations
} }
/// Whether two elements match the same not-common style-affecting attribute
/// rules.
///
/// This is used to test elements and candidates in the style-sharing
/// candidate cache.
pub fn match_same_not_common_style_affecting_attributes_rules<E>(&self, pub fn match_same_not_common_style_affecting_attributes_rules<E>(&self,
element: &E, element: &E,
candidate: &E) -> bool candidate: &E) -> bool
where E: ElementExt where E: ElementExt,
{ {
use selectors::matching::StyleRelations; use selectors::matching::StyleRelations;
use selectors::matching::matches_complex_selector; use selectors::matching::matches_complex_selector;
@ -572,15 +615,19 @@ impl Stylist {
true true
} }
/// Returns the rule root node.
#[inline] #[inline]
pub fn rule_tree_root(&self) -> StrongRuleNode { pub fn rule_tree_root(&self) -> StrongRuleNode {
self.rule_tree.root() self.rule_tree.root()
} }
/// Returns whether two elements match the same sibling-affecting rules.
///
/// This is also for the style sharing candidate cache.
pub fn match_same_sibling_affecting_rules<E>(&self, pub fn match_same_sibling_affecting_rules<E>(&self,
element: &E, element: &E,
candidate: &E) -> bool candidate: &E) -> bool
where E: ElementExt where E: ElementExt,
{ {
use selectors::matching::StyleRelations; use selectors::matching::StyleRelations;
use selectors::matching::matches_complex_selector; use selectors::matching::matches_complex_selector;
@ -610,7 +657,11 @@ impl Stylist {
true true
} }
pub fn compute_restyle_hint<E>(&self, element: &E, /// Given an element, and a snapshot that represents a previous state of the
/// element, compute the appropriate restyle hint, that is, the kind of
/// restyle we need to do.
pub fn compute_restyle_hint<E>(&self,
element: &E,
snapshot: &Snapshot, snapshot: &Snapshot,
// NB: We need to pass current_state as an argument because // NB: We need to pass current_state as an argument because
// selectors::Element doesn't provide access to ElementState // selectors::Element doesn't provide access to ElementState
@ -618,7 +669,7 @@ impl Stylist {
// more expensive than getting it directly from the caller. // more expensive than getting it directly from the caller.
current_state: ElementState) current_state: ElementState)
-> RestyleHint -> RestyleHint
where E: ElementExt + Clone where E: ElementExt + Clone,
{ {
self.state_deps.compute_hint(element, snapshot, current_state) self.state_deps.compute_hint(element, snapshot, current_state)
} }
@ -632,6 +683,9 @@ impl Drop for Stylist {
// dropped all strong rule node references before now, then we will // dropped all strong rule node references before now, then we will
// leak them, since there will be no way to call gc() on the rule tree // leak them, since there will be no way to call gc() on the rule tree
// after this point. // after this point.
//
// TODO(emilio): We can at least assert all the elements in the free
// list are indeed free.
unsafe { self.rule_tree.gc(); } unsafe { self.rule_tree.gc(); }
} }
} }
@ -688,11 +742,15 @@ impl PerPseudoElementSelectorMap {
/// Hence, the union of the rules keyed on each of element's classes, ID, /// Hence, the union of the rules keyed on each of element's classes, ID,
/// element name, etc. will contain the Rules that actually match that /// element name, etc. will contain the Rules that actually match that
/// element. /// element.
///
/// TODO: Tune the initial capacity of the HashMap
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SelectorMap { pub struct SelectorMap {
// TODO: Tune the initial capacity of the HashMap /// A hash from an ID to rules which contain that ID selector.
pub id_hash: FnvHashMap<Atom, Vec<Rule>>, pub id_hash: FnvHashMap<Atom, Vec<Rule>>,
/// A hash from a class name to rules which contain that class selector.
pub class_hash: FnvHashMap<Atom, Vec<Rule>>, pub class_hash: FnvHashMap<Atom, Vec<Rule>>,
/// A hash from local name to rules which contain that local name selector.
pub local_name_hash: FnvHashMap<LocalName, Vec<Rule>>, pub local_name_hash: FnvHashMap<LocalName, Vec<Rule>>,
/// Same as local_name_hash, but keys are lower-cased. /// Same as local_name_hash, but keys are lower-cased.
/// For HTML elements in HTML documents. /// For HTML elements in HTML documents.
@ -709,6 +767,7 @@ fn sort_by_key<T, F: Fn(&T) -> K, K: Ord>(v: &mut [T], f: F) {
} }
impl SelectorMap { impl SelectorMap {
/// Trivially constructs an empty `SelectorMap`.
pub fn new() -> Self { pub fn new() -> Self {
SelectorMap { SelectorMap {
id_hash: HashMap::default(), id_hash: HashMap::default(),
@ -941,17 +1000,28 @@ impl SelectorMap {
} }
} }
/// A rule, that wraps a style rule, but represents a single selector of the
/// rule.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Rule { pub struct Rule {
// This is an Arc because Rule will essentially be cloned for every element /// The selector this struct represents.
// that it matches. Selector contains an owned vector (through /// This is an Arc because Rule will essentially be cloned for every element
// ComplexSelector) and we want to avoid the allocation. /// that it matches. Selector contains an owned vector (through
/// ComplexSelector) and we want to avoid the allocation.
///
/// FIXME(emilio): We should be able to get rid of it and just use the style
/// rule? This predates the time where the rule was in `selectors`, and the
/// style rule was a generic parameter to it. It's not trivial though, due
/// to the specificity.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub selector: Arc<ComplexSelector<SelectorImpl>>, pub selector: Arc<ComplexSelector<SelectorImpl>>,
/// The actual style rule.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub style_rule: Arc<RwLock<StyleRule>>, pub style_rule: Arc<RwLock<StyleRule>>,
/// The source order this style rule appears in.
pub source_order: usize, pub source_order: usize,
/// The specificity of the rule this selector represents.
pub specificity: u32, pub specificity: u32,
} }
@ -966,19 +1036,28 @@ impl Rule {
} }
} }
/// A property declaration together with its precedence among rules of equal specificity so that /// A property declaration together with its precedence among rules of equal
/// we can sort them. /// specificity so that we can sort them.
///
/// This represents the declarations in a given declaration block for a given
/// importance.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ApplicableDeclarationBlock { pub struct ApplicableDeclarationBlock {
/// The style source, either a style rule, or a property declaration block.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub source: StyleSource, pub source: StyleSource,
/// The importance of this declaration block.
pub importance: Importance, pub importance: Importance,
/// The source order of this block.
pub source_order: usize, pub source_order: usize,
/// The specificity of the selector this block is represented by.
pub specificity: u32, pub specificity: u32,
} }
impl ApplicableDeclarationBlock { impl ApplicableDeclarationBlock {
/// Constructs an applicable declaration block from a given property
/// declaration block and importance.
#[inline] #[inline]
pub fn from_declarations(declarations: Arc<RwLock<PropertyDeclarationBlock>>, pub fn from_declarations(declarations: Arc<RwLock<PropertyDeclarationBlock>>,
importance: Importance) importance: Importance)
@ -992,6 +1071,8 @@ impl ApplicableDeclarationBlock {
} }
} }
/// An iterator over the declarations that a given block represent, which is
/// effectively a filter by importance.
pub struct ApplicableDeclarationBlockIter<'a> { pub struct ApplicableDeclarationBlockIter<'a> {
iter: slice::Iter<'a, (PropertyDeclaration, Importance)>, iter: slice::Iter<'a, (PropertyDeclaration, Importance)>,
importance: Importance, importance: Importance,