diff --git a/servo/components/style/data.rs b/servo/components/style/data.rs index 2cd2d84e67b4..56f2de5beb69 100644 --- a/servo/components/style/data.rs +++ b/servo/components/style/data.rs @@ -246,8 +246,8 @@ impl ElementData { return InvalidationResult::empty(); } - use invalidation::element::collector::StateAndAttrInvalidationProcessor; use invalidation::element::invalidator::TreeStyleInvalidator; + use invalidation::element::state_and_attributes::StateAndAttrInvalidationProcessor; debug!("invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \ handled_snapshot: {}, pseudo: {:?}", diff --git a/servo/components/style/gecko/non_ts_pseudo_class_list.rs b/servo/components/style/gecko/non_ts_pseudo_class_list.rs index b5c2c4cd5ba8..0a5fe00be025 100644 --- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs +++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs @@ -116,9 +116,6 @@ macro_rules! apply_non_ts_list { ], string: [ ("lang", Lang, lang, _, _), - ], - keyword: [ - ("-moz-locale-dir", MozLocaleDir, mozLocaleDir, _, _), ] } } diff --git a/servo/components/style/gecko/selector_parser.rs b/servo/components/style/gecko/selector_parser.rs index 72da9346a363..b70054fe84a8 100644 --- a/servo/components/style/gecko/selector_parser.rs +++ b/servo/components/style/gecko/selector_parser.rs @@ -36,9 +36,8 @@ pub type PseudoClassStringArg = Box<[u16]>; macro_rules! pseudo_class_name { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], - keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { - #[doc = "Our representation of a non tree-structural pseudo-class."] + string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { + /// Our representation of a non tree-structural pseudo-class. #[derive(Clone, Debug, Eq, PartialEq)] pub enum NonTSPseudoClass { $( @@ -49,10 +48,6 @@ macro_rules! pseudo_class_name { #[doc = $s_css] $s_name(PseudoClassStringArg), )* - $( - #[doc = $k_css] - $k_name(Box<[u16]>), - )* /// The `:dir` pseudo-class. Dir(Box), /// The non-standard `:-moz-any` pseudo-class. @@ -60,6 +55,8 @@ macro_rules! pseudo_class_name { /// TODO(emilio): We disallow combinators and pseudos here, so we /// should use SimpleSelector instead MozAny(Box<[Selector]>), + /// The non-standard `:-moz-locale-dir` pseudo-class. + MozLocaleDir(Box), } } } @@ -71,8 +68,7 @@ impl ToCss for NonTSPseudoClass { use fmt::Write; macro_rules! pseudo_class_serialize { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], - keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { + string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => concat!(":", $css),)* $(NonTSPseudoClass::$s_name(ref s) => { @@ -87,16 +83,14 @@ impl ToCss for NonTSPseudoClass { } return dest.write_str(")") }, )* - $(NonTSPseudoClass::$k_name(ref s) => { - // Don't include the terminating nul. - let value = String::from_utf16(&s[..s.len() - 1]).unwrap(); - dest.write_str(concat!(":", $k_css, "("))?; - dest.write_str(&value)?; + NonTSPseudoClass::MozLocaleDir(ref dir) => { + dest.write_str(":-moz-locale-dir(")?; + dir.to_css(dest)?; return dest.write_char(')') - }, )* + }, NonTSPseudoClass::Dir(ref dir) => { dest.write_str(":dir(")?; - (**dir).to_css(dest)?; + dir.to_css(dest)?; return dest.write_char(')') }, NonTSPseudoClass::MozAny(ref selectors) => { @@ -146,13 +140,14 @@ impl NonTSPseudoClass { } macro_rules! pseudo_class_check_is_enabled_in { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], - keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { + string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => check_flag!($flags),)* $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)* - $(NonTSPseudoClass::$k_name(..) => check_flag!($k_flags),)* - NonTSPseudoClass::Dir(_) => false, + // TODO(emilio): Maybe -moz-locale-dir shouldn't be + // content-exposed. + NonTSPseudoClass::MozLocaleDir(_) | + NonTSPseudoClass::Dir(_) | NonTSPseudoClass::MozAny(_) => false, } } @@ -191,13 +186,12 @@ impl NonTSPseudoClass { } macro_rules! pseudo_class_state { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], - keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { + string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => flag!($state),)* $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)* - $(NonTSPseudoClass::$k_name(..) => flag!($k_state),)* - NonTSPseudoClass::Dir(..) => ElementState::empty(), + NonTSPseudoClass::Dir(..) | + NonTSPseudoClass::MozLocaleDir(..) | NonTSPseudoClass::MozAny(..) => ElementState::empty(), } } @@ -256,12 +250,11 @@ impl NonTSPseudoClass { } macro_rules! pseudo_class_geckotype { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], - keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { + string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)* $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)* - $(NonTSPseudoClass::$k_name(..) => gecko_type!($k_gecko_type),)* + NonTSPseudoClass::MozLocaleDir(_) => gecko_type!(mozLocaleDir), NonTSPseudoClass::Dir(_) => gecko_type!(dir), NonTSPseudoClass::MozAny(_) => gecko_type!(any), } @@ -353,8 +346,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { ) -> Result> { macro_rules! pseudo_class_parse { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], - keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { + string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { match_ignore_ascii_case! { &name, $($css => NonTSPseudoClass::$name,)* _ => return Err(location.new_custom_error( @@ -378,8 +370,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { ) -> Result> { macro_rules! pseudo_class_string_parse { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*], - keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { + string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { match_ignore_ascii_case! { &name, $($s_css => { let name = parser.expect_ident_or_string()?; @@ -388,23 +379,15 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { let utf16: Vec = name.encode_utf16().chain(Some(0u16)).collect(); NonTSPseudoClass::$s_name(utf16.into_boxed_slice()) }, )* - $($k_css => { - let name = parser.expect_ident()?; - // Convert to ASCII-lowercase nul-terminated UTF-16 string. - let utf16: Vec = name.encode_utf16().map(utf16_to_ascii_lowercase) - .chain(Some(0u16)).collect(); - NonTSPseudoClass::$k_name(utf16.into_boxed_slice()) - }, )* + "-moz-locale-dir" => { + NonTSPseudoClass::MozLocaleDir( + Box::new(Direction::parse(parser)?) + ) + }, "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)) + NonTSPseudoClass::Dir( + Box::new(Direction::parse(parser)?) + ) }, "-moz-any" => { NonTSPseudoClass::MozAny( @@ -496,13 +479,6 @@ impl SelectorImpl { } } -fn utf16_to_ascii_lowercase(unit: u16) -> u16 { - match unit { - 65...90 => unit + 32, // A-Z => a-z - _ => unit - } -} - unsafe impl HasFFI for SelectorList { type FFIType = RawServoSelectorList; } diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs index 687b95fde829..56ad17e6009e 100644 --- a/servo/components/style/gecko/wrapper.rs +++ b/servo/components/style/gecko/wrapper.rs @@ -46,7 +46,6 @@ use gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_IsSignificantChild; use gecko_bindings::bindings::Gecko_MatchLang; -use gecko_bindings::bindings::Gecko_MatchStringArgPseudo; use gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr; use gecko_bindings::bindings::Gecko_UpdateAnimations; use gecko_bindings::structs; @@ -2111,7 +2110,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { self.get_document_theme() == DocumentTheme::Doc_Theme_Dark } NonTSPseudoClass::MozWindowInactive => { - self.document_state().contains(DocumentState::NS_DOCUMENT_STATE_WINDOW_INACTIVE) + self.document_state() + .contains(DocumentState::NS_DOCUMENT_STATE_WINDOW_INACTIVE) } NonTSPseudoClass::MozPlaceholder => false, NonTSPseudoClass::MozAny(ref sels) => { @@ -2125,13 +2125,15 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::Lang(ref lang_arg) => { self.match_element_lang(None, lang_arg) } - NonTSPseudoClass::MozLocaleDir(ref s) => { - unsafe { - Gecko_MatchStringArgPseudo( - self.0, - pseudo_class.to_gecko_pseudoclasstype().unwrap(), - s.as_ptr(), - ) + NonTSPseudoClass::MozLocaleDir(ref dir) => { + let doc_is_rtl = + self.document_state() + .contains(DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE); + + match **dir { + Direction::Ltr => !doc_is_rtl, + Direction::Rtl => doc_is_rtl, + Direction::Other(..) => false, } } NonTSPseudoClass::Dir(ref dir) => { diff --git a/servo/components/style/invalidation/element/mod.rs b/servo/components/style/invalidation/element/mod.rs index 3947a2d057c3..52e4bd388440 100644 --- a/servo/components/style/invalidation/element/mod.rs +++ b/servo/components/style/invalidation/element/mod.rs @@ -4,8 +4,8 @@ //! Invalidation of element styles due to attribute or style changes. -pub mod collector; pub mod element_wrapper; pub mod invalidation_map; pub mod invalidator; pub mod restyle_hints; +pub mod state_and_attributes; diff --git a/servo/components/style/invalidation/element/collector.rs b/servo/components/style/invalidation/element/state_and_attributes.rs similarity index 92% rename from servo/components/style/invalidation/element/collector.rs rename to servo/components/style/invalidation/element/state_and_attributes.rs index b19198b2120e..37d6fc675b2e 100644 --- a/servo/components/style/invalidation/element/collector.rs +++ b/servo/components/style/invalidation/element/state_and_attributes.rs @@ -93,6 +93,50 @@ impl<'a, 'b: 'a, E: TElement> StateAndAttrInvalidationProcessor<'a, 'b, E> { } } + +/// Whether we should process the descendants of a given element for style +/// invalidation. +pub fn should_process_descendants(data: &ElementData) -> bool { + !data.styles.is_display_none() && + !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS) +} + +/// Propagates the bits after invalidating a descendant child. +pub fn invalidated_descendants(element: E, child: E) +where + E: TElement, +{ + if child.get_data().is_none() { + return; + } + + // The child may not be a flattened tree child of the current element, + // but may be arbitrarily deep. + // + // Since we keep the traversal flags in terms of the flattened tree, + // we need to propagate it as appropriate. + let mut current = child.traversal_parent(); + while let Some(parent) = current.take() { + unsafe { parent.set_dirty_descendants() }; + current = parent.traversal_parent(); + + if parent == element { + break; + } + } +} + +/// Sets the appropriate restyle hint after invalidating the style of a given +/// element. +pub fn invalidated_self(element: E) +where + E: TElement, +{ + if let Some(mut data) = element.mutate_data() { + data.hint.insert(RestyleHint::RESTYLE_SELF); + } +} + impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, E> for StateAndAttrInvalidationProcessor<'a, 'b, E> where E: TElement, @@ -251,17 +295,15 @@ where fn should_process_descendants(&mut self, element: E) -> bool { if element == self.element { - return !self.data.styles.is_display_none() && - !self.data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS) + return should_process_descendants(&self.data) } let data = match element.borrow_data() { + Some(d) => d, None => return false, - Some(data) => data, }; - !data.styles.is_display_none() && - !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS) + should_process_descendants(&data) } fn recursion_limit_exceeded(&mut self, element: E) { @@ -276,31 +318,12 @@ where } fn invalidated_descendants(&mut self, element: E, child: E) { - if child.get_data().is_none() { - return; - } - - // The child may not be a flattened tree child of the current element, - // but may be arbitrarily deep. - // - // Since we keep the traversal flags in terms of the flattened tree, - // we need to propagate it as appropriate. - let mut current = child.traversal_parent(); - while let Some(parent) = current.take() { - unsafe { parent.set_dirty_descendants() }; - current = parent.traversal_parent(); - - if parent == element { - break; - } - } + invalidated_descendants(element, child) } fn invalidated_self(&mut self, element: E) { debug_assert_ne!(element, self.element); - if let Some(mut data) = element.mutate_data() { - data.hint.insert(RestyleHint::RESTYLE_SELF); - } + invalidated_self(element); } } diff --git a/servo/components/style/selector_parser.rs b/servo/components/style/selector_parser.rs index b4870557cc2a..54978d284b72 100644 --- a/servo/components/style/selector_parser.rs +++ b/servo/components/style/selector_parser.rs @@ -183,9 +183,23 @@ pub enum Direction { /// right-to-left semantic directionality 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())), + }) + } +} + impl ToCss for Direction { fn to_css(&self, dest: &mut W) -> fmt::Result where W: Write { let dir_str = match *self {