diff --git a/src/components/gfx/render_context.rs b/src/components/gfx/render_context.rs index 415339834a2..f36445acf8c 100644 --- a/src/components/gfx/render_context.rs +++ b/src/components/gfx/render_context.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use servo_msg::compositor::LayerBuffer; +use servo_msg::compositor_msg::LayerBuffer; use font_context::FontContext; use geometry::Au; use opts::Opts; diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 62d2094c1e3..e51ee22e08c 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -7,8 +7,9 @@ use azure::{AzFloat, AzGLContext}; use azure::azure_hl::{B8G8R8A8, DrawTarget}; use display_list::DisplayList; -use servo_msg::compositor::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer}; -use servo_msg::compositor::LayerBufferSet; +use servo_msg::compositor_msg::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer}; +use servo_msg::compositor_msg::{CompositorToken, LayerBufferSet}; +use servo_msg::constellation_msg::{ConstellationChan}; use font_context::FontContext; use geom::matrix2d::Matrix2D; use geom::point::Point2D; @@ -29,70 +30,33 @@ pub struct RenderLayer { size: Size2D } -pub enum Msg { - AttachCompositorMsg(C), +pub enum Msg { RenderMsg(RenderLayer), ReRenderMsg(f32), + TokenBestowMsg(CompositorToken), + TokenInvalidateMsg, ExitMsg(Chan<()>), } -pub struct RenderChan { - chan: SharedChan>, +#[deriving(Clone)] +pub struct RenderChan { + chan: SharedChan, } -impl Clone for RenderChan { - pub fn clone(&self) -> RenderChan { - RenderChan { - chan: self.chan.clone(), - } - } -} - -impl RenderChan { - pub fn new(chan: Chan>) -> RenderChan { +impl RenderChan { + pub fn new(chan: Chan) -> RenderChan { RenderChan { chan: SharedChan::new(chan), } } - pub fn send(&self, msg: Msg) { + pub fn send(&self, msg: Msg) { self.chan.send(msg); } } -pub fn create_render_task(port: Port>, - compositor: C, - opts: Opts, - profiler_chan: ProfilerChan) { - let compositor_cell = Cell::new(compositor); - let opts_cell = Cell::new(opts); - let port = Cell::new(port); - - do spawn { - let compositor = compositor_cell.take(); - let share_gl_context = compositor.get_gl_context(); - let opts = opts_cell.with_ref(|o| copy *o); - let profiler_chan = profiler_chan.clone(); - let profiler_chan_copy = profiler_chan.clone(); - - // FIXME: rust/#5967 - let mut renderer = Renderer { - port: port.take(), - compositor: compositor, - font_ctx: @mut FontContext::new(opts.render_backend, - false, - profiler_chan), - opts: opts_cell.take(), - profiler_chan: profiler_chan_copy, - share_gl_context: share_gl_context, - render_layer: None, - }; - - renderer.start(); - } -} - -priv struct Renderer { - port: Port>, +priv struct RenderTask { + id: uint, + port: Port, compositor: C, font_ctx: @mut FontContext, opts: Opts, @@ -104,15 +68,60 @@ priv struct Renderer { /// The layer to be rendered render_layer: Option, + /// A channel to the constellation for surrendering token + constellation_chan: ConstellationChan, + /// A token that grants permission to send paint messages to compositor + compositor_token: Option, + /// Cached copy of last layers rendered + last_paint_msg: Option<(LayerBufferSet, Size2D)>, } -impl Renderer { +impl RenderTask { + pub fn create(id: uint, + port: Port, + compositor: C, + opts: Opts, + constellation_chan: ConstellationChan, + profiler_chan: ProfilerChan) { + let compositor_cell = Cell::new(compositor); + let opts_cell = Cell::new(opts); + let port = Cell::new(port); + let constellation_chan = Cell::new(constellation_chan); + + do spawn { + let compositor = compositor_cell.take(); + let share_gl_context = compositor.get_gl_context(); + let opts = opts_cell.with_ref(|o| copy *o); + let profiler_chan = profiler_chan.clone(); + let profiler_chan_clone = profiler_chan.clone(); + + // FIXME: rust/#5967 + let mut render_task = RenderTask { + id: id, + port: port.take(), + compositor: compositor, + font_ctx: @mut FontContext::new(opts.render_backend, + false, + profiler_chan), + opts: opts_cell.take(), + profiler_chan: profiler_chan_clone, + share_gl_context: share_gl_context, + render_layer: None, + + constellation_chan: constellation_chan.take(), + compositor_token: None, + last_paint_msg: None, + }; + + render_task.start(); + } + } + fn start(&mut self) { - debug!("renderer: beginning rendering loop"); + debug!("render_task: beginning rendering loop"); loop { match self.port.recv() { - AttachCompositorMsg(compositor) => self.compositor = compositor, RenderMsg(render_layer) => { self.render_layer = Some(render_layer); self.render(1.0); @@ -120,6 +129,19 @@ impl Renderer { ReRenderMsg(scale) => { self.render(scale); } + TokenBestowMsg(token) => { + self.compositor_token = Some(token); + match self.last_paint_msg { + Some((ref layer_buffer_set, ref layer_size)) => { + self.compositor.paint(self.id, layer_buffer_set.clone(), *layer_size); + self.compositor.set_render_state(IdleRenderState); + } + None => {} + } + } + TokenInvalidateMsg => { + self.compositor_token = None; + } ExitMsg(response_ch) => { response_ch.send(()); break; @@ -129,7 +151,7 @@ impl Renderer { } fn render(&mut self, scale: f32) { - debug!("renderer: rendering"); + debug!("render_task: rendering"); let render_layer; match (self.render_layer) { @@ -140,7 +162,7 @@ impl Renderer { } self.compositor.set_render_state(RenderingRenderState); - do profile(time::RenderingCategory, self.profiler_chan.clone()) { + do time::profile(time::RenderingCategory, self.profiler_chan.clone()) { let tile_size = self.opts.tile_size; // FIXME: Try not to create a new array here. @@ -164,7 +186,8 @@ impl Renderer { let buffer = LayerBuffer { draw_target: DrawTarget::new_with_fbo(self.opts.render_backend, self.share_gl_context, - Size2D(width as i32, height as i32), + Size2D(width as i32, + height as i32), B8G8R8A8), rect: tile_rect, screen_pos: screen_rect, @@ -210,8 +233,12 @@ impl Renderer { buffers: new_buffers, }; - debug!("renderer: returning surface"); - self.compositor.paint(layer_buffer_set, render_layer.size); + debug!("render_task: returning surface"); + if self.compositor_token.is_some() { + self.compositor.paint(self.id, layer_buffer_set.clone(), render_layer.size); + } + debug!("caching paint msg"); + self.last_paint_msg = Some((layer_buffer_set, render_layer.size)); self.compositor.set_render_state(IdleRenderState); } } diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 9e3a2a1067e..5d7a11b2bed 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -4,12 +4,16 @@ use platform::{Application, Window}; use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent}; -use script::script_task::{LoadMsg, SendEventMsg}; +use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg}; use script::layout_interface::{LayoutChan, RouteScriptMsg}; use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent}; use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; -use servo_msg::compositor::{RenderListener, LayerBufferSet, RenderState}; -use servo_msg::compositor::{ReadyState, ScriptListener}; + + +use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState}; +use servo_msg::compositor_msg::{ReadyState, ScriptListener}; +use servo_msg::constellation_msg::{CompositorAck, ConstellationChan}; +use servo_msg::constellation_msg; use gfx::render_task::{RenderChan, ReRenderMsg}; use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context}; @@ -32,6 +36,7 @@ use servo_util::{time, url}; use servo_util::time::profile; use servo_util::time::ProfilerChan; +pub use windowing; /// The implementation of the layers-based compositor. #[deriving(Clone)] @@ -55,8 +60,8 @@ impl RenderListener for CompositorChan { self.chan.send(GetGLContext(chan)); port.recv() } - fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D) { - self.chan.send(Paint(layer_buffer_set, new_size)) + fn paint(&self, id: uint, layer_buffer_set: LayerBufferSet, new_size: Size2D) { + self.chan.send(Paint(id, layer_buffer_set, new_size)) } fn set_render_state(&self, render_state: RenderState) { self.chan.send(ChangeRenderState(render_state)) @@ -81,15 +86,13 @@ pub enum Msg { /// Requests the compositors GL context. GetGLContext(Chan), /// Requests that the compositor paint the given layer buffer set for the given page size. - Paint(LayerBufferSet, Size2D), + Paint(uint, LayerBufferSet, Size2D), /// Alerts the compositor to the current status of page loading. ChangeReadyState(ReadyState), /// Alerts the compositor to the current status of rendering. ChangeRenderState(RenderState), - /// Sets the channel to the current layout task - SetLayoutChan(LayoutChan), - /// Sets the channel to the current renderer - SetRenderChan(RenderChan), + /// Sets the channel to the current layout and render tasks, along with their id + SetLayoutRenderChans(LayoutChan, RenderChan , uint, ConstellationChan) } /// Azure surface wrapping to work with the layers infrastructure. @@ -176,9 +179,19 @@ impl CompositorTask { let local_zoom = @mut 1f32; // Channel to the current renderer. // FIXME: This probably shouldn't be stored like this. - let render_chan: @mut Option> = @mut None; + let render_chan: @mut Option = @mut None; + let pipeline_id: @mut Option = @mut None; let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| { + let layout_chan_clone = layout_chan.clone(); + do window.set_navigation_callback |direction| { + let direction = match direction { + windowing::Forward => constellation_msg::Forward, + windowing::Back => constellation_msg::Back, + }; + layout_chan_clone.send(RouteScriptMsg(NavigateMsg(direction))); + } + let layout_chan_clone = layout_chan.clone(); // Hook the windowing system's resize callback up to the resize rate limiter. do window.set_resize_callback |width, height| { @@ -186,7 +199,7 @@ impl CompositorTask { if *window_size != new_size { debug!("osmain: window resized to %ux%u", width, height); *window_size = new_size; - layout_chan_clone.chan.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height)))); + layout_chan_clone.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height)))); } else { debug!("osmain: dropping window resize since size is still %ux%u", width, height); } @@ -197,7 +210,7 @@ impl CompositorTask { // When the user enters a new URL, load it. do window.set_load_url_callback |url_string| { debug!("osmain: loading URL `%s`", url_string); - layout_chan_clone.chan.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None)))); + layout_chan_clone.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None)))); } let layout_chan_clone = layout_chan.clone(); @@ -229,7 +242,7 @@ impl CompositorTask { event = MouseUpEvent(button, world_mouse_point(layer_mouse_point)); } } - layout_chan_clone.chan.send(RouteScriptMsg(SendEventMsg(event))); + layout_chan_clone.send(RouteScriptMsg(SendEventMsg(event))); } }; @@ -242,17 +255,24 @@ impl CompositorTask { ChangeReadyState(ready_state) => window.set_ready_state(ready_state), ChangeRenderState(render_state) => window.set_render_state(render_state), - SetLayoutChan(layout_chan) => { - update_layout_callbacks(layout_chan); - } - - SetRenderChan(new_render_chan) => { + SetLayoutRenderChans(new_layout_chan, + new_render_chan, + new_pipeline_id, + response_chan) => { + update_layout_callbacks(new_layout_chan); *render_chan = Some(new_render_chan); + *pipeline_id = Some(new_pipeline_id); + response_chan.send(CompositorAck(new_pipeline_id)); } GetGLContext(chan) => chan.send(current_gl_context()), - Paint(new_layer_buffer_set, new_size) => { + Paint(id, new_layer_buffer_set, new_size) => { + match *pipeline_id { + Some(pipeline_id) => if id != pipeline_id { loop; }, + None => { loop; }, + } + debug!("osmain: received new frame"); *page_size = Size2D(new_size.width as f32, new_size.height as f32); @@ -410,6 +430,7 @@ impl CompositorTask { window.set_needs_display() } + // Enter the main event loop. while !*done { // Check for new messages coming from the rendering task. @@ -432,3 +453,4 @@ fn on_osmain(f: ~fn()) { f(); } } + diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs new file mode 100644 index 00000000000..a18b56cbd50 --- /dev/null +++ b/src/components/main/constellation.rs @@ -0,0 +1,255 @@ +/* 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/. */ + +use compositing::{CompositorChan, SetLayoutRenderChans}; + +use std::cell::Cell; +use std::comm; +use std::comm::Port; +use std::task; +use gfx::opts::Opts; +use gfx::render_task::{TokenBestowMsg, TokenInvalidateMsg}; +use pipeline::Pipeline; +use servo_msg::compositor_msg::{CompositorToken}; +use servo_msg::constellation_msg::{CompositorAck, ConstellationChan, ExitMsg}; +use servo_msg::constellation_msg::{LoadUrlMsg, Msg, NavigateMsg, RendererReadyMsg}; +use servo_msg::constellation_msg; +use script::script_task::ExecuteMsg; +use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; +use servo_net::resource_task::ResourceTask; +use servo_net::resource_task; +use servo_util::time::ProfilerChan; +use std::hashmap::HashMap; +use std::util::replace; + +/// Maintains the pipelines and navigation context and grants permission to composite +pub struct Constellation { + chan: ConstellationChan, + request_port: Port, + compositor_chan: CompositorChan, + resource_task: ResourceTask, + image_cache_task: ImageCacheTask, + pipelines: HashMap, + navigation_context: NavigationContext, + next_id: uint, + current_token_bearer: Option, + next_token_bearer: Option, + profiler_chan: ProfilerChan, + opts: Opts, +} + +/// Stores the ID's of the pipelines previous and next in the browser's history +pub struct NavigationContext { + previous: ~[uint], + next: ~[uint], + current: Option, +} + +impl NavigationContext { + pub fn new() -> NavigationContext { + NavigationContext { + previous: ~[], + next: ~[], + current: None, + } + } + + /* Note that the following two methods can fail. They should only be called * + * when it is known that, e.g., there exists a previous page or a next page. */ + + pub fn back(&mut self) -> uint { + self.next.push(self.current.get()); + self.current = Some(self.previous.pop()); + debug!("previous: %? next: %? current: %?", self.previous, self.next, self.current); + self.current.get() + } + + pub fn forward(&mut self) -> uint { + self.previous.push(self.current.get()); + self.current = Some(self.next.pop()); + debug!("previous: %? next: %? current: %?", self.previous, self.next, self.current); + self.current.get() + } + + /// Navigates to a new id, returning all id's evicted from next + pub fn navigate(&mut self, id: uint) -> ~[uint] { + let evicted = replace(&mut self.next, ~[]); + do self.current.mutate_default(id) |cur_id| { + self.previous.push(cur_id); + id + } + evicted + } +} + +impl Constellation { + pub fn start(compositor_chan: CompositorChan, + opts: &Opts, + resource_task: ResourceTask, + image_cache_task: ImageCacheTask, + profiler_chan: ProfilerChan) + -> ConstellationChan { + + let opts = Cell::new(copy *opts); + + let (constellation_port, constellation_chan) = special_stream!(ConstellationChan); + let constellation_port = Cell::new(constellation_port); + + let compositor_chan = Cell::new(compositor_chan); + let constellation_chan_clone = Cell::new(constellation_chan.clone()); + + let resource_task = Cell::new(resource_task); + let image_cache_task = Cell::new(image_cache_task); + let profiler_chan = Cell::new(profiler_chan); + + do task::spawn { + let mut constellation = Constellation { + chan: constellation_chan_clone.take(), + request_port: constellation_port.take(), + compositor_chan: compositor_chan.take(), + resource_task: resource_task.take(), + image_cache_task: image_cache_task.take(), + pipelines: HashMap::new(), + navigation_context: NavigationContext::new(), + next_id: 0, + current_token_bearer: None, + next_token_bearer: None, + profiler_chan: profiler_chan.take(), + opts: opts.take(), + }; + constellation.run(); + } + constellation_chan + } + + fn run(&mut self) { + loop { + let request = self.request_port.recv(); + if !self.handle_request(request) { + break; + } + } + } + + /// Helper function for getting a unique pipeline ID + fn get_next_id(&mut self) -> uint { + let id = self.next_id; + self.next_id = id + 1; + id + } + + /// Handles loading pages, navigation, and granting access to the compositor + fn handle_request(&mut self, request: Msg) -> bool { + match request { + // Load a new page, usually either from a mouse click or typed url + LoadUrlMsg(url) => { + let pipeline_id = self.get_next_id(); + let mut pipeline = Pipeline::create(pipeline_id, + self.chan.clone(), + self.compositor_chan.clone(), + self.image_cache_task.clone(), + self.resource_task.clone(), + self.profiler_chan.clone(), + copy self.opts); + if url.path.ends_with(".js") { + pipeline.script_chan.send(ExecuteMsg(url)); + } else { + pipeline.load(url); + pipeline.navigation_type = Some(constellation_msg::Load); + self.next_token_bearer = Some(pipeline_id); + } + self.pipelines.insert(pipeline_id, pipeline); + } + + // Handle a forward or back request + NavigateMsg(direction) => { + debug!("received message to navigate %?", direction); + let destination_id = match direction { + constellation_msg::Forward => { + if self.navigation_context.next.is_empty() { + debug!("no next page to navigate to"); + return true + } + self.navigation_context.forward() + } + constellation_msg::Back => { + if self.navigation_context.previous.is_empty() { + debug!("no previous page to navigate to"); + return true + } + self.navigation_context.back() + } + }; + debug!("navigating to pipeline %u", destination_id); + let mut pipeline = self.pipelines.pop(&destination_id).unwrap(); + pipeline.navigation_type = Some(constellation_msg::Navigate); + pipeline.reload(); + self.pipelines.insert(destination_id, pipeline); + self.next_token_bearer = Some(destination_id); + self.update_token_bearer(); + } + + // Notification that rendering has finished and is requesting permission to paint. + RendererReadyMsg(pipeline_id) => { + let next_token_bearer = self.next_token_bearer; + for next_token_bearer.iter().advance |&id| { + if pipeline_id == id { + self.update_token_bearer(); + } + } + } + + // Acknowledgement from the compositor that it has updated its active pipeline id + CompositorAck(id) => { + self.bestow_compositor_token(id); + } + + ExitMsg(sender) => { + for self.pipelines.each |_, pipeline| { + pipeline.exit(); + } + self.image_cache_task.exit(); + self.resource_task.send(resource_task::Exit); + + sender.send(()); + return false + } + } + true + } + + fn update_token_bearer(&mut self) { + let current_token_bearer = replace(&mut self.current_token_bearer, None); + for current_token_bearer.iter().advance |id| { + self.pipelines.get(id).render_chan.send(TokenInvalidateMsg); + } + let id = self.next_token_bearer.get(); + let pipeline = self.pipelines.get(&id); + self.compositor_chan.send(SetLayoutRenderChans(pipeline.layout_chan.clone(), + pipeline.render_chan.clone(), + id, + self.chan.clone())); + } + + // Sends a compositor token to a renderer; optionally updates navigation to reflect a new page + fn bestow_compositor_token(&mut self, id: uint) { + let pipeline = self.pipelines.get(&id); + pipeline.render_chan.send(TokenBestowMsg(CompositorToken::new())); + self.current_token_bearer = Some(id); + self.next_token_bearer = None; + // Don't navigate on Navigate type, because that is handled by forward/back + match pipeline.navigation_type.get() { + constellation_msg::Load => { + let evicted = self.navigation_context.navigate(id); + /* FIXME(tkuehn): the following code causes a segfault + for evicted.iter().advance |id| { + self.pipelines.get(id).exit(); + } + */ + } + _ => {} + } + } +} + diff --git a/src/components/main/css/select.rs b/src/components/main/css/select.rs index 351dc7b6dfb..b776ade5978 100644 --- a/src/components/main/css/select.rs +++ b/src/components/main/css/select.rs @@ -6,7 +6,6 @@ use extra::net::url::Url; use url_from_str = extra::net::url::from_str; use std::cell::Cell; use std::result; -use std::str; use newcss::stylesheet::Stylesheet; use newcss::select::SelectCtx; use newcss::types::OriginUA; diff --git a/src/components/main/css/select_handler.rs b/src/components/main/css/select_handler.rs index 0267975136d..be7b21b3924 100644 --- a/src/components/main/css/select_handler.rs +++ b/src/components/main/css/select_handler.rs @@ -6,7 +6,6 @@ /// Implementation of the callbacks that the CSS selector engine uses to query the DOM. /// -use std::str; use std::str::eq_slice; use newcss::select::SelectHandler; use script::dom::node::{AbstractNode, LayoutView}; diff --git a/src/components/main/engine.rs b/src/components/main/engine.rs deleted file mode 100644 index cdabbba1ae2..00000000000 --- a/src/components/main/engine.rs +++ /dev/null @@ -1,152 +0,0 @@ -/* 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/. */ - -use compositing::{CompositorChan, SetLayoutChan, SetRenderChan}; -use layout::layout_task; - -use std::cell::Cell; -use std::comm; -use std::comm::Port; -use std::task; -use gfx::opts::Opts; -use gfx::render_task::RenderChan; -use gfx::render_task; -use servo_msg::compositor::{ScriptListener, ReadyState}; -use servo_msg::engine::{EngineChan, ExitMsg, LoadUrlMsg, Msg}; -use script::layout_interface::LayoutChan; -use script::layout_interface; -use script::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptContext, ScriptChan}; -use script::script_task; -use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; -use servo_net::resource_task::ResourceTask; -use servo_net::resource_task; -use servo_util::time::{ProfilerChan}; - -pub struct Engine { - request_port: Port, - compositor_chan: CompositorChan, - render_chan: RenderChan, - resource_task: ResourceTask, - image_cache_task: ImageCacheTask, - layout_chan: LayoutChan, - script_chan: ScriptChan, - profiler_chan: ProfilerChan, -} - -impl Engine { - pub fn start(compositor_chan: CompositorChan, - opts: &Opts, - resource_task: ResourceTask, - image_cache_task: ImageCacheTask, - profiler_chan: ProfilerChan) - -> EngineChan { - macro_rules! closure_stream( - ($Msg:ty, $Chan:ident) => ( - { - let (port, chan) = comm::stream::<$Msg>(); - (Cell::new(port), $Chan::new(chan)) - } - ); - ) - - // Create the script port and channel. - let (script_port, script_chan) = closure_stream!(ScriptMsg, ScriptChan); - - // Create the engine port and channel. - let (engine_port, engine_chan) = closure_stream!(Msg, EngineChan); - - // Create the layout port and channel. - let (layout_port, layout_chan) = closure_stream!(layout_interface::Msg, LayoutChan); - - let (render_port, render_chan) = comm::stream::>(); - let (render_port, render_chan) = (Cell::new(render_port), RenderChan::new(render_chan)); - - - compositor_chan.send(SetLayoutChan(layout_chan.clone())); - compositor_chan.send(SetRenderChan(render_chan.clone())); - - let compositor_chan = Cell::new(compositor_chan); - - let opts = Cell::new(copy *opts); - - { - let engine_chan = engine_chan.clone(); - do task::spawn { - let compositor_chan = compositor_chan.take(); - render_task::create_render_task(render_port.take(), - compositor_chan.clone(), - opts.with_ref(|o| copy *o), - profiler_chan.clone()); - - let opts = opts.take(); - - layout_task::create_layout_task(layout_port.take(), - script_chan.clone(), - render_chan.clone(), - image_cache_task.clone(), - opts, - profiler_chan.clone()); - - let compositor_chan_clone = compositor_chan.clone(); - ScriptContext::create_script_context(layout_chan.clone(), - script_port.take(), - script_chan.clone(), - engine_chan.clone(), - |msg: ReadyState| { - compositor_chan_clone.set_ready_state(msg) - }, - resource_task.clone(), - image_cache_task.clone()); - - Engine { - request_port: engine_port.take(), - compositor_chan: compositor_chan.clone(), - render_chan: render_chan.clone(), - resource_task: resource_task.clone(), - image_cache_task: image_cache_task.clone(), - layout_chan: layout_chan.clone(), - script_chan: script_chan.clone(), - profiler_chan: profiler_chan.clone(), - }.run(); - } - } - engine_chan - } - - fn run(&self) { - while self.handle_request(self.request_port.recv()) { - // Go on... - } - } - - fn handle_request(&self, request: Msg) -> bool { - match request { - LoadUrlMsg(url) => { - if url.path.ends_with(".js") { - self.script_chan.send(ExecuteMsg(url)) - } else { - self.script_chan.send(LoadMsg(url)) - } - return true - } - - ExitMsg(sender) => { - self.script_chan.send(script_task::ExitMsg); - self.layout_chan.send(layout_interface::ExitMsg); - - let (response_port, response_chan) = comm::stream(); - - self.render_chan.send(render_task::ExitMsg(response_chan)); - response_port.recv(); - - self.image_cache_task.exit(); - self.resource_task.send(resource_task::Exit); - - sender.send(()); - return false - } - } - } -} - diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index c101803397f..c1008673402 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -840,7 +840,7 @@ impl RenderBox { pub fn dump_indent(&self, indent: uint) { let mut string = ~""; for uint::range(0u, indent) |_i| { - string += ~" "; + string += " "; } string += self.debug_str(); diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index a2ec046ca17..9ddd9b0e7e8 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -394,7 +394,7 @@ impl<'self> FlowContext { pub fn dump_indent(&self, indent: uint) { let mut s = ~"|"; for uint::range(0, indent) |_i| { - s += ~"---- "; + s += "---- "; } s += self.debug_str(); @@ -412,7 +412,7 @@ impl<'self> FlowContext { let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| { fmt!("%s b%d", *s, box.id()) }); - s += ~")"; + s += ")"; s }, BlockFlow(block) => { diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 7f91d3dce29..0f93798af10 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -5,7 +5,6 @@ //! The layout task. Performs layout on the DOM, builds display lists and sends them to be /// rendered. -use compositing::CompositorChan; use css::matching::MatchMethods; use css::select::new_css_select_ctx; use layout::aux::{LayoutData, LayoutAuxMethods}; @@ -17,7 +16,7 @@ use layout::flow::FlowContext; use std::cast::transmute; use std::cell::Cell; -use std::comm::{Chan, Port}; +use std::comm::{Port}; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; @@ -34,39 +33,21 @@ use script::dom::node::{AbstractNode, LayoutView}; use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery}; use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse}; use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery}; -use script::layout_interface::{LayoutResponse, MatchSelectorsDocumentDamage, Msg}; +use script::layout_interface::{MatchSelectorsDocumentDamage, Msg}; use script::layout_interface::{QueryMsg, RouteScriptMsg, Reflow, ReflowDocumentDamage}; use script::layout_interface::{ReflowForDisplay, ReflowMsg}; use script::script_task::{ReflowCompleteMsg, ScriptChan, ScriptMsg, SendEventMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::LocalImageCache; use servo_util::tree::{TreeNodeRef, TreeUtils}; -use servo_util::time::{ProfilerChan, profile, time}; +use servo_util::time::{ProfilerChan, profile}; use servo_util::time; use extra::net::url::Url; -pub fn create_layout_task(port: Port, - script_chan: ScriptChan, - render_chan: RenderChan, - img_cache_task: ImageCacheTask, - opts: Opts, - profiler_chan: ProfilerChan) { - let port = Cell::new(port); - do spawn { - let mut layout = Layout::new(port.take(), - script_chan.clone(), - render_chan.clone(), - img_cache_task.clone(), - &opts, - profiler_chan.clone()); - layout.start(); - }; -} - -struct Layout { +struct LayoutTask { port: Port, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan, image_cache_task: ImageCacheTask, local_image_cache: @mut LocalImageCache, font_ctx: @mut FontContext, @@ -80,17 +61,35 @@ struct Layout { profiler_chan: ProfilerChan, } -impl Layout { +impl LayoutTask { + pub fn create(port: Port, + script_chan: ScriptChan, + render_chan: RenderChan, + img_cache_task: ImageCacheTask, + opts: Opts, + profiler_chan: ProfilerChan) { + let port = Cell::new(port); + do spawn { + let mut layout = LayoutTask::new(port.take(), + script_chan.clone(), + render_chan.clone(), + img_cache_task.clone(), + &opts, + profiler_chan.clone()); + layout.start(); + }; + } + fn new(port: Port, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan, image_cache_task: ImageCacheTask, opts: &Opts, profiler_chan: ProfilerChan) - -> Layout { + -> LayoutTask { let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone()); - Layout { + LayoutTask { port: port, script_chan: script_chan, render_chan: render_chan, @@ -135,13 +134,14 @@ impl Layout { self.handle_reflow(data.take()); } } - QueryMsg(query, chan) => { - let chan = Cell::new(chan); + QueryMsg(query) => { + let query = Cell::new(query); do profile(time::LayoutQueryCategory, self.profiler_chan.clone()) { - self.handle_query(query, chan.take()); + self.handle_query(query.take()); } } RouteScriptMsg(script_msg) => { + debug!("layout: routing %? to script task", script_msg); self.route_script_msg(script_msg); } ExitMsg => { @@ -268,9 +268,9 @@ impl Layout { /// Handles a query from the script task. This is the main routine that DOM functions like /// `getClientRects()` or `getBoundingClientRect()` ultimately invoke. - fn handle_query(&self, query: LayoutQuery, reply_chan: Chan>) { + fn handle_query(&self, query: LayoutQuery) { match query { - ContentBoxQuery(node) => { + ContentBoxQuery(node, reply_chan) => { // FIXME: Isolate this transmutation into a single "bridge" module. let node: AbstractNode = unsafe { transmute(node) @@ -302,7 +302,7 @@ impl Layout { reply_chan.send(response) } - ContentBoxesQuery(node) => { + ContentBoxesQuery(node, reply_chan) => { // FIXME: Isolate this transmutation into a single "bridge" module. let node: AbstractNode = unsafe { transmute(node) @@ -322,7 +322,7 @@ impl Layout { reply_chan.send(response) } - HitTestQuery(node, point) => { + HitTestQuery(node, point, reply_chan) => { // FIXME: Isolate this transmutation into a single "bridge" module. let node: AbstractNode = unsafe { transmute(node) diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs index b6b3e43eb99..59093ed70ff 100644 --- a/src/components/main/layout/text.rs +++ b/src/components/main/layout/text.rs @@ -234,7 +234,7 @@ impl TextRunScanner { for clump.eachi |i| { let range = new_ranges[i - self.clump.begin()]; if range.length() == 0 { - error!("Elided an `UnscannedTextbox` because it was zero-length after \ + debug!("Elided an `UnscannedTextbox` because it was zero-length after \ compression; %s", in_boxes[i].debug_str()); loop diff --git a/src/components/main/macros.rs b/src/components/main/macros.rs index 8fe65bccfb6..ed7ec2d2f15 100644 --- a/src/components/main/macros.rs +++ b/src/components/main/macros.rs @@ -1,71 +1,12 @@ /* 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/. */ - -{ - macro_rules! move_ref( - { $x:expr } => { unsafe { let y <- *ptr::to_unsafe_ptr(*$x); y } } - ) - - macro_rules! move_val( - { $x:expr } => { unsafe { let y <- *ptr::to_unsafe_ptr(*$x); y } } - ) - - // select! - macro_rules! select_if( - +#[macro_escape]; +macro_rules! special_stream( + ($Chan:ident) => ( { - $index:expr, - $count:expr - } => { - fail - }; - - { - $index:expr, - $count:expr, - $port:path => [ - $(type_this $message:path$(($(x $x: ident),+))dont_type_this* - -> $next:ident => { $e:expr }),+ - ] - $(, $ports:path => [ - $(type_this $messages:path$(($(x $xs: ident),+))dont_type_this* - -> $nexts:ident => { $es:expr }),+ - ] )* - } => { - if $index == $count { - match pipes::try_recv($port) { - $(Some($message($($(ref $x,)+)* ref next)) => { - // FIXME (#2329) we really want move out of enum here. - let $next = move_ref!(next); - $e - })+ - _ => fail - } - } else { - select_if!( - $index, - $count + 1 - $(, $ports => [ - $(type_this $messages$(($(x $xs),+))dont_type_this* - -> $nexts => { $es }),+ - ])* - ) - } - }; - ) - - macro_rules! select( - { - $( $port:path => { - $($message:path$(($($x: ident),+))dont_type_this* - -> $next:ident $e:expr),+ - } )+ - } => { - let index = pipes::selecti([$(($port).header()),+]/_); - select_if!(index, 0 $(, $port => [ - $(type_this $message$(($(x $x),+))dont_type_this* -> $next => { $e }),+ - ])+) + let (port, chan) = comm::stream::(); + (port, $Chan::new(chan)) } - ) -} + ); +) diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs new file mode 100644 index 00000000000..f5b6cf9c664 --- /dev/null +++ b/src/components/main/pipeline.rs @@ -0,0 +1,110 @@ +/* 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/. */ + +use extra::net::url::Url; +use compositing::CompositorChan; +use gfx::render_task::{RenderChan, RenderTask}; +use gfx::render_task; +use gfx::opts::Opts; +use layout::layout_task::LayoutTask; +use script::layout_interface::LayoutChan; +use script::script_task::LoadMsg; +use servo_msg::constellation_msg::{ConstellationChan, NavigationType}; +use script::script_task::{ScriptTask, ScriptChan}; +use script::script_task; +use servo_net::image_cache_task::ImageCacheTask; +use servo_net::resource_task::ResourceTask; +use servo_util::time::ProfilerChan; +use std::comm; + +/// A uniquely-identifiable pipeline of stript task, layout task, and render task. +pub struct Pipeline { + id: uint, + script_chan: ScriptChan, + layout_chan: LayoutChan, + render_chan: RenderChan, + /// The most recently loaded url + url: Option, + navigation_type: Option, +} + +impl Pipeline { + /// Starts a render task, layout task, and script task. Returns the channels wrapped in a struct. + pub fn create(id: uint, + constellation_chan: ConstellationChan, + compositor_chan: CompositorChan, + image_cache_task: ImageCacheTask, + resource_task: ResourceTask, + profiler_chan: ProfilerChan, + opts: Opts) -> Pipeline { + + let (script_port, script_chan) = special_stream!(ScriptChan); + let (layout_port, layout_chan) = special_stream!(LayoutChan); + let (render_port, render_chan) = special_stream!(RenderChan); + + RenderTask::create(id, + render_port, + compositor_chan.clone(), + copy opts, + constellation_chan.clone(), + profiler_chan.clone()); + + LayoutTask::create(layout_port, + script_chan.clone(), + render_chan.clone(), + image_cache_task.clone(), + copy opts, + profiler_chan); + + ScriptTask::create(id, + compositor_chan, + layout_chan.clone(), + script_port, + script_chan.clone(), + constellation_chan, + resource_task, + image_cache_task); + + Pipeline::new(id, + script_chan, + layout_chan, + render_chan) + } + + pub fn new(id: uint, + script_chan: ScriptChan, + layout_chan: LayoutChan, + render_chan: RenderChan) + -> Pipeline { + Pipeline { + id: id, + script_chan: script_chan, + layout_chan: layout_chan, + render_chan: render_chan, + url: None, + navigation_type: None, + } + } + + pub fn load(&mut self, url: Url) { + self.url = Some(url.clone()); + self.script_chan.send(LoadMsg(url)); + } + + pub fn reload(&self) { + for self.url.iter().advance |&url| { + self.script_chan.send(LoadMsg(url)); + } + } + + pub fn exit(&self) { + // Script task handles shutting down layout, as well + self.script_chan.send(script_task::ExitMsg); + + let (response_port, response_chan) = comm::stream(); + self.render_chan.send(render_task::ExitMsg(response_chan)); + response_port.recv(); + } +} + diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs index a47fab68acc..95fd22d9ef4 100644 --- a/src/components/main/platform/common/glut_windowing.rs +++ b/src/components/main/platform/common/glut_windowing.rs @@ -9,15 +9,16 @@ use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, MouseCallback}; use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent, WindowClickEvent}; -use windowing::{WindowMouseDownEvent, WindowMouseUpEvent, ZoomCallback}; +use windowing::{WindowMouseDownEvent, WindowMouseUpEvent, ZoomCallback, Forward, Back, NavigationCallback}; use alert::{Alert, AlertMethods}; use std::libc::c_int; use geom::point::Point2D; use geom::size::Size2D; -use servo_msg::compositor::{IdleRenderState, RenderState, RenderingRenderState}; -use servo_msg::compositor::{FinishedLoading, Loading, PerformingLayout, ReadyState}; -use glut::glut::{ACTIVE_CTRL, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight, WindowWidth}; +use servo_msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState}; +use servo_msg::compositor_msg::{FinishedLoading, Loading, PerformingLayout, ReadyState}; +use glut::glut::{ACTIVE_CTRL, ACTIVE_SHIFT, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight}; +use glut::glut::WindowWidth; use glut::glut; use glut::machack; @@ -44,6 +45,7 @@ pub struct Window { mouse_callback: Option, scroll_callback: Option, zoom_callback: Option, + navigation_callback: Option, drag_origin: Point2D, @@ -72,6 +74,7 @@ impl WindowMethods for Window { mouse_callback: None, scroll_callback: None, zoom_callback: None, + navigation_callback: None, drag_origin: Point2D(0 as c_int, 0), @@ -177,6 +180,11 @@ impl WindowMethods for Window { self.zoom_callback = Some(new_zoom_callback) } + /// Registers a callback to be run when backspace or shift-backspace is pressed. + pub fn set_navigation_callback(&mut self, new_navigation_callback: NavigationCallback) { + self.navigation_callback = Some(new_navigation_callback) + } + /// Spins the event loop. pub fn check_loop(@mut self) { glut::check_loop() @@ -226,20 +234,33 @@ impl Window { /// Helper function to handle keyboard events. fn handle_key(&self, key: u8) { - debug!("got key: %d", key as int); + debug!("got key: %?", key); + let modifiers = glut::get_modifiers(); match key { - 12 => self.load_url(), // Ctrl+L - k if k == ('=' as u8) && (glut::get_modifiers() & ACTIVE_CTRL) != 0 => { // Ctrl++ - for self.zoom_callback.iter().advance |&callback| { - callback(0.1); - } - } - k if k == 31 && (glut::get_modifiers() & ACTIVE_CTRL) != 0 => { // Ctrl+- + 12 => self.load_url(), // Ctrl+L + 31 if (modifiers & ACTIVE_CTRL) != 0 => { // Ctrl+- for self.zoom_callback.iter().advance |&callback| { callback(-0.1); } } - _ => {} + 127 => { + for self.navigation_callback.iter().advance |&callback| { + if (modifiers & ACTIVE_SHIFT) != 0 { // Shift+Backspace + callback(Forward); + } + else { + callback(Back); + } + } + } + c => match c as char { + '=' if (modifiers & ACTIVE_CTRL) != 0 => { // Ctrl++ + for self.zoom_callback.iter().advance |&callback| { + callback(0.1); + } + } + _ => {} + } } } diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc index d2b3ae08c41..3ee37198619 100755 --- a/src/components/main/servo.rc +++ b/src/components/main/servo.rc @@ -35,8 +35,8 @@ extern mod core_graphics; extern mod core_text; use compositing::{CompositorChan, CompositorTask}; -use engine::Engine; -use servo_msg::engine::{ExitMsg, LoadUrlMsg}; +use constellation::Constellation; +use servo_msg::constellation_msg::{ExitMsg, LoadUrlMsg}; use gfx::opts; use servo_net::image_cache_task::ImageCacheTask; @@ -53,6 +53,8 @@ use std::os; #[path="compositing/mod.rs"] pub mod compositing; +pub mod macros; + pub mod css { priv mod select_handler; priv mod node_util; @@ -62,7 +64,8 @@ pub mod css { pub mod node_style; } -pub mod engine; +pub mod constellation; +pub mod pipeline; pub mod layout { pub mod block; @@ -121,24 +124,24 @@ fn run(opts: &Opts) { let resource_task = ResourceTask(); let image_cache_task = ImageCacheTask(resource_task.clone()); - let engine_chan = Engine::start(compositor_chan.clone(), + let constellation_chan = Constellation::start(compositor_chan.clone(), opts, resource_task, image_cache_task, profiler_chan.clone()); - // Send the URL command to the engine task. + // Send the URL command to the constellation. for opts.urls.each |filename| { - engine_chan.send(LoadUrlMsg(make_url(copy *filename, None))) + constellation_chan.send(LoadUrlMsg(make_url(copy *filename, None))) } // Wait for the compositor to shut down. shutdown_port.recv(); - // Shut the engine down. + // Shut the constellation down. debug!("master: Shut down"); - let (exit_response_from_engine, exit_chan) = comm::stream(); - engine_chan.send(ExitMsg(exit_chan)); - exit_response_from_engine.recv(); + let (exit_response_from_constellation, exit_chan) = comm::stream(); + constellation_chan.send(ExitMsg(exit_chan)); + exit_response_from_constellation.recv(); } diff --git a/src/components/main/windowing.rs b/src/components/main/windowing.rs index 56a6cbcb0ab..8a102c055c4 100644 --- a/src/components/main/windowing.rs +++ b/src/components/main/windowing.rs @@ -6,7 +6,7 @@ use geom::point::Point2D; use geom::size::Size2D; -use servo_msg::compositor::{ReadyState, RenderState}; +use servo_msg::compositor_msg::{ReadyState, RenderState}; pub enum WindowMouseEvent { WindowClickEvent(uint, Point2D), @@ -14,6 +14,11 @@ pub enum WindowMouseEvent { WindowMouseUpEvent(uint, Point2D), } +pub enum WindowNavigateMsg { + Forward, + Back, +} + /// Type of the function that is called when the screen is to be redisplayed. pub type CompositeCallback = @fn(); @@ -29,9 +34,12 @@ pub type MouseCallback = @fn(WindowMouseEvent); /// Type of the function that is called when the user scrolls. pub type ScrollCallback = @fn(Point2D); -///Type of the function that is called when the user zooms. +/// Type of the function that is called when the user zooms. pub type ZoomCallback = @fn(f32); +/// Type of the function that is called when the user clicks backspace or shift-backspace +pub type NavigationCallback = @fn(WindowNavigateMsg); + /// Methods for an abstract Application. pub trait ApplicationMethods { fn new() -> Self; @@ -57,6 +65,8 @@ pub trait WindowMethods { pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback); /// Registers a callback to run when the user zooms. pub fn set_zoom_callback(&mut self, new_zoom_callback: ZoomCallback); + /// Registers a callback to run when the user presses backspace or shift-backspace. + pub fn set_navigation_callback(&mut self, new_navigation_callback: NavigationCallback); /// Spins the event loop. pub fn check_loop(@mut self); diff --git a/src/components/msg/compositor.rs b/src/components/msg/compositor_msg.rs similarity index 66% rename from src/components/msg/compositor.rs rename to src/components/msg/compositor_msg.rs index 6b6311fd1ba..666a40d4b9b 100644 --- a/src/components/msg/compositor.rs +++ b/src/components/msg/compositor_msg.rs @@ -6,7 +6,9 @@ use azure::azure_hl::DrawTarget; use azure::azure::AzGLContext; use geom::rect::Rect; use geom::size::Size2D; +use std::util::NonCopyable; +#[deriving(Clone)] pub struct LayerBuffer { draw_target: DrawTarget, @@ -22,6 +24,7 @@ pub struct LayerBuffer { /// A set of layer buffers. This is an atomic unit used to switch between the front and back /// buffers. +#[deriving(Clone)] pub struct LayerBufferSet { buffers: ~[LayerBuffer] } @@ -33,14 +36,6 @@ pub enum RenderState { RenderingRenderState, } -/// The interface used by the renderer to acquire draw targets for each rendered frame and -/// submit them to be drawn to the display. -pub trait RenderListener { - fn get_gl_context(&self) -> AzGLContext; - fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D); - fn set_render_state(&self, render_state: RenderState); -} - pub enum ReadyState { /// Informs the compositor that a page is loading. Used for setting status Loading, @@ -50,8 +45,33 @@ pub enum ReadyState { FinishedLoading, } +/// The interface used by the renderer to acquire draw targets for each render frame and +/// submit them to be drawn to the display. +pub trait RenderListener { + fn get_gl_context(&self) -> AzGLContext; + fn paint(&self, id: uint, layer_buffer_set: LayerBufferSet, new_size: Size2D); + fn set_render_state(&self, render_state: RenderState); +} + /// The interface used by the script task to tell the compositor to update its ready state, /// which is used in displaying the appropriate message in the window's title. pub trait ScriptListener : Clone { fn set_ready_state(&self, ReadyState); } + +/// Signifies to the renderer likely control of the compositor. Controlling the compositor token +/// is necessary but not sufficient for the renderer to successfully send paint messages to the +/// compositor. Only the render tasks controlling compositor tokens may send messages, and the +/// compositor is guaranteed to only accept messages from one of those tasks at a time. +pub struct CompositorToken { + construction_restrictor: NonCopyable, +} + +impl CompositorToken { + pub fn new() -> CompositorToken { + CompositorToken { + // Of course, this doesn't guarantee that renderers will invalidate their tokens + construction_restrictor: NonCopyable::new(), + } + } +} diff --git a/src/components/msg/constellation_msg.rs b/src/components/msg/constellation_msg.rs new file mode 100644 index 00000000000..bffd0b513b9 --- /dev/null +++ b/src/components/msg/constellation_msg.rs @@ -0,0 +1,44 @@ +/* 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/. */ + +//! The high-level interface from script to constellation. Using this abstract interface helps reduce +/// coupling between these two components + +use std::comm::{Chan, SharedChan}; +use extra::net::url::Url; + +#[deriving(Clone)] +pub struct ConstellationChan { + chan: SharedChan, +} + +impl ConstellationChan { + pub fn new(chan: Chan) -> ConstellationChan { + ConstellationChan { + chan: SharedChan::new(chan), + } + } + pub fn send(&self, msg: Msg) { + self.chan.send(msg); + } +} + +pub enum Msg { + LoadUrlMsg(Url), + NavigateMsg(NavigationDirection), + ExitMsg(Chan<()>), + RendererReadyMsg(uint), + CompositorAck(uint), +} + +/// Represents the two different ways to which a page can be navigated +enum NavigationType { + Load, // entered or clicked on a url + Navigate, // browser forward/back buttons +} + +pub enum NavigationDirection { + Forward, + Back, +} diff --git a/src/components/msg/engine.rs b/src/components/msg/engine.rs deleted file mode 100644 index 83e0f085ee0..00000000000 --- a/src/components/msg/engine.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* 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/. */ - -//! The high-level interface from script to engine. Using this abstract interface helps reduce -/// coupling between these two components - -use std::comm::{Chan, SharedChan}; -use extra::net::url::Url; - -#[deriving(Clone)] -pub struct EngineChan { - chan: SharedChan, -} - -impl EngineChan { - pub fn new(chan: Chan) -> EngineChan { - EngineChan { - chan: SharedChan::new(chan), - } - } - pub fn send(&self, msg: Msg) { - self.chan.send(msg); - } -} - -pub enum Msg { - LoadUrlMsg(Url), - ExitMsg(Chan<()>), -} - diff --git a/src/components/msg/msg.rc b/src/components/msg/msg.rc index efafbecd2e6..318d91fbdc6 100644 --- a/src/components/msg/msg.rc +++ b/src/components/msg/msg.rc @@ -14,5 +14,5 @@ extern mod std; extern mod geom; extern mod extra; -pub mod compositor; -pub mod engine; +pub mod compositor_msg; +pub mod constellation_msg; diff --git a/src/components/script/dom/bindings/element.rs b/src/components/script/dom/bindings/element.rs index 9a34f7c5dc6..b79daf75db7 100644 --- a/src/components/script/dom/bindings/element.rs +++ b/src/components/script/dom/bindings/element.rs @@ -16,6 +16,7 @@ use std::cast; use std::i32; use std::libc; use std::libc::c_uint; +use std::comm; use std::ptr; use std::ptr::null; use std::result; @@ -229,13 +230,9 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa let width = match node.type_id() { ElementNodeTypeId(HTMLImageElementTypeId) => { let script_context = task_from_context(cx); - match (*script_context).query_layout(ContentBoxQuery(node)) { - Ok(rect) => { - match rect { - ContentBoxResponse(rect) => rect.size.width.to_px(), - _ => fail!(~"unexpected layout reply") - } - } + let (port, chan) = comm::stream(); + match (*script_context).query_layout(ContentBoxQuery(node, chan), port) { + Ok(ContentBoxResponse(rect)) => rect.size.width.to_px(), Err(()) => 0 } // TODO: if nothing is being rendered(?), return zero dimensions diff --git a/src/components/script/dom/bindings/proxyhandler.rs b/src/components/script/dom/bindings/proxyhandler.rs index 2780a969eb2..cbbc4f1525d 100644 --- a/src/components/script/dom/bindings/proxyhandler.rs +++ b/src/components/script/dom/bindings/proxyhandler.rs @@ -71,7 +71,7 @@ pub fn _obj_toString(cx: *JSContext, className: *libc::c_char) -> *JSString { return ptr::null(); } - let result = ~"[object " + name + ~"]"; + let result = ~"[object " + name + "]"; for result.iter().enumerate().advance |(i, c)| { *chars.offset(i) = c as jschar; } @@ -86,4 +86,4 @@ pub fn _obj_toString(cx: *JSContext, className: *libc::c_char) -> *JSString { pub fn GetExpandoObject(_proxy: *JSObject) -> *JSObject { ptr::null() -} \ No newline at end of file +} diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 9d3105fefca..5a42e109268 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -21,12 +21,12 @@ use js::glue::{PROPERTY_STUB, STRICT_PROPERTY_STUB, ENUMERATE_STUB, CONVERT_STUB use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewObject, JS_NewFunction}; use js::jsapi::{JS_DefineProperties, JS_WrapValue, JS_ForwardGetPropertyTo}; use js::jsapi::{JS_EncodeString, JS_free, JS_GetStringCharsAndLength}; -use js::jsapi::{JS_GetClass, JS_GetPrototype, JS_LinkConstructorAndPrototype}; +use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype}; use js::jsapi::{JS_GetFunctionPrototype, JS_InternString, JS_GetFunctionObject}; use js::jsapi::{JS_HasPropertyById, JS_GetPrototype, JS_GetGlobalForObject}; use js::jsapi::{JS_NewStringCopyN, JS_DefineFunctions, JS_DefineProperty}; use js::jsapi::{JS_ValueToString, JS_GetReservedSlot, JS_SetReservedSlot}; -use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSNative}; +use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass, JSNative}; use js::jsapi::{JSFunctionSpec, JSPropertySpec, JSVal, JSPropertyDescriptor}; use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType; use js::rust::Compartment; @@ -83,7 +83,7 @@ extern fn InterfaceObjectToString(cx: *JSContext, _argc: uint, vp: *mut JSVal) - } let name = jsval_to_str(cx, *v).get(); - let retval = str(~"function " + name + ~"() {\n [native code]\n}"); + let retval = str(~"function " + name + "() {\n [native code]\n}"); *vp = domstring_to_jsval(cx, &retval); return 1; } diff --git a/src/components/script/dom/characterdata.rs b/src/components/script/dom/characterdata.rs index 91838432f6b..9d77d3b64ee 100644 --- a/src/components/script/dom/characterdata.rs +++ b/src/components/script/dom/characterdata.rs @@ -7,8 +7,6 @@ use dom::bindings::utils::{DOMString, null_string, str}; use dom::node::{Node, NodeTypeId, ScriptView}; -use std::str; - pub struct CharacterData { parent: Node, data: DOMString diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index e707a95ebb9..2595a68657d 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -12,6 +12,7 @@ use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery}; use layout_interface::{ContentBoxesResponse}; use std::cell::Cell; +use std::comm; use std::uint; use std::str::eq_slice; use extra::net::url::Url; @@ -165,20 +166,19 @@ impl<'self> Element { Some(win) => { let node = self.parent.abstract.get(); assert!(node.is_element()); - let script_context = unsafe { - &mut *win.script_context + let script_task = unsafe { + &mut *win.script_task }; - match script_context.query_layout(ContentBoxesQuery(node)) { - Ok(rects) => match rects { - ContentBoxesResponse(rects) => - do rects.map |r| { - ClientRect::new( - r.origin.y.to_f32(), - (r.origin.y + r.size.height).to_f32(), - r.origin.x.to_f32(), - (r.origin.x + r.size.width).to_f32()) - }, - _ => fail!(~"unexpected layout reply") + let (port, chan) = comm::stream(); + match script_task.query_layout(ContentBoxesQuery(node, chan), port) { + Ok(ContentBoxesResponse(rects)) => { + do rects.map |r| { + ClientRect::new( + r.origin.y.to_f32(), + (r.origin.y + r.size.height).to_f32(), + r.origin.x.to_f32(), + (r.origin.x + r.size.width).to_f32()) + } }, Err(()) => { debug!("layout query error"); @@ -207,16 +207,15 @@ impl<'self> Element { Some(win) => { let node = self.parent.abstract.get(); assert!(node.is_element()); - let script_context = unsafe { &mut *win.script_context }; - match script_context.query_layout(ContentBoxQuery(node)) { - Ok(rect) => match rect { - ContentBoxResponse(rect) => - Some(ClientRect::new( - rect.origin.y.to_f32(), - (rect.origin.y + rect.size.height).to_f32(), - rect.origin.x.to_f32(), - (rect.origin.x + rect.size.width).to_f32())), - _ => fail!(~"unexpected layout result") + let script_task = unsafe { &mut *win.script_task }; + let (port, chan) = comm::stream(); + match script_task.query_layout(ContentBoxQuery(node, chan), port) { + Ok(ContentBoxResponse(rect)) => { + Some(ClientRect::new( + rect.origin.y.to_f32(), + (rect.origin.y + rect.size.height).to_f32(), + rect.origin.x.to_f32(), + (rect.origin.x + rect.size.width).to_f32())) }, Err(()) => { debug!("error querying layout"); diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs index 047b734e6b4..6b4be15b701 100644 --- a/src/components/script/dom/event.rs +++ b/src/components/script/dom/event.rs @@ -9,8 +9,6 @@ use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache}; use geom::point::Point2D; -use std::comm; - pub enum Event { ResizeEvent(uint, uint), ReflowEvent, diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index d415288bfa2..c573f9912fb 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -367,7 +367,7 @@ impl AbstractNode { pub fn dump_indent(&self, indent: uint) { let mut s = ~""; for uint::range(0u, indent) |_i| { - s += ~" "; + s += " "; } s += self.debug_str(); diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index 5fb07c66f07..41f99eaa9e9 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -6,7 +6,7 @@ use dom::bindings::utils::WrapperCache; use dom::bindings::window; use layout_interface::ReflowForScriptQuery; -use script_task::{ExitMsg, FireTimerMsg, ScriptChan, ScriptContext}; +use script_task::{ExitMsg, FireTimerMsg, ScriptChan, ScriptTask}; use std::comm; use std::comm::Chan; @@ -29,7 +29,7 @@ pub enum TimerControlMsg { pub struct Window { timer_chan: Chan, script_chan: ScriptChan, - script_context: *mut ScriptContext, + script_task: *mut ScriptTask, wrapper: WrapperCache } @@ -89,11 +89,11 @@ impl Window { pub fn content_changed(&self) { unsafe { - (*self.script_context).reflow_all(ReflowForScriptQuery) + (*self.script_task).reflow_all(ReflowForScriptQuery) } } - pub fn new(script_chan: ScriptChan, script_context: *mut ScriptContext) + pub fn new(script_chan: ScriptChan, script_task: *mut ScriptTask) -> @mut Window { let script_chan_clone = script_chan.clone(); let win = @mut Window { @@ -112,11 +112,11 @@ impl Window { } timer_chan }, - script_context: script_context, + script_task: script_task, }; unsafe { - let compartment = (*script_context).js_compartment; + let compartment = (*script_task).js_compartment; window::create(compartment, win); } win diff --git a/src/components/script/html/cssparse.rs b/src/components/script/html/cssparse.rs index 1278ba47f6c..95a600682ac 100644 --- a/src/components/script/html/cssparse.rs +++ b/src/components/script/html/cssparse.rs @@ -8,7 +8,6 @@ use std::cell::Cell; use std::comm; use std::comm::Port; use std::task; -use std::str; use newcss::stylesheet::Stylesheet; use newcss::util::DataStream; use servo_net::resource_task::{ResourceTask, ProgressMsg, Load, Payload, Done}; diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index 6309e488d7e..9540fcc87d4 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -303,9 +303,9 @@ pub fn parse_html(url: Url, // Handle CSS style sheets from elements ElementNodeTypeId(HTMLLinkElementTypeId) => { do node.with_imm_element |element| { - match (element.get_attr(~"rel"), element.get_attr(~"href")) { + match (element.get_attr("rel"), element.get_attr("href")) { (Some(rel), Some(href)) => { - if rel == ~"stylesheet" { + if rel == "stylesheet" { debug!("found CSS stylesheet: %s", href); let url = make_url(href.to_str(), Some(url2.clone())); css_chan2.send(CSSTaskNewFile(UrlProvenance(url))); @@ -317,7 +317,7 @@ pub fn parse_html(url: Url, }, ElementNodeTypeId(HTMLImageElementTypeId) => { do node.with_mut_image_element |image_element| { - let src_opt = image_element.parent.get_attr(~"src").map(|x| x.to_str()); + let src_opt = image_element.parent.get_attr("src").map(|x| x.to_str()); match src_opt { None => {} Some(src) => { @@ -401,7 +401,7 @@ pub fn parse_html(url: Url, unsafe { let script: AbstractNode = NodeWrapping::from_hubbub_node(script); do script.with_imm_element |script| { - match script.get_attr(~"src") { + match script.get_attr("src") { Some(src) => { debug!("found script: %s", src); let new_url = make_url(src.to_str(), Some(url.clone())); diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs index 3a1531b83e6..766c728067b 100644 --- a/src/components/script/layout_interface.rs +++ b/src/components/script/layout_interface.rs @@ -30,7 +30,7 @@ pub enum Msg { /// Performs a synchronous layout request. /// /// FIXME(pcwalton): As noted below, this isn't very type safe. - QueryMsg(LayoutQuery, Chan>), + QueryMsg(LayoutQuery), /// Routes a message (usually from the compositor) to the appropriate script task RouteScriptMsg(ScriptMsg), @@ -42,25 +42,16 @@ pub enum Msg { /// Synchronous messages that script can send to layout. pub enum LayoutQuery { /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call. - ContentBoxQuery(AbstractNode), + ContentBoxQuery(AbstractNode, Chan>), /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. - ContentBoxesQuery(AbstractNode), + ContentBoxesQuery(AbstractNode, Chan>), /// Requests the node containing the point of interest - HitTestQuery(AbstractNode, Point2D), + HitTestQuery(AbstractNode, Point2D, Chan>), } -/// The reply of a synchronous message from script to layout. -/// -/// FIXME(pcwalton): This isn't very type safe. Maybe `LayoutQuery` objects should include -/// response channels? -pub enum LayoutResponse { - /// A response to the `ContentBoxQuery` message. - ContentBoxResponse(Rect), - /// A response to the `ContentBoxesQuery` message. - ContentBoxesResponse(~[Rect]), - /// A response to the `HitTestQuery` message. - HitTestResponse(AbstractNode), -} +pub struct ContentBoxResponse(Rect); +pub struct ContentBoxesResponse(~[Rect]); +pub struct HitTestResponse(AbstractNode); /// Determines which part of the pub enum DocumentDamageLevel { diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 7ef0b0035e1..4d46b241b3c 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -5,19 +5,23 @@ /// The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing /// and layout tasks. -use servo_msg::compositor::{ReadyState, Loading, PerformingLayout, FinishedLoading}; +use servo_msg::compositor_msg::{ScriptListener, Loading, PerformingLayout}; +use servo_msg::compositor_msg::FinishedLoading; use dom::bindings::utils::GlobalStaticData; use dom::document::Document; use dom::element::Element; use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseUpEvent}; use dom::node::{AbstractNode, ScriptView, define_bindings}; use dom::window::Window; -use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery}; -use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutChan}; -use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage}; -use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg}; +use layout_interface::{AddStylesheetMsg, DocumentDamage}; +use layout_interface::{DocumentDamageLevel, HitTestQuery, HitTestResponse, LayoutQuery}; +use layout_interface::{LayoutChan, MatchSelectorsDocumentDamage, QueryMsg, Reflow}; +use layout_interface::{ReflowDocumentDamage, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal}; +use layout_interface::ReflowMsg; use layout_interface; -use servo_msg::engine::{EngineChan, LoadUrlMsg}; +use servo_msg::constellation_msg::{ConstellationChan, LoadUrlMsg, NavigationDirection}; +use servo_msg::constellation_msg::RendererReadyMsg; +use servo_msg::constellation_msg; use std::cast::transmute; use std::cell::Cell; @@ -51,13 +55,15 @@ pub enum ScriptMsg { LoadMsg(Url), /// Executes a standalone script. ExecuteMsg(Url), + /// Instructs the script task to send a navigate message to the constellation. + NavigateMsg(NavigationDirection), /// Sends a DOM event. SendEventMsg(Event), /// Fires a JavaScript timeout. FireTimerMsg(~TimerData), /// Notifies script that reflow is finished. ReflowCompleteMsg, - /// Exits the engine. + /// Exits the constellation. ExitMsg, } @@ -91,7 +97,9 @@ pub struct Frame { /// frames. /// /// FIXME: Rename to `Page`, following WebKit? -pub struct ScriptContext { +pub struct ScriptTask { + /// A unique identifier to the script's pipeline + id: uint, /// A handle to the layout task. layout_chan: LayoutChan, /// A handle to the image cache task. @@ -108,10 +116,10 @@ pub struct ScriptContext { /// messages. script_chan: ScriptChan, - /// For communicating load url messages to the engine - engine_chan: EngineChan, - /// For communicating loading messages to the compositor - compositor_task: ~fn(ReadyState), + /// For communicating load url messages to the constellation + constellation_chan: ConstellationChan, + /// For permission to communicate ready state messages to the compositor + compositor: @ScriptListener, /// The JavaScript runtime. js_runtime: js::rust::rt, @@ -132,28 +140,33 @@ pub struct ScriptContext { window_size: Size2D, /// What parts of the document are dirty, if any. damage: Option, + + /// Cached copy of the most recent url loaded by the script + /// TODO(tkuehn): this currently does not follow any particular caching policy + /// and simply caches pages forever (!). + last_loaded_url: Option, } -fn global_script_context_key(_: @ScriptContext) {} +fn global_script_context_key(_: @ScriptTask) {} /// Returns this task's script context singleton. -pub fn global_script_context() -> @ScriptContext { +pub fn global_script_context() -> @ScriptTask { unsafe { local_data::local_data_get(global_script_context_key).get() } } -/// Returns the script context from the JS Context. +/// Returns the script task from the JS Context. /// /// FIXME: Rename to `script_context_from_js_context`. -pub fn task_from_context(js_context: *JSContext) -> *mut ScriptContext { +pub fn task_from_context(js_context: *JSContext) -> *mut ScriptTask { unsafe { - JS_GetContextPrivate(js_context) as *mut ScriptContext + JS_GetContextPrivate(js_context) as *mut ScriptTask } } #[unsafe_destructor] -impl Drop for ScriptContext { +impl Drop for ScriptTask { fn finalize(&self) { unsafe { let _ = local_data::local_data_pop(global_script_context_key); @@ -161,16 +174,17 @@ impl Drop for ScriptContext { } } -impl ScriptContext { - /// Creates a new script context. - pub fn new(layout_chan: LayoutChan, +impl ScriptTask { + /// Creates a new script task. + pub fn new(id: uint, + compositor: @ScriptListener, + layout_chan: LayoutChan, script_port: Port, script_chan: ScriptChan, - engine_chan: EngineChan, - compositor_task: ~fn(ReadyState), + constellation_chan: ConstellationChan, resource_task: ResourceTask, img_cache_task: ImageCacheTask) - -> @mut ScriptContext { + -> @mut ScriptTask { let js_runtime = js::rust::rt(); let js_context = js_runtime.cx(); @@ -182,7 +196,10 @@ impl ScriptContext { Err(()) => fail!("Failed to create a compartment"), }; - let script_context = @mut ScriptContext { + let script_task = @mut ScriptTask { + id: id, + compositor: compositor, + layout_chan: layout_chan, image_cache_task: img_cache_task, resource_task: resource_task, @@ -191,8 +208,7 @@ impl ScriptContext { script_port: script_port, script_chan: script_chan, - engine_chan: engine_chan, - compositor_task: compositor_task, + constellation_chan: constellation_chan, js_runtime: js_runtime, js_context: js_context, @@ -205,19 +221,21 @@ impl ScriptContext { window_size: Size2D(800u, 600), damage: None, + + last_loaded_url: None, }; // Indirection for Rust Issue #6248, dynamic freeze scope artifically extended - let script_context_ptr = { - let borrowed_ctx= &mut *script_context; - borrowed_ctx as *mut ScriptContext + let script_task_ptr = { + let borrowed_ctx= &mut *script_task; + borrowed_ctx as *mut ScriptTask }; unsafe { - js_context.set_cx_private(script_context_ptr as *()); - local_data::local_data_set(global_script_context_key, transmute(script_context)) + js_context.set_cx_private(script_task_ptr as *()); + local_data::local_data_set(global_script_context_key, transmute(script_task)) } - script_context + script_task } /// Starts the script task. After calling this method, the script task will loop receiving @@ -228,58 +246,47 @@ impl ScriptContext { } } - pub fn create_script_context(layout_chan: LayoutChan, - script_port: Port, - script_chan: ScriptChan, - engine_chan: EngineChan, - compositor_task: ~fn(ReadyState), - resource_task: ResourceTask, - image_cache_task: ImageCacheTask) { + pub fn create(id: uint, + compositor: C, + layout_chan: LayoutChan, + script_port: Port, + script_chan: ScriptChan, + constellation_chan: ConstellationChan, + resource_task: ResourceTask, + image_cache_task: ImageCacheTask) { + let compositor = Cell::new(compositor); let script_port = Cell::new(script_port); - let compositor_task = Cell::new(compositor_task); // FIXME: rust#6399 let mut the_task = task(); the_task.sched_mode(SingleThreaded); - do the_task.spawn { - let script_context = ScriptContext::new(layout_chan.clone(), - script_port.take(), - script_chan.clone(), - engine_chan.clone(), - compositor_task.take(), - resource_task.clone(), - image_cache_task.clone()); - script_context.start(); + do spawn { + let script_task = ScriptTask::new(id, + @compositor.take() as @ScriptListener, + layout_chan.clone(), + script_port.take(), + script_chan.clone(), + constellation_chan.clone(), + resource_task.clone(), + image_cache_task.clone()); + script_task.start(); } } /// Handles an incoming control message. fn handle_msg(&mut self) -> bool { match self.script_port.recv() { - LoadMsg(url) => { - self.load(url); - true - } - ExecuteMsg(url) => { - self.handle_execute_msg(url); - true - } - SendEventMsg(event) => { - self.handle_event(event); - true - } - FireTimerMsg(timer_data) => { - self.handle_fire_timer_msg(timer_data); - true - } - ReflowCompleteMsg => { - self.handle_reflow_complete_msg(); - true - } + LoadMsg(url) => self.load(url), + ExecuteMsg(url) => self.handle_execute_msg(url), + SendEventMsg(event) => self.handle_event(event), + FireTimerMsg(timer_data) => self.handle_fire_timer_msg(timer_data), + NavigateMsg(direction) => self.handle_navigate_msg(direction), + ReflowCompleteMsg => self.handle_reflow_complete_msg(), ExitMsg => { self.handle_exit_msg(); - false + return false } } + true } /// Handles a request to execute a script. @@ -324,7 +331,13 @@ impl ScriptContext { /// Handles a notification that reflow completed. fn handle_reflow_complete_msg(&mut self) { self.layout_join_port = None; - self.set_ready_state(FinishedLoading) + self.constellation_chan.send(RendererReadyMsg(self.id)); + self.compositor.set_ready_state(FinishedLoading); + } + + /// Handles a navigate forward or backward message. + fn handle_navigate_msg(&self, direction: NavigationDirection) { + self.constellation_chan.send(constellation_msg::NavigateMsg(direction)); } /// Handles a request to exit the script task and shut down layout. @@ -337,15 +350,12 @@ impl ScriptContext { self.layout_chan.send(layout_interface::ExitMsg) } - // tells the compositor when loading starts and finishes - // FIXME ~compositor_interface doesn't work right now, which is why this is necessary - fn set_ready_state(&self, msg: ReadyState) { - (self.compositor_task)(msg); - } - /// The entry point to document loading. Defines bindings, sets up the window and document /// objects, parses HTML and CSS, and kicks off initial layout. fn load(&mut self, url: Url) { + for self.last_loaded_url.iter().advance |&last_loaded_url| { + if url == last_loaded_url { return; } + } // Define the script DOM bindings. // // FIXME: Can this be done earlier, to save the flag? @@ -354,11 +364,11 @@ impl ScriptContext { self.bindings_initialized = true } - self.set_ready_state(Loading); + self.compositor.set_ready_state(Loading); // Parse HTML. // // Note: We can parse the next document in parallel with any previous documents. - let html_parsing_result = hubbub_html_parser::parse_html(copy url, + let html_parsing_result = hubbub_html_parser::parse_html(url.clone(), self.resource_task.clone(), self.image_cache_task.clone()); @@ -392,7 +402,7 @@ impl ScriptContext { self.root_frame = Some(Frame { document: document, window: window, - url: url + url: url.clone(), }); // Perform the initial reflow. @@ -412,6 +422,7 @@ impl ScriptContext { ~"???", 1); } + self.last_loaded_url = Some(url); } /// Sends a ping to layout and waits for the response. The response will arrive when the @@ -446,7 +457,7 @@ impl ScriptContext { self.join_layout(); // Tell the user that we're performing layout. - self.set_ready_state(PerformingLayout); + self.compositor.set_ready_state(PerformingLayout); // Layout will let us know when it's done. let (join_port, join_chan) = comm::stream(); @@ -478,7 +489,7 @@ impl ScriptContext { /// FIXME: This should basically never be used. pub fn reflow_all(&mut self, goal: ReflowGoal) { for self.root_frame.iter().advance |root_frame| { - ScriptContext::damage(&mut self.damage, + ScriptTask::damage(&mut self.damage, root_frame.document.root, MatchSelectorsDocumentDamage) } @@ -487,12 +498,10 @@ impl ScriptContext { } /// Sends the given query to layout. - pub fn query_layout(&mut self, query: LayoutQuery) -> Result { - self.join_layout(); - - let (response_port, response_chan) = comm::stream(); - self.layout_chan.send(QueryMsg(query, response_chan)); - response_port.recv() + pub fn query_layout(&mut self, query: LayoutQuery, response_port: Port>) -> Result { + self.join_layout(); + self.layout_chan.send(QueryMsg(query)); + response_port.recv() } /// Adds the given damage. @@ -526,7 +535,7 @@ impl ScriptContext { self.window_size = Size2D(new_width, new_height); for self.root_frame.iter().advance |root_frame| { - ScriptContext::damage(&mut self.damage, + ScriptTask::damage(&mut self.damage, root_frame.document.root, ReflowDocumentDamage); } @@ -541,7 +550,7 @@ impl ScriptContext { debug!("script got reflow event"); for self.root_frame.iter().advance |root_frame| { - ScriptContext::damage(&mut self.damage, + ScriptTask::damage(&mut self.damage, root_frame.document.root, MatchSelectorsDocumentDamage); } @@ -557,7 +566,8 @@ impl ScriptContext { Some(ref frame) => frame.document.root, None => fail!("root frame is None") }; - match self.query_layout(HitTestQuery(root, point)) { + let (port, chan) = comm::stream(); + match self.query_layout(HitTestQuery(root, point, chan), port) { Ok(node) => match node { HitTestResponse(node) => { debug!("clicked on %?", node.debug_str()); @@ -580,12 +590,11 @@ impl ScriptContext { } } } - _ => fail!(~"unexpected layout reply") }, Err(()) => { debug!(fmt!("layout query error")); } - }; + } } MouseDownEvent(*) => {} MouseUpEvent(*) => {} @@ -594,7 +603,7 @@ impl ScriptContext { priv fn load_url_from_element(&self, element: &Element) { // if the node's element is "a," load url from href attr - for element.attrs.each |attr| { + for element.attrs.iter().advance |attr| { if attr.name == ~"href" { debug!("clicked on link to %?", attr.value); let current_url = match self.root_frame { @@ -602,7 +611,7 @@ impl ScriptContext { None => None }; let url = make_url(attr.value.clone(), current_url); - self.engine_chan.send(LoadUrlMsg(url)); + self.constellation_chan.send(LoadUrlMsg(url)); } } } diff --git a/src/components/util/time.rs b/src/components/util/time.rs index 00e222baaeb..ac94abcfb7a 100644 --- a/src/components/util/time.rs +++ b/src/components/util/time.rs @@ -152,7 +152,7 @@ impl Profiler { priv fn print_buckets(&mut self) { println(fmt!("%31s %15s %15s %15s %15s %15s", - "_category (ms)_", "_mean (ms)_", "_median (ms)_", + "_category_", "_mean (ms)_", "_median (ms)_", "_min (ms)_", "_max (ms)_", "_bucket size_")); for self.buckets.mut_iter().advance |bucket| { match *bucket {