layout: Rewrite clipping to be per-display-item instead of having

a separate `ClipDisplayItem`.

We push down clipping areas during absolute position calculation. This
makes display items into a flat list, improving cache locality. It
dramatically simplifies the code all around.

Because we need to push down clip rects even for absolutely-positioned
children of non-absolutely-positioned flows, this patch alters the
parallel traversal to compute absolute positions for
absolutely-positioned children at the same time it computes absolute
positions for other children. This doesn't seem to break anything either
in theory (since the overall order remains correct) or in practice. It
simplifies the parallel traversal code quite a bit.

See the relevant Gecko bug:
https://bugzilla.mozilla.org/show_bug.cgi?id=615734
This commit is contained in:
Patrick Walton 2014-10-10 15:13:12 -07:00
parent f350879574
commit bffaad118e
12 changed files with 312 additions and 487 deletions

View file

@ -37,7 +37,7 @@ use gfx::display_list::{RootOfStackingContextLevel};
use gfx::render_task::RenderLayer;
use serialize::{Encoder, Encodable};
use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable};
use servo_util::geometry::{Au, MAX_AU};
use servo_util::geometry::{Au, MAX_AU, MAX_RECT};
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use std::cmp::{max, min};
use std::fmt;
@ -1078,18 +1078,19 @@ impl BlockFlow {
fn build_display_list_block_common(&mut self,
layout_context: &LayoutContext,
background_border_level: BackgroundAndBorderLevel) {
let rel_offset =
let relative_offset =
self.fragment.relative_position(&self.base
.absolute_position_info
.relative_containing_block_size);
.absolute_position_info
.relative_containing_block_size);
// Add the box that starts the block context.
let mut display_list = DisplayList::new();
let mut accumulator = self.fragment.build_display_list(
&mut display_list,
layout_context,
self.base.abs_position.add_size(&rel_offset.to_physical(self.base.writing_mode)),
background_border_level);
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() {
@ -1098,19 +1099,21 @@ impl BlockFlow {
continue
}
accumulator.push_child(&mut display_list, kid);
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.
accumulator.push_child(&mut display_list, abs_descendant_link);
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()));
}
accumulator.finish(&mut display_list);
self.base.display_list = display_list;
self.base.layers = child_layers
}
@ -1696,6 +1699,10 @@ impl Flow for BlockFlow {
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
if self.is_root() {
self.base.clip_rect = MAX_RECT;
}
if self.is_absolutely_positioned() {
let position_start = self.base.position.start.to_physical(
self.base.writing_mode, container_size);
@ -1737,8 +1744,11 @@ impl Flow for BlockFlow {
absolute_position_info.layers_needed_for_positioned_flows =
self.base.flags.layers_needed_for_descendants();
// Process children.
// Compute the clipping rectangle for children.
let this_position = self.base.abs_position;
let clip_rect = self.fragment.clip_rect_for_children(self.base.clip_rect, this_position);
// Process children.
let writing_mode = self.base.writing_mode;
for kid in self.base.child_iter() {
if !kid.is_absolutely_positioned() {
@ -1749,6 +1759,8 @@ impl Flow for BlockFlow {
container_size);
kid_base.absolute_position_info = absolute_position_info
}
flow::mut_base(kid).clip_rect = clip_rect
}
// Process absolute descendant links.

View file

@ -1204,7 +1204,6 @@ impl FlowConstructionUtils for FlowRef {
base.children.push_back(new_child);
let _ = base.parallel.children_count.fetch_add(1, Relaxed);
let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed);
}
/// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to

View file

@ -46,7 +46,7 @@ use table_wrapper::TableWrapperFlow;
use wrapper::ThreadSafeLayoutNode;
use collections::dlist::DList;
use geom::Point2D;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayList;
use gfx::render_task::RenderLayer;
use serialize::{Encoder, Encodable};
@ -59,7 +59,7 @@ use std::num::Zero;
use std::fmt;
use std::iter::Zip;
use std::raw;
use std::sync::atomics::{AtomicUint, Relaxed, SeqCst};
use std::sync::atomics::{AtomicUint, SeqCst};
use std::slice::MutItems;
use style::computed_values::{clear, float, position, text_align};
@ -168,14 +168,14 @@ pub trait Flow: fmt::Show + ToString + Sync {
fail!("called col_inline_sizes() on an other flow than table-row/table-rowgroup/table")
}
/// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes.
/// Fails otherwise.
/// If this is a table row flow or table rowgroup flow or table flow, returns column min
/// inline-sizes. Fails otherwise.
fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_min_inline_sizes() on an other flow than table-row/table-rowgroup/table")
}
/// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes.
/// Fails otherwise.
/// If this is a table row flow or table rowgroup flow or table flow, returns column min
/// inline-sizes. Fails otherwise.
fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
fail!("called col_pref_inline_sizes() on an other flow than table-row/table-rowgroup/table")
}
@ -230,8 +230,8 @@ pub trait Flow: fmt::Show + ToString + Sync {
}
fn compute_collapsible_block_start_margin(&mut self,
_layout_context: &mut LayoutContext,
_margin_collapse_info: &mut MarginCollapseInfo) {
_layout_context: &mut LayoutContext,
_margin_collapse_info: &mut MarginCollapseInfo) {
// The default implementation is a no-op.
}
@ -679,8 +679,10 @@ pub type DescendantOffsetIter<'a> = Zip<DescendantIter<'a>, MutItems<'a, Au>>;
pub struct AbsolutePositionInfo {
/// The size of the containing block for relatively-positioned descendants.
pub relative_containing_block_size: LogicalSize<Au>,
/// The position of the absolute containing block.
pub absolute_containing_block_position: Point2D<Au>,
/// Whether the absolute containing block forces positioned descendants to be layerized.
///
/// FIXME(pcwalton): Move into `FlowFlags`.
@ -770,6 +772,12 @@ pub struct BaseFlow {
/// FIXME(pcwalton): Merge with `absolute_static_i_offset` and `fixed_static_i_offset` above?
pub absolute_position_info: AbsolutePositionInfo,
/// The clipping rectangle for this flow and its descendants, in layer coordinates.
///
/// TODO(pcwalton): When we have `border-radius` this will need to at least support rounded
/// rectangles.
pub clip_rect: Rect<Au>,
/// The unflattened display items for this flow.
pub display_list: DisplayList,
@ -785,11 +793,10 @@ pub struct BaseFlow {
impl fmt::Show for BaseFlow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"@ {}, CC {}, ADC {}, CADC {}",
"@ {}, CC {}, ADC {}",
self.position,
self.parallel.children_count.load(SeqCst),
self.abs_descendants.len(),
self.parallel.children_and_absolute_descendant_count.load(SeqCst))
self.abs_descendants.len())
}
}
@ -851,6 +858,7 @@ impl BaseFlow {
display_list: DisplayList::new(),
layers: DList::new(),
absolute_position_info: AbsolutePositionInfo::new(writing_mode),
clip_rect: Rect(Zero::zero(), Size2D(Au(0), Au(0))),
flags: FlowFlags::new(),
writing_mode: writing_mode,
@ -1210,10 +1218,6 @@ impl MutableOwnedFlowUtils for FlowRef {
let block = self.get_mut().as_block();
block.base.abs_descendants = abs_descendants;
block.base
.parallel
.children_and_absolute_descendant_count
.fetch_add(block.base.abs_descendants.len() as int, Relaxed);
for descendant_link in block.base.abs_descendants.iter() {
let base = mut_base(descendant_link);

View file

@ -11,7 +11,6 @@ use construct::FlowConstructor;
use context::LayoutContext;
use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
use flow::Flow;
use flow;
use flow_ref::FlowRef;
use inline::{InlineFragmentContext, InlineMetrics};
use layout_debug;
@ -25,13 +24,12 @@ 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, ClipDisplayItem, ClipDisplayItemClass};
use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList, ImageDisplayItem};
use gfx::display_list::{ImageDisplayItemClass, LineDisplayItem};
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::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel};
use gfx::display_list::{TextDisplayItem, TextDisplayItemClass};
use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
use gfx::display_list::{SidewaysLeft, SidewaysRight, SolidColorDisplayItem};
use gfx::display_list::{SolidColorDisplayItemClass, StackingLevel, TextDisplayItem};
use gfx::display_list::{TextDisplayItemClass, Upright};
use gfx::font::FontStyle;
use gfx::text::glyph::CharIndex;
use gfx::text::text_run::TextRun;
@ -40,7 +38,7 @@ use serialize::{Encodable, Encoder};
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
use servo_net::image::holder::ImageHolder;
use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
use servo_util::geometry::{Au, ZERO_RECT};
use servo_util::geometry;
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode};
use servo_util::range::*;
@ -49,7 +47,6 @@ use servo_util::str::is_whitespace;
use std::cmp::{max, min};
use std::fmt;
use std::from_str::FromStr;
use std::mem;
use std::num::Zero;
use style::{ComputedValues, TElement, TNode, cascade_anonymous, RGBA};
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto};
@ -60,24 +57,26 @@ use style::computed_values::{text_decoration, vertical_align, visibility, white_
use sync::{Arc, Mutex};
use url::Url;
/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position themselves. In
/// general, fragments do not have a simple correspondence with CSS fragments in the specification:
/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position
/// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the
/// specification:
///
/// * Several fragments may correspond to the same CSS box or DOM node. For example, a CSS text box
/// broken across two lines is represented by two fragments.
///
/// * Some CSS fragments are not created at all, such as some anonymous block fragments induced by inline
/// fragments with block-level sibling fragments. In that case, Servo uses an `InlineFlow` with
/// `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is
/// positioned as if it were a block fragment, but its children are positioned according to inline
/// flow.
/// * Some CSS fragments are not created at all, such as some anonymous block fragments induced by
/// inline fragments with block-level sibling fragments. In that case, Servo uses an `InlineFlow`
/// with `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is
/// positioned as if it were a block fragment, but its children are positioned according to
/// inline flow.
///
/// A `GenericFragment` 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. Different
/// types of fragments may also contain custom data; for example, text fragments contain text.
/// 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.
/// Different types of fragments may also contain custom data; for example, text fragments contain
/// text.
///
/// FIXME(#2260, pcwalton): This can be slimmed down some.
#[deriving(Clone)]
@ -787,7 +786,8 @@ impl Fragment {
list: &mut DisplayList,
layout_context: &LayoutContext,
level: StackingLevel,
absolute_bounds: &Rect<Au>) {
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
@ -795,7 +795,7 @@ impl 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),
base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect),
color: background_color.to_gfx_color(),
};
@ -828,12 +828,10 @@ impl Fragment {
let image_height = Au::from_px(image.height as int);
let mut bounds = *absolute_bounds;
// Add clip item.
// Clip.
//
// TODO: Check the bounds to see if a clip item is actually required.
let mut clip_display_item = box ClipDisplayItem {
base: BaseDisplayItem::new(bounds, self.node, level),
children: DisplayList::new(),
};
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 {
@ -884,14 +882,12 @@ impl Fragment {
// Create the image display item.
let image_display_item = ImageDisplayItemClass(box ImageDisplayItem {
base: BaseDisplayItem::new(bounds, self.node, level),
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)),
});
clip_display_item.children.push(image_display_item);
list.push(ClipDisplayItemClass(clip_display_item))
list.push(image_display_item)
}
/// Adds the display items necessary to paint the borders of this fragment to a display list if
@ -900,7 +896,8 @@ impl Fragment {
style: &ComputedValues,
list: &mut DisplayList,
abs_bounds: &Rect<Au>,
level: StackingLevel) {
level: StackingLevel,
clip_rect: &Rect<Au>) {
let border = style.logical_border_width();
if border.is_zero() {
return
@ -913,7 +910,7 @@ impl Fragment {
// Append the border to the display list.
let border_display_item = box BorderDisplayItem {
base: BaseDisplayItem::new(*abs_bounds, self.node, level),
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(),
@ -929,9 +926,10 @@ impl Fragment {
}
fn build_debug_borders_around_text_fragments(&self,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
text_fragment: &ScannedTextFragmentInfo) {
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.
@ -942,7 +940,10 @@ impl Fragment {
// 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),
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)
@ -958,7 +959,7 @@ impl Fragment {
baseline.origin = baseline.origin + flow_origin;
let line_display_item = box LineDisplayItem {
base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel),
base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect),
color: rgb(0, 200, 0),
style: border_style::dashed,
};
@ -966,8 +967,9 @@ impl Fragment {
}
fn build_debug_borders_around_fragment(&self,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>) {
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.
@ -978,7 +980,10 @@ impl Fragment {
// 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),
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)
@ -994,12 +999,13 @@ impl Fragment {
/// * `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)
-> ChildDisplayListAccumulator {
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>| {
@ -1016,22 +1022,13 @@ impl Fragment {
layout_context.shared.dirty,
flow_origin);
let may_need_clip = match self.specific {
ScannedTextFragment(_) => false,
_ => true,
};
let mut accumulator = ChildDisplayListAccumulator::new(self.style(),
absolute_fragment_bounds,
self.node,
ContentStackingLevel,
may_need_clip);
if self.style().get_inheritedbox().visibility != visibility::visible {
return accumulator
return
}
if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) {
debug!("Fragment::build_display_list: Did not intersect...");
return accumulator
return
}
debug!("Fragment::build_display_list: intersected. Adding display item...");
@ -1043,7 +1040,8 @@ impl Fragment {
// 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);
level,
*clip_rect);
display_list.push(PseudoDisplayItemClass(base_display_item));
// Add the background to the list, if applicable.
@ -1055,7 +1053,8 @@ impl Fragment {
display_list,
layout_context,
level,
&absolute_fragment_bounds);
&absolute_fragment_bounds,
clip_rect);
}
}
None => {}
@ -1068,7 +1067,8 @@ impl Fragment {
display_list,
layout_context,
level,
&absolute_fragment_bounds);
&absolute_fragment_bounds,
clip_rect);
}
}
@ -1082,7 +1082,8 @@ impl Fragment {
&**style,
display_list,
&absolute_fragment_bounds,
level);
level,
clip_rect);
}
}
None => {}
@ -1090,10 +1091,12 @@ impl Fragment {
match self.specific {
ScannedTextFragment(_) => {},
_ => {
self.build_display_list_for_borders_if_applicable(&*self.style,
display_list,
&absolute_fragment_bounds,
level);
self.build_display_list_for_borders_if_applicable(
&*self.style,
display_list,
&absolute_fragment_bounds,
level,
clip_rect);
}
}
}
@ -1125,15 +1128,17 @@ impl Fragment {
};
let text_display_item = box TextDisplayItem {
base: BaseDisplayItem::new(
absolute_content_box, self.node, ContentStackingLevel),
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,
};
accumulator.push(display_list, TextDisplayItemClass(text_display_item));
display_list.push(TextDisplayItemClass(text_display_item));
// Create display items for text decoration
{
@ -1141,14 +1146,17 @@ impl Fragment {
match maybe_color {
None => {},
Some(color) => {
accumulator.push(display_list, SolidColorDisplayItemClass(
box SolidColorDisplayItem {
base: BaseDisplayItem::new(
rect_to_absolute(self.style.writing_mode, rect()),
self.node, ContentStackingLevel),
color: color.to_gfx_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(),
}));
}
}
};
@ -1181,15 +1189,19 @@ impl Fragment {
// FIXME(#2263, 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_text_fragments(display_list,
flow_origin,
text_fragment))
flow_origin,
text_fragment,
clip_rect))
}
GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment |
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) |
InlineAbsoluteHypotheticalFragment(_) => {
// 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))
debug!("{:?}",
self.build_debug_borders_around_fragment(display_list,
flow_origin,
clip_rect))
}
ImageFragment(ref mut image_fragment) => {
let image_ref = &mut image_fragment.image;
@ -1201,12 +1213,13 @@ impl Fragment {
let image_display_item = box ImageDisplayItem {
base: BaseDisplayItem::new(absolute_content_box,
self.node,
ContentStackingLevel),
ContentStackingLevel,
*clip_rect),
image: image.clone(),
stretch_size: absolute_content_box.size,
};
accumulator.push(display_list, ImageDisplayItemClass(image_display_item))
display_list.push(ImageDisplayItemClass(image_display_item))
}
None => {
// No image data at all? Do nothing.
@ -1220,7 +1233,8 @@ impl Fragment {
// 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))
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.
//
@ -1240,8 +1254,6 @@ impl Fragment {
}
_ => {}
}
accumulator
}
/// Returns the intrinsic inline-sizes of this fragment.
@ -1800,6 +1812,28 @@ 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 {
@ -1828,61 +1862,3 @@ impl fmt::Show for Fragment {
}
}
/// An object that accumulates display lists of child flows, applying a clipping rect if necessary.
pub struct ChildDisplayListAccumulator {
clip_display_item: Option<Box<ClipDisplayItem>>,
}
impl ChildDisplayListAccumulator {
/// Creates a `ChildDisplayListAccumulator` from the `overflow` property in the given style.
fn new(style: &ComputedValues,
bounds: Rect<Au>,
node: OpaqueNode,
level: StackingLevel,
may_need_clip: bool)
-> ChildDisplayListAccumulator {
ChildDisplayListAccumulator {
clip_display_item: match (may_need_clip, style.get_box().overflow) {
(true, overflow::hidden) | (true, overflow::auto) | (true, overflow::scroll) => {
Some(box ClipDisplayItem {
base: BaseDisplayItem::new(bounds, node, level),
children: DisplayList::new(),
})
},
(false, _) | (_, overflow::visible) => None,
}
}
}
/// Pushes the given display item onto this display list.
pub fn push(&mut self, parent_display_list: &mut DisplayList, item: DisplayItem) {
match self.clip_display_item {
None => parent_display_list.push(item),
Some(ref mut clip_display_item) => clip_display_item.children.push(item),
}
}
/// Pushes the display items from the given child onto this display list.
pub fn push_child(&mut self, parent_display_list: &mut DisplayList, child: &mut Flow) {
let kid_display_list = mem::replace(&mut flow::mut_base(child).display_list,
DisplayList::new());
match self.clip_display_item {
None => parent_display_list.push_all_move(kid_display_list),
Some(ref mut clip_display_item) => {
clip_display_item.children.push_all_move(kid_display_list)
}
}
}
/// Consumes this accumulator and pushes the clipping item, if any, onto the given display
/// list.
pub fn finish(self, display_list: &mut DisplayList) {
let ChildDisplayListAccumulator {
clip_display_item
} = self;
match clip_display_item {
None => {}
Some(clip_display_item) => display_list.push(ClipDisplayItemClass(clip_display_item)),
}
}
}

View file

@ -9,8 +9,8 @@ use context::LayoutContext;
use floats::{FloatLeft, Floats, PlacementInfo};
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
use flow;
use fragment::{Fragment, InlineBlockFragment, ScannedTextFragment, ScannedTextFragmentInfo};
use fragment::{SplitInfo};
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
use layout_debug;
use model::IntrinsicISizes;
use text;
@ -18,7 +18,7 @@ use wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf};
use geom::Rect;
use gfx::display_list::ContentLevel;
use gfx::display_list::{ContentLevel, DisplayList};
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
use geom::Size2D;
@ -704,19 +704,20 @@ impl InlineFlow {
let fragment_position = self.base
.abs_position
.add_size(&rel_offset.to_physical(self.base.writing_mode));
let mut accumulator = fragment.build_display_list(&mut self.base.display_list,
layout_context,
fragment_position,
ContentLevel);
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.get_mut();
accumulator.push_child(&mut self.base.display_list, block_flow);
self.base.display_list.push_all_move(
mem::replace(&mut flow::mut_base(block_flow).display_list,
DisplayList::new()));
}
_ => {}
}
accumulator.finish(&mut self.base.display_list);
}
}
@ -1124,16 +1125,40 @@ impl Flow for InlineFlow {
fn compute_absolute_position(&mut self) {
for fragment in self.fragments.fragments.iter_mut() {
match fragment.specific {
let absolute_position = match fragment.specific {
InlineBlockFragment(ref mut info) => {
let block_flow = info.flow_ref.get_mut().as_block();
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
block_flow.base.abs_position =
self.base.abs_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
container_size);
block_flow.base.abs_position
}
InlineAbsoluteHypotheticalFragment(ref mut info) => {
let block_flow = info.flow_ref.get_mut().as_block();
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
block_flow.base.abs_position =
self.base.abs_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
container_size);
block_flow.base.abs_position
}
_ => continue,
};
let clip_rect = fragment.clip_rect_for_children(self.base.clip_rect,
absolute_position);
match fragment.specific {
InlineBlockFragment(ref mut info) => {
flow::mut_base(info.flow_ref.get_mut()).clip_rect = clip_rect
}
InlineAbsoluteHypotheticalFragment(ref mut info) => {
flow::mut_base(info.flow_ref.get_mut()).clip_rect = clip_rect
}
_ => {}
}

View file

@ -26,8 +26,8 @@ use encoding::all::UTF_8;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem};
use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode};
use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayItemIterator, DisplayList};
use gfx::display_list::{OpaqueNode};
use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
use gfx::{render_task, color};
use layout_traits;
@ -852,7 +852,6 @@ impl LayoutRPC for LayoutRPCImpl {
mut iter: DisplayItemIterator,
node: OpaqueNode) {
for item in iter {
union_boxes_for_node(accumulator, item.children(), node);
if item.base().node == node {
match *accumulator {
None => *accumulator = Some(item.base().bounds),
@ -884,7 +883,6 @@ impl LayoutRPC for LayoutRPCImpl {
mut iter: DisplayItemIterator,
node: OpaqueNode) {
for item in iter {
add_boxes_for_node(accumulator, item.children(), node);
if item.base().node == node {
accumulator.push(item.base().bounds)
}
@ -907,47 +905,25 @@ impl LayoutRPC for LayoutRPCImpl {
/// Requests the node containing the point of interest
fn hit_test(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()> {
fn hit_test<'a,I:Iterator<&'a DisplayItem>>(x: Au, y: Au, mut iterator: I)
-> Option<HitTestResponse> {
fn hit_test<'a,I>(point: Point2D<Au>, mut iterator: I)
-> Option<HitTestResponse>
where I: Iterator<&'a DisplayItem> {
for item in iterator {
match *item {
ClipDisplayItemClass(ref cc) => {
if geometry::rect_contains_point(cc.base.bounds, Point2D(x, y)) {
let ret = hit_test(x, y, cc.children.list.iter().rev());
if !ret.is_none() {
return ret
}
}
continue
}
_ => {}
}
let bounds = item.bounds();
// TODO(tikue): This check should really be performed by a method of
// DisplayItem.
if x < bounds.origin.x + bounds.size.width &&
bounds.origin.x <= x &&
y < bounds.origin.y + bounds.size.height &&
bounds.origin.y <= y {
return Some(HitTestResponse(item.base()
.node
.to_untrusted_node_address()))
// TODO(tikue): This check should really be performed by a method of `DisplayItem`.
if geometry::rect_contains_point(item.base().clip_rect, point) &&
geometry::rect_contains_point(item.bounds(), point) {
return Some(HitTestResponse(item.base().node.to_untrusted_node_address()))
}
}
let ret: Option<HitTestResponse> = None;
ret
None
}
let (x, y) = (Au::from_frac_px(point.x as f64),
Au::from_frac_px(point.y as f64));
let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
let resp = {
let &LayoutRPCImpl(ref rw_data) = self;
let rw_data = rw_data.lock();
match rw_data.display_list {
None => fail!("no display list!"),
Some(ref display_list) => hit_test(x, y, display_list.list.iter().rev()),
Some(ref display_list) => hit_test(point, display_list.list.iter().rev()),
}
};
@ -957,49 +933,29 @@ impl LayoutRPC for LayoutRPCImpl {
Err(())
}
fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()> {
fn mouse_over_test<'a,
I:Iterator<&'a DisplayItem>>(
x: Au,
y: Au,
mut iterator: I,
result: &mut Vec<UntrustedNodeAddress>) {
fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>)
-> Result<MouseOverResponse, ()> {
fn mouse_over_test<'a,I>(point: Point2D<Au>,
mut iterator: I,
result: &mut Vec<UntrustedNodeAddress>)
where I: Iterator<&'a DisplayItem> {
for item in iterator {
match *item {
ClipDisplayItemClass(ref cc) => {
mouse_over_test(x, y, cc.children.list.iter().rev(), result);
}
_ => {
let bounds = item.bounds();
// TODO(tikue): This check should really be performed by a method
// of DisplayItem.
if x < bounds.origin.x + bounds.size.width &&
bounds.origin.x <= x &&
y < bounds.origin.y + bounds.size.height &&
bounds.origin.y <= y {
result.push(item.base()
.node
.to_untrusted_node_address());
}
}
// TODO(tikue): This check should really be performed by a method of `DisplayItem`.
if geometry::rect_contains_point(item.bounds(), point) {
result.push(item.base().node.to_untrusted_node_address())
}
}
}
let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!();
let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
{
let &LayoutRPCImpl(ref rw_data) = self;
let rw_data = rw_data.lock();
match rw_data.display_list {
None => fail!("no display list!"),
Some(ref display_list) => {
mouse_over_test(x,
y,
display_list.list.iter().rev(),
&mut mouse_over_list);
mouse_over_test(point, display_list.list.iter().rev(), &mut mouse_over_list);
}
};
}

View file

@ -188,8 +188,6 @@ trait ParallelPostorderDomTraversal : PostorderDomTraversal {
pub struct FlowParallelInfo {
/// The number of children that still need work done.
pub children_count: AtomicInt,
/// The number of children and absolute descendants that still need work done.
pub children_and_absolute_descendant_count: AtomicInt,
/// The address of the parent flow.
pub parent: UnsafeFlow,
}
@ -198,7 +196,6 @@ impl FlowParallelInfo {
pub fn new() -> FlowParallelInfo {
FlowParallelInfo {
children_count: AtomicInt::new(0),
children_and_absolute_descendant_count: AtomicInt::new(0),
parent: null_unsafe_flow(),
}
}
@ -382,39 +379,12 @@ fn compute_absolute_position(unsafe_flow: UnsafeFlow,
// Compute the absolute position for the flow.
flow.get_mut().compute_absolute_position();
// If we are the containing block, count the number of absolutely-positioned children, so
// that we don't double-count them in the `children_and_absolute_descendant_count`
// reference count.
let mut absolutely_positioned_child_count = 0u;
// Enqueue all children.
for kid in flow::child_iter(flow.get_mut()) {
if kid.is_absolutely_positioned() {
absolutely_positioned_child_count += 1;
}
}
drop(flow::mut_base(flow.get_mut()).parallel
.children_and_absolute_descendant_count
.fetch_sub(absolutely_positioned_child_count as int,
SeqCst));
// Enqueue all non-absolutely-positioned children.
for kid in flow::child_iter(flow.get_mut()) {
if !kid.is_absolutely_positioned() {
had_descendants = true;
proxy.push(WorkUnit {
fun: compute_absolute_position,
data: borrowed_flow_to_unsafe_flow(kid),
});
}
}
// Possibly enqueue absolute descendants.
for absolute_descendant_link in flow::mut_base(flow.get_mut()).abs_descendants.iter() {
had_descendants = true;
let descendant = absolute_descendant_link;
proxy.push(WorkUnit {
fun: compute_absolute_position,
data: borrowed_flow_to_unsafe_flow(descendant),
data: borrowed_flow_to_unsafe_flow(kid),
});
}
@ -441,26 +411,12 @@ fn build_display_list(mut unsafe_flow: UnsafeFlow,
{
let base = flow::mut_base(flow.get_mut());
// Reset the count of children and absolute descendants for the next layout
// traversal.
let children_and_absolute_descendant_count = base.children.len() +
base.abs_descendants.len();
base.parallel
.children_and_absolute_descendant_count
.store(children_and_absolute_descendant_count as int, Relaxed);
// Reset the count of children for the next layout traversal.
base.parallel.children_count.store(base.children.len() as int, Relaxed);
}
// Possibly enqueue the parent.
let unsafe_parent = if flow.get().is_absolutely_positioned() {
match *flow::mut_base(flow.get_mut()).absolute_cb.get() {
None => fail!("no absolute containing block for absolutely positioned?!"),
Some(ref mut absolute_cb) => {
mut_borrowed_flow_to_unsafe_flow(absolute_cb.get_mut())
}
}
} else {
flow::mut_base(flow.get_mut()).parallel.parent
};
let unsafe_parent = flow::mut_base(flow.get_mut()).parallel.parent;
if unsafe_parent == null_unsafe_flow() {
// We're done!
break
@ -472,7 +428,7 @@ fn build_display_list(mut unsafe_flow: UnsafeFlow,
let parent: &mut FlowRef = mem::transmute(&unsafe_parent);
let parent_base = flow::mut_base(parent.get_mut());
if parent_base.parallel
.children_and_absolute_descendant_count
.children_count
.fetch_sub(1, SeqCst) == 1 {
// We were the last child of our parent. Build display lists for our parent.
unsafe_flow = unsafe_parent

View file

@ -323,13 +323,7 @@ impl<'a> BuildDisplayList<'a> {
flow.compute_absolute_position();
for kid in flow::mut_base(flow).child_iter() {
if !kid.is_absolutely_positioned() {
self.process(kid)
}
}
for absolute_descendant_link in flow::mut_base(flow).abs_descendants.iter() {
self.process(absolute_descendant_link)
self.process(kid)
}
flow.build_display_list(self.layout_context)