auto merge of #2235 : pcwalton/servo/parallel-floats, r=larsbergstrom

layout: Rewrite display list building to be parallel and to handle
overflow correctly, and opportunistically lay out blocks in parallel
even if floats are present.

This series of commits fixes the `inline-height-test` in Acid2 by
implementing proper overflow as well as the inline "strut". (See CSS 2.1
§ 10.8.1.) It was accidentally working before because tables' descendant
flows were not being laid out properly.

Display list building is now parallel and is done by bubbling up display
items and layers from parent to child. Speedups of around 60%-70% are
observed on Wikipedia with a 4-core HyperThreaded Core i7. More
optimizations are possible; this is just a start.

To minimize the amount of data that needs to be bubbled up, as well as
to make proper handling of `overflow: hidden` clipping easier, the
`StackingContext` abstraction is now purely internal to the display
list. Instead of placing items into a stacking context directly, display
items are placed into display lists with an explicit `StackingLevel`
provided. The stacking level determines a display item's position within
the stacking context. When a stacking context is finished, it is
flattened with the the `flatten` method, which shuffles the display
items that make up the context into their proper order while handling
clipping properly.

r? @SimonSapin and/or @larsbergstrom
This commit is contained in:
bors-servo 2014-05-02 19:10:20 -04:00
commit 1a88996c04
20 changed files with 1515 additions and 1068 deletions

View file

@ -18,12 +18,15 @@ use color::Color;
use render_context::RenderContext; use render_context::RenderContext;
use text::TextRun; use text::TextRun;
use collections::deque::Deque;
use collections::dlist::DList;
use collections::dlist;
use geom::{Point2D, Rect, SideOffsets2D, Size2D}; use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use libc::uintptr_t; use libc::uintptr_t;
use servo_net::image::base::Image; use servo_net::image::base::Image;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::range::Range; use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVecIterator}; use servo_util::smallvec::{SmallVec, SmallVec0};
use std::mem; use std::mem;
use std::slice::Items; use std::slice::Items;
use style::computed_values::border_style; use style::computed_values::border_style;
@ -46,11 +49,35 @@ impl OpaqueNode {
} }
} }
/// A stacking context. See CSS 2.1 § E.2. "Steps" below refer to steps in that section of the /// "Steps" as defined by CSS 2.1 § E.2.
/// specification. #[deriving(Eq)]
pub enum StackingLevel {
/// The border and backgrounds for the root of this stacking context: steps 1 and 2.
BackgroundAndBordersStackingLevel,
/// Borders and backgrounds for block-level descendants: step 4.
BlockBackgroundsAndBordersStackingLevel,
/// Floats: step 5. These are treated as pseudo-stacking contexts.
FloatStackingLevel,
/// All other content.
ContentStackingLevel,
/// Positioned descendant stacking contexts, along with their `z-index` levels.
/// ///
/// TODO(pcwalton): Outlines. /// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle
pub struct StackingContext { /// `auto`, not just an integer.
PositionedDescendantStackingLevel(i32)
}
impl StackingLevel {
pub fn from_background_and_border_level(level: BackgroundAndBorderLevel) -> StackingLevel {
match level {
RootOfStackingContextLevel => BackgroundAndBordersStackingLevel,
BlockLevel => BlockBackgroundsAndBordersStackingLevel,
ContentLevel => ContentStackingLevel,
}
}
}
struct StackingContext {
/// The border and backgrounds for the root of this stacking context: steps 1 and 2. /// The border and backgrounds for the root of this stacking context: steps 1 and 2.
pub background_and_borders: DisplayList, pub background_and_borders: DisplayList,
/// Borders and backgrounds for block-level descendants: step 4. /// Borders and backgrounds for block-level descendants: step 4.
@ -59,47 +86,208 @@ pub struct StackingContext {
pub floats: DisplayList, pub floats: DisplayList,
/// All other content. /// All other content.
pub content: DisplayList, pub content: DisplayList,
/// Positioned descendant stacking contexts, along with their `z-index` levels. /// Positioned descendant stacking contexts, along with their `z-index` levels.
/// ///
/// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle /// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle
/// `auto`, not just an integer. In this case we should store an actual stacking context, not /// `auto`, not just an integer.
/// a flattened display list. pub positioned_descendants: SmallVec0<(i32, DisplayList)>,
pub positioned_descendants: SmallVec0<(int, DisplayList)>,
} }
impl StackingContext { impl StackingContext {
pub fn new() -> StackingContext { /// Creates a stacking context from a display list.
StackingContext { fn new(list: DisplayList) -> StackingContext {
let DisplayList {
list: mut list
} = list;
let mut stacking_context = StackingContext {
background_and_borders: DisplayList::new(), background_and_borders: DisplayList::new(),
block_backgrounds_and_borders: DisplayList::new(), block_backgrounds_and_borders: DisplayList::new(),
floats: DisplayList::new(), floats: DisplayList::new(),
content: DisplayList::new(), content: DisplayList::new(),
positioned_descendants: SmallVec0::new(), positioned_descendants: SmallVec0::new(),
};
for item in list.move_iter() {
match item {
ClipDisplayItemClass(~ClipDisplayItem {
base: base,
children: sublist
}) => {
let sub_stacking_context = StackingContext::new(sublist);
stacking_context.merge_with_clip(sub_stacking_context, &base.bounds, base.node)
}
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
.mut_iter()
.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_back(item);
stacking_context.positioned_descendants.push((z_index, new_list))
}
}
}
} }
} }
pub fn list_for_background_and_border_level<'a>( stacking_context
&'a mut self,
level: BackgroundAndBorderLevel)
-> &'a mut DisplayList {
match level {
RootOfStackingContextLevel => &mut self.background_and_borders,
BlockLevel => &mut self.block_backgrounds_and_borders,
ContentLevel => &mut self.content,
}
} }
/// Flattens a stacking context into a display list according to the steps in CSS 2.1 § E.2. /// Merges another stacking context into this one, with the given clipping rectangle and DOM
pub fn flatten(self) -> DisplayList { /// node that supplies it.
// Steps 1 and 2: Borders and background for the root. fn merge_with_clip(&mut self,
other: StackingContext,
clip_rect: &Rect<Au>,
clipping_dom_node: OpaqueNode) {
let StackingContext { let StackingContext {
background_and_borders: mut result, background_and_borders,
block_backgrounds_and_borders, block_backgrounds_and_borders,
floats, floats,
content, content,
positioned_descendants: mut positioned_descendants positioned_descendants: mut positioned_descendants
} = self; } = 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(~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.move_iter() {
match self.positioned_descendants
.mut_iter()
.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.
pub enum BackgroundAndBorderLevel {
RootOfStackingContextLevel,
BlockLevel,
ContentLevel,
}
/// A list of rendering operations to be performed.
pub struct DisplayList {
pub list: DList<DisplayItem>,
}
pub enum DisplayListIterator<'a> {
EmptyDisplayListIterator,
ParentDisplayListIterator(Items<'a,DisplayList>),
}
impl<'a> Iterator<&'a DisplayList> for DisplayListIterator<'a> {
#[inline]
fn next(&mut self) -> Option<&'a DisplayList> {
match *self {
EmptyDisplayListIterator => None,
ParentDisplayListIterator(ref mut subiterator) => subiterator.next(),
}
}
}
impl DisplayList {
/// Creates a new display list.
pub fn new() -> DisplayList {
DisplayList {
list: DList::new(),
}
}
fn dump(&self) {
for item in self.list.iter() {
item.debug_with_level(0);
}
}
/// Appends the given item to the display list.
pub fn push(&mut self, item: DisplayItem) {
self.list.push_back(item)
}
/// Appends the given display list to this display list, consuming the other display list in
/// the process.
pub fn push_all_move(&mut self, other: DisplayList) {
self.list.append(other.list)
}
/// 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) {
debug!("Beginning display list.");
for item in self.list.iter() {
item.draw_into_context(render_context)
}
debug!("Ending display list.");
}
/// Returns a preorder iterator over the given display list.
pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> {
ParentDisplayItemIterator(self.list.iter())
}
/// Returns true if this list is empty and false otherwise.
fn is_empty(&self) -> bool {
self.list.len() == 0
}
/// Flattens a display list into a display list with a single stacking level according to the
/// steps in CSS 2.1 § E.2.
///
/// This must be called before `draw_into_context()` is for correct results.
pub fn flatten(self, resulting_level: StackingLevel) -> DisplayList {
// TODO(pcwalton): Sort positioned children according to z-index.
let mut result = DisplayList::new();
let StackingContext {
background_and_borders,
block_backgrounds_and_borders,
floats,
content,
positioned_descendants: mut positioned_descendants
} = StackingContext::new(self);
// Steps 1 and 2: Borders and background for the root.
result.push_all_move(background_and_borders);
// TODO(pcwalton): Sort positioned children according to z-index. // TODO(pcwalton): Sort positioned children according to z-index.
@ -130,75 +318,20 @@ impl StackingContext {
// TODO(pcwalton): Step 10: Outlines. // TODO(pcwalton): Step 10: Outlines.
result.set_stacking_level(resulting_level);
result result
} }
}
/// Which level to place backgrounds and borders in. /// Sets the stacking level for this display list and all its subitems.
pub enum BackgroundAndBorderLevel { fn set_stacking_level(&mut self, new_level: StackingLevel) {
RootOfStackingContextLevel, for item in self.list.mut_iter() {
BlockLevel, item.mut_base().level = new_level;
ContentLevel, match item.mut_sublist() {
} None => {}
Some(sublist) => sublist.set_stacking_level(new_level),
/// A list of rendering operations to be performed.
pub struct DisplayList {
pub list: SmallVec0<DisplayItem>,
}
pub enum DisplayListIterator<'a> {
EmptyDisplayListIterator,
ParentDisplayListIterator(Items<'a,DisplayList>),
}
impl<'a> Iterator<&'a DisplayList> for DisplayListIterator<'a> {
#[inline]
fn next(&mut self) -> Option<&'a DisplayList> {
match *self {
EmptyDisplayListIterator => None,
ParentDisplayListIterator(ref mut subiterator) => subiterator.next(),
} }
} }
} }
impl DisplayList {
/// Creates a new display list.
pub fn new() -> DisplayList {
DisplayList {
list: SmallVec0::new(),
}
}
fn dump(&self) {
for item in self.list.iter() {
item.debug_with_level(0);
}
}
/// Appends the given item to the display list.
pub fn push(&mut self, item: DisplayItem) {
self.list.push(item)
}
/// Appends the given display list to this display list, consuming the other display list in
/// the process.
pub fn push_all_move(&mut self, other: DisplayList) {
self.list.push_all_move(other.list)
}
/// Draws the display list into the given render context.
pub fn draw_into_context(&self, render_context: &mut RenderContext) {
debug!("Beginning display list.");
for item in self.list.iter() {
item.draw_into_context(render_context)
}
debug!("Ending display list.");
}
/// Returns a preorder iterator over the given display list.
pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> {
ParentDisplayItemIterator(self.list.iter())
}
} }
/// One drawing command in the list. /// One drawing command in the list.
@ -208,7 +341,14 @@ pub enum DisplayItem {
ImageDisplayItemClass(~ImageDisplayItem), ImageDisplayItemClass(~ImageDisplayItem),
BorderDisplayItemClass(~BorderDisplayItem), BorderDisplayItemClass(~BorderDisplayItem),
LineDisplayItemClass(~LineDisplayItem), LineDisplayItemClass(~LineDisplayItem),
ClipDisplayItemClass(~ClipDisplayItem) ClipDisplayItemClass(~ClipDisplayItem),
/// A pseudo-display item that exists only so that queries like `ContentBoxQuery` and
/// `ContentBoxesQuery` can be answered.
///
/// FIXME(pcwalton): This is really bogus. Those queries should not consult the display list
/// but should instead consult the flow/box tree.
PseudoDisplayItemClass(~BaseDisplayItem),
} }
/// Information common to all display items. /// Information common to all display items.
@ -220,6 +360,19 @@ pub struct BaseDisplayItem {
/// The originating DOM node. /// The originating DOM node.
pub node: OpaqueNode, pub node: OpaqueNode,
/// The stacking level in which this display item lives.
pub level: StackingLevel,
}
impl BaseDisplayItem {
pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel) -> BaseDisplayItem {
BaseDisplayItem {
bounds: bounds,
node: node,
level: level,
}
}
} }
/// Renders a solid color. /// Renders a solid color.
@ -292,15 +445,27 @@ pub struct LineDisplayItem {
pub style: border_style::T pub style: border_style::T
} }
/// Clips a list of child display items to this display item's boundaries.
pub struct ClipDisplayItem { pub struct ClipDisplayItem {
/// The base information.
pub base: BaseDisplayItem, pub base: BaseDisplayItem,
pub child_list: SmallVec0<DisplayItem>,
pub need_clip: bool /// 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> { pub enum DisplayItemIterator<'a> {
EmptyDisplayItemIterator, EmptyDisplayItemIterator,
ParentDisplayItemIterator(SmallVecIterator<'a,DisplayItem>), ParentDisplayItemIterator(dlist::Items<'a,DisplayItem>),
} }
impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> { impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
@ -316,22 +481,21 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
impl DisplayItem { impl DisplayItem {
/// Renders this display item into the given render context. /// Renders this display item into the given render context.
fn draw_into_context(&self, render_context: &mut RenderContext) { fn draw_into_context(&self, render_context: &mut RenderContext) {
// This should have been flattened to the content stacking level first.
assert!(self.base().level == ContentStackingLevel);
match *self { match *self {
SolidColorDisplayItemClass(ref solid_color) => { SolidColorDisplayItemClass(ref solid_color) => {
render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color) render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
} }
ClipDisplayItemClass(ref clip) => { ClipDisplayItemClass(ref clip) => {
if clip.need_clip {
render_context.draw_push_clip(&clip.base.bounds); render_context.draw_push_clip(&clip.base.bounds);
} for item in clip.children.iter() {
for item in clip.child_list.iter() {
(*item).draw_into_context(render_context); (*item).draw_into_context(render_context);
} }
if clip.need_clip {
render_context.draw_pop_clip(); render_context.draw_pop_clip();
} }
}
TextDisplayItemClass(ref text) => { TextDisplayItemClass(ref text) => {
debug!("Drawing text at {:?}.", text.base.bounds); debug!("Drawing text at {:?}.", text.base.bounds);
@ -412,6 +576,8 @@ impl DisplayItem {
line.color, line.color,
line.style) line.style)
} }
PseudoDisplayItemClass(_) => {}
} }
} }
@ -423,6 +589,19 @@ impl DisplayItem {
BorderDisplayItemClass(ref border) => &border.base, BorderDisplayItemClass(ref border) => &border.base,
LineDisplayItemClass(ref line) => &line.base, LineDisplayItemClass(ref line) => &line.base,
ClipDisplayItemClass(ref clip) => &clip.base, ClipDisplayItemClass(ref clip) => &clip.base,
PseudoDisplayItemClass(ref base) => &**base,
}
}
pub fn mut_base<'a>(&'a mut self) -> &'a mut BaseDisplayItem {
match *self {
SolidColorDisplayItemClass(ref mut solid_color) => &mut solid_color.base,
TextDisplayItemClass(ref mut text) => &mut text.base,
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,
} }
} }
@ -432,12 +611,26 @@ impl DisplayItem {
pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> { pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> {
match *self { match *self {
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()), ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.children.list.iter()),
SolidColorDisplayItemClass(..) | SolidColorDisplayItemClass(..) |
TextDisplayItemClass(..) | TextDisplayItemClass(..) |
ImageDisplayItemClass(..) | ImageDisplayItemClass(..) |
BorderDisplayItemClass(..) | BorderDisplayItemClass(..) |
LineDisplayItemClass(..) => EmptyDisplayItemIterator, 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,
} }
} }
@ -460,8 +653,9 @@ impl DisplayItem {
BorderDisplayItemClass(_) => "Border", BorderDisplayItemClass(_) => "Border",
LineDisplayItemClass(_) => "Line", LineDisplayItemClass(_) => "Line",
ClipDisplayItemClass(_) => "Clip", ClipDisplayItemClass(_) => "Clip",
PseudoDisplayItemClass(_) => "Pseudo",
}; };
format!("{} @ {:?}", class, self.base().bounds) format!("{} @ {} ({:x})", class, self.base().bounds, self.base().node.id())
} }
} }

View file

