mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
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:
commit
d3a81373e4
51 changed files with 2655 additions and 1419 deletions
|
@ -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),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
@ -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>,
|
||||
|
|
242
components/style/keyframes.rs
Normal file
242
components/style/keyframes.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) };
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
878
components/style/properties/helpers/animated_properties.mako.rs
Normal file
878
components/style/properties/helpers/animated_properties.mako.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.)))),
|
||||
]),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
],
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue