diff options
Diffstat (limited to 'src')
30 files changed, 1324 insertions, 743 deletions
diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 4bb2b2a45a2..af80d384b69 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -9,6 +9,7 @@ use azure::azure_hl::{B8G8R8A8, DrawTarget}; use display_list::DisplayList; use servo_msg::compositor_msg::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer}; use servo_msg::compositor_msg::{LayerBufferSet}; +use servo_msg::constellation_msg::PipelineId; use font_context::FontContext; use geom::matrix2d::Matrix2D; use geom::size::Size2D; @@ -70,7 +71,7 @@ impl RenderChan { } priv struct RenderTask<C> { - id: uint, + id: PipelineId, port: Port<Msg>, compositor: C, font_ctx: @mut FontContext, @@ -90,7 +91,7 @@ priv struct RenderTask<C> { } impl<C: RenderListener + Send> RenderTask<C> { - pub fn create(id: uint, + pub fn create(id: PipelineId, port: Port<Msg>, compositor: C, opts: Opts, diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index f274ba835d0..ebb18f7441d 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -5,7 +5,6 @@ use platform::{Application, Window}; use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent}; use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg}; -use script::layout_interface::{LayoutChan, RouteScriptMsg}; use windowing::{ApplicationMethods, WindowEvent, WindowMethods}; use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass}; @@ -14,9 +13,9 @@ use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEven use servo_msg::compositor_msg::{RenderListener, LayerBuffer, LayerBufferSet, RenderState}; use servo_msg::compositor_msg::{ReadyState, ScriptListener}; -use servo_msg::constellation_msg::{CompositorAck, ConstellationChan}; +use servo_msg::constellation_msg::{CompositorAck, ConstellationChan, PipelineId}; use servo_msg::constellation_msg; -use gfx::render_task::{RenderChan, ReRenderMsg}; +use gfx::render_task::ReRenderMsg; use gfx::opts::Opts; use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context}; @@ -50,6 +49,8 @@ pub use windowing; use extra::time::precise_time_s; use compositing::quadtree::Quadtree; +use constellation::SendableFrameTree; +use pipeline::Pipeline; mod quadtree; /// The implementation of the layers-based compositor. @@ -78,7 +79,7 @@ impl RenderListener for CompositorChan { port.recv() } - fn paint(&self, id: uint, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>) { + fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>) { self.chan.send(Paint(id, layer_buffer_set, new_size)) } @@ -134,13 +135,13 @@ pub enum Msg { DeleteLayer, /// Requests that the compositor paint the given layer buffer set for the given page size. - Paint(uint, arc::ARC<LayerBufferSet>, Size2D<uint>), + Paint(PipelineId, arc::ARC<LayerBufferSet>, Size2D<uint>), /// 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 and render tasks, along with their id - SetLayoutRenderChans(LayoutChan, RenderChan , uint, ConstellationChan) + SetIds(SendableFrameTree, ConstellationChan) } /// Azure surface wrapping to work with the layers infrastructure. @@ -234,12 +235,12 @@ impl CompositorTask { let mut world_zoom = 1f32; // Keeps track of local zoom factor. Reset to 1 after a rerender event. let mut local_zoom = 1f32; - // Channel to the current renderer. - // FIXME: This probably shouldn't be stored like this. - - let mut render_chan: Option<RenderChan> = None; - let mut pipeline_id: Option<uint> = None; - let mut layout_chan: Option<LayoutChan> = None; + // Channel to the outermost frame's pipeline. + // FIXME: Compositor currently only asks for tiles to composite from this pipeline, + // Subframes need to be handled, as well. Additionally, events are only forwarded + // to this pipeline, but they should be routed to the appropriate pipeline via + // the constellation. + let mut pipeline: Option<Pipeline> = None; // Quadtree for this layer // FIXME: This should be one-per-layer @@ -317,9 +318,9 @@ impl CompositorTask { window_size), world_zoom); if !tile_request.is_empty() { - match render_chan { - Some(ref chan) => { - chan.send(ReRenderMsg(tile_request, world_zoom)); + match pipeline { + Some(ref pipeline) => { + pipeline.render_chan.send(ReRenderMsg(tile_request, world_zoom)); } _ => { println("Warning: Compositor: Cannot send tile request, no render chan initialized"); @@ -344,14 +345,9 @@ impl CompositorTask { ChangeReadyState(ready_state) => window.set_ready_state(ready_state), ChangeRenderState(render_state) => window.set_render_state(render_state), - SetLayoutRenderChans(new_layout_chan, - new_render_chan, - new_pipeline_id, - response_chan) => { - layout_chan = Some(new_layout_chan); - render_chan = Some(new_render_chan); - pipeline_id = Some(new_pipeline_id); - response_chan.send(CompositorAck(new_pipeline_id)); + SetIds(frame_tree, response_chan) => { + pipeline = Some(frame_tree.pipeline); + response_chan.send(CompositorAck(pipeline.get_ref().id.clone())); } GetSize(chan) => { @@ -378,8 +374,8 @@ impl CompositorTask { } Paint(id, new_layer_buffer_set, new_size) => { - match pipeline_id { - Some(pipeline_id) => if id != pipeline_id { loop; }, + match pipeline { + Some(ref pipeline) => if id != pipeline.id { loop; }, None => { loop; }, } @@ -417,8 +413,8 @@ impl CompositorTask { if window_size != new_size { debug!("osmain: window resized to %ux%u", width, height); window_size = new_size; - match layout_chan { - Some(ref chan) => chan.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height)))), + match pipeline { + Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), ResizeEvent(width, height))), None => error!("Compositor: Recieved resize event without initialized layout chan"), } } else { @@ -428,8 +424,8 @@ impl CompositorTask { LoadUrlWindowEvent(url_string) => { debug!("osmain: loading URL `%s`", url_string); - match layout_chan { - Some(ref chan) => chan.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None)))), + match pipeline { + Some(ref pipeline) => pipeline.script_chan.send(LoadMsg(pipeline.id.clone(), url::make_url(url_string.to_str(), None))), None => error!("Compositor: Recieved loadurl event without initialized layout chan"), } } @@ -450,8 +446,8 @@ impl CompositorTask { event = MouseUpEvent(button, world_mouse_point(layer_mouse_point)); } } - match layout_chan { - Some(ref chan) => chan.send(RouteScriptMsg(SendEventMsg(event))), + match pipeline { + Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), event)), None => error!("Compositor: Recieved mouse event without initialized layout chan"), } } @@ -530,8 +526,8 @@ impl CompositorTask { windowing::Forward => constellation_msg::Forward, windowing::Back => constellation_msg::Back, }; - match layout_chan { - Some(ref chan) => chan.send(RouteScriptMsg(NavigateMsg(direction))), + match pipeline { + Some(ref pipeline) => pipeline.script_chan.send(NavigateMsg(pipeline.id.clone(), direction)), None => error!("Compositor: Recieved navigation event without initialized layout chan"), } } diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 88edb160404..a86b2453844 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.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 compositing::{CompositorChan, SetLayoutRenderChans}; +use compositing::{CompositorChan, SetIds}; use extra::net::url; @@ -10,11 +10,13 @@ use std::cell::Cell; use std::comm; use std::comm::Port; use std::task; +use geom::size::Size2D; use gfx::opts::Opts; -use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked}; use pipeline::Pipeline; -use servo_msg::constellation_msg::{CompositorAck, ConstellationChan, ExitMsg, LoadUrlMsg}; -use servo_msg::constellation_msg::{Msg, NavigateMsg, RendererReadyMsg, ResizedWindowBroadcast}; +use servo_msg::constellation_msg::{CompositorAck, ConstellationChan, ExitMsg}; +use servo_msg::constellation_msg::{InitLoadUrlMsg, LoadIframeUrlMsg, LoadUrlMsg}; +use servo_msg::constellation_msg::{Msg, NavigateMsg}; +use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowBroadcast}; use servo_msg::constellation_msg; use script::script_task::{ResizeInactiveMsg, ExecuteMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; @@ -23,6 +25,7 @@ use servo_net::resource_task; use servo_util::time::ProfilerChan; use std::hashmap::HashMap; use std::util::replace; +use extra::future::from_value; /// Maintains the pipelines and navigation context and grants permission to composite pub struct Constellation { @@ -31,20 +34,121 @@ pub struct Constellation { compositor_chan: CompositorChan, resource_task: ResourceTask, image_cache_task: ImageCacheTask, - pipelines: HashMap<uint, Pipeline>, + pipelines: HashMap<PipelineId, @mut Pipeline>, navigation_context: NavigationContext, - next_id: uint, - current_painter: Option<uint>, - next_painter: Option<uint>, + priv next_pipeline_id: PipelineId, + pending_frames: ~[FrameChange], 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<uint>, +/// Stores the Id of the outermost frame's pipeline, along with a vector of children frames +#[deriving(Clone)] +struct FrameTree { + pipeline: @mut Pipeline, + parent: Option<@mut Pipeline>, + children: ~[@mut FrameTree], +} + +pub struct SendableFrameTree { + pipeline: Pipeline, + children: ~[SendableFrameTree], +} + +impl SendableFrameTree { + fn contains(&self, id: PipelineId) -> bool { + self.pipeline.id == id || + do self.children.iter().any |frame_tree| { + frame_tree.contains(id) + } + } +} + +impl FrameTree { + fn contains(&self, id: PipelineId) -> bool { + self.pipeline.id == id || + do self.children.iter().any |frame_tree| { + frame_tree.contains(id) + } + } + + /// Returns the frame tree whose key is id + fn find_mut(@mut self, id: PipelineId) -> Option<@mut FrameTree> { + if self.pipeline.id == id { return Some(self); } + for self.children.mut_iter().advance |frame_tree| { + let found = frame_tree.find_mut(id); + if found.is_some() { return found; } + } + None + } + + /// Replaces a node of the frame tree in place. Returns the node that was removed or the original node + /// if the node to replace could not be found. + fn replace_child(&mut self, id: PipelineId, new_child: @mut FrameTree) -> Result<@mut FrameTree, @mut FrameTree> { + let new_child_cell = Cell::new(new_child); + for self.children.mut_iter().advance |child| { + let new_child = new_child_cell.take(); + if child.pipeline.id == id { + new_child.parent = child.parent; + return Ok(replace(child, new_child)); + } + let replaced = child.replace_child(id, new_child); + if replaced.is_ok() { + return replaced; + } + new_child_cell.put_back(replaced.get_err()); + } + Err(new_child_cell.take()) + } + + fn to_sendable(&self) -> SendableFrameTree { + let mut sendable_frame_tree = SendableFrameTree { + pipeline: (*self.pipeline).clone(), + children: ~[], + }; + + for self.children.iter().advance |frame_tree| { + sendable_frame_tree.children.push(frame_tree.to_sendable()); + } + sendable_frame_tree + } + + pub fn iter(@mut self) -> FrameTreeIterator { + FrameTreeIterator { + stack: ~[self], + } + } +} + +pub struct FrameTreeIterator { + priv stack: ~[@mut FrameTree], +} + +impl Iterator<@mut FrameTree> for FrameTreeIterator { + fn next(&mut self) -> Option<@mut FrameTree> { + if !self.stack.is_empty() { + let next = self.stack.pop(); + for next.children.iter().advance |&child| { + self.stack.push(child); + } + Some(next) + } else { + None + } + } +} + +/// Represents the portion of a page that is changing in navigating. +struct FrameChange { + before: Option<PipelineId>, + after: @mut FrameTree, +} + +/// Stores the Id's of the pipelines previous and next in the browser's history +struct NavigationContext { + previous: ~[@mut FrameTree], + next: ~[@mut FrameTree], + current: Option<@mut FrameTree>, } impl NavigationContext { @@ -57,32 +161,61 @@ impl NavigationContext { } /* 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. */ + * when it is known that there exists either a previous page or a next page. */ - pub fn back(&mut self) -> uint { - self.next.push(self.current.get()); + pub fn back(&mut self) -> @mut FrameTree { + self.next.push(self.current.swap_unwrap()); self.current = Some(self.previous.pop()); - debug!("previous: %? next: %? current: %u", self.previous, self.next, self.current.get()); + debug!("previous: %? next: %? current: %?", self.previous, self.next, *self.current.get_ref()); self.current.get() } - pub fn forward(&mut self) -> uint { - self.previous.push(self.current.get()); + pub fn forward(&mut self) -> @mut FrameTree { + self.previous.push(self.current.swap_unwrap()); self.current = Some(self.next.pop()); - debug!("previous: %? next: %? current: %u", self.previous, self.next, self.current.get()); + debug!("previous: %? next: %? current: %?", self.previous, self.next, *self.current.get_ref()); self.current.get() } - /// Navigates to a new id, returning all id's evicted from next - pub fn navigate(&mut self, id: uint) -> ~[uint] { - debug!("navigating to %u", id); + /// Navigates to a new set of page frames, returning all evicted frame trees + pub fn navigate(&mut self, frame_tree: @mut FrameTree) -> ~[@mut FrameTree] { + debug!("navigating to %?", frame_tree); let evicted = replace(&mut self.next, ~[]); - do self.current.mutate_default(id) |cur_id| { - self.previous.push(cur_id); - id + if self.current.is_some() { + self.previous.push(self.current.swap_unwrap()); } + self.current = Some(frame_tree); evicted } + + /// Returns the frame tree whose key is pipeline_id. + pub fn find(&mut self, pipeline_id: PipelineId) -> Option<@mut FrameTree> { + for self.current.mut_iter().advance |frame_tree| { + let found = frame_tree.find_mut(pipeline_id); + if found.is_some() { return found; } + } + let mut forward = self.next.mut_rev_iter(); + let mut backward = self.previous.mut_rev_iter(); + loop { + match (forward.next(), backward.next()) { + (None, None) => { + return None; + } + (next_forward, next_backward) => { + let mut next_forward = next_forward; + let mut next_backward = next_backward; + for next_forward.mut_iter().advance |frame_tree| { + let found = frame_tree.find_mut(pipeline_id); + if found.is_some() { return found; } + } + for next_backward.mut_iter().advance |frame_tree| { + let found = frame_tree.find_mut(pipeline_id); + if found.is_some() { return found; } + } + } + } + } + } } impl Constellation { @@ -114,9 +247,8 @@ impl Constellation { image_cache_task: image_cache_task.take(), pipelines: HashMap::new(), navigation_context: NavigationContext::new(), - next_id: 0, - current_painter: None, - next_painter: None, + next_pipeline_id: PipelineId(0), + pending_frames: ~[], profiler_chan: profiler_chan.take(), opts: opts.take(), }; @@ -134,41 +266,216 @@ impl Constellation { } } - /// 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; + /// Helper function for getting a unique pipeline Id + fn get_next_pipeline_id(&mut self) -> PipelineId { + let id = self.next_pipeline_id; + self.next_pipeline_id = PipelineId(*id + 1); id } + + /// Convenience function for getting the currently active frame tree. + /// The currently active frame tree should always be the current painter + fn current_frame<'a>(&'a self) -> &'a Option<@mut FrameTree> { + &self.navigation_context.current + } /// 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) => { + + // Acknowledgement from the compositor that it has updated its active pipeline id + CompositorAck(id) => { + let pending_index = do self.pending_frames.rposition |frame_change| { + frame_change.after.pipeline.id == id + }.expect("Constellation: received compositor ack for frame tree not currently + pending compositor ack. This is a bug."); + let frame_tree = self.pending_frames.swap_remove(pending_index).after; + self.grant_paint_permission(frame_tree); + } + + ExitMsg(sender) => { + for self.pipelines.iter().advance |(_id, ref pipeline)| { + pipeline.exit(); + } + self.image_cache_task.exit(); + self.resource_task.send(resource_task::Exit); + + sender.send(()); + return false + } + + // This should only be called once per constellation, and only by the browser + InitLoadUrlMsg(url) => { + let pipeline = @mut Pipeline::create(self.get_next_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, + { + let size = self.compositor_chan.get_size(); + from_value(Size2D(size.width as uint, size.height as uint)) + }); + if url.path.ends_with(".js") { + pipeline.script_chan.send(ExecuteMsg(pipeline.id, url)); + } else { + pipeline.load(url); + pipeline.navigation_type = Some(constellation_msg::Load); + + self.pending_frames.push(FrameChange{ + before: None, + after: @mut FrameTree { + pipeline: pipeline, + parent: None, + children: ~[], + }, + }); + } + self.pipelines.insert(pipeline.id, pipeline); + } + + LoadIframeUrlMsg(url, source_pipeline_id, size_future) => { + // A message from the script associated with pipeline_id that it has + // parsed an iframe during html parsing. This iframe will result in a + // new pipeline being spawned and a frame tree being added to pipeline_id's + // frame tree's children. This message is never the result of a link clicked + // or a new url entered. + // Start by finding the frame tree matching the pipeline id, + // and add the new pipeline to its sub frames. The frame tree + // could already be in the navigation context, or it could still + // be loading, in which case it is a new id in a constellation.pending_frames + // frame change, because pages aren't added to the navi context until they finish + // loading (excluding iframes). Which is checked first is seemingly arbitrary + let next_pipeline_id = self.get_next_pipeline_id(); + let frame_tree = { + let frame_tree = self.navigation_context.find(source_pipeline_id); + match frame_tree { + Some(frame_tree) => frame_tree, + None => { + let frame_change = do self.pending_frames.mut_iter().find_ |frame_change| { + frame_change.after.pipeline.id == source_pipeline_id + }; + let frame_change = frame_change.expect("Constellation: source pipeline id + of LoadIframeUrlMsg is not in navigation context nor a pending painter. + This should be impossible."); + frame_change.after + } + } + }; + + // Compare the pipeline's url to the new url. If the origin is the same, + // then reuse the script task in creating the new pipeline + let source_pipeline = *self.pipelines.find(&source_pipeline_id).expect("Constellation: + source Id of LoadIframeUrlMsg does have an associated pipeline in + constellation. This should be impossible."); + + let source_url = source_pipeline.url.clone().expect("Constellation: LoadUrlIframeMsg's + source's Url is None. There should never be a LoadUrlIframeMsg from a pipeline + that was never given a url to load."); + + let pipeline = @mut if (source_url.host == url.host && + source_url.port == url.port) { + // Reuse the script task if same-origin url's + Pipeline::with_script(next_pipeline_id, + self.chan.clone(), + self.compositor_chan.clone(), + self.image_cache_task.clone(), + self.profiler_chan.clone(), + copy self.opts, + source_pipeline, + size_future) + } else { + // Create a new script task if not same-origin url's + Pipeline::create(next_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, + size_future) + }; + + if url.path.ends_with(".js") { + pipeline.execute(url); + } else { + pipeline.load(url); + pipeline.navigation_type = None; + } + frame_tree.children.push(@mut FrameTree { + pipeline: pipeline, + parent: Some(source_pipeline), + children: ~[], + }); + self.pipelines.insert(pipeline.id, pipeline); + } + + // Load a new page, usually -- but not always -- from a mouse click or typed url + // If there is already a pending page (self.pending_frames), it will not be overridden; + // However, if the id is not encompassed by another change, it will be. + LoadUrlMsg(source_id, url, size_future) => { debug!("received message to load %s", url::to_str(&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); + // Make sure no pending page would be overridden. + for self.pending_frames.iter().advance |frame_change| { + let old_id = frame_change.before.expect("Constellation: Received load msg + from pipeline, but there is no currently active page. This should + be impossible."); + let changing_frame = self.current_frame().get_ref().find_mut(old_id).expect("Constellation: + Pending change has non-active source pipeline. This should be + impossible."); + if changing_frame.contains(source_id) { + // id that sent load msg is being changed already; abort + return true; + } + } + // Being here means either there are no pending frames, or none of the pending + // changes would be overriden by changing the subframe associated with source_id. + + let next_pipeline_id = self.get_next_pipeline_id(); + let pipeline = @mut Pipeline::create(next_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, + size_future); + if url.path.ends_with(".js") { - pipeline.script_chan.send(ExecuteMsg(url)); + pipeline.script_chan.send(ExecuteMsg(pipeline.id, url)); } else { pipeline.load(url); pipeline.navigation_type = Some(constellation_msg::Load); - self.next_painter = Some(pipeline_id); + + let parent = if self.current_frame().get_ref().pipeline.id == source_id { + // source_id is the root of the current frame tree; replace whole tree + None + } else { + // id is not the root of the current frame tree, but is in the frame tree; + // replace only the subtree + let source_frame = self.current_frame().get_ref().find_mut(source_id).expect( + "Constellation: received a LoadUrlMsg from a pipeline_id associated + with a pipeline not in the active frame tree. This should be + impossible."); + source_frame.parent + }; + self.pending_frames.push(FrameChange{ + before: Some(source_id), + after: @mut FrameTree { + pipeline: pipeline, + parent: parent, + children: ~[], + }, + }); } - self.pipelines.insert(pipeline_id, pipeline); + 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 { + let destination_frame = match direction { constellation_msg::Forward => { if self.navigation_context.next.is_empty() { debug!("no next page to navigate to"); @@ -184,79 +491,152 @@ impl Constellation { self.navigation_context.back() } }; - 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_painter = Some(destination_id); - self.update_painter(); + + for destination_frame.iter().advance |frame| { + let pipeline = &frame.pipeline; + pipeline.navigation_type = Some(constellation_msg::Navigate); + pipeline.reload(); + } + self.compositor_chan.send(SetIds(destination_frame.to_sendable(), self.chan.clone())); + + let before_id = self.current_frame().get_ref().pipeline.id.clone(); + self.pending_frames.clear(); + self.pending_frames.push(FrameChange { + before: Some(before_id), + after: destination_frame, + }); + + // TODO(tkuehn): what is the "critical point" beyond which pending frames + // should not be cleared? Currently, the behavior is that forward/back + // navigation always has navigation priority, and after that new page loading is + // first come, first served. + let old = self.current_frame().get_ref(); + for old.iter().advance |frame| { + frame.pipeline.revoke_paint_permission(); + } } // Notification that rendering has finished and is requesting permission to paint. RendererReadyMsg(pipeline_id) => { - let next_painter = self.next_painter; - for next_painter.iter().advance |&id| { - if pipeline_id == id { - self.update_painter(); + // This message could originate from a pipeline in the navigation context or + // from a pending frame. The only time that we will grant paint permission is + // when the message originates from a pending frame or the current frame. + + for self.current_frame().iter().advance |current_frame| { + // Messages originating in the current frame are not navigations; + // TODO(tkuehn): In fact, this kind of message might be provably + // impossible to occur. + if current_frame.contains(pipeline_id) { + for current_frame.iter().advance |frame| { + frame.pipeline.grant_paint_permission(); + } + return true; } } - } - ResizedWindowBroadcast(new_size) => match self.current_painter { - Some(current_painter_id) => for self.pipelines.iter().advance |(&id, pipeline)| { - if current_painter_id != id { - pipeline.script_chan.send(ResizeInactiveMsg(new_size)); - } - }, - None => for self.pipelines.iter().advance |(_, pipeline)| { - pipeline.script_chan.send(ResizeInactiveMsg(new_size)); - }, - }, + // Find the pending frame change whose new pipeline id is pipeline_id. + // If it is not found, it simply means that this pipeline will not receive + // permission to paint. + let pending_index = do self.pending_frames.rposition |frame_change| { + frame_change.after.pipeline.id == pipeline_id + }; + for pending_index.iter().advance |&pending_index| { + let frame_change = self.pending_frames.swap_remove(pending_index); + let to_add = frame_change.after; - // Acknowledgement from the compositor that it has updated its active pipeline id - CompositorAck(id) => { - self.grant_paint_permission(id); - } + // Create the next frame tree that will be given to the compositor + let next_frame_tree = match to_add.parent { + None => to_add, // to_add is the root + Some(_parent) => self.current_frame().get(), + }; - ExitMsg(sender) => { - for self.pipelines.iter().advance |(_, pipeline)| { - pipeline.exit(); + // If there are frames to revoke permission from, do so now. + match frame_change.before { + Some(revoke_id) => { + let current_frame = self.current_frame().get(); + + let to_revoke = current_frame.find_mut(revoke_id).expect( + "Constellation: pending frame change refers to an old + frame not contained in the current frame. This is a bug"); + + for to_revoke.iter().advance |frame| { + frame.pipeline.revoke_paint_permission(); + } + + // If to_add is not the root frame, then replace revoked_frame with it + if to_add.parent.is_some() { + next_frame_tree.replace_child(revoke_id, to_add); + } + self.pending_frames.push(FrameChange { + before: Some(revoke_id), + after: next_frame_tree + }); + } + + None => { + // Add to_add to parent's children, if it is not the root + let parent = &to_add.parent; + let to_add = Cell::new(to_add); + for parent.iter().advance |parent| { + let parent = next_frame_tree.find_mut(parent.id).expect( + "Constellation: pending frame has a parent frame that is not + active. This is a bug."); + parent.children.push(to_add.take()); + } + self.pending_frames.push(FrameChange { + before: None, + after: next_frame_tree + }); + } + } + self.compositor_chan.send(SetIds(next_frame_tree.to_sendable(), self.chan.clone())); } - self.image_cache_task.exit(); - self.resource_task.send(resource_task::Exit); + } - sender.send(()); - return false + ResizedWindowBroadcast(new_size) => match *self.current_frame() { + Some(ref current_frame) => { + let current_frame_id = current_frame.pipeline.id.clone(); + for self.navigation_context.previous.iter().advance |frame_tree| { + let pipeline = &frame_tree.pipeline; + if current_frame_id != pipeline.id { + pipeline.script_chan.send(ResizeInactiveMsg(new_size)); + } + } + for self.navigation_context.next.iter().advance |frame_tree| { + let pipeline = &frame_tree.pipeline; + if current_frame_id != pipeline.id { + pipeline.script_chan.send(ResizeInactiveMsg(new_size)); + } + } + } + None => { + for self.navigation_context.previous.iter().advance |frame_tree| { + frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size)); + } + for self.navigation_context.next.iter().advance |frame_tree| { + frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size)); + } + } } + } true } - fn update_painter(&mut self) { - let current_painter = replace(&mut self.current_painter, None); - for current_painter.iter().advance |id| { - self.pipelines.get(id).render_chan.send(PaintPermissionRevoked); + // Grants a frame tree permission to paint; optionally updates navigation to reflect a new page + fn grant_paint_permission(&mut self, frame_tree: @mut FrameTree) { + // Give permission to paint to the new frame and all child frames + for frame_tree.iter().advance |frame| { + frame.pipeline.grant_paint_permission(); } - let id = replace(&mut self.next_painter, None); - let id = id.expect("constellation: called update painter when there was no next painter"); - let pipeline = self.pipelines.get(&id); - self.compositor_chan.send(SetLayoutRenderChans(pipeline.layout_chan.clone(), - pipeline.render_chan.clone(), - id, - self.chan.clone())); - } - // Grants a renderer permission to paint; optionally updates navigation to reflect a new page - fn grant_paint_permission(&mut self, id: uint) { - let pipeline = self.pipelines.get(&id); - pipeline.render_chan.send(PaintPermissionGranted); - self.current_painter = Some(id); - // 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); - for evicted.iter().advance |id| { - self.pipelines.get(id).exit(); + // Don't navigate on Navigate type (or None, as in the case of parsed iframes that finish + // loading) + match frame_tree.pipeline.navigation_type { + Some(constellation_msg::Load) => { + let evicted = self.navigation_context.navigate(frame_tree); + for evicted.iter().advance |frame_tree| { + frame_tree.pipeline.exit(); } } _ => {} diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 1943191c89a..68ba371d110 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -34,9 +34,10 @@ 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::{MatchSelectorsDocumentDamage, Msg}; -use script::layout_interface::{QueryMsg, RouteScriptMsg, Reflow, ReflowDocumentDamage}; +use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage}; use script::layout_interface::{ReflowForDisplay, ReflowMsg}; -use script::script_task::{ReflowCompleteMsg, ScriptChan, ScriptMsg, SendEventMsg}; +use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg}; +use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::LocalImageCache; use servo_util::tree::{TreeNodeRef, TreeUtils}; @@ -45,7 +46,9 @@ use servo_util::time; use extra::net::url::Url; struct LayoutTask { + id: PipelineId, port: Port<Msg>, + constellation_chan: ConstellationChan, script_chan: ScriptChan, render_chan: RenderChan, image_cache_task: ImageCacheTask, @@ -62,25 +65,38 @@ struct LayoutTask { } impl LayoutTask { - pub fn create(port: Port<Msg>, - script_chan: ScriptChan, - render_chan: RenderChan, - img_cache_task: ImageCacheTask, - opts: Opts, - profiler_chan: ProfilerChan) { + pub fn create(id: PipelineId, + port: Port<Msg>, + constellation_chan: ConstellationChan, + script_chan: ScriptChan, + render_chan: RenderChan, + img_cache_task: ImageCacheTask, + opts: Opts, + profiler_chan: ProfilerChan) { + let port = Cell::new(port); + let constellation_chan = Cell::new(constellation_chan); + let script_chan = Cell::new(script_chan); + let render_chan = Cell::new(render_chan); + let img_cache_task = Cell::new(img_cache_task); + let profiler_chan = Cell::new(profiler_chan); + do spawn { - let mut layout = LayoutTask::new(port.take(), - script_chan.clone(), - render_chan.clone(), - img_cache_task.clone(), - &opts, - profiler_chan.clone()); + let mut layout = LayoutTask::new(id, + port.take(), + constellation_chan.take(), + script_chan.take(), + render_chan.take(), + img_cache_task.take(), + &opts, + profiler_chan.take()); layout.start(); }; } - fn new(port: Port<Msg>, + fn new(id: PipelineId, + port: Port<Msg>, + constellation_chan: ConstellationChan, script_chan: ScriptChan, render_chan: RenderChan, image_cache_task: ImageCacheTask, @@ -90,7 +106,9 @@ impl LayoutTask { let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone()); LayoutTask { + id: id, port: port, + constellation_chan: constellation_chan, script_chan: script_chan, render_chan: render_chan, image_cache_task: image_cache_task.clone(), @@ -140,10 +158,6 @@ impl LayoutTask { self.handle_query(query.take()); } } - RouteScriptMsg(script_msg) => { - debug!("layout: routing %? to script task", script_msg); - self.route_script_msg(script_msg); - } ExitMsg => { debug!("layout: ExitMsg received"); return false @@ -265,7 +279,7 @@ impl LayoutTask { // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without // either select or a filtered recv() that only looks for messages of a given type. data.script_join_chan.send(()); - data.script_chan.send(ReflowCompleteMsg); + data.script_chan.send(ReflowCompleteMsg(self.id)); } /// Handles a query from the script task. This is the main routine that DOM functions like @@ -381,12 +395,6 @@ impl LayoutTask { } } - // TODO(tkuehn): once there are multiple script tasks, this is where the layout task will - // determine which script task should receive the message. The prototype will need to change - fn route_script_msg(&self, script_msg: ScriptMsg) { - self.script_chan.send(script_msg); - } - // When images can't be loaded in time to display they trigger // this callback in some task somewhere. This will send a message // to the script task, and ultimately cause the image to be @@ -398,10 +406,11 @@ impl LayoutTask { // make multiple copies of the callback, and the dom event // channel is not a copyable type, so this is actually a // little factory to produce callbacks + let id = self.id.clone(); let f: @fn() -> ~fn(ImageResponseMsg) = || { let script_chan = script_chan.clone(); let f: ~fn(ImageResponseMsg) = |_| { - script_chan.send(SendEventMsg(ReflowEvent)) + script_chan.send(SendEventMsg(id.clone(), ReflowEvent)) }; f }; diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs index 45852132bbf..645fb051b7d 100644 --- a/src/components/main/pipeline.rs +++ b/src/components/main/pipeline.rs @@ -5,22 +5,26 @@ use extra::net::url::Url; use compositing::CompositorChan; use gfx::render_task::{RenderChan, RenderTask}; +use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked}; 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::{ExecuteMsg, LoadMsg}; +use servo_msg::constellation_msg::{ConstellationChan, NavigationType, PipelineId}; +use script::script_task::{AttachLayoutMsg, NewLayoutInfo, 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 geom::size::Size2D; +use extra::future::Future; use std::comm; /// A uniquely-identifiable pipeline of stript task, layout task, and render task. +#[deriving(Clone)] pub struct Pipeline { - id: uint, + id: PipelineId, script_chan: ScriptChan, layout_chan: LayoutChan, render_chan: RenderChan, @@ -31,15 +35,15 @@ pub struct Pipeline { 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 { + pub fn with_script(id: PipelineId, + constellation_chan: ConstellationChan, + compositor_chan: CompositorChan, + image_cache_task: ImageCacheTask, + profiler_chan: ProfilerChan, + opts: Opts, + script_pipeline: &Pipeline, + size_future: Future<Size2D<uint>>) -> 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); @@ -49,30 +53,76 @@ impl Pipeline { copy opts, profiler_chan.clone()); - LayoutTask::create(layout_port, - script_chan.clone(), + LayoutTask::create(id, + layout_port, + constellation_chan, + script_pipeline.script_chan.clone(), render_chan.clone(), image_cache_task.clone(), copy opts, profiler_chan); + let new_layout_info = NewLayoutInfo { + old_id: script_pipeline.id.clone(), + new_id: id, + layout_chan: layout_chan.clone(), + size_future: size_future, + }; + + script_pipeline.script_chan.send(AttachLayoutMsg(new_layout_info)); + + Pipeline::new(id, + script_pipeline.script_chan.clone(), + layout_chan, + render_chan) + + } + + pub fn create(id: PipelineId, + constellation_chan: ConstellationChan, + compositor_chan: CompositorChan, + image_cache_task: ImageCacheTask, + resource_task: ResourceTask, + profiler_chan: ProfilerChan, + opts: Opts, + size: Future<Size2D<uint>>) -> 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); + ScriptTask::create(id, compositor_chan.clone(), layout_chan.clone(), script_port, script_chan.clone(), - constellation_chan, + constellation_chan.clone(), resource_task, - image_cache_task, - compositor_chan.get_size()); + image_cache_task.clone(), + size); + + + RenderTask::create(id, + render_port, + compositor_chan.clone(), + copy opts, + profiler_chan.clone()); + LayoutTask::create(id, + layout_port, + constellation_chan, + script_chan.clone(), + render_chan.clone(), + image_cache_task, + copy opts, + profiler_chan); Pipeline::new(id, script_chan, layout_chan, render_chan) } - pub fn new(id: uint, + pub fn new(id: PipelineId, script_chan: ScriptChan, layout_chan: LayoutChan, render_chan: RenderChan) @@ -89,12 +139,25 @@ impl Pipeline { pub fn load(&mut self, url: Url) { self.url = Some(url.clone()); - self.script_chan.send(LoadMsg(url)); + self.script_chan.send(LoadMsg(self.id, url)); + } + + pub fn execute(&mut self, url: Url) { + self.url = Some(url.clone()); + self.script_chan.send(ExecuteMsg(self.id, url)); + } + + pub fn grant_paint_permission(&self) { + self.render_chan.send(PaintPermissionGranted); + } + + pub fn revoke_paint_permission(&self) { + self.render_chan.send(PaintPermissionRevoked); } pub fn reload(&self) { for self.url.iter().advance |url| { - self.script_chan.send(LoadMsg(url.clone())); + self.script_chan.send(LoadMsg(self.id, url.clone())); } } diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc index 1f660764c93..ea66cc356d9 100755 --- a/src/components/main/servo.rc +++ b/src/components/main/servo.rc @@ -37,7 +37,7 @@ extern mod core_text; use compositing::{CompositorChan, CompositorTask}; use constellation::Constellation; -use servo_msg::constellation_msg::{ExitMsg, LoadUrlMsg}; +use servo_msg::constellation_msg::{ExitMsg, InitLoadUrlMsg}; use gfx::opts; use servo_net::image_cache_task::ImageCacheTask; @@ -133,7 +133,7 @@ fn run(opts: &Opts) { // Send the URL command to the constellation. for opts.urls.iter().advance |filename| { - constellation_chan.send(LoadUrlMsg(make_url(copy *filename, None))) + constellation_chan.send(InitLoadUrlMsg(make_url(copy *filename, None))) } // Wait for the compositor to shut down. diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index 3ec0ddcb684..5da0ddf204f 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -7,6 +7,7 @@ use azure::azure::AzGLContext; use geom::rect::Rect; use geom::size::Size2D; +use constellation_msg::PipelineId; use extra::arc; @@ -60,7 +61,7 @@ pub trait RenderListener { fn new_layer(&self, Size2D<uint>, uint); fn resize_layer(&self, Size2D<uint>); fn delete_layer(&self); - fn paint(&self, id: uint, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>); + fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>); fn set_render_state(&self, render_state: RenderState); } diff --git a/src/components/msg/constellation_msg.rs b/src/components/msg/constellation_msg.rs index 57accdddc63..c261f94dc42 100644 --- a/src/components/msg/constellation_msg.rs +++ b/src/components/msg/constellation_msg.rs @@ -7,6 +7,7 @@ use std::comm::{Chan, SharedChan}; use extra::net::url::Url; +use extra::future::Future; use geom::size::Size2D; #[deriving(Clone)] @@ -26,21 +27,28 @@ impl ConstellationChan { } pub enum Msg { - LoadUrlMsg(Url), - NavigateMsg(NavigationDirection), + CompositorAck(PipelineId), ExitMsg(Chan<()>), - RendererReadyMsg(uint), - CompositorAck(uint), + InitLoadUrlMsg(Url), + LoadUrlMsg(PipelineId, Url, Future<Size2D<uint>>), + LoadIframeUrlMsg(Url, PipelineId, Future<Size2D<uint>>), + NavigateMsg(NavigationDirection), + RendererReadyMsg(PipelineId), ResizedWindowBroadcast(Size2D<uint>), } /// Represents the two different ways to which a page can be navigated +#[deriving(Clone, Eq, IterBytes)] enum NavigationType { Load, // entered or clicked on a url Navigate, // browser forward/back buttons } +#[deriving(Clone, Eq, IterBytes)] pub enum NavigationDirection { Forward, Back, } + +#[deriving(Clone, Eq, IterBytes)] +pub struct PipelineId(uint); diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index f20a4de0729..44113ad9e50 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -2458,8 +2458,8 @@ def CreateBindingJSObject(descriptor, parent): if descriptor.proxy: handler = """ //let cache = ptr::to_unsafe_ptr(aObject.get_wrappercache()); - let script_context = task_from_context(aCx); - let handler = (*script_context).dom_static.proxy_handlers.get(&(PrototypeList::id::%s as uint)); + let page = page_from_context(aCx); + let handler = (*page).js_info.get_ref().dom_static.proxy_handlers.get(&(PrototypeList::id::%s as uint)); """ % descriptor.name create = handler + """ let obj = NewProxyObject(aCx, *handler, ptr::to_unsafe_ptr(&RUST_PRIVATE_TO_JSVAL(squirrel_away(aObject) as *libc::c_void)), @@ -2629,8 +2629,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): elif props.hasNonChromeOnly(): idsToInit.append(props.variableName(False)) if len(idsToInit) > 0: - setup = CGList([CGGeneric("let script_context = task_from_context(aCx);"), - CGList([CGGeneric("let %s_ids_mut = (*script_context).dom_static.attribute_ids.get(&(PrototypeList::id::%s as uint));" % (varname, self.descriptor.name)) for varname in idsToInit], '\n')], '\n') + setup = CGList([CGGeneric("let page = page_from_context(aCx);"), + CGList([CGGeneric("let %s_ids_mut = (*page).js_info.get_ref().dom_static.attribute_ids.get(&(PrototypeList::id::%s as uint));" % (varname, self.descriptor.name)) for varname in idsToInit], '\n')], '\n') initIds = CGList( [CGGeneric("!InitIds(aCx, %s, *%s_ids_mut)" % (varname, varname)) for varname in idsToInit], ' ||\n') @@ -2818,7 +2818,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): else: getter = "GetConstructorObject" - body = " let script_context = task_from_context(aCx);\n" + body = "let page = page_from_context(aCx);" #XXXjdm This self.descriptor.concrete check shouldn't be necessary if not self.descriptor.concrete or self.descriptor.proxy: body += """ let traps = ProxyTraps { @@ -2851,12 +2851,12 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): getElementIfPresent: ptr::null(), getPrototypeOf: ptr::null() }; - (*script_context).dom_static.proxy_handlers.insert(PrototypeList::id::%s as uint, + (*page).js_info.get_mut_ref().dom_static.proxy_handlers.insert(PrototypeList::id::%s as uint, CreateProxyHandler(ptr::to_unsafe_ptr(&traps), ptr::to_unsafe_ptr(&Class) as *libc::c_void)); """ % self.descriptor.name else: - body += """ (*script_context).dom_static.attribute_ids.insert(PrototypeList::id::%s as uint, + body += """ (*page).js_info.get_ref().dom_static.attribute_ids.insert(PrototypeList::id::%s as uint, vec::cast_to_mut(vec::from_slice(sAttributes_ids))); """ % self.descriptor.name body = "" #XXXjdm xray stuff isn't necessary yet @@ -3445,12 +3445,12 @@ class CGXrayHelper(CGAbstractExternMethod): def definition_body(self): varNames = self.properties.variableNames(True) - setup = "let script_context = task_from_context(cx);\n" + setup = "let page = page_from_context(cx);\n" methods = self.properties.methods if methods.hasNonChromeOnly() or methods.hasChromeOnly(): methodArgs = "Some(vec::zip_slice(%(methods)s, *method_ids))" % varNames - setup += "let method_ids = (*script_context).dom_static.method_ids.get(&(PrototypeList::id::ClientRect as uint));\n" + setup += "let method_ids = (*page).js_info.get_ref().dom_static.method_ids.get(&(PrototypeList::id::ClientRect as uint));\n" else: methodArgs = "None" methodArgs = CGGeneric(methodArgs) @@ -3458,7 +3458,7 @@ class CGXrayHelper(CGAbstractExternMethod): attrs = self.properties.attrs if attrs.hasNonChromeOnly() or attrs.hasChromeOnly(): attrArgs = "Some(vec::zip_slice(%(attrs)s, *attr_ids))" % varNames - setup += "let attr_ids = (*script_context).dom_static.attribute_ids.get(&(PrototypeList::id::ClientRect as uint));\n" + setup += "let attr_ids = (*page).js_info.get_ref().dom_static.attribute_ids.get(&(PrototypeList::id::ClientRect as uint));\n" else: attrArgs = "None" attrArgs = CGGeneric(attrArgs) @@ -3466,7 +3466,7 @@ class CGXrayHelper(CGAbstractExternMethod): consts = self.properties.consts if consts.hasNonChromeOnly() or consts.hasChromeOnly(): constArgs = "Some(vec::zip_slice(%(consts)s, *const_ids))" % varNames - setup += "let const_ids = (*script_context).dom_static.constant_ids.get(&(PrototypeList::id::ClientRect as uint));\n" + setup += "let const_ids = (*page).js_info.get_ref().dom_static.constant_ids.get(&(PrototypeList::id::ClientRect as uint));\n" else: constArgs = "None" constArgs = CGGeneric(constArgs) @@ -3751,8 +3751,8 @@ class CGClassConstructHook(CGAbstractExternMethod): //XXXjdm Gecko obtains a GlobalObject from the global (maybe from the private value, // or through unwrapping a slot or something). We'll punt and get the Window // from the context for now. - let script_context = task_from_context(cx); - let global = (*script_context).root_frame.get_ref().window; + let page = page_from_context(cx); + let global = (*page).frame.get_ref().window; let obj = global.get_wrappercache().get_wrapper(); """ preArgs = ["global"] @@ -4365,7 +4365,7 @@ class CGBindingRoot(CGThing): 'dom::uievent::*', #XXXjdm 'dom::windowproxy::*', #XXXjdm 'dom::bindings::codegen::*', #XXXjdm - 'script_task::task_from_context', + 'script_task::{JSPageInfo, page_from_context}', 'dom::bindings::utils::EnumEntry', 'dom::node::ScriptView', 'std::cast', diff --git a/src/components/script/dom/bindings/element.rs b/src/components/script/dom/bindings/element.rs index e186e8c97ae..98663ba9de9 100644 --- a/src/components/script/dom/bindings/element.rs +++ b/src/components/script/dom/bindings/element.rs @@ -11,7 +11,7 @@ use dom::element::{HTMLImageElementTypeId, HTMLHeadElementTypeId, HTMLScriptElem HTMLDivElementTypeId}; use dom::node::{AbstractNode, ScriptView, ElementNodeTypeId}; use layout_interface::{ContentBoxQuery, ContentBoxResponse}; -use script_task::task_from_context; +use script_task::page_from_context; use super::utils; use std::cast; @@ -230,9 +230,10 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa let node = unwrap(obj); let width = match node.type_id() { ElementNodeTypeId(HTMLImageElementTypeId) => { - let script_context = task_from_context(cx); + let page = page_from_context(cx); let (port, chan) = comm::stream(); - match (*script_context).query_layout(ContentBoxQuery(node, chan), port) { + // TODO(tkuehn): currently this just queries top-level page's layout. Need to handle subframes. + match (*page).query_layout(ContentBoxQuery(node, chan), port) { Ok(ContentBoxResponse(rect)) => rect.size.width.to_px(), Err(()) => 0 } diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 67419b26dfc..c2a0aad6633 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -5,7 +5,7 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::node; use dom::node::{AbstractNode, ScriptView}; -use script_task::task_from_context; +use script_task::page_from_context; use std::cast; use std::hashmap::HashMap; @@ -217,8 +217,8 @@ pub unsafe fn domstring_to_jsval(cx: *JSContext, string: &DOMString) -> JSVal { pub fn get_compartment(cx: *JSContext) -> @mut Compartment { unsafe { - let script_context = task_from_context(cx); - let compartment = (*script_context).js_compartment; + let page = page_from_context(cx); + let compartment = (*page).js_info.get_ref().js_compartment; assert!(cx == compartment.cx.ptr); compartment } diff --git a/src/components/script/dom/blob.rs b/src/components/script/dom/blob.rs index 2f9d90c2b1c..e8760d985d0 100644 --- a/src/components/script/dom/blob.rs +++ b/src/components/script/dom/blob.rs @@ -4,7 +4,7 @@ use dom::bindings::utils::{WrapperCache, BindingObject, CacheableWrapper}; use dom::bindings::codegen::BlobBinding; -use script_task::{task_from_context}; +use script_task::{page_from_context}; use js::jsapi::{JSContext, JSObject}; @@ -35,9 +35,9 @@ impl CacheableWrapper for Blob { impl BindingObject for Blob { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/dom/clientrect.rs b/src/components/script/dom/clientrect.rs index cd3f9f344e6..9ec68d1e06b 100644 --- a/src/components/script/dom/clientrect.rs +++ b/src/components/script/dom/clientrect.rs @@ -4,7 +4,7 @@ use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; use dom::bindings::codegen::ClientRectBinding; -use script_task::{task_from_context, global_script_context}; +use script_task::page_from_context; use js::jsapi::{JSObject, JSContext, JSVal}; use js::glue::RUST_OBJECT_TO_JSVAL; @@ -21,7 +21,7 @@ pub struct ClientRect { } impl ClientRect { - pub fn new(top: f32, bottom: f32, left: f32, right: f32) -> @mut ClientRect { + pub fn new(top: f32, bottom: f32, left: f32, right: f32, cx: *JSContext, scope: *JSObject) -> @mut ClientRect { let rect = @mut ClientRect { top: top, bottom: bottom, @@ -29,16 +29,11 @@ impl ClientRect { right: right, wrapper: WrapperCache::new() }; - rect.init_wrapper(); + rect.init_wrapper(cx, scope); rect } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } @@ -82,9 +77,9 @@ impl CacheableWrapper for ClientRect { impl BindingObject for ClientRect { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/dom/clientrectlist.rs b/src/components/script/dom/clientrectlist.rs index 2fdbc108f36..023b6ac37a6 100644 --- a/src/components/script/dom/clientrectlist.rs +++ b/src/components/script/dom/clientrectlist.rs @@ -5,7 +5,7 @@ use dom::bindings::codegen::ClientRectListBinding; use dom::bindings::utils::{WrapperCache, CacheableWrapper, BindingObject}; use dom::clientrect::ClientRect; -use script_task::{task_from_context, global_script_context}; +use script_task::page_from_context; use js::jsapi::{JSObject, JSContext}; @@ -17,21 +17,16 @@ pub struct ClientRectList { } impl ClientRectList { - pub fn new(rects: ~[@mut ClientRect]) -> @mut ClientRectList { + pub fn new(rects: ~[@mut ClientRect], cx: *JSContext, scope: *JSObject) -> @mut ClientRectList { let list = @mut ClientRectList { wrapper: WrapperCache::new(), rects: rects }; - list.init_wrapper(); + list.init_wrapper(cx, scope); list } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } @@ -68,9 +63,9 @@ impl CacheableWrapper for ClientRectList { impl BindingObject for ClientRectList { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index f497e533fba..80f677e35cf 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -13,7 +13,6 @@ use dom::htmlcollection::HTMLCollection; use dom::node::{AbstractNode, ScriptView, Node}; use dom::window::Window; use dom::windowproxy::WindowProxy; -use script_task::global_script_context; use js::JSPROP_ENUMERATE; use js::glue::*; @@ -37,20 +36,20 @@ pub fn Document(root: AbstractNode<ScriptView>, window: Option<@mut Window>) -> wrapper: WrapperCache::new(), window: window }; - let compartment = global_script_context().js_compartment; + let compartment = (*window.get_ref().page).js_info.get_ref().js_compartment; do root.with_base |base| { assert!(base.wrapper.get_wrapper().is_not_null()); let rootable = base.wrapper.get_rootable(); JS_AddObjectRoot(compartment.cx.ptr, rootable); } - let cx = global_script_context().js_compartment.cx.ptr; + let cx = (*window.get_ref().page).js_info.get_ref().js_compartment.cx.ptr; doc.wrap_object_shared(cx, ptr::null()); //XXXjdm a proper scope would be nice match window { Some(win) => { //FIXME: This is a hack until Window is autogenerated - let compartment = (*win.script_task).js_compartment; + let compartment = (*win.page).js_info.get_ref().js_compartment; compartment.define_property(~"document", RUST_OBJECT_TO_JSVAL(doc.wrapper.wrapper), GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, @@ -89,7 +88,8 @@ impl Document { parent: Element::new(HTMLHtmlElementTypeId, ~"html") }; - let root = unsafe { Node::as_abstract_node(root) }; + let cx = unsafe {(*_owner.page).js_info.get_ref().js_compartment.cx.ptr}; + let root = unsafe { Node::as_abstract_node(cx, root) }; Document(root, None) } @@ -129,15 +129,27 @@ impl Document { } } }; - HTMLCollection::new(elements) + let win = self.window.get_ref(); + let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr}; + let cache = win.get_wrappercache(); + let scope = cache.get_wrapper(); + HTMLCollection::new(elements, cx, scope) } pub fn GetElementsByTagNameNS(&self, _ns: DOMString, _tag: DOMString) -> @mut HTMLCollection { - HTMLCollection::new(~[]) + let win = self.window.get_ref(); + let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr}; + let cache = win.get_wrappercache(); + let scope = cache.get_wrapper(); + HTMLCollection::new(~[], cx, scope) } pub fn GetElementsByClassName(&self, _class: DOMString) -> @mut HTMLCollection { - HTMLCollection::new(~[]) + let win = self.window.get_ref(); + let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr}; + let cache = win.get_wrappercache(); + let scope = cache.get_wrapper(); + HTMLCollection::new(~[], cx, scope) } @@ -276,7 +288,11 @@ impl Document { } } }; - HTMLCollection::new(elements) + let win = self.window.get_ref(); + let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr}; + let cache = win.get_wrappercache(); + let scope = cache.get_wrapper(); + HTMLCollection::new(elements, cx, scope) } pub fn content_changed(&self) { @@ -287,7 +303,7 @@ impl Document { pub fn teardown(&self) { unsafe { - let compartment = global_script_context().js_compartment; + let compartment = (*self.window.get_ref().page).js_info.get_ref().js_compartment; do self.root.with_base |node| { assert!(node.wrapper.get_wrapper().is_not_null()); let rootable = node.wrapper.get_rootable(); diff --git a/src/components/script/dom/domparser.rs b/src/components/script/dom/domparser.rs index e5609e60f49..0b8ba1f0326 100644 --- a/src/components/script/dom/domparser.rs +++ b/src/components/script/dom/domparser.rs @@ -8,7 +8,6 @@ use dom::document::Document; use dom::element::{Element, HTMLHtmlElement, HTMLHtmlElementTypeId}; use dom::node::Node; use dom::window::Window; -use script_task::global_script_context; pub struct DOMParser { owner: @mut Window, //XXXjdm Document instead? @@ -22,7 +21,8 @@ impl DOMParser { wrapper: WrapperCache::new() }; - let cx = global_script_context().js_compartment.cx.ptr; + // TODO(tkuehn): This just handles the top-level page. Need to handle subframes. + let cx = unsafe {(*owner.page).js_info.get_ref().js_compartment.cx.ptr}; let cache = owner.get_wrappercache(); let scope = cache.get_wrapper(); parser.wrap_object_shared(cx, scope); @@ -43,7 +43,7 @@ impl DOMParser { parent: Element::new(HTMLHtmlElementTypeId, ~"html") }; - let root = Node::as_abstract_node(root); + let root = Node::as_abstract_node((*self.owner.page).js_info.get_ref().js_compartment.cx.ptr, root); Document(root, None) } } diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 3983d1b83f2..b83d81d0700 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -4,19 +4,20 @@ //! Element nodes. -use dom::bindings::utils::DOMString; +use dom::bindings::utils::{DOMString, CacheableWrapper}; use dom::clientrect::ClientRect; use dom::clientrectlist::ClientRectList; use dom::node::{ElementNodeTypeId, Node, ScriptView}; -use html::hubbub_html_parser::HtmlParserResult; use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery}; use layout_interface::{ContentBoxesResponse}; use std::cell::Cell; +use std::comm::ChanOne; use std::comm; use std::uint; use std::str::eq_slice; use extra::net::url::Url; +use geom::size::Size2D; pub struct Element { parent: Node<ScriptView>, @@ -112,7 +113,7 @@ pub struct HTMLHeadingElement { pub struct HTMLIframeElement { parent: Element, frame: Option<Url>, - parse_result: Option<Port<HtmlParserResult>> + size_future_chan: Option<ChanOne<Size2D<uint>>>, } pub struct HTMLImageElement { @@ -168,44 +169,53 @@ impl<'self> Element { } pub fn getClientRects(&self) -> Option<@mut ClientRectList> { - let rects = match self.parent.owner_doc { + let (rects, cx, scope) = match self.parent.owner_doc { Some(doc) => { match doc.window { Some(win) => { let node = self.parent.abstract.get(); assert!(node.is_element()); - let script_task = unsafe { - &mut *win.script_task + let page = unsafe { + &mut *win.page }; let (port, chan) = comm::stream(); - match script_task.query_layout(ContentBoxesQuery(node, chan), port) { + // TODO(tkuehn): currently just queries top-level page layout. Needs to query + // subframe layout if this element is in a subframe. Probably need an ID field. + match page.query_layout(ContentBoxesQuery(node, chan), port) { Ok(ContentBoxesResponse(rects)) => { - do rects.map |r| { + let cx = (*page).js_info.get_ref().js_compartment.cx.ptr; + let cache = win.get_wrappercache(); + let scope = cache.get_wrapper(); + let 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()) - } + (r.origin.x + r.size.width).to_f32(), + cx, + scope) + }; + Some((rects, cx, scope)) }, Err(()) => { debug!("layout query error"); - ~[] + None } } } None => { debug!("no window"); - ~[] + None } } } None => { debug!("no document"); - ~[] + None } - }; - Some(ClientRectList::new(rects)) + }.get(); + + Some(ClientRectList::new(rects, cx, scope)) } pub fn getBoundingClientRect(&self) -> Option<@mut ClientRect> { @@ -213,17 +223,26 @@ impl<'self> Element { Some(doc) => { match doc.window { Some(win) => { + let page = unsafe { + &mut *win.page + }; let node = self.parent.abstract.get(); assert!(node.is_element()); - let script_task = unsafe { &mut *win.script_task }; let (port, chan) = comm::stream(); - match script_task.query_layout(ContentBoxQuery(node, chan), port) { + // TODO(tkuehn): currently just queries top-level page layout. Needs to query + // subframe layout if this element is in a subframe. Probably need an ID field. + match page.query_layout(ContentBoxQuery(node, chan), port) { Ok(ContentBoxResponse(rect)) => { + let cx = (*page).js_info.get_ref().js_compartment.cx.ptr; + let cache = win.get_wrappercache(); + let scope = cache.get_wrapper(); 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())) + (rect.origin.x + rect.size.width).to_f32(), + cx, + scope)) }, Err(()) => { debug!("error querying layout"); diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs index 952a8a227cd..9f9135d23e9 100644 --- a/src/components/script/dom/event.rs +++ b/src/components/script/dom/event.rs @@ -7,12 +7,13 @@ use dom::window::Window; use dom::bindings::codegen::EventBinding; use dom::bindings::utils::{CacheableWrapper, BindingObject, DerivedWrapper}; use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache}; -use script_task::{task_from_context, global_script_context}; use geom::point::Point2D; use js::glue::RUST_OBJECT_TO_JSVAL; use js::jsapi::{JSObject, JSContext, JSVal}; +use script_task::page_from_context; + use std::cast; @@ -45,12 +46,7 @@ impl Event_ { } } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } @@ -131,9 +127,9 @@ impl CacheableWrapper for Event_ { impl BindingObject for Event_ { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/dom/eventtarget.rs b/src/components/script/dom/eventtarget.rs index d5f3ff309e8..dbb972fc14e 100644 --- a/src/components/script/dom/eventtarget.rs +++ b/src/components/script/dom/eventtarget.rs @@ -4,7 +4,7 @@ use dom::bindings::codegen::EventTargetBinding; use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; -use script_task::{task_from_context, global_script_context}; +use script_task::page_from_context; use js::glue::RUST_OBJECT_TO_JSVAL; use js::jsapi::{JSObject, JSContext, JSVal}; @@ -22,12 +22,7 @@ impl EventTarget { } } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } } @@ -45,9 +40,10 @@ impl CacheableWrapper for EventTarget { impl BindingObject for EventTarget { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); + // TODO(tkuehn): This only handles top-level pages. Needs to handle subframes. unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/dom/formdata.rs b/src/components/script/dom/formdata.rs index 5c3d49e1371..71a96b49f89 100644 --- a/src/components/script/dom/formdata.rs +++ b/src/components/script/dom/formdata.rs @@ -6,7 +6,7 @@ use dom::bindings::utils::{CacheableWrapper, BindingObject, DerivedWrapper}; use dom::bindings::utils::{WrapperCache, DOMString, str}; use dom::bindings::codegen::FormDataBinding; use dom::blob::Blob; -use script_task::{task_from_context, global_script_context}; +use script_task::{page_from_context}; use js::jsapi::{JSObject, JSContext, JSVal}; use js::glue::RUST_OBJECT_TO_JSVAL; @@ -32,12 +32,7 @@ impl FormData { } } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } @@ -69,9 +64,9 @@ impl CacheableWrapper for FormData { impl BindingObject for FormData { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/dom/htmlcollection.rs b/src/components/script/dom/htmlcollection.rs index da0b29e4e40..a19ed5f669d 100644 --- a/src/components/script/dom/htmlcollection.rs +++ b/src/components/script/dom/htmlcollection.rs @@ -6,7 +6,7 @@ use dom::bindings::codegen::HTMLCollectionBinding; use dom::bindings::utils::{CacheableWrapper, BindingObject, WrapperCache}; use dom::bindings::utils::{DOMString, ErrorResult}; use dom::node::{AbstractNode, ScriptView}; -use script_task::{task_from_context, global_script_context}; +use script_task::page_from_context; use js::jsapi::{JSObject, JSContext}; @@ -19,21 +19,16 @@ pub struct HTMLCollection { } impl HTMLCollection { - pub fn new(elements: ~[AbstractNode<ScriptView>]) -> @mut HTMLCollection { + pub fn new(elements: ~[AbstractNode<ScriptView>], cx: *JSContext, scope: *JSObject) -> @mut HTMLCollection { let collection = @mut HTMLCollection { elements: elements, wrapper: WrapperCache::new() }; - collection.init_wrapper(); + collection.init_wrapper(cx, scope); collection } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } @@ -62,9 +57,10 @@ impl HTMLCollection { impl BindingObject for HTMLCollection { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); + // TODO(tkuehn): This only handles the top-level frame. Need to grab subframes. unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/dom/mouseevent.rs b/src/components/script/dom/mouseevent.rs index 7ea73d27429..175f35a5833 100644 --- a/src/components/script/dom/mouseevent.rs +++ b/src/components/script/dom/mouseevent.rs @@ -9,7 +9,6 @@ use dom::eventtarget::EventTarget; use dom::uievent::UIEvent; use dom::window::Window; use dom::windowproxy::WindowProxy; -use script_task::{global_script_context}; use js::glue::RUST_OBJECT_TO_JSVAL; use js::jsapi::{JSObject, JSContext, JSVal}; @@ -49,12 +48,7 @@ impl MouseEvent { } } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } @@ -181,4 +175,4 @@ impl DerivedWrapper for MouseEvent { } } -}
\ No newline at end of file +} diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index e7cd5f01d33..ab2023151ed 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -11,13 +11,13 @@ use dom::characterdata::CharacterData; use dom::document::Document; use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId, HTMLIframeElementTypeId, HTMLIframeElement}; use dom::element::{HTMLStyleElementTypeId}; -use script_task::global_script_context; use std::cast; use std::cast::transmute; use std::libc::c_void; use std::uint; use js::rust::Compartment; +use js::jsapi::{JSContext}; use netsurfcss::util::VoidPtrLike; use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils}; @@ -424,12 +424,11 @@ impl<View> Iterator<AbstractNode<View>> for AbstractNodeChildrenIterator<View> { } impl Node<ScriptView> { - pub unsafe fn as_abstract_node<N>(node: ~N) -> AbstractNode<ScriptView> { + pub unsafe fn as_abstract_node<N>(cx: *JSContext, node: ~N) -> AbstractNode<ScriptView> { // This surrenders memory management of the node! let mut node = AbstractNode { obj: transmute(node), }; - let cx = global_script_context().js_compartment.cx.ptr; node::create(cx, &mut node); node } diff --git a/src/components/script/dom/uievent.rs b/src/components/script/dom/uievent.rs index 65bf468b0bb..417eccaaebe 100644 --- a/src/components/script/dom/uievent.rs +++ b/src/components/script/dom/uievent.rs @@ -10,8 +10,6 @@ use dom::event::Event_; use dom::window::Window; use dom::windowproxy::WindowProxy; -use script_task::global_script_context; - use js::glue::RUST_OBJECT_TO_JSVAL; use js::jsapi::{JSObject, JSContext, JSVal}; @@ -35,12 +33,7 @@ impl UIEvent { } } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index 7c69ea5aee5..213b211ed91 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -6,7 +6,8 @@ use dom::bindings::utils::WrapperCache; use dom::bindings::window; use layout_interface::ReflowForScriptQuery; -use script_task::{ExitMsg, FireTimerMsg, ScriptChan, ScriptTask}; +use script_task::{ExitMsg, FireTimerMsg, Page, ScriptChan}; +use servo_msg::compositor_msg::ScriptListener; use std::comm; use std::comm::Chan; @@ -27,12 +28,14 @@ pub enum TimerControlMsg { //FIXME If we're going to store the script task, find a way to do so safely. Currently it's // only used for querying layout from arbitrary script. pub struct Window { - timer_chan: Chan<TimerControlMsg>, + page: *mut Page, script_chan: ScriptChan, - script_task: *mut ScriptTask, - wrapper: WrapperCache + compositor: @ScriptListener, + wrapper: WrapperCache, + timer_chan: Chan<TimerControlMsg>, } +#[unsafe_destructor] impl Drop for Window { fn drop(&self) { self.timer_chan.send(TimerMessage_Close); @@ -89,34 +92,37 @@ impl Window { pub fn content_changed(&self) { unsafe { - (*self.script_task).reflow_all(ReflowForScriptQuery) + // TODO(tkuehn): currently reflow top-level, but want to reflow only the associated frame + (*self.page).reflow_all(ReflowForScriptQuery, self.script_chan.clone(), self.compositor); } } - pub fn new(script_chan: ScriptChan, script_task: *mut ScriptTask) + pub fn new(page: *mut Page, script_chan: ScriptChan, compositor: @ScriptListener) -> @mut Window { let script_chan_clone = script_chan.clone(); let win = @mut Window { - wrapper: WrapperCache::new(), + page: page, script_chan: script_chan, + compositor: compositor, + wrapper: WrapperCache::new(), timer_chan: { let (timer_port, timer_chan) = comm::stream::<TimerControlMsg>(); do spawn { loop { match timer_port.recv() { TimerMessage_Close => break, - TimerMessage_Fire(td) => script_chan_clone.chan.send(FireTimerMsg(td)), + TimerMessage_Fire(td) => unsafe {script_chan_clone.chan.send(FireTimerMsg((*page).id.clone(), td))}, TimerMessage_TriggerExit => script_chan_clone.chan.send(ExitMsg), } } } timer_chan }, - script_task: script_task, }; unsafe { - let compartment = (*script_task).js_compartment; + // TODO(tkuehn): This just grabs the top-level page. Need to handle subframes. + let compartment = (*page).js_info.get_ref().js_compartment; window::create(compartment, win); } win diff --git a/src/components/script/dom/windowproxy.rs b/src/components/script/dom/windowproxy.rs index 89b373c0716..350a9e87df6 100644 --- a/src/components/script/dom/windowproxy.rs +++ b/src/components/script/dom/windowproxy.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject}; -use script_task::{global_script_context, task_from_context}; +use script_task::page_from_context; use js::jsapi::{JSContext, JSObject}; @@ -18,21 +18,16 @@ impl WindowProxy { } } - pub fn init_wrapper(@mut self) { - let script_context = global_script_context(); - let cx = script_context.js_compartment.cx.ptr; - let owner = script_context.root_frame.get_ref().window; - let cache = owner.get_wrappercache(); - let scope = cache.get_wrapper(); + pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } } impl BindingObject for WindowProxy { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - let script_context = task_from_context(cx); + let page = page_from_context(cx); unsafe { - (*script_context).root_frame.get_ref().window as @mut CacheableWrapper + (*page).frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index 2f5591464cf..e1ad1c28d34 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -31,6 +31,7 @@ use dom::element::{Element, Attr}; use dom::node::{AbstractNode, Comment, Doctype, ElementNodeTypeId, Node, ScriptView}; use dom::node::{Text}; use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser}; +use js::jsapi::JSContext; use newcss::stylesheet::Stylesheet; use std::cast; @@ -48,9 +49,11 @@ use servo_util::tree::TreeUtils; use servo_util::url::make_url; use extra::net::url::Url; use extra::net::url; +use extra::future::{Future, from_port}; +use geom::size::Size2D; macro_rules! handle_element( - ($tag:expr, $string:expr, $type_id:expr, $ctor:ident, [ $(($field:ident : $field_init:expr)),* ]) => ( + ($cx: expr, $tag:expr, $string:expr, $type_id:expr, $ctor:ident, [ $(($field:ident : $field_init:expr)),* ]) => ( if eq_slice($tag, $string) { let _element = ~$ctor { parent: Element::new($type_id, ($tag).to_str()), @@ -59,7 +62,7 @@ macro_rules! handle_element( )* }; unsafe { - return Node::as_abstract_node(_element); + return Node::as_abstract_node(cx, _element); } } ) @@ -79,7 +82,8 @@ enum JSMessage { pub struct HtmlParserResult { root: AbstractNode<ScriptView>, - style_port: Port<Option<Stylesheet>>, + style_port: Port<Stylesheet>, + iframe_port: Port<(Url, Future<Size2D<uint>>)>, js_port: Port<JSResult>, } @@ -112,7 +116,7 @@ spawned, collates them, and sends them to the given result channel. * `from_parent` - A port on which to receive new links. */ -fn css_link_listener(to_parent: Chan<Option<Stylesheet>>, +fn css_link_listener(to_parent: Chan<Stylesheet>, from_parent: Port<CSSMessage>, resource_task: ResourceTask) { let mut result_vec = ~[]; @@ -131,9 +135,8 @@ fn css_link_listener(to_parent: Chan<Option<Stylesheet>>, // Send the sheets back in order // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these for result_vec.iter().advance |port| { - to_parent.send(Some(port.recv())); + to_parent.send(port.recv()); } - to_parent.send(None); } fn js_script_listener(to_parent: Chan<~[~[u8]]>, @@ -184,63 +187,62 @@ fn js_script_listener(to_parent: Chan<~[~[u8]]>, // Silly macros to handle constructing DOM nodes. This produces bad code and should be optimized // via atomization (issue #85). -fn build_element_from_tag(tag: &str) -> AbstractNode<ScriptView> { +fn build_element_from_tag(cx: *JSContext, tag: &str) -> AbstractNode<ScriptView> { // TODO (Issue #85): use atoms - handle_element!(tag, "a", HTMLAnchorElementTypeId, HTMLAnchorElement, []); - handle_element!(tag, "aside", HTMLAsideElementTypeId, HTMLAsideElement, []); - handle_element!(tag, "br", HTMLBRElementTypeId, HTMLBRElement, []); - handle_element!(tag, "body", HTMLBodyElementTypeId, HTMLBodyElement, []); - handle_element!(tag, "bold", HTMLBoldElementTypeId, HTMLBoldElement, []); - handle_element!(tag, "div", HTMLDivElementTypeId, HTMLDivElement, []); - handle_element!(tag, "font", HTMLFontElementTypeId, HTMLFontElement, []); - handle_element!(tag, "form", HTMLFormElementTypeId, HTMLFormElement, []); - handle_element!(tag, "hr", HTMLHRElementTypeId, HTMLHRElement, []); - handle_element!(tag, "head", HTMLHeadElementTypeId, HTMLHeadElement, []); - handle_element!(tag, "html", HTMLHtmlElementTypeId, HTMLHtmlElement, []); - handle_element!(tag, "input", HTMLInputElementTypeId, HTMLInputElement, []); - handle_element!(tag, "i", HTMLItalicElementTypeId, HTMLItalicElement, []); - handle_element!(tag, "link", HTMLLinkElementTypeId, HTMLLinkElement, []); - handle_element!(tag, "li", HTMLListItemElementTypeId, HTMLListItemElement, []); - handle_element!(tag, "meta", HTMLMetaElementTypeId, HTMLMetaElement, []); - handle_element!(tag, "ol", HTMLOListElementTypeId, HTMLOListElement, []); - handle_element!(tag, "option", HTMLOptionElementTypeId, HTMLOptionElement, []); - handle_element!(tag, "p", HTMLParagraphElementTypeId, HTMLParagraphElement, []); - handle_element!(tag, "script", HTMLScriptElementTypeId, HTMLScriptElement, []); - handle_element!(tag, "section", HTMLSectionElementTypeId, HTMLSectionElement, []); - handle_element!(tag, "select", HTMLSelectElementTypeId, HTMLSelectElement, []); - handle_element!(tag, "small", HTMLSmallElementTypeId, HTMLSmallElement, []); - handle_element!(tag, "span", HTMLSpanElementTypeId, HTMLSpanElement, []); - handle_element!(tag, "style", HTMLStyleElementTypeId, HTMLStyleElement, []); - handle_element!(tag, "tbody", HTMLTableBodyElementTypeId, HTMLTableBodyElement, []); - handle_element!(tag, "td", HTMLTableCellElementTypeId, HTMLTableCellElement, []); - handle_element!(tag, "table", HTMLTableElementTypeId, HTMLTableElement, []); - handle_element!(tag, "tr", HTMLTableRowElementTypeId, HTMLTableRowElement, []); - handle_element!(tag, "title", HTMLTitleElementTypeId, HTMLTitleElement, []); - handle_element!(tag, "ul", HTMLUListElementTypeId, HTMLUListElement, []); - - handle_element!(tag, "img", HTMLImageElementTypeId, HTMLImageElement, [(image: None)]); - handle_element!(tag, "iframe", HTMLIframeElementTypeId, HTMLIframeElement, - [(frame: None), (parse_result: None)]); - - handle_element!(tag, "h1", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading1)]); - handle_element!(tag, "h2", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading2)]); - handle_element!(tag, "h3", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading3)]); - handle_element!(tag, "h4", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading4)]); - handle_element!(tag, "h5", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading5)]); - handle_element!(tag, "h6", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading6)]); + handle_element!(cx, tag, "a", HTMLAnchorElementTypeId, HTMLAnchorElement, []); + handle_element!(cx, tag, "aside", HTMLAsideElementTypeId, HTMLAsideElement, []); + handle_element!(cx, tag, "br", HTMLBRElementTypeId, HTMLBRElement, []); + handle_element!(cx, tag, "body", HTMLBodyElementTypeId, HTMLBodyElement, []); + handle_element!(cx, tag, "bold", HTMLBoldElementTypeId, HTMLBoldElement, []); + handle_element!(cx, tag, "div", HTMLDivElementTypeId, HTMLDivElement, []); + handle_element!(cx, tag, "font", HTMLFontElementTypeId, HTMLFontElement, []); + handle_element!(cx, tag, "form", HTMLFormElementTypeId, HTMLFormElement, []); + handle_element!(cx, tag, "hr", HTMLHRElementTypeId, HTMLHRElement, []); + handle_element!(cx, tag, "head", HTMLHeadElementTypeId, HTMLHeadElement, []); + handle_element!(cx, tag, "html", HTMLHtmlElementTypeId, HTMLHtmlElement, []); + handle_element!(cx, tag, "input", HTMLInputElementTypeId, HTMLInputElement, []); + handle_element!(cx, tag, "i", HTMLItalicElementTypeId, HTMLItalicElement, []); + handle_element!(cx, tag, "link", HTMLLinkElementTypeId, HTMLLinkElement, []); + handle_element!(cx, tag, "li", HTMLListItemElementTypeId, HTMLListItemElement, []); + handle_element!(cx, tag, "meta", HTMLMetaElementTypeId, HTMLMetaElement, []); + handle_element!(cx, tag, "ol", HTMLOListElementTypeId, HTMLOListElement, []); + handle_element!(cx, tag, "option", HTMLOptionElementTypeId, HTMLOptionElement, []); + handle_element!(cx, tag, "p", HTMLParagraphElementTypeId, HTMLParagraphElement, []); + handle_element!(cx, tag, "script", HTMLScriptElementTypeId, HTMLScriptElement, []); + handle_element!(cx, tag, "section", HTMLSectionElementTypeId, HTMLSectionElement, []); + handle_element!(cx, tag, "select", HTMLSelectElementTypeId, HTMLSelectElement, []); + handle_element!(cx, tag, "small", HTMLSmallElementTypeId, HTMLSmallElement, []); + handle_element!(cx, tag, "span", HTMLSpanElementTypeId, HTMLSpanElement, []); + handle_element!(cx, tag, "style", HTMLStyleElementTypeId, HTMLStyleElement, []); + handle_element!(cx, tag, "tbody", HTMLTableBodyElementTypeId, HTMLTableBodyElement, []); + handle_element!(cx, tag, "td", HTMLTableCellElementTypeId, HTMLTableCellElement, []); + handle_element!(cx, tag, "table", HTMLTableElementTypeId, HTMLTableElement, []); + handle_element!(cx, tag, "tr", HTMLTableRowElementTypeId, HTMLTableRowElement, []); + handle_element!(cx, tag, "title", HTMLTitleElementTypeId, HTMLTitleElement, []); + handle_element!(cx, tag, "ul", HTMLUListElementTypeId, HTMLUListElement, []); + + handle_element!(cx, tag, "img", HTMLImageElementTypeId, HTMLImageElement, [(image: None)]); + handle_element!(cx, tag, "iframe", HTMLIframeElementTypeId, HTMLIframeElement, [(frame: None), (size_future_chan: None)]); + + handle_element!(cx, tag, "h1", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading1)]); + handle_element!(cx, tag, "h2", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading2)]); + handle_element!(cx, tag, "h3", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading3)]); + handle_element!(cx, tag, "h4", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading4)]); + handle_element!(cx, tag, "h5", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading5)]); + handle_element!(cx, tag, "h6", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading6)]); unsafe { - Node::as_abstract_node(~Element::new(UnknownElementTypeId, tag.to_str())) + Node::as_abstract_node(cx, ~Element::new(UnknownElementTypeId, tag.to_str())) } } #[allow(non_implicitly_copyable_typarams)] -pub fn parse_html(url: Url, +pub fn parse_html(cx: *JSContext, + url: Url, resource_task: ResourceTask, image_cache_task: ImageCacheTask) -> HtmlParserResult { // Spawn a CSS parser to receive links to CSS style sheets. let resource_task2 = resource_task.clone(); - let resource_task3 = resource_task.clone(); let (stylesheet_port, stylesheet_chan) = comm::stream(); let stylesheet_chan = Cell::new(stylesheet_chan); @@ -268,7 +270,7 @@ pub fn parse_html(url: Url, // Build the root node. let root = ~HTMLHtmlElement { parent: Element::new(HTMLHtmlElementTypeId, ~"html") }; - let root = unsafe { Node::as_abstract_node(root) }; + let root = unsafe { Node::as_abstract_node(cx, root) }; debug!("created new node"); let mut parser = hubbub::Parser("UTF-8", false); debug!("created parser"); @@ -277,11 +279,12 @@ pub fn parse_html(url: Url, parser.enable_styling(true); let (css_chan2, css_chan3, js_chan2) = (css_chan.clone(), css_chan.clone(), js_chan.clone()); + let (iframe_port, iframe_chan) = comm::stream(); parser.set_tree_handler(~hubbub::TreeHandler { create_comment: |data: ~str| { debug!("create comment"); unsafe { - Node::as_abstract_node(~Comment::new(data)).to_hubbub_node() + Node::as_abstract_node(cx, ~Comment::new(data)).to_hubbub_node() } }, create_doctype: |doctype: ~hubbub::Doctype| { @@ -295,12 +298,12 @@ pub fn parse_html(url: Url, system_id, force_quirks); unsafe { - Node::as_abstract_node(node).to_hubbub_node() + Node::as_abstract_node(cx, node).to_hubbub_node() } }, create_element: |tag: ~hubbub::Tag| { debug!("create element"); - let node = build_element_from_tag(tag.name); + let node = build_element_from_tag(cx, tag.name); debug!("-- attach attrs"); do node.as_mut_element |element| { @@ -325,30 +328,22 @@ pub fn parse_html(url: Url, _ => {} } } - }, + } + ElementNodeTypeId(HTMLIframeElementTypeId) => { do node.with_mut_iframe_element |iframe_element| { let src_opt = iframe_element.parent.get_attr("src").map(|x| x.to_str()); - match src_opt { - None => {} - Some(src) => { - let iframe_url = make_url(src, Some(url2.clone())); - iframe_element.frame = Some(iframe_url.clone()); - let (parse_port, parse_chan) = comm::stream(); - iframe_element.parse_result = Some(parse_port); - let image_cache_task2 = Cell::new(image_cache_task.clone()); - let resource_task3 = Cell::new(resource_task3.clone()); - let iframe_url = Cell::new(iframe_url); - do task::spawn { - let result = parse_html(iframe_url.take(), - resource_task3.take(), - image_cache_task2.take()); - parse_chan.send(result); - } - } + for src_opt.iter().advance |src| { + let iframe_url = make_url(src.clone(), Some(url2.clone())); + iframe_element.frame = Some(iframe_url.clone()); + let (port, chan) = comm::oneshot(); + iframe_element.size_future_chan = Some(chan); + let size_future = from_port(port); + iframe_chan.send((iframe_url, size_future)); } } } + ElementNodeTypeId(HTMLImageElementTypeId) => { do node.with_mut_image_element |image_element| { let src_opt = image_element.parent.get_attr("src").map(|x| x.to_str()); @@ -365,6 +360,7 @@ pub fn parse_html(url: Url, } } } + //TODO (Issue #86): handle inline styles ('style' attr) _ => {} } @@ -374,7 +370,7 @@ pub fn parse_html(url: Url, create_text: |data: ~str| { debug!("create text"); unsafe { - Node::as_abstract_node(~Text::new(data)).to_hubbub_node() + Node::as_abstract_node(cx, ~Text::new(data)).to_hubbub_node() } }, ref_node: |_| {}, @@ -496,6 +492,7 @@ pub fn parse_html(url: Url, HtmlParserResult { root: root, style_port: stylesheet_port, + iframe_port: iframe_port, js_port: js_result_port, } } diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs index 766c728067b..9c9f27328c6 100644 --- a/src/components/script/layout_interface.rs +++ b/src/components/script/layout_interface.rs @@ -7,8 +7,7 @@ /// from layout. use dom::node::{AbstractNode, ScriptView, LayoutView}; -use script_task::{ScriptMsg, ScriptChan}; - +use script_task::{ScriptChan}; use std::comm::{Chan, SharedChan}; use geom::rect::Rect; use geom::size::Size2D; @@ -32,9 +31,6 @@ pub enum Msg { /// FIXME(pcwalton): As noted below, this isn't very type safe. QueryMsg(LayoutQuery), - /// Routes a message (usually from the compositor) to the appropriate script task - RouteScriptMsg(ScriptMsg), - /// Requests that the layout task shut down and exit. ExitMsg, } diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index b0d9af20cfc..bd52d3f8e20 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -11,7 +11,7 @@ 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::node::define_bindings; use dom::window::Window; use layout_interface::{AddStylesheetMsg, DocumentDamage}; use layout_interface::{DocumentDamageLevel, HitTestQuery, HitTestResponse, LayoutQuery}; @@ -20,20 +20,22 @@ use layout_interface::{ReflowDocumentDamage, ReflowForDisplay, ReflowGoal}; use layout_interface::ReflowMsg; use layout_interface; use servo_msg::constellation_msg::{ConstellationChan, LoadUrlMsg, NavigationDirection}; -use servo_msg::constellation_msg::{RendererReadyMsg, ResizedWindowBroadcast}; +use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowBroadcast}; +use servo_msg::constellation_msg::{LoadIframeUrlMsg}; use servo_msg::constellation_msg; -use std::cast::transmute; +use newcss::stylesheet::Stylesheet; + use std::cell::Cell; use std::comm; -use std::comm::{Port, SharedChan}; +use std::comm::{Port, SharedChan, Select2}; use std::io::read_whole_file; -use std::local_data; use std::ptr::null; use std::task::{SingleThreaded, task}; use std::util::replace; use dom::window::TimerData; use geom::size::Size2D; +use html::hubbub_html_parser::HtmlParserResult; use html::hubbub_html_parser; use js::JSVAL_NULL; use js::global::{global_class, debug_fns}; @@ -48,27 +50,37 @@ use servo_util::tree::TreeNodeRef; use servo_util::url::make_url; use extra::net::url::Url; use extra::net::url; +use extra::future::{from_value, Future}; /// Messages used to control the script task. pub enum ScriptMsg { - /// Loads a new URL. - LoadMsg(Url), + /// Loads a new URL on the specified pipeline. + LoadMsg(PipelineId, Url), + /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent + AttachLayoutMsg(NewLayoutInfo), /// Executes a standalone script. - ExecuteMsg(Url), + ExecuteMsg(PipelineId, Url), /// Instructs the script task to send a navigate message to the constellation. - NavigateMsg(NavigationDirection), + NavigateMsg(PipelineId, NavigationDirection), /// Sends a DOM event. - SendEventMsg(Event), + SendEventMsg(PipelineId, Event), /// Fires a JavaScript timeout. - FireTimerMsg(~TimerData), + FireTimerMsg(PipelineId, ~TimerData), /// Notifies script that reflow is finished. - ReflowCompleteMsg, + ReflowCompleteMsg(PipelineId), /// Notifies script that window has been resized but to not take immediate action. ResizeInactiveMsg(Size2D<uint>), /// Exits the constellation. ExitMsg, } +pub struct NewLayoutInfo { + old_id: PipelineId, + new_id: PipelineId, + layout_chan: LayoutChan, + size_future: Future<Size2D<uint>>, +} + /// Encapsulates external communication with the script task. #[deriving(Clone)] pub struct ScriptChan { @@ -77,7 +89,7 @@ pub struct ScriptChan { } impl ScriptChan { - /// Creates a new script task. + /// Creates a new script chan. pub fn new(chan: Chan<ScriptMsg>) -> ScriptChan { ScriptChan { chan: SharedChan::new(chan) @@ -88,11 +100,242 @@ impl ScriptChan { } } +/// Encapsulates a handle to a frame and its associate layout information +pub struct Page { + /// Pipeline id associated with this page. + id: PipelineId, + + /// The outermost frame containing the document, window, and page URL. + frame: Option<Frame>, + + /// A handle for communicating messages to the layout task. + layout_chan: LayoutChan, + + /// The port that we will use to join layout. If this is `None`, then layout is not running. + layout_join_port: Option<Port<()>>, + + /// What parts of the document are dirty, if any. + damage: Option<DocumentDamage>, + + /// The current size of the window, in pixels. + window_size: Future<Size2D<uint>>, + + js_info: Option<JSPageInfo>, + + /// 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 (!). The bool indicates if reflow is required + /// when reloading. + url: Option<(Url, bool)>, +} + +pub struct PageTree { + page: @mut Page, + inner: ~[PageTree], +} + +pub struct PageTreeIterator<'self> { + priv stack: ~[&'self mut PageTree], +} + +impl PageTree { + fn new(id: PipelineId, layout_chan: LayoutChan, size_future: Future<Size2D<uint>>) -> PageTree { + PageTree { + page: @mut Page { + id: id, + frame: None, + layout_chan: layout_chan, + layout_join_port: None, + damage: None, + window_size: size_future, + js_info: None, + url: None, + }, + inner: ~[], + } + } + + pub fn find<'a> (&'a mut self, id: PipelineId) -> Option<&'a mut PageTree> { + if self.page.id == id { return Some(self); } + for self.inner.mut_iter().advance |page_tree| { + let found = page_tree.find(id); + if found.is_some() { return found; } + } + None + } + + pub fn iter<'a>(&'a mut self) -> PageTreeIterator<'a> { + PageTreeIterator { + stack: ~[self], + } + } +} + +impl<'self> Iterator<@mut Page> for PageTreeIterator<'self> { + fn next(&mut self) -> Option<@mut Page> { + if !self.stack.is_empty() { + let next = self.stack.pop(); + { + for next.inner.mut_iter().advance |child| { + self.stack.push(child); + } + } + Some(next.page) + } else { + None + } + } +} + +impl Page { + /// Adds the given damage. + fn damage(&mut self, level: DocumentDamageLevel) { + match self.damage { + None => {} + Some(ref mut damage) => { + // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor. + damage.root = self.frame.get_ref().document.root; + damage.level.add(level); + return + } + } + + self.damage = Some(DocumentDamage { + root: self.frame.get_ref().document.root, + level: level, + }) + } + + /// Sends a ping to layout and waits for the response. The response will arrive when the + /// layout task has finished any pending request messages. + fn join_layout(&mut self) { + if self.layout_join_port.is_some() { + let join_port = replace(&mut self.layout_join_port, None); + match join_port { + Some(ref join_port) => { + if !join_port.peek() { + info!("script: waiting on layout"); + } + + join_port.recv(); + + debug!("script: layout joined") + } + None => fail!(~"reader forked but no join port?"), + } + } + } + + /// Sends the given query to layout. + pub fn query_layout<T: Send>(&mut self, + query: LayoutQuery, + response_port: Port<Result<T, ()>>) + -> Result<T,()> { + self.join_layout(); + self.layout_chan.send(QueryMsg(query)); + response_port.recv() + } + + /// This method will wait until the layout task has completed its current action, join the + /// layout task, and then request a new layout run. It won't wait for the new layout + /// computation to finish. + /// + /// This function fails if there is no root frame. + fn reflow(&mut self, goal: ReflowGoal, script_chan: ScriptChan, compositor: @ScriptListener) { + + debug!("script: performing reflow for goal %?", goal); + + // Now, join the layout so that they will see the latest changes we have made. + self.join_layout(); + + // Tell the user that we're performing layout. + compositor.set_ready_state(PerformingLayout); + + // Layout will let us know when it's done. + let (join_port, join_chan) = comm::stream(); + self.layout_join_port = Some(join_port); + + match self.frame { + None => fail!(~"Tried to relayout with no root frame!"), + Some(ref frame) => { + // Send new document and relevant styles to layout. + let reflow = ~Reflow { + document_root: frame.document.root, + url: copy self.url.get_ref().first(), + goal: goal, + window_size: self.window_size.get(), + script_chan: script_chan, + script_join_chan: join_chan, + damage: replace(&mut self.damage, None).unwrap(), + }; + + self.layout_chan.send(ReflowMsg(reflow)) + } + } + + debug!("script: layout forked") + } + + /// Reflows the entire document. + /// + /// FIXME: This should basically never be used. + pub fn reflow_all(&mut self, goal: ReflowGoal, script_chan: ScriptChan, compositor: @ScriptListener) { + if self.frame.is_some() { + self.damage(MatchSelectorsDocumentDamage); + } + + self.reflow(goal, script_chan, compositor) + } + + pub fn initialize_js_info(&mut self, js_context: @Cx) { + // Note that the order that these variables are initialized is _not_ arbitrary. Switching them around + // can -- and likely will -- lead to things breaking. + + js_context.set_default_options_and_version(); + js_context.set_logging_error_reporter(); + + let compartment = match js_context.new_compartment(global_class) { + Ok(c) => c, + Err(()) => fail!("Failed to create a compartment"), + }; + + // Indirection for Rust Issue #6248, dynamic freeze scope artifically extended + let page_ptr = { + let borrowed_page = &mut *self; + borrowed_page as *mut Page + }; + + unsafe { + js_context.set_cx_private(page_ptr as *()); + } + + self.js_info = Some(JSPageInfo { + dom_static: GlobalStaticData(), + bindings_initialized: false, + js_compartment: compartment, + js_context: js_context, + }); + } + +} + /// Information for one frame in the browsing context. pub struct Frame { document: @mut Document, window: @mut Window, - url: Url, + +} + +/// Encapsulation of the javascript information associated with each frame. +pub struct JSPageInfo { + /// Global static data related to the DOM. + dom_static: GlobalStaticData, + /// Flag indicating if the JS bindings have been initialized. + bindings_initialized: bool, + /// The JavaScript compartment for the origin associated with the script task. + js_compartment: @mut Compartment, + /// The JavaScript context. + js_context: @Cx, } /// Information for an entire page. Pages are top-level browsing contexts and can contain multiple @@ -100,135 +343,64 @@ pub struct Frame { /// /// FIXME: Rename to `Page`, following WebKit? 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 information pertaining to page layout + page_tree: PageTree, /// A handle to the image cache task. image_cache_task: ImageCacheTask, /// A handle to the resource task. resource_task: ResourceTask, - /// The port that we will use to join layout. If this is `None`, then layout is not currently - /// running. - layout_join_port: Option<Port<()>>, - /// The port on which we receive messages (load URL, exit, etc.) - script_port: Port<ScriptMsg>, - /// A channel for us to hand out when we want some other task to be able to send us script - /// messages. - script_chan: ScriptChan, + /// The port on which the script task receives messages (load URL, exit, etc.) + port: Port<ScriptMsg>, + /// A channel to hand out when some other task needs to be able to respond to a message from + /// the script task. + chan: ScriptChan, /// For communicating load url messages to the constellation constellation_chan: ConstellationChan, - /// For permission to communicate ready state messages to the compositor + /// A handle to the compositor for communicating ready state messages. compositor: @ScriptListener, /// The JavaScript runtime. js_runtime: js::rust::rt, - /// The JavaScript context. - js_context: @Cx, - /// The JavaScript compartment. - js_compartment: @mut Compartment, - - /// Global static data related to the DOM. - dom_static: GlobalStaticData, - /// Whether the JS bindings have been initialized. - bindings_initialized: bool, - - /// The outermost frame. This frame contains the document, window, and page URL. - root_frame: Option<Frame>, - - /// The current size of the window, in pixels. - window_size: Size2D<uint>, - /// What parts of the document are dirty, if any. - damage: Option<DocumentDamage>, - - /// 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 (!). The bool indicates if reflow is required - last_loaded_url: Option<(Url, bool)>, } -fn global_script_context_key(_: @ScriptTask) {} - -/// Returns this task's script context singleton. -pub fn global_script_context() -> @ScriptTask { +/// Returns the relevant page from the associated JS Context. +pub fn page_from_context(js_context: *JSContext) -> *mut Page { unsafe { - local_data::local_data_get(global_script_context_key).get() - } -} - -/// 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 ScriptTask { - unsafe { - JS_GetContextPrivate(js_context) as *mut ScriptTask + JS_GetContextPrivate(js_context) as *mut Page } } impl ScriptTask { /// Creates a new script task. - pub fn new(id: uint, + pub fn new(id: PipelineId, compositor: @ScriptListener, layout_chan: LayoutChan, - script_port: Port<ScriptMsg>, - script_chan: ScriptChan, + port: Port<ScriptMsg>, + chan: ScriptChan, constellation_chan: ConstellationChan, resource_task: ResourceTask, img_cache_task: ImageCacheTask, - initial_size: Size2D<int>) + initial_size: Future<Size2D<uint>>) -> @mut ScriptTask { let js_runtime = js::rust::rt(); - let js_context = js_runtime.cx(); - - js_context.set_default_options_and_version(); - js_context.set_logging_error_reporter(); - - let compartment = match js_context.new_compartment(global_class) { - Ok(c) => c, - Err(()) => fail!("Failed to create a compartment"), - }; let script_task = @mut ScriptTask { - id: id, - compositor: compositor, + page_tree: PageTree::new(id, layout_chan, initial_size), - layout_chan: layout_chan, image_cache_task: img_cache_task, resource_task: resource_task, - layout_join_port: None, - script_port: script_port, - script_chan: script_chan, - + port: port, + chan: chan, constellation_chan: constellation_chan, + compositor: compositor, js_runtime: js_runtime, - js_context: js_context, - js_compartment: compartment, - - dom_static: GlobalStaticData(), - bindings_initialized: false, - - root_frame: None, - - window_size: Size2D(initial_size.width as uint, initial_size.height as uint), - damage: None, - - last_loaded_url: None, }; - // Indirection for Rust Issue #6248, dynamic freeze scope artifically extended - let script_task_ptr = { - let borrowed_ctx= &mut *script_task; - borrowed_ctx as *mut ScriptTask - }; - - unsafe { - js_context.set_cx_private(script_task_ptr as *()); - local_data::local_data_set(global_script_context_key, transmute(script_task)) - } + script_task.page_tree.page.initialize_js_info(script_task.js_runtime.cx()); script_task } @@ -240,17 +412,18 @@ impl ScriptTask { } } - pub fn create<C: ScriptListener + Send>(id: uint, - compositor: C, - layout_chan: LayoutChan, - script_port: Port<ScriptMsg>, - script_chan: ScriptChan, - constellation_chan: ConstellationChan, - resource_task: ResourceTask, - image_cache_task: ImageCacheTask, - initial_size: Size2D<int>) { + pub fn create<C: ScriptListener + Send>(id: PipelineId, + compositor: C, + layout_chan: LayoutChan, + port: Port<ScriptMsg>, + chan: ScriptChan, + constellation_chan: ConstellationChan, + resource_task: ResourceTask, + image_cache_task: ImageCacheTask, + initial_size: Future<Size2D<uint>>) { let compositor = Cell::new(compositor); - let script_port = Cell::new(script_port); + let port = Cell::new(port); + let initial_size = Cell::new(initial_size); // FIXME: rust#6399 let mut the_task = task(); the_task.sched_mode(SingleThreaded); @@ -258,25 +431,27 @@ impl ScriptTask { let script_task = ScriptTask::new(id, @compositor.take() as @ScriptListener, layout_chan.clone(), - script_port.take(), - script_chan.clone(), + port.take(), + chan.clone(), constellation_chan.clone(), resource_task.clone(), image_cache_task.clone(), - initial_size); + initial_size.take()); script_task.start(); } } /// Handles an incoming control message. fn handle_msg(&mut self) -> bool { - match self.script_port.recv() { - 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(), + match self.port.recv() { + // TODO(tkuehn) need to handle auxiliary layouts for iframes + AttachLayoutMsg(new_layout_info) => self.handle_new_layout(new_layout_info), + LoadMsg(id, url) => self.load(id, url), + ExecuteMsg(id, url) => self.handle_execute_msg(id, url), + SendEventMsg(id, event) => self.handle_event(id, event), + FireTimerMsg(id, timer_data) => self.handle_fire_timer_msg(id, timer_data), + NavigateMsg(id, direction) => self.handle_navigate_msg(id, direction), + ReflowCompleteMsg(id) => self.handle_reflow_complete_msg(id), ResizeInactiveMsg(new_size) => self.handle_resize_inactive_msg(new_size), ExitMsg => { self.handle_exit_msg(); @@ -286,309 +461,267 @@ impl ScriptTask { true } + fn handle_new_layout(&mut self, new_layout_info: NewLayoutInfo) { + let NewLayoutInfo { + old_id, + new_id, + layout_chan, + size_future + } = new_layout_info; + + let parent_page_tree = self.page_tree.find(old_id).expect("ScriptTask: received a layout + whose parent has a PipelineId which does not correspond to a pipeline in the script + task's page tree. This is a bug."); + let new_page_tree = PageTree::new(new_id, layout_chan, size_future); + new_page_tree.page.initialize_js_info(self.js_runtime.cx()); + + parent_page_tree.inner.push(new_page_tree); + } + /// Handles a request to execute a script. - fn handle_execute_msg(&self, url: Url) { + fn handle_execute_msg(&mut self, id: PipelineId, url: Url) { debug!("script: Received url `%s` to execute", url::to_str(&url)); + let page_tree = self.page_tree.find(id).expect("ScriptTask: received fire timer msg for a + pipeline ID not associated with this script task. This is a bug."); + let js_info = page_tree.page.js_info.get_ref(); + match read_whole_file(&Path(url.path)) { Err(msg) => println(fmt!("Error opening %s: %s", url::to_str(&url), msg)), Ok(bytes) => { - self.js_compartment.define_functions(debug_fns); - let _ = self.js_context.evaluate_script(self.js_compartment.global_obj, - bytes, - copy url.path, - 1); + js_info.js_compartment.define_functions(debug_fns); + js_info.js_context.evaluate_script(js_info.js_compartment.global_obj, + bytes, + copy url.path, + 1); } } } /// Handles a timer that fired. - fn handle_fire_timer_msg(&mut self, timer_data: ~TimerData) { + fn handle_fire_timer_msg(&mut self, id: PipelineId, timer_data: ~TimerData) { + let page = self.page_tree.find(id).expect("ScriptTask: received fire timer msg for a + pipeline ID not associated with this script task. This is a bug.").page; unsafe { + let js_info = page.js_info.get_ref(); let this_value = if timer_data.args.len() > 0 { RUST_JSVAL_TO_OBJECT(timer_data.args[0]) } else { - self.js_compartment.global_obj.ptr + js_info.js_compartment.global_obj.ptr }; // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`. let rval = JSVAL_NULL; - JS_CallFunctionValue(self.js_context.ptr, + JS_CallFunctionValue(js_info.js_context.ptr, this_value, timer_data.funval, 0, null(), &rval); - // We don't know what the script changed, so for now we will do a total redisplay. - self.reflow_all(ReflowForDisplay) } + // We don't know what the script changed, so for now we will do a total redisplay. + page.reflow_all(ReflowForDisplay, self.chan.clone(), self.compositor); } /// Handles a notification that reflow completed. - fn handle_reflow_complete_msg(&mut self) { - self.layout_join_port = None; - self.constellation_chan.send(RendererReadyMsg(self.id)); + fn handle_reflow_complete_msg(&mut self, pipeline_id: PipelineId) { + self.page_tree.find(pipeline_id).expect("ScriptTask: received a load + message for a layout channel that is not associated with this script task. This + is a bug.").page.layout_join_port = None; + self.constellation_chan.send(RendererReadyMsg(pipeline_id)); self.compositor.set_ready_state(FinishedLoading); } /// Handles a navigate forward or backward message. - fn handle_navigate_msg(&self, direction: NavigationDirection) { + fn handle_navigate_msg(&self, pipeline_id: PipelineId, direction: NavigationDirection) { self.constellation_chan.send(constellation_msg::NavigateMsg(direction)); } /// Window was resized, but this script was not active, so don't reflow yet fn handle_resize_inactive_msg(&mut self, new_size: Size2D<uint>) { - self.window_size = new_size; - let last_loaded_url = replace(&mut self.last_loaded_url, None); + self.page_tree.page.window_size = from_value(new_size); + let last_loaded_url = replace(&mut self.page_tree.page.url, None); for last_loaded_url.iter().advance |last_loaded_url| { - self.last_loaded_url = Some((last_loaded_url.first(), true)); + self.page_tree.page.url = Some((last_loaded_url.first(), true)); } } /// Handles a request to exit the script task and shut down layout. fn handle_exit_msg(&mut self) { - self.join_layout(); - for self.root_frame.iter().advance |frame| { - frame.document.teardown(); - } - - self.layout_chan.send(layout_interface::ExitMsg); - - unsafe { - let _ = local_data::local_data_pop(global_script_context_key); + for self.page_tree.iter().advance |page| { + page.join_layout(); + page.frame.get().document.teardown(); + page.layout_chan.send(layout_interface::ExitMsg); } } /// 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) { - let last_loaded_url = replace(&mut self.last_loaded_url, None); + fn load(&mut self, pipeline_id: PipelineId, url: Url) { + let page = self.page_tree.find(pipeline_id).expect("ScriptTask: received a load + message for a layout channel that is not associated with this script task. This + is a bug.").page; + let last_loaded_url = replace(&mut page.url, None); for last_loaded_url.iter().advance |last_loaded_url| { let (ref last_loaded_url, needs_reflow) = *last_loaded_url; if *last_loaded_url == url { if needs_reflow { - self.reflow_all(ReflowForDisplay); - self.last_loaded_url = Some((last_loaded_url.clone(), false)); + page.reflow_all(ReflowForDisplay, self.chan.clone(), self.compositor); + page.url = Some((last_loaded_url.clone(), false)); } return; } } - // Define the script DOM bindings. - // - // FIXME: Can this be done earlier, to save the flag? - if !self.bindings_initialized { - define_bindings(self.js_compartment); - self.bindings_initialized = true + { + let js_info = page.js_info.get_mut_ref(); + // Define the script DOM bindings. + // + // FIXME: Can this be done earlier, to save the flag? + //let js_info = page.js_info.get_ref(); + if !js_info.bindings_initialized { + define_bindings(js_info.js_compartment); + js_info.bindings_initialized = true; + } } 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(url.clone(), + let html_parsing_result = hubbub_html_parser::parse_html(page.js_info.get_ref().js_compartment.cx.ptr, + url.clone(), self.resource_task.clone(), self.image_cache_task.clone()); - let root_node = html_parsing_result.root; - - // Send style sheets over to layout. - // - // FIXME: These should be streamed to layout as they're parsed. We don't need to stop here - // in the script task. - loop { - match html_parsing_result.style_port.recv() { - Some(sheet) => self.layout_chan.send(AddStylesheetMsg(sheet)), - None => break, - } - } - - // Receive the JavaScript scripts. - let js_scripts = html_parsing_result.js_port.recv(); - debug!("js_scripts: %?", js_scripts); + let HtmlParserResult {root, js_port, style_port, iframe_port} = html_parsing_result; // Create the window and document objects. - let window = Window::new(self.script_chan.clone(), &mut *self); - let document = Document(root_node, Some(window)); + let window = Window::new(&mut *page, self.chan.clone(), self.compositor); + let document = Document(root, Some(window)); // Tie the root into the document. - do root_node.with_mut_base |base| { + do root.with_mut_base |base| { base.add_to_doc(document) } // Create the root frame. - self.root_frame = Some(Frame { + page.frame = Some(Frame { document: document, window: window, - url: url.clone(), }); + page.url = Some((url.clone(), true)); - // Perform the initial reflow. - self.damage = Some(DocumentDamage { - root: root_node, - level: MatchSelectorsDocumentDamage, - }); - self.reflow(ReflowForDisplay); - - // Define debug functions. - self.js_compartment.define_functions(debug_fns); - - // Evaluate every script in the document. - for js_scripts.iter().advance |bytes| { - let _ = self.js_context.evaluate_script(self.js_compartment.global_obj, - bytes.clone(), - ~"???", - 1); - } - self.last_loaded_url = Some((url, false)); - } - - /// Sends a ping to layout and waits for the response. The response will arrive when the - /// layout task has finished any pending request messages. - fn join_layout(&mut self) { - if self.layout_join_port.is_some() { - let join_port = replace(&mut self.layout_join_port, None); - match join_port { - Some(ref join_port) => { - if !join_port.peek() { - info!("script: waiting on layout"); - } - - join_port.recv(); + // Send style sheets over to layout. + // + // FIXME: These should be streamed to layout as they're parsed. We don't need to stop here + // in the script task. - debug!("script: layout joined") + let get_iframes = |iframe_port: &Port<(Url, Future<Size2D<uint>>)>| loop { + match iframe_port.try_recv() { + None => break, + Some((iframe_url, size_future)) => { + self.constellation_chan.send(LoadIframeUrlMsg(iframe_url, + pipeline_id, + size_future)); } - None => fail!(~"reader forked but no join port?"), } - } - } - - /// This method will wait until the layout task has completed its current action, join the - /// layout task, and then request a new layout run. It won't wait for the new layout - /// computation to finish. - /// - /// This function fails if there is no root frame. - fn reflow(&mut self, goal: ReflowGoal) { - debug!("script: performing reflow for goal %?", goal); - - // Now, join the layout so that they will see the latest changes we have made. - self.join_layout(); - - // Tell the user that we're performing layout. - self.compositor.set_ready_state(PerformingLayout); - - // Layout will let us know when it's done. - let (join_port, join_chan) = comm::stream(); - self.layout_join_port = Some(join_port); - - match self.root_frame { - None => fail!(~"Tried to relayout with no root frame!"), - Some(ref root_frame) => { - // Send new document and relevant styles to layout. - let reflow = ~Reflow { - document_root: root_frame.document.root, - url: copy root_frame.url, - goal: goal, - window_size: self.window_size, - script_chan: self.script_chan.clone(), - script_join_chan: join_chan, - damage: replace(&mut self.damage, None).unwrap(), - }; + }; - self.layout_chan.send(ReflowMsg(reflow)) + let get_stylesheets = |style_port: &Port<Stylesheet>| loop { + match style_port.try_recv() { + None => break, + Some(sheet) => page.layout_chan.send(AddStylesheetMsg(sheet)), + } + }; + + let mut select_ports = (style_port, iframe_port); + loop { + match select_ports.try_select() { + Left(None) => { + get_iframes(select_ports.second_ref()); + break; + } + Left(Some(sheet)) => { + page.layout_chan.send(AddStylesheetMsg(sheet)); + } + Right(Some((iframe_url, size_future))) => { + self.constellation_chan.send(LoadIframeUrlMsg(iframe_url, + pipeline_id, + size_future)); + } + Right(None) => { + get_stylesheets(select_ports.first_ref()); + break; + } } } - debug!("script: layout forked") - } - - /// Reflows the entire document. - /// - /// FIXME: This should basically never be used. - pub fn reflow_all(&mut self, goal: ReflowGoal) { - for self.root_frame.iter().advance |root_frame| { - ScriptTask::damage(&mut self.damage, - root_frame.document.root, - MatchSelectorsDocumentDamage) - } + // Receive the JavaScript scripts. + let js_scripts = js_port.recv(); + debug!("js_scripts: %?", js_scripts); - self.reflow(goal) - } + // Perform the initial reflow. + page.damage = Some(DocumentDamage { + root: root, + level: MatchSelectorsDocumentDamage, + }); + page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor); + page.url = Some((url, false)); - /// Sends the given query to layout. - pub fn query_layout<T: Send>(&mut self, query: LayoutQuery, response_port: Port<Result<T, ()>>) -> Result<T,()> { - self.join_layout(); - self.layout_chan.send(QueryMsg(query)); - response_port.recv() - } + // Define debug functions. + let js_info = page.js_info.get_ref(); + js_info.js_compartment.define_functions(debug_fns); - /// Adds the given damage. - fn damage(damage: &mut Option<DocumentDamage>, - root: AbstractNode<ScriptView>, - level: DocumentDamageLevel) { - match *damage { - None => {} - Some(ref mut damage) => { - // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor. - damage.root = root; - damage.level.add(level); - return - } + // Evaluate every script in the document. + for js_scripts.iter().advance |bytes| { + let _ = js_info.js_context.evaluate_script(js_info.js_compartment.global_obj, + bytes.clone(), + ~"???", + 1); } - - *damage = Some(DocumentDamage { - root: root, - level: level, - }) } /// This is the main entry point for receiving and dispatching DOM events. /// /// TODO: Actually perform DOM event dispatch. - fn handle_event(&mut self, event: Event) { + fn handle_event(&mut self, pipeline_id: PipelineId, event: Event) { + let page = self.page_tree.find(pipeline_id).expect("ScriptTask: received an event + message for a layout channel that is not associated with this script task. This + is a bug.").page; + match event { ResizeEvent(new_width, new_height) => { debug!("script got resize event: %u, %u", new_width, new_height); - self.window_size = Size2D(new_width, new_height); + page.window_size = from_value(Size2D(new_width, new_height)); - for self.root_frame.iter().advance |root_frame| { - ScriptTask::damage(&mut self.damage, - root_frame.document.root, - ReflowDocumentDamage); + if page.frame.is_some() { + page.damage(ReflowDocumentDamage); + page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor) } - if self.root_frame.is_some() { - self.reflow(ReflowForDisplay) - } - self.constellation_chan.send(ResizedWindowBroadcast(self.window_size)); + self.constellation_chan.send(ResizedWindowBroadcast(page.window_size.get().clone())); } // FIXME(pcwalton): This reflows the entire document and is not incremental-y. ReflowEvent => { debug!("script got reflow event"); - for self.root_frame.iter().advance |root_frame| { - ScriptTask::damage(&mut self.damage, - root_frame.document.root, - MatchSelectorsDocumentDamage); - } - - if self.root_frame.is_some() { - self.reflow(ReflowForDisplay) + if page.frame.is_some() { + page.damage(MatchSelectorsDocumentDamage); + page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor) } } - ClickEvent(_button, point) => { + ClickEvent(page_button, point) => { debug!("ClickEvent: clicked at %?", point); - let root = match self.root_frame { - Some(ref frame) => frame.document.root, - None => fail!("root frame is None") - }; + + let root = page.frame.expect("root frame is None").document.root; let (port, chan) = comm::stream(); - match self.query_layout(HitTestQuery(root, point, chan), port) { + match page.query_layout(HitTestQuery(root, point, chan), port) { Ok(node) => match node { HitTestResponse(node) => { debug!("clicked on %s", node.debug_str()); @@ -605,7 +738,7 @@ impl ScriptTask { if node.is_element() { do node.with_imm_element |element| { if "a" == element.tag_name { - self.load_url_from_element(element) + self.load_url_from_element(page, element) } } } @@ -621,16 +754,16 @@ impl ScriptTask { } } - priv fn load_url_from_element(&self, element: &Element) { + priv fn load_url_from_element(&self, page: @mut Page, element: &Element) { // if the node's element is "a," load url from href attr let href = element.get_attr("href"); for href.iter().advance |href| { debug!("clicked on link to %s", *href); - let current_url = do self.root_frame.map |frame| { - frame.url.clone() + let current_url = do page.url.map |&(ref url, _)| { + url.clone() }; let url = make_url(href.to_owned(), current_url); - self.constellation_chan.send(LoadUrlMsg(url)); + self.constellation_chan.send(LoadUrlMsg(page.id, url, from_value(page.window_size.get()))); } } } diff --git a/src/components/util/time.rs b/src/components/util/time.rs index 34de480b99c..db3963da08b 100644 --- a/src/components/util/time.rs +++ b/src/components/util/time.rs @@ -7,6 +7,7 @@ use extra::time::precise_time_ns; use std::cell::Cell; use std::comm::{Port, SharedChan}; use extra::sort::tim_sort; +use std::iterator::AdditiveIterator; // front-end representation of the profiler used to communicate with the profiler #[deriving(Clone)] @@ -162,7 +163,7 @@ impl Profiler { let data_len = data.len(); if data_len > 0 { let (mean, median, &min, &max) = - (data.iter().fold(0f, |a, b| a + *b) / (data_len as float), + (data.iter().transform(|&x|x).sum() / (data_len as float), data[data_len / 2], data.iter().min().unwrap(), data.iter().max().unwrap()); |