From f514a8b36a1336b4d27160122446787ebc4cbf56 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Mon, 5 Aug 2013 18:56:13 -0700 Subject: [PATCH 01/19] add rect to FrameTree --- src/components/main/constellation.rs | 67 ++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 32da23206b0..c0f28d642e2 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -9,6 +9,7 @@ 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}; @@ -44,13 +45,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,15 +62,34 @@ impl Clone for FrameTree { } } +struct ChildFrameTree { + frame_tree: @mut FrameTree, + 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) } } @@ -77,7 +98,7 @@ impl SendableFrameTree { impl FrameTree { fn contains(&self, id: PipelineId) -> bool { self.pipeline.id == id || - do self.children.iter().any |frame_tree| { + do self.children.iter().any |&ChildFrameTree { frame_tree: ref frame_tree, _ }| { frame_tree.contains(id) } } @@ -85,7 +106,8 @@ impl FrameTree { /// 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| { + let mut finder = do self.children.iter() + .filter_map |&ChildFrameTree { frame_tree: ref frame_tree, _ }| { frame_tree.find_mut(id) }; finder.next() @@ -95,7 +117,7 @@ impl FrameTree { /// 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() { + for &ChildFrameTree { frame_tree: ref mut child, _ } in self.children.mut_iter() { let new_child = new_child_cell.take(); if child.pipeline.id == id { new_child.parent = child.parent; @@ -125,6 +147,15 @@ impl FrameTree { } } +impl ChildFrameTree { + fn to_sendable(&self) -> SendableChildFrameTree { + SendableChildFrameTree { + frame_tree: self.frame_tree.to_sendable(), + rect: self.rect, + } + } +} + pub struct FrameTreeIterator { priv stack: ~[@mut FrameTree], } @@ -133,7 +164,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 next.children.iter().advance |&ChildFrameTree { frame_tree, _ }| { + self.stack.push(frame_tree); + } Some(next) } else { None @@ -390,10 +423,13 @@ impl Constellation { pipeline.load(url, None); } for frame_tree in frame_trees.iter() { - frame_tree.children.push(@mut FrameTree { - pipeline: pipeline, - parent: Some(source_pipeline), - children: ~[], + frame_tree.children.push(ChildFrameTree { + frame_tree: @mut FrameTree { + pipeline: pipeline, + parent: Some(source_pipeline), + children: ~[], + }, + rect: None }); } self.pipelines.insert(pipeline.id, pipeline); @@ -558,7 +594,10 @@ impl Constellation { 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()); + parent.children.push(ChildFrameTree { + frame_tree: to_add.take(), + rect: None, + }); } } } From 5a1a56507b49f9747e3899041033c24b00ead7a3 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Tue, 6 Aug 2013 13:05:35 -0700 Subject: [PATCH 02/19] add comment re: clipping rect --- src/components/main/constellation.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index c0f28d642e2..a6f1b6f9a40 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -64,6 +64,8 @@ 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>, } From e64609eb255147b12f79d66764698de3d815efcc Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Mon, 5 Aug 2013 13:37:04 -0700 Subject: [PATCH 03/19] placeholders for FrameRectMsg from Layout to Constellation --- src/components/main/constellation.rs | 5 +++++ src/components/msg/constellation_msg.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index a6f1b6f9a40..7009dca19c0 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -359,6 +359,11 @@ impl Constellation { self.pipelines.insert(pipeline.id, pipeline); } +/* + FrameRectMsg(pipeline_id, subpage_id, rect, rect_type) => { + } + +*/ 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 diff --git a/src/components/msg/constellation_msg.rs b/src/components/msg/constellation_msg.rs index af0a01184a3..eb7b196c25d 100644 --- a/src/components/msg/constellation_msg.rs +++ b/src/components/msg/constellation_msg.rs @@ -29,6 +29,7 @@ impl ConstellationChan { pub enum Msg { ExitMsg(Chan<()>), InitLoadUrlMsg(Url), + //FrameRectMsg(PipelineId, SubpageId, Rect), LoadUrlMsg(PipelineId, Url, Future>), LoadIframeUrlMsg(Url, PipelineId, SubpageId, Future>), NavigateMsg(NavigationDirection), From 2e166fb7ca13da95fd7425d53b58c1d25b29e14d Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Mon, 5 Aug 2013 18:56:13 -0700 Subject: [PATCH 04/19] add rect to FrameTree --- src/components/main/constellation.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 7009dca19c0..6efcf8c7ff3 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -15,14 +15,14 @@ use pipeline::Pipeline; use servo_msg::constellation_msg::{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::{PipelineId, RendererReadyMsg, ResizedWindowBroadcast, SubpageId}; use servo_msg::constellation_msg; use script::script_task::{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; @@ -37,6 +37,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, } @@ -283,6 +284,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(), }; @@ -360,7 +362,22 @@ impl Constellation { } /* - FrameRectMsg(pipeline_id, subpage_id, rect, rect_type) => { + FrameRectMsg(pipeline_id, subpage_id, rect) => { + 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.consume_iter().chain_(matching_pending_frames).collect() + }; + let mut subframes = HashSet::new(); + for self.current_frame().find(pipeline_id).iter().advance |subframe| { + self.compositor_chan.send(ResizeLayerMsg( + } + if self.current_frame().contains(pipeline_id) { + self.compositor_chan.send(ResizeLayerMsg(pipeline_ + } + let frame_trees = self.navigation_context.find_all(pipeline_id); } */ @@ -429,6 +446,7 @@ impl Constellation { } else { pipeline.load(url, None); } + 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 { @@ -436,7 +454,7 @@ impl Constellation { parent: Some(source_pipeline), children: ~[], }, - rect: None + rect: rect, }); } self.pipelines.insert(pipeline.id, pipeline); From e4d44fb8d2e357b9142ab38ff33be083fa98fe9f Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Tue, 6 Aug 2013 15:18:52 -0700 Subject: [PATCH 05/19] added CompositorLayer::from_frame_tree --- .../main/compositing/compositor_layer.rs | 22 +++++++++++++++++ src/components/main/constellation.rs | 24 ++----------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index fce05548531..b4e79cf6ac2 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -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. @@ -81,6 +82,27 @@ impl CompositorLayer { } } + pub fn from_frame_tree(frame_tree: SendableFrameTree, tile_size: uint, max_mem: Option) -> CompositorLayer { + let SendableFrameTree { pipeline, children } = frame_tree; + CompositorLayer { + pipeline: pipeline, + page_size: None, + scroll_offset: Point2D(0f32, 0f32), + children: (do children.consume_iter().transform |child| { + let SendableChildFrameTree { frame_tree, rect } = child; + let container = @mut ContainerLayer(); + container.scissor = rect; + CompositorLayerChild { + child: ~CompositorLayer::from_frame_tree(frame_tree, tile_size, max_mem), + container: container, + } + }).collect(), + quadtree: NoTree(tile_size, max_mem), + root_layer: @mut ContainerLayer(), + hidden: true, + } + } + // 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 diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 6efcf8c7ff3..8fb698f8132 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -22,7 +22,7 @@ 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, HashSet}; +use std::hashmap::HashMap; use std::util::replace; use extra::future::from_value; @@ -361,26 +361,6 @@ impl Constellation { self.pipelines.insert(pipeline.id, pipeline); } -/* - FrameRectMsg(pipeline_id, subpage_id, rect) => { - 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.consume_iter().chain_(matching_pending_frames).collect() - }; - let mut subframes = HashSet::new(); - for self.current_frame().find(pipeline_id).iter().advance |subframe| { - self.compositor_chan.send(ResizeLayerMsg( - } - if self.current_frame().contains(pipeline_id) { - self.compositor_chan.send(ResizeLayerMsg(pipeline_ - } - let frame_trees = self.navigation_context.find_all(pipeline_id); - } - -*/ 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 @@ -446,7 +426,7 @@ impl Constellation { } else { pipeline.load(url, None); } - let rect = self.pending_sizes.pop((source_pipeline_id, subpage_id)); + 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 { From 86f0aacb3d59e908c0817e3906f267e3608133a2 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Tue, 6 Aug 2013 19:05:24 -0700 Subject: [PATCH 06/19] reorganized constellation. compositor routes dom events via constellation. constellation handles iframe sizing and resizing. --- src/components/gfx/render_task.rs | 4 +- src/components/main/compositing/mod.rs | 74 ++- src/components/main/constellation.rs | 692 +++++++++++++----------- src/components/msg/constellation_msg.rs | 5 +- src/components/script/script_task.rs | 18 +- 5 files changed, 445 insertions(+), 348 deletions(-) diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 6ddbb19df64..8b3d275dbb1 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -136,7 +136,7 @@ impl RenderTask { match self.port.recv() { RenderMsg(render_layer) => { if self.paint_permission { - self.compositor.new_layer(self.id, render_layer.size); + self.compositor.resize_layer(self.id, render_layer.size); } self.render_layer = Some(render_layer); } @@ -147,7 +147,7 @@ impl RenderTask { self.paint_permission = true; match self.render_layer { Some(ref render_layer) => { - self.compositor.new_layer(self.id, render_layer.size); + self.compositor.resize_layer(self.id, render_layer.size); } None => {} } diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 1931037b793..fe430853567 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}; @@ -14,7 +12,7 @@ use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEven use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState}; use servo_msg::compositor_msg::{ReadyState, ScriptListener}; -use servo_msg::constellation_msg::PipelineId; +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; @@ -86,10 +84,12 @@ impl RenderListener for CompositorChan { } 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)) + let Size2D { width, height } = page_size; + self.chan.send(ResizeLayer(id, Size2D(width as f32, height as f32))) } fn delete_layer(&self, id: PipelineId) { self.chan.send(DeleteLayer(id)) @@ -130,9 +130,9 @@ pub enum Msg { // TODO: Attach epochs to these messages /// Alerts the compositor that there is a new layer to be rendered. - NewLayer(PipelineId, Size2D), + NewLayer(PipelineId, Size2D), /// Alerts the compositor that the specified layer has changed size. - ResizeLayer(PipelineId, Size2D), + ResizeLayer(PipelineId, Size2D), /// Alerts the compositor that the specified layer has been deleted. DeleteLayer(PipelineId), /// Invalidate a rect for a given layer @@ -145,7 +145,7 @@ pub enum Msg { /// 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 +207,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,13 +216,9 @@ 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 = || { @@ -243,9 +239,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 +268,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; @@ -284,8 +293,9 @@ impl CompositorTask { 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), + assert!(layer.resize(id, + Size2D(new_size.width as f32, + new_size.height as f32), page_window)); ask_for_tiles(); } @@ -340,12 +350,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 +365,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 +429,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/constellation.rs b/src/components/main/constellation.rs index 8fb698f8132..4b70eaf04ed 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -2,7 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use compositing::{CompositorChan, SetIds}; +use compositing::{CompositorChan, SetIds, ResizeLayer}; +use script::dom::event::ResizeEvent; use std::cell::Cell; use std::comm; @@ -12,19 +13,19 @@ 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, SubpageId}; +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::future::{Future, from_value}; /// Maintains the pipelines and navigation context and grants permission to composite pub struct Constellation { @@ -167,7 +168,7 @@ 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 |&ChildFrameTree { frame_tree, _ }| { + for &ChildFrameTree { frame_tree, _ } in next.children.iter() { self.stack.push(frame_tree); } Some(next) @@ -318,328 +319,407 @@ 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); - } - 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); + 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(ChildFrameTree { - frame_tree: to_add.take(), - rect: None, - }); - } - } - } - 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, 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); + } + fn handle_frame_rect_msg(&mut self, pipeline_id: PipelineId, subpage_id: SubpageId, rect: Rect) { + let mut already_sent = HashSet::new(); + + // If the subframe is in the current frame tree, the compositor needs the new size + let source_frame = self.current_frame().unwrap().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(ResizeLayer(pipeline.id, rect.size)); + already_sent.insert(pipeline.id.clone()); + break; + } + } + } + // Go through the navigation context 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) { + // 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); + } + 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!("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, 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); + } + + 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(Some(constellation_msg::Navigate)); + } + self.grant_paint_permission(destination_frame); + + } + + fn handle_renderer_ready_msg(&mut self, pipeline_id: PipelineId) { + // 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; + } + } + + // 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. + // This conveniently keeps scissor rect size intact. + 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 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); + } + } + + 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 &@FrameTree { pipeline: ref pipeline, _ } in self.navigation_context.previous.iter() + .chain(self.navigation_context.next.iter()) { + 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) { // Give permission to paint to the new frame and all child frames @@ -666,7 +746,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/msg/constellation_msg.rs b/src/components/msg/constellation_msg.rs index eb7b196c25d..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,12 +30,12 @@ impl ConstellationChan { pub enum Msg { ExitMsg(Chan<()>), InitLoadUrlMsg(Url), - //FrameRectMsg(PipelineId, SubpageId, Rect), + 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/script_task.rs b/src/components/script/script_task.rs index 28249044e7d..edec2571e2e 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 @@ -543,11 +543,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)); } } @@ -700,8 +702,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. From 666c29480e805a5daa65acf5b34692a4c9c8b3f9 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Wed, 7 Aug 2013 17:52:09 -0700 Subject: [PATCH 07/19] refactor iframe element fields to support sending size to the constellation --- src/components/script/dom/element.rs | 1 + .../script/dom/htmliframeelement.rs | 24 ++++++++++++++++--- .../script/html/hubbub_html_parser.rs | 24 ++++++++++++++----- src/components/script/script_task.rs | 3 ++- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 8e6f620c28b..d198a29175e 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -44,6 +44,7 @@ use newcss::stylesheet::Stylesheet; use js::jsapi::{JSContext, JSObject}; +use std::util::replace; use std::cell::Cell; use std::comm; use std::str::eq_slice; diff --git a/src/components/script/dom/htmliframeelement.rs b/src/components/script/dom/htmliframeelement.rs index 98217737a27..8fa028baff2 100644 --- a/src/components/script/dom/htmliframeelement.rs +++ b/src/components/script/dom/htmliframeelement.rs @@ -16,10 +16,28 @@ use extra::url::Url; 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_consume |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 +141,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..dce79fe58ae 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -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,15 +383,25 @@ 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)); - iframe_chan.send(HtmlDiscoveredIFrame((iframe_url, subpage_id, size_future))); + // 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 edec2571e2e..a32dbd8edeb 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -603,7 +603,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; From a58b01bc8690e101576ce3d1937243abb01f1147 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Wed, 7 Aug 2013 19:52:30 -0700 Subject: [PATCH 08/19] support in layout to send iframe sizes --- src/components/main/constellation.rs | 2 ++ src/components/main/layout/block.rs | 20 ++++++++++++++++++++ src/components/main/layout/model.rs | 6 ++++++ 3 files changed, 28 insertions(+) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 4b70eaf04ed..80bc29dad04 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -398,6 +398,7 @@ impl Constellation { } 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 @@ -639,6 +640,7 @@ impl Constellation { // TODO(tkuehn): In fact, this kind of message might be provably // impossible to occur. if current_frame.contains(pipeline_id) { + debug!("updating compositor frame tree with %?", current_frame); self.set_ids(current_frame); return; } diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index b30794a0343..bd20995348e 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -14,6 +14,7 @@ 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; @@ -364,6 +365,25 @@ 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(x.to_f32(), y.to_f32()), + Size2D(w.to_f32(), h.to_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/model.rs b/src/components/main/layout/model.rs index f3c2a4ef85d..938a175bd03 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -119,6 +119,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 } From 93368a2bf34300610b720d25b70e236273b89e33 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Wed, 7 Aug 2013 19:54:29 -0700 Subject: [PATCH 09/19] removed old TODO --- src/components/script/dom/element.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index d198a29175e..e71e7b87e9d 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -386,8 +386,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}; From bd5526de940c4cdd2413666be56ca86860f493d9 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Thu, 8 Aug 2013 12:43:11 -0700 Subject: [PATCH 10/19] add iframe support --- .../main/compositing/compositor_layer.rs | 37 +++++++++---------- src/components/main/constellation.rs | 37 ++++++++++--------- src/components/main/layout/block.rs | 8 ++-- src/components/main/layout/float.rs | 3 ++ src/components/main/layout/inline.rs | 4 ++ src/components/script/dom/element.rs | 1 - .../script/dom/htmliframeelement.rs | 10 +++-- .../script/html/hubbub_html_parser.rs | 6 +-- 8 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index b4e79cf6ac2..881a781eb26 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -82,25 +82,23 @@ impl CompositorLayer { } } - pub fn from_frame_tree(frame_tree: SendableFrameTree, tile_size: uint, max_mem: Option) -> CompositorLayer { + pub fn from_frame_tree(frame_tree: SendableFrameTree, + tile_size: uint, + max_mem: Option) -> CompositorLayer { let SendableFrameTree { pipeline, children } = frame_tree; - CompositorLayer { - pipeline: pipeline, - page_size: None, - scroll_offset: Point2D(0f32, 0f32), - children: (do children.consume_iter().transform |child| { - let SendableChildFrameTree { frame_tree, rect } = child; - let container = @mut ContainerLayer(); - container.scissor = rect; - CompositorLayerChild { - child: ~CompositorLayer::from_frame_tree(frame_tree, tile_size, max_mem), - container: container, - } - }).collect(), - quadtree: NoTree(tile_size, max_mem), - root_layer: @mut ContainerLayer(), - hidden: true, - } + let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem); + layer.children = (do children.consume_iter().transform |child| { + let SendableChildFrameTree { frame_tree, rect } = child; + let container = @mut ContainerLayer(); + container.scissor = rect; + CompositorLayerChild { + child: ~CompositorLayer::from_frame_tree(frame_tree, + tile_size, + max_mem), + container: container, + } + }).collect(); + layer } // Move the layer by as relative specified amount in page coordinates. Does not change @@ -188,7 +186,8 @@ 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); diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 80bc29dad04..b16e3fb76d6 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -402,24 +402,27 @@ impl Constellation { let mut already_sent = HashSet::new(); // If the subframe is in the current frame tree, the compositor needs the new size - let source_frame = self.current_frame().unwrap().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(ResizeLayer(pipeline.id, rect.size)); - already_sent.insert(pipeline.id.clone()); - break; - } - } + 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.iter().advance |source_frame| { + 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(ResizeLayer(pipeline.id, rect.size)); + already_sent.insert(pipeline.id.clone()); + break; + } + } + } } - // Go through the navigation context and tell each associated pipeline to resize. + // 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| { diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index bd20995348e..0379689d5fb 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -17,7 +17,7 @@ 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; @@ -379,8 +379,10 @@ impl BlockFlowData { 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(x.to_f32(), y.to_f32()), - Size2D(w.to_f32(), h.to_f32()))); + 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))); } } diff --git a/src/components/main/layout/float.rs b/src/components/main/layout/float.rs index bfabef57526..efd46b80e60 100644 --- a/src/components/main/layout/float.rs +++ b/src/components/main/layout/float.rs @@ -295,6 +295,9 @@ impl FloatFlowData { list: &Cell>) -> bool { + if self.common.node.is_iframe_element() { + println("float iframe"); + } 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 d1a9c5e9708..42f0a69b625 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -755,6 +755,10 @@ impl InlineFlowData { list: &Cell>) -> bool { + if self.common.node.is_iframe_element() { + println("inline iframe"); + } + let abs_rect = Rect(self.common.abs_position, self.common.position.size); if !abs_rect.intersects(dirty) { return false; diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index e71e7b87e9d..3aa30ea7dee 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -44,7 +44,6 @@ use newcss::stylesheet::Stylesheet; use js::jsapi::{JSContext, JSObject}; -use std::util::replace; use std::cell::Cell; use std::comm; use std::str::eq_slice; diff --git a/src/components/script/dom/htmliframeelement.rs b/src/components/script/dom/htmliframeelement.rs index 8fa028baff2..4b8199e0bdd 100644 --- a/src/components/script/dom/htmliframeelement.rs +++ b/src/components/script/dom/htmliframeelement.rs @@ -7,26 +7,28 @@ 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, - size: Option, + size: Option, } -struct IframeSize { +struct IFrameSize { pipeline_id: PipelineId, subpage_id: SubpageId, future_chan: Option>>, constellation_chan: ConstellationChan, } -impl IframeSize { +impl IFrameSize { pub fn set_rect(&mut self, rect: Rect) { let future_chan = replace(&mut self.future_chan, None); do future_chan.map_consume |future_chan| { diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index dce79fe58ae..f78807a8a35 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; @@ -253,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: 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)]); @@ -395,7 +395,7 @@ pub fn parse_html(cx: *JSContext, unsafe { (*page).id } }; - iframe_element.size = Some(IframeSize { + iframe_element.size = Some(IFrameSize { pipeline_id: pipeline_id, subpage_id: subpage_id, future_chan: Some(chan), From 2c44288c26b95c04f8eddf4d835f51c342bed7ec Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Thu, 8 Aug 2013 16:31:52 -0700 Subject: [PATCH 11/19] some more minor refactoring in constellation --- src/components/main/constellation.rs | 43 +++++++++++++--------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index b16e3fb76d6..518a955cbc3 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -100,40 +100,37 @@ impl SendableFrameTree { } impl FrameTree { - fn contains(&self, id: PipelineId) -> bool { - self.pipeline.id == id || - do self.children.iter().any |&ChildFrameTree { frame_tree: ref 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 |&ChildFrameTree { frame_tree: ref 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 &ChildFrameTree { frame_tree: ref mut 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) + -> Result<@mut FrameTree, @mut FrameTree> { + let mut child = (do self.iter().filter_map |frame_tree| { + (do frame_tree.children.iter().find |child| { + child.frame_tree.pipeline.id == id + }).map(|& &child| child) + }).next(); + match child { + Some(ref mut child) => { + new_child.parent = child.frame_tree.parent; + Left(replace(&mut child.frame_tree, new_child)) } - new_child_cell.put_back(replaced.unwrap_right()); + None => Right(new_child) } - Right(new_child_cell.take()) } fn to_sendable(&self) -> SendableFrameTree { From 8993434c39bd64efded22ed60607859239c3f3ed Mon Sep 17 00:00:00 2001 From: eschweic Date: Thu, 8 Aug 2013 15:22:02 -0700 Subject: [PATCH 12/19] Better layer resize methods --- src/components/gfx/render_task.rs | 4 +- .../main/compositing/compositor_layer.rs | 60 ++++++++++++++----- src/components/main/compositing/mod.rs | 35 ++++++++--- src/components/main/constellation.rs | 4 +- src/components/msg/compositor_msg.rs | 3 +- 5 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 8b3d275dbb1..3d507531145 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -136,7 +136,7 @@ impl RenderTask { match self.port.recv() { RenderMsg(render_layer) => { if self.paint_permission { - self.compositor.resize_layer(self.id, render_layer.size); + self.compositor.set_layer_page_size(self.id, render_layer.size); } self.render_layer = Some(render_layer); } @@ -147,7 +147,7 @@ impl RenderTask { self.paint_permission = true; match self.render_layer { Some(ref render_layer) => { - self.compositor.resize_layer(self.id, render_layer.size); + self.compositor.set_layer_page_size(self.id, render_layer.size); } None => {} } diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index 881a781eb26..cfe868bf4d4 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -62,7 +62,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 { @@ -82,6 +83,7 @@ impl CompositorLayer { } } + /// Constructs a CompositorLayer tree from a frame tree. pub fn from_frame_tree(frame_tree: SendableFrameTree, tile_size: uint, max_mem: Option) -> CompositorLayer { @@ -225,6 +227,7 @@ 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() { @@ -236,6 +239,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; } @@ -244,7 +251,8 @@ 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 { if self.pipeline.id == pipeline_id { @@ -257,27 +265,47 @@ 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) + } + + // A helper method to resize sublayers. + fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D) -> bool { + for self.children.mut_iter().advance |child_node| { + if pipeline_id != child_node.child.pipeline.id { + loop; + } + let child = &mut child_node.child; + 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().transform(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size)) } - + // 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) { diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index fe430853567..2a17b8128ca 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -87,10 +87,18 @@ impl RenderListener for CompositorChan { 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) { + fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D) { let Size2D { width, height } = page_size; - self.chan.send(ResizeLayer(id, Size2D(width as f32, height as f32))) + self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32))) } + 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)) } @@ -131,8 +139,10 @@ pub enum Msg { // 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), + /// Alerts the compositor that the specified layer's page has changed size. + SetLayerPageSize(PipelineId, Size2D), + /// 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 @@ -288,15 +298,22 @@ impl CompositorTask { ask_for_tiles(); } - ResizeLayer(id, new_size) => { + SetLayerPageSize(id, new_size) => { 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)); + 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 => {} diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 518a955cbc3..a9d54cfc320 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, SetIds, ResizeLayer}; +use compositing::{CompositorChan, SetIds, SetLayerClipRect}; use script::dom::event::ResizeEvent; use std::cell::Cell; @@ -412,7 +412,7 @@ impl Constellation { pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), ResizeEvent(width as uint, height as uint))); - self.compositor_chan.send(ResizeLayer(pipeline.id, rect.size)); + self.compositor_chan.send(SetLayerClipRect(pipeline.id, rect)); already_sent.insert(pipeline.id.clone()); break; } diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index f1337c94301..284b14099b1 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -59,7 +59,8 @@ pub enum ReadyState { 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); + fn set_layer_clip_rect(&self, PipelineId, Rect); fn delete_layer(&self, PipelineId); fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc); fn set_render_state(&self, render_state: RenderState); From f2c00f7e2870c05573c18713d0d33f025d66010a Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Thu, 8 Aug 2013 23:06:10 -0700 Subject: [PATCH 13/19] move navigation_type from Pipeline to FrameChange --- .../main/compositing/compositor_layer.rs | 6 ++-- src/components/main/compositing/mod.rs | 1 + src/components/main/constellation.rs | 33 +++++++++++-------- src/components/main/pipeline.rs | 16 ++++----- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index cfe868bf4d4..2993bc339f5 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -339,7 +339,8 @@ 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, }; @@ -384,7 +385,8 @@ impl CompositorLayer { if self.pipeline.id == pipeline_id { { // 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, }; diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 2a17b8128ca..bd307b11411 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -299,6 +299,7 @@ impl CompositorTask { } SetLayerPageSize(id, new_size) => { + println(fmt!("Compositor: id %? sent new layer of size %?", id, new_size)); match compositor_layer { Some(ref mut layer) => { let page_window = Size2D(window_size.width as f32 / world_zoom, diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index a9d54cfc320..16783f21315 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -15,7 +15,7 @@ use gfx::opts::Opts; use pipeline::Pipeline; 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::{Msg, NavigateMsg, NavigationType}; use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowMsg, SubpageId}; use servo_msg::constellation_msg; use script::script_task::{SendEventMsg, ResizeInactiveMsg, ExecuteMsg}; @@ -157,6 +157,9 @@ impl ChildFrameTree { } } +/// 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], } @@ -165,7 +168,7 @@ impl Iterator<@mut FrameTree> for FrameTreeIterator { fn next(&mut self) -> Option<@mut FrameTree> { if !self.stack.is_empty() { let next = self.stack.pop(); - for &ChildFrameTree { frame_tree, _ } in next.children.iter() { + for &ChildFrameTree { frame_tree, _ } in next.children.rev_iter() { self.stack.push(frame_tree); } Some(next) @@ -179,6 +182,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 @@ -380,7 +384,7 @@ impl Constellation { if url.path.ends_with(".js") { pipeline.script_chan.send(ExecuteMsg(pipeline.id, url)); } else { - pipeline.load(url, Some(constellation_msg::Load)); + pipeline.load(url); self.pending_frames.push(FrameChange{ before: None, @@ -389,6 +393,7 @@ impl Constellation { parent: None, children: ~[], }, + navigation_type: constellation_msg::Load, }); } self.pipelines.insert(pipeline.id, pipeline); @@ -518,7 +523,7 @@ impl Constellation { if url.path.ends_with(".js") { pipeline.execute(url); } else { - pipeline.load(url, None); + pipeline.load(url); } let rect = self.pending_sizes.pop(&(source_pipeline_id, subpage_id)); for frame_tree in frame_trees.iter() { @@ -574,7 +579,7 @@ impl Constellation { if url.path.ends_with(".js") { pipeline.script_chan.send(ExecuteMsg(pipeline.id, url)); } else { - pipeline.load(url, Some(constellation_msg::Load)); + pipeline.load(url); self.pending_frames.push(FrameChange{ before: Some(source_id), @@ -583,6 +588,7 @@ impl Constellation { parent: parent, children: ~[], }, + navigation_type: constellation_msg::Load, }); } self.pipelines.insert(pipeline.id, pipeline); @@ -624,9 +630,9 @@ impl Constellation { for frame in destination_frame.iter() { let pipeline = &frame.pipeline; - pipeline.reload(Some(constellation_msg::Navigate)); + pipeline.reload(); } - self.grant_paint_permission(destination_frame); + self.grant_paint_permission(destination_frame, constellation_msg::Navigate); } @@ -640,8 +646,6 @@ impl Constellation { // TODO(tkuehn): In fact, this kind of message might be provably // impossible to occur. if current_frame.contains(pipeline_id) { - debug!("updating compositor frame tree with %?", current_frame); - self.set_ids(current_frame); return; } } @@ -701,7 +705,7 @@ impl Constellation { } } } - self.grant_paint_permission(next_frame_tree); + self.grant_paint_permission(next_frame_tree, frame_change.navigation_type); } } @@ -713,8 +717,9 @@ impl Constellation { ResizeEvent(width, height))); already_seen.insert(pipeline.id.clone()); } - for &@FrameTree { pipeline: ref pipeline, _ } in self.navigation_context.previous.iter() + 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()); @@ -723,14 +728,14 @@ impl Constellation { } // 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 diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs index ab12a1f73e0..a1760dc7868 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_consume() |url| { + self.load(url); + }; } pub fn exit(&self) { From eb6973c7dc9de4224fe75f3b0a149a7a09392df0 Mon Sep 17 00:00:00 2001 From: eschweic Date: Thu, 8 Aug 2013 20:49:58 -0700 Subject: [PATCH 14/19] Implement epochs; fix integration bugs --- src/components/gfx/render_task.rs | 21 +++++-- .../main/compositing/compositor_layer.rs | 62 ++++++++++++++----- src/components/main/compositing/mod.rs | 30 ++++----- src/components/main/compositing/quadtree.rs | 27 ++++++-- src/components/main/constellation.rs | 12 +++- src/components/main/pipeline.rs | 2 +- src/components/msg/compositor_msg.rs | 4 +- .../script/dom/htmliframeelement.rs | 2 +- .../script/html/hubbub_html_parser.rs | 2 +- 9 files changed, 114 insertions(+), 48 deletions(-) diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 3d507531145..4521f9074e9 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -32,7 +32,7 @@ pub struct RenderLayer { pub enum Msg { RenderMsg(RenderLayer), - ReRenderMsg(~[BufferRequest], f32, PipelineId), + ReRenderMsg(~[BufferRequest], f32, PipelineId, uint), 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_counter: uint, } impl RenderTask { @@ -123,6 +125,7 @@ impl RenderTask { paint_permission: false, last_paint_msg: None, + epoch_counter: 0, }; render_task.start(); @@ -136,18 +139,24 @@ impl RenderTask { match self.port.recv() { RenderMsg(render_layer) => { if self.paint_permission { - self.compositor.set_layer_page_size(self.id, render_layer.size); + self.epoch_counter += 1; + self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch_counter); } self.render_layer = Some(render_layer); } - ReRenderMsg(tiles, scale, id) => { - self.render(tiles, scale, id); + ReRenderMsg(tiles, scale, id, epoch) => { + if self.epoch_counter == epoch { + self.render(tiles, scale, id); + } else { + debug!("renderer epoch mismatch: %? != %?", self.epoch_counter, epoch); + } } PaintPermissionGranted => { self.paint_permission = true; match self.render_layer { Some(ref render_layer) => { - self.compositor.set_layer_page_size(self.id, render_layer.size); + self.epoch_counter += 1; + self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch_counter); } 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_counter); } 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 2993bc339f5..edba5e63569 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -43,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: uint, } /// Helper struct for keeping CompositorLayer children organized. @@ -79,7 +82,8 @@ impl CompositorLayer { max_mem)), }, root_layer: @mut ContainerLayer(), - hidden: true, + hidden: page_size.is_none(), + epoch: 0, } } @@ -89,20 +93,38 @@ impl CompositorLayer { 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.consume_iter().transform |child| { + layer.children = (do children.move_iter().map |child| { let SendableChildFrameTree { frame_tree, rect } = child; let container = @mut ContainerLayer(); - container.scissor = rect; + match rect { + Some(rect) => { + container.scissor = Some(Rect(Point2D(100f32, 200f32), Size2D(700f32, 800f32))); + container.common.transform = identity().translate(100f32, 200f32, 0f32); + + // FIXME: The top two lines are temporary until layout window sizes are fixed. + // When they are, uncomment the next 2 lines: + + // 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: ~CompositorLayer::from_frame_tree(frame_tree, - tile_size, - max_mem), + 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 @@ -195,7 +217,7 @@ impl CompositorLayer { 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 { @@ -230,7 +252,7 @@ impl CompositorLayer { // 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; } @@ -254,8 +276,9 @@ impl CompositorLayer { // 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: uint) -> 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 { @@ -271,16 +294,17 @@ impl CompositorLayer { self.hidden = false; return true; } - self.resize_helper(pipeline_id, new_size) + 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) -> bool { - for self.children.mut_iter().advance |child_node| { + fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D, epoch: uint) -> 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 { @@ -303,7 +327,7 @@ impl CompositorLayer { } // ID does not match ours, so recurse on descendents (including hidden children) - self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size)) + 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 @@ -336,7 +360,7 @@ impl CompositorLayer { } }; } - + // Add new tiles. let quadtree = match self.quadtree { NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?, @@ -381,8 +405,14 @@ 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: uint) -> 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 for %?, @@ -400,7 +430,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 bd307b11411..13852e1e8b2 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -79,17 +79,17 @@ 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: uint) { + self.chan.send(Paint(id, layer_buffer_set, epoch)) } fn new_layer(&self, id: PipelineId, page_size: Size2D) { let Size2D { width, height } = page_size; self.chan.send(NewLayer(id, Size2D(width as f32, height as f32))) } - fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D) { + fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D, epoch: uint) { let Size2D { width, height } = page_size; - self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32))) + 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, @@ -136,11 +136,10 @@ 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's page has changed size. - SetLayerPageSize(PipelineId, Size2D), + SetLayerPageSize(PipelineId, Size2D, uint), /// 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. @@ -149,7 +148,7 @@ pub enum Msg { 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, uint), /// Alerts the compositor to the current status of page loading. ChangeReadyState(ReadyState), /// Alerts the compositor to the current status of rendering. @@ -235,8 +234,12 @@ impl CompositorTask { 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!("layer is hidden!"); //eschweic + } } }; @@ -298,13 +301,12 @@ impl CompositorTask { ask_for_tiles(); } - SetLayerPageSize(id, new_size) => { - println(fmt!("Compositor: id %? sent new layer of size %?", 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, new_size, page_window)); + assert!(layer.resize(id, new_size, page_window, epoch)); ask_for_tiles(); } None => {} @@ -331,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 => { diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs index 0ffc9fa31e7..6d1b323952e 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); diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 16783f21315..966c6cdb312 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -26,6 +26,7 @@ use servo_util::time::ProfilerChan; use std::hashmap::{HashMap, HashSet}; use std::util::replace; use extra::future::{Future, from_value}; +use extra::url::Url; /// Maintains the pipelines and navigation context and grants permission to composite pub struct Constellation { @@ -108,7 +109,7 @@ impl FrameTree { /// Returns the frame tree whose key is id fn find_mut(@mut self, id: PipelineId) -> Option<@mut FrameTree> { - do self.iter().find_ |frame_tree| { + do self.iter().find |frame_tree| { id == frame_tree.pipeline.id } } @@ -118,7 +119,7 @@ impl FrameTree { fn replace_child(@mut self, id: PipelineId, new_child: @mut FrameTree) - -> Result<@mut FrameTree, @mut FrameTree> { + -> Either<@mut FrameTree, @mut FrameTree> { let mut child = (do self.iter().filter_map |frame_tree| { (do frame_tree.children.iter().find |child| { child.frame_tree.pipeline.id == id @@ -407,7 +408,7 @@ impl Constellation { 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.iter().advance |source_frame| { + 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 @@ -646,6 +647,11 @@ impl Constellation { // TODO(tkuehn): In fact, this kind of message might be provably // impossible to occur. if current_frame.contains(pipeline_id) { + //debug!("updating compositor frame tree with %?", current_frame); + //self.set_ids(current_frame); + for frame in current_frame.iter() { + frame.pipeline.grant_paint_permission(); + } return; } } diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs index a1760dc7868..33e70ed6ebf 100644 --- a/src/components/main/pipeline.rs +++ b/src/components/main/pipeline.rs @@ -161,7 +161,7 @@ impl Pipeline { } pub fn reload(&mut self) { - do self.url.clone().map_consume() |url| { + do self.url.clone().map_move() |url| { self.load(url); }; } diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index 284b14099b1..929b5a158d3 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -59,10 +59,10 @@ pub enum ReadyState { pub trait RenderListener { fn get_gl_context(&self) -> AzGLContext; fn new_layer(&self, PipelineId, Size2D); - fn set_layer_page_size(&self, PipelineId, Size2D); + fn set_layer_page_size(&self, PipelineId, Size2D, uint); 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, uint); fn set_render_state(&self, render_state: RenderState); } diff --git a/src/components/script/dom/htmliframeelement.rs b/src/components/script/dom/htmliframeelement.rs index 4b8199e0bdd..288a988928a 100644 --- a/src/components/script/dom/htmliframeelement.rs +++ b/src/components/script/dom/htmliframeelement.rs @@ -31,7 +31,7 @@ struct IFrameSize { impl IFrameSize { pub fn set_rect(&mut self, rect: Rect) { let future_chan = replace(&mut self.future_chan, None); - do future_chan.map_consume |future_chan| { + do future_chan.map_move |future_chan| { let Size2D { width, height } = rect.size; future_chan.send(Size2D(width as uint, height as uint)); }; diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index f78807a8a35..e14ddac2f4b 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -401,7 +401,7 @@ pub fn parse_html(cx: *JSContext, future_chan: Some(chan), constellation_chan: constellation_chan.clone(), }); - iframe_chan.send(HtmlDiscoveredIFrame(iframe_url, subpage_id, size_future)); + iframe_chan.send(HtmlDiscoveredIFrame((iframe_url, subpage_id, size_future))); } } } From 70cb58f2cf3553defd10ef344e33877453e350da Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Fri, 9 Aug 2013 00:29:18 -0700 Subject: [PATCH 15/19] fixed bug in FrameTree.replace_child that caused a copy that broke the code --- src/components/main/constellation.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 966c6cdb312..e3f46d8d56d 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -116,22 +116,16 @@ impl FrameTree { /// 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 mut child = (do self.iter().filter_map |frame_tree| { - (do frame_tree.children.iter().find |child| { - child.frame_tree.pipeline.id == id - }).map(|& &child| child) - }).next(); - match child { - Some(ref mut child) => { + 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; - Left(replace(&mut child.frame_tree, new_child)) + return Left(replace(&mut child.frame_tree, new_child)); } - None => Right(new_child) } + Right(new_child) } fn to_sendable(&self) -> SendableFrameTree { @@ -541,7 +535,7 @@ impl Constellation { } fn handle_load_url_msg(&mut self, source_id: PipelineId, url: Url, size_future: Future>) { - debug!("received message to load %s", url.to_str()); + 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 @@ -675,6 +669,7 @@ impl Constellation { // 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( @@ -687,9 +682,12 @@ impl Constellation { // 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() { - next_frame_tree.replace_child(revoke_id, to_add); + 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 => { From 813bb2e43f68089b512b1f53fbdeaf40b39f5b4e Mon Sep 17 00:00:00 2001 From: eschweic Date: Fri, 9 Aug 2013 10:46:26 -0700 Subject: [PATCH 16/19] Add Epoch newtype; address review comments --- src/components/gfx/render_task.rs | 22 ++++++------- .../main/compositing/compositor_layer.rs | 32 +++++++++++-------- src/components/main/compositing/mod.rs | 12 +++---- src/components/msg/compositor_msg.rs | 14 ++++++-- 4 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 4521f9074e9..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, uint), + ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch), PaintPermissionGranted, PaintPermissionRevoked, ExitMsg(Chan<()>), @@ -90,7 +90,7 @@ struct RenderTask { /// Cached copy of last layers rendered last_paint_msg: Option<(arc::Arc, Size2D)>, /// A counter for epoch messages - epoch_counter: uint, + epoch: Epoch, } impl RenderTask { @@ -125,7 +125,7 @@ impl RenderTask { paint_permission: false, last_paint_msg: None, - epoch_counter: 0, + epoch: Epoch(0), }; render_task.start(); @@ -139,24 +139,24 @@ impl RenderTask { match self.port.recv() { RenderMsg(render_layer) => { if self.paint_permission { - self.epoch_counter += 1; - self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch_counter); + 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, epoch) => { - if self.epoch_counter == epoch { + if self.epoch == epoch { self.render(tiles, scale, id); } else { - debug!("renderer epoch mismatch: %? != %?", self.epoch_counter, epoch); + debug!("renderer epoch mismatch: %? != %?", self.epoch, epoch); } } PaintPermissionGranted => { self.paint_permission = true; match self.render_layer { Some(ref render_layer) => { - self.epoch_counter += 1; - self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch_counter); + self.epoch.next(); + self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch); } None => {} } @@ -244,7 +244,7 @@ impl RenderTask { debug!("render_task: returning surface"); if self.paint_permission { - self.compositor.paint(id, layer_buffer_set.clone(), self.epoch_counter); + 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 edba5e63569..8aba63008a9 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; @@ -45,7 +45,7 @@ pub struct CompositorLayer { 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: uint, + epoch: Epoch, } /// Helper struct for keeping CompositorLayer children organized. @@ -82,8 +82,8 @@ impl CompositorLayer { max_mem)), }, root_layer: @mut ContainerLayer(), - hidden: page_size.is_none(), - epoch: 0, + hidden: true, + epoch: Epoch(0), } } @@ -98,20 +98,20 @@ impl CompositorLayer { let container = @mut ContainerLayer(); match rect { Some(rect) => { - container.scissor = Some(Rect(Point2D(100f32, 200f32), Size2D(700f32, 800f32))); - container.common.transform = identity().translate(100f32, 200f32, 0f32); + //container.scissor = Some(Rect(Point2D(100f32, 200f32), Size2D(700f32, 800f32))); + //container.common.transform = identity().translate(100f32, 200f32, 0f32); // FIXME: The top two lines are temporary until layout window sizes are fixed. // When they are, uncomment the next 2 lines: - // container.scissor = Some(rect); - // container.common.transform = identity().translate(rect.origin.x, - // rect.origin.y, - // 0f32); + 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)); @@ -124,7 +124,6 @@ impl CompositorLayer { 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 @@ -276,7 +275,7 @@ impl CompositorLayer { // 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, epoch: uint) -> 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); @@ -298,8 +297,13 @@ impl CompositorLayer { } // A helper method to resize sublayers. +<<<<<<< HEAD fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D, epoch: uint) -> bool { for child_node in self.children.mut_iter() { +======= + fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D, epoch: Epoch) -> bool { + for self.children.mut_iter().advance |child_node| { +>>>>>>> 2ad7d8e... Add Epoch newtype; address review comments if pipeline_id != child_node.child.pipeline.id { loop; } @@ -406,7 +410,7 @@ impl CompositorLayer { // Add LayerBuffers to the specified layer. Returns false if the layer is not found. // 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: uint) -> bool { + 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); diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 13852e1e8b2..a480ee76b39 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -11,7 +11,7 @@ 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::compositor_msg::{ReadyState, ScriptListener, Epoch}; use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, PipelineId, ResizedWindowMsg, LoadUrlMsg}; use servo_msg::constellation_msg; use gfx::opts::Opts; @@ -79,7 +79,7 @@ impl RenderListener for CompositorChan { port.recv() } - fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc, epoch: uint) { + fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc, epoch: Epoch) { self.chan.send(Paint(id, layer_buffer_set, epoch)) } @@ -87,7 +87,7 @@ impl RenderListener for CompositorChan { let Size2D { width, height } = page_size; self.chan.send(NewLayer(id, Size2D(width as f32, height as f32))) } - fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D, epoch: uint) { + 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)) } @@ -139,7 +139,7 @@ pub enum Msg { /// Alerts the compositor that there is a new layer to be rendered. NewLayer(PipelineId, Size2D), /// Alerts the compositor that the specified layer's page has changed size. - SetLayerPageSize(PipelineId, Size2D, uint), + 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. @@ -148,7 +148,7 @@ pub enum Msg { InvalidateRect(PipelineId, Rect), /// Requests that the compositor paint the given layer buffer set for the given page size. - Paint(PipelineId, arc::Arc, uint), + 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. @@ -238,7 +238,7 @@ impl CompositorTask { recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page), world_zoom) || recomposite; } else { - debug!("layer is hidden!"); //eschweic + debug!("Compositor: root layer is hidden!"); } } }; diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index 929b5a158d3..949f418c317 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -54,15 +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 set_layer_page_size(&self, PipelineId, Size2D, uint); + 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, uint); + fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc, Epoch); fn set_render_state(&self, render_state: RenderState); } From b43714185204de002e140599d20ff3020455dd4a Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Sat, 10 Aug 2013 18:01:30 -0700 Subject: [PATCH 17/19] add todos for iframe size messaging in float/inline --- src/components/main/layout/float.rs | 3 ++- src/components/main/layout/inline.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/main/layout/float.rs b/src/components/main/layout/float.rs index efd46b80e60..8f4ffb8824b 100644 --- a/src/components/main/layout/float.rs +++ b/src/components/main/layout/float.rs @@ -295,8 +295,9 @@ impl FloatFlowData { list: &Cell>) -> bool { + //TODO: implement iframe size messaging if self.common.node.is_iframe_element() { - println("float iframe"); + 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) { diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 42f0a69b625..a636c13bbbd 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -755,8 +755,9 @@ impl InlineFlowData { list: &Cell>) -> bool { + //TODO: implement inline iframe size messaging if self.common.node.is_iframe_element() { - println("inline iframe"); + error!("inline iframe size messaging not implemented yet"); } let abs_rect = Rect(self.common.abs_position, self.common.position.size); From 14fc16af2e22959e55e353366fbaee6076719b3f Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Tue, 13 Aug 2013 21:48:08 -0400 Subject: [PATCH 18/19] remove commented out code from compositor_layer::from_frame_tree --- src/components/main/compositing/compositor_layer.rs | 13 +------------ src/components/main/constellation.rs | 7 +++++-- src/components/script/script_task.rs | 4 +++- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index 8aba63008a9..5d11c5d2860 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -98,12 +98,6 @@ impl CompositorLayer { let container = @mut ContainerLayer(); match rect { Some(rect) => { - //container.scissor = Some(Rect(Point2D(100f32, 200f32), Size2D(700f32, 800f32))); - //container.common.transform = identity().translate(100f32, 200f32, 0f32); - - // FIXME: The top two lines are temporary until layout window sizes are fixed. - // When they are, uncomment the next 2 lines: - container.scissor = Some(rect); container.common.transform = identity().translate(rect.origin.x, rect.origin.y, @@ -297,13 +291,8 @@ impl CompositorLayer { } // A helper method to resize sublayers. -<<<<<<< HEAD - fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D, epoch: uint) -> bool { - for child_node in self.children.mut_iter() { -======= fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D, epoch: Epoch) -> bool { - for self.children.mut_iter().advance |child_node| { ->>>>>>> 2ad7d8e... Add Epoch newtype; address review comments + for child_node in self.children.mut_iter() { if pipeline_id != child_node.child.pipeline.id { loop; } diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index e3f46d8d56d..a00aec606c6 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -25,6 +25,7 @@ use servo_net::resource_task; use servo_util::time::ProfilerChan; use std::hashmap::{HashMap, HashSet}; use std::util::replace; +use extra::url::Url; use extra::future::{Future, from_value}; use extra::url::Url; @@ -492,6 +493,7 @@ impl Constellation { // 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), @@ -503,6 +505,7 @@ impl Constellation { 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), @@ -518,6 +521,7 @@ impl Constellation { 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)); @@ -632,6 +636,7 @@ impl Constellation { } 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. @@ -641,8 +646,6 @@ impl Constellation { // TODO(tkuehn): In fact, this kind of message might be provably // impossible to occur. if current_frame.contains(pipeline_id) { - //debug!("updating compositor frame tree with %?", current_frame); - //self.set_ids(current_frame); for frame in current_frame.iter() { frame.pipeline.grant_paint_permission(); } diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index a32dbd8edeb..870dec7468d 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -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; @@ -567,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 From c1638f8e277c1bea79324c001b9de55c26e77f56 Mon Sep 17 00:00:00 2001 From: eschweic Date: Tue, 20 Aug 2013 09:53:20 -0700 Subject: [PATCH 19/19] Fix quatree test; warning police --- src/components/main/compositing/quadtree.rs | 8 ++++---- src/components/main/constellation.rs | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs index 6d1b323952e..364f44b5b42 100644 --- a/src/components/main/compositing/quadtree.rs +++ b/src/components/main/compositing/quadtree.rs @@ -750,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 a00aec606c6..1cadfe16a43 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -27,7 +27,6 @@ use std::hashmap::{HashMap, HashSet}; use std::util::replace; use extra::url::Url; use extra::future::{Future, from_value}; -use extra::url::Url; /// Maintains the pipelines and navigation context and grants permission to composite pub struct Constellation {