diff --git a/components/selectors/gecko_like_types.rs b/components/selectors/gecko_like_types.rs index e0ef0f81e9d..de0c4034e8c 100644 --- a/components/selectors/gecko_like_types.rs +++ b/components/selectors/gecko_like_types.rs @@ -10,6 +10,7 @@ pub enum PseudoClass { Bare, String(Box<[u16]>), + Dir(Box<()>), MozAny(Box<[()]>), } diff --git a/components/style/gecko/non_ts_pseudo_class_list.rs b/components/style/gecko/non_ts_pseudo_class_list.rs index 6a16d4dc3db..f7a2077e788 100644 --- a/components/style/gecko/non_ts_pseudo_class_list.rs +++ b/components/style/gecko/non_ts_pseudo_class_list.rs @@ -120,7 +120,6 @@ macro_rules! apply_non_ts_list { ], keyword: [ ("-moz-locale-dir", MozLocaleDir, mozLocaleDir, _, _), - ("dir", Dir, dir, _, _), ] } } diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 5d6c46af464..db4372a4610 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -9,13 +9,13 @@ use element_state::{DocumentState, ElementState}; use gecko_bindings::structs::CSSPseudoClassType; use gecko_bindings::structs::RawServoSelectorList; use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; -use selector_parser::{SelectorParser, PseudoElementCascadeType}; +use selector_parser::{Direction, SelectorParser, PseudoElementCascadeType}; use selectors::SelectorList; use selectors::parser::{Selector, SelectorMethods, SelectorParseErrorKind}; use selectors::visitor::SelectorVisitor; use std::fmt; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; -use style_traits::{ParseError, StyleParseErrorKind}; +use style_traits::{ParseError, StyleParseErrorKind, ToCss as ToCss_}; pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT}; pub use gecko::snapshot::SnapshotMap; @@ -53,6 +53,8 @@ macro_rules! pseudo_class_name { #[doc = $k_css] $k_name(Box<[u16]>), )* + /// The `:dir` pseudo-class. + Dir(Box), /// The non-standard `:-moz-any` pseudo-class. /// /// TODO(emilio): We disallow combinators and pseudos here, so we @@ -92,6 +94,12 @@ impl ToCss for NonTSPseudoClass { dest.write_str(&value)?; return dest.write_char(')') }, )* + NonTSPseudoClass::Dir(ref dir) => { + dest.write_str(":dir(")?; + // FIXME: This should be escaped as an identifier; see #19231 + (**dir).to_css(dest)?; + return dest.write_char(')') + }, NonTSPseudoClass::MozAny(ref selectors) => { dest.write_str(":-moz-any(")?; let mut iter = selectors.iter(); @@ -145,6 +153,7 @@ impl NonTSPseudoClass { $(NonTSPseudoClass::$name => check_flag!($flags),)* $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)* $(NonTSPseudoClass::$k_name(..) => check_flag!($k_flags),)* + NonTSPseudoClass::Dir(_) => false, NonTSPseudoClass::MozAny(_) => false, } } @@ -189,6 +198,7 @@ impl NonTSPseudoClass { $(NonTSPseudoClass::$name => flag!($state),)* $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)* $(NonTSPseudoClass::$k_name(..) => flag!($k_state),)* + NonTSPseudoClass::Dir(..) => ElementState::empty(), NonTSPseudoClass::MozAny(..) => ElementState::empty(), } } @@ -253,6 +263,7 @@ impl NonTSPseudoClass { $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)* $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)* $(NonTSPseudoClass::$k_name(..) => gecko_type!($k_gecko_type),)* + NonTSPseudoClass::Dir(_) => gecko_type!(dir), NonTSPseudoClass::MozAny(_) => gecko_type!(any), } } @@ -360,6 +371,17 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { .chain(Some(0u16)).collect(); NonTSPseudoClass::$k_name(utf16.into_boxed_slice()) }, )* + "dir" => { + let name: &str = parser.expect_ident()?; + let direction = match_ignore_ascii_case! { name, + "rtl" => Direction::Rtl, + "ltr" => Direction::Ltr, + _ => { + Direction::Other(Box::from(name)) + }, + }; + NonTSPseudoClass::Dir(Box::new(direction)) + }, "-moz-any" => { let selectors = parser.parse_comma_separated(|input| { Selector::parse(self, input) diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index d26a1d8663e..732e50627dd 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -74,7 +74,7 @@ use properties::animated_properties::{AnimationValue, AnimationValueMap}; use properties::animated_properties::TransitionProperty; use properties::style_structs::Font; use rule_tree::CascadeLevel as ServoCascadeLevel; -use selector_parser::{AttrValue, PseudoClassStringArg}; +use selector_parser::{AttrValue, Direction, PseudoClassStringArg}; use selectors::{Element, OpaqueElement}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint}; use selectors::matching::{ElementSelectorFlags, MatchingContext}; @@ -2068,8 +2068,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::Lang(ref lang_arg) => { self.match_element_lang(None, lang_arg) } - NonTSPseudoClass::MozLocaleDir(ref s) | - NonTSPseudoClass::Dir(ref s) => { + NonTSPseudoClass::MozLocaleDir(ref s) => { unsafe { Gecko_MatchStringArgPseudo( self.0, @@ -2078,6 +2077,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { ) } } + NonTSPseudoClass::Dir(ref dir) => { + match **dir { + Direction::Ltr => self.get_state().intersects(ElementState::IN_LTR_STATE), + Direction::Rtl => self.get_state().intersects(ElementState::IN_RTL_STATE), + Direction::Other(..) => false, + } + } } } diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index 8b3fb0385bb..feab46d8f7e 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -182,9 +182,9 @@ impl<'a, E> Element for ElementWrapper<'a, E> // FIXME(bz): How can I set this up so once Servo adds :dir() // support we don't forget to update this code? #[cfg(feature = "gecko")] - NonTSPseudoClass::Dir(ref s) => { + NonTSPseudoClass::Dir(ref dir) => { use invalidation::element::invalidation_map::dir_selector_to_state; - let selector_flag = dir_selector_to_state(s); + let selector_flag = dir_selector_to_state(dir); if selector_flag.is_empty() { // :dir() with some random argument; does not match. return false; diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs index e1eaa2721c3..0ae08aa0bfa 100644 --- a/components/style/invalidation/element/invalidation_map.rs +++ b/components/style/invalidation/element/invalidation_map.rs @@ -10,6 +10,8 @@ use element_state::ElementState; use fallible::FallibleVec; use hashglobe::FailedAllocationError; use selector_map::{MaybeCaseInsensitiveHashMap, SelectorMap, SelectorMapEntry}; +#[cfg(feature = "gecko")] +use selector_parser::Direction; use selector_parser::SelectorImpl; use selectors::attr::NamespaceConstraint; use selectors::parser::{Combinator, Component}; @@ -19,21 +21,15 @@ use smallvec::SmallVec; #[cfg(feature = "gecko")] /// Gets the element state relevant to the given `:dir` pseudo-class selector. -pub fn dir_selector_to_state(s: &[u16]) -> ElementState { - use element_state::ElementState; - - // Jump through some hoops to deal with our Box<[u16]> thing. - const LTR: [u16; 4] = [b'l' as u16, b't' as u16, b'r' as u16, 0]; - const RTL: [u16; 4] = [b'r' as u16, b't' as u16, b'l' as u16, 0]; - - if LTR == *s { - ElementState::IN_LTR_STATE - } else if RTL == *s { - ElementState::IN_RTL_STATE - } else { - // :dir(something-random) is a valid selector, but shouldn't - // match anything. - ElementState::empty() +pub fn dir_selector_to_state(dir: &Direction) -> ElementState { + match *dir { + Direction::Ltr => ElementState::IN_LTR_STATE, + Direction::Rtl => ElementState::IN_RTL_STATE, + Direction::Other(_) => { + // :dir(something-random) is a valid selector, but shouldn't + // match anything. + ElementState::empty() + }, } } @@ -342,8 +338,8 @@ impl SelectorVisitor for CompoundSelectorDependencyCollector { self.other_attributes |= pc.is_attr_based(); self.state |= match *pc { #[cfg(feature = "gecko")] - NonTSPseudoClass::Dir(ref s) => { - dir_selector_to_state(s) + NonTSPseudoClass::Dir(ref dir) => { + dir_selector_to_state(dir) } _ => pc.state_flag(), }; diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs index 62d88f2bf3b..d2f86092c8a 100644 --- a/components/style/selector_parser.rs +++ b/components/style/selector_parser.rs @@ -8,8 +8,8 @@ use cssparser::{Parser as CssParser, ParserInput}; use selectors::parser::SelectorList; -use std::fmt::{self, Debug}; -use style_traits::ParseError; +use std::fmt::{self, Debug, Write}; +use style_traits::{ParseError, ToCss}; use stylesheets::{Origin, Namespaces, UrlExtraData}; /// A convenient alias for the type that represents an attribute value used for @@ -173,3 +173,25 @@ impl PerPseudoElementMap { self.entries.iter() } } + +/// Values for the :dir() pseudo class +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Direction { + /// left-to-right semantic directionality + Ltr, + /// right-to-left semantic directionality + Rtl, + /// Some other provided directionality value + Other(Box), +} + +impl ToCss for Direction { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: Write { + let dir_str = match *self { + Direction::Rtl => "rtl", + Direction::Ltr => "ltr", + Direction::Other(ref other) => other, + }; + dest.write_str(dir_str) + } +}