@ -15,7 +15,6 @@
use layout::box_::{Box, ImageBox, ScannedTextBox}; use layout::box_::{Box, ImageBox, ScannedTextBox};
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo}; use layout::floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo};
use layout::flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base}; use layout::flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
@ -28,18 +27,22 @@ use layout::wrapper::ThreadSafeLayoutNode;
use style::ComputedValues; use style::ComputedValues;
use style::computed_values::{clear, position}; use style::computed_values::{clear, position};
use collections::Deque;
use collections::dlist::DList;
use geom::{Point2D, Rect, Size2D}; use geom::{Point2D, Rect, Size2D};
use gfx::color; use gfx::color;
use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, RootOfStackingContextLevel}; use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, ContentStackingLevel, DisplayList};
use gfx::display_list::{StackingContext}; use gfx::display_list::{FloatStackingLevel, PositionedDescendantStackingLevel};
use gfx::display_list::{RootOfStackingContextLevel};
use gfx::render_task::RenderLayer; use gfx::render_task::RenderLayer;
use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable}; use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable};
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::smallvec::{SmallVec, SmallVec0}; use servo_util::smallvec::{SmallVec, SmallVec0};
use std::mem;
use std::num::Zero; use std::num::Zero;
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None}; use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage}; use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, display, float, overflow};
use sync::Arc; use sync::Arc;
/// Information specific to floated blocks. /// Information specific to floated blocks.
@ -52,9 +55,6 @@ pub struct FloatedBlockInfo {
/// Index into the box list for inline floats /// Index into the box list for inline floats
pub index: Option<uint>, pub index: Option<uint>,
/// Number of floated children
pub floated_children: uint,
/// Left or right? /// Left or right?
pub float_kind: FloatKind, pub float_kind: FloatKind,
} }
@ -65,7 +65,6 @@ impl FloatedBlockInfo {
containing_width: Au(0), containing_width: Au(0),
rel_pos: Point2D(Au(0), Au(0)), rel_pos: Point2D(Au(0), Au(0)),
index: None, index: None,
floated_children: 0,
float_kind: float_kind, float_kind: float_kind,
} }
} }
@ -377,13 +376,10 @@ enum CandidateHeightIteratorStatus {
} }
// A helper function used in height calculation. // A helper function used in height calculation.
fn translate_including_floats(cur_y: &mut Au, delta: Au, inorder: bool, floats: &mut Floats) { fn translate_including_floats(cur_y: &mut Au, delta: Au, floats: &mut Floats) {
*cur_y = *cur_y + delta; *cur_y = *cur_y + delta;
if inorder {
floats.translate(Point2D(Au(0), -delta)); floats.translate(Point2D(Au(0), -delta));
} }
}
/// The real assign-heights traversal for flows with position 'absolute'. /// The real assign-heights traversal for flows with position 'absolute'.
/// ///
@ -801,14 +797,13 @@ impl BlockFlow {
#[inline(always)] #[inline(always)]
pub fn assign_height_block_base(&mut self, pub fn assign_height_block_base(&mut self,
layout_context: &mut LayoutContext, layout_context: &mut LayoutContext,
inorder: bool,
margins_may_collapse: MarginsMayCollapseFlag) { margins_may_collapse: MarginsMayCollapseFlag) {
// Our current border-box position. // Our current border-box position.
let mut cur_y = Au(0); let mut cur_y = Au(0);
// Absolute positioning establishes a block formatting context. Don't propagate floats // Absolute positioning establishes a block formatting context. Don't propagate floats
// in or out. (But do propagate them between kids.) // in or out. (But do propagate them between kids.)
if inorder && self.is_absolutely_positioned() { if self.is_absolutely_positioned() {
self.base.floats = Floats::new(); self.base.floats = Floats::new();
} }
if margins_may_collapse != MarginsMayCollapse { if margins_may_collapse != MarginsMayCollapse {
@ -820,7 +815,7 @@ impl BlockFlow {
// The sum of our top border and top padding. // The sum of our top border and top padding.
let top_offset = self.box_.border_padding.top; let top_offset = self.box_.border_padding.top;
translate_including_floats(&mut cur_y, top_offset, inorder, &mut self.base.floats); translate_including_floats(&mut cur_y, top_offset, &mut self.base.floats);
let can_collapse_top_margin_with_kids = let can_collapse_top_margin_with_kids =
margins_may_collapse == MarginsMayCollapse && margins_may_collapse == MarginsMayCollapse &&
@ -829,19 +824,15 @@ impl BlockFlow {
margin_collapse_info.initialize_top_margin(&self.box_, margin_collapse_info.initialize_top_margin(&self.box_,
can_collapse_top_margin_with_kids); can_collapse_top_margin_with_kids);
// At this point, cur_y is at the content edge of the flow's box. // At this point, `cur_y` is at the content edge of our box. Now iterate over children.
let mut floats = self.base.floats.clone(); let mut floats = self.base.floats.clone();
let mut layers_needed_for_descendants = false; let mut layers_needed_for_descendants = false;
for kid in self.base.child_iter() { for kid in self.base.child_iter() {
if kid.is_absolutely_positioned() { if kid.is_absolutely_positioned() {
// Assume that the *hypothetical box* for an absolute flow starts immediately after // Assume that the *hypothetical box* for an absolute flow starts immediately after
// the bottom border edge of the previous flow. // the bottom border edge of the previous flow.
kid.as_block().base.position.origin.y = cur_y; flow::mut_base(kid).position.origin.y = cur_y;
kid.assign_height_for_inorder_child_if_necessary(layout_context);
if inorder {
kid.assign_height_inorder(layout_context)
}
propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid); propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
// Skip the collapsing and float processing for absolute flow kids and continue // Skip the collapsing and float processing for absolute flow kids and continue
@ -850,86 +841,82 @@ impl BlockFlow {
} }
// Assign height now for the child if it was impacted by floats and we couldn't before. // Assign height now for the child if it was impacted by floats and we couldn't before.
let mut floats_out = None; flow::mut_base(kid).floats = floats.clone();
if inorder {
if !kid.is_float() {
let kid_base = flow::mut_base(kid);
if kid_base.clear != clear::none {
// We have clearance, so assume there are no floats in and perform layout.
//
// FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or
// `clear: right` and there are still floats to impact, of course. But this
// gets complicated with margin collapse. Possibly the right thing to do is
// to lay out the block again in this rare case. (Note that WebKit can lay
// blocks out twice; this may be related, although I haven't looked into it
// closely.)
kid_base.floats = Floats::new()
} else {
kid_base.floats = floats.clone()
}
} else {
let kid_base = flow::mut_base(kid);
kid_base.position.origin.y = margin_collapse_info.current_float_ceiling();
kid_base.floats = floats.clone()
}
kid.assign_height_inorder(layout_context);
floats_out = Some(flow::mut_base(kid).floats.clone());
// A horrible hack that has to do with the fact that `origin.y` is used for
// something else later (containing block for float).
if kid.is_float() { if kid.is_float() {
flow::mut_base(kid).position.origin.y = cur_y; // FIXME(pcwalton): Using `position.origin.y` to mean the float ceiling is a
} // bit of a hack.
} flow::mut_base(kid).position.origin.y =
margin_collapse_info.current_float_ceiling();
propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid); propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
// If the child was a float, stop here. let kid_was_impacted_by_floats =
if kid.is_float() { kid.assign_height_for_inorder_child_if_necessary(layout_context);
if inorder { assert!(kid_was_impacted_by_floats); // As it was a float itself...
floats = floats_out.take_unwrap();
} let kid_base = flow::mut_base(kid);
kid_base.position.origin.y = cur_y;
floats = kid_base.floats.clone();
continue continue
} }
// Handle any (possibly collapsed) top margin.
let kid_base = flow::mut_base(kid);
let delta = margin_collapse_info.advance_top_margin(&kid_base.collapsible_margins);
translate_including_floats(&mut cur_y, delta, inorder, &mut floats);
// Clear past floats, if necessary. // If we have clearance, assume there are no floats in.
if inorder { //
let clearance = match kid_base.clear { // FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or `clear:
// right` and there are still floats to impact, of course. But this gets complicated
// with margin collapse. Possibly the right thing to do is to lay out the block again
// in this rare case. (Note that WebKit can lay blocks out twice; this may be related,
// although I haven't looked into it closely.)
if kid.float_clearance() != clear::none {
flow::mut_base(kid).floats = Floats::new()
}
// Lay the child out if this was an in-order traversal.
let kid_was_impacted_by_floats =
kid.assign_height_for_inorder_child_if_necessary(layout_context);
// Mark flows for layerization if necessary to handle painting order correctly.
propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
// Handle any (possibly collapsed) top margin.
let delta =
margin_collapse_info.advance_top_margin(&flow::base(kid).collapsible_margins);
translate_including_floats(&mut cur_y, delta, &mut floats);
// Clear past the floats that came in, if necessary.
let clearance = match kid.float_clearance() {
clear::none => Au(0), clear::none => Au(0),
clear::left => floats.clearance(ClearLeft), clear::left => floats.clearance(ClearLeft),
clear::right => floats.clearance(ClearRight), clear::right => floats.clearance(ClearRight),
clear::both => floats.clearance(ClearBoth), clear::both => floats.clearance(ClearBoth),
}; };
cur_y = cur_y + clearance cur_y = cur_y + clearance;
}
// At this point, `cur_y` is at the border edge of the child. // At this point, `cur_y` is at the border edge of the child.
assert!(kid_base.position.origin.y == Au(0)); flow::mut_base(kid).position.origin.y = cur_y;
kid_base.position.origin.y = cur_y;
// If this was an inorder traversal, grab the child's floats now. // Now pull out the child's outgoing floats. We didn't do this immediately after the
if inorder { // `assign_height_for_inorder_child_if_necessary` call because clearance on a block
floats = floats_out.take_unwrap() // operates on the floats that come *in*, not the floats that go *out*.
if kid_was_impacted_by_floats {
floats = flow::mut_base(kid).floats.clone()
} }
// Move past the child's border box. Do not use the `translate_including_floats` // Move past the child's border box. Do not use the `translate_including_floats`
// function here because the child has already translated floats past its border box. // function here because the child has already translated floats past its border box.
let kid_base = flow::mut_base(kid);
cur_y = cur_y + kid_base.position.size.height; cur_y = cur_y + kid_base.position.size.height;
// Handle any (possibly collapsed) bottom margin. // Handle any (possibly collapsed) bottom margin.
let delta = margin_collapse_info.advance_bottom_margin(&kid_base.collapsible_margins); let delta = margin_collapse_info.advance_bottom_margin(&kid_base.collapsible_margins);
translate_including_floats(&mut cur_y, delta, inorder, &mut floats); translate_including_floats(&mut cur_y, delta, &mut floats);
} }
// Mark ourselves for layerization if that will be necessary to paint in the proper order
// (CSS 2.1, Appendix E).
self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants); self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants);
// Collect various offsets needed by absolutely positioned descendants.
self.collect_static_y_offsets_from_kids(); self.collect_static_y_offsets_from_kids();
// Add in our bottom margin and compute our collapsible margins. // Add in our bottom margin and compute our collapsible margins.
@ -942,7 +929,7 @@ impl BlockFlow {
&self.box_, &self.box_,
can_collapse_bottom_margin_with_kids); can_collapse_bottom_margin_with_kids);
self.base.collapsible_margins = collapsible_margins; self.base.collapsible_margins = collapsible_margins;
translate_including_floats(&mut cur_y, delta, inorder, &mut floats); translate_including_floats(&mut cur_y, delta, &mut floats);
// FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this
// is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the // is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the
@ -981,11 +968,11 @@ impl BlockFlow {
// Adjust `cur_y` as necessary to account for the explicitly-specified height. // Adjust `cur_y` as necessary to account for the explicitly-specified height.
height = candidate_height_iterator.candidate_value; height = candidate_height_iterator.candidate_value;
let delta = height - (cur_y - top_offset); let delta = height - (cur_y - top_offset);
translate_including_floats(&mut cur_y, delta, inorder, &mut floats); translate_including_floats(&mut cur_y, delta, &mut floats);
// Compute content height and noncontent height. // Compute content height and noncontent height.
let bottom_offset = self.box_.border_padding.bottom; let bottom_offset = self.box_.border_padding.bottom;
translate_including_floats(&mut cur_y, bottom_offset, inorder, &mut floats); translate_including_floats(&mut cur_y, bottom_offset, &mut floats);
// Now that `cur_y` is at the bottom of the border box, compute the final border box // Now that `cur_y` is at the bottom of the border box, compute the final border box
// position. // position.
@ -1011,17 +998,15 @@ impl BlockFlow {
/// Add placement information about current float flow for use by the parent. /// Add placement information about current float flow for use by the parent.
/// ///
/// Also, use information given by parent about other floats to find out /// Also, use information given by parent about other floats to find out our relative position.
/// our relative position.
/// ///
/// This does not give any information about any float descendants because /// This does not give any information about any float descendants because they do not affect
/// they do not affect elements outside of the subtree rooted at this /// elements outside of the subtree rooted at this float.
/// float.
/// ///
/// This function is called on a kid flow by a parent. /// This function is called on a kid flow by a parent. Therefore, `assign_height_float` was
/// Therefore, assign_height_float was already called on this kid flow by /// already called on this kid flow by the traversal function. So, the values used are
/// the traversal function. So, the values used are well-defined. /// well-defined.
pub fn assign_height_float_inorder(&mut self) { pub fn place_float(&mut self) {
let height = self.box_.border_box.size.height; let height = self.box_.border_box.size.height;
let clearance = match self.box_.clear() { let clearance = match self.box_.clear() {
None => Au(0), None => Au(0),
@ -1054,19 +1039,15 @@ impl BlockFlow {
/// ///
/// It does not calculate the height of the flow itself. /// It does not calculate the height of the flow itself.
pub fn assign_height_float(&mut self, ctx: &mut LayoutContext) { pub fn assign_height_float(&mut self, ctx: &mut LayoutContext) {
// Now that we've determined our height, propagate that out.
let has_inorder_children = self.base.num_floats > 0;
if has_inorder_children {
let mut floats = Floats::new(); let mut floats = Floats::new();
for kid in self.base.child_iter() { for kid in self.base.child_iter() {
flow::mut_base(kid).floats = floats; flow::mut_base(kid).floats = floats.clone();
kid.assign_height_inorder(ctx); kid.assign_height_for_inorder_child_if_necessary(ctx);
floats = flow::mut_base(kid).floats.clone(); floats = flow::mut_base(kid).floats.clone();
} }
// Floats establish a block formatting context, so we discard the output floats here. // Floats establish a block formatting context, so we discard the output floats here.
drop(floats); drop(floats);
}
let top_offset = self.box_.margin.top + self.box_.border_padding.top; let top_offset = self.box_.margin.top + self.box_.border_padding.top;
let mut cur_y = top_offset; let mut cur_y = top_offset;
@ -1101,99 +1082,75 @@ impl BlockFlow {
} }
fn build_display_list_block_common(&mut self, fn build_display_list_block_common(&mut self,
stacking_context: &mut StackingContext, layout_context: &LayoutContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo,
offset: Point2D<Au>, offset: Point2D<Au>,
background_border_level: BackgroundAndBorderLevel) { background_border_level: BackgroundAndBorderLevel) {
let mut info = *info; let rel_offset =
let rel_offset = self.box_.relative_position(&info.relative_containing_block_size, None); self.box_.relative_position(&self.base
.absolute_position_info
.relative_containing_block_size,
None);
// Add the box that starts the block context. // Add the box that starts the block context.
self.box_.build_display_list(stacking_context, let mut display_list = DisplayList::new();
builder, let mut accumulator =
&info, self.box_.build_display_list(&mut display_list,
layout_context,
self.base.abs_position + rel_offset + offset, self.base.abs_position + rel_offset + offset,
background_border_level, background_border_level,
None); None);
// For relatively-positioned descendants, the containing block formed by a block is let mut child_layers = DList::new();
// just the content box. The containing block for absolutely-positioned descendants,
// on the other hand, only established if we are positioned.
info.relative_containing_block_size = self.box_.content_box().size;
if self.is_positioned() {
info.absolute_containing_block_position =
self.base.abs_position +
self.generated_containing_block_rect().origin +
self.box_.relative_position(&info.relative_containing_block_size, None)
}
let this_position = self.base.abs_position;
for kid in self.base.child_iter() { for kid in self.base.child_iter() {
{
let kid_base = flow::mut_base(kid);
kid_base.abs_position = this_position + kid_base.position.origin + rel_offset +
offset;
}
if kid.is_absolutely_positioned() { if kid.is_absolutely_positioned() {
// All absolute flows will be handled by their containing block. // All absolute flows will be handled by their containing block.
continue continue
} }
kid.build_display_list(stacking_context, builder, &info); accumulator.push_child(&mut display_list, kid);
child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new()))
} }
// Process absolute descendant links. // Process absolute descendant links.
let mut absolute_info = info;
absolute_info.layers_needed_for_positioned_flows =
self.base.flags.layers_needed_for_descendants();
for abs_descendant_link in self.base.abs_descendants.iter() { for abs_descendant_link in self.base.abs_descendants.iter() {
match abs_descendant_link.resolve() { match abs_descendant_link.resolve() {
Some(flow) => { Some(kid) => {
// TODO(pradeep): Send in our absolute position directly. // TODO(pradeep): Send in our absolute position directly.
flow.build_display_list(stacking_context, builder, &absolute_info) accumulator.push_child(&mut display_list, kid);
child_layers.append(mem::replace(&mut flow::mut_base(kid).layers,
DList::new()));
} }
None => fail!("empty Rawlink to a descendant") None => fail!("empty Rawlink to a descendant")
} }
} }
accumulator.finish(&mut *self, display_list);
self.base.layers = child_layers
} }
/// Add display items for current block. /// Add display items for current block.
/// ///
/// Set the absolute position for children after doing any offsetting for /// Set the absolute position for children after doing any offsetting for
/// position: relative. /// position: relative.
pub fn build_display_list_block(&mut self, pub fn build_display_list_block(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
if self.is_float() { if self.is_float() {
// TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index: // 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. // auto` kids into the parent stacking context, when that is supported.
self.build_display_list_float(stacking_context, builder, info) self.build_display_list_float(layout_context)
} else if self.is_absolutely_positioned() { } else if self.is_absolutely_positioned() {
self.build_display_list_abs(stacking_context, builder, info) self.build_display_list_abs(layout_context)
} else { } else {
self.build_display_list_block_common(stacking_context, self.build_display_list_block_common(layout_context, Zero::zero(), BlockLevel)
builder,
info,
Point2D(Au(0), Au(0)),
BlockLevel)
} }
} }
pub fn build_display_list_float(&mut self, pub fn build_display_list_float(&mut self, layout_context: &LayoutContext) {
parent_stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
let mut stacking_context = StackingContext::new();
let float_offset = self.float.get_ref().rel_pos; let float_offset = self.float.get_ref().rel_pos;
self.build_display_list_block_common(&mut stacking_context, self.build_display_list_block_common(layout_context,
builder,
info,
float_offset, float_offset,
RootOfStackingContextLevel); RootOfStackingContextLevel);
parent_stacking_context.floats.push_all_move(stacking_context.flatten()) self.base.display_list = mem::replace(&mut self.base.display_list,
DisplayList::new()).flatten(FloatStackingLevel)
} }
/// Calculate and set the height, offsets, etc. for absolutely positioned flow. /// Calculate and set the height, offsets, etc. for absolutely positioned flow.
@ -1283,58 +1240,41 @@ impl BlockFlow {
} }
/// Add display items for Absolutely Positioned flow. /// Add display items for Absolutely Positioned flow.
pub fn build_display_list_abs(&mut self, fn build_display_list_abs(&mut self, layout_context: &LayoutContext) {
parent_stacking_context: &mut StackingContext, self.build_display_list_block_common(layout_context,
builder: &mut DisplayListBuilder, Zero::zero(),
info: &DisplayListBuildingInfo) {
let mut stacking_context = StackingContext::new();
let mut info = *info;
info.absolute_containing_block_position = if self.is_fixed() {
// The viewport is initially at (0, 0).
self.base.position.origin
} else {
// Absolute position of Containing Block + position of absolute flow
// wrt Containing Block
info.absolute_containing_block_position + self.base.position.origin
};
// Set the absolute position, which will be passed down later as part
// of containing block details for absolute descendants.
self.base.abs_position = info.absolute_containing_block_position;
self.build_display_list_block_common(&mut stacking_context,
builder,
&info,
Point2D(Au(0), Au(0)),
RootOfStackingContextLevel); RootOfStackingContextLevel);
if !info.layers_needed_for_positioned_flows && !self.base.flags.needs_layer() { if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
!self.base.flags.needs_layer() {
// We didn't need a layer. // We didn't need a layer.
// //
// TODO(#781, pcwalton): `z-index`. // TODO(#781, pcwalton): `z-index`.
parent_stacking_context.positioned_descendants.push((0, stacking_context.flatten())); self.base.display_list =
mem::replace(&mut self.base.display_list,
DisplayList::new()).flatten(PositionedDescendantStackingLevel(0));
return return
} }
// If we got here, then we need a new layer. // If we got here, then we need a new layer.
let size = Size2D(self.base.position.size.width.to_nearest_px() as uint, let size = Size2D(self.base.position.size.width.to_nearest_px() as uint,
self.base.position.size.height.to_nearest_px() as uint); self.base.position.size.height.to_nearest_px() as uint);
let origin = Point2D(info.absolute_containing_block_position.x.to_nearest_px() as uint, let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint,
info.absolute_containing_block_position.y.to_nearest_px() as uint); self.base.abs_position.y.to_nearest_px() as uint);
let scroll_policy = if self.is_fixed() { let scroll_policy = if self.is_fixed() {
FixedPosition FixedPosition
} else { } else {
Scrollable Scrollable
}; };
let display_list = mem::replace(&mut self.base.display_list, DisplayList::new());
let new_layer = RenderLayer { let new_layer = RenderLayer {
id: self.layer_id(0), id: self.layer_id(0),
display_list: Arc::new(stacking_context.flatten()), display_list: Arc::new(display_list.flatten(ContentStackingLevel)),
position: Rect(origin, size), position: Rect(origin, size),
background_color: color::rgba(255.0, 255.0, 255.0, 0.0), background_color: color::rgba(255.0, 255.0, 255.0, 0.0),
scroll_policy: scroll_policy, scroll_policy: scroll_policy,
}; };
builder.layers.push(new_layer) self.base.layers.push_back(new_layer)
} }
/// Return the top outer edge of the Hypothetical Box for an absolute flow. /// Return the top outer edge of the Hypothetical Box for an absolute flow.
@ -1348,27 +1288,15 @@ impl BlockFlow {
self.base.position.origin.y self.base.position.origin.y
} }
/// Initializes the containing width if this block flow is a float. This is done at the start
/// of `assign_widths`.
fn set_containing_width_if_float(&mut self, containing_block_width: Au) {
if self.is_float() {
self.float.get_mut_ref().containing_width = containing_block_width;
// Parent usually sets this, but floats are never inorder
self.base.flags.set_inorder(false)
}
}
/// Assigns the computed left content edge and width to all the children of this block flow. /// Assigns the computed left content edge and width to all the children of this block flow.
/// Also computes whether each child will be impacted by floats.
pub fn propagate_assigned_width_to_children(&mut self, pub fn propagate_assigned_width_to_children(&mut self,
left_content_edge: Au, left_content_edge: Au,
content_width: Au, content_width: Au,
opt_col_widths: Option<~[Au]>) { opt_col_widths: Option<~[Au]>) {
let has_inorder_children = if self.is_float() { // Keep track of whether floats could impact each child.
self.base.num_floats > 0 let mut left_floats_impact_child = self.base.flags.impacted_by_left_floats();
} else { let mut right_floats_impact_child = self.base.flags.impacted_by_right_floats();
self.base.flags.inorder() || self.base.num_floats > 0
};
let kid_abs_cb_x_offset; let kid_abs_cb_x_offset;
if self.is_positioned() { if self.is_positioned() {
@ -1398,15 +1326,30 @@ impl BlockFlow {
{ {
let child_base = flow::mut_base(kid); let child_base = flow::mut_base(kid);
// Left margin edge of kid flow is at our left content edge // The left margin edge of the child flow is at our left content edge.
child_base.position.origin.x = left_content_edge; child_base.position.origin.x = left_content_edge;
// Width of kid flow is our content width // The width of the child flow is our content width.
child_base.position.size.width = content_width; child_base.position.size.width = content_width;
child_base.flags.set_inorder(has_inorder_children);
if !child_base.flags.inorder() {
child_base.floats = Floats::new();
} }
// Determine float impaction.
match kid.float_clearance() {
clear::none => {}
clear::left => left_floats_impact_child = false,
clear::right => right_floats_impact_child = false,
clear::both => {
left_floats_impact_child = false;
right_floats_impact_child = false;
}
}
{
let kid_base = flow::mut_base(kid);
left_floats_impact_child = left_floats_impact_child ||
kid_base.flags.has_left_floated_descendants();
right_floats_impact_child = right_floats_impact_child ||
kid_base.flags.has_right_floated_descendants();
kid_base.flags.set_impacted_by_left_floats(left_floats_impact_child);
kid_base.flags.set_impacted_by_right_floats(right_floats_impact_child);
} }
// Handle tables. // Handle tables.
@ -1459,20 +1402,45 @@ impl Flow for BlockFlow {
self self
} }
/* Recursively (bottom-up) determine the context's preferred and /// Returns the direction that this flow clears floats in, if any.
minimum widths. When called on this context, all child contexts fn float_clearance(&self) -> clear::T {
have had their min/pref widths set. This function must decide self.box_.style().Box.get().clear
min/pref widths based on child context widths and dimensions of }
any boxes it is responsible for flowing. */
/* TODO: inline-blocks */ /// Returns true if this flow is a block formatting context and false otherwise.
fn is_block_formatting_context(&self, only_impactable_by_floats: bool) -> bool {
let style = self.box_.style();
if style.Box.get().float != float::none {
return !only_impactable_by_floats
}
if style.Box.get().overflow != overflow::visible {
return true
}
match style.Box.get().display {
display::table_cell | display::table_caption | display::inline_block => true,
_ => false,
}
}
/// Pass 1 of reflow: computes minimum and preferred widths.
///
/// Recursively (bottom-up) determine the flow's minimum and preferred widths. When called on
/// this flow, all child flows have had their minimum and preferred widths set. This function
/// must decide minimum/preferred widths based on its children's widths and the dimensions of
/// any boxes it is responsible for flowing.
///
/// TODO(pcwalton): Inline blocks.
fn bubble_widths(&mut self, _: &mut LayoutContext) { fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut num_floats = 0; let mut flags = self.base.flags;
flags.set_has_left_floated_descendants(false);
flags.set_has_right_floated_descendants(false);
// Find the maximum width from children. // Find the maximum width from children.
let mut intrinsic_widths = IntrinsicWidths::new(); let mut intrinsic_widths = IntrinsicWidths::new();
for child_ctx in self.base.child_iter() { for child_ctx in self.base.child_iter() {
assert!(child_ctx.is_block_flow() || child_ctx.is_inline_flow() || child_ctx.is_table_kind()); assert!(child_ctx.is_block_flow() ||
child_ctx.is_inline_flow() ||
child_ctx.is_table_kind());
let child_base = flow::mut_base(child_ctx); let child_base = flow::mut_base(child_ctx);
intrinsic_widths.minimum_width = intrinsic_widths.minimum_width =
@ -1481,14 +1449,8 @@ impl Flow for BlockFlow {
intrinsic_widths.preferred_width = intrinsic_widths.preferred_width =
geometry::max(intrinsic_widths.preferred_width, geometry::max(intrinsic_widths.preferred_width,
child_base.intrinsic_widths.total_preferred_width()); child_base.intrinsic_widths.total_preferred_width());
num_floats = num_floats + child_base.num_floats;
}
if self.is_float() { flags.union_floated_descendants_flags(child_base.flags);
self.base.num_floats = 1;
self.float.get_mut_ref().floated_children = num_floats;
} else {
self.base.num_floats = num_floats;
} }
let box_intrinsic_widths = self.box_.intrinsic_widths(None); let box_intrinsic_widths = self.box_.intrinsic_widths(None);
@ -1497,8 +1459,14 @@ impl Flow for BlockFlow {
intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width, intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width,
box_intrinsic_widths.preferred_width); box_intrinsic_widths.preferred_width);
intrinsic_widths.surround_width = box_intrinsic_widths.surround_width; intrinsic_widths.surround_width = box_intrinsic_widths.surround_width;
self.base.intrinsic_widths = intrinsic_widths;
self.base.intrinsic_widths = intrinsic_widths match self.box_.style().Box.get().float {
float::none => {}
float::left => flags.set_has_left_floated_descendants(true),
float::right => flags.set_has_right_floated_descendants(true),
}
self.base.flags = flags
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@ -1519,20 +1487,24 @@ impl Flow for BlockFlow {
self.base.position.origin = Zero::zero(); self.base.position.origin = Zero::zero();
self.base.position.size.width = ctx.screen_size.width; self.base.position.size.width = ctx.screen_size.width;
self.base.floats = Floats::new(); self.base.floats = Floats::new();
// Root element is not floated
self.base.flags.set_inorder(false); // The root element is never impacted by floats.
self.base.flags.set_impacted_by_left_floats(false);
self.base.flags.set_impacted_by_right_floats(false);
} }
// The position was set to the containing block by the flow's parent. // The position was set to the containing block by the flow's parent.
let containing_block_width = self.base.position.size.width; let containing_block_width = self.base.position.size.width;
self.set_containing_width_if_float(containing_block_width);
self.compute_used_width(ctx, containing_block_width); self.compute_used_width(ctx, containing_block_width);
if self.is_float() {
self.float.get_mut_ref().containing_width = containing_block_width;
}
// Assign `clear` now so that the assign-heights pass will have the correct value for // Block formatting contexts are never impacted by floats.
// it. if self.is_block_formatting_context(false) {
self.base.clear = self.box_.style().Box.get().clear; self.base.flags.set_impacted_by_left_floats(false);
self.base.flags.set_impacted_by_right_floats(false);
}
// Move in from the left border edge // Move in from the left border edge
let left_content_edge = self.box_.border_box.origin.x + self.box_.border_padding.left; let left_content_edge = self.box_.border_box.origin.x + self.box_.border_padding.left;
@ -1546,18 +1518,24 @@ impl Flow for BlockFlow {
self.propagate_assigned_width_to_children(left_content_edge, content_width, None); self.propagate_assigned_width_to_children(left_content_edge, content_width, None);
} }
/// This is called on kid flows by a parent. /// Assigns heights in-order; or, if this is a float, places the float. The default
/// implementation simply assigns heights if this flow is impacted by floats. Returns true if
/// this child was impacted by floats or false otherwise.
/// ///
/// Hence, we can assume that assign_height has already been called on the /// This is called on child flows by the parent. Hence, we can assume that `assign_height` has
/// kid (because of the bottom-up traversal). /// already been called on the child (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { fn assign_height_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext)
-> bool {
if self.is_float() { if self.is_float() {
debug!("assign_height_inorder_float: assigning height for float"); self.place_float();
self.assign_height_float_inorder(); return true
} else {
debug!("assign_height_inorder: assigning height for block");
self.assign_height_block_base(ctx, true, MarginsMayCollapse);
} }
let impacted = self.base.flags.impacted_by_floats();
if impacted {
self.assign_height(layout_context);
}
impacted
} }
fn assign_height(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
@ -1569,13 +1547,77 @@ impl Flow for BlockFlow {
self.assign_height_float(ctx); self.assign_height_float(ctx);
} else { } else {
debug!("assign_height: assigning height for block"); debug!("assign_height: assigning height for block");
// This is the only case in which a block flow can start an inorder self.assign_height_block_base(ctx, MarginsMayCollapse);
// subtraversal. }
if self.is_root() && self.base.num_floats > 0 { }
self.assign_height_inorder(ctx);
return; fn compute_absolute_position(&mut self) {
if self.is_absolutely_positioned() {
self.base
.absolute_position_info
.absolute_containing_block_position = if self.is_fixed() {
// The viewport is initially at (0, 0).
self.base.position.origin
} else {
// Absolute position of the containing block + position of absolute flow w/r/t the
// containing block.
self.base.absolute_position_info.absolute_containing_block_position +
self.base.position.origin
};
// Set the absolute position, which will be passed down later as part
// of containing block details for absolute descendants.
self.base.abs_position =
self.base.absolute_position_info.absolute_containing_block_position;
}
// For relatively-positioned descendants, the containing block formed by a block is just
// the content box. The containing block for absolutely-positioned descendants, on the
// other hand, is only established if we are positioned.
let relative_offset =
self.box_.relative_position(&self.base
.absolute_position_info
.relative_containing_block_size,
None);
if self.is_positioned() {
self.base.absolute_position_info.absolute_containing_block_position =
self.base.abs_position +
self.generated_containing_block_rect().origin +
relative_offset
};
let float_offset = if self.is_float() {
self.float.get_ref().rel_pos
} else {
Zero::zero()
};
// Compute absolute position info for children.
let mut absolute_position_info = self.base.absolute_position_info;
absolute_position_info.relative_containing_block_size = self.box_.content_box().size;
absolute_position_info.layers_needed_for_positioned_flows =
self.base.flags.layers_needed_for_descendants();
// Process children.
let this_position = self.base.abs_position;
for kid in self.base.child_iter() {
if !kid.is_absolutely_positioned() {
let kid_base = flow::mut_base(kid);
kid_base.abs_position = this_position + kid_base.position.origin +
relative_offset + float_offset;
kid_base.absolute_position_info = absolute_position_info
}
}
// Process absolute descendant links.
for absolute_descendant_link in self.base.abs_descendants.iter() {
match absolute_descendant_link.resolve() {
Some(absolute_descendant) => {
flow::mut_base(absolute_descendant).absolute_position_info =
absolute_position_info
}
None => fail!("empty Rawlink to a descendant")
} }
self.assign_height_block_base(ctx, false, MarginsMayCollapse);
} }
} }

View file

@ -7,25 +7,26 @@
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType}; use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
use layout::flow::Flow; use layout::flow::Flow;
use layout::inline::InlineFragmentContext; use layout::flow;
use layout::inline::{InlineFragmentContext, InlineMetrics};
use layout::model::{Auto, IntrinsicWidths, MaybeAuto, Specified, specified}; use layout::model::{Auto, IntrinsicWidths, MaybeAuto, Specified, specified};
use layout::model; use layout::model;
use layout::util::OpaqueNodeMethods; use layout::text;
use layout::util::{OpaqueNodeMethods, ToGfxColor};
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode}; use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
use sync::Arc;
use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use geom::approxeq::ApproxEq; use geom::approxeq::ApproxEq;
use gfx::color::rgb; use gfx::color::rgb;
use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem}; use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass}; use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass};
use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList, ImageDisplayItem};
use gfx::display_list::{LineDisplayItemClass, OpaqueNode, SolidColorDisplayItem}; use gfx::display_list::{ImageDisplayItemClass, LineDisplayItem};
use gfx::display_list::{SolidColorDisplayItemClass, StackingContext, TextDecorations}; use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass};
use gfx::display_list::{TextDisplayItem, TextDisplayItemClass}; use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel};
use gfx::display_list::{TextDecorations, TextDisplayItem, TextDisplayItemClass};
use gfx::font::FontStyle; use gfx::font::FontStyle;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
@ -34,17 +35,18 @@ use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::range::*; use servo_util::range::*;
use servo_util::namespace; use servo_util::namespace;
use servo_util::smallvec::{SmallVec, SmallVec0}; use servo_util::smallvec::SmallVec;
use servo_util::str::is_whitespace; use servo_util::str::is_whitespace;
use std::cast; use std::cast;
use std::from_str::FromStr; use std::from_str::FromStr;
use std::iter::AdditiveIterator; use std::iter::AdditiveIterator;
use std::mem;
use std::num::Zero; use std::num::Zero;
use style::{ComputedValues, TElement, TNode, cascade, initial_values}; use style::{ComputedValues, TElement, TNode, cascade, initial_values};
use style::computed_values::{LengthOrPercentageOrAuto, overflow, LPA_Auto, background_attachment}; use style::computed_values::{LengthOrPercentageOrAuto, overflow, LPA_Auto, background_attachment};
use style::computed_values::{background_repeat, border_style, clear, font_family, line_height}; use style::computed_values::{background_repeat, border_style, clear, position, text_align};
use style::computed_values::{position, text_align, text_decoration, vertical_align, visibility}; use style::computed_values::{text_decoration, vertical_align, visibility, white_space};
use style::computed_values::{white_space}; use sync::Arc;
use url::Url; use url::Url;
/// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In /// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In
@ -434,13 +436,7 @@ impl Box {
} }
pub fn calculate_line_height(&self, font_size: Au) -> Au { pub fn calculate_line_height(&self, font_size: Au) -> Au {
let from_inline = match self.style().InheritedBox.get().line_height { text::line_height_from_style(self.style(), font_size)
line_height::Normal => font_size.scale_by(1.14),
line_height::Number(l) => font_size.scale_by(l),
line_height::Length(l) => l
};
let minimum = self.style().InheritedBox.get()._servo_minimum_line_height;
Au::max(from_inline, minimum)
} }
/// Returns the sum of the widths of all the borders of this fragment. This is private because /// Returns the sum of the widths of all the borders of this fragment. This is private because
@ -575,32 +571,9 @@ impl Box {
} }
} }
/// Converts this node's computed style to a font style used for rendering. /// Converts this fragment's computed style to a font style used for rendering.
///
/// FIXME(pcwalton): This should not be necessary; just make the font part of style sharable
/// with the display list somehow. (Perhaps we should use an ARC.)
pub fn font_style(&self) -> FontStyle { pub fn font_style(&self) -> FontStyle {
let my_style = self.style(); text::computed_style_to_font_style(self.style())
debug!("(font style) start");
// FIXME: Too much allocation here.
let font_families = my_style.Font.get().font_family.iter().map(|family| {
match *family {
font_family::FamilyName(ref name) => (*name).clone(),
}
}).collect();
debug!("(font style) font families: `{:?}`", font_families);
let font_size = my_style.Font.get().font_size.to_f64().unwrap() / 60.0;
debug!("(font style) font size: `{:f}px`", font_size);
FontStyle {
pt_size: font_size,
weight: my_style.Font.get().font_weight,
style: my_style.Font.get().font_style,
families: font_families,
}
} }
#[inline(always)] #[inline(always)]
@ -658,7 +631,8 @@ impl Box {
/// necessary. /// necessary.
pub fn build_display_list_for_background_if_applicable(&self, pub fn build_display_list_for_background_if_applicable(&self,
list: &mut DisplayList, list: &mut DisplayList,
builder: &DisplayListBuilder, layout_context: &LayoutContext,
level: StackingLevel,
absolute_bounds: &Rect<Au>) { absolute_bounds: &Rect<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not // 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 // needed. We could use display list optimization to clean this up, but it still seems
@ -668,10 +642,7 @@ impl Box {
let background_color = style.resolve_color(style.Background.get().background_color); let background_color = style.resolve_color(style.Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) { if !background_color.alpha.approx_eq(&0.0) {
let display_item = ~SolidColorDisplayItem { let display_item = ~SolidColorDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(*absolute_bounds, self.node, level),
bounds: *absolute_bounds,
node: self.node,
},
color: background_color.to_gfx_color(), color: background_color.to_gfx_color(),
}; };
@ -681,25 +652,34 @@ impl Box {
// The background image is painted on top of the background color. // The background image is painted on top of the background color.
// Implements background image, per spec: // Implements background image, per spec:
// http://www.w3.org/TR/CSS21/colors.html#background // http://www.w3.org/TR/CSS21/colors.html#background
match style.Background.get().background_image { let background = style.Background.get();
Some(ref image_url) => { let image_url = match background.background_image {
let mut holder = ImageHolder::new(image_url.clone(), None => return,
builder.ctx.image_cache.clone()); Some(ref image_url) => image_url,
match holder.get_image() { };
Some(image) => {
let mut holder = ImageHolder::new(image_url.clone(), layout_context.image_cache.clone());
let image = match holder.get_image() {
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"); debug!("(building display list) building background image");
// Adjust bounds for `background-position` and `background-attachment`. // Adjust bounds for `background-position` and `background-attachment`.
let mut bounds = *absolute_bounds; let mut bounds = *absolute_bounds;
let horizontal_position = model::specified( let horizontal_position = model::specified(background.background_position.horizontal,
style.Background.get().background_position.horizontal,
bounds.size.width); bounds.size.width);
let vertical_position = model::specified( let vertical_position = model::specified(background.background_position.vertical,
style.Background.get().background_position.vertical,
bounds.size.height); bounds.size.height);
let clip_display_item; let clip_display_item;
match style.Background.get().background_attachment { match background.background_attachment {
background_attachment::scroll => { background_attachment::scroll => {
clip_display_item = None; clip_display_item = None;
bounds.origin.x = bounds.origin.x + horizontal_position; bounds.origin.x = bounds.origin.x + horizontal_position;
@ -709,12 +689,8 @@ impl Box {
} }
background_attachment::fixed => { background_attachment::fixed => {
clip_display_item = Some(~ClipDisplayItem { clip_display_item = Some(~ClipDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(bounds, self.node, level),
bounds: bounds, children: DisplayList::new(),
node: self.node,
},
child_list: SmallVec0::new(),
need_clip: true,
}); });
bounds = Rect { bounds = Rect {
@ -724,8 +700,9 @@ impl Box {
} }
} }
} }
// Adjust sizes for `background-repeat`. // Adjust sizes for `background-repeat`.
match style.Background.get().background_repeat { match background.background_repeat {
background_repeat::no_repeat => { background_repeat::no_repeat => {
bounds.size.width = Au::from_px(image.width as int); bounds.size.width = Au::from_px(image.width as int);
bounds.size.height = Au::from_px(image.height as int) bounds.size.height = Au::from_px(image.height as int)
@ -739,13 +716,9 @@ impl Box {
background_repeat::repeat => {} background_repeat::repeat => {}
}; };
// Create the image display item. // Create the image display item.
let image_display_item = ImageDisplayItemClass(~ImageDisplayItem { let image_display_item = ImageDisplayItemClass(~ImageDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(bounds, self.node, level),
bounds: bounds,
node: self.node,
},
image: image.clone(), image: image.clone(),
stretch_size: Size2D(Au::from_px(image.width as int), stretch_size: Size2D(Au::from_px(image.width as int),
Au::from_px(image.height as int)), Au::from_px(image.height as int)),
@ -754,28 +727,18 @@ impl Box {
match clip_display_item { match clip_display_item {
None => list.push(image_display_item), None => list.push(image_display_item),
Some(mut clip_display_item) => { Some(mut clip_display_item) => {
clip_display_item.child_list.push(image_display_item); clip_display_item.children.push(image_display_item);
list.push(ClipDisplayItemClass(clip_display_item)) list.push(ClipDisplayItemClass(clip_display_item))
} }
} }
} }
None => {
// No image data at all? Do nothing.
//
// TODO: Add some kind of placeholder background image.
debug!("(building display list) no background image :(");
}
}
}
None => {}
}
}
/// Adds the display items necessary to paint the borders of this box to a display list if /// Adds the display items necessary to paint the borders of this box to a display list if
/// necessary. /// necessary.
pub fn build_display_list_for_borders_if_applicable(&self, pub fn build_display_list_for_borders_if_applicable(&self,
list: &mut DisplayList, list: &mut DisplayList,
abs_bounds: &Rect<Au>, abs_bounds: &Rect<Au>,
level: StackingLevel,
inline_fragment_context: inline_fragment_context:
Option<InlineFragmentContext>) { Option<InlineFragmentContext>) {
// Fast path. // Fast path.
@ -792,10 +755,7 @@ impl Box {
// Append the border to the display list. // Append the border to the display list.
let border_display_item = ~BorderDisplayItem { let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(*abs_bounds, self.node, level),
bounds: *abs_bounds,
node: self.node,
},
border: border, border: border,
color: SideOffsets2D::new(top_color.to_gfx_color(), color: SideOffsets2D::new(top_color.to_gfx_color(),
right_color.to_gfx_color(), right_color.to_gfx_color(),
@ -811,7 +771,7 @@ impl Box {
} }
fn build_debug_borders_around_text_boxes(&self, fn build_debug_borders_around_text_boxes(&self,
stacking_context: &mut StackingContext, display_list: &mut DisplayList,
flow_origin: Point2D<Au>, flow_origin: Point2D<Au>,
text_box: &ScannedTextBoxInfo) { text_box: &ScannedTextBoxInfo) {
let box_bounds = self.border_box; let box_bounds = self.border_box;
@ -821,16 +781,12 @@ impl Box {
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1)); let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
let border_display_item = ~BorderDisplayItem { let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(absolute_box_bounds, self.node, ContentStackingLevel),
bounds: absolute_box_bounds,
node: self.node,
},
border: debug_border, border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid) style: SideOffsets2D::new_all_same(border_style::solid)
}; };
stacking_context.content.push(BorderDisplayItemClass(border_display_item)); display_list.push(BorderDisplayItemClass(border_display_item));
// Draw a rectangle representing the baselines. // Draw a rectangle representing the baselines.
let ascent = text_box.run.metrics_for_range(&text_box.range).ascent; let ascent = text_box.run.metrics_for_range(&text_box.range).ascent;
@ -838,19 +794,15 @@ impl Box {
Size2D(absolute_box_bounds.size.width, Au(0))); Size2D(absolute_box_bounds.size.width, Au(0)));
let line_display_item = ~LineDisplayItem { let line_display_item = ~LineDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel),
bounds: baseline,
node: self.node,
},
color: rgb(0, 200, 0), color: rgb(0, 200, 0),
style: border_style::dashed, style: border_style::dashed,
}; };
stacking_context.content.push(LineDisplayItemClass(line_display_item)) display_list.push(LineDisplayItemClass(line_display_item));
} }
fn build_debug_borders_around_box(&self, fn build_debug_borders_around_box(&self,
stacking_context: &mut StackingContext, display_list: &mut DisplayList,
flow_origin: Point2D<Au>) { flow_origin: Point2D<Au>) {
let box_bounds = self.border_box; let box_bounds = self.border_box;
let absolute_box_bounds = box_bounds.translate(&flow_origin); let absolute_box_bounds = box_bounds.translate(&flow_origin);
@ -859,34 +811,30 @@ impl Box {
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1)); let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
let border_display_item = ~BorderDisplayItem { let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(absolute_box_bounds, self.node, ContentStackingLevel),
bounds: absolute_box_bounds,
node: self.node,
},
border: debug_border, border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid) style: SideOffsets2D::new_all_same(border_style::solid)
}; };
stacking_context.content.push(BorderDisplayItemClass(border_display_item)) display_list.push(BorderDisplayItemClass(border_display_item))
} }
/// Adds the display items for this box to the given stacking context. /// Adds the display items for this box to the given stacking context.
/// ///
/// Arguments: /// Arguments:
/// ///
/// * `stacking_context`: The stacking context to add display items to. /// * `display_list`: The unflattened display list to add display items to.
/// * `builder`: The display list builder, which manages the coordinate system and options. /// * `layout_context`: The layout context.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. /// * `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. /// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
/// box. /// box.
pub fn build_display_list(&mut self, pub fn build_display_list(&self,
stacking_context: &mut StackingContext, display_list: &mut DisplayList,
builder: &DisplayListBuilder, layout_context: &LayoutContext,
_: &DisplayListBuildingInfo,
flow_origin: Point2D<Au>, flow_origin: Point2D<Au>,
background_and_border_level: BackgroundAndBorderLevel, background_and_border_level: BackgroundAndBorderLevel,
inline_fragment_context: Option<InlineFragmentContext>) { inline_fragment_context: Option<InlineFragmentContext>)
-> ChildDisplayListAccumulator {
// Box position wrt to the owning flow. // Box position wrt to the owning flow.
let box_bounds = self.border_box; let box_bounds = self.border_box;
let absolute_box_bounds = box_bounds.translate(&flow_origin); let absolute_box_bounds = box_bounds.translate(&flow_origin);
@ -894,36 +842,49 @@ impl Box {
box_bounds, box_bounds,
absolute_box_bounds, absolute_box_bounds,
self.debug_str()); self.debug_str());
debug!("Box::build_display_list: dirty={}, flow_origin={}", builder.dirty, flow_origin); debug!("Box::build_display_list: dirty={}, flow_origin={}",
layout_context.dirty,
flow_origin);
let mut accumulator = ChildDisplayListAccumulator::new(self.style(),
absolute_box_bounds,
self.node,
ContentStackingLevel);
if self.style().InheritedBox.get().visibility != visibility::visible { if self.style().InheritedBox.get().visibility != visibility::visible {
return return accumulator
} }
if !absolute_box_bounds.intersects(&builder.dirty) { if !absolute_box_bounds.intersects(&layout_context.dirty) {
debug!("Box::build_display_list: Did not intersect..."); debug!("Box::build_display_list: Did not intersect...");
return return accumulator
} }
debug!("Box::build_display_list: intersected. Adding display item..."); debug!("Box::build_display_list: intersected. Adding display item...");
{ {
let list = let level =
stacking_context.list_for_background_and_border_level(background_and_border_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 = ~BaseDisplayItem::new(absolute_box_bounds, self.node, level);
display_list.push(PseudoDisplayItemClass(base_display_item));
// Add the background to the list, if applicable. // Add the background to the list, if applicable.
self.build_display_list_for_background_if_applicable(list, self.build_display_list_for_background_if_applicable(display_list,
builder, layout_context,
level,
&absolute_box_bounds); &absolute_box_bounds);
// Add a border, if applicable. // Add a border, if applicable.
// //
// TODO: Outlines. // TODO: Outlines.
self.build_display_list_for_borders_if_applicable(list, self.build_display_list_for_borders_if_applicable(display_list,
&absolute_box_bounds, &absolute_box_bounds,
level,
inline_fragment_context); inline_fragment_context);
} }
// Add a clip, if applicable.
match self.specific { match self.specific {
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."), UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
TableColumnBox(_) => fail!("Shouldn't see table column boxes here."), TableColumnBox(_) => fail!("Shouldn't see table column boxes here."),
@ -949,41 +910,27 @@ impl Box {
// Create the text box. // Create the text box.
let text_display_item = ~TextDisplayItem { let text_display_item = ~TextDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(bounds, self.node, ContentStackingLevel),
bounds: bounds,
node: self.node,
},
text_run: text_box.run.clone(), text_run: text_box.run.clone(),
range: text_box.range, range: text_box.range,
text_color: text_color, text_color: text_color,
text_decorations: text_decorations, text_decorations: text_decorations,
}; };
accumulator.push(display_list, TextDisplayItemClass(text_display_item));
stacking_context.content.push(TextDisplayItemClass(text_display_item));
// Draw debug frames for text bounds. // Draw debug frames for text bounds.
// //
// FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure. // FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure.
// We should have a real `SERVO_DEBUG` system. // We should have a real `SERVO_DEBUG` system.
debug!("{:?}", self.build_debug_borders_around_text_boxes(stacking_context, debug!("{:?}", self.build_debug_borders_around_text_boxes(display_list,
flow_origin, flow_origin,
text_box)) text_box))
}, },
GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox | GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox |
TableWrapperBox => { TableWrapperBox => {
let item = ~ClipDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
node: self.node,
},
child_list: SmallVec0::new(),
need_clip: self.needs_clip()
};
stacking_context.content.push(ClipDisplayItemClass(item));
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system. // should have a real `SERVO_DEBUG` system.
debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin)) debug!("{:?}", self.build_debug_borders_around_box(display_list, flow_origin))
}, },
ImageBox(_) => { ImageBox(_) => {
let mut bounds = absolute_box_bounds.clone(); let mut bounds = absolute_box_bounds.clone();
@ -993,23 +940,22 @@ impl Box {
bounds.size.height = bounds.size.height - self.border_padding.vertical(); bounds.size.height = bounds.size.height - self.border_padding.vertical();
match self.specific { match self.specific {
ImageBox(ref mut image_box) => { ImageBox(ref image_box) => {
let image_ref = &mut image_box.image; let image_ref = &image_box.image;
match image_ref.get_image() { match image_ref.get_image_if_present() {
Some(image) => { Some(image) => {
debug!("(building display list) building image box"); debug!("(building display list) building image box");
// Place the image into the display list. // Place the image into the display list.
let image_display_item = ~ImageDisplayItem { let image_display_item = ~ImageDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem::new(bounds,
bounds: bounds, self.node,
node: self.node, ContentStackingLevel),
},
image: image.clone(), image: image.clone(),
stretch_size: bounds.size, stretch_size: bounds.size,
}; };
stacking_context.content accumulator.push(display_list,
.push(ImageDisplayItemClass(image_display_item)) ImageDisplayItemClass(image_display_item))
} }
None => { None => {
// No image data at all? Do nothing. // No image data at all? Do nothing.
@ -1024,7 +970,7 @@ impl Box {
// FIXME(pcwalton): This is a bit of an abuse of the logging // FIXME(pcwalton): This is a bit of an abuse of the logging
// infrastructure. We should have a real `SERVO_DEBUG` system. // infrastructure. We should have a real `SERVO_DEBUG` system.
debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin)) debug!("{:?}", self.build_debug_borders_around_box(display_list, flow_origin))
} }
} }
@ -1040,10 +986,12 @@ impl Box {
// iframe is actually going to be displayed. // iframe is actually going to be displayed.
match self.specific { match self.specific {
IframeBox(ref iframe_box) => { IframeBox(ref iframe_box) => {
self.finalize_position_and_size_of_iframe(iframe_box, flow_origin, builder.ctx) self.finalize_position_and_size_of_iframe(iframe_box, flow_origin, layout_context)
} }
_ => {} _ => {}
} }
accumulator
} }
/// Returns the intrinsic widths of this fragment. /// Returns the intrinsic widths of this fragment.
@ -1120,6 +1068,9 @@ impl Box {
} }
ScannedTextBox(ref text_box_info) => { ScannedTextBox(ref text_box_info) => {
// Compute the height based on the line-height and font size. // Compute the height based on the line-height and font size.
//
// FIXME(pcwalton): Shouldn't we use the value of the `font-size` property below
// instead of the bounding box of the text run?
let (range, run) = (&text_box_info.range, &text_box_info.run); let (range, run) = (&text_box_info.range, &text_box_info.run);
let text_bounds = run.metrics_for_range(range).bounding_box; let text_bounds = run.metrics_for_range(range).bounding_box;
let em_size = text_bounds.size.height; let em_size = text_bounds.size.height;
@ -1401,6 +1352,34 @@ impl Box {
} }
} }
/// Calculates height above baseline, depth below baseline, and ascent for this fragment when
/// used in an inline formatting context. See CSS 2.1 § 10.8.1.
pub fn inline_metrics(&self) -> InlineMetrics {
match self.specific {
ImageBox(ref image_box_info) => {
let computed_height = image_box_info.computed_height();
InlineMetrics {
height_above_baseline: computed_height + self.border_padding.vertical(),
depth_below_baseline: Au(0),
ascent: computed_height + self.border_padding.bottom,
}
}
ScannedTextBox(ref text_box) => {
// See CSS 2.1 § 10.8.1.
let font_size = self.style().Font.get().font_size;
let line_height = self.calculate_line_height(font_size);
InlineMetrics::from_font_metrics(&text_box.run.font_metrics, line_height)
}
_ => {
InlineMetrics {
height_above_baseline: self.border_box.size.height,
depth_below_baseline: Au(0),
ascent: self.border_box.size.height,
}
}
}
}
/// Returns true if this box can merge with another adjacent box or false otherwise. /// Returns true if this box can merge with another adjacent box or false otherwise.
pub fn can_merge_with_box(&self, other: &Box) -> bool { pub fn can_merge_with_box(&self, other: &Box) -> bool {
match (&self.specific, &other.specific) { match (&self.specific, &other.specific) {
@ -1484,3 +1463,60 @@ impl Box {
chan.send(msg) chan.send(msg)
} }
} }
/// An object that accumulates display lists of child flows, applying a clipping rect if necessary.
pub struct ChildDisplayListAccumulator {
clip_display_item: Option<~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)
-> ChildDisplayListAccumulator {
ChildDisplayListAccumulator {
clip_display_item: match style.Box.get().overflow {
overflow::hidden => {
Some(~ClipDisplayItem {
base: BaseDisplayItem::new(bounds, node, level),
children: DisplayList::new(),
})
}
_ => 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 display list
/// associated with the given flow, along with the items in the given display list.
pub fn finish(self, parent: &mut Flow, mut display_list: DisplayList) {
let ChildDisplayListAccumulator {
clip_display_item
} = self;
match clip_display_item {
None => {}
Some(clip_display_item) => display_list.push(ClipDisplayItemClass(clip_display_item)),
}
flow::mut_base(parent).display_list = display_list
}
}

View file

@ -396,7 +396,9 @@ impl<'a> FlowConstructor<'a> {
} }
} }
let mut inline_flow = ~InlineFlow::from_boxes((*node).clone(), boxes) as ~Flow:Share; let mut inline_flow = ~InlineFlow::from_boxes((*node).clone(), boxes);
inline_flow.compute_minimum_ascent_and_descent(self.font_context(), &**node.style());
let mut inline_flow = inline_flow as ~Flow:Share;
TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow); TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow);
inline_flow.finish(self.layout_context); inline_flow.finish(self.layout_context);

