mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Bug 1364412: Allow pseudo-element selectors to store state. r=bholley
MozReview-Commit-ID: CzAwg2uxqO2 Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
parent
5bd6b92494
commit
10560ae043
4 changed files with 117 additions and 22 deletions
|
@ -5,6 +5,7 @@
|
|||
//! Gecko-specific bits for selector-parsing.
|
||||
|
||||
use cssparser::{Parser, ToCss};
|
||||
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
|
||||
use element_state::ElementState;
|
||||
use gecko_bindings::structs::CSSPseudoClassType;
|
||||
use gecko_bindings::structs::nsIAtom;
|
||||
|
@ -325,6 +326,15 @@ impl NonTSPseudoClass {
|
|||
apply_non_ts_list!(pseudo_class_check_internal)
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/selectors-4/#useraction-pseudos
|
||||
///
|
||||
/// We intentionally skip the link-related ones.
|
||||
fn is_safe_user_action_state(&self) -> bool {
|
||||
matches!(*self, NonTSPseudoClass::Hover |
|
||||
NonTSPseudoClass::Active |
|
||||
NonTSPseudoClass::Focus)
|
||||
}
|
||||
|
||||
/// Get the state flag associated with a pseudo-class, if any.
|
||||
pub fn state_flag(&self) -> ElementState {
|
||||
macro_rules! flag {
|
||||
|
@ -375,6 +385,53 @@ impl NonTSPseudoClass {
|
|||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct SelectorImpl;
|
||||
|
||||
/// Some subset of pseudo-elements in Gecko are sensitive to some state
|
||||
/// selectors.
|
||||
///
|
||||
/// We store the sensitive states in this struct in order to properly handle
|
||||
/// these.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct PseudoElementSelector {
|
||||
pseudo: PseudoElement,
|
||||
state: ElementState,
|
||||
}
|
||||
|
||||
impl PseudoElementSelector {
|
||||
/// Returns the pseudo-element this selector represents.
|
||||
pub fn pseudo_element(&self) -> &PseudoElement {
|
||||
&self.pseudo
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for PseudoElementSelector {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||
where W: fmt::Write,
|
||||
{
|
||||
if cfg!(debug_assertions) {
|
||||
let mut state = self.state;
|
||||
state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
|
||||
assert_eq!(state, ElementState::empty(),
|
||||
"Unhandled pseudo-element state selector?");
|
||||
}
|
||||
|
||||
self.pseudo.to_css(dest)?;
|
||||
|
||||
if self.state.contains(IN_HOVER_STATE) {
|
||||
dest.write_str(":hover")?
|
||||
}
|
||||
|
||||
if self.state.contains(IN_ACTIVE_STATE) {
|
||||
dest.write_str(":active")?
|
||||
}
|
||||
|
||||
if self.state.contains(IN_FOCUS_STATE) {
|
||||
dest.write_str(":focus")?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::selectors::SelectorImpl for SelectorImpl {
|
||||
type AttrValue = Atom;
|
||||
type Identifier = Atom;
|
||||
|
@ -385,7 +442,7 @@ impl ::selectors::SelectorImpl for SelectorImpl {
|
|||
type BorrowedNamespaceUrl = WeakNamespace;
|
||||
type BorrowedLocalName = WeakAtom;
|
||||
|
||||
type PseudoElement = PseudoElement;
|
||||
type PseudoElementSelector = PseudoElementSelector;
|
||||
type NonTSPseudoClass = NonTSPseudoClass;
|
||||
}
|
||||
|
||||
|
@ -447,11 +504,34 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
|
||||
match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
|
||||
Some(pseudo) => Ok(pseudo),
|
||||
None => Err(()),
|
||||
}
|
||||
fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
|
||||
let pseudo =
|
||||
match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
|
||||
Some(pseudo) => pseudo,
|
||||
None => return Err(()),
|
||||
};
|
||||
|
||||
let state = input.try(|input| {
|
||||
let mut state = ElementState::empty();
|
||||
|
||||
while !input.is_exhausted() {
|
||||
input.expect_colon()?;
|
||||
let ident = input.expect_ident()?;
|
||||
let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;
|
||||
|
||||
if !pseudo_class.is_safe_user_action_state() {
|
||||
return Err(())
|
||||
}
|
||||
state.insert(pseudo_class.state_flag());
|
||||
}
|
||||
|
||||
Ok(state)
|
||||
});
|
||||
|
||||
Ok(PseudoElementSelector {
|
||||
pseudo: pseudo,
|
||||
state: state.unwrap_or(ElementState::empty()),
|
||||
})
|
||||
}
|
||||
|
||||
fn default_namespace(&self) -> Option<Namespace> {
|
||||
|
|
|
@ -78,6 +78,18 @@ impl ToCss for PseudoElement {
|
|||
pub const EAGER_PSEUDO_COUNT: usize = 3;
|
||||
|
||||
impl PseudoElement {
|
||||
/// The pseudo-element, used for compatibility with Gecko's
|
||||
/// `PseudoElementSelector`.
|
||||
pub fn pseudo_element(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
|
||||
/// The pseudo-element selector's state, used for compatibility with Gecko's
|
||||
/// `PseudoElementSelector`.
|
||||
pub fn state(&self) -> ElementState {
|
||||
ElementState::empty()
|
||||
}
|
||||
|
||||
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
||||
#[inline]
|
||||
pub fn eager_index(&self) -> usize {
|
||||
|
@ -252,7 +264,7 @@ impl NonTSPseudoClass {
|
|||
pub struct SelectorImpl;
|
||||
|
||||
impl ::selectors::SelectorImpl for SelectorImpl {
|
||||
type PseudoElement = PseudoElement;
|
||||
type PseudoElementSelector = PseudoElement;
|
||||
type NonTSPseudoClass = NonTSPseudoClass;
|
||||
|
||||
type AttrValue = String;
|
||||
|
@ -311,7 +323,10 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
|||
Ok(pseudo_class)
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
|
||||
fn parse_pseudo_element(&self,
|
||||
name: Cow<str>,
|
||||
_input: &mut CssParser)
|
||||
-> Result<PseudoElement, ()> {
|
||||
use self::PseudoElement::*;
|
||||
let pseudo_element = match_ignore_ascii_case! { &name,
|
||||
"before" => Before,
|
||||
|
|
|
@ -449,9 +449,9 @@ impl Stylist {
|
|||
rule: &Arc<Locked<StyleRule>>,
|
||||
stylesheet: &Stylesheet)
|
||||
{
|
||||
let map = if let Some(ref pseudo) = selector.pseudo_element {
|
||||
let map = if let Some(ref pseudo_selector) = selector.pseudo_element {
|
||||
self.pseudos_map
|
||||
.entry(pseudo.clone())
|
||||
.entry(pseudo_selector.pseudo_element().clone())
|
||||
.or_insert_with(PerPseudoElementSelectorMap::new)
|
||||
.borrow_for_origin(&stylesheet.origin)
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue