aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-10-19 09:23:18 -0700
committerPatrick Walton <pcwalton@mimiga.net>2014-11-04 13:51:56 -0800
commit10f7b49cf7dc8f77cf70750927a2c85650312033 (patch)
tree12f36afcf90ee22be0bfc41906bd628d0f07c6fa /components
parente483a189a3c24d0fe475cf2a8dedb11821f7ee21 (diff)
downloadservo-10f7b49cf7dc8f77cf70750927a2c85650312033.tar.gz
servo-10f7b49cf7dc8f77cf70750927a2c85650312033.zip
Invert control flow, fix resizing, and improve checkerboarding
significantly by giving tiles some time to paint before we render unrendered content.
Diffstat (limited to 'components')
-rw-r--r--components/compositing/compositor.rs457
-rw-r--r--components/compositing/compositor_task.rs167
-rw-r--r--components/compositing/constellation.rs158
-rw-r--r--components/compositing/headless.rs123
-rw-r--r--components/compositing/lib.rs5
-rw-r--r--components/compositing/pipeline.rs8
-rw-r--r--components/compositing/scrolling.rs73
-rw-r--r--components/compositing/windowing.rs42
-rw-r--r--components/gfx/display_list/mod.rs140
-rw-r--r--components/gfx/lib.rs1
-rw-r--r--components/gfx/render_context.rs136
-rw-r--r--components/gfx/render_task.rs7
-rw-r--r--components/msg/compositor_msg.rs22
-rw-r--r--components/msg/constellation_msg.rs4
-rw-r--r--components/script/dom/window.rs10
-rw-r--r--components/script/page.rs5
-rw-r--r--components/script/script_task.rs47
-rw-r--r--components/script_traits/lib.rs28
-rw-r--r--components/util/time.rs4
19 files changed, 890 insertions, 547 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index e11f66d1e34..eb0301e9208 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -7,21 +7,23 @@ use compositor_layer::{ScrollPositionChanged, WantsScrollEvents};
use compositor_task::{Msg, CompositorTask, Exit, ChangeReadyState, SetIds, LayerProperties};
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
-use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
+use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded, ScrollTimeout};
+use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
use constellation::SendableFrameTree;
use pipeline::CompositionPipeline;
+use scrolling::ScrollingTimerProxy;
use windowing;
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
-use windowing::PinchZoomWindowEvent;
+use windowing::{PinchZoomWindowEvent};
use azure::azure_hl;
use std::cmp;
+use std::mem;
use std::num::Zero;
-use std::time::duration::Duration;
use geom::point::{Point2D, TypedPoint2D};
use geom::rect::{Rect, TypedRect};
use geom::size::TypedSize2D;
@@ -45,20 +47,18 @@ use servo_util::memory::MemoryProfilerChan;
use servo_util::opts;
use servo_util::time::{profile, TimeProfilerChan};
use servo_util::{memory, time};
-use std::io::timer::sleep;
use std::collections::hashmap::HashMap;
use std::path::Path;
use std::rc::Rc;
-use time::precise_time_s;
+use time::{precise_time_ns, precise_time_s};
use url::Url;
-
pub struct IOCompositor<Window: WindowMethods> {
/// The application window.
window: Rc<Window>,
/// The port on which we receive messages.
- port: Receiver<Msg>,
+ port: Box<CompositorReceiver>,
/// The render context.
context: RenderContext,
@@ -82,20 +82,23 @@ pub struct IOCompositor<Window: WindowMethods> {
/// The device pixel ratio for this window.
hidpi_factor: ScaleFactor<ScreenPx, DevicePixel, f32>,
- /// Tracks whether the renderer has finished its first rendering
- composite_ready: bool,
+ /// A handle to the scrolling timer.
+ scrolling_timer: ScrollingTimerProxy,
+
+ /// Tracks whether we should composite this frame.
+ composition_request: CompositionRequest,
/// Tracks whether we are in the process of shutting down, or have shut down and should close
/// the compositor.
shutdown_state: ShutdownState,
- /// Tracks whether we need to re-composite a page.
- recomposite: bool,
-
/// Tracks outstanding render_msg's sent to the render tasks.
outstanding_render_msgs: uint,
- /// Tracks whether the zoom action has happend recently.
+ /// Tracks the last composite time.
+ last_composite_time: u64,
+
+ /// Tracks whether the zoom action has happened recently.
zoom_action: bool,
/// The time of the last zoom action has started.
@@ -112,6 +115,9 @@ pub struct IOCompositor<Window: WindowMethods> {
/// many times for a single page.
got_load_complete_message: bool,
+ /// Whether we have gotten a `SetIds` message.
+ got_set_ids_message: bool,
+
/// The channel on which messages can be sent to the constellation.
constellation_chan: ConstellationChan,
@@ -122,10 +128,25 @@ pub struct IOCompositor<Window: WindowMethods> {
memory_profiler_chan: MemoryProfilerChan,
/// Pending scroll to fragment event, if any
- fragment_point: Option<Point2D<f32>>
+ fragment_point: Option<Point2D<f32>>,
+
+ /// Pending scroll events.
+ pending_scroll_events: Vec<ScrollEvent>,
+}
+
+pub struct ScrollEvent {
+ delta: TypedPoint2D<DevicePixel,f32>,
+ cursor: TypedPoint2D<DevicePixel,i32>,
}
#[deriving(PartialEq)]
+enum CompositionRequest {
+ NoCompositingNecessary,
+ CompositeOnScrollTimeout(u64),
+ CompositeNow,
+}
+
+#[deriving(PartialEq, Show)]
enum ShutdownState {
NotShuttingDown,
ShuttingDown,
@@ -139,11 +160,12 @@ struct HitTestResult {
impl<Window: WindowMethods> IOCompositor<Window> {
fn new(window: Rc<Window>,
- port: Receiver<Msg>,
+ sender: Box<CompositorProxy+Send>,
+ receiver: Box<CompositorReceiver>,
constellation_chan: ConstellationChan,
time_profiler_chan: TimeProfilerChan,
- memory_profiler_chan: MemoryProfilerChan) -> IOCompositor<Window> {
-
+ memory_profiler_chan: MemoryProfilerChan)
+ -> IOCompositor<Window> {
// Create an initial layer tree.
//
// TODO: There should be no initial layer tree until the renderer creates one from the
@@ -155,7 +177,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let show_debug_borders = opts::get().show_debug_borders;
IOCompositor {
window: window,
- port: port,
+ port: receiver,
context: rendergl::RenderContext::new(context, show_debug_borders),
root_pipeline: None,
scene: Scene::new(Rect {
@@ -164,9 +186,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}),
window_size: window_size,
hidpi_factor: hidpi_factor,
- composite_ready: false,
+ scrolling_timer: ScrollingTimerProxy::new(sender),
+ composition_request: NoCompositingNecessary,
+ pending_scroll_events: Vec::new(),
shutdown_state: NotShuttingDown,
- recomposite: false,
page_zoom: ScaleFactor(1.0),
viewport_zoom: ScaleFactor(1.0),
zoom_action: false,
@@ -174,167 +197,129 @@ impl<Window: WindowMethods> IOCompositor<Window> {
ready_states: HashMap::new(),
render_states: HashMap::new(),
got_load_complete_message: false,
+ got_set_ids_message: false,
constellation_chan: constellation_chan,
time_profiler_chan: time_profiler_chan,
memory_profiler_chan: memory_profiler_chan,
fragment_point: None,
outstanding_render_msgs: 0,
+ last_composite_time: 0,
}
}
pub fn create(window: Rc<Window>,
- port: Receiver<Msg>,
+ sender: Box<CompositorProxy+Send>,
+ receiver: Box<CompositorReceiver>,
constellation_chan: ConstellationChan,
time_profiler_chan: TimeProfilerChan,
- memory_profiler_chan: MemoryProfilerChan) {
+ memory_profiler_chan: MemoryProfilerChan)
+ -> IOCompositor<Window> {
let mut compositor = IOCompositor::new(window,
- port,
+ sender,
+ receiver,
constellation_chan,
time_profiler_chan,
memory_profiler_chan);
- compositor.update_zoom_transform();
- // Starts the compositor, which listens for messages on the specified port.
- compositor.run();
- }
+ // Set the size of the root layer.
+ compositor.update_zoom_transform();
- fn run (&mut self) {
// Tell the constellation about the initial window size.
- self.send_window_size();
+ compositor.send_window_size();
- // Enter the main event loop.
- while self.shutdown_state != FinishedShuttingDown {
- // Check for new messages coming from the rendering task.
- self.handle_message();
-
- if self.shutdown_state == FinishedShuttingDown {
- // We have exited the compositor and passing window
- // messages to script may crash.
- debug!("Exiting the compositor due to a request from script.");
- break;
- }
+ compositor
+ }
- // Check for messages coming from the windowing system.
- let msg = self.window.recv();
- self.handle_window_message(msg);
+ fn handle_browser_message(&mut self, msg: Msg) -> bool {
+ match (msg, self.shutdown_state) {
+ (_, FinishedShuttingDown) =>
+ fail!("compositor shouldn't be handling messages after shutting down"),
- // If asked to recomposite and renderer has run at least once
- if self.recomposite && self.composite_ready {
- self.recomposite = false;
- self.composite();
+ (Exit(chan), _) => {
+ debug!("shutting down the constellation");
+ let ConstellationChan(ref con_chan) = self.constellation_chan;
+ con_chan.send(ExitMsg);
+ chan.send(());
+ self.shutdown_state = ShuttingDown;
}
- sleep(Duration::milliseconds(10));
-
- // If a pinch-zoom happened recently, ask for tiles at the new resolution
- if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 {
- self.zoom_action = false;
- self.scene.mark_layer_contents_as_changed_recursively();
- self.send_buffer_requests_for_all_layers();
+ (ShutdownComplete, _) => {
+ debug!("constellation completed shutdown");
+ self.shutdown_state = FinishedShuttingDown;
+ return false;
}
- }
-
- // Clear out the compositor layers so that painting tasks can destroy the buffers.
- match self.scene.root {
- None => {}
- Some(ref layer) => layer.forget_all_tiles(),
- }
-
- // Drain compositor port, sometimes messages contain channels that are blocking
- // another task from finishing (i.e. SetIds)
- loop {
- match self.port.try_recv() {
- Err(_) => break,
- Ok(_) => {},
+ (ChangeReadyState(pipeline_id, ready_state), NotShuttingDown) => {
+ self.change_ready_state(pipeline_id, ready_state);
}
- }
-
- // Tell the profiler and memory profiler to shut down.
- let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
- time_profiler_chan.send(time::ExitMsg);
-
- let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
- memory_profiler_chan.send(memory::ExitMsg);
- }
-
- fn handle_message(&mut self) {
- loop {
- match (self.port.try_recv(), self.shutdown_state) {
- (_, FinishedShuttingDown) =>
- fail!("compositor shouldn't be handling messages after shutting down"),
- (Err(_), _) => break,
+ (ChangeRenderState(pipeline_id, render_state), NotShuttingDown) => {
+ self.change_render_state(pipeline_id, render_state);
+ }
- (Ok(Exit(chan)), _) => {
- debug!("shutting down the constellation");
- let ConstellationChan(ref con_chan) = self.constellation_chan;
- con_chan.send(ExitMsg);
- chan.send(());
- self.shutdown_state = ShuttingDown;
- }
+ (RenderMsgDiscarded, NotShuttingDown) => {
+ self.remove_outstanding_render_msg();
+ }
- (Ok(ShutdownComplete), _) => {
- debug!("constellation completed shutdown");
- self.shutdown_state = FinishedShuttingDown;
- break;
- }
+ (SetIds(frame_tree, response_chan, new_constellation_chan), NotShuttingDown) => {
+ self.set_frame_tree(&frame_tree,
+ response_chan,
+ new_constellation_chan);
+ }
- (Ok(ChangeReadyState(pipeline_id, ready_state)), NotShuttingDown) => {
- self.change_ready_state(pipeline_id, ready_state);
- }
+ (CreateOrUpdateRootLayer(layer_properties), NotShuttingDown) => {
+ self.create_or_update_root_layer(layer_properties);
+ }
- (Ok(ChangeRenderState(pipeline_id, render_state)), NotShuttingDown) => {
- self.change_render_state(pipeline_id, render_state);
- }
+ (CreateOrUpdateDescendantLayer(layer_properties), NotShuttingDown) => {
+ self.create_or_update_descendant_layer(layer_properties);
+ }
- (Ok(RenderMsgDiscarded), NotShuttingDown) => {
- self.remove_outstanding_render_msg();
- }
+ (GetGraphicsMetadata(chan), NotShuttingDown) => {
+ chan.send(Some(self.window.native_metadata()));
+ }
- (Ok(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
- self.set_frame_tree(&frame_tree,
- response_chan,
- new_constellation_chan);
- }
+ (SetLayerOrigin(pipeline_id, layer_id, origin), NotShuttingDown) => {
+ self.set_layer_origin(pipeline_id, layer_id, origin);
+ }
- (Ok(GetGraphicsMetadata(chan)), NotShuttingDown) => {
- chan.send(Some(self.window.native_metadata()));
+ (Paint(pipeline_id, epoch, replies), NotShuttingDown) => {
+ for (layer_id, new_layer_buffer_set) in replies.into_iter() {
+ self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
}
+ self.remove_outstanding_render_msg();
+ }
- (Ok(CreateOrUpdateRootLayer(layer_properties)), NotShuttingDown) => {
- self.create_or_update_root_layer(layer_properties);
- }
+ (ScrollFragmentPoint(pipeline_id, layer_id, point), NotShuttingDown) => {
+ self.scroll_fragment_to_point(pipeline_id, layer_id, point);
+ }
- (Ok(CreateOrUpdateDescendantLayer(layer_properties)), NotShuttingDown) => {
- self.create_or_update_descendant_layer(layer_properties);
- }
+ (LoadComplete(..), NotShuttingDown) => {
+ self.got_load_complete_message = true;
- (Ok(SetLayerOrigin(pipeline_id, layer_id, origin)), NotShuttingDown) => {
- self.set_layer_origin(pipeline_id, layer_id, origin);
+ // If we're rendering in headless mode, schedule a recomposite.
+ if opts::get().output_file.is_some() {
+ self.composite_if_necessary();
}
+ }
- (Ok(Paint(pipeline_id, epoch, replies)), NotShuttingDown) => {
- for (layer_id, new_layer_buffer_set) in replies.into_iter() {
- self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
+ (ScrollTimeout(timestamp), NotShuttingDown) => {
+ debug!("scroll timeout, drawing unrendered content!");
+ match self.composition_request {
+ CompositeOnScrollTimeout(this_timestamp) if timestamp == this_timestamp => {
+ self.composition_request = CompositeNow
}
- self.remove_outstanding_render_msg();
- }
-
- (Ok(ScrollFragmentPoint(pipeline_id, layer_id, point)), NotShuttingDown) => {
- self.scroll_fragment_to_point(pipeline_id, layer_id, point);
- }
-
- (Ok(LoadComplete(..)), NotShuttingDown) => {
- self.got_load_complete_message = true;
+ _ => {}
}
-
- // When we are shutting_down, we need to avoid performing operations
- // such as Paint that may crash because we have begun tearing down
- // the rest of our resources.
- (_, ShuttingDown) => { }
}
+
+ // When we are shutting_down, we need to avoid performing operations
+ // such as Paint that may crash because we have begun tearing down
+ // the rest of our resources.
+ (_, ShuttingDown) => { }
}
+
+ true
}
fn change_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
@@ -342,6 +327,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
ready_state,
|_key, value| *value = ready_state);
self.window.set_ready_state(self.get_earliest_pipeline_ready_state());
+
+ // If we're rendering in headless mode, schedule a recomposite.
+ if opts::get().output_file.is_some() {
+ self.composite_if_necessary()
+ }
}
fn get_earliest_pipeline_ready_state(&self) -> ReadyState {
@@ -357,9 +347,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
render_state,
|_key, value| *value = render_state);
self.window.set_render_state(render_state);
- if render_state == IdleRenderState {
- self.composite_ready = true;
- }
}
fn all_pipelines_in_idle_render_state(&self) -> bool {
@@ -417,6 +404,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
// Initialize the new constellation channel by sending it the root window size.
self.constellation_chan = new_constellation_chan;
self.send_window_size();
+
+ self.got_set_ids_message = true;
+ self.composite_if_necessary();
}
fn create_frame_tree_root_layers(&mut self,
@@ -529,7 +519,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}));
}
-
pub fn move_layer(&self,
pipeline_id: PipelineId,
layer_id: LayerId,
@@ -555,10 +544,21 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fail!("Compositor: Tried to scroll to fragment with unknown layer.");
}
- self.recomposite = true;
+ self.start_scrolling_timer_if_necessary();
}
None => {}
- };
+ }
+ }
+
+ fn start_scrolling_timer_if_necessary(&mut self) {
+ match self.composition_request {
+ CompositeNow | CompositeOnScrollTimeout(_) => return,
+ NoCompositingNecessary => {}
+ }
+
+ let timestamp = precise_time_ns();
+ self.scrolling_timer.scroll_event_processed(timestamp);
+ self.composition_request = CompositeOnScrollTimeout(timestamp);
}
fn set_layer_origin(&mut self,
@@ -580,7 +580,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
layer_id: LayerId,
new_layer_buffer_set: Box<LayerBufferSet>,
epoch: Epoch) {
- debug!("compositor received new frame");
+ debug!("compositor received new frame at size {}x{}",
+ self.window_size.width.get(),
+ self.window_size.height.get());
// From now on, if we destroy the buffers, they will leak.
let mut new_layer_buffer_set = new_layer_buffer_set;
@@ -588,8 +590,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
Some(ref layer) => {
+ // FIXME(pcwalton): This is going to cause problems with inconsistent frames since
+ // we only composite one layer at a time.
assert!(layer.add_buffers(new_layer_buffer_set, epoch));
- self.recomposite = true;
+ self.composite_if_necessary();
}
None => {
// FIXME: This may potentially be triggered by a race condition where a
@@ -605,8 +609,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
layer_id: LayerId,
point: Point2D<f32>) {
if self.move_layer(pipeline_id, layer_id, Point2D::from_untyped(&point)) {
- self.recomposite = true;
- self.send_buffer_requests_for_all_layers();
+ if self.send_buffer_requests_for_all_layers() {
+ self.start_scrolling_timer_if_necessary();
+ }
} else {
self.fragment_point = Some(point);
}
@@ -617,7 +622,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
IdleWindowEvent => {}
RefreshWindowEvent => {
- self.recomposite = true;
+ self.composite_if_necessary()
}
ResizeWindowEvent(size) => {
@@ -685,6 +690,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
debug!("osmain: window resized to {:?}", new_size);
self.window_size = new_size;
+
self.scene.set_root_layer_size(new_size.as_f32());
self.send_window_size();
}
@@ -698,7 +704,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
layers"),
};
- let msg = LoadUrlMsg(root_pipeline_id, LoadData::new(Url::parse(url_string.as_slice()).unwrap()));
+ let msg = LoadUrlMsg(root_pipeline_id,
+ LoadData::new(Url::parse(url_string.as_slice()).unwrap()));
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(msg);
}
@@ -725,18 +732,29 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn on_scroll_window_event(&mut self,
delta: TypedPoint2D<DevicePixel, f32>,
cursor: TypedPoint2D<DevicePixel, i32>) {
- let delta = delta / self.scene.scale;
- let cursor = cursor.as_f32() / self.scene.scale;
+ self.pending_scroll_events.push(ScrollEvent {
+ delta: delta,
+ cursor: cursor,
+ });
- let mut scroll = false;
- match self.scene.root {
- Some(ref mut layer) => {
- scroll = layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged;
- }
- None => { }
+ self.composite_if_necessary();
+ }
+
+ fn process_pending_scroll_events(&mut self) {
+ for scroll_event in 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;
+
+ let scrolled = match self.scene.root {
+ Some(ref mut layer) => {
+ layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged
+ }
+ None => false,
+ };
+
+ self.start_scrolling_timer_if_necessary();
+ self.send_buffer_requests_for_all_layers();
}
- self.recomposite_if(scroll);
- self.send_buffer_requests_for_all_layers();
}
fn device_pixels_per_screen_px(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
@@ -768,6 +786,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.send_window_size();
}
+ // TODO(pcwalton): I think this should go through the same queuing as scroll events do.
fn on_pinch_zoom_window_event(&mut self, magnification: f32) {
self.zoom_action = true;
self.zoom_time = precise_time_s();
@@ -792,7 +811,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
None => { }
}
- self.recomposite = true;
+ self.composite_if_necessary();
}
fn on_navigation_window_event(&self, direction: WindowNavigateMsg) {
@@ -840,9 +859,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
match self.root_pipeline {
Some(ref pipeline) => {
let unused_buffers = self.scene.collect_unused_buffers();
- let have_unused_buffers = unused_buffers.len() > 0;
- self.recomposite = self.recomposite || have_unused_buffers;
- if have_unused_buffers {
+ if unused_buffers.len() != 0 {
let message = UnusedBufferMsg(unused_buffers);
let _ = pipeline.render_chan.send_opt(message);
}
@@ -851,7 +868,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
- fn send_buffer_requests_for_all_layers(&mut self) {
+ /// Returns true if any buffer requests were sent or false otherwise.
+ fn send_buffer_requests_for_all_layers(&mut self) -> bool {
let mut layers_and_requests = Vec::new();
self.scene.get_buffer_requests(&mut layers_and_requests,
Rect(TypedPoint2D(0f32, 0f32), self.window_size.as_f32()));
@@ -860,7 +878,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.send_back_unused_buffers();
if layers_and_requests.len() == 0 {
- return;
+ return false;
}
// We want to batch requests for each pipeline to avoid race conditions
@@ -875,6 +893,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
self.add_outstanding_render_msg(num_render_msgs_sent);
+ true
}
fn is_ready_to_render_image_output(&self) -> bool {
@@ -893,6 +912,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if !self.all_pipelines_in_idle_render_state() {
return false;
}
+
+ if !self.got_set_ids_message {
+ return false;
+ }
+
return true;
}
@@ -939,10 +963,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
});
if output_image {
- let path = from_str::<Path>(opts::get().output_file.as_ref().unwrap().as_slice()).unwrap();
+ let path =
+ from_str::<Path>(opts::get().output_file.as_ref().unwrap().as_slice()).unwrap();
let mut pixels = gl::read_pixels(0, 0,
- width as GLsizei,
- height as GLsizei,
+ width as gl::GLsizei,
+ height as gl::GLsizei,
gl::RGB, gl::UNSIGNED_BYTE);
gl::bind_framebuffer(gl::FRAMEBUFFER, 0);
@@ -976,18 +1001,26 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.shutdown_state = ShuttingDown;
}
+ // Perform the page flip. This will likely block for a while.
self.window.present();
+ self.last_composite_time = precise_time_ns();
+
let exit = opts::get().exit_after_load;
if exit {
debug!("shutting down the constellation for exit_after_load");
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ExitMsg);
}
+
+ self.composition_request = NoCompositingNecessary;
+ self.process_pending_scroll_events();
}
- fn recomposite_if(&mut self, result: bool) {
- self.recomposite = result || self.recomposite;
+ fn composite_if_necessary(&mut self) {
+ if self.composition_request == NoCompositingNecessary {
+ self.composition_request = CompositeNow
+ }
}
fn find_topmost_layer_at_point_for_layer(&self,
@@ -1054,3 +1087,87 @@ fn find_layer_with_pipeline_and_layer_id_for_layer(layer: Rc<Layer<CompositorDat
return None;
}
+
+impl<Window> CompositorEventListener for IOCompositor<Window> where Window: WindowMethods {
+ fn handle_event(&mut self, msg: WindowEvent) -> bool {
+ // Check for new messages coming from the other tasks in the system.
+ loop {
+ match self.port.try_recv_compositor_msg() {
+ None => break,
+ Some(msg) => {
+ if !self.handle_browser_message(msg) {
+ break
+ }
+ }
+ }
+ }
+
+ if self.shutdown_state == FinishedShuttingDown {
+ // We have exited the compositor and passing window
+ // messages to script may crash.
+ debug!("Exiting the compositor due to a request from script.");
+ return false;
+ }
+
+ // Handle the message coming from the windowing system.
+ self.handle_window_message(msg);
+
+ // If a pinch-zoom happened recently, ask for tiles at the new resolution
+ if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 {
+ self.zoom_action = false;
+ self.scene.mark_layer_contents_as_changed_recursively();
+ self.send_buffer_requests_for_all_layers();
+ }
+
+ match self.composition_request {
+ NoCompositingNecessary | CompositeOnScrollTimeout(_) => {}
+ CompositeNow => self.composite(),
+ }
+
+ self.shutdown_state != FinishedShuttingDown
+ }
+
+ /// Repaints and recomposites synchronously. You must be careful when calling this, as if a
+ /// paint is not scheduled the compositor will hang forever.
+ ///
+ /// This is used when resizing the window.
+ fn repaint_synchronously(&mut self) {
+ while self.shutdown_state != ShuttingDown {
+ let msg = self.port.recv_compositor_msg();
+ let is_paint = match msg {
+ Paint(..) => true,
+ _ => false,
+ };
+ let keep_going = self.handle_browser_message(msg);
+ if is_paint {
+ self.composite();
+ break
+ }
+ if !keep_going {
+ break
+ }
+ }
+ }
+
+ fn shutdown(&mut self) {
+ // Clear out the compositor layers so that painting tasks can destroy the buffers.
+ match self.scene.root {
+ None => {}
+ Some(ref layer) => layer.forget_all_tiles(),
+ }
+
+ // Drain compositor port, sometimes messages contain channels that are blocking
+ // another task from finishing (i.e. SetIds)
+ while self.port.try_recv_compositor_msg().is_some() {}
+
+ // Tell the profiler, memory profiler, and scrolling timer to shut down.
+ let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
+ time_profiler_chan.send(time::ExitMsg);
+
+ let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
+ memory_profiler_chan.send(memory::ExitMsg);
+
+ self.scrolling_timer.shutdown();
+ }
+}
+
diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs
index dd215af28f7..5fe2c4bb57a 100644
--- a/components/compositing/compositor_task.rs
+++ b/components/compositing/compositor_task.rs
@@ -2,12 +2,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+//! Communication with the compositor task.
+
pub use windowing;
+pub use constellation::SendableFrameTree;
use compositor;
use headless;
-pub use constellation::SendableFrameTree;
-use windowing::WindowMethods;
+use windowing::{WindowEvent, WindowMethods};
use azure::azure_hl::{SourceSurfaceMethods, Color};
use geom::point::Point2D;
@@ -21,39 +23,64 @@ use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_util::memory::MemoryProfilerChan;
use servo_util::time::TimeProfilerChan;
use std::comm::{channel, Sender, Receiver};
+use std::fmt::{FormatError, Formatter, Show};
use std::rc::Rc;
-
use url::Url;
-/// The implementation of the layers-based compositor.
-#[deriving(Clone)]
-pub struct CompositorChan {
- /// A channel on which messages can be sent to the compositor.
- pub chan: Sender<Msg>,
+/// Sends messages to the compositor. This is a trait supplied by the port because the method used
+/// to communicate with the compositor may have to kick OS event loops awake, communicate cross-
+/// process, and so forth.
+pub trait CompositorProxy : 'static + Send {
+ /// Sends a message to the compositor.
+ fn send(&mut self, msg: Msg);
+ /// Clones the compositor proxy.
+ fn clone_compositor_proxy(&self) -> Box<CompositorProxy+'static+Send>;
+}
+
+/// The port that the compositor receives messages on. As above, this is a trait supplied by the
+/// Servo port.
+pub trait CompositorReceiver for Sized? : 'static {
+ /// Receives the next message inbound for the compositor. This must not block.
+ fn try_recv_compositor_msg(&mut self) -> Option<Msg>;
+ /// Synchronously waits for, and returns, the next message inbound for the compositor.
+ fn recv_compositor_msg(&mut self) -> Msg;
+}
+
+/// A convenience implementation of `CompositorReceiver` for a plain old Rust `Receiver`.
+impl CompositorReceiver for Receiver<Msg> {
+ fn try_recv_compositor_msg(&mut self) -> Option<Msg> {
+ match self.try_recv() {
+ Ok(msg) => Some(msg),
+ Err(_) => None,
+ }
+ }
+ fn recv_compositor_msg(&mut self) -> Msg {
+ self.recv()
+ }
}
/// Implementation of the abstract `ScriptListener` interface.
-impl ScriptListener for CompositorChan {
- fn set_ready_state(&self, pipeline_id: PipelineId, ready_state: ReadyState) {
+impl ScriptListener for Box<CompositorProxy+'static+Send> {
+ fn set_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
let msg = ChangeReadyState(pipeline_id, ready_state);
- self.chan.send(msg);
+ self.send(msg);
}
- fn scroll_fragment_point(&self,
+ fn scroll_fragment_point(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
point: Point2D<f32>) {
- self.chan.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
+ self.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
}
- fn close(&self) {
+ fn close(&mut self) {
let (chan, port) = channel();
- self.chan.send(Exit(chan));
+ self.send(Exit(chan));
port.recv();
}
- fn dup(&self) -> Box<ScriptListener+'static> {
- box self.clone() as Box<ScriptListener+'static>
+ fn dup(&mut self) -> Box<ScriptListener+'static> {
+ box self.clone_compositor_proxy() as Box<ScriptListener+'static>
}
}
@@ -83,21 +110,21 @@ impl LayerProperties {
}
/// Implementation of the abstract `RenderListener` interface.
-impl RenderListener for CompositorChan {
- fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata> {
+impl RenderListener for Box<CompositorProxy+'static+Send> {
+ fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata> {
let (chan, port) = channel();
- self.chan.send(GetGraphicsMetadata(chan));
+ self.send(GetGraphicsMetadata(chan));
port.recv()
}
- fn paint(&self,
+ fn paint(&mut self,
pipeline_id: PipelineId,
epoch: Epoch,
replies: Vec<(LayerId, Box<LayerBufferSet>)>) {
- self.chan.send(Paint(pipeline_id, epoch, replies));
+ self.send(Paint(pipeline_id, epoch, replies));
}
- fn initialize_layers_for_pipeline(&self,
+ fn initialize_layers_for_pipeline(&mut self,
pipeline_id: PipelineId,
metadata: Vec<LayerMetadata>,
epoch: Epoch) {
@@ -108,36 +135,23 @@ impl RenderListener for CompositorChan {
for metadata in metadata.iter() {
let layer_properties = LayerProperties::new(pipeline_id, epoch, metadata);
if first {
- self.chan.send(CreateOrUpdateRootLayer(layer_properties));
+ self.send(CreateOrUpdateRootLayer(layer_properties));
first = false
} else {
- self.chan.send(CreateOrUpdateDescendantLayer(layer_properties));
+ self.send(CreateOrUpdateDescendantLayer(layer_properties));
}
}
}
- fn render_msg_discarded(&self) {
- self.chan.send(RenderMsgDiscarded);
+ fn render_msg_discarded(&mut self) {
+ self.send(RenderMsgDiscarded);
}
- fn set_render_state(&self, pipeline_id: PipelineId, render_state: RenderState) {
- self.chan.send(ChangeRenderState(pipeline_id, render_state))
+ fn set_render_state(&mut self, pipeline_id: PipelineId, render_state: RenderState) {
+ self.send(ChangeRenderState(pipeline_id, render_state))
}
}
-impl CompositorChan {
- pub fn new() -> (Receiver<Msg>, CompositorChan) {
- let (chan, port) = channel();
- let compositor_chan = CompositorChan {
- chan: chan,
- };
- (port, compositor_chan)
- }
-
- pub fn send(&self, msg: Msg) {
- self.chan.send(msg);
- }
-}
/// Messages from the painting task and the constellation task to the compositor task.
pub enum Msg {
/// Requests that the compositor shut down.
@@ -177,6 +191,30 @@ pub enum Msg {
SetIds(SendableFrameTree, Sender<()>, ConstellationChan),
/// The load of a page for a given URL has completed.
LoadComplete(PipelineId, Url),
+ /// Indicates that the scrolling timeout with the given starting timestamp has happened and a
+ /// composite should happen. (See the `scrolling` module.)
+ ScrollTimeout(u64),
+}
+
+impl Show for Msg {
+ fn fmt(&self, f: &mut Formatter) -> Result<(),FormatError> {
+ match *self {
+ Exit(..) => write!(f, "Exit"),
+ ShutdownComplete(..) => write!(f, "ShutdownComplete"),
+ GetGraphicsMetadata(..) => write!(f, "GetGraphicsMetadata"),
+ CreateOrUpdateRootLayer(..) => write!(f, "CreateOrUpdateRootLayer"),
+ CreateOrUpdateDescendantLayer(..) => write!(f, "CreateOrUpdateDescendantLayer"),
+ SetLayerOrigin(..) => write!(f, "SetLayerOrigin"),
+ ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
+ Paint(..) => write!(f, "Paint"),
+ ChangeReadyState(..) => write!(f, "ChangeReadyState"),
+ ChangeRenderState(..) => write!(f, "ChangeRenderState"),
+ RenderMsgDiscarded(..) => write!(f, "RenderMsgDiscarded"),
+ SetIds(..) => write!(f, "SetIds"),
+ LoadComplete(..) => write!(f, "LoadComplete"),
+ ScrollTimeout(..) => write!(f, "ScrollTimeout"),
+ }
+ }
}
pub struct CompositorTask;
@@ -196,27 +234,38 @@ impl CompositorTask {
NativeCompositingGraphicsContext::new()
}
- pub fn create<Window: WindowMethods>(
- window: Option<Rc<Window>>,
- port: Receiver<Msg>,
- constellation_chan: ConstellationChan,
- time_profiler_chan: TimeProfilerChan,
- memory_profiler_chan: MemoryProfilerChan) {
-
+ pub fn create<Window>(window: Option<Rc<Window>>,
+ sender: Box<CompositorProxy+Send>,
+ receiver: Box<CompositorReceiver>,
+ constellation_chan: ConstellationChan,
+ time_profiler_chan: TimeProfilerChan,
+ memory_profiler_chan: MemoryProfilerChan)
+ -> Box<CompositorEventListener + 'static>
+ where Window: WindowMethods + 'static {
match window {
Some(window) => {
- compositor::IOCompositor::create(window,
- port,
- constellation_chan.clone(),
- time_profiler_chan,
- memory_profiler_chan)
+ box compositor::IOCompositor::create(window,
+ sender,
+ receiver,
+ constellation_chan.clone(),
+ time_profiler_chan,
+ memory_profiler_chan)
+ as Box<CompositorEventListener>
}
None => {
- headless::NullCompositor::create(port,
- constellation_chan.clone(),
- time_profiler_chan,
- memory_profiler_chan)
+ box headless::NullCompositor::create(receiver,
+ constellation_chan.clone(),
+ time_profiler_chan,
+ memory_profiler_chan)
+ as Box<CompositorEventListener>
}
- };
+ }
}
}
+
+pub trait CompositorEventListener {
+ fn handle_event(&mut self, event: WindowEvent) -> bool;
+ fn repaint_synchronously(&mut self);
+ fn shutdown(&mut self);
+}
+
diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs
index 9b0446c9b0a..7a7204dfdaf 100644
--- a/components/compositing/constellation.rs
+++ b/components/compositing/constellation.rs
@@ -2,53 +2,79 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use compositor_task::{CompositorChan, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds};
+use pipeline::{Pipeline, CompositionPipeline};
+
+use compositor_task::{CompositorProxy, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds};
use devtools_traits::DevtoolsControlChan;
-use std::collections::hashmap::{HashMap, HashSet};
use geom::rect::{Rect, TypedRect};
use geom::scale_factor::ScaleFactor;
+use gfx::font_cache_task::FontCacheTask;
use gfx::render_task;
-use libc;
-use pipeline::{Pipeline, CompositionPipeline};
+use layers::geometry::DevicePixel;
use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
+use libc;
use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
use script_traits::{ScriptControlChan, ScriptTaskFactory};
use servo_msg::compositor_msg::LayerId;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
-use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
-use servo_msg::constellation_msg::{LoadData, NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg};
-use servo_msg::constellation_msg::{SubpageId, WindowSizeData};
+use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg};
+use servo_msg::constellation_msg::{LoadData, NavigateMsg, NavigationType, PipelineId};
+use servo_msg::constellation_msg::{RendererReadyMsg, ResizedWindowMsg, SubpageId, WindowSizeData};
use servo_msg::constellation_msg;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
-use gfx::font_cache_task::FontCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_net::resource_task;
-use servo_util::geometry::PagePx;
+use servo_util::geometry::{PagePx, ViewportPx};
use servo_util::opts;
-use servo_util::time::TimeProfilerChan;
use servo_util::task::spawn_named;
+use servo_util::time::TimeProfilerChan;
use std::cell::RefCell;
-use std::mem::replace;
+use std::collections::hashmap::{HashMap, HashSet};
use std::io;
+use std::mem::replace;
use std::rc::Rc;
use url::Url;
-/// Maintains the pipelines and navigation context and grants permission to composite
+/// Maintains the pipelines and navigation context and grants permission to composite.
pub struct Constellation<LTF, STF> {
+ /// A channel through which messages can be sent to this object.
pub chan: ConstellationChan,
+
+ /// Receives messages.
pub request_port: Receiver<Msg>,
- pub compositor_chan: CompositorChan,
+
+ /// A channel (the implementation of which is port-specific) through which messages can be sent
+ /// to the compositor.
+ pub compositor_proxy: Box<CompositorProxy>,
+
+ /// A channel through which messages can be sent to the resource task.
pub resource_task: ResourceTask,
+
+ /// A channel through which messages can be sent to the image cache task.
pub image_cache_task: ImageCacheTask,
+
+ /// A channel through which messages can be sent to the developer tools.
devtools_chan: Option<DevtoolsControlChan>,
+
+ /// A list of all the pipelines. (See the `pipeline` module for more details.)
pipelines: HashMap<PipelineId, Rc<Pipeline>>,
+
+ /// A channel through which messages can be sent to the font cache.
font_cache_task: FontCacheTask,
+
navigation_context: NavigationContext,
+
+ /// The next free ID to assign to a pipeline.
next_pipeline_id: PipelineId,
+
pending_frames: Vec<FrameChange>,
+
pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
+
+ /// A channel through which messages can be sent to the time profiler.
pub time_profiler_chan: TimeProfilerChan,
+
pub window_size: WindowSizeData,
}
@@ -86,7 +112,11 @@ impl FrameTree {
fn to_sendable(&self) -> SendableFrameTree {
let sendable_frame_tree = SendableFrameTree {
pipeline: self.pipeline.to_sendable(),
- children: self.children.borrow().iter().map(|frame_tree| frame_tree.to_sendable()).collect(),
+ children: self.children
+ .borrow()
+ .iter()
+ .map(|frame_tree| frame_tree.to_sendable())
+ .collect(),
};
sendable_frame_tree
}
@@ -109,8 +139,8 @@ impl FrameTreeTraversal for Rc<FrameTree> {
self.iter().find(|frame_tree| id == frame_tree.pipeline.id)
}
- /// Replaces a node of the frame tree in place. Returns the node that was removed or the original node
- /// if the node to replace could not be found.
+ /// Replaces a node of the frame tree in place. Returns the node that was removed or the
+ /// original node if the node to replace could not be found.
fn replace_child(&self, id: PipelineId, new_child: Rc<FrameTree>) -> ReplaceResult {
for frame_tree in self.iter() {
let mut children = frame_tree.children.borrow_mut();
@@ -239,7 +269,7 @@ impl NavigationContext {
}
impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
- pub fn start(compositor_chan: CompositorChan,
+ pub fn start(compositor_proxy: Box<CompositorProxy+Send>,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask,
font_cache_task: FontCacheTask,
@@ -249,10 +279,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let (constellation_port, constellation_chan) = ConstellationChan::new();
let constellation_chan_clone = constellation_chan.clone();
spawn_named("Constellation", proc() {
- let mut constellation : Constellation<LTF, STF> = Constellation {
+ let mut constellation: Constellation<LTF, STF> = Constellation {
chan: constellation_chan_clone,
request_port: constellation_port,
- compositor_chan: compositor_chan,
+ compositor_proxy: compositor_proxy,
devtools_chan: devtools_chan,
resource_task: resource_task,
image_cache_task: image_cache_task,
@@ -293,7 +323,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let pipe = Pipeline::create::<LTF, STF>(id,
subpage_id,
self.chan.clone(),
- self.compositor_chan.clone(),
+ self.compositor_proxy.clone_compositor_proxy(),
self.devtools_chan.clone(),
self.image_cache_task.clone(),
self.font_cache_task.clone(),
@@ -367,7 +397,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// script, and reflow messages have been sent.
LoadCompleteMsg(pipeline_id, url) => {
debug!("constellation got load complete message");
- self.compositor_chan.send(LoadComplete(pipeline_id, url));
+ self.compositor_proxy.send(LoadComplete(pipeline_id, url));
}
// Handle a forward or back request
NavigateMsg(direction) => {
@@ -387,14 +417,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
true
}
- fn handle_exit(&self) {
+ fn handle_exit(&mut self) {
for (_id, ref pipeline) in self.pipelines.iter() {
pipeline.exit();
}
self.image_cache_task.exit();
self.resource_task.send(resource_task::Exit);
self.font_cache_task.exit();
- self.compositor_chan.send(ShutdownComplete);
+ self.compositor_proxy.send(ShutdownComplete);
}
fn handle_failure_msg(&mut self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>) {
@@ -491,38 +521,23 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let frames = self.find_all(pipeline_id);
{
- // Update a child's frame rect and inform its script task of the change,
- // if it hasn't been already. Optionally inform the compositor if
- // resize happens immediately.
- let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
- child_frame_tree.rect = Some(rect);
- // NOTE: work around borrowchk issues
- let pipeline = &child_frame_tree.frame_tree.pipeline;
- if !already_sent.contains(&pipeline.id) {
- if is_active {
- let ScriptControlChan(ref script_chan) = pipeline.script_chan;
- script_chan.send(ResizeMsg(pipeline.id, WindowSizeData {
- visible_viewport: rect.size,
- initial_viewport: rect.size * ScaleFactor(1.0),
- device_pixel_ratio: self.window_size.device_pixel_ratio,
- }));
- self.compositor_chan.send(SetLayerOrigin(pipeline.id,
- LayerId::null(),
- rect.to_untyped().origin));
- } else {
- already_sent.insert(pipeline.id);
- }
- };
- };
-
// If the subframe is in the current frame tree, the compositor needs the new size
- for current_frame in self.current_frame().iter() {
+ for current_frame in self.navigation_context.current.iter() {
debug!("Constellation: Sending size for frame in current frame tree.");
let source_frame = current_frame.find(pipeline_id);
for source_frame in source_frame.iter() {
let mut children = source_frame.children.borrow_mut();
- let found_child = children.iter_mut().find(|child| subpage_eq(child));
- found_child.map(|child| update_child_rect(child, true));
+ match children.iter_mut().find(|child| subpage_eq(child)) {
+ None => {}
+ Some(child) => {
+ update_child_rect(child,
+ rect,
+ true,
+ &mut already_sent,
+ &mut self.compositor_proxy,
+ self.window_size.device_pixel_ratio)
+ }
+ }
}
}
@@ -530,7 +545,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
for frame_tree in frames.iter() {
let mut children = frame_tree.children.borrow_mut();
let found_child = children.iter_mut().find(|child| subpage_eq(child));
- found_child.map(|child| update_child_rect(child, false));
+ found_child.map(|child| {
+ update_child_rect(child,
+ rect,
+ false,
+ &mut already_sent,
+ &mut self.compositor_proxy,
+ self.window_size.device_pixel_ratio)
+ });
}
}
@@ -539,8 +561,38 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
if already_sent.len() == 0 {
self.pending_sizes.insert((pipeline_id, subpage_id), rect);
}
+
+ // Update a child's frame rect and inform its script task of the change,
+ // if it hasn't been already. Optionally inform the compositor if
+ // resize happens immediately.
+ fn update_child_rect(child_frame_tree: &mut ChildFrameTree,
+ rect: TypedRect<PagePx,f32>,
+ is_active: bool,
+ already_sent: &mut HashSet<PipelineId>,
+ compositor_proxy: &mut Box<CompositorProxy>,
+ device_pixel_ratio: ScaleFactor<ViewportPx,DevicePixel,f32>) {
+ child_frame_tree.rect = Some(rect);
+ // NOTE: work around borrowchk issues
+ let pipeline = &child_frame_tree.frame_tree.pipeline;
+ if !already_sent.contains(&pipeline.id) {
+ if is_active {
+ let ScriptControlChan(ref script_chan) = pipeline.script_chan;
+ script_chan.send(ResizeMsg(pipeline.id, WindowSizeData {
+ visible_viewport: rect.size,
+ initial_viewport: rect.size * ScaleFactor(1.0),
+ device_pixel_ratio: device_pixel_ratio,
+ }));
+ compositor_proxy.send(SetLayerOrigin(pipeline.id,
+ LayerId::null(),
+ rect.to_untyped().origin));
+ } else {
+ already_sent.insert(pipeline.id);
+ }
+ };
+ }
}
+
fn handle_load_iframe_url_msg(&mut self,
url: Url,
source_pipeline_id: PipelineId,
@@ -851,10 +903,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
}
}
- fn set_ids(&self, frame_tree: &Rc<FrameTree>) {
+ fn set_ids(&mut self, frame_tree: &Rc<FrameTree>) {
let (chan, port) = channel();
debug!("Constellation sending SetIds");
- self.compositor_chan.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone()));
+ self.compositor_proxy.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone()));
match port.recv_opt() {
Ok(()) => {
let mut iter = frame_tree.iter();
diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs
index a1817560670..648b71034e0 100644
--- a/components/compositing/headless.rs
+++ b/components/compositing/headless.rs
@@ -2,10 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use compositor_task::{Msg, Exit, ChangeReadyState, SetIds};
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
-use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
-use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
+use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds};
+use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
+use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout};
+use windowing::WindowEvent;
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
@@ -21,79 +22,97 @@ use servo_util::time;
/// It's intended for headless testing.
pub struct NullCompositor {
/// The port on which we receive messages.
- pub port: Receiver<Msg>,
+ pub port: Box<CompositorReceiver>,
+ /// A channel to the constellation.
+ constellation_chan: ConstellationChan,
+ /// A channel to the time profiler.
+ time_profiler_chan: TimeProfilerChan,
+ /// A channel to the memory profiler.
+ memory_profiler_chan: MemoryProfilerChan,
}
impl NullCompositor {
- fn new(port: Receiver<Msg>) -> NullCompositor {
+ fn new(port: Box<CompositorReceiver>,
+ constellation_chan: ConstellationChan,
+ time_profiler_chan: TimeProfilerChan,
+ memory_profiler_chan: MemoryProfilerChan)
+ -> NullCompositor {
NullCompositor {
port: port,
+ constellation_chan: constellation_chan,
+ time_profiler_chan: time_profiler_chan,
+ memory_profiler_chan: memory_profiler_chan,
}
}
- pub fn create(port: Receiver<Msg>,
+ pub fn create(port: Box<CompositorReceiver>,
constellation_chan: ConstellationChan,
time_profiler_chan: TimeProfilerChan,
- memory_profiler_chan: MemoryProfilerChan) {
- let compositor = NullCompositor::new(port);
+ memory_profiler_chan: MemoryProfilerChan)
+ -> NullCompositor {
+ let compositor = NullCompositor::new(port,
+ constellation_chan,
+ time_profiler_chan,
+ memory_profiler_chan);
// Tell the constellation about the initial fake size.
{
- let ConstellationChan(ref chan) = constellation_chan;
+ let ConstellationChan(ref chan) = compositor.constellation_chan;
chan.send(ResizedWindowMsg(WindowSizeData {
initial_viewport: TypedSize2D(640_f32, 480_f32),
visible_viewport: TypedSize2D(640_f32, 480_f32),
device_pixel_ratio: ScaleFactor(1.0),
}));
}
- compositor.handle_message(constellation_chan);
- // Drain compositor port, sometimes messages contain channels that are blocking
- // another task from finishing (i.e. SetIds)
- loop {
- match compositor.port.try_recv() {
- Err(_) => break,
- Ok(_) => {},
+ compositor
+ }
+}
+
+impl CompositorEventListener for NullCompositor {
+ fn handle_event(&mut self, _: WindowEvent) -> bool {
+ match self.port.recv_compositor_msg() {
+ Exit(chan) => {
+ debug!("shutting down the constellation");
+ let ConstellationChan(ref con_chan) = self.constellation_chan;
+ con_chan.send(ExitMsg);
+ chan.send(());
}
- }
- time_profiler_chan.send(time::ExitMsg);
- memory_profiler_chan.send(memory::ExitMsg);
- }
+ ShutdownComplete => {
+ debug!("constellation completed shutdown");
+ return false
+ }
+
+ GetGraphicsMetadata(chan) => {
+ chan.send(None);
+ }
- fn handle_message(&self, constellation_chan: ConstellationChan) {
- loop {
- match self.port.recv() {
- Exit(chan) => {
- debug!("shutting down the constellation");
- let ConstellationChan(ref con_chan) = constellation_chan;
- con_chan.send(ExitMsg);
- chan.send(());
- }
-
- ShutdownComplete => {
- debug!("constellation completed shutdown");
- break
- }
-
- GetGraphicsMetadata(chan) => {
- chan.send(None);
- }
-
- SetIds(_, response_chan, _) => {
- response_chan.send(());
- }
-
- // Explicitly list ignored messages so that when we add a new one,
- // we'll notice and think about whether it needs a response, like
- // SetIds.
-
- CreateOrUpdateRootLayer(..) |
- CreateOrUpdateDescendantLayer(..) |
- SetLayerOrigin(..) | Paint(..) |
- ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
- LoadComplete(..) | RenderMsgDiscarded(..) => ()
+ SetIds(_, response_chan, _) => {
+ response_chan.send(());
}
+
+ // Explicitly list ignored messages so that when we add a new one,
+ // we'll notice and think about whether it needs a response, like
+ // SetIds.
+
+ CreateOrUpdateRootLayer(..) |
+ CreateOrUpdateDescendantLayer(..) |
+ SetLayerOrigin(..) | Paint(..) |
+ ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
+ LoadComplete(..) | RenderMsgDiscarded(..) | ScrollTimeout(..) => ()
}
+ true
+ }
+
+ fn repaint_synchronously(&mut self) {}
+
+ fn shutdown(&mut self) {
+ // Drain compositor port, sometimes messages contain channels that are blocking
+ // another task from finishing (i.e. SetIds)
+ while self.port.try_recv_compositor_msg().is_some() {}
+
+ self.time_profiler_chan.send(time::ExitMsg);
+ self.memory_profiler_chan.send(memory::ExitMsg);
}
}
diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs
index e5af7de77ca..550b079f0f9 100644
--- a/components/compositing/lib.rs
+++ b/components/compositing/lib.rs
@@ -30,6 +30,7 @@ extern crate "util" as servo_util;
extern crate gleam;
extern crate libc;
+extern crate native;
extern crate time;
extern crate url;
@@ -38,12 +39,14 @@ extern crate core_graphics;
#[cfg(target_os="macos")]
extern crate core_text;
-pub use compositor_task::{CompositorChan, CompositorTask};
+pub use compositor_task::{CompositorEventListener, CompositorProxy, CompositorTask};
pub use constellation::Constellation;
pub mod compositor_task;
mod compositor_layer;
+mod scrolling;
+
mod compositor;
mod headless;
diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs
index 149fd0bffed..3fadfd90eaa 100644
--- a/components/compositing/pipeline.rs
+++ b/components/compositing/pipeline.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use CompositorChan;
+use CompositorProxy;
use layout_traits::{LayoutTaskFactory, LayoutControlChan};
use script_traits::{ScriptControlChan, ScriptTaskFactory};
use script_traits::{AttachLayoutMsg, LoadMsg, NewLayoutInfo, ExitPipelineMsg};
@@ -47,7 +47,7 @@ impl Pipeline {
id: PipelineId,
subpage_id: Option<SubpageId>,
constellation_chan: ConstellationChan,
- compositor_chan: CompositorChan,
+ compositor_proxy: Box<CompositorProxy+'static+Send>,
devtools_chan: Option<DevtoolsControlChan>,
image_cache_task: ImageCacheTask,
font_cache_task: FontCacheTask,
@@ -73,7 +73,7 @@ impl Pipeline {
let (script_chan, script_port) = channel();
ScriptTaskFactory::create(None::<&mut STF>,
id,
- box compositor_chan.clone(),
+ compositor_proxy.clone_compositor_proxy(),
&layout_pair,
ScriptControlChan(script_chan.clone()),
script_port,
@@ -101,7 +101,7 @@ impl Pipeline {
RenderTask::create(id,
render_port,
- compositor_chan.clone(),
+ compositor_proxy,
constellation_chan.clone(),
font_cache_task.clone(),
failure.clone(),
diff --git a/components/compositing/scrolling.rs b/components/compositing/scrolling.rs
new file mode 100644
index 00000000000..d3dfb6a8fdb
--- /dev/null
+++ b/components/compositing/scrolling.rs
@@ -0,0 +1,73 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! A timer thread that gives the painting task a little time to catch up when the user scrolls.
+
+use compositor_task::{CompositorProxy, ScrollTimeout};
+
+use native::task::NativeTaskBuilder;
+use std::io::timer;
+use std::task::TaskBuilder;
+use std::time::duration::Duration;
+use time;
+
+/// The amount of time in nanoseconds that we give to the painting thread to paint new tiles upon
+/// processing a scroll event that caused new tiles to be revealed. When this expires, we give up
+/// and composite anyway (showing a "checkerboard") to avoid dropping the frame.
+static TIMEOUT: i64 = 12_000_000;
+
+pub struct ScrollingTimerProxy {
+ sender: Sender<ToScrollingTimerMsg>,
+}
+
+pub struct ScrollingTimer {
+ compositor_proxy: Box<CompositorProxy>,
+ receiver: Receiver<ToScrollingTimerMsg>,
+}
+
+enum ToScrollingTimerMsg {
+ ExitMsg,
+ ScrollEventProcessedMsg(u64),
+}
+
+impl ScrollingTimerProxy {
+ pub fn new(compositor_proxy: Box<CompositorProxy+Send>) -> ScrollingTimerProxy {
+ let (to_scrolling_timer_sender, to_scrolling_timer_receiver) = channel();
+ TaskBuilder::new().native().spawn(proc() {
+ let mut scrolling_timer = ScrollingTimer {
+ compositor_proxy: compositor_proxy,
+ receiver: to_scrolling_timer_receiver,
+ };
+ scrolling_timer.run();
+ });
+ ScrollingTimerProxy {
+ sender: to_scrolling_timer_sender,
+ }
+ }
+
+ pub fn scroll_event_processed(&mut self, timestamp: u64) {
+ self.sender.send(ScrollEventProcessedMsg(timestamp))
+ }
+
+ pub fn shutdown(&mut self) {
+ self.sender.send(ExitMsg);
+ }
+}
+
+impl ScrollingTimer {
+ pub fn run(&mut self) {
+ loop {
+ match self.receiver.recv_opt() {
+ Ok(ScrollEventProcessedMsg(timestamp)) => {
+ let target = timestamp as i64 + TIMEOUT;
+ let delta = target - (time::precise_time_ns() as i64);
+ timer::sleep(Duration::nanoseconds(delta));
+ self.compositor_proxy.send(ScrollTimeout(timestamp));
+ }
+ Ok(ExitMsg) | Err(_) => break,
+ }
+ }
+ }
+}
+
diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs
index 915a15d77b8..5815de6e2b5 100644
--- a/components/compositing/windowing.rs
+++ b/components/compositing/windowing.rs
@@ -4,6 +4,8 @@
//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`.
+use compositor_task::{CompositorProxy, CompositorReceiver};
+
use geom::point::TypedPoint2D;
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
@@ -11,6 +13,8 @@ use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use servo_msg::compositor_msg::{ReadyState, RenderState};
use servo_util::geometry::ScreenPx;
+use std::fmt::{FormatError, Formatter, Show};
+use std::rc::Rc;
pub enum MouseWindowEvent {
MouseWindowClickEvent(uint, TypedPoint2D<DevicePixel, f32>),
@@ -25,10 +29,12 @@ pub enum WindowNavigateMsg {
/// Events that the windowing system sends to Servo.
pub enum WindowEvent {
- /// Sent when no message has arrived.
+ /// Sent when no message has arrived, but the event loop was kicked for some reason (perhaps
+ /// by another Servo subsystem).
///
- /// FIXME: This is a bogus event and is only used because we don't have the new
- /// scheduler integrated with the platform event loop.
+ /// FIXME(pcwalton): This is kind of ugly and may not work well with multiprocess Servo.
+ /// It's possible that this should be something like
+ /// `CompositorMessageWindowEvent(compositor_task::Msg)` instead.
IdleWindowEvent,
/// Sent when part of the window is marked dirty and needs to be redrawn.
RefreshWindowEvent,
@@ -54,6 +60,25 @@ pub enum WindowEvent {
QuitWindowEvent,
}
+impl Show for WindowEvent {
+ fn fmt(&self, f: &mut Formatter) -> Result<(),FormatError> {
+ match *self {
+ IdleWindowEvent => write!(f, "Idle"),
+ RefreshWindowEvent => write!(f, "Refresh"),
+ ResizeWindowEvent(..) => write!(f, "Resize"),
+ LoadUrlWindowEvent(..) => write!(f, "LoadUrl"),
+ MouseWindowEventClass(..) => write!(f, "Mouse"),
+ MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
+ ScrollWindowEvent(..) => write!(f, "Scroll"),
+ ZoomWindowEvent(..) => write!(f, "Zoom"),
+ PinchZoomWindowEvent(..) => write!(f, "PinchZoom"),
+ NavigationWindowEvent(..) => write!(f, "Navigation"),
+ FinishedWindowEvent => write!(f, "Finished"),
+ QuitWindowEvent => write!(f, "Quit"),
+ }
+ }
+}
+
pub trait WindowMethods {
/// Returns the size of the window in hardware pixels.
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint>;
@@ -62,9 +87,6 @@ pub trait WindowMethods {
/// Presents the window to the screen (perhaps by page flipping).
fn present(&self);
- /// Spins the event loop and returns the next event.
- fn recv(&self) -> WindowEvent;
-
/// Sets the ready state of the current page.
fn set_ready_state(&self, ready_state: ReadyState);
/// Sets the render state of the current page.
@@ -75,5 +97,13 @@ pub trait WindowMethods {
/// Gets the OS native graphics information for this window.
fn native_metadata(&self) -> NativeGraphicsMetadata;
+
+ /// Creates a channel to the compositor. The dummy parameter is needed because we don't have
+ /// UFCS in Rust yet.
+ ///
+ /// This is part of the windowing system because its implementation often involves OS-specific
+ /// magic to wake the up window's event loop.
+ fn create_compositor_channel(_: &Option<Rc<Self>>)
+ -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>);
}
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs
index bdc136f3730..7caa3835486 100644
--- a/components/gfx/display_list/mod.rs
+++ b/components/gfx/display_list/mod.rs
@@ -19,6 +19,7 @@ use render_context::RenderContext;
use text::glyph::CharIndex;
use text::TextRun;
+use azure::azure::AzFloat;
use collections::Deque;
use collections::dlist::{mod, DList};
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
@@ -26,18 +27,11 @@ use libc::uintptr_t;
use servo_net::image::base::Image;
use servo_util::dlist as servo_dlist;
use servo_util::geometry::Au;
-use servo_util::opts;
use servo_util::range::Range;
use std::fmt;
use std::slice::Items;
use style::computed_values::border_style;
use sync::Arc;
-use std::num::Zero;
-use std::ptr;
-
-use azure::AzFloat;
-use azure::scaled_font::ScaledFont;
-use azure::azure_hl::ColorPattern;
pub mod optimizer;
@@ -58,88 +52,6 @@ impl OpaqueNode {
}
}
-trait ScaledFontExtensionMethods {
- fn draw_text_into_context(&self,
- rctx: &RenderContext,
- run: &Box<TextRun>,
- range: &Range<CharIndex>,
- baseline_origin: Point2D<Au>,
- color: Color,
- antialias: bool);
-}
-
-impl ScaledFontExtensionMethods for ScaledFont {
- fn draw_text_into_context(&self,
- rctx: &RenderContext,
- run: &Box<TextRun>,
- range: &Range<CharIndex>,
- baseline_origin: Point2D<Au>,
- color: Color,
- antialias: bool) {
- use libc::types::common::c99::uint32_t;
- use azure::{struct__AzDrawOptions,
- struct__AzGlyph,
- struct__AzGlyphBuffer,
- struct__AzPoint};
- use azure::azure::{AzDrawTargetFillGlyphs};
-
- let target = rctx.get_draw_target();
- let pattern = ColorPattern::new(color);
- let azure_pattern = pattern.azure_color_pattern;
- assert!(azure_pattern.is_not_null());
-
- let fields = if antialias {
- 0x0200
- } else {
- 0
- };
-
- let mut options = struct__AzDrawOptions {
- mAlpha: 1f64 as AzFloat,
- fields: fields,
- };
-
- let mut origin = baseline_origin.clone();
- let mut azglyphs = vec!();
- azglyphs.reserve(range.length().to_uint());
-
- for (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) {
- for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) {
- let glyph_advance = glyph.advance();
- let glyph_offset = glyph.offset().unwrap_or(Zero::zero());
-
- let azglyph = struct__AzGlyph {
- mIndex: glyph.id() as uint32_t,
- mPosition: struct__AzPoint {
- x: (origin.x + glyph_offset.x).to_subpx() as AzFloat,
- y: (origin.y + glyph_offset.y).to_subpx() as AzFloat
- }
- };
- origin = Point2D(origin.x + glyph_advance, origin.y);
- azglyphs.push(azglyph)
- };
- }
-
- let azglyph_buf_len = azglyphs.len();
- if azglyph_buf_len == 0 { return; } // Otherwise the Quartz backend will assert.
-
- let mut glyphbuf = struct__AzGlyphBuffer {
- mGlyphs: azglyphs.as_mut_ptr(),
- mNumGlyphs: azglyph_buf_len as uint32_t
- };
-
- unsafe {
- // TODO(Issue #64): this call needs to move into azure_hl.rs
- AzDrawTargetFillGlyphs(target.azure_draw_target,
- self.get_ref(),
- &mut glyphbuf,
- azure_pattern,
- &mut options,
- ptr::null_mut());
- }
- }
-}
-
/// "Steps" as defined by CSS 2.1 § E.2.
#[deriving(Clone, PartialEq, Show)]
pub enum StackingLevel {
@@ -543,55 +455,7 @@ impl DisplayItem {
TextDisplayItemClass(ref text) => {
debug!("Drawing text at {}.", text.base.bounds);
-
- // Optimization: Don’t set a transform matrix for upright text,
- // and pass a strart point to `draw_text_into_context`.
- // For sideways text, it’s easier to do the rotation such that its center
- // (the baseline’s start point) is at (0, 0) coordinates.
- let baseline_origin = match text.orientation {
- Upright => text.baseline_origin,
- SidewaysLeft => {
- let x = text.baseline_origin.x.to_nearest_px() as AzFloat;
- let y = text.baseline_origin.y.to_nearest_px() as AzFloat;
- render_context.draw_target.set_transform(&current_transform.mul(
- &Matrix2D::new(
- 0., -1.,
- 1., 0.,
- x, y
- )
- ));
- Zero::zero()
- },
- SidewaysRight => {
- let x = text.baseline_origin.x.to_nearest_px() as AzFloat;
- let y = text.baseline_origin.y.to_nearest_px() as AzFloat;
- render_context.draw_target.set_transform(&current_transform.mul(
- &Matrix2D::new(
- 0., 1.,
- -1., 0.,
- x, y
- )
- ));
- Zero::zero()
- }
- };
-
- render_context.font_ctx.get_render_font_from_template(
- &text.text_run.font_template,
- text.text_run.actual_pt_size
- ).borrow().draw_text_into_context(
- render_context,
- &*text.text_run,
- &text.range,
- baseline_origin,
- text.text_color,
- opts::get().enable_text_antialiasing
- );
-
- // Undo the transform, only when we did one.
- if text.orientation != Upright {
- render_context.draw_target.set_transform(current_transform)
- }
+ render_context.draw_text(&**text, current_transform);
}
ImageDisplayItemClass(ref image_item) => {
diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs
index 28312a09440..b5f8c3e961d 100644
--- a/components/gfx/lib.rs
+++ b/components/gfx/lib.rs
@@ -30,6 +30,7 @@ extern crate "util" as servo_util;
extern crate "msg" as servo_msg;
extern crate style;
extern crate sync;
+extern crate time;
extern crate url;
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
diff --git a/components/gfx/render_context.rs b/components/gfx/render_context.rs
index e2c893890c1..b682b8f5f02 100644
--- a/components/gfx/render_context.rs
+++ b/components/gfx/render_context.rs
@@ -2,23 +2,32 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+use display_list::{SidewaysLeft, SidewaysRight, TextDisplayItem, Upright};
use font_context::FontContext;
use style::computed_values::border_style;
use azure::azure_hl::{B8G8R8A8, A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
-use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, SourceOp, StrokeOptions};
-use azure::AZ_CAP_BUTT;
-use azure::AzFloat;
+use azure::azure_hl::{DrawSurfaceOptions,DrawTarget, Linear, SourceOp, StrokeOptions};
+use azure::scaled_font::ScaledFont;
+use azure::{AZ_CAP_BUTT, AzDrawTargetFillGlyphs, AzFloat, struct__AzDrawOptions, struct__AzGlyph};
+use azure::{struct__AzGlyphBuffer, struct__AzPoint};
+use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::side_offsets::SideOffsets2D;
-use libc::types::common::c99::uint16_t;
+use libc::types::common::c99::{uint16_t, uint32_t};
use libc::size_t;
use png::{RGB8, RGBA8, K8, KA8};
use servo_net::image::base::Image;
use servo_util::geometry::Au;
+use servo_util::opts;
+use servo_util::range::Range;
+use std::num::Zero;
+use std::ptr;
use sync::Arc;
+use text::glyph::CharIndex;
+use text::TextRun;
pub struct RenderContext<'a> {
pub draw_target: &'a DrawTarget,
@@ -390,6 +399,50 @@ impl<'a> RenderContext<'a> {
self.draw_border_path(original_bounds, direction, border, scaled_color);
}
+ pub fn draw_text(&mut self,
+ text: &TextDisplayItem,
+ current_transform: &Matrix2D<AzFloat>) {
+ // Optimization: Don’t set a transform matrix for upright text, and pass a start point to
+ // `draw_text_into_context`.
+ //
+ // For sideways text, it’s easier to do the rotation such that its center (the baseline’s
+ // start point) is at (0, 0) coordinates.
+ let baseline_origin = match text.orientation {
+ Upright => text.baseline_origin,
+ SidewaysLeft => {
+ let x = text.baseline_origin.x.to_subpx() as AzFloat;
+ let y = text.baseline_origin.y.to_subpx() as AzFloat;
+ self.draw_target.set_transform(&current_transform.mul(&Matrix2D::new(0., -1.,
+ 1., 0.,
+ x, y)));
+ Zero::zero()
+ }
+ SidewaysRight => {
+ let x = text.baseline_origin.x.to_subpx() as AzFloat;
+ let y = text.baseline_origin.y.to_subpx() as AzFloat;
+ self.draw_target.set_transform(&current_transform.mul(&Matrix2D::new(0., 1.,
+ -1., 0.,
+ x, y)));
+ Zero::zero()
+ }
+ };
+
+ self.font_ctx
+ .get_render_font_from_template(&text.text_run.font_template,
+ text.text_run.actual_pt_size)
+ .borrow()
+ .draw_text_into_context(self,
+ &*text.text_run,
+ &text.range,
+ baseline_origin,
+ text.text_color,
+ opts::get().enable_text_antialiasing);
+
+ // Undo the transform, only when we did one.
+ if text.orientation != Upright {
+ self.draw_target.set_transform(current_transform)
+ }
+ }
}
trait ToAzureRect {
@@ -417,3 +470,78 @@ impl ToSideOffsetsPx for SideOffsets2D<Au> {
self.left.to_nearest_px() as AzFloat)
}
}
+
+trait ScaledFontExtensionMethods {
+ fn draw_text_into_context(&self,
+ rctx: &RenderContext,
+ run: &Box<TextRun>,
+ range: &Range<CharIndex>,
+ baseline_origin: Point2D<Au>,
+ color: Color,
+ antialias: bool);
+}
+
+impl ScaledFontExtensionMethods for ScaledFont {
+ fn draw_text_into_context(&self,
+ rctx: &RenderContext,
+ run: &Box<TextRun>,
+ range: &Range<CharIndex>,
+ baseline_origin: Point2D<Au>,
+ color: Color,
+ antialias: bool) {
+ let target = rctx.get_draw_target();
+ let pattern = ColorPattern::new(color);
+ let azure_pattern = pattern.azure_color_pattern;
+ assert!(azure_pattern.is_not_null());
+
+ let fields = if antialias {
+ 0x0200
+ } else {
+ 0
+ };
+
+ let mut options = struct__AzDrawOptions {
+ mAlpha: 1f64 as AzFloat,
+ fields: fields,
+ };
+
+ let mut origin = baseline_origin.clone();
+ let mut azglyphs = vec!();
+ azglyphs.reserve(range.length().to_uint());
+
+ for (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) {
+ for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) {
+ let glyph_advance = glyph.advance();
+ let glyph_offset = glyph.offset().unwrap_or(Zero::zero());
+ let azglyph = struct__AzGlyph {
+ mIndex: glyph.id() as uint32_t,
+ mPosition: struct__AzPoint {
+ x: (origin.x + glyph_offset.x).to_subpx() as AzFloat,
+ y: (origin.y + glyph_offset.y).to_subpx() as AzFloat
+ }
+ };
+ origin = Point2D(origin.x + glyph_advance, origin.y);
+ azglyphs.push(azglyph)
+ };
+ }
+
+ let azglyph_buf_len = azglyphs.len();
+ if azglyph_buf_len == 0 { return; } // Otherwise the Quartz backend will assert.
+
+ let mut glyphbuf = struct__AzGlyphBuffer {
+ mGlyphs: azglyphs.as_mut_ptr(),
+ mNumGlyphs: azglyph_buf_len as uint32_t
+ };
+
+ unsafe {
+ // TODO(Issue #64): this call needs to move into azure_hl.rs
+ AzDrawTargetFillGlyphs(target.azure_draw_target,
+ self.get_ref(),
+ &mut glyphbuf,
+ azure_pattern,
+ &mut options,
+ ptr::null_mut());
+ }
+ }
+}
+
diff --git a/components/gfx/render_task.rs b/components/gfx/render_task.rs
index 0662eef3d99..9eeeed51000 100644
--- a/components/gfx/render_task.rs
+++ b/components/gfx/render_task.rs
@@ -7,6 +7,7 @@
use buffer_map::BufferMap;
use display_list::optimizer::DisplayListOptimizer;
use display_list::DisplayList;
+use font_cache_task::FontCacheTask;
use font_context::FontContext;
use render_context::RenderContext;
@@ -37,7 +38,6 @@ use std::comm::{Receiver, Sender, channel};
use std::mem;
use std::task::TaskBuilder;
use sync::Arc;
-use font_cache_task::FontCacheTask;
/// Information about a layer that layout sends to the painting task.
#[deriving(Clone)]
@@ -153,7 +153,10 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
shutdown_chan: Sender<()>) {
let ConstellationChan(c) = constellation_chan.clone();
spawn_named_with_send_on_failure("RenderTask", task_state::Render, proc() {
- { // Ensures RenderTask and graphics context are destroyed before shutdown msg
+ {
+ // Ensures that the render task and graphics context are destroyed before the
+ // shutdown message.
+ let mut compositor = compositor;
let native_graphics_context = compositor.get_graphics_metadata().map(
|md| NativePaintingGraphicsContext::from_metadata(&md));
let worker_threads = WorkerThreadProxy::spawn(compositor.get_graphics_metadata(),
diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs
index c8713d7a119..a4cc14a12ea 100644
--- a/components/msg/compositor_msg.rs
+++ b/components/msg/compositor_msg.rs
@@ -84,36 +84,36 @@ pub struct LayerMetadata {
/// The interface used by the renderer to acquire draw targets for each render frame and
/// submit them to be drawn to the display.
-pub trait RenderListener {
- fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>;
+pub trait RenderListener for Sized? {
+ fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata>;
/// Informs the compositor of the layers for the given pipeline. The compositor responds by
/// creating and/or destroying render layers as necessary.
- fn initialize_layers_for_pipeline(&self,
+ fn initialize_layers_for_pipeline(&mut self,
pipeline_id: PipelineId,
metadata: Vec<LayerMetadata>,
epoch: Epoch);
/// Sends new tiles for the given layer to the compositor.
- fn paint(&self,
+ fn paint(&mut self,
pipeline_id: PipelineId,
epoch: Epoch,
replies: Vec<(LayerId, Box<LayerBufferSet>)>);
- fn render_msg_discarded(&self);
- fn set_render_state(&self, PipelineId, RenderState);
+ fn render_msg_discarded(&mut self);
+ fn set_render_state(&mut self, PipelineId, RenderState);
}
/// The interface used by the script task to tell the compositor to update its ready state,
/// which is used in displaying the appropriate message in the window's title.
-pub trait ScriptListener : Clone {
- fn set_ready_state(&self, PipelineId, ReadyState);
- fn scroll_fragment_point(&self,
+pub trait ScriptListener {
+ fn set_ready_state(&mut self, PipelineId, ReadyState);
+ fn scroll_fragment_point(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
point: Point2D<f32>);
- fn close(&self);
- fn dup(&self) -> Box<ScriptListener+'static>;
+ fn close(&mut self);
+ fn dup(&mut self) -> Box<ScriptListener+'static>;
}
impl<E, S: Encoder<E>> Encodable<S, E> for Box<ScriptListener+'static> {
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 737ef3f4d98..00357752ee1 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-//! The high-level interface from script to constellation. Using this abstract interface helps reduce
-//! coupling between these two components
+//! The high-level interface from script to constellation. Using this abstract interface helps
+//! reduce coupling between these two components.
use geom::rect::Rect;
use geom::size::TypedSize2D;
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index c9eeedc2a2a..90c18f5dca5 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::bindings::cell::{DOMRefCell, Ref};
+use dom::bindings::cell::{DOMRefCell, Ref, RefMut};
use dom::bindings::codegen::Bindings::EventHandlerBinding::{OnErrorEventHandlerNonNull, EventHandlerNonNull};
use dom::bindings::codegen::Bindings::WindowBinding;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
@@ -53,7 +53,7 @@ pub struct Window {
location: MutNullableJS<Location>,
navigator: MutNullableJS<Navigator>,
image_cache_task: ImageCacheTask,
- compositor: Box<ScriptListener+'static>,
+ compositor: DOMRefCell<Box<ScriptListener+'static>>,
browser_context: DOMRefCell<Option<BrowserContext>>,
page: Rc<Page>,
performance: MutNullableJS<Performance>,
@@ -81,8 +81,8 @@ impl Window {
&self.image_cache_task
}
- pub fn compositor<'a>(&'a self) -> &'a ScriptListener+'static {
- &*self.compositor
+ pub fn compositor(&self) -> RefMut<Box<ScriptListener+'static>> {
+ self.compositor.borrow_mut()
}
pub fn browser_context(&self) -> Ref<Option<BrowserContext>> {
@@ -398,7 +398,7 @@ impl Window {
script_chan: script_chan,
control_chan: control_chan,
console: Default::default(),
- compositor: compositor,
+ compositor: DOMRefCell::new(compositor),
page: page,
location: Default::default(),
navigator: Default::default(),
diff --git a/components/script/page.rs b/components/script/page.rs
index 01b396126ad..4e87ebe9050 100644
--- a/components/script/page.rs
+++ b/components/script/page.rs
@@ -182,7 +182,7 @@ impl Page {
if force_reflow {
let frame = self.frame();
let window = frame.as_ref().unwrap().window.root();
- self.reflow(reflow_goal, window.control_chan().clone(), window.compositor(), query);
+ self.reflow(reflow_goal, window.control_chan().clone(), &mut **window.compositor(), query);
} else {
self.avoided_reflows.set(self.avoided_reflows.get() + 1);
}
@@ -328,9 +328,8 @@ impl Page {
pub fn reflow(&self,
goal: ReflowGoal,
script_chan: ScriptControlChan,
- compositor: &ScriptListener,
+ compositor: &mut ScriptListener,
query_type: ReflowQueryType) {
-
let root = match *self.frame() {
None => return,
Some(ref frame) => {
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 5dd6f7c2d02..8595ebf0737 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -168,7 +168,7 @@ pub struct ScriptTask {
/// For communicating load url messages to the constellation
constellation_chan: ConstellationChan,
/// A handle to the compositor for communicating ready state messages.
- compositor: Box<ScriptListener+'static>,
+ compositor: DOMRefCell<Box<ScriptListener+'static>>,
/// For providing instructions to an optional devtools server.
devtools_chan: Option<DevtoolsControlChan>,
@@ -248,25 +248,25 @@ impl ScriptTaskFactory for ScriptTask {
box pair.sender() as Box<Any+Send>
}
- fn create<C:ScriptListener + Send + 'static>(
- _phantom: Option<&mut ScriptTask>,
- id: PipelineId,
- compositor: Box<C>,
- layout_chan: &OpaqueScriptLayoutChannel,
- control_chan: ScriptControlChan,
- control_port: Receiver<ConstellationControlMsg>,
- constellation_chan: ConstellationChan,
- failure_msg: Failure,
- resource_task: ResourceTask,
- image_cache_task: ImageCacheTask,
- devtools_chan: Option<DevtoolsControlChan>,
- window_size: WindowSizeData) {
+ fn create<C>(_phantom: Option<&mut ScriptTask>,
+ id: PipelineId,
+ compositor: C,
+ layout_chan: &OpaqueScriptLayoutChannel,
+ control_chan: ScriptControlChan,
+ control_port: Receiver<ConstellationControlMsg>,
+ constellation_chan: ConstellationChan,
+ failure_msg: Failure,
+ resource_task: ResourceTask,
+ image_cache_task: ImageCacheTask,
+ devtools_chan: Option<DevtoolsControlChan>,
+ window_size: WindowSizeData)
+ where C: ScriptListener + Send + 'static {
let ConstellationChan(const_chan) = constellation_chan.clone();
let (script_chan, script_port) = channel();
let layout_chan = LayoutChan(layout_chan.sender());
spawn_named_with_send_on_failure("ScriptTask", task_state::Script, proc() {
let script_task = ScriptTask::new(id,
- compositor as Box<ScriptListener>,
+ box compositor as Box<ScriptListener>,
layout_chan,
script_port,
ScriptChan(script_chan),
@@ -350,7 +350,7 @@ impl ScriptTask {
control_chan: control_chan,
control_port: control_port,
constellation_chan: constellation_chan,
- compositor: compositor,
+ compositor: DOMRefCell::new(compositor),
devtools_chan: devtools_chan,
devtools_port: devtools_receiver,
@@ -671,7 +671,7 @@ impl ScriptTask {
*layout_join_port = None;
}
- self.compositor.set_ready_state(pipeline_id, FinishedLoading);
+ self.compositor.borrow_mut().set_ready_state(pipeline_id, FinishedLoading);
if page.pending_reflows.get() > 0 {
page.pending_reflows.set(0);
@@ -709,7 +709,7 @@ impl ScriptTask {
// TODO(tkuehn): currently there is only one window,
// so this can afford to be naive and just shut down the
// compositor. In the future it'll need to be smarter.
- self.compositor.close();
+ self.compositor.borrow_mut().close();
}
/// Handles a request to exit the script task and shut down layout.
@@ -771,7 +771,7 @@ impl ScriptTask {
page.clone(),
self.chan.clone(),
self.control_chan.clone(),
- self.compositor.dup(),
+ self.compositor.borrow_mut().dup(),
self.image_cache_task.clone()).root();
let doc_url = if is_javascript {
let doc_url = last_url.unwrap_or_else(|| {
@@ -787,7 +787,7 @@ impl ScriptTask {
window.init_browser_context(*document);
- self.compositor.set_ready_state(pipeline_id, Loading);
+ self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading);
let parser_input = if !is_javascript {
InputUrl(url.clone())
@@ -858,7 +858,7 @@ impl ScriptTask {
// Really what needs to happen is that this needs to go through layout to ask which
// layer the element belongs to, and have it send the scroll message to the
// compositor.
- self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point);
+ self.compositor.borrow_mut().scroll_fragment_point(pipeline_id, LayerId::null(), point);
}
fn force_reflow(&self, page: &Page) {
@@ -873,7 +873,10 @@ impl ScriptTask {
}
page.damage();
- page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor, NoQuery);
+ page.reflow(ReflowForDisplay,
+ self.control_chan.clone(),
+ &mut **self.compositor.borrow_mut(),
+ NoQuery);
}
/// This is the main entry point for receiving and dispatching DOM events.
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 918739d2b88..0caf54ba341 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -89,18 +89,20 @@ impl<S: Encoder<E>, E> Encodable<S, E> for ScriptControlChan {
}
pub trait ScriptTaskFactory {
- fn create<C: ScriptListener + Send>(_phantom: Option<&mut Self>,
- id: PipelineId,
- compositor: Box<C>,
- layout_chan: &OpaqueScriptLayoutChannel,
- control_chan: ScriptControlChan,
- control_port: Receiver<ConstellationControlMsg>,
- constellation_msg: ConstellationChan,
- failure_msg: Failure,
- resource_task: ResourceTask,
- image_cache_task: ImageCacheTask,
- devtools_chan: Option<DevtoolsControlChan>,
- window_size: WindowSizeData);
+ fn create<C>(_phantom: Option<&mut Self>,
+ id: PipelineId,
+ compositor: C,
+ layout_chan: &OpaqueScriptLayoutChannel,
+ control_chan: ScriptControlChan,
+ control_port: Receiver<ConstellationControlMsg>,
+ constellation_msg: ConstellationChan,
+ failure_msg: Failure,
+ resource_task: ResourceTask,
+ image_cache_task: ImageCacheTask,
+ devtools_chan: Option<DevtoolsControlChan>,
+ window_size: WindowSizeData)
+ where C: ScriptListener + Send;
fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel;
- fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel) -> Box<Any+Send>;
+ fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel)
+ -> Box<Any+Send>;
}
diff --git a/components/util/time.rs b/components/util/time.rs
index 232e73b3802..b4a60da36ef 100644
--- a/components/util/time.rs
+++ b/components/util/time.rs
@@ -4,13 +4,13 @@
//! Timing functions.
-use std_time::precise_time_ns;
use collections::treemap::TreeMap;
use std::comm::{Sender, channel, Receiver};
use std::f64;
-use std::iter::AdditiveIterator;
use std::io::timer::sleep;
+use std::iter::AdditiveIterator;
use std::time::duration::Duration;
+use std_time::precise_time_ns;
use task::{spawn_named};
use url::Url;