Auto merge of #11766 - emilio:keyframes-parsing, r=SimonSapin,pcwalton

Add `@keyframes` and `animation-*` support.

- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors

<!-- Either: -->
- [x] There are tests for these changes

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

This adds support for parsing `@keyframes` rules, and animation properties. Stylo will need it sometime soonish, plus I want to make animations work in Servo.

The remaining part is doin the math and trigger the animations correctly from servo. I don't expect it to be *that* hard, but probaby I'll need to learn a bit more about the current animation infra (e.g. why the heck is the `new_animations_sender` guarded by a `Mutex`?).

I'd expect to land this, since this is already a bunch of work, this is the part exclusively required by stylo (at least if we don't use Servo's machinery), the media query parsing is tested, and the properties land after a flag, but if you prefer to wait until I finish this up it's fine for me too.

r? @SimonSapin

cc @pcwalton @bholley

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11766)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-06-28 17:31:01 -05:00 committed by GitHub
commit d3a81373e4
51 changed files with 2655 additions and 1419 deletions

View file

@ -1136,7 +1136,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let msg = LayoutControlMsg::TickAnimations;
match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.layout_chan.send(msg),
None => return warn!("Pipeline {:?} got script tick after closure.", pipeline_id),
None => return warn!("Pipeline {:?} got layout tick after closure.", pipeline_id),
}
}
};

View file

