Skip to content

Commit

Permalink
Bug 1340683 - stylo: Implement the :-moz-any pseudo-class
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrubeck committed Mar 16, 2017
1 parent e34aac0 commit 2872c8b
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 40 deletions.
7 changes: 5 additions & 2 deletions components/script/dom/element.rs
Expand Up @@ -86,7 +86,7 @@ use parking_lot::RwLock;
use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType;
use script_thread::Runnable;
use selectors::matching::{ElementSelectorFlags, matches};
use selectors::matching::{ElementSelectorFlags, StyleRelations, matches};
use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
Expand Down Expand Up @@ -2398,7 +2398,10 @@ impl<'a> ::selectors::Element for Root<Element> {
self.namespace()
}

fn match_non_ts_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
fn match_non_ts_pseudo_class(&self,
pseudo_class: &NonTSPseudoClass,
_: &mut StyleRelations,
_: &mut ElementSelectorFlags) -> bool {
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
Expand Down
12 changes: 9 additions & 3 deletions components/script/layout_wrapper.rs
Expand Up @@ -50,7 +50,7 @@ use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, Truste
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use selectors::matching::ElementSelectorFlags;
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
use servo_url::ServoUrl;
Expand Down Expand Up @@ -611,7 +611,10 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
self.element.namespace()
}

fn match_non_ts_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
fn match_non_ts_pseudo_class(&self,
pseudo_class: &NonTSPseudoClass,
_: &mut StyleRelations,
_: &mut ElementSelectorFlags) -> bool {
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
Expand Down Expand Up @@ -1106,7 +1109,10 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
self.element.get_namespace()
}

fn match_non_ts_pseudo_class(&self, _: &NonTSPseudoClass) -> bool {
fn match_non_ts_pseudo_class(&self,
_: &NonTSPseudoClass,
_: &mut StyleRelations,
_: &mut ElementSelectorFlags) -> bool {
// NB: This could maybe be implemented
warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
false
Expand Down
2 changes: 1 addition & 1 deletion components/selectors/matching.rs
Expand Up @@ -413,7 +413,7 @@ fn matches_simple_selector<E>(
false
}
SimpleSelector::NonTSPseudoClass(ref pc) => {
relation_if!(element.match_non_ts_pseudo_class(pc),
relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags),
AFFECTED_BY_STATE)
}
SimpleSelector::FirstChild => {
Expand Down
57 changes: 36 additions & 21 deletions components/selectors/parser.rs
Expand Up @@ -49,7 +49,7 @@ macro_rules! with_all_bounds {

/// non tree-structural pseudo-classes
/// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss;
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods;

/// pseudo-elements
type PseudoElement: $($CommonBounds)* + Sized + ToCss;
Expand Down Expand Up @@ -120,7 +120,7 @@ pub trait Parser {
}
}

#[derive(PartialEq, Clone, Debug)]
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct SelectorList<Impl: SelectorImpl>(pub Vec<Selector<Impl>>);

impl<Impl: SelectorImpl> SelectorList<Impl> {
Expand All @@ -135,7 +135,7 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
}
}

#[derive(PartialEq, Clone)]
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Selector<Impl: SelectorImpl> {
pub complex_selector: Arc<ComplexSelector<Impl>>,
pub pseudo_element: Option<Impl::PseudoElement>,
Expand All @@ -159,6 +159,8 @@ fn affects_sibling<Impl: SelectorImpl>(simple_selector: &SimpleSelector<Impl>) -
SimpleSelector::LastOfType |
SimpleSelector::OnlyOfType => true,

SimpleSelector::NonTSPseudoClass(ref pseudo_class) => pseudo_class.affects_siblings(),

_ => false,
}
}
Expand All @@ -180,28 +182,36 @@ fn matches_non_common_style_affecting_attribute<Impl: SelectorImpl>(simple_selec
SimpleSelector::AttrSuffixMatch(..) |
SimpleSelector::AttrSubstringMatch(..) => true,

SimpleSelector::NonTSPseudoClass(ref pseudo_class) =>
pseudo_class.matches_non_common_style_affecting_attribute(),

// This deliberately includes Attr*NeverMatch
// which never match regardless of element attributes.
_ => false,
}
}

