mirror of
https://github.com/servo/servo.git
synced 2025-07-29 10:10:34 +01:00
Invert control flow, fix resizing, and improve checkerboarding
significantly by giving tiles some time to paint before we render unrendered content.
This commit is contained in:
parent
e483a189a3
commit
10f7b49cf7
27 changed files with 1195 additions and 678 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -221,14 +221,14 @@ name = "glfw"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b"
|
source = "git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae)",
|
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674)",
|
||||||
"semver 0.0.1 (git+https://github.com/rust-lang/semver#d04583a173395b76c1eaa15cc630a5f6f8f0ae10)",
|
"semver 0.0.1 (git+https://github.com/rust-lang/semver#d04583a173395b76c1eaa15cc630a5f6f8f0ae10)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glfw-sys"
|
name = "glfw-sys"
|
||||||
version = "3.0.4"
|
version = "3.0.4"
|
||||||
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae"
|
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glfw_app"
|
name = "glfw_app"
|
||||||
|
|
|
@ -7,21 +7,23 @@ use compositor_layer::{ScrollPositionChanged, WantsScrollEvents};
|
||||||
use compositor_task::{Msg, CompositorTask, Exit, ChangeReadyState, SetIds, LayerProperties};
|
use compositor_task::{Msg, CompositorTask, Exit, ChangeReadyState, SetIds, LayerProperties};
|
||||||
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
|
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
|
||||||
use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
|
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 constellation::SendableFrameTree;
|
||||||
use pipeline::CompositionPipeline;
|
use pipeline::CompositionPipeline;
|
||||||
|
use scrolling::ScrollingTimerProxy;
|
||||||
use windowing;
|
use windowing;
|
||||||
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
|
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
|
||||||
use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
|
use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
|
||||||
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
|
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
|
||||||
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
|
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
|
||||||
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
|
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
|
||||||
use windowing::PinchZoomWindowEvent;
|
use windowing::{PinchZoomWindowEvent};
|
||||||
|
|
||||||
use azure::azure_hl;
|
use azure::azure_hl;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use std::mem;
|
||||||
use std::num::Zero;
|
use std::num::Zero;
|
||||||
use std::time::duration::Duration;
|
|
||||||
use geom::point::{Point2D, TypedPoint2D};
|
use geom::point::{Point2D, TypedPoint2D};
|
||||||
use geom::rect::{Rect, TypedRect};
|
use geom::rect::{Rect, TypedRect};
|
||||||
use geom::size::TypedSize2D;
|
use geom::size::TypedSize2D;
|
||||||
|
@ -45,20 +47,18 @@ use servo_util::memory::MemoryProfilerChan;
|
||||||
use servo_util::opts;
|
use servo_util::opts;
|
||||||
use servo_util::time::{profile, TimeProfilerChan};
|
use servo_util::time::{profile, TimeProfilerChan};
|
||||||
use servo_util::{memory, time};
|
use servo_util::{memory, time};
|
||||||
use std::io::timer::sleep;
|
|
||||||
use std::collections::hashmap::HashMap;
|
use std::collections::hashmap::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use time::precise_time_s;
|
use time::{precise_time_ns, precise_time_s};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
||||||
pub struct IOCompositor<Window: WindowMethods> {
|
pub struct IOCompositor<Window: WindowMethods> {
|
||||||
/// The application window.
|
/// The application window.
|
||||||
window: Rc<Window>,
|
window: Rc<Window>,
|
||||||
|
|
||||||
/// The port on which we receive messages.
|
/// The port on which we receive messages.
|
||||||
port: Receiver<Msg>,
|
port: Box<CompositorReceiver>,
|
||||||
|
|
||||||
/// The render context.
|
/// The render context.
|
||||||
context: RenderContext,
|
context: RenderContext,
|
||||||
|
@ -82,20 +82,23 @@ pub struct IOCompositor<Window: WindowMethods> {
|
||||||
/// The device pixel ratio for this window.
|
/// The device pixel ratio for this window.
|
||||||
hidpi_factor: ScaleFactor<ScreenPx, DevicePixel, f32>,
|
hidpi_factor: ScaleFactor<ScreenPx, DevicePixel, f32>,
|
||||||
|
|
||||||
/// Tracks whether the renderer has finished its first rendering
|
/// A handle to the scrolling timer.
|
||||||
composite_ready: bool,
|
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
|
/// Tracks whether we are in the process of shutting down, or have shut down and should close
|
||||||
/// the compositor.
|
/// the compositor.
|
||||||
shutdown_state: ShutdownState,
|
shutdown_state: ShutdownState,
|
||||||
|
|
||||||
/// Tracks whether we need to re-composite a page.
|
|
||||||
recomposite: bool,
|
|
||||||
|
|
||||||
/// Tracks outstanding render_msg's sent to the render tasks.
|
/// Tracks outstanding render_msg's sent to the render tasks.
|
||||||
outstanding_render_msgs: uint,
|
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,
|
zoom_action: bool,
|
||||||
|
|
||||||
/// The time of the last zoom action has started.
|
/// The time of the last zoom action has started.
|
||||||
|
@ -112,6 +115,9 @@ pub struct IOCompositor<Window: WindowMethods> {
|
||||||
/// many times for a single page.
|
/// many times for a single page.
|
||||||
got_load_complete_message: bool,
|
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.
|
/// The channel on which messages can be sent to the constellation.
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
|
|
||||||
|
@ -122,10 +128,25 @@ pub struct IOCompositor<Window: WindowMethods> {
|
||||||
memory_profiler_chan: MemoryProfilerChan,
|
memory_profiler_chan: MemoryProfilerChan,
|
||||||
|
|
||||||
/// Pending scroll to fragment event, if any
|
/// 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)]
|
#[deriving(PartialEq)]
|
||||||
|
enum CompositionRequest {
|
||||||
|
NoCompositingNecessary,
|
||||||
|
CompositeOnScrollTimeout(u64),
|
||||||
|
CompositeNow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(PartialEq, Show)]
|
||||||
enum ShutdownState {
|
enum ShutdownState {
|
||||||
NotShuttingDown,
|
NotShuttingDown,
|
||||||
ShuttingDown,
|
ShuttingDown,
|
||||||
|
@ -139,11 +160,12 @@ struct HitTestResult {
|
||||||
|
|
||||||
impl<Window: WindowMethods> IOCompositor<Window> {
|
impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
fn new(window: Rc<Window>,
|
fn new(window: Rc<Window>,
|
||||||
port: Receiver<Msg>,
|
sender: Box<CompositorProxy+Send>,
|
||||||
|
receiver: Box<CompositorReceiver>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
memory_profiler_chan: MemoryProfilerChan) -> IOCompositor<Window> {
|
memory_profiler_chan: MemoryProfilerChan)
|
||||||
|
-> IOCompositor<Window> {
|
||||||
// Create an initial layer tree.
|
// Create an initial layer tree.
|
||||||
//
|
//
|
||||||
// TODO: There should be no initial layer tree until the renderer creates one from the
|
// 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;
|
let show_debug_borders = opts::get().show_debug_borders;
|
||||||
IOCompositor {
|
IOCompositor {
|
||||||
window: window,
|
window: window,
|
||||||
port: port,
|
port: receiver,
|
||||||
context: rendergl::RenderContext::new(context, show_debug_borders),
|
context: rendergl::RenderContext::new(context, show_debug_borders),
|
||||||
root_pipeline: None,
|
root_pipeline: None,
|
||||||
scene: Scene::new(Rect {
|
scene: Scene::new(Rect {
|
||||||
|
@ -164,9 +186,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
}),
|
}),
|
||||||
window_size: window_size,
|
window_size: window_size,
|
||||||
hidpi_factor: hidpi_factor,
|
hidpi_factor: hidpi_factor,
|
||||||
composite_ready: false,
|
scrolling_timer: ScrollingTimerProxy::new(sender),
|
||||||
|
composition_request: NoCompositingNecessary,
|
||||||
|
pending_scroll_events: Vec::new(),
|
||||||
shutdown_state: NotShuttingDown,
|
shutdown_state: NotShuttingDown,
|
||||||
recomposite: false,
|
|
||||||
page_zoom: ScaleFactor(1.0),
|
page_zoom: ScaleFactor(1.0),
|
||||||
viewport_zoom: ScaleFactor(1.0),
|
viewport_zoom: ScaleFactor(1.0),
|
||||||
zoom_action: false,
|
zoom_action: false,
|
||||||
|
@ -174,167 +197,129 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
ready_states: HashMap::new(),
|
ready_states: HashMap::new(),
|
||||||
render_states: HashMap::new(),
|
render_states: HashMap::new(),
|
||||||
got_load_complete_message: false,
|
got_load_complete_message: false,
|
||||||
|
got_set_ids_message: false,
|
||||||
constellation_chan: constellation_chan,
|
constellation_chan: constellation_chan,
|
||||||
time_profiler_chan: time_profiler_chan,
|
time_profiler_chan: time_profiler_chan,
|
||||||
memory_profiler_chan: memory_profiler_chan,
|
memory_profiler_chan: memory_profiler_chan,
|
||||||
fragment_point: None,
|
fragment_point: None,
|
||||||
outstanding_render_msgs: 0,
|
outstanding_render_msgs: 0,
|
||||||
|
last_composite_time: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(window: Rc<Window>,
|
pub fn create(window: Rc<Window>,
|
||||||
port: Receiver<Msg>,
|
sender: Box<CompositorProxy+Send>,
|
||||||
|
receiver: Box<CompositorReceiver>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
memory_profiler_chan: MemoryProfilerChan) {
|
memory_profiler_chan: MemoryProfilerChan)
|
||||||
|
-> IOCompositor<Window> {
|
||||||
let mut compositor = IOCompositor::new(window,
|
let mut compositor = IOCompositor::new(window,
|
||||||
port,
|
sender,
|
||||||
|
receiver,
|
||||||
constellation_chan,
|
constellation_chan,
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
memory_profiler_chan);
|
memory_profiler_chan);
|
||||||
|
|
||||||
|
// Set the size of the root layer.
|
||||||
compositor.update_zoom_transform();
|
compositor.update_zoom_transform();
|
||||||
|
|
||||||
// Starts the compositor, which listens for messages on the specified port.
|
|
||||||
compositor.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run (&mut self) {
|
|
||||||
// Tell the constellation about the initial window size.
|
// Tell the constellation about the initial window size.
|
||||||
self.send_window_size();
|
compositor.send_window_size();
|
||||||
|
|
||||||
// Enter the main event loop.
|
compositor
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for messages coming from the windowing system.
|
|
||||||
let msg = self.window.recv();
|
|
||||||
self.handle_window_message(msg);
|
|
||||||
|
|
||||||
// If asked to recomposite and renderer has run at least once
|
|
||||||
if self.recomposite && self.composite_ready {
|
|
||||||
self.recomposite = false;
|
|
||||||
self.composite();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(_) => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
fn handle_browser_message(&mut self, msg: Msg) -> bool {
|
||||||
loop {
|
match (msg, self.shutdown_state) {
|
||||||
match (self.port.try_recv(), self.shutdown_state) {
|
(_, FinishedShuttingDown) =>
|
||||||
(_, FinishedShuttingDown) =>
|
fail!("compositor shouldn't be handling messages after shutting down"),
|
||||||
fail!("compositor shouldn't be handling messages after shutting down"),
|
|
||||||
|
|
||||||
(Err(_), _) => break,
|
(Exit(chan), _) => {
|
||||||
|
debug!("shutting down the constellation");
|
||||||
(Ok(Exit(chan)), _) => {
|
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
||||||
debug!("shutting down the constellation");
|
con_chan.send(ExitMsg);
|
||||||
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
chan.send(());
|
||||||
con_chan.send(ExitMsg);
|
self.shutdown_state = ShuttingDown;
|
||||||
chan.send(());
|
|
||||||
self.shutdown_state = ShuttingDown;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(ShutdownComplete), _) => {
|
|
||||||
debug!("constellation completed shutdown");
|
|
||||||
self.shutdown_state = FinishedShuttingDown;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(ChangeReadyState(pipeline_id, ready_state)), NotShuttingDown) => {
|
|
||||||
self.change_ready_state(pipeline_id, ready_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(ChangeRenderState(pipeline_id, render_state)), NotShuttingDown) => {
|
|
||||||
self.change_render_state(pipeline_id, render_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(RenderMsgDiscarded), NotShuttingDown) => {
|
|
||||||
self.remove_outstanding_render_msg();
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
|
|
||||||
self.set_frame_tree(&frame_tree,
|
|
||||||
response_chan,
|
|
||||||
new_constellation_chan);
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(GetGraphicsMetadata(chan)), NotShuttingDown) => {
|
|
||||||
chan.send(Some(self.window.native_metadata()));
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(CreateOrUpdateRootLayer(layer_properties)), NotShuttingDown) => {
|
|
||||||
self.create_or_update_root_layer(layer_properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(CreateOrUpdateDescendantLayer(layer_properties)), NotShuttingDown) => {
|
|
||||||
self.create_or_update_descendant_layer(layer_properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
(Ok(SetLayerOrigin(pipeline_id, layer_id, origin)), NotShuttingDown) => {
|
|
||||||
self.set_layer_origin(pipeline_id, layer_id, origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
(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);
|
|
||||||
}
|
|
||||||
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) => { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(ShutdownComplete, _) => {
|
||||||
|
debug!("constellation completed shutdown");
|
||||||
|
self.shutdown_state = FinishedShuttingDown;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
(ChangeReadyState(pipeline_id, ready_state), NotShuttingDown) => {
|
||||||
|
self.change_ready_state(pipeline_id, ready_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
(ChangeRenderState(pipeline_id, render_state), NotShuttingDown) => {
|
||||||
|
self.change_render_state(pipeline_id, render_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
(RenderMsgDiscarded, NotShuttingDown) => {
|
||||||
|
self.remove_outstanding_render_msg();
|
||||||
|
}
|
||||||
|
|
||||||
|
(SetIds(frame_tree, response_chan, new_constellation_chan), NotShuttingDown) => {
|
||||||
|
self.set_frame_tree(&frame_tree,
|
||||||
|
response_chan,
|
||||||
|
new_constellation_chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
(CreateOrUpdateRootLayer(layer_properties), NotShuttingDown) => {
|
||||||
|
self.create_or_update_root_layer(layer_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
(CreateOrUpdateDescendantLayer(layer_properties), NotShuttingDown) => {
|
||||||
|
self.create_or_update_descendant_layer(layer_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
(GetGraphicsMetadata(chan), NotShuttingDown) => {
|
||||||
|
chan.send(Some(self.window.native_metadata()));
|
||||||
|
}
|
||||||
|
|
||||||
|
(SetLayerOrigin(pipeline_id, layer_id, origin), NotShuttingDown) => {
|
||||||
|
self.set_layer_origin(pipeline_id, layer_id, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
(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();
|
||||||
|
}
|
||||||
|
|
||||||
|
(ScrollFragmentPoint(pipeline_id, layer_id, point), NotShuttingDown) => {
|
||||||
|
self.scroll_fragment_to_point(pipeline_id, layer_id, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
(LoadComplete(..), NotShuttingDown) => {
|
||||||
|
self.got_load_complete_message = true;
|
||||||
|
|
||||||
|
// If we're rendering in headless mode, schedule a recomposite.
|
||||||
|
if opts::get().output_file.is_some() {
|
||||||
|
self.composite_if_necessary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(ScrollTimeout(timestamp), NotShuttingDown) => {
|
||||||
|
debug!("scroll timeout, drawing unrendered content!");
|
||||||
|
match self.composition_request {
|
||||||
|
CompositeOnScrollTimeout(this_timestamp) if timestamp == this_timestamp => {
|
||||||
|
self.composition_request = CompositeNow
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
fn change_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||||
|
@ -342,6 +327,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
ready_state,
|
ready_state,
|
||||||
|_key, value| *value = ready_state);
|
|_key, value| *value = ready_state);
|
||||||
self.window.set_ready_state(self.get_earliest_pipeline_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 {
|
fn get_earliest_pipeline_ready_state(&self) -> ReadyState {
|
||||||
|
@ -357,9 +347,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
render_state,
|
render_state,
|
||||||
|_key, value| *value = render_state);
|
|_key, value| *value = render_state);
|
||||||
self.window.set_render_state(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 {
|
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.
|
// Initialize the new constellation channel by sending it the root window size.
|
||||||
self.constellation_chan = new_constellation_chan;
|
self.constellation_chan = new_constellation_chan;
|
||||||
self.send_window_size();
|
self.send_window_size();
|
||||||
|
|
||||||
|
self.got_set_ids_message = true;
|
||||||
|
self.composite_if_necessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_frame_tree_root_layers(&mut self,
|
fn create_frame_tree_root_layers(&mut self,
|
||||||
|
@ -529,7 +519,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn move_layer(&self,
|
pub fn move_layer(&self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
|
@ -555,10 +544,21 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
fail!("Compositor: Tried to scroll to fragment with unknown layer.");
|
fail!("Compositor: Tried to scroll to fragment with unknown layer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.recomposite = true;
|
self.start_scrolling_timer_if_necessary();
|
||||||
}
|
}
|
||||||
None => {}
|
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,
|
fn set_layer_origin(&mut self,
|
||||||
|
@ -580,7 +580,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
new_layer_buffer_set: Box<LayerBufferSet>,
|
new_layer_buffer_set: Box<LayerBufferSet>,
|
||||||
epoch: Epoch) {
|
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.
|
// From now on, if we destroy the buffers, they will leak.
|
||||||
let mut new_layer_buffer_set = new_layer_buffer_set;
|
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) {
|
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
|
||||||
Some(ref layer) => {
|
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));
|
assert!(layer.add_buffers(new_layer_buffer_set, epoch));
|
||||||
self.recomposite = true;
|
self.composite_if_necessary();
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// FIXME: This may potentially be triggered by a race condition where a
|
// FIXME: This may potentially be triggered by a race condition where a
|
||||||
|
@ -605,8 +609,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
point: Point2D<f32>) {
|
point: Point2D<f32>) {
|
||||||
if self.move_layer(pipeline_id, layer_id, Point2D::from_untyped(&point)) {
|
if self.move_layer(pipeline_id, layer_id, Point2D::from_untyped(&point)) {
|
||||||
self.recomposite = true;
|
if self.send_buffer_requests_for_all_layers() {
|
||||||
self.send_buffer_requests_for_all_layers();
|
self.start_scrolling_timer_if_necessary();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.fragment_point = Some(point);
|
self.fragment_point = Some(point);
|
||||||
}
|
}
|
||||||
|
@ -617,7 +622,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
IdleWindowEvent => {}
|
IdleWindowEvent => {}
|
||||||
|
|
||||||
RefreshWindowEvent => {
|
RefreshWindowEvent => {
|
||||||
self.recomposite = true;
|
self.composite_if_necessary()
|
||||||
}
|
}
|
||||||
|
|
||||||
ResizeWindowEvent(size) => {
|
ResizeWindowEvent(size) => {
|
||||||
|
@ -685,6 +690,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
|
|
||||||
debug!("osmain: window resized to {:?}", new_size);
|
debug!("osmain: window resized to {:?}", new_size);
|
||||||
self.window_size = new_size;
|
self.window_size = new_size;
|
||||||
|
|
||||||
self.scene.set_root_layer_size(new_size.as_f32());
|
self.scene.set_root_layer_size(new_size.as_f32());
|
||||||
self.send_window_size();
|
self.send_window_size();
|
||||||
}
|
}
|
||||||
|
@ -698,7 +704,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
layers"),
|
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;
|
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||||
chan.send(msg);
|
chan.send(msg);
|
||||||
}
|
}
|
||||||
|
@ -725,18 +732,29 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
fn on_scroll_window_event(&mut self,
|
fn on_scroll_window_event(&mut self,
|
||||||
delta: TypedPoint2D<DevicePixel, f32>,
|
delta: TypedPoint2D<DevicePixel, f32>,
|
||||||
cursor: TypedPoint2D<DevicePixel, i32>) {
|
cursor: TypedPoint2D<DevicePixel, i32>) {
|
||||||
let delta = delta / self.scene.scale;
|
self.pending_scroll_events.push(ScrollEvent {
|
||||||
let cursor = cursor.as_f32() / self.scene.scale;
|
delta: delta,
|
||||||
|
cursor: cursor,
|
||||||
|
});
|
||||||
|
|
||||||
let mut scroll = false;
|
self.composite_if_necessary();
|
||||||
match self.scene.root {
|
}
|
||||||
Some(ref mut layer) => {
|
|
||||||
scroll = layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged;
|
fn process_pending_scroll_events(&mut self) {
|
||||||
}
|
for scroll_event in mem::replace(&mut self.pending_scroll_events, Vec::new()).into_iter() {
|
||||||
None => { }
|
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> {
|
fn device_pixels_per_screen_px(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
|
||||||
|
@ -768,6 +786,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
self.send_window_size();
|
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) {
|
fn on_pinch_zoom_window_event(&mut self, magnification: f32) {
|
||||||
self.zoom_action = true;
|
self.zoom_action = true;
|
||||||
self.zoom_time = precise_time_s();
|
self.zoom_time = precise_time_s();
|
||||||
|
@ -792,7 +811,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
None => { }
|
None => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
self.recomposite = true;
|
self.composite_if_necessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_navigation_window_event(&self, direction: WindowNavigateMsg) {
|
fn on_navigation_window_event(&self, direction: WindowNavigateMsg) {
|
||||||
|
@ -840,9 +859,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
match self.root_pipeline {
|
match self.root_pipeline {
|
||||||
Some(ref pipeline) => {
|
Some(ref pipeline) => {
|
||||||
let unused_buffers = self.scene.collect_unused_buffers();
|
let unused_buffers = self.scene.collect_unused_buffers();
|
||||||
let have_unused_buffers = unused_buffers.len() > 0;
|
if unused_buffers.len() != 0 {
|
||||||
self.recomposite = self.recomposite || have_unused_buffers;
|
|
||||||
if have_unused_buffers {
|
|
||||||
let message = UnusedBufferMsg(unused_buffers);
|
let message = UnusedBufferMsg(unused_buffers);
|
||||||
let _ = pipeline.render_chan.send_opt(message);
|
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();
|
let mut layers_and_requests = Vec::new();
|
||||||
self.scene.get_buffer_requests(&mut layers_and_requests,
|
self.scene.get_buffer_requests(&mut layers_and_requests,
|
||||||
Rect(TypedPoint2D(0f32, 0f32), self.window_size.as_f32()));
|
Rect(TypedPoint2D(0f32, 0f32), self.window_size.as_f32()));
|
||||||
|
@ -860,7 +878,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
self.send_back_unused_buffers();
|
self.send_back_unused_buffers();
|
||||||
|
|
||||||
if layers_and_requests.len() == 0 {
|
if layers_and_requests.len() == 0 {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to batch requests for each pipeline to avoid race conditions
|
// 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);
|
self.add_outstanding_render_msg(num_render_msgs_sent);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_ready_to_render_image_output(&self) -> bool {
|
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() {
|
if !self.all_pipelines_in_idle_render_state() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.got_set_ids_message {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,10 +963,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if output_image {
|
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,
|
let mut pixels = gl::read_pixels(0, 0,
|
||||||
width as GLsizei,
|
width as gl::GLsizei,
|
||||||
height as GLsizei,
|
height as gl::GLsizei,
|
||||||
gl::RGB, gl::UNSIGNED_BYTE);
|
gl::RGB, gl::UNSIGNED_BYTE);
|
||||||
|
|
||||||
gl::bind_framebuffer(gl::FRAMEBUFFER, 0);
|
gl::bind_framebuffer(gl::FRAMEBUFFER, 0);
|
||||||
|
@ -976,18 +1001,26 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
self.shutdown_state = ShuttingDown;
|
self.shutdown_state = ShuttingDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Perform the page flip. This will likely block for a while.
|
||||||
self.window.present();
|
self.window.present();
|
||||||
|
|
||||||
|
self.last_composite_time = precise_time_ns();
|
||||||
|
|
||||||
let exit = opts::get().exit_after_load;
|
let exit = opts::get().exit_after_load;
|
||||||
if exit {
|
if exit {
|
||||||
debug!("shutting down the constellation for exit_after_load");
|
debug!("shutting down the constellation for exit_after_load");
|
||||||
let ConstellationChan(ref chan) = self.constellation_chan;
|
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||||
chan.send(ExitMsg);
|
chan.send(ExitMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.composition_request = NoCompositingNecessary;
|
||||||
|
self.process_pending_scroll_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recomposite_if(&mut self, result: bool) {
|
fn composite_if_necessary(&mut self) {
|
||||||
self.recomposite = result || self.recomposite;
|
if self.composition_request == NoCompositingNecessary {
|
||||||
|
self.composition_request = CompositeNow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_topmost_layer_at_point_for_layer(&self,
|
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;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Communication with the compositor task.
|
||||||
|
|
||||||
pub use windowing;
|
pub use windowing;
|
||||||
|
pub use constellation::SendableFrameTree;
|
||||||
|
|
||||||
use compositor;
|
use compositor;
|
||||||
use headless;
|
use headless;
|
||||||
pub use constellation::SendableFrameTree;
|
use windowing::{WindowEvent, WindowMethods};
|
||||||
use windowing::WindowMethods;
|
|
||||||
|
|
||||||
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
|
@ -21,39 +23,64 @@ use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
|
||||||
use servo_util::memory::MemoryProfilerChan;
|
use servo_util::memory::MemoryProfilerChan;
|
||||||
use servo_util::time::TimeProfilerChan;
|
use servo_util::time::TimeProfilerChan;
|
||||||
use std::comm::{channel, Sender, Receiver};
|
use std::comm::{channel, Sender, Receiver};
|
||||||
|
use std::fmt::{FormatError, Formatter, Show};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// The implementation of the layers-based compositor.
|
/// Sends messages to the compositor. This is a trait supplied by the port because the method used
|
||||||
#[deriving(Clone)]
|
/// to communicate with the compositor may have to kick OS event loops awake, communicate cross-
|
||||||
pub struct CompositorChan {
|
/// process, and so forth.
|
||||||
/// A channel on which messages can be sent to the compositor.
|
pub trait CompositorProxy : 'static + Send {
|
||||||
pub chan: Sender<Msg>,
|
/// 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.
|
/// Implementation of the abstract `ScriptListener` interface.
|
||||||
impl ScriptListener for CompositorChan {
|
impl ScriptListener for Box<CompositorProxy+'static+Send> {
|
||||||
fn set_ready_state(&self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
fn set_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||||
let msg = ChangeReadyState(pipeline_id, ready_state);
|
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,
|
pipeline_id: PipelineId,
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
point: Point2D<f32>) {
|
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();
|
let (chan, port) = channel();
|
||||||
self.chan.send(Exit(chan));
|
self.send(Exit(chan));
|
||||||
port.recv();
|
port.recv();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dup(&self) -> Box<ScriptListener+'static> {
|
fn dup(&mut self) -> Box<ScriptListener+'static> {
|
||||||
box self.clone() as Box<ScriptListener+'static>
|
box self.clone_compositor_proxy() as Box<ScriptListener+'static>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,21 +110,21 @@ impl LayerProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of the abstract `RenderListener` interface.
|
/// Implementation of the abstract `RenderListener` interface.
|
||||||
impl RenderListener for CompositorChan {
|
impl RenderListener for Box<CompositorProxy+'static+Send> {
|
||||||
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata> {
|
fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata> {
|
||||||
let (chan, port) = channel();
|
let (chan, port) = channel();
|
||||||
self.chan.send(GetGraphicsMetadata(chan));
|
self.send(GetGraphicsMetadata(chan));
|
||||||
port.recv()
|
port.recv()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&self,
|
fn paint(&mut self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
replies: Vec<(LayerId, Box<LayerBufferSet>)>) {
|
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,
|
pipeline_id: PipelineId,
|
||||||
metadata: Vec<LayerMetadata>,
|
metadata: Vec<LayerMetadata>,
|
||||||
epoch: Epoch) {
|
epoch: Epoch) {
|
||||||
|
@ -108,36 +135,23 @@ impl RenderListener for CompositorChan {
|
||||||
for metadata in metadata.iter() {
|
for metadata in metadata.iter() {
|
||||||
let layer_properties = LayerProperties::new(pipeline_id, epoch, metadata);
|
let layer_properties = LayerProperties::new(pipeline_id, epoch, metadata);
|
||||||
if first {
|
if first {
|
||||||
self.chan.send(CreateOrUpdateRootLayer(layer_properties));
|
self.send(CreateOrUpdateRootLayer(layer_properties));
|
||||||
first = false
|
first = false
|
||||||
} else {
|
} else {
|
||||||
self.chan.send(CreateOrUpdateDescendantLayer(layer_properties));
|
self.send(CreateOrUpdateDescendantLayer(layer_properties));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_msg_discarded(&self) {
|
fn render_msg_discarded(&mut self) {
|
||||||
self.chan.send(RenderMsgDiscarded);
|
self.send(RenderMsgDiscarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_render_state(&self, pipeline_id: PipelineId, render_state: RenderState) {
|
fn set_render_state(&mut self, pipeline_id: PipelineId, render_state: RenderState) {
|
||||||
self.chan.send(ChangeRenderState(pipeline_id, render_state))
|
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.
|
/// Messages from the painting task and the constellation task to the compositor task.
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
/// Requests that the compositor shut down.
|
/// Requests that the compositor shut down.
|
||||||
|
@ -177,6 +191,30 @@ pub enum Msg {
|
||||||
SetIds(SendableFrameTree, Sender<()>, ConstellationChan),
|
SetIds(SendableFrameTree, Sender<()>, ConstellationChan),
|
||||||
/// The load of a page for a given URL has completed.
|
/// The load of a page for a given URL has completed.
|
||||||
LoadComplete(PipelineId, Url),
|
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;
|
pub struct CompositorTask;
|
||||||
|
@ -196,27 +234,38 @@ impl CompositorTask {
|
||||||
NativeCompositingGraphicsContext::new()
|
NativeCompositingGraphicsContext::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create<Window: WindowMethods>(
|
pub fn create<Window>(window: Option<Rc<Window>>,
|
||||||
window: Option<Rc<Window>>,
|
sender: Box<CompositorProxy+Send>,
|
||||||
port: Receiver<Msg>,
|
receiver: Box<CompositorReceiver>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
memory_profiler_chan: MemoryProfilerChan) {
|
memory_profiler_chan: MemoryProfilerChan)
|
||||||
|
-> Box<CompositorEventListener + 'static>
|
||||||
|
where Window: WindowMethods + 'static {
|
||||||
match window {
|
match window {
|
||||||
Some(window) => {
|
Some(window) => {
|
||||||
compositor::IOCompositor::create(window,
|
box compositor::IOCompositor::create(window,
|
||||||
port,
|
sender,
|
||||||
constellation_chan.clone(),
|
receiver,
|
||||||
time_profiler_chan,
|
constellation_chan.clone(),
|
||||||
memory_profiler_chan)
|
time_profiler_chan,
|
||||||
|
memory_profiler_chan)
|
||||||
|
as Box<CompositorEventListener>
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
headless::NullCompositor::create(port,
|
box headless::NullCompositor::create(receiver,
|
||||||
constellation_chan.clone(),
|
constellation_chan.clone(),
|
||||||
time_profiler_chan,
|
time_profiler_chan,
|
||||||
memory_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,53 +2,79 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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 devtools_traits::DevtoolsControlChan;
|
||||||
use std::collections::hashmap::{HashMap, HashSet};
|
|
||||||
use geom::rect::{Rect, TypedRect};
|
use geom::rect::{Rect, TypedRect};
|
||||||
use geom::scale_factor::ScaleFactor;
|
use geom::scale_factor::ScaleFactor;
|
||||||
|
use gfx::font_cache_task::FontCacheTask;
|
||||||
use gfx::render_task;
|
use gfx::render_task;
|
||||||
use libc;
|
use layers::geometry::DevicePixel;
|
||||||
use pipeline::{Pipeline, CompositionPipeline};
|
|
||||||
use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
|
use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
|
||||||
|
use libc;
|
||||||
use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
|
use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
|
||||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||||
use servo_msg::compositor_msg::LayerId;
|
use servo_msg::compositor_msg::LayerId;
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
|
||||||
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
|
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
|
||||||
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
|
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg};
|
||||||
use servo_msg::constellation_msg::{LoadData, NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg};
|
use servo_msg::constellation_msg::{LoadData, NavigateMsg, NavigationType, PipelineId};
|
||||||
use servo_msg::constellation_msg::{SubpageId, WindowSizeData};
|
use servo_msg::constellation_msg::{RendererReadyMsg, ResizedWindowMsg, SubpageId, WindowSizeData};
|
||||||
use servo_msg::constellation_msg;
|
use servo_msg::constellation_msg;
|
||||||
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
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::ResourceTask;
|
||||||
use servo_net::resource_task;
|
use servo_net::resource_task;
|
||||||
use servo_util::geometry::PagePx;
|
use servo_util::geometry::{PagePx, ViewportPx};
|
||||||
use servo_util::opts;
|
use servo_util::opts;
|
||||||
use servo_util::time::TimeProfilerChan;
|
|
||||||
use servo_util::task::spawn_named;
|
use servo_util::task::spawn_named;
|
||||||
|
use servo_util::time::TimeProfilerChan;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem::replace;
|
use std::collections::hashmap::{HashMap, HashSet};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::mem::replace;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use url::Url;
|
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> {
|
pub struct Constellation<LTF, STF> {
|
||||||
|
/// A channel through which messages can be sent to this object.
|
||||||
pub chan: ConstellationChan,
|
pub chan: ConstellationChan,
|
||||||
|
|
||||||
|
/// Receives messages.
|
||||||
pub request_port: Receiver<Msg>,
|
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,
|
pub resource_task: ResourceTask,
|
||||||
|
|
||||||
|
/// A channel through which messages can be sent to the image cache task.
|
||||||
pub image_cache_task: ImageCacheTask,
|
pub image_cache_task: ImageCacheTask,
|
||||||
|
|
||||||
|
/// A channel through which messages can be sent to the developer tools.
|
||||||
devtools_chan: Option<DevtoolsControlChan>,
|
devtools_chan: Option<DevtoolsControlChan>,
|
||||||
|
|
||||||
|
/// A list of all the pipelines. (See the `pipeline` module for more details.)
|
||||||
pipelines: HashMap<PipelineId, Rc<Pipeline>>,
|
pipelines: HashMap<PipelineId, Rc<Pipeline>>,
|
||||||
|
|
||||||
|
/// A channel through which messages can be sent to the font cache.
|
||||||
font_cache_task: FontCacheTask,
|
font_cache_task: FontCacheTask,
|
||||||
|
|
||||||
navigation_context: NavigationContext,
|
navigation_context: NavigationContext,
|
||||||
|
|
||||||
|
/// The next free ID to assign to a pipeline.
|
||||||
next_pipeline_id: PipelineId,
|
next_pipeline_id: PipelineId,
|
||||||
|
|
||||||
pending_frames: Vec<FrameChange>,
|
pending_frames: Vec<FrameChange>,
|
||||||
|
|
||||||
pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
|
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 time_profiler_chan: TimeProfilerChan,
|
||||||
|
|
||||||
pub window_size: WindowSizeData,
|
pub window_size: WindowSizeData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +112,11 @@ impl FrameTree {
|
||||||
fn to_sendable(&self) -> SendableFrameTree {
|
fn to_sendable(&self) -> SendableFrameTree {
|
||||||
let sendable_frame_tree = SendableFrameTree {
|
let sendable_frame_tree = SendableFrameTree {
|
||||||
pipeline: self.pipeline.to_sendable(),
|
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
|
sendable_frame_tree
|
||||||
}
|
}
|
||||||
|
@ -109,8 +139,8 @@ impl FrameTreeTraversal for Rc<FrameTree> {
|
||||||
self.iter().find(|frame_tree| id == frame_tree.pipeline.id)
|
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
|
/// Replaces a node of the frame tree in place. Returns the node that was removed or the
|
||||||
/// if the node to replace could not be found.
|
/// original node if the node to replace could not be found.
|
||||||
fn replace_child(&self, id: PipelineId, new_child: Rc<FrameTree>) -> ReplaceResult {
|
fn replace_child(&self, id: PipelineId, new_child: Rc<FrameTree>) -> ReplaceResult {
|
||||||
for frame_tree in self.iter() {
|
for frame_tree in self.iter() {
|
||||||
let mut children = frame_tree.children.borrow_mut();
|
let mut children = frame_tree.children.borrow_mut();
|
||||||
|
@ -239,7 +269,7 @@ impl NavigationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
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,
|
resource_task: ResourceTask,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
font_cache_task: FontCacheTask,
|
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_port, constellation_chan) = ConstellationChan::new();
|
||||||
let constellation_chan_clone = constellation_chan.clone();
|
let constellation_chan_clone = constellation_chan.clone();
|
||||||
spawn_named("Constellation", proc() {
|
spawn_named("Constellation", proc() {
|
||||||
let mut constellation : Constellation<LTF, STF> = Constellation {
|
let mut constellation: Constellation<LTF, STF> = Constellation {
|
||||||
chan: constellation_chan_clone,
|
chan: constellation_chan_clone,
|
||||||
request_port: constellation_port,
|
request_port: constellation_port,
|
||||||
compositor_chan: compositor_chan,
|
compositor_proxy: compositor_proxy,
|
||||||
devtools_chan: devtools_chan,
|
devtools_chan: devtools_chan,
|
||||||
resource_task: resource_task,
|
resource_task: resource_task,
|
||||||
image_cache_task: image_cache_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,
|
let pipe = Pipeline::create::<LTF, STF>(id,
|
||||||
subpage_id,
|
subpage_id,
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.compositor_chan.clone(),
|
self.compositor_proxy.clone_compositor_proxy(),
|
||||||
self.devtools_chan.clone(),
|
self.devtools_chan.clone(),
|
||||||
self.image_cache_task.clone(),
|
self.image_cache_task.clone(),
|
||||||
self.font_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.
|
// script, and reflow messages have been sent.
|
||||||
LoadCompleteMsg(pipeline_id, url) => {
|
LoadCompleteMsg(pipeline_id, url) => {
|
||||||
debug!("constellation got load complete message");
|
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
|
// Handle a forward or back request
|
||||||
NavigateMsg(direction) => {
|
NavigateMsg(direction) => {
|
||||||
|
@ -387,14 +417,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_exit(&self) {
|
fn handle_exit(&mut self) {
|
||||||
for (_id, ref pipeline) in self.pipelines.iter() {
|
for (_id, ref pipeline) in self.pipelines.iter() {
|
||||||
pipeline.exit();
|
pipeline.exit();
|
||||||
}
|
}
|
||||||
self.image_cache_task.exit();
|
self.image_cache_task.exit();
|
||||||
self.resource_task.send(resource_task::Exit);
|
self.resource_task.send(resource_task::Exit);
|
||||||
self.font_cache_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>) {
|
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);
|
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
|
// 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.");
|
debug!("Constellation: Sending size for frame in current frame tree.");
|
||||||
let source_frame = current_frame.find(pipeline_id);
|
let source_frame = current_frame.find(pipeline_id);
|
||||||
for source_frame in source_frame.iter() {
|
for source_frame in source_frame.iter() {
|
||||||
let mut children = source_frame.children.borrow_mut();
|
let mut children = source_frame.children.borrow_mut();
|
||||||
let found_child = children.iter_mut().find(|child| subpage_eq(child));
|
match children.iter_mut().find(|child| subpage_eq(child)) {
|
||||||
found_child.map(|child| update_child_rect(child, true));
|
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() {
|
for frame_tree in frames.iter() {
|
||||||
let mut children = frame_tree.children.borrow_mut();
|
let mut children = frame_tree.children.borrow_mut();
|
||||||
let found_child = children.iter_mut().find(|child| subpage_eq(child));
|
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 {
|
if already_sent.len() == 0 {
|
||||||
self.pending_sizes.insert((pipeline_id, subpage_id), rect);
|
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,
|
fn handle_load_iframe_url_msg(&mut self,
|
||||||
url: Url,
|
url: Url,
|
||||||
source_pipeline_id: PipelineId,
|
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();
|
let (chan, port) = channel();
|
||||||
debug!("Constellation sending SetIds");
|
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() {
|
match port.recv_opt() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let mut iter = frame_tree.iter();
|
let mut iter = frame_tree.iter();
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
|
||||||
use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
|
use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds};
|
||||||
use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
||||||
|
use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout};
|
||||||
|
use windowing::WindowEvent;
|
||||||
|
|
||||||
use geom::scale_factor::ScaleFactor;
|
use geom::scale_factor::ScaleFactor;
|
||||||
use geom::size::TypedSize2D;
|
use geom::size::TypedSize2D;
|
||||||
|
@ -21,79 +22,97 @@ use servo_util::time;
|
||||||
/// It's intended for headless testing.
|
/// It's intended for headless testing.
|
||||||
pub struct NullCompositor {
|
pub struct NullCompositor {
|
||||||
/// The port on which we receive messages.
|
/// 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 {
|
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 {
|
NullCompositor {
|
||||||
port: port,
|
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,
|
constellation_chan: ConstellationChan,
|
||||||
time_profiler_chan: TimeProfilerChan,
|
time_profiler_chan: TimeProfilerChan,
|
||||||
memory_profiler_chan: MemoryProfilerChan) {
|
memory_profiler_chan: MemoryProfilerChan)
|
||||||
let compositor = NullCompositor::new(port);
|
-> NullCompositor {
|
||||||
|
let compositor = NullCompositor::new(port,
|
||||||
|
constellation_chan,
|
||||||
|
time_profiler_chan,
|
||||||
|
memory_profiler_chan);
|
||||||
|
|
||||||
// Tell the constellation about the initial fake size.
|
// 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 {
|
chan.send(ResizedWindowMsg(WindowSizeData {
|
||||||
initial_viewport: TypedSize2D(640_f32, 480_f32),
|
initial_viewport: TypedSize2D(640_f32, 480_f32),
|
||||||
visible_viewport: TypedSize2D(640_f32, 480_f32),
|
visible_viewport: TypedSize2D(640_f32, 480_f32),
|
||||||
device_pixel_ratio: ScaleFactor(1.0),
|
device_pixel_ratio: ScaleFactor(1.0),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
compositor.handle_message(constellation_chan);
|
|
||||||
|
|
||||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
compositor
|
||||||
// another task from finishing (i.e. SetIds)
|
}
|
||||||
loop {
|
}
|
||||||
match compositor.port.try_recv() {
|
|
||||||
Err(_) => break,
|
impl CompositorEventListener for NullCompositor {
|
||||||
Ok(_) => {},
|
fn handle_event(&mut self, _: WindowEvent) -> bool {
|
||||||
}
|
match self.port.recv_compositor_msg() {
|
||||||
}
|
Exit(chan) => {
|
||||||
|
debug!("shutting down the constellation");
|
||||||
time_profiler_chan.send(time::ExitMsg);
|
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
||||||
memory_profiler_chan.send(memory::ExitMsg);
|
con_chan.send(ExitMsg);
|
||||||
}
|
chan.send(());
|
||||||
|
}
|
||||||
fn handle_message(&self, constellation_chan: ConstellationChan) {
|
|
||||||
loop {
|
ShutdownComplete => {
|
||||||
match self.port.recv() {
|
debug!("constellation completed shutdown");
|
||||||
Exit(chan) => {
|
return false
|
||||||
debug!("shutting down the constellation");
|
}
|
||||||
let ConstellationChan(ref con_chan) = constellation_chan;
|
|
||||||
con_chan.send(ExitMsg);
|
GetGraphicsMetadata(chan) => {
|
||||||
chan.send(());
|
chan.send(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShutdownComplete => {
|
SetIds(_, response_chan, _) => {
|
||||||
debug!("constellation completed shutdown");
|
response_chan.send(());
|
||||||
break
|
}
|
||||||
}
|
|
||||||
|
// Explicitly list ignored messages so that when we add a new one,
|
||||||
GetGraphicsMetadata(chan) => {
|
// we'll notice and think about whether it needs a response, like
|
||||||
chan.send(None);
|
// SetIds.
|
||||||
}
|
|
||||||
|
CreateOrUpdateRootLayer(..) |
|
||||||
SetIds(_, response_chan, _) => {
|
CreateOrUpdateDescendantLayer(..) |
|
||||||
response_chan.send(());
|
SetLayerOrigin(..) | Paint(..) |
|
||||||
}
|
ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
|
||||||
|
LoadComplete(..) | RenderMsgDiscarded(..) | ScrollTimeout(..) => ()
|
||||||
// Explicitly list ignored messages so that when we add a new one,
|
}
|
||||||
// we'll notice and think about whether it needs a response, like
|
true
|
||||||
// SetIds.
|
}
|
||||||
|
|
||||||
CreateOrUpdateRootLayer(..) |
|
fn repaint_synchronously(&mut self) {}
|
||||||
CreateOrUpdateDescendantLayer(..) |
|
|
||||||
SetLayerOrigin(..) | Paint(..) |
|
fn shutdown(&mut self) {
|
||||||
ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
|
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||||
LoadComplete(..) | RenderMsgDiscarded(..) => ()
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ extern crate "util" as servo_util;
|
||||||
extern crate gleam;
|
extern crate gleam;
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
extern crate native;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
|
@ -38,12 +39,14 @@ extern crate core_graphics;
|
||||||
#[cfg(target_os="macos")]
|
#[cfg(target_os="macos")]
|
||||||
extern crate core_text;
|
extern crate core_text;
|
||||||
|
|
||||||
pub use compositor_task::{CompositorChan, CompositorTask};
|
pub use compositor_task::{CompositorEventListener, CompositorProxy, CompositorTask};
|
||||||
pub use constellation::Constellation;
|
pub use constellation::Constellation;
|
||||||
|
|
||||||
pub mod compositor_task;
|
pub mod compositor_task;
|
||||||
|
|
||||||
mod compositor_layer;
|
mod compositor_layer;
|
||||||
|
mod scrolling;
|
||||||
|
|
||||||
mod compositor;
|
mod compositor;
|
||||||
mod headless;
|
mod headless;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use CompositorChan;
|
use CompositorProxy;
|
||||||
use layout_traits::{LayoutTaskFactory, LayoutControlChan};
|
use layout_traits::{LayoutTaskFactory, LayoutControlChan};
|
||||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||||
use script_traits::{AttachLayoutMsg, LoadMsg, NewLayoutInfo, ExitPipelineMsg};
|
use script_traits::{AttachLayoutMsg, LoadMsg, NewLayoutInfo, ExitPipelineMsg};
|
||||||
|
@ -47,7 +47,7 @@ impl Pipeline {
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
subpage_id: Option<SubpageId>,
|
subpage_id: Option<SubpageId>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
compositor_chan: CompositorChan,
|
compositor_proxy: Box<CompositorProxy+'static+Send>,
|
||||||
devtools_chan: Option<DevtoolsControlChan>,
|
devtools_chan: Option<DevtoolsControlChan>,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
font_cache_task: FontCacheTask,
|
font_cache_task: FontCacheTask,
|
||||||
|
@ -73,7 +73,7 @@ impl Pipeline {
|
||||||
let (script_chan, script_port) = channel();
|
let (script_chan, script_port) = channel();
|
||||||
ScriptTaskFactory::create(None::<&mut STF>,
|
ScriptTaskFactory::create(None::<&mut STF>,
|
||||||
id,
|
id,
|
||||||
box compositor_chan.clone(),
|
compositor_proxy.clone_compositor_proxy(),
|
||||||
&layout_pair,
|
&layout_pair,
|
||||||
ScriptControlChan(script_chan.clone()),
|
ScriptControlChan(script_chan.clone()),
|
||||||
script_port,
|
script_port,
|
||||||
|
@ -101,7 +101,7 @@ impl Pipeline {
|
||||||
|
|
||||||
RenderTask::create(id,
|
RenderTask::create(id,
|
||||||
render_port,
|
render_port,
|
||||||
compositor_chan.clone(),
|
compositor_proxy,
|
||||||
constellation_chan.clone(),
|
constellation_chan.clone(),
|
||||||
font_cache_task.clone(),
|
font_cache_task.clone(),
|
||||||
failure.clone(),
|
failure.clone(),
|
||||||
|
|
73
components/compositing/scrolling.rs
Normal file
73
components/compositing/scrolling.rs
Normal file
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`.
|
//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`.
|
||||||
|
|
||||||
|
use compositor_task::{CompositorProxy, CompositorReceiver};
|
||||||
|
|
||||||
use geom::point::TypedPoint2D;
|
use geom::point::TypedPoint2D;
|
||||||
use geom::scale_factor::ScaleFactor;
|
use geom::scale_factor::ScaleFactor;
|
||||||
use geom::size::TypedSize2D;
|
use geom::size::TypedSize2D;
|
||||||
|
@ -11,6 +13,8 @@ use layers::geometry::DevicePixel;
|
||||||
use layers::platform::surface::NativeGraphicsMetadata;
|
use layers::platform::surface::NativeGraphicsMetadata;
|
||||||
use servo_msg::compositor_msg::{ReadyState, RenderState};
|
use servo_msg::compositor_msg::{ReadyState, RenderState};
|
||||||
use servo_util::geometry::ScreenPx;
|
use servo_util::geometry::ScreenPx;
|
||||||
|
use std::fmt::{FormatError, Formatter, Show};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub enum MouseWindowEvent {
|
pub enum MouseWindowEvent {
|
||||||
MouseWindowClickEvent(uint, TypedPoint2D<DevicePixel, f32>),
|
MouseWindowClickEvent(uint, TypedPoint2D<DevicePixel, f32>),
|
||||||
|
@ -25,10 +29,12 @@ pub enum WindowNavigateMsg {
|
||||||
|
|
||||||
/// Events that the windowing system sends to Servo.
|
/// Events that the windowing system sends to Servo.
|
||||||
pub enum WindowEvent {
|
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
|
/// FIXME(pcwalton): This is kind of ugly and may not work well with multiprocess Servo.
|
||||||
/// scheduler integrated with the platform event loop.
|
/// It's possible that this should be something like
|
||||||
|
/// `CompositorMessageWindowEvent(compositor_task::Msg)` instead.
|
||||||
IdleWindowEvent,
|
IdleWindowEvent,
|
||||||
/// Sent when part of the window is marked dirty and needs to be redrawn.
|
/// Sent when part of the window is marked dirty and needs to be redrawn.
|
||||||
RefreshWindowEvent,
|
RefreshWindowEvent,
|
||||||
|
@ -54,6 +60,25 @@ pub enum WindowEvent {
|
||||||
QuitWindowEvent,
|
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 {
|
pub trait WindowMethods {
|
||||||
/// Returns the size of the window in hardware pixels.
|
/// Returns the size of the window in hardware pixels.
|
||||||
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint>;
|
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint>;
|
||||||
|
@ -62,9 +87,6 @@ pub trait WindowMethods {
|
||||||
/// Presents the window to the screen (perhaps by page flipping).
|
/// Presents the window to the screen (perhaps by page flipping).
|
||||||
fn present(&self);
|
fn present(&self);
|
||||||
|
|
||||||
/// Spins the event loop and returns the next event.
|
|
||||||
fn recv(&self) -> WindowEvent;
|
|
||||||
|
|
||||||
/// Sets the ready state of the current page.
|
/// Sets the ready state of the current page.
|
||||||
fn set_ready_state(&self, ready_state: ReadyState);
|
fn set_ready_state(&self, ready_state: ReadyState);
|
||||||
/// Sets the render state of the current page.
|
/// Sets the render state of the current page.
|
||||||
|
@ -75,5 +97,13 @@ pub trait WindowMethods {
|
||||||
|
|
||||||
/// Gets the OS native graphics information for this window.
|
/// Gets the OS native graphics information for this window.
|
||||||
fn native_metadata(&self) -> NativeGraphicsMetadata;
|
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>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ use render_context::RenderContext;
|
||||||
use text::glyph::CharIndex;
|
use text::glyph::CharIndex;
|
||||||
use text::TextRun;
|
use text::TextRun;
|
||||||
|
|
||||||
|
use azure::azure::AzFloat;
|
||||||
use collections::Deque;
|
use collections::Deque;
|
||||||
use collections::dlist::{mod, DList};
|
use collections::dlist::{mod, DList};
|
||||||
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
|
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
|
||||||
|
@ -26,18 +27,11 @@ use libc::uintptr_t;
|
||||||
use servo_net::image::base::Image;
|
use servo_net::image::base::Image;
|
||||||
use servo_util::dlist as servo_dlist;
|
use servo_util::dlist as servo_dlist;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::opts;
|
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::slice::Items;
|
use std::slice::Items;
|
||||||
use style::computed_values::border_style;
|
use style::computed_values::border_style;
|
||||||
use sync::Arc;
|
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;
|
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.
|
/// "Steps" as defined by CSS 2.1 § E.2.
|
||||||
#[deriving(Clone, PartialEq, Show)]
|
#[deriving(Clone, PartialEq, Show)]
|
||||||
pub enum StackingLevel {
|
pub enum StackingLevel {
|
||||||
|
@ -543,55 +455,7 @@ impl DisplayItem {
|
||||||
|
|
||||||
TextDisplayItemClass(ref text) => {
|
TextDisplayItemClass(ref text) => {
|
||||||
debug!("Drawing text at {}.", text.base.bounds);
|
debug!("Drawing text at {}.", text.base.bounds);
|
||||||
|
render_context.draw_text(&**text, current_transform);
|
||||||
// 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(¤t_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(¤t_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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageDisplayItemClass(ref image_item) => {
|
ImageDisplayItemClass(ref image_item) => {
|
||||||
|
|
|
@ -30,6 +30,7 @@ extern crate "util" as servo_util;
|
||||||
extern crate "msg" as servo_msg;
|
extern crate "msg" as servo_msg;
|
||||||
extern crate style;
|
extern crate style;
|
||||||
extern crate sync;
|
extern crate sync;
|
||||||
|
extern crate time;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
|
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
|
||||||
|
|
|
@ -2,23 +2,32 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use display_list::{SidewaysLeft, SidewaysRight, TextDisplayItem, Upright};
|
||||||
use font_context::FontContext;
|
use font_context::FontContext;
|
||||||
use style::computed_values::border_style;
|
use style::computed_values::border_style;
|
||||||
|
|
||||||
use azure::azure_hl::{B8G8R8A8, A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
|
use azure::azure_hl::{B8G8R8A8, A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
|
||||||
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, SourceOp, StrokeOptions};
|
use azure::azure_hl::{DrawSurfaceOptions,DrawTarget, Linear, SourceOp, StrokeOptions};
|
||||||
use azure::AZ_CAP_BUTT;
|
use azure::scaled_font::ScaledFont;
|
||||||
use azure::AzFloat;
|
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::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use geom::side_offsets::SideOffsets2D;
|
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 libc::size_t;
|
||||||
use png::{RGB8, RGBA8, K8, KA8};
|
use png::{RGB8, RGBA8, K8, KA8};
|
||||||
use servo_net::image::base::Image;
|
use servo_net::image::base::Image;
|
||||||
use servo_util::geometry::Au;
|
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 sync::Arc;
|
||||||
|
use text::glyph::CharIndex;
|
||||||
|
use text::TextRun;
|
||||||
|
|
||||||
pub struct RenderContext<'a> {
|
pub struct RenderContext<'a> {
|
||||||
pub draw_target: &'a DrawTarget,
|
pub draw_target: &'a DrawTarget,
|
||||||
|
@ -390,6 +399,50 @@ impl<'a> RenderContext<'a> {
|
||||||
self.draw_border_path(original_bounds, direction, border, scaled_color);
|
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(¤t_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(¤t_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 {
|
trait ToAzureRect {
|
||||||
|
@ -417,3 +470,78 @@ impl ToSideOffsetsPx for SideOffsets2D<Au> {
|
||||||
self.left.to_nearest_px() as AzFloat)
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
use buffer_map::BufferMap;
|
use buffer_map::BufferMap;
|
||||||
use display_list::optimizer::DisplayListOptimizer;
|
use display_list::optimizer::DisplayListOptimizer;
|
||||||
use display_list::DisplayList;
|
use display_list::DisplayList;
|
||||||
|
use font_cache_task::FontCacheTask;
|
||||||
use font_context::FontContext;
|
use font_context::FontContext;
|
||||||
use render_context::RenderContext;
|
use render_context::RenderContext;
|
||||||
|
|
||||||
|
@ -37,7 +38,6 @@ use std::comm::{Receiver, Sender, channel};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::task::TaskBuilder;
|
use std::task::TaskBuilder;
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
use font_cache_task::FontCacheTask;
|
|
||||||
|
|
||||||
/// Information about a layer that layout sends to the painting task.
|
/// Information about a layer that layout sends to the painting task.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
|
@ -153,7 +153,10 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
|
||||||
shutdown_chan: Sender<()>) {
|
shutdown_chan: Sender<()>) {
|
||||||
let ConstellationChan(c) = constellation_chan.clone();
|
let ConstellationChan(c) = constellation_chan.clone();
|
||||||
spawn_named_with_send_on_failure("RenderTask", task_state::Render, proc() {
|
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(
|
let native_graphics_context = compositor.get_graphics_metadata().map(
|
||||||
|md| NativePaintingGraphicsContext::from_metadata(&md));
|
|md| NativePaintingGraphicsContext::from_metadata(&md));
|
||||||
let worker_threads = WorkerThreadProxy::spawn(compositor.get_graphics_metadata(),
|
let worker_threads = WorkerThreadProxy::spawn(compositor.get_graphics_metadata(),
|
||||||
|
|
|
@ -84,36 +84,36 @@ pub struct LayerMetadata {
|
||||||
|
|
||||||
/// The interface used by the renderer to acquire draw targets for each render frame and
|
/// The interface used by the renderer to acquire draw targets for each render frame and
|
||||||
/// submit them to be drawn to the display.
|
/// submit them to be drawn to the display.
|
||||||
pub trait RenderListener {
|
pub trait RenderListener for Sized? {
|
||||||
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>;
|
fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata>;
|
||||||
|
|
||||||
/// Informs the compositor of the layers for the given pipeline. The compositor responds by
|
/// Informs the compositor of the layers for the given pipeline. The compositor responds by
|
||||||
/// creating and/or destroying render layers as necessary.
|
/// creating and/or destroying render layers as necessary.
|
||||||
fn initialize_layers_for_pipeline(&self,
|
fn initialize_layers_for_pipeline(&mut self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
metadata: Vec<LayerMetadata>,
|
metadata: Vec<LayerMetadata>,
|
||||||
epoch: Epoch);
|
epoch: Epoch);
|
||||||
|
|
||||||
/// Sends new tiles for the given layer to the compositor.
|
/// Sends new tiles for the given layer to the compositor.
|
||||||
fn paint(&self,
|
fn paint(&mut self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
replies: Vec<(LayerId, Box<LayerBufferSet>)>);
|
replies: Vec<(LayerId, Box<LayerBufferSet>)>);
|
||||||
|
|
||||||
fn render_msg_discarded(&self);
|
fn render_msg_discarded(&mut self);
|
||||||
fn set_render_state(&self, PipelineId, RenderState);
|
fn set_render_state(&mut self, PipelineId, RenderState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The interface used by the script task to tell the compositor to update its ready state,
|
/// 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.
|
/// which is used in displaying the appropriate message in the window's title.
|
||||||
pub trait ScriptListener : Clone {
|
pub trait ScriptListener {
|
||||||
fn set_ready_state(&self, PipelineId, ReadyState);
|
fn set_ready_state(&mut self, PipelineId, ReadyState);
|
||||||
fn scroll_fragment_point(&self,
|
fn scroll_fragment_point(&mut self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
point: Point2D<f32>);
|
point: Point2D<f32>);
|
||||||
fn close(&self);
|
fn close(&mut self);
|
||||||
fn dup(&self) -> Box<ScriptListener+'static>;
|
fn dup(&mut self) -> Box<ScriptListener+'static>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, S: Encoder<E>> Encodable<S, E> for Box<ScriptListener+'static> {
|
impl<E, S: Encoder<E>> Encodable<S, E> for Box<ScriptListener+'static> {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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
|
//! The high-level interface from script to constellation. Using this abstract interface helps
|
||||||
//! coupling between these two components
|
//! reduce coupling between these two components.
|
||||||
|
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::TypedSize2D;
|
use geom::size::TypedSize2D;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::EventHandlerBinding::{OnErrorEventHandlerNonNull, EventHandlerNonNull};
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding;
|
use dom::bindings::codegen::Bindings::WindowBinding;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
|
@ -53,7 +53,7 @@ pub struct Window {
|
||||||
location: MutNullableJS<Location>,
|
location: MutNullableJS<Location>,
|
||||||
navigator: MutNullableJS<Navigator>,
|
navigator: MutNullableJS<Navigator>,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
compositor: Box<ScriptListener+'static>,
|
compositor: DOMRefCell<Box<ScriptListener+'static>>,
|
||||||
browser_context: DOMRefCell<Option<BrowserContext>>,
|
browser_context: DOMRefCell<Option<BrowserContext>>,
|
||||||
page: Rc<Page>,
|
page: Rc<Page>,
|
||||||
performance: MutNullableJS<Performance>,
|
performance: MutNullableJS<Performance>,
|
||||||
|
@ -81,8 +81,8 @@ impl Window {
|
||||||
&self.image_cache_task
|
&self.image_cache_task
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compositor<'a>(&'a self) -> &'a ScriptListener+'static {
|
pub fn compositor(&self) -> RefMut<Box<ScriptListener+'static>> {
|
||||||
&*self.compositor
|
self.compositor.borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn browser_context(&self) -> Ref<Option<BrowserContext>> {
|
pub fn browser_context(&self) -> Ref<Option<BrowserContext>> {
|
||||||
|
@ -398,7 +398,7 @@ impl Window {
|
||||||
script_chan: script_chan,
|
script_chan: script_chan,
|
||||||
control_chan: control_chan,
|
control_chan: control_chan,
|
||||||
console: Default::default(),
|
console: Default::default(),
|
||||||
compositor: compositor,
|
compositor: DOMRefCell::new(compositor),
|
||||||
page: page,
|
page: page,
|
||||||
location: Default::default(),
|
location: Default::default(),
|
||||||
navigator: Default::default(),
|
navigator: Default::default(),
|
||||||
|
|
|
@ -182,7 +182,7 @@ impl Page {
|
||||||
if force_reflow {
|
if force_reflow {
|
||||||
let frame = self.frame();
|
let frame = self.frame();
|
||||||
let window = frame.as_ref().unwrap().window.root();
|
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 {
|
} else {
|
||||||
self.avoided_reflows.set(self.avoided_reflows.get() + 1);
|
self.avoided_reflows.set(self.avoided_reflows.get() + 1);
|
||||||
}
|
}
|
||||||
|
@ -328,9 +328,8 @@ impl Page {
|
||||||
pub fn reflow(&self,
|
pub fn reflow(&self,
|
||||||
goal: ReflowGoal,
|
goal: ReflowGoal,
|
||||||
script_chan: ScriptControlChan,
|
script_chan: ScriptControlChan,
|
||||||
compositor: &ScriptListener,
|
compositor: &mut ScriptListener,
|
||||||
query_type: ReflowQueryType) {
|
query_type: ReflowQueryType) {
|
||||||
|
|
||||||
let root = match *self.frame() {
|
let root = match *self.frame() {
|
||||||
None => return,
|
None => return,
|
||||||
Some(ref frame) => {
|
Some(ref frame) => {
|
||||||
|
|
|
@ -168,7 +168,7 @@ pub struct ScriptTask {
|
||||||
/// For communicating load url messages to the constellation
|
/// For communicating load url messages to the constellation
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
/// A handle to the compositor for communicating ready state messages.
|
/// 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.
|
/// For providing instructions to an optional devtools server.
|
||||||
devtools_chan: Option<DevtoolsControlChan>,
|
devtools_chan: Option<DevtoolsControlChan>,
|
||||||
|
@ -248,25 +248,25 @@ impl ScriptTaskFactory for ScriptTask {
|
||||||
box pair.sender() as Box<Any+Send>
|
box pair.sender() as Box<Any+Send>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create<C:ScriptListener + Send + 'static>(
|
fn create<C>(_phantom: Option<&mut ScriptTask>,
|
||||||
_phantom: Option<&mut ScriptTask>,
|
id: PipelineId,
|
||||||
id: PipelineId,
|
compositor: C,
|
||||||
compositor: Box<C>,
|
layout_chan: &OpaqueScriptLayoutChannel,
|
||||||
layout_chan: &OpaqueScriptLayoutChannel,
|
control_chan: ScriptControlChan,
|
||||||
control_chan: ScriptControlChan,
|
control_port: Receiver<ConstellationControlMsg>,
|
||||||
control_port: Receiver<ConstellationControlMsg>,
|
constellation_chan: ConstellationChan,
|
||||||
constellation_chan: ConstellationChan,
|
failure_msg: Failure,
|
||||||
failure_msg: Failure,
|
resource_task: ResourceTask,
|
||||||
resource_task: ResourceTask,
|
image_cache_task: ImageCacheTask,
|
||||||
image_cache_task: ImageCacheTask,
|
devtools_chan: Option<DevtoolsControlChan>,
|
||||||
devtools_chan: Option<DevtoolsControlChan>,
|
window_size: WindowSizeData)
|
||||||
window_size: WindowSizeData) {
|
where C: ScriptListener + Send + 'static {
|
||||||
let ConstellationChan(const_chan) = constellation_chan.clone();
|
let ConstellationChan(const_chan) = constellation_chan.clone();
|
||||||
let (script_chan, script_port) = channel();
|
let (script_chan, script_port) = channel();
|
||||||
let layout_chan = LayoutChan(layout_chan.sender());
|
let layout_chan = LayoutChan(layout_chan.sender());
|
||||||
spawn_named_with_send_on_failure("ScriptTask", task_state::Script, proc() {
|
spawn_named_with_send_on_failure("ScriptTask", task_state::Script, proc() {
|
||||||
let script_task = ScriptTask::new(id,
|
let script_task = ScriptTask::new(id,
|
||||||
compositor as Box<ScriptListener>,
|
box compositor as Box<ScriptListener>,
|
||||||
layout_chan,
|
layout_chan,
|
||||||
script_port,
|
script_port,
|
||||||
ScriptChan(script_chan),
|
ScriptChan(script_chan),
|
||||||
|
@ -350,7 +350,7 @@ impl ScriptTask {
|
||||||
control_chan: control_chan,
|
control_chan: control_chan,
|
||||||
control_port: control_port,
|
control_port: control_port,
|
||||||
constellation_chan: constellation_chan,
|
constellation_chan: constellation_chan,
|
||||||
compositor: compositor,
|
compositor: DOMRefCell::new(compositor),
|
||||||
devtools_chan: devtools_chan,
|
devtools_chan: devtools_chan,
|
||||||
devtools_port: devtools_receiver,
|
devtools_port: devtools_receiver,
|
||||||
|
|
||||||
|
@ -671,7 +671,7 @@ impl ScriptTask {
|
||||||
*layout_join_port = None;
|
*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 {
|
if page.pending_reflows.get() > 0 {
|
||||||
page.pending_reflows.set(0);
|
page.pending_reflows.set(0);
|
||||||
|
@ -709,7 +709,7 @@ impl ScriptTask {
|
||||||
// TODO(tkuehn): currently there is only one window,
|
// TODO(tkuehn): currently there is only one window,
|
||||||
// so this can afford to be naive and just shut down the
|
// so this can afford to be naive and just shut down the
|
||||||
// compositor. In the future it'll need to be smarter.
|
// 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.
|
/// Handles a request to exit the script task and shut down layout.
|
||||||
|
@ -771,7 +771,7 @@ impl ScriptTask {
|
||||||
page.clone(),
|
page.clone(),
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.control_chan.clone(),
|
self.control_chan.clone(),
|
||||||
self.compositor.dup(),
|
self.compositor.borrow_mut().dup(),
|
||||||
self.image_cache_task.clone()).root();
|
self.image_cache_task.clone()).root();
|
||||||
let doc_url = if is_javascript {
|
let doc_url = if is_javascript {
|
||||||
let doc_url = last_url.unwrap_or_else(|| {
|
let doc_url = last_url.unwrap_or_else(|| {
|
||||||
|
@ -787,7 +787,7 @@ impl ScriptTask {
|
||||||
|
|
||||||
window.init_browser_context(*document);
|
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 {
|
let parser_input = if !is_javascript {
|
||||||
InputUrl(url.clone())
|
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
|
// 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
|
// layer the element belongs to, and have it send the scroll message to the
|
||||||
// compositor.
|
// 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) {
|
fn force_reflow(&self, page: &Page) {
|
||||||
|
@ -873,7 +873,10 @@ impl ScriptTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
page.damage();
|
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.
|
/// This is the main entry point for receiving and dispatching DOM events.
|
||||||
|
|
|
@ -89,18 +89,20 @@ impl<S: Encoder<E>, E> Encodable<S, E> for ScriptControlChan {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ScriptTaskFactory {
|
pub trait ScriptTaskFactory {
|
||||||
fn create<C: ScriptListener + Send>(_phantom: Option<&mut Self>,
|
fn create<C>(_phantom: Option<&mut Self>,
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
compositor: Box<C>,
|
compositor: C,
|
||||||
layout_chan: &OpaqueScriptLayoutChannel,
|
layout_chan: &OpaqueScriptLayoutChannel,
|
||||||
control_chan: ScriptControlChan,
|
control_chan: ScriptControlChan,
|
||||||
control_port: Receiver<ConstellationControlMsg>,
|
control_port: Receiver<ConstellationControlMsg>,
|
||||||
constellation_msg: ConstellationChan,
|
constellation_msg: ConstellationChan,
|
||||||
failure_msg: Failure,
|
failure_msg: Failure,
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
devtools_chan: Option<DevtoolsControlChan>,
|
devtools_chan: Option<DevtoolsControlChan>,
|
||||||
window_size: WindowSizeData);
|
window_size: WindowSizeData)
|
||||||
|
where C: ScriptListener + Send;
|
||||||
fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel;
|
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>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
|
|
||||||
//! Timing functions.
|
//! Timing functions.
|
||||||
|
|
||||||
use std_time::precise_time_ns;
|
|
||||||
use collections::treemap::TreeMap;
|
use collections::treemap::TreeMap;
|
||||||
use std::comm::{Sender, channel, Receiver};
|
use std::comm::{Sender, channel, Receiver};
|
||||||
use std::f64;
|
use std::f64;
|
||||||
use std::iter::AdditiveIterator;
|
|
||||||
use std::io::timer::sleep;
|
use std::io::timer::sleep;
|
||||||
|
use std::iter::AdditiveIterator;
|
||||||
use std::time::duration::Duration;
|
use std::time::duration::Duration;
|
||||||
|
use std_time::precise_time_ns;
|
||||||
use task::{spawn_named};
|
use task::{spawn_named};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
|
10
ports/cef/Cargo.lock
generated
10
ports/cef/Cargo.lock
generated
|
@ -8,7 +8,7 @@ dependencies = [
|
||||||
"devtools 0.0.1",
|
"devtools 0.0.1",
|
||||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86)",
|
"geom 0.1.0 (git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86)",
|
||||||
"gfx 0.0.1",
|
"gfx 0.0.1",
|
||||||
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b)",
|
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#cec2861dd75eb721694b3176f94a276d77018bde)",
|
||||||
"glfw_app 0.0.1",
|
"glfw_app 0.0.1",
|
||||||
"js 0.1.0 (git+https://github.com/servo/rust-mozjs#1ec216a2577c03738fa11a78958bb2a0fd3f7fbd)",
|
"js 0.1.0 (git+https://github.com/servo/rust-mozjs#1ec216a2577c03738fa11a78958bb2a0fd3f7fbd)",
|
||||||
"layers 0.1.0 (git+https://github.com/servo/rust-layers#3737b00270644594a95896a2cd37ffca36d5bc5f)",
|
"layers 0.1.0 (git+https://github.com/servo/rust-layers#3737b00270644594a95896a2cd37ffca36d5bc5f)",
|
||||||
|
@ -229,16 +229,16 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glfw"
|
name = "glfw"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b"
|
source = "git+https://github.com/servo/glfw-rs?ref=servo#cec2861dd75eb721694b3176f94a276d77018bde"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae)",
|
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674)",
|
||||||
"semver 0.0.1 (git+https://github.com/rust-lang/semver#d04583a173395b76c1eaa15cc630a5f6f8f0ae10)",
|
"semver 0.0.1 (git+https://github.com/rust-lang/semver#d04583a173395b76c1eaa15cc630a5f6f8f0ae10)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glfw-sys"
|
name = "glfw-sys"
|
||||||
version = "3.0.4"
|
version = "3.0.4"
|
||||||
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae"
|
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glfw_app"
|
name = "glfw_app"
|
||||||
|
@ -248,7 +248,7 @@ dependencies = [
|
||||||
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl#698c6c5409c1049ba5a7e0f7bdddf97f91dc4cf5)",
|
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl#698c6c5409c1049ba5a7e0f7bdddf97f91dc4cf5)",
|
||||||
"compositing 0.0.1",
|
"compositing 0.0.1",
|
||||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86)",
|
"geom 0.1.0 (git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86)",
|
||||||
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b)",
|
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#cec2861dd75eb721694b3176f94a276d77018bde)",
|
||||||
"layers 0.1.0 (git+https://github.com/servo/rust-layers#3737b00270644594a95896a2cd37ffca36d5bc5f)",
|
"layers 0.1.0 (git+https://github.com/servo/rust-layers#3737b00270644594a95896a2cd37ffca36d5bc5f)",
|
||||||
"msg 0.0.1",
|
"msg 0.0.1",
|
||||||
"util 0.0.1",
|
"util 0.0.1",
|
||||||
|
|
|
@ -9,12 +9,11 @@ use geom::size::TypedSize2D;
|
||||||
use glfw_app;
|
use glfw_app;
|
||||||
use libc::{c_int, c_void};
|
use libc::{c_int, c_void};
|
||||||
use native;
|
use native;
|
||||||
use servo;
|
use servo::Browser;
|
||||||
use servo_util::opts;
|
use servo_util::opts;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use types::{cef_app_t, cef_main_args_t, cef_settings_t};
|
use types::{cef_app_t, cef_main_args_t, cef_settings_t};
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn cef_initialize(args: *const cef_main_args_t,
|
pub extern "C" fn cef_initialize(args: *const cef_main_args_t,
|
||||||
_settings: *mut cef_settings_t,
|
_settings: *mut cef_settings_t,
|
||||||
|
@ -77,8 +76,10 @@ pub extern "C" fn cef_run_message_loop() {
|
||||||
validate_display_list_geometry: false,
|
validate_display_list_geometry: false,
|
||||||
});
|
});
|
||||||
native::start(0, 0 as *const *const u8, proc() {
|
native::start(0, 0 as *const *const u8, proc() {
|
||||||
let window = Some(glfw_app::create_window());
|
let window = glfw_app::create_window();
|
||||||
servo::run(window);
|
let mut browser = Browser::new(Some(window.clone()));
|
||||||
|
while browser.handle_event(window.wait_events()) {}
|
||||||
|
browser.shutdown()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,17 @@ extern crate msg;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate util;
|
extern crate util;
|
||||||
|
|
||||||
|
use compositing::windowing::WindowEvent;
|
||||||
use geom::scale_factor::ScaleFactor;
|
use geom::scale_factor::ScaleFactor;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use window::Window;
|
use window::Window;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
|
|
||||||
mod window;
|
pub mod window;
|
||||||
|
|
||||||
|
pub trait NestedEventLoopListener {
|
||||||
|
fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_window() -> Rc<Window> {
|
pub fn create_window() -> Rc<Window> {
|
||||||
// Initialize GLFW.
|
// Initialize GLFW.
|
||||||
|
|
|
@ -4,14 +4,18 @@
|
||||||
|
|
||||||
//! A windowing implementation using GLFW.
|
//! A windowing implementation using GLFW.
|
||||||
|
|
||||||
|
use NestedEventLoopListener;
|
||||||
|
|
||||||
use alert::{Alert, AlertMethods};
|
use alert::{Alert, AlertMethods};
|
||||||
use compositing::windowing::{WindowEvent, WindowMethods};
|
use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver};
|
||||||
|
use compositing::windowing::{Forward, Back};
|
||||||
use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent};
|
use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent};
|
||||||
use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass, ScrollWindowEvent};
|
use compositing::windowing::{MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
||||||
use compositing::windowing::{ZoomWindowEvent, PinchZoomWindowEvent, NavigationWindowEvent};
|
use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass};
|
||||||
use compositing::windowing::{FinishedWindowEvent, QuitWindowEvent, MouseWindowClickEvent};
|
use compositing::windowing::{MouseWindowMouseUpEvent, RefreshWindowEvent};
|
||||||
use compositing::windowing::{MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
use compositing::windowing::{NavigationWindowEvent, ScrollWindowEvent, ZoomWindowEvent};
|
||||||
use compositing::windowing::{RefreshWindowEvent, Forward, Back};
|
use compositing::windowing::{PinchZoomWindowEvent, QuitWindowEvent};
|
||||||
|
use compositing::windowing::{WindowEvent, WindowMethods, FinishedWindowEvent};
|
||||||
use geom::point::{Point2D, TypedPoint2D};
|
use geom::point::{Point2D, TypedPoint2D};
|
||||||
use geom::scale_factor::ScaleFactor;
|
use geom::scale_factor::ScaleFactor;
|
||||||
use geom::size::TypedSize2D;
|
use geom::size::TypedSize2D;
|
||||||
|
@ -19,8 +23,8 @@ use glfw::{mod, Context};
|
||||||
use layers::geometry::DevicePixel;
|
use layers::geometry::DevicePixel;
|
||||||
use layers::platform::surface::NativeGraphicsMetadata;
|
use layers::platform::surface::NativeGraphicsMetadata;
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
use msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
|
|
||||||
use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};
|
use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};
|
||||||
|
use msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::comm::Receiver;
|
use std::comm::Receiver;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -84,12 +88,50 @@ impl Window {
|
||||||
window.glfw_window.set_cursor_pos_polling(true);
|
window.glfw_window.set_cursor_pos_polling(true);
|
||||||
window.glfw_window.set_scroll_polling(true);
|
window.glfw_window.set_scroll_polling(true);
|
||||||
|
|
||||||
let wrapped_window = Rc::new(window);
|
glfw.set_swap_interval(1);
|
||||||
|
|
||||||
wrapped_window
|
Rc::new(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait_events(&self) -> WindowEvent {
|
||||||
|
{
|
||||||
|
let mut event_queue = self.event_queue.borrow_mut();
|
||||||
|
if !event_queue.is_empty() {
|
||||||
|
return event_queue.remove(0).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.glfw.wait_events();
|
||||||
|
for (_, event) in glfw::flush_messages(&self.events) {
|
||||||
|
self.handle_window_event(&self.glfw_window, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.glfw_window.should_close() {
|
||||||
|
QuitWindowEvent
|
||||||
|
} else {
|
||||||
|
self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_nested_event_loop_listener(
|
||||||
|
&self,
|
||||||
|
listener: *mut NestedEventLoopListener + 'static) {
|
||||||
|
self.glfw_window.set_refresh_polling(false);
|
||||||
|
glfw::ffi::glfwSetWindowRefreshCallback(self.glfw_window.ptr, Some(on_refresh));
|
||||||
|
glfw::ffi::glfwSetFramebufferSizeCallback(self.glfw_window.ptr, Some(on_framebuffer_size));
|
||||||
|
g_nested_event_loop_listener = Some(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn remove_nested_event_loop_listener(&self) {
|
||||||
|
glfw::ffi::glfwSetWindowRefreshCallback(self.glfw_window.ptr, None);
|
||||||
|
glfw::ffi::glfwSetFramebufferSizeCallback(self.glfw_window.ptr, None);
|
||||||
|
self.glfw_window.set_refresh_polling(true);
|
||||||
|
g_nested_event_loop_listener = None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static mut g_nested_event_loop_listener: Option<*mut NestedEventLoopListener + 'static> = None;
|
||||||
|
|
||||||
impl WindowMethods for Window {
|
impl WindowMethods for Window {
|
||||||
/// Returns the size of the window in hardware pixels.
|
/// Returns the size of the window in hardware pixels.
|
||||||
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
|
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
|
||||||
|
@ -108,26 +150,6 @@ impl WindowMethods for Window {
|
||||||
self.glfw_window.swap_buffers();
|
self.glfw_window.swap_buffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recv(&self) -> WindowEvent {
|
|
||||||
{
|
|
||||||
let mut event_queue = self.event_queue.borrow_mut();
|
|
||||||
if !event_queue.is_empty() {
|
|
||||||
return event_queue.remove(0).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.glfw.poll_events();
|
|
||||||
for (_, event) in glfw::flush_messages(&self.events) {
|
|
||||||
self.handle_window_event(&self.glfw_window, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.glfw_window.should_close() {
|
|
||||||
QuitWindowEvent
|
|
||||||
} else {
|
|
||||||
self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the ready state.
|
/// Sets the ready state.
|
||||||
fn set_ready_state(&self, ready_state: ReadyState) {
|
fn set_ready_state(&self, ready_state: ReadyState) {
|
||||||
self.ready_state.set(ready_state);
|
self.ready_state.set(ready_state);
|
||||||
|
@ -169,6 +191,15 @@ impl WindowMethods for Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_compositor_channel(_: &Option<Rc<Window>>)
|
||||||
|
-> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
|
||||||
|
let (sender, receiver) = channel();
|
||||||
|
(box GlfwCompositorProxy {
|
||||||
|
sender: sender,
|
||||||
|
} as Box<CompositorProxy+Send>,
|
||||||
|
box receiver as Box<CompositorReceiver>)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -219,7 +250,6 @@ impl Window {
|
||||||
self.scroll_window(dx, dy);
|
self.scroll_window(dx, dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -346,3 +376,45 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GlfwCompositorProxy {
|
||||||
|
sender: Sender<compositor_task::Msg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompositorProxy for GlfwCompositorProxy {
|
||||||
|
fn send(&mut self, msg: compositor_task::Msg) {
|
||||||
|
// Send a message and kick the OS event loop awake.
|
||||||
|
self.sender.send(msg);
|
||||||
|
glfw::Glfw::post_empty_event()
|
||||||
|
}
|
||||||
|
fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
|
||||||
|
box GlfwCompositorProxy {
|
||||||
|
sender: self.sender.clone(),
|
||||||
|
} as Box<CompositorProxy+Send>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn on_refresh(_glfw_window: *mut glfw::ffi::GLFWwindow) {
|
||||||
|
unsafe {
|
||||||
|
match g_nested_event_loop_listener {
|
||||||
|
None => {}
|
||||||
|
Some(listener) => {
|
||||||
|
(*listener).handle_event_from_nested_event_loop(RefreshWindowEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn on_framebuffer_size(_glfw_window: *mut glfw::ffi::GLFWwindow,
|
||||||
|
width: c_int,
|
||||||
|
height: c_int) {
|
||||||
|
unsafe {
|
||||||
|
match g_nested_event_loop_listener {
|
||||||
|
None => {}
|
||||||
|
Some(listener) => {
|
||||||
|
let size = TypedSize2D(width as uint, height as uint);
|
||||||
|
(*listener).handle_event_from_nested_event_loop(ResizeWindowEvent(size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
151
src/lib.rs
151
src/lib.rs
|
@ -30,10 +30,11 @@ extern crate native;
|
||||||
extern crate rustrt;
|
extern crate rustrt;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
|
use compositing::CompositorEventListener;
|
||||||
|
use compositing::windowing::{WindowEvent, WindowMethods};
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use compositing::{CompositorChan, CompositorTask, Constellation};
|
use compositing::{CompositorProxy, CompositorTask, Constellation};
|
||||||
#[cfg(not(test))]
|
|
||||||
use compositing::windowing::WindowMethods;
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg};
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
@ -63,77 +64,105 @@ use std::rc::Rc;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use std::task::TaskBuilder;
|
use std::task::TaskBuilder;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
pub struct Browser<Window> {
|
||||||
pub fn run<Window: WindowMethods>(window: Option<Rc<Window>>) {
|
pool: green::SchedPool,
|
||||||
::servo_util::opts::set_experimental_enabled(opts::get().enable_experimental);
|
compositor: Box<CompositorEventListener + 'static>,
|
||||||
let opts = opts::get();
|
}
|
||||||
RegisterBindings::RegisterProxyHandlers();
|
|
||||||
|
|
||||||
let mut pool_config = green::PoolConfig::new();
|
impl<Window> Browser<Window> where Window: WindowMethods + 'static {
|
||||||
pool_config.event_loop_factory = rustuv::event_loop;
|
#[cfg(not(test))]
|
||||||
let mut pool = green::SchedPool::new(pool_config);
|
pub fn new(window: Option<Rc<Window>>) -> Browser<Window> {
|
||||||
|
::servo_util::opts::set_experimental_enabled(opts::get().enable_experimental);
|
||||||
|
let opts = opts::get();
|
||||||
|
RegisterBindings::RegisterProxyHandlers();
|
||||||
|
|
||||||
let (compositor_port, compositor_chan) = CompositorChan::new();
|
let mut pool_config = green::PoolConfig::new();
|
||||||
let time_profiler_chan = TimeProfiler::create(opts.time_profiler_period);
|
pool_config.event_loop_factory = rustuv::event_loop;
|
||||||
let memory_profiler_chan = MemoryProfiler::create(opts.memory_profiler_period);
|
let mut pool = green::SchedPool::new(pool_config);
|
||||||
let devtools_chan = opts.devtools_port.map(|port| {
|
let shared_task_pool = TaskPool::new(8);
|
||||||
devtools::start_server(port)
|
|
||||||
});
|
|
||||||
|
|
||||||
let time_profiler_chan_clone = time_profiler_chan.clone();
|
let (compositor_proxy, compositor_receiver) =
|
||||||
let shared_task_pool = TaskPool::new(8);
|
WindowMethods::create_compositor_channel(&window);
|
||||||
|
let time_profiler_chan = TimeProfiler::create(opts.time_profiler_period);
|
||||||
|
let memory_profiler_chan = MemoryProfiler::create(opts.memory_profiler_period);
|
||||||
|
let devtools_chan = opts.devtools_port.map(|port| {
|
||||||
|
devtools::start_server(port)
|
||||||
|
});
|
||||||
|
|
||||||
let (result_chan, result_port) = channel();
|
let opts_clone = opts.clone();
|
||||||
TaskBuilder::new()
|
let time_profiler_chan_clone = time_profiler_chan.clone();
|
||||||
.green(&mut pool)
|
|
||||||
.spawn(proc() {
|
let (result_chan, result_port) = channel();
|
||||||
// Create a Servo instance.
|
let compositor_proxy_for_constellation = compositor_proxy.clone_compositor_proxy();
|
||||||
let resource_task = new_resource_task(opts.user_agent.clone());
|
TaskBuilder::new()
|
||||||
// If we are emitting an output file, then we need to block on
|
.green(&mut pool)
|
||||||
// image load or we risk emitting an output file missing the
|
.spawn(proc() {
|
||||||
// image.
|
let opts = &opts_clone;
|
||||||
let image_cache_task = if opts.output_file.is_some() {
|
// Create a Servo instance.
|
||||||
|
let resource_task = new_resource_task(opts.user_agent.clone());
|
||||||
|
// If we are emitting an output file, then we need to block on
|
||||||
|
// image load or we risk emitting an output file missing the
|
||||||
|
// image.
|
||||||
|
let image_cache_task = if opts.output_file.is_some() {
|
||||||
ImageCacheTask::new_sync(resource_task.clone(), shared_task_pool)
|
ImageCacheTask::new_sync(resource_task.clone(), shared_task_pool)
|
||||||
} else {
|
} else {
|
||||||
ImageCacheTask::new(resource_task.clone(), shared_task_pool)
|
ImageCacheTask::new(resource_task.clone(), shared_task_pool)
|
||||||
};
|
};
|
||||||
let font_cache_task = FontCacheTask::new(resource_task.clone());
|
let font_cache_task = FontCacheTask::new(resource_task.clone());
|
||||||
let constellation_chan = Constellation::<layout::layout_task::LayoutTask,
|
let constellation_chan = Constellation::<layout::layout_task::LayoutTask,
|
||||||
script::script_task::ScriptTask>::start(
|
script::script_task::ScriptTask>::start(
|
||||||
compositor_chan,
|
compositor_proxy_for_constellation,
|
||||||
resource_task,
|
resource_task,
|
||||||
image_cache_task,
|
image_cache_task,
|
||||||
font_cache_task,
|
font_cache_task,
|
||||||
time_profiler_chan_clone,
|
time_profiler_chan_clone,
|
||||||
devtools_chan);
|
devtools_chan);
|
||||||
|
|
||||||
// Send the URL command to the constellation.
|
// Send the URL command to the constellation.
|
||||||
let cwd = os::getcwd();
|
let cwd = os::getcwd();
|
||||||
for url in opts.urls.iter() {
|
for url in opts.urls.iter() {
|
||||||
let url = match url::Url::parse(url.as_slice()) {
|
let url = match url::Url::parse(url.as_slice()) {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
Err(url::RelativeUrlWithoutBase)
|
Err(url::RelativeUrlWithoutBase)
|
||||||
=> url::Url::from_file_path(&cwd.join(url.as_slice())).unwrap(),
|
=> url::Url::from_file_path(&cwd.join(url.as_slice())).unwrap(),
|
||||||
Err(_) => fail!("URL parsing failed"),
|
Err(_) => fail!("URL parsing failed"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ConstellationChan(ref chan) = constellation_chan;
|
let ConstellationChan(ref chan) = constellation_chan;
|
||||||
chan.send(InitLoadUrlMsg(url));
|
chan.send(InitLoadUrlMsg(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the constallation Chan as the result
|
||||||
|
result_chan.send(constellation_chan);
|
||||||
|
});
|
||||||
|
|
||||||
|
let constellation_chan = result_port.recv();
|
||||||
|
|
||||||
|
debug!("preparing to enter main loop");
|
||||||
|
let compositor = CompositorTask::create(window,
|
||||||
|
compositor_proxy,
|
||||||
|
compositor_receiver,
|
||||||
|
constellation_chan,
|
||||||
|
time_profiler_chan,
|
||||||
|
memory_profiler_chan);
|
||||||
|
|
||||||
|
Browser {
|
||||||
|
pool: pool,
|
||||||
|
compositor: compositor,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send the constallation Chan as the result
|
pub fn handle_event(&mut self, event: WindowEvent) -> bool {
|
||||||
result_chan.send(constellation_chan);
|
self.compositor.handle_event(event)
|
||||||
});
|
}
|
||||||
|
|
||||||
let constellation_chan = result_port.recv();
|
pub fn repaint_synchronously(&mut self) {
|
||||||
|
self.compositor.repaint_synchronously()
|
||||||
|
}
|
||||||
|
|
||||||
debug!("preparing to enter main loop");
|
pub fn shutdown(mut self) {
|
||||||
CompositorTask::create(window,
|
self.compositor.shutdown();
|
||||||
compositor_port,
|
self.pool.shutdown();
|
||||||
constellation_chan,
|
}
|
||||||
time_profiler_chan,
|
|
||||||
memory_profiler_chan);
|
|
||||||
|
|
||||||
pool.shutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
src/main.rs
71
src/main.rs
|
@ -9,10 +9,13 @@
|
||||||
|
|
||||||
extern crate servo;
|
extern crate servo;
|
||||||
extern crate native;
|
extern crate native;
|
||||||
|
extern crate time;
|
||||||
extern crate "util" as servo_util;
|
extern crate "util" as servo_util;
|
||||||
|
|
||||||
#[cfg(not(test),not(target_os="android"))]
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
extern crate glfw_app;
|
extern crate glfw_app;
|
||||||
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
|
extern crate compositing;
|
||||||
|
|
||||||
#[cfg(not(test),not(target_os="android"))]
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
use servo_util::opts;
|
use servo_util::opts;
|
||||||
|
@ -21,11 +24,18 @@ use servo_util::opts;
|
||||||
use servo_util::rtinstrument;
|
use servo_util::rtinstrument;
|
||||||
|
|
||||||
#[cfg(not(test),not(target_os="android"))]
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
use servo::run;
|
use servo::Browser;
|
||||||
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
|
use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, WindowEvent};
|
||||||
|
|
||||||
#[cfg(not(test),not(target_os="android"))]
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
use std::os;
|
use std::os;
|
||||||
|
|
||||||
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
|
struct BrowserWrapper {
|
||||||
|
browser: Browser<glfw_app::window::Window>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(test), not(target_os="android"))]
|
#[cfg(not(test), not(target_os="android"))]
|
||||||
#[start]
|
#[start]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -37,7 +47,46 @@ fn start(argc: int, argv: *const *const u8) -> int {
|
||||||
} else {
|
} else {
|
||||||
Some(glfw_app::create_window())
|
Some(glfw_app::create_window())
|
||||||
};
|
};
|
||||||
run(window);
|
|
||||||
|
let mut browser = BrowserWrapper {
|
||||||
|
browser: Browser::new(window.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match window {
|
||||||
|
None => {}
|
||||||
|
Some(ref window) => {
|
||||||
|
unsafe {
|
||||||
|
window.set_nested_event_loop_listener(&mut browser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let should_continue = match window {
|
||||||
|
None => browser.browser.handle_event(IdleWindowEvent),
|
||||||
|
Some(ref window) => {
|
||||||
|
let event = window.wait_events();
|
||||||
|
browser.browser.handle_event(event)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !should_continue {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match window {
|
||||||
|
None => {}
|
||||||
|
Some(ref window) => {
|
||||||
|
unsafe {
|
||||||
|
window.remove_nested_event_loop_listener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let BrowserWrapper {
|
||||||
|
browser
|
||||||
|
} = browser;
|
||||||
|
browser.shutdown();
|
||||||
|
|
||||||
rtinstrument::teardown();
|
rtinstrument::teardown();
|
||||||
}
|
}
|
||||||
|
@ -46,3 +95,21 @@ fn start(argc: int, argv: *const *const u8) -> int {
|
||||||
|
|
||||||
#[cfg(not(test), target_os="android")]
|
#[cfg(not(test), target_os="android")]
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
|
#[cfg(not(test),not(target_os="android"))]
|
||||||
|
impl glfw_app::NestedEventLoopListener for BrowserWrapper {
|
||||||
|
fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
|
||||||
|
let is_resize = match event {
|
||||||
|
ResizeWindowEvent(..) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if !self.browser.handle_event(event) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if is_resize {
|
||||||
|
self.browser.repaint_synchronously()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit cec2861dd75eb721694b3176f94a276d77018bde
|
Subproject commit 8edf667fbf289d909c7d1bdde03acf6d78168674
|
Loading…
Add table
Add a link
Reference in a new issue