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 all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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
@@ -886,22 +886,12 @@ impl ScriptThread {

fn handle_msg_from_constellation(&self, msg: ConstellationControlMsg) {
match msg {
ConstellationControlMsg::AttachLayout(_) =>
panic!("should have handled AttachLayout already"),
ConstellationControlMsg::Navigate(pipeline_id, subpage_id, load_data) =>
self.handle_navigate(pipeline_id, Some(subpage_id), load_data),
ConstellationControlMsg::SendEvent(id, event) =>
self.handle_event(id, event),
ConstellationControlMsg::ResizeInactive(id, new_size) =>
self.handle_resize_inactive_msg(id, new_size),
ConstellationControlMsg::Viewport(..) =>
panic!("should have handled Viewport already"),
ConstellationControlMsg::SetScrollState(..) =>
panic!("should have handled SetScrollState already"),
ConstellationControlMsg::Resize(..) =>
panic!("should have handled Resize already"),
ConstellationControlMsg::ExitPipeline(..) =>
panic!("should have handled ExitPipeline already"),
ConstellationControlMsg::GetTitle(pipeline_id) =>
self.handle_get_title_msg(pipeline_id),
ConstellationControlMsg::Freeze(pipeline_id) =>
@@ -943,6 +933,12 @@ impl ScriptThread {
self.handle_css_error_reporting(pipeline_id, filename, line, column, msg),
ConstellationControlMsg::Reload(pipeline_id) =>
self.handle_reload(pipeline_id),
msg @ ConstellationControlMsg::AttachLayout(..) |
msg @ ConstellationControlMsg::Viewport(..) |
msg @ ConstellationControlMsg::SetScrollState(..) |
msg @ ConstellationControlMsg::Resize(..) |
msg @ ConstellationControlMsg::ExitPipeline(..) =>
panic!("should have handled {:?} already", msg),
}
}

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

@@ -61,6 +61,7 @@ use profile_traits::mem;
use profile_traits::time as profile_time;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
use std::fmt;
use std::sync::mpsc::{Sender, Receiver};
use style_traits::{PagePx, ViewportPx};
use url::Url;
@@ -207,6 +208,37 @@ pub enum ConstellationControlMsg {
Reload(PipelineId),
}

impl fmt::Debug for ConstellationControlMsg {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
use self::ConstellationControlMsg::*;
write!(formatter, "ConstellationMsg::{}", match *self {
AttachLayout(..) => "AttachLayout",
Resize(..) => "Resize",
ResizeInactive(..) => "ResizeInactive",
ExitPipeline(..) => "ExitPipeline",
SendEvent(..) => "SendEvent",
Viewport(..) => "Viewport",
SetScrollState(..) => "SetScrollState",
GetTitle(..) => "GetTitle",
Freeze(..) => "Freeze",
Thaw(..) => "Thaw",
ChangeFrameVisibilityStatus(..) => "ChangeFrameVisibilityStatus",
NotifyVisibilityChange(..) => "NotifyVisibilityChange",
Navigate(..) => "Navigate",
MozBrowserEvent(..) => "MozBrowserEvent",
UpdateSubpageId(..) => "UpdateSubpageId",
FocusIFrame(..) => "FocusIFrame",
WebDriverScriptCommand(..) => "WebDriverScriptCommand",
TickAllAnimations(..) => "TickAllAnimations",
WebFontLoaded(..) => "WebFontLoaded",
DispatchFrameLoadEvent { .. } => "DispatchFrameLoadEvent",
FramedContentChanged(..) => "FramedContentChanged",
ReportCSSError(..) => "ReportCSSError",
Reload(..) => "Reload",
})
}
}

/// Used to determine if a script has any pending asynchronous activity.
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum DocumentState {

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)]
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.