aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/gfx/display_list.rs338
-rw-r--r--src/components/main/layout/block.rs542
-rw-r--r--src/components/main/layout/box_.rs444
-rw-r--r--src/components/main/layout/construct.rs4
-rw-r--r--src/components/main/layout/context.rs4
-rw-r--r--src/components/main/layout/display_list_builder.rs52
-rw-r--r--src/components/main/layout/flow.rs184
-rw-r--r--src/components/main/layout/inline.rs384
-rw-r--r--src/components/main/layout/layout_task.rs164
-rw-r--r--src/components/main/layout/parallel.rs138
-rw-r--r--src/components/main/layout/table.rs38
-rw-r--r--src/components/main/layout/table_caption.rs23
-rw-r--r--src/components/main/layout/table_cell.rs34
-rw-r--r--src/components/main/layout/table_row.rs33
-rw-r--r--src/components/main/layout/table_rowgroup.rs32
-rw-r--r--src/components/main/layout/table_wrapper.rs35
-rw-r--r--src/components/main/layout/text.rs55
-rw-r--r--src/components/main/layout/util.rs13
-rwxr-xr-xsrc/components/main/servo.rs1
-rw-r--r--src/components/net/image/holder.rs5
20 files changed, 1485 insertions, 1038 deletions
diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs
index 585ad7650aa..6d47d76fc08 100644
--- a/src/components/gfx/display_list.rs
+++ b/src/components/gfx/display_list.rs
@@ -18,12 +18,15 @@ use color::Color;
use render_context::RenderContext;
use text::TextRun;
+use collections::deque::Deque;
+use collections::dlist::DList;
+use collections::dlist;
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use libc::uintptr_t;
use servo_net::image::base::Image;
use servo_util::geometry::Au;
use servo_util::range::Range;
-use servo_util::smallvec::{SmallVec, SmallVec0, SmallVecIterator};
+use servo_util::smallvec::{SmallVec, SmallVec0};
use std::mem;
use std::slice::Items;
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
-/// specification.
-///
-/// TODO(pcwalton): Outlines.
-pub struct StackingContext {
+/// "Steps" as defined by CSS 2.1 § E.2.
+#[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): `z-index` should be the actual CSS property value in order to handle
+ /// `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.
pub background_and_borders: DisplayList,
/// Borders and backgrounds for block-level descendants: step 4.
@@ -59,78 +86,115 @@ pub struct StackingContext {
pub floats: DisplayList,
/// All other content.
pub content: DisplayList,
-
/// 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
- /// `auto`, not just an integer. In this case we should store an actual stacking context, not
- /// a flattened display list.
- pub positioned_descendants: SmallVec0<(int, DisplayList)>,
+ /// `auto`, not just an integer.
+ pub positioned_descendants: SmallVec0<(i32, DisplayList)>,
}
impl StackingContext {
- pub fn new() -> StackingContext {
- StackingContext {
+ /// Creates a stacking context from a display list.
+ fn new(list: DisplayList) -> StackingContext {
+ let DisplayList {
+ list: mut list
+ } = list;
+
+ let mut stacking_context = StackingContext {
background_and_borders: DisplayList::new(),
block_backgrounds_and_borders: DisplayList::new(),
floats: DisplayList::new(),
content: DisplayList::new(),
positioned_descendants: SmallVec0::new(),
- }
- }
+ };
- pub fn list_for_background_and_border_level<'a>(
- &'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,
+ 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))
+ }
+ }
+ }
+ }
}
+
+ stacking_context
}
- /// Flattens a stacking context into a display list according to the steps in CSS 2.1 § E.2.
- pub fn flatten(self) -> DisplayList {
- // Steps 1 and 2: Borders and background for the root.
+ /// Merges another stacking context into this one, with the given clipping rectangle and DOM
+ /// node that supplies it.
+ fn merge_with_clip(&mut self,
+ other: StackingContext,
+ clip_rect: &Rect<Au>,
+ clipping_dom_node: OpaqueNode) {
let StackingContext {
- background_and_borders: mut result,
+ background_and_borders,
block_backgrounds_and_borders,
floats,
content,
positioned_descendants: mut positioned_descendants
- } = self;
-
- // TODO(pcwalton): Sort positioned children according to z-index.
+ } = other;
- // Step 3: Positioned descendants with negative z-indices.
- for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
- if *z_index < 0 {
- result.push_all_move(mem::replace(list, DisplayList::new()))
+ 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)))
}
- }
-
- // Step 4: Block backgrounds and borders.
- result.push_all_move(block_backgrounds_and_borders);
-
- // Step 5: Floats.
- result.push_all_move(floats);
-
- // TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
-
- // Step 7: Content.
- result.push_all_move(content);
+ };
- // Steps 8 and 9: Positioned descendants with nonnegative z-indices.
- for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
- if *z_index >= 0 {
- result.push_all_move(mem::replace(list, DisplayList::new()))
+ 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 => {}
}
- }
-
- // TODO(pcwalton): Step 10: Outlines.
- result
+ let mut new_list = DisplayList::new();
+ push(&mut new_list, list, PositionedDescendantStackingLevel(z_index));
+ self.positioned_descendants.push((z_index, new_list));
+ }
}
}
@@ -143,7 +207,7 @@ pub enum BackgroundAndBorderLevel {
/// A list of rendering operations to be performed.
pub struct DisplayList {
- pub list: SmallVec0<DisplayItem>,
+ pub list: DList<DisplayItem>,
}
pub enum DisplayListIterator<'a> {
@@ -165,7 +229,7 @@ impl DisplayList {
/// Creates a new display list.
pub fn new() -> DisplayList {
DisplayList {
- list: SmallVec0::new(),
+ list: DList::new(),
}
}
@@ -177,16 +241,17 @@ impl DisplayList {
/// Appends the given item to the display list.
pub fn push(&mut self, item: DisplayItem) {
- self.list.push(item)
+ 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.push_all_move(other.list)
+ self.list.append(other.list)
}
- /// Draws the display list into the given render context.
+ /// 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() {
@@ -199,6 +264,74 @@ impl DisplayList {
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.
+
+ // Step 3: Positioned descendants with negative z-indices.
+ for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
+ if *z_index < 0 {
+ result.push_all_move(mem::replace(list, DisplayList::new()))
+ }
+ }
+
+ // Step 4: Block backgrounds and borders.
+ result.push_all_move(block_backgrounds_and_borders);
+
+ // Step 5: Floats.
+ result.push_all_move(floats);
+
+ // TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
+
+ // Step 7: Content.
+ result.push_all_move(content);
+
+ // Steps 8 and 9: Positioned descendants with nonnegative z-indices.
+ for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
+ if *z_index >= 0 {
+ result.push_all_move(mem::replace(list, DisplayList::new()))
+ }
+ }
+
+ // TODO(pcwalton): Step 10: Outlines.
+
+ result.set_stacking_level(resulting_level);
+ result
+ }
+
+ /// Sets the stacking level for this display list and all its subitems.
+ fn set_stacking_level(&mut self, new_level: StackingLevel) {
+ for item in self.list.mut_iter() {
+ item.mut_base().level = new_level;
+ match item.mut_sublist() {
+ None => {}
+ Some(sublist) => sublist.set_stacking_level(new_level),
+ }
+ }
+ }
}
/// One drawing command in the list.
@@ -208,7 +341,14 @@ pub enum DisplayItem {
ImageDisplayItemClass(~ImageDisplayItem),
BorderDisplayItemClass(~BorderDisplayItem),
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.
@@ -220,6 +360,19 @@ pub struct BaseDisplayItem {
/// The originating DOM node.
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.
@@ -292,15 +445,27 @@ pub struct LineDisplayItem {
pub style: border_style::T
}
+/// Clips a list of child display items to this display item's boundaries.
pub struct ClipDisplayItem {
+ /// The base information.
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> {
EmptyDisplayItemIterator,
- ParentDisplayItemIterator(SmallVecIterator<'a,DisplayItem>),
+ ParentDisplayItemIterator(dlist::Items<'a,DisplayItem>),
}
impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
@@ -316,21 +481,20 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
impl DisplayItem {
/// Renders this display item into the given render context.
fn draw_into_context(&self, render_context: &mut RenderContext) {
+ // This should have been flattened to the content stacking level first.
+ assert!(self.base().level == ContentStackingLevel);
+
match *self {
SolidColorDisplayItemClass(ref solid_color) => {
render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
}
ClipDisplayItemClass(ref clip) => {
- if clip.need_clip {
- render_context.draw_push_clip(&clip.base.bounds);
- }
- for item in clip.child_list.iter() {
+ render_context.draw_push_clip(&clip.base.bounds);
+ for item in clip.children.iter() {
(*item).draw_into_context(render_context);
}
- if clip.need_clip {
- render_context.draw_pop_clip();
- }
+ render_context.draw_pop_clip();
}
TextDisplayItemClass(ref text) => {
@@ -412,6 +576,8 @@ impl DisplayItem {
line.color,
line.style)
}
+
+ PseudoDisplayItemClass(_) => {}
}
}
@@ -423,6 +589,19 @@ impl DisplayItem {
BorderDisplayItemClass(ref border) => &border.base,
LineDisplayItemClass(ref line) => &line.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> {
match *self {
- ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()),
+ ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.children.list.iter()),
+ SolidColorDisplayItemClass(..) |
+ TextDisplayItemClass(..) |
+ ImageDisplayItemClass(..) |
+ BorderDisplayItemClass(..) |
+ LineDisplayItemClass(..) |
+ PseudoDisplayItemClass(..) => EmptyDisplayItemIterator,
+ }
+ }
+
+ /// Returns a mutable reference to the sublist contained within this display list item, if any.
+ fn mut_sublist<'a>(&'a mut self) -> Option<&'a mut DisplayList> {
+ match *self {
+ ClipDisplayItemClass(ref mut clip) => Some(&mut clip.children),
SolidColorDisplayItemClass(..) |
TextDisplayItemClass(..) |
ImageDisplayItemClass(..) |
BorderDisplayItemClass(..) |
- LineDisplayItemClass(..) => EmptyDisplayItemIterator,
+ LineDisplayItemClass(..) |
+ PseudoDisplayItemClass(..) => None,
}
}
@@ -460,8 +653,9 @@ impl DisplayItem {
BorderDisplayItemClass(_) => "Border",
LineDisplayItemClass(_) => "Line",
ClipDisplayItemClass(_) => "Clip",
+ PseudoDisplayItemClass(_) => "Pseudo",
};
- format!("{} @ {:?}", class, self.base().bounds)
+ format!("{} @ {} ({:x})", class, self.base().bounds, self.base().node.id())
}
}
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs
index 32f0aee6197..60c37eca7ed 100644
--- a/src/components/main/layout/block.rs
+++ b/src/components/main/layout/block.rs
@@ -15,7 +15,6 @@
use layout::box_::{Box, ImageBox, ScannedTextBox};
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo};
use layout::flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
@@ -28,18 +27,22 @@ use layout::wrapper::ThreadSafeLayoutNode;
use style::ComputedValues;
use style::computed_values::{clear, position};
+use collections::Deque;
+use collections::dlist::DList;
use geom::{Point2D, Rect, Size2D};
use gfx::color;
-use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, RootOfStackingContextLevel};
-use gfx::display_list::{StackingContext};
+use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, ContentStackingLevel, DisplayList};
+use gfx::display_list::{FloatStackingLevel, PositionedDescendantStackingLevel};
+use gfx::display_list::{RootOfStackingContextLevel};
use gfx::render_task::RenderLayer;
use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable};
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::smallvec::{SmallVec, SmallVec0};
+use std::mem;
use std::num::Zero;
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;
/// Information specific to floated blocks.
@@ -52,9 +55,6 @@ pub struct FloatedBlockInfo {
/// Index into the box list for inline floats
pub index: Option<uint>,
- /// Number of floated children
- pub floated_children: uint,
-
/// Left or right?
pub float_kind: FloatKind,
}
@@ -65,7 +65,6 @@ impl FloatedBlockInfo {
containing_width: Au(0),
rel_pos: Point2D(Au(0), Au(0)),
index: None,
- floated_children: 0,
float_kind: float_kind,
}
}
@@ -377,12 +376,9 @@ enum CandidateHeightIteratorStatus {
}
// 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;
-
- if inorder {
- floats.translate(Point2D(Au(0), -delta));
- }
+ floats.translate(Point2D(Au(0), -delta));
}
/// The real assign-heights traversal for flows with position 'absolute'.
@@ -801,14 +797,13 @@ impl BlockFlow {
#[inline(always)]
pub fn assign_height_block_base(&mut self,
layout_context: &mut LayoutContext,
- inorder: bool,
margins_may_collapse: MarginsMayCollapseFlag) {
// Our current border-box position.
let mut cur_y = Au(0);
// Absolute positioning establishes a block formatting context. Don't propagate floats
// 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();
}
if margins_may_collapse != MarginsMayCollapse {
@@ -820,7 +815,7 @@ impl BlockFlow {
// The sum of our top border and top padding.
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 =
margins_may_collapse == MarginsMayCollapse &&
@@ -829,19 +824,15 @@ impl BlockFlow {
margin_collapse_info.initialize_top_margin(&self.box_,
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 layers_needed_for_descendants = false;
for kid in self.base.child_iter() {
if kid.is_absolutely_positioned() {
// Assume that the *hypothetical box* for an absolute flow starts immediately after
// the bottom border edge of the previous flow.
- kid.as_block().base.position.origin.y = cur_y;
-
- if inorder {
- kid.assign_height_inorder(layout_context)
- }
-
+ flow::mut_base(kid).position.origin.y = cur_y;
+ kid.assign_height_for_inorder_child_if_necessary(layout_context);
propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
// 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.
- let mut floats_out = None;
- 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);
+ flow::mut_base(kid).floats = floats.clone();
+ if kid.is_float() {
+ // 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);
- floats_out = Some(flow::mut_base(kid).floats.clone());
+ let kid_was_impacted_by_floats =
+ kid.assign_height_for_inorder_child_if_necessary(layout_context);
+ assert!(kid_was_impacted_by_floats); // As it was a float itself...
- // 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() {
- flow::mut_base(kid).position.origin.y = cur_y;
- }
+ let kid_base = flow::mut_base(kid);
+ kid_base.position.origin.y = cur_y;
+ floats = kid_base.floats.clone();
+ continue
}
- propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
- // If the child was a float, stop here.
- if kid.is_float() {
- if inorder {
- floats = floats_out.take_unwrap();
- }
- continue
+ // If we have clearance, assume there are no floats in.
+ //
+ // 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()
}
- // 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);
+ // 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);
- // Clear past floats, if necessary.
- if inorder {
- let clearance = match kid_base.clear {
- clear::none => Au(0),
- clear::left => floats.clearance(ClearLeft),
- clear::right => floats.clearance(ClearRight),
- clear::both => floats.clearance(ClearBoth),
- };
- cur_y = cur_y + clearance
- }
+ // 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::left => floats.clearance(ClearLeft),
+ clear::right => floats.clearance(ClearRight),
+ clear::both => floats.clearance(ClearBoth),
+ };
+ cur_y = cur_y + clearance;
// At this point, `cur_y` is at the border edge of the child.
- assert!(kid_base.position.origin.y == Au(0));
- kid_base.position.origin.y = cur_y;
+ flow::mut_base(kid).position.origin.y = cur_y;
- // If this was an inorder traversal, grab the child's floats now.
- if inorder {
- floats = floats_out.take_unwrap()
+ // Now pull out the child's outgoing floats. We didn't do this immediately after the
+ // `assign_height_for_inorder_child_if_necessary` call because clearance on a block
+ // 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`
// 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;
// Handle any (possibly collapsed) bottom margin.
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);
+ // Collect various offsets needed by absolutely positioned descendants.
self.collect_static_y_offsets_from_kids();
// Add in our bottom margin and compute our collapsible margins.
@@ -942,7 +929,7 @@ impl BlockFlow {
&self.box_,
can_collapse_bottom_margin_with_kids);
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
// 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.
height = candidate_height_iterator.candidate_value;
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.
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
// position.
@@ -1011,17 +998,15 @@ impl BlockFlow {
/// Add placement information about current float flow for use by the parent.
///
- /// Also, use information given by parent about other floats to find out
- /// our relative position.
+ /// Also, use information given by parent about other floats to find out our relative position.
///
- /// This does not give any information about any float descendants because
- /// they do not affect elements outside of the subtree rooted at this
- /// float.
+ /// This does not give any information about any float descendants because they do not affect
+ /// elements outside of the subtree rooted at this float.
///
- /// This function is called on a kid flow by a parent.
- /// Therefore, assign_height_float was already called on this kid flow by
- /// the traversal function. So, the values used are well-defined.
- pub fn assign_height_float_inorder(&mut self) {
+ /// This function is called on a kid flow by a parent. Therefore, `assign_height_float` was
+ /// already called on this kid flow by the traversal function. So, the values used are
+ /// well-defined.
+ pub fn place_float(&mut self) {
let height = self.box_.border_box.size.height;
let clearance = match self.box_.clear() {
None => Au(0),
@@ -1054,20 +1039,16 @@ impl BlockFlow {
///
/// It does not calculate the height of the flow itself.
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();
- for kid in self.base.child_iter() {
- flow::mut_base(kid).floats = floats;
- kid.assign_height_inorder(ctx);
- floats = flow::mut_base(kid).floats.clone();
- }
-
- // Floats establish a block formatting context, so we discard the output floats here.
- drop(floats);
+ let mut floats = Floats::new();
+ for kid in self.base.child_iter() {
+ flow::mut_base(kid).floats = floats.clone();
+ kid.assign_height_for_inorder_child_if_necessary(ctx);
+ floats = flow::mut_base(kid).floats.clone();
}
+ // Floats establish a block formatting context, so we discard the output floats here.
+ drop(floats);
+
let top_offset = self.box_.margin.top + self.box_.border_padding.top;
let mut cur_y = top_offset;
@@ -1101,99 +1082,75 @@ impl BlockFlow {
}
fn build_display_list_block_common(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo,
+ layout_context: &LayoutContext,
offset: Point2D<Au>,
background_border_level: BackgroundAndBorderLevel) {
- let mut info = *info;
- let rel_offset = self.box_.relative_position(&info.relative_containing_block_size, None);
+ let rel_offset =
+ self.box_.relative_position(&self.base
+ .absolute_position_info
+ .relative_containing_block_size,
+ None);
// Add the box that starts the block context.
- self.box_.build_display_list(stacking_context,
- builder,
- &info,
- self.base.abs_position + rel_offset + offset,
- background_border_level,
- None);
-
- // 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, 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;
+ let mut display_list = DisplayList::new();
+ let mut accumulator =
+ self.box_.build_display_list(&mut display_list,
+ layout_context,
+ self.base.abs_position + rel_offset + offset,
+ background_border_level,
+ None);
+
+ let mut child_layers = DList::new();
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() {
// All absolute flows will be handled by their containing block.
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.
- 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() {
match abs_descendant_link.resolve() {
- Some(flow) => {
+ Some(kid) => {
// 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")
}
}
+
+ accumulator.finish(&mut *self, display_list);
+ self.base.layers = child_layers
}
/// Add display items for current block.
///
/// Set the absolute position for children after doing any offsetting for
/// position: relative.
- pub fn build_display_list_block(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_block(&mut self, layout_context: &LayoutContext) {
if self.is_float() {
// TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
// auto` kids into the parent stacking context, when that is supported.
- self.build_display_list_float(stacking_context, builder, info)
+ self.build_display_list_float(layout_context)
} else if self.is_absolutely_positioned() {
- self.build_display_list_abs(stacking_context, builder, info)
+ self.build_display_list_abs(layout_context)
} else {
- self.build_display_list_block_common(stacking_context,
- builder,
- info,
- Point2D(Au(0), Au(0)),
- BlockLevel)
+ self.build_display_list_block_common(layout_context, Zero::zero(), BlockLevel)
}
}
- pub fn build_display_list_float(&mut self,
- parent_stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
- let mut stacking_context = StackingContext::new();
+ pub fn build_display_list_float(&mut self, layout_context: &LayoutContext) {
let float_offset = self.float.get_ref().rel_pos;
- self.build_display_list_block_common(&mut stacking_context,
- builder,
- info,
+ self.build_display_list_block_common(layout_context,
float_offset,
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.
@@ -1283,58 +1240,41 @@ impl BlockFlow {
}
/// Add display items for Absolutely Positioned flow.
- pub fn build_display_list_abs(&mut self,
- parent_stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- 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)),
+ fn build_display_list_abs(&mut self, layout_context: &LayoutContext) {
+ self.build_display_list_block_common(layout_context,
+ Zero::zero(),
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.
//
// 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
}
// If we got here, then we need a new layer.
let size = Size2D(self.base.position.size.width.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,
- info.absolute_containing_block_position.y.to_nearest_px() as uint);
+ let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint,
+ self.base.abs_position.y.to_nearest_px() as uint);
let scroll_policy = if self.is_fixed() {
FixedPosition
} else {
Scrollable
};
+ let display_list = mem::replace(&mut self.base.display_list, DisplayList::new());
let new_layer = RenderLayer {
id: self.layer_id(0),
- display_list: Arc::new(stacking_context.flatten()),
+ display_list: Arc::new(display_list.flatten(ContentStackingLevel)),
position: Rect(origin, size),
background_color: color::rgba(255.0, 255.0, 255.0, 0.0),
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.
@@ -1348,27 +1288,15 @@ impl BlockFlow {
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.
+ /// Also computes whether each child will be impacted by floats.
pub fn propagate_assigned_width_to_children(&mut self,
left_content_edge: Au,
content_width: Au,
opt_col_widths: Option<~[Au]>) {
- let has_inorder_children = if self.is_float() {
- self.base.num_floats > 0
- } else {
- self.base.flags.inorder() || self.base.num_floats > 0
- };
+ // Keep track of whether floats could impact each child.
+ let mut left_floats_impact_child = self.base.flags.impacted_by_left_floats();
+ let mut right_floats_impact_child = self.base.flags.impacted_by_right_floats();
let kid_abs_cb_x_offset;
if self.is_positioned() {
@@ -1398,16 +1326,31 @@ impl BlockFlow {
{
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;
- // 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.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.
match opt_col_widths {
@@ -1459,20 +1402,45 @@ impl Flow for BlockFlow {
self
}
- /* Recursively (bottom-up) determine the context's preferred and
- minimum widths. When called on this context, all child contexts
- have had their min/pref widths set. This function must decide
- min/pref widths based on child context widths and dimensions of
- any boxes it is responsible for flowing. */
+ /// Returns the direction that this flow clears floats in, if any.
+ fn float_clearance(&self) -> clear::T {
+ self.box_.style().Box.get().clear
+ }
- /* 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) {
- 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.
let mut intrinsic_widths = IntrinsicWidths::new();
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);
intrinsic_widths.minimum_width =
@@ -1481,14 +1449,8 @@ impl Flow for BlockFlow {
intrinsic_widths.preferred_width =
geometry::max(intrinsic_widths.preferred_width,
child_base.intrinsic_widths.total_preferred_width());
- num_floats = num_floats + child_base.num_floats;
- }
- if self.is_float() {
- self.base.num_floats = 1;
- self.float.get_mut_ref().floated_children = num_floats;
- } else {
- self.base.num_floats = num_floats;
+ flags.union_floated_descendants_flags(child_base.flags);
}
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,
box_intrinsic_widths.preferred_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
@@ -1519,20 +1487,24 @@ impl Flow for BlockFlow {
self.base.position.origin = Zero::zero();
self.base.position.size.width = ctx.screen_size.width;
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.
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);
+ 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
- // it.
- self.base.clear = self.box_.style().Box.get().clear;
+ // Block formatting contexts are never impacted by floats.
+ if self.is_block_formatting_context(false) {
+ 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
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);
}
- /// 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
- /// kid (because of the bottom-up traversal).
- fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
+ /// This is called on child flows by the parent. Hence, we can assume that `assign_height` has
+ /// already been called on the child (because of the bottom-up traversal).
+ fn assign_height_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext)
+ -> bool {
if self.is_float() {
- debug!("assign_height_inorder_float: assigning height for float");
- self.assign_height_float_inorder();
- } else {
- debug!("assign_height_inorder: assigning height for block");
- self.assign_height_block_base(ctx, true, MarginsMayCollapse);
+ self.place_float();
+ return true
}
+
+ let impacted = self.base.flags.impacted_by_floats();
+ if impacted {
+ self.assign_height(layout_context);
+ }
+ impacted
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
@@ -1569,13 +1547,77 @@ impl Flow for BlockFlow {
self.assign_height_float(ctx);
} else {
debug!("assign_height: assigning height for block");
- // This is the only case in which a block flow can start an inorder
- // subtraversal.
- if self.is_root() && self.base.num_floats > 0 {
- self.assign_height_inorder(ctx);
- return;
+ self.assign_height_block_base(ctx, MarginsMayCollapse);
+ }
+ }
+
+ 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);
}
}
diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs
index 6db04e95bee..41b133243e1 100644
--- a/src/components/main/layout/box_.rs
+++ b/src/components/main/layout/box_.rs
@@ -7,25 +7,26 @@
use css::node_style::StyledNode;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
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;
-use layout::util::OpaqueNodeMethods;
+use layout::text;
+use layout::util::{OpaqueNodeMethods, ToGfxColor};
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
-use sync::Arc;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use geom::approxeq::ApproxEq;
use gfx::color::rgb;
use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass};
-use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
-use gfx::display_list::{LineDisplayItemClass, OpaqueNode, SolidColorDisplayItem};
-use gfx::display_list::{SolidColorDisplayItemClass, StackingContext, TextDecorations};
-use gfx::display_list::{TextDisplayItem, TextDisplayItemClass};
+use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList, ImageDisplayItem};
+use gfx::display_list::{ImageDisplayItemClass, LineDisplayItem};
+use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass};
+use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel};
+use gfx::display_list::{TextDecorations, TextDisplayItem, TextDisplayItemClass};
use gfx::font::FontStyle;
use gfx::text::text_run::TextRun;
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::range::*;
use servo_util::namespace;
-use servo_util::smallvec::{SmallVec, SmallVec0};
+use servo_util::smallvec::SmallVec;
use servo_util::str::is_whitespace;
use std::cast;
use std::from_str::FromStr;
use std::iter::AdditiveIterator;
+use std::mem;
use std::num::Zero;
use style::{ComputedValues, TElement, TNode, cascade, initial_values};
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::{position, text_align, text_decoration, vertical_align, visibility};
-use style::computed_values::{white_space};
+use style::computed_values::{background_repeat, border_style, clear, position, text_align};
+use style::computed_values::{text_decoration, vertical_align, visibility, white_space};
+use sync::Arc;
use url::Url;
/// 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 {
- let from_inline = match self.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 = self.style().InheritedBox.get()._servo_minimum_line_height;
- Au::max(from_inline, minimum)
+ text::line_height_from_style(self.style(), font_size)
}
/// 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.
- ///
- /// 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.)
+ /// Converts this fragment's computed style to a font style used for rendering.
pub fn font_style(&self) -> FontStyle {
- let my_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,
- }
+ text::computed_style_to_font_style(self.style())
}
#[inline(always)]
@@ -658,7 +631,8 @@ impl Box {
/// necessary.
pub fn build_display_list_for_background_if_applicable(&self,
list: &mut DisplayList,
- builder: &DisplayListBuilder,
+ layout_context: &LayoutContext,
+ level: StackingLevel,
absolute_bounds: &Rect<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems
@@ -668,10 +642,7 @@ impl Box {
let background_color = style.resolve_color(style.Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) {
let display_item = ~SolidColorDisplayItem {
- base: BaseDisplayItem {
- bounds: *absolute_bounds,
- node: self.node,
- },
+ base: BaseDisplayItem::new(*absolute_bounds, self.node, level),
color: background_color.to_gfx_color(),
};
@@ -681,93 +652,84 @@ impl Box {
// The background image is painted on top of the background color.
// Implements background image, per spec:
// http://www.w3.org/TR/CSS21/colors.html#background
- match style.Background.get().background_image {
- Some(ref image_url) => {
- let mut holder = ImageHolder::new(image_url.clone(),
- builder.ctx.image_cache.clone());
- match holder.get_image() {
- Some(image) => {
- debug!("(building display list) building background image");
-
- // Adjust bounds for `background-position` and `background-attachment`.
- let mut bounds = *absolute_bounds;
- let horizontal_position = model::specified(
- style.Background.get().background_position.horizontal,
- bounds.size.width);
- let vertical_position = model::specified(
- style.Background.get().background_position.vertical,
- bounds.size.height);
-
- let clip_display_item;
- match style.Background.get().background_attachment {
- background_attachment::scroll => {
- clip_display_item = None;
- bounds.origin.x = bounds.origin.x + horizontal_position;
- bounds.origin.y = bounds.origin.y + vertical_position;
- bounds.size.width = bounds.size.width - horizontal_position;
- bounds.size.height = bounds.size.height - vertical_position;
- }
- background_attachment::fixed => {
- clip_display_item = Some(~ClipDisplayItem {
- base: BaseDisplayItem {
- bounds: bounds,
- node: self.node,
- },
- child_list: SmallVec0::new(),
- need_clip: true,
- });
-
- bounds = Rect {
- origin: Point2D(horizontal_position, vertical_position),
- size: Size2D(bounds.origin.x + bounds.size.width,
- bounds.origin.y + bounds.size.height),
- }
- }
- }
- // Adjust sizes for `background-repeat`.
- match style.Background.get().background_repeat {
- background_repeat::no_repeat => {
- bounds.size.width = Au::from_px(image.width as int);
- bounds.size.height = Au::from_px(image.height as int)
- }
- background_repeat::repeat_x => {
- bounds.size.height = Au::from_px(image.height as int)
- }
- background_repeat::repeat_y => {
- bounds.size.width = Au::from_px(image.width as int)
- }
- background_repeat::repeat => {}
- };
-
-
- // Create the image display item.
- let image_display_item = ImageDisplayItemClass(~ImageDisplayItem {
- base: BaseDisplayItem {
- bounds: bounds,
- node: self.node,
- },
- image: image.clone(),
- stretch_size: Size2D(Au::from_px(image.width as int),
- Au::from_px(image.height as int)),
- });
-
- match clip_display_item {
- None => list.push(image_display_item),
- Some(mut clip_display_item) => {
- clip_display_item.child_list.push(image_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 :(");
- }
+ let background = style.Background.get();
+ let image_url = match background.background_image {
+ None => return,
+ Some(ref image_url) => image_url,
+ };
+
+ let mut holder = ImageHolder::new(image_url.clone(), layout_context.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");
+
+ // Adjust bounds for `background-position` and `background-attachment`.
+ let mut bounds = *absolute_bounds;
+ let horizontal_position = model::specified(background.background_position.horizontal,
+ bounds.size.width);
+ let vertical_position = model::specified(background.background_position.vertical,
+ bounds.size.height);
+
+ let clip_display_item;
+ match background.background_attachment {
+ background_attachment::scroll => {
+ clip_display_item = None;
+ bounds.origin.x = bounds.origin.x + horizontal_position;
+ bounds.origin.y = bounds.origin.y + vertical_position;
+ bounds.size.width = bounds.size.width - horizontal_position;
+ bounds.size.height = bounds.size.height - vertical_position;
+ }
+ background_attachment::fixed => {
+ clip_display_item = Some(~ClipDisplayItem {
+ base: BaseDisplayItem::new(bounds, self.node, level),
+ children: DisplayList::new(),
+ });
+
+ bounds = Rect {
+ origin: Point2D(horizontal_position, vertical_position),
+ size: Size2D(bounds.origin.x + bounds.size.width,
+ bounds.origin.y + bounds.size.height),
}
}
- None => {}
+ }
+
+ // Adjust sizes for `background-repeat`.
+ match background.background_repeat {
+ background_repeat::no_repeat => {
+ bounds.size.width = Au::from_px(image.width as int);
+ bounds.size.height = Au::from_px(image.height as int)
+ }
+ background_repeat::repeat_x => {
+ bounds.size.height = Au::from_px(image.height as int)
+ }
+ background_repeat::repeat_y => {
+ bounds.size.width = Au::from_px(image.width as int)
+ }
+ background_repeat::repeat => {}
+ };
+
+ // Create the image display item.
+ let image_display_item = ImageDisplayItemClass(~ImageDisplayItem {
+ base: BaseDisplayItem::new(bounds, self.node, level),
+ image: image.clone(),
+ stretch_size: Size2D(Au::from_px(image.width as int),
+ Au::from_px(image.height as int)),
+ });
+
+ match clip_display_item {
+ None => list.push(image_display_item),
+ Some(mut clip_display_item) => {
+ clip_display_item.children.push(image_display_item);
+ list.push(ClipDisplayItemClass(clip_display_item))
+ }
}
}
@@ -776,6 +738,7 @@ impl Box {
pub fn build_display_list_for_borders_if_applicable(&self,
list: &mut DisplayList,
abs_bounds: &Rect<Au>,
+ level: StackingLevel,
inline_fragment_context:
Option<InlineFragmentContext>) {
// Fast path.
@@ -792,10 +755,7 @@ impl Box {
// Append the border to the display list.
let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: *abs_bounds,
- node: self.node,
- },
+ base: BaseDisplayItem::new(*abs_bounds, self.node, level),
border: border,
color: SideOffsets2D::new(top_color.to_gfx_color(),
right_color.to_gfx_color(),
@@ -811,7 +771,7 @@ impl Box {
}
fn build_debug_borders_around_text_boxes(&self,
- stacking_context: &mut StackingContext,
+ display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
text_box: &ScannedTextBoxInfo) {
let box_bounds = self.border_box;
@@ -821,16 +781,12 @@ impl Box {
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: absolute_box_bounds,
- node: self.node,
- },
+ base: BaseDisplayItem::new(absolute_box_bounds, self.node, ContentStackingLevel),
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
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.
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)));
let line_display_item = ~LineDisplayItem {
- base: BaseDisplayItem {
- bounds: baseline,
- node: self.node,
- },
+ base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel),
color: rgb(0, 200, 0),
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,
- stacking_context: &mut StackingContext,
+ display_list: &mut DisplayList,
flow_origin: Point2D<Au>) {
let box_bounds = self.border_box;
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 border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: absolute_box_bounds,
- node: self.node,
- },
+ base: BaseDisplayItem::new(absolute_box_bounds, self.node, ContentStackingLevel),
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
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.
///
/// Arguments:
///
- /// * `stacking_context`: The stacking context to add display items to.
- /// * `builder`: The display list builder, which manages the coordinate system and options.
+ /// * `display_list`: The unflattened display list to add display items to.
+ /// * `layout_context`: The layout context.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
/// box.
- pub fn build_display_list(&mut self,
- stacking_context: &mut StackingContext,
- builder: &DisplayListBuilder,
- _: &DisplayListBuildingInfo,
+ pub fn build_display_list(&self,
+ display_list: &mut DisplayList,
+ layout_context: &LayoutContext,
flow_origin: Point2D<Au>,
background_and_border_level: BackgroundAndBorderLevel,
- inline_fragment_context: Option<InlineFragmentContext>) {
+ inline_fragment_context: Option<InlineFragmentContext>)
+ -> ChildDisplayListAccumulator {
// Box position wrt to the owning flow.
let box_bounds = self.border_box;
let absolute_box_bounds = box_bounds.translate(&flow_origin);
@@ -894,36 +842,49 @@ impl Box {
box_bounds,
absolute_box_bounds,
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 {
- 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...");
- return
+ return accumulator
}
debug!("Box::build_display_list: intersected. Adding display item...");
{
- let list =
- stacking_context.list_for_background_and_border_level(background_and_border_level);
+ let level =
+ StackingLevel::from_background_and_border_level(background_and_border_level);
+
+ // Add a pseudo-display item for content box queries. This is a very bogus thing to do.
+ let base_display_item = ~BaseDisplayItem::new(absolute_box_bounds, self.node, level);
+ display_list.push(PseudoDisplayItemClass(base_display_item));
// Add the background to the list, if applicable.
- self.build_display_list_for_background_if_applicable(list,
- builder,
+ self.build_display_list_for_background_if_applicable(display_list,
+ layout_context,
+ level,
&absolute_box_bounds);
// Add a border, if applicable.
//
// TODO: Outlines.
- self.build_display_list_for_borders_if_applicable(list,
+ self.build_display_list_for_borders_if_applicable(display_list,
&absolute_box_bounds,
+ level,
inline_fragment_context);
}
+ // Add a clip, if applicable.
match self.specific {
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
TableColumnBox(_) => fail!("Shouldn't see table column boxes here."),
@@ -949,41 +910,27 @@ impl Box {
// Create the text box.
let text_display_item = ~TextDisplayItem {
- base: BaseDisplayItem {
- bounds: bounds,
- node: self.node,
- },
+ base: BaseDisplayItem::new(bounds, self.node, ContentStackingLevel),
text_run: text_box.run.clone(),
range: text_box.range,
text_color: text_color,
text_decorations: text_decorations,
};
-
- stacking_context.content.push(TextDisplayItemClass(text_display_item));
+ accumulator.push(display_list, TextDisplayItemClass(text_display_item));
// Draw debug frames for text bounds.
//
// FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure.
// We should have a real `SERVO_DEBUG` system.
- debug!("{:?}", self.build_debug_borders_around_text_boxes(stacking_context,
+ debug!("{:?}", self.build_debug_borders_around_text_boxes(display_list,
flow_origin,
text_box))
},
GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox |
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
// 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(_) => {
let mut bounds = absolute_box_bounds.clone();
@@ -993,23 +940,22 @@ impl Box {
bounds.size.height = bounds.size.height - self.border_padding.vertical();
match self.specific {
- ImageBox(ref mut image_box) => {
- let image_ref = &mut image_box.image;
- match image_ref.get_image() {
+ ImageBox(ref image_box) => {
+ let image_ref = &image_box.image;
+ match image_ref.get_image_if_present() {
Some(image) => {
debug!("(building display list) building image box");
// Place the image into the display list.
let image_display_item = ~ImageDisplayItem {
- base: BaseDisplayItem {
- bounds: bounds,
- node: self.node,
- },
+ base: BaseDisplayItem::new(bounds,
+ self.node,
+ ContentStackingLevel),
image: image.clone(),
stretch_size: bounds.size,
};
- stacking_context.content
- .push(ImageDisplayItemClass(image_display_item))
+ accumulator.push(display_list,
+ ImageDisplayItemClass(image_display_item))
}
None => {
// 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
// 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.
match self.specific {
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.
@@ -1120,6 +1068,9 @@ impl Box {
}
ScannedTextBox(ref text_box_info) => {
// 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 text_bounds = run.metrics_for_range(range).bounding_box;
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.
pub fn can_merge_with_box(&self, other: &Box) -> bool {
match (&self.specific, &other.specific) {
@@ -1484,3 +1463,60 @@ impl Box {
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
+ }
+}
+
diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs
index 92059e44d18..72ce8e09b97 100644
--- a/src/components/main/layout/construct.rs
+++ b/src/components/main/layout/construct.rs
@@ -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);
inline_flow.finish(self.layout_context);
diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs
index 8b7db32538a..3910de1a3ac 100644
--- a/src/components/main/layout/context.rs
+++ b/src/components/main/layout/context.rs
@@ -6,6 +6,7 @@
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
+use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::OpaqueNode;
use gfx::font_context::{FontContext, FontContextInfo};
@@ -87,6 +88,9 @@ pub struct LayoutContext {
/// The command line options.
pub opts: Opts,
+
+ /// The dirty rectangle, used during display list building.
+ pub dirty: Rect<Au>,
}
#[cfg(not(target_os="android"))]
diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs
deleted file mode 100644
index 2a508977351..00000000000
--- a/src/components/main/layout/display_list_builder.rs
+++ /dev/null
@@ -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)
- }
-}
-
diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs
index 2b1563dcf09..cda5b87e6a5 100644
--- a/src/components/main/layout/flow.rs
+++ b/src/components/main/layout/flow.rs
@@ -30,7 +30,6 @@ use layout::block::BlockFlow;
use layout::box_::{Box, TableRowBox, TableCellBox};
use layout::construct::OptVector;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::Floats;
use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
use layout::incremental::RestyleDamage;
@@ -48,14 +47,18 @@ use layout::table_cell::TableCellFlow;
use layout::wrapper::ThreadSafeLayoutNode;
use collections::Deque;
+use collections::dlist::DList;
use geom::point::Point2D;
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_util::geometry::Au;
use servo_util::smallvec::{SmallVec, SmallVec0};
use std::cast;
use std::iter::Zip;
+use std::num::Zero;
use std::sync::atomics::Relaxed;
use std::slice::MutItems;
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.
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")
}
@@ -144,6 +148,11 @@ pub trait Flow {
// Main methods
/// 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) {
fail!("bubble_widths not yet implemented")
}
@@ -158,9 +167,32 @@ pub trait Flow {
fail!("assign_height not yet implemented")
}
- /// In-order version of pass 3a of reflow: computes heights with floats present.
- fn assign_height_inorder(&mut self, _ctx: &mut LayoutContext) {
- fail!("assign_height_inorder not yet implemented")
+ /// 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.
+ 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,
@@ -355,11 +387,8 @@ pub trait MutableFlowUtils {
/// Computes the overflow region for this flow.
fn store_overflow(self, _: &mut LayoutContext);
- /// Builds the display lists for this flow and its descendants.
- fn build_display_list(self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo);
+ /// Builds the display lists for this flow.
+ fn build_display_list(self, layout_context: &LayoutContext);
/// Destroys the flow.
fn destroy(self);
@@ -387,6 +416,7 @@ pub trait MutableOwnedFlowUtils {
fn destroy(&mut self);
}
+#[deriving(Eq, Show)]
pub enum FlowClass {
BlockFlowClass,
InlineFlowClass,
@@ -436,6 +466,26 @@ pub trait PostorderFlowTraversal {
#[deriving(Clone)]
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.
///
/// 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.
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
// block.
bitfield!(FlowFlags,
@@ -492,6 +539,18 @@ impl FlowFlags {
let FlowFlags(pff) = parent;
*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.
@@ -548,6 +607,31 @@ pub type DescendantIter<'a> = MutItems<'a, Rawlink>;
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.
pub struct BaseFlow {
pub restyle_damage: RestyleDamage,
@@ -584,16 +668,6 @@ pub struct BaseFlow {
/// The floats next to this flow.
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.
pub collapsible_margins: CollapsibleMargins,
@@ -614,6 +688,18 @@ pub struct BaseFlow {
/// Reference to the Containing Block, if this flow is absolutely positioned.
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.
///
/// TODO(pcwalton): Pack this into the flags? Need to be careful because manipulation of this
@@ -650,14 +736,15 @@ impl BaseFlow {
parallel: FlowParallelInfo::new(),
floats: Floats::new(),
- num_floats: 0,
collapsible_margins: CollapsibleMargins::new(),
- clear: clear::none,
abs_position: Point2D(Au::new(0), Au::new(0)),
abs_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0),
fixed_static_x_offset: Au::new(0),
absolute_cb: ContainingBlockLink::new(),
+ display_list: DisplayList::new(),
+ layers: DList::new(),
+ absolute_position_info: AbsolutePositionInfo::new(),
destroyed: false,
@@ -931,48 +1018,28 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
///
/// 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
/// display list building pass.
///
/// * `info`: Per-flow display list building information.
- fn build_display_list(self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ fn build_display_list(self, layout_context: &LayoutContext) {
debug!("Flow: building display list");
match self.class() {
- BlockFlowClass => {
- self.as_block().build_display_list_block(stacking_context, builder, info)
- }
- InlineFlowClass => {
- self.as_inline().build_display_list_inline(stacking_context, builder, info)
- }
+ BlockFlowClass => self.as_block().build_display_list_block(layout_context),
+ InlineFlowClass => self.as_inline().build_display_list_inline(layout_context),
TableWrapperFlowClass => {
- self.as_table_wrapper().build_display_list_table_wrapper(stacking_context,
- builder,
- info)
- }
- TableFlowClass => {
- self.as_table().build_display_list_table(stacking_context, builder, info)
+ self.as_table_wrapper().build_display_list_table_wrapper(layout_context)
}
+ TableFlowClass => self.as_table().build_display_list_table(layout_context),
TableRowGroupFlowClass => {
- self.as_table_rowgroup().build_display_list_table_rowgroup(stacking_context,
- builder,
- info)
- }
- TableRowFlowClass => {
- self.as_table_row().build_display_list_table_row(stacking_context, builder, info)
+ self.as_table_rowgroup().build_display_list_table_rowgroup(layout_context)
}
+ TableRowFlowClass => self.as_table_row().build_display_list_table_row(layout_context),
TableCaptionFlowClass => {
- self.as_table_caption().build_display_list_table_caption(stacking_context,
- builder,
- info)
+ self.as_table_caption().build_display_list_table_caption(layout_context)
}
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 => {
// Nothing to do here, as column groups don't render.
@@ -1001,6 +1068,7 @@ impl MutableOwnedFlowUtils for ~Flow:Share {
let base = mut_base(*self);
base.children.push_back(new_child);
let _ = base.parallel.children_count.fetch_add(1, Relaxed);
+ let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed);
}
/// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it.
@@ -1025,6 +1093,10 @@ impl MutableOwnedFlowUtils for ~Flow:Share {
let self_link = Rawlink::some(*self);
let block = self.as_block();
block.base.abs_descendants = abs_descendants;
+ block.base
+ .parallel
+ .children_and_absolute_descendant_count
+ .fetch_add(block.base.abs_descendants.len() as int, Relaxed);
for descendant_link in block.base.abs_descendants.iter() {
match descendant_link.resolve() {
@@ -1066,6 +1138,10 @@ impl ContainingBlockLink {
self.link = link
}
+ pub unsafe fn resolve(&mut self) -> Option<&mut Flow> {
+ self.link.resolve()
+ }
+
#[inline]
pub fn generated_containing_block_rect(&mut self) -> Rect<Au> {
self.link.resolve().unwrap().generated_containing_block_rect()
diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs
index ab4e8e43208..6d446da5125 100644
--- a/src/components/main/layout/inline.rs
+++ b/src/components/main/layout/inline.rs
@@ -3,21 +3,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode;
-use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, ScannedTextBox};
-use layout::box_::{SplitDidFit, SplitDidNotFit, TableBox, TableCellBox, TableColumnBox};
-use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox};
+use layout::box_::{Box, CannotSplit, SplitDidFit, SplitDidNotFit};
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{FloatLeft, Floats, PlacementInfo};
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use layout::flow;
use layout::model::IntrinsicWidths;
use layout::model;
+use layout::text;
use layout::wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf};
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;
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
/// lines.
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 {
@@ -581,6 +589,8 @@ impl InlineFlow {
base: BaseFlow::new(node),
boxes: boxes,
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();
}
- pub fn build_display_list_inline(&mut self,
- stacking_context: &mut StackingContext,
- builder: &DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) {
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
}
@@ -605,14 +612,15 @@ impl InlineFlow {
debug!("Flow: building display list for {:u} inline boxes", self.boxes.len());
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));
- fragment.build_display_list(stacking_context,
- builder,
- info,
- self.base.abs_position + rel_offset,
- ContentLevel,
- Some(context));
+ drop(fragment.build_display_list(&mut self.base.display_list,
+ layout_context,
+ self.base.abs_position + rel_offset,
+ ContentLevel,
+ Some(context)));
}
// 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.
}
- /// Returns the relative offset from the baseline for this box, taking into account the value
- /// of the CSS `vertical-align` property.
+ /// Returns the distance from the baseline for the logical top left corner of this fragment,
+ /// 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.
/// That is, if the box has a `top` or `bottom` value, true is returned.
- fn relative_offset_from_baseline(cur_box: &Box,
- ascent: Au,
- parent_text_top: Au,
- parent_text_bottom: Au,
- top_from_base: &mut Au,
- bottom_from_base: &mut Au,
- biggest_top: &mut Au,
- biggest_bottom: &mut Au)
- -> (Au, bool) {
- match cur_box.vertical_align() {
+ fn distance_from_baseline(fragment: &Box,
+ ascent: Au,
+ parent_text_top: Au,
+ parent_text_bottom: Au,
+ height_above_baseline: &mut Au,
+ depth_below_baseline: &mut Au,
+ largest_height_for_top_fragments: &mut Au,
+ largest_height_for_bottom_fragments: &mut Au)
+ -> (Au, bool) {
+ match fragment.vertical_align() {
vertical_align::baseline => (-ascent, false),
vertical_align::middle => {
// TODO: x-height value should be used from font info.
- let xheight = Au::new(0);
- (-(xheight + cur_box.content_height()).scale_by(0.5), false)
+ let xheight = Au(0);
+ (-(xheight + fragment.content_height()).scale_by(0.5), false)
},
vertical_align::sub => {
- // TODO: The proper position for subscripts should be used.
- // Lower the baseline to the proper position for subscripts
- let sub_offset = Au::new(0);
+ // TODO: The proper position for subscripts should be used. Lower the baseline to
+ // the proper position for subscripts.
+ let sub_offset = Au(0);
(sub_offset - ascent, false)
},
vertical_align::super_ => {
- // TODO: The proper position for superscripts should be used.
- // Raise the baseline to the proper position for superscripts
- let super_offset = Au::new(0);
+ // TODO: The proper position for superscripts should be used. Raise the baseline to
+ // the proper position for superscripts.
+ let super_offset = Au(0);
(-super_offset - ascent, false)
},
vertical_align::text_top => {
- let box_height = *top_from_base + *bottom_from_base;
- let prev_bottom_from_base = *bottom_from_base;
- *top_from_base = parent_text_top;
- *bottom_from_base = box_height - *top_from_base;
- (*bottom_from_base - prev_bottom_from_base - ascent, false)
+ let box_height = *height_above_baseline + *depth_below_baseline;
+ let prev_depth_below_baseline = *depth_below_baseline;
+ *height_above_baseline = parent_text_top;
+ *depth_below_baseline = box_height - *height_above_baseline;
+ (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
},
vertical_align::text_bottom => {
- let box_height = *top_from_base + *bottom_from_base;
- let prev_bottom_from_base = *bottom_from_base;
- *bottom_from_base = parent_text_bottom;
- *top_from_base = box_height - *bottom_from_base;
- (*bottom_from_base - prev_bottom_from_base - ascent, false)
+ let box_height = *height_above_baseline + *depth_below_baseline;
+ let prev_depth_below_baseline = *depth_below_baseline;
+ *depth_below_baseline = parent_text_bottom;
+ *height_above_baseline = box_height - *depth_below_baseline;
+ (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
},
vertical_align::top => {
- if *biggest_top < (*top_from_base + *bottom_from_base) {
- *biggest_top = *top_from_base + *bottom_from_base;
- }
- let offset_top = *top_from_base - ascent;
+ *largest_height_for_top_fragments =
+ Au::max(*largest_height_for_top_fragments,
+ *height_above_baseline + *depth_below_baseline);
+ let offset_top = *height_above_baseline - ascent;
(offset_top, true)
},
vertical_align::bottom => {
- if *biggest_bottom < (*top_from_base + *bottom_from_base) {
- *biggest_bottom = *top_from_base + *bottom_from_base;
- }
- let offset_bottom = -(*bottom_from_base + ascent);
+ *largest_height_for_bottom_fragments =
+ Au::max(*largest_height_for_bottom_fragments,
+ *height_above_baseline + *depth_below_baseline);
+ let offset_bottom = -(*depth_below_baseline + ascent);
(offset_bottom, true)
},
vertical_align::Length(length) => (-(length + ascent), false),
vertical_align::Percentage(p) => {
- let pt_size = cur_box.font_style().pt_size;
- let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size));
+ let pt_size = fragment.font_style().pt_size;
+ let line_height = fragment.calculate_line_height(Au::from_pt(pt_size));
let percent_offset = line_height.scale_by(p);
(-(percent_offset + ascent), false)
}
@@ -718,6 +727,21 @@ impl InlineFlow {
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 {
@@ -734,12 +758,8 @@ impl Flow for InlineFlow {
}
fn bubble_widths(&mut self, _: &mut LayoutContext) {
- let mut num_floats = 0;
-
for kid in self.base.child_iter() {
- let child_base = flow::mut_base(kid);
- num_floats += child_base.num_floats;
- child_base.floats = Floats::new();
+ flow::mut_base(kid).floats = Floats::new();
}
let mut intrinsic_widths = IntrinsicWidths::new();
@@ -754,7 +774,6 @@ impl Flow for InlineFlow {
}
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
@@ -786,16 +805,7 @@ impl Flow for InlineFlow {
// 'inline-block' box that created this flow before recursing.
}
- fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
- 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
+ /// Calculate and set the height of this flow. See CSS 2.1 § 10.6.1.
fn assign_height(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow");
@@ -817,85 +827,42 @@ impl Flow for InlineFlow {
let scanner_floats = self.base.floats.clone();
let mut scanner = LineboxScanner::new(scanner_floats);
-
- // Access the linebox scanner.
scanner.scan_for_lines(self);
- let mut line_height_offset = Au::new(0);
// All lines use text alignment of the flow.
let text_align = self.base.flags.text_align();
// 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() {
// Lay out boxes horizontally.
InlineFlow::set_horizontal_box_positions(&mut self.boxes, line, text_align);
- // Set the top y position of the current linebox.
+ // Set the top y position of the current line box.
// `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 the baseline to the top and bottom of the line box.
+ let mut largest_height_above_baseline = self.minimum_height_above_baseline;
+ let mut largest_depth_below_baseline = self.minimum_depth_below_baseline;
- // Calculate the distance from baseline to the top and bottom of the linebox.
- let (mut topmost, mut bottommost) = (Au(0), Au(0));
- // Calculate the biggest height among boxes with 'top' and 'bottom' values
+ // Calculate the largest height among boxes with 'top' and 'bottom' values
// 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() {
- let cur_box = self.boxes.boxes.get_mut(box_i);
-
- let top = cur_box.border_padding.top;
-
- // FIXME(pcwalton): Move into `box.rs` like the rest of box-specific layout code?
- let (top_from_base, bottom_from_base, ascent) = match cur_box.specific {
- ImageBox(_) => {
- 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 fragment = self.boxes.boxes.get_mut(box_i);
- let mut top_from_base = top_from_base;
- let mut bottom_from_base = bottom_from_base;
+ let InlineMetrics {
+ height_above_baseline: mut height_above_baseline,
+ depth_below_baseline: mut depth_below_baseline,
+ ascent
+ } = fragment.inline_metrics();
- // 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
+ // To calculate text-top and text-bottom value when `vertical-align` is involved,
+ // we should find the top and bottom of the content area of the parent box.
+ // "Content area" is defined in CSS 2.1 § 10.6.1.
//
// 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
@@ -904,74 +871,84 @@ impl Flow for InlineFlow {
// 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.
//
- // 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.
- 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
// 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
- // updated or not. That is, if the box has a `top` or `bottom` value,
- // `no_update_flag` becomes true.
- let (offset, no_update_flag) =
- InlineFlow::relative_offset_from_baseline(cur_box,
- ascent,
- parent_text_top,
- parent_text_bottom,
- &mut top_from_base,
- &mut bottom_from_base,
- &mut biggest_top,
- &mut biggest_bottom);
-
- // If the current box has 'top' or 'bottom' value, no_update_flag is true.
- // Otherwise, topmost and bottomost are updated.
- if !no_update_flag && top_from_base > topmost {
- topmost = top_from_base;
- }
- if !no_update_flag && bottom_from_base > bottommost {
- bottommost = bottom_from_base;
+ // The no-update flag decides whether `largest_height_for_top_fragments` and
+ // `largest_height_for_bottom_fragments` are to be updated or not. This will be set
+ // if and only if the fragment has `vertical-align` set to `top` or `bottom`.
+ let (distance_from_baseline, no_update_flag) =
+ InlineFlow::distance_from_baseline(
+ fragment,
+ ascent,
+ parent_text_top,
+ parent_text_bottom,
+ &mut height_above_baseline,
+ &mut depth_below_baseline,
+ &mut largest_height_for_top_fragments,
+ &mut largest_height_for_bottom_fragments);
+
+ // Unless the current fragment has `vertical-align` set to `top` or `bottom`,
+ // `largest_height_above_baseline` and `largest_depth_below_baseline` are updated.
+ if !no_update_flag {
+ largest_height_above_baseline = Au::max(height_above_baseline,
+ largest_height_above_baseline);
+ largest_depth_below_baseline = Au::max(depth_below_baseline,
+ 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'
- // value. Then, if necessary, update the topmost.
- let topmost_of_bottom = biggest_bottom - bottommost;
- if topmost_of_bottom > topmost {
- topmost = topmost_of_bottom;
- }
+ // Calculate the distance from the baseline to the top of the largest box with a
+ // value for `bottom`. Then, if necessary, update `largest_height_above_baseline`.
+ largest_height_above_baseline =
+ Au::max(largest_height_above_baseline,
+ largest_height_for_bottom_fragments - largest_depth_below_baseline);
- // Calculate the distance from baseline to the bottom of the biggest box with 'top'
- // value. Then, if necessary, update the bottommost.
- let bottommost_of_top = biggest_top - topmost;
- if bottommost_of_top > bottommost {
- bottommost = bottommost_of_top;
- }
+ // Calculate the distance from baseline to the bottom of the largest box with a value
+ // for `top`. Then, if necessary, update `largest_depth_below_baseline`.
+ largest_depth_below_baseline =
+ Au::max(largest_depth_below_baseline,
+ largest_height_for_top_fragments - largest_height_above_baseline);
- // Now, the baseline offset from the top of linebox is set as topmost.
- let baseline_offset = topmost;
+ // Now, the distance from the logical top of the line box to the baseline can be
+ // 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() {
- let cur_box = self.boxes.get_mut(box_i);
- let adjust_offset = match cur_box.vertical_align() {
- vertical_align::top => Au::new(0),
- vertical_align::bottom => baseline_offset + bottommost,
- _ => baseline_offset,
- };
-
- cur_box.border_box.origin.y = cur_box.border_box.origin.y + adjust_offset;
+ let fragment = self.boxes.get_mut(box_i);
+ match fragment.vertical_align() {
+ vertical_align::top => {
+ fragment.border_box.origin.y = fragment.border_box.origin.y +
+ line_distance_from_flow_top
+ }
+ vertical_align::bottom => {
+ 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 linebox in the next loop.
- line_height_offset = line_height_offset + topmost + bottommost -
- line.bounds.size.height;
- line.bounds.size.height = topmost + bottommost;
+ // This is used to set the top y position of the next line box in the next loop.
+ line.bounds.size.height = largest_height_above_baseline + largest_depth_below_baseline;
+ line_distance_from_flow_top = line_distance_from_flow_top + line.bounds.size.height;
} // End of `lines.each` loop.
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
/// DOM structure from the flat box list when it's needed.
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.
pub struct RangeIterator<'a> {
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,
+ }
+ }
+}
+
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index 64b833fc55b..e21cd7a52c8 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -11,21 +11,21 @@ use css::select::new_stylist;
use css::node_style::StyledNode;
use layout::construct::{FlowConstructionResult, NoConstructionResult};
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow;
use layout::incremental::RestyleDamage;
use layout::parallel::PaddedUnsafeFlow;
use layout::parallel;
-use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods};
+use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
+use collections::dlist::DList;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
-use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
-use gfx::display_list::{OpaqueNode, StackingContext};
+use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem};
+use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode};
use gfx::font_context::{FontContext, FontContextInfo};
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
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;
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;
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.
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.
@@ -225,7 +229,31 @@ impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
#[inline]
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()),
parallel_traversal: parallel_traversal,
profiler_chan: profiler_chan,
- opts: opts.clone()
+ opts: opts.clone(),
+ dirty: Rect::zero(),
}
}
@@ -349,6 +378,7 @@ impl LayoutTask {
url: (*url).clone(),
reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root),
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.
if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
- let mut root_stacking_context = StackingContext::new();
- 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_ctx.dirty = flow::base(layout_root).position.clone();
- layout_root.build_display_list(&mut root_stacking_context,
- &mut display_list_builder,
- &display_list_building_info);
+ match self.parallel_traversal {
+ None => {
+ 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
// it with extreme prejudice.
@@ -677,12 +710,9 @@ impl LayoutTask {
}
}
- let root_size = Size2D(display_list_building_info.relative_containing_block_size
- .width
- .to_nearest_px() as uint,
- display_list_building_info.relative_containing_block_size
- .height
- .to_nearest_px() as uint);
+ let root_size = flow::base(layout_root).position.size;
+ let root_size = Size2D(root_size.width.to_nearest_px() as uint,
+ root_size.height.to_nearest_px() as uint);
let render_layer = RenderLayer {
id: layout_root.layer_id(0),
display_list: display_list.clone(),
@@ -693,13 +723,15 @@ impl LayoutTask {
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();
layers.push(render_layer);
- let DisplayListBuilder {
- layers: sublayers,
- ..
- } = display_list_builder;
- layers.push_all_move(sublayers);
+ for layer in mem::replace(&mut flow::mut_base(layout_root).layers,
+ DList::new()).move_iter() {
+ layers.push(layer)
+ }
debug!("Layout done!");
@@ -726,7 +758,6 @@ impl LayoutTask {
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
ContentBoxQuery(node, reply_chan) => {
let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
-
fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>,
mut iter: DisplayItemIterator,
node: OpaqueNode) {
@@ -774,28 +805,22 @@ impl LayoutTask {
reply_chan.send(ContentBoxesResponse(boxes))
}
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> {
- for item in list.rev_iter() {
+ for item in iterator {
match *item {
ClipDisplayItemClass(ref cc) => {
- if !cc.need_clip || geometry::rect_contains_point(cc.base.bounds,
- Point2D(x, y)) {
- let ret = hit_test(x, y, cc.child_list.as_slice());
+ if geometry::rect_contains_point(cc.base.bounds, Point2D(x, y)) {
+ let ret = hit_test(x, y, cc.children.list.rev_iter());
if !ret.is_none() {
return ret
}
}
+ continue
}
_ => {}
}
- }
- for item in list.rev_iter() {
- match *item {
- ClipDisplayItemClass(_) => continue,
- _ => {}
- }
let bounds = item.bounds();
// 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));
let resp = match self.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() {
reply_chan.send(Ok(resp.unwrap()));
@@ -826,44 +851,43 @@ impl LayoutTask {
}
MouseOverQuery(_, point, reply_chan) => {
- fn mouse_over_test(x: Au,
+ fn mouse_over_test<'a,
+ I:Iterator<&'a DisplayItem>>(
+ x: Au,
y: Au,
- list: &[DisplayItem],
+ mut iterator: I,
result: &mut Vec<UntrustedNodeAddress>) {
- for item in list.rev_iter() {
+ for item in iterator {
match *item {
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);
+ }
+ _ => {
+ let bounds = item.bounds();
+
+ // TODO(tikue): This check should really be performed by a method
+ // of DisplayItem.
+ if x < bounds.origin.x + bounds.size.width &&
+ bounds.origin.x <= x &&
+ y < bounds.origin.y + bounds.size.height &&
+ bounds.origin.y <= y {
+ result.push(item.base()
+ .node
+ .to_untrusted_node_address());
+ }
}
- _ => {}
- }
- }
-
- for item in list.rev_iter() {
- let bounds = item.bounds();
-
- // TODO(tikue): This check should really be performed by a method of
- // DisplayItem.
- if x < bounds.origin.x + bounds.size.width &&
- bounds.origin.x <= x &&
- y < bounds.origin.y + bounds.size.height &&
- bounds.origin.y <= y {
- result.push(item.base()
- .node
- .to_untrusted_node_address());
}
}
}
let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!();
- let (x, y) = (Au::from_frac_px(point.x as f64),
- Au::from_frac_px(point.y as f64));
+ let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
match self.display_list {
None => fail!("no display list!"),
Some(ref display_list) => {
mouse_over_test(x,
y,
- display_list.list.as_slice(),
+ display_list.list.rev_iter(),
&mut mouse_over_list);
}
};
diff --git a/src/components/main/layout/parallel.rs b/src/components/main/layout/parallel.rs
index b3d8ae08594..983ba09d4d5 100644
--- a/src/components/main/layout/parallel.rs
+++ b/src/components/main/layout/parallel.rs
@@ -10,7 +10,7 @@ use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasS
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
use layout::extra::LayoutAuxMethods;
-use layout::flow::{Flow, PreorderFlowTraversal, PostorderFlowTraversal};
+use layout::flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal};
use layout::layout_task::{BubbleWidthsTraversal};
@@ -105,6 +105,8 @@ impl DomParallelInfo {
pub struct FlowParallelInfo {
/// The number of children that still need work done.
pub children_count: AtomicInt,
+ /// The number of children and absolute descendants that still need work done.
+ pub children_and_absolute_descendant_count: AtomicInt,
/// The address of the parent flow.
pub parent: UnsafeFlow,
}
@@ -113,6 +115,7 @@ impl FlowParallelInfo {
pub fn new() -> FlowParallelInfo {
FlowParallelInfo {
children_count: AtomicInt::new(0),
+ children_and_absolute_descendant_count: AtomicInt::new(0),
parent: null_unsafe_flow(),
}
}
@@ -180,6 +183,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
unsafe_flow: UnsafeFlow,
proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>);
+ #[inline(always)]
fn run_parallel_helper(&mut self,
unsafe_flow: UnsafeFlow,
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)
}
+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,
layout_context: &mut LayoutContext,
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()
}
+
+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()
+}
+
diff --git a/src/components/main/layout/table.rs b/src/components/main/layout/table.rs
index 1a558374e51..81305216cd4 100644
--- a/src/components/main/layout/table.rs
+++ b/src/components/main/layout/table.rs
@@ -9,14 +9,11 @@ use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor;
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;
use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout};
use layout::wrapper::ThreadSafeLayoutNode;
-use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
use servo_util::geometry;
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
/// methods
#[inline(always)]
- fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) {
- self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
+ fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext) {
+ self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse);
}
- pub fn build_display_list_table(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_table(&mut self, layout_context: &LayoutContext) {
debug!("build_display_list_table: same process as block flow");
- self.block_flow.build_display_list_block(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 pref_width = Au(0);
let mut did_first_row = false;
- let mut num_floats = 0;
for kid in self.block_flow.base.child_iter() {
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.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
@@ -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()));
}
- /// 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) {
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 {
diff --git a/src/components/main/layout/table_caption.rs b/src/components/main/layout/table_caption.rs
index 38d0260ac53..6be42bf3730 100644
--- a/src/components/main/layout/table_caption.rs
+++ b/src/components/main/layout/table_caption.rs
@@ -7,12 +7,9 @@
use layout::block::BlockFlow;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCaptionFlowClass, FlowClass, Flow};
use layout::wrapper::ThreadSafeLayoutNode;
-use gfx::display_list::StackingContext;
-
/// A table formatting context.
pub struct TableCaptionFlow {
pub block_flow: BlockFlow,
@@ -31,12 +28,9 @@ impl TableCaptionFlow {
self.block_flow.teardown();
}
- pub fn build_display_list_table_caption(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_table_caption(&mut self, layout_context: &LayoutContext) {
debug!("build_display_list_table_caption: same process as block flow");
- self.block_flow.build_display_list_block(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);
}
- /// 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) {
debug!("assign_height: assigning height for table_caption");
self.block_flow.assign_height(ctx);
}
+ fn compute_absolute_position(&mut self) {
+ self.block_flow.compute_absolute_position()
+ }
+
fn debug_str(&self) -> ~str {
let txt = ~"TableCaptionFlow: ";
txt.append(self.block_flow.box_.debug_str())
diff --git a/src/components/main/layout/table_cell.rs b/src/components/main/layout/table_cell.rs
index 17dbbf45efb..3af8f434969 100644
--- a/src/components/main/layout/table_cell.rs
+++ b/src/components/main/layout/table_cell.rs
@@ -7,13 +7,11 @@
use layout::box_::Box;
use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCellFlowClass, FlowClass, Flow};
use layout::model::{MaybeAuto};
use layout::table::InternalTable;
use layout::wrapper::ThreadSafeLayoutNode;
-use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
/// A table formatting context.
@@ -23,9 +21,7 @@ pub struct TableCellFlow {
}
impl TableCellFlow {
- pub fn from_node_and_box(node: &ThreadSafeLayoutNode,
- box_: Box)
- -> TableCellFlow {
+ pub fn from_node_and_box(node: &ThreadSafeLayoutNode, box_: Box) -> TableCellFlow {
TableCellFlow {
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
/// methods
#[inline(always)]
- fn assign_height_table_cell_base(&mut self,
- layout_context: &mut LayoutContext,
- inorder: bool) {
- self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse)
+ fn assign_height_table_cell_base(&mut self, layout_context: &mut LayoutContext) {
+ self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse)
}
- pub fn build_display_list_table_cell(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_table_cell(&mut self, layout_context: &LayoutContext) {
debug!("build_display_list_table: same process as block flow");
- self.block_flow.build_display_list_block(stacking_context, builder, info)
+ self.block_flow.build_display_list_block(layout_context)
}
}
@@ -114,18 +105,13 @@ impl Flow for TableCellFlow {
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) {
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 {
diff --git a/src/components/main/layout/table_row.rs b/src/components/main/layout/table_row.rs
index 274df4d3bcd..4bc80b53fa5 100644
--- a/src/components/main/layout/table_row.rs
+++ b/src/components/main/layout/table_row.rs
@@ -9,14 +9,12 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow;
use layout::table::InternalTable;
use layout::model::{MaybeAuto, Specified, Auto};
use layout::wrapper::ThreadSafeLayoutNode;
-use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
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
/// methods
#[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 /* 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 )
let mut max_y = Au::new(0);
for kid in self.block_flow.base.child_iter() {
- if inorder {
- kid.assign_height_inorder(layout_context)
- }
+ kid.assign_height_for_inorder_child_if_necessary(layout_context);
{
let child_box = kid.as_table_cell().box_();
@@ -136,12 +132,9 @@ impl TableRowFlow {
}
}
- pub fn build_display_list_table_row(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_table_row(&mut self, layout_context: &LayoutContext) {
debug!("build_display_list_table_row: same process as block flow");
- self.block_flow.build_display_list_block(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) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
- let mut num_floats = 0;
/* find the specified widths from child table-cell contexts */
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_cell());
@@ -198,9 +190,7 @@ impl Flow for TableRowFlow {
self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width);
min_width = min_width + child_base.intrinsic_widths.minimum_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.preferred_width = geometry::max(min_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()));
}
- /// 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) {
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 {
diff --git a/src/components/main/layout/table_rowgroup.rs b/src/components/main/layout/table_rowgroup.rs
index ece55cd8bdf..e8e11bd8a66 100644
--- a/src/components/main/layout/table_rowgroup.rs
+++ b/src/components/main/layout/table_rowgroup.rs
@@ -9,13 +9,11 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow;
use layout::table::{InternalTable, TableFlow};
use layout::wrapper::ThreadSafeLayoutNode;
-use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
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
/// methods
#[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 mut cur_y = top_offset;
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);
child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height;
@@ -99,12 +99,9 @@ impl TableRowGroupFlow {
self.block_flow.base.position.size.height = height;
}
- pub fn build_display_list_table_rowgroup(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_table_rowgroup(&mut self, layout_context: &LayoutContext) {
debug!("build_display_list_table_rowgroup: same process as block flow");
- self.block_flow.build_display_list_block(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) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
- let mut num_floats = 0;
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_row());
@@ -174,11 +170,8 @@ impl Flow for TableRowGroupFlow {
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.preferred_width = geometry::max(min_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()));
}
- /// 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) {
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 {
diff --git a/src/components/main/layout/table_wrapper.rs b/src/components/main/layout/table_wrapper.rs
index 060cbee6713..8cb064a7add 100644
--- a/src/components/main/layout/table_wrapper.rs
+++ b/src/components/main/layout/table_wrapper.rs
@@ -9,13 +9,11 @@ use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::FloatKind;
use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::model::{Specified, Auto, specified};
use layout::wrapper::ThreadSafeLayoutNode;
-use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
use servo_util::geometry;
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
/// methods
#[inline(always)]
- fn assign_height_table_wrapper_base(&mut self,
- layout_context: &mut LayoutContext,
- inorder: bool) {
- self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
+ fn assign_height_table_wrapper_base(&mut self, layout_context: &mut LayoutContext) {
+ self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse);
}
- pub fn build_display_list_table_wrapper(&mut self,
- stacking_context: &mut StackingContext,
- builder: &mut DisplayListBuilder,
- info: &DisplayListBuildingInfo) {
+ pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) {
debug!("build_display_list_table_wrapper: same process as block flow");
- self.block_flow.build_display_list_block(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);
}
- /// 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) {
if self.is_float() {
debug!("assign_height_float: assigning height for floated table_wrapper");
self.block_flow.assign_height_float(ctx);
} else {
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 {
let txt = if self.is_float() {
~"TableWrapperFlow(Float): "
diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs
index f60fececdf5..63773e23f83 100644
--- a/src/components/main/layout/text.rs
+++ b/src/components/main/layout/text.rs
@@ -8,14 +8,17 @@ use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
use layout::flow::Flow;
use layout::inline::InlineBoxes;
+use gfx::font::{FontMetrics, FontStyle};
use gfx::font_context::FontContext;
use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
+use servo_util::geometry::Au;
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec0};
use std::mem;
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;
struct NewLinePositions {
@@ -256,3 +259,53 @@ impl TextRunScanner {
new_whitespace
} // 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)
+}
+
diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs
index 3a64b4cd6a9..48c782d7f3a 100644
--- a/src/components/main/layout/util.rs
+++ b/src/components/main/layout/util.rs
@@ -7,6 +7,7 @@ use layout::parallel::DomParallelInfo;
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use gfx::display_list::OpaqueNode;
+use gfx;
use libc::uintptr_t;
use script::dom::bindings::js::JS;
use script::dom::bindings::utils::Reflectable;
@@ -15,6 +16,7 @@ use script::layout_interface::{LayoutChan, UntrustedNodeAddress, TrustedNodeAddr
use std::cast;
use std::cell::{Ref, RefMut};
use style::ComputedValues;
+use style;
use sync::Arc;
/// Data that layout associates with a node.
@@ -148,6 +150,17 @@ impl OpaqueNodeMethods for OpaqueNode {
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)
+ }
}
diff --git a/src/components/main/servo.rs b/src/components/main/servo.rs
index 91e3b1c7c5b..37b9092a7c0 100755
--- a/src/components/main/servo.rs
+++ b/src/components/main/servo.rs
@@ -98,7 +98,6 @@ pub mod layout {
pub mod box_;
pub mod construct;
pub mod context;
- pub mod display_list_builder;
pub mod floats;
pub mod flow;
pub mod flow_list;
diff --git a/src/components/net/image/holder.rs b/src/components/net/image/holder.rs
index cf9a65ef61f..40a9739fb37 100644
--- a/src/components/net/image/holder.rs
+++ b/src/components/net/image/holder.rs
@@ -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>> {
debug!("get_image() {}", self.url.to_str());