mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +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;
|
let msg = LayoutControlMsg::TickAnimations;
|
||||||
match self.pipelines.get(&pipeline_id) {
|
match self.pipelines.get(&pipeline_id) {
|
||||||
Some(pipeline) => pipeline.layout_chan.send(msg),
|
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.
|
//! CSS transitions and animations.
|
||||||
|
|
||||||
|
use context::SharedLayoutContext;
|
||||||
use flow::{self, Flow};
|
use flow::{self, Flow};
|
||||||
use gfx::display_list::OpaqueNode;
|
use gfx::display_list::OpaqueNode;
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
|
@ -11,21 +12,44 @@ use msg::constellation_msg::PipelineId;
|
||||||
use script_layout_interface::restyle_damage::RestyleDamage;
|
use script_layout_interface::restyle_damage::RestyleDamage;
|
||||||
use script_traits::{AnimationState, LayoutMsg as ConstellationMsg};
|
use script_traits::{AnimationState, LayoutMsg as ConstellationMsg};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use style::animation::{Animation, update_style_for_animation};
|
use style::animation::{Animation, update_style_for_animation};
|
||||||
|
use style::selector_impl::{SelectorImplExt, ServoSelectorImpl};
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
/// Processes any new animations that were discovered after style recalculation.
|
/// Processes any new animations that were discovered after style recalculation.
|
||||||
/// Also expire any old animations that have completed, inserting them into `expired_animations`.
|
/// Also expire any old animations that have completed, inserting them into
|
||||||
pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
|
/// `expired_animations`.
|
||||||
running_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
|
pub fn update_animation_state<Impl: SelectorImplExt>(constellation_chan: &IpcSender<ConstellationMsg>,
|
||||||
expired_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
|
running_animations: &mut HashMap<OpaqueNode, Vec<Animation<Impl>>>,
|
||||||
new_animations_receiver: &Receiver<Animation>,
|
expired_animations: &mut HashMap<OpaqueNode, Vec<Animation<Impl>>>,
|
||||||
pipeline_id: PipelineId) {
|
new_animations_receiver: &Receiver<Animation<Impl>>,
|
||||||
let mut new_running_animations = Vec::new();
|
pipeline_id: PipelineId) {
|
||||||
|
let mut new_running_animations = vec![];
|
||||||
while let Ok(animation) = new_animations_receiver.try_recv() {
|
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() {
|
if running_animations.is_empty() && new_running_animations.is_empty() {
|
||||||
|
@ -34,62 +58,82 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expire old running animations.
|
|
||||||
let now = time::precise_time_s();
|
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() {
|
for (key, running_animations) in running_animations.iter_mut() {
|
||||||
let mut animations_still_running = vec![];
|
let mut animations_still_running = vec![];
|
||||||
for running_animation in running_animations.drain(..) {
|
for mut running_animation in running_animations.drain(..) {
|
||||||
if now < running_animation.end_time {
|
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);
|
animations_still_running.push(running_animation);
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
match expired_animations.entry(*key) {
|
|
||||||
Entry::Vacant(entry) => {
|
expired_animations.entry(*key)
|
||||||
entry.insert(vec![running_animation]);
|
.or_insert_with(Vec::new)
|
||||||
}
|
.push(running_animation);
|
||||||
Entry::Occupied(mut entry) => entry.get_mut().push(running_animation),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if animations_still_running.len() == 0 {
|
|
||||||
|
if animations_still_running.is_empty() {
|
||||||
keys_to_remove.push(*key);
|
keys_to_remove.push(*key);
|
||||||
} else {
|
} else {
|
||||||
*running_animations = animations_still_running
|
*running_animations = animations_still_running
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key in keys_to_remove {
|
for key in keys_to_remove {
|
||||||
running_animations.remove(&key).unwrap();
|
running_animations.remove(&key).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new running animations.
|
// Add new running animations.
|
||||||
for new_running_animation in new_running_animations {
|
for new_running_animation in new_running_animations {
|
||||||
match running_animations.entry(new_running_animation.node) {
|
running_animations.entry(*new_running_animation.node())
|
||||||
Entry::Vacant(entry) => {
|
.or_insert_with(Vec::new)
|
||||||
entry.insert(vec![new_running_animation]);
|
.push(new_running_animation);
|
||||||
}
|
|
||||||
Entry::Occupied(mut entry) => entry.get_mut().push(new_running_animation),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let animation_state;
|
let animation_state = if running_animations.is_empty() {
|
||||||
if running_animations.is_empty() {
|
AnimationState::NoAnimationsPresent
|
||||||
animation_state = AnimationState::NoAnimationsPresent;
|
|
||||||
} else {
|
} else {
|
||||||
animation_state = AnimationState::AnimationsPresent;
|
AnimationState::AnimationsPresent
|
||||||
}
|
};
|
||||||
|
|
||||||
constellation_chan.send(ConstellationMsg::ChangeRunningAnimationsState(pipeline_id, animation_state))
|
constellation_chan.send(ConstellationMsg::ChangeRunningAnimationsState(pipeline_id, animation_state))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recalculates style for a set of animations. This does *not* run with the DOM lock held.
|
/// Recalculates style for a set of animations. This does *not* run with the DOM
|
||||||
pub fn recalc_style_for_animations(flow: &mut Flow,
|
/// lock held.
|
||||||
animations: &HashMap<OpaqueNode, Vec<Animation>>) {
|
// 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();
|
let mut damage = RestyleDamage::empty();
|
||||||
flow.mutate_fragments(&mut |fragment| {
|
flow.mutate_fragments(&mut |fragment| {
|
||||||
if let Some(ref animations) = animations.get(&fragment.node) {
|
if let Some(ref animations) = animations.get(&fragment.node) {
|
||||||
for animation in *animations {
|
for animation in animations.iter() {
|
||||||
update_style_for_animation(animation, &mut fragment.style, Some(&mut damage));
|
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);
|
let base = flow::mut_base(flow);
|
||||||
base.restyle_damage.insert(damage);
|
base.restyle_damage.insert(damage);
|
||||||
for kid in base.children.iter_mut() {
|
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::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
|
use std::sync::{Arc, Mutex, MutexGuard, RwLock};
|
||||||
use style::animation::Animation;
|
|
||||||
use style::computed_values::{filter, mix_blend_mode};
|
use style::computed_values::{filter, mix_blend_mode};
|
||||||
use style::context::ReflowGoal;
|
use style::context::ReflowGoal;
|
||||||
use style::dom::{TDocument, TElement, TNode};
|
use style::dom::{TDocument, TElement, TNode};
|
||||||
|
@ -109,7 +108,7 @@ use style::parallel::WorkQueueData;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::refcell::RefCell;
|
use style::refcell::RefCell;
|
||||||
use style::selector_matching::USER_OR_USER_AGENT_STYLESHEETS;
|
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 style::stylesheets::CSSRuleIteratorExt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::geometry::MAX_RECT;
|
use util::geometry::MAX_RECT;
|
||||||
|
@ -1290,7 +1289,7 @@ impl LayoutThread {
|
||||||
self.tick_animations(&mut rw_data);
|
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 {
|
let reflow_info = Reflow {
|
||||||
goal: ReflowGoal::ForDisplay,
|
goal: ReflowGoal::ForDisplay,
|
||||||
page_clip_rect: MAX_RECT,
|
page_clip_rect: MAX_RECT,
|
||||||
|
@ -1307,8 +1306,9 @@ impl LayoutThread {
|
||||||
self.profiler_metadata(),
|
self.profiler_metadata(),
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
|| {
|
|| {
|
||||||
animation::recalc_style_for_animations(flow_ref::deref_mut(&mut root_flow),
|
animation::recalc_style_for_animations(&layout_context,
|
||||||
&*animations)
|
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 flex-shrink;
|
||||||
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString alignSelf;
|
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString alignSelf;
|
||||||
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString align-self;
|
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString align-self;
|
||||||
|
|
||||||
|
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animation-name;
|
||||||
|
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString animationName;
|
||||||
|
[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.
|
// 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.
|
// Receive at least one message so we don't spinloop.
|
||||||
let mut event = {
|
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
|
/// A channel on which new animations that have been triggered by style recalculation can be
|
||||||
/// sent.
|
/// sent.
|
||||||
pub new_animations_sender: Mutex<Sender<Animation>>,
|
pub new_animations_sender: Mutex<Sender<Animation<Impl>>>,
|
||||||
|
|
||||||
/// Why is this reflow occurring
|
/// Why is this reflow occurring
|
||||||
pub goal: ReflowGoal,
|
pub goal: ReflowGoal,
|
||||||
|
|
||||||
/// The animations that are currently running.
|
/// 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.
|
/// 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
|
///The CSS error reporter for all CSS loaded in this layout thread
|
||||||
pub error_reporter: Box<ParseErrorReporter + Sync>,
|
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 element_state;
|
||||||
pub mod error_reporting;
|
pub mod error_reporting;
|
||||||
pub mod font_face;
|
pub mod font_face;
|
||||||
|
pub mod keyframes;
|
||||||
pub mod logical_geometry;
|
pub mod logical_geometry;
|
||||||
pub mod matching;
|
pub mod matching;
|
||||||
pub mod media_queries;
|
pub mod media_queries;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use animation::{self, Animation};
|
use animation::{self, Animation};
|
||||||
use context::SharedStyleContext;
|
use context::{SharedStyleContext, LocalStyleContext};
|
||||||
use data::PrivateStyleData;
|
use data::PrivateStyleData;
|
||||||
use dom::{TElement, TNode, TRestyleDamage};
|
use dom::{TElement, TNode, TRestyleDamage};
|
||||||
use properties::{ComputedValues, PropertyDeclaration, cascade};
|
use properties::{ComputedValues, PropertyDeclaration, cascade};
|
||||||
|
@ -21,8 +21,7 @@ use smallvec::SmallVec;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::Arc;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use string_cache::{Atom, Namespace};
|
use string_cache::{Atom, Namespace};
|
||||||
use util::arc_ptr_eq;
|
use util::arc_ptr_eq;
|
||||||
use util::cache::{LRUCache, SimpleHashCache};
|
use util::cache::{LRUCache, SimpleHashCache};
|
||||||
|
@ -54,7 +53,9 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: &
|
||||||
|
|
||||||
pub struct ApplicableDeclarations<Impl: SelectorImplExt> {
|
pub struct ApplicableDeclarations<Impl: SelectorImplExt> {
|
||||||
pub normal: SmallVec<[DeclarationBlock; 16]>,
|
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.
|
/// Whether the `normal` declarations are shareable with other nodes.
|
||||||
pub normal_shareable: bool,
|
pub normal_shareable: bool,
|
||||||
|
@ -365,7 +366,11 @@ pub enum StyleSharingResult<ConcreteRestyleDamage: TRestyleDamage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PrivateMatchMethods: TNode
|
trait PrivateMatchMethods: TNode
|
||||||
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt {
|
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt<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,
|
fn cascade_node_pseudo_element(&self,
|
||||||
context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>,
|
context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>,
|
||||||
parent_style: Option<&Arc<Self::ConcreteComputedValues>>,
|
parent_style: Option<&Arc<Self::ConcreteComputedValues>>,
|
||||||
|
@ -373,7 +378,6 @@ trait PrivateMatchMethods: TNode
|
||||||
mut style: Option<&mut Arc<Self::ConcreteComputedValues>>,
|
mut style: Option<&mut Arc<Self::ConcreteComputedValues>>,
|
||||||
applicable_declarations_cache:
|
applicable_declarations_cache:
|
||||||
&mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>,
|
&mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>,
|
||||||
new_animations_sender: &Mutex<Sender<Animation>>,
|
|
||||||
shareable: bool,
|
shareable: bool,
|
||||||
animate_properties: bool)
|
animate_properties: bool)
|
||||||
-> (Self::ConcreteRestyleDamage, Arc<Self::ConcreteComputedValues>) {
|
-> (Self::ConcreteRestyleDamage, Arc<Self::ConcreteComputedValues>) {
|
||||||
|
@ -382,14 +386,15 @@ trait PrivateMatchMethods: TNode
|
||||||
cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable;
|
cacheable = !self.update_animations_for_cascade(context, &mut style) && cacheable;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut this_style;
|
let this_style;
|
||||||
match parent_style {
|
match parent_style {
|
||||||
Some(ref parent_style) => {
|
Some(ref parent_style) => {
|
||||||
let cache_entry = applicable_declarations_cache.find(applicable_declarations);
|
let cache_entry = applicable_declarations_cache.find(applicable_declarations);
|
||||||
let cached_computed_values = match cache_entry {
|
let cached_computed_values = match cache_entry {
|
||||||
None => None,
|
|
||||||
Some(ref style) => Some(&**style),
|
Some(ref style) => Some(&**style),
|
||||||
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (the_style, is_cacheable) = cascade(context.viewport_size,
|
let (the_style, is_cacheable) = cascade(context.viewport_size,
|
||||||
applicable_declarations,
|
applicable_declarations,
|
||||||
shareable,
|
shareable,
|
||||||
|
@ -411,22 +416,31 @@ trait PrivateMatchMethods: TNode
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trigger transitions if necessary. This will reset `this_style` back to its old value if
|
let mut this_style = Arc::new(this_style);
|
||||||
// it did trigger a transition.
|
|
||||||
if animate_properties {
|
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 {
|
if let Some(ref style) = style {
|
||||||
let animations_started =
|
animations_started |=
|
||||||
animation::start_transitions_if_applicable::<Self::ConcreteComputedValues>(
|
animation::start_transitions_if_applicable::<<Self::ConcreteElement as Element>::Impl>(
|
||||||
new_animations_sender,
|
&context.new_animations_sender,
|
||||||
self.opaque(),
|
this_opaque,
|
||||||
&**style,
|
&**style,
|
||||||
&mut this_style);
|
&mut this_style);
|
||||||
cacheable = cacheable && !animations_started
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheable = cacheable && !animations_started
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate style difference.
|
// Calculate style difference.
|
||||||
let this_style = Arc::new(this_style);
|
|
||||||
let damage = Self::ConcreteRestyleDamage::compute(style.map(|s| &*s), &*this_style);
|
let damage = Self::ConcreteRestyleDamage::compute(style.map(|s| &*s), &*this_style);
|
||||||
|
|
||||||
// Cache the resolved style if it was cacheable.
|
// Cache the resolved style if it was cacheable.
|
||||||
|
@ -457,7 +471,11 @@ trait PrivateMatchMethods: TNode
|
||||||
had_animations_to_expire = animations_to_expire.is_some();
|
had_animations_to_expire = animations_to_expire.is_some();
|
||||||
if let Some(ref animations) = animations_to_expire {
|
if let Some(ref animations) = animations_to_expire {
|
||||||
for animation in *animations {
|
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();
|
.is_some();
|
||||||
if had_running_animations {
|
if had_running_animations {
|
||||||
let mut all_running_animations = context.running_animations.write().unwrap();
|
let mut all_running_animations = context.running_animations.write().unwrap();
|
||||||
for running_animation in all_running_animations.get(&this_opaque).unwrap() {
|
for mut running_animation in all_running_animations.get_mut(&this_opaque).unwrap() {
|
||||||
animation::update_style_for_animation::<Self::ConcreteComputedValues,
|
animation::update_style_for_animation::<Self::ConcreteRestyleDamage,
|
||||||
Self::ConcreteRestyleDamage>(running_animation, style, None);
|
<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
|
had_animations_to_expire || had_running_animations
|
||||||
|
@ -486,7 +504,8 @@ trait PrivateMatchMethods: TNode
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: TNode> PrivateMatchMethods for N
|
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 {
|
trait PrivateElementMatchMethods: TElement {
|
||||||
fn share_style_with_candidate_if_possible(&self,
|
fn share_style_with_candidate_if_possible(&self,
|
||||||
|
@ -641,25 +660,27 @@ pub trait MatchMethods : TNode {
|
||||||
|
|
||||||
unsafe fn cascade_node(&self,
|
unsafe fn cascade_node(&self,
|
||||||
context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>,
|
context: &SharedStyleContext<<Self::ConcreteElement as Element>::Impl>,
|
||||||
|
local_context: &LocalStyleContext<Self::ConcreteComputedValues>,
|
||||||
parent: Option<Self>,
|
parent: Option<Self>,
|
||||||
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>,
|
applicable_declarations: &ApplicableDeclarations<<Self::ConcreteElement as Element>::Impl>)
|
||||||
applicable_declarations_cache:
|
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt<ComputedValues = Self::ConcreteComputedValues>
|
||||||
&mut ApplicableDeclarationsCache<Self::ConcreteComputedValues>,
|
{
|
||||||
new_animations_sender: &Mutex<Sender<Animation>>)
|
|
||||||
where <Self::ConcreteElement as Element>::Impl: SelectorImplExt {
|
|
||||||
// Get our parent's style. This must be unsafe so that we don't touch the parent's
|
// Get our parent's style. This must be unsafe so that we don't touch the parent's
|
||||||
// borrow flags.
|
// borrow flags.
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
|
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
|
||||||
// enforced safe, race-free access to the parent style.
|
// enforced safe, race-free access to the parent style.
|
||||||
let parent_style = match parent {
|
let parent_style = match parent {
|
||||||
None => None,
|
|
||||||
Some(parent_node) => {
|
Some(parent_node) => {
|
||||||
let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap();
|
let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap();
|
||||||
Some(parent_style)
|
Some(parent_style)
|
||||||
}
|
}
|
||||||
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut applicable_declarations_cache =
|
||||||
|
local_context.applicable_declarations_cache.borrow_mut();
|
||||||
|
|
||||||
let damage;
|
let damage;
|
||||||
if self.is_text_node() {
|
if self.is_text_node() {
|
||||||
let mut data_ref = self.mutate_data().unwrap();
|
let mut data_ref = self.mutate_data().unwrap();
|
||||||
|
@ -677,8 +698,7 @@ pub trait MatchMethods : TNode {
|
||||||
parent_style,
|
parent_style,
|
||||||
&applicable_declarations.normal,
|
&applicable_declarations.normal,
|
||||||
data.style.as_mut(),
|
data.style.as_mut(),
|
||||||
applicable_declarations_cache,
|
&mut applicable_declarations_cache,
|
||||||
new_animations_sender,
|
|
||||||
applicable_declarations.normal_shareable,
|
applicable_declarations.normal_shareable,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
|
@ -690,15 +710,18 @@ pub trait MatchMethods : TNode {
|
||||||
|
|
||||||
|
|
||||||
if !applicable_declarations_for_this_pseudo.is_empty() {
|
if !applicable_declarations_for_this_pseudo.is_empty() {
|
||||||
|
// NB: Transitions and animations should only work for
|
||||||
|
// pseudo-elements ::before and ::after
|
||||||
|
let should_animate_properties =
|
||||||
|
<Self::ConcreteElement as Element>::Impl::pseudo_is_before_or_after(&pseudo);
|
||||||
let (new_damage, style) = self.cascade_node_pseudo_element(
|
let (new_damage, style) = self.cascade_node_pseudo_element(
|
||||||
context,
|
context,
|
||||||
Some(data.style.as_ref().unwrap()),
|
Some(data.style.as_ref().unwrap()),
|
||||||
&*applicable_declarations_for_this_pseudo,
|
&*applicable_declarations_for_this_pseudo,
|
||||||
data.per_pseudo.get_mut(&pseudo),
|
data.per_pseudo.get_mut(&pseudo),
|
||||||
applicable_declarations_cache,
|
&mut applicable_declarations_cache,
|
||||||
new_animations_sender,
|
|
||||||
false,
|
false,
|
||||||
false);
|
should_animate_properties);
|
||||||
data.per_pseudo.insert(pseudo, style);
|
data.per_pseudo.insert(pseudo, style);
|
||||||
|
|
||||||
damage = damage | new_damage;
|
damage = damage | new_damage;
|
||||||
|
|
|
@ -58,7 +58,7 @@ fn top_down_dom<N, C>(unsafe_nodes: UnsafeNodeList,
|
||||||
where N: TNode, C: DomTraversalContext<N> {
|
where N: TNode, C: DomTraversalContext<N> {
|
||||||
let context = C::new(proxy.user_data(), unsafe_nodes.1);
|
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 {
|
for unsafe_node in *unsafe_nodes.0 {
|
||||||
// Get a real layout node.
|
// Get a real layout node.
|
||||||
let node = unsafe { N::from_unsafe(&unsafe_node) };
|
let node = unsafe { N::from_unsafe(&unsafe_node) };
|
||||||
|
|
|
@ -13,7 +13,7 @@ def to_rust_ident(name):
|
||||||
|
|
||||||
|
|
||||||
def to_camel_case(ident):
|
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):
|
class Keyword(object):
|
||||||
|
@ -45,7 +45,7 @@ class Keyword(object):
|
||||||
|
|
||||||
|
|
||||||
class Longhand(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,
|
predefined_type=None, custom_cascade=False, experimental=False, internal=False,
|
||||||
need_clone=False, gecko_ffi_name=None):
|
need_clone=False, gecko_ffi_name=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -61,6 +61,16 @@ class Longhand(object):
|
||||||
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
||||||
self.derived_from = (derived_from or "").split()
|
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):
|
class Shorthand(object):
|
||||||
def __init__(self, name, sub_properties, experimental=False, internal=False):
|
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
|
* 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/. */
|
* 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)">
|
<%def name="longhand(name, **kwargs)">
|
||||||
<%call expr="raw_longhand(name, **kwargs)">
|
<%call expr="raw_longhand(name, **kwargs)">
|
||||||
|
@ -181,9 +181,11 @@
|
||||||
% endfor
|
% 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])}
|
computed_value::T::${to_rust_ident(values.split()[0])}
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn parse(_context: &ParserContext, input: &mut Parser)
|
pub fn parse(_context: &ParserContext, input: &mut Parser)
|
||||||
-> Result<SpecifiedValue, ()> {
|
-> Result<SpecifiedValue, ()> {
|
||||||
computed_value::T::parse(input)
|
computed_value::T::parse(input)
|
||||||
|
@ -191,6 +193,63 @@
|
||||||
</%call>
|
</%call>
|
||||||
</%def>
|
</%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)">
|
<%def name="shorthand(name, sub_properties, experimental=False, **kwargs)">
|
||||||
<%
|
<%
|
||||||
shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental,
|
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" />
|
<%namespace name="helpers" file="/helpers.mako.rs" />
|
||||||
|
|
||||||
<% data.new_style_struct("Background", inherited=False) %>
|
<% 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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::specified::Image;
|
use values::specified::Image;
|
||||||
|
@ -71,7 +72,7 @@ ${helpers.predefined_type(
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="background-position">
|
<%helpers:longhand name="background-position" animatable="True">
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -186,15 +187,23 @@ ${helpers.predefined_type(
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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 cssparser::{ToCss, Token};
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
|
@ -10,15 +10,19 @@
|
||||||
"bool") for side in ["top", "right", "bottom", "left"]]) %>
|
"bool") for side in ["top", "right", "bottom", "left"]]) %>
|
||||||
|
|
||||||
% 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
|
% endfor
|
||||||
|
|
||||||
% for side in ["top", "right", "bottom", "left"]:
|
% 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
|
% endfor
|
||||||
|
|
||||||
% for side in ["top", "right", "bottom", "left"]:
|
% 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 app_units::Au;
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -60,13 +64,15 @@
|
||||||
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
|
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
|
||||||
${helpers.predefined_type("border-" + corner + "-radius", "BorderRadiusSize",
|
${helpers.predefined_type("border-" + corner + "-radius", "BorderRadiusSize",
|
||||||
"computed::BorderRadiusSize::zero()",
|
"computed::BorderRadiusSize::zero()",
|
||||||
"parse")}
|
"parse",
|
||||||
|
animatable=True)}
|
||||||
% endfor
|
% 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",
|
${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
|
||||||
"content-box margin-box",
|
|
||||||
gecko_ffi_name="mFloatEdge",
|
gecko_ffi_name="mFloatEdge",
|
||||||
gecko_constant_prefix="NS_STYLE_FLOAT_EDGE",
|
gecko_constant_prefix="NS_STYLE_FLOAT_EDGE",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
additional_methods=[Method("transition_count", "usize")]) %>
|
additional_methods=[Method("transition_count", "usize")]) %>
|
||||||
|
|
||||||
// TODO(SimonSapin): don't parse `inline-table`, since we don't support it
|
// 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
|
values = """inline block inline-block
|
||||||
table inline-table table-row-group table-header-group table-footer-group
|
table inline-table table-row-group table-header-group table-footer-group
|
||||||
|
@ -86,9 +89,14 @@
|
||||||
|
|
||||||
</%helpers:longhand>
|
</%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 {
|
impl ToComputedValue for SpecifiedValue {
|
||||||
type ComputedValue = computed_value::T;
|
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_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::{SpecifiedValue, get_initial_value};
|
||||||
pub use super::display::{parse};
|
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>
|
||||||
|
|
||||||
<%helpers:longhand name="vertical-align">
|
<%helpers:longhand name="vertical-align"
|
||||||
|
animatable="True">
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
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
|
// CSS 2.1, Section 11 - Visual effects
|
||||||
|
|
||||||
// Non-standard, see https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box#Specifications
|
// 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",
|
${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box",
|
||||||
internal=True)}
|
products="servo", animatable=False, internal=True)}
|
||||||
|
|
||||||
${helpers.single_keyword("overflow-clip-box", "padding-box content-box", products="gecko",
|
${helpers.single_keyword("overflow-clip-box", "padding-box content-box",
|
||||||
internal=True)}
|
products="gecko", animatable=False, internal=True)}
|
||||||
|
|
||||||
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
|
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
|
||||||
${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=True,
|
${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
gecko_constant_prefix="NS_STYLE_OVERFLOW")}
|
need_clone=True, animatable=False,
|
||||||
|
gecko_constant_prefix="NS_STYLE_OVERFLOW")}
|
||||||
|
|
||||||
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
|
// 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 super::overflow_x;
|
||||||
|
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
|
@ -269,7 +285,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
// TODO(pcwalton): Multiple transitions.
|
// TODO(pcwalton): Multiple transitions.
|
||||||
<%helpers:longhand name="transition-duration">
|
<%helpers:longhand name="transition-duration" animatable="False">
|
||||||
|
use values::computed::ComputedValueAsSpecified;
|
||||||
use values::specified::Time;
|
use values::specified::Time;
|
||||||
|
|
||||||
pub use self::computed_value::T as SpecifiedValue;
|
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))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct T(pub Vec<SingleComputedValue>);
|
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 {
|
impl ToCss for T {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
if self.0.is_empty() {
|
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]
|
#[inline]
|
||||||
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
|
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
|
||||||
Time::parse(input)
|
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): Lots more timing functions.
|
||||||
// TODO(pcwalton): Multiple transitions.
|
// 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 self::computed_value::{StartEnd, TransitionTimingFunction};
|
||||||
|
|
||||||
use euclid::point::Point2D;
|
use euclid::point::Point2D;
|
||||||
|
@ -531,170 +541,17 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
// TODO(pcwalton): Lots more properties.
|
<%helpers:longhand name="transition-property" animatable="False">
|
||||||
<%helpers:longhand name="transition-property">
|
|
||||||
use self::computed_value::TransitionProperty;
|
|
||||||
|
|
||||||
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
|
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
|
||||||
pub use self::computed_value::T as SpecifiedValue;
|
pub use self::computed_value::T as SpecifiedValue;
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
// NB: Can't generate the type here because it needs all the longhands
|
||||||
pub use self::TransitionProperty as SingleComputedValue;
|
// generated beforehand.
|
||||||
|
pub use properties::animated_properties::TransitionProperty;
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
pub use properties::animated_properties::TransitionProperty as SingleComputedValue;
|
||||||
#[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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[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())
|
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,()> {
|
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 {
|
impl ToComputedValue for SpecifiedValue {
|
||||||
|
@ -788,54 +592,208 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", need_clone=
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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::{SingleSpecifiedValue, SpecifiedValue};
|
||||||
pub use properties::longhands::transition_duration::{computed_value};
|
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_single_value};
|
||||||
pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
|
pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
|
<%helpers:longhand name="animation-name" 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
|
// CSSOM View Module
|
||||||
// https://www.w3.org/TR/cssom-view-1/
|
// https://www.w3.org/TR/cssom-view-1/
|
||||||
${helpers.single_keyword("scroll-behavior",
|
${helpers.single_keyword("scroll-behavior",
|
||||||
"auto smooth",
|
"auto smooth",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x
|
// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x
|
||||||
${helpers.single_keyword("scroll-snap-type-x",
|
${helpers.single_keyword("scroll-snap-type-x",
|
||||||
"none mandatory proximity",
|
"none mandatory proximity",
|
||||||
products="gecko",
|
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
|
// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-y
|
||||||
${helpers.single_keyword("scroll-snap-type-y",
|
${helpers.single_keyword("scroll-snap-type-y",
|
||||||
"none mandatory proximity",
|
"none mandatory proximity",
|
||||||
products="gecko",
|
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
|
// Compositing and Blending Level 1
|
||||||
// http://www.w3.org/TR/compositing-1/
|
// http://www.w3.org/TR/compositing-1/
|
||||||
${helpers.single_keyword("isolation",
|
${helpers.single_keyword("isolation",
|
||||||
"auto isolate",
|
"auto isolate",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
${helpers.single_keyword("page-break-after",
|
${helpers.single_keyword("page-break-after",
|
||||||
"auto always avoid left right",
|
"auto always avoid left right",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
${helpers.single_keyword("page-break-before",
|
${helpers.single_keyword("page-break-before",
|
||||||
"auto always avoid left right",
|
"auto always avoid left right",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
${helpers.single_keyword("page-break-inside",
|
${helpers.single_keyword("page-break-inside",
|
||||||
"auto avoid",
|
"auto avoid",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
gecko_ffi_name="mBreakInside",
|
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
|
// CSS Basic User Interface Module Level 3
|
||||||
// http://dev.w3.org/csswg/css-ui/
|
// http://dev.w3.org/csswg/css-ui/
|
||||||
${helpers.single_keyword("resize",
|
${helpers.single_keyword("resize",
|
||||||
"none both horizontal vertical",
|
"none both horizontal vertical",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
// Non-standard
|
// Non-standard
|
||||||
${helpers.single_keyword("-moz-appearance",
|
${helpers.single_keyword("-moz-appearance",
|
||||||
|
@ -862,10 +820,11 @@ ${helpers.single_keyword("-moz-appearance",
|
||||||
""",
|
""",
|
||||||
gecko_ffi_name="mAppearance",
|
gecko_ffi_name="mAppearance",
|
||||||
gecko_constant_prefix="NS_THEME",
|
gecko_constant_prefix="NS_THEME",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding
|
// 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 cssparser::{CssStringWriter, ToCss};
|
||||||
use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
|
use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<% data.new_style_struct("Color", inherited=True) %>
|
<% 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::Color as CSSParserColor;
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
use values::specified::{CSSColor, CSSRGBA};
|
use values::specified::{CSSColor, CSSRGBA};
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
<% data.new_style_struct("Column", inherited=False) %>
|
<% 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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -70,7 +71,8 @@
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -137,7 +139,8 @@
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
|
<% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
|
||||||
|
|
||||||
<%helpers:longhand name="content">
|
<%helpers:longhand name="content" animatable="False">
|
||||||
use cssparser::Token;
|
use cssparser::Token;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
@ -171,7 +171,7 @@
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="counter-increment">
|
<%helpers:longhand name="counter-increment" animatable="False">
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use super::content;
|
use super::content;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
@ -241,7 +241,7 @@
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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};
|
pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
|
||||||
use super::counter_increment::{parse_common};
|
use super::counter_increment::{parse_common};
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,10 @@
|
||||||
|
|
||||||
${helpers.predefined_type("opacity",
|
${helpers.predefined_type("opacity",
|
||||||
"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 cssparser::{self, ToCss};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -223,7 +224,8 @@ ${helpers.predefined_type("opacity",
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="clip">
|
// FIXME: This prop should be animatable
|
||||||
|
<%helpers:longhand name="clip" animatable="False">
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -394,7 +396,8 @@ ${helpers.predefined_type("opacity",
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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;
|
//pub use self::computed_value::T as SpecifiedValue;
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -630,7 +633,7 @@ ${helpers.predefined_type("opacity",
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="transform">
|
<%helpers:longhand name="transform" animatable="True">
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use values::CSSFloat;
|
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 app_units::Au;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
use values::specified::{Length, LengthOrPercentage, Percentage};
|
use values::specified::{Length, LengthOrPercentage, Percentage};
|
||||||
|
@ -1261,10 +1271,12 @@ ${helpers.single_keyword("transform-style", "auto flat preserve-3d")}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
${helpers.predefined_type("perspective",
|
${helpers.predefined_type("perspective",
|
||||||
"LengthOrNone",
|
"LengthOrNone",
|
||||||
"computed::LengthOrNone::None")}
|
"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 values::specified::{LengthOrPercentage, Percentage};
|
||||||
|
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
|
@ -1337,6 +1349,7 @@ ${helpers.predefined_type("perspective",
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
${helpers.single_keyword("mix-blend-mode",
|
${helpers.single_keyword("mix-blend-mode",
|
||||||
"""normal multiply screen overlay darken lighten color-dodge
|
"""normal multiply screen overlay darken lighten color-dodge
|
||||||
color-burn hard-light soft-light difference exclusion hue
|
color-burn hard-light soft-light difference exclusion hue
|
||||||
saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND")}
|
saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND",
|
||||||
|
animatable=False)}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<% data.new_style_struct("Font",
|
<% data.new_style_struct("Font",
|
||||||
inherited=True,
|
inherited=True,
|
||||||
additional_methods=[Method("compute_font_hash", is_mut=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 self::computed_value::FontFamily;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
pub use self::computed_value::T as SpecifiedValue;
|
pub use self::computed_value::T as SpecifiedValue;
|
||||||
|
@ -117,10 +117,15 @@
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
|
|
||||||
${helpers.single_keyword("font-style", "normal italic oblique", gecko_constant_prefix="NS_FONT_STYLE")}
|
${helpers.single_keyword("font-style",
|
||||||
${helpers.single_keyword("font-variant", "normal small-caps")}
|
"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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -241,7 +246,7 @@ ${helpers.single_keyword("font-variant", "normal small-caps")}
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%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 app_units::Au;
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -307,8 +312,14 @@ ${helpers.single_keyword("font-variant", "normal small-caps")}
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
|
// FIXME: This prop should be animatable
|
||||||
${helpers.single_keyword("font-stretch",
|
${helpers.single_keyword("font-stretch",
|
||||||
"normal ultra-condensed extra-condensed condensed semi-condensed semi-expanded \
|
"normal ultra-condensed extra-condensed condensed \
|
||||||
expanded extra-expanded ultra-expanded")}
|
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") %>
|
<% 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.
|
// TODO: collapse. Well, do tables first.
|
||||||
${helpers.single_keyword("visibility",
|
${helpers.single_keyword("visibility",
|
||||||
"visible hidden",
|
"visible hidden",
|
||||||
extra_gecko_values="collapse",
|
extra_gecko_values="collapse",
|
||||||
gecko_ffi_name="mVisible")}
|
gecko_ffi_name="mVisible",
|
||||||
|
animatable=True)}
|
||||||
|
|
||||||
// CSS Writing Modes Level 3
|
// CSS Writing Modes Level 3
|
||||||
// http://dev.w3.org/csswg/css-writing-modes/
|
// http://dev.w3.org/csswg/css-writing-modes/
|
||||||
${helpers.single_keyword("writing-mode",
|
${helpers.single_keyword("writing-mode",
|
||||||
"horizontal-tb vertical-rl vertical-lr",
|
"horizontal-tb vertical-rl vertical-lr",
|
||||||
experimental=True,
|
experimental=True,
|
||||||
need_clone=True)}
|
need_clone=True,
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
|
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
|
||||||
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
|
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
|
||||||
|
@ -29,13 +31,16 @@ ${helpers.single_keyword("text-orientation",
|
||||||
experimental=True,
|
experimental=True,
|
||||||
need_clone=True,
|
need_clone=True,
|
||||||
extra_gecko_values="mixed upright",
|
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
|
// CSS Color Module Level 4
|
||||||
// https://drafts.csswg.org/css-color/
|
// 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 {
|
pub mod computed_value {
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
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
|
// Used in the bottom-up flow construction traversal to avoid constructing flows for
|
||||||
// descendants of nodes with `display: none`.
|
// 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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
|
|
@ -10,36 +10,52 @@
|
||||||
inherited=True,
|
inherited=True,
|
||||||
gecko_name="SVG") %>
|
gecko_name="SVG") %>
|
||||||
|
|
||||||
|
// TODO(emilio): Should some of these types be animatable?
|
||||||
|
|
||||||
// Section 10 - Text
|
// 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
|
// Section 11 - Painting: Filling, Stroking and Marker Symbols
|
||||||
${helpers.single_keyword("color-interpolation", "auto sRGB linearRGB", products="gecko")}
|
${helpers.single_keyword("color-interpolation",
|
||||||
|
|
||||||
${helpers.single_keyword("color-interpolation-filters",
|
|
||||||
"auto sRGB linearRGB",
|
"auto sRGB linearRGB",
|
||||||
products="gecko",
|
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",
|
${helpers.single_keyword("shape-rendering",
|
||||||
"auto optimizeSpeed crispEdges geometricPrecision",
|
"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",
|
${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
|
||||||
products="gecko")}
|
"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
|
// Section 14 - Clipping, Masking and Compositing
|
||||||
${helpers.single_keyword("clip-rule", "nonzero evenodd",
|
${helpers.single_keyword("clip-rule", "nonzero evenodd",
|
||||||
products="gecko",
|
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") %>
|
<% 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("border-collapse", "separate collapse",
|
||||||
${helpers.single_keyword("empty-cells", "show hide", gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS")}
|
gecko_constant_prefix="NS_STYLE_BORDER",
|
||||||
${helpers.single_keyword("caption-side", "top bottom", extra_gecko_values="right left top-outside bottom-outside")}
|
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 app_units::Au;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
|
<% 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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -120,7 +120,7 @@
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="text-align">
|
<%helpers:longhand name="text-align" animatable="False">
|
||||||
pub use self::computed_value::T as SpecifiedValue;
|
pub use self::computed_value::T as SpecifiedValue;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||||||
|
@ -179,7 +179,8 @@
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="letter-spacing">
|
// FIXME: This prop should be animatable.
|
||||||
|
<%helpers:longhand name="letter-spacing" animatable="False">
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -243,7 +244,7 @@
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="word-spacing">
|
<%helpers:longhand name="word-spacing" animatable="False">
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -309,27 +310,33 @@
|
||||||
|
|
||||||
${helpers.predefined_type("text-indent",
|
${helpers.predefined_type("text-indent",
|
||||||
"LengthOrPercentage",
|
"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
|
// Also known as "word-wrap" (which is more popular because of IE), but this is the preferred
|
||||||
// name per CSS-TEXT 6.2.
|
// name per CSS-TEXT 6.2.
|
||||||
${helpers.single_keyword("overflow-wrap",
|
${helpers.single_keyword("overflow-wrap",
|
||||||
"normal break-word",
|
"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.
|
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
|
||||||
${helpers.single_keyword("word-break",
|
${helpers.single_keyword("word-break",
|
||||||
"normal break-all",
|
"normal break-all",
|
||||||
extra_gecko_values="keep-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`.
|
// TODO(pcwalton): Support `text-justify: distribute`.
|
||||||
${helpers.single_keyword("text-justify",
|
${helpers.single_keyword("text-justify",
|
||||||
"auto none inter-word",
|
"auto none inter-word",
|
||||||
products="servo")}
|
products="servo",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
<%helpers:longhand name="-servo-text-decorations-in-effect"
|
<%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 cssparser::{RGBA, ToCss};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -410,8 +417,10 @@ ${helpers.single_keyword("text-justify",
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:single_keyword_computed name="white-space" values="normal pre nowrap pre-wrap pre-line",
|
<%helpers:single_keyword_computed name="white-space"
|
||||||
gecko_constant_prefix="NS_STYLE_WHITESPACE">
|
values="normal pre nowrap pre-wrap pre-line"
|
||||||
|
gecko_constant_prefix="NS_STYLE_WHITESPACE"
|
||||||
|
animatable="False">
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||||||
|
|
||||||
|
@ -448,7 +457,7 @@ ${helpers.single_keyword("text-justify",
|
||||||
}
|
}
|
||||||
</%helpers:single_keyword_computed>
|
</%helpers:single_keyword_computed>
|
||||||
|
|
||||||
<%helpers:longhand name="text-shadow">
|
<%helpers:longhand name="text-shadow" animatable="True">
|
||||||
use cssparser::{self, ToCss};
|
use cssparser::{self, ToCss};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::LocalToCss;
|
use values::LocalToCss;
|
||||||
|
@ -635,16 +644,22 @@ ${helpers.single_keyword("text-justify",
|
||||||
// TODO(pcwalton): `full-width`
|
// TODO(pcwalton): `full-width`
|
||||||
${helpers.single_keyword("text-transform",
|
${helpers.single_keyword("text-transform",
|
||||||
"none capitalize uppercase lowercase",
|
"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
|
// CSS Text Module Level 3
|
||||||
// https://www.w3.org/TR/css-text-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
|
// CSS Ruby Layout Module Level 1
|
||||||
// https://www.w3.org/TR/css-ruby-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) %>
|
<% 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:
|
// 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
|
myanmar oriya persian telugu thai tibetan cjk-earthly-branch
|
||||||
cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana
|
cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana
|
||||||
katakana-iroha""",
|
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 cssparser::{ToCss, Token};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -92,7 +93,7 @@ ${helpers.single_keyword("list-style-type", """
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="quotes">
|
<%helpers:longhand name="quotes" animatable="False">
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
|
|
@ -8,5 +8,6 @@
|
||||||
|
|
||||||
% for side in ["top", "right", "bottom", "left"]:
|
% for side in ["top", "right", "bottom", "left"]:
|
||||||
${helpers.predefined_type("margin-" + side, "LengthOrPercentageOrAuto",
|
${helpers.predefined_type("margin-" + side, "LengthOrPercentageOrAuto",
|
||||||
"computed::LengthOrPercentageOrAuto::Length(Au(0))")}
|
"computed::LengthOrPercentageOrAuto::Length(Au(0))",
|
||||||
|
animatable=True)}
|
||||||
% endfor
|
% endfor
|
||||||
|
|
|
@ -10,9 +10,10 @@
|
||||||
additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
|
additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
|
||||||
|
|
||||||
// TODO(pcwalton): `invert`
|
// 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 use values::specified::BorderStyle as SpecifiedValue;
|
||||||
pub fn get_initial_value() -> SpecifiedValue { SpecifiedValue::none }
|
pub fn get_initial_value() -> SpecifiedValue { SpecifiedValue::none }
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
|
@ -26,7 +27,7 @@ ${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::Curr
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
<%helpers:longhand name="outline-width">
|
<%helpers:longhand name="outline-width" animatable="True">
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::ToCss;
|
use cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -60,10 +61,12 @@ ${helpers.predefined_type("outline-color", "CSSColor", "::cssparser::Color::Curr
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
// The -moz-outline-radius-* properties are non-standard and not on a standards track.
|
// 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"]:
|
% for corner in ["topleft", "topright", "bottomright", "bottomleft"]:
|
||||||
${helpers.predefined_type("-moz-outline-radius-" + corner, "BorderRadiusSize",
|
${helpers.predefined_type("-moz-outline-radius-" + corner, "BorderRadiusSize",
|
||||||
"computed::BorderRadiusSize::zero()",
|
"computed::BorderRadiusSize::zero()",
|
||||||
"parse", products="gecko")}
|
"parse", products="gecko",
|
||||||
|
animatable=False)}
|
||||||
% endfor
|
% 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"]:
|
% for side in ["top", "right", "bottom", "left"]:
|
||||||
${helpers.predefined_type("padding-" + side, "LengthOrPercentage",
|
${helpers.predefined_type("padding-" + side, "LengthOrPercentage",
|
||||||
"computed::LengthOrPercentage::Length(Au(0))",
|
"computed::LengthOrPercentage::Length(Au(0))",
|
||||||
"parse_non_negative")}
|
"parse_non_negative",
|
||||||
|
animatable=True)}
|
||||||
% endfor
|
% endfor
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<% data.new_style_struct("Pointing", inherited=True, gecko_name="UserInterface") %>
|
<% 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;
|
pub use self::computed_value::T as SpecifiedValue;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
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)
|
// NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
|
||||||
// is nonstandard, slated for CSS4-UI.
|
// is nonstandard, slated for CSS4-UI.
|
||||||
// TODO(pcwalton): SVG-only values.
|
// 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",
|
${helpers.single_keyword("-moz-user-input", "none enabled disabled",
|
||||||
gecko_ffi_name="mUserInput", gecko_constant_prefix="NS_STYLE_USER_INPUT")}
|
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",
|
${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only",
|
||||||
gecko_ffi_name="mUserModify", gecko_constant_prefix="NS_STYLE_USER_MODIFY")}
|
products="gecko", gecko_ffi_name="mUserModify",
|
||||||
|
gecko_constant_prefix="NS_STYLE_USER_MODIFY",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
${helpers.single_keyword("-moz-user-focus",
|
${helpers.single_keyword("-moz-user-focus",
|
||||||
"ignore normal select-after select-before select-menu select-same select-all none",
|
"ignore normal select-after select-before select-menu select-same select-all none",
|
||||||
products="gecko",
|
products="gecko", gecko_ffi_name="mUserFocus",
|
||||||
gecko_ffi_name="mUserFocus",
|
gecko_constant_prefix="NS_STYLE_USER_FOCUS",
|
||||||
gecko_constant_prefix="NS_STYLE_USER_FOCUS")}
|
animatable=False)}
|
||||||
|
|
|
@ -8,10 +8,11 @@
|
||||||
|
|
||||||
% for side in ["top", "right", "bottom", "left"]:
|
% for side in ["top", "right", "bottom", "left"]:
|
||||||
${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
|
${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
|
||||||
"computed::LengthOrPercentageOrAuto::Auto")}
|
"computed::LengthOrPercentageOrAuto::Auto",
|
||||||
|
animatable=True)}
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
<%helpers:longhand name="z-index">
|
<%helpers:longhand name="z-index" animatable="True">
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
|
||||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||||||
|
@ -62,39 +63,49 @@
|
||||||
// http://www.w3.org/TR/css3-flexbox/
|
// http://www.w3.org/TR/css3-flexbox/
|
||||||
|
|
||||||
// Flex container properties
|
// 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): 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
|
// 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",
|
${helpers.single_keyword("justify-content", "flex-start flex-end center space-between space-around",
|
||||||
experimental=True,
|
experimental=True,
|
||||||
gecko_constant_prefix="NS_STYLE_JUSTIFY",
|
gecko_constant_prefix="NS_STYLE_JUSTIFY",
|
||||||
products="servo")}
|
products="servo",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
${helpers.single_keyword("align-items", "stretch flex-start flex-end center baseline",
|
${helpers.single_keyword("align-items", "stretch flex-start flex-end center baseline",
|
||||||
experimental=True,
|
experimental=True,
|
||||||
need_clone=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",
|
${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around",
|
||||||
experimental=True,
|
experimental=True,
|
||||||
gecko_constant_prefix="NS_STYLE_ALIGN",
|
gecko_constant_prefix="NS_STYLE_ALIGN",
|
||||||
products="servo")}
|
products="servo",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
// Flex item properties
|
// 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",
|
${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
|
||||||
experimental=True,
|
experimental=True,
|
||||||
need_clone=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
|
// https://drafts.csswg.org/css-flexbox/#propdef-order
|
||||||
<%helpers:longhand name="order">
|
<%helpers:longhand name="order" animatable="True">
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
|
|
||||||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||||||
|
@ -115,41 +126,53 @@ ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
|
// FIXME: This property should be animatable.
|
||||||
${helpers.predefined_type("flex-basis",
|
${helpers.predefined_type("flex-basis",
|
||||||
"LengthOrPercentageOrAutoOrContent",
|
"LengthOrPercentageOrAutoOrContent",
|
||||||
"computed::LengthOrPercentageOrAutoOrContent::Auto")}
|
"computed::LengthOrPercentageOrAutoOrContent::Auto",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
${helpers.predefined_type("width",
|
${helpers.predefined_type("width",
|
||||||
"LengthOrPercentageOrAuto",
|
"LengthOrPercentageOrAuto",
|
||||||
"computed::LengthOrPercentageOrAuto::Auto",
|
"computed::LengthOrPercentageOrAuto::Auto",
|
||||||
"parse_non_negative")}
|
"parse_non_negative",
|
||||||
|
animatable=True)}
|
||||||
|
|
||||||
${helpers.predefined_type("height",
|
${helpers.predefined_type("height",
|
||||||
"LengthOrPercentageOrAuto",
|
"LengthOrPercentageOrAuto",
|
||||||
"computed::LengthOrPercentageOrAuto::Auto",
|
"computed::LengthOrPercentageOrAuto::Auto",
|
||||||
"parse_non_negative")}
|
"parse_non_negative",
|
||||||
|
animatable=True)}
|
||||||
|
|
||||||
${helpers.predefined_type("min-width",
|
${helpers.predefined_type("min-width",
|
||||||
"LengthOrPercentage",
|
"LengthOrPercentage",
|
||||||
"computed::LengthOrPercentage::Length(Au(0))",
|
"computed::LengthOrPercentage::Length(Au(0))",
|
||||||
"parse_non_negative")}
|
"parse_non_negative",
|
||||||
|
animatable=True)}
|
||||||
|
|
||||||
${helpers.predefined_type("max-width",
|
${helpers.predefined_type("max-width",
|
||||||
"LengthOrPercentageOrNone",
|
"LengthOrPercentageOrNone",
|
||||||
"computed::LengthOrPercentageOrNone::None",
|
"computed::LengthOrPercentageOrNone::None",
|
||||||
"parse_non_negative")}
|
"parse_non_negative",
|
||||||
|
animatable=True)}
|
||||||
|
|
||||||
${helpers.predefined_type("min-height",
|
${helpers.predefined_type("min-height",
|
||||||
"LengthOrPercentage",
|
"LengthOrPercentage",
|
||||||
"computed::LengthOrPercentage::Length(Au(0))",
|
"computed::LengthOrPercentage::Length(Au(0))",
|
||||||
"parse_non_negative")}
|
"parse_non_negative",
|
||||||
|
animatable=True)}
|
||||||
|
|
||||||
${helpers.predefined_type("max-height",
|
${helpers.predefined_type("max-height",
|
||||||
"LengthOrPercentageOrNone",
|
"LengthOrPercentageOrNone",
|
||||||
"computed::LengthOrPercentageOrNone::None",
|
"computed::LengthOrPercentageOrNone::None",
|
||||||
"parse_non_negative")}
|
"parse_non_negative",
|
||||||
|
animatable=True)}
|
||||||
|
|
||||||
${helpers.single_keyword("box-sizing",
|
${helpers.single_keyword("box-sizing",
|
||||||
"content-box border-box")}
|
"content-box border-box",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
// CSS Image Values and Replaced Content Module Level 3
|
// CSS Image Values and Replaced Content Module Level 3
|
||||||
// https://drafts.csswg.org/css-images-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") %>
|
<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>
|
||||||
|
|
||||||
|
// TODO: Which of these should be animatable properties?
|
||||||
${helpers.single_keyword("dominant-baseline",
|
${helpers.single_keyword("dominant-baseline",
|
||||||
"""auto use-script no-change reset-size ideographic alphabetic hanging
|
"""auto use-script no-change reset-size ideographic alphabetic hanging
|
||||||
mathematical central middle text-after-edge text-before-edge""",
|
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
|
// Section 13 - Gradients and Patterns
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"stop-color", "CSSColor",
|
"stop-color", "CSSColor",
|
||||||
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
|
"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
|
// Section 15 - Filter Effects
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"flood-color", "CSSColor",
|
"flood-color", "CSSColor",
|
||||||
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
|
"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(
|
${helpers.predefined_type(
|
||||||
"lighting-color", "CSSColor",
|
"lighting-color", "CSSColor",
|
||||||
"CSSParserColor::RGBA(RGBA { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 })",
|
"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
|
// CSS Masking Module Level 1
|
||||||
// https://www.w3.org/TR/css-masking-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) %>
|
<% 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_overline", "bool"),
|
||||||
Method("has_line_through", "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'}"
|
<%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 cssparser::ToCss;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
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",
|
${helpers.single_keyword("text-decoration-style",
|
||||||
"solid double dotted dashed wavy -moz-none",
|
"solid double dotted dashed wavy -moz-none",
|
||||||
products="gecko")}
|
products="gecko",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"text-decoration-color", "CSSColor",
|
"text-decoration-color", "CSSColor",
|
||||||
"CSSParserColor::RGBA(RGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 })",
|
"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/
|
// https://drafts.csswg.org/css-ui-3/
|
||||||
<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>
|
<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>
|
||||||
|
|
||||||
${helpers.single_keyword("ime-mode", "normal auto active disabled inactive", products="gecko",
|
${helpers.single_keyword("ime-mode", "normal auto active disabled inactive",
|
||||||
gecko_ffi_name="mIMEMode")}
|
products="gecko", gecko_ffi_name="mIMEMode",
|
||||||
|
animatable=False)}
|
||||||
|
|
||||||
${helpers.single_keyword("-moz-user-select", "auto text none all", products="gecko",
|
${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.
|
// Non-standard properties that Gecko uses for XUL elements.
|
||||||
<% data.new_style_struct("XUL", inherited=False) %>
|
<% data.new_style_struct("XUL", inherited=False) %>
|
||||||
|
|
||||||
${helpers.single_keyword("-moz-box-align", "stretch start center baseline end", products="gecko",
|
${helpers.single_keyword("-moz-box-align", "stretch start center baseline end",
|
||||||
gecko_ffi_name="mBoxAlign", gecko_constant_prefix="NS_STYLE_BOX_ALIGN")}
|
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",
|
${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative",
|
||||||
gecko_ffi_name="mBoxFlex")}
|
products="gecko", gecko_ffi_name="mBoxFlex",
|
||||||
|
animatable=False)}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use std::ascii::AsciiExt;
|
||||||
use std::boxed::Box as StdBox;
|
use std::boxed::Box as StdBox;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Write;
|
use std::fmt::{Debug, Write};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
@ -132,6 +132,10 @@ pub mod shorthands {
|
||||||
<%include file="/shorthand/text.mako.rs" />
|
<%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.
|
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
|
||||||
// Maybe submit for inclusion in libstd?
|
// Maybe submit for inclusion in libstd?
|
||||||
|
@ -1044,13 +1048,31 @@ impl PropertyDeclaration {
|
||||||
PropertyDeclaration::Custom(_, _) => &[]
|
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 {
|
pub mod style_struct_traits {
|
||||||
use super::longhands;
|
use super::longhands;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
% for style_struct in data.active_style_structs():
|
% 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:
|
% for longhand in style_struct.longhands:
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T);
|
fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T);
|
||||||
|
@ -1079,7 +1101,7 @@ pub mod style_structs {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
% else:
|
% else:
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
% endif
|
% endif
|
||||||
pub struct ${style_struct.servo_struct_name} {
|
pub struct ${style_struct.servo_struct_name} {
|
||||||
|
@ -1121,32 +1143,45 @@ pub mod style_structs {
|
||||||
}
|
}
|
||||||
% endfor
|
% endfor
|
||||||
% elif style_struct.trait_name == "Box":
|
% elif style_struct.trait_name == "Box":
|
||||||
|
#[inline]
|
||||||
fn clone_display(&self) -> longhands::display::computed_value::T {
|
fn clone_display(&self) -> longhands::display::computed_value::T {
|
||||||
self.display.clone()
|
self.display.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_position(&self) -> longhands::position::computed_value::T {
|
fn clone_position(&self) -> longhands::position::computed_value::T {
|
||||||
self.position.clone()
|
self.position.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_float(&self) -> longhands::float::computed_value::T {
|
fn clone_float(&self) -> longhands::float::computed_value::T {
|
||||||
self.float.clone()
|
self.float.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_overflow_x(&self) -> longhands::overflow_x::computed_value::T {
|
fn clone_overflow_x(&self) -> longhands::overflow_x::computed_value::T {
|
||||||
self.overflow_x.clone()
|
self.overflow_x.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T {
|
fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T {
|
||||||
self.overflow_y.clone()
|
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 {
|
fn transition_count(&self) -> usize {
|
||||||
self.transition_property.0.len()
|
self.transition_property.0.len()
|
||||||
}
|
}
|
||||||
% elif style_struct.trait_name == "Color":
|
% elif style_struct.trait_name == "Color":
|
||||||
|
#[inline]
|
||||||
fn clone_color(&self) -> longhands::color::computed_value::T {
|
fn clone_color(&self) -> longhands::color::computed_value::T {
|
||||||
self.color.clone()
|
self.color.clone()
|
||||||
}
|
}
|
||||||
% elif style_struct.trait_name == "Font":
|
% elif style_struct.trait_name == "Font":
|
||||||
|
#[inline]
|
||||||
fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
|
fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
|
||||||
self.font_size.clone()
|
self.font_size.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
|
fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
|
||||||
self.font_weight.clone()
|
self.font_weight.clone()
|
||||||
}
|
}
|
||||||
|
@ -1159,42 +1194,53 @@ pub mod style_structs {
|
||||||
self.hash = hasher.finish()
|
self.hash = hasher.finish()
|
||||||
}
|
}
|
||||||
% elif style_struct.trait_name == "InheritedBox":
|
% elif style_struct.trait_name == "InheritedBox":
|
||||||
|
#[inline]
|
||||||
fn clone_direction(&self) -> longhands::direction::computed_value::T {
|
fn clone_direction(&self) -> longhands::direction::computed_value::T {
|
||||||
self.direction.clone()
|
self.direction.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_writing_mode(&self) -> longhands::writing_mode::computed_value::T {
|
fn clone_writing_mode(&self) -> longhands::writing_mode::computed_value::T {
|
||||||
self.writing_mode.clone()
|
self.writing_mode.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_text_orientation(&self) -> longhands::text_orientation::computed_value::T {
|
fn clone_text_orientation(&self) -> longhands::text_orientation::computed_value::T {
|
||||||
self.text_orientation.clone()
|
self.text_orientation.clone()
|
||||||
}
|
}
|
||||||
% elif style_struct.trait_name == "InheritedText" and product == "servo":
|
% elif style_struct.trait_name == "InheritedText" and product == "servo":
|
||||||
|
#[inline]
|
||||||
fn clone__servo_text_decorations_in_effect(&self) ->
|
fn clone__servo_text_decorations_in_effect(&self) ->
|
||||||
longhands::_servo_text_decorations_in_effect::computed_value::T {
|
longhands::_servo_text_decorations_in_effect::computed_value::T {
|
||||||
self._servo_text_decorations_in_effect.clone()
|
self._servo_text_decorations_in_effect.clone()
|
||||||
}
|
}
|
||||||
% elif style_struct.trait_name == "Outline":
|
% elif style_struct.trait_name == "Outline":
|
||||||
|
#[inline]
|
||||||
fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
|
fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
|
||||||
self.outline_style.clone()
|
self.outline_style.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn outline_has_nonzero_width(&self) -> bool {
|
fn outline_has_nonzero_width(&self) -> bool {
|
||||||
self.outline_width != ::app_units::Au(0)
|
self.outline_width != ::app_units::Au(0)
|
||||||
}
|
}
|
||||||
% elif style_struct.trait_name == "Position":
|
% elif style_struct.trait_name == "Position":
|
||||||
|
#[inline]
|
||||||
fn clone_align_items(&self) -> longhands::align_items::computed_value::T {
|
fn clone_align_items(&self) -> longhands::align_items::computed_value::T {
|
||||||
self.align_items.clone()
|
self.align_items.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn clone_align_self(&self) -> longhands::align_self::computed_value::T {
|
fn clone_align_self(&self) -> longhands::align_self::computed_value::T {
|
||||||
self.align_self.clone()
|
self.align_self.clone()
|
||||||
}
|
}
|
||||||
% elif style_struct.trait_name == "Text":
|
% elif style_struct.trait_name == "Text":
|
||||||
<% text_decoration_field = 'text_decoration' if product == 'servo' else 'text_decoration_line' %>
|
<% text_decoration_field = 'text_decoration' if product == 'servo' else 'text_decoration_line' %>
|
||||||
|
#[inline]
|
||||||
fn has_underline(&self) -> bool {
|
fn has_underline(&self) -> bool {
|
||||||
self.${text_decoration_field}.underline
|
self.${text_decoration_field}.underline
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn has_overline(&self) -> bool {
|
fn has_overline(&self) -> bool {
|
||||||
self.${text_decoration_field}.overline
|
self.${text_decoration_field}.overline
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn has_line_through(&self) -> bool {
|
fn has_line_through(&self) -> bool {
|
||||||
self.${text_decoration_field}.line_through
|
self.${text_decoration_field}.line_through
|
||||||
}
|
}
|
||||||
|
@ -1204,7 +1250,7 @@ pub mod style_structs {
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ComputedValues : Clone + Send + Sync + 'static {
|
pub trait ComputedValues : Debug + Clone + Send + Sync + 'static {
|
||||||
% for style_struct in data.active_style_structs():
|
% for style_struct in data.active_style_structs():
|
||||||
type Concrete${style_struct.trait_name}: style_struct_traits::${style_struct.trait_name};
|
type Concrete${style_struct.trait_name}: style_struct_traits::${style_struct.trait_name};
|
||||||
% endfor
|
% endfor
|
||||||
|
@ -1247,7 +1293,7 @@ pub trait ComputedValues : Clone + Send + Sync + 'static {
|
||||||
fn is_multicol(&self) -> bool;
|
fn is_multicol(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct ServoComputedValues {
|
pub struct ServoComputedValues {
|
||||||
% for style_struct in data.active_style_structs():
|
% for style_struct in data.active_style_structs():
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
let (mut timing_function, mut delay) = (None, None);
|
let (mut timing_function, mut delay) = (None, None);
|
||||||
loop {
|
loop {
|
||||||
if property.is_none() {
|
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);
|
property = Some(value);
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use properties::{self, ServoComputedValues};
|
||||||
use selector_matching::{USER_OR_USER_AGENT_STYLESHEETS, QUIRKS_MODE_STYLESHEET};
|
use selector_matching::{USER_OR_USER_AGENT_STYLESHEETS, QUIRKS_MODE_STYLESHEET};
|
||||||
use selectors::Element;
|
use selectors::Element;
|
||||||
use selectors::parser::{ParserContext, SelectorImpl};
|
use selectors::parser::{ParserContext, SelectorImpl};
|
||||||
|
use std::fmt::Debug;
|
||||||
use stylesheets::Stylesheet;
|
use stylesheets::Stylesheet;
|
||||||
|
|
||||||
/// This function determines if a pseudo-element is eagerly cascaded or not.
|
/// 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;
|
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;
|
type ComputedValues: properties::ComputedValues;
|
||||||
|
|
||||||
fn pseudo_element_cascade_type(pseudo: &Self::PseudoElement) -> PseudoElementCascadeType;
|
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;
|
fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState;
|
||||||
|
|
||||||
|
@ -109,6 +113,15 @@ pub enum PseudoElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PseudoElement {
|
impl PseudoElement {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_before_or_after(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
PseudoElement::Before |
|
||||||
|
PseudoElement::After => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cascade_type(&self) -> PseudoElementCascadeType {
|
pub fn cascade_type(&self) -> PseudoElementCascadeType {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -249,6 +262,11 @@ impl SelectorImplExt for ServoSelectorImpl {
|
||||||
pc.state_flag()
|
pc.state_flag()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
|
||||||
|
pseudo.is_before_or_after()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>] {
|
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>] {
|
||||||
&*USER_OR_USER_AGENT_STYLESHEETS
|
&*USER_OR_USER_AGENT_STYLESHEETS
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
use dom::PresentationalHintsSynthetizer;
|
use dom::PresentationalHintsSynthetizer;
|
||||||
use element_state::*;
|
use element_state::*;
|
||||||
use error_reporting::StdoutErrorReporter;
|
use error_reporting::StdoutErrorReporter;
|
||||||
|
use keyframes::KeyframesAnimation;
|
||||||
use media_queries::{Device, MediaType};
|
use media_queries::{Device, MediaType};
|
||||||
use parser::ParserContextExtraData;
|
use parser::ParserContextExtraData;
|
||||||
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock};
|
use properties::{self, PropertyDeclaration, PropertyDeclarationBlock};
|
||||||
|
@ -22,8 +23,9 @@ use std::collections::HashMap;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use string_cache::Atom;
|
||||||
use style_traits::viewport::ViewportConstraints;
|
use style_traits::viewport::ViewportConstraints;
|
||||||
use stylesheets::{CSSRuleIteratorExt, Origin, Stylesheet};
|
use stylesheets::{CSSRule, CSSRuleIteratorExt, Origin, Stylesheet};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::resource_files::read_resource_file;
|
use util::resource_files::read_resource_file;
|
||||||
|
@ -126,6 +128,9 @@ pub struct Stylist<Impl: SelectorImplExt> {
|
||||||
PerPseudoElementSelectorMap<Impl>,
|
PerPseudoElementSelectorMap<Impl>,
|
||||||
BuildHasherDefault<::fnv::FnvHasher>>,
|
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.
|
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
||||||
/// These are eagerly computed once, and then used to resolve the new
|
/// These are eagerly computed once, and then used to resolve the new
|
||||||
/// computed values on the fly on layout.
|
/// computed values on the fly on layout.
|
||||||
|
@ -150,6 +155,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
|
|
||||||
element_map: PerPseudoElementSelectorMap::new(),
|
element_map: PerPseudoElementSelectorMap::new(),
|
||||||
pseudos_map: HashMap::with_hasher(Default::default()),
|
pseudos_map: HashMap::with_hasher(Default::default()),
|
||||||
|
animations: HashMap::with_hasher(Default::default()),
|
||||||
precomputed_pseudo_element_decls: HashMap::with_hasher(Default::default()),
|
precomputed_pseudo_element_decls: HashMap::with_hasher(Default::default()),
|
||||||
rules_source_order: 0,
|
rules_source_order: 0,
|
||||||
state_deps: DependencySet::new(),
|
state_deps: DependencySet::new(),
|
||||||
|
@ -173,6 +179,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
|
|
||||||
self.element_map = PerPseudoElementSelectorMap::new();
|
self.element_map = PerPseudoElementSelectorMap::new();
|
||||||
self.pseudos_map = HashMap::with_hasher(Default::default());
|
self.pseudos_map = HashMap::with_hasher(Default::default());
|
||||||
|
self.animations = HashMap::with_hasher(Default::default());
|
||||||
Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
|
||||||
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
|
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() {
|
for rule in stylesheet.effective_rules(&self.device) {
|
||||||
append!(style_rule, normal);
|
match *rule {
|
||||||
append!(style_rule, important);
|
CSSRule::Style(ref style_rule) => {
|
||||||
rules_source_order += 1;
|
append!(style_rule, normal);
|
||||||
for selector in &style_rule.selectors {
|
append!(style_rule, important);
|
||||||
self.state_deps.note_selector(selector.compound_selectors.clone());
|
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| {
|
Impl::each_precomputed_pseudo_element(|pseudo| {
|
||||||
// TODO: Consider not doing this and just getting the rules on the
|
// 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
|
// 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, _) =
|
let (computed, _) =
|
||||||
properties::cascade(self.device.au_viewport_size(),
|
properties::cascade(self.device.au_viewport_size(),
|
||||||
&declarations, false,
|
&declarations, false,
|
||||||
parent.map(|p| &**p), None,
|
parent.map(|p| &**p),
|
||||||
|
None,
|
||||||
Box::new(StdoutErrorReporter));
|
Box::new(StdoutErrorReporter));
|
||||||
Some(Arc::new(computed))
|
Some(Arc::new(computed))
|
||||||
} else {
|
} else {
|
||||||
|
@ -437,6 +464,11 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
pub fn is_device_dirty(&self) -> bool {
|
pub fn is_device_dirty(&self) -> bool {
|
||||||
self.is_device_dirty
|
self.is_device_dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn animations(&self) -> &HashMap<Atom, KeyframesAnimation> {
|
||||||
|
&self.animations
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map that contains the CSS rules for a given origin.
|
/// 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
|
/* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
//! Concrete types for servo Style implementation
|
//! Concrete types for servo Style implementation
|
||||||
|
|
||||||
|
use animation;
|
||||||
use context;
|
use context;
|
||||||
use data;
|
use data;
|
||||||
use properties::ServoComputedValues;
|
use properties::ServoComputedValues;
|
||||||
|
@ -15,3 +15,4 @@ pub type Stylesheet = stylesheets::Stylesheet<ServoSelectorImpl>;
|
||||||
pub type PrivateStyleData = data::PrivateStyleData<ServoSelectorImpl, ServoComputedValues>;
|
pub type PrivateStyleData = data::PrivateStyleData<ServoSelectorImpl, ServoComputedValues>;
|
||||||
pub type Stylist = selector_matching::Stylist<ServoSelectorImpl>;
|
pub type Stylist = selector_matching::Stylist<ServoSelectorImpl>;
|
||||||
pub type SharedStyleContext = context::SharedStyleContext<ServoSelectorImpl>;
|
pub type SharedStyleContext = context::SharedStyleContext<ServoSelectorImpl>;
|
||||||
|
pub type Animation = animation::Animation<ServoSelectorImpl>;
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
//! Style sheets and their CSS rules.
|
//! Style sheets and their CSS rules.
|
||||||
|
|
||||||
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes};
|
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes};
|
||||||
use cssparser::{AtRuleType, RuleListParser};
|
use cssparser::{AtRuleType, RuleListParser, Token};
|
||||||
use encoding::EncodingRef;
|
use encoding::EncodingRef;
|
||||||
use error_reporting::ParseErrorReporter;
|
use error_reporting::ParseErrorReporter;
|
||||||
use font_face::{FontFaceRule, parse_font_face_block};
|
use font_face::{FontFaceRule, parse_font_face_block};
|
||||||
|
use keyframes::{Keyframe, parse_keyframe_list};
|
||||||
use media_queries::{Device, MediaQueryList, parse_media_query_list};
|
use media_queries::{Device, MediaQueryList, parse_media_query_list};
|
||||||
use parser::{ParserContext, ParserContextExtraData, log_css_error};
|
use parser::{ParserContext, ParserContextExtraData, log_css_error};
|
||||||
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
|
use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
|
||||||
|
@ -62,6 +63,14 @@ pub enum CSSRule<Impl: SelectorImpl> {
|
||||||
Media(MediaRule<Impl>),
|
Media(MediaRule<Impl>),
|
||||||
FontFace(FontFaceRule),
|
FontFace(FontFaceRule),
|
||||||
Viewport(ViewportRule),
|
Viewport(ViewportRule),
|
||||||
|
Keyframes(KeyframesRule),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, HeapSizeOf, PartialEq)]
|
||||||
|
pub struct KeyframesRule {
|
||||||
|
pub name: Atom,
|
||||||
|
pub keyframes: Vec<Keyframe>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -71,6 +80,7 @@ pub struct MediaRule<Impl: SelectorImpl> {
|
||||||
pub rules: Vec<CSSRule<Impl>>,
|
pub rules: Vec<CSSRule<Impl>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<Impl: SelectorImpl> MediaRule<Impl> {
|
impl<Impl: SelectorImpl> MediaRule<Impl> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn evaluate(&self, device: &Device) -> bool {
|
pub fn evaluate(&self, device: &Device) -> bool {
|
||||||
|
@ -127,7 +137,7 @@ impl<Impl: SelectorImpl> Stylesheet<Impl> {
|
||||||
let mut input = Parser::new(css);
|
let mut input = Parser::new(css);
|
||||||
input.look_for_viewport_percentages();
|
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);
|
let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
|
||||||
while let Some(result) = iter.next() {
|
while let Some(result) = iter.next() {
|
||||||
|
@ -142,6 +152,7 @@ impl<Impl: SelectorImpl> Stylesheet<Impl> {
|
||||||
Some(namespace.clone());
|
Some(namespace.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rules.push(rule);
|
rules.push(rule);
|
||||||
}
|
}
|
||||||
Err(range) => {
|
Err(range) => {
|
||||||
|
@ -153,6 +164,7 @@ impl<Impl: SelectorImpl> Stylesheet<Impl> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stylesheet {
|
Stylesheet {
|
||||||
origin: origin,
|
origin: origin,
|
||||||
rules: rules,
|
rules: rules,
|
||||||
|
@ -253,7 +265,7 @@ pub mod rule_filter {
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use super::super::font_face::FontFaceRule;
|
use super::super::font_face::FontFaceRule;
|
||||||
use super::super::viewport::ViewportRule;
|
use super::super::viewport::ViewportRule;
|
||||||
use super::{CSSRule, MediaRule, StyleRule};
|
use super::{CSSRule, KeyframesRule, MediaRule, StyleRule};
|
||||||
|
|
||||||
macro_rules! rule_filter {
|
macro_rules! rule_filter {
|
||||||
($variant:ident -> $value:ty) => {
|
($variant:ident -> $value:ty) => {
|
||||||
|
@ -266,6 +278,7 @@ pub mod rule_filter {
|
||||||
|
|
||||||
impl<'a, I, Impl: SelectorImpl + 'a> $variant<'a, I>
|
impl<'a, I, Impl: SelectorImpl + 'a> $variant<'a, I>
|
||||||
where I: Iterator<Item=&'a CSSRule<Impl>> {
|
where I: Iterator<Item=&'a CSSRule<Impl>> {
|
||||||
|
#[inline]
|
||||||
pub fn new(iter: I) -> $variant<'a, I> {
|
pub fn new(iter: I) -> $variant<'a, I> {
|
||||||
$variant {
|
$variant {
|
||||||
iter: iter,
|
iter: iter,
|
||||||
|
@ -300,6 +313,7 @@ pub mod rule_filter {
|
||||||
rule_filter!(Style -> StyleRule<Impl>);
|
rule_filter!(Style -> StyleRule<Impl>);
|
||||||
rule_filter!(FontFace -> FontFaceRule);
|
rule_filter!(FontFace -> FontFaceRule);
|
||||||
rule_filter!(Viewport -> ViewportRule);
|
rule_filter!(Viewport -> ViewportRule);
|
||||||
|
rule_filter!(Keyframes -> KeyframesRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extension methods for `CSSRule` iterators.
|
/// Extension methods for `CSSRule` iterators.
|
||||||
|
@ -315,6 +329,9 @@ pub trait CSSRuleIteratorExt<'a, Impl: SelectorImpl + 'a>: Iterator<Item=&'a CSS
|
||||||
|
|
||||||
/// Yield only @viewport rules.
|
/// Yield only @viewport rules.
|
||||||
fn viewport(self) -> rule_filter::Viewport<'a, Self>;
|
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>> {
|
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> {
|
fn viewport(self) -> rule_filter::Viewport<'a, I> {
|
||||||
rule_filter::Viewport::new(self)
|
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>> {
|
fn parse_nested_rules<Impl: SelectorImpl>(context: &ParserContext, input: &mut Parser) -> Vec<CSSRule<Impl>> {
|
||||||
|
@ -376,9 +398,14 @@ enum State {
|
||||||
|
|
||||||
|
|
||||||
enum AtRulePrelude {
|
enum AtRulePrelude {
|
||||||
|
/// A @font-face rule prelude.
|
||||||
FontFace,
|
FontFace,
|
||||||
|
/// A @media rule prelude, with its media queries.
|
||||||
Media(MediaQueryList),
|
Media(MediaQueryList),
|
||||||
|
/// A @viewport rule prelude.
|
||||||
Viewport,
|
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(())
|
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(())
|
_ => Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,11 +532,16 @@ impl<'a, 'b, Impl: SelectorImpl> AtRuleParser for NestedRuleParser<'a, 'b, Impl>
|
||||||
AtRulePrelude::Viewport => {
|
AtRulePrelude::Viewport => {
|
||||||
ViewportRule::parse(input, self.context).map(CSSRule::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> {
|
impl<'a, 'b, Impl: SelectorImpl> QualifiedRuleParser for NestedRuleParser<'a, 'b, Impl> {
|
||||||
type Prelude = Vec<Selector<Impl>>;
|
type Prelude = Vec<Selector<Impl>>;
|
||||||
type QualifiedRule = CSSRule<Impl>;
|
type QualifiedRule = CSSRule<Impl>;
|
||||||
|
|
|
@ -219,10 +219,9 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C,
|
||||||
// Perform the CSS cascade.
|
// Perform the CSS cascade.
|
||||||
unsafe {
|
unsafe {
|
||||||
node.cascade_node(&context.shared_context(),
|
node.cascade_node(&context.shared_context(),
|
||||||
|
&context.local_context(),
|
||||||
parent_opt,
|
parent_opt,
|
||||||
&applicable_declarations,
|
&applicable_declarations);
|
||||||
&mut context.local_context().applicable_declarations_cache.borrow_mut(),
|
|
||||||
&context.shared_context().new_animations_sender);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ourselves to the LRU cache.
|
// Add ourselves to the LRU cache.
|
||||||
|
|
|
@ -1391,16 +1391,13 @@ pub mod specified {
|
||||||
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
|
pub fn parse_border_radius(input: &mut Parser) -> Result<BorderRadiusSize, ()> {
|
||||||
input.try(BorderRadiusSize::parse).or_else(|()| {
|
input.try(BorderRadiusSize::parse).or_else(|()| {
|
||||||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||||||
"thin" =>
|
"thin" => Ok(BorderRadiusSize::circle(
|
||||||
Ok(BorderRadiusSize::circle(
|
LengthOrPercentage::Length(Length::from_px(1.)))),
|
||||||
LengthOrPercentage::Length(Length::from_px(1.)))),
|
"medium" => Ok(BorderRadiusSize::circle(
|
||||||
"medium" =>
|
LengthOrPercentage::Length(Length::from_px(3.)))),
|
||||||
Ok(BorderRadiusSize::circle(
|
"thick" => Ok(BorderRadiusSize::circle(
|
||||||
LengthOrPercentage::Length(Length::from_px(3.)))),
|
LengthOrPercentage::Length(Length::from_px(5.)))),
|
||||||
"thick" =>
|
_ => Err(())
|
||||||
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))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
|
pub struct BorderRadiusSize(pub Size2D<LengthOrPercentage>);
|
||||||
|
|
||||||
|
|
|
@ -562,8 +562,8 @@ pub trait MaybeNew {
|
||||||
|
|
||||||
impl MaybeNew for ViewportConstraints {
|
impl MaybeNew for ViewportConstraints {
|
||||||
fn maybe_new(initial_viewport: TypedSize2D<ViewportPx, f32>,
|
fn maybe_new(initial_viewport: TypedSize2D<ViewportPx, f32>,
|
||||||
rule: &ViewportRule)
|
rule: &ViewportRule)
|
||||||
-> Option<ViewportConstraints>
|
-> Option<ViewportConstraints>
|
||||||
{
|
{
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,11 @@ use euclid::Size2D;
|
||||||
use euclid::size::TypedSize2D;
|
use euclid::size::TypedSize2D;
|
||||||
use gecko_bindings::bindings::RawServoStyleSet;
|
use gecko_bindings::bindings::RawServoStyleSet;
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
use selector_impl::{Stylist, Stylesheet, SharedStyleContext};
|
use selector_impl::{GeckoSelectorImpl, Stylist, Stylesheet, SharedStyleContext};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use style::animation::Animation;
|
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
use style::media_queries::{Device, MediaType};
|
use style::media_queries::{Device, MediaType};
|
||||||
use style::parallel::WorkQueueData;
|
use style::parallel::WorkQueueData;
|
||||||
|
@ -19,6 +18,8 @@ use util::geometry::ViewportPx;
|
||||||
use util::thread_state;
|
use util::thread_state;
|
||||||
use util::workqueue::WorkQueue;
|
use util::workqueue::WorkQueue;
|
||||||
|
|
||||||
|
pub type Animation = ::style::animation::Animation<GeckoSelectorImpl>;
|
||||||
|
|
||||||
pub struct PerDocumentStyleData {
|
pub struct PerDocumentStyleData {
|
||||||
/// Rule processor.
|
/// Rule processor.
|
||||||
pub stylist: Arc<Stylist>,
|
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::{convert_rgba_to_nscolor, debug_assert_unit_is_safe_to_copy};
|
||||||
use values::round_border_to_device_pixels;
|
use values::round_border_to_device_pixels;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GeckoComputedValues {
|
pub struct GeckoComputedValues {
|
||||||
% for style_struct in data.style_structs:
|
% for style_struct in data.style_structs:
|
||||||
${style_struct.ident}: Arc<${style_struct.gecko_struct_name}>,
|
${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"]
|
force_stub += ["list-style-type", "text-overflow"]
|
||||||
# These are booleans.
|
# These are booleans.
|
||||||
force_stub += ["page-break-after", "page-break-before"]
|
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.
|
# Types used with predefined_type()-defined properties that we can auto-generate.
|
||||||
predefined_types = {
|
predefined_types = {
|
||||||
|
|
|
@ -16,6 +16,7 @@ pub type PrivateStyleData = style::data::PrivateStyleData<GeckoSelectorImpl, Gec
|
||||||
#[cfg(feature = "servo_features")]
|
#[cfg(feature = "servo_features")]
|
||||||
known_heap_size!(0, GeckoSelectorImpl, PseudoElement, NonTSPseudoClass);
|
known_heap_size!(0, GeckoSelectorImpl, PseudoElement, NonTSPseudoClass);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct GeckoSelectorImpl;
|
pub struct GeckoSelectorImpl;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
@ -379,6 +380,15 @@ impl SelectorImplExt for GeckoSelectorImpl {
|
||||||
fun(AnonBox(MozSVGText));
|
fun(AnonBox(MozSVGText));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
|
||||||
|
match *pseudo {
|
||||||
|
PseudoElement::Before |
|
||||||
|
PseudoElement::After => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
|
fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
|
||||||
pc.state_flag()
|
pc.state_flag()
|
||||||
|
|
|
@ -15,6 +15,7 @@ use gecko_bindings::structs::nsIAtom;
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::char;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::marker::PhantomData;
|
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 {
|
impl<'a> From<&'a str> for Atom {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(string: &str) -> Atom {
|
fn from(string: &str) -> Atom {
|
||||||
|
|
|
@ -9,11 +9,13 @@ use std::borrow::ToOwned;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use string_cache::{Atom, Namespace};
|
use string_cache::{Atom, Namespace};
|
||||||
|
use style::error_reporting::ParseErrorReporter;
|
||||||
|
use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage};
|
||||||
use style::parser::ParserContextExtraData;
|
use style::parser::ParserContextExtraData;
|
||||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
|
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
|
||||||
use style::stylesheets::{CSSRule, StyleRule, Origin};
|
|
||||||
use style::error_reporting::ParseErrorReporter;
|
|
||||||
use style::servo::Stylesheet;
|
use style::servo::Stylesheet;
|
||||||
|
use style::stylesheets::{CSSRule, StyleRule, KeyframesRule, Origin};
|
||||||
|
use style::values::specified::{LengthOrPercentageOrAuto, Percentage};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -24,7 +26,10 @@ fn test_parse_stylesheet() {
|
||||||
input[type=hidden i] { display: none !important; }
|
input[type=hidden i] { display: none !important; }
|
||||||
html , body /**/ { display: block; }
|
html , body /**/ { display: block; }
|
||||||
#d1 > .ok { background: blue; }
|
#d1 > .ok { background: blue; }
|
||||||
";
|
@keyframes foo {
|
||||||
|
from { width: 0% }
|
||||||
|
to { width: 100%}
|
||||||
|
}";
|
||||||
let url = Url::parse("about::test").unwrap();
|
let url = Url::parse("about::test").unwrap();
|
||||||
let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
|
let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
|
||||||
Box::new(CSSErrorReporterTest),
|
Box::new(CSSErrorReporterTest),
|
||||||
|
@ -145,6 +150,28 @@ fn test_parse_stylesheet() {
|
||||||
important: Arc::new(vec![]),
|
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