View file

@ -6,6 +6,7 @@
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use gfx::font_context::{FontContext, FontContextInfo}; use gfx::font_context::{FontContext, FontContextInfo};
@ -87,6 +88,9 @@ pub struct LayoutContext {
/// The command line options. /// The command line options.
pub opts: Opts, pub opts: Opts,
/// The dirty rectangle, used during display list building.
pub dirty: Rect<Au>,
} }
#[cfg(not(target_os="android"))] #[cfg(not(target_os="android"))]

View file

@ -1,52 +0,0 @@
/* 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/. */
//! Constructs display lists from boxes.
use layout::context::LayoutContext;
use geom::{Point2D, Rect, Size2D};
use gfx::render_task::RenderLayer;
use gfx;
use servo_util::geometry::Au;
use servo_util::smallvec::SmallVec0;
use style;
/// Manages the information needed to construct the display list.
pub struct DisplayListBuilder<'a> {
pub ctx: &'a LayoutContext,
/// A list of render layers that we've built up, root layer not included.
pub layers: SmallVec0<RenderLayer>,
/// The dirty rect.
pub dirty: Rect<Au>,
}
/// Information needed at each step of the display list building traversal.
pub struct DisplayListBuildingInfo {
/// The size of the containing block for relatively-positioned descendants.
pub relative_containing_block_size: Size2D<Au>,
/// The position and size of the absolute containing block.
pub absolute_containing_block_position: Point2D<Au>,
/// Whether the absolute containing block forces positioned descendants to be layerized.
pub layers_needed_for_positioned_flows: bool,
}
//
// Miscellaneous useful routines
//
/// Allows a CSS color to be converted into a graphics color.
pub trait ToGfxColor {
/// Converts a CSS color to a graphics color.
fn to_gfx_color(&self) -> gfx::color::Color;
}
impl ToGfxColor for style::computed_values::RGBA {
fn to_gfx_color(&self) -> gfx::color::Color {
gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
}
}

