mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
style: Document the matching
module.
This commit is contained in:
parent
30d010342a
commit
7bf80e5d50
1 changed files with 84 additions and 15 deletions
|
@ -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>>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue