Use a restyle for animation ticks

This change corrects synchronization issues with animations, by
reworking the animation processing model to do a quick restyle and
incremental layout when ticking animations.

While this change adds overhead to animation ticks, the idea is that
this will be the fallback when synchronous behavior is required to
fulfill specification requirements. In the optimistic case, many
animations could be updated and applied off-the-main-thread and then
resynchronized when style information is queried by script.

Fixes #13865.
This commit is contained in:
Martin Robinson 2020-04-30 15:38:56 +02:00
parent 5e44327325
commit b585ce5b1f
39 changed files with 1285 additions and 1662 deletions

1
Cargo.lock generated
View file

@ -4680,6 +4680,7 @@ dependencies = [
name = "script_traits"
version = "0.0.1"
dependencies = [
"bitflags",
"bluetooth_traits",
"canvas_traits",
"cookie",

View file

@ -1042,22 +1042,24 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
let animation_callbacks_running = self
.pipeline_details(pipeline_id)
.animation_callbacks_running;
if animation_callbacks_running {
let msg = ConstellationMsg::TickAnimation(pipeline_id, AnimationTickType::Script);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending tick to constellation failed ({:?}).", e);
}
let animations_running = self.pipeline_details(pipeline_id).animations_running;
if !animation_callbacks_running && !animations_running {
return;
}
// We may need to tick animations in layout. (See #12749.)
let animations_running = self.pipeline_details(pipeline_id).animations_running;
let mut tick_type = AnimationTickType::empty();
if animations_running {
let msg = ConstellationMsg::TickAnimation(pipeline_id, AnimationTickType::Layout);
tick_type.insert(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS);
}
if animation_callbacks_running {
tick_type.insert(AnimationTickType::REQUEST_ANIMATION_FRAME);
}
let msg = ConstellationMsg::TickAnimation(pipeline_id, tick_type);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending tick to constellation failed ({:?}).", e);
}
}
}
fn constrain_viewport(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints) {
let is_root = self

View file

@ -3458,27 +3458,13 @@ where
}
fn handle_tick_animation(&mut self, pipeline_id: PipelineId, tick_type: AnimationTickType) {
let result = match tick_type {
AnimationTickType::Script => {
let msg = ConstellationControlMsg::TickAllAnimations(pipeline_id);
match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.event_loop.send(msg),
None => {
return warn!("Pipeline {:?} got script tick after closure.", pipeline_id);
},
}
},
AnimationTickType::Layout => match self.pipelines.get(&pipeline_id) {
Some(pipeline) => {
let msg = LayoutControlMsg::TickAnimations(pipeline.load_data.url.origin());
pipeline.layout_chan.send(msg)
},
None => {
return warn!("Pipeline {:?} got layout tick after closure.", pipeline_id);
},
},
let pipeline = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline,
None => return warn!("Pipeline {:?} got script tick after closure.", pipeline_id),
};
if let Err(e) = result {
let message = ConstellationControlMsg::TickAllAnimations(pipeline_id, tick_type);
if let Err(e) = pipeline.event_loop.send(message) {
self.handle_send_error(pipeline_id, e);
}
}

View file

