diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index 7a7e3a72c30..46b7525100a 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -64,6 +64,7 @@ pub enum DisplayItem { TextDisplayItemClass(~TextDisplayItem), ImageDisplayItemClass(~ImageDisplayItem), BorderDisplayItemClass(~BorderDisplayItem), + ClipDisplayItemClass(~ClipDisplayItem) } /// Information common to all display items. @@ -111,6 +112,12 @@ pub struct BorderDisplayItem { style: SideOffsets2D } +pub struct ClipDisplayItem { + base: BaseDisplayItem, + child_list: ~[DisplayItem], + need_clip: bool +} + impl DisplayItem { /// Renders this display item into the given render context. fn draw_into_context(&self, render_context: &RenderContext) { @@ -119,6 +126,18 @@ impl DisplayItem { render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color) } + ClipDisplayItemClass(ref clip) => { + if clip.need_clip { + render_context.draw_push_clip(&clip.base.bounds); + } + for item in clip.child_list.iter() { + (*item).draw_into_context(render_context); + } + if clip.need_clip { + render_context.draw_pop_clip(); + } + } + TextDisplayItemClass(ref text) => { debug!("Drawing text at {:?}.", text.base.bounds); @@ -182,7 +201,8 @@ impl DisplayItem { SolidColorDisplayItemClass(ref solid_color) => transmute_region(&solid_color.base), TextDisplayItemClass(ref text) => transmute_region(&text.base), ImageDisplayItemClass(ref image_item) => transmute_region(&image_item.base), - BorderDisplayItemClass(ref border) => transmute_region(&border.base) + BorderDisplayItemClass(ref border) => transmute_region(&border.base), + ClipDisplayItemClass(ref clip) => transmute_region(&clip.base), } } } diff --git a/src/components/gfx/render_context.rs b/src/components/gfx/render_context.rs old mode 100644 new mode 100755 index 8fe801cd3f4..fd6bb947fd9 --- a/src/components/gfx/render_context.rs +++ b/src/components/gfx/render_context.rs @@ -101,6 +101,28 @@ impl<'self> RenderContext<'self> { &draw_opts); } + pub fn draw_push_clip(&self, bounds: &Rect) { + let rect = bounds.to_azure_rect(); + let path_builder = self.draw_target.create_path_builder(); + + let left_top = Point2D(rect.origin.x, rect.origin.y); + let right_top = Point2D(rect.origin.x + rect.size.width, rect.origin.y); + let left_bottom = Point2D(rect.origin.x, rect.origin.y + rect.size.height); + let right_bottom = Point2D(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); + + path_builder.move_to(left_top); + path_builder.line_to(right_top); + path_builder.line_to(right_bottom); + path_builder.line_to(left_bottom); + + let path = path_builder.finish(); + self.draw_target.push_clip(&path); + } + + pub fn draw_pop_clip(&self) { + self.draw_target.pop_clip(); + } + pub fn draw_image(&self, bounds: Rect, image: Arc<~Image>) { let image = image.get(); let size = Size2D(image.width as i32, image.height as i32); diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 7f4514e3c4e..09a113d4a8e 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -9,7 +9,7 @@ use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass}; use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass}; use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem}; -use gfx::display_list::{TextDisplayItemClass}; +use gfx::display_list::{TextDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass}; use gfx::font::{FontStyle, FontWeight300}; use gfx::text::text_run::TextRun; use gfx::color::rgb; @@ -28,7 +28,7 @@ use std::unstable::raw::Box; use style::ComputedValues; use style::computed_values::{ border_style, clear, float, font_family, font_style, line_height, - position, text_align, text_decoration, vertical_align, LengthOrPercentage}; + position, text_align, text_decoration, vertical_align, LengthOrPercentage, overflow}; use css::node_style::StyledNode; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; @@ -78,6 +78,13 @@ pub trait RenderBox { fail!("as_unscanned_text_render_box() called on a non-unscanned-text-render-box") } + /// If this is an unscanned text render box, returns the underlying object. Fails otherwise. + /// + /// FIXME(pcwalton): Ugly. Replace with a real downcast operation. + fn as_generic_render_box(@self) -> @GenericRenderBox { + fail!("as_generic_render_box() called on a generic-render-box") + } + /// Cleans up all memory associated with this render box. fn teardown(&self) {} @@ -188,6 +195,13 @@ impl GenericRenderBox { base: base, } } + + fn need_clip(&self) -> bool { + if self.base.node.style().Box.overflow == overflow::hidden { + return true; + } + false + } } impl RenderBox for GenericRenderBox { @@ -195,6 +209,10 @@ impl RenderBox for GenericRenderBox { GenericRenderBoxClass } + fn as_generic_render_box(@self) -> @GenericRenderBox { + self + } + fn minimum_and_preferred_widths(&self) -> (Au, Au) { let guessed_width = self.base.guess_width(); (guessed_width, guessed_width) @@ -1029,6 +1047,19 @@ impl RenderBoxUtils for @RenderBox { // Add the background to the list, if applicable. self.paint_background_if_applicable(list, &absolute_box_bounds); + do list.with_mut_ref |list| { + let item = ~ClipDisplayItem { + base: BaseDisplayItem { + bounds: absolute_box_bounds, + extra: ExtraDisplayListData::new(self), + }, + child_list: ~[], + need_clip: false + }; + list.append_item(ClipDisplayItemClass(item)); + } + + let nearest_ancestor_element = base.nearest_ancestor_element(); let color = nearest_ancestor_element.style().Color.color.to_gfx_color(); @@ -1099,6 +1130,19 @@ impl RenderBoxUtils for @RenderBox { // Add the background to the list, if applicable. self.paint_background_if_applicable(list, &absolute_box_bounds); + let generic_box = self.as_generic_render_box(); + do list.with_mut_ref |list| { + let item = ~ClipDisplayItem { + base: BaseDisplayItem { + bounds: absolute_box_bounds, + extra: ExtraDisplayListData::new(self), + }, + child_list: ~[], + need_clip: generic_box.need_clip() + }; + list.append_item(ClipDisplayItemClass(item)); + } + // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // should have a real `SERVO_DEBUG` system. debug!("{:?}", { @@ -1122,11 +1166,23 @@ impl RenderBoxUtils for @RenderBox { }); }, ImageRenderBoxClass => { - let image_box = self.as_image_render_box(); - // Add the background to the list, if applicable. self.paint_background_if_applicable(list, &absolute_box_bounds); + do list.with_mut_ref |list| { + let item = ~ClipDisplayItem { + base: BaseDisplayItem { + bounds: absolute_box_bounds, + extra: ExtraDisplayListData::new(self), + }, + child_list: ~[], + need_clip: false + }; + list.append_item(ClipDisplayItemClass(item)); + } + + let image_box = self.as_image_render_box(); + match image_box.image.mutate().ptr.get_image() { Some(image) => { debug!("(building display list) building image box"); diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 27d61f9dd84..16a68c0b8b3 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -34,6 +34,7 @@ use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::float_context::{FloatContext, Invalid}; use layout::incremental::RestyleDamage; use layout::inline::InlineFlow; +use gfx::display_list::{ClipDisplayItemClass}; use extra::dlist::{DList, DListIterator, MutDListIterator}; use extra::container::Deque; @@ -526,7 +527,34 @@ impl<'self> MutableFlowUtils for &'self mut FlowContext { InlineFlowClass => self.as_inline().build_display_list_inline(builder, dirty, list), FloatFlowClass => self.as_float().build_display_list_float(builder, dirty, list), _ => fail!("Tried to build_display_list_recurse of flow: {:?}", self), + }; + + if list.with_mut_ref(|list| list.list.len() == 0) { + return true; } + + let child_list = ~Cell::new(DisplayList::new()); + for kid in child_iter(self) { + kid.build_display_list(builder,dirty,child_list); + } + + do list.with_mut_ref |list| { + let result = list.list.mut_rev_iter().position(|item| { + match *item { + ClipDisplayItemClass(ref mut item) => { + item.child_list.push_all_move(child_list.take().list); + true + }, + _ => false, + } + }); + + if result.is_none() { + fail!("fail to find parent item"); + } + + } + true } } diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 46e9ca16457..edd7c8475da 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -22,7 +22,7 @@ use extra::arc::{Arc, RWArc}; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; -use gfx::display_list::DisplayList; +use gfx::display_list::{DisplayList,DisplayItem,ClipDisplayItemClass}; use gfx::font_context::FontContext; use gfx::opts::Opts; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer}; @@ -41,7 +41,6 @@ use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_util::geometry::Au; -use servo_util::range::Range; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; use servo_util::tree::TreeNodeRef; @@ -181,26 +180,6 @@ impl<'self> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'s } } -/// The display list building traversal. In WebKit this corresponds to `paint`. In Gecko this -/// corresponds to `BuildDisplayListForChild`. -struct DisplayListBuildingTraversal<'self> { - builder: DisplayListBuilder<'self>, - root_pos: Rect, - display_list: ~Cell>>, -} - -impl<'self> PreorderFlowTraversal for DisplayListBuildingTraversal<'self> { - #[inline] - fn process(&mut self, _: &mut FlowContext) -> bool { - true - } - - #[inline] - fn should_prune(&mut self, flow: &mut FlowContext) -> bool { - flow.build_display_list(&self.builder, &self.root_pos, self.display_list) - } -} - struct LayoutImageResponder { id: PipelineId, script_chan: ScriptChan, @@ -472,49 +451,20 @@ impl LayoutTask { // Build the display list if necessary, and send it to the renderer. if data.goal == ReflowForDisplay { do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) { - // TODO: Set options on the builder before building. - // TODO: Be smarter about what needs painting. - let mut traversal = DisplayListBuildingTraversal { - builder: DisplayListBuilder { + let root_size = flow::base(layout_root).position.size; + let display_list= ~Cell::new(DisplayList::>::new()); + let dirty = flow::base(layout_root).position.clone(); + layout_root.build_display_list( + &DisplayListBuilder { ctx: &layout_ctx, }, - root_pos: flow::base(layout_root).position.clone(), - display_list: ~Cell::new(DisplayList::>::new()), - }; + &dirty, + display_list); - let _ = layout_root.traverse_preorder(&mut traversal); - - let root_size = flow::base(layout_root).position.size; - - let display_list = Arc::new(traversal.display_list.take()); + 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) - }; - - // FIXME(pcwalton): Why are we cloning the display list here?! - match *node.mutate_layout_data().ptr { - Some(ref mut layout_data) => { - let boxes = &mut layout_data.boxes; - boxes.display_list = Some(display_list.clone()); - - if boxes.range.is_none() { - debug!("Creating initial range for node"); - 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"); - } - - boxes.range.unwrap().extend_by(1); - } - } - None => fail!("no layout data"), - } + self.display_item_bound_to_node(&display_list.get().list[i]); } let mut color = color::rgba(255.0, 255.0, 255.0, 255.0); @@ -556,6 +506,42 @@ impl LayoutTask { data.script_chan.send(ReflowCompleteMsg(self.id, data.id)); } + fn display_item_bound_to_node(&mut self,item: &DisplayItem>) { + let node: AbstractNode = unsafe { + transmute(item.base().extra) + }; + + match *node.mutate_layout_data().ptr { + Some(ref mut layout_data) => { + let boxes = &mut layout_data.boxes; + + if boxes.display_bound_list.is_none() { + boxes.display_bound_list = Some(~[]); + } + match boxes.display_bound_list { + Some(ref mut list) => list.push(item.base().bounds), + None => {} + } + + if boxes.display_bound.is_none() { + boxes.display_bound = Some(item.base().bounds); + } else { + boxes.display_bound = Some(boxes.display_bound.unwrap().union(&item.base().bounds)); + } + } + None => fail!("no layout data"), + } + + match *item { + ClipDisplayItemClass(ref cc) => { + for item in cc.child_list.iter() { + self.display_item_bound_to_node(item); + } + } + _ => {} + } + } + /// Handles a query from the script task. This is the main routine that DOM functions like /// `getClientRects()` or `getBoundingClientRect()` ultimately invoke. fn handle_query(&self, query: LayoutQuery) { @@ -570,19 +556,8 @@ impl LayoutTask { // FIXME(pcwalton): Why are we cloning the display list here?! let layout_data = node.borrow_layout_data(); let boxes = &layout_data.ptr.as_ref().unwrap().boxes; - match (boxes.display_list.clone(), 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()) - } - } - rect - } + match boxes.display_bound { + Some(_) => boxes.display_bound, _ => { let mut acc: Option> = None; for child in node.children() { @@ -614,10 +589,10 @@ impl LayoutTask { -> ~[Rect] { let layout_data = node.borrow_layout_data(); let boxes = &layout_data.ptr.as_ref().unwrap().boxes; - match (boxes.display_list.clone(), boxes.range) { - (Some(display_list), Some(range)) => { - for i in range.eachi() { - box_accumulator.push(display_list.get().list[i].bounds()); + match boxes.display_bound_list { + Some(ref display_bound_list) => { + for item in display_bound_list.iter() { + box_accumulator.push(*item); } } _ => { @@ -634,29 +609,54 @@ impl LayoutTask { reply_chan.send(ContentBoxesResponse(boxes)) } HitTestQuery(_, point, reply_chan) => { + fn hit_test(x:Au, y:Au, list: &[DisplayItem>]) -> Option { + + 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; + } + } + _ => {} + } + } + + for item in list.rev_iter() { + match *item { + ClipDisplayItemClass(_) => continue, + _ => {} + } + let bounds = 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(item.base().extra) + }; + let resp = Some(HitTestResponse(node)); + return resp; + } + } + + let ret: Option = None; + ret + } let response = { match self.display_list { Some(ref list) => { let display_list = list.get(); let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64)); - 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; - } + let resp = hit_test(x,y,display_list.list); + if resp.is_none() { + Err(()) + } else { + Ok(resp.unwrap()) } - resp } None => { error!("Can't hit test: no display list"); diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs index e6409bb6dbc..306f613f715 100644 --- a/src/components/main/layout/util.rs +++ b/src/components/main/layout/util.rs @@ -15,18 +15,22 @@ use std::cast; use std::iter::Enumerate; use std::vec::VecIterator; use style::{ComputedValues, PropertyDeclaration}; +use geom::rect::Rect; +use servo_util::geometry::Au; /// The boxes associated with a node. pub struct DisplayBoxes { display_list: Option>>>, - range: Option, + display_bound_list: Option<~[Rect]>, + display_bound: Option> } impl DisplayBoxes { pub fn init() -> DisplayBoxes { DisplayBoxes { display_list: None, - range: None, + display_bound_list: None, + display_bound: None, } } } diff --git a/src/components/style/properties.rs.mako b/src/components/style/properties.rs.mako index e86de37b89a..6c46de663be 100644 --- a/src/components/style/properties.rs.mako +++ b/src/components/style/properties.rs.mako @@ -371,6 +371,7 @@ pub mod longhands { // CSS 2.1, Section 11 - Visual effects + ${single_keyword("overflow", "visible hidden", inherited=False)} // TODO: scroll auto // CSS 2.1, Section 12 - Generated content, automatic numbering, and lists diff --git a/src/support/azure/rust-azure b/src/support/azure/rust-azure index 13fbdbeddfc..60ee86c802f 160000 --- a/src/support/azure/rust-azure +++ b/src/support/azure/rust-azure @@ -1 +1 @@ -Subproject commit 13fbdbeddfccbc3e451fa9ae47f334f4f626a051 +Subproject commit 60ee86c802f45a8e87fa462cd21d4b074f837cec