diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index cc84f58aa13..e9652b8386f 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -54,14 +54,14 @@ macro_rules! pseudo_class_name { $s_name(PseudoClassStringArg), )* /// The `:dir` pseudo-class. - Dir(Box), + Dir(Direction), /// The non-standard `:-moz-any` pseudo-class. /// /// TODO(emilio): We disallow combinators and pseudos here, so we /// should use SimpleSelector instead MozAny(ThinBoxedSlice>), /// The non-standard `:-moz-locale-dir` pseudo-class. - MozLocaleDir(Box), + MozLocaleDir(Direction), } } } @@ -411,14 +411,10 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { NonTSPseudoClass::$s_name(utf16.into_boxed_slice().into()) }, )* "-moz-locale-dir" => { - NonTSPseudoClass::MozLocaleDir( - Box::new(Direction::parse(parser)?) - ) + NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?) }, "dir" => { - NonTSPseudoClass::Dir( - Box::new(Direction::parse(parser)?) - ) + NonTSPseudoClass::Dir(Direction::parse(parser)?) }, "-moz-any" => { NonTSPseudoClass::MozAny( diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 00b75b83512..914bdad852b 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -69,7 +69,7 @@ use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; use properties::animated_properties::{AnimationValue, AnimationValueMap}; use properties::style_structs::Font; use rule_tree::CascadeLevel as ServoCascadeLevel; -use selector_parser::{AttrValue, Direction, PseudoClassStringArg}; +use selector_parser::{AttrValue, HorizontalDirection, PseudoClassStringArg}; use selectors::{Element, OpaqueElement}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator}; use selectors::attr::{CaseSensitivity, NamespaceConstraint}; @@ -2237,24 +2237,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::MozLocaleDir(ref dir) => { let state_bit = DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE; if context.extra_data.document_state.intersects(state_bit) { - // NOTE(emilio): We could still return false for - // Direction::Other(..), but we don't bother. + // NOTE(emilio): We could still return false for values + // other than "ltr" and "rtl", but we don't bother. return !context.in_negation(); } let doc_is_rtl = self.document_state().contains(state_bit); - match **dir { - Direction::Ltr => !doc_is_rtl, - Direction::Rtl => doc_is_rtl, - Direction::Other(..) => false, + match dir.as_horizontal_direction() { + Some(HorizontalDirection::Ltr) => !doc_is_rtl, + Some(HorizontalDirection::Rtl) => doc_is_rtl, + None => false, } }, - NonTSPseudoClass::Dir(ref dir) => match **dir { - Direction::Ltr => self.state().intersects(ElementState::IN_LTR_STATE), - Direction::Rtl => self.state().intersects(ElementState::IN_RTL_STATE), - Direction::Other(..) => false, - }, + NonTSPseudoClass::Dir(ref dir) => { + self.state().intersects(dir.element_state()) + } } } diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index cb4f79e92e1..e788d2dd876 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -188,8 +188,7 @@ where // support we don't forget to update this code? #[cfg(feature = "gecko")] NonTSPseudoClass::Dir(ref dir) => { - use invalidation::element::invalidation_map::dir_selector_to_state; - let selector_flag = dir_selector_to_state(dir); + let selector_flag = dir.element_state(); 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 67849d89627..26a4609760f 100644 --- a/components/style/invalidation/element/invalidation_map.rs +++ b/components/style/invalidation/element/invalidation_map.rs @@ -10,8 +10,6 @@ use element_state::{DocumentState, 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,20 +17,6 @@ use selectors::parser::{Selector, SelectorIter, Visit}; use selectors::visitor::SelectorVisitor; use smallvec::SmallVec; -#[cfg(feature = "gecko")] -/// Gets the element state relevant to the given `:dir` pseudo-class selector. -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() - }, - } -} - /// Mapping between (partial) CompoundSelectors (and the combinator to their /// right) and the states and attributes they depend on. /// @@ -382,7 +366,7 @@ impl<'a> SelectorVisitor for CompoundSelectorDependencyCollector<'a> { self.other_attributes |= pc.is_attr_based(); self.state |= match *pc { #[cfg(feature = "gecko")] - NonTSPseudoClass::Dir(ref dir) => dir_selector_to_state(dir), + NonTSPseudoClass::Dir(ref dir) => dir.element_state(), _ => pc.state_flag(), }; *self.document_state |= pc.document_state_flag(); diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs index 7250502b1ad..14f09f50d34 100644 --- a/components/style/selector_parser.rs +++ b/components/style/selector_parser.rs @@ -7,10 +7,13 @@ #![deny(missing_docs)] use cssparser::{Parser as CssParser, ParserInput}; +use element_state::ElementState; use selectors::parser::SelectorList; use std::fmt::{self, Debug, Write}; +use string_cache::Atom; use style_traits::{CssWriter, ParseError, ToCss}; use stylesheets::{Namespaces, Origin, UrlExtraData}; +use values::serialize_atom_identifier; /// A convenient alias for the type that represents an attribute value used for /// selector parser implementation. @@ -172,27 +175,49 @@ impl PerPseudoElementMap { } /// Values for the :dir() pseudo class +/// +/// "ltr" and "rtl" values are normalized to lowercase. #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] -pub enum Direction { - /// left-to-right semantic directionality +pub struct Direction(pub Atom); + +/// Horizontal values for the :dir() pseudo class +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum HorizontalDirection { + /// :dir(ltr) Ltr, - /// right-to-left semantic directionality + /// :dir(rtl) Rtl, - /// Some other provided directionality value - /// - /// TODO(emilio): If we atomize we can then unbox in NonTSPseudoClass. - Other(Box), } impl Direction { /// Parse a direction value. pub fn parse<'i, 't>(parser: &mut CssParser<'i, 't>) -> Result> { let ident = parser.expect_ident()?; - Ok(match_ignore_ascii_case! { &ident, - "rtl" => Direction::Rtl, - "ltr" => Direction::Ltr, - _ => Direction::Other(Box::from(ident.as_ref())), - }) + Ok(Direction(match_ignore_ascii_case! { &ident, + "rtl" => atom!("rtl"), + "ltr" => atom!("ltr"), + _ => Atom::from(ident.as_ref()), + })) + } + + /// Convert this Direction into a HorizontalDirection, if applicable + pub fn as_horizontal_direction(&self) -> Option { + if self.0 == atom!("ltr") { + Some(HorizontalDirection::Ltr) + } else if self.0 == atom!("rtl") { + Some(HorizontalDirection::Rtl) + } else { + None + } + } + + /// Gets the element state relevant to this :dir() selector. + pub fn element_state(&self) -> ElementState { + match self.as_horizontal_direction() { + Some(HorizontalDirection::Ltr) => ElementState::IN_LTR_STATE, + Some(HorizontalDirection::Rtl) => ElementState::IN_RTL_STATE, + None => ElementState::empty(), + } } } @@ -201,12 +226,6 @@ impl ToCss for Direction { where W: Write, { - let dir_str = match *self { - Direction::Rtl => "rtl", - Direction::Ltr => "ltr", - // FIXME: This should be escaped as an identifier; see #19231 - Direction::Other(ref other) => other, - }; - dest.write_str(dir_str) + serialize_atom_identifier(&self.0, dest) } }