servo/components/style/selector_impl.rs
2016-06-28 15:20:59 +00:00

285 lines
8.5 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
//! The pseudo-classes and pseudo-elements supported by the style system.
use element_state::ElementState;
use properties::{self, ServoComputedValues};
use selector_matching::{USER_OR_USER_AGENT_STYLESHEETS, QUIRKS_MODE_STYLESHEET};
use selectors::Element;
use selectors::parser::{ParserContext, SelectorImpl};
use std::fmt::Debug;
use stylesheets::Stylesheet;
/// This function determines if a pseudo-element is eagerly cascaded or not.
///
/// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
/// `::before` and `::after`). They inherit styles normally as another
/// selector would do, and they're part of the cascade.
///
/// Lazy pseudo-elements are affected by selector matching, but they're only
/// computed when needed, and not before. They're useful for general
/// pseudo-elements that are not very common.
///
/// Note that in Servo lazy pseudo-elements are restricted to a subset of
/// selectors, so you can't use it for public pseudo-elements. This is not the
/// case with Gecko though.
///
/// Precomputed ones skip the cascade process entirely, mostly as an
/// optimisation since they are private pseudo-elements (like
/// `::-servo-details-content`).
///
/// This pseudo-elements are resolved on the fly using *only* global rules
/// (rules of the form `*|*`), and applying them to the parent style.
///
/// If you're implementing a public selector that the end-user might customize,
/// then you probably need to make it eager.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PseudoElementCascadeType {
Eager,
Lazy,
Precomputed,
}
impl PseudoElementCascadeType {
#[inline]
pub fn is_eager(&self) -> bool {
*self == PseudoElementCascadeType::Eager
}
#[inline]
pub fn is_lazy(&self) -> bool {
*self == PseudoElementCascadeType::Lazy
}
#[inline]
pub fn is_precomputed(&self) -> bool {
*self == PseudoElementCascadeType::Precomputed
}
}
pub trait ElementExt: Element {
fn is_link(&self) -> bool;
}
// NB: The `Clone` trait is here for convenience due to:
// https://github.com/rust-lang/rust/issues/26925
pub trait SelectorImplExt : SelectorImpl + Clone + Debug + Sized {
type ComputedValues: properties::ComputedValues;
fn pseudo_element_cascade_type(pseudo: &Self::PseudoElement) -> PseudoElementCascadeType;
fn each_pseudo_element<F>(mut fun: F)
where F: FnMut(Self::PseudoElement);
#[inline]
fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
Self::each_pseudo_element(|pseudo| {
if Self::pseudo_element_cascade_type(&pseudo).is_eager() {
fun(pseudo)
}
})
}
#[inline]
fn each_precomputed_pseudo_element<F>(mut fun: F)
where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
Self::each_pseudo_element(|pseudo| {
if Self::pseudo_element_cascade_type(&pseudo).is_precomputed() {
fun(pseudo)
}
})
}
fn pseudo_is_before_or_after(pseudo: &Self::PseudoElement) -> bool;
fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState;
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>];
fn get_quirks_mode_stylesheet() -> Option<&'static Stylesheet<Self>>;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum PseudoElement {
Before,
After,
Selection,
DetailsSummary,
DetailsContent,
}
impl PseudoElement {
#[inline]
pub fn is_before_or_after(&self) -> bool {
match *self {
PseudoElement::Before |
PseudoElement::After => true,
_ => false,
}
}
#[inline]
pub fn cascade_type(&self) -> PseudoElementCascadeType {
match *self {
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Selection => PseudoElementCascadeType::Eager,
PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
PseudoElement::DetailsContent => PseudoElementCascadeType::Precomputed,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum NonTSPseudoClass {
AnyLink,
Link,
Visited,
Active,
Focus,
Hover,
Enabled,
Disabled,
Checked,
Indeterminate,
ServoNonZeroBorder,
ReadWrite,
ReadOnly,
PlaceholderShown,
}
impl NonTSPseudoClass {
pub fn state_flag(&self) -> ElementState {
use element_state::*;
use self::NonTSPseudoClass::*;
match *self {
Active => IN_ACTIVE_STATE,
Focus => IN_FOCUS_STATE,
Hover => IN_HOVER_STATE,
Enabled => IN_ENABLED_STATE,
Disabled => IN_DISABLED_STATE,
Checked => IN_CHECKED_STATE,
Indeterminate => IN_INDETERMINATE_STATE,
ReadOnly | ReadWrite => IN_READ_WRITE_STATE,
PlaceholderShown => IN_PLACEHOLDER_SHOWN_STATE,
AnyLink |
Link |
Visited |
ServoNonZeroBorder => ElementState::empty(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ServoSelectorImpl;
impl SelectorImpl for ServoSelectorImpl {
type PseudoElement = PseudoElement;
type NonTSPseudoClass = NonTSPseudoClass;
fn parse_non_ts_pseudo_class(context: &ParserContext,
name: &str) -> Result<NonTSPseudoClass, ()> {
use self::NonTSPseudoClass::*;
let pseudo_class = match_ignore_ascii_case! { name,
"any-link" => AnyLink,
"link" => Link,
"visited" => Visited,
"active" => Active,
"focus" => Focus,
"hover" => Hover,
"enabled" => Enabled,
"disabled" => Disabled,
"checked" => Checked,
"indeterminate" => Indeterminate,
"read-write" => ReadWrite,
"read-only" => ReadOnly,
"placeholder-shown" => PlaceholderShown,
"-servo-nonzero-border" => {
if !context.in_user_agent_stylesheet {
return Err(());
}
ServoNonZeroBorder
},
_ => return Err(())
};
Ok(pseudo_class)
}
fn parse_pseudo_element(context: &ParserContext,
name: &str) -> Result<PseudoElement, ()> {
use self::PseudoElement::*;
let pseudo_element = match_ignore_ascii_case! { name,
"before" => Before,
"after" => After,
"selection" => Selection,
"-servo-details-summary" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
DetailsSummary
},
"-servo-details-content" => {
if !context.in_user_agent_stylesheet {
return Err(())
}
DetailsContent
},
_ => return Err(())
};
Ok(pseudo_element)
}
}
impl SelectorImplExt for ServoSelectorImpl {
type ComputedValues = ServoComputedValues;
#[inline]
fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
pseudo.cascade_type()
}
#[inline]
fn each_pseudo_element<F>(mut fun: F)
where F: FnMut(PseudoElement) {
fun(PseudoElement::Before);
fun(PseudoElement::After);
fun(PseudoElement::DetailsContent);
fun(PseudoElement::DetailsSummary);
fun(PseudoElement::Selection);
}
#[inline]
fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
pc.state_flag()
}
#[inline]
fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
pseudo.is_before_or_after()
}
#[inline]
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>] {
&*USER_OR_USER_AGENT_STYLESHEETS
}
#[inline]
fn get_quirks_mode_stylesheet() -> Option<&'static Stylesheet<Self>> {
Some(&*QUIRKS_MODE_STYLESHEET)
}
}
impl<E: Element<Impl=ServoSelectorImpl>> ElementExt for E {
fn is_link(&self) -> bool {
self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink)
}
}