style: Refactor to pass animations cleanly, land animation-name parsing as experimental

This commit is contained in:
Emilio Cobos Álvarez 2016-06-17 03:51:57 +02:00
parent c1fd7432e9
commit 60192bb830
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
12 changed files with 152 additions and 30 deletions

View file

@ -327,4 +327,7 @@ partial interface CSSStyleDeclaration {
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex-shrink; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex-shrink;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString alignSelf; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString alignSelf;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString align-self; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString align-self;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-name;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationName;
}; };

View file

@ -817,7 +817,7 @@ fn can_interpolate_list(from_list: &[TransformOperation],
fn interpolate_transform_list(from_list: &[TransformOperation], fn interpolate_transform_list(from_list: &[TransformOperation],
to_list: &[TransformOperation], to_list: &[TransformOperation],
time: f64) -> TransformList { time: f64) -> TransformList {
let mut result = vec!(); let mut result = vec![];
if can_interpolate_list(from_list, to_list) { if can_interpolate_list(from_list, to_list) {
for (from, to) in from_list.iter().zip(to_list) { for (from, to) in from_list.iter().zip(to_list) {

View file

@ -6,8 +6,8 @@
#![allow(unsafe_code)] #![allow(unsafe_code)]
use animation::{self, Animation}; use animation;
use context::SharedStyleContext; use context::{SharedStyleContext, LocalStyleContext};
use data::PrivateStyleData; use data::PrivateStyleData;
use dom::{TElement, TNode, TRestyleDamage}; use dom::{TElement, TNode, TRestyleDamage};
use properties::{ComputedValues, PropertyDeclaration, cascade}; use properties::{ComputedValues, PropertyDeclaration, cascade};
@ -21,8 +21,7 @@ use smallvec::SmallVec;
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::slice::Iter; use std::slice::Iter;
use std::sync::mpsc::Sender; use std::sync::Arc;
use std::sync::{Arc, Mutex};
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
use util::arc_ptr_eq; use util::arc_ptr_eq;
use util::cache::{LRUCache, SimpleHashCache}; use util::cache::{LRUCache, SimpleHashCache};
@ -366,6 +365,10 @@ pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
trait PrivateMatchMethods: TNode trait PrivateMatchMethods: TNode
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt { where <Self::ConcreteElement as Element>::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, fn cascade_node_pseudo_element(&self,
context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>, context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>,
parent_style: Option<&Arc<Self::ConcreteComputedValues>>, parent_style: Option<&Arc<Self::ConcreteComputedValues>>,
@ -373,13 +376,14 @@ trait PrivateMatchMethods: TNode
mut style: Option<&mut Arc<Self::ConcreteComputedValues>>, mut style: Option<&mut Arc<Self::ConcreteComputedValues>>,
applicable_declarations_cache: applicable_declarations_cache:
&mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>, &mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>,
new_animations_sender: &Mutex<Sender<Animation>>,
shareable: bool, shareable: bool,
animate_properties: bool) animate_properties: bool)
-> (Self::ConcreteRestyleDamage, Arc<Self::ConcreteComputedValues>) { -> (Self::ConcreteRestyleDamage, Arc<Self::ConcreteComputedValues>) {
let mut cacheable = true; let mut cacheable = true;
let mut animations = None;
if animate_properties { if animate_properties {
cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable; cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable;
animations = Some(context.stylist.animations())
} }
let mut this_style; let mut this_style;
@ -387,14 +391,16 @@ trait PrivateMatchMethods: TNode
Some(ref parent_style) => { Some(ref parent_style) => {
let cache_entry = applicable_declarations_cache.find(applicable_declarations); let cache_entry = applicable_declarations_cache.find(applicable_declarations);
let cached_computed_values = match cache_entry { let cached_computed_values = match cache_entry {
None => None,
Some(ref style) => Some(&**style), Some(ref style) => Some(&**style),
None => None,
}; };
let (the_style, is_cacheable) = cascade(context.viewport_size, let (the_style, is_cacheable) = cascade(context.viewport_size,
applicable_declarations, applicable_declarations,
shareable, shareable,
Some(&***parent_style), Some(&***parent_style),
cached_computed_values, cached_computed_values,
animations,
context.error_reporter.clone()); context.error_reporter.clone());
cacheable = cacheable && is_cacheable; cacheable = cacheable && is_cacheable;
this_style = the_style this_style = the_style
@ -405,6 +411,7 @@ trait PrivateMatchMethods: TNode
shareable, shareable,
None, None,
None, None,
animations,
context.error_reporter.clone()); context.error_reporter.clone());
cacheable = cacheable && is_cacheable; cacheable = cacheable && is_cacheable;
this_style = the_style this_style = the_style
@ -417,7 +424,7 @@ trait PrivateMatchMethods: TNode
if let Some(ref style) = style { if let Some(ref style) = style {
let animations_started = let animations_started =
animation::start_transitions_if_applicable::<Self::ConcreteComputedValues>( animation::start_transitions_if_applicable::<Self::ConcreteComputedValues>(
new_animations_sender, &context.new_animations_sender,
self.opaque(), self.opaque(),
&**style, &**style,
&mut this_style); &mut this_style);
@ -641,11 +648,9 @@ pub trait MatchMethods : TNode {
unsafe fn cascade_node(&self, unsafe fn cascade_node(&self,
context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>, context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>,
local_context: &LocalStyleContext<Self::ConcreteComputedValues>,
parent: Option<Self>, parent: Option<Self>,
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>, applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>)
applicable_declarations_cache:
&mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>,
new_animations_sender: &Mutex<Sender<Animation>>)
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt { where <Self::ConcreteElement as Element>::Impl: SelectorImplExt {
// Get our parent's style. This must be unsafe so that we don't touch the parent's // Get our parent's style. This must be unsafe so that we don't touch the parent's
// borrow flags. // borrow flags.
@ -653,13 +658,16 @@ pub trait MatchMethods : TNode {
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow // FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
// enforced safe, race-free access to the parent style. // enforced safe, race-free access to the parent style.
let parent_style = match parent { let parent_style = match parent {
None => None,
Some(parent_node) => { Some(parent_node) => {
let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap(); let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap();
Some(parent_style) Some(parent_style)
} }
None => None,
}; };
let mut applicable_declarations_cache =
local_context.applicable_declarations_cache.borrow_mut();
let damage; let damage;
if self.is_text_node() { if self.is_text_node() {
let mut data_ref = self.mutate_data().unwrap(); let mut data_ref = self.mutate_data().unwrap();
@ -677,8 +685,7 @@ pub trait MatchMethods : TNode {
parent_style, parent_style,
&applicable_declarations.normal, &applicable_declarations.normal,
data.style.as_mut(), data.style.as_mut(),
applicable_declarations_cache, &mut applicable_declarations_cache,
new_animations_sender,
applicable_declarations.normal_shareable, applicable_declarations.normal_shareable,
true); true);
@ -690,15 +697,18 @@ pub trait MatchMethods : TNode {
if !applicable_declarations_for_this_pseudo.is_empty() { 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 =
<Self::ConcreteElement as Element>::Impl::pseudo_is_before_or_after(&pseudo);
let (new_damage, style) = self.cascade_node_pseudo_element( let (new_damage, style) = self.cascade_node_pseudo_element(
context, context,
Some(data.style.as_ref().unwrap()), Some(data.style.as_ref().unwrap()),
&*applicable_declarations_for_this_pseudo, &*applicable_declarations_for_this_pseudo,
data.per_pseudo.get_mut(&pseudo), data.per_pseudo.get_mut(&pseudo),
applicable_declarations_cache, &mut applicable_declarations_cache,
new_animations_sender,
false, false,
false); should_animate_properties);
data.per_pseudo.insert(pseudo, style); data.per_pseudo.insert(pseudo, style);
damage = damage | new_damage; damage = damage | new_damage;

View file

@ -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}; pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
</%helpers:longhand> </%helpers:longhand>
<%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<String>);
impl ToCss for T {
fn to_css<W>(&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<String>);
impl ToCss for SpecifiedValue {
fn to_css<W>(&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<SpecifiedValue,()> {
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<Cx: TContext>(&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)
}
}
</%helpers:longhand>
// CSSOM View Module // CSSOM View Module
// https://www.w3.org/TR/cssom-view-1/ // https://www.w3.org/TR/cssom-view-1/
${helpers.single_keyword("scroll-behavior", ${helpers.single_keyword("scroll-behavior",

View file

@ -12,7 +12,7 @@
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::boxed::Box as StdBox; use std::boxed::Box as StdBox;
use std::collections::HashSet; use std::collections::{HashMap, HashSet};
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
use std::sync::Arc; use std::sync::Arc;
@ -22,6 +22,7 @@ use cssparser::Color as CSSParserColor;
use cssparser::{Parser, RGBA, AtRuleParser, DeclarationParser, Delimiter, use cssparser::{Parser, RGBA, AtRuleParser, DeclarationParser, Delimiter,
DeclarationListParser, parse_important, ToCss, TokenSerializationType}; DeclarationListParser, parse_important, ToCss, TokenSerializationType};
use error_reporting::ParseErrorReporter; use error_reporting::ParseErrorReporter;
use keyframes::Keyframe;
use url::Url; use url::Url;
use euclid::side_offsets::SideOffsets2D; use euclid::side_offsets::SideOffsets2D;
use euclid::size::Size2D; use euclid::size::Size2D;
@ -1594,12 +1595,14 @@ fn cascade_with_cached_declarations<C: ComputedValues>(
parent_style: &C, parent_style: &C,
cached_style: &C, cached_style: &C,
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>, custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
animations: Option<<&HashMap<String, Vec<Keyframe>>>,
mut error_reporter: StdBox<ParseErrorReporter + Send>) mut error_reporter: StdBox<ParseErrorReporter + Send>)
-> C { -> C {
let mut context = computed::Context { let mut context = computed::Context {
is_root_element: false, is_root_element: false,
viewport_size: viewport_size, viewport_size: viewport_size,
inherited_style: parent_style, inherited_style: parent_style,
animations: animations,
style: C::new( style: C::new(
custom_properties, custom_properties,
shareable, shareable,
@ -1739,6 +1742,7 @@ pub fn cascade<C: ComputedValues>(
shareable: bool, shareable: bool,
parent_style: Option<<&C>, parent_style: Option<<&C>,
cached_style: Option<<&C>, cached_style: Option<<&C>,
animations: Option<<&HashMap<String, Vec<Keyframe>>>,
mut error_reporter: StdBox<ParseErrorReporter + Send>) mut error_reporter: StdBox<ParseErrorReporter + Send>)
-> (C, bool) { -> (C, bool) {
use properties::style_struct_traits::{Border, Box, Font, Outline}; use properties::style_struct_traits::{Border, Box, Font, Outline};
@ -1774,6 +1778,7 @@ pub fn cascade<C: ComputedValues>(
parent_style, parent_style,
cached_style, cached_style,
custom_properties, custom_properties,
animations,
error_reporter); error_reporter);
return (style, false) return (style, false)
} }
@ -1782,6 +1787,7 @@ pub fn cascade<C: ComputedValues>(
is_root_element: is_root_element, is_root_element: is_root_element,
viewport_size: viewport_size, viewport_size: viewport_size,
inherited_style: inherited_style, inherited_style: inherited_style,
animations: animations,
style: C::new( style: C::new(
custom_properties, custom_properties,
shareable, shareable,

View file

@ -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; fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState;
@ -109,6 +110,15 @@ pub enum PseudoElement {
} }
impl PseudoElement { impl PseudoElement {
#[inline]
pub fn is_before_or_after(&self) -> bool {
match *self {
PseudoElement::Before |
PseudoElement::After => true,
_ => false,
}
}
#[inline] #[inline]
pub fn cascade_type(&self) -> PseudoElementCascadeType { pub fn cascade_type(&self) -> PseudoElementCascadeType {
match *self { match *self {
@ -249,6 +259,11 @@ impl SelectorImplExt for ServoSelectorImpl {
pc.state_flag() pc.state_flag()
} }
#[inline]
fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
pseudo.is_before_or_after()
}
#[inline] #[inline]
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>] { fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>] {
&*USER_OR_USER_AGENT_STYLESHEETS &*USER_OR_USER_AGENT_STYLESHEETS

View file

@ -128,9 +128,7 @@ pub struct Stylist<Impl: SelectorImplExt> {
BuildHasherDefault<::fnv::FnvHasher>>, BuildHasherDefault<::fnv::FnvHasher>>,
/// A map with all the animations indexed by name. /// A map with all the animations indexed by name.
animations: HashMap<String, animations: HashMap<String, Vec<Keyframe>>,
Vec<Keyframe>,
BuildHasherDefault<::fnv::FnvHasher>>,
/// Applicable declarations for a given non-eagerly cascaded pseudo-element. /// Applicable declarations for a given non-eagerly cascaded pseudo-element.
/// These are eagerly computed once, and then used to resolve the new /// These are eagerly computed once, and then used to resolve the new
@ -290,7 +288,8 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
let (computed, _) = let (computed, _) =
properties::cascade(self.device.au_viewport_size(), properties::cascade(self.device.au_viewport_size(),
&declarations, false, &declarations, false,
parent.map(|p| &**p), None, parent.map(|p| &**p),
None, None,
Box::new(StdoutErrorReporter)); Box::new(StdoutErrorReporter));
Some(Arc::new(computed)) Some(Arc::new(computed))
} else { } else {
@ -323,7 +322,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
let (computed, _) = let (computed, _) =
properties::cascade(self.device.au_viewport_size(), properties::cascade(self.device.au_viewport_size(),
&declarations, false, &declarations, false,
Some(&**parent), None, Some(&**parent), None, None,
Box::new(StdoutErrorReporter)); Box::new(StdoutErrorReporter));
Some(Arc::new(computed)) Some(Arc::new(computed))
} }
@ -457,6 +456,11 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
pub fn is_device_dirty(&self) -> bool { pub fn is_device_dirty(&self) -> bool {
self.is_device_dirty self.is_device_dirty
} }
#[inline]
pub fn animations(&self) -> &HashMap<String, Vec<Keyframe>> {
&self.animations
}
} }
/// Map that contains the CSS rules for a given origin. /// Map that contains the CSS rules for a given origin.

View file

@ -278,7 +278,6 @@ pub mod rule_filter {
impl<'a, I, Impl: SelectorImpl + 'a> $variant<'a, I> impl<'a, I, Impl: SelectorImpl + 'a> $variant<'a, I>
where I: Iterator<Item=&'a CSSRule<Impl>> { where I: Iterator<Item=&'a CSSRule<Impl>> {
#[inline] #[inline]
pub fn new(iter: I) -> $variant<'a, I> { pub fn new(iter: I) -> $variant<'a, I> {
$variant { $variant {

View file

@ -219,10 +219,9 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C,
// Perform the CSS cascade. // Perform the CSS cascade.
unsafe { unsafe {
node.cascade_node(&context.shared_context(), node.cascade_node(&context.shared_context(),
&context.local_context(),
parent_opt, parent_opt,
&applicable_declarations, &applicable_declarations);
&mut context.local_context().applicable_declarations_cache.borrow_mut(),
&context.shared_context().new_animations_sender);
} }
// Add ourselves to the LRU cache. // Add ourselves to the LRU cache.

View file

@ -1561,8 +1561,10 @@ pub mod specified {
pub mod computed { pub mod computed {
use app_units::Au; use app_units::Au;
use euclid::size::Size2D; use euclid::size::Size2D;
use keyframes::Keyframe;
use properties::ComputedValues; use properties::ComputedValues;
use properties::style_struct_traits::Font; use properties::style_struct_traits::Font;
use std::collections::HashMap;
use std::fmt; use std::fmt;
use super::LocalToCss; use super::LocalToCss;
use super::specified::AngleOrCorner; use super::specified::AngleOrCorner;
@ -1578,6 +1580,7 @@ pub mod computed {
fn inherited_style(&self) -> &Self::ConcreteComputedValues; fn inherited_style(&self) -> &Self::ConcreteComputedValues;
fn style(&self) -> &Self::ConcreteComputedValues; fn style(&self) -> &Self::ConcreteComputedValues;
fn mutate_style(&mut self) -> &mut Self::ConcreteComputedValues; fn mutate_style(&mut self) -> &mut Self::ConcreteComputedValues;
fn animations(&self) -> Option<&HashMap<String, Vec<Keyframe>>>;
} }
pub struct Context<'a, C: ComputedValues> { pub struct Context<'a, C: ComputedValues> {
@ -1585,6 +1588,7 @@ pub mod computed {
pub viewport_size: Size2D<Au>, pub viewport_size: Size2D<Au>,
pub inherited_style: &'a C, pub inherited_style: &'a C,
pub animations: Option<&'a HashMap<String, Vec<Keyframe>>>,
/// Values access through this need to be in the properties "computed early": /// Values access through this need to be in the properties "computed early":
/// color, text-decoration, font-size, display, position, float, border-*-style, outline-style /// color, text-decoration, font-size, display, position, float, border-*-style, outline-style
pub style: C, pub style: C,
@ -1597,6 +1601,7 @@ pub mod computed {
fn inherited_style(&self) -> &C { &self.inherited_style } fn inherited_style(&self) -> &C { &self.inherited_style }
fn style(&self) -> &C { &self.style } fn style(&self) -> &C { &self.style }
fn mutate_style(&mut self) -> &mut C { &mut self.style } fn mutate_style(&mut self) -> &mut C { &mut self.style }
fn animations(&self) -> Option<&HashMap<String, Vec<Keyframe>>> { self.animations }
} }
pub trait ToComputedValue { pub trait ToComputedValue {

View file

@ -562,8 +562,8 @@ pub trait MaybeNew {
impl MaybeNew for ViewportConstraints { impl MaybeNew for ViewportConstraints {
fn maybe_new(initial_viewport: TypedSize2D<ViewportPx, f32>, fn maybe_new(initial_viewport: TypedSize2D<ViewportPx, f32>,
rule: &ViewportRule) rule: &ViewportRule)
-> Option<ViewportConstraints> -> Option<ViewportConstraints>
{ {
use std::cmp; use std::cmp;
@ -648,6 +648,7 @@ impl MaybeNew for ViewportConstraints {
viewport_size: initial_viewport, viewport_size: initial_viewport,
inherited_style: ServoComputedValues::initial_values(), inherited_style: ServoComputedValues::initial_values(),
style: ServoComputedValues::initial_values().clone(), style: ServoComputedValues::initial_values().clone(),
animations: None,
}; };
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom' // DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'

View file

@ -379,6 +379,15 @@ impl SelectorImplExt for GeckoSelectorImpl {
fun(AnonBox(MozSVGText)); fun(AnonBox(MozSVGText));
} }
#[inline]
fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
match *pseudo {
PseudoElement::Before |
PseudoElement::After => true,
_ => false,
}
}
#[inline] #[inline]
fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState { fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
pc.state_flag() pc.state_flag()