diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-12-11 14:22:20 -0800 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-12-11 14:24:55 -0800 |
commit | 3cddaf8da69b147f4808019ccdfca12b5f293a40 (patch) | |
tree | 0bddd5028e5c4b4933ba1566f1b483fd7dcc45c8 | |
parent | 512d55ecefac4c5f7f6fc52ad5cedb7f2664b0c9 (diff) | |
download | servo-3cddaf8da69b147f4808019ccdfca12b5f293a40.tar.gz servo-3cddaf8da69b147f4808019ccdfca12b5f293a40.zip |
layout: Make table layout idempotent.
By "idempotent" I mean that later passes do not stomp on data from
earlier passes, so that we can run the passes individually for
incremental reflow. The main change here was to stop overwriting the
"minimum inline-size" field of each column with the column's computed
inline-size.
-rw-r--r-- | components/layout/block.rs | 26 | ||||
-rw-r--r-- | components/layout/flow.rs | 16 | ||||
-rw-r--r-- | components/layout/table.rs | 103 | ||||
-rw-r--r-- | components/layout/table_cell.rs | 4 | ||||
-rw-r--r-- | components/layout/table_row.rs | 39 | ||||
-rw-r--r-- | components/layout/table_rowgroup.rs | 85 | ||||
-rw-r--r-- | components/layout/table_wrapper.rs | 155 |
7 files changed, 243 insertions, 185 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index 6b141b1e749..8e8920e4033 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -47,7 +47,7 @@ use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse, MarginsCollapseThrough}; use model::{MaybeAuto, NoCollapsibleMargins, Specified, specified, specified_or_none}; -use table::ColumnInlineSize; +use table::ColumnComputedInlineSize; use wrapper::ThreadSafeLayoutNode; use geom::Size2D; @@ -1258,7 +1258,7 @@ impl BlockFlow { &mut self, inline_start_content_edge: Au, content_inline_size: Au, - optional_column_inline_sizes: Option<&[ColumnInlineSize]>) { + optional_column_computed_inline_sizes: Option<&[ColumnComputedInlineSize]>) { // Keep track of whether floats could impact each child. let mut inline_start_floats_impact_child = self.base.flags.contains(IMPACTED_BY_LEFT_FLOATS); @@ -1369,12 +1369,12 @@ impl BlockFlow { } // Handle tables. - match optional_column_inline_sizes { - Some(ref column_inline_sizes) => { + match optional_column_computed_inline_sizes { + Some(ref column_computed_inline_sizes) => { propagate_column_inline_sizes_to_child(kid, i, content_inline_size, - *column_inline_sizes, + *column_computed_inline_sizes, &mut inline_start_margin_edge) } None => {} @@ -2551,22 +2551,24 @@ impl ISizeAndMarginsComputer for FloatReplaced { } } -fn propagate_column_inline_sizes_to_child(kid: &mut Flow, - child_index: uint, - content_inline_size: Au, - column_inline_sizes: &[ColumnInlineSize], - inline_start_margin_edge: &mut Au) { +fn propagate_column_inline_sizes_to_child( + kid: &mut Flow, + child_index: uint, + content_inline_size: Au, + column_computed_inline_sizes: &[ColumnComputedInlineSize], + inline_start_margin_edge: &mut Au) { // If kid is table_rowgroup or table_row, the column inline-sizes info should be copied from // its parent. // // FIXME(pcwalton): This seems inefficient. Reference count it instead? let inline_size = if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() { - *kid.column_inline_sizes() = column_inline_sizes.iter().map(|&x| x).collect(); + *kid.column_computed_inline_sizes() = + column_computed_inline_sizes.iter().map(|&x| x).collect(); // ISize of kid flow is our content inline-size. content_inline_size } else if kid.is_table_cell() { - column_inline_sizes[child_index].minimum_length + column_computed_inline_sizes[child_index].size } else { // ISize of kid flow is our content inline-size. content_inline_size diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 598a8717cdb..5435cacbe67 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -38,7 +38,7 @@ use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use inline::InlineFlow; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; use parallel::FlowParallelInfo; -use table::{ColumnInlineSize, TableFlow}; +use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, TableFlow}; use table_caption::TableCaptionFlow; use table_cell::TableCellFlow; use table_colgroup::TableColGroupFlow; @@ -163,10 +163,16 @@ pub trait Flow: fmt::Show + ToString + Sync { panic!("called as_table_cell() on a non-tablecell flow") } - /// If this is a table row or table rowgroup or table flow, returns column inline-sizes. - /// Fails otherwise. - fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> { - panic!("called column_inline_sizes() on non-table flow") + /// If this is a table row, table rowgroup, or table flow, returns column intrinsic + /// inline-sizes. Fails otherwise. + fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> { + panic!("called column_intrinsic_inline_sizes() on non-table flow") + } + + /// If this is a table row, table rowgroup, or table flow, returns column computed + /// inline-sizes. Fails otherwise. + fn column_computed_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnComputedInlineSize> { + panic!("called column_intrinsic_inline_sizes() on non-table flow") } // Main methods diff --git a/components/layout/table.rs b/components/layout/table.rs index 20bc19c91fc..b65fa689919 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -34,8 +34,13 @@ use sync::Arc; pub struct TableFlow { pub block_flow: BlockFlow, - /// Information about the inline-sizes of each column. - pub column_inline_sizes: Vec<ColumnInlineSize>, + /// Information about the intrinsic inline-sizes of each column, computed bottom-up during + /// intrinsic inline-size bubbling. + pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>, + + /// Information about the actual inline-sizes of each column, computed top-down during actual + /// inline-size bubbling. + pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>, /// Table-layout property pub table_layout: TableLayout, @@ -54,7 +59,8 @@ impl TableFlow { }; TableFlow { block_flow: block_flow, - column_inline_sizes: Vec::new(), + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), table_layout: table_layout } } @@ -71,7 +77,8 @@ impl TableFlow { }; TableFlow { block_flow: block_flow, - column_inline_sizes: Vec::new(), + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), table_layout: table_layout } } @@ -89,7 +96,8 @@ impl TableFlow { }; TableFlow { block_flow: block_flow, - column_inline_sizes: Vec::new(), + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), table_layout: table_layout } } @@ -97,13 +105,13 @@ impl TableFlow { /// Update the corresponding value of `self_inline_sizes` if a value of `kid_inline_sizes` has /// a larger value than one of `self_inline_sizes`. Returns the minimum and preferred inline /// sizes. - pub fn update_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnInlineSize>, - child_inline_sizes: &Vec<ColumnInlineSize>) + pub fn update_column_inline_sizes(parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>, + child_inline_sizes: &Vec<ColumnIntrinsicInlineSize>) -> IntrinsicISizes { let mut total_inline_sizes = IntrinsicISizes::new(); for (parent_sizes, child_sizes) in parent_inline_sizes.iter_mut() .zip(child_inline_sizes.iter()) { - *parent_sizes = ColumnInlineSize { + *parent_sizes = ColumnIntrinsicInlineSize { minimum_length: max(parent_sizes.minimum_length, child_sizes.minimum_length), percentage: parent_sizes.greatest_percentage(child_sizes), preferred: max(parent_sizes.preferred, child_sizes.preferred), @@ -147,8 +155,12 @@ impl Flow for TableFlow { &mut self.block_flow } - fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> { - &mut self.column_inline_sizes + fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> { + &mut self.column_intrinsic_inline_sizes + } + + fn column_computed_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnComputedInlineSize> { + &mut self.column_computed_inline_sizes } /// The specified column inline-sizes are set from column group and the first row for the fixed @@ -165,7 +177,7 @@ impl Flow for TableFlow { debug_assert!(kid.is_proper_table_child()); if kid.is_table_colgroup() { for specified_inline_size in kid.as_table_colgroup().inline_sizes.iter() { - self.column_inline_sizes.push(ColumnInlineSize { + self.column_intrinsic_inline_sizes.push(ColumnIntrinsicInlineSize { minimum_length: match *specified_inline_size { LPA_Auto | LPA_Percentage(_) => Au(0), LPA_Length(length) => length, @@ -188,25 +200,26 @@ impl Flow for TableFlow { // Fixed table layout only looks at the first row. if !did_first_row { did_first_row = true; - for child_column_inline_size in kid.column_inline_sizes().iter() { - self.column_inline_sizes.push(*child_column_inline_size); + for child_column_inline_size in kid.column_intrinsic_inline_sizes() + .iter() { + self.column_intrinsic_inline_sizes.push(*child_column_inline_size); } } } AutoLayout => { - let child_column_inline_sizes = kid.column_inline_sizes(); - let mut child_intrinsic_sizes = - TableFlow::update_column_inline_sizes(&mut self.column_inline_sizes, - child_column_inline_sizes); + let child_column_inline_sizes = kid.column_intrinsic_inline_sizes(); + let mut child_intrinsic_sizes = TableFlow::update_column_inline_sizes( + &mut self.column_intrinsic_inline_sizes, + child_column_inline_sizes); // Add new columns if processing this row caused us to discover them. let child_column_count = child_column_inline_sizes.len(); - let parent_column_count = self.column_inline_sizes.len(); + let parent_column_count = self.column_intrinsic_inline_sizes.len(); debug!("table until the previous row has {} column(s) and this row has {} \ column(s)", parent_column_count, child_column_count); - self.column_inline_sizes.reserve(child_column_count); + self.column_intrinsic_inline_sizes.reserve(child_column_count); for i in range(parent_column_count, child_column_count) { let inline_size_for_new_column = (*child_column_inline_sizes)[i]; child_intrinsic_sizes.minimum_inline_size = @@ -215,7 +228,7 @@ impl Flow for TableFlow { child_intrinsic_sizes.preferred_inline_size = child_intrinsic_sizes.preferred_inline_size + inline_size_for_new_column.preferred; - self.column_inline_sizes.push(inline_size_for_new_column); + self.column_intrinsic_inline_sizes.push(inline_size_for_new_column); } computation.union_block(&child_intrinsic_sizes) @@ -239,7 +252,7 @@ impl Flow for TableFlow { let mut num_unspecified_inline_sizes = 0; let mut total_column_inline_size = Au(0); - for column_inline_size in self.column_inline_sizes.iter() { + for column_inline_size in self.column_intrinsic_inline_sizes.iter() { let this_column_inline_size = column_inline_size.minimum_length; if this_column_inline_size == Au(0) { num_unspecified_inline_sizes += 1 @@ -263,28 +276,38 @@ impl Flow for TableFlow { 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. + self.column_computed_inline_sizes.clear(); if total_column_inline_size < content_inline_size && num_unspecified_inline_sizes == 0 { - let ratio = content_inline_size.to_subpx() / total_column_inline_size.to_subpx(); - for column_inline_size in self.column_inline_sizes.iter_mut() { - column_inline_size.minimum_length = column_inline_size.minimum_length.scale_by(ratio); - column_inline_size.percentage = 0.0; + let ratio = content_inline_size.to_subpx() / + total_column_inline_size.to_subpx(); + for column_inline_size in self.column_intrinsic_inline_sizes.iter() { + self.column_computed_inline_sizes.push(ColumnComputedInlineSize { + size: column_inline_size.minimum_length.scale_by(ratio), + }); } } else if num_unspecified_inline_sizes != 0 { let extra_column_inline_size = (content_inline_size - total_column_inline_size) / num_unspecified_inline_sizes; - for column_inline_size in self.column_inline_sizes.iter_mut() { + for column_inline_size in self.column_intrinsic_inline_sizes.iter() { if column_inline_size.minimum_length == Au(0) && column_inline_size.percentage == 0.0 { - column_inline_size.minimum_length = extra_column_inline_size / - num_unspecified_inline_sizes + self.column_computed_inline_sizes.push(ColumnComputedInlineSize { + size: extra_column_inline_size / num_unspecified_inline_sizes, + }); + } else { + self.column_computed_inline_sizes.push(ColumnComputedInlineSize { + size: column_inline_size.minimum_length, + }); } - column_inline_size.percentage = 0.0; } } } - _ => {} + _ => { + // The table wrapper already computed the inline-sizes and propagated them down + // to us. + } } // As tables are always wrapped inside a table wrapper, they are never impacted by floats. @@ -294,7 +317,7 @@ impl Flow for TableFlow { self.block_flow.propagate_assigned_inline_size_to_children( inline_start_content_edge, content_inline_size, - Some(self.column_inline_sizes.as_slice())); + Some(self.column_computed_inline_sizes.as_slice())); } fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) { @@ -365,7 +388,7 @@ impl ISizeAndMarginsComputer for InternalTable { } } -/// Information about the inline sizes of columns within a table. +/// Information about the intrinsic inline sizes of columns within a table. /// /// During table inline-size bubbling, we might need to store both a percentage constraint and a /// specific width constraint. For instance, one cell might say that it wants to be 100 pixels wide @@ -377,7 +400,7 @@ impl ISizeAndMarginsComputer for InternalTable { /// potentially store both a specified width *and* a specified percentage, so that the inline-size /// assignment phase of layout will know which one to pick. #[deriving(Clone, Encodable, Show)] -pub struct ColumnInlineSize { +pub struct ColumnIntrinsicInlineSize { /// The preferred intrinsic inline size. pub preferred: Au, /// The largest specified size of this column as a length. @@ -388,7 +411,7 @@ pub struct ColumnInlineSize { pub constrained: bool, } -impl ColumnInlineSize { +impl ColumnIntrinsicInlineSize { /// Returns the true minimum size of this column, given the containing block's inline size. /// Beware that this is generally only correct for fixed table layout. (Compare CSS 2.1 § /// 17.5.2.1 with the algorithm in INTRINSIC § 4.) @@ -397,7 +420,7 @@ impl ColumnInlineSize { } /// Returns the higher of the two percentages specified in `self` and `other`. - pub fn greatest_percentage(&self, other: &ColumnInlineSize) -> CSSFloat { + pub fn greatest_percentage(&self, other: &ColumnIntrinsicInlineSize) -> CSSFloat { if self.percentage > other.percentage { self.percentage } else { @@ -405,3 +428,13 @@ impl ColumnInlineSize { } } } + +/// The actual inline size for each column. +/// +/// TODO(pcwalton): There will probably be some `border-collapse`-related info in here too +/// eventually. +#[deriving(Encodable)] +pub struct ColumnComputedInlineSize { + /// The computed size of this inline column. + pub size: Au, +} diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 9fc6c17b52b..7dbfa3d678c 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -45,10 +45,8 @@ impl TableCellFlow { /// Assign block-size for table-cell flow. /// - /// TODO(#2015, 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 + /// methods. #[inline(always)] fn assign_block_size_table_cell_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse) diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 4c12ef1ef87..5ecdac0b5b4 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -14,7 +14,7 @@ use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow; use fragment::{Fragment, FragmentBoundsIterator}; use layout_debug; -use table::{ColumnInlineSize, InternalTable}; +use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable}; use model::{MaybeAuto, Specified, Auto}; use wrapper::ThreadSafeLayoutNode; @@ -30,8 +30,11 @@ use sync::Arc; pub struct TableRowFlow { pub block_flow: BlockFlow, - /// Information about the inline-sizes of each column. - pub column_inline_sizes: Vec<ColumnInlineSize>, + /// Information about the intrinsic inline-sizes of each column. + pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>, + + /// Information about the computed inline-sizes of each column. + pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>, } impl TableRowFlow { @@ -40,7 +43,8 @@ impl TableRowFlow { -> TableRowFlow { TableRowFlow { block_flow: BlockFlow::from_node_and_fragment(node, fragment), - column_inline_sizes: Vec::new() + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), } } @@ -49,7 +53,8 @@ impl TableRowFlow { -> TableRowFlow { TableRowFlow { block_flow: BlockFlow::from_node(constructor, node), - column_inline_sizes: Vec::new() + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), } } @@ -150,8 +155,12 @@ impl Flow for TableRowFlow { &mut self.block_flow } - fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> { - &mut self.column_inline_sizes + fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> { + &mut self.column_intrinsic_inline_sizes + } + + fn column_computed_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnComputedInlineSize> { + &mut self.column_computed_inline_sizes } /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When @@ -180,7 +189,7 @@ impl Flow for TableRowFlow { // Collect minimum and preferred inline-sizes of the cell for automatic table layout // calculation. let child_base = flow::mut_base(kid); - let child_column_inline_size = ColumnInlineSize { + let child_column_inline_size = ColumnIntrinsicInlineSize { minimum_length: match child_specified_inline_size { LPA_Auto | LPA_Percentage(_) => { child_base.intrinsic_inline_sizes.minimum_inline_size @@ -199,7 +208,7 @@ impl Flow for TableRowFlow { }; min_inline_size = min_inline_size + child_column_inline_size.minimum_length; pref_inline_size = pref_inline_size + child_column_inline_size.preferred; - self.column_inline_sizes.push(child_column_inline_size); + self.column_intrinsic_inline_sizes.push(child_column_inline_size); } self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size; self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = max(min_inline_size, @@ -208,7 +217,7 @@ impl Flow for TableRowFlow { /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. /// When called on this context, the context has had its inline-size set by the parent context. - fn assign_inline_sizes(&mut self, ctx: &LayoutContext) { + fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) { let _scope = layout_debug_scope!("table_row::assign_inline_sizes {:x}", self.block_flow.base.debug_id()); debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_row"); @@ -221,13 +230,13 @@ impl Flow for TableRowFlow { let inline_size_computer = InternalTable; inline_size_computer.compute_used_inline_size(&mut self.block_flow, - ctx, + layout_context, containing_block_inline_size); - self.block_flow - .propagate_assigned_inline_size_to_children(inline_start_content_edge, - containing_block_inline_size, - Some(self.column_inline_sizes.as_slice())); + self.block_flow.propagate_assigned_inline_size_to_children( + inline_start_content_edge, + containing_block_inline_size, + Some(self.column_computed_inline_sizes.as_slice())); } fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) { diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 865c0a3c081..0b9d37d1182 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -6,16 +6,14 @@ #![deny(unsafe_blocks)] -use block::BlockFlow; -use block::ISizeAndMarginsComputer; +use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayNotCollapse}; use construct::FlowConstructor; use context::LayoutContext; use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils}; -use flow; use fragment::{Fragment, FragmentBoundsIterator}; use layout_debug; use model::IntrinsicISizesContribution; -use table::{ColumnInlineSize, InternalTable, TableFlow}; +use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, TableFlow}; use wrapper::ThreadSafeLayoutNode; use servo_util::geometry::Au; @@ -28,26 +26,29 @@ use sync::Arc; pub struct TableRowGroupFlow { pub block_flow: BlockFlow, - /// Information about the inline-sizes of each column. - pub column_inline_sizes: Vec<ColumnInlineSize>, + /// Information about the intrinsic inline-sizes of each column. + pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>, + + /// Information about the actual inline sizes of each column. + pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>, } impl TableRowGroupFlow { - pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, - fragment: Fragment) + pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) -> TableRowGroupFlow { TableRowGroupFlow { block_flow: BlockFlow::from_node_and_fragment(node, fragment), - column_inline_sizes: Vec::new(), + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), } } - pub fn from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode) + pub fn from_node(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> TableRowGroupFlow { TableRowGroupFlow { block_flow: BlockFlow::from_node(constructor, node), - column_inline_sizes: Vec::new(), + column_intrinsic_inline_sizes: Vec::new(), + column_computed_inline_sizes: Vec::new(), } } @@ -55,41 +56,13 @@ impl TableRowGroupFlow { &self.block_flow.fragment } - fn initialize_offsets(&mut self) -> (Au, Au, Au) { - // TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and - // inline-start_offset should be updated. Currently, they are set as Au(0). - (Au(0), Au(0), Au(0)) - } - /// Assign block-size 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 + /// methods. #[inline(always)] fn assign_block_size_table_rowgroup_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) { - let (block_start_offset, _, _) = self.initialize_offsets(); - - let mut cur_y = block_start_offset; - - for kid in self.block_flow.base.child_iter() { - kid.place_float_if_applicable(layout_context); - if !flow::base(kid).flags.is_float() { - kid.assign_block_size_for_inorder_child_if_necessary(layout_context); - } - - let child_node = flow::mut_base(kid); - child_node.position.start.b = cur_y; - cur_y = cur_y + child_node.position.size.block; - } - - let block_size = cur_y - block_start_offset; - - let mut position = self.block_flow.fragment.border_box; - position.size.block = block_size; - self.block_flow.fragment.border_box = position; - self.block_flow.base.position.size.block = block_size; + self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse) } } @@ -110,10 +83,15 @@ impl Flow for TableRowGroupFlow { &mut self.block_flow } - fn column_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnInlineSize> { - &mut self.column_inline_sizes + fn column_intrinsic_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnIntrinsicInlineSize> { + &mut self.column_intrinsic_inline_sizes } + fn column_computed_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<ColumnComputedInlineSize> { + &mut self.column_computed_inline_sizes + } + + /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When /// called on this context, all child contexts have had their min/pref inline-sizes set. This /// function must decide min/pref inline-sizes based on child context inline-sizes and @@ -132,20 +110,19 @@ impl Flow for TableRowGroupFlow { assert!(kid.is_table_row()); // Calculate minimum and preferred inline sizes for automatic table layout. - if self.column_inline_sizes.is_empty() { + if self.column_intrinsic_inline_sizes.is_empty() { // We're the first row. - debug_assert!(self.column_inline_sizes.is_empty()); - self.column_inline_sizes = kid.column_inline_sizes().clone(); + self.column_intrinsic_inline_sizes = kid.column_intrinsic_inline_sizes().clone(); } else { let mut child_intrinsic_sizes = - TableFlow::update_column_inline_sizes(&mut self.column_inline_sizes, - kid.column_inline_sizes()); + TableFlow::update_column_inline_sizes(&mut self.column_intrinsic_inline_sizes, + kid.column_intrinsic_inline_sizes()); // update the number of column inline-sizes from table-rows. - let column_count = self.column_inline_sizes.len(); - let child_column_count = kid.column_inline_sizes().len(); + let column_count = self.column_intrinsic_inline_sizes.len(); + let child_column_count = kid.column_intrinsic_inline_sizes().len(); for i in range(column_count, child_column_count) { - let this_column_inline_size = (*kid.column_inline_sizes())[i]; + let this_column_inline_size = (*kid.column_intrinsic_inline_sizes())[i]; // FIXME(pcwalton): Ignoring the percentage here seems dubious. child_intrinsic_sizes.minimum_inline_size = @@ -154,7 +131,7 @@ impl Flow for TableRowGroupFlow { child_intrinsic_sizes.preferred_inline_size = child_intrinsic_sizes.preferred_inline_size + this_column_inline_size.preferred; - self.column_inline_sizes.push(this_column_inline_size); + self.column_intrinsic_inline_sizes.push(this_column_inline_size); } computation.union_block(&child_intrinsic_sizes) @@ -186,7 +163,7 @@ impl Flow for TableRowGroupFlow { self.block_flow.propagate_assigned_inline_size_to_children( inline_start_content_edge, content_inline_size, - Some(self.column_inline_sizes.as_slice())); + Some(self.column_computed_inline_sizes.as_slice())); } fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) { diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index 7ec0ed874a4..5003dca66c1 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -21,7 +21,7 @@ use floats::FloatKind; use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; use fragment::{Fragment, FragmentBoundsIterator}; -use table::ColumnInlineSize; +use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize}; use wrapper::ThreadSafeLayoutNode; use servo_util::geometry::Au; @@ -43,13 +43,7 @@ pub struct TableWrapperFlow { pub block_flow: BlockFlow, /// Intrinsic column inline sizes according to INTRINSIC § 4.1 - pub intrinsic_column_inline_sizes: Vec<ColumnInlineSize>, - - /// Computed inline-size for each column. - /// - /// FIXME: This should be a separate type that only contains computed inline - /// sizes. - pub column_inline_sizes: Vec<ColumnInlineSize>, + pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>, /// Table-layout property pub table_layout: TableLayout, @@ -68,8 +62,7 @@ impl TableWrapperFlow { }; TableWrapperFlow { block_flow: block_flow, - intrinsic_column_inline_sizes: vec!(), - column_inline_sizes: vec!(), + column_intrinsic_inline_sizes: vec!(), table_layout: table_layout } } @@ -86,8 +79,7 @@ impl TableWrapperFlow { }; TableWrapperFlow { block_flow: block_flow, - intrinsic_column_inline_sizes: vec!(), - column_inline_sizes: vec!(), + column_intrinsic_inline_sizes: vec!(), table_layout: table_layout } } @@ -105,14 +97,15 @@ impl TableWrapperFlow { }; TableWrapperFlow { block_flow: block_flow, - intrinsic_column_inline_sizes: vec!(), - column_inline_sizes: vec!(), + column_intrinsic_inline_sizes: vec!(), table_layout: table_layout } } /// Calculates table column sizes for automatic layout per INTRINSIC § 4.3. - fn calculate_table_column_sizes_for_automatic_layout(&mut self) { + fn calculate_table_column_sizes_for_automatic_layout( + &mut self, + intermediate_column_inline_sizes: &mut [IntermediateColumnInlineSize]) { // Find the padding and border of our first child, which is the table itself. // // This is a little weird because we're computing border/padding/margins for our child, @@ -141,9 +134,9 @@ impl TableWrapperFlow { // Compute all the guesses for the column sizes, and sum them. let mut total_guess = AutoLayoutCandidateGuess::new(); let guesses: Vec<AutoLayoutCandidateGuess> = - self.column_inline_sizes.iter().map(|column_inline_size| { - let guess = AutoLayoutCandidateGuess::from_column_inline_size( - column_inline_size, + self.column_intrinsic_inline_sizes.iter().map(|column_intrinsic_inline_size| { + let guess = AutoLayoutCandidateGuess::from_column_intrinsic_inline_size( + column_intrinsic_inline_size, available_inline_size); total_guess = total_guess + guess; guess @@ -153,12 +146,11 @@ impl TableWrapperFlow { let selection = SelectedAutoLayoutCandidateGuess::select(&total_guess, available_inline_size); let mut total_used_inline_size = Au(0); - for (column_inline_size, guess) in self.column_inline_sizes - .iter_mut() - .zip(guesses.iter()) { - column_inline_size.minimum_length = guess.calculate(selection); - column_inline_size.percentage = 0.0; - total_used_inline_size = total_used_inline_size + column_inline_size.minimum_length + for (intermediate_column_inline_size, guess) in + intermediate_column_inline_sizes.iter_mut().zip(guesses.iter()) { + intermediate_column_inline_size.size = guess.calculate(selection); + intermediate_column_inline_size.percentage = 0.0; + total_used_inline_size = total_used_inline_size + intermediate_column_inline_size.size } // Distribute excess inline-size if necessary per INTRINSIC § 4.4. @@ -168,13 +160,17 @@ impl TableWrapperFlow { if excess_inline_size > Au(0) && selection == UsePreferredGuessAndDistributeExcessInlineSize { let mut info = ExcessInlineSizeDistributionInfo::new(); - for column_inline_size in self.column_inline_sizes.iter() { - info.update(column_inline_size) + for column_intrinsic_inline_size in self.column_intrinsic_inline_sizes.iter() { + info.update(column_intrinsic_inline_size) } let mut total_distributed_excess_size = Au(0); - for column_inline_size in self.column_inline_sizes.iter_mut() { - info.distribute_excess_inline_size_to_column(column_inline_size, + for (intermediate_column_inline_size, column_intrinsic_inline_size) in + intermediate_column_inline_sizes.iter_mut() + .zip(self.column_intrinsic_inline_sizes + .iter()) { + info.distribute_excess_inline_size_to_column(intermediate_column_inline_size, + column_intrinsic_inline_size, excess_inline_size, &mut total_distributed_excess_size) } @@ -234,11 +230,11 @@ impl Flow for TableWrapperFlow { } fn bubble_inline_sizes(&mut self) { - // Get the column inline-sizes info from the table flow. + // Get the intrinsic column inline-sizes info from the table flow. for kid in self.block_flow.base.child_iter() { debug_assert!(kid.is_table_caption() || kid.is_table()); if kid.is_table() { - self.intrinsic_column_inline_sizes = kid.column_inline_sizes().clone() + self.column_intrinsic_inline_sizes = kid.column_intrinsic_inline_sizes().clone() } } @@ -253,7 +249,14 @@ impl Flow for TableWrapperFlow { "table_wrapper" }); - self.column_inline_sizes = self.intrinsic_column_inline_sizes.clone(); + let mut intermediate_column_inline_sizes = self.column_intrinsic_inline_sizes + .iter() + .map(|column_intrinsic_inline_size| { + IntermediateColumnInlineSize { + size: column_intrinsic_inline_size.minimum_length, + percentage: column_intrinsic_inline_size.percentage, + } + }).collect::<Vec<_>>(); // Table wrappers are essentially block formatting contexts and are therefore never // impacted by floats. @@ -273,7 +276,8 @@ impl Flow for TableWrapperFlow { match self.table_layout { FixedLayout => {} AutoLayout => { - self.calculate_table_column_sizes_for_automatic_layout() + self.calculate_table_column_sizes_for_automatic_layout( + intermediate_column_inline_sizes.as_mut_slice()) } } @@ -283,12 +287,29 @@ impl Flow for TableWrapperFlow { // In case of fixed layout, column inline-sizes are calculated in table flow. let assigned_column_inline_sizes = match self.table_layout { FixedLayout => None, - AutoLayout => Some(self.column_inline_sizes.as_slice()) + AutoLayout => { + Some(intermediate_column_inline_sizes.iter().map(|sizes| { + ColumnComputedInlineSize { + size: sizes.size, + } + }).collect::<Vec<_>>()) + } }; - self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, - content_inline_size, - assigned_column_inline_sizes); + match assigned_column_inline_sizes { + None => { + self.block_flow.propagate_assigned_inline_size_to_children( + inline_start_content_edge, + content_inline_size, + None) + } + Some(ref assigned_column_inline_sizes) => { + self.block_flow.propagate_assigned_inline_size_to_children( + inline_start_content_edge, + content_inline_size, + Some(assigned_column_inline_sizes.as_slice())); + } + } } @@ -395,27 +416,28 @@ impl AutoLayoutCandidateGuess { } /// Fills in the inline-size guesses for this column per INTRINSIC § 4.3. - fn from_column_inline_size(column_inline_size: &ColumnInlineSize, assignable_inline_size: Au) - -> AutoLayoutCandidateGuess { + fn from_column_intrinsic_inline_size(column_intrinsic_inline_size: &ColumnIntrinsicInlineSize, + assignable_inline_size: Au) + -> AutoLayoutCandidateGuess { let minimum_percentage_guess = - max(assignable_inline_size.scale_by(column_inline_size.percentage), - column_inline_size.minimum_length); + max(assignable_inline_size.scale_by(column_intrinsic_inline_size.percentage), + column_intrinsic_inline_size.minimum_length); AutoLayoutCandidateGuess { - minimum_guess: column_inline_size.minimum_length, + minimum_guess: column_intrinsic_inline_size.minimum_length, minimum_percentage_guess: minimum_percentage_guess, // FIXME(pcwalton): We need the notion of *constrainedness* per INTRINSIC § 4 to // implement this one correctly. - minimum_specified_guess: if column_inline_size.percentage > 0.0 { + minimum_specified_guess: if column_intrinsic_inline_size.percentage > 0.0 { minimum_percentage_guess - } else if column_inline_size.constrained { - column_inline_size.preferred + } else if column_intrinsic_inline_size.constrained { + column_intrinsic_inline_size.preferred } else { - column_inline_size.minimum_length + column_intrinsic_inline_size.minimum_length }, - preferred_guess: if column_inline_size.percentage > 0.0 { + preferred_guess: if column_intrinsic_inline_size.percentage > 0.0 { minimum_percentage_guess } else { - column_inline_size.preferred + column_intrinsic_inline_size.preferred }, } } @@ -526,19 +548,21 @@ impl ExcessInlineSizeDistributionInfo { } } - fn update(&mut self, column_inline_size: &ColumnInlineSize) { - if !column_inline_size.constrained && column_inline_size.percentage == 0.0 { + fn update(&mut self, column_intrinsic_inline_size: &ColumnIntrinsicInlineSize) { + if !column_intrinsic_inline_size.constrained && + column_intrinsic_inline_size.percentage == 0.0 { self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage = self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage + - column_inline_size.preferred; + column_intrinsic_inline_size.preferred; self.count_of_nonconstrained_columns_with_no_percentage += 1 } - if column_inline_size.constrained && column_inline_size.percentage == 0.0 { + if column_intrinsic_inline_size.constrained && + column_intrinsic_inline_size.percentage == 0.0 { self.preferred_inline_size_of_constrained_columns_with_no_percentage = self.preferred_inline_size_of_constrained_columns_with_no_percentage + - column_inline_size.preferred + column_intrinsic_inline_size.preferred } - self.total_percentage += column_inline_size.percentage; + self.total_percentage += column_intrinsic_inline_size.percentage; self.column_count += 1 } @@ -547,23 +571,25 @@ impl ExcessInlineSizeDistributionInfo { /// /// `#[inline]` so the compiler will hoist out the branch, which is loop-invariant. #[inline] - fn distribute_excess_inline_size_to_column(&self, - column_inline_size: &mut ColumnInlineSize, - excess_inline_size: Au, - total_distributed_excess_size: &mut Au) { + fn distribute_excess_inline_size_to_column( + &self, + intermediate_column_inline_size: &mut IntermediateColumnInlineSize, + column_intrinsic_inline_size: &ColumnIntrinsicInlineSize, + excess_inline_size: Au, + total_distributed_excess_size: &mut Au) { let proportion = if self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage > Au(0) { - column_inline_size.preferred.to_subpx() / + column_intrinsic_inline_size.preferred.to_subpx() / self.preferred_inline_size_of_nonconstrained_columns_with_no_percentage .to_subpx() } else if self.count_of_nonconstrained_columns_with_no_percentage > 0 { 1.0 / (self.count_of_nonconstrained_columns_with_no_percentage as CSSFloat) } else if self.preferred_inline_size_of_constrained_columns_with_no_percentage > Au(0) { - column_inline_size.preferred.to_subpx() / + column_intrinsic_inline_size.preferred.to_subpx() / self.preferred_inline_size_of_constrained_columns_with_no_percentage.to_subpx() } else if self.total_percentage > 0.0 { - column_inline_size.percentage / self.total_percentage + column_intrinsic_inline_size.percentage / self.total_percentage } else { 1.0 / (self.column_count as CSSFloat) }; @@ -573,7 +599,14 @@ impl ExcessInlineSizeDistributionInfo { let amount_to_distribute = min(excess_inline_size.scale_by(proportion), excess_inline_size - *total_distributed_excess_size); *total_distributed_excess_size = *total_distributed_excess_size + amount_to_distribute; - column_inline_size.minimum_length = column_inline_size.minimum_length + + intermediate_column_inline_size.size = intermediate_column_inline_size.size + amount_to_distribute } } + +/// An intermediate column size assignment. +struct IntermediateColumnInlineSize { + size: Au, + percentage: f64, +} + |