style: Document the matching module.

This commit is contained in:
Emilio Cobos Álvarez 2016-12-31 14:48:35 +01:00
parent 30d010342a
commit 7bf80e5d50
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C

View file

@ -5,6 +5,7 @@
//! High-level interface to CSS selector matching. //! High-level interface to CSS selector matching.
#![allow(unsafe_code)] #![allow(unsafe_code)]
#![deny(missing_docs)]
use {Atom, LocalName}; use {Atom, LocalName};
use animation::{self, Animation, PropertyAnimation}; use animation::{self, Animation, PropertyAnimation};
@ -49,11 +50,22 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
flags flags
} }
/// The rule nodes for each of the pseudo-elements of an element.
///
/// TODO(emilio): Probably shouldn't be a `HashMap` by default, but a smaller
/// array.
type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode, type PseudoRuleNodes = HashMap<PseudoElement, StrongRuleNode,
BuildHasherDefault<::fnv::FnvHasher>>; BuildHasherDefault<::fnv::FnvHasher>>;
/// The results of selector matching on an element.
pub struct MatchResults { pub struct MatchResults {
/// The rule node reference that represents the rules matched by the
/// element.
pub primary: StrongRuleNode, pub primary: StrongRuleNode,
/// A set of style relations (different hints about what rules matched or
/// could have matched).
pub relations: StyleRelations, pub relations: StyleRelations,
/// The results of selector-matching the pseudo-elements.
pub per_pseudo: PseudoRuleNodes, pub per_pseudo: PseudoRuleNodes,
} }
@ -65,7 +77,11 @@ impl MatchResults {
} }
} }
/// Information regarding a candidate. /// Information regarding a style sharing candidate.
///
/// Note that this information is stored in TLS and cleared after the traversal,
/// and once here, the style information of the element is immutable, so it's
/// safe to access.
/// ///
/// TODO: We can stick a lot more info here. /// TODO: We can stick a lot more info here.
#[derive(Debug)] #[derive(Debug)]
@ -75,7 +91,7 @@ struct StyleSharingCandidate<E: TElement> {
element: SendElement<E>, element: SendElement<E>,
/// The cached common style affecting attribute info. /// The cached common style affecting attribute info.
common_style_affecting_attributes: Option<CommonStyleAffectingAttributes>, common_style_affecting_attributes: Option<CommonStyleAffectingAttributes>,
/// the cached class names. /// The cached class names.
class_attributes: Option<Vec<Atom>>, class_attributes: Option<Vec<Atom>>,
} }
@ -95,20 +111,39 @@ pub struct StyleSharingCandidateCache<E: TElement> {
cache: LRUCache<StyleSharingCandidate<E>, ()>, cache: LRUCache<StyleSharingCandidate<E>, ()>,
} }
/// A cache miss result.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum CacheMiss { pub enum CacheMiss {
/// The parents don't match.
Parent, Parent,
/// The local name of the element and the candidate don't match.
LocalName, LocalName,
/// The namespace of the element and the candidate don't match.
Namespace, Namespace,
/// One of the element or the candidate was a link, but the other one
/// wasn't.
Link, Link,
/// The element and the candidate match different kind of rules. This can
/// only happen in Gecko.
UserAndAuthorRules, UserAndAuthorRules,
/// The element and the candidate are in a different state.
State, State,
/// The element had an id attribute, which qualifies for a unique style.
IdAttr, IdAttr,
/// The element had a style attribute, which qualifies for a unique style.
StyleAttr, StyleAttr,
/// The element and the candidate class names didn't match.
Class, Class,
/// The element and the candidate common style affecting attributes didn't
/// match.
CommonStyleAffectingAttributes, CommonStyleAffectingAttributes,
/// The presentation hints didn't match.
PresHints, PresHints,
/// The element and the candidate didn't match the same set of
/// sibling-affecting rules.
SiblingRules, SiblingRules,
/// The element and the candidate didn't match the same set of non-common
/// style affecting attribute selectors.
NonCommonAttrRules, NonCommonAttrRules,
} }
@ -213,27 +248,43 @@ fn have_same_presentational_hints<E: TElement>(element: &E, candidate: &E) -> bo
} }
bitflags! { bitflags! {
/// A set of common style-affecting attributes we check separately to
/// optimize the style sharing cache.
pub flags CommonStyleAffectingAttributes: u8 { pub flags CommonStyleAffectingAttributes: u8 {
/// The `hidden` attribute.
const HIDDEN_ATTRIBUTE = 0x01, const HIDDEN_ATTRIBUTE = 0x01,
/// The `nowrap` attribute.
const NO_WRAP_ATTRIBUTE = 0x02, const NO_WRAP_ATTRIBUTE = 0x02,
/// The `align="left"` attribute.
const ALIGN_LEFT_ATTRIBUTE = 0x04, const ALIGN_LEFT_ATTRIBUTE = 0x04,
/// The `align="center"` attribute.
const ALIGN_CENTER_ATTRIBUTE = 0x08, const ALIGN_CENTER_ATTRIBUTE = 0x08,
/// The `align="right"` attribute.
const ALIGN_RIGHT_ATTRIBUTE = 0x10, const ALIGN_RIGHT_ATTRIBUTE = 0x10,
} }
} }
/// The information of how to match a given common-style affecting attribute.
pub struct CommonStyleAffectingAttributeInfo { pub struct CommonStyleAffectingAttributeInfo {
/// The attribute name.
pub attr_name: LocalName, pub attr_name: LocalName,
/// The matching mode for the attribute.
pub mode: CommonStyleAffectingAttributeMode, pub mode: CommonStyleAffectingAttributeMode,
} }
/// How should we match a given common style-affecting attribute?
#[derive(Clone)] #[derive(Clone)]
pub enum CommonStyleAffectingAttributeMode { pub enum CommonStyleAffectingAttributeMode {
/// Just for presence?
IsPresent(CommonStyleAffectingAttributes), IsPresent(CommonStyleAffectingAttributes),
/// For presence and equality with a given value.
IsEqual(Atom, CommonStyleAffectingAttributes), IsEqual(Atom, CommonStyleAffectingAttributes),
} }
// NB: This must match the order in `selectors::matching::CommonStyleAffectingAttributes`. /// The common style affecting attribute array.
///
/// TODO: This should be a `const static` or similar, but couldn't be because
/// `Atom`s have destructors.
#[inline] #[inline]
pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo; 5] { pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo; 5] {
[ [
@ -260,9 +311,14 @@ pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo
] ]
} }
/// Attributes that, if present, disable style sharing. All legacy HTML attributes must be in /// Attributes that, if present, disable style sharing. All legacy HTML
/// either this list or `common_style_affecting_attributes`. See the comment in /// attributes must be in either this list or
/// `common_style_affecting_attributes`. See the comment in
/// `synthesize_presentational_hints_for_legacy_attributes`. /// `synthesize_presentational_hints_for_legacy_attributes`.
///
/// TODO(emilio): This is not accurate now, we don't disable style sharing for
/// this now since we check for attribute selectors in the stylesheet. Consider
/// removing this.
pub fn rare_style_affecting_attributes() -> [LocalName; 4] { pub fn rare_style_affecting_attributes() -> [LocalName; 4] {
[local_name!("bgcolor"), local_name!("border"), local_name!("colspan"), local_name!("rowspan")] [local_name!("bgcolor"), local_name!("border"), local_name!("colspan"), local_name!("rowspan")]
} }
@ -301,6 +357,7 @@ fn match_same_sibling_affecting_rules<E: TElement>(element: &E,
static STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 8; static STYLE_SHARING_CANDIDATE_CACHE_SIZE: usize = 8;
impl<E: TElement> StyleSharingCandidateCache<E> { impl<E: TElement> StyleSharingCandidateCache<E> {
/// Create a new style sharing candidate cache.
pub fn new() -> Self { pub fn new() -> Self {
StyleSharingCandidateCache { StyleSharingCandidateCache {
cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE), cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE),
@ -311,6 +368,9 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
self.cache.iter_mut() self.cache.iter_mut()
} }
/// Tries to insert an element in the style sharing cache.
///
/// Fails if we know it should never be in the cache.
pub fn insert_if_possible(&mut self, pub fn insert_if_possible(&mut self,
element: &E, element: &E,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
@ -353,10 +413,12 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
}, ()); }, ());
} }
/// Touch a given index in the style sharing candidate cache.
pub fn touch(&mut self, index: usize) { pub fn touch(&mut self, index: usize) {
self.cache.touch(index); self.cache.touch(index);
} }
/// Clear the style sharing candidate cache.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.cache.evict_all() self.cache.evict_all()
} }
@ -367,16 +429,14 @@ pub enum StyleSharingResult {
/// We didn't find anybody to share the style with. /// We didn't find anybody to share the style with.
CannotShare, CannotShare,
/// The node's style can be shared. The integer specifies the index in the /// The node's style can be shared. The integer specifies the index in the
/// LRU cache that was hit and the damage that was done, and the restyle /// LRU cache that was hit and the damage that was done.
/// result the original result of the candidate's styling, that is, whether
/// it should stop the traversal or not.
StyleWasShared(usize), StyleWasShared(usize),
} }
// Callers need to pass several boolean flags to cascade_node_pseudo_element. /// Callers need to pass several boolean flags to cascade_node_pseudo_element.
// We encapsulate them in this struct to avoid mixing them up. /// We encapsulate them in this struct to avoid mixing them up.
// ///
// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps? /// FIXME(pcwalton): Unify with `CascadeFlags`, perhaps?
struct CascadeBooleans { struct CascadeBooleans {
shareable: bool, shareable: bool,
animate: bool, animate: bool,
@ -511,7 +571,9 @@ fn compute_rule_node<E: TElement>(context: &StyleContext<E>,
impl<E: TElement> PrivateMatchMethods for E {} impl<E: TElement> PrivateMatchMethods for E {}
/// The public API that elements expose for selector matching.
pub trait MatchMethods : TElement { pub trait MatchMethods : TElement {
/// Runs selector matching of this element, and returns the result.
fn match_element(&self, context: &StyleContext<Self>, parent_bf: Option<&BloomFilter>) fn match_element(&self, context: &StyleContext<Self>, parent_bf: Option<&BloomFilter>)
-> MatchResults -> MatchResults
{ {
@ -556,9 +618,10 @@ pub trait MatchMethods : TElement {
} }
} }
/// Attempts to share a style with another node. This method is unsafe because it depends on /// Attempts to share a style with another node. This method is unsafe
/// the `style_sharing_candidate_cache` having only live nodes in it, and we have no way to /// because it depends on the `style_sharing_candidate_cache` having only
/// guarantee that at the type system level yet. /// live nodes in it, and we have no way to guarantee that at the type
/// system level yet.
unsafe fn share_style_if_possible(&self, unsafe fn share_style_if_possible(&self,
style_sharing_candidate_cache: style_sharing_candidate_cache:
&mut StyleSharingCandidateCache<Self>, &mut StyleSharingCandidateCache<Self>,
@ -671,6 +734,9 @@ pub trait MatchMethods : TElement {
self.each_class(|class| bf.remove(class)); self.each_class(|class| bf.remove(class));
} }
/// Given the old and new style of this element, and whether it's a
/// pseudo-element, compute the restyle damage used to determine which
/// kind of layout or painting operations we'll need.
fn compute_restyle_damage(&self, fn compute_restyle_damage(&self,
old_style: Option<&Arc<ComputedValues>>, old_style: Option<&Arc<ComputedValues>>,
new_style: &Arc<ComputedValues>, new_style: &Arc<ComputedValues>,
@ -709,6 +775,8 @@ pub trait MatchMethods : TElement {
} }
} }
/// Given the results of selector matching, run the CSS cascade and style
/// the node, potentially starting any new transitions or animations.
fn cascade_node(&self, fn cascade_node(&self,
context: &StyleContext<Self>, context: &StyleContext<Self>,
mut data: &mut AtomicRefMut<ElementData>, mut data: &mut AtomicRefMut<ElementData>,
@ -783,6 +851,7 @@ pub trait MatchMethods : TElement {
data.finish_styling(new_styles, damage); data.finish_styling(new_styles, damage);
} }
/// Given the old and new styling results, compute the final restyle damage.
fn compute_damage_and_cascade_pseudos( fn compute_damage_and_cascade_pseudos(
&self, &self,
old_primary: Option<&Arc<ComputedValues>>, old_primary: Option<&Arc<ComputedValues>>,