From 5ea2c6dcfdfc24ce3730f736fcd045a13bcdc5b6 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 18 Dec 2014 18:59:42 -0800 Subject: [PATCH 1/2] layout: Paint stacking contexts' overflow areas properly. This was making `box-shadow` not show up in many cases, in particular, but the effects were not limited to that. --- components/compositing/compositor_layer.rs | 10 +-- components/gfx/display_list/mod.rs | 40 ++++++----- components/gfx/display_list/optimizer.rs | 3 +- components/gfx/paint_task.rs | 53 ++++++++------- components/layout/block.rs | 29 ++++---- components/layout/display_list_builder.rs | 28 +++++--- components/layout/flow.rs | 66 +++++++++---------- components/layout/fragment.rs | 57 +++++++++++++--- components/layout/inline.rs | 16 +++-- components/layout/layout_task.rs | 33 +++++----- components/layout/list_item.rs | 11 +++- components/layout/sequential.rs | 8 +-- components/layout/table.rs | 11 +++- components/layout/table_caption.rs | 11 +++- components/layout/table_cell.rs | 11 +++- components/layout/table_colgroup.rs | 11 +++- components/layout/table_row.rs | 11 +++- components/layout/table_rowgroup.rs | 11 +++- components/layout/table_wrapper.rs | 11 +++- components/layout/traversal.rs | 7 +- components/msg/compositor_msg.rs | 2 +- components/servo/Cargo.lock | 2 +- ports/cef/Cargo.lock | 2 +- ports/gonk/Cargo.lock | 2 +- tests/ref/basic.list | 2 + tests/ref/position_fixed_tile_edge_3.html | 2 +- tests/ref/stacking_context_overflow_a.html | 22 +++++++ tests/ref/stacking_context_overflow_ref.html | 21 ++++++ ...g_context_overflow_relative_outline_a.html | 25 +++++++ ...context_overflow_relative_outline_ref.html | 18 +++++ 30 files changed, 357 insertions(+), 179 deletions(-) create mode 100644 tests/ref/stacking_context_overflow_a.html create mode 100644 tests/ref/stacking_context_overflow_ref.html create mode 100644 tests/ref/stacking_context_overflow_relative_outline_a.html create mode 100644 tests/ref/stacking_context_overflow_relative_outline_ref.html diff --git a/components/compositing/compositor_layer.rs b/components/compositing/compositor_layer.rs index 4c13d7fb8a0..3658172847e 100644 --- a/components/compositing/compositor_layer.rs +++ b/components/compositing/compositor_layer.rs @@ -283,8 +283,7 @@ impl CompositorLayer for Layer { self.clamp_scroll_offset_and_scroll_layer(scroll_offset + delta) } - fn clamp_scroll_offset_and_scroll_layer(&self, - new_offset: TypedPoint2D) + fn clamp_scroll_offset_and_scroll_layer(&self, new_offset: TypedPoint2D) -> ScrollEventResult { let layer_size = self.bounds.borrow().size; let content_size = calculate_content_size_for_layer(self); @@ -339,17 +338,14 @@ impl CompositorLayer for Layer { let _ = chan.send_opt(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)); } - fn scroll_layer_and_all_child_layers(&self, - new_offset: TypedPoint2D) + fn scroll_layer_and_all_child_layers(&self, new_offset: TypedPoint2D) -> bool { let mut result = false; // Only scroll this layer if it's not fixed-positioned. if self.extra_data.borrow().scroll_policy != FixedPosition { let new_offset = new_offset.to_untyped(); - *self.transform.borrow_mut() = identity().translate(new_offset.x, - new_offset.y, - 0.0); + *self.transform.borrow_mut() = identity().translate(new_offset.x, new_offset.y, 0.0); *self.content_offset.borrow_mut() = Point2D::from_untyped(&new_offset); result = true } diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index af32ffad76b..ecd7b170246 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -34,7 +34,7 @@ use servo_msg::compositor_msg::LayerId; use servo_net::image::base::Image; use servo_util::cursor::Cursor; use servo_util::dlist as servo_dlist; -use servo_util::geometry::{mod, Au, MAX_RECT, ZERO_POINT, ZERO_RECT}; +use servo_util::geometry::{mod, Au, MAX_RECT, ZERO_RECT}; use servo_util::range::Range; use servo_util::smallvec::{SmallVec, SmallVec8}; use std::fmt; @@ -160,9 +160,8 @@ pub struct StackingContext { pub layer: Option>, /// The position and size of this stacking context. pub bounds: Rect, - /// The clipping rect for this stacking context, in the coordinate system of the *parent* - /// stacking context. - pub clip_rect: Rect, + /// The overflow rect for this stacking context in its coordinate system. + pub overflow: Rect, /// The `z-index` for this stacking context. pub z_index: i32, /// The opacity of this stacking context. @@ -171,12 +170,10 @@ pub struct StackingContext { impl StackingContext { /// Creates a new stacking context. - /// - /// TODO(pcwalton): Stacking contexts should not always be clipped to their bounds, to handle - /// overflow properly. #[inline] pub fn new(display_list: Box, - bounds: Rect, + bounds: &Rect, + overflow: &Rect, z_index: i32, opacity: AzFloat, layer: Option>) @@ -184,8 +181,8 @@ impl StackingContext { StackingContext { display_list: display_list, layer: layer, - bounds: bounds, - clip_rect: Rect(ZERO_POINT, bounds.size), + bounds: *bounds, + overflow: *overflow, z_index: z_index, opacity: opacity, } @@ -196,7 +193,7 @@ impl StackingContext { paint_context: &mut PaintContext, tile_bounds: &Rect, transform: &Matrix2D, - clip_rect: Option>) { + clip_rect: Option<&Rect>) { let temporary_draw_target = paint_context.get_or_create_temporary_draw_target(self.opacity); { @@ -205,7 +202,7 @@ impl StackingContext { font_ctx: &mut *paint_context.font_ctx, page_rect: paint_context.page_rect, screen_rect: paint_context.screen_rect, - clip_rect: clip_rect, + clip_rect: clip_rect.map(|clip_rect| *clip_rect), transient_clip: None, }; @@ -252,7 +249,7 @@ impl StackingContext { positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext, &new_tile_rect, &new_transform, - Some(positioned_kid.clip_rect)) + Some(&positioned_kid.overflow)) } } @@ -295,7 +292,7 @@ impl StackingContext { positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext, &new_tile_rect, &new_transform, - Some(positioned_kid.clip_rect)) + Some(&positioned_kid.overflow)) } } @@ -329,11 +326,18 @@ impl StackingContext { } }; - let child_stacking_context_bounds = child_stacking_context.bounds.to_azure_rect(); - let tile_subrect = tile_bounds.intersection(&child_stacking_context_bounds) + // Translate the child's overflow region into our coordinate system. + let child_stacking_context_overflow = + child_stacking_context.overflow.translate(&child_stacking_context.bounds.origin) + .to_azure_rect(); + + // Intersect that with the current tile boundaries to find the tile boundaries that the + // child covers. + let tile_subrect = tile_bounds.intersection(&child_stacking_context_overflow) .unwrap_or(ZERO_AZURE_RECT); - let offset = tile_subrect.origin - child_stacking_context_bounds.origin; - Rect(offset, tile_subrect.size) + + // Translate the resulting rect into the child's coordinate system. + tile_subrect.translate(&-child_stacking_context.bounds.to_azure_rect().origin) } /// Places all nodes containing the point of interest into `result`, topmost first. If diff --git a/components/gfx/display_list/optimizer.rs b/components/gfx/display_list/optimizer.rs index c0dac718c51..1b7998f86c9 100644 --- a/components/gfx/display_list/optimizer.rs +++ b/components/gfx/display_list/optimizer.rs @@ -59,7 +59,8 @@ impl DisplayListOptimizer { mut stacking_contexts: I) where I: Iterator<&'a Arc> { for stacking_context in stacking_contexts { - if self.visible_rect.intersects(&stacking_context.bounds) { + let overflow = stacking_context.overflow.translate(&stacking_context.bounds.origin); + if self.visible_rect.intersects(&overflow) { result_list.push_back((*stacking_context).clone()) } } diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs index 1210c34627b..5fdacfa838d 100644 --- a/components/gfx/paint_task.rs +++ b/components/gfx/paint_task.rs @@ -148,20 +148,21 @@ fn initialize_layers(compositor: &mut C, stacking_context: &StackingContext, page_position: &Point2D) { let page_position = stacking_context.bounds.origin + *page_position; - match stacking_context.layer { - None => {} - Some(ref paint_layer) => { - metadata.push(LayerMetadata { - id: paint_layer.id, - position: - Rect(Point2D(page_position.x.to_nearest_px() as uint, - page_position.y.to_nearest_px() as uint), - Size2D(stacking_context.bounds.size.width.to_nearest_px() as uint, - stacking_context.bounds.size.height.to_nearest_px() as uint)), - background_color: paint_layer.background_color, - scroll_policy: paint_layer.scroll_policy, - }) - } + if let Some(ref paint_layer) = stacking_context.layer { + // Layers start at the top left of their overflow rect, as far as the info we give to + // the compositor is concerned. + let overflow_relative_page_position = page_position + stacking_context.overflow.origin; + let layer_position = + Rect(Point2D(overflow_relative_page_position.x.to_nearest_px() as i32, + overflow_relative_page_position.y.to_nearest_px() as i32), + Size2D(stacking_context.overflow.size.width.to_nearest_px() as i32, + stacking_context.overflow.size.height.to_nearest_px() as i32)); + metadata.push(LayerMetadata { + id: paint_layer.id, + position: layer_position, + background_color: paint_layer.background_color, + scroll_policy: paint_layer.scroll_policy, + }) } for kid in stacking_context.display_list.children.iter() { @@ -384,15 +385,14 @@ impl PaintTask where C: PaintListener + Send { layer_id: LayerId) { profile(TimeProfilerCategory::Painting, None, self.time_profiler_chan.clone(), || { // Bail out if there is no appropriate stacking context. - let stacking_context = match self.root_stacking_context { - Some(ref stacking_context) => { - match display_list::find_stacking_context_with_layer_id(stacking_context, - layer_id) { - Some(stacking_context) => stacking_context, - None => return, - } + let stacking_context = if let Some(ref stacking_context) = self.root_stacking_context { + match display_list::find_stacking_context_with_layer_id(stacking_context, + layer_id) { + Some(stacking_context) => stacking_context, + None => return, } - None => return, + } else { + return }; // Divide up the layer into tiles and distribute them to workers via a simple round- @@ -547,8 +547,13 @@ impl WorkerThread { transient_clip: None, }; + // Apply a translation to start at the boundaries of the stacking context, since the + // layer's origin starts at its overflow rect's origin. + let tile_bounds = tile.page_rect.translate( + &Point2D(stacking_context.overflow.origin.x.to_subpx() as AzFloat, + stacking_context.overflow.origin.y.to_subpx() as AzFloat)); + // Apply the translation to paint the tile we want. - let tile_bounds = tile.page_rect; let matrix: Matrix2D = Matrix2D::identity(); let matrix = matrix.scale(scale as AzFloat, scale as AzFloat); let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat, @@ -561,7 +566,7 @@ impl WorkerThread { profile(TimeProfilerCategory::PaintingPerTile, None, self.time_profiler_sender.clone(), || { stacking_context.optimize_and_draw_into_context(&mut paint_context, - &tile.page_rect, + &tile_bounds, &matrix, None); paint_context.draw_target.flush(); diff --git a/components/layout/block.rs b/components/layout/block.rs index 02b58ce463e..eec1f945d1c 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -41,7 +41,7 @@ use flow::{LAYERS_NEEDED_FOR_DESCENDANTS, NEEDS_LAYER}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow::{CLEARS_LEFT, CLEARS_RIGHT}; use flow; -use fragment::{Fragment, FragmentBoundsIterator, SpecificFragmentInfo}; +use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{IntrinsicISizes, MarginCollapseInfo}; @@ -49,7 +49,7 @@ use model::{MaybeAuto, CollapsibleMargins, specified, specified_or_none}; use table::ColumnComputedInlineSize; use wrapper::ThreadSafeLayoutNode; -use geom::Size2D; +use geom::{Rect, Size2D}; use gfx::display_list::{ClippingRegion, DisplayList}; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::LayerId; @@ -1797,13 +1797,6 @@ impl Flow for BlockFlow { self.flags.insert(IS_ROOT) } - /// Return true if store overflow is delayed for this flow. - /// - /// Currently happens only for absolutely positioned flows. - fn is_store_overflow_delayed(&mut self) -> bool { - self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) - } - fn is_root(&self) -> bool { self.flags.contains(IS_ROOT) } @@ -1864,12 +1857,13 @@ impl Flow for BlockFlow { self.fragment.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + fn compute_overflow(&self) -> Rect { + self.fragment.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { if iterator.should_process(&self.fragment) { - let fragment_origin = - self.base.stacking_relative_position_of_child_fragment(&self.fragment); - iterator.process(&self.fragment, - self.fragment.stacking_relative_bounds(&fragment_origin)); + iterator.process(&self.fragment, self.fragment.compute_overflow()); } } } @@ -2455,11 +2449,14 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced { MaybeAuto::Specified(fragment.content_inline_size()) } - fn containing_block_inline_size(&self, block: &mut BlockFlow, _: Au, ctx: &LayoutContext) -> Au { + fn containing_block_inline_size(&self, block: &mut BlockFlow, _: Au, ctx: &LayoutContext) + -> Au { block.containing_block_size(ctx.shared.screen_size).inline } - fn set_flow_x_coord_if_necessary(&self, block: &mut BlockFlow, solution: ISizeConstraintSolution) { + fn set_flow_x_coord_if_necessary(&self, + block: &mut BlockFlow, + solution: ISizeConstraintSolution) { // Set the x-coordinate of the absolute flow wrt to its containing block. block.base.position.start.i = solution.inline_start; } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index a9ca6d5f547..2696bcc3dc5 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1103,7 +1103,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { background_border_level); self.base.display_list_building_result = if self.fragment.establishes_stacking_context() { - DisplayListBuildingResult::StackingContext(self.create_stacking_context(display_list, None)) + DisplayListBuildingResult::StackingContext(self.create_stacking_context(display_list, + None)) } else { DisplayListBuildingResult::Normal(display_list) } @@ -1120,7 +1121,9 @@ impl BlockFlowDisplayListBuilding for BlockFlow { !self.base.flags.contains(NEEDS_LAYER) { // We didn't need a layer. self.base.display_list_building_result = - DisplayListBuildingResult::StackingContext(self.create_stacking_context(display_list, None)); + DisplayListBuildingResult::StackingContext(self.create_stacking_context( + display_list, + None)); return } @@ -1137,7 +1140,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { Some(Arc::new(PaintLayer::new(self.layer_id(0), transparent, scroll_policy)))); - self.base.display_list_building_result = DisplayListBuildingResult::StackingContext(stacking_context) + self.base.display_list_building_result = + DisplayListBuildingResult::StackingContext(stacking_context) } fn build_display_list_for_floating_block(&mut self, @@ -1149,7 +1153,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { display_list.form_float_pseudo_stacking_context(); self.base.display_list_building_result = if self.fragment.establishes_stacking_context() { - DisplayListBuildingResult::StackingContext(self.create_stacking_context(display_list, None)) + DisplayListBuildingResult::StackingContext(self.create_stacking_context(display_list, + None)) } else { DisplayListBuildingResult::Normal(display_list) } @@ -1165,7 +1170,9 @@ impl BlockFlowDisplayListBuilding for BlockFlow { } else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { self.build_display_list_for_absolutely_positioned_block(display_list, layout_context) } else { - self.build_display_list_for_static_block(display_list, layout_context, BackgroundAndBorderLevel::Block) + self.build_display_list_for_static_block(display_list, + layout_context, + BackgroundAndBorderLevel::Block) } } @@ -1173,11 +1180,16 @@ impl BlockFlowDisplayListBuilding for BlockFlow { display_list: Box, layer: Option>) -> Arc { - let bounds = Rect(self.base.stacking_relative_position, - self.base.overflow.size.to_physical(self.base.writing_mode)); + let size = self.base.position.size.to_physical(self.base.writing_mode); + let bounds = Rect(self.base.stacking_relative_position, size); let z_index = self.fragment.style().get_box().z_index.number_or_zero(); let opacity = self.fragment.style().get_effects().opacity as f32; - Arc::new(StackingContext::new(display_list, bounds, z_index, opacity, layer)) + Arc::new(StackingContext::new(display_list, + &bounds, + &self.base.overflow, + z_index, + opacity, + layer)) } } diff --git a/components/layout/flow.rs b/components/layout/flow.rs index a36fca0df0a..f20d5a0c128 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -32,7 +32,7 @@ use display_list_builder::DisplayListBuildingResult; use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::FlowRef; -use fragment::{Fragment, FragmentBoundsIterator, SpecificFragmentInfo}; +use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo}; use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use inline::InlineFlow; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; @@ -50,7 +50,7 @@ use geom::{Point2D, Rect, Size2D}; use gfx::display_list::ClippingRegion; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::LayerId; -use servo_util::geometry::Au; +use servo_util::geometry::{Au, ZERO_RECT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use std::mem; use std::fmt; @@ -220,8 +220,11 @@ pub trait Flow: fmt::Show + ToString + Sync { /// Phase 5 of reflow: builds display lists. fn build_display_list(&mut self, layout_context: &LayoutContext); - /// Perform an iteration of fragment bounds on this flow. - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator); + /// Returns the union of all overflow rects of all of this flow's fragments. + fn compute_overflow(&self) -> Rect; + + /// Iterates through overflow rects of all of this flow's fragments. + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator); fn compute_collapsible_block_start_margin(&mut self, _layout_context: &mut LayoutContext, @@ -718,7 +721,7 @@ pub struct BaseFlow { /// The amount of overflow of this flow, relative to the containing block. Must include all the /// pixels of all the display list items for correct invalidation. - pub overflow: LogicalRect, + pub overflow: Rect, /// Data used during parallel traversals. /// @@ -894,7 +897,7 @@ impl BaseFlow { children: FlowList::new(), intrinsic_inline_sizes: IntrinsicISizes::new(), position: LogicalRect::zero(writing_mode), - overflow: LogicalRect::zero(writing_mode), + overflow: ZERO_RECT, parallel: FlowParallelInfo::new(), floats: Floats::new(writing_mode), collapsible_margins: CollapsibleMargins::new(), @@ -929,10 +932,12 @@ impl BaseFlow { /// Ensures that all display list items generated by this flow are within the flow's overflow /// rect. This should only be used for debugging. pub fn validate_display_list_geometry(&self) { - let position_with_overflow = self.position.union(&self.overflow); - let bounds = Rect(self.stacking_relative_position, - Size2D(position_with_overflow.size.inline, - position_with_overflow.size.block)); + // FIXME(pcwalton, #2795): Get the real container size. + let container_size = Size2D::zero(); + let position_with_overflow = self.position + .to_physical(self.writing_mode, container_size) + .union(&self.overflow); + let bounds = Rect(self.stacking_relative_position, position_with_overflow.size); let all_items = match self.display_list_building_result { DisplayListBuildingResult::None => Vec::new(), @@ -1175,40 +1180,29 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a { /// already been set. /// Assumption: Absolute descendants have had their overflow calculated. fn store_overflow(self, _: &LayoutContext) { - let my_position = mut_base(self).position; - - // FIXME(pcwalton): We should calculate overflow on a per-fragment basis, because their - // styles can affect overflow regions. Consider `box-shadow`, `outline`, etc.--anything - // that can draw outside the border box. For now we assume overflow is the border box, but - // that is wrong. - let mut overflow = my_position; - + // Calculate overflow on a per-fragment basis. + let mut overflow = self.compute_overflow(); if self.is_block_container() { - let writing_mode = base(self).writing_mode; - // FIXME(#2795): Get the real container size + // FIXME(#2795): Get the real container size. let container_size = Size2D::zero(); for kid in child_iter(self) { - if kid.is_store_overflow_delayed() { - // Absolute flows will be handled by their CB. If we are - // their CB, they will show up in `abs_descendants`. - continue; + if base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) { + continue } - let kid_base = base(kid); - let mut kid_overflow = kid_base.overflow.convert( - kid_base.writing_mode, writing_mode, container_size); - kid_overflow = kid_overflow.translate(&my_position.start); - overflow = overflow.union(&kid_overflow) + let kid_overflow = base(kid).overflow; + let kid_position = base(kid).position.to_physical(base(kid).writing_mode, + container_size); + overflow = overflow.union(&kid_overflow.translate(&kid_position.origin)) } - // FIXME(#2004, pcwalton): This is wrong for `position: fixed`. - for descendant_link in mut_base(self).abs_descendants.iter() { - let kid_base = base(descendant_link); - let mut kid_overflow = kid_base.overflow.convert( - kid_base.writing_mode, writing_mode, container_size); - kid_overflow = kid_overflow.translate(&my_position.start); - overflow = overflow.union(&kid_overflow) + for kid in mut_base(self).abs_descendants.iter() { + let kid_overflow = base(kid).overflow; + let kid_position = base(kid).position.to_physical(base(kid).writing_mode, + container_size); + overflow = overflow.union(&kid_overflow.translate(&kid_position.origin)) } } + mut_base(self).overflow = overflow; } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index b50f484f8d9..c66087364ed 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -23,7 +23,7 @@ use util::OpaqueNodeMethods; use wrapper::{TLayoutNode, ThreadSafeLayoutNode}; use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::OpaqueNode; +use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, OpaqueNode}; use gfx::text::glyph::CharIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; use script_traits::UntrustedNodeAddress; @@ -531,8 +531,9 @@ impl Fragment { // Foo // // - // Anonymous table fragments, SpecificFragmentInfo::TableRow and SpecificFragmentInfo::TableCell, are generated around - // `Foo`, but they shouldn't inherit the border. + // Anonymous table fragments, SpecificFragmentInfo::TableRow and + // SpecificFragmentInfo::TableCell, are generated around `Foo`, but they shouldn't inherit + // the border. let node_style = cascade_anonymous(&**node.style()); let writing_mode = node_style.writing_mode; @@ -647,7 +648,10 @@ impl Fragment { fn quantities_included_in_intrinsic_inline_size(&self) -> QuantitiesIncludedInIntrinsicInlineSizes { match self.specific { - SpecificFragmentInfo::Generic | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::InlineBlock(_) => { + SpecificFragmentInfo::Generic | + SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::InlineBlock(_) => { QuantitiesIncludedInIntrinsicInlineSizes::all() } SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => { @@ -836,9 +840,7 @@ impl Fragment { } // Return offset from original position because of `position: relative`. - pub fn relative_position(&self, - containing_block_size: &LogicalSize) - -> LogicalSize { + pub fn relative_position(&self, containing_block_size: &LogicalSize) -> LogicalSize { fn from_style(style: &ComputedValues, container_size: &LogicalSize) -> LogicalSize { let offsets = style.logical_position(); @@ -1567,6 +1569,41 @@ impl Fragment { } } } + + /// Computes the overflow rect of this fragment relative to the start of the flow. + pub fn compute_overflow(&self) -> Rect { + // FIXME(pcwalton, #2795): Get the real container size. + let container_size = Size2D::zero(); + let mut border_box = self.border_box.to_physical(self.style.writing_mode, container_size); + + // Relative position can cause us to draw outside our border box. + // + // FIXME(pcwalton): I'm not a fan of the way this makes us crawl though so many styles all + // the time. Can't we handle relative positioning by just adjusting `border_box`? + let relative_position = + self.relative_position(&LogicalSize::zero(self.style.writing_mode)); + border_box = + border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode)); + let mut overflow = border_box; + + // Box shadows cause us to draw outside our border box. + for box_shadow in self.style().get_effects().box_shadow.iter() { + let offset = Point2D(box_shadow.offset_x, box_shadow.offset_y); + let inflation = box_shadow.spread_radius + + box_shadow.blur_radius * BOX_SHADOW_INFLATION_FACTOR; + overflow = overflow.union(&border_box.translate(&offset).inflate(inflation, inflation)) + } + + // Outlines cause us to draw outside our border box. + let outline_width = self.style.get_outline().outline_width; + if outline_width != Au(0) { + overflow = overflow.union(&border_box.inflate(outline_width, outline_width)) + } + + // FIXME(pcwalton): Sometimes excessively fancy glyphs can make us draw outside our border + // box too. + overflow + } } impl fmt::Show for Fragment { @@ -1600,10 +1637,10 @@ bitflags! { } } -/// A top-down fragment bounds iteration handler. -pub trait FragmentBoundsIterator { +/// A top-down fragment overflow region iteration handler. +pub trait FragmentOverflowIterator { /// The operation to perform. - fn process(&mut self, fragment: &Fragment, bounds: Rect); + fn process(&mut self, fragment: &Fragment, overflow: Rect); /// Returns true if this fragment must be processed in-order. If this returns false, /// we skip the operation for this fragment, but continue processing siblings. diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 2230fb3e823..f8a19708d1e 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -12,7 +12,7 @@ use flow::{BaseFlow, FlowClass, Flow, MutableFlowUtils, ForceNonfloatedFlag}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow; use fragment::{Fragment, SpecificFragmentInfo}; -use fragment::{FragmentBoundsIterator, ScannedTextFragmentInfo}; +use fragment::{FragmentOverflowIterator, ScannedTextFragmentInfo}; use fragment::SplitInfo; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; @@ -20,12 +20,12 @@ use model::IntrinsicISizesContribution; use text; use collections::{RingBuf}; -use geom::Size2D; +use geom::{Rect, Size2D}; use gfx::display_list::DisplayList; use gfx::font::FontMetrics; use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; -use servo_util::geometry::Au; +use servo_util::geometry::{Au, ZERO_RECT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; use servo_util::opts; use servo_util::range::{Range, RangeIndex}; @@ -1271,7 +1271,15 @@ impl Flow for InlineFlow { fn repair_style(&mut self, _: &Arc) {} - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + fn compute_overflow(&self) -> Rect { + let mut overflow = ZERO_RECT; + for fragment in self.fragments.fragments.iter() { + overflow = overflow.union(&fragment.compute_overflow()) + } + overflow + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { for fragment in self.fragments.fragments.iter() { if iterator.should_process(fragment) { let fragment_origin = diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 090b51cee70..22e00904c75 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -10,7 +10,7 @@ use construct::ConstructionResult; use context::SharedLayoutContext; use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use flow_ref::FlowRef; -use fragment::{Fragment, FragmentBoundsIterator}; +use fragment::{Fragment, FragmentOverflowIterator}; use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT}; use layout_debug; use parallel::{mod, UnsafeFlow}; @@ -604,7 +604,7 @@ impl LayoutTask { // FIXME(pcwalton): This has not been updated to handle the stacking context relative // stuff. So the position is wrong in most cases. let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node); - let mut iterator = UnioningFragmentBoundsIterator::new(requested_node); + let mut iterator = UnioningFragmentOverflowIterator::new(requested_node); sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator); rw_data.content_box_response = iterator.rect; } @@ -616,7 +616,7 @@ impl LayoutTask { // FIXME(pcwalton): This has not been updated to handle the stacking context relative // stuff. So the position is wrong in most cases. let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node); - let mut iterator = CollectingFragmentBoundsIterator::new(requested_node); + let mut iterator = CollectingFragmentOverflowIterator::new(requested_node); sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator); rw_data.content_boxes_response = iterator.rects; } @@ -689,11 +689,12 @@ impl LayoutTask { flow::mut_base(layout_root.deref_mut()).display_list_building_result .add_to(&mut *display_list); let paint_layer = Arc::new(PaintLayer::new(layout_root.layer_id(0), - color, - Scrollable)); + color, + Scrollable)); let origin = Rect(Point2D(Au(0), Au(0)), root_size); let stacking_context = Arc::new(StackingContext::new(display_list, - origin, + &origin, + &origin, 0, 1.0, Some(paint_layer))); @@ -1016,21 +1017,21 @@ impl LayoutRPC for LayoutRPCImpl { } } -struct UnioningFragmentBoundsIterator { +struct UnioningFragmentOverflowIterator { node_address: OpaqueNode, rect: Rect, } -impl UnioningFragmentBoundsIterator { - fn new(node_address: OpaqueNode) -> UnioningFragmentBoundsIterator { - UnioningFragmentBoundsIterator { +impl UnioningFragmentOverflowIterator { + fn new(node_address: OpaqueNode) -> UnioningFragmentOverflowIterator { + UnioningFragmentOverflowIterator { node_address: node_address, rect: Rect::zero(), } } } -impl FragmentBoundsIterator for UnioningFragmentBoundsIterator { +impl FragmentOverflowIterator for UnioningFragmentOverflowIterator { fn process(&mut self, _: &Fragment, bounds: Rect) { if self.rect.is_empty() { self.rect = bounds; @@ -1044,21 +1045,21 @@ impl FragmentBoundsIterator for UnioningFragmentBoundsIterator { } } -struct CollectingFragmentBoundsIterator { +struct CollectingFragmentOverflowIterator { node_address: OpaqueNode, rects: Vec>, } -impl CollectingFragmentBoundsIterator { - fn new(node_address: OpaqueNode) -> CollectingFragmentBoundsIterator { - CollectingFragmentBoundsIterator { +impl CollectingFragmentOverflowIterator { + fn new(node_address: OpaqueNode) -> CollectingFragmentOverflowIterator { + CollectingFragmentOverflowIterator { node_address: node_address, rects: Vec::new(), } } } -impl FragmentBoundsIterator for CollectingFragmentBoundsIterator { +impl FragmentOverflowIterator for CollectingFragmentOverflowIterator { fn process(&mut self, _: &Fragment, bounds: Rect) { self.rects.push(bounds); } diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index 3b31d1fdc44..0d0c71d526c 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -12,9 +12,10 @@ use construct::FlowConstructor; use context::LayoutContext; use display_list_builder::ListItemFlowDisplayListBuilding; use flow::{Flow, FlowClass}; -use fragment::{Fragment, FragmentBoundsIterator}; +use fragment::{Fragment, FragmentOverflowIterator}; use wrapper::ThreadSafeLayoutNode; +use geom::Rect; use gfx::display_list::DisplayList; use servo_util::geometry::Au; use servo_util::opts; @@ -111,8 +112,12 @@ impl Flow for ListItemFlow { self.block_flow.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { - self.block_flow.iterate_through_fragment_bounds(iterator); + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + self.block_flow.iterate_through_fragment_overflow(iterator); } } diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 4636fa9c3eb..01ba0c098ed 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -8,7 +8,7 @@ use context::{LayoutContext, SharedLayoutContext}; use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal}; use flow; use flow_ref::FlowRef; -use fragment::FragmentBoundsIterator; +use fragment::FragmentOverflowIterator; use servo_util::opts; use traversal::{BubbleISizes, RecalcStyleForNode, ConstructFlows}; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes}; @@ -95,9 +95,9 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef, } pub fn iterate_through_flow_tree_fragment_bounds(root: &mut FlowRef, - iterator: &mut FragmentBoundsIterator) { - fn doit(flow: &mut Flow, iterator: &mut FragmentBoundsIterator) { - flow.iterate_through_fragment_bounds(iterator); + iterator: &mut FragmentOverflowIterator) { + fn doit(flow: &mut Flow, iterator: &mut FragmentOverflowIterator) { + flow.iterate_through_fragment_overflow(iterator); for kid in flow::mut_base(flow).child_iter() { doit(kid, iterator); diff --git a/components/layout/table.rs b/components/layout/table.rs index 8f46ef868ff..81c9f40d43d 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -13,13 +13,14 @@ use context::LayoutContext; use floats::FloatKind; use flow::{mod, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; use flow::ImmutableFlowUtils; -use fragment::{Fragment, FragmentBoundsIterator}; +use fragment::{Fragment, FragmentOverflowIterator}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution}; use table_row::CellIntrinsicInlineSize; use table_wrapper::TableLayout; use wrapper::ThreadSafeLayoutNode; +use geom::Rect; use servo_util::geometry::Au; use servo_util::logical_geometry::LogicalRect; use std::cmp::max; @@ -384,8 +385,12 @@ impl Flow for TableFlow { self.block_flow.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { - self.block_flow.iterate_through_fragment_bounds(iterator); + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + self.block_flow.iterate_through_fragment_overflow(iterator); } } diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index 65493e2c24a..4864e49096a 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -10,9 +10,10 @@ use block::BlockFlow; use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::FragmentBoundsIterator; +use fragment::FragmentOverflowIterator; use wrapper::ThreadSafeLayoutNode; +use geom::Rect; use servo_util::geometry::Au; use std::fmt; use style::ComputedValues; @@ -81,8 +82,12 @@ impl Flow for TableCaptionFlow { self.block_flow.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { - self.block_flow.iterate_through_fragment_bounds(iterator); + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + self.block_flow.iterate_through_fragment_overflow(iterator); } } diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 3d1f1fd54e3..583c328f315 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -9,12 +9,13 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use context::LayoutContext; use flow::{Flow, FlowClass}; -use fragment::{Fragment, FragmentBoundsIterator}; +use fragment::{Fragment, FragmentOverflowIterator}; use model::{MaybeAuto}; use layout_debug; use table::InternalTable; use wrapper::ThreadSafeLayoutNode; +use geom::Rect; use servo_util::geometry::Au; use std::fmt; use style::{UnsignedIntegerAttribute, ComputedValues}; @@ -162,8 +163,12 @@ impl Flow for TableCellFlow { self.block_flow.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { - self.block_flow.iterate_through_fragment_bounds(iterator); + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + self.block_flow.iterate_through_fragment_overflow(iterator); } } diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index 5ce9f559268..4eb1cae8022 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -9,11 +9,12 @@ use context::LayoutContext; use css::node_style::StyledNode; use flow::{BaseFlow, FlowClass, Flow, ForceNonfloatedFlag}; -use fragment::{Fragment, FragmentBoundsIterator, SpecificFragmentInfo}; +use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo}; use layout_debug; use wrapper::ThreadSafeLayoutNode; -use servo_util::geometry::Au; +use geom::Rect; +use servo_util::geometry::{Au, ZERO_RECT}; use std::cmp::max; use std::fmt; use style::computed_values::LengthOrPercentageOrAuto; @@ -96,7 +97,11 @@ impl Flow for TableColGroupFlow { fn repair_style(&mut self, _: &Arc) {} - fn iterate_through_fragment_bounds(&self, _: &mut FragmentBoundsIterator) { + fn compute_overflow(&self) -> Rect { + ZERO_RECT + } + + fn iterate_through_fragment_overflow(&self, _: &mut FragmentOverflowIterator) { } } diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index d4979de8800..d27d7d1ffb4 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -12,12 +12,13 @@ use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow, ImmutableFlowUtils}; use flow; -use fragment::{Fragment, FragmentBoundsIterator}; +use fragment::{Fragment, FragmentOverflowIterator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use model::MaybeAuto; use wrapper::ThreadSafeLayoutNode; +use geom::Rect; use servo_util::geometry::Au; use std::cmp::max; use std::fmt; @@ -315,8 +316,12 @@ impl Flow for TableRowFlow { self.block_flow.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { - self.block_flow.iterate_through_fragment_bounds(iterator); + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + self.block_flow.iterate_through_fragment_overflow(iterator); } } diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index e004890c53d..5d3673c1612 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -10,11 +10,12 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::{Fragment, FragmentBoundsIterator}; +use fragment::{Fragment, FragmentOverflowIterator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use wrapper::ThreadSafeLayoutNode; +use geom::Rect; use servo_util::geometry::Au; use std::fmt; use style::ComputedValues; @@ -150,8 +151,12 @@ impl Flow for TableRowGroupFlow { self.block_flow.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { - self.block_flow.iterate_through_fragment_bounds(iterator); + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + self.block_flow.iterate_through_fragment_overflow(iterator); } } diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index e3d6433a36f..fa8497c46d7 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -19,10 +19,11 @@ use context::LayoutContext; use floats::FloatKind; use flow::{FlowClass, Flow, ImmutableFlowUtils}; use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; -use fragment::{Fragment, FragmentBoundsIterator}; +use fragment::{Fragment, FragmentOverflowIterator}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize}; use wrapper::ThreadSafeLayoutNode; +use geom::Rect; use servo_util::geometry::Au; use std::cmp::{max, min}; use std::fmt; @@ -358,8 +359,12 @@ impl Flow for TableWrapperFlow { self.block_flow.repair_style(new_style) } - fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { - self.block_flow.iterate_through_fragment_bounds(iterator); + fn compute_overflow(&self) -> Rect { + self.block_flow.compute_overflow() + } + + fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + self.block_flow.iterate_through_fragment_overflow(iterator); } } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 4b824160902..df56254c066 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -332,12 +332,7 @@ impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflow<'a> { } flow.assign_block_size(self.layout_context); - - // Skip store-overflow for absolutely positioned flows. That will be - // done in a separate traversal. - if !flow.is_store_overflow_delayed() { - flow.store_overflow(self.layout_context); - } + flow.store_overflow(self.layout_context); } #[inline] diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index 284e40b53af..9d280cef673 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -75,7 +75,7 @@ pub struct LayerMetadata { /// An opaque ID. This is usually the address of the flow and index of the box within it. pub id: LayerId, /// The position and size of the layer in pixels. - pub position: Rect, + pub position: Rect, /// The background color of the layer. pub background_color: Color, /// The scrolling policy of this layer. diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index e3662c1a3c4..b92f7ea77ce 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -248,7 +248,7 @@ source = "git+https://github.com/alexcrichton/gcc-rs#903e8f8a2e3766ad3d514404d45 [[package]] name = "geom" version = "0.1.0" -source = "git+https://github.com/servo/rust-geom#da6b4a36a5549cf78bf702f0b9387b5c8cf61498" +source = "git+https://github.com/servo/rust-geom#0f77c6ad116748b7e6bedbf2414d4ceea17debc2" [[package]] name = "gfx" diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 3ebeef17673..e203988b28b 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -244,7 +244,7 @@ source = "git+https://github.com/alexcrichton/gcc-rs#903e8f8a2e3766ad3d514404d45 [[package]] name = "geom" version = "0.1.0" -source = "git+https://github.com/servo/rust-geom#da6b4a36a5549cf78bf702f0b9387b5c8cf61498" +source = "git+https://github.com/servo/rust-geom#0f77c6ad116748b7e6bedbf2414d4ceea17debc2" [[package]] name = "gfx" diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 19641c7399c..6be4f9e4535 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -227,7 +227,7 @@ source = "git+https://github.com/alexcrichton/gcc-rs#903e8f8a2e3766ad3d514404d45 [[package]] name = "geom" version = "0.1.0" -source = "git+https://github.com/servo/rust-geom#da6b4a36a5549cf78bf702f0b9387b5c8cf61498" +source = "git+https://github.com/servo/rust-geom#0f77c6ad116748b7e6bedbf2414d4ceea17debc2" [[package]] name = "gfx" diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 29ad0825f00..67e6300969f 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -222,3 +222,5 @@ fragment=top != ../html/acid2.html acid2_ref.html != inset_blackborder.html blackborder_ref.html != outset_blackborder.html blackborder_ref.html == border_radius_clip_a.html border_radius_clip_ref.html +== stacking_context_overflow_a.html stacking_context_overflow_ref.html +== stacking_context_overflow_relative_outline_a.html stacking_context_overflow_relative_outline_ref.html diff --git a/tests/ref/position_fixed_tile_edge_3.html b/tests/ref/position_fixed_tile_edge_3.html index dcf2341c170..60e20fb22b0 100644 --- a/tests/ref/position_fixed_tile_edge_3.html +++ b/tests/ref/position_fixed_tile_edge_3.html @@ -1,7 +1,7 @@
-
+
diff --git a/tests/ref/stacking_context_overflow_a.html b/tests/ref/stacking_context_overflow_a.html new file mode 100644 index 00000000000..ea87a5dba55 --- /dev/null +++ b/tests/ref/stacking_context_overflow_a.html @@ -0,0 +1,22 @@ + + + + + + +
CSS IS AWESOME
+ + + diff --git a/tests/ref/stacking_context_overflow_ref.html b/tests/ref/stacking_context_overflow_ref.html new file mode 100644 index 00000000000..49991c449ab --- /dev/null +++ b/tests/ref/stacking_context_overflow_ref.html @@ -0,0 +1,21 @@ + + + + + + +
CSS IS AWESOME
+ + + + diff --git a/tests/ref/stacking_context_overflow_relative_outline_a.html b/tests/ref/stacking_context_overflow_relative_outline_a.html new file mode 100644 index 00000000000..64b965063fc --- /dev/null +++ b/tests/ref/stacking_context_overflow_relative_outline_a.html @@ -0,0 +1,25 @@ + + + + +

+ + + diff --git a/tests/ref/stacking_context_overflow_relative_outline_ref.html b/tests/ref/stacking_context_overflow_relative_outline_ref.html new file mode 100644 index 00000000000..6117d76ff8f --- /dev/null +++ b/tests/ref/stacking_context_overflow_relative_outline_ref.html @@ -0,0 +1,18 @@ + + + + +
+ + + + From bf540d590a519cb07d70ee470050e2e4a37ccfc5 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 24 Dec 2014 21:40:06 -0600 Subject: [PATCH 2/2] layout: Explicitly thread border box dimensions and relative offsets through display list building. The old `flow_origin` concept was ill-defined (sometimes the border box plus the flow origin, sometimes including horizontal margins and sometimes not, sometimes including relative position and sometimes not), leading to brittleness and test failures. This commit reworks the logic to always pass border box origins in during display list building. --- components/gfx/display_list/mod.rs | 14 + components/layout/block.rs | 84 ++-- components/layout/construct.rs | 63 ++- components/layout/display_list_builder.rs | 461 +++++++++++----------- components/layout/flow.rs | 27 +- components/layout/fragment.rs | 115 ++++-- components/layout/inline.rs | 116 ++---- components/layout/layout_task.rs | 42 +- components/layout/list_item.rs | 10 +- components/layout/sequential.rs | 33 +- components/layout/table.rs | 10 +- components/layout/table_caption.rs | 10 +- components/layout/table_cell.rs | 10 +- components/layout/table_colgroup.rs | 9 +- components/layout/table_row.rs | 10 +- components/layout/table_rowgroup.rs | 10 +- components/layout/table_wrapper.rs | 10 +- components/style/lib.rs | 2 +- components/style/properties/mod.rs.mako | 9 + tests/content/harness.js | 2 +- 20 files changed, 591 insertions(+), 456 deletions(-) diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index ecd7b170246..1905f73885c 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -609,6 +609,20 @@ impl ClippingRegion { }); self } + + /// Translates this clipping region by the given vector. + #[inline] + pub fn translate(&self, delta: &Point2D) -> ClippingRegion { + ClippingRegion { + main: self.main.translate(delta), + complex: self.complex.iter().map(|complex| { + ComplexClippingRegion { + rect: complex.rect.translate(delta), + radii: complex.radii, + } + }).collect(), + } + } } /// Metadata attached to each display item. This is useful for performing auxiliary tasks with diff --git a/components/layout/block.rs b/components/layout/block.rs index eec1f945d1c..3f7fd4be823 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -32,7 +32,7 @@ use context::LayoutContext; use css::node_style::StyledNode; use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding}; use floats::{ClearType, FloatKind, Floats, PlacementInfo}; -use flow::{AbsolutePositionInfo, BaseFlow, ForceNonfloatedFlag, FlowClass, Flow}; +use flow::{mod, AbsolutePositionInfo, BaseFlow, ForceNonfloatedFlag, FlowClass, Flow}; use flow::{ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal}; use flow::{PostorderFlowTraversal, mut_base}; use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS}; @@ -40,8 +40,7 @@ use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; use flow::{LAYERS_NEEDED_FOR_DESCENDANTS, NEEDS_LAYER}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow::{CLEARS_LEFT, CLEARS_RIGHT}; -use flow; -use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo}; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{IntrinsicISizes, MarginCollapseInfo}; @@ -49,11 +48,11 @@ use model::{MaybeAuto, CollapsibleMargins, specified, specified_or_none}; use table::ColumnComputedInlineSize; use wrapper::ThreadSafeLayoutNode; -use geom::{Rect, Size2D}; +use geom::{Point2D, Rect, Size2D}; use gfx::display_list::{ClippingRegion, DisplayList}; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::LayerId; -use servo_util::geometry::{Au, MAX_AU, ZERO_POINT}; +use servo_util::geometry::{Au, MAX_AU}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; use std::cmp::{max, min}; @@ -1769,23 +1768,40 @@ impl Flow for BlockFlow { }; // Compute the origin and clipping rectangle for children. - let origin_for_children = if self.fragment.establishes_stacking_context() { - ZERO_POINT + let relative_offset = relative_offset.to_physical(self.base.writing_mode); + let origin_for_children; + let clip_in_child_coordinate_system; + if self.fragment.establishes_stacking_context() { + // We establish a stacking context, so the position of our children is vertically + // correct, but has to be adjusted to accommodate horizontal margins. (Note the + // calculation involving `position` below and recall that inline-direction flow + // positions are relative to the edges of the margin box.) + // + // FIXME(pcwalton): Is this vertical-writing-direction-safe? + let margin = self.fragment.margin.to_physical(self.base.writing_mode); + origin_for_children = Point2D(-margin.left, Au(0)) + relative_offset; + clip_in_child_coordinate_system = + self.base.clip.translate(&-self.base.stacking_relative_position) } else { - self.base.stacking_relative_position - }; - let clip = self.fragment.clipping_region_for_children(&self.base.clip, - &origin_for_children); + origin_for_children = self.base.stacking_relative_position + relative_offset; + clip_in_child_coordinate_system = self.base.clip.clone() + } + let stacking_relative_border_box = + self.fragment + .stacking_relative_border_box(&self.base.stacking_relative_position, + &self.base + .absolute_position_info + .relative_containing_block_size, + CoordinateSystem::Self); + let clip = self.fragment.clipping_region_for_children(&clip_in_child_coordinate_system, + &stacking_relative_border_box); // Process children. - let writing_mode = self.base.writing_mode; for kid in self.base.child_iter() { if !flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) { let kid_base = flow::mut_base(kid); - kid_base.stacking_relative_position = - origin_for_children - + kid_base.position.start.to_physical(kid_base.writing_mode, container_size) - + relative_offset.to_physical(writing_mode); + kid_base.stacking_relative_position = origin_for_children + + kid_base.position.start.to_physical(kid_base.writing_mode, container_size); } flow::mut_base(kid).absolute_position_info = absolute_position_info_for_children; @@ -1832,16 +1848,20 @@ impl Flow for BlockFlow { fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) && - self.fragment.style().logical_position().inline_start == LengthOrPercentageOrAuto::Auto && - self.fragment.style().logical_position().inline_end == LengthOrPercentageOrAuto::Auto { + self.fragment.style().logical_position().inline_start == + LengthOrPercentageOrAuto::Auto && + self.fragment.style().logical_position().inline_end == + LengthOrPercentageOrAuto::Auto { self.base.position.start.i = inline_position } } fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) && - self.fragment.style().logical_position().block_start == LengthOrPercentageOrAuto::Auto && - self.fragment.style().logical_position().block_end == LengthOrPercentageOrAuto::Auto { + self.fragment.style().logical_position().block_start == + LengthOrPercentageOrAuto::Auto && + self.fragment.style().logical_position().block_end == + LengthOrPercentageOrAuto::Auto { self.base.position.start.b = block_position } } @@ -1861,16 +1881,32 @@ impl Flow for BlockFlow { self.fragment.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - if iterator.should_process(&self.fragment) { - iterator.process(&self.fragment, self.fragment.compute_overflow()); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + if !iterator.should_process(&self.fragment) { + return } + + iterator.process(&self.fragment, + &self.fragment + .stacking_relative_border_box(&self.base.stacking_relative_position, + &self.base + .absolute_position_info + .relative_containing_block_size, + CoordinateSystem::Parent) + .translate(stacking_context_position)); } } impl fmt::Show for BlockFlow { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} - {:x}: frag={} ({})", self.class(), self.base.debug_id(), self.fragment, self.base) + write!(f, + "{} - {:x}: frag={} ({})", + self.class(), + self.base.debug_id(), + self.fragment, + self.base) } } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 94605e103ec..c2911b13640 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -52,9 +52,9 @@ use servo_util::opts; use std::collections::DList; use std::mem; use std::sync::atomic::Relaxed; -use style::ComputedValues; use style::computed_values::{caption_side, display, empty_cells, float, list_style_position}; use style::computed_values::{position}; +use style::{mod, ComputedValues}; use sync::Arc; use url::Url; @@ -681,7 +681,7 @@ impl<'a> FlowConstructor<'a> { /// doesn't render its children, so this just nukes a child's fragments and creates a /// `Fragment`. fn build_fragments_for_replaced_inline_content(&mut self, node: &ThreadSafeLayoutNode) - -> ConstructionResult { + -> ConstructionResult { for kid in node.children() { kid.set_flow_construction_result(ConstructionResult::None) } @@ -697,24 +697,42 @@ impl<'a> FlowConstructor<'a> { node.restyle_damage())) } + // If the value of `display` property is not `inline`, then we have a situation like + // `
foo bar baz
`. The fragments for `foo`, `bar`, and + // `baz` had better not be absolutely positioned! + let mut style = (*node.style()).clone(); + if style.get_box().display != display::inline { + style = Arc::new(style::make_inline(&*style)) + } + // If this is generated content, then we need to initialize the accumulator with the // fragment corresponding to that content. Otherwise, just initialize with the ordinary // fragment that needs to be generated for this inline node. let fragment = if node.get_pseudo_element_type() != PseudoElementType::Normal { - let fragment_info = SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)); - Fragment::new_from_specific_info(node, fragment_info) + let fragment_info = + SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node)); + Fragment::from_opaque_node_and_style( + OpaqueNodeMethods::from_thread_safe_layout_node(node), + style, + node.restyle_damage(), + fragment_info) } else { - Fragment::new(self, node) + Fragment::from_opaque_node_and_style( + OpaqueNodeMethods::from_thread_safe_layout_node(node), + style, + node.restyle_damage(), + self.build_specific_fragment_info_for_node(node)) }; let mut fragments = DList::new(); fragments.push_back(fragment); - let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { - splits: DList::new(), - fragments: fragments, - abs_descendants: Descendants::new(), - }); + let construction_item = + ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { + splits: DList::new(), + fragments: fragments, + abs_descendants: Descendants::new(), + }); ConstructionResult::ConstructionItem(construction_item) } @@ -726,17 +744,19 @@ impl<'a> FlowConstructor<'a> { _ => unreachable!() }; - let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new(block_flow)); + let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new( + block_flow)); let fragment = Fragment::new_from_specific_info(node, fragment_info); let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); fragment_accumulator.fragments.push_back(fragment); - let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { - splits: DList::new(), - fragments: fragment_accumulator.to_dlist(), - abs_descendants: abs_descendants, - }); + let construction_item = + ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { + splits: DList::new(), + fragments: fragment_accumulator.to_dlist(), + abs_descendants: abs_descendants, + }); ConstructionResult::ConstructionItem(construction_item) } @@ -757,11 +777,12 @@ impl<'a> FlowConstructor<'a> { let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node); fragment_accumulator.fragments.push_back(fragment); - let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { - splits: DList::new(), - fragments: fragment_accumulator.to_dlist(), - abs_descendants: abs_descendants, - }); + let construction_item = + ConstructionItem::InlineFragments(InlineFragmentsConstructionResult { + splits: DList::new(), + fragments: fragment_accumulator.to_dlist(), + abs_descendants: abs_descendants, + }); ConstructionResult::ConstructionItem(construction_item) } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 2696bcc3dc5..80877ffaaf9 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -13,8 +13,9 @@ use block::BlockFlow; use context::LayoutContext; use flow::{mod, Flow, IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER}; -use fragment::{Fragment, SpecificFragmentInfo, IframeFragmentInfo, ImageFragmentInfo}; -use fragment::ScannedTextFragmentInfo; +use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo}; +use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; +use inline::InlineFlow; use list_item::ListItemFlow; use model; use util::{OpaqueNodeMethods, ToGfxColor}; @@ -36,8 +37,8 @@ use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_msg::constellation_msg::ConstellationChan; use servo_net::image::holder::ImageHolder; use servo_util::cursor::{DefaultCursor, TextCursor, VerticalTextCursor}; -use servo_util::geometry::{mod, Au, ZERO_POINT}; -use servo_util::logical_geometry::{LogicalRect, WritingMode}; +use servo_util::geometry::{mod, Au}; +use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; use std::default::Default; use std::num::FloatMath; @@ -133,16 +134,19 @@ pub trait FragmentDisplayListBuilding { absolute_bounds: &Rect, clip: &ClippingRegion); + /// Adds display items necessary to draw debug boxes around a scanned text fragment. fn build_debug_borders_around_text_fragments(&self, style: &ComputedValues, display_list: &mut DisplayList, - flow_origin: Point2D, + stacking_relative_border_box: &Rect, + stacking_relative_content_box: &Rect, text_fragment: &ScannedTextFragmentInfo, clip: &ClippingRegion); + /// Adds display items necessary to draw debug boxes around this fragment. fn build_debug_borders_around_fragment(&self, display_list: &mut DisplayList, - flow_origin: Point2D, + stacking_relative_border_box: &Rect, clip: &ClippingRegion); /// Adds the display items for this fragment to the given display list. @@ -152,12 +156,16 @@ pub trait FragmentDisplayListBuilding { /// * `display_list`: The display list to add display items to. /// * `layout_context`: The layout context. /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. - /// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow. + /// * `stacking_relative_flow_origin`: Position of the origin of the owning flow with respect + /// to its nearest ancestor stacking context. + /// * `relative_containing_block_size`: The size of the containing block that + /// `position: relative` makes use of. /// * `clip`: The region to clip the display items to. fn build_display_list(&mut self, display_list: &mut DisplayList, layout_context: &LayoutContext, - flow_origin: Point2D, + stacking_relative_flow_origin: &Point2D, + relative_containing_block_size: &LogicalSize, background_and_border_level: BackgroundAndBorderLevel, clip: &ClippingRegion); @@ -168,12 +176,17 @@ pub trait FragmentDisplayListBuilding { offset: Point2D, layout_context: &LayoutContext); - fn clipping_region_for_children(&self, current_clip: &ClippingRegion, flow_origin: &Point2D) + /// Returns the appropriate clipping region for descendants of this flow. + fn clipping_region_for_children(&self, + current_clip: &ClippingRegion, + stacking_relative_border_box: &Rect) -> ClippingRegion; /// Calculates the clipping rectangle for a fragment, taking the `clip` property into account /// per CSS 2.1 ยง 11.1.2. - fn calculate_style_specified_clip(&self, parent_clip: &ClippingRegion, origin: &Point2D) + fn calculate_style_specified_clip(&self, + parent_clip: &ClippingRegion, + stacking_relative_border_box: &Rect) -> ClippingRegion; /// Creates the text display item for one text fragment. @@ -181,23 +194,20 @@ pub trait FragmentDisplayListBuilding { display_list: &mut DisplayList, text_fragment: &ScannedTextFragmentInfo, text_color: RGBA, - offset: &Point2D, - flow_origin: &Point2D, + stacking_relative_content_box: &Rect, clip: &ClippingRegion); /// Creates the display item for a text decoration: underline, overline, or line-through. fn build_display_list_for_text_decoration(&self, display_list: &mut DisplayList, - color: RGBA, - flow_origin: &Point2D, - clip: &ClippingRegion, - logical_bounds: &LogicalRect, - offset: &Point2D); + color: &RGBA, + stacking_relative_box: &LogicalRect, + clip: &ClippingRegion); /// A helper method that `build_display_list` calls to create per-fragment-type display items. fn build_fragment_type_specific_display_items(&mut self, display_list: &mut DisplayList, - flow_origin: Point2D, + stacking_relative_border_box: &Rect, clip: &ClippingRegion); } @@ -580,20 +590,16 @@ impl FragmentDisplayListBuilding for Fragment { fn build_debug_borders_around_text_fragments(&self, style: &ComputedValues, display_list: &mut DisplayList, - flow_origin: Point2D, + stacking_relative_border_box: &Rect, + stacking_relative_content_box: &Rect, text_fragment: &ScannedTextFragmentInfo, clip: &ClippingRegion) { - // FIXME(#2795): Get the real container size + // FIXME(pcwalton, #2795): Get the real container size. let container_size = Size2D::zero(); - // Fragment position wrt to the owning flow. - let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size); - let absolute_fragment_bounds = Rect( - fragment_bounds.origin + flow_origin, - fragment_bounds.size); // Compute the text fragment bounds and draw a border surrounding them. display_list.content.push_back(DisplayItem::BorderClass(box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, + base: BaseDisplayItem::new(*stacking_relative_border_box, DisplayItemMetadata::new(self.node, style, DefaultCursor), (*clip).clone()), border_widths: SideOffsets2D::new_all_same(Au::from_px(1)), @@ -603,12 +609,12 @@ impl FragmentDisplayListBuilding for Fragment { })); // Draw a rectangle representing the baselines. - let ascent = text_fragment.run.ascent(); - let mut baseline = self.border_box.clone(); - baseline.start.b = baseline.start.b + ascent; + let mut baseline = LogicalRect::from_physical(self.style.writing_mode, + *stacking_relative_content_box, + container_size); + baseline.start.b = baseline.start.b + text_fragment.run.ascent(); baseline.size.block = Au(0); - let mut baseline = baseline.to_physical(self.style.writing_mode, container_size); - baseline.origin = baseline.origin + flow_origin; + let baseline = baseline.to_physical(self.style.writing_mode, container_size); let line_display_item = box LineDisplayItem { base: BaseDisplayItem::new(baseline, @@ -622,19 +628,11 @@ impl FragmentDisplayListBuilding for Fragment { fn build_debug_borders_around_fragment(&self, display_list: &mut DisplayList, - flow_origin: Point2D, + stacking_relative_border_box: &Rect, clip: &ClippingRegion) { - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); - // Fragment position wrt to the owning flow. - let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size); - let absolute_fragment_bounds = Rect( - fragment_bounds.origin + flow_origin, - fragment_bounds.size); - // This prints a debug border around the border of this fragment. display_list.content.push_back(DisplayItem::BorderClass(box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, + base: BaseDisplayItem::new(*stacking_relative_border_box, DisplayItemMetadata::new(self.node, &*self.style, DefaultCursor), @@ -646,7 +644,9 @@ impl FragmentDisplayListBuilding for Fragment { })); } - fn calculate_style_specified_clip(&self, parent_clip: &ClippingRegion, origin: &Point2D) + fn calculate_style_specified_clip(&self, + parent_clip: &ClippingRegion, + stacking_relative_border_box: &Rect) -> ClippingRegion { // Account for `clip` per CSS 2.1 ยง 11.1.2. let style_clip_rect = match (self.style().get_box().position, @@ -656,54 +656,49 @@ impl FragmentDisplayListBuilding for Fragment { }; // FIXME(pcwalton, #2795): Get the real container size. - let border_box = self.border_box.to_physical(self.style.writing_mode, Size2D::zero()); - let clip_origin = Point2D(border_box.origin.x + style_clip_rect.left, - border_box.origin.y + style_clip_rect.top); - let new_clip_rect = - Rect(clip_origin + *origin, - Size2D(style_clip_rect.right.unwrap_or(border_box.size.width) - clip_origin.x, - style_clip_rect.bottom.unwrap_or(border_box.size.height) - clip_origin.y)); - (*parent_clip).clone().intersect_rect(&new_clip_rect) + let clip_origin = Point2D(stacking_relative_border_box.origin.x + style_clip_rect.left, + stacking_relative_border_box.origin.y + style_clip_rect.top); + let right = style_clip_rect.right.unwrap_or(stacking_relative_border_box.size.width); + let bottom = style_clip_rect.bottom.unwrap_or(stacking_relative_border_box.size.height); + let clip_size = Size2D(right - clip_origin.x, bottom - clip_origin.y); + (*parent_clip).clone().intersect_rect(&Rect(clip_origin, clip_size)) } fn build_display_list(&mut self, display_list: &mut DisplayList, layout_context: &LayoutContext, - flow_origin: Point2D, + stacking_relative_flow_origin: &Point2D, + relative_containing_block_size: &LogicalSize, background_and_border_level: BackgroundAndBorderLevel, clip: &ClippingRegion) { // Compute the fragment position relative to the parent stacking context. If the fragment // itself establishes a stacking context, then the origin of its position will be (0, 0) // for the purposes of this computation. - let stacking_relative_flow_origin = if self.establishes_stacking_context() { - ZERO_POINT - } else { - flow_origin - }; - let absolute_fragment_bounds = - self.stacking_relative_bounds(&stacking_relative_flow_origin); + let stacking_relative_border_box = + self.stacking_relative_border_box(stacking_relative_flow_origin, + relative_containing_block_size, + CoordinateSystem::Self); - debug!("Fragment::build_display_list at rel={}, abs={}: {}", + debug!("Fragment::build_display_list at rel={}, abs={}, dirty={}, flow origin={}: {}", self.border_box, - absolute_fragment_bounds, - self); - debug!("Fragment::build_display_list: dirty={}, flow_origin={}", + stacking_relative_border_box, layout_context.shared.dirty, - flow_origin); + stacking_relative_flow_origin, + self); if self.style().get_inheritedbox().visibility != visibility::visible { return } - if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) { + if !stacking_relative_border_box.intersects(&layout_context.shared.dirty) { debug!("Fragment::build_display_list: Did not intersect..."); return } // Calculate the clip rect. If there's nothing to render at all, don't even construct // display list items. - let clip = self.calculate_style_specified_clip(clip, &absolute_fragment_bounds.origin); - if !clip.might_intersect_rect(&absolute_fragment_bounds) { + let clip = self.calculate_style_specified_clip(clip, &stacking_relative_border_box); + if !clip.might_intersect_rect(&stacking_relative_border_box) { return; } @@ -713,15 +708,34 @@ impl FragmentDisplayListBuilding for Fragment { let level = StackingLevel::from_background_and_border_level(background_and_border_level); - // Add a shadow to the list, if applicable. + // Add shadows, background, borders, and outlines, if applicable. if let Some(ref inline_context) = self.inline_context { for style in inline_context.styles.iter().rev() { - self.build_display_list_for_box_shadow_if_applicable(&**style, - display_list, - layout_context, - level, - &absolute_fragment_bounds, - &clip); + self.build_display_list_for_box_shadow_if_applicable( + &**style, + display_list, + layout_context, + level, + &stacking_relative_border_box, + &clip); + self.build_display_list_for_background_if_applicable( + &**style, + display_list, + layout_context, + level, + &stacking_relative_border_box, + &clip); + self.build_display_list_for_borders_if_applicable( + &**style, + display_list, + &stacking_relative_border_box, + level, + &clip); + self.build_display_list_for_outline_if_applicable( + &**style, + display_list, + &stacking_relative_border_box, + &clip); } } if !self.is_scanned_text_fragment() { @@ -729,62 +743,35 @@ impl FragmentDisplayListBuilding for Fragment { display_list, layout_context, level, - &absolute_fragment_bounds, + &stacking_relative_border_box, &clip); - } - - // Add the background to the list, if applicable. - if let Some(ref inline_context) = self.inline_context { - for style in inline_context.styles.iter().rev() { - self.build_display_list_for_background_if_applicable(&**style, - display_list, - layout_context, - level, - &absolute_fragment_bounds, - &clip); - } - } - if !self.is_scanned_text_fragment() { self.build_display_list_for_background_if_applicable(&*self.style, display_list, layout_context, level, - &absolute_fragment_bounds, + &stacking_relative_border_box, &clip); - } - - // Add a border and outlines, if applicable. - if let Some(ref inline_context) = self.inline_context { - for style in inline_context.styles.iter().rev() { - self.build_display_list_for_borders_if_applicable(&**style, - display_list, - &absolute_fragment_bounds, - level, - &clip); - self.build_display_list_for_outline_if_applicable(&**style, - display_list, - &absolute_fragment_bounds, - &clip); - } - } - if !self.is_scanned_text_fragment() { self.build_display_list_for_borders_if_applicable(&*self.style, display_list, - &absolute_fragment_bounds, + &stacking_relative_border_box, level, &clip); self.build_display_list_for_outline_if_applicable(&*self.style, display_list, - &absolute_fragment_bounds, + &stacking_relative_border_box, &clip); } } // Create special per-fragment-type display items. - self.build_fragment_type_specific_display_items(display_list, flow_origin, &clip); + self.build_fragment_type_specific_display_items(display_list, + &stacking_relative_border_box, + &clip); if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, flow_origin, &clip) + self.build_debug_borders_around_fragment(display_list, + &stacking_relative_border_box, + &clip) } // If this is an iframe, then send its position and size up to the constellation. @@ -799,31 +786,18 @@ impl FragmentDisplayListBuilding for Fragment { // the iframe is actually going to be displayed. if let SpecificFragmentInfo::Iframe(ref iframe_fragment) = self.specific { self.finalize_position_and_size_of_iframe(&**iframe_fragment, - absolute_fragment_bounds.origin, + stacking_relative_border_box.origin, layout_context) } } fn build_fragment_type_specific_display_items(&mut self, display_list: &mut DisplayList, - flow_origin: Point2D, + stacking_relative_border_box: &Rect, clip: &ClippingRegion) { - // Compute the fragment position relative to the parent stacking context. If the fragment - // itself establishes a stacking context, then the origin of its position will be (0, 0) - // for the purposes of this computation. - let stacking_relative_flow_origin = if self.establishes_stacking_context() { - ZERO_POINT - } else { - flow_origin - }; - - // FIXME(#2795): Get the real container size. - let content_box = self.content_box(); - let container_size = Size2D::zero(); - let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect| { - let physical_rect = logical_rect.to_physical(writing_mode, container_size); - Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size) - }; + // Compute the context box position relative to the parent stacking context. + let stacking_relative_content_box = + self.stacking_relative_content_box(stacking_relative_border_box); match self.specific { SpecificFragmentInfo::UnscannedText(_) => { @@ -838,16 +812,16 @@ impl FragmentDisplayListBuilding for Fragment { self.build_display_list_for_text_fragment(display_list, &**text_fragment, text_color, - &flow_origin, - &Point2D(Au(0), Au(0)), + &stacking_relative_content_box, clip); if opts::get().show_debug_fragment_borders { self.build_debug_borders_around_text_fragments(self.style(), display_list, - flow_origin, + stacking_relative_border_box, + &stacking_relative_content_box, &**text_fragment, - clip); + clip) } } SpecificFragmentInfo::Generic | @@ -859,25 +833,25 @@ impl FragmentDisplayListBuilding for Fragment { SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => { if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, flow_origin, clip); + self.build_debug_borders_around_fragment(display_list, + stacking_relative_border_box, + clip); } } SpecificFragmentInfo::Image(ref mut image_fragment) => { let image_ref = &mut image_fragment.image; if let Some(image) = image_ref.get_image(self.node.to_untrusted_node_address()) { debug!("(building display list) building image fragment"); - let absolute_content_box = rect_to_absolute(self.style.writing_mode, - content_box); // Place the image into the display list. display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem { - base: BaseDisplayItem::new(absolute_content_box, + base: BaseDisplayItem::new(stacking_relative_content_box, DisplayItemMetadata::new(self.node, &*self.style, DefaultCursor), (*clip).clone()), image: image.clone(), - stretch_size: absolute_content_box.size, + stretch_size: stacking_relative_content_box.size, })); } else { // No image data at all? Do nothing. @@ -910,7 +884,9 @@ impl FragmentDisplayListBuilding for Fragment { iframe_rect)); } - fn clipping_region_for_children(&self, current_clip: &ClippingRegion, flow_origin: &Point2D) + fn clipping_region_for_children(&self, + current_clip: &ClippingRegion, + stacking_relative_border_box: &Rect) -> ClippingRegion { // Don't clip if we're text. if self.is_scanned_text_fragment() { @@ -918,27 +894,24 @@ impl FragmentDisplayListBuilding for Fragment { } // Account for style-specified `clip`. - let current_clip = self.calculate_style_specified_clip(current_clip, flow_origin); + let current_clip = self.calculate_style_specified_clip(current_clip, + stacking_relative_border_box); // Only clip if `overflow` tells us to. match self.style.get_box().overflow { - overflow::hidden | overflow::auto | overflow::scroll => {} - _ => return current_clip, + overflow::hidden | overflow::auto | overflow::scroll => { + // Create a new clip rect. + current_clip.intersect_rect(stacking_relative_border_box) + } + _ => current_clip, } - - // Create a new clip rect. - // - // FIXME(#2795): Get the real container size. - let physical_rect = self.border_box.to_physical(self.style.writing_mode, Size2D::zero()); - current_clip.intersect_rect(&Rect(physical_rect.origin + *flow_origin, physical_rect.size)) } fn build_display_list_for_text_fragment(&self, display_list: &mut DisplayList, text_fragment: &ScannedTextFragmentInfo, text_color: RGBA, - flow_origin: &Point2D, - offset: &Point2D, + stacking_relative_content_box: &Rect, clip: &ClippingRegion) { // Determine the orientation and cursor to use. let (orientation, cursor) = if self.style.writing_mode.is_vertical() { @@ -955,29 +928,16 @@ impl FragmentDisplayListBuilding for Fragment { // // FIXME(pcwalton): Get the real container size. let container_size = Size2D::zero(); - let content_box = self.content_box(); let metrics = &text_fragment.run.font_metrics; - let baseline_origin = { - let mut content_box_start = content_box.start; - content_box_start.b = content_box_start.b + metrics.ascent; - content_box_start.to_physical(self.style.writing_mode, container_size) + *flow_origin + - *offset - }; - let stacking_relative_flow_origin = if self.establishes_stacking_context() { - ZERO_POINT - } else { - *flow_origin - }; - let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect| { - let physical_rect = logical_rect.to_physical(writing_mode, container_size); - Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size) - }; - let content_rect = rect_to_absolute(self.style.writing_mode, - content_box).translate(offset); + let baseline_origin = stacking_relative_content_box.origin + + LogicalPoint::new(self.style.writing_mode, + Au(0), + metrics.ascent).to_physical(self.style.writing_mode, + container_size); // Create the text display item. display_list.content.push_back(DisplayItem::TextClass(box TextDisplayItem { - base: BaseDisplayItem::new(content_rect, + base: BaseDisplayItem::new(*stacking_relative_content_box, DisplayItemMetadata::new(self.node, self.style(), cursor), (*clip).clone()), text_run: text_fragment.run.clone(), @@ -989,63 +949,55 @@ impl FragmentDisplayListBuilding for Fragment { // Create display items for text decorations. let text_decorations = self.style().get_inheritedtext()._servo_text_decorations_in_effect; - if let Some(underline_color) = text_decorations.underline { - let mut rect = content_box.clone(); - rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset; - rect.size.block = metrics.underline_size; + let stacking_relative_content_box = + LogicalRect::from_physical(self.style.writing_mode, + *stacking_relative_content_box, + container_size); + if let Some(ref underline_color) = text_decorations.underline { + let mut stacking_relative_box = stacking_relative_content_box; + stacking_relative_box.start.b = stacking_relative_content_box.start.b + + metrics.ascent - metrics.underline_offset; + stacking_relative_box.size.block = metrics.underline_size; self.build_display_list_for_text_decoration(display_list, underline_color, - flow_origin, - clip, - &rect, - offset) + &stacking_relative_box, + clip) } - if let Some(overline_color) = text_decorations.overline { - let mut rect = content_box.clone(); - rect.size.block = metrics.underline_size; + if let Some(ref overline_color) = text_decorations.overline { + let mut stacking_relative_box = stacking_relative_content_box; + stacking_relative_box.size.block = metrics.underline_size; self.build_display_list_for_text_decoration(display_list, overline_color, - flow_origin, - clip, - &rect, - offset) + &stacking_relative_box, + clip) } - if let Some(line_through_color) = text_decorations.line_through { - let mut rect = content_box.clone(); - rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset; - rect.size.block = metrics.strikeout_size; + if let Some(ref line_through_color) = text_decorations.line_through { + let mut stacking_relative_box = stacking_relative_content_box; + stacking_relative_box.start.b = stacking_relative_box.start.b + metrics.ascent - + metrics.strikeout_offset; + stacking_relative_box.size.block = metrics.strikeout_size; self.build_display_list_for_text_decoration(display_list, line_through_color, - flow_origin, - clip, - &rect, - offset) + &stacking_relative_box, + clip) } } fn build_display_list_for_text_decoration(&self, display_list: &mut DisplayList, - color: RGBA, - flow_origin: &Point2D, - clip: &ClippingRegion, - logical_bounds: &LogicalRect, - offset: &Point2D) { - // FIXME(pcwalton): Get the real container size. + color: &RGBA, + stacking_relative_box: &LogicalRect, + clip: &ClippingRegion) { + // FIXME(pcwalton, #2795): Get the real container size. let container_size = Size2D::zero(); - let stacking_relative_flow_origin = if self.establishes_stacking_context() { - ZERO_POINT - } else { - *flow_origin - }; - let physical_rect = logical_bounds.to_physical(self.style.writing_mode, container_size); + let stacking_relative_box = stacking_relative_box.to_physical(self.style.writing_mode, + container_size); - let bounds = Rect(physical_rect.origin + stacking_relative_flow_origin, - physical_rect.size).translate(offset); let metadata = DisplayItemMetadata::new(self.node, &*self.style, DefaultCursor); display_list.content.push_back(DisplayItem::SolidColorClass(box SolidColorDisplayItem { - base: BaseDisplayItem::new(bounds, metadata, (*clip).clone()), + base: BaseDisplayItem::new(stacking_relative_box, metadata, (*clip).clone()), color: color.to_gfx_color(), })) } @@ -1081,14 +1033,16 @@ impl BlockFlowDisplayListBuilding for BlockFlow { layout_context: &LayoutContext, background_border_level: BackgroundAndBorderLevel) { // Add the box that starts the block context. - let stacking_relative_fragment_origin = - self.base.stacking_relative_position_of_child_fragment(&self.fragment); self.fragment.build_display_list(display_list, layout_context, - stacking_relative_fragment_origin, + &self.base.stacking_relative_position, + &self.base + .absolute_position_info + .relative_containing_block_size, background_border_level, &self.base.clip); + // Add children. for kid in self.base.children.iter_mut() { flow::mut_base(kid).display_list_building_result.add_to(display_list); } @@ -1180,19 +1134,70 @@ impl BlockFlowDisplayListBuilding for BlockFlow { display_list: Box, layer: Option>) -> Arc { - let size = self.base.position.size.to_physical(self.base.writing_mode); - let bounds = Rect(self.base.stacking_relative_position, size); - let z_index = self.fragment.style().get_box().z_index.number_or_zero(); - let opacity = self.fragment.style().get_effects().opacity as f32; + debug_assert!(self.fragment.establishes_stacking_context()); + let border_box = self.fragment + .stacking_relative_border_box(&self.base.stacking_relative_position, + &self.base + .absolute_position_info + .relative_containing_block_size, + CoordinateSystem::Parent); + + // FIXME(pcwalton): Is this vertical-writing-direction-safe? + let margin = self.fragment.margin.to_physical(self.base.writing_mode); + let overflow = self.base.overflow.translate(&-Point2D(margin.left, Au(0))); + Arc::new(StackingContext::new(display_list, - &bounds, - &self.base.overflow, - z_index, - opacity, + &border_box, + &overflow, + self.fragment.style().get_box().z_index.number_or_zero(), + self.fragment.style().get_effects().opacity as f32, layer)) } } +pub trait InlineFlowDisplayListBuilding { + fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext); +} + +impl InlineFlowDisplayListBuilding for InlineFlow { + fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext) { + // TODO(#228): Once we form lines and have their cached bounds, we can be smarter and + // not recurse on a line if nothing in it can intersect the dirty region. + debug!("Flow: building display list for {:u} inline fragments", self.fragments.len()); + + let mut display_list = box DisplayList::new(); + for fragment in self.fragments.fragments.iter_mut() { + fragment.build_display_list(&mut *display_list, + layout_context, + &self.base.stacking_relative_position, + &self.base + .absolute_position_info + .relative_containing_block_size, + BackgroundAndBorderLevel::Content, + &self.base.clip); + match fragment.specific { + SpecificFragmentInfo::InlineBlock(ref mut block_flow) => { + let block_flow = block_flow.flow_ref.deref_mut(); + flow::mut_base(block_flow).display_list_building_result + .add_to(&mut *display_list) + } + SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => { + let block_flow = block_flow.flow_ref.deref_mut(); + flow::mut_base(block_flow).display_list_building_result + .add_to(&mut *display_list) + } + _ => {} + } + } + + self.base.display_list_building_result = DisplayListBuildingResult::Normal(display_list); + + if opts::get().validate_display_list_geometry { + self.base.validate_display_list_geometry(); + } + } +} + pub trait ListItemFlowDisplayListBuilding { fn build_display_list_for_list_item(&mut self, display_list: Box, @@ -1204,17 +1209,16 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow { mut display_list: Box, layout_context: &LayoutContext) { // Draw the marker, if applicable. - match self.marker { - None => {} - Some(ref mut marker) => { - let stacking_relative_fragment_origin = - self.block_flow.base.stacking_relative_position_of_child_fragment(marker); - marker.build_display_list(&mut *display_list, - layout_context, - stacking_relative_fragment_origin, - BackgroundAndBorderLevel::Content, - &self.block_flow.base.clip); - } + if let Some(ref mut marker) = self.marker { + marker.build_display_list(&mut *display_list, + layout_context, + &self.block_flow.base.stacking_relative_position, + &self.block_flow + .base + .absolute_position_info + .relative_containing_block_size, + BackgroundAndBorderLevel::Content, + &self.block_flow.base.clip); } // Draw the rest of the block. @@ -1292,3 +1296,4 @@ impl StackingContextConstruction for DisplayList { } } } + diff --git a/components/layout/flow.rs b/components/layout/flow.rs index f20d5a0c128..d252a93269b 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -32,7 +32,7 @@ use display_list_builder::DisplayListBuildingResult; use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::FlowRef; -use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo}; +use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use inline::InlineFlow; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; @@ -223,8 +223,10 @@ pub trait Flow: fmt::Show + ToString + Sync { /// Returns the union of all overflow rects of all of this flow's fragments. fn compute_overflow(&self) -> Rect; - /// Iterates through overflow rects of all of this flow's fragments. - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator); + /// Iterates through border boxes of all of this flow's fragments. + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D); fn compute_collapsible_block_start_margin(&mut self, _layout_context: &mut LayoutContext, @@ -958,17 +960,6 @@ impl BaseFlow { } } } - - /// Returns the position of the given fragment relative to the start of the nearest ancestor - /// stacking context. The fragment must be a child fragment of this flow. - pub fn stacking_relative_position_of_child_fragment(&self, fragment: &Fragment) - -> Point2D { - let relative_offset = - fragment.relative_position(&self - .absolute_position_info - .relative_containing_block_size); - self.stacking_relative_position.add_size(&relative_offset.to_physical(self.writing_mode)) - } } impl<'a> ImmutableFlowUtils for &'a Flow + 'a { @@ -1065,14 +1056,14 @@ impl<'a> ImmutableFlowUtils for &'a Flow + 'a { let flow = match self.class() { FlowClass::Table | FlowClass::TableRowGroup => { let fragment = - Fragment::new_anonymous_table_fragment(node, - SpecificFragmentInfo::TableRow); + Fragment::new_anonymous_from_specific_info(node, + SpecificFragmentInfo::TableRow); box TableRowFlow::from_node_and_fragment(node, fragment) as Box }, FlowClass::TableRow => { let fragment = - Fragment::new_anonymous_table_fragment(node, - SpecificFragmentInfo::TableCell); + Fragment::new_anonymous_from_specific_info(node, + SpecificFragmentInfo::TableCell); let hide = node.style().get_inheritedtable().empty_cells == empty_cells::hide; box TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide) as Box diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index c66087364ed..cfc2ab8b1a5 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -31,8 +31,7 @@ use serialize::{Encodable, Encoder}; use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_net::image::holder::ImageHolder; use servo_net::local_image_cache::LocalImageCache; -use servo_util::geometry::Au; -use servo_util::geometry; +use servo_util::geometry::{mod, Au, ZERO_POINT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin}; use servo_util::range::*; use servo_util::smallvec::SmallVec; @@ -62,8 +61,8 @@ use url::Url; /// positioned as if it were a block fragment, but its children are positioned according to /// inline flow. /// -/// A `SpecificFragmentInfo::Generic` is an empty fragment that contributes only borders, margins, padding, and -/// backgrounds. It is analogous to a CSS nonreplaced content box. +/// A `SpecificFragmentInfo::Generic` is an empty fragment that contributes only borders, margins, +/// padding, and backgrounds. It is analogous to a CSS nonreplaced content box. /// /// A fragment's type influences how its styles are interpreted during layout. For example, /// replaced content such as images are resized differently from tables, text, or other content. @@ -83,8 +82,10 @@ pub struct Fragment { /// The CSS style of this fragment. pub style: Arc, - /// The position of this fragment relative to its owning flow. - /// The size includes padding and border, but not margin. + /// The position of this fragment relative to its owning flow. The size includes padding and + /// border, but not margin. + /// + /// NB: This does not account for relative positioning. pub border_box: LogicalRect, /// The sum of border and padding; i.e. the distance from the edge of the border box to the @@ -520,10 +521,28 @@ impl Fragment { } } + /// Constructs a new `Fragment` instance for an anonymous object. + pub fn new_anonymous(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) + -> Fragment { + let node_style = cascade_anonymous(&**node.style()); + let writing_mode = node_style.writing_mode; + Fragment { + node: OpaqueNodeMethods::from_thread_safe_layout_node(node), + style: Arc::new(node_style), + restyle_damage: node.restyle_damage(), + border_box: LogicalRect::zero(writing_mode), + border_padding: LogicalMargin::zero(writing_mode), + margin: LogicalMargin::zero(writing_mode), + specific: constructor.build_specific_fragment_info_for_node(node), + inline_context: None, + debug_id: layout_debug::generate_unique_debug_id(), + } + } + /// Constructs a new `Fragment` instance for an anonymous table object. - pub fn new_anonymous_table_fragment(node: &ThreadSafeLayoutNode, - specific: SpecificFragmentInfo) - -> Fragment { + pub fn new_anonymous_from_specific_info(node: &ThreadSafeLayoutNode, + specific: SpecificFragmentInfo) + -> Fragment { // CSS 2.1 ยง 17.2.1 This is for non-inherited properties on anonymous table fragments // example: // @@ -1508,10 +1527,17 @@ impl Fragment { /// because the corresponding table flow is the primary fragment. pub fn is_primary_fragment(&self) -> bool { match self.specific { - SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | + SpecificFragmentInfo::InlineBlock(_) | + SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | SpecificFragmentInfo::TableWrapper => false, - SpecificFragmentInfo::Generic | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) | - SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableColumn(_) | SpecificFragmentInfo::TableRow | + SpecificFragmentInfo::Generic | + SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::ScannedText(_) | + SpecificFragmentInfo::Table | + SpecificFragmentInfo::TableCell | + SpecificFragmentInfo::TableColumn(_) | + SpecificFragmentInfo::TableRow | SpecificFragmentInfo::UnscannedText(_) => true, } } @@ -1540,15 +1566,48 @@ impl Fragment { self.style = (*new_style).clone() } - /// Given the stacking-context-relative position of the containing flow, returns the boundaries - /// of this fragment relative to the parent stacking context. - pub fn stacking_relative_bounds(&self, stacking_relative_flow_origin: &Point2D) - -> Rect { - // FIXME(#2795): Get the real container size + /// Given the stacking-context-relative position of the containing flow, returns the border box + /// of this fragment relative to the parent stacking context. This takes `position: relative` + /// into account. + /// + /// If `coordinate_system` is `Parent`, this returns the border box in the parent stacking + /// context's coordinate system. Otherwise, if `coordinate_system` is `Self` and this fragment + /// establishes a stacking context itself, this returns a border box anchored at (0, 0). (If + /// this fragment does not establish a stacking context, then it always belongs to its parent + /// stacking context and thus `coordinate_system` is ignored.) + /// + /// This is the method you should use for display list construction as well as + /// `getBoundingClientRect()` and so forth. + pub fn stacking_relative_border_box(&self, + stacking_relative_flow_origin: &Point2D, + relative_containing_block_size: &LogicalSize, + coordinate_system: CoordinateSystem) + -> Rect { + // FIXME(pcwalton, #2795): Get the real container size. let container_size = Size2D::zero(); - self.border_box - .to_physical(self.style.writing_mode, container_size) - .translate(stacking_relative_flow_origin) + let border_box = self.border_box.to_physical(self.style.writing_mode, container_size); + if coordinate_system == CoordinateSystem::Self && self.establishes_stacking_context() { + return Rect(ZERO_POINT, border_box.size) + } + + // FIXME(pcwalton): This can double-count relative position sometimes for inlines (e.g. + // `
x
`, because the `position:relative` trickles down + // to the inline flow. Possibly we should extend the notion of "primary fragment" to fix + // this. + let relative_position = self.relative_position(relative_containing_block_size); + border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode)) + .translate(stacking_relative_flow_origin) + } + + /// Given the stacking-context-relative border box, returns the stacking-context-relative + /// content box. + pub fn stacking_relative_content_box(&self, stacking_relative_border_box: &Rect) + -> Rect { + let border_padding = self.border_padding.to_physical(self.style.writing_mode); + Rect(Point2D(stacking_relative_border_box.origin.x + border_padding.left, + stacking_relative_border_box.origin.y + border_padding.top), + Size2D(stacking_relative_border_box.size.width - border_padding.horizontal(), + stacking_relative_border_box.size.height - border_padding.vertical())) } /// Returns true if this fragment establishes a new stacking context and false otherwise. @@ -1637,13 +1696,23 @@ bitflags! { } } -/// A top-down fragment overflow region iteration handler. -pub trait FragmentOverflowIterator { +/// A top-down fragment border box iteration handler. +pub trait FragmentBorderBoxIterator { /// The operation to perform. - fn process(&mut self, fragment: &Fragment, overflow: Rect); + fn process(&mut self, fragment: &Fragment, overflow: &Rect); /// Returns true if this fragment must be processed in-order. If this returns false, /// we skip the operation for this fragment, but continue processing siblings. fn should_process(&mut self, fragment: &Fragment) -> bool; } +/// The coordinate system used in `stacking_relative_border_box()`. See the documentation of that +/// method for details. +#[deriving(Clone, PartialEq, Show)] +pub enum CoordinateSystem { + /// The border box returned is relative to the fragment's parent stacking context. + Parent, + /// The border box returned is relative to the fragment's own stacking context, if applicable. + Self, +} + diff --git a/components/layout/inline.rs b/components/layout/inline.rs index f8a19708d1e..0a2234bfa8b 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -6,13 +6,13 @@ use css::node_style::StyledNode; use context::LayoutContext; -use display_list_builder::{BackgroundAndBorderLevel, DisplayListBuildingResult, FragmentDisplayListBuilding}; +use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding}; use floats::{FloatKind, Floats, PlacementInfo}; use flow::{BaseFlow, FlowClass, Flow, MutableFlowUtils, ForceNonfloatedFlag}; use flow::{IS_ABSOLUTELY_POSITIONED}; use flow; -use fragment::{Fragment, SpecificFragmentInfo}; -use fragment::{FragmentOverflowIterator, ScannedTextFragmentInfo}; +use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, ScannedTextFragmentInfo}; +use fragment::{SpecificFragmentInfo}; use fragment::SplitInfo; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; @@ -20,16 +20,14 @@ use model::IntrinsicISizesContribution; use text; use collections::{RingBuf}; -use geom::{Rect, Size2D}; -use gfx::display_list::DisplayList; +use geom::{Point2D, Rect}; use gfx::font::FontMetrics; use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; +use servo_util::arc_ptr_eq; use servo_util::geometry::{Au, ZERO_RECT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; -use servo_util::opts; use servo_util::range::{Range, RangeIndex}; -use servo_util::arc_ptr_eq; use std::cmp::max; use std::fmt; use std::mem; @@ -1186,44 +1184,29 @@ impl Flow for InlineFlow { fn compute_absolute_position(&mut self) { for fragment in self.fragments.fragments.iter_mut() { - let stacking_relative_position = match fragment.specific { - SpecificFragmentInfo::InlineBlock(ref mut info) => { - let block_flow = info.flow_ref.as_block(); - block_flow.base.absolute_position_info = self.base.absolute_position_info; - - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); - block_flow.base.stacking_relative_position = - self.base.stacking_relative_position + - fragment.border_box.start.to_physical(self.base.writing_mode, - container_size); - block_flow.base.stacking_relative_position - } - SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => { - let block_flow = info.flow_ref.as_block(); - block_flow.base.absolute_position_info = self.base.absolute_position_info; - - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); - block_flow.base.stacking_relative_position = - self.base.stacking_relative_position + - fragment.border_box.start.to_physical(self.base.writing_mode, - container_size); - block_flow.base.stacking_relative_position - - } - _ => continue, - }; - + let stacking_relative_border_box = + fragment.stacking_relative_border_box(&self.base.stacking_relative_position, + &self.base + .absolute_position_info + .relative_containing_block_size, + CoordinateSystem::Self); let clip = fragment.clipping_region_for_children(&self.base.clip, - &stacking_relative_position); - + &stacking_relative_border_box); match fragment.specific { SpecificFragmentInfo::InlineBlock(ref mut info) => { - flow::mut_base(info.flow_ref.deref_mut()).clip = clip + flow::mut_base(info.flow_ref.deref_mut()).clip = clip; + let block_flow = info.flow_ref.as_block(); + block_flow.base.absolute_position_info = self.base.absolute_position_info; + block_flow.base.stacking_relative_position = + stacking_relative_border_box.origin; } SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => { - flow::mut_base(info.flow_ref.deref_mut()).clip = clip + flow::mut_base(info.flow_ref.deref_mut()).clip = clip; + let block_flow = info.flow_ref.as_block(); + block_flow.base.absolute_position_info = self.base.absolute_position_info; + block_flow.base.stacking_relative_position = + stacking_relative_border_box.origin + } _ => {} } @@ -1235,38 +1218,7 @@ impl Flow for InlineFlow { fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {} fn build_display_list(&mut self, layout_context: &LayoutContext) { - // TODO(#228): Once we form lines and have their cached bounds, we can be smarter and - // not recurse on a line if nothing in it can intersect the dirty region. - debug!("Flow: building display list for {} inline fragments", self.fragments.len()); - - let mut display_list = box DisplayList::new(); - for fragment in self.fragments.fragments.iter_mut() { - let fragment_origin = self.base.stacking_relative_position_of_child_fragment(fragment); - fragment.build_display_list(&mut *display_list, - layout_context, - fragment_origin, - BackgroundAndBorderLevel::Content, - &self.base.clip); - match fragment.specific { - SpecificFragmentInfo::InlineBlock(ref mut block_flow) => { - let block_flow = block_flow.flow_ref.deref_mut(); - flow::mut_base(block_flow).display_list_building_result - .add_to(&mut *display_list) - } - SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => { - let block_flow = block_flow.flow_ref.deref_mut(); - flow::mut_base(block_flow).display_list_building_result - .add_to(&mut *display_list) - } - _ => {} - } - } - - self.base.display_list_building_result = DisplayListBuildingResult::Normal(display_list); - - if opts::get().validate_display_list_geometry { - self.base.validate_display_list_geometry(); - } + self.build_display_list_for_inline(layout_context) } fn repair_style(&mut self, _: &Arc) {} @@ -1279,13 +1231,23 @@ impl Flow for InlineFlow { overflow } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + // FIXME(#2795): Get the real container size. for fragment in self.fragments.fragments.iter() { - if iterator.should_process(fragment) { - let fragment_origin = - self.base.stacking_relative_position_of_child_fragment(fragment); - iterator.process(fragment, fragment.stacking_relative_bounds(&fragment_origin)); + if !iterator.should_process(fragment) { + continue } + + let stacking_relative_position = &self.base.stacking_relative_position; + let relative_containing_block_size = + &self.base.absolute_position_info.relative_containing_block_size; + iterator.process(fragment, + &fragment.stacking_relative_border_box(stacking_relative_position, + relative_containing_block_size, + CoordinateSystem::Parent) + .translate(stacking_context_position)) } } } diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 22e00904c75..3309ff9c28b 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -10,7 +10,7 @@ use construct::ConstructionResult; use context::SharedLayoutContext; use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use flow_ref::FlowRef; -use fragment::{Fragment, FragmentOverflowIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT}; use layout_debug; use parallel::{mod, UnsafeFlow}; @@ -604,8 +604,8 @@ impl LayoutTask { // FIXME(pcwalton): This has not been updated to handle the stacking context relative // stuff. So the position is wrong in most cases. let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node); - let mut iterator = UnioningFragmentOverflowIterator::new(requested_node); - sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator); + let mut iterator = UnioningFragmentBorderBoxIterator::new(requested_node); + sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); rw_data.content_box_response = iterator.rect; } @@ -616,8 +616,8 @@ impl LayoutTask { // FIXME(pcwalton): This has not been updated to handle the stacking context relative // stuff. So the position is wrong in most cases. let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node); - let mut iterator = CollectingFragmentOverflowIterator::new(requested_node); - sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator); + let mut iterator = CollectingFragmentBorderBoxIterator::new(requested_node); + sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); rw_data.content_boxes_response = iterator.rects; } @@ -1017,26 +1017,26 @@ impl LayoutRPC for LayoutRPCImpl { } } -struct UnioningFragmentOverflowIterator { +struct UnioningFragmentBorderBoxIterator { node_address: OpaqueNode, rect: Rect, } -impl UnioningFragmentOverflowIterator { - fn new(node_address: OpaqueNode) -> UnioningFragmentOverflowIterator { - UnioningFragmentOverflowIterator { +impl UnioningFragmentBorderBoxIterator { + fn new(node_address: OpaqueNode) -> UnioningFragmentBorderBoxIterator { + UnioningFragmentBorderBoxIterator { node_address: node_address, rect: Rect::zero(), } } } -impl FragmentOverflowIterator for UnioningFragmentOverflowIterator { - fn process(&mut self, _: &Fragment, bounds: Rect) { - if self.rect.is_empty() { - self.rect = bounds; +impl FragmentBorderBoxIterator for UnioningFragmentBorderBoxIterator { + fn process(&mut self, _: &Fragment, border_box: &Rect) { + self.rect = if self.rect.is_empty() { + *border_box } else { - self.rect = self.rect.union(&bounds); + self.rect.union(border_box) } } @@ -1045,23 +1045,23 @@ impl FragmentOverflowIterator for UnioningFragmentOverflowIterator { } } -struct CollectingFragmentOverflowIterator { +struct CollectingFragmentBorderBoxIterator { node_address: OpaqueNode, rects: Vec>, } -impl CollectingFragmentOverflowIterator { - fn new(node_address: OpaqueNode) -> CollectingFragmentOverflowIterator { - CollectingFragmentOverflowIterator { +impl CollectingFragmentBorderBoxIterator { + fn new(node_address: OpaqueNode) -> CollectingFragmentBorderBoxIterator { + CollectingFragmentBorderBoxIterator { node_address: node_address, rects: Vec::new(), } } } -impl FragmentOverflowIterator for CollectingFragmentOverflowIterator { - fn process(&mut self, _: &Fragment, bounds: Rect) { - self.rects.push(bounds); +impl FragmentBorderBoxIterator for CollectingFragmentBorderBoxIterator { + fn process(&mut self, _: &Fragment, border_box: &Rect) { + self.rects.push(*border_box); } fn should_process(&mut self, fragment: &Fragment) -> bool { diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index 0d0c71d526c..b13630496c5 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -12,10 +12,10 @@ use construct::FlowConstructor; use context::LayoutContext; use display_list_builder::ListItemFlowDisplayListBuilding; use flow::{Flow, FlowClass}; -use fragment::{Fragment, FragmentOverflowIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use gfx::display_list::DisplayList; use servo_util::geometry::Au; use servo_util::opts; @@ -116,8 +116,10 @@ impl Flow for ListItemFlow { self.block_flow.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - self.block_flow.iterate_through_fragment_overflow(iterator); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 01ba0c098ed..1be77041113 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -5,11 +5,10 @@ //! Implements sequential traversals over the DOM and flow trees. use context::{LayoutContext, SharedLayoutContext}; -use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal}; -use flow; +use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, PostorderFlowTraversal}; +use flow::{PreorderFlowTraversal}; use flow_ref::FlowRef; -use fragment::FragmentOverflowIterator; -use servo_util::opts; +use fragment::FragmentBorderBoxIterator; use traversal::{BubbleISizes, RecalcStyleForNode, ConstructFlows}; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes}; use traversal::{ComputeAbsolutePositions, BuildDisplayList}; @@ -17,6 +16,10 @@ use wrapper::LayoutNode; use wrapper::{PostorderNodeMutTraversal}; use wrapper::{PreorderDomTraversal, PostorderDomTraversal}; +use geom::point::Point2D; +use servo_util::geometry::{Au, ZERO_POINT}; +use servo_util::opts; + pub fn traverse_dom_preorder(root: LayoutNode, shared_layout_context: &SharedLayoutContext) { fn doit(node: LayoutNode, recalc_style: RecalcStyleForNode, construct_flows: ConstructFlows) { @@ -94,15 +97,25 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef, doit(root.deref_mut(), compute_absolute_positions, build_display_list); } -pub fn iterate_through_flow_tree_fragment_bounds(root: &mut FlowRef, - iterator: &mut FragmentOverflowIterator) { - fn doit(flow: &mut Flow, iterator: &mut FragmentOverflowIterator) { - flow.iterate_through_fragment_overflow(iterator); +pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut FlowRef, + iterator: &mut FragmentBorderBoxIterator) { + fn doit(flow: &mut Flow, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position); for kid in flow::mut_base(flow).child_iter() { - doit(kid, iterator); + let stacking_context_position = + if kid.is_block_flow() && kid.as_block().fragment.establishes_stacking_context() { + *stacking_context_position + flow::base(kid).stacking_relative_position + } else { + *stacking_context_position + }; + + // FIXME(#2795): Get the real container size. + doit(kid, iterator, &stacking_context_position); } } - doit(root.deref_mut(), iterator); + doit(root.deref_mut(), iterator, &ZERO_POINT); } diff --git a/components/layout/table.rs b/components/layout/table.rs index 81c9f40d43d..e05abd4d67e 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -13,14 +13,14 @@ use context::LayoutContext; use floats::FloatKind; use flow::{mod, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; use flow::ImmutableFlowUtils; -use fragment::{Fragment, FragmentOverflowIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution}; use table_row::CellIntrinsicInlineSize; use table_wrapper::TableLayout; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use servo_util::geometry::Au; use servo_util::logical_geometry::LogicalRect; use std::cmp::max; @@ -389,8 +389,10 @@ impl Flow for TableFlow { self.block_flow.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - self.block_flow.iterate_through_fragment_overflow(iterator); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index 4864e49096a..1a77cde7092 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -10,10 +10,10 @@ use block::BlockFlow; use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::FragmentOverflowIterator; +use fragment::FragmentBorderBoxIterator; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use servo_util::geometry::Au; use std::fmt; use style::ComputedValues; @@ -86,8 +86,10 @@ impl Flow for TableCaptionFlow { self.block_flow.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - self.block_flow.iterate_through_fragment_overflow(iterator); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 583c328f315..b97a12e143d 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -9,13 +9,13 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use context::LayoutContext; use flow::{Flow, FlowClass}; -use fragment::{Fragment, FragmentOverflowIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use model::{MaybeAuto}; use layout_debug; use table::InternalTable; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use servo_util::geometry::Au; use std::fmt; use style::{UnsignedIntegerAttribute, ComputedValues}; @@ -167,8 +167,10 @@ impl Flow for TableCellFlow { self.block_flow.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - self.block_flow.iterate_through_fragment_overflow(iterator); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index 4eb1cae8022..5e7b066d206 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -9,11 +9,11 @@ use context::LayoutContext; use css::node_style::StyledNode; use flow::{BaseFlow, FlowClass, Flow, ForceNonfloatedFlag}; -use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo}; +use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use layout_debug; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use servo_util::geometry::{Au, ZERO_RECT}; use std::cmp::max; use std::fmt; @@ -101,8 +101,9 @@ impl Flow for TableColGroupFlow { ZERO_RECT } - fn iterate_through_fragment_overflow(&self, _: &mut FragmentOverflowIterator) { - } + fn iterate_through_fragment_border_boxes(&self, + _: &mut FragmentBorderBoxIterator, + _: &Point2D) {} } impl fmt::Show for TableColGroupFlow { diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index d27d7d1ffb4..b3e9e1ae7f6 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -12,13 +12,13 @@ use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow, ImmutableFlowUtils}; use flow; -use fragment::{Fragment, FragmentOverflowIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use model::MaybeAuto; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use servo_util::geometry::Au; use std::cmp::max; use std::fmt; @@ -320,8 +320,10 @@ impl Flow for TableRowFlow { self.block_flow.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - self.block_flow.iterate_through_fragment_overflow(iterator); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 5d3673c1612..2c2847565c7 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -10,12 +10,12 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use construct::FlowConstructor; use context::LayoutContext; use flow::{FlowClass, Flow}; -use fragment::{Fragment, FragmentOverflowIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use servo_util::geometry::Au; use std::fmt; use style::ComputedValues; @@ -155,8 +155,10 @@ impl Flow for TableRowGroupFlow { self.block_flow.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - self.block_flow.iterate_through_fragment_overflow(iterator); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index fa8497c46d7..1c68b7fa924 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -19,11 +19,11 @@ use context::LayoutContext; use floats::FloatKind; use flow::{FlowClass, Flow, ImmutableFlowUtils}; use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; -use fragment::{Fragment, FragmentOverflowIterator}; +use fragment::{Fragment, FragmentBorderBoxIterator}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize}; use wrapper::ThreadSafeLayoutNode; -use geom::Rect; +use geom::{Point2D, Rect}; use servo_util::geometry::Au; use std::cmp::{max, min}; use std::fmt; @@ -363,8 +363,10 @@ impl Flow for TableWrapperFlow { self.block_flow.compute_overflow() } - fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) { - self.block_flow.iterate_through_fragment_overflow(iterator); + fn iterate_through_fragment_border_boxes(&self, + iterator: &mut FragmentBorderBoxIterator, + stacking_context_position: &Point2D) { + self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position) } } diff --git a/components/style/lib.rs b/components/style/lib.rs index 23739b52b1a..4c8efd3a3aa 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -44,7 +44,7 @@ pub use selector_matching::{matches, matches_simple_selector, common_style_affec pub use selector_matching::{rare_style_affecting_attributes}; pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE, SELECTOR_WHITESPACE}; pub use properties::{cascade, cascade_anonymous, computed, longhands_from_shorthand}; -pub use properties::is_supported_property; +pub use properties::{is_supported_property, make_inline}; pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs}; pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult}; diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako index 02e3886c522..47db730d0f1 100644 --- a/components/style/properties/mod.rs.mako +++ b/components/style/properties/mod.rs.mako @@ -3183,6 +3183,15 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues { result } +/// Sets `display` to `inline` and `position` to `static`. +#[inline] +pub fn make_inline(style: &ComputedValues) -> ComputedValues { + let mut style = (*style).clone(); + style.box_.make_unique().display = longhands::display::computed_value::T::inline; + style.box_.make_unique().position = longhands::position::computed_value::T::static_; + style +} + pub fn is_supported_property(property: &str) -> bool { match property { % for property in SHORTHANDS: diff --git a/tests/content/harness.js b/tests/content/harness.js index 2738ca2e6a6..452c72fa67d 100644 --- a/tests/content/harness.js +++ b/tests/content/harness.js @@ -22,7 +22,7 @@ function _pass(s, m) { function _printer(opstr, op) { return function (a, b, msg) { - let f = op(a,b) ? _pass : _fail; + var f = op(a,b) ? _pass : _fail; if (!msg) msg = ""; f(a + " " + opstr + " " + b, msg); };