Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style: Add support to test animations programatically. #12392

Merged
merged 2 commits into from Jul 20, 2016
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Prev

style: Add a new Timer structure to the shared style context, and bas…

…ic infrastructure for controlling animations.
  • Loading branch information
emilio committed Jul 20, 2016
commit 0b67b218d0c8dc48a5301227802296aff98af6d7
@@ -38,7 +38,6 @@ smallvec = "0.1"
string_cache = {version = "0.2.20", features = ["heap_size"]}
style = {path = "../style"}
style_traits = {path = "../style_traits"}
time = "0.1"
unicode-bidi = "0.2"
unicode-script = {version = "0.1", features = ["harfbuzz"]}
url = {version = "1.0.0", features = ["heap_size"]}
@@ -14,7 +14,7 @@ use script_traits::{AnimationState, LayoutMsg as ConstellationMsg};
use std::collections::HashMap;
use std::sync::mpsc::Receiver;
use style::animation::{Animation, update_style_for_animation};
use time;
use style::timer::Timer;

/// Processes any new animations that were discovered after style recalculation.
/// Also expire any old animations that have completed, inserting them into
@@ -23,7 +23,8 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
running_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
expired_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
new_animations_receiver: &Receiver<Animation>,
pipeline_id: PipelineId) {
pipeline_id: PipelineId,
timer: &Timer) {
let mut new_running_animations = vec![];
while let Ok(animation) = new_animations_receiver.try_recv() {
let mut should_push = true;
@@ -37,7 +38,7 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
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);
anim_state.update_from_other(&state, timer);
should_push = false;
break;
}
@@ -57,7 +58,7 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
return
}

let now = time::precise_time_s();
let now = timer.seconds();
// Expire old running animations.
//
// TODO: Do not expunge Keyframes animations, since we need that state if
@@ -50,7 +50,6 @@ extern crate smallvec;
#[macro_use(atom, ns)] extern crate string_cache;
extern crate style;
extern crate style_traits;
extern crate time;
extern crate unicode_bidi;
extern crate unicode_script;
extern crate url;
@@ -110,11 +110,13 @@ use style::refcell::RefCell;
use style::selector_matching::Stylist;
use style::servo_selector_impl::USER_OR_USER_AGENT_STYLESHEETS;
use style::stylesheets::{Stylesheet, CSSRuleIteratorExt};
use style::timer::Timer;
use style::workqueue::WorkQueue;
use url::Url;
use util::geometry::MAX_RECT;
use util::ipc::OptionalIpcSender;
use util::opts;
use util::prefs::PREFS;
use util::thread;
use util::thread_state;

@@ -226,6 +228,10 @@ pub struct LayoutThread {

// Webrender interface, if enabled.
webrender_api: Option<webrender_traits::RenderApi>,

/// The timer object to control the timing of the animations. This should
/// only be a test-mode timer during testing for animations.
timer: Timer,
}

impl LayoutThreadFactory for LayoutThread {
@@ -459,13 +465,20 @@ impl LayoutThread {
offset_parent_response: OffsetParentResponse::empty(),
margin_style_response: MarginStyleResponse::empty(),
stacking_context_scroll_offsets: HashMap::new(),
})),
error_reporter: CSSErrorReporter {
pipelineid: id,
script_chan: Arc::new(Mutex::new(script_chan)),
},
webrender_image_cache:
Arc::new(RwLock::new(HashMap::with_hasher(Default::default()))),
})),
error_reporter: CSSErrorReporter {
pipelineid: id,
script_chan: Arc::new(Mutex::new(script_chan)),
},
webrender_image_cache:
Arc::new(RwLock::new(HashMap::with_hasher(Default::default()))),
timer:
if PREFS.get("layout.animations.test.enabled")
.as_boolean().unwrap_or(false) {
Timer::test_mode()
} else {
Timer::new()
},
}
}

