diff options
author | bors-servo <release+servo@mozilla.com> | 2014-05-02 19:10:20 -0400 |
---|---|---|
committer | bors-servo <release+servo@mozilla.com> | 2014-05-02 19:10:20 -0400 |
commit | 1a88996c0438212a4e77369515a243c9c24c4518 (patch) | |
tree | 8d3ca70eb151331cb0acefdb397e857e2c7c284f /src | |
parent | 1ab22d947008b90e01c565736d3d3953ad491648 (diff) | |
parent | 81f5da9dd852065cdf87a9d038d45cd8d8df3662 (diff) | |
download | servo-1a88996c0438212a4e77369515a243c9c24c4518.tar.gz servo-1a88996c0438212a4e77369515a243c9c24c4518.zip |
auto merge of #2235 : pcwalton/servo/parallel-floats, r=larsbergstrom
layout: Rewrite display list building to be parallel and to handle
overflow correctly, and opportunistically lay out blocks in parallel
even if floats are present.
This series of commits fixes the `inline-height-test` in Acid2 by
implementing proper overflow as well as the inline "strut". (See CSS 2.1
§ 10.8.1.) It was accidentally working before because tables' descendant
flows were not being laid out properly.
Display list building is now parallel and is done by bubbling up display
items and layers from parent to child. Speedups of around 60%-70% are
observed on Wikipedia with a 4-core HyperThreaded Core i7. More
optimizations are possible; this is just a start.
To minimize the amount of data that needs to be bubbled up, as well as
to make proper handling of `overflow: hidden` clipping easier, the
`StackingContext` abstraction is now purely internal to the display
list. Instead of placing items into a stacking context directly, display
items are placed into display lists with an explicit `StackingLevel`
provided. The stacking level determines a display item's position within
the stacking context. When a stacking context is finished, it is
flattened with the the `flatten` method, which shuffles the display
items that make up the context into their proper order while handling
clipping properly.
r? @SimonSapin and/or @larsbergstrom
Diffstat (limited to 'src')
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()); |