From 9700b0e8b35b6d9f4d0f29ec60c225fdf1aeb796 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 8 Dec 2016 15:47:22 -0800 Subject: [PATCH] Account for rowspan in inline layout of table columns/cells --- components/layout/table.rs | 22 ++++- components/layout/table_row.rs | 90 +++++++++++++++++-- components/layout/table_wrapper.rs | 3 +- tests/wpt/mozilla/meta/MANIFEST.json | 36 ++++++++ .../tests/css/table_rowspan_simple_a.html | 20 +++++ .../tests/css/table_rowspan_simple_ref.html | 20 +++++ 6 files changed, 180 insertions(+), 11 deletions(-) create mode 100644 tests/wpt/mozilla/tests/css/table_rowspan_simple_a.html create mode 100644 tests/wpt/mozilla/tests/css/table_rowspan_simple_ref.html diff --git a/components/layout/table.rs b/components/layout/table.rs index 6d95ff0b1e04..031cf2ab1976 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -94,7 +94,16 @@ impl TableFlow { preferred_inline_size: surrounding_size, }; let mut column_index = 0; + let mut incoming_rowspan = vec![]; + for child_cell_inline_size in child_cell_inline_sizes { + // Skip any column occupied by a cell from a previous row. + while column_index < incoming_rowspan.len() && incoming_rowspan[column_index] != 1 { + if incoming_rowspan[column_index] > 1 { + incoming_rowspan[column_index] -= 1; + } + column_index += 1; + } for _ in 0..child_cell_inline_size.column_span { if column_index < parent_inline_sizes.len() { // We already have some intrinsic size information for this column. Merge it in @@ -130,6 +139,14 @@ impl TableFlow { total_inline_sizes.preferred_inline_size += parent_inline_sizes[column_index].preferred; + // If this cell spans later rows, record its rowspan. + if child_cell_inline_size.row_span > 1 { + if incoming_rowspan.len() < column_index + 1 { + incoming_rowspan.resize(column_index + 1, 0); + } + incoming_rowspan[column_index] = child_cell_inline_size.row_span; + } + column_index += 1 } } @@ -418,6 +435,7 @@ impl Flow for TableFlow { &self.collapsed_inline_direction_border_widths_for_table; let mut collapsed_block_direction_border_widths_for_table = self.collapsed_block_direction_border_widths_for_table.iter().peekable(); + let mut incoming_rowspan = vec![]; self.block_flow.propagate_assigned_inline_size_to_children(shared_context, inline_start_content_edge, inline_end_content_edge, @@ -432,7 +450,8 @@ impl Flow for TableFlow { child_flow, writing_mode, column_computed_inline_sizes, - &spacing_per_cell); + &spacing_per_cell, + &mut incoming_rowspan); if child_flow.is_table_row() { let child_table_row = child_flow.as_mut_table_row(); child_table_row.populate_collapsed_border_spacing( @@ -647,6 +666,7 @@ fn perform_border_collapse_for_row(child_table_row: &mut TableRowFlow, next_block_borders: NextBlockCollapsedBorders, inline_spacing: &mut Vec, block_spacing: &mut Vec) { + // TODO mbrubeck: Take rowspan and colspan into account. let number_of_borders_inline_direction = child_table_row.preliminary_collapsed_borders.inline.len(); // Compute interior inline borders. for (i, this_inline_border) in child_table_row.preliminary_collapsed_borders diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 4f8af643846b..6db82388d0a2 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -45,6 +45,12 @@ pub struct TableRowFlow { /// Information about the computed inline-sizes of each column. pub column_computed_inline_sizes: Vec, + /// The number of remaining rows spanned by cells in previous rows, indexed by column. + /// + /// Columns that are not included in this vector have the default rowspan of "1". If there are + /// no cells with rowspan != 1 in previous rows, this vector may be empty. + pub incoming_rowspan: Vec, + /// The spacing for this row, propagated down from the table during the inline-size assignment /// phase. pub spacing: border_spacing::T, @@ -90,6 +96,7 @@ impl TableRowFlow { block_flow: BlockFlow::from_fragment(fragment), cell_intrinsic_inline_sizes: Vec::new(), column_computed_inline_sizes: Vec::new(), + incoming_rowspan: Vec::new(), spacing: border_spacing::T { horizontal: Au(0), vertical: Au(0), @@ -353,12 +360,23 @@ impl Flow for TableRowFlow { containing_block_inline_size); // Spread out the completed inline sizes among columns with spans > 1. - let mut computed_inline_size_for_cells = Vec::new(); - let mut column_computed_inline_size_iterator = self.column_computed_inline_sizes.iter(); + let num_columns = self.column_computed_inline_sizes.len(); + let mut computed_inline_size_for_cells = Vec::with_capacity(num_columns); + let mut col = 0; + for cell_intrinsic_inline_size in &self.cell_intrinsic_inline_sizes { + // Skip any column occupied by a cell from a previous row. + while col < self.incoming_rowspan.len() && self.incoming_rowspan[col] != 1 { + let size = match self.column_computed_inline_sizes.get(col) { + Some(column_computed_inline_size) => *column_computed_inline_size, + None => ColumnComputedInlineSize { size: Au(0) } // See FIXME below. + }; + computed_inline_size_for_cells.push(size); + col += 1; + } // Start with the computed inline size for the first column in the span. let mut column_computed_inline_size = - match column_computed_inline_size_iterator.next() { + match self.column_computed_inline_sizes.get(col) { Some(column_computed_inline_size) => *column_computed_inline_size, None => { // We're in fixed layout mode and there are more cells in this row than @@ -371,16 +389,18 @@ impl Flow for TableRowFlow { } } }; + col += 1; // Add in computed inline sizes for any extra columns in the span. for _ in 1..cell_intrinsic_inline_size.column_span { let extra_column_computed_inline_size = - match column_computed_inline_size_iterator.next() { + match self.column_computed_inline_sizes.get(col) { Some(column_computed_inline_size) => column_computed_inline_size, None => break, }; column_computed_inline_size.size = column_computed_inline_size.size + extra_column_computed_inline_size.size + self.spacing.horizontal; + col += 1; } computed_inline_size_for_cells.push(column_computed_inline_size) @@ -402,6 +422,9 @@ impl Flow for TableRowFlow { let spacing = self.spacing; let row_writing_mode = self.block_flow.base.writing_mode; let table_writing_mode = self.table_writing_mode; + let incoming_rowspan = &self.incoming_rowspan; + let mut column_index = 0; + self.block_flow.propagate_assigned_inline_size_to_children(shared_context, inline_start_content_edge, inline_end_content_edge, @@ -415,6 +438,8 @@ impl Flow for TableRowFlow { set_inline_position_of_child_flow( child_flow, child_index, + &mut column_index, + incoming_rowspan, row_writing_mode, table_writing_mode, &computed_inline_size_for_cells, @@ -714,13 +739,14 @@ impl CollapsedBorder { } } -/// Pushes column inline size and border collapse info down to a child. +/// Pushes column inline size, incoming rowspan, and border collapse info down to a child. pub fn propagate_column_inline_sizes_to_child( child_flow: &mut Flow, table_writing_mode: WritingMode, column_computed_inline_sizes: &[ColumnComputedInlineSize], - border_spacing: &border_spacing::T) { - // If the child is a row group or a row, the column inline-size info should be copied from its + border_spacing: &border_spacing::T, + incoming_rowspan: &mut Vec) { + // If the child is a row group or a row, the column inline-size and rowspan info should be copied from its // parent. // // FIXME(pcwalton): This seems inefficient. Reference count it instead? @@ -736,7 +762,8 @@ pub fn propagate_column_inline_sizes_to_child( propagate_column_inline_sizes_to_child(kid, table_writing_mode, column_computed_inline_sizes, - border_spacing); + border_spacing, + incoming_rowspan); } } FlowClass::TableRow => { @@ -745,6 +772,32 @@ pub fn propagate_column_inline_sizes_to_child( column_computed_inline_sizes.to_vec(); child_table_row_flow.spacing = *border_spacing; child_table_row_flow.table_writing_mode = table_writing_mode; + child_table_row_flow.incoming_rowspan = incoming_rowspan.clone(); + + // Update the incoming rowspan for the next row. + let mut col = 0; + for cell in &child_table_row_flow.cell_intrinsic_inline_sizes { + // Skip any column occupied by a cell from a previous row. + while col < incoming_rowspan.len() && incoming_rowspan[col] != 1 { + if incoming_rowspan[col] > 1 { + incoming_rowspan[col] -= 1; + } + col += 1; + } + for _ in 0..cell.column_span { + if col < incoming_rowspan.len() && incoming_rowspan[col] > 1 { + incoming_rowspan[col] -= 1; + } + // If this cell spans later rows, record its rowspan. + if cell.row_span != 1 { + if incoming_rowspan.len() < col + 1 { + incoming_rowspan.resize(col + 1, 1); + } + incoming_rowspan[col] = max(cell.row_span, incoming_rowspan[col]); + } + col += 1; + } + } } c => warn!("unexpected flow in table {:?}", c) } @@ -754,6 +807,8 @@ pub fn propagate_column_inline_sizes_to_child( fn set_inline_position_of_child_flow( child_flow: &mut Flow, child_index: usize, + column_index: &mut usize, + incoming_rowspan: &[u32], row_writing_mode: WritingMode, table_writing_mode: WritingMode, column_computed_inline_sizes: &[ColumnComputedInlineSize], @@ -768,6 +823,21 @@ fn set_inline_position_of_child_flow( let reverse_column_order = table_writing_mode.is_bidi_ltr() != row_writing_mode.is_bidi_ltr(); + // Advance past any column occupied by a cell from a previous row. + while *column_index < incoming_rowspan.len() && incoming_rowspan[*column_index] != 1 { + let column_inline_size = column_computed_inline_sizes[*column_index].size; + let border_inline_size = match *border_collapse_info { + Some(_) => Au(0), // FIXME: Make collapsed borders account for colspan/rowspan. + None => border_spacing.horizontal, + }; + if reverse_column_order { + *inline_end_margin_edge += column_inline_size + border_inline_size; + } else { + *inline_start_margin_edge += column_inline_size + border_inline_size; + } + *column_index += 1; + } + // Handle border collapsing, if necessary. let child_table_cell = child_flow.as_mut_table_cell(); match *border_collapse_info { @@ -822,7 +892,9 @@ fn set_inline_position_of_child_flow( } } - let column_inline_size = column_computed_inline_sizes[child_index].size; + let column_inline_size = column_computed_inline_sizes[*column_index].size; + *column_index += 1; + let kid_base = &mut child_table_cell.block_flow.base; kid_base.block_container_inline_size = column_inline_size; diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index 372fdbd086f8..62f4d76b1b2a 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -420,7 +420,8 @@ impl Flow for TableWrapperFlow { child_flow, writing_mode, assigned_column_inline_sizes, - &border_spacing); + &border_spacing, + &mut vec![]); }) } } diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 23b77f9ed852..e91bdccb049d 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -5292,6 +5292,18 @@ "url": "/_mozilla/css/table_row_direction_a.html" } ], + "css/table_rowspan_simple_a.html": [ + { + "path": "css/table_rowspan_simple_a.html", + "references": [ + [ + "/_mozilla/css/table_rowspan_simple_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/table_rowspan_simple_a.html" + } + ], "css/table_specified_width_a.html": [ { "path": "css/table_specified_width_a.html", @@ -20328,6 +20340,30 @@ "url": "/_mozilla/css/table_row_direction_a.html" } ], + "css/table_rowspan_simple_a.html": [ + { + "path": "css/table_rowspan_simple_a.html", + "references": [ + [ + "/_mozilla/css/table_rowspan_simple_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/table_rowspan_simple_a.html" + } + ], + "css/table_rowspan_simple_ref.html": [ + { + "path": "css/table_rowspan_simple_ref.html", + "references": [ + [ + "/_mozilla/css/table_rowspan_simple_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/table_rowspan_simple_ref.html" + } + ], "css/table_specified_width_a.html": [ { "path": "css/table_specified_width_a.html", diff --git a/tests/wpt/mozilla/tests/css/table_rowspan_simple_a.html b/tests/wpt/mozilla/tests/css/table_rowspan_simple_a.html new file mode 100644 index 000000000000..8a0df11a942b --- /dev/null +++ b/tests/wpt/mozilla/tests/css/table_rowspan_simple_a.html @@ -0,0 +1,20 @@ + + + + + + + + + +
  
 
+ + + diff --git a/tests/wpt/mozilla/tests/css/table_rowspan_simple_ref.html b/tests/wpt/mozilla/tests/css/table_rowspan_simple_ref.html new file mode 100644 index 000000000000..83580dd1d391 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/table_rowspan_simple_ref.html @@ -0,0 +1,20 @@ + + + + + + + + + +
  
  
+ + +