diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index d91465ae5834..1586c28b8608 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -19,19 +19,22 @@ use render_context::RenderContext; use std::cell::Cell; use std::comm::{Chan, Port, SharedChan}; +use extra::arc::Arc; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; use extra::arc; -pub struct RenderLayer { - display_list: DisplayList<()>, + + +pub struct RenderLayer { + display_list: Arc>, size: Size2D } -pub enum Msg { - RenderMsg(RenderLayer), +pub enum Msg { + RenderMsg(RenderLayer), ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch), PaintPermissionGranted, PaintPermissionRevoked, @@ -56,24 +59,24 @@ pub fn BufferRequest(screen_rect: Rect, page_rect: Rect) -> BufferReq } #[deriving(Clone)] -pub struct RenderChan { - chan: SharedChan, +pub struct RenderChan { + chan: SharedChan>, } -impl RenderChan { - pub fn new(chan: Chan) -> RenderChan { +impl RenderChan { + pub fn new(chan: Chan>) -> RenderChan { RenderChan { chan: SharedChan::new(chan), } } - pub fn send(&self, msg: Msg) { + pub fn send(&self, msg: Msg) { self.chan.send(msg); } } -struct RenderTask { +struct RenderTask { id: PipelineId, - port: Port, + port: Port>, compositor: C, font_ctx: @mut FontContext, opts: Opts, @@ -84,7 +87,7 @@ struct RenderTask { share_gl_context: AzGLContext, /// The layer to be rendered - render_layer: Option, + render_layer: Option>, /// Permission to send paint messages to the compositor paint_permission: bool, /// Cached copy of last layers rendered @@ -93,9 +96,9 @@ struct RenderTask { epoch: Epoch, } -impl RenderTask { +impl RenderTask { pub fn create(id: PipelineId, - port: Port, + port: Port>, compositor: C, opts: Opts, profiler_chan: ProfilerChan) { @@ -226,7 +229,7 @@ impl RenderTask { // Draw the display list. do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) { - render_layer.display_list.draw_into_context(&ctx); + render_layer.display_list.get().draw_into_context(&ctx); ctx.canvas.draw_target.flush(); } } diff --git a/src/components/main/layout/aux.rs b/src/components/main/layout/aux.rs index f49d73de9132..4f1968021efd 100644 --- a/src/components/main/layout/aux.rs +++ b/src/components/main/layout/aux.rs @@ -4,13 +4,20 @@ //! Code for managing the layout data in the DOM. -use layout::flow::FlowContext; use layout::incremental::RestyleDamage; +use gfx::display_list::DisplayList; +use servo_util::range::Range; +use extra::arc::Arc; use newcss::complete::CompleteSelectResults; use script::dom::node::{AbstractNode, LayoutView}; use servo_util::tree::TreeNodeRef; +pub struct DisplayBoxes { + display_list: Option>>>, + range: Option, +} + /// Data that layout associates with a node. pub struct LayoutData { /// The results of CSS styling for this node. @@ -19,8 +26,9 @@ pub struct LayoutData { /// Description of how to account for recent style changes. restyle_damage: Option, - /// The CSS flow that this node is associated with. - flow: Option, + /// The boxes assosiated with this flow. + /// Used for getBoundingClientRect and friends. + boxes: DisplayBoxes, } impl LayoutData { @@ -29,7 +37,7 @@ impl LayoutData { LayoutData { style: None, restyle_damage: None, - flow: None, + boxes: DisplayBoxes{ display_list: None, range: None }, } } } @@ -65,14 +73,8 @@ impl LayoutAuxMethods for AbstractNode { /// box in the COW model) and populates it with an empty style object. fn initialize_layout_data(self) -> Option<@mut LayoutData> { if self.has_layout_data() { - { - let layout_data = &mut self.layout_data().flow; - match *layout_data { - Some(ref flow) => flow.teardown(), - None => () - } - } - self.layout_data().flow = None; + self.layout_data().boxes.display_list = None; + self.layout_data().boxes.range = None; None } else { let data = @mut LayoutData::new(); diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs index 3e7cd5632068..035d4b94e18d 100644 --- a/src/components/main/layout/box_builder.rs +++ b/src/components/main/layout/box_builder.rs @@ -4,7 +4,6 @@ //! Creates CSS boxes from a DOM tree. -use layout::aux::LayoutAuxMethods; use layout::block::BlockFlowData; use layout::float::FloatFlowData; use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox}; @@ -334,18 +333,6 @@ impl LayoutTreeBuilder { this_generator.pop_node(layout_ctx, cur_node); self.simplify_children_of_flow(layout_ctx, &mut this_generator.flow); - // store reference to the flow context which contains any - // boxes that correspond to child_flow.node. These boxes may - // eventually be elided or split, but the mapping between - // nodes and FlowContexts should not change during layout. - let flow: &FlowContext = &this_generator.flow; - for child_flow in flow.children() { - do child_flow.with_base |child_node| { - let dom_node = child_node.node; - assert!(dom_node.has_layout_data()); - dom_node.layout_data().flow = Some(child_flow); - } - } Some(next_generator) } diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs index c992a32fc697..cc89f7cb61b1 100644 --- a/src/components/main/layout/display_list_builder.rs +++ b/src/components/main/layout/display_list_builder.rs @@ -6,20 +6,29 @@ use layout::box::RenderBox; use layout::context::LayoutContext; +use std::cast::transmute; +use script::dom::node::AbstractNode; use gfx; use newcss; -/// Extra display list data is either nothing (if the display list is to be rendered) or the -/// originating render box (if the display list is generated for hit testing). +/// Display list data is usually an AbstractNode with view () to indicate +/// that nodes in this view shoud not really be touched. The idea is to +/// store the nodes in the display list and have layout transmute them. pub trait ExtraDisplayListData { fn new(box: RenderBox) -> Self; } -/// The type representing the lack of extra display list data. This is used when sending display -/// list data off to be rendered. pub type Nothing = (); +impl ExtraDisplayListData for AbstractNode<()> { + fn new (box: RenderBox) -> AbstractNode<()> { + unsafe { + transmute(box.node()) + } + } +} + impl ExtraDisplayListData for Nothing { fn new(_: RenderBox) -> Nothing { () diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index a3e7901c3c05..109bc3c7dd48 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -8,7 +8,6 @@ use css::matching::MatchMethods; use css::select::new_css_select_ctx; use layout::aux::{LayoutData, LayoutAuxMethods}; -use layout::box::RenderBox; use layout::box_builder::LayoutTreeBuilder; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder}; @@ -17,7 +16,9 @@ use layout::incremental::{RestyleDamage, BubbleWidths}; use std::cast::transmute; use std::cell::Cell; +use std::uint; use std::comm::{Port}; +use extra::arc::Arc; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; @@ -44,6 +45,7 @@ use servo_net::local_image_cache::LocalImageCache; use servo_util::tree::TreeNodeRef; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; +use servo_util::range::Range; use extra::url::Url; struct LayoutTask { @@ -51,7 +53,7 @@ struct LayoutTask { port: Port, constellation_chan: ConstellationChan, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan>, image_cache_task: ImageCacheTask, local_image_cache: @mut LocalImageCache, font_ctx: @mut FontContext, @@ -61,6 +63,8 @@ struct LayoutTask { /// This is used to root reader data. layout_refs: ~[@mut LayoutData], + display_list: Option>>>, + css_select_ctx: @mut SelectCtx, profiler_chan: ProfilerChan, } @@ -70,7 +74,7 @@ impl LayoutTask { port: Port, constellation_chan: ConstellationChan, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan>, img_cache_task: ImageCacheTask, opts: Opts, profiler_chan: ProfilerChan) { @@ -99,7 +103,7 @@ impl LayoutTask { port: Port, constellation_chan: ConstellationChan, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan>, image_cache_task: ImageCacheTask, opts: &Opts, profiler_chan: ProfilerChan) @@ -117,6 +121,8 @@ impl LayoutTask { font_ctx: fctx, doc_url: None, screen_size: None, + + display_list: None, layout_refs: ~[], css_select_ctx: @mut new_css_select_ctx(), @@ -271,7 +277,7 @@ impl LayoutTask { }; // FIXME: We want to do - // for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow)) { + // for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow)) // but FloatContext values can't be reused, so we need to recompute them every time. for flow in layout_root.traverse_preorder() { flow.assign_widths(&mut layout_ctx); @@ -291,7 +297,7 @@ impl LayoutTask { ctx: &layout_ctx, }; - let display_list = @Cell::new(DisplayList::new()); + let display_list = ~Cell::new(DisplayList::new::>()); // TODO: Set options on the builder before building. // TODO: Be smarter about what needs painting. @@ -303,11 +309,39 @@ impl LayoutTask { base.position.size }; + let display_list = Arc::new(display_list.take()); + + for i in range(0,display_list.get().list.len()) { + let node: AbstractNode = unsafe { + transmute(display_list.get().list[i].base().extra) + }; + assert!(node.has_layout_data(), "Node has display item but no layout data"); + + let layout_data = node.layout_data(); + layout_data.boxes.display_list = Some(display_list.clone()); + + if layout_data.boxes.range.is_none() { + debug!("Creating initial range for node"); + layout_data.boxes.range = Some(Range::new(i,1)); + } else { + debug!("Appending item to range"); + unsafe { + let old_node: AbstractNode<()> = transmute(node); + assert!(old_node == display_list.get().list[i-1].base().extra, + "Non-contiguous arrangement of display items"); + } + + layout_data.boxes.range.unwrap().extend_by(1); + } + } + let render_layer = RenderLayer { - display_list: display_list.take(), + display_list: display_list.clone(), size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint) }; + self.display_list = Some(display_list.clone()); + self.render_chan.send(RenderMsg(render_layer)); } // time(layout: display list building) } @@ -330,19 +364,15 @@ impl LayoutTask { transmute(node) }; - let response = match node.layout_data().flow { - None => { - error!("no flow present"); - Err(()) - } - Some(flow) => { - let start_val: Option> = None; - let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| { - match acc { - Some(acc) => Some(acc.union(&box.content_box())), - None => Some(box.content_box()) + let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) { + (Some(display_list), Some(range)) => { + let mut rect: Option> = None; + for i in range.eachi() { + rect = match rect { + Some(acc) => Some(acc.union(&display_list.get().list[i].bounds())), + None => Some(display_list.get().list[i].bounds()) } - }; + } match rect { None => { @@ -352,6 +382,10 @@ impl LayoutTask { Some(rect) => Ok(ContentBoxResponse(rect)) } } + _ => { + error!("no display list present"); + Err(()) + } }; reply_chan.send(response) @@ -362,71 +396,49 @@ impl LayoutTask { transmute(node) }; - let response = match node.layout_data().flow { - None => Err(()), - Some(flow) => { + let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) { + (Some(display_list), Some(range)) => { let mut boxes = ~[]; - for box in flow.iter_all_boxes() { - if box.node() == node { - boxes.push(box.content_box()); - } + for i in range.eachi() { + boxes.push(display_list.get().list[i].bounds()); } Ok(ContentBoxesResponse(boxes)) } + _ => Err(()), }; reply_chan.send(response) } - HitTestQuery(node, point, reply_chan) => { - // FIXME: Isolate this transmutation into a single "bridge" module. - let node: AbstractNode = unsafe { - transmute(node) - }; - let mut flow_node: AbstractNode = node; - for node in node.traverse_preorder() { - if node.layout_data().flow.is_some() { - flow_node = node; - break; - } - }; - - let response = match flow_node.layout_data().flow { - None => { - debug!("HitTestQuery: flow is None"); - Err(()) - } - Some(flow) => { - let layout_ctx = self.build_layout_context(); - let builder = DisplayListBuilder { - ctx: &layout_ctx, - }; - let display_list: @Cell> = - @Cell::new(DisplayList::new()); - - do flow.partially_traverse_preorder |this_flow| { - this_flow.build_display_list(&builder, - &flow.position(), - display_list) - - } - let (x, y) = (Au::from_frac_px(point.x as float), - Au::from_frac_px(point.y as float)); - let mut resp = Err(()); - let display_list = &display_list.take().list; - // iterate in reverse to ensure we have the most recently painted render box - for display_item in display_list.rev_iter() { - let bounds = display_item.bounds(); - // TODO this check should really be performed by a method of DisplayItem - if x <= bounds.origin.x + bounds.size.width && - bounds.origin.x <= x && - y < bounds.origin.y + bounds.size.height && - bounds.origin.y < y { - resp = Ok(HitTestResponse(display_item.base().extra.node())); - break; + HitTestQuery(_, point, reply_chan) => { + let response = { + match self.display_list { + Some(ref list) => { + let display_list = list.get(); + let (x, y) = (Au::from_frac_px(point.x as float), + Au::from_frac_px(point.y as float)); + let mut resp = Err(()); + // iterate in reverse to ensure we have the most recently painted render box + for display_item in display_list.list.rev_iter() { + let bounds = display_item.bounds(); + // TODO this check should really be performed by a method of DisplayItem + if x <= bounds.origin.x + bounds.size.width && + bounds.origin.x <= x && + y < bounds.origin.y + bounds.size.height && + bounds.origin.y < y { + let node: AbstractNode = unsafe { + transmute(display_item.base().extra) + }; + resp = Ok(HitTestResponse(node)); + break; + } } + resp } - resp + None => { + error!("Can't hit test: no display list"); + Err(()) + }, } }; diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs index 33e70ed6ebfb..0204748c20bb 100644 --- a/src/components/main/pipeline.rs +++ b/src/components/main/pipeline.rs @@ -12,6 +12,7 @@ use layout::layout_task::LayoutTask; use script::layout_interface::LayoutChan; use script::script_task::{ExecuteMsg, LoadMsg}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId}; +use script::dom::node::AbstractNode; use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan}; use script::script_task; use servo_net::image_cache_task::ImageCacheTask; @@ -28,7 +29,7 @@ pub struct Pipeline { subpage_id: Option, script_chan: ScriptChan, layout_chan: LayoutChan, - render_chan: RenderChan, + render_chan: RenderChan>, /// The most recently loaded url url: Option, } @@ -130,7 +131,7 @@ impl Pipeline { subpage_id: Option, script_chan: ScriptChan, layout_chan: LayoutChan, - render_chan: RenderChan) + render_chan: RenderChan>) -> Pipeline { Pipeline { id: id,