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 alignSelf;
[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],
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) {

View file

@ -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<ConcreteRestyleDamage: TRestyleDamage> {
trait PrivateMatchMethods: TNode
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,
context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>,
parent_style: Option<&Arc<Self::ConcreteComputedValues>>,
@ -373,13 +376,14 @@ trait PrivateMatchMethods: TNode
mut style: Option<&mut Arc<Self::ConcreteComputedValues>>,
applicable_declarations_cache:
&mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>,
new_animations_sender: &Mutex<Sender<Animation>>,
shareable: bool,
animate_properties: bool)
-> (Self::ConcreteRestyleDamage, Arc<Self::ConcreteComputedValues>) {
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::<Self::ConcreteComputedValues>(
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<<Self::ConcreteElement as Element>::Impl>,
local_context: &LocalStyleContext<Self::ConcreteComputedValues>,
parent: Option<Self>,
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>,
applicable_declarations_cache:
&mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>,
new_animations_sender: &Mutex<Sender<Animation>>)
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>)
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
// 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 =
<Self::ConcreteElement as Element>::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;

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};
</%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
// https://www.w3.org/TR/cssom-view-1/
${helpers.single_keyword("scroll-behavior",

View file

@ -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<C: ComputedValues>(
parent_style: &C,
cached_style: &C,
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
animations: Option<<&HashMap<String, Vec<Keyframe>>>,
mut error_reporter: StdBox<ParseErrorReporter + Send>)
-> 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<C: ComputedValues>(
shareable: bool,
parent_style: Option<<&C>,
cached_style: Option<<&C>,
animations: Option<<&HashMap<String, Vec<Keyframe>>>,
mut error_reporter: StdBox<ParseErrorReporter + Send>)
-> (C, bool) {
use properties::style_struct_traits::{Border, Box, Font, Outline};
@ -1774,6 +1778,7 @@ pub fn cascade<C: ComputedValues>(
parent_style,
cached_style,
custom_properties,
animations,
error_reporter);
return (style, false)
}
@ -1782,6 +1787,7 @@ pub fn cascade<C: ComputedValues>(
is_root_element: is_root_element,
viewport_size: viewport_size,
inherited_style: inherited_style,
animations: animations,
style: C::new(
custom_properties,
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;
@ -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<Self>] {
&*USER_OR_USER_AGENT_STYLESHEETS

View file

@ -128,9 +128,7 @@ pub struct Stylist<Impl: SelectorImplExt> {
BuildHasherDefault<::fnv::FnvHasher>>,
/// A map with all the animations indexed by name.
animations: HashMap<String,
Vec<Keyframe>,
BuildHasherDefault<::fnv::FnvHasher>>,
animations: HashMap<String, Vec<Keyframe>>,
/// 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<Impl: SelectorImplExt> Stylist<Impl> {
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<Impl: SelectorImplExt> Stylist<Impl> {
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<Impl: SelectorImplExt> Stylist<Impl> {
pub fn is_device_dirty(&self) -> bool {
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.

View file

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

View file

@ -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.

View file

@ -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<String, Vec<Keyframe>>>;
}
pub struct Context<'a, C: ComputedValues> {
@ -1585,6 +1588,7 @@ pub mod computed {
pub viewport_size: Size2D<Au>,
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":
/// 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<String, Vec<Keyframe>>> { self.animations }
}
pub trait ToComputedValue {

View file

@ -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'

View file

@ -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()