style: Document Gecko's selector parser.

This commit is contained in:
Emilio Cobos Álvarez 2017-01-02 03:45:21 +01:00
parent 2778354455
commit bcac8265c9
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Gecko-specific bits for selector-parsing.
use cssparser::ToCss; use cssparser::ToCss;
use element_state::ElementState; use element_state::ElementState;
use selector_parser::{SelectorParser, PseudoElementCascadeType}; use selector_parser::{SelectorParser, PseudoElementCascadeType};
@ -11,11 +13,16 @@ use std::borrow::Cow;
use std::fmt; use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
/// NOTE: The boolean field represents whether this element is an anonymous box. /// A representation of a CSS pseudo-element.
/// ///
/// This is just for convenience, instead of recomputing it. Also, note that /// In Gecko, we represent pseudo-elements as plain `Atom`s.
/// Atom is always a static atom, so if space is a concern, we can use the ///
/// raw pointer and use the lower bit to represent it without space overhead. /// The boolean field represents whether this element is an anonymous box. This
/// is just for convenience, instead of recomputing it.
///
/// Also, note that the `Atom` member is always a static atom, so if space is a
/// concern, we can use the raw pointer and use the lower bit to represent it
/// without space overhead.
/// ///
/// FIXME(emilio): we know all these atoms are static. Patches are starting to /// FIXME(emilio): we know all these atoms are static. Patches are starting to
/// pile up, but a further potential optimisation is generating bindings without /// pile up, but a further potential optimisation is generating bindings without
@ -32,16 +39,22 @@ use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
pub struct PseudoElement(Atom, bool); pub struct PseudoElement(Atom, bool);
impl PseudoElement { impl PseudoElement {
/// Get the pseudo-element as an atom.
#[inline] #[inline]
pub fn as_atom(&self) -> &Atom { pub fn as_atom(&self) -> &Atom {
&self.0 &self.0
} }
/// Whether this pseudo-element is an anonymous box.
#[inline] #[inline]
fn is_anon_box(&self) -> bool { fn is_anon_box(&self) -> bool {
self.1 self.1
} }
/// Construct a pseudo-element from an `Atom`, receiving whether it is also
/// an anonymous box, and don't check it on release builds.
///
/// On debug builds we assert it's the result we expect.
#[inline] #[inline]
pub fn from_atom_unchecked(atom: Atom, is_anon_box: bool) -> Self { pub fn from_atom_unchecked(atom: Atom, is_anon_box: bool) -> Self {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
@ -73,6 +86,13 @@ impl PseudoElement {
None None
} }
/// Constructs an atom from a string of text, and whether we're in a
/// user-agent stylesheet.
///
/// If we're not in a user-agent stylesheet, we will never parse anonymous
/// box pseudo-elements.
///
/// Returns `None` if the pseudo-element is not recognised.
#[inline] #[inline]
fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> { fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
@ -102,20 +122,36 @@ impl ToCss for PseudoElement {
} }
} }
/// Our representation of a non tree-structural pseudo-class.
///
/// FIXME(emilio): Find a way to autogenerate this.
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum NonTSPseudoClass { pub enum NonTSPseudoClass {
/// :any-link
AnyLink, AnyLink,
/// :link
Link, Link,
/// :visited
Visited, Visited,
/// :active
Active, Active,
/// :focus
Focus, Focus,
/// :fullscreen
Fullscreen, Fullscreen,
/// :hover
Hover, Hover,
/// :enabled
Enabled, Enabled,
/// :disabled
Disabled, Disabled,
/// :checked
Checked, Checked,
/// :indeterminate
Indeterminate, Indeterminate,
/// :read-write
ReadWrite, ReadWrite,
/// :read-only
ReadOnly, ReadOnly,
} }
@ -141,6 +177,7 @@ impl ToCss for NonTSPseudoClass {
} }
impl NonTSPseudoClass { impl NonTSPseudoClass {
/// Get the state flag associated with a pseudo-class, if any.
pub fn state_flag(&self) -> ElementState { pub fn state_flag(&self) -> ElementState {
use element_state::*; use element_state::*;
use self::NonTSPseudoClass::*; use self::NonTSPseudoClass::*;
@ -162,6 +199,7 @@ impl NonTSPseudoClass {
} }
} }
/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct SelectorImpl; pub struct SelectorImpl;
@ -231,6 +269,13 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
impl SelectorImpl { impl SelectorImpl {
#[inline] #[inline]
/// Returns the kind of cascade type that a given pseudo is going to use.
///
/// In Gecko we only compute ::before and ::after eagerly. We save the rules
/// for anonymous boxes separately, so we resolve them as precomputed
/// pseudos.
///
/// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType { pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
if Self::pseudo_is_before_or_after(pseudo) { if Self::pseudo_is_before_or_after(pseudo) {
return PseudoElementCascadeType::Eager return PseudoElementCascadeType::Eager
@ -244,8 +289,9 @@ impl SelectorImpl {
} }
#[inline] #[inline]
/// Executes a function for each pseudo-element.
pub fn each_pseudo_element<F>(mut fun: F) pub fn each_pseudo_element<F>(mut fun: F)
where F: FnMut(PseudoElement) where F: FnMut(PseudoElement),
{ {
macro_rules! pseudo_element { macro_rules! pseudo_element {
($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{ ($pseudo_str_with_colon:expr, $atom:expr, $is_anon_box:expr) => {{
@ -257,12 +303,15 @@ impl SelectorImpl {
} }
#[inline] #[inline]
/// Returns whether the given pseudo-element is `::before` or `::after`.
pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool { pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
*pseudo.as_atom() == atom!(":before") || *pseudo.as_atom() == atom!(":before") ||
*pseudo.as_atom() == atom!(":after") *pseudo.as_atom() == atom!(":after")
} }
#[inline] #[inline]
/// Returns the relevant state flag for a given non-tree-structural
/// pseudo-class.
pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState { pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
pc.state_flag() pc.state_flag()
} }