diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index cf7c3ade7f2..b656fc15e43 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -327,4 +327,7 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex-shrink; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString alignSelf; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString align-self; + + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-name; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationName; }; diff --git a/components/style/animation.rs b/components/style/animation.rs index 4a399a973f5..3cd994af327 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -817,7 +817,7 @@ fn can_interpolate_list(from_list: &[TransformOperation], fn interpolate_transform_list(from_list: &[TransformOperation], to_list: &[TransformOperation], time: f64) -> TransformList { - let mut result = vec!(); + let mut result = vec![]; if can_interpolate_list(from_list, to_list) { for (from, to) in from_list.iter().zip(to_list) { diff --git a/components/style/matching.rs b/components/style/matching.rs index 31d12ce4312..2e4b7922f7e 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -6,8 +6,8 @@ #![allow(unsafe_code)] -use animation::{self, Animation}; -use context::SharedStyleContext; +use animation; +use context::{SharedStyleContext, LocalStyleContext}; use data::PrivateStyleData; use dom::{TElement, TNode, TRestyleDamage}; use properties::{ComputedValues, PropertyDeclaration, cascade}; @@ -21,8 +21,7 @@ use smallvec::SmallVec; use std::collections::HashMap; use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::slice::Iter; -use std::sync::mpsc::Sender; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use string_cache::{Atom, Namespace}; use util::arc_ptr_eq; use util::cache::{LRUCache, SimpleHashCache}; @@ -366,6 +365,10 @@ pub enum StyleSharingResult { trait PrivateMatchMethods: TNode where ::Impl: SelectorImplExt { + /// Actually cascades style for a node or a pseudo-element of a node. + /// + /// Note that animations only apply to nodes or ::before or ::after + /// pseudo-elements. fn cascade_node_pseudo_element(&self, context: &SharedStyleContext<::Impl>, parent_style: Option<&Arc>, @@ -373,13 +376,14 @@ trait PrivateMatchMethods: TNode mut style: Option<&mut Arc>, applicable_declarations_cache: &mut ApplicableDeclarationsCache, - new_animations_sender: &Mutex>, shareable: bool, animate_properties: bool) -> (Self::ConcreteRestyleDamage, Arc) { let mut cacheable = true; + let mut animations = None; if animate_properties { cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable; + animations = Some(context.stylist.animations()) } let mut this_style; @@ -387,14 +391,16 @@ trait PrivateMatchMethods: TNode Some(ref parent_style) => { let cache_entry = applicable_declarations_cache.find(applicable_declarations); let cached_computed_values = match cache_entry { - None => None, Some(ref style) => Some(&**style), + None => None, }; + let (the_style, is_cacheable) = cascade(context.viewport_size, applicable_declarations, shareable, Some(&***parent_style), cached_computed_values, + animations, context.error_reporter.clone()); cacheable = cacheable && is_cacheable; this_style = the_style @@ -405,6 +411,7 @@ trait PrivateMatchMethods: TNode shareable, None, None, + animations, context.error_reporter.clone()); cacheable = cacheable && is_cacheable; this_style = the_style @@ -417,7 +424,7 @@ trait PrivateMatchMethods: TNode if let Some(ref style) = style { let animations_started = animation::start_transitions_if_applicable::( - new_animations_sender, + &context.new_animations_sender, self.opaque(), &**style, &mut this_style); @@ -641,11 +648,9 @@ pub trait MatchMethods : TNode { unsafe fn cascade_node(&self, context: &SharedStyleContext<::Impl>, + local_context: &LocalStyleContext, parent: Option, - applicable_declarations: &ApplicableDeclarations<::Impl>, - applicable_declarations_cache: - &mut ApplicableDeclarationsCache, - new_animations_sender: &Mutex>) + applicable_declarations: &ApplicableDeclarations<::Impl>) where ::Impl: SelectorImplExt { // Get our parent's style. This must be unsafe so that we don't touch the parent's // borrow flags. @@ -653,13 +658,16 @@ pub trait MatchMethods : TNode { // FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow // enforced safe, race-free access to the parent style. let parent_style = match parent { - None => None, Some(parent_node) => { let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap(); Some(parent_style) } + None => None, }; + let mut applicable_declarations_cache = + local_context.applicable_declarations_cache.borrow_mut(); + let damage; if self.is_text_node() { let mut data_ref = self.mutate_data().unwrap(); @@ -677,8 +685,7 @@ pub trait MatchMethods : TNode { parent_style, &applicable_declarations.normal, data.style.as_mut(), - applicable_declarations_cache, - new_animations_sender, + &mut applicable_declarations_cache, applicable_declarations.normal_shareable, true); @@ -690,15 +697,18 @@ pub trait MatchMethods : TNode { if !applicable_declarations_for_this_pseudo.is_empty() { + // NB: Transitions and animations should only work for + // pseudo-elements ::before and ::after + let should_animate_properties = + ::Impl::pseudo_is_before_or_after(&pseudo); let (new_damage, style) = self.cascade_node_pseudo_element( context, Some(data.style.as_ref().unwrap()), &*applicable_declarations_for_this_pseudo, data.per_pseudo.get_mut(&pseudo), - applicable_declarations_cache, - new_animations_sender, + &mut applicable_declarations_cache, false, - false); + should_animate_properties); data.per_pseudo.insert(pseudo, style); damage = damage | new_damage; diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index c1eba31c3a3..4d50e99594f 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -795,6 +795,77 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone= pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one}; +<%helpers:longhand name="animation-name" experimental="True"> + use cssparser::ToCss; + use std::borrow::Cow; + use std::fmt; + + pub mod computed_value { + use cssparser::ToCss; + use std::fmt; + + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct T(pub Vec); + + impl ToCss for T { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + for (i, name) in self.0.iter().enumerate() { + if i != 0 { + try!(dest.write_str(", ")); + } + try!(dest.write_str(&name)); + } + Ok(()) + } + } + } + + // TODO: Use Cows? Probably more codegen work would be needed, and this + // could not be that worth it (animations arent *that* used). + #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + pub struct SpecifiedValue(Vec); + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + for (i, name) in self.0.iter().enumerate() { + if i != 0 { + try!(dest.write_str(", ")); + } + try!(dest.write_str(&name)); + } + Ok(()) + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T(vec![]) + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { + Ok(SpecifiedValue(try!(input.parse_comma_separated(|input| { + input.expect_ident().map(Cow::into_owned) + })))) + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Cx) -> computed_value::T { + let mut ret = vec![]; + if let Some(animations) = context.animations() { + for name in self.0.iter() { + if animations.contains_key(&**name) { + ret.push(name.clone()); + } + } + } + computed_value::T(ret) + } + } + + // CSSOM View Module // https://www.w3.org/TR/cssom-view-1/ ${helpers.single_keyword("scroll-behavior", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 2e5ab399482..3df108e0e6a 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -12,7 +12,7 @@ use std::ascii::AsciiExt; use std::boxed::Box as StdBox; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::fmt; use std::fmt::Write; use std::sync::Arc; @@ -22,6 +22,7 @@ use cssparser::Color as CSSParserColor; use cssparser::{Parser, RGBA, AtRuleParser, DeclarationParser, Delimiter, DeclarationListParser, parse_important, ToCss, TokenSerializationType}; use error_reporting::ParseErrorReporter; +use keyframes::Keyframe; use url::Url; use euclid::side_offsets::SideOffsets2D; use euclid::size::Size2D; @@ -1594,12 +1595,14 @@ fn cascade_with_cached_declarations( parent_style: &C, cached_style: &C, custom_properties: Option>, + animations: Option<<&HashMap>>, mut error_reporter: StdBox) -> C { let mut context = computed::Context { is_root_element: false, viewport_size: viewport_size, inherited_style: parent_style, + animations: animations, style: C::new( custom_properties, shareable, @@ -1739,6 +1742,7 @@ pub fn cascade( shareable: bool, parent_style: Option<<&C>, cached_style: Option<<&C>, + animations: Option<<&HashMap>>, mut error_reporter: StdBox) -> (C, bool) { use properties::style_struct_traits::{Border, Box, Font, Outline}; @@ -1774,6 +1778,7 @@ pub fn cascade( parent_style, cached_style, custom_properties, + animations, error_reporter); return (style, false) } @@ -1782,6 +1787,7 @@ pub fn cascade( is_root_element: is_root_element, viewport_size: viewport_size, inherited_style: inherited_style, + animations: animations, style: C::new( custom_properties, shareable, diff --git a/components/style/selector_impl.rs b/components/style/selector_impl.rs index 29e3fa36927..a394ef987cc 100644 --- a/components/style/selector_impl.rs +++ b/components/style/selector_impl.rs @@ -90,6 +90,7 @@ pub trait SelectorImplExt : SelectorImpl + Sized { }) } + fn pseudo_is_before_or_after(pseudo: &Self::PseudoElement) -> bool; fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState; @@ -109,6 +110,15 @@ pub enum PseudoElement { } impl PseudoElement { + #[inline] + pub fn is_before_or_after(&self) -> bool { + match *self { + PseudoElement::Before | + PseudoElement::After => true, + _ => false, + } + } + #[inline] pub fn cascade_type(&self) -> PseudoElementCascadeType { match *self { @@ -249,6 +259,11 @@ impl SelectorImplExt for ServoSelectorImpl { pc.state_flag() } + #[inline] + fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool { + pseudo.is_before_or_after() + } + #[inline] fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet] { &*USER_OR_USER_AGENT_STYLESHEETS diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 64bf1b38863..a3fe67b1b75 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -128,9 +128,7 @@ pub struct Stylist { BuildHasherDefault<::fnv::FnvHasher>>, /// A map with all the animations indexed by name. - animations: HashMap, - BuildHasherDefault<::fnv::FnvHasher>>, + animations: HashMap>, /// Applicable declarations for a given non-eagerly cascaded pseudo-element. /// These are eagerly computed once, and then used to resolve the new @@ -290,7 +288,8 @@ impl Stylist { let (computed, _) = properties::cascade(self.device.au_viewport_size(), &declarations, false, - parent.map(|p| &**p), None, + parent.map(|p| &**p), + None, None, Box::new(StdoutErrorReporter)); Some(Arc::new(computed)) } else { @@ -323,7 +322,7 @@ impl Stylist { let (computed, _) = properties::cascade(self.device.au_viewport_size(), &declarations, false, - Some(&**parent), None, + Some(&**parent), None, None, Box::new(StdoutErrorReporter)); Some(Arc::new(computed)) } @@ -457,6 +456,11 @@ impl Stylist { pub fn is_device_dirty(&self) -> bool { self.is_device_dirty } + + #[inline] + pub fn animations(&self) -> &HashMap> { + &self.animations + } } /// Map that contains the CSS rules for a given origin. diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 09be8f542dd..c4a2ca24c3b 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -278,7 +278,6 @@ pub mod rule_filter { impl<'a, I, Impl: SelectorImpl + 'a> $variant<'a, I> where I: Iterator> { - #[inline] pub fn new(iter: I) -> $variant<'a, I> { $variant { diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 281806e2263..5e5b7d31948 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -219,10 +219,9 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C, // Perform the CSS cascade. unsafe { node.cascade_node(&context.shared_context(), + &context.local_context(), parent_opt, - &applicable_declarations, - &mut context.local_context().applicable_declarations_cache.borrow_mut(), - &context.shared_context().new_animations_sender); + &applicable_declarations); } // Add ourselves to the LRU cache. diff --git a/components/style/values.rs b/components/style/values.rs index f515a403ab4..0a62485a185 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -1561,8 +1561,10 @@ pub mod specified { pub mod computed { use app_units::Au; use euclid::size::Size2D; + use keyframes::Keyframe; use properties::ComputedValues; use properties::style_struct_traits::Font; + use std::collections::HashMap; use std::fmt; use super::LocalToCss; use super::specified::AngleOrCorner; @@ -1578,6 +1580,7 @@ pub mod computed { fn inherited_style(&self) -> &Self::ConcreteComputedValues; fn style(&self) -> &Self::ConcreteComputedValues; fn mutate_style(&mut self) -> &mut Self::ConcreteComputedValues; + fn animations(&self) -> Option<&HashMap>>; } pub struct Context<'a, C: ComputedValues> { @@ -1585,6 +1588,7 @@ pub mod computed { pub viewport_size: Size2D, pub inherited_style: &'a C, + pub animations: Option<&'a HashMap>>, /// Values access through this need to be in the properties "computed early": /// color, text-decoration, font-size, display, position, float, border-*-style, outline-style pub style: C, @@ -1597,6 +1601,7 @@ pub mod computed { fn inherited_style(&self) -> &C { &self.inherited_style } fn style(&self) -> &C { &self.style } fn mutate_style(&mut self) -> &mut C { &mut self.style } + fn animations(&self) -> Option<&HashMap>> { self.animations } } pub trait ToComputedValue { diff --git a/components/style/viewport.rs b/components/style/viewport.rs index f763600d399..1707721a362 100644 --- a/components/style/viewport.rs +++ b/components/style/viewport.rs @@ -562,8 +562,8 @@ pub trait MaybeNew { impl MaybeNew for ViewportConstraints { fn maybe_new(initial_viewport: TypedSize2D, - rule: &ViewportRule) - -> Option + rule: &ViewportRule) + -> Option { use std::cmp; @@ -648,6 +648,7 @@ impl MaybeNew for ViewportConstraints { viewport_size: initial_viewport, inherited_style: ServoComputedValues::initial_values(), style: ServoComputedValues::initial_values().clone(), + animations: None, }; // DEVICE-ADAPT ยง 9.3 Resolving 'extend-to-zoom' diff --git a/ports/geckolib/selector_impl.rs b/ports/geckolib/selector_impl.rs index 1c315df24c1..dda81ecec06 100644 --- a/ports/geckolib/selector_impl.rs +++ b/ports/geckolib/selector_impl.rs @@ -379,6 +379,15 @@ impl SelectorImplExt for GeckoSelectorImpl { fun(AnonBox(MozSVGText)); } + #[inline] + fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool { + match *pseudo { + PseudoElement::Before | + PseudoElement::After => true, + _ => false, + } + } + #[inline] fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState { pc.state_flag()