From c5f2142d8fd619ec979e7a963637b1f439499d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 2 Jan 2017 01:10:16 +0100 Subject: [PATCH] style: Document the stylist module. --- components/layout_thread/lib.rs | 13 ++-- components/style/stylist.rs | 125 ++++++++++++++++++++++++++------ 2 files changed, 108 insertions(+), 30 deletions(-) diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index f9f4c371a94..78a1295c1b5 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -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()) { diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 5bfcba258fe..587855115b2 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -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>, + /// 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], 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>, @@ -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(&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]) { 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 { - &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( &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 { &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(&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(&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(&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(&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>, + /// A hash from a class name to rules which contain that class selector. pub class_hash: FnvHashMap>, + /// A hash from local name to rules which contain that local name selector. pub local_name_hash: FnvHashMap>, /// Same as local_name_hash, but keys are lower-cased. /// For HTML elements in HTML documents. @@ -709,6 +767,7 @@ fn sort_by_key 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>, + /// The actual style rule. #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")] pub style_rule: Arc>, + /// 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>, 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,