use std::{cmp, fmt};
use app_units::Au;
use base::print_tree::PrintTree;
use euclid::default::Point2D;
use log::{debug, trace};
use serde::Serialize;
use style::computed_values::{border_collapse, border_spacing, table_layout};
use style::context::SharedStyleContext;
use style::logical_geometry::LogicalSize;
use style::properties::ComputedValues;
use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::computed::Size;
use style::values::CSSFloat;
use crate::block::{
BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer, ISizeConstraintInput,
ISizeConstraintSolution,
};
use crate::context::LayoutContext;
use crate::display_list::{
BorderPaintingMode, DisplayListBuildState, StackingContextCollectionFlags,
StackingContextCollectionState,
};
use crate::flow::{
BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, GetBaseFlow, ImmutableFlowUtils,
OpaqueFlow,
};
use crate::flow_list::{FlowListIterator, MutFlowListIterator};
use crate::fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use crate::model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto};
use crate::table_cell::TableCellFlow;
use crate::table_row::{
self, CellIntrinsicInlineSize, CollapsedBorder, CollapsedBorderFrom, TableRowFlow,
TableRowSizeData,
};
use crate::table_wrapper::TableLayout;
use crate::{layout_debug, layout_debug_scope};
#[allow(unsafe_code)]
unsafe impl crate::flow::HasBaseFlow for TableFlow {}
#[derive(Serialize)]
#[repr(C)]
pub struct TableFlow {
pub block_flow: BlockFlow,
pub column_intrinsic_inline_sizes: Vec<ColumnIntrinsicInlineSize>,
pub column_computed_inline_sizes: Vec<ColumnComputedInlineSize>,
pub collapsed_inline_direction_border_widths_for_table: Vec<Au>,
pub collapsed_block_direction_border_widths_for_table: Vec<Au>,
pub table_layout: TableLayout,
}
impl TableFlow {
pub fn from_fragment(fragment: Fragment) -> TableFlow {
let mut block_flow = BlockFlow::from_fragment(fragment);
let table_layout =
if block_flow.fragment().style().get_table().table_layout == table_layout::T::Fixed {
TableLayout::Fixed
} else {
TableLayout::Auto
};
TableFlow {
block_flow,
column_intrinsic_inline_sizes: Vec::new(),
column_computed_inline_sizes: Vec::new(),
collapsed_inline_direction_border_widths_for_table: Vec::new(),
collapsed_block_direction_border_widths_for_table: Vec::new(),
table_layout,
}
}
fn update_automatic_column_inline_sizes(
parent_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
child_cell_inline_sizes: &[CellIntrinsicInlineSize],
surrounding_size: Au,
) -> IntrinsicISizes {
let mut total_inline_sizes = IntrinsicISizes {
minimum_inline_size: surrounding_size,
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 {
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() {
let parent_sizes = &mut parent_inline_sizes[column_index];
if child_cell_inline_size.column_span > 1 {
} else {
let column_size = &child_cell_inline_size.column_size;
*parent_sizes = ColumnIntrinsicInlineSize {
minimum_length: cmp::max(
parent_sizes.minimum_length,
column_size.minimum_length,
),
percentage: parent_sizes.greatest_percentage(column_size),
preferred: cmp::max(parent_sizes.preferred, column_size.preferred),
constrained: parent_sizes.constrained || column_size.constrained,
}
}
} else {
debug_assert_eq!(column_index, parent_inline_sizes.len());
if child_cell_inline_size.column_span > 1 {
parent_inline_sizes.push(ColumnIntrinsicInlineSize::new())
} else {
parent_inline_sizes.push(child_cell_inline_size.column_size)
}
}
total_inline_sizes.minimum_inline_size +=
parent_inline_sizes[column_index].minimum_length;
total_inline_sizes.preferred_inline_size +=
parent_inline_sizes[column_index].preferred;
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
}
}
total_inline_sizes
}
fn update_column_inline_sizes_for_row(
row: &TableRowFlow,
column_inline_sizes: &mut Vec<ColumnIntrinsicInlineSize>,
computation: &mut IntrinsicISizesContribution,
first_row: bool,
table_layout: TableLayout,
surrounding_inline_size: Au,
) {
match table_layout {
TableLayout::Fixed => {
if first_row {
for cell_inline_size in &row.cell_intrinsic_inline_sizes {
column_inline_sizes.push(cell_inline_size.column_size);
}
}
},
TableLayout::Auto => {
computation.union_block(&TableFlow::update_automatic_column_inline_sizes(
column_inline_sizes,
&row.cell_intrinsic_inline_sizes,
surrounding_inline_size,
))
},
}
}
pub fn spacing(&self) -> border_spacing::T {
let style = self.block_flow.fragment.style();
match style.get_inherited_table().border_collapse {
border_collapse::T::Separate => style.get_inherited_table().border_spacing,
border_collapse::T::Collapse => border_spacing::T::zero(),
}
}
pub fn total_horizontal_spacing(&self) -> Au {
let num_columns = self.column_intrinsic_inline_sizes.len();
if num_columns == 0 {
return Au(0);
}
self.spacing().horizontal() * (num_columns as i32 + 1)
}
fn column_styles(&self) -> Vec<ColumnStyle> {
let mut styles = vec![];
for group in self
.block_flow
.base
.child_iter()
.filter(|kid| kid.is_table_colgroup())
{
let group = group.as_table_colgroup();
let colgroup_style = group.fragment.as_ref().map(|f| f.style());
if group.cols.is_empty() {
let span = group
.fragment
.as_ref()
.map(|f| f.column_span())
.unwrap_or(1);
styles.push(ColumnStyle {
span,
colgroup_style,
col_style: None,
});
} else {
for col in &group.cols {
styles.push(ColumnStyle {
span: col.column_span(),
colgroup_style,
col_style: Some(col.style()),
})
}
}
}
styles
}
}
impl Flow for TableFlow {
fn class(&self) -> FlowClass {
FlowClass::Table
}
fn as_mut_table(&mut self) -> &mut TableFlow {
self
}
fn as_table(&self) -> &TableFlow {
self
}
fn as_mut_block(&mut self) -> &mut BlockFlow {
&mut self.block_flow
}
fn as_block(&self) -> &BlockFlow {
&self.block_flow
}
fn mark_as_root(&mut self) {
self.block_flow.mark_as_root();
}
fn bubble_inline_sizes(&mut self) {
let _scope = layout_debug_scope!(
"table::bubble_inline_sizes {:x}",
self.block_flow.base.debug_id()
);
for kid in self
.block_flow
.base
.child_iter_mut()
.filter(|kid| kid.is_table_colgroup())
{
for specified_inline_size in &kid.as_mut_table_colgroup().inline_sizes {
self.column_intrinsic_inline_sizes
.push(ColumnIntrinsicInlineSize {
minimum_length: specified_inline_size
.maybe_to_used_value(None)
.unwrap_or(Au(0)),
percentage: match *specified_inline_size {
Size::LengthPercentage(ref lp) => {
lp.0.to_percentage().map_or(0.0, |p| p.0)
},
_ => 0.0,
},
preferred: Au(0),
constrained: false,
})
}
}
self.collapsed_inline_direction_border_widths_for_table = Vec::new();
self.collapsed_block_direction_border_widths_for_table = vec![Au(0)];
let collapsing_borders = self
.block_flow
.fragment
.style
.get_inherited_table()
.border_collapse ==
border_collapse::T::Collapse;
let table_inline_collapsed_borders = if collapsing_borders {
Some(TableInlineCollapsedBorders {
start: CollapsedBorder::inline_start(
&self.block_flow.fragment.style,
CollapsedBorderFrom::Table,
),
end: CollapsedBorder::inline_end(
&self.block_flow.fragment.style,
CollapsedBorderFrom::Table,
),
})
} else {
None
};
let mut computation = IntrinsicISizesContribution::new();
let mut previous_collapsed_block_end_borders =
PreviousBlockCollapsedBorders::FromTable(CollapsedBorder::block_start(
&self.block_flow.fragment.style,
CollapsedBorderFrom::Table,
));
let mut first_row = true;
let (border_padding, _) = self.block_flow.fragment.surrounding_intrinsic_inline_size();
{
let mut iterator = TableRowIterator::new(&mut self.block_flow.base).peekable();
while let Some(row) = iterator.next() {
TableFlow::update_column_inline_sizes_for_row(
row,
&mut self.column_intrinsic_inline_sizes,
&mut computation,
first_row,
self.table_layout,
border_padding,
);
if collapsing_borders {
let next_index_and_sibling = iterator.peek();
let next_collapsed_borders_in_block_direction = match next_index_and_sibling {
Some(next_sibling) => NextBlockCollapsedBorders::FromNextRow(
&next_sibling
.as_table_row()
.preliminary_collapsed_borders
.block_start,
),
None => NextBlockCollapsedBorders::FromTable(CollapsedBorder::block_end(
&self.block_flow.fragment.style,
CollapsedBorderFrom::Table,
)),
};
perform_border_collapse_for_row(
row,
table_inline_collapsed_borders.as_ref().unwrap(),
previous_collapsed_block_end_borders,
next_collapsed_borders_in_block_direction,
&mut self.collapsed_inline_direction_border_widths_for_table,
&mut self.collapsed_block_direction_border_widths_for_table,
);
previous_collapsed_block_end_borders =
PreviousBlockCollapsedBorders::FromPreviousRow(
row.final_collapsed_borders.block_end.clone(),
);
}
first_row = false
}
}
let total_horizontal_spacing = self.total_horizontal_spacing();
let mut style_specified_intrinsic_inline_size = self
.block_flow
.fragment
.style_specified_intrinsic_inline_size()
.finish();
style_specified_intrinsic_inline_size.minimum_inline_size -= total_horizontal_spacing;
style_specified_intrinsic_inline_size.preferred_inline_size -= total_horizontal_spacing;
computation.union_block(&style_specified_intrinsic_inline_size);
computation.surrounding_size += total_horizontal_spacing;
self.block_flow.base.intrinsic_inline_sizes = computation.finish()
}
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
let _scope = layout_debug_scope!(
"table::assign_inline_sizes {:x}",
self.block_flow.base.debug_id()
);
debug!(
"assign_inline_sizes({}): assigning inline_size for flow",
"table"
);
trace!("TableFlow before assigning: {:?}", &self);
let shared_context = layout_context.shared_context();
let containing_block_inline_size = self.block_flow.base.block_container_inline_size;
let mut constrained_column_inline_sizes_indices = vec![];
let mut unspecified_inline_sizes_indices = vec![];
for (idx, column_inline_size) in self.column_intrinsic_inline_sizes.iter().enumerate() {
if column_inline_size.constrained {
constrained_column_inline_sizes_indices.push(idx);
} else if column_inline_size.percentage == 0.0 {
unspecified_inline_sizes_indices.push(idx);
}
}
let inline_size_computer = InternalTable;
inline_size_computer.compute_used_inline_size(
&mut self.block_flow,
shared_context,
containing_block_inline_size,
);
let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
let inline_end_content_edge = self.block_flow.fragment.border_padding.inline_end;
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
let spacing_per_cell = self.spacing();
let total_horizontal_spacing = self.total_horizontal_spacing();
let content_inline_size = self.block_flow.fragment.border_box.size.inline -
padding_and_borders -
total_horizontal_spacing;
let mut remaining_inline_size = content_inline_size;
match self.table_layout {
TableLayout::Fixed => {
self.column_computed_inline_sizes.clear();
for column_inline_size in &self.column_intrinsic_inline_sizes {
if column_inline_size.constrained {
self.column_computed_inline_sizes
.push(ColumnComputedInlineSize {
size: column_inline_size.minimum_length,
});
remaining_inline_size -= column_inline_size.minimum_length;
} else if column_inline_size.percentage != 0.0 {
let size = remaining_inline_size.scale_by(column_inline_size.percentage);
self.column_computed_inline_sizes
.push(ColumnComputedInlineSize { size });
remaining_inline_size -= size;
} else {
self.column_computed_inline_sizes
.push(ColumnComputedInlineSize { size: Au(0) });
}
}
if !unspecified_inline_sizes_indices.is_empty() {
for &index in &unspecified_inline_sizes_indices {
self.column_computed_inline_sizes[index].size = remaining_inline_size
.scale_by(1.0 / unspecified_inline_sizes_indices.len() as f32);
}
} else {
let total_minimum_size = self
.column_intrinsic_inline_sizes
.iter()
.filter(|size| size.constrained)
.map(|size| size.minimum_length.0 as f32)
.sum::<f32>();
for &index in &constrained_column_inline_sizes_indices {
let inline_size = self.column_computed_inline_sizes[index].size.0;
self.column_computed_inline_sizes[index].size +=
remaining_inline_size.scale_by(inline_size as f32 / total_minimum_size);
}
}
},
_ => {
},
}
let column_computed_inline_sizes = &self.column_computed_inline_sizes;
let collapsed_inline_direction_border_widths_for_table =
&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,
content_inline_size,
|child_flow,
_child_index,
_content_inline_size,
writing_mode,
_inline_start_margin_edge,
_inline_end_margin_edge| {
table_row::propagate_column_inline_sizes_to_child(
child_flow,
writing_mode,
column_computed_inline_sizes,
&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(
collapsed_inline_direction_border_widths_for_table,
&mut collapsed_block_direction_border_widths_for_table,
);
} else if child_flow.is_table_rowgroup() {
let child_table_rowgroup = child_flow.as_mut_table_rowgroup();
child_table_rowgroup.populate_collapsed_border_spacing(
collapsed_inline_direction_border_widths_for_table,
&mut collapsed_block_direction_border_widths_for_table,
);
}
},
);
trace!("TableFlow after assigning: {:?}", &self);
}
fn assign_block_size(&mut self, lc: &LayoutContext) {
debug!("assign_block_size: assigning block_size for table");
trace!("TableFlow before assigning: {:?}", &self);
let vertical_spacing = self.spacing().vertical();
self.block_flow
.assign_block_size_for_table_like_flow(vertical_spacing, lc);
trace!("TableFlow after assigning: {:?}", &self);
}
fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
self.block_flow
.compute_stacking_relative_position(layout_context)
}
fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
self.block_flow.generated_containing_block_size(flow)
}
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
self.block_flow
.update_late_computed_inline_position_if_necessary(inline_position)
}
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
self.block_flow
.update_late_computed_block_position_if_necessary(block_position)
}
fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
let border_painting_mode = match self
.block_flow
.fragment
.style
.get_inherited_table()
.border_collapse
{
border_collapse::T::Separate => BorderPaintingMode::Separate,
border_collapse::T::Collapse => BorderPaintingMode::Hidden,
};
self.block_flow
.build_display_list_for_block(state, border_painting_mode);
let iter = TableCellStyleIterator::new(self);
for style in iter {
style.build_display_list(state)
}
}
fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
self.block_flow.collect_stacking_contexts_for_block(
state,
StackingContextCollectionFlags::NEVER_CREATES_STACKING_CONTEXT,
);
}
fn repair_style(&mut self, new_style: &crate::ServoArc<ComputedValues>) {
self.block_flow.repair_style(new_style)
}
fn compute_overflow(&self) -> Overflow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_border_boxes(
&self,
iterator: &mut dyn FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>,
) {
self.block_flow.iterate_through_fragment_border_boxes(
iterator,
level,
stacking_context_position,
)
}
fn mutate_fragments(&mut self, mutator: &mut dyn FnMut(&mut Fragment)) {
self.block_flow.mutate_fragments(mutator)
}
fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
self.block_flow.print_extra_flow_children(print_tree);
}
}
#[derive(Debug)]
struct ColumnStyle<'table> {
span: u32,
colgroup_style: Option<&'table ComputedValues>,
col_style: Option<&'table ComputedValues>,
}
impl fmt::Debug for TableFlow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TableFlow: {:?}", self.block_flow)
}
}
pub struct InternalTable;
impl ISizeAndMarginsComputer for InternalTable {
fn compute_used_inline_size(
&self,
block: &mut BlockFlow,
shared_context: &SharedStyleContext,
parent_flow_inline_size: Au,
) {
let mut input = self.compute_inline_size_constraint_inputs(
block,
parent_flow_inline_size,
shared_context,
);
let minimum_inline_size = block.base.intrinsic_inline_sizes.minimum_inline_size -
block.fragment.border_padding.inline_start_end();
input.available_inline_size = cmp::max(input.available_inline_size, minimum_inline_size);
let solution = self.solve_inline_size_constraints(block, &input);
self.set_inline_size_constraint_solutions(block, solution);
}
fn solve_inline_size_constraints(
&self,
_: &mut BlockFlow,
input: &ISizeConstraintInput,
) -> ISizeConstraintSolution {
ISizeConstraintSolution::new(input.available_inline_size, Au(0), Au(0))
}
}
#[derive(Clone, Copy, Debug, Serialize)]
pub struct ColumnIntrinsicInlineSize {
pub preferred: Au,
pub minimum_length: Au,
pub percentage: CSSFloat,
pub constrained: bool,
}
impl ColumnIntrinsicInlineSize {
pub fn new() -> ColumnIntrinsicInlineSize {
ColumnIntrinsicInlineSize {
preferred: Au(0),
minimum_length: Au(0),
percentage: 0.0,
constrained: false,
}
}
pub fn greatest_percentage(&self, other: &ColumnIntrinsicInlineSize) -> CSSFloat {
if self.percentage > other.percentage {
self.percentage
} else {
other.percentage
}
}
}
impl Default for ColumnIntrinsicInlineSize {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, Serialize)]
pub struct ColumnComputedInlineSize {
pub size: Au,
}
pub trait VecExt<T> {
fn push_or_set(&mut self, index: usize, value: T) -> &mut T;
fn get_mut_or_push(&mut self, index: usize, zero: T) -> &mut T;
}
impl<T> VecExt<T> for Vec<T> {
fn push_or_set(&mut self, index: usize, value: T) -> &mut T {
if index < self.len() {
self[index] = value
} else {
debug_assert_eq!(index, self.len());
self.push(value)
}
&mut self[index]
}
fn get_mut_or_push(&mut self, index: usize, zero: T) -> &mut T {
if index >= self.len() {
debug_assert_eq!(index, self.len());
self.push(zero)
}
&mut self[index]
}
}
fn perform_border_collapse_for_row(
child_table_row: &mut TableRowFlow,
table_inline_borders: &TableInlineCollapsedBorders,
previous_block_borders: PreviousBlockCollapsedBorders,
next_block_borders: NextBlockCollapsedBorders,
inline_spacing: &mut Vec<Au>,
block_spacing: &mut Vec<Au>,
) {
let number_of_borders_inline_direction =
child_table_row.preliminary_collapsed_borders.inline.len();
for (i, this_inline_border) in child_table_row
.preliminary_collapsed_borders
.inline
.iter_mut()
.enumerate()
{
child_table_row
.final_collapsed_borders
.inline
.push_or_set(i, this_inline_border.clone());
if i == 0 {
child_table_row.final_collapsed_borders.inline[i].combine(&table_inline_borders.start);
} else if i + 1 == number_of_borders_inline_direction {
child_table_row.final_collapsed_borders.inline[i].combine(&table_inline_borders.end);
}
let inline_spacing = inline_spacing.get_mut_or_push(i, Au(0));
*inline_spacing = cmp::max(
*inline_spacing,
child_table_row.final_collapsed_borders.inline[i].width,
)
}
let block_start_borders = &mut child_table_row.final_collapsed_borders.block_start;
block_start_borders.clone_from(&child_table_row.preliminary_collapsed_borders.block_start);
for (i, this_border) in block_start_borders.iter_mut().enumerate() {
match previous_block_borders {
PreviousBlockCollapsedBorders::FromPreviousRow(ref previous_block_borders) => {
if previous_block_borders.len() > i {
this_border.combine(&previous_block_borders[i]);
}
},
PreviousBlockCollapsedBorders::FromTable(ref table_border) => {
this_border.combine(table_border);
},
}
}
let next_block = &mut child_table_row.final_collapsed_borders.block_end;
block_spacing.push(Au(0));
let block_spacing = block_spacing.last_mut().unwrap();
for (i, this_block_border) in child_table_row
.preliminary_collapsed_borders
.block_end
.iter()
.enumerate()
{
let next_block = next_block.push_or_set(i, this_block_border.clone());
match next_block_borders {
NextBlockCollapsedBorders::FromNextRow(next_block_borders) => {
if next_block_borders.len() > i {
next_block.combine(&next_block_borders[i])
}
},
NextBlockCollapsedBorders::FromTable(ref next_block_borders) => {
next_block.combine(next_block_borders);
},
}
*block_spacing = cmp::max(*block_spacing, next_block.width)
}
}
pub trait TableLikeFlow {
fn assign_block_size_for_table_like_flow(
&mut self,
block_direction_spacing: Au,
layout_context: &LayoutContext,
);
}
impl TableLikeFlow for BlockFlow {
fn assign_block_size_for_table_like_flow(
&mut self,
block_direction_spacing: Au,
layout_context: &LayoutContext,
) {
debug_assert!(
self.fragment.style.get_inherited_table().border_collapse ==
border_collapse::T::Separate ||
block_direction_spacing == Au(0)
);
fn border_spacing_for_row(
fragment: &Fragment,
row: &TableRowFlow,
block_direction_spacing: Au,
) -> Au {
match fragment.style.get_inherited_table().border_collapse {
border_collapse::T::Separate => block_direction_spacing,
border_collapse::T::Collapse => row.collapsed_border_spacing.block_start,
}
}
if self
.base
.restyle_damage
.contains(ServoRestyleDamage::REFLOW)
{
let mut sizes = vec![Default::default()];
let mut cumulative_border_spacing = Au(0);
let mut incoming_rowspan_data = vec![];
let mut rowgroup_id = 0;
let mut first = true;
for kid in self.base.child_iter_mut() {
if kid.is_table_row() {
if first {
first = false;
continue;
}
cumulative_border_spacing += border_spacing_for_row(
&self.fragment,
kid.as_table_row(),
block_direction_spacing,
);
sizes.push(TableRowSizeData {
size: Au(0),
cumulative_border_spacing,
rowgroup_id,
});
} else if kid.is_table_rowgroup() && !first {
rowgroup_id += 1;
}
}
let mut i = 0;
for kid in self.base.child_iter_mut() {
if kid.is_table_row() {
let size = kid.as_mut_table_row().compute_block_size_table_row_base(
layout_context,
&mut incoming_rowspan_data,
&sizes,
i,
);
sizes[i].size = size;
i += 1;
}
}
let block_start_border_padding = self.fragment.border_padding.block_start;
let mut current_block_offset = block_start_border_padding;
let mut has_rows = false;
let mut i = 0;
for kid in self.base.child_iter_mut() {
if kid.is_table_row() {
has_rows = true;
let row = kid.as_mut_table_row();
row.assign_block_size_to_self_and_children(&sizes, i);
row.mut_base().restyle_damage.remove(
ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW,
);
current_block_offset +=
border_spacing_for_row(&self.fragment, row, block_direction_spacing);
i += 1;
}
kid.mut_base().position.start.b = current_block_offset;
let kid_base = kid.mut_base();
current_block_offset += kid_base.position.size.block;
}
let mut block_size = current_block_offset - block_start_border_padding;
let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
&self.fragment,
self.base.block_container_explicit_block_size,
);
while let Some(candidate_block_size) = candidate_block_size_iterator.next() {
candidate_block_size_iterator.candidate_value = match candidate_block_size {
MaybeAuto::Auto => block_size,
MaybeAuto::Specified(value) => value,
};
}
block_size = candidate_block_size_iterator.candidate_value;
let delta = block_size - (current_block_offset - block_start_border_padding);
current_block_offset += delta;
let block_end_offset = self.fragment.border_padding.block_end +
if has_rows {
block_direction_spacing
} else {
Au(0)
};
current_block_offset += block_end_offset;
self.fragment.border_box.size.block = current_block_offset;
self.fragment.border_box.start.b = Au(0);
self.base.position.size.block = current_block_offset;
for kid in self.base.child_iter_mut() {
kid.mut_base().early_absolute_position_info = EarlyAbsolutePositionInfo {
relative_containing_block_size: self.fragment.content_box().size,
relative_containing_block_mode: self.fragment.style().writing_mode,
};
}
}
self.base
.restyle_damage
.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
}
}
#[derive(Debug)]
struct TableInlineCollapsedBorders {
start: CollapsedBorder,
end: CollapsedBorder,
}
enum PreviousBlockCollapsedBorders {
FromPreviousRow(Vec<CollapsedBorder>),
FromTable(CollapsedBorder),
}
enum NextBlockCollapsedBorders<'a> {
FromNextRow(&'a [CollapsedBorder]),
FromTable(CollapsedBorder),
}
struct TableRowAndGroupIterator<'a> {
kids: FlowListIterator<'a>,
group: Option<(&'a Fragment, FlowListIterator<'a>)>,
}
impl<'a> TableRowAndGroupIterator<'a> {
fn new(base: &'a BaseFlow) -> Self {
TableRowAndGroupIterator {
kids: base.child_iter(),
group: None,
}
}
}
impl<'a> Iterator for TableRowAndGroupIterator<'a> {
type Item = (Option<&'a Fragment>, &'a TableRowFlow);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some(ref mut group) = self.group {
if let Some(grandkid) = group.1.next() {
return Some((Some(group.0), grandkid.as_table_row()));
}
}
self.group = None;
match self.kids.next() {
Some(kid) => {
if kid.is_table_rowgroup() {
let rowgroup = kid.as_table_rowgroup();
let iter = rowgroup.block_flow.base.child_iter();
self.group = Some((&rowgroup.block_flow.fragment, iter));
self.next()
} else if kid.is_table_row() {
Some((None, kid.as_table_row()))
} else {
self.next() }
},
None => None,
}
}
}
struct MutTableRowAndGroupIterator<'a> {
kids: MutFlowListIterator<'a>,
group: Option<(&'a Fragment, MutFlowListIterator<'a>)>,
}
impl<'a> MutTableRowAndGroupIterator<'a> {
fn new(base: &'a mut BaseFlow) -> Self {
MutTableRowAndGroupIterator {
kids: base.child_iter_mut(),
group: None,
}
}
}
impl<'a> Iterator for MutTableRowAndGroupIterator<'a> {
type Item = (Option<&'a Fragment>, &'a mut TableRowFlow);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some(ref mut group) = self.group {
if let Some(grandkid) = group.1.next() {
return Some((Some(group.0), grandkid.as_mut_table_row()));
}
}
self.group = None;
match self.kids.next() {
Some(kid) => {
if kid.is_table_rowgroup() {
let rowgroup = kid.as_mut_table_rowgroup();
let iter = rowgroup.block_flow.base.child_iter_mut();
self.group = Some((&rowgroup.block_flow.fragment, iter));
self.next()
} else if kid.is_table_row() {
Some((None, kid.as_mut_table_row()))
} else {
self.next() }
},
None => None,
}
}
}
struct TableRowIterator<'a>(MutTableRowAndGroupIterator<'a>);
impl<'a> TableRowIterator<'a> {
fn new(base: &'a mut BaseFlow) -> Self {
TableRowIterator(MutTableRowAndGroupIterator::new(base))
}
}
impl<'a> Iterator for TableRowIterator<'a> {
type Item = &'a mut TableRowFlow;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|n| n.1)
}
}
struct TableCellStyleIterator<'table> {
column_styles: Vec<ColumnStyle<'table>>,
row_iterator: TableRowAndGroupIterator<'table>,
row_info: Option<TableCellStyleIteratorRowInfo<'table>>,
column_index: TableCellColumnIndexData,
}
struct TableCellStyleIteratorRowInfo<'table> {
row: &'table TableRowFlow,
rowgroup: Option<&'table Fragment>,
cell_iterator: FlowListIterator<'table>,
}
impl<'table> TableCellStyleIterator<'table> {
fn new(table: &'table TableFlow) -> Self {
let column_styles = table.column_styles();
let mut row_iterator = TableRowAndGroupIterator::new(&table.block_flow.base);
let row_info = if let Some((group, row)) = row_iterator.next() {
Some(TableCellStyleIteratorRowInfo {
row,
rowgroup: group,
cell_iterator: row.block_flow.base.child_iter(),
})
} else {
None
};
TableCellStyleIterator {
column_styles,
row_iterator,
row_info,
column_index: Default::default(),
}
}
}
struct TableCellStyleInfo<'table> {
cell: &'table TableCellFlow,
colgroup_style: Option<&'table ComputedValues>,
col_style: Option<&'table ComputedValues>,
rowgroup_style: Option<&'table ComputedValues>,
row_style: &'table ComputedValues,
}
#[derive(Default)]
struct TableCellColumnIndexData {
pub absolute: u32,
pub relative: u32,
pub relative_offset: u32,
}
impl TableCellColumnIndexData {
fn advance(&mut self, amount: u32, column_styles: &[ColumnStyle]) {
self.absolute += amount;
self.relative_offset += amount;
if let Some(mut current_col) = column_styles.get(self.relative as usize) {
while self.relative_offset >= current_col.span {
self.relative += 1;
self.relative_offset -= current_col.span;
if let Some(column_style) = column_styles.get(self.relative as usize) {
current_col = column_style;
} else {
break;
}
}
}
}
}
impl<'table> Iterator for TableCellStyleIterator<'table> {
type Item = TableCellStyleInfo<'table>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut row_info) = self.row_info.take() {
if let Some(rowspan) = row_info
.row
.incoming_rowspan
.get(self.column_index.absolute as usize)
{
if *rowspan > 1 {
self.column_index.advance(1, &self.column_styles);
self.row_info = Some(row_info);
return self.next();
}
}
if let Some(cell) = row_info.cell_iterator.next() {
let rowgroup_style = row_info.rowgroup.map(|r| r.style());
let row_style = row_info.row.block_flow.fragment.style();
let cell = cell.as_table_cell();
let (col_style, colgroup_style) = if let Some(column_style) =
self.column_styles.get(self.column_index.relative as usize)
{
let styles = (column_style.col_style, column_style.colgroup_style);
self.column_index
.advance(cell.column_span, &self.column_styles);
styles
} else {
(None, None)
};
self.row_info = Some(row_info);
Some(TableCellStyleInfo {
cell,
colgroup_style,
col_style,
rowgroup_style,
row_style,
})
} else {
if let Some((group, row)) = self.row_iterator.next() {
self.row_info = Some(TableCellStyleIteratorRowInfo {
row,
rowgroup: group,
cell_iterator: row.block_flow.base.child_iter(),
});
self.column_index = Default::default();
self.next()
} else {
None
}
}
} else {
None
}
}
}
impl<'table> TableCellStyleInfo<'table> {
fn build_display_list(&self, mut state: &mut DisplayListBuildState) {
use style::computed_values::visibility::T as Visibility;
if !self.cell.visible ||
self.cell
.block_flow
.fragment
.style()
.get_inherited_box()
.visibility !=
Visibility::Visible
{
return;
}
let border_painting_mode = match self
.cell
.block_flow
.fragment
.style
.get_inherited_table()
.border_collapse
{
border_collapse::T::Separate => BorderPaintingMode::Separate,
border_collapse::T::Collapse => {
BorderPaintingMode::Collapse(&self.cell.collapsed_borders)
},
};
{
let cell_flow = &self.cell.block_flow;
let build_dl = |sty: &ComputedValues, state: &mut &mut DisplayListBuildState| {
let background = sty.get_background();
let background_color = sty.resolve_color(background.background_color.clone());
cell_flow.build_display_list_for_background_if_applicable_with_background(
state,
background,
background_color,
);
};
if let Some(sty) = self.colgroup_style {
build_dl(sty, &mut state);
}
if let Some(sty) = self.col_style {
build_dl(sty, &mut state);
}
if let Some(sty) = self.rowgroup_style {
build_dl(sty, &mut state);
}
build_dl(self.row_style, &mut state);
}
self.cell
.block_flow
.build_display_list_for_block_no_damage(state, border_painting_mode)
}
}