impl<Impl: SelectorImpl> Selector<Impl> {
pub trait SelectorMethods {
fn affects_siblings(&self) -> bool;
fn matches_non_common_style_affecting_attribute(&self) -> bool;
}

impl<Impl: SelectorImpl> SelectorMethods for Selector<Impl> {
/// Whether this selector, if matching on a set of siblings, could affect
/// other sibling's style.
pub fn affects_siblings(&self) -> bool {
fn affects_siblings(&self) -> bool {
self.complex_selector.affects_siblings()
}

pub fn matches_non_common_style_affecting_attribute(&self) -> bool {
fn matches_non_common_style_affecting_attribute(&self) -> bool {
self.complex_selector.matches_non_common_style_affecting_attribute()
}
}

impl<Impl: SelectorImpl> ComplexSelector<Impl> {
impl<Impl: SelectorImpl> SelectorMethods for ComplexSelector<Impl> {
/// Whether this complex selector, if matching on a set of siblings,
/// could affect other sibling's style.
pub fn affects_siblings(&self) -> bool {
fn affects_siblings(&self) -> bool {
match self.next {
Some((_, Combinator::NextSibling)) |
Some((_, Combinator::LaterSibling)) => return true,
Expand All @@ -214,7 +224,7 @@ impl<Impl: SelectorImpl> ComplexSelector<Impl> {
}
}

pub fn matches_non_common_style_affecting_attribute(&self) -> bool {
fn matches_non_common_style_affecting_attribute(&self) -> bool {
match self.compound_selector.last() {
Some(ref selector) => matches_non_common_style_affecting_attribute(selector),
None => false,
Expand Down Expand Up @@ -714,18 +724,18 @@ fn parse_complex_selector_and_pseudo_element<P, Impl>(
Ok((complex, pseudo_element))
}

fn parse_complex_selector<P, Impl>(
parser: &P,
input: &mut CssParser)
-> Result<ComplexSelector<Impl>, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl
{
let (complex, pseudo_element) =
parse_complex_selector_and_pseudo_element(parser, input)?;
if pseudo_element.is_some() {
return Err(())
impl<Impl: SelectorImpl> ComplexSelector<Impl> {
/// Parse a complex selector.
pub fn parse<P>(parser: &P, input: &mut CssParser) -> Result<Self, ()>
where P: Parser<Impl=Impl>
{
let (complex, pseudo_element) =
parse_complex_selector_and_pseudo_element(parser, input)?;
if pseudo_element.is_some() {
return Err(())
}
Ok(complex)
}
Ok(complex)
}

/// * `Err(())`: Invalid selector, abort
Expand Down Expand Up @@ -934,7 +944,7 @@ fn parse_negation<P, Impl>(parser: &P,
-> Result<SimpleSelector<Impl>, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl
{
input.parse_comma_separated(|input| parse_complex_selector(parser, input).map(Arc::new))
input.parse_comma_separated(|input| ComplexSelector::parse(parser, input).map(Arc::new))
.map(SimpleSelector::Negation)
}

Expand Down Expand Up @@ -1164,6 +1174,11 @@ pub mod tests {
}
}

impl SelectorMethods for PseudoClass {
fn affects_siblings(&self) -> bool { false }
fn matches_non_common_style_affecting_attribute(&self) -> bool { false }
}

#[derive(PartialEq, Debug)]
pub struct DummySelectorImpl;

Expand Down
6 changes: 5 additions & 1 deletion components/selectors/tree.rs
Expand Up @@ -5,6 +5,7 @@
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
//! style.

use matching::{ElementSelectorFlags, StyleRelations};
use parser::{AttrSelector, SelectorImpl};
use std::ascii::AsciiExt;

Expand Down Expand Up @@ -138,7 +139,10 @@ pub trait Element: MatchAttr + Sized {
fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName;
fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl;

fn match_non_ts_pseudo_class(&self, pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass) -> bool;
fn match_non_ts_pseudo_class(&self,
pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) -> bool;

fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;
Expand Down
54 changes: 52 additions & 2 deletions components/style/gecko/selector_parser.rs
Expand Up @@ -8,9 +8,10 @@ use cssparser::{Parser, ToCss};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use gecko_bindings::structs::nsIAtom;
use restyle_hints::complex_selector_to_state;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selector_parser::{attr_equals_selector_is_shareable, attr_exists_selector_is_shareable};
use selectors::parser::AttrSelector;
use selectors::parser::{AttrSelector, ComplexSelector, SelectorMethods};
use std::borrow::Cow;
use std::fmt;
use std::ptr;
Expand Down Expand Up @@ -152,6 +153,8 @@ macro_rules! pseudo_class_name {
#[doc = $s_css]
$s_name(Box<str>),
)*
/// The non-standard `:-moz-any` pseudo-class.
MozAny(Vec<ComplexSelector<SelectorImpl>>),
}
}
}
Expand All @@ -167,6 +170,17 @@ impl ToCss for NonTSPseudoClass {
$(NonTSPseudoClass::$s_name(ref s) => {
return dest.write_str(&format!(":{}({})", $s_css, s))
}, )*
NonTSPseudoClass::MozAny(ref selectors) => {
dest.write_str(":-moz-any(")?;
let mut iter = selectors.iter();
let first = iter.next().expect(":-moz-any must have at least 1 selector");
first.to_css(dest)?;
for selector in iter {
dest.write_str(", ")?;
selector.to_css(dest)?;
}
return dest.write_str(")")
}
}
}
}
Expand All @@ -175,6 +189,29 @@ impl ToCss for NonTSPseudoClass {
}
}

impl SelectorMethods for NonTSPseudoClass {
#[inline]
fn affects_siblings(&self) -> bool {
match *self {
NonTSPseudoClass::MozAny(ref selectors) => {
selectors.iter().any(|s| s.affects_siblings())
}
_ => false
}
}

#[inline]
fn matches_non_common_style_affecting_attribute(&self) -> bool {
match *self {
NonTSPseudoClass::MozAny(ref selectors) => {
selectors.iter().any(|s| s.matches_non_common_style_affecting_attribute())
}
_ => false
}
}
}


impl NonTSPseudoClass {
/// A pseudo-class is internal if it can only be used inside
/// user agent style sheets.
Expand All @@ -189,6 +226,7 @@ impl NonTSPseudoClass {
match *self {
$(NonTSPseudoClass::$name => check_flag!($flags),)*
$(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)*
NonTSPseudoClass::MozAny(_) => false,
}
}
}
Expand All @@ -207,6 +245,11 @@ impl NonTSPseudoClass {
match *self {
$(NonTSPseudoClass::$name => flag!($state),)*
$(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
NonTSPseudoClass::MozAny(ref selectors) => {
selectors.iter().fold(ElementState::empty(), |state, s| {
state | complex_selector_to_state(s)
})
}
}
}
}
Expand All @@ -226,6 +269,7 @@ impl NonTSPseudoClass {
match *self {
$(NonTSPseudoClass::$name => gecko_type!($gecko_type),)*
$(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)*
NonTSPseudoClass::MozAny(_) => gecko_type!(any),
}
}
}
Expand All @@ -234,7 +278,7 @@ impl NonTSPseudoClass {
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SelectorImpl;

impl ::selectors::SelectorImpl for SelectorImpl {
Expand Down Expand Up @@ -293,6 +337,12 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
let name = String::from(parser.expect_ident_or_string()?).into_boxed_str();
NonTSPseudoClass::$s_name(name)
}, )*
"-moz-any" => {
let selectors = parser.parse_comma_separated(|input| {
ComplexSelector::parse(self, input)
})?;
NonTSPseudoClass::MozAny(selectors)
}
_ => return Err(())
}
}
Expand Down
16 changes: 12 additions & 4 deletions components/style/gecko/wrapper.rs
Expand Up @@ -49,7 +49,7 @@ use properties::PropertyDeclarationBlock;
use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::{ElementExt, Snapshot};
use selectors::Element;
use selectors::matching::ElementSelectorFlags;
use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_complex_selector};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_url::ServoUrl;
use sink::Push;
Expand Down Expand Up @@ -637,7 +637,10 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
}
}

fn match_non_ts_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
fn match_non_ts_pseudo_class(&self,
pseudo_class: &NonTSPseudoClass,
relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) -> bool {
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::AnyLink => unsafe { Gecko_IsLink(self.0) },
Expand Down Expand Up @@ -667,7 +670,10 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
NonTSPseudoClass::MozBrowserFrame => unsafe {
Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
},
_ => false
NonTSPseudoClass::MozSystemMetric(_) => false,
NonTSPseudoClass::MozAny(ref sels) => {
sels.iter().any(|s| matches_complex_selector(s, self, None, relations, flags))
}
}
}

Expand Down Expand Up @@ -805,7 +811,9 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
impl<'le> ElementExt for GeckoElement<'le> {
#[inline]
fn is_link(&self) -> bool {
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink)
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty())
}

#[inline]
Expand Down

0 comments on commit 2872c8b

Please sign in to comment.