@@ -501,6 +514,7 @@ impl LayoutThread {
expired_animations: self.expired_animations.clone(),
error_reporter: self.error_reporter.clone(),
local_context_creation_data: Mutex::new(local_style_context_creation_data),
timer: self.timer.clone(),
},
image_cache_thread: self.image_cache_thread.clone(),
image_cache_sender: Mutex::new(self.image_cache_sender.clone()),
@@ -653,6 +667,9 @@ impl LayoutThread {
let _rw_data = possibly_locked_rw_data.lock();
sender.send(self.epoch).unwrap();
},
Msg::AdvanceClockMs(how_many) => {
self.handle_advance_clock_ms(how_many, possibly_locked_rw_data);
}
Msg::GetWebFontLoadState(sender) => {
let _rw_data = possibly_locked_rw_data.lock();
let outstanding_web_fonts = self.outstanding_web_fonts.load(Ordering::SeqCst);
@@ -795,6 +812,14 @@ impl LayoutThread {
possibly_locked_rw_data.block(rw_data);
}

/// Advances the animation clock of the document.
fn handle_advance_clock_ms<'a, 'b>(&mut self,
how_many_ms: i32,
possibly_locked_rw_data: &mut RwData<'a, 'b>) {
self.timer.increment(how_many_ms as f64 / 1000.0);
self.tick_all_animations(possibly_locked_rw_data);
}

/// Sets quirks mode for the document, causing the quirks mode stylesheet to be used.
fn handle_set_quirks_mode<'a, 'b>(&self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {
let mut rw_data = possibly_locked_rw_data.lock();
@@ -1350,7 +1375,8 @@ impl LayoutThread {
&mut *self.running_animations.write().unwrap(),
&mut *self.expired_animations.write().unwrap(),
&self.new_animations_receiver,
self.id);
self.id,
&self.timer);

profile(time::ProfilerCategory::LayoutRestyleDamagePropagation,
self.profiler_metadata(),
@@ -581,6 +581,10 @@ impl TestBindingMethods for TestBinding {
}
}

fn AdvanceClock(&self, ms: i32) {
self.global().r().as_window().advance_animation_clock(ms);
}

fn Panic(&self) { panic!("explicit panic from script") }
}

@@ -424,6 +424,8 @@ interface TestBinding {
static void prefControlledStaticMethodDisabled();
[Pref="dom.testbinding.prefcontrolled.enabled"]
const unsigned short prefControlledConstDisabled = 0;
[Pref="layout.animations.test.enabled"]
void advanceClock(long millis);

[Pref="dom.testbinding.prefcontrolled2.enabled"]
readonly attribute boolean prefControlledAttributeEnabled;
@@ -1030,6 +1030,12 @@ impl Window {
recv.recv().unwrap_or((Size2D::zero(), Point2D::zero()))
}

/// Advances the layout animation clock by `delta` milliseconds, and then
/// forces a reflow.
pub fn advance_animation_clock(&self, delta: i32) {
self.layout_chan.send(Msg::AdvanceClockMs(delta)).unwrap();
}

/// Reflows the page unconditionally if possible and not suppressed. This
/// method will wait for the layout thread to complete (but see the `TODO`
/// below). If there is no window size yet, the page is presumed invisible
@@ -40,6 +40,11 @@ pub enum Msg {
/// Requests that the layout thread render the next frame of all animations.
TickAnimations,

/// Updates layout's timer for animation testing from script.
///
/// The inner field is the number of *milliseconds* to advance.
AdvanceClockMs(i32),

/// Requests that the layout thread reflow with a newly-loaded Web font.
ReflowWithNewlyLoadedWebFont,

Some generated files are not rendered by default. Learn more.

@@ -20,7 +20,7 @@ use selectors::matching::DeclarationBlock;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use string_cache::Atom;
use time;
use timer::Timer;
use values::computed::Time;

/// This structure represents a keyframes animation current iteration state.
@@ -122,7 +122,9 @@ impl KeyframesAnimationState {
///
/// There are some bits of state we can't just replace, over all taking in
/// account times, so here's that logic.
pub fn update_from_other(&mut self, other: &Self) {
pub fn update_from_other(&mut self,
other: &Self,
timer: &Timer) {
use self::KeyframesRunningState::*;

debug!("KeyframesAnimationState::update_from_other({:?}, {:?})", self, other);
@@ -146,11 +148,11 @@ impl KeyframesAnimationState {
// If we're pausing the animation, compute the progress value.
match (&mut self.running_state, old_running_state) {
(&mut Running, Paused(progress))
=> new_started_at = time::precise_time_s() - (self.duration * progress),
=> new_started_at = timer.seconds() - (self.duration * progress),
(&mut Paused(ref mut new), Paused(old))
=> *new = old,
(&mut Paused(ref mut progress), Running)
=> *progress = (time::precise_time_s() - old_started_at) / old_duration,
=> *progress = (timer.seconds() - old_started_at) / old_duration,
_ => {},
}

@@ -341,7 +343,8 @@ impl PropertyAnimation {
pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>,
node: OpaqueNode,
old_style: &ComputedValues,
new_style: &mut Arc<ComputedValues>)
new_style: &mut Arc<ComputedValues>,
timer: &Timer)
-> bool {
let mut had_animations = false;
for i in 0..new_style.get_box().transition_property_count() {
@@ -355,7 +358,7 @@ pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>

// Kick off the animation.
let box_style = new_style.get_box();
let now = time::precise_time_s();
let now = timer.seconds();
let start_time =
now + (box_style.transition_delay_mod(i).seconds() as f64);
new_animations_sender
@@ -424,7 +427,7 @@ pub fn maybe_start_animations(context: &SharedStyleContext,
}

let delay = box_style.animation_delay_mod(i).seconds();
let now = time::precise_time_s();
let now = context.timer.seconds();
let animation_start = now + delay as f64;
let duration = box_style.animation_duration_mod(i).seconds();
let iteration_state = match box_style.animation_iteration_count_mod(i) {
@@ -497,7 +500,7 @@ where Damage: TRestyleDamage {
match *animation {
Animation::Transition(_, start_time, ref frame, _) => {
debug!("update_style_for_animation: transition found");
let now = time::precise_time_s();
let now = context.timer.seconds();
let mut new_style = (*style).clone();
let updated_style = update_style_for_animation_frame(&mut new_style,
now, start_time,
@@ -516,7 +519,7 @@ where Damage: TRestyleDamage {
let started_at = state.started_at;

let now = match state.running_state {
KeyframesRunningState::Running => time::precise_time_s(),
KeyframesRunningState::Running => context.timer.seconds(),
KeyframesRunningState::Paused(progress) => started_at + duration * progress,
};

@@ -15,6 +15,7 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex, RwLock};
use timer::Timer;

/// This structure is used to create a local style context from a shared one.
pub struct LocalStyleContextCreationInfo {
@@ -57,6 +58,10 @@ pub struct SharedStyleContext {

/// Data needed to create the local style context from the shared one.
pub local_context_creation_data: Mutex<LocalStyleContextCreationInfo>,

/// The current timer for transitions and animations. This is needed to test
/// them.
pub timer: Timer,
}

pub struct LocalStyleContext {
@@ -101,6 +101,7 @@ pub mod sink;
pub mod str;
pub mod stylesheets;
mod tid;
pub mod timer;
pub mod traversal;
#[macro_use]
#[allow(non_camel_case_types)]
@@ -435,7 +435,8 @@ trait PrivateMatchMethods: TNode
new_animations_sender,
this_opaque,
&**style,
&mut this_style);
&mut this_style,
&shared_context.timer);
}

cacheable = cacheable && !animations_started
@@ -0,0 +1,58 @@
/* 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 time;

/// The `TimerMode` is used to determine what time should the `Timer` return,
/// either a fixed value (in the `Test` mode), or the actual time (in the
/// `Current` mode).
#[derive(Debug, Clone)]
enum TimerMode {
Test(f64),
Current,
}

/// A `Timer` struct that takes care of giving the current time for animations.
///
/// This is needed to be allowed to hook the time in the animations' test-mode.
#[derive(Debug, Clone)]
pub struct Timer {
mode: TimerMode,
}

impl Timer {
/// Creates a new "normal" timer, i.e., a "Current" mode timer.
#[inline]
pub fn new() -> Self {
Timer {
mode: TimerMode::Current,
}
}

/// Creates a new "test mode" timer, with initial time 0.
#[inline]
pub fn test_mode() -> Self {
Timer {
mode: TimerMode::Test(0.),
}
}

/// Returns the current time, at least from the caller's perspective. In
/// test mode returns whatever the value is.
pub fn seconds(&self) -> f64 {
match self.mode {
TimerMode::Test(test_value) => test_value,
TimerMode::Current => time::precise_time_s(),
}
}

/// Increments the current clock. Panics if the clock is not on test mode.
pub fn increment(&mut self, by: f64) {
match self.mode {
TimerMode::Test(ref mut val)
=> *val += by,
TimerMode::Current
=> panic!("Timer::increment called for a non-test mode timer. This is a bug."),
}
}
}

Some generated files are not rendered by default. Learn more.

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.