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

@ -196,93 +196,35 @@ impl StackingContext {
};
for item in list.into_iter() {
match item {
ClipDisplayItemClass(box ClipDisplayItem {
base: base,
children: sublist
}) => {
let sub_stacking_context = StackingContext::new(sublist);
stacking_context.merge_with_clip(sub_stacking_context, &base.bounds, base.node)
match item.base().level {
BackgroundAndBordersStackingLevel => {
stacking_context.background_and_borders.push(item)
}
item => {
match item.base().level {
BackgroundAndBordersStackingLevel => {
stacking_context.background_and_borders.push(item)
}
BlockBackgroundsAndBordersStackingLevel => {
stacking_context.block_backgrounds_and_borders.push(item)
}
FloatStackingLevel => stacking_context.floats.push(item),
ContentStackingLevel => stacking_context.content.push(item),
PositionedDescendantStackingLevel(z_index) => {
match stacking_context.positioned_descendants
.iter_mut()
.find(|& &(z, _)| z_index == z) {
Some(&(_, ref mut my_list)) => {
my_list.push(item);
continue
}
None => {}
}
let mut new_list = DisplayList::new();
new_list.list.push(item);
stacking_context.positioned_descendants.push((z_index, new_list))
BlockBackgroundsAndBordersStackingLevel => {
stacking_context.block_backgrounds_and_borders.push(item)
}
FloatStackingLevel => stacking_context.floats.push(item),
ContentStackingLevel => stacking_context.content.push(item),
PositionedDescendantStackingLevel(z_index) => {
match stacking_context.positioned_descendants
.iter_mut()
.find(|& &(z, _)| z_index == z) {
Some(&(_, ref mut my_list)) => {
my_list.push(item);
continue
}
None => {}
}
let mut new_list = DisplayList::new();
new_list.list.push(item);
stacking_context.positioned_descendants.push((z_index, new_list))
}
}
}
stacking_context
}
/// Merges another stacking context into this one, with the given clipping rectangle and DOM
/// node that supplies it.
fn merge_with_clip(&mut self,
other: StackingContext,
clip_rect: &Rect<Au>,
clipping_dom_node: OpaqueNode) {
let StackingContext {
background_and_borders,
block_backgrounds_and_borders,
floats,
content,
positioned_descendants: positioned_descendants
} = other;
let push = |destination: &mut DisplayList, source: DisplayList, level| {
if !source.is_empty() {
let base = BaseDisplayItem::new(*clip_rect, clipping_dom_node, level);
destination.push(ClipDisplayItemClass(box ClipDisplayItem::new(base, source)))
}
};
push(&mut self.background_and_borders,
background_and_borders,
BackgroundAndBordersStackingLevel);
push(&mut self.block_backgrounds_and_borders,
block_backgrounds_and_borders,
BlockBackgroundsAndBordersStackingLevel);
push(&mut self.floats, floats, FloatStackingLevel);
push(&mut self.content, content, ContentStackingLevel);
for (z_index, list) in positioned_descendants.into_iter() {
match self.positioned_descendants
.iter_mut()
.find(|& &(existing_z_index, _)| z_index == existing_z_index) {
Some(&(_, ref mut existing_list)) => {
push(existing_list, list, PositionedDescendantStackingLevel(z_index));
continue
}
None => {}
}
let mut new_list = DisplayList::new();
push(&mut new_list, list, PositionedDescendantStackingLevel(z_index));
self.positioned_descendants.push((z_index, new_list));
}
}
}
/// Which level to place backgrounds and borders in.
@ -342,11 +284,13 @@ impl DisplayList {
/// Draws the display list into the given render context. The display list must be flattened
/// first for correct painting.
pub fn draw_into_context(&self, render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>) {
pub fn draw_into_context(&self,
render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>,
current_clip_rect: &Rect<Au>) {
debug!("Beginning display list.");
for item in self.list.iter() {
item.draw_into_context(render_context, current_transform)
item.draw_into_context(render_context, current_transform, current_clip_rect)
}
debug!("Ending display list.");
}
@ -356,12 +300,6 @@ impl DisplayList {
ParentDisplayItemIterator(self.list.iter())
}
/// Returns true if this list is empty and false otherwise.
#[inline]
fn is_empty(&self) -> bool {
self.list.is_empty()
}
/// Flattens a display list into a display list with a single stacking level according to the
/// steps in CSS 2.1 § E.2.
///
@ -421,10 +359,6 @@ impl DisplayList {
fn set_stacking_level(&mut self, new_level: StackingLevel) {
for item in self.list.iter_mut() {
item.mut_base().level = new_level;
match item.mut_sublist() {
None => {}
Some(sublist) => sublist.set_stacking_level(new_level),
}
}
}
}
@ -437,7 +371,6 @@ pub enum DisplayItem {
ImageDisplayItemClass(Box<ImageDisplayItem>),
BorderDisplayItemClass(Box<BorderDisplayItem>),
LineDisplayItemClass(Box<LineDisplayItem>),
ClipDisplayItemClass(Box<ClipDisplayItem>),
/// A pseudo-display item that exists only so that queries like `ContentBoxQuery` and
/// `ContentBoxesQuery` can be answered.
@ -450,9 +383,7 @@ pub enum DisplayItem {
/// Information common to all display items.
#[deriving(Clone)]
pub struct BaseDisplayItem {
/// The boundaries of the display item.
///
/// TODO: Which coordinate system should this use?
/// The boundaries of the display item, in layer coordinates.
pub bounds: Rect<Au>,
/// The originating DOM node.
@ -460,14 +391,22 @@ pub struct BaseDisplayItem {
/// The stacking level in which this display item lives.
pub level: StackingLevel,
/// The rectangle to clip to.
///
/// TODO(pcwalton): Eventually, to handle `border-radius`, this will (at least) need to grow
/// the ability to describe rounded rectangles.
pub clip_rect: Rect<Au>,
}
impl BaseDisplayItem {
pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel) -> BaseDisplayItem {
pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel, clip_rect: Rect<Au>)
-> BaseDisplayItem {
BaseDisplayItem {
bounds: bounds,
node: node,
level: level,
clip_rect: clip_rect,
}
}
}
@ -544,25 +483,6 @@ pub struct LineDisplayItem {
pub style: border_style::T
}
/// Clips a list of child display items to this display item's boundaries.
#[deriving(Clone)]
pub struct ClipDisplayItem {
/// The base information.
pub base: BaseDisplayItem,
/// The child nodes.
pub children: DisplayList,
}
impl ClipDisplayItem {
pub fn new(base: BaseDisplayItem, children: DisplayList) -> ClipDisplayItem {
ClipDisplayItem {
base: base,
children: children,
}
}
}
pub enum DisplayItemIterator<'a> {
EmptyDisplayItemIterator,
ParentDisplayItemIterator(dlist::Items<'a,DisplayItem>),
@ -580,24 +500,24 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
impl DisplayItem {
/// Renders this display item into the given render context.
fn draw_into_context(&self, render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>) {
fn draw_into_context(&self,
render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>,
current_clip_rect: &Rect<Au>) {
// This should have been flattened to the content stacking level first.
assert!(self.base().level == ContentStackingLevel);
let clip_rect = &self.base().clip_rect;
let need_to_clip = current_clip_rect != clip_rect;
if need_to_clip {
render_context.draw_push_clip(clip_rect);
}
match *self {
SolidColorDisplayItemClass(ref solid_color) => {
render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
}
ClipDisplayItemClass(ref clip) => {
render_context.draw_push_clip(&clip.base.bounds);
for item in clip.children.iter() {
(*item).draw_into_context(render_context, current_transform);
}
render_context.draw_pop_clip();
}
TextDisplayItemClass(ref text) => {
debug!("Drawing text at {}.", text.base.bounds);
@ -688,6 +608,10 @@ impl DisplayItem {
PseudoDisplayItemClass(_) => {}
}
if need_to_clip {
render_context.draw_pop_clip();
}
}
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem {
@ -697,7 +621,6 @@ impl DisplayItem {
ImageDisplayItemClass(ref image_item) => &image_item.base,
BorderDisplayItemClass(ref border) => &border.base,
LineDisplayItemClass(ref line) => &line.base,
ClipDisplayItemClass(ref clip) => &clip.base,
PseudoDisplayItemClass(ref base) => &**base,
}
}
@ -709,7 +632,6 @@ impl DisplayItem {
ImageDisplayItemClass(ref mut image_item) => &mut image_item.base,
BorderDisplayItemClass(ref mut border) => &mut border.base,
LineDisplayItemClass(ref mut line) => &mut line.base,
ClipDisplayItemClass(ref mut clip) => &mut clip.base,
PseudoDisplayItemClass(ref mut base) => &mut **base,
}
}
@ -718,40 +640,12 @@ impl DisplayItem {
self.base().bounds
}
pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> {
match *self {
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.children.list.iter()),
SolidColorDisplayItemClass(..) |
TextDisplayItemClass(..) |
ImageDisplayItemClass(..) |
BorderDisplayItemClass(..) |
LineDisplayItemClass(..) |
PseudoDisplayItemClass(..) => EmptyDisplayItemIterator,
}
}
/// Returns a mutable reference to the sublist contained within this display list item, if any.
fn mut_sublist<'a>(&'a mut self) -> Option<&'a mut DisplayList> {
match *self {
ClipDisplayItemClass(ref mut clip) => Some(&mut clip.children),
SolidColorDisplayItemClass(..) |
TextDisplayItemClass(..) |
ImageDisplayItemClass(..) |
BorderDisplayItemClass(..) |
LineDisplayItemClass(..) |
PseudoDisplayItemClass(..) => None,
}
}
pub fn debug_with_level(&self, level: uint) {
let mut indent = String::new();
for _ in range(0, level) {
indent.push_str("| ")
}
debug!("{}+ {}", indent, self);
for child in self.children() {
child.debug_with_level(level + 1);
}
}
}
@ -764,7 +658,6 @@ impl fmt::Show for DisplayItem {
ImageDisplayItemClass(_) => "Image",
BorderDisplayItemClass(_) => "Border",
LineDisplayItemClass(_) => "Line",
ClipDisplayItemClass(_) => "Clip",
PseudoDisplayItemClass(_) => "Pseudo",
},
self.base().bounds,

View file

@ -2,9 +2,7 @@
* 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/. */
use display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass, DisplayItem};
use display_list::{DisplayList, ImageDisplayItemClass, LineDisplayItemClass};
use display_list::{PseudoDisplayItemClass, SolidColorDisplayItemClass, TextDisplayItemClass};
use display_list::{DisplayItem, DisplayList};
use collections::dlist::DList;
use geom::rect::Rect;
@ -45,28 +43,11 @@ impl DisplayListOptimizer {
fn process_display_item(&self, display_item: &DisplayItem) -> Option<DisplayItem> {
// Eliminate display items outside the visible region.
if !self.visible_rect.intersects(&display_item.base().bounds) {
return None
}
// Recur.
match *display_item {
ClipDisplayItemClass(ref clip) => {
let new_children = self.process_display_list(&clip.children);
if new_children.is_empty() {
return None
}
Some(ClipDisplayItemClass(box ClipDisplayItem {
base: clip.base.clone(),
children: new_children,
}))
}
BorderDisplayItemClass(_) | ImageDisplayItemClass(_) | LineDisplayItemClass(_) |
PseudoDisplayItemClass(_) | SolidColorDisplayItemClass(_) |
TextDisplayItemClass(_) => {
Some((*display_item).clone())
}
if !self.visible_rect.intersects(&display_item.base().bounds) ||
!self.visible_rect.intersects(&display_item.base().clip_rect) {
None
} else {
Some((*display_item).clone())
}
}
}

View file

@ -25,13 +25,14 @@ use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderSt
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry;
use servo_util::geometry::{Au, mod};
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::task::spawn_named_with_send_on_failure;
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use std::i32;
use sync::Arc;
use font_cache_task::FontCacheTask;
@ -356,8 +357,13 @@ impl<C:RenderListener + Send> RenderTask<C> {
ctx.clear();
// Draw the display list.
profile(time::RenderingDrawingCategory, None, self.time_profiler_chan.clone(), || {
display_list.draw_into_context(&mut ctx, &matrix);
profile(time::RenderingDrawingCategory,
None,
self.time_profiler_chan.clone(),
|| {
let clip_rect = Rect(Point2D(Au(i32::MIN), Au(i32::MIN)),
Size2D(Au(i32::MAX), Au(i32::MAX)));
display_list.draw_into_context(&mut ctx, &matrix, &clip_rect);
ctx.draw_target.flush();
});
}

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)

View file

@ -73,6 +73,29 @@ impl Default for Au {
}
}
pub static ZERO_RECT: Rect<Au> = Rect {
origin: Point2D {
x: Au(0),
y: Au(0),
},
size: Size2D {
width: Au(0),
height: Au(0),
}
};
pub static MAX_RECT: Rect<Au> = Rect {
origin: Point2D {
x: Au(i32::MIN / 2),
y: Au(i32::MIN / 2),
},
size: Size2D {
width: MAX_AU,
height: MAX_AU,
}
};
pub static MIN_AU: Au = Au(i32::MIN);
pub static MAX_AU: Au = Au(i32::MAX);
impl<E, S: Encoder<E>> Encodable<S, E> for Au {