diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index dcafd4bf9d7..3c94ca21339 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -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; @@ -2392,7 +2392,10 @@ impl<'a> ::selectors::Element for Root { 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 | diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index b8a38247fd7..98ce86745f2 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -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; @@ -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 | @@ -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 diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index efe1d76007e..f567b2bc7ca 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -413,7 +413,7 @@ fn matches_simple_selector( 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 => { diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index d5cae30042f..c921d1fb9b1 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -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; @@ -120,7 +120,7 @@ pub trait Parser { } } -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct SelectorList(pub Vec>); impl SelectorList { @@ -135,7 +135,7 @@ impl SelectorList { } } -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Eq, Hash, Clone)] pub struct Selector { pub complex_selector: Arc>, pub pseudo_element: Option, @@ -159,6 +159,8 @@ fn affects_sibling(simple_selector: &SimpleSelector) - SimpleSelector::LastOfType | SimpleSelector::OnlyOfType => true, + SimpleSelector::NonTSPseudoClass(ref pseudo_class) => pseudo_class.affects_siblings(), + _ => false, } } @@ -180,28 +182,36 @@ fn matches_non_common_style_affecting_attribute(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 Selector { +pub trait SelectorMethods { + fn affects_siblings(&self) -> bool; + fn matches_non_common_style_affecting_attribute(&self) -> bool; +} + +impl SelectorMethods for Selector { /// 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 ComplexSelector { +impl SelectorMethods for ComplexSelector { /// 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, @@ -214,7 +224,7 @@ impl ComplexSelector { } } - 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, @@ -714,18 +724,18 @@ fn parse_complex_selector_and_pseudo_element( Ok((complex, pseudo_element)) } -fn parse_complex_selector( - parser: &P, - input: &mut CssParser) - -> Result, ()> - where P: Parser, Impl: SelectorImpl -{ - let (complex, pseudo_element) = - parse_complex_selector_and_pseudo_element(parser, input)?; - if pseudo_element.is_some() { - return Err(()) +impl ComplexSelector { + /// Parse a complex selector. + pub fn parse

(parser: &P, input: &mut CssParser) -> Result + where P: Parser + { + 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 @@ -934,7 +944,7 @@ fn parse_negation(parser: &P, -> Result, ()> where P: Parser, 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) } @@ -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; diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index a76e1d577c8..315d9d7196d 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -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; @@ -138,7 +139,10 @@ pub trait Element: MatchAttr + Sized { fn get_local_name(&self) -> &::BorrowedLocalName; fn get_namespace(&self) -> &::BorrowedNamespaceUrl; - fn match_non_ts_pseudo_class(&self, pc: &::NonTSPseudoClass) -> bool; + fn match_non_ts_pseudo_class(&self, + pc: &::NonTSPseudoClass, + relations: &mut StyleRelations, + flags: &mut ElementSelectorFlags) -> bool; fn get_id(&self) -> Option<::Identifier>; fn has_class(&self, name: &::ClassName) -> bool; diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index d465b7fa791..8b3b019dc77 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -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; @@ -152,6 +153,8 @@ macro_rules! pseudo_class_name { #[doc = $s_css] $s_name(Box), )* + /// The non-standard `:-moz-any` pseudo-class. + MozAny(Vec>), } } } @@ -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(")") + } } } } @@ -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. @@ -189,6 +226,7 @@ impl NonTSPseudoClass { match *self { $(NonTSPseudoClass::$name => check_flag!($flags),)* $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)* + NonTSPseudoClass::MozAny(_) => false, } } } @@ -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) + }) + } } } } @@ -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), } } } @@ -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 { @@ -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(()) } } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 76974f0d2f7..86ac901e407 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -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; @@ -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) }, @@ -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)) + } } } @@ -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] diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 7b744284e0b..bfa47169783 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -269,14 +269,17 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E> impl<'a, E> Element for ElementWrapper<'a, E> where E: TElement, { - 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 { let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class); if flag == ElementState::empty() { - self.element.match_non_ts_pseudo_class(pseudo_class) + self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags) } else { match self.snapshot.and_then(|s| s.state()) { Some(snapshot_state) => snapshot_state.contains(flag), - _ => self.element.match_non_ts_pseudo_class(pseudo_class) + _ => self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags) } } } @@ -347,6 +350,13 @@ impl<'a, E> Element for ElementWrapper<'a, E> } } +/// Returns the union of any `ElementState` flags for components of a `ComplexSelector` +pub fn complex_selector_to_state(sel: &ComplexSelector) -> ElementState { + sel.compound_selector.iter().fold(ElementState::empty(), |state, s| { + state | selector_to_state(s) + }) +} + fn selector_to_state(sel: &SimpleSelector) -> ElementState { match *sel { SimpleSelector::NonTSPseudoClass(ref pc) => SelectorImpl::pseudo_class_state_flag(pc), diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index ab4c0b658f8..78601a9b0dd 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -14,7 +14,8 @@ use restyle_hints::ElementSnapshot; use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser}; use selector_parser::{attr_equals_selector_is_shareable, attr_exists_selector_is_shareable}; use selectors::{Element, MatchAttrGeneric}; -use selectors::parser::AttrSelector; +use selectors::matching::{ElementSelectorFlags, StyleRelations}; +use selectors::parser::{AttrSelector, SelectorMethods}; use std::borrow::Cow; use std::fmt; use std::fmt::Debug; @@ -60,7 +61,6 @@ impl ToCss for PseudoElement { } } - impl PseudoElement { /// Whether the current pseudo element is :before or :after. #[inline] @@ -150,6 +150,13 @@ impl ToCss for NonTSPseudoClass { } } +impl SelectorMethods for NonTSPseudoClass { + #[inline] + fn affects_siblings(&self) -> bool { false } + #[inline] + fn matches_non_common_style_affecting_attribute(&self) -> bool { false } +} + impl NonTSPseudoClass { /// Gets a given state flag for this pseudo-class. This is used to do /// selector matching, and it's set from the DOM. @@ -450,7 +457,9 @@ impl MatchAttrGeneric for ServoElementSnapshot { impl + Debug> ElementExt for E { 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] diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 79044cd1fb5..f7dd149e7d3 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -27,6 +27,7 @@ use selectors::matching::{AFFECTED_BY_ANIMATIONS, AFFECTED_BY_TRANSITIONS}; use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS}; use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_complex_selector}; use selectors::parser::{Selector, SimpleSelector, LocalName as LocalNameSelector, ComplexSelector}; +use selectors::parser::SelectorMethods; use sink::Push; use smallvec::VecLike; use std::borrow::Borrow;