View file

@ -30,7 +30,6 @@ use layout::block::BlockFlow;
use layout::box_::{Box, TableRowBox, TableCellBox}; use layout::box_::{Box, TableRowBox, TableCellBox};
use layout::construct::OptVector; use layout::construct::OptVector;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::Floats; use layout::floats::Floats;
use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator}; use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
@ -48,14 +47,18 @@ use layout::table_cell::TableCellFlow;
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use collections::Deque; use collections::Deque;
use collections::dlist::DList;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use gfx::display_list::StackingContext; use geom::size::Size2D;
use gfx::display_list::DisplayList;
use gfx::render_task::RenderLayer;
use servo_msg::compositor_msg::LayerId; use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::smallvec::{SmallVec, SmallVec0}; use servo_util::smallvec::{SmallVec, SmallVec0};
use std::cast; use std::cast;
use std::iter::Zip; use std::iter::Zip;
use std::num::Zero;
use std::sync::atomics::Relaxed; use std::sync::atomics::Relaxed;
use std::slice::MutItems; use std::slice::MutItems;
use style::computed_values::{clear, position, text_align}; use style::computed_values::{clear, position, text_align};
@ -74,6 +77,7 @@ pub trait Flow {
/// If this is a block flow, returns the underlying object. Fails otherwise. /// If this is a block flow, returns the underlying object. Fails otherwise.
fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow { fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
debug!("called as_block() on a flow of type {}", self.class());
fail!("called as_block() on a non-block flow") fail!("called as_block() on a non-block flow")
} }
@ -144,6 +148,11 @@ pub trait Flow {
// Main methods // Main methods
/// Pass 1 of reflow: computes minimum and preferred widths. /// Pass 1 of reflow: computes minimum and preferred widths.
///
/// Recursively (bottom-up) determine the flow's minimum and preferred widths. When called on
/// this flow, all child flows have had their minimum and preferred widths set. This function
/// must decide minimum/preferred widths based on its children's widths and the dimensions of
/// any boxes it is responsible for flowing.
fn bubble_widths(&mut self, _ctx: &mut LayoutContext) { fn bubble_widths(&mut self, _ctx: &mut LayoutContext) {
fail!("bubble_widths not yet implemented") fail!("bubble_widths not yet implemented")
} }
@ -158,9 +167,32 @@ pub trait Flow {
fail!("assign_height not yet implemented") fail!("assign_height not yet implemented")
} }
/// In-order version of pass 3a of reflow: computes heights with floats present. /// Assigns heights in-order; or, if this is a float, places the float. The default
fn assign_height_inorder(&mut self, _ctx: &mut LayoutContext) { /// implementation simply assigns heights if this flow is impacted by floats. Returns true if
fail!("assign_height_inorder not yet implemented") /// this child was impacted by floats or false otherwise.
fn assign_height_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext)
-> bool {
let impacted = base(self).flags.impacted_by_floats();
if impacted {
self.assign_height(layout_context);
}
impacted
}
/// Phase 4 of reflow: computes absolute positions.
fn compute_absolute_position(&mut self) {
// The default implementation is a no-op.
}
/// Returns the direction that this flow clears floats in, if any.
fn float_clearance(&self) -> clear::T {
clear::none
}
/// Returns true if this float is a block formatting context and false otherwise. The default
/// implementation returns false.
fn is_block_formatting_context(&self, _only_impactable_by_floats: bool) -> bool {
false
} }
fn compute_collapsible_top_margin(&mut self, fn compute_collapsible_top_margin(&mut self,
@ -355,11 +387,8 @@ pub trait MutableFlowUtils {
/// Computes the overflow region for this flow. /// Computes the overflow region for this flow.
fn store_overflow(self, _: &mut LayoutContext); fn store_overflow(self, _: &mut LayoutContext);
/// Builds the display lists for this flow and its descendants. /// Builds the display lists for this flow.
fn build_display_list(self, fn build_display_list(self, layout_context: &LayoutContext);
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo);
/// Destroys the flow. /// Destroys the flow.
fn destroy(self); fn destroy(self);
@ -387,6 +416,7 @@ pub trait MutableOwnedFlowUtils {
fn destroy(&mut self); fn destroy(&mut self);
} }
#[deriving(Eq, Show)]
pub enum FlowClass { pub enum FlowClass {
BlockFlowClass, BlockFlowClass,
InlineFlowClass, InlineFlowClass,
@ -436,6 +466,26 @@ pub trait PostorderFlowTraversal {
#[deriving(Clone)] #[deriving(Clone)]
pub struct FlowFlags(pub u8); pub struct FlowFlags(pub u8);
/// The bitmask of flags that represent the `has_left_floated_descendants` and
/// `has_right_floated_descendants` fields.
///
/// NB: If you update this field, you must update the bitfields below.
static HAS_FLOATED_DESCENDANTS_BITMASK: u8 = 0b0000_0011;
// Whether this flow has descendants that float left in the same block formatting context.
bitfield!(FlowFlags, has_left_floated_descendants, set_has_left_floated_descendants, 0b0000_0001)
// Whether this flow has descendants that float right in the same block formatting context.
bitfield!(FlowFlags, has_right_floated_descendants, set_has_right_floated_descendants, 0b0000_0010)
// Whether this flow is impacted by floats to the left in the same block formatting context (i.e.
// its height depends on some prior flows with `float: left`).
bitfield!(FlowFlags, impacted_by_left_floats, set_impacted_by_left_floats, 0b0000_0100)
// Whether this flow is impacted by floats to the right in the same block formatting context (i.e.
// its height depends on some prior flows with `float: right`).
bitfield!(FlowFlags, impacted_by_right_floats, set_impacted_by_right_floats, 0b0000_1000)
/// The bitmask of flags that represent the text alignment field. /// The bitmask of flags that represent the text alignment field.
/// ///
/// NB: If you update this field, you must update the bitfields below. /// NB: If you update this field, you must update the bitfields below.
@ -446,9 +496,6 @@ static TEXT_ALIGN_BITMASK: u8 = 0b0011_0000;
/// NB: If you update this field, you must update the bitfields below. /// NB: If you update this field, you must update the bitfields below.
static TEXT_ALIGN_SHIFT: u8 = 4; static TEXT_ALIGN_SHIFT: u8 = 4;
// Whether we need an in-order traversal.
bitfield!(FlowFlags, inorder, set_inorder, 0b0000_0001)
// Whether this flow contains a flow that has its own layer within the same absolute containing // Whether this flow contains a flow that has its own layer within the same absolute containing
// block. // block.
bitfield!(FlowFlags, bitfield!(FlowFlags,
@ -492,6 +539,18 @@ impl FlowFlags {
let FlowFlags(pff) = parent; let FlowFlags(pff) = parent;
*self = FlowFlags(ff | (pff & TEXT_ALIGN_BITMASK)) *self = FlowFlags(ff | (pff & TEXT_ALIGN_BITMASK))
} }
#[inline]
pub fn union_floated_descendants_flags(&mut self, other: FlowFlags) {
let FlowFlags(my_flags) = *self;
let FlowFlags(other_flags) = other;
*self = FlowFlags(my_flags | (other_flags & HAS_FLOATED_DESCENDANTS_BITMASK))
}
#[inline]
pub fn impacted_by_floats(&self) -> bool {
self.impacted_by_left_floats() || self.impacted_by_right_floats()
}
} }
/// The Descendants of a flow. /// The Descendants of a flow.
@ -548,6 +607,31 @@ pub type DescendantIter<'a> = MutItems<'a, Rawlink>;
pub type DescendantOffsetIter<'a> = Zip<MutItems<'a, Rawlink>, MutItems<'a, Au>>; pub type DescendantOffsetIter<'a> = Zip<MutItems<'a, Rawlink>, MutItems<'a, Au>>;
/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
/// confused with absolutely-positioned flows).
pub struct AbsolutePositionInfo {
/// The size of the containing block for relatively-positioned descendants.
pub relative_containing_block_size: Size2D<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`.
pub layers_needed_for_positioned_flows: bool,
}
impl AbsolutePositionInfo {
pub fn new() -> AbsolutePositionInfo {
// FIXME(pcwalton): The initial relative containing block size should be equal to the size
// of the root layer.
AbsolutePositionInfo {
relative_containing_block_size: Size2D::zero(),
absolute_containing_block_position: Zero::zero(),
layers_needed_for_positioned_flows: false,
}
}
}
/// Data common to all flows. /// Data common to all flows.
pub struct BaseFlow { pub struct BaseFlow {
pub restyle_damage: RestyleDamage, pub restyle_damage: RestyleDamage,
@ -584,16 +668,6 @@ pub struct BaseFlow {
/// The floats next to this flow. /// The floats next to this flow.
pub floats: Floats, pub floats: Floats,
/// The value of this flow's `clear` property, if any.
pub clear: clear::T,
/// For normal flows, this is the number of floated descendants that are
/// not contained within any other floated descendant of this flow. For
/// floats, it is 1.
/// It is used to allocate float data if necessary and to
/// decide whether to do an in-order traversal for assign_height.
pub num_floats: uint,
/// The collapsible margins for this flow, if any. /// The collapsible margins for this flow, if any.
pub collapsible_margins: CollapsibleMargins, pub collapsible_margins: CollapsibleMargins,
@ -614,6 +688,18 @@ pub struct BaseFlow {
/// Reference to the Containing Block, if this flow is absolutely positioned. /// Reference to the Containing Block, if this flow is absolutely positioned.
pub absolute_cb: ContainingBlockLink, pub absolute_cb: ContainingBlockLink,
/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
/// confused with absolutely-positioned flows).
///
/// FIXME(pcwalton): Merge with `absolute_static_x_offset` and `fixed_static_x_offset` above?
pub absolute_position_info: AbsolutePositionInfo,
/// The unflattened display items for this flow.
pub display_list: DisplayList,
/// Any layers that we're bubbling up, in a linked list.
pub layers: DList<RenderLayer>,
/// Whether this flow has been destroyed. /// Whether this flow has been destroyed.
/// ///
/// TODO(pcwalton): Pack this into the flags? Need to be careful because manipulation of this /// TODO(pcwalton): Pack this into the flags? Need to be careful because manipulation of this
@ -650,14 +736,15 @@ impl BaseFlow {
parallel: FlowParallelInfo::new(), parallel: FlowParallelInfo::new(),
floats: Floats::new(), floats: Floats::new(),
num_floats: 0,
collapsible_margins: CollapsibleMargins::new(), collapsible_margins: CollapsibleMargins::new(),
clear: clear::none,
abs_position: Point2D(Au::new(0), Au::new(0)), abs_position: Point2D(Au::new(0), Au::new(0)),
abs_descendants: Descendants::new(), abs_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0), absolute_static_x_offset: Au::new(0),
fixed_static_x_offset: Au::new(0), fixed_static_x_offset: Au::new(0),
absolute_cb: ContainingBlockLink::new(), absolute_cb: ContainingBlockLink::new(),
display_list: DisplayList::new(),
layers: DList::new(),
absolute_position_info: AbsolutePositionInfo::new(),
destroyed: false, destroyed: false,
@ -931,48 +1018,28 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
/// ///
/// Arguments: /// Arguments:
/// ///
/// * `stacking_context`: The parent stacking context that this flow belongs to and to which
/// display items will be added.
///
/// * `builder`: The display list builder, which contains information used during the entire /// * `builder`: The display list builder, which contains information used during the entire
/// display list building pass. /// display list building pass.
/// ///
/// * `info`: Per-flow display list building information. /// * `info`: Per-flow display list building information.
fn build_display_list(self, fn build_display_list(self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
debug!("Flow: building display list"); debug!("Flow: building display list");
match self.class() { match self.class() {
BlockFlowClass => { BlockFlowClass => self.as_block().build_display_list_block(layout_context),
self.as_block().build_display_list_block(stacking_context, builder, info) InlineFlowClass => self.as_inline().build_display_list_inline(layout_context),
}
InlineFlowClass => {
self.as_inline().build_display_list_inline(stacking_context, builder, info)
}
TableWrapperFlowClass => { TableWrapperFlowClass => {
self.as_table_wrapper().build_display_list_table_wrapper(stacking_context, self.as_table_wrapper().build_display_list_table_wrapper(layout_context)
builder,
info)
}
TableFlowClass => {
self.as_table().build_display_list_table(stacking_context, builder, info)
} }
TableFlowClass => self.as_table().build_display_list_table(layout_context),
TableRowGroupFlowClass => { TableRowGroupFlowClass => {
self.as_table_rowgroup().build_display_list_table_rowgroup(stacking_context, self.as_table_rowgroup().build_display_list_table_rowgroup(layout_context)
builder,
info)
}
TableRowFlowClass => {
self.as_table_row().build_display_list_table_row(stacking_context, builder, info)
} }
TableRowFlowClass => self.as_table_row().build_display_list_table_row(layout_context),
TableCaptionFlowClass => { TableCaptionFlowClass => {
self.as_table_caption().build_display_list_table_caption(stacking_context, self.as_table_caption().build_display_list_table_caption(layout_context)
builder,
info)
} }
TableCellFlowClass => { TableCellFlowClass => {
self.as_table_cell().build_display_list_table_cell(stacking_context, builder, info) self.as_table_cell().build_display_list_table_cell(layout_context)
} }
TableColGroupFlowClass => { TableColGroupFlowClass => {
// Nothing to do here, as column groups don't render. // Nothing to do here, as column groups don't render.
@ -1001,6 +1068,7 @@ impl MutableOwnedFlowUtils for ~Flow:Share {
let base = mut_base(*self); let base = mut_base(*self);
base.children.push_back(new_child); base.children.push_back(new_child);
let _ = base.parallel.children_count.fetch_add(1, Relaxed); 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 boxes may be added to it. /// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it.
@ -1025,6 +1093,10 @@ impl MutableOwnedFlowUtils for ~Flow:Share {
let self_link = Rawlink::some(*self); let self_link = Rawlink::some(*self);
let block = self.as_block(); let block = self.as_block();
block.base.abs_descendants = abs_descendants; 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() { for descendant_link in block.base.abs_descendants.iter() {
match descendant_link.resolve() { match descendant_link.resolve() {
@ -1066,6 +1138,10 @@ impl ContainingBlockLink {
self.link = link self.link = link
} }
pub unsafe fn resolve(&mut self) -> Option<&mut Flow> {
self.link.resolve()
}
#[inline] #[inline]
pub fn generated_containing_block_rect(&mut self) -> Rect<Au> { pub fn generated_containing_block_rect(&mut self) -> Rect<Au> {
self.link.resolve().unwrap().generated_containing_block_rect() self.link.resolve().unwrap().generated_containing_block_rect()

View file

@ -3,21 +3,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, ScannedTextBox}; use layout::box_::{Box, CannotSplit, SplitDidFit, SplitDidNotFit};
use layout::box_::{SplitDidFit, SplitDidNotFit, TableBox, TableCellBox, TableColumnBox};
use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{FloatLeft, Floats, PlacementInfo}; use layout::floats::{FloatLeft, Floats, PlacementInfo};
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass}; use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use layout::flow; use layout::flow;
use layout::model::IntrinsicWidths; use layout::model::IntrinsicWidths;
use layout::model; use layout::model;
use layout::text;
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf}; use collections::{Deque, RingBuf};
use geom::{Point2D, Rect, SideOffsets2D, Size2D}; use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use gfx::display_list::{ContentLevel, StackingContext}; use gfx::display_list::ContentLevel;
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::range::Range; use servo_util::range::Range;
@ -573,6 +573,14 @@ pub struct InlineFlow {
/// are the result of inline layout. This also includes some metadata used for positioning /// are the result of inline layout. This also includes some metadata used for positioning
/// lines. /// lines.
pub lines: SmallVec0<LineBox>, pub lines: SmallVec0<LineBox>,
/// The minimum height above the baseline for each line, as specified by the line height and
/// font style.
pub minimum_height_above_baseline: Au,
/// The minimum depth below the baseline for each line, as specified by the line height and
/// font style.
pub minimum_depth_below_baseline: Au,
} }
impl InlineFlow { impl InlineFlow {
@ -581,6 +589,8 @@ impl InlineFlow {
base: BaseFlow::new(node), base: BaseFlow::new(node),
boxes: boxes, boxes: boxes,
lines: SmallVec0::new(), lines: SmallVec0::new(),
minimum_height_above_baseline: Au(0),
minimum_depth_below_baseline: Au(0),
} }
} }
@ -591,12 +601,9 @@ impl InlineFlow {
self.boxes = InlineBoxes::new(); self.boxes = InlineBoxes::new();
} }
pub fn build_display_list_inline(&mut self, pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &DisplayListBuilder,
info: &DisplayListBuildingInfo) {
let abs_rect = Rect(self.base.abs_position, self.base.position.size); let abs_rect = Rect(self.base.abs_position, self.base.position.size);
if !abs_rect.intersects(&builder.dirty) { if !abs_rect.intersects(&layout_context.dirty) {
return return
} }
@ -605,14 +612,15 @@ impl InlineFlow {
debug!("Flow: building display list for {:u} inline boxes", self.boxes.len()); debug!("Flow: building display list for {:u} inline boxes", self.boxes.len());
for (fragment, context) in self.boxes.mut_iter() { for (fragment, context) in self.boxes.mut_iter() {
let rel_offset = fragment.relative_position(&info.relative_containing_block_size, let rel_offset = fragment.relative_position(&self.base
.absolute_position_info
.relative_containing_block_size,
Some(context)); Some(context));
fragment.build_display_list(stacking_context, drop(fragment.build_display_list(&mut self.base.display_list,
builder, layout_context,
info,
self.base.abs_position + rel_offset, self.base.abs_position + rel_offset,
ContentLevel, ContentLevel,
Some(context)); Some(context)));
} }
// TODO(#225): Should `inline-block` elements have flows as children of the inline flow or // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
@ -621,71 +629,72 @@ impl InlineFlow {
// For now, don't traverse the subtree rooted here. // For now, don't traverse the subtree rooted here.
} }
/// Returns the relative offset from the baseline for this box, taking into account the value /// Returns the distance from the baseline for the logical top left corner of this fragment,
/// of the CSS `vertical-align` property. /// taking into account the value of the CSS `vertical-align` property. Negative values mean
/// "toward the logical top" and positive values mean "toward the logical bottom".
/// ///
/// The extra boolean is set if and only if `biggest_top` and/or `biggest_bottom` were updated. /// The extra boolean is set if and only if `biggest_top` and/or `biggest_bottom` were updated.
/// That is, if the box has a `top` or `bottom` value, true is returned. /// That is, if the box has a `top` or `bottom` value, true is returned.
fn relative_offset_from_baseline(cur_box: &Box, fn distance_from_baseline(fragment: &Box,
ascent: Au, ascent: Au,
parent_text_top: Au, parent_text_top: Au,
parent_text_bottom: Au, parent_text_bottom: Au,
top_from_base: &mut Au, height_above_baseline: &mut Au,
bottom_from_base: &mut Au, depth_below_baseline: &mut Au,
biggest_top: &mut Au, largest_height_for_top_fragments: &mut Au,
biggest_bottom: &mut Au) largest_height_for_bottom_fragments: &mut Au)
-> (Au, bool) { -> (Au, bool) {
match cur_box.vertical_align() { match fragment.vertical_align() {
vertical_align::baseline => (-ascent, false), vertical_align::baseline => (-ascent, false),
vertical_align::middle => { vertical_align::middle => {
// TODO: x-height value should be used from font info. // TODO: x-height value should be used from font info.
let xheight = Au::new(0); let xheight = Au(0);
(-(xheight + cur_box.content_height()).scale_by(0.5), false) (-(xheight + fragment.content_height()).scale_by(0.5), false)
}, },
vertical_align::sub => { vertical_align::sub => {
// TODO: The proper position for subscripts should be used. // TODO: The proper position for subscripts should be used. Lower the baseline to
// Lower the baseline to the proper position for subscripts // the proper position for subscripts.
let sub_offset = Au::new(0); let sub_offset = Au(0);
(sub_offset - ascent, false) (sub_offset - ascent, false)
}, },
vertical_align::super_ => { vertical_align::super_ => {
// TODO: The proper position for superscripts should be used. // TODO: The proper position for superscripts should be used. Raise the baseline to
// Raise the baseline to the proper position for superscripts // the proper position for superscripts.
let super_offset = Au::new(0); let super_offset = Au(0);
(-super_offset - ascent, false) (-super_offset - ascent, false)
}, },
vertical_align::text_top => { vertical_align::text_top => {
let box_height = *top_from_base + *bottom_from_base; let box_height = *height_above_baseline + *depth_below_baseline;
let prev_bottom_from_base = *bottom_from_base; let prev_depth_below_baseline = *depth_below_baseline;
*top_from_base = parent_text_top; *height_above_baseline = parent_text_top;
*bottom_from_base = box_height - *top_from_base; *depth_below_baseline = box_height - *height_above_baseline;
(*bottom_from_base - prev_bottom_from_base - ascent, false) (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
}, },
vertical_align::text_bottom => { vertical_align::text_bottom => {
let box_height = *top_from_base + *bottom_from_base; let box_height = *height_above_baseline + *depth_below_baseline;
let prev_bottom_from_base = *bottom_from_base; let prev_depth_below_baseline = *depth_below_baseline;
*bottom_from_base = parent_text_bottom; *depth_below_baseline = parent_text_bottom;
*top_from_base = box_height - *bottom_from_base; *height_above_baseline = box_height - *depth_below_baseline;
(*bottom_from_base - prev_bottom_from_base - ascent, false) (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
}, },
vertical_align::top => { vertical_align::top => {
if *biggest_top < (*top_from_base + *bottom_from_base) { *largest_height_for_top_fragments =
*biggest_top = *top_from_base + *bottom_from_base; Au::max(*largest_height_for_top_fragments,
} *height_above_baseline + *depth_below_baseline);
let offset_top = *top_from_base - ascent; let offset_top = *height_above_baseline - ascent;
(offset_top, true) (offset_top, true)
}, },
vertical_align::bottom => { vertical_align::bottom => {
if *biggest_bottom < (*top_from_base + *bottom_from_base) { *largest_height_for_bottom_fragments =
*biggest_bottom = *top_from_base + *bottom_from_base; Au::max(*largest_height_for_bottom_fragments,
} *height_above_baseline + *depth_below_baseline);
let offset_bottom = -(*bottom_from_base + ascent); let offset_bottom = -(*depth_below_baseline + ascent);
(offset_bottom, true) (offset_bottom, true)
}, },
vertical_align::Length(length) => (-(length + ascent), false), vertical_align::Length(length) => (-(length + ascent), false),
vertical_align::Percentage(p) => { vertical_align::Percentage(p) => {
let pt_size = cur_box.font_style().pt_size; let pt_size = fragment.font_style().pt_size;
let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size)); let line_height = fragment.calculate_line_height(Au::from_pt(pt_size));
let percent_offset = line_height.scale_by(p); let percent_offset = line_height.scale_by(p);
(-(percent_offset + ascent), false) (-(percent_offset + ascent), false)
} }
@ -718,6 +727,21 @@ impl InlineFlow {
offset_x = offset_x + size.width; offset_x = offset_x + size.width;
} }
} }
/// Computes the minimum ascent and descent for each line. This is done during flow
/// construction.
///
/// `style` is the style of the block.
pub fn compute_minimum_ascent_and_descent(&mut self,
font_context: &mut FontContext,
style: &ComputedValues) {
let font_style = text::computed_style_to_font_style(style);
let font_metrics = text::font_metrics_for_style(font_context, &font_style);
let line_height = text::line_height_from_style(style, style.Font.get().font_size);
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
self.minimum_height_above_baseline = inline_metrics.height_above_baseline;
self.minimum_depth_below_baseline = inline_metrics.depth_below_baseline;
}
} }
impl Flow for InlineFlow { impl Flow for InlineFlow {
@ -734,12 +758,8 @@ impl Flow for InlineFlow {
} }
fn bubble_widths(&mut self, _: &mut LayoutContext) { fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut num_floats = 0;
for kid in self.base.child_iter() { for kid in self.base.child_iter() {
let child_base = flow::mut_base(kid); flow::mut_base(kid).floats = Floats::new();
num_floats += child_base.num_floats;
child_base.floats = Floats::new();
} }
let mut intrinsic_widths = IntrinsicWidths::new(); let mut intrinsic_widths = IntrinsicWidths::new();
@ -754,7 +774,6 @@ impl Flow for InlineFlow {
} }
self.base.intrinsic_widths = intrinsic_widths; self.base.intrinsic_widths = intrinsic_widths;
self.base.num_floats = num_floats;
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@ -786,16 +805,7 @@ impl Flow for InlineFlow {
// 'inline-block' box that created this flow before recursing. // 'inline-block' box that created this flow before recursing.
} }
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { /// Calculate and set the height of this flow. See CSS 2.1 § 10.6.1.
for kid in self.base.child_iter() {
kid.assign_height_inorder(ctx);
}
self.assign_height(ctx);
}
/// Calculate and set the height of this Flow.
///
/// CSS Section 10.6.1
fn assign_height(&mut self, _: &mut LayoutContext) { fn assign_height(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow"); debug!("assign_height_inline: assigning height for flow");
@ -817,85 +827,42 @@ impl Flow for InlineFlow {
let scanner_floats = self.base.floats.clone(); let scanner_floats = self.base.floats.clone();
let mut scanner = LineboxScanner::new(scanner_floats); let mut scanner = LineboxScanner::new(scanner_floats);
// Access the linebox scanner.
scanner.scan_for_lines(self); scanner.scan_for_lines(self);
let mut line_height_offset = Au::new(0);
// All lines use text alignment of the flow. // All lines use text alignment of the flow.
let text_align = self.base.flags.text_align(); let text_align = self.base.flags.text_align();
// Now, go through each line and lay out the boxes inside. // Now, go through each line and lay out the boxes inside.
let mut line_distance_from_flow_top = Au(0);
for line in self.lines.mut_iter() { for line in self.lines.mut_iter() {
// Lay out boxes horizontally. // Lay out boxes horizontally.
InlineFlow::set_horizontal_box_positions(&mut self.boxes, line, text_align); InlineFlow::set_horizontal_box_positions(&mut self.boxes, line, text_align);
// Set the top y position of the current line box. // Set the top y position of the current line box.
// `line_height_offset` is updated at the end of the previous loop. // `line_height_offset` is updated at the end of the previous loop.
line.bounds.origin.y = line.bounds.origin.y + line_height_offset; line.bounds.origin.y = line_distance_from_flow_top;
// Calculate the distance from baseline to the top and bottom of the linebox. // Calculate the distance from the baseline to the top and bottom of the line box.
let (mut topmost, mut bottommost) = (Au(0), Au(0)); let mut largest_height_above_baseline = self.minimum_height_above_baseline;
// Calculate the biggest height among boxes with 'top' and 'bottom' values let mut largest_depth_below_baseline = self.minimum_depth_below_baseline;
// Calculate the largest height among boxes with 'top' and 'bottom' values
// respectively. // respectively.
let (mut biggest_top, mut biggest_bottom) = (Au(0), Au(0)); let (mut largest_height_for_top_fragments, mut largest_height_for_bottom_fragments) =
(Au(0), Au(0));
for box_i in line.range.eachi() { for box_i in line.range.eachi() {
let cur_box = self.boxes.boxes.get_mut(box_i); let fragment = self.boxes.boxes.get_mut(box_i);
let top = cur_box.border_padding.top; let InlineMetrics {
height_above_baseline: mut height_above_baseline,
depth_below_baseline: mut depth_below_baseline,
ascent
} = fragment.inline_metrics();
// FIXME(pcwalton): Move into `box.rs` like the rest of box-specific layout code? // To calculate text-top and text-bottom value when `vertical-align` is involved,
let (top_from_base, bottom_from_base, ascent) = match cur_box.specific { // we should find the top and bottom of the content area of the parent box.
ImageBox(_) => { // "Content area" is defined in CSS 2.1 § 10.6.1.
let mut height = cur_box.content_height();
// TODO: margin, border, padding's top and bottom should be calculated in
// advance, since baseline of image is bottom margin edge.
let bottom = cur_box.border_padding.bottom;
let noncontent_height = top + bottom;
height = height + noncontent_height;
let ascent = height + bottom;
(height, Au::new(0), ascent)
},
ScannedTextBox(ref text_box) => {
let range = &text_box.range;
let run = &text_box.run;
// Compute the height based on the line-height and font size
let text_bounds = run.metrics_for_range(range).bounding_box;
let em_size = text_bounds.size.height;
let line_height = cur_box.calculate_line_height(em_size);
// Find the top and bottom of the content area.
// Those are used in text-top and text-bottom value of 'vertical-align'
let text_ascent = text_box.run.font_metrics.ascent;
// Offset from the top of the box is 1/2 of the leading + ascent
let text_offset = text_ascent + (line_height - em_size).scale_by(0.5);
text_bounds.translate(&Point2D(cur_box.border_box.origin.x, Au(0)));
(text_offset, line_height - text_offset, text_ascent)
},
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
TableWrapperBox => {
let height = cur_box.border_box.size.height;
(height, Au::new(0), height)
},
TableColumnBox(_) => fail!("Table column boxes do not have height"),
UnscannedTextBox(_) => {
fail!("Unscanned text boxes should have been scanned by now.")
}
};
let mut top_from_base = top_from_base;
let mut bottom_from_base = bottom_from_base;
// To calculate text-top and text-bottom value of 'vertical-align',
// we should find the top and bottom of the content area of parent box.
// The content area is defined in:
// http://www.w3.org/TR/CSS2/visudet.html#inline-non-replaced
// //
// TODO: We should extract em-box info from the font size of the parent and // TODO: We should extract em-box info from the font size of the parent and
// calculate the distances from the baseline to the top and the bottom of the // calculate the distances from the baseline to the top and the bottom of the
@ -904,74 +871,84 @@ impl Flow for InlineFlow {
// We should calculate the distance from baseline to the top of parent's content // We should calculate the distance from baseline to the top of parent's content
// area. But for now we assume it's the font size. // area. But for now we assume it's the font size.
// //
// The spec does not state which font to use. Previous versions of the code used // CSS 2.1 does not state which font to use. Previous versions of the code used
// the parent's font; this code uses the current font. // the parent's font; this code uses the current font.
let parent_text_top = cur_box.style().Font.get().font_size; let parent_text_top = fragment.style().Font.get().font_size;
// We should calculate the distance from baseline to the bottom of the parent's // We should calculate the distance from baseline to the bottom of the parent's
// content area. But for now we assume it's zero. // content area. But for now we assume it's zero.
let parent_text_bottom = Au::new(0); let parent_text_bottom = Au(0);
// Calculate a relative offset from the baseline. // Calculate the final height above the baseline for this box.
// //
// The no-update flag decides whether `biggest_top` and `biggest_bottom` are // The no-update flag decides whether `largest_height_for_top_fragments` and
// updated or not. That is, if the box has a `top` or `bottom` value, // `largest_height_for_bottom_fragments` are to be updated or not. This will be set
// `no_update_flag` becomes true. // if and only if the fragment has `vertical-align` set to `top` or `bottom`.
let (offset, no_update_flag) = let (distance_from_baseline, no_update_flag) =
InlineFlow::relative_offset_from_baseline(cur_box, InlineFlow::distance_from_baseline(
fragment,
ascent, ascent,
parent_text_top, parent_text_top,
parent_text_bottom, parent_text_bottom,
&mut top_from_base, &mut height_above_baseline,
&mut bottom_from_base, &mut depth_below_baseline,
&mut biggest_top, &mut largest_height_for_top_fragments,
&mut biggest_bottom); &mut largest_height_for_bottom_fragments);
// If the current box has 'top' or 'bottom' value, no_update_flag is true. // Unless the current fragment has `vertical-align` set to `top` or `bottom`,
// Otherwise, topmost and bottomost are updated. // `largest_height_above_baseline` and `largest_depth_below_baseline` are updated.
if !no_update_flag && top_from_base > topmost { if !no_update_flag {
topmost = top_from_base; largest_height_above_baseline = Au::max(height_above_baseline,
} largest_height_above_baseline);
if !no_update_flag && bottom_from_base > bottommost { largest_depth_below_baseline = Au::max(depth_below_baseline,
bottommost = bottom_from_base; largest_depth_below_baseline);
} }
cur_box.border_box.origin.y = line.bounds.origin.y + offset + top; // Temporarily use `fragment.border_box.origin.y` to mean "the distance from the
// baseline". We will assign the real value later.
fragment.border_box.origin.y = distance_from_baseline
} }
// Calculate the distance from baseline to the top of the biggest box with 'bottom' // Calculate the distance from the baseline to the top of the largest box with a
// value. Then, if necessary, update the topmost. // value for `bottom`. Then, if necessary, update `largest_height_above_baseline`.
let topmost_of_bottom = biggest_bottom - bottommost; largest_height_above_baseline =
if topmost_of_bottom > topmost { Au::max(largest_height_above_baseline,
topmost = topmost_of_bottom; largest_height_for_bottom_fragments - largest_depth_below_baseline);
}
// Calculate the distance from baseline to the bottom of the biggest box with 'top' // Calculate the distance from baseline to the bottom of the largest box with a value
// value. Then, if necessary, update the bottommost. // for `top`. Then, if necessary, update `largest_depth_below_baseline`.
let bottommost_of_top = biggest_top - topmost; largest_depth_below_baseline =
if bottommost_of_top > bottommost { Au::max(largest_depth_below_baseline,
bottommost = bottommost_of_top; largest_height_for_top_fragments - largest_height_above_baseline);
}
// Now, the baseline offset from the top of linebox is set as topmost. // Now, the distance from the logical top of the line box to the baseline can be
let baseline_offset = topmost; // computed as `largest_height_above_baseline`.
let baseline_distance_from_top = largest_height_above_baseline;
// All boxes' y position is updated following the new baseline offset. // Compute the final positions in the block direction of each fragment. Recall that
// `fragment.border_box.origin.y` was set to the distance from the baseline above.
for box_i in line.range.eachi() { for box_i in line.range.eachi() {
let cur_box = self.boxes.get_mut(box_i); let fragment = self.boxes.get_mut(box_i);
let adjust_offset = match cur_box.vertical_align() { match fragment.vertical_align() {
vertical_align::top => Au::new(0), vertical_align::top => {
vertical_align::bottom => baseline_offset + bottommost, fragment.border_box.origin.y = fragment.border_box.origin.y +
_ => baseline_offset, line_distance_from_flow_top
}; }
vertical_align::bottom => {
cur_box.border_box.origin.y = cur_box.border_box.origin.y + adjust_offset; fragment.border_box.origin.y = fragment.border_box.origin.y +
line_distance_from_flow_top + baseline_distance_from_top +
largest_depth_below_baseline
}
_ => {
fragment.border_box.origin.y = fragment.border_box.origin.y +
line_distance_from_flow_top + baseline_distance_from_top
}
}
} }
// This is used to set the top y position of the next line box in the next loop. // This is used to set the top y position of the next line box in the next loop.
line_height_offset = line_height_offset + topmost + bottommost - line.bounds.size.height = largest_height_above_baseline + largest_depth_below_baseline;
line.bounds.size.height; line_distance_from_flow_top = line_distance_from_flow_top + line.bounds.size.height;
line.bounds.size.height = topmost + bottommost;
} // End of `lines.each` loop. } // End of `lines.each` loop.
self.base.position.size.height = self.base.position.size.height =
@ -998,6 +975,12 @@ impl Flow for InlineFlow {
} }
} }
struct FragmentFixupWorkItem {
style: Arc<ComputedValues>,
new_start_index: uint,
old_end_index: uint,
}
/// Information that inline flows keep about a single nested element. This is used to recover the /// Information that inline flows keep about a single nested element. This is used to recover the
/// DOM structure from the flat box list when it's needed. /// DOM structure from the flat box list when it's needed.
pub struct FragmentRange { pub struct FragmentRange {
@ -1028,12 +1011,6 @@ impl FragmentRange {
} }
} }
struct FragmentFixupWorkItem {
style: Arc<ComputedValues>,
new_start_index: uint,
old_end_index: uint,
}
/// The type of an iterator over fragment ranges in the fragment map. /// The type of an iterator over fragment ranges in the fragment map.
pub struct RangeIterator<'a> { pub struct RangeIterator<'a> {
iter: Items<'a,FragmentRange>, iter: Items<'a,FragmentRange>,
@ -1237,3 +1214,24 @@ impl<'a> InlineFragmentContext<'a> {
} }
} }
/// Height above the baseline, depth below the baseline, and ascent for a fragment. See CSS 2.1 §
/// 10.8.1.
pub struct InlineMetrics {
pub height_above_baseline: Au,
pub depth_below_baseline: Au,
pub ascent: Au,
}
impl InlineMetrics {
/// Calculates inline metrics from font metrics and line height per CSS 2.1 § 10.8.1.
#[inline]
pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
let leading = line_height - (font_metrics.ascent + font_metrics.descent);
InlineMetrics {
height_above_baseline: font_metrics.ascent + leading.scale_by(0.5),
depth_below_baseline: font_metrics.descent + leading.scale_by(0.5),
ascent: font_metrics.ascent,
}
}
}

View file

@ -11,21 +11,21 @@ use css::select::new_stylist;
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::construct::{FlowConstructionResult, NoConstructionResult}; use layout::construct::{FlowConstructionResult, NoConstructionResult};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow; use layout::flow;
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use layout::parallel::PaddedUnsafeFlow; use layout::parallel::PaddedUnsafeFlow;
use layout::parallel; use layout::parallel;
use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods}; use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use collections::dlist::DList;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList}; use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem};
use gfx::display_list::{OpaqueNode, StackingContext}; use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode};
use gfx::font_context::{FontContext, FontContextInfo}; use gfx::font_context::{FontContext, FontContextInfo};
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer}; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use gfx::{render_task, color}; use gfx::{render_task, color};
@ -48,7 +48,7 @@ use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::opts::Opts; use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec1}; use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use servo_util::task::send_on_failure; use servo_util::task::send_on_failure;
@ -107,7 +107,11 @@ pub struct LayoutTask {
/// The channel on which messages can be sent to the profiler. /// The channel on which messages can be sent to the profiler.
pub profiler_chan: ProfilerChan, pub profiler_chan: ProfilerChan,
pub opts: Opts /// The command-line options.
pub opts: Opts,
/// The dirty rect. Used during display list construction.
pub dirty: Rect<Au>,
} }
/// The damage computation traversal. /// The damage computation traversal.
@ -225,7 +229,31 @@ impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
#[inline] #[inline]
fn should_process(&mut self, flow: &mut Flow) -> bool { fn should_process(&mut self, flow: &mut Flow) -> bool {
!flow::base(flow).flags.inorder() !flow::base(flow).flags.impacted_by_floats()
}
}
/// The display list construction traversal.
pub struct BuildDisplayListTraversal<'a> {
layout_context: &'a LayoutContext,
}
impl<'a> BuildDisplayListTraversal<'a> {
#[inline]
fn process(&mut self, flow: &mut Flow) {
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.resolve().unwrap())
}
flow.build_display_list(self.layout_context)
} }
} }
@ -319,7 +347,8 @@ impl LayoutTask {
initial_css_values: Arc::new(style::initial_values()), initial_css_values: Arc::new(style::initial_values()),
parallel_traversal: parallel_traversal, parallel_traversal: parallel_traversal,
profiler_chan: profiler_chan, profiler_chan: profiler_chan,
opts: opts.clone() opts: opts.clone(),
dirty: Rect::zero(),
} }
} }
@ -349,6 +378,7 @@ impl LayoutTask {
url: (*url).clone(), url: (*url).clone(),
reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root), reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root),
opts: self.opts.clone(), opts: self.opts.clone(),
dirty: Rect::zero(),
} }
} }
@ -634,23 +664,26 @@ impl LayoutTask {
// Build the display list if necessary, and send it to the renderer. // Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay { if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || { profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
let mut root_stacking_context = StackingContext::new(); layout_ctx.dirty = flow::base(layout_root).position.clone();
let mut display_list_builder = DisplayListBuilder {
ctx: &layout_ctx,
layers: SmallVec0::new(),
dirty: flow::base(layout_root).position.clone(),
};
let display_list_building_info = DisplayListBuildingInfo {
relative_containing_block_size: flow::base(layout_root).position.size,
absolute_containing_block_position: Point2D(Au(0), Au(0)),
layers_needed_for_positioned_flows: false,
};
layout_root.build_display_list(&mut root_stacking_context, match self.parallel_traversal {
&mut display_list_builder, None => {
&display_list_building_info); let mut traversal = BuildDisplayListTraversal {
layout_context: &layout_ctx,
};
traversal.process(layout_root);
}
Some(ref mut traversal) => {
parallel::build_display_list_for_subtree(&mut layout_root,
self.profiler_chan.clone(),
&mut layout_ctx,
traversal);
}
}
let display_list = Arc::new(root_stacking_context.flatten()); let root_display_list = mem::replace(&mut flow::mut_base(layout_root).display_list,
DisplayList::new());
let display_list = Arc::new(root_display_list.flatten(ContentStackingLevel));
// FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
// it with extreme prejudice. // it with extreme prejudice.
@ -677,12 +710,9 @@ impl LayoutTask {
} }
} }
let root_size = Size2D(display_list_building_info.relative_containing_block_size let root_size = flow::base(layout_root).position.size;
.width let root_size = Size2D(root_size.width.to_nearest_px() as uint,
.to_nearest_px() as uint, root_size.height.to_nearest_px() as uint);
display_list_building_info.relative_containing_block_size
.height
.to_nearest_px() as uint);
let render_layer = RenderLayer { let render_layer = RenderLayer {
id: layout_root.layer_id(0), id: layout_root.layer_id(0),
display_list: display_list.clone(), display_list: display_list.clone(),
@ -693,13 +723,15 @@ impl LayoutTask {
self.display_list = Some(display_list.clone()); self.display_list = Some(display_list.clone());
// TODO(pcwalton): Eventually, when we have incremental reflow, this will have to
// be smarter in order to handle retained layer contents properly from reflow to
// reflow.
let mut layers = SmallVec1::new(); let mut layers = SmallVec1::new();
layers.push(render_layer); layers.push(render_layer);
let DisplayListBuilder { for layer in mem::replace(&mut flow::mut_base(layout_root).layers,
layers: sublayers, DList::new()).move_iter() {
.. layers.push(layer)
} = display_list_builder; }
layers.push_all_move(sublayers);
debug!("Layout done!"); debug!("Layout done!");
@ -726,7 +758,6 @@ impl LayoutTask {
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`. // need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
ContentBoxQuery(node, reply_chan) => { ContentBoxQuery(node, reply_chan) => {
let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node); let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>, fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>,
mut iter: DisplayItemIterator, mut iter: DisplayItemIterator,
node: OpaqueNode) { node: OpaqueNode) {
@ -774,28 +805,22 @@ impl LayoutTask {
reply_chan.send(ContentBoxesResponse(boxes)) reply_chan.send(ContentBoxesResponse(boxes))
} }
HitTestQuery(_, point, reply_chan) => { HitTestQuery(_, point, reply_chan) => {
fn hit_test(x: Au, y: Au, list: &[DisplayItem]) fn hit_test<'a,I:Iterator<&'a DisplayItem>>(x: Au, y: Au, mut iterator: I)
-> Option<HitTestResponse> { -> Option<HitTestResponse> {
for item in list.rev_iter() { for item in iterator {
match *item { match *item {
ClipDisplayItemClass(ref cc) => { ClipDisplayItemClass(ref cc) => {
if !cc.need_clip || geometry::rect_contains_point(cc.base.bounds, if geometry::rect_contains_point(cc.base.bounds, Point2D(x, y)) {
Point2D(x, y)) { let ret = hit_test(x, y, cc.children.list.rev_iter());
let ret = hit_test(x, y, cc.child_list.as_slice());
if !ret.is_none() { if !ret.is_none() {
return ret return ret
} }
} }
continue
} }
_ => {} _ => {}
} }
}
for item in list.rev_iter() {
match *item {
ClipDisplayItemClass(_) => continue,
_ => {}
}
let bounds = item.bounds(); let bounds = item.bounds();
// TODO(tikue): This check should really be performed by a method of // TODO(tikue): This check should really be performed by a method of
@ -816,7 +841,7 @@ impl LayoutTask {
Au::from_frac_px(point.y as f64)); Au::from_frac_px(point.y as f64));
let resp = match self.display_list { let resp = match self.display_list {
None => fail!("no display list!"), None => fail!("no display list!"),
Some(ref display_list) => hit_test(x, y, display_list.list.as_slice()), Some(ref display_list) => hit_test(x, y, display_list.list.rev_iter()),
}; };
if resp.is_some() { if resp.is_some() {
reply_chan.send(Ok(resp.unwrap())); reply_chan.send(Ok(resp.unwrap()));
@ -826,24 +851,22 @@ impl LayoutTask {
} }
MouseOverQuery(_, point, reply_chan) => { MouseOverQuery(_, point, reply_chan) => {
fn mouse_over_test(x: Au, fn mouse_over_test<'a,
I:Iterator<&'a DisplayItem>>(
x: Au,
y: Au, y: Au,
list: &[DisplayItem], mut iterator: I,
result: &mut Vec<UntrustedNodeAddress>) { result: &mut Vec<UntrustedNodeAddress>) {
for item in list.rev_iter() { for item in iterator {
match *item { match *item {
ClipDisplayItemClass(ref cc) => { ClipDisplayItemClass(ref cc) => {
mouse_over_test(x, y, cc.child_list.as_slice(), result); mouse_over_test(x, y, cc.children.list.rev_iter(), result);
} }
_ => {} _ => {
}
}
for item in list.rev_iter() {
let bounds = item.bounds(); let bounds = item.bounds();
// TODO(tikue): This check should really be performed by a method of // TODO(tikue): This check should really be performed by a method
// DisplayItem. // of DisplayItem.
if x < bounds.origin.x + bounds.size.width && if x < bounds.origin.x + bounds.size.width &&
bounds.origin.x <= x && bounds.origin.x <= x &&
y < bounds.origin.y + bounds.size.height && y < bounds.origin.y + bounds.size.height &&
@ -854,16 +877,17 @@ impl LayoutTask {
} }
} }
} }
}
}
let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!(); let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!();
let (x, y) = (Au::from_frac_px(point.x as f64), let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
Au::from_frac_px(point.y as f64));
match self.display_list { match self.display_list {
None => fail!("no display list!"), None => fail!("no display list!"),
Some(ref display_list) => { Some(ref display_list) => {
mouse_over_test(x, mouse_over_test(x,
y, y,
display_list.list.as_slice(), display_list.list.rev_iter(),
&mut mouse_over_list); &mut mouse_over_list);
} }
}; };

View file

@ -10,7 +10,7 @@ use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasS
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::extra::LayoutAuxMethods; use layout::extra::LayoutAuxMethods;
use layout::flow::{Flow, PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow; use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal}; use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal};
use layout::layout_task::{BubbleWidthsTraversal}; use layout::layout_task::{BubbleWidthsTraversal};
@ -105,6 +105,8 @@ impl DomParallelInfo {
pub struct FlowParallelInfo { pub struct FlowParallelInfo {
/// The number of children that still need work done. /// The number of children that still need work done.
pub children_count: AtomicInt, 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. /// The address of the parent flow.
pub parent: UnsafeFlow, pub parent: UnsafeFlow,
} }
@ -113,6 +115,7 @@ impl FlowParallelInfo {
pub fn new() -> FlowParallelInfo { pub fn new() -> FlowParallelInfo {
FlowParallelInfo { FlowParallelInfo {
children_count: AtomicInt::new(0), children_count: AtomicInt::new(0),
children_and_absolute_descendant_count: AtomicInt::new(0),
parent: null_unsafe_flow(), parent: null_unsafe_flow(),
} }
} }
@ -180,6 +183,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
unsafe_flow: UnsafeFlow, unsafe_flow: UnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>); proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>);
#[inline(always)]
fn run_parallel_helper(&mut self, fn run_parallel_helper(&mut self,
unsafe_flow: UnsafeFlow, unsafe_flow: UnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>, proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>,
@ -405,6 +409,117 @@ fn assign_heights_and_store_overflow(unsafe_flow: PaddedUnsafeFlow,
assign_heights_traversal.run_parallel(unsafe_flow.to_flow(), proxy) assign_heights_traversal.run_parallel(unsafe_flow.to_flow(), proxy)
} }
fn compute_absolute_position(unsafe_flow: PaddedUnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) {
let mut had_descendants = false;
unsafe {
// Get a real flow.
let flow: &mut ~Flow:Share = cast::transmute(&unsafe_flow);
// Compute the absolute position for the flow.
flow.compute_absolute_position();
// Count the number of absolutely-positioned children, so that we can subtract it from
// from `children_and_absolute_descendant_count` to get the number of real children.
let mut absolutely_positioned_child_count = 0;
for kid in flow::child_iter(*flow) {
if kid.is_absolutely_positioned() {
absolutely_positioned_child_count += 1;
}
}
// Don't enqueue absolutely positioned children.
drop(flow::mut_base(*flow).parallel
.children_and_absolute_descendant_count
.fetch_sub(absolutely_positioned_child_count as int, SeqCst));
// Possibly enqueue the children.
for kid in flow::child_iter(*flow) {
if !kid.is_absolutely_positioned() {
had_descendants = true;
proxy.push(WorkUnit {
fun: compute_absolute_position,
data: UnsafeFlowConversions::from_flow(&borrowed_flow_to_unsafe_flow(kid)),
});
}
}
// Possibly enqueue absolute descendants.
for absolute_descendant_link in flow::mut_base(*flow).abs_descendants.iter() {
had_descendants = true;
let descendant = absolute_descendant_link.resolve().unwrap();
proxy.push(WorkUnit {
fun: compute_absolute_position,
data: UnsafeFlowConversions::from_flow(&borrowed_flow_to_unsafe_flow(descendant)),
});
}
// If there were no more descendants, start building the display list.
if !had_descendants {
build_display_list(UnsafeFlowConversions::from_flow(
&mut_owned_flow_to_unsafe_flow(flow)),
proxy)
}
}
}
fn build_display_list(mut unsafe_flow: PaddedUnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) {
let layout_context: &mut LayoutContext = unsafe {
cast::transmute(*proxy.user_data())
};
loop {
unsafe {
// Get a real flow.
let flow: &mut ~Flow:Share = cast::transmute(&unsafe_flow);
// Build display lists.
flow.build_display_list(layout_context);
{
let base = flow::mut_base(*flow);
// 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);
}
// Possibly enqueue the parent.
let unsafe_parent = if flow.is_absolutely_positioned() {
mut_borrowed_flow_to_unsafe_flow(flow::mut_base(*flow).absolute_cb
.resolve()
.unwrap())
} else {
flow::mut_base(*flow).parallel.parent
};
if unsafe_parent == null_unsafe_flow() {
// We're done!
break
}
// No, we're not at the root yet. Then are we the last child
// of our parent to finish processing? If so, we can continue
// on with our parent; otherwise, we've gotta wait.
let parent: &mut ~Flow:Share = cast::transmute(&unsafe_parent);
let parent_base = flow::mut_base(*parent);
if parent_base.parallel
.children_and_absolute_descendant_count
.fetch_sub(1, SeqCst) == 1 {
// We were the last child of our parent. Build display lists for our parent.
unsafe_flow = UnsafeFlowConversions::from_flow(&unsafe_parent)
} else {
// Stop.
break
}
}
}
}
pub fn recalc_style_for_subtree(root_node: &LayoutNode, pub fn recalc_style_for_subtree(root_node: &LayoutNode,
layout_context: &mut LayoutContext, layout_context: &mut LayoutContext,
queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) { queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) {
@ -442,3 +557,24 @@ pub fn traverse_flow_tree_preorder(root: &mut ~Flow:Share,
queue.data = ptr::mut_null() queue.data = ptr::mut_null()
} }
pub fn build_display_list_for_subtree(root: &mut ~Flow:Share,
profiler_chan: ProfilerChan,
layout_context: &mut LayoutContext,
queue: &mut WorkQueue<*mut LayoutContext,PaddedUnsafeFlow>) {
unsafe {
queue.data = cast::transmute(layout_context)
}
profile(time::LayoutParallelWarmupCategory, profiler_chan, || {
queue.push(WorkUnit {
fun: compute_absolute_position,
data: UnsafeFlowConversions::from_flow(&mut_owned_flow_to_unsafe_flow(root)),
})
});
queue.run();
queue.data = ptr::mut_null()
}

View file

@ -9,14 +9,11 @@ use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::block::{WidthConstraintInput, WidthConstraintSolution}; use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::floats::FloatKind;
use layout::floats::{FloatKind};
use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow;
use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout}; use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use gfx::display_list::StackingContext;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use style::computed_values::table_layout; use style::computed_values::table_layout;
@ -132,16 +129,13 @@ impl TableFlow {
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) { fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext) {
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse); self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse);
} }
pub fn build_display_list_table(&mut self, pub fn build_display_list_table(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
debug!("build_display_list_table: same process as block flow"); debug!("build_display_list_table: same process as block flow");
self.block_flow.build_display_list_block(stacking_context, builder, info); self.block_flow.build_display_list_block(layout_context);
} }
} }
@ -178,7 +172,6 @@ impl Flow for TableFlow {
let mut min_width = Au(0); let mut min_width = Au(0);
let mut pref_width = Au(0); let mut pref_width = Au(0);
let mut did_first_row = false; let mut did_first_row = false;
let mut num_floats = 0;
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_proper_table_child()); assert!(kid.is_proper_table_child());
@ -238,12 +231,10 @@ impl Flow for TableFlow {
} }
} }
} }
let child_base = flow::mut_base(kid);
num_floats = num_floats + child_base.num_floats;
} }
self.block_flow.base.num_floats = num_floats;
self.block_flow.base.intrinsic_widths.minimum_width = min_width; self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width, pref_width); self.block_flow.base.intrinsic_widths.preferred_width =
geometry::max(min_width, pref_width);
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@ -295,18 +286,13 @@ impl Flow for TableFlow {
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone())); self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
} }
/// This is called on kid flows by a parent.
///
/// Hence, we can assume that assign_height has already been called on the
/// kid (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_inorder: assigning height for table");
self.assign_height_table_base(ctx, true);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table"); debug!("assign_height: assigning height for table");
self.assign_height_table_base(ctx, false); self.assign_height_table_base(ctx);
}
fn compute_absolute_position(&mut self) {
self.block_flow.compute_absolute_position()
} }
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {

View file

@ -7,12 +7,9 @@
use layout::block::BlockFlow; use layout::block::BlockFlow;
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCaptionFlowClass, FlowClass, Flow}; use layout::flow::{TableCaptionFlowClass, FlowClass, Flow};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use gfx::display_list::StackingContext;
/// A table formatting context. /// A table formatting context.
pub struct TableCaptionFlow { pub struct TableCaptionFlow {
pub block_flow: BlockFlow, pub block_flow: BlockFlow,
@ -31,12 +28,9 @@ impl TableCaptionFlow {
self.block_flow.teardown(); self.block_flow.teardown();
} }
pub fn build_display_list_table_caption(&mut self, pub fn build_display_list_table_caption(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_caption: same process as block flow"); debug!("build_display_list_table_caption: same process as block flow");
self.block_flow.build_display_list_block(stacking_context, builder, info) self.block_flow.build_display_list_block(layout_context)
} }
} }
@ -62,20 +56,15 @@ impl Flow for TableCaptionFlow {
self.block_flow.assign_widths(ctx); self.block_flow.assign_widths(ctx);
} }
/// This is called on kid flows by a parent.
///
/// Hence, we can assume that assign_height has already been called on the
/// kid (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_inorder: assigning height for table_caption");
self.block_flow.assign_height_inorder(ctx);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_caption"); debug!("assign_height: assigning height for table_caption");
self.block_flow.assign_height(ctx); self.block_flow.assign_height(ctx);
} }
fn compute_absolute_position(&mut self) {
self.block_flow.compute_absolute_position()
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = ~"TableCaptionFlow: "; let txt = ~"TableCaptionFlow: ";
txt.append(self.block_flow.box_.debug_str()) txt.append(self.block_flow.box_.debug_str())

View file

@ -7,13 +7,11 @@
use layout::box_::Box; use layout::box_::Box;
use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCellFlowClass, FlowClass, Flow}; use layout::flow::{TableCellFlowClass, FlowClass, Flow};
use layout::model::{MaybeAuto}; use layout::model::{MaybeAuto};
use layout::table::InternalTable; use layout::table::InternalTable;
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use gfx::display_list::StackingContext;
use servo_util::geometry::Au; use servo_util::geometry::Au;
/// A table formatting context. /// A table formatting context.
@ -23,9 +21,7 @@ pub struct TableCellFlow {
} }
impl TableCellFlow { impl TableCellFlow {
pub fn from_node_and_box(node: &ThreadSafeLayoutNode, pub fn from_node_and_box(node: &ThreadSafeLayoutNode, box_: Box) -> TableCellFlow {
box_: Box)
-> TableCellFlow {
TableCellFlow { TableCellFlow {
block_flow: BlockFlow::from_node_and_box(node, box_) block_flow: BlockFlow::from_node_and_box(node, box_)
} }
@ -50,18 +46,13 @@ impl TableCellFlow {
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_cell_base(&mut self, fn assign_height_table_cell_base(&mut self, layout_context: &mut LayoutContext) {
layout_context: &mut LayoutContext, self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse)
inorder: bool) {
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse)
} }
pub fn build_display_list_table_cell(&mut self, pub fn build_display_list_table_cell(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
debug!("build_display_list_table: same process as block flow"); debug!("build_display_list_table: same process as block flow");
self.block_flow.build_display_list_block(stacking_context, builder, info) self.block_flow.build_display_list_block(layout_context)
} }
} }
@ -114,18 +105,13 @@ impl Flow for TableCellFlow {
None); None);
} }
/// This is called on kid flows by a parent.
///
/// Hence, we can assume that assign_height has already been called on the
/// kid (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_inorder: assigning height for table_cell");
self.assign_height_table_cell_base(ctx, true);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_cell"); debug!("assign_height: assigning height for table_cell");
self.assign_height_table_cell_base(ctx, false); self.assign_height_table_cell_base(ctx);
}
fn compute_absolute_position(&mut self) {
self.block_flow.compute_absolute_position()
} }
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {

View file

@ -9,14 +9,12 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer; use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow; use layout::flow;
use layout::table::InternalTable; use layout::table::InternalTable;
use layout::model::{MaybeAuto, Specified, Auto}; use layout::model::{MaybeAuto, Specified, Auto};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use gfx::display_list::StackingContext;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
@ -81,7 +79,7 @@ impl TableRowFlow {
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) { fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext) {
let (top_offset, _, _) = self.initialize_offsets(); let (top_offset, _, _) = self.initialize_offsets();
let /* mut */ cur_y = top_offset; let /* mut */ cur_y = top_offset;
@ -89,9 +87,7 @@ impl TableRowFlow {
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells ) // Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells )
let mut max_y = Au::new(0); let mut max_y = Au::new(0);
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
if inorder { kid.assign_height_for_inorder_child_if_necessary(layout_context);
kid.assign_height_inorder(layout_context)
}
{ {
let child_box = kid.as_table_cell().box_(); let child_box = kid.as_table_cell().box_();
@ -136,12 +132,9 @@ impl TableRowFlow {
} }
} }
pub fn build_display_list_table_row(&mut self, pub fn build_display_list_table_row(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_row: same process as block flow"); debug!("build_display_list_table_row: same process as block flow");
self.block_flow.build_display_list_block(stacking_context, builder, info) self.block_flow.build_display_list_block(layout_context)
} }
} }
@ -179,7 +172,6 @@ impl Flow for TableRowFlow {
fn bubble_widths(&mut self, _: &mut LayoutContext) { fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut min_width = Au(0); let mut min_width = Au(0);
let mut pref_width = Au(0); let mut pref_width = Au(0);
let mut num_floats = 0;
/* find the specified widths from child table-cell contexts */ /* find the specified widths from child table-cell contexts */
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_cell()); assert!(kid.is_table_cell());
@ -198,9 +190,7 @@ impl Flow for TableRowFlow {
self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width); self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width);
min_width = min_width + child_base.intrinsic_widths.minimum_width; min_width = min_width + child_base.intrinsic_widths.minimum_width;
pref_width = pref_width + child_base.intrinsic_widths.preferred_width; pref_width = pref_width + child_base.intrinsic_widths.preferred_width;
num_floats = num_floats + child_base.num_floats;
} }
self.block_flow.base.num_floats = num_floats;
self.block_flow.base.intrinsic_widths.minimum_width = min_width; self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width, self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
pref_width); pref_width);
@ -222,18 +212,13 @@ impl Flow for TableRowFlow {
self.block_flow.propagate_assigned_width_to_children(left_content_edge, Au(0), Some(self.col_widths.clone())); self.block_flow.propagate_assigned_width_to_children(left_content_edge, Au(0), Some(self.col_widths.clone()));
} }
/// This is called on kid flows by a parent.
///
/// Hence, we can assume that assign_height has already been called on the
/// kid (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_inorder: assigning height for table_row");
self.assign_height_table_row_base(ctx, true);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_row"); debug!("assign_height: assigning height for table_row");
self.assign_height_table_row_base(ctx, false); self.assign_height_table_row_base(ctx);
}
fn compute_absolute_position(&mut self) {
self.block_flow.compute_absolute_position()
} }
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {

View file

@ -9,13 +9,11 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer; use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow; use layout::flow;
use layout::table::{InternalTable, TableFlow}; use layout::table::{InternalTable, TableFlow};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use gfx::display_list::StackingContext;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
@ -80,12 +78,14 @@ impl TableRowGroupFlow {
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_rowgroup_base(&mut self, _: &mut LayoutContext, _: bool) { fn assign_height_table_rowgroup_base(&mut self, layout_context: &mut LayoutContext) {
let (top_offset, _, _) = self.initialize_offsets(); let (top_offset, _, _) = self.initialize_offsets();
let mut cur_y = top_offset; let mut cur_y = top_offset;
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
kid.assign_height_for_inorder_child_if_necessary(layout_context);
let child_node = flow::mut_base(kid); let child_node = flow::mut_base(kid);
child_node.position.origin.y = cur_y; child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height; cur_y = cur_y + child_node.position.size.height;
@ -99,12 +99,9 @@ impl TableRowGroupFlow {
self.block_flow.base.position.size.height = height; self.block_flow.base.position.size.height = height;
} }
pub fn build_display_list_table_rowgroup(&mut self, pub fn build_display_list_table_rowgroup(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_rowgroup: same process as block flow"); debug!("build_display_list_table_rowgroup: same process as block flow");
self.block_flow.build_display_list_block(stacking_context, builder, info) self.block_flow.build_display_list_block(layout_context)
} }
} }
@ -143,7 +140,6 @@ impl Flow for TableRowGroupFlow {
fn bubble_widths(&mut self, _: &mut LayoutContext) { fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut min_width = Au(0); let mut min_width = Au(0);
let mut pref_width = Au(0); let mut pref_width = Au(0);
let mut num_floats = 0;
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_row()); assert!(kid.is_table_row());
@ -174,11 +170,8 @@ impl Flow for TableRowGroupFlow {
pref_width = pref_width + new_kid_pref; pref_width = pref_width + new_kid_pref;
} }
} }
let child_base = flow::mut_base(kid);
num_floats = num_floats + child_base.num_floats;
} }
self.block_flow.base.num_floats = num_floats;
self.block_flow.base.intrinsic_widths.minimum_width = min_width; self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width, self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
pref_width); pref_width);
@ -201,18 +194,13 @@ impl Flow for TableRowGroupFlow {
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone())); self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
} }
/// This is called on kid flows by a parent.
///
/// Hence, we can assume that assign_height has already been called on the
/// kid (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height_inorder: assigning height for table_rowgroup");
self.assign_height_table_rowgroup_base(ctx, true);
}
fn assign_height(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
debug!("assign_height: assigning height for table_rowgroup"); debug!("assign_height: assigning height for table_rowgroup");
self.assign_height_table_rowgroup_base(ctx, false); self.assign_height_table_rowgroup_base(ctx);
}
fn compute_absolute_position(&mut self) {
self.block_flow.compute_absolute_position()
} }
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {

View file

@ -9,13 +9,11 @@ use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::block::{WidthConstraintInput, WidthConstraintSolution}; use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::FloatKind; use layout::floats::FloatKind;
use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::model::{Specified, Auto, specified}; use layout::model::{Specified, Auto, specified};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use gfx::display_list::StackingContext;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use style::computed_values::table_layout; use style::computed_values::table_layout;
@ -104,18 +102,13 @@ impl TableWrapperFlow {
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_wrapper_base(&mut self, fn assign_height_table_wrapper_base(&mut self, layout_context: &mut LayoutContext) {
layout_context: &mut LayoutContext, self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse);
inorder: bool) {
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
} }
pub fn build_display_list_table_wrapper(&mut self, pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) {
stacking_context: &mut StackingContext,
builder: &mut DisplayListBuilder,
info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_wrapper: same process as block flow"); debug!("build_display_list_table_wrapper: same process as block flow");
self.block_flow.build_display_list_block(stacking_context, builder, info); self.block_flow.build_display_list_block(layout_context);
} }
} }
@ -187,30 +180,20 @@ impl Flow for TableWrapperFlow {
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, assigned_col_widths); self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, assigned_col_widths);
} }
/// This is called on kid flows by a parent.
///
/// Hence, we can assume that assign_height has already been called on the
/// kid (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
if self.is_float() {
debug!("assign_height_inorder_float: assigning height for floated table_wrapper");
self.block_flow.assign_height_float_inorder();
} else {
debug!("assign_height_inorder: assigning height for table_wrapper");
self.assign_height_table_wrapper_base(ctx, true);
}
}
fn assign_height(&mut self, ctx: &mut LayoutContext) { fn assign_height(&mut self, ctx: &mut LayoutContext) {
if self.is_float() { if self.is_float() {
debug!("assign_height_float: assigning height for floated table_wrapper"); debug!("assign_height_float: assigning height for floated table_wrapper");
self.block_flow.assign_height_float(ctx); self.block_flow.assign_height_float(ctx);
} else { } else {
debug!("assign_height: assigning height for table_wrapper"); debug!("assign_height: assigning height for table_wrapper");
self.assign_height_table_wrapper_base(ctx, false); self.assign_height_table_wrapper_base(ctx);
} }
} }
fn compute_absolute_position(&mut self) {
self.block_flow.compute_absolute_position()
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = if self.is_float() { let txt = if self.is_float() {
~"TableWrapperFlow(Float): " ~"TableWrapperFlow(Float): "

View file

@ -8,14 +8,17 @@ use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
use layout::flow::Flow; use layout::flow::Flow;
use layout::inline::InlineBoxes; use layout::inline::InlineBoxes;
use gfx::font::{FontMetrics, FontStyle};
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone}; use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
use servo_util::geometry::Au;
use servo_util::range::Range; use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec0}; use servo_util::smallvec::{SmallVec, SmallVec0};
use std::mem; use std::mem;
use std::slice; use std::slice;
use style::computed_values::white_space; use style::ComputedValues;
use style::computed_values::{font_family, line_height, white_space};
use sync::Arc; use sync::Arc;
struct NewLinePositions { struct NewLinePositions {
@ -256,3 +259,53 @@ impl TextRunScanner {
new_whitespace new_whitespace
} // End of `flush_clump_to_list`. } // End of `flush_clump_to_list`.
} }
/// Returns the metrics of the font represented by the given `FontStyle`, respectively.
///
/// `#[inline]` because often the caller only needs a few fields from the font metrics.
#[inline]
pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: &FontStyle)
-> FontMetrics {
let fontgroup = font_context.get_resolved_font_for_style(font_style);
fontgroup.borrow().fonts[0].borrow().metrics.clone()
}
/// Converts a computed style to a font style used for rendering.
///
/// FIXME(pcwalton): This should not be necessary; just make the font part of the style sharable
/// with the display list somehow. (Perhaps we should use an ARC.)
pub fn computed_style_to_font_style(style: &ComputedValues) -> FontStyle {
debug!("(font style) start");
// FIXME: Too much allocation here.
let mut font_families = style.Font.get().font_family.iter().map(|family| {
match *family {
font_family::FamilyName(ref name) => (*name).clone(),
}
});
debug!("(font style) font families: `{:?}`", font_families);
let font_size = style.Font.get().font_size.to_f64().unwrap() / 60.0;
debug!("(font style) font size: `{:f}px`", font_size);
FontStyle {
pt_size: font_size,
weight: style.Font.get().font_weight,
style: style.Font.get().font_style,
families: font_families.collect(),
}
}
/// Returns the line height needed by the given computed style and font size.
///
/// FIXME(pcwalton): I believe this should not take a separate `font-size` parameter.
pub fn line_height_from_style(style: &ComputedValues, font_size: Au) -> Au {
let from_inline = match style.InheritedBox.get().line_height {
line_height::Normal => font_size.scale_by(1.14),
line_height::Number(l) => font_size.scale_by(l),
line_height::Length(l) => l
};
let minimum = style.InheritedBox.get()._servo_minimum_line_height;
Au::max(from_inline, minimum)
}