@ -4,6 +4,7 @@
//! CSS transitions and animations.
use context::SharedLayoutContext;
use flow::{self, Flow};
use gfx::display_list::OpaqueNode;
use ipc_channel::ipc::IpcSender;
@ -11,21 +12,44 @@ use msg::constellation_msg::PipelineId;
use script_layout_interface::restyle_damage::RestyleDamage;
use script_traits::{AnimationState, LayoutMsg as ConstellationMsg};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::mpsc::Receiver;
use style::animation::{Animation, update_style_for_animation};
use style::selector_impl::{SelectorImplExt, ServoSelectorImpl};
use time;
/// Processes any new animations that were discovered after style recalculation.
/// Also expire any old animations that have completed, inserting them into `expired_animations`.
pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
running_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
expired_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
new_animations_receiver: &Receiver<Animation>,
pipeline_id: PipelineId) {
let mut new_running_animations = Vec::new();
/// Also expire any old animations that have completed, inserting them into
/// `expired_animations`.
pub fn update_animation_state<Impl: SelectorImplExt>(constellation_chan: &IpcSender<ConstellationMsg>,
running_animations: &mut HashMap<OpaqueNode, Vec<Animation<Impl>>>,
expired_animations: &mut HashMap<OpaqueNode, Vec<Animation<Impl>>>,
new_animations_receiver: &Receiver<Animation<Impl>>,
pipeline_id: PipelineId) {
let mut new_running_animations = vec![];
while let Ok(animation) = new_animations_receiver.try_recv() {
new_running_animations.push(animation)
let mut should_push = true;
if let Animation::Keyframes(ref node, ref name, ref state) = animation {
// If the animation was already present in the list for the
// node, just update its state, else push the new animation to
// run.
if let Some(ref mut animations) = running_animations.get_mut(node) {
// TODO: This being linear is probably not optimal.
for mut anim in animations.iter_mut() {
if let Animation::Keyframes(_, ref anim_name, ref mut anim_state) = *anim {
if *name == *anim_name {
debug!("update_animation_state: Found other animation {}", name);
anim_state.update_from_other(&state);
should_push = false;
break;
}
}
}
}
}
if should_push {
new_running_animations.push(animation);
}
}
if running_animations.is_empty() && new_running_animations.is_empty() {
@ -34,62 +58,82 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
return
}
// Expire old running animations.
let now = time::precise_time_s();
let mut keys_to_remove = Vec::new();
// Expire old running animations.
//
// TODO: Do not expunge Keyframes animations, since we need that state if
// the animation gets re-triggered. Probably worth splitting in two
// different maps, or at least using a linked list?
let mut keys_to_remove = vec![];
for (key, running_animations) in running_animations.iter_mut() {
let mut animations_still_running = vec![];
for running_animation in running_animations.drain(..) {
if now < running_animation.end_time {
for mut running_animation in running_animations.drain(..) {
let still_running = !running_animation.is_expired() && match running_animation {
Animation::Transition(_, started_at, ref frame, _expired) => {
now < started_at + frame.duration
}
Animation::Keyframes(_, _, ref mut state) => {
// This animation is still running, or we need to keep
// iterating.
now < state.started_at + state.duration || state.tick()
}
};
if still_running {
animations_still_running.push(running_animation);
continue
}
match expired_animations.entry(*key) {
Entry::Vacant(entry) => {
entry.insert(vec![running_animation]);
}
Entry::Occupied(mut entry) => entry.get_mut().push(running_animation),
}
expired_animations.entry(*key)
.or_insert_with(Vec::new)
.push(running_animation);
}
if animations_still_running.len() == 0 {
if animations_still_running.is_empty() {
keys_to_remove.push(*key);
} else {
*running_animations = animations_still_running
}
}
for key in keys_to_remove {
running_animations.remove(&key).unwrap();
}
// Add new running animations.
for new_running_animation in new_running_animations {
match running_animations.entry(new_running_animation.node) {
Entry::Vacant(entry) => {
entry.insert(vec![new_running_animation]);
}
Entry::Occupied(mut entry) => entry.get_mut().push(new_running_animation),
}
running_animations.entry(*new_running_animation.node())
.or_insert_with(Vec::new)
.push(new_running_animation);
}
let animation_state;
if running_animations.is_empty() {
animation_state = AnimationState::NoAnimationsPresent;
let animation_state = if running_animations.is_empty() {
AnimationState::NoAnimationsPresent
} else {
animation_state = AnimationState::AnimationsPresent;
}
AnimationState::AnimationsPresent
};
constellation_chan.send(ConstellationMsg::ChangeRunningAnimationsState(pipeline_id, animation_state))
.unwrap();
}
/// Recalculates style for a set of animations. This does *not* run with the DOM lock held.
pub fn recalc_style_for_animations(flow: &mut Flow,
animations: &HashMap<OpaqueNode, Vec<Animation>>) {
/// Recalculates style for a set of animations. This does *not* run with the DOM
/// lock held.
// NB: This is specific for ServoSelectorImpl, since the layout context and the
// flows are ServoSelectorImpl specific too. If that goes away at some point,
// this should be made generic.
pub fn recalc_style_for_animations(context: &SharedLayoutContext,
flow: &mut Flow,
animations: &HashMap<OpaqueNode,
Vec<Animation<ServoSelectorImpl>>>) {
let mut damage = RestyleDamage::empty();
flow.mutate_fragments(&mut |fragment| {
if let Some(ref animations) = animations.get(&fragment.node) {
for animation in *animations {
update_style_for_animation(animation, &mut fragment.style, Some(&mut damage));
for animation in animations.iter() {
update_style_for_animation(&context.style_context,
animation,
&mut fragment.style,
Some(&mut damage));
}
}
});
@ -97,6 +141,6 @@ pub fn recalc_style_for_animations(flow: &mut Flow,
let base = flow::mut_base(flow);
base.restyle_damage.insert(damage);
for kid in base.children.iter_mut() {
recalc_style_for_animations(kid, animations)
recalc_style_for_animations(context, kid, animations)
}
}

View file

@ -98,7 +98,6 @@ use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
use style::animation::Animation;
use style::computed_values::{filter, mix_blend_mode};
use style::context::ReflowGoal;
use style::dom::{TDocument, TElement, TNode};
@ -109,7 +108,7 @@ use style::parallel::WorkQueueData;
use style::properties::ComputedValues;
use style::refcell::RefCell;
use style::selector_matching::USER_OR_USER_AGENT_STYLESHEETS;
use style::servo::{SharedStyleContext, Stylesheet, Stylist};
use style::servo::{Animation, SharedStyleContext, Stylesheet, Stylist};
use style::stylesheets::CSSRuleIteratorExt;
use url::Url;
use util::geometry::MAX_RECT;
@ -1290,7 +1289,7 @@ impl LayoutThread {
self.tick_animations(&mut rw_data);
}
pub fn tick_animations(&mut self, rw_data: &mut LayoutThreadData) {
fn tick_animations(&mut self, rw_data: &mut LayoutThreadData) {
let reflow_info = Reflow {
goal: ReflowGoal::ForDisplay,
page_clip_rect: MAX_RECT,
@ -1307,8 +1306,9 @@ impl LayoutThread {
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| {
animation::recalc_style_for_animations(flow_ref::deref_mut(&mut root_flow),
&*animations)
animation::recalc_style_for_animations(&layout_context,
flow_ref::deref_mut(&mut root_flow),
&animations)
});
}

View file

@ -327,4 +327,21 @@ 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;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-duration;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationDuration;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-timing-function;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationTimingFunction;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-iteration-count;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationIterationCount;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-direction;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationDirection;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-play-state;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationPlayState;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-fill-mode;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationFillMode;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-delay;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationDelay;
};

View file

@ -671,7 +671,7 @@ impl ScriptThread {
}
// Store new resizes, and gather all other events.
let mut sequential = vec!();
let mut sequential = vec![];
// Receive at least one message so we don't spinloop.
let mut event = {

File diff suppressed because it is too large Load diff

View file

@ -34,16 +34,16 @@ pub struct SharedStyleContext<Impl: SelectorImplExt> {
/// A channel on which new animations that have been triggered by style recalculation can be
/// sent.
pub new_animations_sender: Mutex<Sender<Animation>>,
pub new_animations_sender: Mutex<Sender<Animation<Impl>>>,
/// Why is this reflow occurring
pub goal: ReflowGoal,
/// The animations that are currently running.
pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation<Impl>>>>>,
/// The list of animations that have expired since the last style recalculation.
pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation<Impl>>>>>,
///The CSS error reporter for all CSS loaded in this layout thread
pub error_reporter: Box<ParseErrorReporter + Sync>,

View file

@ -0,0 +1,242 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::{AtRuleParser, Delimiter, Parser, QualifiedRuleParser, RuleListParser};
use parser::{ParserContext, log_css_error};
use properties::animated_properties::TransitionProperty;
use properties::{PropertyDeclaration, parse_property_declaration_list};
use std::sync::Arc;
/// A number from 1 to 100, indicating the percentage of the animation where
/// this keyframe should run.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, HeapSizeOf)]
pub struct KeyframePercentage(pub f32);
impl ::std::cmp::Ord for KeyframePercentage {
#[inline]
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
// We know we have a number from 0 to 1, so unwrap() here is safe.
self.0.partial_cmp(&other.0).unwrap()
}
}
impl ::std::cmp::Eq for KeyframePercentage { }
impl KeyframePercentage {
#[inline]
pub fn new(value: f32) -> KeyframePercentage {
debug_assert!(value >= 0. && value <= 1.);
KeyframePercentage(value)
}
fn parse(input: &mut Parser) -> Result<KeyframePercentage, ()> {
let percentage = if input.try(|input| input.expect_ident_matching("from")).is_ok() {
KeyframePercentage::new(0.)
} else if input.try(|input| input.expect_ident_matching("to")).is_ok() {
KeyframePercentage::new(1.)
} else {
let percentage = try!(input.expect_percentage());
if percentage > 1. || percentage < 0. {
return Err(());
}
KeyframePercentage::new(percentage)
};
Ok(percentage)
}
}
/// A keyframes selector is a list of percentages or from/to symbols, which are
/// converted at parse time to percentages.
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct KeyframeSelector(Vec<KeyframePercentage>);
impl KeyframeSelector {
#[inline]
pub fn percentages(&self) -> &[KeyframePercentage] {
&self.0
}
/// A dummy public function so we can write a unit test for this.
pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
KeyframeSelector(percentages)
}
}
/// A keyframe.
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct Keyframe {
pub selector: KeyframeSelector,
pub declarations: Arc<Vec<PropertyDeclaration>>,
}
impl Keyframe {
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Keyframe, ()> {
let percentages = try!(input.parse_until_before(Delimiter::CurlyBracketBlock, |input| {
input.parse_comma_separated(|input| KeyframePercentage::parse(input))
}));
let selector = KeyframeSelector(percentages);
try!(input.expect_curly_bracket_block());
let declarations = input.parse_nested_block(|input| {
Ok(parse_property_declaration_list(context, input))
}).unwrap();
// NB: Important declarations are explicitely ignored in the spec.
Ok(Keyframe {
selector: selector,
declarations: declarations.normal,
})
}
}
/// A keyframes step value. This can be a synthetised keyframes animation, that
/// is, one autogenerated from the current computed values, or a list of
/// declarations to apply.
// TODO: Find a better name for this?
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub enum KeyframesStepValue {
Declarations(Arc<Vec<PropertyDeclaration>>),
ComputedValues,
}
/// A single step from a keyframe animation.
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct KeyframesStep {
/// The percentage of the animation duration when this step starts.
pub start_percentage: KeyframePercentage,
/// Declarations that will determine the final style during the step, or
/// `ComputedValues` if this is an autogenerated step.
pub value: KeyframesStepValue,
}
impl KeyframesStep {
#[inline]
fn new(percentage: KeyframePercentage,
value: KeyframesStepValue) -> Self {
KeyframesStep {
start_percentage: percentage,
value: value,
}
}
}
/// This structure represents a list of animation steps computed from the list
/// of keyframes, in order.
///
/// It only takes into account animable properties.
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct KeyframesAnimation {
pub steps: Vec<KeyframesStep>,
/// The properties that change in this animation.
pub properties_changed: Vec<TransitionProperty>,
}
/// Get all the animated properties in a keyframes animation. Note that it's not
/// defined what happens when a property is not on a keyframe, so we only peek
/// the props of the first one.
///
/// In practice, browsers seem to try to do their best job at it, so we might
/// want to go through all the actual keyframes and deduplicate properties.
fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
let mut ret = vec![];
// NB: declarations are already deduplicated, so we don't have to check for
// it here.
for declaration in keyframe.declarations.iter() {
if let Some(property) = TransitionProperty::from_declaration(&declaration) {
ret.push(property);
}
}
ret
}
impl KeyframesAnimation {
pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<Self> {
let animated_properties = get_animated_properties(&keyframes[0]);
if keyframes.is_empty() || animated_properties.is_empty() {
return None;
}
let mut steps = vec![];
for keyframe in keyframes {
for percentage in keyframe.selector.0.iter() {
steps.push(KeyframesStep::new(*percentage,
KeyframesStepValue::Declarations(keyframe.declarations.clone())));
}
}
// Sort by the start percentage, so we can easily find a frame.
steps.sort_by_key(|step| step.start_percentage);
// Prepend autogenerated keyframes if appropriate.
if steps[0].start_percentage.0 != 0. {
steps.insert(0, KeyframesStep::new(KeyframePercentage::new(0.),
KeyframesStepValue::ComputedValues));
}
if steps.last().unwrap().start_percentage.0 != 1. {
steps.push(KeyframesStep::new(KeyframePercentage::new(0.),
KeyframesStepValue::ComputedValues));
}
Some(KeyframesAnimation {
steps: steps,
properties_changed: animated_properties,
})
}
}
/// Parses a keyframes list, like:
/// 0%, 50% {
/// width: 50%;
/// }
///
/// 40%, 60%, 100% {
/// width: 100%;
/// }
struct KeyframeListParser<'a> {
context: &'a ParserContext<'a>,
}
pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Vec<Keyframe> {
RuleListParser::new_for_nested_rule(input, KeyframeListParser { context: context })
.filter_map(Result::ok)
.collect()
}
enum Void {}
impl<'a> AtRuleParser for KeyframeListParser<'a> {
type Prelude = Void;
type AtRule = Keyframe;
}
impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
type Prelude = KeyframeSelector;
type QualifiedRule = Keyframe;
fn parse_prelude(&self, input: &mut Parser) -> Result<Self::Prelude, ()> {
let start = input.position();
match input.parse_comma_separated(|input| KeyframePercentage::parse(input)) {
Ok(percentages) => Ok(KeyframeSelector(percentages)),
Err(()) => {
let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start));
log_css_error(input, start, &message, self.context);
Err(())
}
}
}
fn parse_block(&self, prelude: Self::Prelude, input: &mut Parser)
-> Result<Self::QualifiedRule, ()> {
Ok(Keyframe {
selector: prelude,
// FIXME: needs parsing different from parse_property_declaration_list:
// https://drafts.csswg.org/css-animations/#keyframes
// Paragraph "The <declaration-list> inside of <keyframe-block> ..."
declarations: parse_property_declaration_list(self.context, input).normal,
})
}
}

View file

@ -76,6 +76,7 @@ pub mod dom;
pub mod element_state;
pub mod error_reporting;
pub mod font_face;
pub mod keyframes;
pub mod logical_geometry;
pub mod matching;
pub mod media_queries;

View file

@ -7,7 +7,7 @@
#![allow(unsafe_code)]
use animation::{self, Animation};
use context::SharedStyleContext;
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};
@ -54,7 +53,9 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
pub struct ApplicableDeclarations<Impl: SelectorImplExt> {
pub normal: SmallVec<[DeclarationBlock; 16]>,
pub per_pseudo: HashMap<Impl::PseudoElement, Vec<DeclarationBlock>, BuildHasherDefault<::fnv::FnvHasher>>,
pub per_pseudo: HashMap<Impl::PseudoElement,
Vec<DeclarationBlock>,
BuildHasherDefault<::fnv::FnvHasher>>,
/// Whether the `normal` declarations are shareable with other nodes.
pub normal_shareable: bool,
@ -365,7 +366,11 @@ pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
}
trait PrivateMatchMethods: TNode
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt {
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues = Self::ConcreteComputedValues> {
/// 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,7 +378,6 @@ 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>) {
@ -382,14 +386,15 @@ trait PrivateMatchMethods: TNode
cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable;
}
let mut this_style;
let this_style;
match parent_style {
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,
@ -411,22 +416,31 @@ trait PrivateMatchMethods: TNode
}
};
// Trigger transitions if necessary. This will reset `this_style` back to its old value if
// it did trigger a transition.
let mut this_style = Arc::new(this_style);
if animate_properties {
let this_opaque = self.opaque();
// Trigger any present animations if necessary.
let mut animations_started = animation::maybe_start_animations::<<Self::ConcreteElement as Element>::Impl>(
&context,
this_opaque,
&this_style);
// Trigger transitions if necessary. This will reset `this_style` back
// to its old value if it did trigger a transition.
if let Some(ref style) = style {
let animations_started =
animation::start_transitions_if_applicable::<Self::ConcreteComputedValues>(
new_animations_sender,
self.opaque(),
animations_started |=
animation::start_transitions_if_applicable::<<Self::ConcreteElement as Element>::Impl>(
&context.new_animations_sender,
this_opaque,
&**style,
&mut this_style);
cacheable = cacheable && !animations_started
}
cacheable = cacheable && !animations_started
}
// Calculate style difference.
let this_style = Arc::new(this_style);
let damage = Self::ConcreteRestyleDamage::compute(style.map(|s| &*s), &*this_style);
// Cache the resolved style if it was cacheable.
@ -457,7 +471,11 @@ trait PrivateMatchMethods: TNode
had_animations_to_expire = animations_to_expire.is_some();
if let Some(ref animations) = animations_to_expire {
for animation in *animations {
animation.property_animation.update(Arc::make_mut(style).as_servo_mut(), 1.0);
// NB: Expiring a keyframes animation is the same as not
// applying the keyframes style to it, so we're safe.
if let Animation::Transition(_, _, ref frame, _) = *animation {
frame.property_animation.update(Arc::make_mut(style), 1.0);
}
}
}
}
@ -474,11 +492,11 @@ trait PrivateMatchMethods: TNode
.is_some();
if had_running_animations {
let mut all_running_animations = context.running_animations.write().unwrap();
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
animation::update_style_for_animation::<Self::ConcreteComputedValues,
Self::ConcreteRestyleDamage>(running_animation, style, None);
for mut running_animation in all_running_animations.get_mut(&this_opaque).unwrap() {
animation::update_style_for_animation::<Self::ConcreteRestyleDamage,
<Self::ConcreteElement as Element>::Impl>(context, running_animation, style, None);
running_animation.mark_as_expired();
}
all_running_animations.remove(&this_opaque);
}
had_animations_to_expire || had_running_animations
@ -486,7 +504,8 @@ trait PrivateMatchMethods: TNode
}
impl<N: TNode> PrivateMatchMethods for N
where <N::ConcreteElement as Element>::Impl: SelectorImplExt {}
where <N::ConcreteElement as Element>::Impl:
SelectorImplExt<ComputedValues = N::ConcreteComputedValues> {}
trait PrivateElementMatchMethods: TElement {
fn share_style_with_candidate_if_possible(&self,
@ -641,25 +660,27 @@ 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>>)
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt {
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>)
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues = Self::ConcreteComputedValues>
{
// Get our parent's style. This must be unsafe so that we don't touch the parent's
// borrow flags.
//
// 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 +698,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 +710,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

@ -58,7 +58,7 @@ fn top_down_dom<N, C>(unsafe_nodes: UnsafeNodeList,
where N: TNode, C: DomTraversalContext<N> {
let context = C::new(proxy.user_data(), unsafe_nodes.1);
let mut discovered_child_nodes = Vec::new();
let mut discovered_child_nodes = vec![];
for unsafe_node in *unsafe_nodes.0 {
// Get a real layout node.
let node = unsafe { N::from_unsafe(&unsafe_node) };

View file

@ -13,7 +13,7 @@ def to_rust_ident(name):
def to_camel_case(ident):
return re.sub("_([a-z])", lambda m: m.group(1).upper(), ident.strip("_").capitalize())
return re.sub("(^|_|-)([a-z])", lambda m: m.group(2).upper(), ident.strip("_").strip("-"))
class Keyword(object):
@ -45,7 +45,7 @@ class Keyword(object):
class Longhand(object):
def __init__(self, style_struct, name, derived_from=None, keyword=None,
def __init__(self, style_struct, name, animatable=None, derived_from=None, keyword=None,
predefined_type=None, custom_cascade=False, experimental=False, internal=False,
need_clone=False, gecko_ffi_name=None):
self.name = name
@ -61,6 +61,16 @@ class Longhand(object):
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
self.derived_from = (derived_from or "").split()
# This is done like this since just a plain bool argument seemed like
# really random.
if animatable is None:
raise TypeError("animatable should be specified for " + name + ")")
if isinstance(animatable, bool):
self.animatable = animatable
else:
assert animatable == "True" or animatable == "False"
self.animatable = animatable == "True"
class Shorthand(object):
def __init__(self, name, sub_properties, experimental=False, internal=False):

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
<%! from data import Keyword, to_rust_ident %>
<%! from data import Keyword, to_rust_ident, to_camel_case %>
<%def name="longhand(name, **kwargs)">
<%call expr="raw_longhand(name, **kwargs)">
@ -181,9 +181,11 @@
% endfor
}
}
#[inline] pub fn get_initial_value() -> computed_value::T {
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::${to_rust_ident(values.split()[0])}
}
#[inline]
pub fn parse(_context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
computed_value::T::parse(input)
@ -191,6 +193,63 @@
</%call>
</%def>
<%def name="keyword_list(name, values, **kwargs)">
<%
keyword_kwargs = {a: kwargs.pop(a, None) for a in [
'gecko_constant_prefix', 'extra_gecko_values', 'extra_servo_values'
]}
%>
<%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
use values::computed::ComputedValueAsSpecified;
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct T(pub Vec<${to_camel_case(name)}>);
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
debug_assert!(!self.0.is_empty(), "Always parses at least one");
for (index, item) in self.0.iter().enumerate() {
if index != 0 {
try!(dest.write_str(", "));
}
try!(item.to_css(dest));
}
Ok(())
}
}
define_css_keyword_enum! { ${to_camel_case(name)}:
% for value in data.longhands_by_name[name].keyword.values_for(product):
"${value}" => ${to_rust_ident(value)},
% endfor
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(vec![
computed_value::${to_camel_case(name)}::${to_rust_ident(values.split()[0])}
])
}
#[inline]
pub fn parse(_context: &ParserContext, input: &mut Parser)
-> Result<SpecifiedValue, ()> {
Ok(SpecifiedValue(try!(
input.parse_comma_separated(computed_value::${to_camel_case(name)}::parse))))
}
impl ComputedValueAsSpecified for SpecifiedValue {}
</%call>
</%def>
<%def name="shorthand(name, sub_properties, experimental=False, **kwargs)">
<%
shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental,

View file

@ -0,0 +1,878 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
use cssparser::{Color as CSSParserColor, Parser, RGBA, ToCss};
use euclid::{Point2D, Size2D};
use properties::PropertyDeclaration;
use properties::longhands;
use properties::longhands::background_position::computed_value::T as BackgroundPosition;
use properties::longhands::background_size::computed_value::T as BackgroundSize;
use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
use properties::longhands::clip::computed_value::ClipRect;
use properties::longhands::font_weight::computed_value::T as FontWeight;
use properties::longhands::line_height::computed_value::T as LineHeight;
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
use properties::longhands::text_shadow::computed_value::TextShadow;
use properties::longhands::box_shadow::computed_value::T as BoxShadowList;
use properties::longhands::box_shadow::computed_value::BoxShadow;
use properties::longhands::transform::computed_value::ComputedMatrix;
use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
use properties::longhands::transform::computed_value::T as TransformList;
use properties::longhands::transform_origin::computed_value::T as TransformOrigin;
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
use properties::longhands::visibility::computed_value::T as Visibility;
use properties::longhands::z_index::computed_value::T as ZIndex;
use properties::style_struct_traits::*;
use std::cmp::{self, Ordering};
use std::fmt;
use std::iter::repeat;
use super::ComputedValues;
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
use values::computed::{BorderRadiusSize, LengthOrNone};
use values::computed::{CalcLengthOrPercentage, LengthOrPercentage};
// NB: This needs to be here because it needs all the longhands generated
// beforehand.
#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)]
pub enum TransitionProperty {
All,
% for prop in data.longhands:
% if prop.animatable:
${prop.camel_case},
% endif
% endfor
}
impl TransitionProperty {
/// Iterates over each property that is not `All`.
pub fn each<F: FnMut(TransitionProperty) -> ()>(mut cb: F) {
% for prop in data.longhands:
% if prop.animatable:
cb(TransitionProperty::${prop.camel_case});
% endif
% endfor
}
pub fn parse(input: &mut Parser) -> Result<Self, ()> {
match_ignore_ascii_case! { try!(input.expect_ident()),
"all" => Ok(TransitionProperty::All),
% for prop in data.longhands:
% if prop.animatable:
"${prop.name}" => Ok(TransitionProperty::${prop.camel_case}),
% endif
% endfor
_ => Err(())
}
}
pub fn from_declaration(declaration: &PropertyDeclaration) -> Option<Self> {
match *declaration {
% for prop in data.longhands:
% if prop.animatable:
PropertyDeclaration::${prop.camel_case}(..)
=> Some(TransitionProperty::${prop.camel_case}),
% endif
% endfor
_ => None,
}
}
}
impl ToCss for TransitionProperty {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
TransitionProperty::All => dest.write_str("all"),
% for prop in data.longhands:
% if prop.animatable:
TransitionProperty::${prop.camel_case} => dest.write_str("${prop.name}"),
% endif
% endfor
}
}
}
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
pub enum AnimatedProperty {
% for prop in data.longhands:
% if prop.animatable:
${prop.camel_case}(longhands::${prop.ident}::computed_value::T,
longhands::${prop.ident}::computed_value::T),
% endif
% endfor
}
impl AnimatedProperty {
pub fn does_animate(&self) -> bool {
match *self {
% for prop in data.longhands:
% if prop.animatable:
AnimatedProperty::${prop.camel_case}(ref from, ref to) => from != to,
% endif
% endfor
}
}
pub fn update<C: ComputedValues>(&self, style: &mut C, progress: f64) {
match *self {
% for prop in data.longhands:
% if prop.animatable:
AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
if let Some(value) = from.interpolate(to, progress) {
style.mutate_${prop.style_struct.ident.strip("_")}().set_${prop.ident}(value);
}
}
% endif
% endfor
}
}
// NB: Transition properties need clone
pub fn from_transition_property<C: ComputedValues>(transition_property: &TransitionProperty,
old_style: &C,
new_style: &C) -> AnimatedProperty {
// TODO: Generalise this for GeckoLib, adding clone_xxx to the
// appropiate longhands.
let old_style = old_style.as_servo();
let new_style = new_style.as_servo();
match *transition_property {
TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
% for prop in data.longhands:
% if prop.animatable:
TransitionProperty::${prop.camel_case} => {
AnimatedProperty::${prop.camel_case}(
old_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone(),
new_style.get_${prop.style_struct.ident.strip("_")}().${prop.ident}.clone())
}
% endif
% endfor
}
}
}
/// A trait used to implement [interpolation][interpolated-types].
///
/// [interpolated-types]: https://drafts.csswg.org/css-transitions/#interpolated-types
pub trait Interpolate: Sized {
fn interpolate(&self, other: &Self, time: f64) -> Option<Self>;
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
impl Interpolate for Au {
#[inline]
fn interpolate(&self, other: &Au, time: f64) -> Option<Au> {
Some(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * time).round() as i32))
}
}
impl <T> Interpolate for Option<T> where T: Interpolate {
#[inline]
fn interpolate(&self, other: &Option<T>, time: f64) -> Option<Option<T>> {
match (self, other) {
(&Some(ref this), &Some(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(Some(value))
})
}
(_, _) => None
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
impl Interpolate for f32 {
#[inline]
fn interpolate(&self, other: &f32, time: f64) -> Option<f32> {
Some(((*self as f64) + ((*other as f64) - (*self as f64)) * time) as f32)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
impl Interpolate for f64 {
#[inline]
fn interpolate(&self, other: &f64, time: f64) -> Option<f64> {
Some(*self + (*other - *self) * time)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
impl Interpolate for i32 {
#[inline]
fn interpolate(&self, other: &i32, time: f64) -> Option<i32> {
let a = *self as f64;
let b = *other as f64;
Some((a + (b - a) * time).round() as i32)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
impl Interpolate for Angle {
#[inline]
fn interpolate(&self, other: &Angle, time: f64) -> Option<Angle> {
self.radians().interpolate(&other.radians(), time).map(Angle)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-visibility
impl Interpolate for Visibility {
#[inline]
fn interpolate(&self, other: &Visibility, time: f64)
-> Option<Visibility> {
match (*self, *other) {
(Visibility::visible, _) | (_, Visibility::visible) => {
if time >= 0.0 && time <= 1.0 {
Some(Visibility::visible)
} else if time < 0.0 {
Some(*self)
} else {
Some(*other)
}
}
(_, _) => None,
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-integer
impl Interpolate for ZIndex {
#[inline]
fn interpolate(&self, other: &ZIndex, time: f64)
-> Option<ZIndex> {
match (*self, *other) {
(ZIndex::Number(ref this),
ZIndex::Number(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(ZIndex::Number(value))
})
}
(_, _) => None,
}
}
}
impl<T: Interpolate + Clone> Interpolate for Size2D<T> {
#[inline]
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
let width = match self.width.interpolate(&other.width, time) {
Some(width) => width,
None => return None,
};
let height = match self.height.interpolate(&other.height, time) {
Some(height) => height,
None => return None,
};
Some(Size2D::new(width, height))
}
}
impl<T: Interpolate + Clone> Interpolate for Point2D<T> {
#[inline]
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
let x = match self.x.interpolate(&other.x, time) {
Some(x) => x,
None => return None,
};
let y = match self.y.interpolate(&other.y, time) {
Some(y) => y,
None => return None,
};
Some(Point2D::new(x, y))
}
}
impl Interpolate for BorderRadiusSize {
#[inline]
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
self.0.interpolate(&other.0, time).map(BorderRadiusSize)
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-length
impl Interpolate for VerticalAlign {
#[inline]
fn interpolate(&self, other: &VerticalAlign, time: f64)
-> Option<VerticalAlign> {
match (*self, *other) {
(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)),
VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => {
this.interpolate(other, time).and_then(|value| {
Some(VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value)))
})
}
(_, _) => None,
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
impl Interpolate for BorderSpacing {
#[inline]
fn interpolate(&self, other: &BorderSpacing, time: f64)
-> Option<BorderSpacing> {
self.horizontal.interpolate(&other.horizontal, time).and_then(|horizontal| {
self.vertical.interpolate(&other.vertical, time).and_then(|vertical| {
Some(BorderSpacing { horizontal: horizontal, vertical: vertical })
})
})
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-color
impl Interpolate for RGBA {
#[inline]
fn interpolate(&self, other: &RGBA, time: f64) -> Option<RGBA> {
match (self.red.interpolate(&other.red, time),
self.green.interpolate(&other.green, time),
self.blue.interpolate(&other.blue, time),
self.alpha.interpolate(&other.alpha, time)) {
(Some(red), Some(green), Some(blue), Some(alpha)) => {
Some(RGBA { red: red, green: green, blue: blue, alpha: alpha })
}
(_, _, _, _) => None
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-color
impl Interpolate for CSSParserColor {
#[inline]
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
match (*self, *other) {
(CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(CSSParserColor::RGBA(value))
})
}
(_, _) => None,
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
impl Interpolate for CalcLengthOrPercentage {
#[inline]
fn interpolate(&self, other: &CalcLengthOrPercentage, time: f64)
-> Option<CalcLengthOrPercentage> {
Some(CalcLengthOrPercentage {
length: self.length().interpolate(&other.length(), time),
percentage: self.percentage().interpolate(&other.percentage(), time),
})
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
impl Interpolate for LengthOrPercentage {
#[inline]
fn interpolate(&self, other: &LengthOrPercentage, time: f64)
-> Option<LengthOrPercentage> {
match (*self, *other) {
(LengthOrPercentage::Length(ref this),
LengthOrPercentage::Length(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LengthOrPercentage::Length(value))
})
}
(LengthOrPercentage::Percentage(ref this),
LengthOrPercentage::Percentage(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LengthOrPercentage::Percentage(value))
})
}
(this, other) => {
let this: CalcLengthOrPercentage = From::from(this);
let other: CalcLengthOrPercentage = From::from(other);
this.interpolate(&other, time).and_then(|value| {
Some(LengthOrPercentage::Calc(value))
})
}
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
impl Interpolate for LengthOrPercentageOrAuto {
#[inline]
fn interpolate(&self, other: &LengthOrPercentageOrAuto, time: f64)
-> Option<LengthOrPercentageOrAuto> {
match (*self, *other) {
(LengthOrPercentageOrAuto::Length(ref this),
LengthOrPercentageOrAuto::Length(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LengthOrPercentageOrAuto::Length(value))
})
}
(LengthOrPercentageOrAuto::Percentage(ref this),
LengthOrPercentageOrAuto::Percentage(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LengthOrPercentageOrAuto::Percentage(value))
})
}
(LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
Some(LengthOrPercentageOrAuto::Auto)
}
(this, other) => {
let this: Option<CalcLengthOrPercentage> = From::from(this);
let other: Option<CalcLengthOrPercentage> = From::from(other);
this.interpolate(&other, time).unwrap_or(None).and_then(|value| {
Some(LengthOrPercentageOrAuto::Calc(value))
})
}
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
impl Interpolate for LengthOrPercentageOrNone {
#[inline]
fn interpolate(&self, other: &LengthOrPercentageOrNone, time: f64)
-> Option<LengthOrPercentageOrNone> {
match (*self, *other) {
(LengthOrPercentageOrNone::Length(ref this),
LengthOrPercentageOrNone::Length(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LengthOrPercentageOrNone::Length(value))
})
}
(LengthOrPercentageOrNone::Percentage(ref this),
LengthOrPercentageOrNone::Percentage(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LengthOrPercentageOrNone::Percentage(value))
})
}
(LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
Some(LengthOrPercentageOrNone::None)
}
(_, _) => None,
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-number
/// https://drafts.csswg.org/css-transitions/#animtype-length
impl Interpolate for LineHeight {
#[inline]
fn interpolate(&self, other: &LineHeight, time: f64)
-> Option<LineHeight> {
match (*self, *other) {
(LineHeight::Length(ref this),
LineHeight::Length(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LineHeight::Length(value))
})
}
(LineHeight::Number(ref this),
LineHeight::Number(ref other)) => {
this.interpolate(other, time).and_then(|value| {
Some(LineHeight::Number(value))
})
}
(LineHeight::Normal, LineHeight::Normal) => {
Some(LineHeight::Normal)
}
(_, _) => None,
}
}
}
/// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight
impl Interpolate for FontWeight {
#[inline]
fn interpolate(&self, other: &FontWeight, time: f64)
-> Option<FontWeight> {
let a = (*self as u32) as f64;
let b = (*other as u32) as f64;
let weight = a + (b - a) * time;
Some(if weight < 150. {
FontWeight::Weight100
} else if weight < 250. {
FontWeight::Weight200
} else if weight < 350. {
FontWeight::Weight300
} else if weight < 450. {
FontWeight::Weight400
} else if weight < 550. {
FontWeight::Weight500
} else if weight < 650. {
FontWeight::Weight600
} else if weight < 750. {
FontWeight::Weight700
} else if weight < 850. {
FontWeight::Weight800
} else {
FontWeight::Weight900
})
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-rect
impl Interpolate for ClipRect {
#[inline]
fn interpolate(&self, other: &ClipRect, time: f64)
-> Option<ClipRect> {
match (self.top.interpolate(&other.top, time),
self.right.interpolate(&other.right, time),
self.bottom.interpolate(&other.bottom, time),
self.left.interpolate(&other.left, time)) {
(Some(top), Some(right), Some(bottom), Some(left)) => {
Some(ClipRect { top: top, right: right, bottom: bottom, left: left })
},
(_, _, _, _) => None,
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
impl Interpolate for BackgroundPosition {
#[inline]
fn interpolate(&self, other: &BackgroundPosition, time: f64)
-> Option<BackgroundPosition> {
match (self.horizontal.interpolate(&other.horizontal, time),
self.vertical.interpolate(&other.vertical, time)) {
(Some(horizontal), Some(vertical)) => {
Some(BackgroundPosition { horizontal: horizontal, vertical: vertical })
},
(_, _) => None,
}
}
}
impl Interpolate for BackgroundSize {
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
use properties::longhands::background_size::computed_value::ExplicitSize;
match (self, other) {
(&BackgroundSize::Explicit(ref me), &BackgroundSize::Explicit(ref other))
=> match (me.width.interpolate(&other.width, time),
me.height.interpolate(&other.height, time)) {
(Some(width), Some(height))
=> Some(BackgroundSize::Explicit(
ExplicitSize { width: width, height: height })),
_ => None,
},
_ => None
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
impl Interpolate for TextShadow {
#[inline]
fn interpolate(&self, other: &TextShadow, time: f64)
-> Option<TextShadow> {
match (self.offset_x.interpolate(&other.offset_x, time),
self.offset_y.interpolate(&other.offset_y, time),
self.blur_radius.interpolate(&other.blur_radius, time),
self.color.interpolate(&other.color, time)) {
(Some(offset_x), Some(offset_y), Some(blur_radius), Some(color)) => {
Some(TextShadow { offset_x: offset_x, offset_y: offset_y, blur_radius: blur_radius, color: color })
},
_ => None,
}
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
impl Interpolate for TextShadowList {
#[inline]
fn interpolate(&self, other: &TextShadowList, time: f64)
-> Option<TextShadowList> {
let zero = TextShadow {
offset_x: Au(0),
offset_y: Au(0),
blur_radius: Au(0),
color: CSSParserColor::RGBA(RGBA {
red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0
})
};
let interpolate_each = |(a, b): (&TextShadow, &TextShadow)| {
a.interpolate(b, time).unwrap()
};
Some(TextShadowList(match self.0.len().cmp(&other.0.len()) {
Ordering::Less => other.0.iter().chain(repeat(&zero)).zip(other.0.iter()).map(interpolate_each).collect(),
_ => self.0.iter().zip(other.0.iter().chain(repeat(&zero))).map(interpolate_each).collect(),
}))
}
}
/// Check if it's possible to do a direct numerical interpolation
/// between these two transform lists.
/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
fn can_interpolate_list(from_list: &[TransformOperation],
to_list: &[TransformOperation]) -> bool {
// Lists must be equal length
if from_list.len() != to_list.len() {
return false;
}
// Each transform operation must match primitive type in other list
for (from, to) in from_list.iter().zip(to_list) {
match (from, to) {
(&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
(&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
(&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
(&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
(&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
(&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
_ => {
return false;
}
}
}
true
}
/// Interpolate two transform lists.
/// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
fn interpolate_transform_list(from_list: &[TransformOperation],
to_list: &[TransformOperation],
time: f64) -> TransformList {
let mut result = vec![];
if can_interpolate_list(from_list, to_list) {
for (from, to) in from_list.iter().zip(to_list) {
match (from, to) {
(&TransformOperation::Matrix(from),
&TransformOperation::Matrix(_to)) => {
// TODO(gw): Implement matrix decomposition and interpolation
result.push(TransformOperation::Matrix(from));
}
(&TransformOperation::Skew(fx, fy),
&TransformOperation::Skew(tx, ty)) => {
let ix = fx.interpolate(&tx, time).unwrap();
let iy = fy.interpolate(&ty, time).unwrap();
result.push(TransformOperation::Skew(ix, iy));
}
(&TransformOperation::Translate(fx, fy, fz),
&TransformOperation::Translate(tx, ty, tz)) => {
let ix = fx.interpolate(&tx, time).unwrap();
let iy = fy.interpolate(&ty, time).unwrap();
let iz = fz.interpolate(&tz, time).unwrap();
result.push(TransformOperation::Translate(ix, iy, iz));
}
(&TransformOperation::Scale(fx, fy, fz),
&TransformOperation::Scale(tx, ty, tz)) => {
let ix = fx.interpolate(&tx, time).unwrap();
let iy = fy.interpolate(&ty, time).unwrap();
let iz = fz.interpolate(&tz, time).unwrap();
result.push(TransformOperation::Scale(ix, iy, iz));
}
(&TransformOperation::Rotate(fx, fy, fz, fa),
&TransformOperation::Rotate(_tx, _ty, _tz, _ta)) => {
// TODO(gw): Implement matrix decomposition and interpolation
result.push(TransformOperation::Rotate(fx, fy, fz, fa));
}
(&TransformOperation::Perspective(fd),
&TransformOperation::Perspective(_td)) => {
// TODO(gw): Implement matrix decomposition and interpolation
result.push(TransformOperation::Perspective(fd));
}
_ => {
// This should be unreachable due to the can_interpolate_list() call.
unreachable!();
}
}
}
} else {
// TODO(gw): Implement matrix decomposition and interpolation
result.extend_from_slice(from_list);
}
TransformList(Some(result))
}
/// Build an equivalent 'identity transform function list' based
/// on an existing transform list.
/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOperation> {
let mut result = vec!();
for operation in list {
match *operation {
TransformOperation::Matrix(..) => {
let identity = ComputedMatrix::identity();
result.push(TransformOperation::Matrix(identity));
}
TransformOperation::Skew(..) => {
result.push(TransformOperation::Skew(Angle(0.0), Angle(0.0)));
}
TransformOperation::Translate(..) => {
result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
LengthOrPercentage::zero(),
Au(0)));
}
TransformOperation::Scale(..) => {
result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
}
TransformOperation::Rotate(..) => {
result.push(TransformOperation::Rotate(0.0, 0.0, 1.0, Angle(0.0)));
}
TransformOperation::Perspective(..) => {
// http://dev.w3.org/csswg/css-transforms/#identity-transform-function
let identity = ComputedMatrix::identity();
result.push(TransformOperation::Matrix(identity));
}
}
}
result
}
impl Interpolate for BoxShadowList {
#[inline]
fn interpolate(&self, other: &Self, time: f64)
-> Option<Self> {
// The inset value must change
let mut zero = BoxShadow {
offset_x: Au(0),
offset_y: Au(0),
spread_radius: Au(0),
blur_radius: Au(0),
color: CSSParserColor::RGBA(RGBA {
red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0
}),
inset: false,
};
let max_len = cmp::max(self.0.len(), other.0.len());
let mut result = Vec::with_capacity(max_len);
for i in 0..max_len {
let shadow = match (self.0.get(i), other.0.get(i)) {
(Some(shadow), Some(other)) => {
match shadow.interpolate(other, time) {
Some(shadow) => shadow,
None => return None,
}
}
(Some(shadow), None) => {
zero.inset = shadow.inset;
shadow.interpolate(&zero, time).unwrap()
}
(None, Some(shadow)) => {
zero.inset = shadow.inset;
zero.interpolate(&shadow, time).unwrap()
}
(None, None) => unreachable!(),
};
result.push(shadow);
}
Some(BoxShadowList(result))
}
}
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
impl Interpolate for BoxShadow {
#[inline]
fn interpolate(&self, other: &Self, time: f64)
-> Option<Self> {
if self.inset != other.inset {
return None;
}
let x = match self.offset_x.interpolate(&other.offset_x, time) {
Some(x) => x,
None => return None,
};
let y = match self.offset_y.interpolate(&other.offset_y, time) {
Some(y) => y,
None => return None,
};
let color = match self.color.interpolate(&other.color, time) {
Some(c) => c,
None => return None,
};
let spread = match self.spread_radius.interpolate(&other.spread_radius, time) {
Some(s) => s,
None => return None,
};
let blur = match self.blur_radius.interpolate(&other.blur_radius, time) {
Some(r) => r,
None => return None,
};
Some(BoxShadow {
offset_x: x,
offset_y: y,
blur_radius: blur,
spread_radius: spread,
color: color,
inset: self.inset,
})
}
}
impl Interpolate for LengthOrNone {
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
match (*self, *other) {
(LengthOrNone::Length(ref len), LengthOrNone::Length(ref other)) =>
len.interpolate(&other, time).map(LengthOrNone::Length),
_ => None,
}
}
}
impl Interpolate for TransformOrigin {
fn interpolate(&self, other: &Self, time: f64) -> Option<Self> {
let horizontal = match self.horizontal.interpolate(&other.horizontal, time) {
Some(h) => h,
None => return None,
};
let vertical = match self.vertical.interpolate(&other.vertical, time) {
Some(v) => v,
None => return None,
};
let depth = match self.depth.interpolate(&other.depth, time) {
Some(d) => d,
None => return None,
};
Some(TransformOrigin {
horizontal: horizontal,
vertical: vertical,
depth: depth,
})
}
}
/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
impl Interpolate for TransformList {
#[inline]
fn interpolate(&self, other: &TransformList, time: f64) -> Option<TransformList> {
// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
let result = match (&self.0, &other.0) {
(&Some(ref from_list), &Some(ref to_list)) => {
// Two lists of transforms
interpolate_transform_list(from_list, &to_list, time)
}
(&Some(ref from_list), &None) => {
// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
let to_list = build_identity_transform_list(from_list);
interpolate_transform_list(from_list, &to_list, time)
}
(&None, &Some(ref to_list)) => {
// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
let from_list = build_identity_transform_list(to_list);
interpolate_transform_list(&from_list, to_list, time)
}
_ => {
// http://dev.w3.org/csswg/css-transforms/#none-none-animation
TransformList(None)
}
};
Some(result)
}
}

View file

@ -5,11 +5,12 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<% data.new_style_struct("Background", inherited=False) %>
${helpers.predefined_type(
"background-color", "CSSColor",
"::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")}
<%helpers:longhand name="background-image">
${helpers.predefined_type("background-color", "CSSColor",
"::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */",
animatable=True)}
<%helpers:longhand name="background-image" animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::specified::Image;
@ -71,7 +72,7 @@ ${helpers.predefined_type(
}
</%helpers:longhand>
<%helpers:longhand name="background-position">
<%helpers:longhand name="background-position" animatable="True">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;
@ -186,15 +187,23 @@ ${helpers.predefined_type(
}
</%helpers:longhand>
${helpers.single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")}
${helpers.single_keyword("background-repeat",
"repeat repeat-x repeat-y no-repeat",
animatable=False)}
${helpers.single_keyword("background-attachment", "scroll fixed" + (" local" if product == "gecko" else ""))}
${helpers.single_keyword("background-attachment",
"scroll fixed" + (" local" if product == "gecko" else ""),
animatable=False)}
${helpers.single_keyword("background-clip", "border-box padding-box content-box")}
${helpers.single_keyword("background-clip",
"border-box padding-box content-box",
animatable=False)}
${helpers.single_keyword("background-origin", "padding-box border-box content-box")}
${helpers.single_keyword("background-origin",
"padding-box border-box content-box",
animatable=False)}
<%helpers:longhand name="background-size">
<%helpers:longhand name="background-size" animatable="True">
use cssparser::{ToCss, Token};
use std::ascii::AsciiExt;
use std::fmt;

View file

@ -10,15 +10,19 @@
"bool") for side in ["top", "right", "bottom", "left"]]) %>
% for side in ["top", "right", "bottom", "left"]:
${helpers.predefined_type("border-%s-color" % side, "CSSColor", "::cssparser::Color::CurrentColor")}
${helpers.predefined_type("border-%s-color" % side, "CSSColor",
"::cssparser::Color::CurrentColor",
animatable=True)}
% endfor
% for side in ["top", "right", "bottom", "left"]:
${helpers.predefined_type("border-%s-style" % side, "BorderStyle", "specified::BorderStyle::none", need_clone=True)}
${helpers.predefined_type("border-%s-style" % side, "BorderStyle",
"specified::BorderStyle::none",
need_clone=True, animatable=False)}
% endfor
% for side in ["top", "right", "bottom", "left"]:
<%helpers:longhand name="border-${side}-width">
<%helpers:longhand name="border-${side}-width" animatable="True">
use app_units::Au;
use cssparser::ToCss;
use std::fmt;
@ -60,13 +64,15 @@
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
${helpers.predefined_type("border-" + corner + "-radius", "BorderRadiusSize",
"computed::BorderRadiusSize::zero()",
"parse")}
"parse",
animatable=True)}
% endfor
${helpers.single_keyword("box-decoration-break", "slice clone", products="gecko")}
${helpers.single_keyword("box-decoration-break", "slice clone",
products="gecko", animatable=False)}
${helpers.single_keyword("-moz-float-edge",
"content-box margin-box",
${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
gecko_ffi_name="mFloatEdge",
gecko_constant_prefix="NS_STYLE_FLOAT_EDGE",
products="gecko")}
products="gecko",
animatable=False)}

View file

@ -11,7 +11,10 @@
additional_methods=[Method("transition_count", "usize")]) %>
// TODO(SimonSapin): don't parse `inline-table`, since we don't support it
<%helpers:longhand name="display" need_clone="True" custom_cascade="${product == 'servo'}">
<%helpers:longhand name="display"
need_clone="True"
animatable="False"
custom_cascade="${product == 'servo'}">
<%
values = """inline block inline-block
table inline-table table-row-group table-header-group table-footer-group
@ -86,9 +89,14 @@
</%helpers:longhand>
${helpers.single_keyword("position", "static absolute relative fixed", need_clone=True, extra_gecko_values="sticky")}
${helpers.single_keyword("position", "static absolute relative fixed",
need_clone=True, extra_gecko_values="sticky", animatable=False)}
<%helpers:single_keyword_computed name="float" values="none left right" need_clone="True" gecko_ffi_name="mFloats">
<%helpers:single_keyword_computed name="float"
values="none left right"
animatable="False"
need_clone="True"
gecko_ffi_name="mFloats">
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
@ -107,9 +115,13 @@ ${helpers.single_keyword("position", "static absolute relative fixed", need_clon
</%helpers:single_keyword_computed>
${helpers.single_keyword("clear", "none left right both", gecko_ffi_name="mBreakType")}
${helpers.single_keyword("clear", "none left right both",
animatable=False, gecko_ffi_name="mBreakType")}
<%helpers:longhand name="-servo-display-for-hypothetical-box" derived_from="display" products="servo">
<%helpers:longhand name="-servo-display-for-hypothetical-box"
animatable="False"
derived_from="display"
products="servo">
pub use super::display::{SpecifiedValue, get_initial_value};
pub use super::display::{parse};
@ -125,7 +137,8 @@ ${helpers.single_keyword("clear", "none left right both", gecko_ffi_name="mBreak
</%helpers:longhand>
<%helpers:longhand name="vertical-align">
<%helpers:longhand name="vertical-align"
animatable="True">
use cssparser::ToCss;
use std::fmt;
@ -219,18 +232,21 @@ ${helpers.single_keyword("clear", "none left right both", gecko_ffi_name="mBreak
// CSS 2.1, Section 11 - Visual effects
// Non-standard, see https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box#Specifications
${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box", products="servo",
internal=True)}
${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box",
products="servo", animatable=False, internal=True)}
${helpers.single_keyword("overflow-clip-box", "padding-box content-box", products="gecko",
internal=True)}
${helpers.single_keyword("overflow-clip-box", "padding-box content-box",
products="gecko", animatable=False, internal=True)}
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=True,
gecko_constant_prefix="NS_STYLE_OVERFLOW")}
${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
need_clone=True, animatable=False,
gecko_constant_prefix="NS_STYLE_OVERFLOW")}
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
<%helpers:longhand name="overflow-y" need_clone="True">
<%helpers:longhand name="overflow-y"
need_clone="True"
animatable="False">
use super::overflow_x;
use cssparser::ToCss;
@ -269,7 +285,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
</%helpers:longhand>
// TODO(pcwalton): Multiple transitions.
<%helpers:longhand name="transition-duration">
<%helpers:longhand name="transition-duration" animatable="False">
use values::computed::ComputedValueAsSpecified;
use values::specified::Time;
pub use self::computed_value::T as SpecifiedValue;
@ -286,15 +303,6 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T(pub Vec<SingleComputedValue>);
impl ToComputedValue for T {
type ComputedValue = T;
#[inline]
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> T {
(*self).clone()
}
}
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.0.is_empty() {
@ -311,6 +319,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
}
}
impl ComputedValueAsSpecified for SpecifiedValue {}
#[inline]
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
Time::parse(input)
@ -333,7 +343,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
// TODO(pcwalton): Lots more timing functions.
// TODO(pcwalton): Multiple transitions.
<%helpers:longhand name="transition-timing-function">
<%helpers:longhand name="transition-timing-function" animatable="False">
use self::computed_value::{StartEnd, TransitionTimingFunction};
use euclid::point::Point2D;
@ -531,170 +541,17 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
}
</%helpers:longhand>
// TODO(pcwalton): Lots more properties.
<%helpers:longhand name="transition-property">
use self::computed_value::TransitionProperty;
<%helpers:longhand name="transition-property" animatable="False">
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
pub use self::TransitionProperty as SingleComputedValue;
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum TransitionProperty {
All,
BackgroundColor,
BackgroundPosition,
BorderBottomColor,
BorderBottomWidth,
BorderLeftColor,
BorderLeftWidth,
BorderRightColor,
BorderRightWidth,
BorderSpacing,
BorderTopColor,
BorderTopWidth,
Bottom,
Color,
Clip,
FontSize,
FontWeight,
Height,
Left,
LetterSpacing,
LineHeight,
MarginBottom,
MarginLeft,
MarginRight,
MarginTop,
MaxHeight,
MaxWidth,
MinHeight,
MinWidth,
Opacity,
OutlineColor,
OutlineWidth,
PaddingBottom,
PaddingLeft,
PaddingRight,
PaddingTop,
Right,
TextIndent,
TextShadow,
Top,
Transform,
VerticalAlign,
Visibility,
Width,
WordSpacing,
ZIndex,
}
pub static ALL_TRANSITION_PROPERTIES: [TransitionProperty; 45] = [
TransitionProperty::BackgroundColor,
TransitionProperty::BackgroundPosition,
TransitionProperty::BorderBottomColor,
TransitionProperty::BorderBottomWidth,
TransitionProperty::BorderLeftColor,
TransitionProperty::BorderLeftWidth,
TransitionProperty::BorderRightColor,
TransitionProperty::BorderRightWidth,
TransitionProperty::BorderSpacing,
TransitionProperty::BorderTopColor,
TransitionProperty::BorderTopWidth,
TransitionProperty::Bottom,
TransitionProperty::Color,
TransitionProperty::Clip,
TransitionProperty::FontSize,
TransitionProperty::FontWeight,
TransitionProperty::Height,
TransitionProperty::Left,
TransitionProperty::LetterSpacing,
TransitionProperty::LineHeight,
TransitionProperty::MarginBottom,
TransitionProperty::MarginLeft,
TransitionProperty::MarginRight,
TransitionProperty::MarginTop,
TransitionProperty::MaxHeight,
TransitionProperty::MaxWidth,
TransitionProperty::MinHeight,
TransitionProperty::MinWidth,
TransitionProperty::Opacity,
TransitionProperty::OutlineColor,
TransitionProperty::OutlineWidth,
TransitionProperty::PaddingBottom,
TransitionProperty::PaddingLeft,
TransitionProperty::PaddingRight,
TransitionProperty::PaddingTop,
TransitionProperty::Right,
TransitionProperty::TextIndent,
TransitionProperty::TextShadow,
TransitionProperty::Top,
TransitionProperty::Transform,
TransitionProperty::VerticalAlign,
TransitionProperty::Visibility,
TransitionProperty::Width,
TransitionProperty::WordSpacing,
TransitionProperty::ZIndex,
];
impl ToCss for TransitionProperty {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
TransitionProperty::All => dest.write_str("all"),
TransitionProperty::BackgroundColor => dest.write_str("background-color"),
TransitionProperty::BackgroundPosition => dest.write_str("background-position"),
TransitionProperty::BorderBottomColor => dest.write_str("border-bottom-color"),
TransitionProperty::BorderBottomWidth => dest.write_str("border-bottom-width"),
TransitionProperty::BorderLeftColor => dest.write_str("border-left-color"),
TransitionProperty::BorderLeftWidth => dest.write_str("border-left-width"),
TransitionProperty::BorderRightColor => dest.write_str("border-right-color"),
TransitionProperty::BorderRightWidth => dest.write_str("border-right-width"),
TransitionProperty::BorderSpacing => dest.write_str("border-spacing"),
TransitionProperty::BorderTopColor => dest.write_str("border-top-color"),
TransitionProperty::BorderTopWidth => dest.write_str("border-top-width"),
TransitionProperty::Bottom => dest.write_str("bottom"),
TransitionProperty::Color => dest.write_str("color"),
TransitionProperty::Clip => dest.write_str("clip"),
TransitionProperty::FontSize => dest.write_str("font-size"),
TransitionProperty::FontWeight => dest.write_str("font-weight"),
TransitionProperty::Height => dest.write_str("height"),
TransitionProperty::Left => dest.write_str("left"),
TransitionProperty::LetterSpacing => dest.write_str("letter-spacing"),
TransitionProperty::LineHeight => dest.write_str("line-height"),
TransitionProperty::MarginBottom => dest.write_str("margin-bottom"),
TransitionProperty::MarginLeft => dest.write_str("margin-left"),
TransitionProperty::MarginRight => dest.write_str("margin-right"),
TransitionProperty::MarginTop => dest.write_str("margin-top"),
TransitionProperty::MaxHeight => dest.write_str("max-height"),
TransitionProperty::MaxWidth => dest.write_str("max-width"),
TransitionProperty::MinHeight => dest.write_str("min-height"),
TransitionProperty::MinWidth => dest.write_str("min-width"),
TransitionProperty::Opacity => dest.write_str("opacity"),
TransitionProperty::OutlineColor => dest.write_str("outline-color"),
TransitionProperty::OutlineWidth => dest.write_str("outline-width"),
TransitionProperty::PaddingBottom => dest.write_str("padding-bottom"),
TransitionProperty::PaddingLeft => dest.write_str("padding-left"),
TransitionProperty::PaddingRight => dest.write_str("padding-right"),
TransitionProperty::PaddingTop => dest.write_str("padding-top"),
TransitionProperty::Right => dest.write_str("right"),
TransitionProperty::TextIndent => dest.write_str("text-indent"),
TransitionProperty::TextShadow => dest.write_str("text-shadow"),
TransitionProperty::Top => dest.write_str("top"),
TransitionProperty::Transform => dest.write_str("transform"),
TransitionProperty::VerticalAlign => dest.write_str("vertical-align"),
TransitionProperty::Visibility => dest.write_str("visibility"),
TransitionProperty::Width => dest.write_str("width"),
TransitionProperty::WordSpacing => dest.write_str("word-spacing"),
TransitionProperty::ZIndex => dest.write_str("z-index"),
}
}
}
// NB: Can't generate the type here because it needs all the longhands
// generated beforehand.
pub use properties::animated_properties::TransitionProperty;
pub use properties::animated_properties::TransitionProperty as SingleComputedValue;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@ -721,61 +578,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
computed_value::T(Vec::new())
}
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
match_ignore_ascii_case! {
try!(input.expect_ident()),
"all" => Ok(TransitionProperty::All),
"background-color" => Ok(TransitionProperty::BackgroundColor),
"background-position" => Ok(TransitionProperty::BackgroundPosition),
"border-bottom-color" => Ok(TransitionProperty::BorderBottomColor),
"border-bottom-width" => Ok(TransitionProperty::BorderBottomWidth),
"border-left-color" => Ok(TransitionProperty::BorderLeftColor),
"border-left-width" => Ok(TransitionProperty::BorderLeftWidth),
"border-right-color" => Ok(TransitionProperty::BorderRightColor),
"border-right-width" => Ok(TransitionProperty::BorderRightWidth),
"border-spacing" => Ok(TransitionProperty::BorderSpacing),
"border-top-color" => Ok(TransitionProperty::BorderTopColor),
"border-top-width" => Ok(TransitionProperty::BorderTopWidth),
"bottom" => Ok(TransitionProperty::Bottom),
"color" => Ok(TransitionProperty::Color),
"clip" => Ok(TransitionProperty::Clip),
"font-size" => Ok(TransitionProperty::FontSize),
"font-weight" => Ok(TransitionProperty::FontWeight),
"height" => Ok(TransitionProperty::Height),
"left" => Ok(TransitionProperty::Left),
"letter-spacing" => Ok(TransitionProperty::LetterSpacing),
"line-height" => Ok(TransitionProperty::LineHeight),
"margin-bottom" => Ok(TransitionProperty::MarginBottom),
"margin-left" => Ok(TransitionProperty::MarginLeft),
"margin-right" => Ok(TransitionProperty::MarginRight),
"margin-top" => Ok(TransitionProperty::MarginTop),
"max-height" => Ok(TransitionProperty::MaxHeight),
"max-width" => Ok(TransitionProperty::MaxWidth),
"min-height" => Ok(TransitionProperty::MinHeight),
"min-width" => Ok(TransitionProperty::MinWidth),
"opacity" => Ok(TransitionProperty::Opacity),
"outline-color" => Ok(TransitionProperty::OutlineColor),
"outline-width" => Ok(TransitionProperty::OutlineWidth),
"padding-bottom" => Ok(TransitionProperty::PaddingBottom),
"padding-left" => Ok(TransitionProperty::PaddingLeft),
"padding-right" => Ok(TransitionProperty::PaddingRight),
"padding-top" => Ok(TransitionProperty::PaddingTop),
"right" => Ok(TransitionProperty::Right),
"text-indent" => Ok(TransitionProperty::TextIndent),
"text-shadow" => Ok(TransitionProperty::TextShadow),
"top" => Ok(TransitionProperty::Top),
"transform" => Ok(TransitionProperty::Transform),
"vertical-align" => Ok(TransitionProperty::VerticalAlign),
"visibility" => Ok(TransitionProperty::Visibility),
"width" => Ok(TransitionProperty::Width),
"word-spacing" => Ok(TransitionProperty::WordSpacing),
"z-index" => Ok(TransitionProperty::ZIndex),
_ => Err(())
}
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
Ok(SpecifiedValue(try!(input.parse_comma_separated(SingleSpecifiedValue::parse))))
}
impl ToComputedValue for SpecifiedValue {
@ -788,54 +592,208 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
}
</%helpers:longhand>
<%helpers:longhand name="transition-delay">
<%helpers:longhand name="transition-delay" animatable="False">
pub use properties::longhands::transition_duration::{SingleSpecifiedValue, SpecifiedValue};
pub use properties::longhands::transition_duration::{computed_value};
pub use properties::longhands::transition_duration::{get_initial_single_value};
pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
</%helpers:longhand>
<%helpers:longhand name="animation-name" animatable="False">
use values::computed::ComputedValueAsSpecified;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
use string_cache::Atom;
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct T(pub Vec<Atom>);
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.0.is_empty() {
return dest.write_str("none")
}
for (i, name) in self.0.iter().enumerate() {
if i != 0 {
try!(dest.write_str(", "));
}
// NB: to_string() needed due to geckolib backend.
try!(dest.write_str(&*name.to_string()));
}
Ok(())
}
}
}
pub use self::computed_value::T as SpecifiedValue;
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(vec![])
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
use std::borrow::Cow;
Ok(SpecifiedValue(try!(input.parse_comma_separated(|input| {
input.expect_ident().map(Atom::from)
}))))
}
impl ComputedValueAsSpecified for SpecifiedValue {}
</%helpers:longhand>
<%helpers:longhand name="animation-duration" animatable="False">
pub use super::transition_duration::computed_value;
pub use super::transition_duration::{parse, get_initial_value};
pub use super::transition_duration::SpecifiedValue;
</%helpers:longhand>
<%helpers:longhand name="animation-timing-function" animatable="False">
pub use super::transition_timing_function::computed_value;
pub use super::transition_timing_function::{parse, get_initial_value};
pub use super::transition_timing_function::SpecifiedValue;
</%helpers:longhand>
<%helpers:longhand name="animation-iteration-count" animatable="False">
use values::computed::ComputedValueAsSpecified;
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub enum AnimationIterationCount {
Number(u32),
Infinite,
}
impl ToCss for AnimationIterationCount {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
AnimationIterationCount::Number(n) => write!(dest, "{}", n),
AnimationIterationCount::Infinite => dest.write_str("infinite"),
}
}
}
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
pub struct T(pub Vec<AnimationIterationCount>);
impl ToCss for T {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if self.0.is_empty() {
return dest.write_str("none")
}
for (i, value) in self.0.iter().enumerate() {
if i != 0 {
try!(dest.write_str(", "))
}
try!(value.to_css(dest))
}
Ok(())
}
}
}
pub use self::computed_value::AnimationIterationCount;
pub use self::computed_value::T as SpecifiedValue;
pub fn parse_one(input: &mut Parser) -> Result<AnimationIterationCount, ()> {
if input.try(|input| input.expect_ident_matching("infinite")).is_ok() {
Ok(AnimationIterationCount::Infinite)
} else {
let number = try!(input.expect_integer());
if number < 0 {
return Err(());
}
Ok(AnimationIterationCount::Number(number as u32))
}
}
#[inline]
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
}
pub fn get_initial_value() -> computed_value::T {
computed_value::T(vec![AnimationIterationCount::Number(1)])
}
impl ComputedValueAsSpecified for SpecifiedValue {}
</%helpers:longhand>
${helpers.keyword_list("animation-direction",
"normal reverse alternate alternate-reverse",
animatable=False)}
${helpers.keyword_list("animation-play-state",
"running paused",
need_clone=True,
animatable=False)}
${helpers.keyword_list("animation-fill-mode",
"none forwards backwards both",
animatable=False)}
<%helpers:longhand name="animation-delay" animatable="False">
pub use super::transition_duration::computed_value;
pub use super::transition_duration::{parse, get_initial_value};
pub use super::transition_duration::SpecifiedValue;
</%helpers:longhand>
// CSSOM View Module
// https://www.w3.org/TR/cssom-view-1/
${helpers.single_keyword("scroll-behavior",
"auto smooth",
products="gecko")}
products="gecko",
animatable=False)}
// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x
${helpers.single_keyword("scroll-snap-type-x",
"none mandatory proximity",
products="gecko",
gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE")}
gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE",
animatable=False)}
// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-y
${helpers.single_keyword("scroll-snap-type-y",
"none mandatory proximity",
products="gecko",
gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE")}
gecko_constant_prefix="NS_STYLE_SCROLL_SNAP_TYPE",
animatable=False)}
// Compositing and Blending Level 1
// http://www.w3.org/TR/compositing-1/
${helpers.single_keyword("isolation",
"auto isolate",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.single_keyword("page-break-after",
"auto always avoid left right",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.single_keyword("page-break-before",
"auto always avoid left right",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.single_keyword("page-break-inside",
"auto avoid",
products="gecko",
gecko_ffi_name="mBreakInside",
gecko_constant_prefix="NS_STYLE_PAGE_BREAK")}
gecko_constant_prefix="NS_STYLE_PAGE_BREAK",
animatable=False)}
// CSS Basic User Interface Module Level 3
// http://dev.w3.org/csswg/css-ui/
${helpers.single_keyword("resize",
"none both horizontal vertical",
products="gecko")}
products="gecko",
animatable=False)}
// Non-standard
${helpers.single_keyword("-moz-appearance",
@ -862,10 +820,11 @@ ${helpers.single_keyword("-moz-appearance",
""",
gecko_ffi_name="mAppearance",
gecko_constant_prefix="NS_THEME",
products="gecko")}
products="gecko",
animatable=False)}
// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding
<%helpers:longhand name="-moz-binding" products="gecko">
<%helpers:longhand name="-moz-binding" products="gecko" animatable="False">
use cssparser::{CssStringWriter, ToCss};
use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
use std::fmt::{self, Write};

View file

@ -6,7 +6,7 @@
<% data.new_style_struct("Color", inherited=True) %>
<%helpers:raw_longhand name="color" need_clone="True">
<%helpers:raw_longhand name="color" need_clone="True" animatable="True">
use cssparser::Color as CSSParserColor;
use cssparser::RGBA;
use values::specified::{CSSColor, CSSRGBA};

View file

@ -6,7 +6,8 @@
<% data.new_style_struct("Column", inherited=False) %>
<%helpers:longhand name="column-width" experimental="True">
// FIXME: This prop should be animatable.
<%helpers:longhand name="column-width" experimental="True" animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;
@ -70,7 +71,8 @@
}
</%helpers:longhand>
<%helpers:longhand name="column-count" experimental="True">
// FIXME: This prop should be animatable.
<%helpers:longhand name="column-count" experimental="True" animatable="False">
use cssparser::ToCss;
use std::fmt;
@ -137,7 +139,8 @@
}
</%helpers:longhand>
<%helpers:longhand name="column-gap" experimental="True">
// FIXME: This prop should be animatable.
<%helpers:longhand name="column-gap" experimental="True" animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;

View file

@ -6,7 +6,7 @@
<% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
<%helpers:longhand name="content">
<%helpers:longhand name="content" animatable="False">
use cssparser::Token;
use std::ascii::AsciiExt;
use values::computed::ComputedValueAsSpecified;
@ -171,7 +171,7 @@
}
</%helpers:longhand>
<%helpers:longhand name="counter-increment">
<%helpers:longhand name="counter-increment" animatable="False">
use std::fmt;
use super::content;
use values::computed::ComputedValueAsSpecified;
@ -241,7 +241,7 @@
}
</%helpers:longhand>
<%helpers:longhand name="counter-reset">
<%helpers:longhand name="counter-reset" animatable="False">
pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
use super::counter_increment::{parse_common};

View file

@ -9,9 +9,10 @@
${helpers.predefined_type("opacity",
"Opacity",
"1.0")}
"1.0",
animatable=True)}
<%helpers:longhand name="box-shadow">
<%helpers:longhand name="box-shadow" animatable="True">
use cssparser::{self, ToCss};
use std::fmt;
use values::LocalToCss;
@ -223,7 +224,8 @@ ${helpers.predefined_type("opacity",
}
</%helpers:longhand>
<%helpers:longhand name="clip">
// FIXME: This prop should be animatable
<%helpers:longhand name="clip" animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;
@ -394,7 +396,8 @@ ${helpers.predefined_type("opacity",
}
</%helpers:longhand>
<%helpers:longhand name="filter">
// FIXME: This prop should be animatable
<%helpers:longhand name="filter" animatable="False">
//pub use self::computed_value::T as SpecifiedValue;
use cssparser::ToCss;
use std::fmt;
@ -630,7 +633,7 @@ ${helpers.predefined_type("opacity",
}
</%helpers:longhand>
<%helpers:longhand name="transform">
<%helpers:longhand name="transform" animatable="True">
use app_units::Au;
use values::CSSFloat;
@ -1174,13 +1177,20 @@ pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result<OriginParse
}
}
${helpers.single_keyword("backface-visibility", "visible hidden")}
${helpers.single_keyword("backface-visibility",
"visible hidden",
animatable=False)}
${helpers.single_keyword("transform-box", "border-box fill-box view-box", products="gecko")}
${helpers.single_keyword("transform-box",
"border-box fill-box view-box",
products="gecko",
animatable=False)}
${helpers.single_keyword("transform-style", "auto flat preserve-3d")}
${helpers.single_keyword("transform-style",
"auto flat preserve-3d",
animatable=False)}
<%helpers:longhand name="transform-origin">
<%helpers:longhand name="transform-origin" animatable="True">
use app_units::Au;
use values::LocalToCss;
use values::specified::{Length, LengthOrPercentage, Percentage};
@ -1261,10 +1271,12 @@ ${helpers.single_keyword("transform-style", "auto flat preserve-3d")}
</%helpers:longhand>
${helpers.predefined_type("perspective",
"LengthOrNone",
"computed::LengthOrNone::None")}
"LengthOrNone",
"computed::LengthOrNone::None",
animatable=True)}
<%helpers:longhand name="perspective-origin">
// FIXME: This prop should be animatable
<%helpers:longhand name="perspective-origin" animatable="False">
use values::specified::{LengthOrPercentage, Percentage};
use cssparser::ToCss;
@ -1337,6 +1349,7 @@ ${helpers.predefined_type("perspective",
</%helpers:longhand>
${helpers.single_keyword("mix-blend-mode",
"""normal multiply screen overlay darken lighten color-dodge
color-burn hard-light soft-light difference exclusion hue
saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND")}
"""normal multiply screen overlay darken lighten color-dodge
color-burn hard-light soft-light difference exclusion hue
saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND",
animatable=False)}

View file

@ -8,7 +8,7 @@
<% data.new_style_struct("Font",
inherited=True,
additional_methods=[Method("compute_font_hash", is_mut=True)]) %>
<%helpers:longhand name="font-family">
<%helpers:longhand name="font-family" animatable="False">
use self::computed_value::FontFamily;
use values::computed::ComputedValueAsSpecified;
pub use self::computed_value::T as SpecifiedValue;
@ -117,10 +117,15 @@
</%helpers:longhand>
${helpers.single_keyword("font-style", "normal italic oblique", gecko_constant_prefix="NS_FONT_STYLE")}
${helpers.single_keyword("font-variant", "normal small-caps")}
${helpers.single_keyword("font-style",
"normal italic oblique",
gecko_constant_prefix="NS_FONT_STYLE",
animatable=False)}
${helpers.single_keyword("font-variant",
"normal small-caps",
animatable=False)}
<%helpers:longhand name="font-weight" need_clone="True">
<%helpers:longhand name="font-weight" need_clone="True" animatable="True">
use cssparser::ToCss;
use std::fmt;
@ -241,7 +246,7 @@ ${helpers.single_keyword("font-variant", "normal small-caps")}
}
</%helpers:longhand>
<%helpers:longhand name="font-size" need_clone="True">
<%helpers:longhand name="font-size" need_clone="True" animatable="True">
use app_units::Au;
use cssparser::ToCss;
use std::fmt;
@ -307,8 +312,14 @@ ${helpers.single_keyword("font-variant", "normal small-caps")}
}
</%helpers:longhand>
// FIXME: This prop should be animatable
${helpers.single_keyword("font-stretch",
"normal ultra-condensed extra-condensed condensed semi-condensed semi-expanded \
expanded extra-expanded ultra-expanded")}
"normal ultra-condensed extra-condensed condensed \
semi-condensed semi-expanded expanded extra-expanded \
ultra-expanded",
animatable=False)}
${helpers.single_keyword("font-kerning", "auto none normal", products="gecko")}
${helpers.single_keyword("font-kerning",
"auto none normal",
products="gecko",
animatable=False)}

View file

@ -6,20 +6,22 @@
<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>
${helpers.single_keyword("direction", "ltr rtl", need_clone=True)}
${helpers.single_keyword("direction", "ltr rtl", need_clone=True, animatable=False)}
// TODO: collapse. Well, do tables first.
${helpers.single_keyword("visibility",
"visible hidden",
extra_gecko_values="collapse",
gecko_ffi_name="mVisible")}
gecko_ffi_name="mVisible",
animatable=True)}
// CSS Writing Modes Level 3
// http://dev.w3.org/csswg/css-writing-modes/
${helpers.single_keyword("writing-mode",
"horizontal-tb vertical-rl vertical-lr",
experimental=True,
need_clone=True)}
need_clone=True,
animatable=False)}
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
@ -29,13 +31,16 @@ ${helpers.single_keyword("text-orientation",
experimental=True,
need_clone=True,
extra_gecko_values="mixed upright",
extra_servo_values="sideways-right sideways-left")}
extra_servo_values="sideways-right sideways-left",
animatable=False)}
// CSS Color Module Level 4
// https://drafts.csswg.org/css-color/
${helpers.single_keyword("color-adjust", "economy exact", products="gecko")}
${helpers.single_keyword("color-adjust",
"economy exact", products="gecko",
animatable=False)}
<%helpers:longhand name="image-rendering">
<%helpers:longhand name="image-rendering" animatable="False">
pub mod computed_value {
use cssparser::ToCss;
use std::fmt;
@ -92,7 +97,10 @@ ${helpers.single_keyword("color-adjust", "economy exact", products="gecko")}
// Used in the bottom-up flow construction traversal to avoid constructing flows for
// descendants of nodes with `display: none`.
<%helpers:longhand name="-servo-under-display-none" derived_from="display" products="servo">
<%helpers:longhand name="-servo-under-display-none"
derived_from="display"
products="servo"
animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::computed::ComputedValueAsSpecified;

View file

@ -10,36 +10,52 @@
inherited=True,
gecko_name="SVG") %>
// TODO(emilio): Should some of these types be animatable?
// Section 10 - Text
${helpers.single_keyword("text-anchor", "start middle end", products="gecko")}
${helpers.single_keyword("text-anchor",
"start middle end",
products="gecko",
animatable=False)}
// Section 11 - Painting: Filling, Stroking and Marker Symbols
${helpers.single_keyword("color-interpolation", "auto sRGB linearRGB", products="gecko")}
${helpers.single_keyword("color-interpolation-filters",
${helpers.single_keyword("color-interpolation",
"auto sRGB linearRGB",
products="gecko",
gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION")}
animatable=False)}
${helpers.predefined_type("fill-opacity", "Opacity", "1.0", products="gecko")}
${helpers.single_keyword("color-interpolation-filters", "auto sRGB linearRGB",
products="gecko",
gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION",
animatable=False)}
${helpers.single_keyword("fill-rule", "nonzero evenodd", products="gecko")}
${helpers.predefined_type("fill-opacity", "Opacity", "1.0",
products="gecko", animatable=False)}
${helpers.single_keyword("fill-rule", "nonzero evenodd",
products="gecko", animatable=False)}
${helpers.single_keyword("shape-rendering",
"auto optimizeSpeed crispEdges geometricPrecision",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.single_keyword("stroke-linecap", "butt round square", products="gecko")}
${helpers.single_keyword("stroke-linecap", "butt round square",
products="gecko", animatable=False)}
${helpers.single_keyword("stroke-linejoin", "miter round bevel", products="gecko")}
${helpers.single_keyword("stroke-linejoin", "miter round bevel",
products="gecko", animatable=False)}
${helpers.predefined_type("stroke-miterlimit", "Number", "4.0", "parse_at_least_one",
products="gecko")}
${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
"parse_at_least_one", products="gecko",
animatable=False)}
${helpers.predefined_type("stroke-opacity", "Opacity", "1.0", products="gecko")}
${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
products="gecko", animatable=False)}
// Section 14 - Clipping, Masking and Compositing
${helpers.single_keyword("clip-rule", "nonzero evenodd",
products="gecko",
gecko_constant_prefix="NS_STYLE_FILL_RULE")}
gecko_constant_prefix="NS_STYLE_FILL_RULE",
animatable=False)}

View file

@ -6,11 +6,17 @@
<% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>
${helpers.single_keyword("border-collapse", "separate collapse", gecko_constant_prefix="NS_STYLE_BORDER")}
${helpers.single_keyword("empty-cells", "show hide", gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS")}
${helpers.single_keyword("caption-side", "top bottom", extra_gecko_values="right left top-outside bottom-outside")}
${helpers.single_keyword("border-collapse", "separate collapse",
gecko_constant_prefix="NS_STYLE_BORDER",
animatable=False)}
${helpers.single_keyword("empty-cells", "show hide",
gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
animatable=False)}
${helpers.single_keyword("caption-side", "top bottom",
extra_gecko_values="right left top-outside bottom-outside",
animatable=False)}
<%helpers:longhand name="border-spacing">
<%helpers:longhand name="border-spacing" animatable="False">
use app_units::Au;
use values::LocalToCss;

View file

@ -6,7 +6,7 @@
<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
<%helpers:longhand name="line-height">
<%helpers:longhand name="line-height" animatable="True">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;
@ -120,7 +120,7 @@
}
</%helpers:longhand>
<%helpers:longhand name="text-align">
<%helpers:longhand name="text-align" animatable="False">
pub use self::computed_value::T as SpecifiedValue;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
@ -179,7 +179,8 @@
}
</%helpers:longhand>
<%helpers:longhand name="letter-spacing">
// FIXME: This prop should be animatable.
<%helpers:longhand name="letter-spacing" animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;
@ -243,7 +244,7 @@
}
</%helpers:longhand>
<%helpers:longhand name="word-spacing">
<%helpers:longhand name="word-spacing" animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::LocalToCss;
@ -309,27 +310,33 @@
${helpers.predefined_type("text-indent",
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))")}
"computed::LengthOrPercentage::Length(Au(0))",
animatable=True)}
// Also known as "word-wrap" (which is more popular because of IE), but this is the preferred
// name per CSS-TEXT 6.2.
${helpers.single_keyword("overflow-wrap",
"normal break-word",
gecko_constant_prefix="NS_STYLE_OVERFLOWWRAP")}
gecko_constant_prefix="NS_STYLE_OVERFLOWWRAP",
animatable=False)}
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
${helpers.single_keyword("word-break",
"normal break-all",
extra_gecko_values="keep-all",
gecko_constant_prefix="NS_STYLE_WORDBREAK")}
gecko_constant_prefix="NS_STYLE_WORDBREAK",
animatable=False)}
// TODO(pcwalton): Support `text-justify: distribute`.
${helpers.single_keyword("text-justify",
"auto none inter-word",
products="servo")}
products="servo",
animatable=False)}
<%helpers:longhand name="-servo-text-decorations-in-effect"
derived_from="display text-decoration" need_clone="True" products="servo">
derived_from="display text-decoration"
need_clone="True" products="servo"
animatable="False">
use cssparser::{RGBA, ToCss};
use std::fmt;
@ -410,8 +417,10 @@ ${helpers.single_keyword("text-justify",
}
</%helpers:longhand>
<%helpers:single_keyword_computed name="white-space" values="normal pre nowrap pre-wrap pre-line",
gecko_constant_prefix="NS_STYLE_WHITESPACE">
<%helpers:single_keyword_computed name="white-space"
values="normal pre nowrap pre-wrap pre-line"
gecko_constant_prefix="NS_STYLE_WHITESPACE"
animatable="False">
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
@ -448,7 +457,7 @@ ${helpers.single_keyword("text-justify",
}
</%helpers:single_keyword_computed>
<%helpers:longhand name="text-shadow">
<%helpers:longhand name="text-shadow" animatable="True">
use cssparser::{self, ToCss};
use std::fmt;
use values::LocalToCss;
@ -635,16 +644,22 @@ ${helpers.single_keyword("text-justify",
// TODO(pcwalton): `full-width`
${helpers.single_keyword("text-transform",
"none capitalize uppercase lowercase",
extra_gecko_values="full-width")}
extra_gecko_values="full-width",
animatable=False)}
${helpers.single_keyword("text-rendering", "auto optimizespeed optimizelegibility geometricprecision")}
${helpers.single_keyword("text-rendering",
"auto optimizespeed optimizelegibility geometricprecision",
animatable=False)}
// CSS Text Module Level 3
// https://www.w3.org/TR/css-text-3/
${helpers.single_keyword("hyphens", "none manual auto", products="gecko")}
${helpers.single_keyword("hyphens", "none manual auto",
products="gecko", animatable=False)}
// CSS Ruby Layout Module Level 1
// https://www.w3.org/TR/css-ruby-1/
${helpers.single_keyword("ruby-align", "start center space-between space-around", products="gecko")}
${helpers.single_keyword("ruby-align", "start center space-between space-around",
products="gecko", animatable=False)}
${helpers.single_keyword("ruby-position", "over under", products="gecko")}
${helpers.single_keyword("ruby-position", "over under",
products="gecko", animatable=False)}

View file

@ -6,7 +6,7 @@
<% data.new_style_struct("List", inherited=True) %>
${helpers.single_keyword("list-style-position", "outside inside")}
${helpers.single_keyword("list-style-position", "outside inside", animatable=False)}
// TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
//
@ -23,9 +23,10 @@ ${helpers.single_keyword("list-style-type", """
myanmar oriya persian telugu thai tibetan cjk-earthly-branch
cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana
katakana-iroha""",
gecko_constant_prefix="NS_STYLE_LIST_STYLE")}
gecko_constant_prefix="NS_STYLE_LIST_STYLE",
animatable=False)}
<%helpers:longhand name="list-style-image">
<%helpers:longhand name="list-style-image" animatable="False">
use cssparser::{ToCss, Token};
use std::fmt;
use url::Url;
@ -92,7 +93,7 @@ ${helpers.single_keyword("list-style-type", """
}
</%helpers:longhand>
<%helpers:longhand name="quotes">
<%helpers:longhand name="quotes" animatable="False">
use std::borrow::Cow;
use std::fmt;
use values::computed::ComputedValueAsSpecified;

View file

@ -8,5 +8,6 @@
% for side in ["top", "right", "bottom", "left"]:
${helpers.predefined_type("margin-" + side, "LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Length(Au(0))")}
"computed::LengthOrPercentageOrAuto::Length(Au(0))",
animatable=True)}
% endfor

View file

@ -10,9 +10,10 @@
additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
// TODO(pcwalton): `invert`
${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::CurrentColor")}
${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::CurrentColor",
animatable=True)}
<%helpers:longhand name="outline-style" need_clone="True">
<%helpers:longhand name="outline-style" need_clone="True" animatable="False">
pub use values::specified::BorderStyle as SpecifiedValue;
pub fn get_initial_value() -> SpecifiedValue { SpecifiedValue::none }
pub mod computed_value {
@ -26,7 +27,7 @@ ${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::Curr
}
</%helpers:longhand>
<%helpers:longhand name="outline-width">
<%helpers:longhand name="outline-width" animatable="True">
use app_units::Au;
use cssparser::ToCss;
use std::fmt;
@ -60,10 +61,12 @@ ${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::Curr
</%helpers:longhand>
// The -moz-outline-radius-* properties are non-standard and not on a standards track.
// TODO: Should they animate?
% for corner in ["topleft", "topright", "bottomright", "bottomleft"]:
${helpers.predefined_type("-moz-outline-radius-" + corner, "BorderRadiusSize",
"computed::BorderRadiusSize::zero()",
"parse", products="gecko")}
"parse", products="gecko",
animatable=False)}
% endfor
${helpers.predefined_type("outline-offset", "Length", "Au(0)")}
${helpers.predefined_type("outline-offset", "Length", "Au(0)", animatable=True)}

View file

@ -9,5 +9,6 @@
% for side in ["top", "right", "bottom", "left"]:
${helpers.predefined_type("padding-" + side, "LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
"parse_non_negative")}
"parse_non_negative",
animatable=True)}
% endfor

View file

@ -6,7 +6,7 @@
<% data.new_style_struct("Pointing", inherited=True, gecko_name="UserInterface") %>
<%helpers:longhand name="cursor">
<%helpers:longhand name="cursor" animatable="False">
pub use self::computed_value::T as SpecifiedValue;
use values::computed::ComputedValueAsSpecified;
@ -54,16 +54,20 @@
// NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
// is nonstandard, slated for CSS4-UI.
// TODO(pcwalton): SVG-only values.
${helpers.single_keyword("pointer-events", "auto none")}
${helpers.single_keyword("pointer-events", "auto none", animatable=False)}
${helpers.single_keyword("-moz-user-input", "none enabled disabled", products="gecko",
gecko_ffi_name="mUserInput", gecko_constant_prefix="NS_STYLE_USER_INPUT")}
${helpers.single_keyword("-moz-user-input", "none enabled disabled",
products="gecko", gecko_ffi_name="mUserInput",
gecko_constant_prefix="NS_STYLE_USER_INPUT",
animatable=False)}
${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only", products="gecko",
gecko_ffi_name="mUserModify", gecko_constant_prefix="NS_STYLE_USER_MODIFY")}
${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only",
products="gecko", gecko_ffi_name="mUserModify",
gecko_constant_prefix="NS_STYLE_USER_MODIFY",
animatable=False)}
${helpers.single_keyword("-moz-user-focus",
"ignore normal select-after select-before select-menu select-same select-all none",
products="gecko",
gecko_ffi_name="mUserFocus",
gecko_constant_prefix="NS_STYLE_USER_FOCUS")}
products="gecko", gecko_ffi_name="mUserFocus",
gecko_constant_prefix="NS_STYLE_USER_FOCUS",
animatable=False)}

View file

@ -8,10 +8,11 @@
% for side in ["top", "right", "bottom", "left"]:
${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto")}
"computed::LengthOrPercentageOrAuto::Auto",
animatable=True)}
% endfor
<%helpers:longhand name="z-index">
<%helpers:longhand name="z-index" animatable="True">
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
@ -62,39 +63,49 @@
// http://www.w3.org/TR/css3-flexbox/
// Flex container properties
${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse", experimental=True)}
${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse",
experimental=True, animatable=False)}
${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse", experimental=True)}
${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
experimental=True, animatable=False)}
// FIXME(stshine): The type of 'justify-content' and 'align-content' is uint16_t in gecko
// FIXME(stshine): Its higher bytes are used to store fallback value. Disable them in geckolib for now
${helpers.single_keyword("justify-content", "flex-start flex-end center space-between space-around",
experimental=True,
gecko_constant_prefix="NS_STYLE_JUSTIFY",
products="servo")}
products="servo",
animatable=False)}
${helpers.single_keyword("align-items", "stretch flex-start flex-end center baseline",
experimental=True,
need_clone=True,
gecko_constant_prefix="NS_STYLE_ALIGN")}
gecko_constant_prefix="NS_STYLE_ALIGN",
animatable=False)}
${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around",
experimental=True,
gecko_constant_prefix="NS_STYLE_ALIGN",
products="servo")}
products="servo",
animatable=False)}
// Flex item properties
${helpers.predefined_type("flex-grow", "Number", "0.0", "parse_non_negative", experimental=True)}
${helpers.predefined_type("flex-grow", "Number",
"0.0", "parse_non_negative",
experimental=True, animatable=True)}
${helpers.predefined_type("flex-shrink", "Number", "1.0", "parse_non_negative", experimental=True)}
${helpers.predefined_type("flex-shrink", "Number",
"1.0", "parse_non_negative",
experimental=True, animatable=True)}
${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
experimental=True,
need_clone=True,
gecko_constant_prefix="NS_STYLE_ALIGN")}
gecko_constant_prefix="NS_STYLE_ALIGN",
animatable=False)}
// https://drafts.csswg.org/css-flexbox/#propdef-order
<%helpers:longhand name="order">
<%helpers:longhand name="order" animatable="True">
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
@ -115,41 +126,53 @@ ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center
}
</%helpers:longhand>
// FIXME: This property should be animatable.
${helpers.predefined_type("flex-basis",
"LengthOrPercentageOrAutoOrContent",
"computed::LengthOrPercentageOrAutoOrContent::Auto")}
"computed::LengthOrPercentageOrAutoOrContent::Auto",
animatable=False)}
${helpers.predefined_type("width",
"LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto",
"parse_non_negative")}
"parse_non_negative",
animatable=True)}
${helpers.predefined_type("height",
"LengthOrPercentageOrAuto",
"computed::LengthOrPercentageOrAuto::Auto",
"parse_non_negative")}
"parse_non_negative",
animatable=True)}
${helpers.predefined_type("min-width",
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
"parse_non_negative")}
"parse_non_negative",
animatable=True)}
${helpers.predefined_type("max-width",
"LengthOrPercentageOrNone",
"computed::LengthOrPercentageOrNone::None",
"parse_non_negative")}
"parse_non_negative",
animatable=True)}
${helpers.predefined_type("min-height",
"LengthOrPercentage",
"computed::LengthOrPercentage::Length(Au(0))",
"parse_non_negative")}
"parse_non_negative",
animatable=True)}
${helpers.predefined_type("max-height",
"LengthOrPercentageOrNone",
"computed::LengthOrPercentageOrNone::None",
"parse_non_negative")}
"parse_non_negative",
animatable=True)}
${helpers.single_keyword("box-sizing",
"content-box border-box")}
"content-box border-box",
animatable=False)}
// CSS Image Values and Replaced Content Module Level 3
// https://drafts.csswg.org/css-images-3/
${helpers.single_keyword("object-fit", "fill contain cover none scale-down", products="gecko")}
${helpers.single_keyword("object-fit", "fill contain cover none scale-down",
products="gecko", animatable=False)}

View file

@ -6,36 +6,46 @@
<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>
// TODO: Which of these should be animatable properties?
${helpers.single_keyword("dominant-baseline",
"""auto use-script no-change reset-size ideographic alphabetic hanging
mathematical central middle text-after-edge text-before-edge""",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.single_keyword("vector-effect", "none non-scaling-stroke", products="gecko")}
${helpers.single_keyword("vector-effect", "none non-scaling-stroke",
products="gecko", animatable=False)}
// Section 13 - Gradients and Patterns
${helpers.predefined_type(
"stop-color", "CSSColor",
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.predefined_type("stop-opacity", "Opacity", "1.0", products="gecko")}
${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
products="gecko",
animatable=False)}
// Section 15 - Filter Effects
${helpers.predefined_type(
"flood-color", "CSSColor",
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.predefined_type("flood-opacity", "Opacity", "1.0", products="gecko")}
${helpers.predefined_type("flood-opacity", "Opacity",
"1.0", products="gecko", animatable=False)}
${helpers.predefined_type(
"lighting-color", "CSSColor",
"CSSParserColor::RGBA(RGBA { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 })",
products="gecko")}
products="gecko",
animatable=False)}
// CSS Masking Module Level 1
// https://www.w3.org/TR/css-masking-1/
${helpers.single_keyword("mask-type", "luminance alpha", products="gecko")}
${helpers.single_keyword("mask-type", "luminance alpha",
products="gecko", animatable=False)}

View file

@ -6,4 +6,5 @@
<% data.new_style_struct("Table", inherited=False) %>
${helpers.single_keyword("table-layout", "auto fixed", gecko_ffi_name="mLayoutStrategy")}
${helpers.single_keyword("table-layout", "auto fixed",
gecko_ffi_name="mLayoutStrategy", animatable=False)}

View file

@ -12,12 +12,16 @@
Method("has_overline", "bool"),
Method("has_line_through", "bool")]) %>
${helpers.single_keyword("text-overflow", "clip ellipsis")}
${helpers.single_keyword("text-overflow", "clip ellipsis", animatable=False)}
${helpers.single_keyword("unicode-bidi", "normal embed isolate bidi-override isolate-override plaintext")}
${helpers.single_keyword("unicode-bidi",
"normal embed isolate bidi-override isolate-override plaintext",
animatable=False)}
// FIXME: This prop should be animatable.
<%helpers:longhand name="${'text-decoration' if product == 'servo' else 'text-decoration-line'}"
custom_cascade="${product == 'servo'}">
custom_cascade="${product == 'servo'}"
animatable="False">
use cssparser::ToCss;
use std::fmt;
use values::computed::ComputedValueAsSpecified;
@ -116,9 +120,11 @@ ${helpers.single_keyword("unicode-bidi", "normal embed isolate bidi-override iso
${helpers.single_keyword("text-decoration-style",
"solid double dotted dashed wavy -moz-none",
products="gecko")}
products="gecko",
animatable=False)}
${helpers.predefined_type(
"text-decoration-color", "CSSColor",
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
products="gecko")}
products="gecko",
animatable=True)}

View file

@ -9,8 +9,11 @@
// https://drafts.csswg.org/css-ui-3/
<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>
${helpers.single_keyword("ime-mode", "normal auto active disabled inactive", products="gecko",
gecko_ffi_name="mIMEMode")}
${helpers.single_keyword("ime-mode", "normal auto active disabled inactive",
products="gecko", gecko_ffi_name="mIMEMode",
animatable=False)}
${helpers.single_keyword("-moz-user-select", "auto text none all", products="gecko",
gecko_ffi_name="mUserSelect", gecko_constant_prefix="NS_STYLE_USER_SELECT")}
gecko_ffi_name="mUserSelect",
gecko_constant_prefix="NS_STYLE_USER_SELECT",
animatable=False)}

View file

@ -8,8 +8,11 @@
// Non-standard properties that Gecko uses for XUL elements.
<% data.new_style_struct("XUL", inherited=False) %>
${helpers.single_keyword("-moz-box-align", "stretch start center baseline end", products="gecko",
gecko_ffi_name="mBoxAlign", gecko_constant_prefix="NS_STYLE_BOX_ALIGN")}
${helpers.single_keyword("-moz-box-align", "stretch start center baseline end",
products="gecko", gecko_ffi_name="mBoxAlign",
gecko_constant_prefix="NS_STYLE_BOX_ALIGN",
animatable=False)}
${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative", products="gecko",
gecko_ffi_name="mBoxFlex")}
${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative",
products="gecko", gecko_ffi_name="mBoxFlex",
animatable=False)}

View file

@ -14,7 +14,7 @@ use std::ascii::AsciiExt;
use std::boxed::Box as StdBox;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Write;
use std::fmt::{Debug, Write};
use std::sync::Arc;
use app_units::Au;
@ -132,6 +132,10 @@ pub mod shorthands {
<%include file="/shorthand/text.mako.rs" />
}
pub mod animated_properties {
<%include file="/helpers/animated_properties.mako.rs" />
}
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
// Maybe submit for inclusion in libstd?
@ -1044,13 +1048,31 @@ impl PropertyDeclaration {
PropertyDeclaration::Custom(_, _) => &[]
}
}
/// Returns true if this property is one of the animable properties, false
/// otherwise.
pub fn is_animatable(&self) -> bool {
match *self {
% for property in data.longhands:
PropertyDeclaration::${property.camel_case}(_) => {
% if property.animatable:
true
% else:
false
% endif
}
% endfor
PropertyDeclaration::Custom(..) => false,
}
}
}
pub mod style_struct_traits {
use super::longhands;
use std::fmt::Debug;
% for style_struct in data.active_style_structs():
pub trait ${style_struct.trait_name}: Clone {
pub trait ${style_struct.trait_name}: Debug + Clone {
% for longhand in style_struct.longhands:
#[allow(non_snake_case)]
fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T);
@ -1079,7 +1101,7 @@ pub mod style_structs {
#[derive(Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
% else:
#[derive(PartialEq, Clone)]
#[derive(PartialEq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
% endif
pub struct ${style_struct.servo_struct_name} {
@ -1121,32 +1143,45 @@ pub mod style_structs {
}
% endfor
% elif style_struct.trait_name == "Box":
#[inline]
fn clone_display(&self) -> longhands::display::computed_value::T {
self.display.clone()
}
#[inline]
fn clone_position(&self) -> longhands::position::computed_value::T {
self.position.clone()
}
#[inline]
fn clone_float(&self) -> longhands::float::computed_value::T {
self.float.clone()
}
#[inline]
fn clone_overflow_x(&self) -> longhands::overflow_x::computed_value::T {
self.overflow_x.clone()
}
#[inline]
fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T {
self.overflow_y.clone()
}
#[inline]
fn clone_animation_play_state(&self) -> longhands::animation_play_state::computed_value::T {
self.animation_play_state.clone()
}
#[inline]
fn transition_count(&self) -> usize {
self.transition_property.0.len()
}
% elif style_struct.trait_name == "Color":
#[inline]
fn clone_color(&self) -> longhands::color::computed_value::T {
self.color.clone()
}
% elif style_struct.trait_name == "Font":
#[inline]
fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
self.font_size.clone()
}
#[inline]
fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
self.font_weight.clone()
}
@ -1159,42 +1194,53 @@ pub mod style_structs {
self.hash = hasher.finish()
}
% elif style_struct.trait_name == "InheritedBox":
#[inline]
fn clone_direction(&self) -> longhands::direction::computed_value::T {
self.direction.clone()
}
#[inline]
fn clone_writing_mode(&self) -> longhands::writing_mode::computed_value::T {
self.writing_mode.clone()
}
#[inline]
fn clone_text_orientation(&self) -> longhands::text_orientation::computed_value::T {
self.text_orientation.clone()
}
% elif style_struct.trait_name == "InheritedText" and product == "servo":
#[inline]
fn clone__servo_text_decorations_in_effect(&self) ->
longhands::_servo_text_decorations_in_effect::computed_value::T {
self._servo_text_decorations_in_effect.clone()
}
% elif style_struct.trait_name == "Outline":
#[inline]
fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
self.outline_style.clone()
}
#[inline]
fn outline_has_nonzero_width(&self) -> bool {
self.outline_width != ::app_units::Au(0)
}
% elif style_struct.trait_name == "Position":
#[inline]
fn clone_align_items(&self) -> longhands::align_items::computed_value::T {
self.align_items.clone()
}
#[inline]
fn clone_align_self(&self) -> longhands::align_self::computed_value::T {
self.align_self.clone()
}
% elif style_struct.trait_name == "Text":
<% text_decoration_field = 'text_decoration' if product == 'servo' else 'text_decoration_line' %>
#[inline]
fn has_underline(&self) -> bool {
self.${text_decoration_field}.underline
}
#[inline]
fn has_overline(&self) -> bool {
self.${text_decoration_field}.overline
}
#[inline]
fn has_line_through(&self) -> bool {
self.${text_decoration_field}.line_through
}
@ -1204,7 +1250,7 @@ pub mod style_structs {
% endfor
}
pub trait ComputedValues : Clone + Send + Sync + 'static {
pub trait ComputedValues : Debug + Clone + Send + Sync + 'static {
% for style_struct in data.active_style_structs():
type Concrete${style_struct.trait_name}: style_struct_traits::${style_struct.trait_name};
% endfor
@ -1247,7 +1293,7 @@ pub trait ComputedValues : Clone + Send + Sync + 'static {
fn is_multicol(&self) -> bool;
}
#[derive(Clone)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct ServoComputedValues {
% for style_struct in data.active_style_structs():

View file

@ -32,7 +32,7 @@
let (mut timing_function, mut delay) = (None, None);
loop {
if property.is_none() {
if let Ok(value) = input.try(|input| transition_property::parse_one(input)) {
if let Ok(value) = input.try(transition_property::SingleSpecifiedValue::parse) {
property = Some(value);
continue
}

View file

@ -9,6 +9,7 @@ use properties::{self, ServoComputedValues};
use selector_matching::{USER_OR_USER_AGENT_STYLESHEETS, QUIRKS_MODE_STYLESHEET};
use selectors::Element;
use selectors::parser::{ParserContext, SelectorImpl};
use std::fmt::Debug;
use stylesheets::Stylesheet;
/// This function determines if a pseudo-element is eagerly cascaded or not.
@ -62,7 +63,9 @@ pub trait ElementExt: Element {
fn is_link(&self) -> bool;
}
pub trait SelectorImplExt : SelectorImpl + Sized {
// NB: The `Clone` trait is here for convenience due to:
// https://github.com/rust-lang/rust/issues/26925
pub trait SelectorImplExt : SelectorImpl + Clone + Debug + Sized {
type ComputedValues: properties::ComputedValues;
fn pseudo_element_cascade_type(pseudo: &Self::PseudoElement) -> PseudoElementCascadeType;
@ -90,6 +93,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 +113,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 +262,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

@ -7,6 +7,7 @@
use dom::PresentationalHintsSynthetizer;
use element_state::*;
use error_reporting::StdoutErrorReporter;
use keyframes::KeyframesAnimation;
use media_queries::{Device, MediaType};
use parser::ParserContextExtraData;
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock};
@ -22,8 +23,9 @@ use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::process;
use std::sync::Arc;
use string_cache::Atom;
use style_traits::viewport::ViewportConstraints;
use stylesheets::{CSSRuleIteratorExt, Origin, Stylesheet};
use stylesheets::{CSSRule, CSSRuleIteratorExt, Origin, Stylesheet};
use url::Url;
use util::opts;
use util::resource_files::read_resource_file;
@ -126,6 +128,9 @@ pub struct Stylist<Impl: SelectorImplExt> {
PerPseudoElementSelectorMap<Impl>,
BuildHasherDefault<::fnv::FnvHasher>>,
/// A map with all the animations indexed by name.
animations: HashMap<Atom, KeyframesAnimation>,
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
/// These are eagerly computed once, and then used to resolve the new
/// computed values on the fly on layout.
@ -150,6 +155,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
element_map: PerPseudoElementSelectorMap::new(),
pseudos_map: HashMap::with_hasher(Default::default()),
animations: HashMap::with_hasher(Default::default()),
precomputed_pseudo_element_decls: HashMap::with_hasher(Default::default()),
rules_source_order: 0,
state_deps: DependencySet::new(),
@ -173,6 +179,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
self.element_map = PerPseudoElementSelectorMap::new();
self.pseudos_map = HashMap::with_hasher(Default::default());
self.animations = HashMap::with_hasher(Default::default());
Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
});
@ -233,17 +240,36 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
};
);
for style_rule in stylesheet.effective_rules(&self.device).style() {
append!(style_rule, normal);
append!(style_rule, important);
rules_source_order += 1;
for selector in &style_rule.selectors {
self.state_deps.note_selector(selector.compound_selectors.clone());
for rule in stylesheet.effective_rules(&self.device) {
match *rule {
CSSRule::Style(ref style_rule) => {
append!(style_rule, normal);
append!(style_rule, important);
rules_source_order += 1;
for selector in &style_rule.selectors {
self.state_deps.note_selector(selector.compound_selectors.clone());
}
self.rules_source_order = rules_source_order;
}
CSSRule::Keyframes(ref keyframes_rule) => {
debug!("Found valid keyframes rule: {:?}", keyframes_rule);
if let Some(animation) = KeyframesAnimation::from_keyframes(&keyframes_rule.keyframes) {
debug!("Found valid keyframe animation: {:?}", animation);
self.animations.insert(keyframes_rule.name.clone(),
animation);
} else {
// If there's a valid keyframes rule, even if it doesn't
// produce an animation, should shadow other animations
// with the same name.
self.animations.remove(&keyframes_rule.name);
}
}
// We don't care about any other rule.
_ => {}
}
}
self.rules_source_order = rules_source_order;
Impl::each_precomputed_pseudo_element(|pseudo| {
// TODO: Consider not doing this and just getting the rules on the
// fly. It should be a bit slower, but we'd take rid of the
@ -270,7 +296,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,
Box::new(StdoutErrorReporter));
Some(Arc::new(computed))
} else {
@ -437,6 +464,11 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
pub fn is_device_dirty(&self) -> bool {
self.is_device_dirty
}
#[inline]
pub fn animations(&self) -> &HashMap<Atom, KeyframesAnimation> {
&self.animations
}
}
/// Map that contains the CSS rules for a given origin.

View file

@ -1,9 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Concrete types for servo Style implementation
use animation;
use context;
use data;
use properties::ServoComputedValues;
@ -15,3 +15,4 @@ pub type Stylesheet = stylesheets::Stylesheet<ServoSelectorImpl>;
pub type PrivateStyleData = data::PrivateStyleData<ServoSelectorImpl, ServoComputedValues>;
pub type Stylist = selector_matching::Stylist<ServoSelectorImpl>;
pub type SharedStyleContext = context::SharedStyleContext<ServoSelectorImpl>;
pub type Animation = animation::Animation<ServoSelectorImpl>;

View file

@ -5,10 +5,11 @@
//! Style sheets and their CSS rules.
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes};
use cssparser::{AtRuleType, RuleListParser};
use cssparser::{AtRuleType, RuleListParser, Token};
use encoding::EncodingRef;
use error_reporting::ParseErrorReporter;
use font_face::{FontFaceRule, parse_font_face_block};
use keyframes::{Keyframe, parse_keyframe_list};
use media_queries::{Device, MediaQueryList, parse_media_query_list};
use parser::{ParserContext, ParserContextExtraData, log_css_error};
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
@ -62,6 +63,14 @@ pub enum CSSRule<Impl: SelectorImpl> {
Media(MediaRule<Impl>),
FontFace(FontFaceRule),
Viewport(ViewportRule),
Keyframes(KeyframesRule),
}
#[derive(Debug, HeapSizeOf, PartialEq)]
pub struct KeyframesRule {
pub name: Atom,
pub keyframes: Vec<Keyframe>,
}
#[derive(Debug, PartialEq)]
@ -71,6 +80,7 @@ pub struct MediaRule<Impl: SelectorImpl> {
pub rules: Vec<CSSRule<Impl>>,
}
impl<Impl: SelectorImpl> MediaRule<Impl> {
#[inline]
pub fn evaluate(&self, device: &Device) -> bool {
@ -127,7 +137,7 @@ impl<Impl: SelectorImpl> Stylesheet<Impl> {
let mut input = Parser::new(css);
input.look_for_viewport_percentages();
let mut rules = Vec::new();
let mut rules = vec![];
{
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
while let Some(result) = iter.next() {
@ -142,6 +152,7 @@ impl<Impl: SelectorImpl> Stylesheet<Impl> {
Some(namespace.clone());
}
}
rules.push(rule);
}
Err(range) => {
@ -153,6 +164,7 @@ impl<Impl: SelectorImpl> Stylesheet<Impl> {
}
}
}
Stylesheet {
origin: origin,
rules: rules,
@ -253,7 +265,7 @@ pub mod rule_filter {
use std::marker::PhantomData;
use super::super::font_face::FontFaceRule;
use super::super::viewport::ViewportRule;
use super::{CSSRule, MediaRule, StyleRule};
use super::{CSSRule, KeyframesRule, MediaRule, StyleRule};
macro_rules! rule_filter {
($variant:ident -> $value:ty) => {
@ -266,6 +278,7 @@ 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 {
iter: iter,
@ -300,6 +313,7 @@ pub mod rule_filter {
rule_filter!(Style -> StyleRule<Impl>);
rule_filter!(FontFace -> FontFaceRule);
rule_filter!(Viewport -> ViewportRule);
rule_filter!(Keyframes -> KeyframesRule);
}
/// Extension methods for `CSSRule` iterators.
@ -315,6 +329,9 @@ pub trait CSSRuleIteratorExt<'a, Impl: SelectorImpl + 'a>: Iterator<Item=&'a CSS
/// Yield only @viewport rules.
fn viewport(self) -> rule_filter::Viewport<'a, Self>;
/// Yield only @keyframes rules.
fn keyframes(self) -> rule_filter::Keyframes<'a, Self>;
}
impl<'a, I, Impl: SelectorImpl + 'a> CSSRuleIteratorExt<'a, Impl> for I where I: Iterator<Item=&'a CSSRule<Impl>> {
@ -337,6 +354,11 @@ impl<'a, I, Impl: SelectorImpl + 'a> CSSRuleIteratorExt<'a, Impl> for I where I:
fn viewport(self) -> rule_filter::Viewport<'a, I> {
rule_filter::Viewport::new(self)
}
#[inline]
fn keyframes(self) -> rule_filter::Keyframes<'a, I> {
rule_filter::Keyframes::new(self)
}
}
fn parse_nested_rules<Impl: SelectorImpl>(context: &ParserContext, input: &mut Parser) -> Vec<CSSRule<Impl>> {
@ -376,9 +398,14 @@ enum State {
enum AtRulePrelude {
/// A @font-face rule prelude.
FontFace,
/// A @media rule prelude, with its media queries.
Media(MediaQueryList),
/// A @viewport rule prelude.
Viewport,
/// A @keyframes rule, with its animation name.
Keyframes(Atom),
}
@ -478,6 +505,15 @@ impl<'a, 'b, Impl: SelectorImpl> AtRuleParser for NestedRuleParser<'a, 'b, Impl>
Err(())
}
},
"keyframes" => {
let name = match input.next() {
Ok(Token::Ident(ref value)) if value != "none" => Atom::from(&**value),
Ok(Token::QuotedString(value)) => Atom::from(&*value),
_ => return Err(())
};
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(Atom::from(name))))
},
_ => Err(())
}
}
@ -496,11 +532,16 @@ impl<'a, 'b, Impl: SelectorImpl> AtRuleParser for NestedRuleParser<'a, 'b, Impl>
AtRulePrelude::Viewport => {
ViewportRule::parse(input, self.context).map(CSSRule::Viewport)
}
AtRulePrelude::Keyframes(name) => {
Ok(CSSRule::Keyframes(KeyframesRule {
name: name,
keyframes: parse_keyframe_list(&self.context, input),
}))
}
}
}
}
impl<'a, 'b, Impl: SelectorImpl> QualifiedRuleParser for NestedRuleParser<'a, 'b, Impl> {
type Prelude = Vec<Selector<Impl>>;
type QualifiedRule = CSSRule<Impl>;

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

@ -1391,16 +1391,13 @@ pub mod specified {
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
input.try(BorderRadiusSize::parse).or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),
"thin" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(1.)))),
"medium" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(3.)))),
"thick" =>
Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(5.)))),
_ => Err(())
"thin" => Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(1.)))),
"medium" => Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(3.)))),
"thick" => Ok(BorderRadiusSize::circle(
LengthOrPercentage::Length(Length::from_px(5.)))),
_ => Err(())
}
})
}
@ -1752,7 +1749,7 @@ pub mod computed {
}
#[derive(PartialEq, Clone, Copy)]
#[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);

View file

@ -562,8 +562,8 @@ pub trait MaybeNew {
impl MaybeNew for ViewportConstraints {
fn maybe_new(initial_viewport: TypedSize2D<ViewportPx, f32>,
rule: &ViewportRule)
-> Option<ViewportConstraints>
rule: &ViewportRule)
-> Option<ViewportConstraints>
{
use std::cmp;

View file

@ -6,12 +6,11 @@ use euclid::Size2D;
use euclid::size::TypedSize2D;
use gecko_bindings::bindings::RawServoStyleSet;
use num_cpus;
use selector_impl::{Stylist, Stylesheet, SharedStyleContext};
use selector_impl::{GeckoSelectorImpl, Stylist, Stylesheet, SharedStyleContext};
use std::cmp;
use std::collections::HashMap;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, RwLock};
use style::animation::Animation;
use style::dom::OpaqueNode;
use style::media_queries::{Device, MediaType};
use style::parallel::WorkQueueData;
@ -19,6 +18,8 @@ use util::geometry::ViewportPx;
use util::thread_state;
use util::workqueue::WorkQueue;
pub type Animation = ::style::animation::Animation<GeckoSelectorImpl>;
pub struct PerDocumentStyleData {
/// Rule processor.
pub stylist: Arc<Stylist>,

View file

@ -39,7 +39,7 @@ use values::{StyleCoordHelpers, ToGeckoStyleCoord, convert_nscolor_to_rgba};
use values::{convert_rgba_to_nscolor, debug_assert_unit_is_safe_to_copy};
use values::round_border_to_device_pixels;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct GeckoComputedValues {
% for style_struct in data.style_structs:
${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
@ -389,6 +389,12 @@ impl Debug for ${style_struct.gecko_struct_name} {
force_stub += ["list-style-type", "text-overflow"]
# These are booleans.
force_stub += ["page-break-after", "page-break-before"]
# In a nsTArray, have to be done manually, but probably not too much work
# (the "filling them", not the "making them work")
force_stub += ["animation-name", "animation-duration",
"animation-timing-function", "animation-iteration-count",
"animation-direction", "animation-play-state",
"animation-fill-mode", "animation-delay"]
# Types used with predefined_type()-defined properties that we can auto-generate.
predefined_types = {

View file

@ -16,6 +16,7 @@ pub type PrivateStyleData = style::data::PrivateStyleData<GeckoSelectorImpl, Gec
#[cfg(feature = "servo_features")]
known_heap_size!(0, GeckoSelectorImpl, PseudoElement, NonTSPseudoClass);
#[derive(Debug, Clone)]
pub struct GeckoSelectorImpl;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
@ -379,6 +380,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()

View file

@ -15,6 +15,7 @@ use gecko_bindings::structs::nsIAtom;
use heapsize::HeapSizeOf;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::Cow;
use std::char;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
@ -206,6 +207,15 @@ impl fmt::Debug for Atom {
}
}
impl fmt::Display for Atom {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
for c in char::decode_utf16(self.as_slice().iter().cloned()) {
try!(write!(w, "{}", c.unwrap_or(char::REPLACEMENT_CHARACTER)))
}
Ok(())
}
}
impl<'a> From<&'a str> for Atom {
#[inline]
fn from(string: &str) -> Atom {

View file

@ -9,11 +9,13 @@ use std::borrow::ToOwned;
use std::sync::Arc;
use std::sync::Mutex;
use string_cache::{Atom, Namespace};
use style::error_reporting::ParseErrorReporter;
use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage};
use style::parser::ParserContextExtraData;
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
use style::stylesheets::{CSSRule, StyleRule, Origin};
use style::error_reporting::ParseErrorReporter;
use style::servo::Stylesheet;
use style::stylesheets::{CSSRule, StyleRule, KeyframesRule, Origin};
use style::values::specified::{LengthOrPercentageOrAuto, Percentage};
use url::Url;
#[test]
@ -24,7 +26,10 @@ fn test_parse_stylesheet() {
input[type=hidden i] { display: none !important; }
html , body /**/ { display: block; }
#d1 > .ok { background: blue; }
";
@keyframes foo {
from { width: 0% }
to { width: 100%}
}";
let url = Url::parse("about::test").unwrap();
let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
Box::new(CSSErrorReporterTest),
@ -145,6 +150,28 @@ fn test_parse_stylesheet() {
important: Arc::new(vec![]),
},
}),
CSSRule::Keyframes(KeyframesRule {
name: "foo".into(),
keyframes: vec![
Keyframe {
selector: KeyframeSelector::new_for_unit_testing(
vec![KeyframePercentage::new(0.)]),
declarations: Arc::new(vec![
PropertyDeclaration::Width(DeclaredValue::Value(
LengthOrPercentageOrAuto::Percentage(Percentage(0.)))),
]),
},
Keyframe {
selector: KeyframeSelector::new_for_unit_testing(
vec![KeyframePercentage::new(1.)]),
declarations: Arc::new(vec![
PropertyDeclaration::Width(DeclaredValue::Value(
LengthOrPercentageOrAuto::Percentage(Percentage(1.)))),
]),
},
]
})
],
});
}