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);
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 = match constraints {
Some(ref constraints) => {
self.viewport_size =
rw_data.stylist.viewport_constraints().map_or(current_screen_size, |constraints| {
debug!("Viewport constraints: {:?}", constraints);
// other rules are evaluated against the actual viewport
Size2D::new(Au::from_f32_px(constraints.size.width),
Au::from_f32_px(constraints.size.height))
}
None => current_screen_size,
};
});
// Handle conditions where the entire flow tree is invalid.
let mut needs_dirtying = false;
let viewport_size_changed = self.viewport_size != old_viewport_size;
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
rw_data.constellation_chan
.send(ConstellationMsg::ViewportConstrained(self.id, constraints))
.send(ConstellationMsg::ViewportConstrained(self.id, constraints.clone()))
.unwrap();
}
if data.document_stylesheets.iter().any(|sheet| sheet.dirty_on_viewport_size_change()) {

View file

@ -4,6 +4,8 @@
//! Selector matching.
#![deny(missing_docs)]
use {Atom, LocalName};
use data::ComputedStyle;
use dom::PresentationalHintsSynthetizer;
@ -28,7 +30,6 @@ use smallvec::VecLike;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::fmt;
use std::hash::BuildHasherDefault;
use std::hash::Hash;
use std::slice;
use std::sync::Arc;
@ -83,6 +84,8 @@ pub struct Stylist {
/// FIXME(emilio): Use the rule tree!
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,
/// Selector dependencies used to compute restyle hints.
@ -99,6 +102,7 @@ pub struct Stylist {
}
impl Stylist {
/// Construct a new `Stylist`, using a given `Device`.
#[inline]
pub fn new(device: Device) -> Self {
let mut stylist = Stylist {
@ -129,6 +133,12 @@ impl 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,
doc_stylesheets: &[Arc<Stylesheet>],
ua_stylesheets: Option<&UserAgentStylesheets>,
@ -258,9 +268,10 @@ impl Stylist {
/// 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 flows.
/// 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
/// flows.
pub fn precomputed_values_for_pseudo(&self,
pseudo: &PseudoElement,
parent: Option<&Arc<ComputedValues>>,
@ -272,7 +283,7 @@ impl Stylist {
// use into_iter.
let rule_node =
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();
if inherit_all {
@ -320,6 +331,13 @@ impl Stylist {
.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,
element: &E,
pseudo: &PseudoElement,
@ -344,7 +362,8 @@ impl Stylist {
MatchingReason::ForStyling);
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 =
properties::cascade(self.device.au_viewport_size(),
@ -357,6 +376,11 @@ impl Stylist {
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>]) {
let cascaded_rule = ViewportRule {
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);
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);
}
@ -389,21 +416,29 @@ impl Stylist {
self.device = Arc::new(device);
}
pub fn viewport_constraints(&self) -> &Option<ViewportConstraints> {
&self.viewport_constraints
/// Returns the viewport constraints that apply to this document because of
/// 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) {
// 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;
}
/// Returns the applicable CSS declarations for the given element.
///
/// This corresponds to `ElementRuleCollector` in WebKit.
///
/// The returned boolean indicates whether the style is *shareable*;
/// that is, whether the matched selectors are simple enough to allow the
/// matching logic to be reduced to the logic in
/// `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
/// The returned `StyleRelations` indicate hints about which kind of rules
/// have matched.
#[allow(unsafe_code)]
pub fn push_applicable_declarations<E, V>(
&self,
@ -530,20 +565,28 @@ impl Stylist {
relations
}
/// 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 map of registered `@keyframes` animations.
#[inline]
pub fn animations(&self) -> &FnvHashMap<Atom, KeyframesAnimation> {
&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,
element: &E,
candidate: &E) -> bool
where E: ElementExt
where E: ElementExt,
{
use selectors::matching::StyleRelations;
use selectors::matching::matches_complex_selector;
@ -572,15 +615,19 @@ impl Stylist {
true
}
/// Returns the rule root node.
#[inline]
pub fn rule_tree_root(&self) -> StrongRuleNode {
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,
element: &E,
candidate: &E) -> bool
where E: ElementExt
where E: ElementExt,
{
use selectors::matching::StyleRelations;
use selectors::matching::matches_complex_selector;
@ -610,7 +657,11 @@ impl Stylist {
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,
// NB: We need to pass current_state as an argument because
// selectors::Element doesn't provide access to ElementState
@ -618,7 +669,7 @@ impl Stylist {
// more expensive than getting it directly from the caller.
current_state: ElementState)
-> RestyleHint
where E: ElementExt + Clone
where E: ElementExt + Clone,
{
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
// leak them, since there will be no way to call gc() on the rule tree
// 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(); }
}
}
@ -688,11 +742,15 @@ impl PerPseudoElementSelectorMap {
/// 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.
///
/// TODO: Tune the initial capacity of the HashMap
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
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>>,
/// A hash from a class name to rules which contain that class selector.
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>>,
/// Same as local_name_hash, but keys are lower-cased.
/// 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 {
/// Trivially constructs an empty `SelectorMap`.
pub fn new() -> Self {
SelectorMap {
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))]
#[derive(Clone, Debug)]
pub struct Rule {
// This is an Arc because Rule will essentially be cloned for every element
// that it matches. Selector contains an owned vector (through
// ComplexSelector) and we want to avoid the allocation.
/// The selector this struct represents.
/// This is an Arc because Rule will essentially be cloned for every element
/// 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")]
pub selector: Arc<ComplexSelector<SelectorImpl>>,
/// The actual style rule.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub style_rule: Arc<RwLock<StyleRule>>,
/// The source order this style rule appears in.
pub source_order: usize,
/// The specificity of the rule this selector represents.
pub specificity: u32,
}
@ -966,19 +1036,28 @@ impl Rule {
}
}
/// A property declaration together with its precedence among rules of equal specificity so that
/// we can sort them.
/// A property declaration together with its precedence among rules of equal
/// 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))]
#[derive(Debug, Clone)]
pub struct ApplicableDeclarationBlock {
/// The style source, either a style rule, or a property declaration block.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub source: StyleSource,
/// The importance of this declaration block.
pub importance: Importance,
/// The source order of this block.
pub source_order: usize,
/// The specificity of the selector this block is represented by.
pub specificity: u32,
}
impl ApplicableDeclarationBlock {
/// Constructs an applicable declaration block from a given property
/// declaration block and importance.
#[inline]
pub fn from_declarations(declarations: Arc<RwLock<PropertyDeclarationBlock>>,
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> {
iter: slice::Iter<'a, (PropertyDeclaration, Importance)>,
importance: Importance,