diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-03-28 12:55:29 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-04-03 14:50:56 -0700 |
commit | cd9d824c21167721fca40a4eff9563b8316d6c1a (patch) | |
tree | 842382875b8155467b5bb08ea347ad0131760f6c /src/components/main/layout/layout_task.rs | |
parent | f8e3e50db5041fd753d0cb83a7cb1464725cc976 (diff) | |
download | servo-cd9d824c21167721fca40a4eff9563b8316d6c1a.tar.gz servo-cd9d824c21167721fca40a4eff9563b8316d6c1a.zip |
servo: Implement stacking contexts and allow multiple layers per
pipeline. This handles fixed positioning mostly correctly.
Diffstat (limited to 'src/components/main/layout/layout_task.rs')
-rw-r--r-- | src/components/main/layout/layout_task.rs | 166 |
1 files changed, 102 insertions, 64 deletions
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index b9050816668..aad21667c2f 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -11,22 +11,22 @@ use css::select::new_stylist; use css::node_style::StyledNode; use layout::construct::{FlowConstructionResult, NoConstructionResult}; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, ToGfxColor}; +use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor}; use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow; use layout::incremental::RestyleDamage; use layout::parallel::PaddedUnsafeFlow; use layout::parallel; -use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper}; +use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods}; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; use extra::url::Url; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; -use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator}; -use gfx::display_list::{DisplayList, DisplayListCollection}; +use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList}; +use gfx::display_list::{OpaqueNode, StackingContext}; use gfx::font_context::{FontContext, FontContextInfo}; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer}; use gfx::{render_task, color}; @@ -41,18 +41,20 @@ use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, Pr use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress}; use script::layout_interface::{ReflowForDisplay, ReflowMsg}; use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg}; +use servo_msg::compositor_msg::Scrollable; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_util::geometry::Au; +use servo_util::geometry; use servo_util::opts::Opts; +use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec1}; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; use servo_util::task::send_on_failure; use servo_util::workqueue::WorkQueue; use std::cast::transmute; use std::cast; -use std::cell::RefCell; use std::comm::Port; use std::mem; use std::ptr; @@ -79,7 +81,7 @@ pub struct LayoutTask { script_chan: ScriptChan, /// The channel on which messages can be sent to the painting task. - render_chan: RenderChan<OpaqueNode>, + render_chan: RenderChan, /// The channel on which messages can be sent to the image cache. image_cache_task: ImageCacheTask, @@ -91,7 +93,7 @@ pub struct LayoutTask { screen_size: Size2D<Au>, /// A cached display list. - display_list_collection: Option<Arc<DisplayListCollection<OpaqueNode>>>, + display_list: Option<Arc<DisplayList>>, stylist: ~Stylist, @@ -251,7 +253,7 @@ impl LayoutTask { constellation_chan: ConstellationChan, failure_msg: Failure, script_chan: ScriptChan, - render_chan: RenderChan<OpaqueNode>, + render_chan: RenderChan, img_cache_task: ImageCacheTask, opts: Opts, profiler_chan: ProfilerChan, @@ -282,7 +284,7 @@ impl LayoutTask { chan: LayoutChan, constellation_chan: ConstellationChan, script_chan: ScriptChan, - render_chan: RenderChan<OpaqueNode>, + render_chan: RenderChan, image_cache_task: ImageCacheTask, opts: &Opts, profiler_chan: ProfilerChan) @@ -306,7 +308,7 @@ impl LayoutTask { local_image_cache: local_image_cache, screen_size: screen_size, - display_list_collection: None, + display_list: None, stylist: ~new_stylist(), initial_css_values: Arc::new(style::initial_values()), parallel_traversal: parallel_traversal, @@ -339,7 +341,7 @@ impl LayoutTask { stylist: &*self.stylist, initial_css_values: self.initial_css_values.clone(), url: (*url).clone(), - reflow_root: OpaqueNode::from_layout_node(reflow_root), + reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root), opts: self.opts.clone(), } } @@ -627,23 +629,27 @@ impl LayoutTask { // Build the display list if necessary, and send it to the renderer. if data.goal == ReflowForDisplay { profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || { - let root_size = flow::base(layout_root).position.size; - let root_abs_position = Point2D(Au::new(0), Au::new(0)); - let mut display_list_collection = DisplayListCollection::new(); - display_list_collection.add_list(DisplayList::<OpaqueNode>::new()); - let display_list_collection = ~RefCell::new(display_list_collection); - let dirty = flow::base(layout_root).position.clone(); - let display_list_builder = DisplayListBuilder { + let mut root_stacking_context = StackingContext::new(); + let mut display_list_builder = DisplayListBuilder { ctx: &layout_ctx, + layers: SmallVec0::new(), + dirty: flow::base(layout_root).position.clone(), + }; + let display_list_building_info = DisplayListBuildingInfo { + containing_block_size: flow::base(layout_root).position.size, + absolute_containing_block_position: Point2D(Au(0), Au(0)), + layers_needed_for_positioned_flows: false, }; - layout_root.build_display_lists(&display_list_builder, &root_size, - root_abs_position, - &dirty, 0u, display_list_collection); - let display_list_collection = Arc::new(display_list_collection.unwrap()); + layout_root.build_display_list(&mut root_stacking_context, + &mut display_list_builder, + &display_list_building_info); - let mut color = color::rgba(255.0, 255.0, 255.0, 255.0); + let display_list = Arc::new(root_stacking_context.flatten()); + // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor + // it with extreme prejudice. + let mut color = color::rgba(255.0, 255.0, 255.0, 255.0); for child in node.traverse_preorder() { if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) || child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) { @@ -668,18 +674,33 @@ impl LayoutTask { } } + let root_size = Size2D(display_list_building_info.containing_block_size + .width + .to_nearest_px() as uint, + display_list_building_info.containing_block_size + .height + .to_nearest_px() as uint); let render_layer = RenderLayer { - display_list_collection: display_list_collection.clone(), - size: Size2D(root_size.width.to_nearest_px() as uint, - root_size.height.to_nearest_px() as uint), - color: color + id: layout_root.layer_id(0), + display_list: display_list.clone(), + rect: Rect(Point2D(0u, 0u), root_size), + color: color, + scroll_policy: Scrollable, }; - self.display_list_collection = Some(display_list_collection.clone()); + self.display_list = Some(display_list.clone()); + + let mut layers = SmallVec1::new(); + layers.push(render_layer); + let DisplayListBuilder { + layers: sublayers, + .. + } = display_list_builder; + layers.push_all_move(sublayers); debug!("Layout done!"); - self.render_chan.send(RenderMsg(render_layer)); + self.render_chan.send(RenderMsg(layers)); }); } @@ -701,15 +722,14 @@ impl LayoutTask { // The neat thing here is that in order to answer the following two queries we only // need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`. ContentBoxQuery(node, reply_chan) => { - let node = OpaqueNode::from_script_node(node); + let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node); - fn union_boxes_for_node<'a>( - accumulator: &mut Option<Rect<Au>>, - mut iter: DisplayItemIterator<'a,OpaqueNode>, + fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>, + mut iter: DisplayItemIterator, node: OpaqueNode) { for item in iter { union_boxes_for_node(accumulator, item.children(), node); - if item.base().extra == node { + if item.base().node == node { match *accumulator { None => *accumulator = Some(item.base().bounds), Some(ref mut acc) => *acc = acc.union(&item.base().bounds), @@ -719,41 +739,49 @@ impl LayoutTask { } let mut rect = None; - for display_list in self.display_list_collection.as_ref().unwrap().get().iter() { - union_boxes_for_node(&mut rect, display_list.iter(), node); + match self.display_list { + None => fail!("no display list!"), + Some(ref display_list) => { + union_boxes_for_node(&mut rect, display_list.get().iter(), node) + } } reply_chan.send(ContentBoxResponse(rect.unwrap_or(Au::zero_rect()))) } ContentBoxesQuery(node, reply_chan) => { - let node = OpaqueNode::from_script_node(node); + let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node); - fn add_boxes_for_node<'a>( - accumulator: &mut ~[Rect<Au>], - mut iter: DisplayItemIterator<'a,OpaqueNode>, + fn add_boxes_for_node(accumulator: &mut ~[Rect<Au>], + mut iter: DisplayItemIterator, node: OpaqueNode) { for item in iter { add_boxes_for_node(accumulator, item.children(), node); - if item.base().extra == node { + if item.base().node == node { accumulator.push(item.base().bounds) } } } let mut boxes = ~[]; - for display_list in self.display_list_collection.as_ref().unwrap().get().iter() { - add_boxes_for_node(&mut boxes, display_list.iter(), node); + match self.display_list { + None => fail!("no display list!"), + Some(ref display_list) => { + add_boxes_for_node(&mut boxes, display_list.get().iter(), node) + } } reply_chan.send(ContentBoxesResponse(boxes)) } HitTestQuery(_, point, reply_chan) => { - fn hit_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>]) + fn hit_test(x: Au, y: Au, list: &[DisplayItem]) -> Option<HitTestResponse> { for item in list.rev_iter() { match *item { ClipDisplayItemClass(ref cc) => { - let ret = hit_test(x, y, cc.child_list); - if !ret.is_none() { - return ret; + if !cc.need_clip || geometry::rect_contains_point(cc.base.bounds, + Point2D(x, y)) { + let ret = hit_test(x, y, cc.child_list.as_slice()); + if !ret.is_none() { + return ret + } } } _ => {} @@ -774,31 +802,35 @@ impl LayoutTask { y < bounds.origin.y + bounds.size.height && bounds.origin.y <= y { return Some(HitTestResponse(item.base() - .extra + .node .to_untrusted_node_address())) } } let ret: Option<HitTestResponse> = None; ret } - for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() { - let (x, y) = (Au::from_frac_px(point.x as f64), - Au::from_frac_px(point.y as f64)); - let resp = hit_test(x,y,display_list.list); - if resp.is_some() { - reply_chan.send(Ok(resp.unwrap())); - return - } + let (x, y) = (Au::from_frac_px(point.x as f64), + Au::from_frac_px(point.y as f64)); + let resp = match self.display_list { + None => fail!("no display list!"), + Some(ref display_list) => hit_test(x, y, display_list.get().list.as_slice()), + }; + if resp.is_some() { + reply_chan.send(Ok(resp.unwrap())); + return } reply_chan.send(Err(())); } MouseOverQuery(_, point, reply_chan) => { - fn mouse_over_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>], result: &mut ~[UntrustedNodeAddress]) { + fn mouse_over_test(x: Au, + y: Au, + list: &[DisplayItem], + result: &mut ~[UntrustedNodeAddress]) { for item in list.rev_iter() { match *item { ClipDisplayItemClass(ref cc) => { - mouse_over_test(x, y, cc.child_list, result); + mouse_over_test(x, y, cc.child_list.as_slice(), result); } _ => {} } @@ -814,18 +846,24 @@ impl LayoutTask { y < bounds.origin.y + bounds.size.height && bounds.origin.y <= y { result.push(item.base() - .extra + .node .to_untrusted_node_address()); } } } let mut mouse_over_list:~[UntrustedNodeAddress] = ~[]; - for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() { - let (x, y) = (Au::from_frac_px(point.x as f64), - Au::from_frac_px(point.y as f64)); - mouse_over_test(x,y,display_list.list, &mut mouse_over_list); - } + let (x, y) = (Au::from_frac_px(point.x as f64), + Au::from_frac_px(point.y as f64)); + match self.display_list { + None => fail!("no display list!"), + Some(ref display_list) => { + mouse_over_test(x, + y, + display_list.get().list.as_slice(), + &mut mouse_over_list); + } + }; if mouse_over_list.is_empty() { reply_chan.send(Err(())); |