View file

@ -7,6 +7,7 @@ use layout::parallel::DomParallelInfo;
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use gfx::display_list::OpaqueNode; use gfx::display_list::OpaqueNode;
use gfx;
use libc::uintptr_t; use libc::uintptr_t;
use script::dom::bindings::js::JS; use script::dom::bindings::js::JS;
use script::dom::bindings::utils::Reflectable; use script::dom::bindings::utils::Reflectable;
@ -15,6 +16,7 @@ use script::layout_interface::{LayoutChan, UntrustedNodeAddress, TrustedNodeAddr
use std::cast; use std::cast;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use style::ComputedValues; use style::ComputedValues;
use style;
use sync::Arc; use sync::Arc;
/// Data that layout associates with a node. /// Data that layout associates with a node.
@ -148,6 +150,17 @@ impl OpaqueNodeMethods for OpaqueNode {
addr addr
} }
} }
}
/// Allows a CSS color to be converted into a graphics color.
pub trait ToGfxColor {
/// Converts a CSS color to a graphics color.
fn to_gfx_color(&self) -> gfx::color::Color;
}
impl ToGfxColor for style::computed_values::RGBA {
fn to_gfx_color(&self) -> gfx::color::Color {
gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
}
} }

View file

@ -98,7 +98,6 @@ pub mod layout {
pub mod box_; pub mod box_;
pub mod construct; pub mod construct;
pub mod context; pub mod context;
pub mod display_list_builder;
pub mod floats; pub mod floats;
pub mod flow; pub mod flow;
pub mod flow_list; pub mod flow_list;

View file

@ -109,6 +109,11 @@ impl ImageHolder {
}) })
} }
pub fn get_image_if_present(&self) -> Option<Arc<~Image>> {
debug!("get_image_if_present() {}", self.url.to_str());
self.image.clone()
}
pub fn get_image(&mut self) -> Option<Arc<~Image>> { pub fn get_image(&mut self) -> Option<Arc<~Image>> {
debug!("get_image() {}", self.url.to_str()); debug!("get_image() {}", self.url.to_str());