diff options
author | Junyoung Cho <jun0cho@gmail.com> | 2014-03-28 13:26:42 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-04-03 14:50:57 -0700 |
commit | 901c4483b210834e87b1d2458da28df90240a6d0 (patch) | |
tree | 8a1fbf84c00837610f9b1fd4ad1f208068f11f60 /src | |
parent | 10aed5bc1fd3966fccfa9999580229baedf00564 (diff) | |
download | servo-901c4483b210834e87b1d2458da28df90240a6d0.tar.gz servo-901c4483b210834e87b1d2458da28df90240a6d0.zip |
layout: Implement enough of automatic table layout to pass Acid2.
Diffstat (limited to 'src')
-rw-r--r-- | src/components/main/layout/block.rs | 105 | ||||
-rw-r--r-- | src/components/main/layout/flow.rs | 249 | ||||
-rw-r--r-- | src/components/main/layout/table.rs | 242 | ||||
-rw-r--r-- | src/components/main/layout/table_caption.rs | 31 | ||||
-rw-r--r-- | src/components/main/layout/table_cell.rs | 90 | ||||
-rw-r--r-- | src/components/main/layout/table_colgroup.rs | 6 | ||||
-rw-r--r-- | src/components/main/layout/table_row.rs | 94 | ||||
-rw-r--r-- | src/components/main/layout/table_rowgroup.rs | 109 | ||||
-rw-r--r-- | src/components/main/layout/table_wrapper.rs | 195 |
9 files changed, 570 insertions, 551 deletions
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 0711e43818a..e8d9cedae5d 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -1432,6 +1432,111 @@ impl BlockFlow { fn get_hypothetical_top_edge(&self) -> Au { self.base.position.origin.y } + + 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_info.flags.set_inorder(false); + } + } + + 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_info.flags.inorder() || self.base.num_floats > 0 + }; + + let kid_abs_cb_x_offset; + if self.is_positioned() { + match self.box_ { + Some(ref box_) => { + // Pass yourself as a new Containing Block + // The static x offset for any immediate kid flows will be the + // left padding + kid_abs_cb_x_offset = box_.padding.get().left; + } + None => fail!("BlockFlow: no principal box found"), + } + } else { + // For kids, the left margin edge will be at our left content edge. + // The current static offset is at our left margin + // edge. So move in to the left content edge. + kid_abs_cb_x_offset = self.base.absolute_static_x_offset + left_content_edge; + } + let kid_fixed_cb_x_offset = self.base.fixed_static_x_offset + left_content_edge; + + // This value is used only for table cells. + let mut kid_left_margin_edge = left_content_edge; + + // FIXME(ksh8281): avoid copy + let flags_info = self.base.flags_info.clone(); + for (i, kid) in self.base.child_iter().enumerate() { + if kid.is_block_flow() { + let kid_block = kid.as_block(); + kid_block.base.absolute_static_x_offset = kid_abs_cb_x_offset; + kid_block.base.fixed_static_x_offset = kid_fixed_cb_x_offset; + } + + { + let child_base = flow::mut_base(kid); + // Left margin edge of kid flow is at our left content edge + child_base.position.origin.x = left_content_edge; + // Width of kid flow is our content width + child_base.position.size.width = content_width; + child_base.flags_info.flags.set_inorder(has_inorder_children); + + if !child_base.flags_info.flags.inorder() { + child_base.floats = Floats::new(); + } + } + + // Handle tables. + match opt_col_widths { + Some(ref col_widths) => { + // If kid is table_rowgroup or table_row, the column widths info should be + // copied from its parent. + let kid_width; + if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() { + *kid.col_widths() = col_widths.clone(); + + // Width of kid flow is our content width. + kid_width = content_width + } else if kid.is_table_cell() { + // If kid is table_cell, the x offset and width for each cell should be + // calculated from parent's column widths info. + kid_left_margin_edge = if i == 0 { + Au(0) + } else { + kid_left_margin_edge + col_widths[i-1] + }; + + kid_width = col_widths[i] + } else { + // Width of kid flow is our content width. + kid_width = content_width + } + + let kid_base = flow::mut_base(kid); + kid_base.position.origin.x = kid_left_margin_edge; + kid_base.position.size.width = kid_width; + } + None => {} + } + + // Per CSS 2.1 § 16.3.1, text decoration propagates to all children in flow. + // + // TODO(pcwalton): When we have out-of-flow children, don't unconditionally propagate. + let child_base = flow::mut_base(kid); + child_base.flags_info.propagate_text_decoration_from_parent(&flags_info); + child_base.flags_info.propagate_text_alignment_from_parent(&flags_info) + } + } } impl Flow for BlockFlow { diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 0d1eff03f8c..b6119762e9c 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -26,14 +26,16 @@ /// similar methods. use css::node_style::StyledNode; -use layout::block::{BlockFlow}; +use layout::block::BlockFlow; use layout::box_::{Box, TableRowBox, TableCellBox}; -use layout::context::LayoutContext; use layout::construct::OptVector; -use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; +use layout::context::LayoutContext; +use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor}; use layout::floats::Floats; +use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator}; use layout::incremental::RestyleDamage; use layout::inline::InlineFlow; +use layout::model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo}; use layout::parallel::FlowParallelInfo; use layout::parallel; use layout::table_wrapper::TableWrapperFlow; @@ -44,24 +46,22 @@ use layout::table_row::TableRowFlow; use layout::table_caption::TableCaptionFlow; use layout::table_cell::TableCellFlow; use layout::wrapper::ThreadSafeLayoutNode; -use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator}; use collections::Deque; -use geom::point::Point2D; use geom::Size2D; +use geom::point::Point2D; use geom::rect::Rect; -use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection, DisplayList}; -use layout::display_list_builder::ToGfxColor; use gfx::color::Color; -use servo_util::smallvec::{SmallVec, SmallVec0}; +use gfx::display_list::StackingContext; +use servo_msg::compositor_msg::LayerId; use servo_util::geometry::Au; +use servo_util::smallvec::{SmallVec, SmallVec0}; use std::cast; -use std::cell::RefCell; +use std::iter::Zip; use std::sync::atomics::Relaxed; use std::vec::MutItems; -use std::iter::Zip; use style::ComputedValues; -use style::computed_values::{text_align, position}; +use style::computed_values::{clear, position, text_align}; /// Virtual methods that make up a float context. /// @@ -126,6 +126,24 @@ pub trait Flow { fail!("called as_table_cell() on a non-tablecell flow") } + /// If this is a table row or table rowgroup or table flow, returns column widths. + /// Fails otherwise. + fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] { + fail!("called col_widths() on an other flow than table-row/table-rowgroup/table") + } + + /// If this is a table row flow or table rowgroup flow or table flow, returns column min widths. + /// Fails otherwise. + fn col_min_widths<'a>(&'a self) -> &'a ~[Au] { + fail!("called col_min_widths() on an other flow than table-row/table-rowgroup/table") + } + + /// If this is a table row flow or table rowgroup flow or table flow, returns column min widths. + /// Fails otherwise. + fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] { + fail!("called col_pref_widths() on an other flow than table-row/table-rowgroup/table") + } + // Main methods /// Pass 1 of reflow: computes minimum and preferred widths. @@ -1094,183 +1112,62 @@ impl<'a> MutableFlowUtils for &'a mut Flow { None => fail!("empty Rawlink to a descendant") } } - - if self.is_root() { - for fixed_descendant_link in mut_base(self).fixed_descendants.iter() { - match fixed_descendant_link.resolve() { - Some(flow) => { - let mut kid_overflow = base(flow).overflow; - kid_overflow = kid_overflow.translate(&my_position.origin); - overflow = overflow.union(&kid_overflow) - } - None => fail!("empty Rawlink to a descendant") - } - } - } } mut_base(self).overflow = overflow; } - /// Push display items for current flow and its children onto `list`. + /// Push display items for current flow and its descendants onto the appropriate display lists + /// of the given stacking context. + /// + /// Arguments: /// - /// For InlineFlow, add display items for all its boxes onto list`. - /// For BlockFlow, add a ClipDisplayItemClass for itself and its children, - /// plus any other display items like border. + /// * `stacking_context`: The parent stacking context that this flow belongs to and to which + /// display items will be added. /// - /// `container_block_size`: Size of the Containing Block for the current - /// flow. This is used for relative positioning (which resolves percentage - /// values for 'top', etc. after all Containing Block heights have been computed.) - /// `absolute_cb_abs_position`: Absolute position of the Containing Block - /// for the flow if it is absolutely positioned. - fn build_display_lists<E:ExtraDisplayListData>( - self, - builder: &DisplayListBuilder, - container_block_size: &Size2D<Au>, - absolute_cb_abs_position: Point2D<Au>, - dirty: &Rect<Au>, - mut index: uint, - lists: &RefCell<DisplayListCollection<E>>) - -> bool { + /// * `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) { debug!("Flow: building display list"); - index = match self.class() { - BlockFlowClass => self.as_block().build_display_list_block(builder, - container_block_size, - absolute_cb_abs_position, - dirty, - index, - lists), - InlineFlowClass => self.as_inline().build_display_list_inline(builder, container_block_size, dirty, index, lists), - TableWrapperFlowClass => self.as_table_wrapper().build_display_list_table_wrapper(builder, - container_block_size, - absolute_cb_abs_position, - dirty, - index, - lists), - TableFlowClass => self.as_table().build_display_list_table(builder, - container_block_size, - absolute_cb_abs_position, - dirty, - index, - lists), - TableRowGroupFlowClass => self.as_table_rowgroup().build_display_list_table_rowgroup(builder, - container_block_size, - absolute_cb_abs_position, - dirty, - index, - lists), - TableRowFlowClass => self.as_table_row().build_display_list_table_row(builder, - container_block_size, - absolute_cb_abs_position, - dirty, - index, - lists), - TableCaptionFlowClass => self.as_table_caption().build_display_list_table_caption(builder, - container_block_size, - absolute_cb_abs_position, - dirty, - index, - lists), - TableCellFlowClass => self.as_table_cell().build_display_list_table_cell(builder, - container_block_size, - absolute_cb_abs_position, - dirty, - index, - lists), - TableColGroupFlowClass => index, - }; - - if lists.with_mut(|lists| lists.lists[index].list.len() == 0) { - return true; - } - - if self.is_block_container() || self.is_table_kind() { - let block = self.as_block(); - let mut child_lists = DisplayListCollection::new(); - child_lists.add_list(DisplayList::new()); - let child_lists = RefCell::new(child_lists); - let container_block_size; - let abs_cb_position; - // TODO(pradeep): Move this into a generated CB function and stuff in Flow. - match block.box_ { - Some(ref box_) => { - // The Containing Block formed by a Block for relatively - // positioned descendants is the content box. - container_block_size = box_.content_box_size(); - - abs_cb_position = if block.is_positioned() { - block.base.abs_position + block.generated_cb_position() - } else { - absolute_cb_abs_position - }; - } - None => fail!("Flow: block container should have a box_") + match self.class() { + BlockFlowClass => { + self.as_block().build_display_list_block(stacking_context, builder, info) } - - for kid in block.base.child_iter() { - if kid.is_absolutely_positioned() { - // All absolute flows will be handled by their CB. - continue; - } - kid.build_display_lists(builder, &container_block_size, - abs_cb_position, - dirty, 0u, &child_lists); + InlineFlowClass => { + self.as_inline().build_display_list_inline(stacking_context, builder, info) } - - // TODO: Maybe we should handle position 'absolute' and 'fixed' - // descendants before normal descendants just in case there is a - // problem when display-list building is parallel and both the - // original parent and this flow access the same absolute flow. - // Note that this can only be done once we have paint order - // working cos currently the later boxes paint over the absolute - // and fixed boxes :| - for abs_descendant_link in block.base.abs_descendants.iter() { - match abs_descendant_link.resolve() { - Some(flow) => { - // TODO(pradeep): Send in your abs_position directly. - flow.build_display_lists(builder, &container_block_size, - abs_cb_position, - dirty, 0u, &child_lists); - } - None => fail!("empty Rawlink to a descendant") - } + TableWrapperFlowClass => { + self.as_table_wrapper().build_display_list_table_wrapper(stacking_context, + builder, + info) } - - if block.is_root() { - for fixed_descendant_link in block.base.fixed_descendants.iter() { - match fixed_descendant_link.resolve() { - Some(flow) => { - flow.build_display_lists(builder, &container_block_size, - abs_cb_position, - dirty, 0u, &child_lists); - } - None => fail!("empty Rawlink to a descendant") - } - } + TableFlowClass => { + self.as_table().build_display_list_table(stacking_context, builder, info) + } + 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) + } + TableCaptionFlowClass => { + self.as_table_caption().build_display_list_table_caption(stacking_context, + builder, + info) + } + TableCellFlowClass => { + self.as_table_cell().build_display_list_table_cell(stacking_context, builder, info) + } + TableColGroupFlowClass => { + // Nothing to do here, I guess? --pcwalton } - - let mut child_lists = Some(child_lists.unwrap()); - // Find parent ClipDisplayItemClass and push all child display items - // under it - lists.with_mut(|lists| { - let mut child_lists = child_lists.take_unwrap(); - let result = lists.lists[index].list.mut_rev_iter().position(|item| { - match *item { - ClipDisplayItemClass(ref mut item) => { - item.child_list.push_all_move(child_lists.lists.shift().unwrap().list); - true - }, - _ => false, - } - }); - - if result.is_none() { - fail!("fail to find parent item"); - } - - lists.lists.push_all_move(child_lists.lists); - }); } - true } /// Destroys the flow. diff --git a/src/components/main/layout/table.rs b/src/components/main/layout/table.rs index 55ce4de5844..78bc267baf6 100644 --- a/src/components/main/layout/table.rs +++ b/src/components/main/layout/table.rs @@ -5,22 +5,21 @@ //! CSS table formatting contexts. use layout::box_::Box; -use layout::block::BlockFlow; -use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution}; +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, ExtraDisplayListData}; +use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; 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 std::cell::RefCell; -use style::computed_values::table_layout; -use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::DisplayListCollection; +use gfx::display_list::StackingContext; use servo_util::geometry::Au; +use servo_util::geometry; +use style::computed_values::table_layout; /// A table flow corresponded to the table's internal table box under a table wrapper flow. /// The properties `position`, `float`, and `margin-*` are used on the table wrapper box, @@ -31,6 +30,12 @@ pub struct TableFlow { /// Column widths col_widths: ~[Au], + /// Column min widths. + col_min_widths: ~[Au], + + /// Column pref widths. + col_pref_widths: ~[Au], + /// Table-layout property table_layout: TableLayout, } @@ -49,6 +54,8 @@ impl TableFlow { TableFlow { block_flow: block_flow, col_widths: ~[], + col_min_widths: ~[], + col_pref_widths: ~[], table_layout: table_layout } } @@ -66,6 +73,8 @@ impl TableFlow { TableFlow { block_flow: block_flow, col_widths: ~[], + col_min_widths: ~[], + col_pref_widths: ~[], table_layout: table_layout } } @@ -84,6 +93,8 @@ impl TableFlow { TableFlow { block_flow: block_flow, col_widths: ~[], + col_min_widths: ~[], + col_pref_widths: ~[], table_layout: table_layout } } @@ -91,61 +102,46 @@ impl TableFlow { pub fn teardown(&mut self) { self.block_flow.teardown(); self.col_widths = ~[]; + self.col_min_widths = ~[]; + self.col_pref_widths = ~[]; + } + + /// Update the corresponding value of self_widths if a value of kid_widths has larger value + /// than one of self_widths. + pub fn update_col_widths(self_widths: &mut ~[Au], kid_widths: &~[Au]) -> Au { + let mut sum_widths = Au(0); + let mut kid_widths_it = kid_widths.iter(); + for self_width in self_widths.mut_iter() { + match kid_widths_it.next() { + Some(kid_width) => { + if *self_width < *kid_width { + *self_width = *kid_width; + } + }, + None => {} + } + sum_widths = sum_widths + *self_width; + } + sum_widths } /// Assign height for table flow. /// + /// TODO(pcwalton): This probably doesn't handle margin collapse right. + /// /// 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, ctx: &mut LayoutContext, inorder: bool) { - - let (_, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(true); - - self.block_flow.handle_children_floats_if_necessary(ctx, inorder, - left_offset, top_offset); - - let mut cur_y = top_offset; - for kid in self.block_flow.base.child_iter() { - let child_node = flow::mut_base(kid); - child_node.position.origin.y = cur_y; - cur_y = cur_y + child_node.position.size.height; - } - - let height = cur_y - top_offset; - - let mut noncontent_height = Au::new(0); - for box_ in self.block_flow.box_.iter() { - let mut position = box_.border_box.get(); - - // noncontent_height = border_top/bottom + padding_top/bottom of box - noncontent_height = box_.noncontent_height(); - - position.origin.y = Au(0); - position.size.height = height + noncontent_height; - - box_.border_box.set(position); - } - - self.block_flow.base.position.size.height = height + noncontent_height; - - self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, - top_offset, bottom_offset, left_offset); + fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) { + self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse); } - pub fn build_display_list_table<E:ExtraDisplayListData>( - &mut self, - builder: &DisplayListBuilder, - container_block_size: &Size2D<Au>, - absolute_cb_abs_position: Point2D<Au>, - dirty: &Rect<Au>, - index: uint, - lists: &RefCell<DisplayListCollection<E>>) - -> uint { + pub fn build_display_list_table(&mut self, + stacking_context: &mut StackingContext, + builder: &mut DisplayListBuilder, + info: &DisplayListBuildingInfo) { debug!("build_display_list_table: same process as block flow"); - self.block_flow.build_display_list_block(builder, container_block_size, - absolute_cb_abs_position, - dirty, index, lists) + self.block_flow.build_display_list_block(stacking_context, builder, info); } } @@ -162,54 +158,95 @@ impl Flow for TableFlow { &mut self.block_flow } - /// This function finds the specified column widths from column group and the first row. - /// Those are used in fixed table layout calculation. - /* FIXME: automatic table layout calculation */ - fn bubble_widths(&mut self, ctx: &mut LayoutContext) { + fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] { + &mut self.col_widths + } + + fn col_min_widths<'a>(&'a self) -> &'a ~[Au] { + &self.col_min_widths + } + + fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] { + &self.col_pref_widths + } + + /// The specified column widths are set from column group and the first row for the fixed + /// table layout calculation. + /// The maximum min/pref widths of each column are set from the rows for the automatic + /// table layout calculation. + fn bubble_widths(&mut self, _: &mut LayoutContext) { + let mut min_width = Au(0); + let mut pref_width = Au(0); let mut did_first_row = false; + let mut num_floats = 0; - /* find max width from child block contexts */ for kid in self.block_flow.base.child_iter() { assert!(kid.is_proper_table_child()); if kid.is_table_colgroup() { self.col_widths.push_all(kid.as_table_colgroup().widths); + self.col_min_widths = self.col_widths.clone(); + self.col_pref_widths = self.col_widths.clone(); } else if kid.is_table_rowgroup() || kid.is_table_row() { // read column widths from table-row-group/table-row, and assign // width=0 for the columns not defined in column-group // FIXME: need to read widths from either table-header-group OR // first table-row - let kid_col_widths = if kid.is_table_rowgroup() { - &kid.as_table_rowgroup().col_widths - } else { - &kid.as_table_row().col_widths - }; match self.table_layout { - FixedLayout if !did_first_row => { - did_first_row = true; - let mut child_widths = kid_col_widths.iter(); - for col_width in self.col_widths.mut_iter() { - match child_widths.next() { - Some(child_width) => { - if *col_width == Au::new(0) { - *col_width = *child_width; - } - }, - None => break + FixedLayout => { + let kid_col_widths = kid.col_widths(); + if !did_first_row { + did_first_row = true; + let mut child_widths = kid_col_widths.iter(); + for col_width in self.col_widths.mut_iter() { + match child_widths.next() { + Some(child_width) => { + if *col_width == Au::new(0) { + *col_width = *child_width; + } + }, + None => break + } } } + let num_child_cols = kid_col_widths.len(); + let num_cols = self.col_widths.len(); + debug!("table until the previous row has {} column(s) and this row has {} column(s)", + num_cols, num_child_cols); + for i in range(num_cols, num_child_cols) { + self.col_widths.push( kid_col_widths[i] ); + } }, - _ => {} - } - let num_child_cols = kid_col_widths.len(); - let num_cols = self.col_widths.len(); - debug!("colgroup has {} column(s) and child has {} column(s)", num_cols, num_child_cols); - for i in range(num_cols, num_child_cols) { - self.col_widths.push( kid_col_widths[i] ); + AutoLayout => { + min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths()); + pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths()); + + // update the number of column widths from table-rows. + let num_cols = self.col_min_widths.len(); + let num_child_cols = kid.col_min_widths().len(); + debug!("table until the previous row has {} column(s) and this row has {} column(s)", + num_cols, num_child_cols); + for i in range(num_cols, num_child_cols) { + self.col_widths.push(Au::new(0)); + let new_kid_min = kid.col_min_widths()[i]; + self.col_min_widths.push( new_kid_min ); + let new_kid_pref = kid.col_pref_widths()[i]; + self.col_pref_widths.push( new_kid_pref ); + min_width = min_width + new_kid_min; + pref_width = pref_width + new_kid_pref; + } + } } } + let child_base = flow::mut_base(kid); + num_floats = num_floats + child_base.num_floats; + } + for box_ in self.block_flow.box_.iter() { + box_.compute_borders(box_.style()); } - self.block_flow.bubble_widths(ctx); + 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); } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called @@ -242,20 +279,25 @@ impl Flow for TableFlow { content_width = box_.border_box.get().size.width - padding_and_borders; } - // In fixed table layout, we distribute extra space among the unspecified columns if there are - // any, or among all the columns if all are specified. - if (total_column_width < content_width) && (num_unspecified_widths == 0) { - let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap(); - for col_width in self.col_widths.mut_iter() { - *col_width = (*col_width).scale_by(ratio); - } - } else if num_unspecified_widths != 0 { - let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths); - for col_width in self.col_widths.mut_iter() { - if *col_width == Au(0) { - *col_width = extra_column_width; + match self.table_layout { + FixedLayout => { + // In fixed table layout, we distribute extra space among the unspecified columns if there are + // any, or among all the columns if all are specified. + if (total_column_width < content_width) && (num_unspecified_widths == 0) { + let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap(); + for col_width in self.col_widths.mut_iter() { + *col_width = (*col_width).scale_by(ratio); + } + } else if num_unspecified_widths != 0 { + let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths); + for col_width in self.col_widths.mut_iter() { + if *col_width == Au(0) { + *col_width = extra_column_width; + } + } } } + _ => {} } self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone())); @@ -275,20 +317,6 @@ impl Flow for TableFlow { self.assign_height_table_base(ctx, false); } - // CSS Section 8.3.1 - Collapsing Margins - // Since `margin` is not used on table box, `collapsing` and `collapsible` are set to 0 - fn collapse_margins(&mut self, - _: bool, - _: &mut bool, - _: &mut Au, - _: &mut Au, - collapsing: &mut Au, - collapsible: &mut Au) { - // `margin` is not used on table box. - *collapsing = Au::new(0); - *collapsible = Au::new(0); - } - fn debug_str(&self) -> ~str { let txt = ~"TableFlow: "; txt.append(match self.block_flow.box_ { diff --git a/src/components/main/layout/table_caption.rs b/src/components/main/layout/table_caption.rs index b9ca7bd211a..7aecbe22fb5 100644 --- a/src/components/main/layout/table_caption.rs +++ b/src/components/main/layout/table_caption.rs @@ -7,14 +7,11 @@ use layout::block::BlockFlow; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; +use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::flow::{TableCaptionFlowClass, FlowClass, Flow}; use layout::wrapper::ThreadSafeLayoutNode; -use std::cell::RefCell; -use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::DisplayListCollection; -use servo_util::geometry::Au; +use gfx::display_list::StackingContext; /// A table formatting context. pub struct TableCaptionFlow { @@ -34,19 +31,12 @@ impl TableCaptionFlow { self.block_flow.teardown(); } - pub fn build_display_list_table_caption<E:ExtraDisplayListData>( - &mut self, - builder: &DisplayListBuilder, - container_block_size: &Size2D<Au>, - absolute_cb_abs_position: Point2D<Au>, - dirty: &Rect<Au>, - index: uint, - lists: &RefCell<DisplayListCollection<E>>) - -> uint { + pub fn build_display_list_table_caption(&mut self, + stacking_context: &mut StackingContext, + builder: &mut DisplayListBuilder, + info: &DisplayListBuildingInfo) { debug!("build_display_list_table_caption: same process as block flow"); - self.block_flow.build_display_list_block(builder, container_block_size, - absolute_cb_abs_position, - dirty, index, lists) + self.block_flow.build_display_list_block(stacking_context, builder, info) } } @@ -86,13 +76,6 @@ impl Flow for TableCaptionFlow { self.block_flow.assign_height(ctx); } - /// table-caption has margins but is not collapsed with a sibling(table) - /// or its parents(table-wrapper). - /// Therefore, margins to be collapsed do not exist. - fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au, - _: &mut Au, _: &mut Au, _: &mut Au) { - } - fn debug_str(&self) -> ~str { let txt = ~"TableCaptionFlow: "; txt.append(match self.block_flow.box_ { diff --git a/src/components/main/layout/table_cell.rs b/src/components/main/layout/table_cell.rs index 624f0d8deb4..68fff03caa4 100644 --- a/src/components/main/layout/table_cell.rs +++ b/src/components/main/layout/table_cell.rs @@ -5,16 +5,15 @@ //! CSS table formatting contexts. use layout::box_::Box; -use layout::block::{BlockFlow, WidthAndMarginsComputer}; +use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; +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 std::cell::RefCell; -use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::{DisplayListCollection}; +use gfx::display_list::StackingContext; use servo_util::geometry::Au; /// A table formatting context. @@ -42,62 +41,23 @@ impl TableCellFlow { /// Assign height for table-cell flow. /// + /// TODO(pcwalton): This doesn't handle floats right. + /// /// 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, ctx: &mut LayoutContext, inorder: bool) { - let (_, mut top_offset, bottom_offset, left_offset) = self.block_flow - .initialize_offsets(true); - - self.block_flow.handle_children_floats_if_necessary(ctx, inorder, - left_offset, top_offset); - let mut cur_y = top_offset; - // Since table cell does not have `margin`, the first child's top margin and - // the last child's bottom margin do not collapse. - self.block_flow.compute_margin_collapse(&mut cur_y, - &mut top_offset, - &mut Au(0), - &mut Au(0), - false, - false); - - // CSS 2.1 § 17.5.3. Table cell box height is the minimum height required by the content. - let height = cur_y - top_offset; - - // TODO(june0cho): vertical-align of table-cell should be calculated. - let mut noncontent_height = Au::new(0); - for box_ in self.block_flow.box_.iter() { - let mut position = box_.border_box.get(); - - // noncontent_height = border_top/bottom + padding_top/bottom of box - noncontent_height = box_.noncontent_height(); - - position.origin.y = Au(0); - position.size.height = height + noncontent_height; - - box_.border_box.set(position); - } - - self.block_flow.base.position.size.height = height + noncontent_height; - - self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, top_offset, - bottom_offset, left_offset); - self.block_flow.assign_height_absolute_flows(ctx); + 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) } - pub fn build_display_list_table_cell<E:ExtraDisplayListData>( - &mut self, - builder: &DisplayListBuilder, - container_block_size: &Size2D<Au>, - absolute_cb_abs_position: Point2D<Au>, - dirty: &Rect<Au>, - index: uint, - lists: &RefCell<DisplayListCollection<E>>) - -> uint { - debug!("build_display_list_table_cell: same process as block flow"); - self.block_flow.build_display_list_block(builder, container_block_size, - absolute_cb_abs_position, - dirty, index, lists) + pub fn build_display_list_table_cell(&mut self, + stacking_context: &mut StackingContext, + builder: &mut DisplayListBuilder, + info: &DisplayListBuildingInfo) { + debug!("build_display_list_table: same process as block flow"); + self.block_flow.build_display_list_block(stacking_context, builder, info) } } @@ -117,6 +77,18 @@ impl Flow for TableCellFlow { /// Minimum/preferred widths set by this function are used in automatic table layout calculation. fn bubble_widths(&mut self, ctx: &mut LayoutContext) { self.block_flow.bubble_widths(ctx); + for box_ in self.block_flow.box_.iter() { + let specified_width = MaybeAuto::from_style(box_.style().Box.get().width, + Au::new(0)).specified_or_zero(); + if self.block_flow.base.intrinsic_widths.minimum_width < specified_width { + self.block_flow.base.intrinsic_widths.minimum_width = specified_width; + } + if self.block_flow.base.intrinsic_widths.preferred_width < + self.block_flow.base.intrinsic_widths.minimum_width { + self.block_flow.base.intrinsic_widths.preferred_width = + self.block_flow.base.intrinsic_widths.minimum_width; + } + } } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called @@ -156,12 +128,6 @@ impl Flow for TableCellFlow { self.assign_height_table_cell_base(ctx, false); } - /// TableCellBox and their parents(TableRowBox) do not have margins. - /// Therefore, margins to be collapsed do not exist. - fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au, - _: &mut Au, _: &mut Au, _: &mut Au) { - } - fn debug_str(&self) -> ~str { let txt = ~"TableCellFlow: "; txt.append(match self.block_flow.box_ { diff --git a/src/components/main/layout/table_colgroup.rs b/src/components/main/layout/table_colgroup.rs index 8d9e1d98870..a53c825660f 100644 --- a/src/components/main/layout/table_colgroup.rs +++ b/src/components/main/layout/table_colgroup.rs @@ -82,12 +82,6 @@ impl Flow for TableColGroupFlow { fn assign_height(&mut self, _ctx: &mut LayoutContext) { } - /// TableColumnBox and their parents(TableBox) do not have margins. - /// Therefore, margins to be collapsed do not exist. - fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au, - _: &mut Au, _: &mut Au, _: &mut Au) { - } - fn debug_str(&self) -> ~str { let txt = ~"TableColGroupFlow: "; txt.append(match self.box_ { diff --git a/src/components/main/layout/table_row.rs b/src/components/main/layout/table_row.rs index 17ebaded351..8dbc40265d0 100644 --- a/src/components/main/layout/table_row.rs +++ b/src/components/main/layout/table_row.rs @@ -9,16 +9,14 @@ use layout::block::BlockFlow; use layout::block::WidthAndMarginsComputer; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; +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 std::cell::RefCell; -use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::DisplayListCollection; +use gfx::display_list::StackingContext; use servo_util::geometry::Au; use servo_util::geometry; @@ -28,6 +26,12 @@ pub struct TableRowFlow { /// Column widths. col_widths: ~[Au], + + /// Column min widths. + col_min_widths: ~[Au], + + /// Column pref widths. + col_pref_widths: ~[Au], } impl TableRowFlow { @@ -37,6 +41,8 @@ impl TableRowFlow { TableRowFlow { block_flow: BlockFlow::from_node_and_box(node, box_), col_widths: ~[], + col_min_widths: ~[], + col_pref_widths: ~[], } } @@ -46,12 +52,16 @@ impl TableRowFlow { TableRowFlow { block_flow: BlockFlow::from_node(constructor, node), col_widths: ~[], + col_min_widths: ~[], + col_pref_widths: ~[], } } pub fn teardown(&mut self) { self.block_flow.teardown(); self.col_widths = ~[]; + self.col_min_widths = ~[]; + self.col_pref_widths = ~[]; } pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{ @@ -66,19 +76,23 @@ impl TableRowFlow { /// Assign height for table-row flow. /// + /// TODO(pcwalton): This doesn't handle floats and positioned elements right. + /// /// 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, ctx: &mut LayoutContext, inorder: bool) { - let (top_offset, bottom_offset, left_offset) = self.initialize_offsets(); + fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) { + let (top_offset, _, _) = self.initialize_offsets(); - self.block_flow.handle_children_floats_if_necessary(ctx, inorder, - left_offset, top_offset); let mut cur_y = top_offset; // 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) + } + for child_box in kid.as_table_cell().box_().iter() { // TODO: Percentage height let child_specified_height = MaybeAuto::from_style(child_box.style().Box.get().height, @@ -101,6 +115,8 @@ impl TableRowFlow { cur_y = cur_y + height; // Assign the height of own box + // + // FIXME(pcwalton): Take `cur_y` into account. for box_ in self.block_flow.box_.iter() { let mut position = box_.border_box.get(); position.size.height = height; @@ -118,24 +134,14 @@ impl TableRowFlow { let child_node = flow::mut_base(kid); child_node.position.size.height = height; } - - self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, - top_offset, bottom_offset, left_offset); } - pub fn build_display_list_table_row<E:ExtraDisplayListData>( - &mut self, - builder: &DisplayListBuilder, - container_block_size: &Size2D<Au>, - absolute_cb_abs_position: Point2D<Au>, - dirty: &Rect<Au>, - index: uint, - lists: &RefCell<DisplayListCollection<E>>) - -> uint { + pub fn build_display_list_table_row(&mut self, + stacking_context: &mut StackingContext, + builder: &mut DisplayListBuilder, + info: &DisplayListBuildingInfo) { debug!("build_display_list_table_row: same process as block flow"); - self.block_flow.build_display_list_block(builder, container_block_size, - absolute_cb_abs_position, - dirty, index, lists) + self.block_flow.build_display_list_block(stacking_context, builder, info) } } @@ -152,27 +158,51 @@ impl Flow for TableRowFlow { &mut self.block_flow } + fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] { + &mut self.col_widths + } + + fn col_min_widths<'a>(&'a self) -> &'a ~[Au] { + &self.col_min_widths + } + + fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] { + &self.col_pref_widths + } + /// Recursively (bottom-up) determines 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. /// Min/pref widths set by this function are used in automatic table layout calculation. - /// Also, this function collects the specified column widths of children cells. Those are used - /// in fixed table layout calculation - fn bubble_widths(&mut self, ctx: &mut LayoutContext) { + /// The specified column widths of children cells are used in fixed table layout calculation. + 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()); + // collect the specified column widths of cells. These are used in fixed table layout calculation. for child_box in kid.as_table_cell().box_().iter() { let child_specified_width = MaybeAuto::from_style(child_box.style().Box.get().width, Au::new(0)).specified_or_zero(); self.col_widths.push(child_specified_width); } - } - // TODO: calculate min_width & pref_width for automatic table layout calculation - self.block_flow.bubble_widths(ctx); + // collect min_width & pref_width of children cells for automatic table layout calculation. + let child_base = flow::mut_base(kid); + self.col_min_widths.push(child_base.intrinsic_widths.minimum_width); + 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); } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called @@ -205,12 +235,6 @@ impl Flow for TableRowFlow { self.assign_height_table_row_base(ctx, false); } - /// TableRowBox and their parents(TableBox) do not have margins. - /// Therefore, margins to be collapsed do not exist. - fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au, - _: &mut Au, _: &mut Au, _: &mut Au) { - } - fn debug_str(&self) -> ~str { let txt = ~"TableRowFlow: "; txt.append(match self.block_flow.box_ { diff --git a/src/components/main/layout/table_rowgroup.rs b/src/components/main/layout/table_rowgroup.rs index 92bb505d1e9..a0b2364146c 100644 --- a/src/components/main/layout/table_rowgroup.rs +++ b/src/components/main/layout/table_rowgroup.rs @@ -9,16 +9,15 @@ use layout::block::BlockFlow; use layout::block::WidthAndMarginsComputer; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; +use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow; -use layout::table::InternalTable; +use layout::table::{InternalTable, TableFlow}; use layout::wrapper::ThreadSafeLayoutNode; -use std::cell::RefCell; -use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::DisplayListCollection; +use gfx::display_list::StackingContext; use servo_util::geometry::Au; +use servo_util::geometry; /// A table formatting context. pub struct TableRowGroupFlow { @@ -26,6 +25,12 @@ pub struct TableRowGroupFlow { /// Column widths col_widths: ~[Au], + + /// Column min widths. + col_min_widths: ~[Au], + + /// Column pref widths. + col_pref_widths: ~[Au], } impl TableRowGroupFlow { @@ -35,6 +40,8 @@ impl TableRowGroupFlow { TableRowGroupFlow { block_flow: BlockFlow::from_node_and_box(node, box_), col_widths: ~[], + col_min_widths: ~[], + col_pref_widths: ~[], } } @@ -44,12 +51,16 @@ impl TableRowGroupFlow { TableRowGroupFlow { block_flow: BlockFlow::from_node(constructor, node), col_widths: ~[], + col_min_widths: ~[], + col_pref_widths: ~[], } } pub fn teardown(&mut self) { self.block_flow.teardown(); self.col_widths = ~[]; + self.col_min_widths = ~[]; + self.col_pref_widths = ~[]; } pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{ @@ -64,14 +75,14 @@ impl TableRowGroupFlow { /// Assign height for table-rowgroup flow. /// + /// FIXME(pcwalton): This doesn't handle floats right. + /// /// 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, ctx: &mut LayoutContext, inorder: bool) { - let (top_offset, bottom_offset, left_offset) = self.initialize_offsets(); + fn assign_height_table_rowgroup_base(&mut self, _: &mut LayoutContext, _: bool) { + let (top_offset, _, _) = self.initialize_offsets(); - self.block_flow.handle_children_floats_if_necessary(ctx, inorder, - left_offset, top_offset); let mut cur_y = top_offset; for kid in self.block_flow.base.child_iter() { @@ -88,24 +99,14 @@ impl TableRowGroupFlow { box_.border_box.set(position); } self.block_flow.base.position.size.height = height; - - self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, - top_offset, bottom_offset, left_offset); } - pub fn build_display_list_table_rowgroup<E:ExtraDisplayListData>( - &mut self, - builder: &DisplayListBuilder, - container_block_size: &Size2D<Au>, - absolute_cb_abs_position: Point2D<Au>, - dirty: &Rect<Au>, - index: uint, - lists: &RefCell<DisplayListCollection<E>>) - -> uint { + pub fn build_display_list_table_rowgroup(&mut self, + stacking_context: &mut StackingContext, + builder: &mut DisplayListBuilder, + info: &DisplayListBuildingInfo) { debug!("build_display_list_table_rowgroup: same process as block flow"); - self.block_flow.build_display_list_block(builder, container_block_size, - absolute_cb_abs_position, - dirty, index, lists) + self.block_flow.build_display_list_block(stacking_context, builder, info) } } @@ -122,6 +123,18 @@ impl Flow for TableRowGroupFlow { &mut self.block_flow } + fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] { + &mut self.col_widths + } + + fn col_min_widths<'a>(&'a self) -> &'a ~[Au] { + &self.col_min_widths + } + + fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] { + &self.col_pref_widths + } + /// Recursively (bottom-up) determines 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 @@ -129,24 +142,48 @@ impl Flow for TableRowGroupFlow { /// Min/pref widths set by this function are used in automatic table layout calculation. /// Also, this function finds the specified column widths from the first row. /// Those are used in fixed table layout calculation - fn bubble_widths(&mut self, ctx: &mut LayoutContext) { - /* find the specified column widths from the first table-row. - update the number of column widths from other table-rows. */ + 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()); - if self.col_widths.is_empty() { - self.col_widths = kid.as_table_row().col_widths.clone(); + + // calculate min_width & pref_width for automatic table layout calculation + // 'self.col_min_widths' collects the maximum value of cells' min-widths for each column. + // 'self.col_pref_widths' collects the maximum value of cells' pref-widths for each column. + if self.col_widths.is_empty() { // First Row + assert!(self.col_min_widths.is_empty() && self.col_pref_widths.is_empty()); + // 'self.col_widths' collects the specified column widths from the first table-row for fixed table layout calculation. + self.col_widths = kid.col_widths().clone(); + self.col_min_widths = kid.col_min_widths().clone(); + self.col_pref_widths = kid.col_pref_widths().clone(); } else { + min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths()); + pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths()); + + // update the number of column widths from table-rows. let num_cols = self.col_widths.len(); - let num_child_cols = kid.as_table_row().col_widths.len(); - for _ in range(num_cols, num_child_cols) { + let num_child_cols = kid.col_min_widths().len(); + for i in range(num_cols, num_child_cols) { self.col_widths.push(Au::new(0)); + let new_kid_min = kid.col_min_widths()[i]; + self.col_min_widths.push(kid.col_min_widths()[i]); + let new_kid_pref = kid.col_pref_widths()[i]; + self.col_pref_widths.push(kid.col_pref_widths()[i]); + min_width = min_width + new_kid_min; + pref_width = pref_width + new_kid_pref; } } + let child_base = flow::mut_base(kid); + num_floats = num_floats + child_base.num_floats; } - // TODO: calculate min_width & pref_width for automatic table layout calculation - self.block_flow.bubble_widths(ctx); + 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); } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called @@ -180,12 +217,6 @@ impl Flow for TableRowGroupFlow { self.assign_height_table_rowgroup_base(ctx, false); } - /// TableRowBox and their parents(TableBox) do not have margins. - /// Therefore, margins to be collapsed do not exist. - fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au, - _: &mut Au, _: &mut Au, _: &mut Au) { - } - fn debug_str(&self) -> ~str { let txt = ~"TableRowGroupFlow: "; txt.append(match self.block_flow.box_ { diff --git a/src/components/main/layout/table_wrapper.rs b/src/components/main/layout/table_wrapper.rs index d0ddfa11f92..0a6636db81a 100644 --- a/src/components/main/layout/table_wrapper.rs +++ b/src/components/main/layout/table_wrapper.rs @@ -5,23 +5,20 @@ //! CSS table formatting contexts. use layout::box_::Box; -use layout::block::BlockFlow; -use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution}; +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, ExtraDisplayListData}; -use layout::floats::{FloatKind}; +use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; +use layout::floats::FloatKind; use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils}; -use layout::flow; -use layout::model::{MaybeAuto, Specified, Auto, specified}; +use layout::model::{Specified, Auto, specified}; use layout::wrapper::ThreadSafeLayoutNode; -use std::cell::RefCell; -use style::computed_values::table_layout; -use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::DisplayListCollection; +use gfx::display_list::StackingContext; use servo_util::geometry::Au; use servo_util::geometry; +use style::computed_values::table_layout; pub enum TableLayout { FixedLayout, @@ -103,77 +100,27 @@ impl TableWrapperFlow { /// Assign height for table-wrapper flow. /// `Assign height` of table-wrapper flow follows a similar process to that of block flow. - /// However, table-wrapper flow doesn't consider collapsing margins for flow's children - /// and calculating padding/border. - /// + /// /// 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, ctx: &mut LayoutContext, inorder: bool) { - - // Note: Ignoring clearance for absolute flows as of now. - let ignore_clear = self.is_absolutely_positioned(); - let (clearance, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(ignore_clear); - - self.block_flow.handle_children_floats_if_necessary(ctx, inorder, - left_offset, top_offset); - - // Table wrapper flow has margin but is not collapsed with kids(table caption and table). - let (margin_top, margin_bottom, _, _) = self.block_flow.precompute_margin(); + 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); + } - let mut cur_y = top_offset; + pub fn build_display_list_table_wrapper(&mut self, + stacking_context: &mut StackingContext, + builder: &mut DisplayListBuilder, + info: &DisplayListBuildingInfo) { + debug!("build_display_list_table_wrapper: same process as block flow"); + self.block_flow.build_display_list_block(stacking_context, builder, info); for kid in self.block_flow.base.child_iter() { - let child_node = flow::mut_base(kid); - child_node.position.origin.y = cur_y; - cur_y = cur_y + child_node.position.size.height; + kid.as_table().block_flow.base.position.size.height = + self.block_flow.base.position.size.height; } - - // top_offset: top margin-edge of the topmost child. - // hence, height = content height - let mut height = cur_y - top_offset; - - // For an absolutely positioned element, store the content height and stop the function. - if self.block_flow.store_content_height_if_absolutely_positioned(height) { - return; - } - - for box_ in self.block_flow.box_.iter() { - let style = box_.style(); - - // At this point, `height` is the height of the containing block, so passing `height` - // as the second argument here effectively makes percentages relative to the containing - // block per CSS 2.1 § 10.5. - height = match MaybeAuto::from_style(style.Box.get().height, height) { - Auto => height, - Specified(value) => geometry::max(value, height) - }; - } - - self.block_flow.compute_height_position(&mut height, - Au(0), - margin_top, - margin_bottom, - clearance); - - self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, - top_offset, bottom_offset, left_offset); - self.block_flow.assign_height_absolute_flows(ctx); - } - - pub fn build_display_list_table_wrapper<E:ExtraDisplayListData>( - &mut self, - builder: &DisplayListBuilder, - container_block_size: &Size2D<Au>, - absolute_cb_abs_position: Point2D<Au>, - dirty: &Rect<Au>, - index: uint, - lists: &RefCell<DisplayListCollection<E>>) - -> uint { - debug!("build_display_list_table_wrapper: same process as block flow"); - self.block_flow.build_display_list_block(builder, container_block_size, - absolute_cb_abs_position, - dirty, index, lists) } } @@ -197,7 +144,7 @@ impl Flow for TableWrapperFlow { any boxes it is responsible for flowing. */ fn bubble_widths(&mut self, ctx: &mut LayoutContext) { - /* find max width from child block contexts */ + // get column widths info from table flow for kid in self.block_flow.base.child_iter() { assert!(kid.is_table_caption() || kid.is_table()); @@ -227,7 +174,7 @@ impl Flow for TableWrapperFlow { let mut left_content_edge = Au::new(0); let mut content_width = containing_block_width; - self.block_flow.set_containing_width_if_float(containing_block_width); + // self.block_flow.set_containing_width_if_float(containing_block_width); let width_computer = TableWrapper; width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width); @@ -243,7 +190,12 @@ impl Flow for TableWrapperFlow { _ => {} } - self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, None); + // In case of fixed layout, column widths are calculated in table flow. + let assigned_col_widths = match self.table_layout { + FixedLayout => None, + AutoLayout => Some(self.col_widths.clone()) + }; + 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. @@ -270,26 +222,6 @@ impl Flow for TableWrapperFlow { } } - // CSS Section 8.3.1 - Collapsing Margins - // `self`: the Flow whose margins we want to collapse. - // `collapsing`: value to be set by this function. This tells us how much - // of the top margin has collapsed with a previous margin. - // `collapsible`: Potential collapsible margin at the bottom of this flow's box. - fn collapse_margins(&mut self, - top_margin_collapsible: bool, - first_in_flow: &mut bool, - margin_top: &mut Au, - top_offset: &mut Au, - collapsing: &mut Au, - collapsible: &mut Au) { - self.block_flow.collapse_margins(top_margin_collapsible, - first_in_flow, - margin_top, - top_offset, - collapsing, - collapsible); - } - fn debug_str(&self) -> ~str { let txt = if self.is_float() { ~"TableWrapperFlow(Float): " @@ -327,10 +259,12 @@ impl TableWrapper { let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow, parent_flow_width, ctx); - match table_wrapper.table_layout { + let computed_width = match table_wrapper.table_layout { FixedLayout => { let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0), |sum, width| sum.add(width)); + + let mut computed_width = input.computed_width.specified_or_zero(); for box_ in table_wrapper.block_flow.box_.iter() { let style = box_.style(); @@ -344,15 +278,72 @@ impl TableWrapper { let border_left = style.Border.get().border_left_width; let border_right = style.Border.get().border_right_width; let padding_and_borders = padding_left + padding_right + border_left + border_right; - let mut computed_width = input.computed_width.specified_or_zero(); // Compare border-edge widths. Because fixed_cells_width indicates content-width, // padding and border values are added to fixed_cells_width. computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width); - input.computed_width = Specified(computed_width); } + computed_width }, - _ => {} - } + AutoLayout => { + // Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2. + // But, this spec is not specified. Since the new spec is specified, it may be modified. See #1687. + let mut cap_min = Au(0); + let mut cols_min = Au(0); + let mut cols_max = Au(0); + let mut col_min_widths = &~[]; + let mut col_pref_widths = &~[]; + for kid in table_wrapper.block_flow.base.child_iter() { + if kid.is_table_caption() { + cap_min = kid.as_block().base.intrinsic_widths.minimum_width; + } else { + assert!(kid.is_table()); + cols_min = kid.as_block().base.intrinsic_widths.minimum_width; + cols_max = kid.as_block().base.intrinsic_widths.preferred_width; + col_min_widths = kid.col_min_widths(); + col_pref_widths = kid.col_pref_widths(); + } + } + // 'extra_width': difference between the calculated table width and minimum width required by all columns. + // It will be distributed over the columns + let (width, extra_width) = match input.computed_width { + Auto => { + if input.available_width > geometry::max(cols_max, cap_min) { + if cols_max > cap_min { + table_wrapper.col_widths = col_pref_widths.clone(); + (cols_max, Au(0)) + } else { + (cap_min, cap_min - cols_min) + } + } else { + let max = if cols_min >= input.available_width && cols_min >= cap_min { + table_wrapper.col_widths = col_min_widths.clone(); + cols_min + } else { + geometry::max(input.available_width, cap_min) + }; + (max, max - cols_min) + } + }, + Specified(width) => { + let max = if cols_min >= width && cols_min >= cap_min { + table_wrapper.col_widths = col_min_widths.clone(); + cols_min + } else { + geometry::max(width, cap_min) + }; + (max, max - cols_min) + } + }; + // The extra width is distributed over the columns + if extra_width > Au(0) { + let cell_len = table_wrapper.col_widths.len() as f64; + table_wrapper.col_widths = col_min_widths.map(|width| + width + extra_width.scale_by(1.0/cell_len)); + } + width + } + }; + input.computed_width = Specified(computed_width); input } } |