Skip to content

Commit

Permalink
layout: Implement CSS transitions per CSS-TRANSITIONS § 2.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pcwalton committed Mar 31, 2015
1 parent c1cc31b commit 66dd8c8
Show file tree
Hide file tree
Showing 31 changed files with 1,598 additions and 219 deletions.
64 changes: 48 additions & 16 deletions components/compositing/compositor.rs
Expand Up @@ -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 gleam::gl::types::{GLint, GLsizei};
use gleam::gl;
use layers::geometry::{DevicePixel, LayerPixel};
use layers::layers::{BufferRequest, Layer, LayerBuffer, LayerBufferSet};
use layers::rendergl;
use layers::rendergl::RenderContext;
use layers::rendergl;
use layers::scene::Scene;
use png;
use gleam::gl::types::{GLint, GLsizei};
use gleam::gl;
use script_traits::{ConstellationControlMsg, ScriptControlChan};
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;
Expand Down Expand Up @@ -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 {
Expand All @@ -172,6 +175,7 @@ impl PipelineDetails {
pipeline: None,
ready_state: ReadyState::Blank,
paint_state: PaintState::Painting,
animations_running: false,
}
}
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions components/compositing/compositor_task.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -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"),
Expand Down
70 changes: 51 additions & 19 deletions components/compositing/constellation.rs
Expand Up @@ -11,33 +11,33 @@ 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};
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> {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -321,14 +323,23 @@ 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,
old_subpage_id,
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.
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
1 change: 1 addition & 0 deletions components/compositing/headless.rs
Expand Up @@ -98,6 +98,7 @@ impl CompositorEventListener for NullCompositor {
Msg::AssignPaintedBuffers(..) |
Msg::ChangeReadyState(..) |
Msg::ChangePaintState(..) |
Msg::ChangeRunningAnimationsState(..) |
Msg::ScrollFragmentPoint(..) |
Msg::LoadComplete |
Msg::PaintMsgDiscarded(..) |
Expand Down
11 changes: 9 additions & 2 deletions components/compositing/pipeline.rs
Expand Up @@ -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<()>,
Expand All @@ -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>,
}

Expand Down Expand Up @@ -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(),
};

Expand Down Expand Up @@ -186,6 +192,7 @@ impl Pipeline {
title: None,
children: vec!(),
rect: rect,
running_animations: false,
}
}

Expand Down
10 changes: 6 additions & 4 deletions components/gfx/paint_task.rs
Expand Up @@ -330,13 +330,13 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
})
}

/// Paints one layer and sends the tiles back to the layer.
/// Paints one layer and places the painted tiles in `replies`.
fn paint(&mut self,
replies: &mut Vec<(LayerId, Box<LayerBufferSet>)>,
mut tiles: Vec<BufferRequest>,
scale: f32,
layer_id: LayerId) {
profile(time::ProfilerCategory::Painting, None, self.time_profiler_chan.clone(), || {
time::profile(time::ProfilerCategory::Painting, None, self.time_profiler_chan.clone(), || {
// Bail out if there is no appropriate stacking context.
let stacking_context = if let Some(ref stacking_context) = self.root_stacking_context {
match display_list::find_stacking_context_with_layer_id(stacking_context,
Expand Down Expand Up @@ -559,8 +559,10 @@ impl WorkerThread {
paint_context.clear();

// Draw the display list.
profile(time::ProfilerCategory::PaintingPerTile, None,
self.time_profiler_sender.clone(), || {
time::profile(time::ProfilerCategory::PaintingPerTile,
None,
self.time_profiler_sender.clone(),
|| {
stacking_context.optimize_and_draw_into_context(&mut paint_context,
&tile_bounds,
&matrix,
Expand Down

5 comments on commit 66dd8c8

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from glennw
at pcwalton@66dd8c8

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging pcwalton/servo/transitions-redux = 66dd8c8 into auto

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pcwalton/servo/transitions-redux = 66dd8c8 merged ok, testing candidate = ebdf1d4

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = ebdf1d4

Please sign in to comment.