diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-10-21 21:05:44 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-10-22 08:02:17 -0700 |
commit | 821793351e3a593f1affed0c57c00fb2443b5af7 (patch) | |
tree | 193600e46cfd07a1308cb3ca3d53b2829384af2b /components | |
parent | 691e42f7ef9005b2466bff85eee21e0363c77050 (diff) | |
download | servo-821793351e3a593f1affed0c57c00fb2443b5af7.tar.gz servo-821793351e3a593f1affed0c57c00fb2443b5af7.zip |
layout: Largely move display list building out to a separate file.
`layout::fragment` and `layout::block` were getting too big.
Diffstat (limited to 'components')
-rw-r--r-- | components/layout/block.rs | 133 | ||||
-rw-r--r-- | components/layout/display_list_builder.rs | 710 | ||||
-rw-r--r-- | components/layout/flow.rs | 92 | ||||
-rw-r--r-- | components/layout/fragment.rs | 551 | ||||
-rw-r--r-- | components/layout/inline.rs | 82 | ||||
-rw-r--r-- | components/layout/lib.rs | 1 | ||||
-rw-r--r-- | components/layout/table.rs | 9 | ||||
-rw-r--r-- | components/layout/table_caption.rs | 10 | ||||
-rw-r--r-- | components/layout/table_cell.rs | 9 | ||||
-rw-r--r-- | components/layout/table_colgroup.rs | 3 | ||||
-rw-r--r-- | components/layout/table_row.rs | 9 | ||||
-rw-r--r-- | components/layout/table_rowgroup.rs | 10 | ||||
-rw-r--r-- | components/layout/table_wrapper.rs | 9 |
13 files changed, 843 insertions, 785 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index db55e3a88fe..1f32e685b2a 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -29,6 +29,7 @@ use construct::FlowConstructor; use context::LayoutContext; +use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding}; use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, PlacementInfo}; use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base}; @@ -40,24 +41,18 @@ use model::{MaybeAuto, NoCollapsibleMargins, Specified, specified, specified_or_ use table::ColumnInlineSize; use wrapper::ThreadSafeLayoutNode; -use collections::dlist::DList; -use geom::{Size2D, Point2D, Rect}; -use gfx::color; -use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, ContentStackingLevel, DisplayList}; -use gfx::display_list::{FloatStackingLevel, PositionedDescendantStackingLevel}; -use gfx::display_list::{RootOfStackingContextLevel}; -use gfx::render_task::RenderLayer; +use geom::Size2D; +use gfx::display_list::BlockLevel; use serialize::{Encoder, Encodable}; -use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable}; +use servo_msg::compositor_msg::LayerId; use servo_util::geometry::{Au, MAX_AU, MAX_RECT}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; +use servo_util::opts; use std::cmp::{max, min}; use std::fmt; -use std::mem; use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None}; use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, box_sizing, clear}; use style::computed_values::{display, float, overflow, position}; -use sync::Arc; /// Information specific to floated blocks. #[deriving(Clone, Encodable)] @@ -1070,70 +1065,6 @@ impl BlockFlow { self.base.position = self.base.position.translate(&float_offset).translate(&margin_offset); } - fn build_display_list_block_common(&mut self, - layout_context: &LayoutContext, - background_border_level: BackgroundAndBorderLevel) { - let relative_offset = - self.fragment.relative_position(&self.base - .absolute_position_info - .relative_containing_block_size); - - // Add the box that starts the block context. - let mut display_list = DisplayList::new(); - self.fragment.build_display_list(&mut display_list, - layout_context, - self.base.abs_position.add_size( - &relative_offset.to_physical(self.base.writing_mode)), - background_border_level, - &self.base.clip_rect); - - let mut child_layers = DList::new(); - for kid in self.base.child_iter() { - if kid.is_absolutely_positioned() { - // All absolute flows will be handled by their containing block. - continue - } - - display_list.push_all_move(mem::replace(&mut flow::mut_base(kid).display_list, - DisplayList::new())); - child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new())) - } - - // Process absolute descendant links. - for abs_descendant_link in self.base.abs_descendants.iter() { - // TODO(pradeep): Send in our absolute position directly. - display_list.push_all_move(mem::replace( - &mut flow::mut_base(abs_descendant_link).display_list, - DisplayList::new())); - child_layers.append(mem::replace(&mut flow::mut_base(abs_descendant_link).layers, - DList::new())); - } - - self.base.display_list = display_list; - self.base.layers = child_layers - } - - /// Add display items for current block. - /// - /// Set the absolute position for children after doing any offsetting for - /// position: relative. - pub fn build_display_list_block(&mut self, layout_context: &LayoutContext) { - if self.is_float() { - // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index: - // auto` kids into the parent stacking context, when that is supported. - self.build_display_list_float(layout_context) - } else if self.is_absolutely_positioned() { - self.build_display_list_abs(layout_context) - } else { - self.build_display_list_block_common(layout_context, BlockLevel) - } - } - - pub fn build_display_list_float(&mut self, layout_context: &LayoutContext) { - self.build_display_list_block_common(layout_context, RootOfStackingContextLevel); - self.base.display_list = mem::replace(&mut self.base.display_list, - DisplayList::new()).flatten(FloatStackingLevel) - } /// Calculate and set the block-size, offsets, etc. for absolutely positioned flow. /// @@ -1232,43 +1163,6 @@ impl BlockFlow { self.base.position.size.block = block_size; } - /// Add display items for Absolutely Positioned flow. - fn build_display_list_abs(&mut self, layout_context: &LayoutContext) { - self.build_display_list_block_common(layout_context, RootOfStackingContextLevel); - - if !self.base.absolute_position_info.layers_needed_for_positioned_flows && - !self.base.flags.needs_layer() { - // We didn't need a layer. - let z_index = self.fragment.style().get_box().z_index.number_or_zero(); - let level = PositionedDescendantStackingLevel(z_index); - self.base.display_list = mem::replace(&mut self.base.display_list, - DisplayList::new()).flatten(level); - return - } - - // If we got here, then we need a new layer. - let layer_rect = self.base.position.union(&self.base.overflow); - let size = Size2D(layer_rect.size.inline.to_nearest_px() as uint, - layer_rect.size.block.to_nearest_px() as uint); - let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint, - self.base.abs_position.y.to_nearest_px() as uint); - - let scroll_policy = if self.is_fixed() { - FixedPosition - } else { - Scrollable - }; - let display_list = mem::replace(&mut self.base.display_list, DisplayList::new()); - let new_layer = RenderLayer { - id: self.layer_id(0), - display_list: Arc::new(display_list.flatten(ContentStackingLevel)), - position: Rect(origin, size), - background_color: color::rgba(1.0, 1.0, 1.0, 0.0), - scroll_policy: scroll_policy, - }; - self.base.layers.push(new_layer) - } - /// Return the block-start outer edge of the hypothetical box for an absolute flow. /// /// This is wrt its parent flow box. @@ -1839,6 +1733,22 @@ impl Flow for BlockFlow { self.base.position.start.b = block_position } } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + if self.is_float() { + // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index: + // auto` kids into the parent stacking context, when that is supported. + self.build_display_list_for_floating_block(layout_context) + } else if self.is_absolutely_positioned() { + self.build_display_list_for_absolutely_positioned_block(layout_context) + } else { + self.build_display_list_for_block(layout_context, BlockLevel) + } + + if opts::get().validate_display_list_geometry { + self.base.validate_display_list_geometry(); + } + } } impl fmt::Show for BlockFlow { @@ -2562,3 +2472,4 @@ fn propagate_column_inline_sizes_to_child(kid: &mut Flow, *inline_start_margin_edge = *inline_start_margin_edge + inline_size } } + diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs new file mode 100644 index 00000000000..3f298a92686 --- /dev/null +++ b/components/layout/display_list_builder.rs @@ -0,0 +1,710 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Builds display lists from flows and fragments. +//! +//! Other browser engines sometimes call this "painting", but it is more accurately called display +//! list building, as the actual painting does not happen here—only deciding *what* we're going to +//! paint. + +#![deny(unsafe_block)] + +use block::BlockFlow; +use context::LayoutContext; +use flow::{mod, Flow}; +use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment}; +use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment, InlineBlockFragment}; +use fragment::{InputFragment, ScannedTextFragment, ScannedTextFragmentInfo, TableFragment}; +use fragment::{TableCellFragment, TableColumnFragment, TableRowFragment, TableWrapperFragment}; +use fragment::{UnscannedTextFragment}; +use model; +use util::{OpaqueNodeMethods, ToGfxColor}; + +use collections::dlist::DList; +use geom::approxeq::ApproxEq; +use geom::{Point2D, Rect, Size2D, SideOffsets2D}; +use gfx::color; +use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem}; +use gfx::display_list::{BorderDisplayItemClass, ContentStackingLevel, DisplayList}; +use gfx::display_list::{FloatStackingLevel, ImageDisplayItem, ImageDisplayItemClass}; +use gfx::display_list::{LineDisplayItem, LineDisplayItemClass, PositionedDescendantStackingLevel}; +use gfx::display_list::{PseudoDisplayItemClass, RootOfStackingContextLevel, SidewaysLeft}; +use gfx::display_list::{SidewaysRight, SolidColorDisplayItem, SolidColorDisplayItemClass}; +use gfx::display_list::{StackingLevel, TextDisplayItem, TextDisplayItemClass, Upright}; +use gfx::render_task::RenderLayer; +use servo_msg::compositor_msg::{FixedPosition, Scrollable}; +use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg}; +use servo_net::image::holder::ImageHolder; +use servo_util::geometry::{mod, Au, ZERO_RECT}; +use servo_util::logical_geometry::{LogicalRect, WritingMode}; +use servo_util::opts; +use std::mem; +use style::{ComputedValues, RGBA}; +use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; +use style::computed_values::{visibility}; +use sync::Arc; + +pub trait FragmentDisplayListBuilding { + /// Adds the display items necessary to paint the background of this fragment to the display + /// list if necessary. + fn build_display_list_for_background_if_applicable(&self, + style: &ComputedValues, + list: &mut DisplayList, + layout_context: &LayoutContext, + level: StackingLevel, + absolute_bounds: &Rect<Au>, + clip_rect: &Rect<Au>); + + /// Adds the display items necessary to paint the borders of this fragment to a display list if + /// necessary. + fn build_display_list_for_borders_if_applicable(&self, + style: &ComputedValues, + list: &mut DisplayList, + abs_bounds: &Rect<Au>, + level: StackingLevel, + clip_rect: &Rect<Au>); + + fn build_debug_borders_around_text_fragments(&self, + display_list: &mut DisplayList, + flow_origin: Point2D<Au>, + text_fragment: &ScannedTextFragmentInfo, + clip_rect: &Rect<Au>); + + fn build_debug_borders_around_fragment(&self, + display_list: &mut DisplayList, + flow_origin: Point2D<Au>, + clip_rect: &Rect<Au>); + + /// Adds the display items for this fragment to the given stacking context. + /// + /// Arguments: + /// + /// * `display_list`: The unflattened 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. + /// * `clip_rect`: The rectangle to clip the display items to. + fn build_display_list(&mut self, + display_list: &mut DisplayList, + layout_context: &LayoutContext, + flow_origin: Point2D<Au>, + background_and_border_level: BackgroundAndBorderLevel, + clip_rect: &Rect<Au>); + + /// Sends the size and position of this iframe fragment to the constellation. This is out of + /// line to guide inlining. + fn finalize_position_and_size_of_iframe(&self, + iframe_fragment: &IframeFragmentInfo, + offset: Point2D<Au>, + layout_context: &LayoutContext); + + fn clip_rect_for_children(&self, current_clip_rect: Rect<Au>, flow_origin: Point2D<Au>) + -> Rect<Au>; +} + +impl FragmentDisplayListBuilding for Fragment { + fn build_display_list_for_background_if_applicable(&self, + style: &ComputedValues, + list: &mut DisplayList, + layout_context: &LayoutContext, + level: StackingLevel, + absolute_bounds: &Rect<Au>, + clip_rect: &Rect<Au>) { + // FIXME: This causes a lot of background colors to be displayed when they are clearly not + // needed. We could use display list optimization to clean this up, but it still seems + // inefficient. What we really want is something like "nearest ancestor element that + // doesn't have a fragment". + let background_color = style.resolve_color(style.get_background().background_color); + if !background_color.alpha.approx_eq(&0.0) { + let display_item = box SolidColorDisplayItem { + base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect), + color: background_color.to_gfx_color(), + }; + + list.push(SolidColorDisplayItemClass(display_item)) + } + + // The background image is painted on top of the background color. + // Implements background image, per spec: + // http://www.w3.org/TR/CSS21/colors.html#background + let background = style.get_background(); + let image_url = match background.background_image { + None => return, + Some(ref image_url) => image_url, + }; + + let mut holder = ImageHolder::new(image_url.clone(), + layout_context.shared.image_cache.clone()); + let image = match holder.get_image(self.node.to_untrusted_node_address()) { + None => { + // No image data at all? Do nothing. + // + // TODO: Add some kind of placeholder background image. + debug!("(building display list) no background image :("); + return + } + Some(image) => image, + }; + debug!("(building display list) building background image"); + + let image_width = Au::from_px(image.width as int); + let image_height = Au::from_px(image.height as int); + let mut bounds = *absolute_bounds; + + // Clip. + // + // TODO: Check the bounds to see if a clip item is actually required. + let clip_rect = clip_rect.intersection(&bounds).unwrap_or(ZERO_RECT); + + // Use background-attachment to get the initial virtual origin + let (virtual_origin_x, virtual_origin_y) = match background.background_attachment { + background_attachment::scroll => { + (absolute_bounds.origin.x, absolute_bounds.origin.y) + } + background_attachment::fixed => { + (Au(0), Au(0)) + } + }; + + // Use background-position to get the offset + let horizontal_position = model::specified(background.background_position.horizontal, + bounds.size.width - image_width); + let vertical_position = model::specified(background.background_position.vertical, + bounds.size.height - image_height); + + let abs_x = virtual_origin_x + horizontal_position; + let abs_y = virtual_origin_y + vertical_position; + + // Adjust origin and size based on background-repeat + match background.background_repeat { + background_repeat::no_repeat => { + bounds.origin.x = abs_x; + bounds.origin.y = abs_y; + bounds.size.width = image_width; + bounds.size.height = image_height; + } + background_repeat::repeat_x => { + bounds.origin.y = abs_y; + bounds.size.height = image_height; + ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, + abs_x, image.width); + } + background_repeat::repeat_y => { + bounds.origin.x = abs_x; + bounds.size.width = image_width; + ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, + abs_y, image.height); + } + background_repeat::repeat => { + ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, + abs_x, image.width); + ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, + abs_y, image.height); + } + }; + + // Create the image display item. + let image_display_item = ImageDisplayItemClass(box ImageDisplayItem { + base: BaseDisplayItem::new(bounds, self.node, level, clip_rect), + image: image.clone(), + stretch_size: Size2D(Au::from_px(image.width as int), + Au::from_px(image.height as int)), + }); + list.push(image_display_item) + } + + /// Adds the display items necessary to paint the borders of this fragment to a display list if + /// necessary. + fn build_display_list_for_borders_if_applicable(&self, + style: &ComputedValues, + list: &mut DisplayList, + abs_bounds: &Rect<Au>, + level: StackingLevel, + clip_rect: &Rect<Au>) { + let border = style.logical_border_width(); + if border.is_zero() { + return + } + + let top_color = style.resolve_color(style.get_border().border_top_color); + let right_color = style.resolve_color(style.get_border().border_right_color); + let bottom_color = style.resolve_color(style.get_border().border_bottom_color); + let left_color = style.resolve_color(style.get_border().border_left_color); + + // Append the border to the display list. + let border_display_item = box BorderDisplayItem { + base: BaseDisplayItem::new(*abs_bounds, self.node, level, *clip_rect), + border: border.to_physical(style.writing_mode), + color: SideOffsets2D::new(top_color.to_gfx_color(), + right_color.to_gfx_color(), + bottom_color.to_gfx_color(), + left_color.to_gfx_color()), + style: SideOffsets2D::new(style.get_border().border_top_style, + style.get_border().border_right_style, + style.get_border().border_bottom_style, + style.get_border().border_left_style) + }; + + list.push(BorderDisplayItemClass(border_display_item)) + } + + fn build_debug_borders_around_text_fragments(&self, + display_list: &mut DisplayList, + flow_origin: Point2D<Au>, + text_fragment: &ScannedTextFragmentInfo, + clip_rect: &Rect<Au>) { + // 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); + + // Compute the text fragment bounds and draw a border surrounding them. + let border_display_item = box BorderDisplayItem { + base: BaseDisplayItem::new(absolute_fragment_bounds, + self.node, + ContentStackingLevel, + *clip_rect), + border: SideOffsets2D::new_all_same(Au::from_px(1)), + color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), + style: SideOffsets2D::new_all_same(border_style::solid) + }; + display_list.push(BorderDisplayItemClass(border_display_item)); + + // 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; + baseline.size.block = Au(0); + let mut baseline = baseline.to_physical(self.style.writing_mode, container_size); + baseline.origin = baseline.origin + flow_origin; + + let line_display_item = box LineDisplayItem { + base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect), + color: color::rgb(0, 200, 0), + style: border_style::dashed, + }; + display_list.push(LineDisplayItemClass(line_display_item)); + } + + fn build_debug_borders_around_fragment(&self, + display_list: &mut DisplayList, + flow_origin: Point2D<Au>, + clip_rect: &Rect<Au>) { + // 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. + let border_display_item = box BorderDisplayItem { + base: BaseDisplayItem::new(absolute_fragment_bounds, + self.node, + ContentStackingLevel, + *clip_rect), + border: SideOffsets2D::new_all_same(Au::from_px(1)), + color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), + style: SideOffsets2D::new_all_same(border_style::solid) + }; + display_list.push(BorderDisplayItemClass(border_display_item)) + } + + fn build_display_list(&mut self, + display_list: &mut DisplayList, + layout_context: &LayoutContext, + flow_origin: Point2D<Au>, + background_and_border_level: BackgroundAndBorderLevel, + clip_rect: &Rect<Au>) { + // FIXME(#2795): Get the real container size + let container_size = Size2D::zero(); + let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| { + let physical_rect = logical_rect.to_physical(writing_mode, container_size); + Rect(physical_rect.origin + flow_origin, physical_rect.size) + }; + // Fragment position wrt to the owning flow. + let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box); + debug!("Fragment::build_display_list at rel={}, abs={}: {}", + self.border_box, + absolute_fragment_bounds, + self); + debug!("Fragment::build_display_list: dirty={}, flow_origin={}", + layout_context.shared.dirty, + flow_origin); + + if self.style().get_inheritedbox().visibility != visibility::visible { + return + } + + if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) { + debug!("Fragment::build_display_list: Did not intersect..."); + return + } + + debug!("Fragment::build_display_list: intersected. Adding display item..."); + + if self.is_primary_fragment() { + let level = + StackingLevel::from_background_and_border_level(background_and_border_level); + + // Add a pseudo-display item for content box queries. This is a very bogus thing to do. + let base_display_item = box BaseDisplayItem::new(absolute_fragment_bounds, + self.node, + level, + *clip_rect); + display_list.push(PseudoDisplayItemClass(base_display_item)); + + // Add the background to the list, if applicable. + match self.inline_context { + Some(ref 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_rect); + } + } + None => {} + } + match self.specific { + ScannedTextFragment(_) => {}, + _ => { + self.build_display_list_for_background_if_applicable( + &*self.style, + display_list, + layout_context, + level, + &absolute_fragment_bounds, + clip_rect); + } + } + + // Add a border, if applicable. + // + // TODO: Outlines. + match self.inline_context { + Some(ref 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_rect); + } + } + None => {} + } + match self.specific { + ScannedTextFragment(_) => {}, + _ => { + self.build_display_list_for_borders_if_applicable( + &*self.style, + display_list, + &absolute_fragment_bounds, + level, + clip_rect); + } + } + } + + let content_box = self.content_box(); + let absolute_content_box = rect_to_absolute(self.style.writing_mode, content_box); + + // Create special per-fragment-type display items. + match self.specific { + UnscannedTextFragment(_) => fail!("Shouldn't see unscanned fragments here."), + TableColumnFragment(_) => fail!("Shouldn't see table column fragments here."), + ScannedTextFragment(ref text_fragment) => { + // Create the text display item. + let orientation = if self.style.writing_mode.is_vertical() { + if self.style.writing_mode.is_sideways_left() { + SidewaysLeft + } else { + SidewaysRight + } + } else { + Upright + }; + + let metrics = &text_fragment.run.font_metrics; + let baseline_origin ={ + let mut tmp = content_box.start; + tmp.b = tmp.b + metrics.ascent; + tmp.to_physical(self.style.writing_mode, container_size) + flow_origin + }; + + let text_display_item = box TextDisplayItem { + base: BaseDisplayItem::new(absolute_content_box, + self.node, + ContentStackingLevel, + *clip_rect), + text_run: text_fragment.run.clone(), + range: text_fragment.range, + text_color: self.style().get_color().color.to_gfx_color(), + orientation: orientation, + baseline_origin: baseline_origin, + }; + display_list.push(TextDisplayItemClass(text_display_item)); + + // Create display items for text decoration + { + let line = |maybe_color: Option<RGBA>, rect: || -> LogicalRect<Au>| { + match maybe_color { + None => {}, + Some(color) => { + display_list.push(SolidColorDisplayItemClass( + box SolidColorDisplayItem { + base: BaseDisplayItem::new( + rect_to_absolute( + self.style.writing_mode, + rect()), + self.node, + ContentStackingLevel, + *clip_rect), + color: color.to_gfx_color(), + })); + } + } + }; + + let text_decorations = + self.style().get_inheritedtext()._servo_text_decorations_in_effect; + line(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; + rect + }); + + line(text_decorations.overline, || { + let mut rect = content_box.clone(); + rect.size.block = metrics.underline_size; + rect + }); + + line(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; + rect + }); + } + + if opts::get().show_debug_fragment_borders { + self.build_debug_borders_around_text_fragments(display_list, + flow_origin, + text_fragment, + clip_rect); + } + } + GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | + TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment | + InlineAbsoluteHypotheticalFragment(_) => { + if opts::get().show_debug_fragment_borders { + self.build_debug_borders_around_fragment(display_list, + flow_origin, + clip_rect); + } + } + ImageFragment(ref mut image_fragment) => { + let image_ref = &mut image_fragment.image; + match image_ref.get_image(self.node.to_untrusted_node_address()) { + Some(image) => { + debug!("(building display list) building image fragment"); + + // Place the image into the display list. + let image_display_item = box ImageDisplayItem { + base: BaseDisplayItem::new(absolute_content_box, + self.node, + ContentStackingLevel, + *clip_rect), + image: image.clone(), + stretch_size: absolute_content_box.size, + }; + + display_list.push(ImageDisplayItemClass(image_display_item)) + } + None => { + // No image data at all? Do nothing. + // + // TODO: Add some kind of placeholder image. + debug!("(building display list) no image :("); + } + } + } + } + + if opts::get().show_debug_fragment_borders { + self.build_debug_borders_around_fragment(display_list, flow_origin, clip_rect) + } + + // If this is an iframe, then send its position and size up to the constellation. + // + // FIXME(pcwalton): Doing this during display list construction seems potentially + // problematic if iframes are outside the area we're computing the display list for, since + // they won't be able to reflow at all until the user scrolls to them. Perhaps we should + // separate this into two parts: first we should send the size only to the constellation + // once that's computed during assign-block-sizes, and second we should should send the + // origin to the constellation here during display list construction. This should work + // because layout for the iframe only needs to know size, and origin is only relevant if + // the iframe is actually going to be displayed. + match self.specific { + IframeFragment(ref iframe_fragment) => { + self.finalize_position_and_size_of_iframe(iframe_fragment, + absolute_fragment_bounds.origin, + layout_context) + } + _ => {} + } + } + + #[inline(never)] + fn finalize_position_and_size_of_iframe(&self, + iframe_fragment: &IframeFragmentInfo, + offset: Point2D<Au>, + layout_context: &LayoutContext) { + let border_padding = (self.border_padding).to_physical(self.style.writing_mode); + let content_size = self.content_box().size.to_physical(self.style.writing_mode); + let iframe_rect = Rect(Point2D(geometry::to_frac_px(offset.x + border_padding.left) as f32, + geometry::to_frac_px(offset.y + border_padding.top) as f32), + Size2D(geometry::to_frac_px(content_size.width) as f32, + geometry::to_frac_px(content_size.height) as f32)); + + debug!("finalizing position and size of iframe for {:?},{:?}", + iframe_fragment.pipeline_id, + iframe_fragment.subpage_id); + let ConstellationChan(ref chan) = layout_context.shared.constellation_chan; + chan.send(FrameRectMsg(iframe_fragment.pipeline_id, + iframe_fragment.subpage_id, + iframe_rect)); + } + + fn clip_rect_for_children(&self, current_clip_rect: Rect<Au>, flow_origin: Point2D<Au>) + -> Rect<Au> { + // Don't clip if we're text. + match self.specific { + ScannedTextFragment(_) => return current_clip_rect, + _ => {} + } + + // Only clip if `overflow` tells us to. + match self.style.get_box().overflow { + overflow::hidden | overflow::auto | overflow::scroll => {} + _ => return current_clip_rect, + } + + // 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_rect.intersection(&Rect(physical_rect.origin + flow_origin, + physical_rect.size)).unwrap_or(ZERO_RECT) + } +} + +pub trait BlockFlowDisplayListBuilding { + fn build_display_list_for_block(&mut self, + layout_context: &LayoutContext, + background_border_level: BackgroundAndBorderLevel); + fn build_display_list_for_absolutely_positioned_block(&mut self, + layout_context: &LayoutContext); + fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext); +} + +impl BlockFlowDisplayListBuilding for BlockFlow { + fn build_display_list_for_block(&mut self, + layout_context: &LayoutContext, + background_border_level: BackgroundAndBorderLevel) { + let relative_offset = + self.fragment.relative_position(&self.base + .absolute_position_info + .relative_containing_block_size); + + // Add the box that starts the block context. + let mut display_list = DisplayList::new(); + self.fragment.build_display_list(&mut display_list, + layout_context, + self.base.abs_position.add_size( + &relative_offset.to_physical(self.base.writing_mode)), + background_border_level, + &self.base.clip_rect); + + let mut child_layers = DList::new(); + for kid in self.base.child_iter() { + if kid.is_absolutely_positioned() { + // All absolute flows will be handled by their containing block. + continue + } + + display_list.push_all_move(mem::replace(&mut flow::mut_base(kid).display_list, + DisplayList::new())); + child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new())) + } + + // Process absolute descendant links. + for abs_descendant_link in self.base.abs_descendants.iter() { + // TODO(pradeep): Send in our absolute position directly. + display_list.push_all_move(mem::replace( + &mut flow::mut_base(abs_descendant_link).display_list, + DisplayList::new())); + child_layers.append(mem::replace(&mut flow::mut_base(abs_descendant_link).layers, + DList::new())); + } + + self.base.display_list = display_list; + self.base.layers = child_layers + } + + fn build_display_list_for_absolutely_positioned_block(&mut self, + layout_context: &LayoutContext) { + self.build_display_list_for_block(layout_context, RootOfStackingContextLevel); + + if !self.base.absolute_position_info.layers_needed_for_positioned_flows && + !self.base.flags.needs_layer() { + // We didn't need a layer. + let z_index = self.fragment.style().get_box().z_index.number_or_zero(); + let level = PositionedDescendantStackingLevel(z_index); + self.base.display_list = mem::replace(&mut self.base.display_list, + DisplayList::new()).flatten(level); + return + } + + // If we got here, then we need a new layer. + let layer_rect = self.base.position.union(&self.base.overflow); + let size = Size2D(layer_rect.size.inline.to_nearest_px() as uint, + layer_rect.size.block.to_nearest_px() as uint); + let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint, + self.base.abs_position.y.to_nearest_px() as uint); + + let scroll_policy = if self.is_fixed() { + FixedPosition + } else { + Scrollable + }; + let display_list = mem::replace(&mut self.base.display_list, DisplayList::new()); + let new_layer = RenderLayer { + id: self.layer_id(0), + display_list: Arc::new(display_list.flatten(ContentStackingLevel)), + position: Rect(origin, size), + background_color: color::rgba(1.0, 1.0, 1.0, 0.0), + scroll_policy: scroll_policy, + }; + self.base.layers.push(new_layer) + } + + fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext) { + self.build_display_list_for_block(layout_context, RootOfStackingContextLevel); + self.base.display_list = mem::replace(&mut self.base.display_list, + DisplayList::new()).flatten(FloatStackingLevel) + } +} + diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 269aac1b81d..38ee01b3c96 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -54,7 +54,6 @@ use servo_msg::compositor_msg::LayerId; use servo_util::geometry::Au; use servo_util::logical_geometry::WritingMode; use servo_util::logical_geometry::{LogicalRect, LogicalSize}; -use servo_util::opts; use std::mem; use std::num::Zero; use std::fmt; @@ -209,6 +208,9 @@ pub trait Flow: fmt::Show + ToString + Sync { // The default implementation is a no-op. } + /// Phase 5 of reflow: builds display lists. + fn build_display_list(&mut self, layout_context: &LayoutContext); + /// Returns the direction that this flow clears floats in, if any. fn float_clearance(&self) -> clear::T { clear::none @@ -406,9 +408,6 @@ pub trait ImmutableFlowUtils { /// Dumps the flow tree for debugging, with a prefix to indicate that we're at the given level. fn dump_with_level(self, level: uint); - - /// Print an error when this Flow's display list items are not within its boundaries. - fn validate_display_list_geometry(self); } pub trait MutableFlowUtils { @@ -425,9 +424,6 @@ pub trait MutableFlowUtils { /// Computes the overflow region for this flow. fn store_overflow(self, _: &LayoutContext); - /// Builds the display lists for this flow. - fn build_display_list(self, layout_context: &LayoutContext); - /// Gathers static block-offsets bubbled up by kids. /// /// This essentially gives us offsets of all absolutely positioned direct descendants and all @@ -858,6 +854,28 @@ impl BaseFlow { let p = self as *const _; p as uint } + + pub fn validate_display_list_geometry(&self) { + let position_with_overflow = self.position.union(&self.overflow); + let bounds = Rect(self.abs_position, + Size2D(position_with_overflow.size.inline, + position_with_overflow.size.block)); + + for item in self.display_list.iter() { + let paint_bounds = match item.base().bounds.intersection(&item.base().clip_rect) { + None => continue, + Some(rect) => rect, + }; + + if paint_bounds.is_empty() { + continue; + } + + if bounds.union(&paint_bounds) != bounds { + error!("DisplayList item {} outside of Flow overflow ({})", item, paint_bounds); + } + } + } } impl<'a> ImmutableFlowUtils for &'a Flow + 'a { @@ -1028,28 +1046,6 @@ impl<'a> ImmutableFlowUtils for &'a Flow + 'a { kid.dump_with_level(level + 1) } } - - fn validate_display_list_geometry(self) { - let position_with_overflow = base(self).position.union(&base(self).overflow); - let bounds = Rect(base(self).abs_position, - Size2D(position_with_overflow.size.inline, - position_with_overflow.size.block)); - - for item in base(self).display_list.iter() { - let paint_bounds = match item.base().bounds.intersection(&item.base().clip_rect) { - None => continue, - Some(rect) => rect, - }; - - if paint_bounds.is_empty() { - continue; - } - - if bounds.union(&paint_bounds) != bounds { - error!("DisplayList item {} outside of Flow overflow ({})", item, paint_bounds); - } - } - } } impl<'a> MutableFlowUtils for &'a mut Flow + 'a { @@ -1110,44 +1106,6 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a { mut_base(self).overflow = overflow; } - /// Push display items for current flow and its descendants onto the appropriate display lists - /// of the given stacking context. - /// - /// Arguments: - /// - /// * `builder`: The display list builder, which contains information used during the entire - /// display list building pass. - /// - /// * `info`: Per-flow display list building information. - fn build_display_list(self, layout_context: &LayoutContext) { - debug!("Flow: building display list"); - match self.class() { - BlockFlowClass => self.as_block().build_display_list_block(layout_context), - InlineFlowClass => self.as_inline().build_display_list_inline(layout_context), - TableWrapperFlowClass => { - self.as_table_wrapper().build_display_list_table_wrapper(layout_context) - } - TableFlowClass => self.as_table().build_display_list_table(layout_context), - TableRowGroupFlowClass => { - self.as_table_rowgroup().build_display_list_table_rowgroup(layout_context) - } - TableRowFlowClass => self.as_table_row().build_display_list_table_row(layout_context), - TableCaptionFlowClass => { - self.as_table_caption().build_display_list_table_caption(layout_context) - } - TableCellFlowClass => { - self.as_table_cell().build_display_list_table_cell(layout_context) - } - TableColGroupFlowClass => { - // Nothing to do here, as column groups don't render. - } - } - - if opts::get().validate_display_list_geometry { - self.validate_display_list_geometry(); - } - } - /// Collect and update static y-offsets bubbled up by kids. /// /// This would essentially give us offsets of all absolutely positioned diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 80d6c68be2a..c946ecf79a9 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -19,44 +19,33 @@ use layout_debug; use model::{Auto, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, Specified, specified}; use model; use text; -use util::{OpaqueNodeMethods, ToGfxColor}; +use util::OpaqueNodeMethods; use wrapper::{TLayoutNode, ThreadSafeLayoutNode}; -use geom::{Point2D, Rect, Size2D, SideOffsets2D}; -use geom::approxeq::ApproxEq; -use gfx::color::rgb; -use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem}; -use gfx::display_list::{BorderDisplayItemClass, ContentStackingLevel, DisplayList}; -use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; -use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass}; -use gfx::display_list::{SidewaysLeft, SidewaysRight, SolidColorDisplayItem}; -use gfx::display_list::{SolidColorDisplayItemClass, StackingLevel, TextDisplayItem}; -use gfx::display_list::{TextDisplayItemClass, Upright}; +use geom::Size2D; +use gfx::display_list::OpaqueNode; use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; use script_traits::UntrustedNodeAddress; use serialize::{Encodable, Encoder}; -use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; +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, ZERO_RECT}; +use servo_util::geometry::Au; use servo_util::geometry; -use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode}; -use servo_util::opts; +use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin}; use servo_util::range::*; use servo_util::smallvec::SmallVec; use servo_util::str::is_whitespace; use std::cmp::{max, min}; use std::fmt; use std::from_str::FromStr; -use std::num::Zero; use string_cache::Atom; -use style::{ComputedValues, TElement, TNode, cascade_anonymous, RGBA}; +use style::{ComputedValues, TElement, TNode, cascade_anonymous}; use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::computed_values::{LengthOrPercentageOrNone}; -use style::computed_values::{overflow, LPA_Auto, background_attachment}; -use style::computed_values::{background_repeat, border_style, clear, position, text_align}; -use style::computed_values::{text_decoration, vertical_align, visibility, white_space}; +use style::computed_values::{LPA_Auto, clear, position, text_align, text_decoration}; +use style::computed_values::{vertical_align, white_space}; use sync::{Arc, Mutex}; use url::Url; @@ -132,6 +121,8 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Fragment { } /// Info specific to the kind of fragment. Keep this enum small. +/// +/// FIXME(pcwalton): We have completely failed at keeping this enum small. #[deriving(Clone)] pub enum SpecificFragmentInfo { GenericFragment, @@ -935,480 +926,6 @@ impl Fragment { } } - /// Adds the display items necessary to paint the background of this fragment to the display - /// list if necessary. - pub fn build_display_list_for_background_if_applicable(&self, - style: &ComputedValues, - list: &mut DisplayList, - layout_context: &LayoutContext, - level: StackingLevel, - absolute_bounds: &Rect<Au>, - clip_rect: &Rect<Au>) { - // FIXME: This causes a lot of background colors to be displayed when they are clearly not - // needed. We could use display list optimization to clean this up, but it still seems - // inefficient. What we really want is something like "nearest ancestor element that - // doesn't have a fragment". - let background_color = style.resolve_color(style.get_background().background_color); - if !background_color.alpha.approx_eq(&0.0) { - let display_item = box SolidColorDisplayItem { - base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect), - color: background_color.to_gfx_color(), - }; - - list.push(SolidColorDisplayItemClass(display_item)) - } - - // The background image is painted on top of the background color. - // Implements background image, per spec: - // http://www.w3.org/TR/CSS21/colors.html#background - let background = style.get_background(); - let image_url = match background.background_image { - None => return, - Some(ref image_url) => image_url, - }; - - let mut holder = ImageHolder::new(image_url.clone(), layout_context.shared.image_cache.clone()); - let image = match holder.get_image(self.node.to_untrusted_node_address()) { - None => { - // No image data at all? Do nothing. - // - // TODO: Add some kind of placeholder background image. - debug!("(building display list) no background image :("); - return - } - Some(image) => image, - }; - debug!("(building display list) building background image"); - - let image_width = Au::from_px(image.width as int); - let image_height = Au::from_px(image.height as int); - let mut bounds = *absolute_bounds; - - // Clip. - // - // TODO: Check the bounds to see if a clip item is actually required. - let clip_rect = clip_rect.intersection(&bounds).unwrap_or(ZERO_RECT); - - // Use background-attachment to get the initial virtual origin - let (virtual_origin_x, virtual_origin_y) = match background.background_attachment { - background_attachment::scroll => { - (absolute_bounds.origin.x, absolute_bounds.origin.y) - } - background_attachment::fixed => { - (Au(0), Au(0)) - } - }; - - // Use background-position to get the offset - let horizontal_position = model::specified(background.background_position.horizontal, - bounds.size.width - image_width); - let vertical_position = model::specified(background.background_position.vertical, - bounds.size.height - image_height); - - let abs_x = virtual_origin_x + horizontal_position; - let abs_y = virtual_origin_y + vertical_position; - - // Adjust origin and size based on background-repeat - match background.background_repeat { - background_repeat::no_repeat => { - bounds.origin.x = abs_x; - bounds.origin.y = abs_y; - bounds.size.width = image_width; - bounds.size.height = image_height; - } - background_repeat::repeat_x => { - bounds.origin.y = abs_y; - bounds.size.height = image_height; - ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, - abs_x, image.width); - } - background_repeat::repeat_y => { - bounds.origin.x = abs_x; - bounds.size.width = image_width; - ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, - abs_y, image.height); - } - background_repeat::repeat => { - ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, - abs_x, image.width); - ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, - abs_y, image.height); - } - }; - - // Create the image display item. - let image_display_item = ImageDisplayItemClass(box ImageDisplayItem { - base: BaseDisplayItem::new(bounds, self.node, level, clip_rect), - image: image.clone(), - stretch_size: Size2D(Au::from_px(image.width as int), - Au::from_px(image.height as int)), - }); - list.push(image_display_item) - } - - /// Adds the display items necessary to paint the borders of this fragment to a display list if - /// necessary. - pub fn build_display_list_for_borders_if_applicable(&self, - style: &ComputedValues, - list: &mut DisplayList, - abs_bounds: &Rect<Au>, - level: StackingLevel, - clip_rect: &Rect<Au>) { - let border = style.logical_border_width(); - if border.is_zero() { - return - } - - let top_color = style.resolve_color(style.get_border().border_top_color); - let right_color = style.resolve_color(style.get_border().border_right_color); - let bottom_color = style.resolve_color(style.get_border().border_bottom_color); - let left_color = style.resolve_color(style.get_border().border_left_color); - - // Append the border to the display list. - let border_display_item = box BorderDisplayItem { - base: BaseDisplayItem::new(*abs_bounds, self.node, level, *clip_rect), - border: border.to_physical(style.writing_mode), - color: SideOffsets2D::new(top_color.to_gfx_color(), - right_color.to_gfx_color(), - bottom_color.to_gfx_color(), - left_color.to_gfx_color()), - style: SideOffsets2D::new(style.get_border().border_top_style, - style.get_border().border_right_style, - style.get_border().border_bottom_style, - style.get_border().border_left_style) - }; - - list.push(BorderDisplayItemClass(border_display_item)) - } - - fn build_debug_borders_around_text_fragments(&self, - display_list: &mut DisplayList, - flow_origin: Point2D<Au>, - text_fragment: &ScannedTextFragmentInfo, - clip_rect: &Rect<Au>) { - // 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); - - // Compute the text fragment bounds and draw a border surrounding them. - let border_display_item = box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, - self.node, - ContentStackingLevel, - *clip_rect), - border: SideOffsets2D::new_all_same(Au::from_px(1)), - color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), - style: SideOffsets2D::new_all_same(border_style::solid) - }; - display_list.push(BorderDisplayItemClass(border_display_item)); - - // 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; - baseline.size.block = Au(0); - let mut baseline = baseline.to_physical(self.style.writing_mode, container_size); - baseline.origin = baseline.origin + flow_origin; - - let line_display_item = box LineDisplayItem { - base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect), - color: rgb(0, 200, 0), - style: border_style::dashed, - }; - display_list.push(LineDisplayItemClass(line_display_item)); - } - - fn build_debug_borders_around_fragment(&self, - display_list: &mut DisplayList, - flow_origin: Point2D<Au>, - clip_rect: &Rect<Au>) { - // 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. - let border_display_item = box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, - self.node, - ContentStackingLevel, - *clip_rect), - border: SideOffsets2D::new_all_same(Au::from_px(1)), - color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), - style: SideOffsets2D::new_all_same(border_style::solid) - }; - display_list.push(BorderDisplayItemClass(border_display_item)) - } - - /// Adds the display items for this fragment to the given stacking context. - /// - /// Arguments: - /// - /// * `display_list`: The unflattened 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. - /// * `clip_rect`: The rectangle to clip the display items to. - pub fn build_display_list(&mut self, - display_list: &mut DisplayList, - layout_context: &LayoutContext, - flow_origin: Point2D<Au>, - background_and_border_level: BackgroundAndBorderLevel, - clip_rect: &Rect<Au>) { - // FIXME(#2795): Get the real container size - let container_size = Size2D::zero(); - let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| { - let physical_rect = logical_rect.to_physical(writing_mode, container_size); - Rect(physical_rect.origin + flow_origin, physical_rect.size) - }; - // Fragment position wrt to the owning flow. - let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box); - debug!("Fragment::build_display_list at rel={}, abs={}: {}", - self.border_box, - absolute_fragment_bounds, - self); - debug!("Fragment::build_display_list: dirty={}, flow_origin={}", - layout_context.shared.dirty, - flow_origin); - - if self.style().get_inheritedbox().visibility != visibility::visible { - return - } - - if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) { - debug!("Fragment::build_display_list: Did not intersect..."); - return - } - - debug!("Fragment::build_display_list: intersected. Adding display item..."); - - if self.is_primary_fragment() { - let level = - StackingLevel::from_background_and_border_level(background_and_border_level); - - // Add a pseudo-display item for content box queries. This is a very bogus thing to do. - let base_display_item = box BaseDisplayItem::new(absolute_fragment_bounds, - self.node, - level, - *clip_rect); - display_list.push(PseudoDisplayItemClass(base_display_item)); - - // Add the background to the list, if applicable. - match self.inline_context { - Some(ref 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_rect); - } - } - None => {} - } - match self.specific { - ScannedTextFragment(_) => {}, - _ => { - self.build_display_list_for_background_if_applicable( - &*self.style, - display_list, - layout_context, - level, - &absolute_fragment_bounds, - clip_rect); - } - } - - // Add a border, if applicable. - // - // TODO: Outlines. - match self.inline_context { - Some(ref 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_rect); - } - } - None => {} - } - match self.specific { - ScannedTextFragment(_) => {}, - _ => { - self.build_display_list_for_borders_if_applicable( - &*self.style, - display_list, - &absolute_fragment_bounds, - level, - clip_rect); - } - } - } - - let content_box = self.content_box(); - let absolute_content_box = rect_to_absolute(self.style.writing_mode, content_box); - - // Create special per-fragment-type display items. - match self.specific { - UnscannedTextFragment(_) => fail!("Shouldn't see unscanned fragments here."), - TableColumnFragment(_) => fail!("Shouldn't see table column fragments here."), - ScannedTextFragment(ref text_fragment) => { - // Create the text display item. - let orientation = if self.style.writing_mode.is_vertical() { - if self.style.writing_mode.is_sideways_left() { - SidewaysLeft - } else { - SidewaysRight - } - } else { - Upright - }; - - let metrics = &text_fragment.run.font_metrics; - let baseline_origin ={ - let mut tmp = content_box.start; - tmp.b = tmp.b + metrics.ascent; - tmp.to_physical(self.style.writing_mode, container_size) + flow_origin - }; - - let text_display_item = box TextDisplayItem { - base: BaseDisplayItem::new(absolute_content_box, - self.node, - ContentStackingLevel, - *clip_rect), - text_run: text_fragment.run.clone(), - range: text_fragment.range, - text_color: self.style().get_color().color.to_gfx_color(), - orientation: orientation, - baseline_origin: baseline_origin, - }; - display_list.push(TextDisplayItemClass(text_display_item)); - - // Create display items for text decoration - { - let line = |maybe_color: Option<RGBA>, rect: || -> LogicalRect<Au>| { - match maybe_color { - None => {}, - Some(color) => { - display_list.push(SolidColorDisplayItemClass( - box SolidColorDisplayItem { - base: BaseDisplayItem::new( - rect_to_absolute( - self.style.writing_mode, - rect()), - self.node, - ContentStackingLevel, - *clip_rect), - color: color.to_gfx_color(), - })); - } - } - }; - - let text_decorations = - self.style().get_inheritedtext()._servo_text_decorations_in_effect; - line(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; - rect - }); - - line(text_decorations.overline, || { - let mut rect = content_box.clone(); - rect.size.block = metrics.underline_size; - rect - }); - - line(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; - rect - }); - } - - if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_text_fragments(display_list, - flow_origin, - text_fragment, - clip_rect); - } - } - GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | - TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment | - InlineAbsoluteHypotheticalFragment(_) => { - if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, - flow_origin, - clip_rect); - } - } - ImageFragment(ref mut image_fragment) => { - let image_ref = &mut image_fragment.image; - match image_ref.get_image(self.node.to_untrusted_node_address()) { - Some(image) => { - debug!("(building display list) building image fragment"); - - // Place the image into the display list. - let image_display_item = box ImageDisplayItem { - base: BaseDisplayItem::new(absolute_content_box, - self.node, - ContentStackingLevel, - *clip_rect), - image: image.clone(), - stretch_size: absolute_content_box.size, - }; - - display_list.push(ImageDisplayItemClass(image_display_item)) - } - None => { - // No image data at all? Do nothing. - // - // TODO: Add some kind of placeholder image. - debug!("(building display list) no image :("); - } - } - } - } - - // FIXME(pcwalton): This is a bit of an abuse of the logging - // infrastructure. We should have a real `SERVO_DEBUG` system. - debug!("{:?}", - self.build_debug_borders_around_fragment(display_list, flow_origin, clip_rect)) - - // If this is an iframe, then send its position and size up to the constellation. - // - // FIXME(pcwalton): Doing this during display list construction seems potentially - // problematic if iframes are outside the area we're computing the display list for, since - // they won't be able to reflow at all until the user scrolls to them. Perhaps we should - // separate this into two parts: first we should send the size only to the constellation - // once that's computed during assign-block-sizes, and second we should should send the - // origin to the constellation here during display list construction. This should work - // because layout for the iframe only needs to know size, and origin is only relevant if - // the iframe is actually going to be displayed. - match self.specific { - IframeFragment(ref iframe_fragment) => { - self.finalize_position_and_size_of_iframe(iframe_fragment, - absolute_fragment_bounds.origin, - layout_context) - } - _ => {} - } - } - /// Computes the intrinsic inline-sizes of this fragment. pub fn compute_intrinsic_inline_sizes(&mut self) -> IntrinsicISizesContribution { let mut result = self.style_specified_intrinsic_inline_size(); @@ -1889,29 +1406,6 @@ impl Fragment { } } - /// Sends the size and position of this iframe fragment to the constellation. This is out of - /// line to guide inlining. - #[inline(never)] - fn finalize_position_and_size_of_iframe(&self, - iframe_fragment: &IframeFragmentInfo, - offset: Point2D<Au>, - layout_context: &LayoutContext) { - let border_padding = (self.border_padding).to_physical(self.style.writing_mode); - let content_size = self.content_box().size.to_physical(self.style.writing_mode); - let iframe_rect = Rect(Point2D(geometry::to_frac_px(offset.x + border_padding.left) as f32, - geometry::to_frac_px(offset.y + border_padding.top) as f32), - Size2D(geometry::to_frac_px(content_size.width) as f32, - geometry::to_frac_px(content_size.height) as f32)); - - debug!("finalizing position and size of iframe for {:?},{:?}", - iframe_fragment.pipeline_id, - iframe_fragment.subpage_id); - let ConstellationChan(ref chan) = layout_context.shared.constellation_chan; - chan.send(FrameRectMsg(iframe_fragment.pipeline_id, - iframe_fragment.subpage_id, - iframe_rect)); - } - /// Returns true if and only if this is the *primary fragment* for the fragment's style object /// (conceptually, though style sharing makes this not really true, of course). The primary /// fragment is the one that draws backgrounds, borders, etc., and takes borders, padding and @@ -1921,7 +1415,7 @@ impl Fragment { /// fragments. Inline-block fragments are not primary fragments because the corresponding block /// flow is the primary fragment, while table wrapper fragments are not primary fragments /// because the corresponding table flow is the primary fragment. - fn is_primary_fragment(&self) -> bool { + pub fn is_primary_fragment(&self) -> bool { match self.specific { InlineBlockFragment(_) | InlineAbsoluteHypotheticalFragment(_) | TableWrapperFragment => false, @@ -1951,27 +1445,6 @@ impl Fragment { } } - pub fn clip_rect_for_children(&self, current_clip_rect: Rect<Au>, flow_origin: Point2D<Au>) - -> Rect<Au> { - // Don't clip if we're text. - match self.specific { - ScannedTextFragment(_) => return current_clip_rect, - _ => {} - } - - // Only clip if `overflow` tells us to. - match self.style.get_box().overflow { - overflow::hidden | overflow::auto | overflow::scroll => {} - _ => return current_clip_rect, - } - - // 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_rect.intersection(&Rect(physical_rect.origin + flow_origin, - physical_rect.size)).unwrap_or(ZERO_RECT) - } } impl fmt::Show for Fragment { diff --git a/components/layout/inline.rs b/components/layout/inline.rs index bdfc9474fe7..154409d0a3f 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -6,6 +6,7 @@ use css::node_style::StyledNode; use context::LayoutContext; +use display_list_builder::FragmentDisplayListBuilding; use floats::{FloatLeft, Floats, PlacementInfo}; use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils}; use flow; @@ -24,6 +25,7 @@ use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; use servo_util::geometry::Au; use servo_util::logical_geometry::{LogicalRect, LogicalSize}; +use servo_util::opts; use servo_util::range::{IntRangeIndex, Range, RangeIndex}; use servo_util::arc_ptr_eq; use std::cmp::max; @@ -706,44 +708,6 @@ impl InlineFlow { } } - pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) { - let size = self.base.position.size.to_physical(self.base.writing_mode); - if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) { - debug!("inline block (abs pos {}, size {}) didn't intersect \ - dirty rect two", - self.base.abs_position, - size); - return - } - - // 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()); - - for fragment in self.fragments.fragments.iter_mut() { - let rel_offset = fragment.relative_position(&self.base - .absolute_position_info - .relative_containing_block_size); - let fragment_position = self.base - .abs_position - .add_size(&rel_offset.to_physical(self.base.writing_mode)); - fragment.build_display_list(&mut self.base.display_list, - layout_context, - fragment_position, - ContentLevel, - &self.base.clip_rect); - match fragment.specific { - InlineBlockFragment(ref mut block_flow) => { - let block_flow = block_flow.flow_ref.deref_mut(); - self.base.display_list.push_all_move( - mem::replace(&mut flow::mut_base(block_flow).display_list, - DisplayList::new())); - } - _ => {} - } - } - } - /// Returns the distance from the baseline for the logical block-start inline-start corner of /// this fragment, taking into account the value of the CSS `vertical-align` property. /// Negative values mean "toward the logical block-start" and positive values mean "toward the @@ -1201,6 +1165,48 @@ impl Flow for InlineFlow { fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {} fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {} + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + let size = self.base.position.size.to_physical(self.base.writing_mode); + if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) { + debug!("inline block (abs pos {}, size {}) didn't intersect \ + dirty rect two", + self.base.abs_position, + size); + return + } + + // 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()); + + for fragment in self.fragments.fragments.iter_mut() { + let rel_offset = fragment.relative_position(&self.base + .absolute_position_info + .relative_containing_block_size); + let fragment_position = self.base + .abs_position + .add_size(&rel_offset.to_physical(self.base.writing_mode)); + fragment.build_display_list(&mut self.base.display_list, + layout_context, + fragment_position, + ContentLevel, + &self.base.clip_rect); + match fragment.specific { + InlineBlockFragment(ref mut block_flow) => { + let block_flow = block_flow.flow_ref.deref_mut(); + self.base.display_list.push_all_move( + mem::replace(&mut flow::mut_base(block_flow).display_list, + DisplayList::new())); + } + _ => {} + } + } + + if opts::get().validate_display_list_geometry { + self.base.validate_display_list_geometry(); + } + } } impl fmt::Show for InlineFlow { diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 0e7eb9434d2..94b2bbc5b68 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -46,6 +46,7 @@ pub mod layout_debug; pub mod block; pub mod construct; pub mod context; +pub mod display_list_builder; pub mod floats; pub mod flow; pub mod flow_list; diff --git a/components/layout/table.rs b/components/layout/table.rs index 5d8bce0d357..f90f291bf22 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -126,11 +126,6 @@ impl TableFlow { fn assign_block_size_table_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse); } - - pub fn build_display_list_table(&mut self, layout_context: &LayoutContext) { - debug!("build_display_list_table: same process as block flow"); - self.block_flow.build_display_list_block(layout_context); - } } impl Flow for TableFlow { @@ -323,6 +318,10 @@ impl Flow for TableFlow { fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + self.block_flow.build_display_list(layout_context); + } } impl fmt::Show for TableFlow { diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index 7a46fddb833..96e50747dc9 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -28,11 +28,6 @@ impl TableCaptionFlow { block_flow: BlockFlow::from_node(constructor, node) } } - - pub fn build_display_list_table_caption(&mut self, layout_context: &LayoutContext) { - debug!("build_display_list_table_caption: same process as block flow"); - self.block_flow.build_display_list_block(layout_context) - } } impl Flow for TableCaptionFlow { @@ -73,6 +68,11 @@ impl Flow for TableCaptionFlow { fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + debug!("build_display_list_table_caption: same process as block flow"); + self.block_flow.build_display_list(layout_context) + } } impl fmt::Show for TableCaptionFlow { diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 3e4bdc2d01d..02c0c4445ef 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -51,11 +51,6 @@ impl TableCellFlow { fn assign_block_size_table_cell_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse) } - - pub fn build_display_list_table_cell(&mut self, layout_context: &LayoutContext) { - debug!("build_display_list_table: same process as block flow"); - self.block_flow.build_display_list_block(layout_context) - } } impl Flow for TableCellFlow { @@ -143,6 +138,10 @@ impl Flow for TableCellFlow { fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + self.block_flow.build_display_list(layout_context) + } } impl fmt::Show for TableCellFlow { diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index 5db1dd3ab2a..96cec13c5aa 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -85,6 +85,9 @@ impl Flow for TableColGroupFlow { fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {} fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {} + + // Table columns are invisible. + fn build_display_list(&mut self, _: &LayoutContext) {} } impl fmt::Show for TableColGroupFlow { diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index ea3395704d4..c4c26281e20 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -126,11 +126,6 @@ impl TableRowFlow { child_node.position.size.block = block_size; } } - - pub fn build_display_list_table_row(&mut self, layout_context: &LayoutContext) { - debug!("build_display_list_table_row: same process as block flow"); - self.block_flow.build_display_list_block(layout_context) - } } impl Flow for TableRowFlow { @@ -246,6 +241,10 @@ impl Flow for TableRowFlow { fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + self.block_flow.build_display_list(layout_context) + } } impl fmt::Show for TableRowFlow { diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index f0a6483515e..e21aedfc5ab 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -86,11 +86,6 @@ impl TableRowGroupFlow { self.block_flow.fragment.border_box = position; self.block_flow.base.position.size.block = block_size; } - - pub fn build_display_list_table_rowgroup(&mut self, layout_context: &LayoutContext) { - debug!("build_display_list_table_rowgroup: same process as block flow"); - self.block_flow.build_display_list_block(layout_context) - } } impl Flow for TableRowGroupFlow { @@ -205,6 +200,11 @@ impl Flow for TableRowGroupFlow { fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + debug!("build_display_list_table_rowgroup: same process as block flow"); + self.block_flow.build_display_list(layout_context) + } } impl fmt::Show for TableRowGroupFlow { diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index 060e78ecf5b..9acc65d672f 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -109,11 +109,6 @@ impl TableWrapperFlow { } } - pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) { - debug!("build_display_list_table_wrapper: same process as block flow"); - self.block_flow.build_display_list_block(layout_context); - } - /// Calculates table column sizes for automatic layout per INTRINSIC § 4.3. fn calculate_table_column_sizes_for_automatic_layout(&mut self) { // Find the padding and border of our first child, which is the table itself. @@ -338,6 +333,10 @@ impl Flow for TableWrapperFlow { fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } + + fn build_display_list(&mut self, layout_context: &LayoutContext) { + self.block_flow.build_display_list(layout_context) + } } impl fmt::Show for TableWrapperFlow { |