layout: Implement CSS transitions per CSS-TRANSITIONS § 2.

Transition events are not yet supported, and the only animatable
properties are `top`, `right`, `bottom`, and `left`. However, all other
features of transitions are supported. There are no automated tests at
present because I'm not sure how best to test it, but three manual tests
are included.
This commit is contained in:
Patrick Walton 2015-03-23 13:32:49 -07:00
parent c1cc31b9d6
commit 66dd8c8a6c
31 changed files with 1603 additions and 224 deletions

View file

@ -13,32 +13,32 @@ use windowing::{MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}
use geom::point::{Point2D, TypedPoint2D};
use geom::rect::{Rect, TypedRect};
use geom::size::{Size2D, TypedSize2D};
use geom::scale_factor::ScaleFactor;
use geom::size::{Size2D, TypedSize2D};
use gfx::color;
use gfx::paint_task::Msg as PaintMsg;
use gfx::paint_task::PaintRequest;
use layers::geometry::{DevicePixel, LayerPixel};
use layers::layers::{BufferRequest, Layer, LayerBuffer, LayerBufferSet};
use layers::rendergl;
use layers::rendergl::RenderContext;
use layers::scene::Scene;
use png;
use gleam::gl::types::{GLint, GLsizei};
use gleam::gl;
use script_traits::{ConstellationControlMsg, ScriptControlChan};
use layers::geometry::{DevicePixel, LayerPixel};
use layers::layers::{BufferRequest, Layer, LayerBuffer, LayerBufferSet};
use layers::rendergl::RenderContext;
use layers::rendergl;
use layers::scene::Scene;
use msg::compositor_msg::{Epoch, LayerId};
use msg::compositor_msg::{ReadyState, PaintState, ScrollPolicy};
use msg::constellation_msg::{ConstellationChan, NavigationDirection};
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, NavigationDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData};
use msg::constellation_msg::{PipelineId, WindowSizeData};
use png;
use profile::mem;
use profile::time::{self, ProfilerCategory, profile};
use script_traits::{ConstellationControlMsg, ScriptControlChan};
use std::cmp;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::mem::replace;
use std::mem as std_mem;
use std::num::Float;
use std::rc::Rc;
use std::slice::bytes::copy_memory;
@ -164,6 +164,9 @@ struct PipelineDetails {
/// The status of this pipeline's PaintTask.
paint_state: PaintState,
/// Whether animations are running.
animations_running: bool,
}
impl PipelineDetails {
@ -172,6 +175,7 @@ impl PipelineDetails {
pipeline: None,
ready_state: ReadyState::Blank,
paint_state: PaintState::Painting,
animations_running: false,
}
}
}
@ -272,6 +276,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.change_paint_state(pipeline_id, paint_state);
}
(Msg::ChangeRunningAnimationsState(pipeline_id, running_animations),
ShutdownState::NotShuttingDown) => {
self.change_running_animations_state(pipeline_id, running_animations);
}
(Msg::ChangePageTitle(pipeline_id, title), ShutdownState::NotShuttingDown) => {
self.change_page_title(pipeline_id, title);
}
@ -311,7 +320,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.set_layer_rect(pipeline_id, layer_id, &rect);
}
(Msg::AssignPaintedBuffers(pipeline_id, epoch, replies), ShutdownState::NotShuttingDown) => {
(Msg::AssignPaintedBuffers(pipeline_id, epoch, replies),
ShutdownState::NotShuttingDown) => {
for (layer_id, new_layer_buffer_set) in replies.into_iter() {
self.assign_painted_buffers(pipeline_id, layer_id, new_layer_buffer_set, epoch);
}
@ -399,6 +409,18 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.window.set_paint_state(paint_state);
}
/// Sets or unsets the animations-running flag for the given pipeline, and schedules a
/// recomposite if necessary.
fn change_running_animations_state(&mut self,
pipeline_id: PipelineId,
animations_running: bool) {
self.get_or_create_pipeline_details(pipeline_id).animations_running = animations_running;
if animations_running {
self.composite_if_necessary();
}
}
pub fn get_or_create_pipeline_details<'a>(&'a mut self,
pipeline_id: PipelineId)
-> &'a mut PipelineDetails {
@ -867,15 +889,13 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn process_pending_scroll_events(&mut self) {
let had_scroll_events = self.pending_scroll_events.len() > 0;
for scroll_event in replace(&mut self.pending_scroll_events, Vec::new()).into_iter() {
for scroll_event in std_mem::replace(&mut self.pending_scroll_events,
Vec::new()).into_iter() {
let delta = scroll_event.delta / self.scene.scale;
let cursor = scroll_event.cursor.as_f32() / self.scene.scale;
match self.scene.root {
Some(ref mut layer) => {
layer.handle_scroll_event(delta, cursor);
}
None => {}
if let Some(ref mut layer) = self.scene.root {
layer.handle_scroll_event(delta, cursor);
}
self.start_scrolling_timer_if_necessary();
@ -887,6 +907,16 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
/// If there are any animations running, dispatches appropriate messages to the constellation.
fn process_animations(&mut self) {
for (pipeline_id, pipeline_details) in self.pipeline_details.iter() {
if !pipeline_details.animations_running {
continue
}
self.constellation_chan.0.send(ConstellationMsg::TickAnimation(*pipeline_id)).unwrap();
}
}
fn device_pixels_per_screen_px(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
match opts::get().device_pixels_per_px {
Some(device_pixels_per_px) => device_pixels_per_px,
@ -1086,7 +1116,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let mut framebuffer_ids = vec!();
let mut texture_ids = vec!();
let (width, height) = (self.window_size.width.get() as usize, self.window_size.height.get() as usize);
let (width, height) =
(self.window_size.width.get() as usize, self.window_size.height.get() as usize);
if output_image {
framebuffer_ids = gl::gen_framebuffers(1);
@ -1172,6 +1203,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.composition_request = CompositionRequest::NoCompositingNecessary;
self.process_pending_scroll_events();
self.process_animations();
}
fn composite_if_necessary(&mut self) {

View file

@ -201,6 +201,8 @@ pub enum Msg {
ChangePageTitle(PipelineId, Option<String>),
/// Alerts the compositor that the current page has changed its URL.
ChangePageUrl(PipelineId, Url),
/// Alerts the compositor that the given pipeline has changed whether it is running animations.
ChangeRunningAnimationsState(PipelineId, bool),
/// Alerts the compositor that a `PaintMsg` has been discarded.
PaintMsgDiscarded,
/// Replaces the current frame tree, typically called during main frame navigation.
@ -231,6 +233,7 @@ impl Debug for Msg {
Msg::AssignPaintedBuffers(..) => write!(f, "AssignPaintedBuffers"),
Msg::ChangeReadyState(..) => write!(f, "ChangeReadyState"),
Msg::ChangePaintState(..) => write!(f, "ChangePaintState"),
Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"),
Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"),
Msg::ChangePageUrl(..) => write!(f, "ChangePageUrl"),
Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"),

View file

@ -11,26 +11,22 @@ use geom::point::Point2D;
use geom::rect::{Rect, TypedRect};
use geom::scale_factor::ScaleFactor;
use gfx::font_cache_task::FontCacheTask;
use layout_traits::LayoutTaskFactory;
use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
use libc;
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{ScriptControlChan, ScriptTaskFactory};
use msg::compositor_msg::LayerId;
use msg::constellation_msg::{self, ConstellationChan, Failure};
use msg::constellation_msg::{IFrameSandboxState, NavigationDirection};
use msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData};
use msg::constellation_msg::{FrameId, PipelineExitType, PipelineId};
use msg::constellation_msg::{SubpageId, WindowSizeData, MozBrowserEvent};
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{FrameId, PipelineExitType, PipelineId};
use msg::constellation_msg::{IFrameSandboxState, MozBrowserEvent, NavigationDirection};
use msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData};
use msg::constellation_msg::{SubpageId, WindowSizeData};
use msg::constellation_msg::{self, ConstellationChan, Failure};
use net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use net::resource_task::{self, ResourceTask};
use net::storage_task::{StorageTask, StorageTaskMsg};
use profile::mem;
use profile::time;
use util::cursor::Cursor;
use util::geometry::PagePx;
use util::opts;
use util::task::spawn_named;
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{ScriptControlChan, ScriptTaskFactory};
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::io::{self, Write};
@ -38,6 +34,10 @@ use std::marker::PhantomData;
use std::mem::replace;
use std::sync::mpsc::{Receiver, channel};
use url::Url;
use util::cursor::Cursor;
use util::geometry::PagePx;
use util::opts;
use util::task::spawn_named;
/// Maintains the pipelines and navigation context and grants permission to composite.
pub struct Constellation<LTF, STF> {
@ -201,8 +201,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
time_profiler_chan: time_profiler_chan,
mem_profiler_chan: mem_profiler_chan,
window_size: WindowSizeData {
visible_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor::new(1.0),
initial_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor::new(1.0),
visible_viewport: opts::get().initial_window_size.as_f32() *
ScaleFactor::new(1.0),
initial_viewport: opts::get().initial_window_size.as_f32() *
ScaleFactor::new(1.0),
device_pixel_ratio: ScaleFactor::new(1.0),
},
phantom: PhantomData,
@ -321,7 +323,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
new_subpage_id,
old_subpage_id,
sandbox) => {
debug!("constellation got iframe URL load message {:?} {:?} {:?}", source_pipeline_id, old_subpage_id, new_subpage_id);
debug!("constellation got iframe URL load message {:?} {:?} {:?}",
source_pipeline_id,
old_subpage_id,
new_subpage_id);
self.handle_script_loaded_url_in_iframe_msg(url,
source_pipeline_id,
new_subpage_id,
@ -329,6 +334,12 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
sandbox);
}
ConstellationMsg::SetCursor(cursor) => self.handle_set_cursor_msg(cursor),
ConstellationMsg::ChangeRunningAnimationsState(pipeline_id, animations_running) => {
self.handle_change_running_animations_state(pipeline_id, animations_running)
}
ConstellationMsg::TickAnimation(pipeline_id) => {
self.handle_tick_animation(pipeline_id)
}
// Load a new page, usually -- but not always -- from a mouse click or typed url
// If there is already a pending page (self.pending_frames), it will not be overridden;
// However, if the id is not encompassed by another change, it will be.
@ -420,15 +431,19 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
debug!("creating replacement pipeline for about:failure");
let window_rect = self.pipeline(pipeline_id).rect;
let new_pipeline_id = self.new_pipeline(parent_info, window_rect, None,
LoadData::new(Url::parse("about:failure").unwrap()));
let new_pipeline_id =
self.new_pipeline(parent_info,
window_rect,
None,
LoadData::new(Url::parse("about:failure").unwrap()));
self.push_pending_frame(new_pipeline_id, Some(pipeline_id));
}
fn handle_init_load(&mut self, url: Url) {
let window_rect = Rect(Point2D::zero(), self.window_size.visible_viewport);
let root_pipeline_id = self.new_pipeline(None, Some(window_rect), None, LoadData::new(url));
let root_pipeline_id =
self.new_pipeline(None, Some(window_rect), None, LoadData::new(url));
self.push_pending_frame(root_pipeline_id, None);
}
@ -509,6 +524,21 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.compositor_proxy.send(CompositorMsg::SetCursor(cursor))
}
fn handle_change_running_animations_state(&mut self,
pipeline_id: PipelineId,
animations_running: bool) {
self.compositor_proxy.send(CompositorMsg::ChangeRunningAnimationsState(pipeline_id,
animations_running))
}
fn handle_tick_animation(&mut self, pipeline_id: PipelineId) {
self.pipeline(pipeline_id)
.layout_chan
.0
.send(LayoutControlMsg::TickAnimationsMsg)
.unwrap();
}
fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) {
// If this load targets an iframe, its framing element may exist
// in a separate script task than the framed document that initiated
@ -907,7 +937,9 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
fn find_subpage(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId) -> &mut Pipeline {
let pipeline_id = *self.subpage_map.get(&(containing_pipeline_id, subpage_id)).expect("no subpage pipeline_id");
let pipeline_id = *self.subpage_map
.get(&(containing_pipeline_id, subpage_id))
.expect("no subpage pipeline_id");
self.mut_pipeline(pipeline_id)
}
}

View file

@ -98,6 +98,7 @@ impl CompositorEventListener for NullCompositor {
Msg::AssignPaintedBuffers(..) |
Msg::ChangeReadyState(..) |
Msg::ChangePaintState(..) |
Msg::ChangeRunningAnimationsState(..) |
Msg::ScrollFragmentPoint(..) |
Msg::LoadComplete |
Msg::PaintMsgDiscarded(..) |

View file

@ -31,6 +31,7 @@ pub struct Pipeline {
pub id: PipelineId,
pub parent_info: Option<(PipelineId, SubpageId)>,
pub script_chan: ScriptControlChan,
/// A channel to layout, for performing reflows and shutdown.
pub layout_chan: LayoutControlChan,
pub paint_chan: PaintChan,
pub layout_shutdown_port: Receiver<()>,
@ -40,6 +41,9 @@ pub struct Pipeline {
/// The title of the most recently-loaded page.
pub title: Option<String>,
pub rect: Option<TypedRect<PagePx, f32>>,
/// Whether this pipeline is currently running animations. Pipelines that are running
/// animations cause composites to be continually scheduled.
pub running_animations: bool,
pub children: Vec<FrameId>,
}
@ -113,12 +117,14 @@ impl Pipeline {
ScriptControlChan(script_chan)
}
Some(script_chan) => {
let (containing_pipeline_id, subpage_id) = parent_info.expect("script_pipeline != None but subpage_id == None");
let (containing_pipeline_id, subpage_id) =
parent_info.expect("script_pipeline != None but subpage_id == None");
let new_layout_info = NewLayoutInfo {
containing_pipeline_id: containing_pipeline_id,
new_pipeline_id: id,
subpage_id: subpage_id,
layout_chan: ScriptTaskFactory::clone_layout_channel(None::<&mut STF>, &layout_pair),
layout_chan: ScriptTaskFactory::clone_layout_channel(None::<&mut STF>,
&layout_pair),
load_data: load_data.clone(),
};
@ -186,6 +192,7 @@ impl Pipeline {
title: None,
children: vec!(),
rect: rect,
running_animations: false,
}
}