@ -4,7 +4,6 @@
//! CSS transitions and animations.
use crate::context::LayoutContext;
use crate::display_list::items::OpaqueNode;
use crate::flow::{Flow, GetBaseFlow};
use crate::opaque_node::OpaqueNodeMethods;
@ -16,54 +15,27 @@ use script_traits::{
AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg,
TransitionOrAnimationEventType,
};
use servo_arc::Arc;
use style::animation::{update_style_for_animation, Animation, ElementAnimationState};
use style::dom::TElement;
use style::font_metrics::ServoMetricsProvider;
use style::selector_parser::RestyleDamage;
use style::timer::Timer;
use style::animation::{Animation, ElementAnimationState};
/// Collect newly animating nodes, which is used by the script process during
/// forced, synchronous reflows to root DOM nodes for the duration of their
/// animations or transitions.
pub fn collect_newly_animating_nodes(
animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>,
mut out: Option<&mut Vec<UntrustedNodeAddress>>,
) {
// This extends the output vector with an iterator that contains a copy of the node
// address for every new animation. This is a bit goofy, but the script thread
// currently stores a rooted node for every property that is transitioning.
if let Some(ref mut out) = out {
out.extend(animation_states.iter().flat_map(|(node, state)| {
std::iter::repeat(node.to_untrusted_node_address()).take(state.new_animations.len())
}));
}
}
/// Processes any new animations that were discovered after style recalculation. Also
/// finish any animations that have completed, inserting them into `finished_animations`.
pub fn update_animation_states(
/// Processes any new animations that were discovered after style recalculation and
/// remove animations for any disconnected nodes. Send messages that trigger events
/// for any events that changed state.
pub fn do_post_style_animations_update(
constellation_chan: &IpcSender<ConstellationMsg>,
script_chan: &IpcSender<ConstellationControlMsg>,
animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationState>,
invalid_nodes: FxHashSet<OpaqueNode>,
pipeline_id: PipelineId,
timer: &Timer,
now: f64,
out: &mut Vec<UntrustedNodeAddress>,
root_flow: &mut dyn Flow,
) {
let had_running_animations = animation_states
.values()
.any(|state| !state.running_animations.is_empty());
// Cancel all animations on any invalid nodes. These entries will later
// be removed from the list of states, because their states will become
// empty.
for node in &invalid_nodes {
if let Some(mut state) = animation_states.remove(node) {
state.cancel_all_animations();
}
}
cancel_animations_for_disconnected_nodes(animation_states, root_flow);
collect_newly_animating_nodes(animation_states, out);
let now = timer.seconds();
let mut have_running_animations = false;
for (node, animation_state) in animation_states.iter_mut() {
update_animation_state(script_chan, animation_state, pipeline_id, now, *node);
@ -89,7 +61,50 @@ pub fn update_animation_states(
.unwrap();
}
pub fn update_animation_state(
/// Collect newly animating nodes, which is used by the script process during
/// forced, synchronous reflows to root DOM nodes for the duration of their
/// animations or transitions.
pub fn collect_newly_animating_nodes(
animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>,
out: &mut Vec<UntrustedNodeAddress>,
) {
// This extends the output vector with an iterator that contains a copy of the node
// address for every new animation. This is a bit goofy, but the script thread
// currently stores a rooted node for every property that is transitioning.
out.extend(animation_states.iter().flat_map(|(node, state)| {
std::iter::repeat(node.to_untrusted_node_address()).take(state.new_animations.len())
}));
}
/// Cancel animations for any nodes which have been removed from the DOM or are display:none.
/// We detect this by looking for nodes that are used in the flow tree.
/// TODO(mrobinson): We should look into a way of doing this during flow tree construction.
/// This also doesn't yet handles nodes that have been reparented.
pub fn cancel_animations_for_disconnected_nodes(
animation_states: &mut FxHashMap<OpaqueNode, ElementAnimationState>,
root_flow: &mut dyn Flow,
) {
// Assume all nodes have been removed until proven otherwise.
let mut invalid_nodes: FxHashSet<OpaqueNode> = animation_states.keys().cloned().collect();
fn traverse_flow(flow: &mut dyn Flow, invalid_nodes: &mut FxHashSet<OpaqueNode>) {
flow.mutate_fragments(&mut |fragment| {
invalid_nodes.remove(&fragment.node);
});
for kid in flow.mut_base().children.iter_mut() {
traverse_flow(kid, invalid_nodes)
}
}
traverse_flow(root_flow, &mut invalid_nodes);
// Cancel animations for any nodes that are no longer in the flow tree.
for node in &invalid_nodes {
if let Some(state) = animation_states.get_mut(node) {
state.cancel_all_animations();
}
}
}
fn update_animation_state(
script_channel: &IpcSender<ConstellationControlMsg>,
animation_state: &mut ElementAnimationState,
pipeline_id: PipelineId,
@ -122,36 +137,28 @@ pub fn update_animation_state(
/// Walk through the list of running animations and remove all of the ones that
/// have ended.
pub fn handle_running_animations(
fn handle_running_animations(
animation_state: &mut ElementAnimationState,
now: f64,
mut send_event: impl FnMut(&Animation, TransitionOrAnimationEventType, f64),
) {
if animation_state.running_animations.is_empty() {
return;
}
let mut running_animations =
std::mem::replace(&mut animation_state.running_animations, Vec::new());
for mut running_animation in running_animations.drain(..) {
let still_running = match running_animation {
Animation::Transition(_, started_at, ref property_animation) => {
now < started_at + (property_animation.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()
},
};
for running_animation in running_animations.drain(..) {
// If the animation is still running, add it back to the list of running animations.
if still_running {
if !running_animation.has_ended(now) {
animation_state.running_animations.push(running_animation);
} else {
debug!("Finishing transition: {:?}", running_animation);
let (event_type, elapsed_time) = match running_animation {
Animation::Transition(_, _, ref property_animation) => (
TransitionOrAnimationEventType::TransitionEnd,
property_animation.duration,
),
Animation::Keyframes(_, _, _, ref mut state) => (
Animation::Keyframes(_, _, _, ref state) => (
TransitionOrAnimationEventType::AnimationEnd,
state.active_duration(),
),
@ -166,7 +173,7 @@ pub fn handle_running_animations(
/// Send events for cancelled animations. Currently this only handles cancelled
/// transitions, but eventually this should handle cancelled CSS animations as
/// well.
pub fn handle_cancelled_animations(
fn handle_cancelled_animations(
animation_state: &mut ElementAnimationState,
now: f64,
mut send_event: impl FnMut(&Animation, TransitionOrAnimationEventType, f64),
@ -188,7 +195,7 @@ pub fn handle_cancelled_animations(
}
}
pub fn handle_new_animations(
fn handle_new_animations(
animation_state: &mut ElementAnimationState,
mut send_event: impl FnMut(&Animation, TransitionOrAnimationEventType, f64),
) {
@ -208,55 +215,3 @@ pub fn handle_new_animations(
animation_state.running_animations.push(animation);
}
}
/// Recalculates style for a set of animations. This does *not* run with the DOM
/// lock held. Returns a set of nodes associated with animations that are no longer
/// valid.
pub fn recalc_style_for_animations<E>(
context: &LayoutContext,
flow: &mut dyn Flow,
animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>,
) -> FxHashSet<OpaqueNode>
where
E: TElement,
{
let mut invalid_nodes = animation_states.keys().cloned().collect();
do_recalc_style_for_animations::<E>(context, flow, animation_states, &mut invalid_nodes);
invalid_nodes
}
fn do_recalc_style_for_animations<E>(
context: &LayoutContext,
flow: &mut dyn Flow,
animation_states: &FxHashMap<OpaqueNode, ElementAnimationState>,
invalid_nodes: &mut FxHashSet<OpaqueNode>,
) where
E: TElement,
{
let mut damage = RestyleDamage::empty();
flow.mutate_fragments(&mut |fragment| {
let animations = match animation_states.get(&fragment.node) {
Some(state) => &state.running_animations,
None => return,
};
invalid_nodes.remove(&fragment.node);
for animation in animations.iter() {
let old_style = fragment.style.clone();
update_style_for_animation::<E>(
&context.style_context,
animation,
Arc::make_mut(&mut fragment.style),
&ServoMetricsProvider,
);
let difference = RestyleDamage::compute_style_difference(&old_style, &fragment.style);
damage |= difference.damage;
}
});
let base = flow.mut_base();
base.restyle_damage.insert(damage);
for kid in base.children.iter_mut() {
do_recalc_style_for_animations::<E>(context, kid, animation_states, invalid_nodes)
}
}

View file

@ -11,8 +11,9 @@ use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageCacheResult};
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
};
use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState};
use script_traits::Painter;
@ -83,20 +84,16 @@ pub struct LayoutContext<'a> {
pub registered_painters: &'a dyn RegisteredPainters,
/// A list of in-progress image loads to be shared with the script thread.
/// A None value means that this layout was not initiated by the script thread.
pub pending_images: Option<Mutex<Vec<PendingImage>>>,
pub pending_images: Mutex<Vec<PendingImage>>,
/// A list of nodes that have just initiated a CSS transition or animation.
/// A None value means that this layout was not initiated by the script thread.
pub newly_animating_nodes: Option<Mutex<Vec<UntrustedNodeAddress>>>,
pub newly_animating_nodes: Mutex<Vec<UntrustedNodeAddress>>,
}
impl<'a> Drop for LayoutContext<'a> {
fn drop(&mut self) {
if !thread::panicking() {
if let Some(ref pending_images) = self.pending_images {
assert!(pending_images.lock().unwrap().is_empty());
}
assert!(self.pending_images.lock().unwrap().is_empty());
}
}
}
@ -113,22 +110,12 @@ impl<'a> LayoutContext<'a> {
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<ImageOrMetadataAvailable> {
//XXXjdm For cases where we do not request an image, we still need to
// ensure the node gets another script-initiated reflow or it
// won't be requested at all.
let can_request = if self.pending_images.is_some() {
CanRequestImages::Yes
} else {
CanRequestImages::No
};
// Check for available image or start tracking.
let cache_result = self.image_cache.get_cached_image_status(
url.clone(),
self.origin.clone(),
None,
use_placeholder,
can_request,
);
match cache_result {
@ -136,18 +123,13 @@ impl<'a> LayoutContext<'a> {
// Image has been requested, is still pending. Return no image for this paint loop.
// When the image loads it will trigger a reflow and/or repaint.
ImageCacheResult::Pending(id) => {
//XXXjdm if self.pending_images is not available, we should make sure that
// this node gets marked dirty again so it gets a script-initiated
// reflow that deals with this properly.
if let Some(ref pending_images) = self.pending_images {
let image = PendingImage {
state: PendingImageState::PendingResponse,
node: node.to_untrusted_node_address(),
id,
origin: self.origin.clone(),
};
pending_images.lock().unwrap().push(image);
}
self.pending_images.lock().unwrap().push(image);
None
},
// Not yet requested - request image or metadata from the cache
@ -158,12 +140,7 @@ impl<'a> LayoutContext<'a> {
id,
origin: self.origin.clone(),
};
self.pending_images
.as_ref()
.unwrap()
.lock()
.unwrap()
.push(image);
self.pending_images.lock().unwrap().push(image);
None
},
// Image failed to load, so just return nothing

View file

@ -8,8 +8,9 @@ use fnv::FnvHashMap;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageCacheResult};
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use net_traits::image_cache::{
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
};
use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState};
use servo_url::{ImmutableOrigin, ServoUrl};
@ -33,8 +34,7 @@ pub struct LayoutContext<'a> {
pub image_cache: Arc<dyn ImageCache>,
/// A list of in-progress image loads to be shared with the script thread.
/// A None value means that this layout was not initiated by the script thread.
pub pending_images: Option<Mutex<Vec<PendingImage>>>,
pub pending_images: Mutex<Vec<PendingImage>>,
pub webrender_image_cache:
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
@ -43,9 +43,7 @@ pub struct LayoutContext<'a> {
impl<'a> Drop for LayoutContext<'a> {
fn drop(&mut self) {
if !std::thread::panicking() {
if let Some(ref pending_images) = self.pending_images {
assert!(pending_images.lock().unwrap().is_empty());
}
assert!(self.pending_images.lock().unwrap().is_empty());
}
}
}
@ -62,22 +60,12 @@ impl<'a> LayoutContext<'a> {
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<ImageOrMetadataAvailable> {
//XXXjdm For cases where we do not request an image, we still need to
// ensure the node gets another script-initiated reflow or it
// won't be requested at all.
let can_request = if self.pending_images.is_some() {
CanRequestImages::Yes
} else {
CanRequestImages::No
};
// Check for available image or start tracking.
let cache_result = self.image_cache.get_cached_image_status(
url.clone(),
self.origin.clone(),
None,
use_placeholder,
can_request,
);
match cache_result {
@ -85,18 +73,13 @@ impl<'a> LayoutContext<'a> {
// Image has been requested, is still pending. Return no image for this paint loop.
// When the image loads it will trigger a reflow and/or repaint.
ImageCacheResult::Pending(id) => {
//XXXjdm if self.pending_images is not available, we should make sure that
// this node gets marked dirty again so it gets a script-initiated
// reflow that deals with this properly.
if let Some(ref pending_images) = self.pending_images {
let image = PendingImage {
state: PendingImageState::PendingResponse,
node: node.to_untrusted_node_address(),
id,
origin: self.origin.clone(),
};
pending_images.lock().unwrap().push(image);
}
self.pending_images.lock().unwrap().push(image);
None
},
// Not yet requested - request image or metadata from the cache
@ -107,12 +90,7 @@ impl<'a> LayoutContext<'a> {
id,
origin: self.origin.clone(),
};
self.pending_images
.as_ref()
.unwrap()
.lock()
.unwrap()
.push(image);
self.pending_images.lock().unwrap().push(image);
None
},
// Image failed to load, so just return nothing

View file

@ -29,7 +29,7 @@ use crossbeam_channel::{Receiver, Sender};
use embedder_traits::resources::{self, Resource};
use euclid::{default::Size2D as UntypedSize2D, Point2D, Rect, Scale, Size2D};
use fnv::FnvHashMap;
use fxhash::{FxHashMap, FxHashSet};
use fxhash::FxHashMap;
use gfx::font;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context;
@ -88,7 +88,6 @@ use servo_arc::Arc as ServoArc;
use servo_atoms::Atom;
use servo_config::opts;
use servo_config::pref;
use servo_geometry::MaxRect;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
@ -622,7 +621,6 @@ impl LayoutThread {
fn build_layout_context<'a>(
&'a self,
guards: StylesheetGuards<'a>,
script_initiated_layout: bool,
snapshot_map: &'a SnapshotMap,
origin: ImmutableOrigin,
) -> LayoutContext<'a> {
@ -636,23 +634,15 @@ impl LayoutThread {
visited_styles_enabled: false,
animation_states: self.animation_states.clone(),
registered_speculative_painters: &self.registered_painters,
timer: self.timer.clone(),
current_time_for_animations: self.timer.seconds(),
traversal_flags: TraversalFlags::empty(),
snapshot_map: snapshot_map,
},
image_cache: self.image_cache.clone(),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: if script_initiated_layout {
Some(Mutex::new(vec![]))
} else {
None
},
newly_animating_nodes: if script_initiated_layout {
Some(Mutex::new(vec![]))
} else {
None
},
pending_images: Mutex::new(vec![]),
newly_animating_nodes: Mutex::new(vec![]),
registered_painters: &self.registered_painters,
}
}
@ -664,8 +654,6 @@ impl LayoutThread {
Msg::SetQuirksMode(..) => LayoutHangAnnotation::SetQuirksMode,
Msg::Reflow(..) => LayoutHangAnnotation::Reflow,
Msg::GetRPC(..) => LayoutHangAnnotation::GetRPC,
Msg::TickAnimations(..) => LayoutHangAnnotation::TickAnimations,
Msg::AdvanceClockMs(..) => LayoutHangAnnotation::AdvanceClockMs,
Msg::CollectReports(..) => LayoutHangAnnotation::CollectReports,
Msg::PrepareToExit(..) => LayoutHangAnnotation::PrepareToExit,
Msg::ExitNow => LayoutHangAnnotation::ExitNow,
@ -712,9 +700,6 @@ impl LayoutThread {
Msg::SetScrollStates(new_scroll_states),
possibly_locked_rw_data,
),
Request::FromPipeline(LayoutControlMsg::TickAnimations(origin)) => {
self.handle_request_helper(Msg::TickAnimations(origin), possibly_locked_rw_data)
},
Request::FromPipeline(LayoutControlMsg::GetCurrentEpoch(sender)) => {
self.handle_request_helper(Msg::GetCurrentEpoch(sender), possibly_locked_rw_data)
},
@ -786,9 +771,6 @@ impl LayoutThread {
|| self.handle_reflow(&mut data, possibly_locked_rw_data),
);
},
Msg::TickAnimations(origin) => {
self.tick_all_animations(possibly_locked_rw_data, origin)
},
Msg::SetScrollStates(new_scroll_states) => {
self.set_scroll_states(new_scroll_states, possibly_locked_rw_data);
},
@ -813,9 +795,6 @@ impl LayoutThread {
let _rw_data = possibly_locked_rw_data.lock();
sender.send(self.epoch.get()).unwrap();
},
Msg::AdvanceClockMs(how_many, do_tick, origin) => {
self.handle_advance_clock_ms(how_many, possibly_locked_rw_data, do_tick, origin);
},
Msg::GetWebFontLoadState(sender) => {
let _rw_data = possibly_locked_rw_data.lock();
let outstanding_web_fonts = self.outstanding_web_fonts.load(Ordering::SeqCst);
@ -993,20 +972,6 @@ impl LayoutThread {
}
}
/// 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>,
tick_animations: bool,
origin: ImmutableOrigin,
) {
self.timer.increment(how_many_ms as f64 / 1000.0);
if tick_animations {
self.tick_all_animations(possibly_locked_rw_data, origin);
}
}
/// Sets quirks mode for the document, causing the quirks mode stylesheet to be used.
fn handle_set_quirks_mode<'a, 'b>(&mut self, quirks_mode: QuirksMode) {
self.stylist.set_quirks_mode(quirks_mode);
@ -1341,6 +1306,12 @@ impl LayoutThread {
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards);
if pref!(layout.animations.test.enabled) {
if let Some(delta) = data.advance_clock_delta {
self.timer.increment(delta as f64 / 1000.0);
}
}
self.stylist
.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size =
@ -1458,7 +1429,7 @@ impl LayoutThread {
self.stylist.flush(&guards, Some(element), Some(&map));
// Create a layout context for use throughout the following passes.
let mut layout_context = self.build_layout_context(guards.clone(), true, &map, origin);
let mut layout_context = self.build_layout_context(guards.clone(), &map, origin);
let pool;
let (thread_pool, num_threads) = if self.parallel_flag {
@ -1539,7 +1510,6 @@ impl LayoutThread {
Some(&document),
&mut rw_data,
&mut layout_context,
FxHashSet::default(),
);
}
@ -1559,17 +1529,10 @@ impl LayoutThread {
context: &mut LayoutContext,
reflow_result: &mut ReflowComplete,
) {
let pending_images = match context.pending_images {
Some(ref pending) => std::mem::replace(&mut *pending.lock().unwrap(), vec![]),
None => vec![],
};
reflow_result.pending_images = pending_images;
let newly_animating_nodes = match context.newly_animating_nodes {
Some(ref nodes) => std::mem::replace(&mut *nodes.lock().unwrap(), vec![]),
None => vec![],
};
reflow_result.newly_animating_nodes = newly_animating_nodes;
reflow_result.pending_images =
std::mem::replace(&mut *context.pending_images.lock().unwrap(), vec![]);
reflow_result.newly_animating_nodes =
std::mem::replace(&mut *context.newly_animating_nodes.lock().unwrap(), vec![]);
let mut root_flow = match self.root_flow.borrow().clone() {
Some(root_flow) => root_flow,
@ -1681,70 +1644,6 @@ impl LayoutThread {
rw_data.scroll_offsets = layout_scroll_states
}
fn tick_all_animations<'a, 'b>(
&mut self,
possibly_locked_rw_data: &mut RwData<'a, 'b>,
origin: ImmutableOrigin,
) {
let mut rw_data = possibly_locked_rw_data.lock();
self.tick_animations(&mut rw_data, origin);
}
fn tick_animations(&mut self, rw_data: &mut LayoutThreadData, origin: ImmutableOrigin) {
if self.relayout_event {
println!(
"**** pipeline={}\tForDisplay\tSpecial\tAnimationTick",
self.id
);
}
if let Some(mut root_flow) = self.root_flow.borrow().clone() {
let reflow_info = Reflow {
page_clip_rect: Rect::max_rect(),
};
// Unwrap here should not panic since self.root_flow is only ever set to Some(_)
// in handle_reflow() where self.document_shared_lock is as well.
let author_shared_lock = self.document_shared_lock.clone().unwrap();
let author_guard = author_shared_lock.read();
let ua_or_user_guard = UA_STYLESHEETS.shared_lock.read();
let guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
let snapshots = SnapshotMap::new();
let mut layout_context = self.build_layout_context(guards, false, &snapshots, origin);
let invalid_nodes = {
// Perform an abbreviated style recalc that operates without access to the DOM.
let animation_states = self.animation_states.read();
profile(
profile_time::ProfilerCategory::LayoutStyleRecalc,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| {
animation::recalc_style_for_animations::<ServoLayoutElement>(
&layout_context,
FlowRef::deref_mut(&mut root_flow),
&animation_states,
)
},
)
};
self.perform_post_style_recalc_layout_passes(
&mut root_flow,
&reflow_info,
&ReflowGoal::TickAnimations,
None,
&mut *rw_data,
&mut layout_context,
invalid_nodes,
);
assert!(layout_context.pending_images.is_none());
assert!(layout_context.newly_animating_nodes.is_none());
}
}
fn perform_post_style_recalc_layout_passes(
&self,
root_flow: &mut FlowRef,
@ -1753,23 +1652,17 @@ impl LayoutThread {
document: Option<&ServoLayoutDocument>,
rw_data: &mut LayoutThreadData,
context: &mut LayoutContext,
invalid_nodes: FxHashSet<OpaqueNode>,
) {
{
let mut newly_animating_nodes = context
.newly_animating_nodes
.as_ref()
.map(|nodes| nodes.lock().unwrap());
let newly_animating_nodes = newly_animating_nodes.as_mut().map(|nodes| &mut **nodes);
let mut animation_states = self.animation_states.write();
animation::collect_newly_animating_nodes(&animation_states, newly_animating_nodes);
animation::update_animation_states(
let mut newly_animating_nodes = context.newly_animating_nodes.lock().unwrap();
animation::do_post_style_animations_update(
&self.constellation_chan,
&self.script_chan,
&mut *animation_states,
invalid_nodes,
&mut *(self.animation_states.write()),
self.id,
&self.timer,
context.style_context.current_time_for_animations,
&mut newly_animating_nodes,
FlowRef::deref_mut(root_flow),
);
}

View file

@ -580,7 +580,6 @@ impl LayoutThread {
fn build_layout_context<'a>(
&'a self,
guards: StylesheetGuards<'a>,
script_initiated_layout: bool,
snapshot_map: &'a SnapshotMap,
origin: ImmutableOrigin,
) -> LayoutContext<'a> {
@ -601,11 +600,7 @@ impl LayoutThread {
image_cache: self.image_cache.clone(),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: if script_initiated_layout {
Some(Mutex::new(Vec::new()))
} else {
None
},
pending_images: Mutex::new(vec![]),
use_rayon: STYLE_THREAD_POOL.pool().is_some(),
}
}
@ -617,8 +612,6 @@ impl LayoutThread {
Msg::SetQuirksMode(..) => LayoutHangAnnotation::SetQuirksMode,
Msg::Reflow(..) => LayoutHangAnnotation::Reflow,
Msg::GetRPC(..) => LayoutHangAnnotation::GetRPC,
Msg::TickAnimations(..) => LayoutHangAnnotation::TickAnimations,
Msg::AdvanceClockMs(..) => LayoutHangAnnotation::AdvanceClockMs,
Msg::CollectReports(..) => LayoutHangAnnotation::CollectReports,
Msg::PrepareToExit(..) => LayoutHangAnnotation::PrepareToExit,
Msg::ExitNow => LayoutHangAnnotation::ExitNow,
@ -665,9 +658,6 @@ impl LayoutThread {
Msg::SetScrollStates(new_scroll_states),
possibly_locked_rw_data,
),
Request::FromPipeline(LayoutControlMsg::TickAnimations(origin)) => {
self.handle_request_helper(Msg::TickAnimations(origin), possibly_locked_rw_data)
},
Request::FromPipeline(LayoutControlMsg::GetCurrentEpoch(sender)) => {
self.handle_request_helper(Msg::GetCurrentEpoch(sender), possibly_locked_rw_data)
},
@ -739,7 +729,6 @@ impl LayoutThread {
|| self.handle_reflow(&mut data, possibly_locked_rw_data),
);
},
Msg::TickAnimations(origin) => self.tick_all_animations(origin),
Msg::SetScrollStates(new_scroll_states) => {
self.set_scroll_states(new_scroll_states, possibly_locked_rw_data);
},
@ -764,9 +753,6 @@ impl LayoutThread {
let _rw_data = possibly_locked_rw_data.lock();
sender.send(self.epoch.get()).unwrap();
},
Msg::AdvanceClockMs(how_many, do_tick, origin) => {
self.handle_advance_clock_ms(how_many, do_tick, origin);
},
Msg::GetWebFontLoadState(sender) => {
let _rw_data = possibly_locked_rw_data.lock();
let outstanding_web_fonts = self.outstanding_web_fonts.load(Ordering::SeqCst);
@ -900,19 +886,6 @@ impl LayoutThread {
}
}
/// Advances the animation clock of the document.
fn handle_advance_clock_ms<'a, 'b>(
&mut self,
how_many_ms: i32,
tick_animations: bool,
origin: ImmutableOrigin,
) {
self.timer.increment(how_many_ms as f64 / 1000.0);
if tick_animations {
self.tick_all_animations(origin);
}
}
/// Sets quirks mode for the document, causing the quirks mode stylesheet to be used.
fn handle_set_quirks_mode<'a, 'b>(&mut self, quirks_mode: QuirksMode) {
self.stylist.set_quirks_mode(quirks_mode);
@ -1004,6 +977,12 @@ impl LayoutThread {
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards);
if pref!(layout.animations.test.enabled) {
if let Some(delta) = data.advance_clock_delta {
self.timer.increment(delta as f64 / 1000.0);
}
}
self.stylist
.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size =
@ -1103,7 +1082,7 @@ impl LayoutThread {
self.stylist.flush(&guards, Some(element), Some(&map));
// Create a layout context for use throughout the following passes.
let mut layout_context = self.build_layout_context(guards.clone(), true, &map, origin);
let mut layout_context = self.build_layout_context(guards.clone(), &map, origin);
let traversal = RecalcStyle::new(layout_context);
let token = {
@ -1195,11 +1174,9 @@ impl LayoutThread {
context: &mut LayoutContext,
reflow_result: &mut ReflowComplete,
) {
let pending_images = match &context.pending_images {
Some(pending) => std::mem::take(&mut *pending.lock().unwrap()),
None => Vec::new(),
};
reflow_result.pending_images = pending_images;
reflow_result.pending_images =
std::mem::replace(&mut *context.pending_images.lock().unwrap(), vec![]);
match *reflow_goal {
ReflowGoal::LayoutQuery(ref querymsg, _) => match querymsg {
&QueryMsg::ContentBoxQuery(node) => {
@ -1304,41 +1281,6 @@ impl LayoutThread {
rw_data.scroll_offsets = layout_scroll_states
}
fn tick_all_animations<'a, 'b>(&mut self, origin: ImmutableOrigin) {
self.tick_animations(origin);
}
fn tick_animations(&mut self, origin: ImmutableOrigin) {
if self.relayout_event {
println!(
"**** pipeline={}\tForDisplay\tSpecial\tAnimationTick",
self.id
);
}
if let Some(root) = &*self.fragment_tree_root.borrow() {
// Unwrap here should not panic since self.fragment_tree_root is only ever set to Some(_)
// in handle_reflow() where self.document_shared_lock is as well.
let author_shared_lock = self.document_shared_lock.clone().unwrap();
let author_guard = author_shared_lock.read();
let ua_or_user_guard = UA_STYLESHEETS.shared_lock.read();
let guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
let snapshots = SnapshotMap::new();
let mut layout_context = self.build_layout_context(guards, false, &snapshots, origin);
self.perform_post_style_recalc_layout_passes(
root.clone(),
&ReflowGoal::TickAnimations,
None,
&mut layout_context,
);
assert!(layout_context.pending_images.is_none());
}
}
fn perform_post_style_recalc_layout_passes(
&self,
fragment_tree: Arc<FragmentTreeRoot>,

View file

@ -530,8 +530,6 @@ pub enum LayoutHangAnnotation {
SetQuirksMode,
Reflow,
GetRPC,
TickAnimations,
AdvanceClockMs,
CollectReports,
PrepareToExit,
ExitNow,

View file

@ -7,8 +7,7 @@ use immeta::load_from_buf;
use ipc_channel::ipc::IpcSender;
use net_traits::image::base::{load_from_memory, Image, ImageMetadata};
use net_traits::image_cache::{
CanRequestImages, CorsStatus, ImageCache, ImageCacheResult, ImageResponder,
PendingImageResponse,
CorsStatus, ImageCache, ImageCacheResult, ImageResponder, PendingImageResponse,
};
use net_traits::image_cache::{ImageOrMetadataAvailable, ImageResponse};
use net_traits::image_cache::{PendingImageId, UsePlaceholder};
@ -147,7 +146,6 @@ impl AllPendingLoads {
url: ServoUrl,
origin: ImmutableOrigin,
cors_status: Option<CorsSettings>,
can_request: CanRequestImages,
) -> CacheResult<'a> {
match self
.url_to_load_key
@ -158,10 +156,6 @@ impl AllPendingLoads {
CacheResult::Hit(*load_key, self.loads.get_mut(load_key).unwrap())
},
Vacant(url_entry) => {
if can_request == CanRequestImages::No {
return CacheResult::Miss(None);
}
let load_key = self.keygen.next();
url_entry.insert(load_key);
@ -461,7 +455,6 @@ impl ImageCache for ImageCacheImpl {
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> ImageCacheResult {
let mut store = self.store.lock().unwrap();
if let Some(result) = store.get_completed_image_if_available(
@ -485,12 +478,9 @@ impl ImageCache for ImageCacheImpl {
}
let decoded = {
let result = store.pending_loads.get_cached(
url.clone(),
origin.clone(),
cors_setting,
can_request,
);
let result = store
.pending_loads
.get_cached(url.clone(), origin.clone(), cors_setting);
match result {
CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
(&Some(Ok(_)), _) => {
@ -539,7 +529,6 @@ impl ImageCache for ImageCacheImpl {
cors_setting: Option<CorsSettings>,
sender: IpcSender<PendingImageResponse>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> ImageCacheResult {
debug!("Track image for {} ({:?})", url, origin);
let cache_result = self.get_cached_image_status(
@ -547,7 +536,6 @@ impl ImageCache for ImageCacheImpl {
origin.clone(),
cors_setting,
use_placeholder,
can_request,
);
match cache_result {

View file

@ -14,15 +14,6 @@ use std::sync::Arc;
// Aux structs and enums.
// ======================================================================
/// Whether a consumer is in a position to request images or not. This can occur
/// when animations are being processed by the layout thread while the script
/// thread is executing in parallel.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum CanRequestImages {
No,
Yes,
}
/// Indicating either entire image or just metadata availability
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub enum ImageOrMetadataAvailable {
@ -119,7 +110,6 @@ pub trait ImageCache: Sync + Send {
origin: ImmutableOrigin,
cors_setting: Option<CorsSettings>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> ImageCacheResult;
/// Add a listener for the provided pending image id, eventually called by
@ -135,7 +125,6 @@ pub trait ImageCache: Sync + Send {
cors_setting: Option<CorsSettings>,
sender: IpcSender<PendingImageResponse>,
use_placeholder: UsePlaceholder,
can_request: CanRequestImages,
) -> ImageCacheResult;
/// Add a new listener for the given pending image id. If the image is already present,

View file

@ -60,8 +60,8 @@ use mime::{self, Mime};
use msg::constellation_msg::PipelineId;
use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache::{
CanRequestImages, CorsStatus, ImageCache, ImageCacheResult, ImageOrMetadataAvailable,
ImageResponse, PendingImageId, PendingImageResponse, UsePlaceholder,
CorsStatus, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse,
PendingImageId, PendingImageResponse, UsePlaceholder,
};
use net_traits::request::{CorsSettings, Destination, Initiator, RequestBuilder};
use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError};
@ -326,7 +326,6 @@ impl HTMLImageElement {
cors_setting_for_element(self.upcast()),
sender,
UsePlaceholder::Yes,
CanRequestImages::Yes,
);
match cache_result {
@ -1107,7 +1106,6 @@ impl HTMLImageElement {
cors_setting_for_element(self.upcast()),
sender,
UsePlaceholder::No,
CanRequestImages::Yes,
);
match cache_result {

View file

@ -27,8 +27,8 @@ use html5ever::{LocalName, Prefix};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::image_cache::{
CanRequestImages, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse,
PendingImageId, UsePlaceholder,
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, PendingImageId,
UsePlaceholder,
};
use net_traits::request::{CredentialsMode, Destination, RequestBuilder};
use net_traits::{
@ -163,7 +163,6 @@ impl HTMLVideoElement {
None,
sender,
UsePlaceholder::No,
CanRequestImages::Yes,
);
match cache_result {

View file

@ -1054,8 +1054,8 @@ impl TestBindingMethods for TestBinding {
}
}
fn AdvanceClock(&self, ms: i32, tick: bool) {
self.global().as_window().advance_animation_clock(ms, tick);
fn AdvanceClock(&self, ms: i32) {
self.global().as_window().advance_animation_clock(ms);
}
fn Panic(&self) {

View file

@ -517,7 +517,7 @@ interface TestBinding {
[Pref="dom.testbinding.prefcontrolled.enabled"]
const unsigned short prefControlledConstDisabled = 0;
[Pref="layout.animations.test.enabled"]
void advanceClock(long millis, optional boolean forceLayoutTick = true);
void advanceClock(long millis);
[Pref="dom.testbinding.prefcontrolled2.enabled"]
readonly attribute boolean prefControlledAttributeEnabled;

View file

@ -173,6 +173,8 @@ pub enum ReflowReason {
IFrameLoadEvent,
MissingExplicitReflow,
ElementStateChanged,
TickAnimations,
AdvanceClock(i32),
}
#[dom_struct]
@ -1544,16 +1546,16 @@ impl Window {
)
}
/// Advances the layout animation clock by `delta` milliseconds, and then
/// forces a reflow if `tick` is true.
pub fn advance_animation_clock(&self, delta: i32, tick: bool) {
self.layout_chan
.send(Msg::AdvanceClockMs(
delta,
tick,
self.origin().immutable().clone(),
))
.unwrap();
/// Prepares to tick animations and then does a reflow which also advances the
/// layout animation clock.
pub fn advance_animation_clock(&self, delta: i32) {
let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
ScriptThread::restyle_animating_nodes_for_advancing_clock(&pipeline_id);
self.force_reflow(
ReflowGoal::TickAnimations,
ReflowReason::AdvanceClock(delta),
None,
);
}
/// Reflows the page unconditionally if possible and not suppressed. This
@ -1626,11 +1628,15 @@ impl Window {
// If this reflow is for display, ensure webgl canvases are composited with
// up-to-date contents.
match reflow_goal {
ReflowGoal::Full => document.flush_dirty_canvases(),
ReflowGoal::TickAnimations | ReflowGoal::LayoutQuery(..) => {},
if for_display {
document.flush_dirty_canvases();
}
let advance_clock_delta = match reason {
ReflowReason::AdvanceClock(delta) => Some(delta),
_ => None,
};
// Send new document and relevant styles to layout.
let needs_display = reflow_goal.needs_display();
let reflow = ScriptReflow {
@ -1645,6 +1651,7 @@ impl Window {
script_join_chan: join_chan,
dom_count: document.dom_count(),
pending_restyles: document.drain_pending_restyles(),
advance_clock_delta,
};
self.layout_chan
@ -2487,6 +2494,8 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow
ReflowReason::IFrameLoadEvent => "\tIFrameLoadEvent",
ReflowReason::MissingExplicitReflow => "\tMissingExplicitReflow",
ReflowReason::ElementStateChanged => "\tElementStateChanged",
ReflowReason::TickAnimations => "\tTickAnimations",
ReflowReason::AdvanceClock(..) => "\tAdvanceClock",
});
println!("{}", debug_msg);

View file

@ -136,12 +136,12 @@ use script_traits::CompositorEvent::{
WheelEvent,
};
use script_traits::{
CompositorEvent, ConstellationControlMsg, DiscardBrowsingContext, DocumentActivity,
EventResult, HistoryEntryReplacement, InitialScriptState, JsEvalResult, LayoutMsg, LoadData,
LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo, Painter,
ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory, ScriptToConstellationChan,
StructuredSerializedData, TimerSchedulerMsg, TouchEventType, TouchId,
TransitionOrAnimationEventType, UntrustedNodeAddress, UpdatePipelineIdReason,
AnimationTickType, CompositorEvent, ConstellationControlMsg, DiscardBrowsingContext,
DocumentActivity, EventResult, HistoryEntryReplacement, InitialScriptState, JsEvalResult,
LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType,
NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory,
ScriptToConstellationChan, StructuredSerializedData, TimerSchedulerMsg, TouchEventType,
TouchId, TransitionOrAnimationEventType, UntrustedNodeAddress, UpdatePipelineIdReason,
WebrenderIpcSender, WheelDelta, WindowSizeData, WindowSizeType,
};
use servo_atoms::Atom;
@ -1497,7 +1497,7 @@ impl ScriptThread {
self.handle_set_scroll_state(id, &scroll_state);
})
},
FromConstellation(ConstellationControlMsg::TickAllAnimations(pipeline_id)) => {
FromConstellation(ConstellationControlMsg::TickAllAnimations(pipeline_id, _)) => {
// step 7.8
if !animation_ticks.contains(&pipeline_id) {
animation_ticks.insert(pipeline_id);
@ -1703,7 +1703,7 @@ impl ScriptThread {
RemoveHistoryStates(id, ..) => Some(id),
FocusIFrame(id, ..) => Some(id),
WebDriverScriptCommand(id, ..) => Some(id),
TickAllAnimations(id) => Some(id),
TickAllAnimations(id, ..) => Some(id),
TransitionOrAnimationEvent { .. } => None,
WebFontLoaded(id) => Some(id),
DispatchIFrameLoadEvent {
@ -1902,8 +1902,8 @@ impl ScriptThread {
ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, msg) => {
self.handle_webdriver_msg(pipeline_id, msg)
},
ConstellationControlMsg::TickAllAnimations(pipeline_id) => {
self.handle_tick_all_animations(pipeline_id)
ConstellationControlMsg::TickAllAnimations(pipeline_id, tick_type) => {
self.handle_tick_all_animations(pipeline_id, tick_type)
},
ConstellationControlMsg::TransitionOrAnimationEvent {
pipeline_id,
@ -2914,14 +2914,46 @@ impl ScriptThread {
debug!("Exited script thread.");
}
fn restyle_animating_nodes(&self, id: &PipelineId) -> bool {
match self.animating_nodes.borrow().get(id) {
Some(nodes) => {
for node in nodes.iter() {
node.dirty(NodeDamage::NodeStyleDamaged);
}
true
},
None => false,
}
}
pub fn restyle_animating_nodes_for_advancing_clock(id: &PipelineId) {
SCRIPT_THREAD_ROOT.with(|root| {
let script_thread = unsafe { &*root.get().unwrap() };
script_thread.restyle_animating_nodes(id);
});
}
/// Handles when layout thread finishes all animation in one tick
fn handle_tick_all_animations(&self, id: PipelineId) {
fn handle_tick_all_animations(&self, id: PipelineId, tick_type: AnimationTickType) {
let document = match self.documents.borrow().find_document(id) {
Some(document) => document,
None => return warn!("Message sent to closed pipeline {}.", id),
};
if tick_type.contains(AnimationTickType::REQUEST_ANIMATION_FRAME) {
document.run_the_animation_frame_callbacks();
}
if tick_type.contains(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS) {
if !self.restyle_animating_nodes(&id) {
return;
}
document.window().force_reflow(
ReflowGoal::TickAnimations,
ReflowReason::TickAnimations,
None,
);
}
}
/// Handles firing of transition-related events.
///
@ -2969,11 +3001,6 @@ impl ScriptThread {
}
}
// Not quite the right thing - see #13865.
if event_type.finalizes_transition_or_animation() {
node.dirty(NodeDamage::NodeStyleDamaged);
}
let event_atom = match event_type {
TransitionOrAnimationEventType::AnimationEnd => atom!("animationend"),
TransitionOrAnimationEventType::TransitionCancel => atom!("transitioncancel"),

View file

@ -47,15 +47,6 @@ pub enum Msg {
/// Get an RPC interface.
GetRPC(Sender<Box<dyn LayoutRPC + Send>>),
/// Requests that the layout thread render the next frame of all animations.
TickAnimations(ImmutableOrigin),
/// Updates layout's timer for animation testing from script.
///
/// The inner field is the number of *milliseconds* to advance, and the bool
/// field is whether animations should be force-ticked.
AdvanceClockMs(i32, bool, ImmutableOrigin),
/// Requests that the layout thread measure its memory usage. The resulting reports are sent back
/// via the supplied channel.
CollectReports(ReportsChan),
@ -216,6 +207,8 @@ pub struct ScriptReflow {
pub origin: ImmutableOrigin,
/// Restyle snapshot map.
pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
/// How much to advance the clock when testing.
pub advance_clock_delta: Option<i32>,
}
pub struct LayoutThreadInit {

View file

@ -11,6 +11,7 @@ name = "script_traits"
path = "lib.rs"
[dependencies]
bitflags = "1.0"
bluetooth_traits = {path = "../bluetooth_traits"}
canvas_traits = {path = "../canvas_traits"}
cookie = "0.11"

View file

@ -9,6 +9,8 @@
#![deny(missing_docs)]
#![deny(unsafe_code)]
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate malloc_size_of;
#[macro_use]
@ -122,8 +124,6 @@ pub enum LayoutControlMsg {
ExitNow,
/// Requests the current epoch (layout counter) from this layout.
GetCurrentEpoch(IpcSender<Epoch>),
/// Asks layout to run another step in its animation.
TickAnimations(ImmutableOrigin),
/// Tells layout about the new scrolling offsets of each scrollable stacking context.
SetScrollStates(Vec<ScrollState>),
/// Requests the current load state of Web fonts. `true` is returned if fonts are still loading
@ -403,7 +403,7 @@ pub enum ConstellationControlMsg {
/// Passes a webdriver command to the script thread for execution
WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
/// Notifies script thread that all animations are done
TickAllAnimations(PipelineId),
TickAllAnimations(PipelineId, AnimationTickType),
/// Notifies the script thread that a transition or animation related event should be sent.
TransitionOrAnimationEvent {
/// The pipeline id of the layout task that sent this message.
@ -812,13 +812,15 @@ pub struct IFrameLoadInfoWithData {
pub window_size: WindowSizeData,
}
/// Specifies whether the script or layout thread needs to be ticked for animation.
#[derive(Debug, Deserialize, Serialize)]
pub enum AnimationTickType {
/// The script thread.
Script,
/// The layout thread.
Layout,
bitflags! {
#[derive(Deserialize, Serialize)]
/// Specifies if rAF should be triggered and/or CSS Animations and Transitions.
pub struct AnimationTickType: u8 {
/// Trigger a call to requestAnimationFrame.
const REQUEST_ANIMATION_FRAME = 0b001;
/// Trigger restyles for CSS Animations and Transitions.
const CSS_ANIMATIONS_AND_TRANSITIONS = 0b010;
}
}
/// The scroll state of a stacking context.

View file

@ -18,7 +18,6 @@ use crate::properties::LonghandIdSet;
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
use crate::stylesheets::Origin;
use crate::timer::Timer;
use crate::values::computed::Time;
use crate::values::computed::TimingFunction;
use crate::values::generics::box_::AnimationIterationCount;
@ -77,30 +76,33 @@ pub struct KeyframesAnimationState {
}
impl KeyframesAnimationState {
/// Performs a tick in the animation state, i.e., increments the counter of
/// the current iteration count, updates times and then toggles the
/// direction if appropriate.
///
/// Returns true if the animation should keep running.
pub fn tick(&mut self) -> bool {
debug!("KeyframesAnimationState::tick");
self.started_at += self.duration + self.delay;
/// Given the current time, advances this animation to the next iteration,
/// updates times, and then toggles the direction if appropriate. Otherwise
/// does nothing.
pub fn iterate_if_necessary(&mut self, time: f64) {
if !self.iteration_over(time) {
return;
}
match self.running_state {
// If it's paused, don't update direction or iteration count.
KeyframesRunningState::Paused(_) => return true,
KeyframesRunningState::Paused(_) => return,
KeyframesRunningState::Running => {},
}
if let KeyframesIterationState::Finite(ref mut current, ref max) = self.iteration_state {
*current += 1.0;
if let KeyframesIterationState::Finite(ref mut current, max) = self.iteration_state {
// If we are already on the final iteration, just exit now.
// NB: This prevent us from updating the direction, which might be
// needed for the correct handling of animation-fill-mode.
if *current >= *max {
return false;
if (max - *current) <= 1.0 {
return;
}
*current += 1.0;
}
// Update the next iteration direction if applicable.
// TODO(mrobinson): The duration might now be wrong for floating point iteration counts.
self.started_at += self.duration + self.delay;
match self.direction {
AnimationDirection::Alternate | AnimationDirection::AlternateReverse => {
self.current_direction = match self.current_direction {
@ -111,8 +113,29 @@ impl KeyframesAnimationState {
},
_ => {},
}
}
true
fn iteration_over(&self, time: f64) -> bool {
time > (self.started_at + self.duration)
}
fn has_ended(&self, time: f64) -> bool {
if !self.iteration_over(time) {
return false;
}
// If we are paused then we haven't ended.
match self.running_state {
KeyframesRunningState::Paused(_) => return false,
KeyframesRunningState::Running => {},
}
// If we have a limited number of iterations and we cannot advance to another
// iteration, then we have ended.
return match self.iteration_state {
KeyframesIterationState::Finite(current, max) if (max - current) <= 1.0 => true,
KeyframesIterationState::Finite(..) | KeyframesIterationState::Infinite => false,
};
}
/// Updates the appropiate state from other animation.
@ -122,7 +145,7 @@ 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, timer: &Timer) {
pub fn update_from_other(&mut self, other: &Self, now: f64) {
use self::KeyframesRunningState::*;
debug!(
@ -148,12 +171,10 @@ 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 = timer.seconds() - (self.duration * progress)
},
(&mut Running, Paused(progress)) => new_started_at = now - (self.duration * progress),
(&mut Paused(ref mut new), Paused(old)) => *new = old,
(&mut Paused(ref mut progress), Running) => {
*progress = (timer.seconds() - old_started_at) / old_duration
*progress = (now - old_started_at) / old_duration
},
_ => {},
}
@ -239,6 +260,18 @@ impl Animation {
}
}
/// Whether or not this animation has ended at the provided time. This does
/// not take into account canceling i.e. when an animation or transition is
/// canceled due to changes in the style.
pub fn has_ended(&self, time: f64) -> bool {
match *self {
Animation::Transition(_, started_at, ref property_animation) => {
time >= started_at + (property_animation.duration)
},
Animation::Keyframes(_, _, _, ref state) => state.has_ended(time),
}
}
/// Whether this animation has the same end value as another one.
#[inline]
fn is_transition_with_same_end_value(&self, other_animation: &PropertyAnimation) -> bool {
@ -463,18 +496,7 @@ impl ElementAnimationState {
false
}
pub(crate) fn apply_completed_animations(&mut self, style: &mut Arc<ComputedValues>) {
for animation in self.finished_animations.iter() {
// TODO: Make this a bit more general and add support animation-fill-mode.
// Without support for that property though, animations that have ended should
// not affect property values. This is why we only process transitions here.
if let Animation::Transition(_, _, property_animation) = animation {
property_animation.update(Arc::make_mut(style), 1.0);
}
}
}
pub(crate) fn apply_running_animations<E>(
pub(crate) fn apply_new_and_running_animations<E>(
&mut self,
context: &SharedStyleContext,
style: &mut Arc<ComputedValues>,
@ -482,35 +504,20 @@ impl ElementAnimationState {
) where
E: TElement,
{
// Return early so that we don't unnecessarily clone the style when making it mutable.
if self.running_animations.is_empty() {
return;
}
if !self.running_animations.is_empty() {
let style = Arc::make_mut(style);
for animation in self.running_animations.iter_mut() {
update_style_for_animation::<E>(context, animation, style, font_metrics);
}
}
pub(crate) fn apply_new_animations<E>(
&mut self,
context: &SharedStyleContext,
style: &mut Arc<ComputedValues>,
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
) where
E: TElement,
{
// Return early so that we don't unnecessarily clone the style when making it mutable.
if self.new_animations.is_empty() {
return;
}
if !self.new_animations.is_empty() {
let style = Arc::make_mut(style);
for animation in self.new_animations.iter_mut() {
update_style_for_animation::<E>(context, animation, style, font_metrics);
}
}
}
/// Whether this `ElementAnimationState` is empty, which means it doesn't
/// hold any animations in any state.
@ -525,24 +532,6 @@ impl ElementAnimationState {
self.new_animations.push(animation);
}
fn add_or_update_new_animation(&mut self, timer: &Timer, new_animation: Animation) {
// If the animation was already present in the list for the node,
// just update its state.
if let Animation::Keyframes(_, _, ref new_name, ref new_state) = new_animation {
for existing_animation in self.running_animations.iter_mut() {
match existing_animation {
Animation::Keyframes(_, _, ref name, ref mut state) if *name == *new_name => {
state.update_from_other(&new_state, timer);
return;
},
_ => {},
}
}
}
// Otherwise just add the new running animation.
self.add_new_animation(new_animation);
}
/// Update our animations given a new style, canceling or starting new animations
/// when appropriate.
pub fn update_animations_for_new_style<E>(
@ -572,33 +561,61 @@ impl ElementAnimationState {
}
maybe_start_animations(element, &context, &new_style, self);
self.iterate_running_animations_if_necessary(context.current_time_for_animations);
}
/// Update our transitions given a new style, canceling or starting new animations
/// when appropriate.
pub fn update_transitions_for_new_style(
pub fn update_transitions_for_new_style<E>(
&mut self,
context: &SharedStyleContext,
opaque_node: OpaqueNode,
before_change_style: Option<&Arc<ComputedValues>>,
new_style: &Arc<ComputedValues>,
) {
old_style: Option<&Arc<ComputedValues>>,
after_change_style: &Arc<ComputedValues>,
font_metrics: &dyn crate::font_metrics::FontMetricsProvider,
) where
E: TElement,
{
// If this is the first style, we don't trigger any transitions and we assume
// there were no previously triggered transitions.
let before_change_style = match before_change_style {
Some(before_change_style) => before_change_style,
let mut before_change_style = match old_style {
Some(old_style) => Arc::clone(old_style),
None => return,
};
// We convert old values into `before-change-style` here.
// See https://drafts.csswg.org/css-transitions/#starting. We need to clone the
// style because this might still be a reference to the original `old_style` and
// we want to preserve that so that we can later properly calculate restyle damage.
if self.running_animations.is_empty() || self.new_animations.is_empty() {
before_change_style = before_change_style.clone();
self.apply_new_and_running_animations::<E>(
context,
&mut before_change_style,
font_metrics,
);
}
let transitioning_properties = start_transitions_if_applicable(
context,
opaque_node,
before_change_style,
new_style,
&before_change_style,
after_change_style,
self,
);
self.cancel_transitions_with_nontransitioning_properties(&transitioning_properties);
}
/// When necessary, iterate our running animations to the next iteration.
pub fn iterate_running_animations_if_necessary(&mut self, time: f64) {
for animation in self.running_animations.iter_mut() {
match *animation {
Animation::Keyframes(_, _, _, ref mut state) => state.iterate_if_necessary(time),
_ => {},
}
}
}
}
/// Kick off any new transitions for this node and return all of the properties that are
@ -651,8 +668,8 @@ pub fn start_transitions_if_applicable(
// Kick off the animation.
debug!("Kicking off transition of {:?}", property_animation);
let box_style = new_style.get_box();
let now = context.timer.seconds();
let start_time = now + (box_style.transition_delay_mod(transition.index).seconds() as f64);
let start_time = context.current_time_for_animations +
(box_style.transition_delay_mod(transition.index).seconds() as f64);
animation_state.add_new_animation(Animation::Transition(
opaque_node,
start_time,
@ -755,8 +772,7 @@ pub fn maybe_start_animations<E>(
}
let delay = box_style.animation_delay_mod(i).seconds();
let now = context.timer.seconds();
let animation_start = now + delay as f64;
let animation_start = context.current_time_for_animations + delay as f64;
let iteration_state = match box_style.animation_iteration_count_mod(i) {
AnimationIterationCount::Infinite => KeyframesIterationState::Infinite,
AnimationIterationCount::Number(n) => KeyframesIterationState::Finite(0.0, n),
@ -778,13 +794,7 @@ pub fn maybe_start_animations<E>(
AnimationPlayState::Running => KeyframesRunningState::Running,
};
animation_state.add_or_update_new_animation(
&context.timer,
Animation::Keyframes(
element.as_node().opaque(),
anim.clone(),
name.clone(),
KeyframesAnimationState {
let new_state = KeyframesAnimationState {
started_at: animation_start,
duration: duration as f64,
delay: delay as f64,
@ -793,9 +803,27 @@ pub fn maybe_start_animations<E>(
direction: animation_direction,
current_direction: initial_direction,
cascade_style: new_style.clone(),
},
),
);
};
// If the animation was already present in the list for the node, just update its state.
for existing_animation in animation_state.running_animations.iter_mut() {
match existing_animation {
Animation::Keyframes(_, _, ref old_name, ref mut old_state)
if *old_name == *name =>
{
old_state.update_from_other(&new_state, context.current_time_for_animations);
return;
}
_ => {},
}
}
animation_state.add_new_animation(Animation::Keyframes(
element.as_node().opaque(),
anim.clone(),
name.clone(),
new_state,
));
}
}
@ -811,7 +839,7 @@ pub fn update_style_for_animation<E>(
debug!("update_style_for_animation: {:?}", animation);
match *animation {
Animation::Transition(_, start_time, ref property_animation) => {
let now = context.timer.seconds();
let now = context.current_time_for_animations;
let progress = (now - start_time) / (property_animation.duration);
let progress = progress.min(1.0);
if progress >= 0.0 {
@ -823,7 +851,7 @@ pub fn update_style_for_animation<E>(
let started_at = state.started_at;
let now = match state.running_state {
KeyframesRunningState::Running => context.timer.seconds(),
KeyframesRunningState::Running => context.current_time_for_animations,
KeyframesRunningState::Paused(progress) => started_at + duration * progress,
};

View file

@ -25,7 +25,6 @@ use crate::shared_lock::StylesheetGuards;
use crate::sharing::StyleSharingCache;
use crate::stylist::Stylist;
use crate::thread_state::{self, ThreadState};
use crate::timer::Timer;
use crate::traversal::DomTraversal;
use crate::traversal_flags::TraversalFlags;
use app_units::Au;
@ -156,9 +155,9 @@ pub struct SharedStyleContext<'a> {
/// Guards for pre-acquired locks
pub guards: StylesheetGuards<'a>,
/// The current timer for transitions and animations. This is needed to test
/// them.
pub timer: Timer,
/// The current time for transitions and animations. This is needed to ensure
/// a consistent sampling time and also to adjust the time for testing.
pub current_time_for_animations: f64,
/// Flags controlling how we traverse the tree.
pub traversal_flags: TraversalFlags,

View file

@ -442,31 +442,17 @@ trait PrivateMatchMethods: TElement {
let mut animation_states = shared_context.animation_states.write();
let mut animation_state = animation_states.remove(&this_opaque).unwrap_or_default();
if let Some(ref mut old_values) = *old_values {
// We convert old values into `before-change-style` here.
// https://drafts.csswg.org/css-transitions/#starting
animation_state.apply_completed_animations(old_values);
animation_state.apply_running_animations::<Self>(
shared_context,
old_values,
&context.thread_local.font_metrics_provider,
);
}
animation_state.update_animations_for_new_style(*self, &shared_context, &new_values);
animation_state.update_transitions_for_new_style(
animation_state.update_transitions_for_new_style::<Self>(
&shared_context,
this_opaque,
old_values.as_ref(),
new_values,
);
animation_state.apply_running_animations::<Self>(
shared_context,
new_values,
&context.thread_local.font_metrics_provider,
);
animation_state.apply_new_animations::<Self>(
animation_state.apply_new_and_running_animations::<Self>(
shared_context,
new_values,
&context.thread_local.font_metrics_provider,

View file

@ -89,9 +89,6 @@
[Web Animations: property <text-shadow> from neutral to [green 20px 20px 20px\] at (-0.3) should be [rgb(255, 176, 0) 7px 33px 7px\]]
expected: FAIL
[CSS Animations: property <text-shadow> from neutral to [green 20px 20px 20px\] at (0.3) should be [rgb(179, 154, 0) 13px 27px 13px\]]
expected: FAIL
[Web Animations: property <text-shadow> from neutral to [green 20px 20px 20px\] at (0) should be [rgb(255, 165, 0) 10px 30px 10px\]]
expected: FAIL

View file

@ -209,21 +209,3 @@
[CSS Animations: property <vertical-align> from [inherit\] to [40px\] at (1.5) should be [10px\]]
expected: FAIL
[CSS Transitions with transition: all: property <vertical-align> from [0px\] to [100px\] at (0.6) should be [60px\]]
expected: FAIL
[CSS Transitions with transition: all: property <vertical-align> from [0px\] to [100px\] at (-0.5) should be [-50px\]]
expected: FAIL
[CSS Transitions: property <vertical-align> from [0px\] to [100px\] at (0.6) should be [60px\]]
expected: FAIL
[CSS Transitions: property <vertical-align> from [0px\] to [100px\] at (-0.5) should be [-50px\]]
expected: FAIL
[CSS Transitions with transition: all: property <vertical-align> from [0px\] to [100px\] at (0.3) should be [30px\]]
expected: FAIL
[CSS Transitions: property <vertical-align> from [0px\] to [100px\] at (0.3) should be [30px\]]
expected: FAIL

View file

@ -3,9 +3,6 @@
[Transitions are canceled when an element is re-parented to the same node]
expected: NOTRUN
[Transitions are canceled when an element is removed from the document]
[Transitions are canceled when an element is re-parented]
expected: TIMEOUT
[Transitions are canceled when an element is re-parented]
expected: NOTRUN

View file

@ -1,2 +0,0 @@
[no-transition-from-ua-to-blocking-stylesheet.html]
expected: FAIL

View file

@ -53,459 +53,3 @@
[background-position length(px) / events]
expected: FAIL
[outline-width length(px) / values]
expected: FAIL
[border-left-width length(em) / values]
expected: FAIL
[border-bottom-width length(px) / values]
expected: FAIL
[border-top-width length(em) / values]
expected: FAIL
[line-height number(decimal) / values]
expected: FAIL
[word-spacing length(mm) / values]
expected: FAIL
[text-indent length(pc) / values]
expected: FAIL
[opacity number[0,1\](zero-to-one) / values]
expected: FAIL
[line-height number(integer) / values]
expected: FAIL
[outline-offset length(px) / values]
expected: FAIL
[font-size length(pt) / values]
expected: FAIL
[max-height length(px) / values]
expected: FAIL
[vertical-align length(in) / values]
expected: FAIL
[max-height percentage(%) / values]
expected: FAIL
[min-width length(px) / values]
expected: FAIL
[border-top-color color(rgba) / values]
expected: FAIL
[border-right-color color(rgba) / values]
expected: FAIL
[font-size length(ex) / values]
expected: FAIL
[min-height length(pc) / values]
expected: FAIL
[line-height length(px) / values]
expected: FAIL
[word-spacing length(px) / values]
expected: FAIL
[vertical-align percentage(%) / values]
expected: FAIL
[border-right-width length(pc) / values]
expected: FAIL
[line-height length(ex) / values]
expected: FAIL
[color color(rgba) / values]
expected: FAIL
[font-size length(in) / values]
expected: FAIL
[font-size length(em) / values]
expected: FAIL
[border-left-width length(mm) / values]
expected: FAIL
[vertical-align length(em) / values]
expected: FAIL
[clip rectangle(rectangle) / values]
expected: FAIL
[word-spacing length(em) / values]
expected: FAIL
[border-right-width length(ex) / values]
expected: FAIL
[border-top-width length(cm) / values]
expected: FAIL
[max-width length(em) / values]
expected: FAIL
[font-size length(cm) / values]
expected: FAIL
[border-right-width length(cm) / values]
expected: FAIL
[outline-offset length(ex) / values]
expected: FAIL
[min-width length(cm) / values]
expected: FAIL
[min-height length(px) / values]
expected: FAIL
[min-width length(mm) / values]
expected: FAIL
[border-bottom-width length(in) / values]
expected: FAIL
[vertical-align length(pc) / values]
expected: FAIL
[vertical-align length(cm) / values]
expected: FAIL
[max-height length(ex) / values]
expected: FAIL
[vertical-align length(ex) / values]
expected: FAIL
[min-height percentage(%) / values]
expected: FAIL
[max-height length(pt) / values]
expected: FAIL
[outline-offset length(mm) / values]
expected: FAIL
[font-weight font-weight(keyword) / values]
expected: FAIL
[max-height length(mm) / values]
expected: FAIL
[outline-offset length(pt) / values]
expected: FAIL
[line-height length(mm) / values]
expected: FAIL
[line-height length(cm) / values]
expected: FAIL
[border-top-width length(mm) / values]
expected: FAIL
[letter-spacing length(in) / values]
expected: FAIL
[border-bottom-color color(rgba) / values]
expected: FAIL
[min-width percentage(%) / values]
expected: FAIL
[min-height length(cm) / values]
expected: FAIL
[letter-spacing length(mm) / values]
expected: FAIL
[font-size percentage(%) / values]
expected: FAIL
[letter-spacing length(cm) / values]
expected: FAIL
[vertical-align length(pt) / values]
expected: FAIL
[border-left-color color(rgba) / values]
expected: FAIL
[letter-spacing length(pc) / values]
expected: FAIL
[letter-spacing length(pt) / values]
expected: FAIL
[word-spacing length(ex) / values]
expected: FAIL
[line-height length(pt) / values]
expected: FAIL
[border-top-width length(px) / values]
expected: FAIL
[min-width length(pt) / values]
expected: FAIL
[border-bottom-width length(mm) / values]
expected: FAIL
[border-bottom-width length(cm) / values]
expected: FAIL
[min-width length(em) / values]
expected: FAIL
[min-height length(em) / values]
expected: FAIL
[max-width length(mm) / values]
expected: FAIL
[min-height length(ex) / values]
expected: FAIL
[max-width length(px) / values]
expected: FAIL
[font-size length(mm) / values]
expected: FAIL
[min-width length(pc) / values]
expected: FAIL
[letter-spacing length(em) / values]
expected: FAIL
[line-height length(em) / values]
expected: FAIL
[border-bottom-width length(pt) / values]
expected: FAIL
[word-spacing length(pc) / values]
expected: FAIL
[outline-offset length(in) / values]
expected: FAIL
[word-spacing length(in) / values]
expected: FAIL
[outline-width length(pt) / values]
expected: FAIL
[border-top-width length(pc) / values]
expected: FAIL
[border-left-width length(px) / values]
expected: FAIL
[font-size length(px) / values]
expected: FAIL
[border-left-width length(cm) / values]
expected: FAIL
[border-right-width length(px) / values]
expected: FAIL
[outline-width length(in) / values]
expected: FAIL
[word-spacing length(pt) / values]
expected: FAIL
[border-right-width length(mm) / values]
expected: FAIL
[max-width length(in) / values]
expected: FAIL
[outline-color color(rgba) / values]
expected: FAIL
[text-indent length(pt) / values]
expected: FAIL
[border-right-width length(pt) / values]
expected: FAIL
[border-left-width length(in) / values]
expected: FAIL
[max-height length(in) / values]
expected: FAIL
[line-height length(in) / values]
expected: FAIL
[border-bottom-width length(em) / values]
expected: FAIL
[outline-width length(ex) / values]
expected: FAIL
[font-size length(pc) / values]
expected: FAIL
[min-width length(in) / values]
expected: FAIL
[outline-width length(cm) / values]
expected: FAIL
[max-width percentage(%) / values]
expected: FAIL
[max-width length(ex) / values]
expected: FAIL
[letter-spacing length(ex) / values]
expected: FAIL
[border-left-width length(ex) / values]
expected: FAIL
[outline-width length(mm) / values]
expected: FAIL
[border-left-width length(pc) / values]
expected: FAIL
[outline-width length(pc) / values]
expected: FAIL
[word-spacing percentage(%) / values]
expected: FAIL
[font-weight font-weight(numeric) / values]
expected: FAIL
[vertical-align length(px) / values]
expected: FAIL
[letter-spacing length(px) / values]
expected: FAIL
[max-width length(pt) / values]
expected: FAIL
[line-height percentage(%) / values]
expected: FAIL
[border-top-width length(pt) / values]
expected: FAIL
[min-height length(mm) / values]
expected: FAIL
[max-height length(cm) / values]
expected: FAIL
[outline-width length(em) / values]
expected: FAIL
[border-right-width length(em) / values]
expected: FAIL
[max-height length(em) / values]
expected: FAIL
[max-width length(cm) / values]
expected: FAIL
[outline-offset length(em) / values]
expected: FAIL
[outline-offset length(cm) / values]
expected: FAIL
[border-top-width length(ex) / values]
expected: FAIL
[border-right-width length(in) / values]
expected: FAIL
[z-index integer(integer) / values]
expected: FAIL
[border-left-width length(pt) / values]
expected: FAIL
[vertical-align length(mm) / values]
expected: FAIL
[border-bottom-width length(pc) / values]
expected: FAIL
[line-height length(pc) / values]
expected: FAIL
[border-top-width length(in) / values]
expected: FAIL
[border-bottom-width length(ex) / values]
expected: FAIL
[min-height length(in) / values]
expected: FAIL
[outline-offset length(pc) / values]
expected: FAIL
[max-height length(pc) / values]
expected: FAIL
[background-color color(rgba) / values]
expected: FAIL
[min-height length(pt) / values]
expected: FAIL
[word-spacing length(cm) / values]
expected: FAIL
[max-width length(pc) / values]
expected: FAIL
[min-width length(ex) / values]
expected: FAIL
[text-indent length(ex) / values]
expected: FAIL
[text-indent length(px) / values]
expected: FAIL
[text-indent length(mm) / values]
expected: FAIL
[text-indent length(cm) / values]
expected: FAIL
[text-shadow shadow(shadow) / values]
expected: FAIL
[text-indent length(in) / values]
expected: FAIL
[text-indent length(em) / values]
expected: FAIL
[text-indent percentage(%) / values]
expected: FAIL

View file

@ -461,45 +461,12 @@
[background-image image(data) / values]
expected: FAIL
[transform transform(rotate) / values]
expected: FAIL
[border-bottom-right-radius border-radius(px-px) / values]
expected: FAIL
[background-image image(gradient) / values]
expected: FAIL
[font-stretch font-stretch(keyword) / values]
expected: FAIL
[border-top-right-radius border-radius(px) / values]
expected: FAIL
[border-top-left-radius border-radius(px-px) / values]
expected: FAIL
[border-bottom-right-radius border-radius(px) / values]
expected: FAIL
[border-bottom-left-radius border-radius(px-px) / values]
expected: FAIL
[background-image image(url) / values]
expected: FAIL
[border-top-left-radius border-radius(px) / values]
expected: FAIL
[border-bottom-left-radius border-radius(px) / values]
expected: FAIL
[box-shadow box-shadow(shadow) / values]
expected: FAIL
[border-top-right-radius border-radius(px-px) / values]
expected: FAIL
[display display(static to absolute) / events]
expected: FAIL

View file

@ -5,48 +5,6 @@
[background-position length-em(em) / values]
expected: FAIL
[border-top-width length-em(em) / values]
expected: FAIL
[max-width length-em(em) / values]
expected: FAIL
[line-height length-em(em) / values]
expected: FAIL
[vertical-align length-em(em) / values]
expected: FAIL
[min-height length-em(em) / values]
expected: FAIL
[border-left-width length-em(em) / values]
expected: FAIL
[word-spacing length-em(em) / values]
expected: FAIL
[outline-width length-em(em) / values]
expected: FAIL
[letter-spacing length-em(em) / values]
expected: FAIL
[max-height length-em(em) / values]
expected: FAIL
[min-width length-em(em) / values]
expected: FAIL
[text-indent length-em(em) / values]
expected: FAIL
[outline-offset length-em(em) / values]
expected: FAIL
[border-right-width length-em(em) / values]
expected: FAIL
[border-bottom-width length-em(em) / values]
[top length-em(em) / values]
expected: FAIL

View file

@ -56,3 +56,813 @@
[background-position length(px) / events]
expected: FAIL
[top length(pt) / events]
expected: FAIL
[border-right-width length(mm) / events]
expected: FAIL
[width length(pt) / events]
expected: FAIL
[border-bottom-width length(pc) / events]
expected: FAIL
[height length(ex) / events]
expected: FAIL
[padding-bottom length(mm) / events]
expected: FAIL
[letter-spacing length(em) / events]
expected: FAIL
[letter-spacing length(ex) / events]
expected: FAIL
[border-top-width length(px) / events]
expected: FAIL
[border-top-width length(pt) / events]
expected: FAIL
[padding-top length(em) / events]
expected: FAIL
[letter-spacing length(mm) / events]
expected: FAIL
[line-height length(in) / events]
expected: FAIL
[font-size percentage(%) / events]
expected: FAIL
[outline-width length(pc) / events]
expected: FAIL
[text-indent percentage(%) / events]
expected: FAIL
[max-width length(in) / events]
expected: FAIL
[margin-top length(cm) / events]
expected: FAIL
[margin-right length(pt) / events]
expected: FAIL
[right length(ex) / events]
expected: FAIL
[vertical-align length(px) / events]
expected: FAIL
[max-height length(em) / events]
expected: FAIL
[width length(ex) / events]
expected: FAIL
[border-bottom-color color(rgba) / events]
expected: FAIL
[width percentage(%) / events]
expected: FAIL
[margin-top length(ex) / events]
expected: FAIL
[border-top-width length(in) / events]
expected: FAIL
[right length(em) / events]
expected: FAIL
[top length(ex) / events]
expected: FAIL
[text-shadow shadow(shadow) / events]
expected: FAIL
[padding-left length(px) / events]
expected: FAIL
[margin-top length(em) / events]
expected: FAIL
[left length(em) / events]
expected: FAIL
[margin-left length(px) / events]
expected: FAIL
[max-height percentage(%) / events]
expected: FAIL
[bottom length(ex) / events]
expected: FAIL
[padding-left length(mm) / events]
expected: FAIL
[right length(mm) / events]
expected: FAIL
[margin-top length(pc) / events]
expected: FAIL
[border-left-width length(pt) / events]
expected: FAIL
[outline-offset length(mm) / events]
expected: FAIL
[vertical-align length(in) / events]
expected: FAIL
[border-bottom-width length(pt) / events]
expected: FAIL
[letter-spacing length(cm) / events]
expected: FAIL
[border-bottom-width length(in) / events]
expected: FAIL
[margin-bottom length(pc) / events]
expected: FAIL
[border-left-width length(mm) / events]
expected: FAIL
[height length(em) / events]
expected: FAIL
[letter-spacing length(px) / events]
expected: FAIL
[height length(cm) / events]
expected: FAIL
[padding-top length(in) / events]
expected: FAIL
[text-indent length(mm) / events]
expected: FAIL
[max-width length(ex) / events]
expected: FAIL
[padding-top length(px) / events]
expected: FAIL
[border-top-width length(pc) / events]
expected: FAIL
[top length(cm) / events]
expected: FAIL
[left length(ex) / events]
expected: FAIL
[min-width length(ex) / events]
expected: FAIL
[left length(pc) / events]
expected: FAIL
[font-size length(px) / events]
expected: FAIL
[vertical-align length(em) / events]
expected: FAIL
[left length(in) / events]
expected: FAIL
[left length(px) / events]
expected: FAIL
[border-bottom-width length(mm) / events]
expected: FAIL
[min-width length(cm) / events]
expected: FAIL
[vertical-align length(cm) / events]
expected: FAIL
[text-indent length(ex) / events]
expected: FAIL
[padding-bottom length(px) / events]
expected: FAIL
[line-height length(em) / events]
expected: FAIL
[max-width percentage(%) / events]
expected: FAIL
[outline-offset length(cm) / events]
expected: FAIL
[top length(in) / events]
expected: FAIL
[border-left-color color(rgba) / events]
expected: FAIL
[margin-left length(mm) / events]
expected: FAIL
[margin-top length(pt) / events]
expected: FAIL
[min-height length(cm) / events]
expected: FAIL
[max-height length(cm) / events]
expected: FAIL
[width length(mm) / events]
expected: FAIL
[width length(pc) / events]
expected: FAIL
[font-size length(pt) / events]
expected: FAIL
[font-size length(in) / events]
expected: FAIL
[vertical-align length(mm) / events]
expected: FAIL
[height length(px) / events]
expected: FAIL
[right percentage(%) / events]
expected: FAIL
[border-top-color color(rgba) / events]
expected: FAIL
[outline-offset length(em) / events]
expected: FAIL
[padding-right length(px) / events]
expected: FAIL
[vertical-align length(ex) / events]
expected: FAIL
[outline-width length(em) / events]
expected: FAIL
[width length(cm) / events]
expected: FAIL
[margin-right length(ex) / events]
expected: FAIL
[text-indent length(in) / events]
expected: FAIL
[border-right-width length(cm) / events]
expected: FAIL
[margin-right length(em) / events]
expected: FAIL
[letter-spacing length(in) / events]
expected: FAIL
[border-left-width length(ex) / events]
expected: FAIL
[min-width length(mm) / events]
expected: FAIL
[letter-spacing length(pc) / events]
expected: FAIL
[margin-right length(pc) / events]
expected: FAIL
[margin-left length(pt) / events]
expected: FAIL
[border-right-width length(pc) / events]
expected: FAIL
[height length(in) / events]
expected: FAIL
[border-left-width length(in) / events]
expected: FAIL
[min-height length(ex) / events]
expected: FAIL
[text-indent length(pc) / events]
expected: FAIL
[text-indent length(em) / events]
expected: FAIL
[bottom length(em) / events]
expected: FAIL
[border-right-color color(rgba) / events]
expected: FAIL
[font-size length(pc) / events]
expected: FAIL
[line-height length(pc) / events]
expected: FAIL
[right length(in) / events]
expected: FAIL
[padding-top length(pt) / events]
expected: FAIL
[min-width length(em) / events]
expected: FAIL
[border-right-width length(pt) / events]
expected: FAIL
[opacity number[0,1\](zero-to-one) / events]
expected: FAIL
[padding-bottom length(cm) / events]
expected: FAIL
[margin-right length(in) / events]
expected: FAIL
[margin-top length(px) / events]
expected: FAIL
[padding-top length(ex) / events]
expected: FAIL
[margin-left length(ex) / events]
expected: FAIL
[text-indent length(px) / events]
expected: FAIL
[text-indent length(cm) / events]
expected: FAIL
[top length(px) / events]
expected: FAIL
[bottom length(cm) / events]
expected: FAIL
[border-bottom-width length(px) / events]
expected: FAIL
[margin-bottom length(ex) / events]
expected: FAIL
[font-weight font-weight(keyword) / events]
expected: FAIL
[font-weight font-weight(numeric) / events]
expected: FAIL
[max-width length(mm) / events]
expected: FAIL
[line-height length(mm) / events]
expected: FAIL
[height percentage(%) / events]
expected: FAIL
[border-right-width length(px) / events]
expected: FAIL
[border-top-width length(mm) / events]
expected: FAIL
[line-height length(cm) / events]
expected: FAIL
[word-spacing length(pt) / events]
expected: FAIL
[border-left-width length(em) / events]
expected: FAIL
[max-width length(pc) / events]
expected: FAIL
[padding-bottom length(em) / events]
expected: FAIL
[min-width length(pt) / events]
expected: FAIL
[outline-width length(cm) / events]
expected: FAIL
[border-right-width length(in) / events]
expected: FAIL
[bottom length(in) / events]
expected: FAIL
[padding-right length(mm) / events]
expected: FAIL
[line-height length(pt) / events]
expected: FAIL
[border-left-width length(px) / events]
expected: FAIL
[left length(cm) / events]
expected: FAIL
[word-spacing length(ex) / events]
expected: FAIL
[margin-right length(px) / events]
expected: FAIL
[clip rectangle(rectangle) / events]
expected: FAIL
[color color(rgba) / events]
expected: FAIL
[margin-left length(cm) / events]
expected: FAIL
[background-color color(rgba) / events]
expected: FAIL
[min-width length(in) / events]
expected: FAIL
[word-spacing percentage(%) / events]
expected: FAIL
[padding-right length(cm) / events]
expected: FAIL
[width length(em) / events]
expected: FAIL
[line-height percentage(%) / events]
expected: FAIL
[margin-left length(em) / events]
expected: FAIL
[margin-right length(cm) / events]
expected: FAIL
[border-bottom-width length(cm) / events]
expected: FAIL
[bottom length(pc) / events]
expected: FAIL
[text-indent length(pt) / events]
expected: FAIL
[top percentage(%) / events]
expected: FAIL
[padding-left length(pt) / events]
expected: FAIL
[min-width percentage(%) / events]
expected: FAIL
[right length(px) / events]
expected: FAIL
[padding-right length(in) / events]
expected: FAIL
[left length(mm) / events]
expected: FAIL
[line-height length(ex) / events]
expected: FAIL
[bottom length(px) / events]
expected: FAIL
[line-height number(integer) / events]
expected: FAIL
[padding-left length(pc) / events]
expected: FAIL
[outline-width length(ex) / events]
expected: FAIL
[padding-left length(cm) / events]
expected: FAIL
[left length(pt) / events]
expected: FAIL
[outline-width length(mm) / events]
expected: FAIL
[padding-left length(ex) / events]
expected: FAIL
[word-spacing length(cm) / events]
expected: FAIL
[font-size length(mm) / events]
expected: FAIL
[margin-bottom length(cm) / events]
expected: FAIL
[word-spacing length(mm) / events]
expected: FAIL
[max-width length(pt) / events]
expected: FAIL
[border-right-width length(em) / events]
expected: FAIL
[font-size length(cm) / events]
expected: FAIL
[margin-left length(in) / events]
expected: FAIL
[top length(em) / events]
expected: FAIL
[padding-right length(pt) / events]
expected: FAIL
[padding-top length(pc) / events]
expected: FAIL
[border-top-width length(ex) / events]
expected: FAIL
[min-height length(pt) / events]
expected: FAIL
[width length(in) / events]
expected: FAIL
[min-width length(pc) / events]
expected: FAIL
[margin-bottom length(mm) / events]
expected: FAIL
[height length(pc) / events]
expected: FAIL
[max-width length(px) / events]
expected: FAIL
[bottom length(pt) / events]
expected: FAIL
[padding-right length(ex) / events]
expected: FAIL
[border-top-width length(em) / events]
expected: FAIL
[max-height length(pt) / events]
expected: FAIL
[vertical-align percentage(%) / events]
expected: FAIL
[word-spacing length(em) / events]
expected: FAIL
[margin-left length(pc) / events]
expected: FAIL
[min-height percentage(%) / events]
expected: FAIL
[padding-bottom length(ex) / events]
expected: FAIL
[line-height number(decimal) / events]
expected: FAIL
[padding-left length(em) / events]
expected: FAIL
[letter-spacing length(pt) / events]
expected: FAIL
[bottom percentage(%) / events]
expected: FAIL
[min-height length(pc) / events]
expected: FAIL
[border-left-width length(pc) / events]
expected: FAIL
[margin-bottom length(in) / events]
expected: FAIL
[outline-width length(pt) / events]
expected: FAIL
[margin-bottom length(em) / events]
expected: FAIL
[top length(mm) / events]
expected: FAIL
[outline-color color(rgba) / events]
expected: FAIL
[bottom length(mm) / events]
expected: FAIL
[padding-top length(mm) / events]
expected: FAIL
[border-right-width length(ex) / events]
expected: FAIL
[border-top-width length(cm) / events]
expected: FAIL
[outline-width length(in) / events]
expected: FAIL
[right length(pt) / events]
expected: FAIL
[width length(px) / events]
expected: FAIL
[height length(pt) / events]
expected: FAIL
[word-spacing length(in) / events]
expected: FAIL
[padding-left length(in) / events]
expected: FAIL
[min-height length(em) / events]
expected: FAIL
[left percentage(%) / events]
expected: FAIL
[word-spacing length(px) / events]
expected: FAIL
[padding-bottom length(pc) / events]
expected: FAIL
[max-height length(ex) / events]
expected: FAIL
[border-left-width length(cm) / events]
expected: FAIL
[outline-offset length(ex) / events]
expected: FAIL
[max-height length(pc) / events]
expected: FAIL
[padding-right length(pc) / events]
expected: FAIL
[top length(pc) / events]
expected: FAIL
[vertical-align length(pc) / events]
expected: FAIL
[min-height length(px) / events]
expected: FAIL
[margin-right length(mm) / events]
expected: FAIL
[max-height length(mm) / events]
expected: FAIL
[right length(pc) / events]
expected: FAIL
[line-height length(px) / events]
expected: FAIL
[word-spacing length(pc) / events]
expected: FAIL
[vertical-align length(pt) / events]
expected: FAIL
[padding-top length(cm) / events]
expected: FAIL
[font-size length(em) / events]
expected: FAIL
[right length(cm) / events]
expected: FAIL
[outline-offset length(pt) / events]
expected: FAIL
[font-size length(ex) / events]
expected: FAIL
[min-height length(in) / events]
expected: FAIL
[max-width length(cm) / events]
expected: FAIL
[border-bottom-width length(ex) / events]
expected: FAIL
[max-height length(px) / events]
expected: FAIL
[min-width length(px) / events]
expected: FAIL
[outline-offset length(px) / events]
expected: FAIL
[margin-top length(in) / events]
expected: FAIL
[outline-offset length(in) / events]
expected: FAIL
[margin-bottom length(pt) / events]
expected: FAIL
[max-width length(em) / events]
expected: FAIL
[padding-bottom length(pt) / events]
expected: FAIL
[min-height length(mm) / events]
expected: FAIL
[padding-bottom length(in) / events]
expected: FAIL
[margin-bottom length(px) / events]
expected: FAIL
[max-height length(in) / events]
expected: FAIL
[outline-width length(px) / events]
expected: FAIL
[padding-right length(em) / events]
expected: FAIL
[height length(mm) / events]
expected: FAIL
[margin-top length(mm) / events]
expected: FAIL
[outline-offset length(pc) / events]
expected: FAIL
[border-bottom-width length(em) / events]
expected: FAIL
[z-index integer(integer) / events]
expected: FAIL

View file

@ -53,459 +53,3 @@
[background-position length(px) / events]
expected: FAIL
[outline-width length(px) / values]
expected: FAIL
[border-bottom-width length(px) / values]
expected: FAIL
[border-top-width length(em) / values]
expected: FAIL
[line-height number(decimal) / values]
expected: FAIL
[opacity number[0,1\](zero-to-one) / values]
expected: FAIL
[line-height number(integer) / values]
expected: FAIL
[outline-offset length(px) / values]
expected: FAIL
[font-size length(pt) / values]
expected: FAIL
[max-height length(px) / values]
expected: FAIL
[vertical-align length(in) / values]
expected: FAIL
[max-height percentage(%) / values]
expected: FAIL
[min-width length(px) / values]
expected: FAIL
[font-size length(ex) / values]
expected: FAIL
[min-height length(pc) / values]
expected: FAIL
[line-height length(px) / values]
expected: FAIL
[word-spacing length(px) / values]
expected: FAIL
[vertical-align percentage(%) / values]
expected: FAIL
[border-right-width length(pc) / values]
expected: FAIL
[line-height length(ex) / values]
expected: FAIL
[color color(rgba) / values]
expected: FAIL
[font-size length(in) / values]
expected: FAIL
[font-size length(em) / values]
expected: FAIL
[vertical-align length(em) / values]
expected: FAIL
[clip rectangle(rectangle) / values]
expected: FAIL
[word-spacing length(em) / values]
expected: FAIL
[border-right-width length(ex) / values]
expected: FAIL
[border-top-width length(cm) / values]
expected: FAIL
[max-width length(em) / values]
expected: FAIL
[font-size length(cm) / values]
expected: FAIL
[border-right-width length(cm) / values]
expected: FAIL
[outline-offset length(ex) / values]
expected: FAIL
[min-width length(cm) / values]
expected: FAIL
[min-height length(px) / values]
expected: FAIL
[min-width length(mm) / values]
expected: FAIL
[border-bottom-width length(in) / values]
expected: FAIL
[vertical-align length(pc) / values]
expected: FAIL
[vertical-align length(cm) / values]
expected: FAIL
[max-height length(ex) / values]
expected: FAIL
[vertical-align length(ex) / values]
expected: FAIL
[min-height percentage(%) / values]
expected: FAIL
[max-height length(pt) / values]
expected: FAIL
[outline-offset length(mm) / values]
expected: FAIL
[font-weight font-weight(keyword) / values]
expected: FAIL
[max-height length(mm) / values]
expected: FAIL
[outline-offset length(pt) / values]
expected: FAIL
[line-height length(mm) / values]
expected: FAIL
[line-height length(cm) / values]
expected: FAIL
[border-top-width length(mm) / values]
expected: FAIL
[letter-spacing length(in) / values]
expected: FAIL
[min-width percentage(%) / values]
expected: FAIL
[min-height length(cm) / values]
expected: FAIL
[letter-spacing length(mm) / values]
expected: FAIL
[font-size percentage(%) / values]
expected: FAIL
[letter-spacing length(cm) / values]
expected: FAIL
[vertical-align length(pt) / values]
expected: FAIL
[letter-spacing length(pc) / values]
expected: FAIL
[letter-spacing length(pt) / values]
expected: FAIL
[word-spacing length(ex) / values]
expected: FAIL
[line-height length(pt) / values]
expected: FAIL
[border-top-width length(px) / values]
expected: FAIL
[min-width length(pt) / values]
expected: FAIL
[border-bottom-width length(mm) / values]
expected: FAIL
[border-bottom-width length(cm) / values]
expected: FAIL
[min-width length(em) / values]
expected: FAIL
[min-height length(em) / values]
expected: FAIL
[max-width length(mm) / values]
expected: FAIL
[min-height length(ex) / values]
expected: FAIL
[max-width length(px) / values]
expected: FAIL
[font-size length(mm) / values]
expected: FAIL
[min-width length(pc) / values]
expected: FAIL
[letter-spacing length(em) / values]
expected: FAIL
[line-height length(em) / values]
expected: FAIL
[border-bottom-width length(pt) / values]
expected: FAIL
[word-spacing length(pc) / values]
expected: FAIL
[outline-offset length(in) / values]
expected: FAIL
[outline-width length(pt) / values]
expected: FAIL
[border-top-width length(pc) / values]
expected: FAIL
[font-size length(px) / values]
expected: FAIL
[border-right-width length(px) / values]
expected: FAIL
[outline-width length(in) / values]
expected: FAIL
[word-spacing length(pt) / values]
expected: FAIL
[border-right-width length(mm) / values]
expected: FAIL
[max-width length(in) / values]
expected: FAIL
[outline-color color(rgba) / values]
expected: FAIL
[border-right-width length(pt) / values]
expected: FAIL
[max-height length(in) / values]
expected: FAIL
[line-height length(in) / values]
expected: FAIL
[border-bottom-width length(em) / values]
expected: FAIL
[outline-width length(ex) / values]
expected: FAIL
[font-size length(pc) / values]
expected: FAIL
[min-width length(in) / values]
expected: FAIL
[outline-width length(cm) / values]
expected: FAIL
[max-width percentage(%) / values]
expected: FAIL
[max-width length(ex) / values]
expected: FAIL
[letter-spacing length(ex) / values]
expected: FAIL
[outline-width length(mm) / values]
expected: FAIL
[outline-width length(pc) / values]
expected: FAIL
[font-weight font-weight(numeric) / values]
expected: FAIL
[vertical-align length(px) / values]
expected: FAIL
[letter-spacing length(px) / values]
expected: FAIL
[max-width length(pt) / values]
expected: FAIL
[line-height percentage(%) / values]
expected: FAIL
[border-top-width length(pt) / values]
expected: FAIL
[min-height length(mm) / values]
expected: FAIL
[max-height length(cm) / values]
expected: FAIL
[outline-width length(em) / values]
expected: FAIL
[border-right-width length(em) / values]
expected: FAIL
[max-height length(em) / values]
expected: FAIL
[max-width length(cm) / values]
expected: FAIL
[outline-offset length(em) / values]
expected: FAIL
[outline-offset length(cm) / values]
expected: FAIL
[border-top-width length(ex) / values]
expected: FAIL
[border-right-width length(in) / values]
expected: FAIL
[z-index integer(integer) / values]
expected: FAIL
[vertical-align length(mm) / values]
expected: FAIL
[border-bottom-width length(pc) / values]
expected: FAIL
[line-height length(pc) / values]
expected: FAIL
[border-top-width length(in) / values]
expected: FAIL
[border-bottom-width length(ex) / values]
expected: FAIL
[min-height length(in) / values]
expected: FAIL
[outline-offset length(pc) / values]
expected: FAIL
[max-height length(pc) / values]
expected: FAIL
[background-color color(rgba) / values]
expected: FAIL
[min-height length(pt) / values]
expected: FAIL
[max-width length(pc) / values]
expected: FAIL
[min-width length(ex) / values]
expected: FAIL
[word-spacing length(mm) / values]
expected: FAIL
[word-spacing length(in) / values]
expected: FAIL
[word-spacing length(cm) / values]
expected: FAIL
[text-indent length(pc) / values]
expected: FAIL
[text-indent length(ex) / values]
expected: FAIL
[text-indent length(px) / values]
expected: FAIL
[text-indent length(mm) / values]
expected: FAIL
[text-indent length(cm) / values]
expected: FAIL
[text-indent length(pt) / values]
expected: FAIL
[text-shadow shadow(shadow) / values]
expected: FAIL
[word-spacing percentage(%) / values]
expected: FAIL
[text-indent length(in) / values]
expected: FAIL
[text-indent length(em) / values]
expected: FAIL
[text-indent percentage(%) / values]
expected: FAIL
[border-left-width length(em) / values]
expected: FAIL
[border-top-color color(rgba) / values]
expected: FAIL
[border-right-color color(rgba) / values]
expected: FAIL
[border-left-width length(mm) / values]
expected: FAIL
[border-bottom-color color(rgba) / values]
expected: FAIL
[border-left-color color(rgba) / values]
expected: FAIL
[border-left-width length(px) / values]
expected: FAIL
[border-left-width length(cm) / values]
expected: FAIL
[border-left-width length(in) / values]
expected: FAIL
[border-left-width length(ex) / values]
expected: FAIL
[border-left-width length(pc) / values]
expected: FAIL
[border-left-width length(pt) / values]
expected: FAIL

View file

@ -5,3 +5,90 @@
[background-position length-em(em) / values]
expected: FAIL
[line-height length-em(em) / events]
expected: FAIL
[padding-left length-em(em) / events]
expected: FAIL
[min-width length-em(em) / events]
expected: FAIL
[right length-em(em) / events]
expected: FAIL
[max-width length-em(em) / events]
expected: FAIL
[text-indent length-em(em) / events]
expected: FAIL
[margin-left length-em(em) / events]
expected: FAIL
[word-spacing length-em(em) / events]
expected: FAIL
[letter-spacing length-em(em) / events]
expected: FAIL
[width length-em(em) / events]
expected: FAIL
[padding-top length-em(em) / events]
expected: FAIL
[margin-right length-em(em) / events]
expected: FAIL
[max-height length-em(em) / events]
expected: FAIL
[padding-bottom length-em(em) / events]
expected: FAIL
[border-top-width length-em(em) / events]
expected: FAIL
[height length-em(em) / events]
expected: FAIL
[border-bottom-width length-em(em) / events]
expected: FAIL
[border-left-width length-em(em) / events]
expected: FAIL
[margin-bottom length-em(em) / events]
expected: FAIL
[min-height length-em(em) / events]
expected: FAIL
[outline-offset length-em(em) / events]
expected: FAIL
[padding-right length-em(em) / events]
expected: FAIL
[top length-em(em) / events]
expected: FAIL
[outline-width length-em(em) / events]
expected: FAIL
[bottom length-em(em) / events]
expected: FAIL
[vertical-align length-em(em) / events]
expected: FAIL
[left length-em(em) / events]
expected: FAIL
[margin-top length-em(em) / events]
expected: FAIL
[border-right-width length-em(em) / events]
expected: FAIL

View file

@ -3,6 +3,3 @@
[Verify border-bottom-color before animation]
expected: FAIL
[Verify border-bottom-color after animation]
expected: FAIL

View file

@ -3,6 +3,3 @@
[Verify color before animation]
expected: FAIL
[Verify color after animation]
expected: FAIL

View file

@ -12871,7 +12871,7 @@
]
],
"transition-raf.html": [
"6159bb9ab333544b4485d11025889ee94186c7eb",
"c38404503408e04b3c75b42df18ec3a7ec0819f5",
[
null,
{}

View file

@ -1,3 +0,0 @@
[basic-transition.html]
type: reftest
disabled: https://github.com/servo/servo/issues/13865

View file

@ -1,3 +0,0 @@
[transition-raf.html]
type: testharness
disabled: https://github.com/servo/servo/issues/13865

View file

@ -34,7 +34,7 @@ async_test(function(t) {
assert_equals(getComputedStyle(box).getPropertyValue('width'), '100px');
box.className = "expose";
// Let the first restyle run at zero, then advance the clock.
setTimeout(function() { test.advanceClock(500, false) }, 0);
setTimeout(function() { test.advanceClock(500) }, 0);
});
}, "Transitions should work during RAF loop")
</script>