diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 6ddbb19df64..d91465ae583 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -8,7 +8,7 @@ use azure::{AzFloat, AzGLContext}; 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::compositor_msg::{LayerBufferSet, Epoch}; use servo_msg::constellation_msg::PipelineId; use font_context::FontContext; use geom::matrix2d::Matrix2D; @@ -32,7 +32,7 @@ pub struct RenderLayer { pub enum Msg { RenderMsg(RenderLayer), - ReRenderMsg(~[BufferRequest], f32, PipelineId), + ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch), PaintPermissionGranted, PaintPermissionRevoked, ExitMsg(Chan<()>), @@ -89,6 +89,8 @@ struct RenderTask { paint_permission: bool, /// Cached copy of last layers rendered last_paint_msg: Option<(arc::Arc, Size2D)>, + /// A counter for epoch messages + epoch: Epoch, } impl RenderTask { @@ -123,6 +125,7 @@ impl RenderTask { paint_permission: false, last_paint_msg: None, + epoch: Epoch(0), }; render_task.start(); @@ -136,18 +139,24 @@ impl RenderTask { match self.port.recv() { RenderMsg(render_layer) => { if self.paint_permission { - self.compositor.new_layer(self.id, render_layer.size); + self.epoch.next(); + self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch); } self.render_layer = Some(render_layer); } - ReRenderMsg(tiles, scale, id) => { - self.render(tiles, scale, id); + ReRenderMsg(tiles, scale, id, epoch) => { + if self.epoch == epoch { + self.render(tiles, scale, id); + } else { + debug!("renderer epoch mismatch: %? != %?", self.epoch, epoch); + } } PaintPermissionGranted => { self.paint_permission = true; match self.render_layer { Some(ref render_layer) => { - self.compositor.new_layer(self.id, render_layer.size); + self.epoch.next(); + self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch); } None => {} } @@ -235,7 +244,7 @@ impl RenderTask { debug!("render_task: returning surface"); if self.paint_permission { - self.compositor.paint(id, layer_buffer_set.clone()); + self.compositor.paint(id, layer_buffer_set.clone(), self.epoch); } debug!("caching paint msg"); self.last_paint_msg = Some((layer_buffer_set, render_layer.size)); diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index fce05548531..5d11c5d2860 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -7,7 +7,7 @@ use geom::size::Size2D; use geom::rect::Rect; use geom::matrix::identity; use gfx::render_task::ReRenderMsg; -use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet}; +use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch}; use servo_msg::constellation_msg::PipelineId; use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent}; use script::script_task::SendEventMsg; @@ -15,6 +15,7 @@ use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEve use compositing::quadtree::{Quadtree, Invalid}; use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager}; use pipeline::Pipeline; +use constellation::{SendableChildFrameTree, SendableFrameTree}; /// The CompositorLayer represents an element on a page that has a unique scroll /// or animation behavior. This can include absolute positioned elements, iframes, etc. @@ -42,6 +43,9 @@ pub struct CompositorLayer { /// When set to true, this layer is ignored by its parents. This is useful for /// soft deletion or when waiting on a page size. hidden: bool, + /// A monotonically increasing counter that keeps track of the current epoch. + /// add_buffer() calls that don't match the current epoch will be ignored. + epoch: Epoch, } /// Helper struct for keeping CompositorLayer children organized. @@ -61,7 +65,8 @@ enum MaybeQuadtree { } impl CompositorLayer { - /// Creates a new CompositorLayer without a page size that is initially hidden. + /// Creates a new CompositorLayer with an optional page size. If no page size is given, + /// the layer is initially hidden and initialized without a quadtree. pub fn new(pipeline: Pipeline, page_size: Option>, tile_size: uint, max_mem: Option) -> CompositorLayer { CompositorLayer { @@ -78,9 +83,41 @@ impl CompositorLayer { }, root_layer: @mut ContainerLayer(), hidden: true, + epoch: Epoch(0), } } + /// Constructs a CompositorLayer tree from a frame tree. + pub fn from_frame_tree(frame_tree: SendableFrameTree, + tile_size: uint, + max_mem: Option) -> CompositorLayer { + let SendableFrameTree { pipeline, children } = frame_tree; + let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem); + layer.children = (do children.move_iter().map |child| { + let SendableChildFrameTree { frame_tree, rect } = child; + let container = @mut ContainerLayer(); + match rect { + Some(rect) => { + container.scissor = Some(rect); + container.common.transform = identity().translate(rect.origin.x, + rect.origin.y, + 0f32); + + } + None => {} + } + + let child_layer = ~CompositorLayer::from_frame_tree(frame_tree, tile_size, max_mem); + container.add_child(ContainerLayerKind(child_layer.root_layer)); + + CompositorLayerChild { + child: child_layer, + container: container, + } + }).collect(); + layer + } + // Move the layer by as relative specified amount in page coordinates. Does not change // the position of the layer relative to its parent. This also takes in a cursor position // to see if the mouse is over child layers first. If a layer successfully scrolled, returns @@ -166,13 +203,14 @@ impl CompositorLayer { let mut redisplay: bool; { // block here to prevent double mutable borrow of self let quadtree = match self.quadtree { - NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"), + NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?, + no quadtree initialized", self.pipeline.id), Tree(ref mut quadtree) => quadtree, }; let (request, r) = quadtree.get_tile_rects_page(rect, scale); redisplay = r; // workaround to make redisplay visible outside block if !request.is_empty() { - self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone())); + self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone(), self.epoch)); } } if redisplay { @@ -204,9 +242,10 @@ impl CompositorLayer { // Move the sublayer to an absolute position in page coordinates relative to its parent, // and clip the layer to the specified size in page coordinates. + // If the layer is hidden and has a defined page size, unhide it. // This method returns false if the specified layer is not found. pub fn set_clipping_rect(&mut self, pipeline_id: PipelineId, new_rect: Rect) -> bool { - for child_node in self.children.iter() { + for child_node in self.children.mut_iter() { if pipeline_id != child_node.child.pipeline.id { loop; } @@ -215,6 +254,10 @@ impl CompositorLayer { new_rect.origin.y, 0.0)); con.scissor = Some(new_rect); + // If possible, unhide child + if child_node.child.hidden && child_node.child.page_size.is_some() { + child_node.child.hidden = false; + } return true; } @@ -223,10 +266,12 @@ impl CompositorLayer { } - // Called when the layer changes size (NOT as a result of a zoom event). + // Set the layer's page size. This signals that the renderer is ready for BufferRequests. + // If the layer is hidden and has a defined clipping rect, unhide it. // This method returns false if the specified layer is not found. - pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D, window_size: Size2D) -> bool { + pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D, window_size: Size2D, epoch: Epoch) -> bool { if self.pipeline.id == pipeline_id { + self.epoch = epoch; self.page_size = Some(new_size); // TODO: might get buffers back here match self.quadtree { @@ -236,27 +281,48 @@ impl CompositorLayer { tile_size, max_mem)), } - // Call scroll for bounds checking of the page shrunk. Use (-1, -1) as the cursor position + // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position // to make sure the scroll isn't propagated downwards. self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size); + self.hidden = false; + return true; + } + self.resize_helper(pipeline_id, new_size, epoch) + } + + // A helper method to resize sublayers. + fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D, epoch: Epoch) -> bool { + for child_node in self.children.mut_iter() { + if pipeline_id != child_node.child.pipeline.id { + loop; + } + let child = &mut child_node.child; + child.epoch = epoch; + child.page_size = Some(new_size); + // TODO: might get buffers back here + match child.quadtree { + Tree(ref mut quadtree) => quadtree.resize(new_size.width as uint, new_size.height as uint), + NoTree(tile_size, max_mem) => child.quadtree = Tree(Quadtree::new(new_size.width as uint, + new_size.height as uint, + tile_size, + max_mem)), + } + match child_node.container.scissor { + Some(scissor) => { + // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position + // to make sure the scroll isn't propagated downwards. + child.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), scissor.size); + child.hidden = false; + } + None => {} // Nothing to do + } return true; } // ID does not match ours, so recurse on descendents (including hidden children) - let transform = |x: &mut CompositorLayerChild| -> bool { - match x.container.scissor { - Some(scissor) => { - x.child.resize(pipeline_id, new_size, scissor.size) - } - None => { - fail!("CompositorLayer: Child layer not clipped"); - } - } - }; - - self.children.mut_iter().any(transform) + self.children.mut_iter().map(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size, epoch)) } - + // Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers // are not rebuilt directly from this method. pub fn build_layer_tree(&mut self) { @@ -287,10 +353,11 @@ impl CompositorLayer { } }; } - + // Add new tiles. let quadtree = match self.quadtree { - NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"), + NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?, + no quadtree initialized", self.pipeline.id), Tree(ref mut quadtree) => quadtree, }; @@ -331,11 +398,18 @@ impl CompositorLayer { } // Add LayerBuffers to the specified layer. Returns false if the layer is not found. - pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet) -> bool { + // If the epoch of the message does not match the layer's epoch, the message is ignored. + pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet, epoch: Epoch) -> bool { if self.pipeline.id == pipeline_id { + if self.epoch != epoch { + debug!("compositor epoch mismatch: %? != %?, id: %?", self.epoch, epoch, self.pipeline.id); + // TODO: send buffers back + return true; + } { // block here to prevent double mutable borrow of self let quadtree = match self.quadtree { - NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"), + NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?, + no quadtree initialized", self.pipeline.id), Tree(ref mut quadtree) => quadtree, }; @@ -349,7 +423,7 @@ impl CompositorLayer { return true; } // ID does not match ours, so recurse on descendents (including hidden children). - self.children.mut_iter().map(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers)) + self.children.mut_iter().map(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers, epoch)) } // Deletes a specified sublayer, including hidden children. Returns false if the layer is not found. diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 1931037b793..a480ee76b39 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -3,8 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use platform::{Application, Window}; -use script::dom::event::ResizeEvent; -use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg}; pub use windowing; use windowing::{ApplicationMethods, WindowEvent, WindowMethods}; @@ -13,8 +11,8 @@ use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, Finis use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState}; -use servo_msg::compositor_msg::{ReadyState, ScriptListener}; -use servo_msg::constellation_msg::PipelineId; +use servo_msg::compositor_msg::{ReadyState, ScriptListener, Epoch}; +use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, PipelineId, ResizedWindowMsg, LoadUrlMsg}; use servo_msg::constellation_msg; use gfx::opts::Opts; @@ -40,11 +38,11 @@ use servo_util::{time, url}; use servo_util::time::profile; use servo_util::time::ProfilerChan; +use extra::future::from_value; use extra::time::precise_time_s; use extra::arc; use constellation::SendableFrameTree; -use pipeline::Pipeline; use compositing::compositor_layer::CompositorLayer; mod quadtree; @@ -81,16 +79,26 @@ impl RenderListener for CompositorChan { port.recv() } - fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc) { - self.chan.send(Paint(id, layer_buffer_set)) + fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc, epoch: Epoch) { + self.chan.send(Paint(id, layer_buffer_set, epoch)) } fn new_layer(&self, id: PipelineId, page_size: Size2D) { - self.chan.send(NewLayer(id, page_size)) + let Size2D { width, height } = page_size; + self.chan.send(NewLayer(id, Size2D(width as f32, height as f32))) } - fn resize_layer(&self, id: PipelineId, page_size: Size2D) { - self.chan.send(ResizeLayer(id, page_size)) + fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D, epoch: Epoch) { + let Size2D { width, height } = page_size; + self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32), epoch)) } + fn set_layer_clip_rect(&self, id: PipelineId, new_rect: Rect) { + let new_rect = Rect(Point2D(new_rect.origin.x as f32, + new_rect.origin.y as f32), + Size2D(new_rect.size.width as f32, + new_rect.size.height as f32)); + self.chan.send(SetLayerClipRect(id, new_rect)) + } + fn delete_layer(&self, id: PipelineId) { self.chan.send(DeleteLayer(id)) } @@ -128,24 +136,25 @@ pub enum Msg { /// Requests the compositors GL context. GetGLContext(Chan), - // TODO: Attach epochs to these messages /// Alerts the compositor that there is a new layer to be rendered. - NewLayer(PipelineId, Size2D), - /// Alerts the compositor that the specified layer has changed size. - ResizeLayer(PipelineId, Size2D), + NewLayer(PipelineId, Size2D), + /// Alerts the compositor that the specified layer's page has changed size. + SetLayerPageSize(PipelineId, Size2D, Epoch), + /// Alerts the compositor that the specified layer's clipping rect has changed. + SetLayerClipRect(PipelineId, Rect), /// Alerts the compositor that the specified layer has been deleted. DeleteLayer(PipelineId), /// Invalidate a rect for a given layer InvalidateRect(PipelineId, Rect), /// Requests that the compositor paint the given layer buffer set for the given page size. - Paint(PipelineId, arc::Arc), + Paint(PipelineId, arc::Arc, Epoch), /// 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 - SetIds(SendableFrameTree, Chan<()>), + SetIds(SendableFrameTree, Chan<()>, ConstellationChan), } /// Azure surface wrapping to work with the layers infrastructure. @@ -207,7 +216,7 @@ impl CompositorTask { let root_layer = @mut ContainerLayer(); let window_size = window.size(); let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity()); - let mut window_size = Size2D(window_size.width as int, window_size.height as int); + let mut window_size = Size2D(window_size.width as uint, window_size.height as uint); let mut done = false; let mut recomposite = false; @@ -216,21 +225,21 @@ impl CompositorTask { let mut zoom_action = false; let mut zoom_time = 0f; - // Channel to the outermost frame's pipeline. - // FIXME: Events are only forwarded to this pipeline, but they should be - // routed to the appropriate pipeline via the constellation. - let mut pipeline: Option = None; - // The root CompositorLayer let mut compositor_layer: Option = None; + let mut constellation_chan: Option = None; // Get BufferRequests from each layer. let ask_for_tiles = || { let window_size_page = Size2D(window_size.width as f32 / world_zoom, window_size.height as f32 / world_zoom); for layer in compositor_layer.mut_iter() { - recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page), - world_zoom) || recomposite; + if !layer.hidden { + recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page), + world_zoom) || recomposite; + } else { + debug!("Compositor: root layer is hidden!"); + } } }; @@ -243,9 +252,22 @@ impl CompositorTask { ChangeReadyState(ready_state) => window.set_ready_state(ready_state), ChangeRenderState(render_state) => window.set_render_state(render_state), - SetIds(frame_tree, response_chan) => { - pipeline = Some(frame_tree.pipeline); + SetIds(frame_tree, response_chan, new_constellation_chan) => { response_chan.send(()); + + // This assumes there is at most one child, which should be the case. + match root_layer.first_child { + Some(old_layer) => root_layer.remove_child(old_layer), + None => {} + } + + let layer = CompositorLayer::from_frame_tree(frame_tree, + self.opts.tile_size, + Some(10000000u)); + root_layer.add_child(ContainerLayerKind(layer.root_layer)); + compositor_layer = Some(layer); + + constellation_chan = Some(new_constellation_chan); } GetSize(chan) => { @@ -259,12 +281,12 @@ impl CompositorTask { // FIXME: This should create an additional layer instead of replacing the current one. // Once ResizeLayer messages are set up, we can switch to the new functionality. - let p = match pipeline { - Some(ref pipeline) => pipeline, + let p = match compositor_layer { + Some(ref compositor_layer) => compositor_layer.pipeline.clone(), None => fail!("Compositor: Received new layer without initialized pipeline"), }; let page_size = Size2D(new_size.width as f32, new_size.height as f32); - let new_layer = CompositorLayer::new(p.clone(), Some(page_size), + let new_layer = CompositorLayer::new(p, Some(page_size), self.opts.tile_size, Some(10000000u)); let current_child = root_layer.first_child; @@ -279,14 +301,22 @@ impl CompositorTask { ask_for_tiles(); } - ResizeLayer(id, new_size) => { + SetLayerPageSize(id, new_size, epoch) => { match compositor_layer { Some(ref mut layer) => { let page_window = Size2D(window_size.width as f32 / world_zoom, window_size.height as f32 / world_zoom); - assert!(layer.resize(id, Size2D(new_size.width as f32, - new_size.height as f32), - page_window)); + assert!(layer.resize(id, new_size, page_window, epoch)); + ask_for_tiles(); + } + None => {} + } + } + + SetLayerClipRect(id, new_rect) => { + match compositor_layer { + Some(ref mut layer) => { + assert!(layer.set_clipping_rect(id, new_rect)); ask_for_tiles(); } None => {} @@ -303,12 +333,12 @@ impl CompositorTask { } } - Paint(id, new_layer_buffer_set) => { + Paint(id, new_layer_buffer_set, epoch) => { debug!("osmain: received new frame"); match compositor_layer { Some(ref mut layer) => { - assert!(layer.add_buffers(id, new_layer_buffer_set.get())); + assert!(layer.add_buffers(id, new_layer_buffer_set.get(), epoch)); recomposite = true; } None => { @@ -340,12 +370,12 @@ impl CompositorTask { IdleWindowEvent => {} ResizeWindowEvent(width, height) => { - let new_size = Size2D(width as int, height as int); + let new_size = Size2D(width, height); if window_size != new_size { debug!("osmain: window resized to %ux%u", width, height); window_size = new_size; - match pipeline { - Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), ResizeEvent(width, height))), + match constellation_chan { + Some(ref chan) => chan.send(ResizedWindowMsg(new_size)), None => error!("Compositor: Recieved resize event without initialized layout chan"), } } else { @@ -355,8 +385,14 @@ impl CompositorTask { LoadUrlWindowEvent(url_string) => { debug!("osmain: loading URL `%s`", url_string); - match pipeline { - Some(ref pipeline) => pipeline.script_chan.send(LoadMsg(pipeline.id.clone(), url::make_url(url_string.to_str(), None))), + let root_pipeline_id = match compositor_layer { + Some(ref layer) => layer.pipeline.id.clone(), + None => fail!("Compositor: Received LoadUrlWindowEvent without initialized compositor layers"), + }; + match constellation_chan { + Some(ref chan) => chan.send(LoadUrlMsg(root_pipeline_id, + url::make_url(url_string.to_str(), None), + from_value(window_size))), None => error!("Compositor: Recieved loadurl event without initialized layout chan"), } } @@ -413,8 +449,8 @@ impl CompositorTask { windowing::Forward => constellation_msg::Forward, windowing::Back => constellation_msg::Back, }; - match pipeline { - Some(ref pipeline) => pipeline.script_chan.send(NavigateMsg(direction)), + match constellation_chan { + Some(ref chan) => chan.send(NavigateMsg(direction)), None => error!("Compositor: Recieved navigation event without initialized layout chan"), } } diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs index 0ffc9fa31e7..364f44b5b42 100644 --- a/src/components/main/compositing/quadtree.rs +++ b/src/components/main/compositing/quadtree.rs @@ -211,11 +211,30 @@ impl Quadtree { (ret, redisplay) } - - /// Resize the quadtree. This can add more space, changing the root node, or it can shrink, making - /// an internal node the new root. - /// TODO: return tiles after shrinking + /// Creates a new quadtree at the specified size. This should be called when the window changes size. + /// TODO: return old tiles. pub fn resize(&mut self, width: uint, height: uint) { + // Spaces must be squares and powers of 2, so expand the space until it is + let longer = width.max(&height); + let num_tiles = div_ceil(longer, self.max_tile_size); + let power_of_two = next_power_of_two(num_tiles); + let size = power_of_two * self.max_tile_size; + + self.root = ~QuadtreeNode { + tile: None, + origin: Point2D(0f32, 0f32), + size: size as f32, + quadrants: [None, None, None, None], + status: Normal, + tile_mem: 0, + }; + self.clip_size = Size2D(width, height); + } + + /// Resize the underlying quadtree without removing tiles already in place. + /// Might be useful later on, but resize() should be used for now. + /// TODO: return tiles after shrinking + pub fn bad_resize(&mut self, width: uint, height: uint) { self.clip_size = Size2D(width, height); let longer = width.max(&height); let new_num_tiles = div_ceil(longer, self.max_tile_size); @@ -731,13 +750,13 @@ pub fn test_resize() { let mut q = Quadtree::new(6, 6, 1, None); q.add_tile_pixel(0, 0, 1f32, T{a: 0}); q.add_tile_pixel(5, 5, 1f32, T{a: 1}); - q.resize(8, 1); + q.bad_resize(8, 1); assert!(q.root.size == 8.0); - q.resize(18, 1); + q.bad_resize(18, 1); assert!(q.root.size == 32.0); - q.resize(8, 1); + q.bad_resize(8, 1); assert!(q.root.size == 8.0); - q.resize(3, 1); + q.bad_resize(3, 1); assert!(q.root.size == 4.0); assert!(q.get_all_tiles().len() == 1); } diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 32da23206b0..1cadfe16a43 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -2,28 +2,31 @@ * 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, SetIds}; +use compositing::{CompositorChan, SetIds, SetLayerClipRect}; +use script::dom::event::ResizeEvent; use std::cell::Cell; use std::comm; use std::comm::Port; use std::task; use geom::size::Size2D; +use geom::rect::Rect; use gfx::opts::Opts; use pipeline::Pipeline; -use servo_msg::constellation_msg::{ConstellationChan, ExitMsg}; +use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FrameRectMsg}; 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::{Msg, NavigateMsg, NavigationType}; +use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowMsg, SubpageId}; use servo_msg::constellation_msg; -use script::script_task::{ResizeInactiveMsg, ExecuteMsg}; +use script::script_task::{SendEventMsg, ResizeInactiveMsg, ExecuteMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; use servo_net::resource_task; use servo_util::time::ProfilerChan; -use std::hashmap::HashMap; +use std::hashmap::{HashMap, HashSet}; use std::util::replace; -use extra::future::from_value; +use extra::url::Url; +use extra::future::{Future, from_value}; /// Maintains the pipelines and navigation context and grants permission to composite pub struct Constellation { @@ -36,6 +39,7 @@ pub struct Constellation { navigation_context: NavigationContext, priv next_pipeline_id: PipelineId, pending_frames: ~[FrameChange], + pending_sizes: HashMap<(PipelineId, SubpageId), Rect>, profiler_chan: ProfilerChan, opts: Opts, } @@ -44,13 +48,14 @@ pub struct Constellation { struct FrameTree { pipeline: @mut Pipeline, parent: Option<@mut Pipeline>, - children: ~[@mut FrameTree], + children: ~[ChildFrameTree], } + // Need to clone the FrameTrees, but _not_ the Pipelines impl Clone for FrameTree { fn clone(&self) -> FrameTree { - let mut children = do self.children.iter().map |&frame_tree| { - @mut (*frame_tree).clone() + let mut children = do self.children.iter().map |child_frame_tree| { + child_frame_tree.clone() }; FrameTree { pipeline: self.pipeline, @@ -60,54 +65,67 @@ impl Clone for FrameTree { } } +struct ChildFrameTree { + frame_tree: @mut FrameTree, + /// Clipping rect representing the size and position, in page coordinates, of the visible + /// region of the child frame relative to the parent. + rect: Option>, +} + +impl Clone for ChildFrameTree { + fn clone(&self) -> ChildFrameTree { + ChildFrameTree { + frame_tree: @mut (*self.frame_tree).clone(), + rect: self.rect.clone(), + } + } +} + pub struct SendableFrameTree { pipeline: Pipeline, - children: ~[SendableFrameTree], + children: ~[SendableChildFrameTree], +} + +pub struct SendableChildFrameTree { + frame_tree: SendableFrameTree, + rect: Option>, } impl SendableFrameTree { fn contains(&self, id: PipelineId) -> bool { self.pipeline.id == id || - do self.children.iter().any |frame_tree| { + do self.children.iter().any |&SendableChildFrameTree { frame_tree: ref 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) + fn contains(@mut self, id: PipelineId) -> bool { + do self.iter().any |frame_tree| { + id == frame_tree.pipeline.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); } - let mut finder = do self.children.iter().filter_map |frame_tree| { - frame_tree.find_mut(id) - }; - finder.next() + do self.iter().find |frame_tree| { + id == frame_tree.pipeline.id + } } /// Replaces a node of the frame tree in place. Returns the node that was removed or the original node /// if the node to replace could not be found. - fn replace_child(&mut self, id: PipelineId, new_child: @mut FrameTree) -> Either<@mut FrameTree, @mut FrameTree> { - let new_child_cell = Cell::new(new_child); - for child in self.children.mut_iter() { - let new_child = new_child_cell.take(); - if child.pipeline.id == id { - new_child.parent = child.parent; - return Left(replace(child, new_child)); - } - let replaced = child.replace_child(id, new_child); - if replaced.is_left() { - return replaced; + fn replace_child(@mut self, id: PipelineId, new_child: @mut FrameTree) -> Either<@mut FrameTree, @mut FrameTree> { + for frame_tree in self.iter() { + let mut child = frame_tree.children.mut_iter() + .find(|child| child.frame_tree.pipeline.id == id); + for child in child.mut_iter() { + new_child.parent = child.frame_tree.parent; + return Left(replace(&mut child.frame_tree, new_child)); } - new_child_cell.put_back(replaced.unwrap_right()); } - Right(new_child_cell.take()) + Right(new_child) } fn to_sendable(&self) -> SendableFrameTree { @@ -125,6 +143,18 @@ impl FrameTree { } } +impl ChildFrameTree { + fn to_sendable(&self) -> SendableChildFrameTree { + SendableChildFrameTree { + frame_tree: self.frame_tree.to_sendable(), + rect: self.rect, + } + } +} + +/// An iterator over a frame tree, returning nodes in depth-first order. +/// Note that this iterator should _not_ be used to mutate nodes _during_ +/// iteration. Mutating nodes once the iterator is out of scope is OK. pub struct FrameTreeIterator { priv stack: ~[@mut FrameTree], } @@ -133,7 +163,9 @@ impl Iterator<@mut FrameTree> for FrameTreeIterator { fn next(&mut self) -> Option<@mut FrameTree> { if !self.stack.is_empty() { let next = self.stack.pop(); - self.stack.push_all(next.children); + for &ChildFrameTree { frame_tree, _ } in next.children.rev_iter() { + self.stack.push(frame_tree); + } Some(next) } else { None @@ -145,6 +177,7 @@ impl Iterator<@mut FrameTree> for FrameTreeIterator { struct FrameChange { before: Option, after: @mut FrameTree, + navigation_type: NavigationType, } /// Stores the Id's of the pipelines previous and next in the browser's history @@ -248,6 +281,7 @@ impl Constellation { navigation_context: NavigationContext::new(), next_pipeline_id: PipelineId(0), pending_frames: ~[], + pending_sizes: HashMap::new(), profiler_chan: profiler_chan.take(), opts: opts.take(), }; @@ -281,330 +315,433 @@ impl Constellation { /// Handles loading pages, navigation, and granting access to the compositor fn handle_request(&mut self, request: Msg) -> bool { match request { - ExitMsg(sender) => { - for (_id, ref pipeline) in self.pipelines.iter() { - pipeline.exit(); - } - self.image_cache_task.exit(); - self.resource_task.send(resource_task::Exit); - - sender.send(()); - return false + self.handle_exit(sender); + 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(), - None, - self.chan.clone(), - self.compositor_chan.clone(), - self.image_cache_task.clone(), - self.resource_task.clone(), - self.profiler_chan.clone(), - self.opts.clone(), - { - 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, 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); + self.handle_init_load(url); + } + // A layout assigned a size and position to a subframe. This needs to be reflected by all + // frame trees in the navigation context containing the subframe. + FrameRectMsg(pipeline_id, subpage_id, rect) => { + self.handle_frame_rect_msg(pipeline_id, subpage_id, rect); } - LoadIframeUrlMsg(url, source_pipeline_id, subpage_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 trees matching the pipeline id, - // and add the new pipeline to their sub frames. - let frame_trees: ~[@mut FrameTree] = { - let matching_navi_frames = self.navigation_context.find_all(source_pipeline_id); - let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| { - frame_change.after.find_mut(source_pipeline_id) - }; - matching_navi_frames.move_iter().chain(matching_pending_frames).collect() - }; - - if frame_trees.is_empty() { - fail!("Constellation: source pipeline id of LoadIframeUrlMsg is not in - navigation context, nor is it in a pending frame. This should be - impossible."); - } - - let next_pipeline_id = self.get_next_pipeline_id(); - - // 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."); - - // FIXME(tkuehn): Need to follow the standardized spec for checking same-origin - 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, - Some(subpage_id), - self.chan.clone(), - self.compositor_chan.clone(), - self.image_cache_task.clone(), - self.profiler_chan.clone(), - self.opts.clone(), - source_pipeline, - size_future) - } else { - // Create a new script task if not same-origin url's - Pipeline::create(next_pipeline_id, - Some(subpage_id), - self.chan.clone(), - self.compositor_chan.clone(), - self.image_cache_task.clone(), - self.resource_task.clone(), - self.profiler_chan.clone(), - self.opts.clone(), - size_future) - }; - - if url.path.ends_with(".js") { - pipeline.execute(url); - } else { - pipeline.load(url, None); - } - for frame_tree in frame_trees.iter() { - frame_tree.children.push(@mut FrameTree { - pipeline: pipeline, - parent: Some(source_pipeline), - children: ~[], - }); - } - self.pipelines.insert(pipeline.id, pipeline); + self.handle_load_iframe_url_msg(url, source_pipeline_id, subpage_id, size_future); } - // 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()); - // Make sure no pending page would be overridden. - 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."); - - for frame_change in self.pending_frames.iter() { - 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) || source_frame.contains(old_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 parent = source_frame.parent.clone(); - let subpage_id = source_frame.pipeline.subpage_id.clone(); - let next_pipeline_id = self.get_next_pipeline_id(); - - let pipeline = @mut Pipeline::create(next_pipeline_id, - subpage_id, - self.chan.clone(), - self.compositor_chan.clone(), - self.image_cache_task.clone(), - self.resource_task.clone(), - self.profiler_chan.clone(), - self.opts.clone(), - size_future); - - if url.path.ends_with(".js") { - pipeline.script_chan.send(ExecuteMsg(pipeline.id, url)); - } else { - pipeline.load(url, Some(constellation_msg::Load)); - - self.pending_frames.push(FrameChange{ - before: Some(source_id), - after: @mut FrameTree { - pipeline: pipeline, - parent: parent, - children: ~[], - }, - }); - } - self.pipelines.insert(pipeline.id, pipeline); + self.handle_load_url_msg(source_id, url, size_future); } - // Handle a forward or back request NavigateMsg(direction) => { - debug!("received message to navigate %?", direction); - - // 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 destination_frame = match direction { - constellation_msg::Forward => { - if self.navigation_context.next.is_empty() { - debug!("no next page to navigate to"); - return true - } else { - let old = self.current_frame().get_ref(); - for frame in old.iter() { - frame.pipeline.revoke_paint_permission(); - } - } - self.navigation_context.forward() - } - constellation_msg::Back => { - if self.navigation_context.previous.is_empty() { - debug!("no previous page to navigate to"); - return true - } else { - let old = self.current_frame().get_ref(); - for frame in old.iter() { - frame.pipeline.revoke_paint_permission(); - } - } - self.navigation_context.back() - } - }; - - for frame in destination_frame.iter() { - let pipeline = &frame.pipeline; - pipeline.reload(Some(constellation_msg::Navigate)); - } - self.grant_paint_permission(destination_frame); - + self.handle_navigate_msg(direction); } - // Notification that rendering has finished and is requesting permission to paint. RendererReadyMsg(pipeline_id) => { - // 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 ¤t_frame in self.current_frame().iter() { - // 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) { - self.set_ids(current_frame); - return true; - } - } - - // 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 in pending_index.iter() { - let frame_change = self.pending_frames.swap_remove(pending_index); - let to_add = frame_change.after; - - // 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) => @mut (*self.current_frame().unwrap()).clone(), - }; - - // If there are frames to revoke permission from, do so now. - match frame_change.before { - Some(revoke_id) => { - let current_frame = self.current_frame().unwrap(); - - 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 frame in to_revoke.iter() { - 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); - } - } - - 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 in parent.iter() { - 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.grant_paint_permission(next_frame_tree); - } + self.handle_renderer_ready_msg(pipeline_id); } - ResizedWindowBroadcast(new_size) => match *self.current_frame() { - Some(ref current_frame) => { - let current_frame_id = current_frame.pipeline.id.clone(); - for frame_tree in self.navigation_context.previous.iter() { - let pipeline = &frame_tree.pipeline; - if current_frame_id != pipeline.id { - pipeline.script_chan.send(ResizeInactiveMsg(new_size)); - } - } - for frame_tree in self.navigation_context.next.iter() { - let pipeline = &frame_tree.pipeline; - if current_frame_id != pipeline.id { - pipeline.script_chan.send(ResizeInactiveMsg(new_size)); - } - } - } - None => { - for frame_tree in self.navigation_context.previous.iter() { - frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size)); - } - for frame_tree in self.navigation_context.next.iter() { - frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size)); - } - } + ResizedWindowMsg(new_size) => { + self.handle_resized_window_msg(new_size); } - } true } + + fn handle_exit(&self, sender: Chan<()>) { + for (_id, ref pipeline) in self.pipelines.iter() { + pipeline.exit(); + } + self.image_cache_task.exit(); + self.resource_task.send(resource_task::Exit); + + sender.send(()); + } + + fn handle_init_load(&mut self, url: Url) { + let pipeline = @mut Pipeline::create(self.get_next_pipeline_id(), + None, + self.chan.clone(), + self.compositor_chan.clone(), + self.image_cache_task.clone(), + self.resource_task.clone(), + self.profiler_chan.clone(), + self.opts.clone(), + { + 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); + + self.pending_frames.push(FrameChange{ + before: None, + after: @mut FrameTree { + pipeline: pipeline, + parent: None, + children: ~[], + }, + navigation_type: constellation_msg::Load, + }); + } + self.pipelines.insert(pipeline.id, pipeline); + } + fn handle_frame_rect_msg(&mut self, pipeline_id: PipelineId, subpage_id: SubpageId, rect: Rect) { + debug!("Received frame rect %? from %?, %?", rect, pipeline_id, subpage_id); + let mut already_sent = HashSet::new(); + + // If the subframe is in the current frame tree, the compositor needs the new size + for current_frame in self.current_frame().iter() { + debug!("Constellation: Sending size for frame in current frame tree."); + let source_frame = current_frame.find_mut(pipeline_id); + for source_frame in source_frame.iter() { + for child_frame_tree in source_frame.children.mut_iter() { + let pipeline = &child_frame_tree.frame_tree.pipeline; + if pipeline.subpage_id.expect("Constellation: child frame does not have a + subpage id. This should not be possible.") == subpage_id { + child_frame_tree.rect = Some(rect.clone()); + let Rect { size: Size2D { width, height }, _ } = rect; + pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), + ResizeEvent(width as uint, + height as uint))); + self.compositor_chan.send(SetLayerClipRect(pipeline.id, rect)); + already_sent.insert(pipeline.id.clone()); + break; + } + } + } + } + // Traverse the navigation context and pending frames and tell each associated pipeline to resize. + let frame_trees: ~[@mut FrameTree] = { + let matching_navi_frames = self.navigation_context.find_all(pipeline_id); + let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| { + frame_change.after.find_mut(pipeline_id) + }; + matching_navi_frames.move_iter().chain(matching_pending_frames).collect() + }; + for frame_tree in frame_trees.iter() { + for child_frame_tree in frame_tree.children.mut_iter() { + let pipeline = &child_frame_tree.frame_tree.pipeline; + if pipeline.subpage_id.expect("Constellation: child frame does not have a + subpage id. This should not be possible.") == subpage_id { + child_frame_tree.rect = Some(rect.clone()); + if !already_sent.contains(&pipeline.id) { + let Size2D { width, height } = rect.size; + pipeline.script_chan.send(ResizeInactiveMsg(pipeline.id.clone(), + Size2D(width as uint, height as uint))); + already_sent.insert(pipeline.id.clone()); + } + break; + } + } + } + + // At this point, if no pipelines were sent a resize msg, then this subpage id + // should be added to pending sizes + if already_sent.len() == 0 { + self.pending_sizes.insert((pipeline_id, subpage_id), rect); + } + } + + fn handle_load_iframe_url_msg(&mut self, + url: Url, + source_pipeline_id: PipelineId, + subpage_id: SubpageId, + size_future: 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 trees matching the pipeline id, + // and add the new pipeline to their sub frames. + let frame_trees: ~[@mut FrameTree] = { + let matching_navi_frames = self.navigation_context.find_all(source_pipeline_id); + let matching_pending_frames = do self.pending_frames.iter().filter_map |frame_change| { + frame_change.after.find_mut(source_pipeline_id) + }; + matching_navi_frames.move_iter().chain(matching_pending_frames).collect() + }; + + if frame_trees.is_empty() { + fail!("Constellation: source pipeline id of LoadIframeUrlMsg is not in + navigation context, nor is it in a pending frame. This should be + impossible."); + } + + let next_pipeline_id = self.get_next_pipeline_id(); + + // 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."); + + // FIXME(tkuehn): Need to follow the standardized spec for checking same-origin + let pipeline = @mut if (source_url.host == url.host && + source_url.port == url.port) { + debug!("Constellation: loading same-origin iframe at %?", url); + // Reuse the script task if same-origin url's + Pipeline::with_script(next_pipeline_id, + Some(subpage_id), + self.chan.clone(), + self.compositor_chan.clone(), + self.image_cache_task.clone(), + self.profiler_chan.clone(), + self.opts.clone(), + source_pipeline, + size_future) + } else { + debug!("Constellation: loading cross-origin iframe at %?", url); + // Create a new script task if not same-origin url's + Pipeline::create(next_pipeline_id, + Some(subpage_id), + self.chan.clone(), + self.compositor_chan.clone(), + self.image_cache_task.clone(), + self.resource_task.clone(), + self.profiler_chan.clone(), + self.opts.clone(), + size_future) + }; + + if url.path.ends_with(".js") { + pipeline.execute(url); + } else { + debug!("Constellation: sending load msg to %?", pipeline); + pipeline.load(url); + } + let rect = self.pending_sizes.pop(&(source_pipeline_id, subpage_id)); + for frame_tree in frame_trees.iter() { + frame_tree.children.push(ChildFrameTree { + frame_tree: @mut FrameTree { + pipeline: pipeline, + parent: Some(source_pipeline), + children: ~[], + }, + rect: rect, + }); + } + self.pipelines.insert(pipeline.id, pipeline); + } + + fn handle_load_url_msg(&mut self, source_id: PipelineId, url: Url, size_future: Future>) { + debug!("Constellation: received message to load %s", url.to_str()); + // Make sure no pending page would be overridden. + 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."); + + for frame_change in self.pending_frames.iter() { + 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) || source_frame.contains(old_id) { + // id that sent load msg is being changed already; abort + return; + } + } + // 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 parent = source_frame.parent.clone(); + let subpage_id = source_frame.pipeline.subpage_id.clone(); + let next_pipeline_id = self.get_next_pipeline_id(); + + let pipeline = @mut Pipeline::create(next_pipeline_id, + subpage_id, + self.chan.clone(), + self.compositor_chan.clone(), + self.image_cache_task.clone(), + self.resource_task.clone(), + self.profiler_chan.clone(), + self.opts.clone(), + size_future); + + if url.path.ends_with(".js") { + pipeline.script_chan.send(ExecuteMsg(pipeline.id, url)); + } else { + pipeline.load(url); + + self.pending_frames.push(FrameChange{ + before: Some(source_id), + after: @mut FrameTree { + pipeline: pipeline, + parent: parent, + children: ~[], + }, + navigation_type: constellation_msg::Load, + }); + } + self.pipelines.insert(pipeline.id, pipeline); + } + + fn handle_navigate_msg(&mut self, direction: constellation_msg::NavigationDirection) { + debug!("received message to navigate %?", direction); + + // 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 destination_frame = match direction { + constellation_msg::Forward => { + if self.navigation_context.next.is_empty() { + debug!("no next page to navigate to"); + return; + } else { + let old = self.current_frame().get_ref(); + for frame in old.iter() { + frame.pipeline.revoke_paint_permission(); + } + } + self.navigation_context.forward() + } + constellation_msg::Back => { + if self.navigation_context.previous.is_empty() { + debug!("no previous page to navigate to"); + return; + } else { + let old = self.current_frame().get_ref(); + for frame in old.iter() { + frame.pipeline.revoke_paint_permission(); + } + } + self.navigation_context.back() + } + }; + + for frame in destination_frame.iter() { + let pipeline = &frame.pipeline; + pipeline.reload(); + } + self.grant_paint_permission(destination_frame, constellation_msg::Navigate); + + } + + fn handle_renderer_ready_msg(&mut self, pipeline_id: PipelineId) { + debug!("Renderer %? ready to send paint msg", pipeline_id); + // 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 ¤t_frame in self.current_frame().iter() { + // 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 frame in current_frame.iter() { + frame.pipeline.grant_paint_permission(); + } + return; + } + } + + // 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 in pending_index.iter() { + let frame_change = self.pending_frames.swap_remove(pending_index); + let to_add = frame_change.after; + + // 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) => @mut (*self.current_frame().unwrap()).clone(), + }; + + // If there are frames to revoke permission from, do so now. + match frame_change.before { + Some(revoke_id) => { + debug!("Constellation: revoking permission from %?", revoke_id); + let current_frame = self.current_frame().unwrap(); + + 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 frame in to_revoke.iter() { + frame.pipeline.revoke_paint_permission(); + } + + // If to_add is not the root frame, then replace revoked_frame with it. + // This conveniently keeps scissor rect size intact. + debug!("Constellation: replacing %? with %? in %?", revoke_id, to_add, next_frame_tree); + if to_add.parent.is_some() { + let replaced = next_frame_tree.replace_child(revoke_id, to_add); + debug!("Replaced child: %?", replaced); + } + debug!("Constellation: frame tree after replacing: %?", 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 in parent.iter() { + let to_add = to_add.take(); + let subpage_id = to_add.pipeline.subpage_id.expect("Constellation: + Child frame's subpage id is None. This should be impossible."); + let rect = self.pending_sizes.pop(&(parent.id, subpage_id)); + 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(ChildFrameTree { + frame_tree: to_add, + rect: rect, + }); + } + } + } + self.grant_paint_permission(next_frame_tree, frame_change.navigation_type); + } + } + + fn handle_resized_window_msg(&mut self, new_size: Size2D) { + let mut already_seen = HashSet::new(); + for &@FrameTree { pipeline: ref pipeline, _ } in self.current_frame().iter() { + let Size2D { width, height } = new_size; + pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), + ResizeEvent(width, height))); + already_seen.insert(pipeline.id.clone()); + } + for frame_tree in self.navigation_context.previous.iter() + .chain(self.navigation_context.next.iter()) { + let pipeline = &frame_tree.pipeline; + if !already_seen.contains(&pipeline.id) { + pipeline.script_chan.send(ResizeInactiveMsg(pipeline.id.clone(), new_size)); + already_seen.insert(pipeline.id.clone()); + } + } + } + // 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) { + fn grant_paint_permission(&mut self, frame_tree: @mut FrameTree, navigation_type: NavigationType) { // Give permission to paint to the new frame and all child frames self.set_ids(frame_tree); // Don't call navigation_context.load() on a Navigate type (or None, as in the case of // parsed iframes that finish loading) - match frame_tree.pipeline.navigation_type { - Some(constellation_msg::Load) => { + match navigation_type { + constellation_msg::Load => { let evicted = self.navigation_context.load(frame_tree); for frame_tree in evicted.iter() { // exit any pipelines that don't exist outside the evicted frame trees @@ -622,7 +759,7 @@ impl Constellation { fn set_ids(&self, frame_tree: @mut FrameTree) { let (port, chan) = comm::stream(); - self.compositor_chan.send(SetIds(frame_tree.to_sendable(), chan)); + self.compositor_chan.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone())); port.recv(); for frame in frame_tree.iter() { frame.pipeline.grant_paint_permission(); diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index b30794a0343..0379689d5fb 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -14,9 +14,10 @@ use layout::float_context::{FloatContext, Invalid}; use std::cell::Cell; use geom::point::Point2D; +use geom::size::Size2D; use geom::rect::Rect; use gfx::display_list::DisplayList; -use gfx::geometry::Au; +use gfx::geometry::{Au, to_frac_px}; use gfx::geometry; use servo_util::tree::TreeNodeRef; @@ -364,6 +365,27 @@ impl BlockFlowData { list: &Cell>) -> bool { + if self.common.node.is_iframe_element() { + let x = self.common.abs_position.x + do self.box.map_default(Au(0)) |box| { + box.with_model(|model| model.margin.left + model.border.left + model.padding.left) + }; + let y = self.common.abs_position.y + do self.box.map_default(Au(0)) |box| { + box.with_model(|model| model.margin.top + model.border.top + model.padding.top) + }; + let w = self.common.position.size.width - do self.box.map_default(Au(0)) |box| { + box.with_model(|model| model.noncontent_width()) + }; + let h = self.common.position.size.height - do self.box.map_default(Au(0)) |box| { + box.with_model(|model| model.noncontent_height()) + }; + do self.common.node.with_mut_iframe_element |iframe_element| { + iframe_element.size.get_mut_ref().set_rect(Rect(Point2D(to_frac_px(x) as f32, + to_frac_px(y) as f32), + Size2D(to_frac_px(w) as f32, + to_frac_px(h) as f32))); + } + } + let abs_rect = Rect(self.common.abs_position, self.common.position.size); if !abs_rect.intersects(dirty) { return false; diff --git a/src/components/main/layout/float.rs b/src/components/main/layout/float.rs index bfabef57526..8f4ffb8824b 100644 --- a/src/components/main/layout/float.rs +++ b/src/components/main/layout/float.rs @@ -295,6 +295,10 @@ impl FloatFlowData { list: &Cell>) -> bool { + //TODO: implement iframe size messaging + if self.common.node.is_iframe_element() { + error!("float iframe size messaging not implemented yet"); + } let abs_rect = Rect(self.common.abs_position, self.common.position.size); if !abs_rect.intersects(dirty) { return false; diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index c39dfbeb2e3..45bb3db3ebb 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -753,6 +753,11 @@ impl InlineFlowData { list: &Cell>) -> bool { + //TODO: implement inline iframe size messaging + if self.common.node.is_iframe_element() { + error!("inline iframe size messaging not implemented yet"); + } + let abs_rect = Rect(self.common.abs_position, self.common.position.size); if !abs_rect.intersects(dirty) { return false; diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 85dc609d2bc..84508c470b6 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -117,6 +117,12 @@ impl BoxModel { left + right } + pub fn noncontent_height(&self) -> Au { + let top = self.margin.top + self.border.top + self.padding.top; + let bottom = self.margin.bottom + self.border.bottom + self.padding.bottom; + top + bottom + } + pub fn offset(&self) -> Au { self.margin.left + self.border.left + self.padding.left } diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs index ab12a1f73e0..33e70ed6ebf 100644 --- a/src/components/main/pipeline.rs +++ b/src/components/main/pipeline.rs @@ -11,7 +11,7 @@ use gfx::opts::Opts; use layout::layout_task::LayoutTask; use script::layout_interface::LayoutChan; use script::script_task::{ExecuteMsg, LoadMsg}; -use servo_msg::constellation_msg::{ConstellationChan, NavigationType, PipelineId, SubpageId}; +use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId}; use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan}; use script::script_task; use servo_net::image_cache_task::ImageCacheTask; @@ -31,7 +31,6 @@ pub struct Pipeline { render_chan: RenderChan, /// The most recently loaded url url: Option, - navigation_type: Option, } impl Pipeline { @@ -140,13 +139,11 @@ impl Pipeline { layout_chan: layout_chan, render_chan: render_chan, url: None, - navigation_type: None, } } - pub fn load(&mut self, url: Url, navigation_type: Option) { + pub fn load(&mut self, url: Url) { self.url = Some(url.clone()); - self.navigation_type = navigation_type; self.script_chan.send(LoadMsg(self.id, url)); } @@ -163,11 +160,10 @@ impl Pipeline { self.render_chan.send(PaintPermissionRevoked); } - pub fn reload(&mut self, navigation_type: Option) { - if self.url.is_some() { - let url = self.url.get_ref().clone(); - self.load(url, navigation_type); - } + pub fn reload(&mut self) { + do self.url.clone().map_move() |url| { + self.load(url); + }; } pub fn exit(&self) { diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index f1337c94301..949f418c317 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -54,14 +54,25 @@ pub enum ReadyState { FinishedLoading, } +/// A newtype struct for denoting the age of messages; prevents race conditions. +#[deriving(Eq)] +pub struct Epoch(uint); + +impl Epoch { + pub fn next(&mut self) { + **self += 1; + } +} + /// The interface used by the renderer to acquire draw targets for each render frame and /// submit them to be drawn to the display. pub trait RenderListener { fn get_gl_context(&self) -> AzGLContext; fn new_layer(&self, PipelineId, Size2D); - fn resize_layer(&self, PipelineId, Size2D); + fn set_layer_page_size(&self, PipelineId, Size2D, Epoch); + fn set_layer_clip_rect(&self, PipelineId, Rect); fn delete_layer(&self, PipelineId); - fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc); + fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc, Epoch); 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 af0a01184a3..51bcbb08c46 100644 --- a/src/components/msg/constellation_msg.rs +++ b/src/components/msg/constellation_msg.rs @@ -9,6 +9,7 @@ use std::comm::{Chan, SharedChan}; use extra::url::Url; use extra::future::Future; use geom::size::Size2D; +use geom::rect::Rect; #[deriving(Clone)] pub struct ConstellationChan { @@ -29,11 +30,12 @@ impl ConstellationChan { pub enum Msg { ExitMsg(Chan<()>), InitLoadUrlMsg(Url), + FrameRectMsg(PipelineId, SubpageId, Rect), LoadUrlMsg(PipelineId, Url, Future>), LoadIframeUrlMsg(Url, PipelineId, SubpageId, Future>), NavigateMsg(NavigationDirection), RendererReadyMsg(PipelineId), - ResizedWindowBroadcast(Size2D), + ResizedWindowMsg(Size2D), } /// Represents the two different ways to which a page can be navigated diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 8e6f620c28b..3aa30ea7dee 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -385,8 +385,6 @@ impl Element { assert!(node.is_element()); let page = win.page; let (port, chan) = comm::stream(); - // 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 unsafe {(*page).query_layout(ContentBoxesQuery(node, chan), port)} { Ok(ContentBoxesResponse(rects)) => { let cx = unsafe {(*page).js_info.get_ref().js_compartment.cx.ptr}; diff --git a/src/components/script/dom/htmliframeelement.rs b/src/components/script/dom/htmliframeelement.rs index 98217737a27..288a988928a 100644 --- a/src/components/script/dom/htmliframeelement.rs +++ b/src/components/script/dom/htmliframeelement.rs @@ -7,19 +7,39 @@ use dom::document::AbstractDocument; use dom::htmlelement::HTMLElement; use dom::windowproxy::WindowProxy; use geom::size::Size2D; +use geom::rect::Rect; -use servo_msg::constellation_msg::SubpageId; +use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; use std::comm::ChanOne; use extra::url::Url; +use std::util::replace; pub struct HTMLIFrameElement { parent: HTMLElement, frame: Option, - subpage_id: Option, - size_future_chan: Option>>, + size: Option, } +struct IFrameSize { + pipeline_id: PipelineId, + subpage_id: SubpageId, + future_chan: Option>>, + constellation_chan: ConstellationChan, +} + +impl IFrameSize { + pub fn set_rect(&mut self, rect: Rect) { + let future_chan = replace(&mut self.future_chan, None); + do future_chan.map_move |future_chan| { + let Size2D { width, height } = rect.size; + future_chan.send(Size2D(width as uint, height as uint)); + }; + self.constellation_chan.send(FrameRectMsg(self.pipeline_id, self.subpage_id, rect)); + } +} + + impl HTMLIFrameElement { pub fn Src(&self) -> DOMString { null_string @@ -123,4 +143,4 @@ impl HTMLIFrameElement { pub fn GetSVGDocument(&self) -> Option { None } -} \ No newline at end of file +} diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index 1aad6dc007c..e14ddac2f4b 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -30,7 +30,7 @@ use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlbodyelement::HTMLBodyElement; use dom::htmlcanvaselement::HTMLCanvasElement; use dom::htmlhrelement::HTMLHRElement; -use dom::htmliframeelement::HTMLIFrameElement; +use dom::htmliframeelement::{IFrameSize, HTMLIFrameElement}; use dom::htmlimageelement::HTMLImageElement; use dom::htmlmetaelement::HTMLMetaElement; use dom::htmlolistelement::HTMLOListElement; @@ -50,6 +50,7 @@ use dom::bindings::utils::str; use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser}; use js::jsapi::JSContext; use newcss::stylesheet::Stylesheet; +use script_task::page_from_context; use std::cast; use std::cell::Cell; @@ -59,7 +60,7 @@ use std::str::eq_slice; use std::task; use std::from_str::FromStr; use hubbub::hubbub; -use servo_msg::constellation_msg::SubpageId; +use servo_msg::constellation_msg::{ConstellationChan, SubpageId}; use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task; use servo_net::resource_task::{Done, Load, Payload, ResourceTask}; @@ -252,7 +253,7 @@ fn build_element_from_tag(cx: *JSContext, tag: &str) -> AbstractNode 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), (subpage_id: None)]); + handle_element!(cx, tag, "iframe", HTMLIframeElementTypeId, HTMLIFrameElement, [(frame: None), (size: None)]); handle_element!(cx, tag, "h1", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading1)]); handle_element!(cx, tag, "h2", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading2)]); @@ -276,7 +277,8 @@ pub fn parse_html(cx: *JSContext, url: Url, resource_task: ResourceTask, image_cache_task: ImageCacheTask, - next_subpage_id: SubpageId) -> HtmlParserResult { + next_subpage_id: SubpageId, + constellation_chan: ConstellationChan) -> HtmlParserResult { debug!("Hubbub: parsing %?", url); // Spawn a CSS parser to receive links to CSS style sheets. let resource_task2 = resource_task.clone(); @@ -381,14 +383,24 @@ pub fn parse_html(cx: *JSContext, // Size future let (port, chan) = comm::oneshot(); - iframe_element.size_future_chan = Some(chan); let size_future = from_port(port); // Subpage Id let subpage_id = next_subpage_id.take(); - iframe_element.subpage_id = Some(subpage_id); next_subpage_id.put_back(SubpageId(*subpage_id + 1)); + // Pipeline Id + let pipeline_id = { + let page = page_from_context(cx); + unsafe { (*page).id } + }; + + iframe_element.size = Some(IFrameSize { + pipeline_id: pipeline_id, + subpage_id: subpage_id, + future_chan: Some(chan), + constellation_chan: constellation_chan.clone(), + }); iframe_chan.send(HtmlDiscoveredIFrame((iframe_url, subpage_id, size_future))); } } diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 28249044e7d..870dec7468d 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -21,7 +21,7 @@ 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::{PipelineId, SubpageId, RendererReadyMsg, ResizedWindowBroadcast}; +use servo_msg::constellation_msg::{PipelineId, SubpageId, RendererReadyMsg}; use servo_msg::constellation_msg::{LoadIframeUrlMsg}; use servo_msg::constellation_msg; @@ -68,7 +68,7 @@ pub enum ScriptMsg { /// Notifies script that reflow is finished. ReflowCompleteMsg(PipelineId), /// Notifies script that window has been resized but to not take immediate action. - ResizeInactiveMsg(Size2D), + ResizeInactiveMsg(PipelineId, Size2D), /// Exits the constellation. ExitMsg, } @@ -454,7 +454,7 @@ impl ScriptTask { FireTimerMsg(id, timer_data) => self.handle_fire_timer_msg(id, timer_data), NavigateMsg(direction) => self.handle_navigate_msg(direction), ReflowCompleteMsg(id) => self.handle_reflow_complete_msg(id), - ResizeInactiveMsg(new_size) => self.handle_resize_inactive_msg(new_size), + ResizeInactiveMsg(id, new_size) => self.handle_resize_inactive_msg(id, new_size), ExitMsg => { self.handle_exit_msg(); return false @@ -464,6 +464,7 @@ impl ScriptTask { } fn handle_new_layout(&mut self, new_layout_info: NewLayoutInfo) { + debug!("Script: new layout: %?", new_layout_info); let NewLayoutInfo { old_id, new_id, @@ -529,6 +530,7 @@ impl ScriptTask { /// Handles a notification that reflow completed. fn handle_reflow_complete_msg(&mut self, pipeline_id: PipelineId) { + debug!("Script: Reflow complete for %?", pipeline_id); 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; @@ -543,11 +545,13 @@ impl ScriptTask { } /// Window was resized, but this script was not active, so don't reflow yet - fn handle_resize_inactive_msg(&mut self, new_size: Size2D) { - self.page_tree.page.window_size = from_value(new_size); - let last_loaded_url = replace(&mut self.page_tree.page.url, None); + fn handle_resize_inactive_msg(&mut self, id: PipelineId, new_size: Size2D) { + let page = self.page_tree.find(id).expect("Received resize message for PipelineId not associated + with a page in the page tree. This is a bug.").page; + page.window_size = from_value(new_size); + let last_loaded_url = replace(&mut page.url, None); for url in last_loaded_url.iter() { - self.page_tree.page.url = Some((url.first(), true)); + page.url = Some((url.first(), true)); } } @@ -565,7 +569,7 @@ impl ScriptTask { /// 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, pipeline_id: PipelineId, url: Url) { - debug!("ScriptTask: loading %?", url); + debug!("ScriptTask: loading %? on page %?", url, pipeline_id); 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 @@ -601,7 +605,8 @@ impl ScriptTask { url.clone(), self.resource_task.clone(), self.image_cache_task.clone(), - page.next_subpage_id.clone()); + page.next_subpage_id.clone(), + self.constellation_chan.clone()); let HtmlParserResult {root, discovery_port} = html_parsing_result; @@ -700,8 +705,6 @@ impl ScriptTask { page.damage(ReflowDocumentDamage); page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor) } - - self.constellation_chan.send(ResizedWindowBroadcast(page.window_size.get().clone())); } // FIXME(pcwalton): This reflows the entire document and is not incremental-y.