#![allow(rustdoc::private_intra_doc_links)]
mod construct;
mod layout;
use std::ops::Range;
use app_units::Au;
use atomic_refcell::AtomicRef;
pub(crate) use construct::AnonymousTableContent;
pub use construct::TableBuilder;
use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
use malloc_size_of_derive::MallocSizeOf;
use script::layout_dom::{ServoLayoutElement, ServoThreadSafeLayoutNode};
use servo_arc::Arc;
use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::properties::style_structs::Font;
use style::selector_parser::PseudoElement;
use super::flow::BlockFormattingContext;
use crate::cell::{ArcRefCell, WeakRefCell};
use crate::dom::WeakLayoutBox;
use crate::flow::BlockContainer;
use crate::formatting_contexts::{
IndependentFormattingContext, IndependentFormattingContextContents,
};
use crate::fragment_tree::BaseFragmentInfo;
use crate::geom::PhysicalVec;
use crate::layout_box_base::LayoutBoxBase;
use crate::style_ext::BorderStyleColor;
use crate::table::layout::TableLayout;
use crate::{PropagatedBoxTreeData, SharedStyle};
pub type TableSize = Size2D<usize, UnknownUnit>;
#[derive(Debug, MallocSizeOf)]
pub struct Table {
style: Arc<ComputedValues>,
grid_style: Arc<ComputedValues>,
grid_base_fragment_info: BaseFragmentInfo,
pub captions: Vec<ArcRefCell<TableCaption>>,
pub column_groups: Vec<ArcRefCell<TableTrackGroup>>,
pub columns: Vec<ArcRefCell<TableTrack>>,
pub row_groups: Vec<ArcRefCell<TableTrackGroup>>,
pub rows: Vec<ArcRefCell<TableTrack>>,
pub slots: Vec<Vec<TableSlot>>,
pub size: TableSize,
anonymous: bool,
percentage_columns_allowed_for_inline_content_sizes: bool,
}
impl Table {
pub(crate) fn new(
style: Arc<ComputedValues>,
grid_style: Arc<ComputedValues>,
base_fragment_info: BaseFragmentInfo,
percentage_columns_allowed_for_inline_content_sizes: bool,
) -> Self {
Self {
style,
grid_style,
grid_base_fragment_info: base_fragment_info,
captions: Vec::new(),
column_groups: Vec::new(),
columns: Vec::new(),
row_groups: Vec::new(),
rows: Vec::new(),
slots: Vec::new(),
size: TableSize::zero(),
anonymous: false,
percentage_columns_allowed_for_inline_content_sizes,
}
}
fn get_slot(&self, coords: TableSlotCoordinates) -> Option<&TableSlot> {
self.slots.get(coords.y)?.get(coords.x)
}
fn resolve_first_cell_coords(
&self,
coords: TableSlotCoordinates,
) -> Option<TableSlotCoordinates> {
match self.get_slot(coords) {
Some(&TableSlot::Cell(_)) => Some(coords),
Some(TableSlot::Spanned(offsets)) => Some(coords - offsets[0]),
_ => None,
}
}
fn resolve_first_cell(
&self,
coords: TableSlotCoordinates,
) -> Option<AtomicRef<'_, TableSlotCell>> {
let resolved_coords = self.resolve_first_cell_coords(coords)?;
let slot = self.get_slot(resolved_coords);
match slot {
Some(TableSlot::Cell(cell)) => Some(cell.borrow()),
_ => unreachable!(
"Spanned slot should not point to an empty cell or another spanned slot."
),
}
}
pub(crate) fn repair_style(
&mut self,
context: &SharedStyleContext,
new_style: &Arc<ComputedValues>,
) {
self.style = new_style.clone();
self.grid_style = context.stylist.style_for_anonymous::<ServoLayoutElement>(
&context.guards,
&PseudoElement::ServoTableGrid,
new_style,
);
}
}
type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
pub type TableSlotOffset = Vector2D<usize, UnknownUnit>;
#[derive(Debug, MallocSizeOf)]
pub struct TableSlotCell {
pub(crate) context: IndependentFormattingContext,
colspan: usize,
rowspan: usize,
}
impl TableSlotCell {
pub fn mock_for_testing(id: usize, colspan: usize, rowspan: usize) -> Self {
let base = LayoutBoxBase::new(
BaseFragmentInfo::new_for_testing(id),
ComputedValues::initial_values_with_font_override(Font::initial_values()).to_arc(),
);
let contents = IndependentFormattingContextContents::Flow(BlockFormattingContext {
contents: BlockContainer::BlockLevelBoxes(Vec::new()),
contains_floats: false,
});
let propagated_data =
PropagatedBoxTreeData::default().disallowing_percentage_table_columns();
Self {
context: IndependentFormattingContext::new(base, contents, propagated_data),
colspan,
rowspan,
}
}
pub fn node_id(&self) -> usize {
self.context
.base
.base_fragment_info
.tag
.map_or(0, |tag| tag.node.0)
}
}
#[derive(MallocSizeOf)]
pub enum TableSlot {
Cell(ArcRefCell<TableSlotCell>),
Spanned(Vec<TableSlotOffset>),
Empty,
}
impl std::fmt::Debug for TableSlot {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Cell(_) => f.debug_tuple("Cell").finish(),
Self::Spanned(spanned) => f.debug_tuple("Spanned").field(spanned).finish(),
Self::Empty => write!(f, "Empty"),
}
}
}
impl TableSlot {
fn new_spanned(offset: TableSlotOffset) -> Self {
Self::Spanned(vec![offset])
}
}
#[derive(Debug, MallocSizeOf)]
pub struct TableTrack {
base: LayoutBoxBase,
group_index: Option<usize>,
is_anonymous: bool,
shared_background_style: SharedStyle,
}
impl TableTrack {
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
self.base.repair_style(new_style);
self.shared_background_style = SharedStyle::new(new_style.clone());
}
}
#[derive(Debug, MallocSizeOf, PartialEq)]
pub enum TableTrackGroupType {
HeaderGroup,
FooterGroup,
RowGroup,
ColumnGroup,
}
#[derive(Debug, MallocSizeOf)]
pub struct TableTrackGroup {
base: LayoutBoxBase,
group_type: TableTrackGroupType,
track_range: Range<usize>,
shared_background_style: SharedStyle,
}
impl TableTrackGroup {
pub(super) fn is_empty(&self) -> bool {
self.track_range.is_empty()
}
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
self.base.repair_style(new_style);
self.shared_background_style = SharedStyle::new(new_style.clone());
}
}
#[derive(Debug, MallocSizeOf)]
pub struct TableCaption {
pub(crate) context: IndependentFormattingContext,
}
#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
pub(crate) struct CollapsedBorder {
pub style_color: BorderStyleColor,
pub width: Au,
}
pub(crate) type CollapsedBorderLine = Vec<CollapsedBorder>;
#[derive(Clone, Debug, MallocSizeOf)]
pub(crate) struct SpecificTableGridInfo {
pub collapsed_borders: PhysicalVec<Vec<CollapsedBorderLine>>,
pub track_sizes: PhysicalVec<Vec<Au>>,
}
pub(crate) struct TableLayoutStyle<'a> {
table: &'a Table,
layout: Option<&'a TableLayout<'a>>,
}
#[derive(Debug, MallocSizeOf)]
pub(crate) enum TableLevelBox {
Caption(ArcRefCell<TableCaption>),
Cell(ArcRefCell<TableSlotCell>),
TrackGroup(ArcRefCell<TableTrackGroup>),
Track(ArcRefCell<TableTrack>),
}
impl TableLevelBox {
pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
match self {
TableLevelBox::Caption(caption) => callback(&caption.borrow().context.base),
TableLevelBox::Cell(cell) => callback(&cell.borrow().context.base),
TableLevelBox::TrackGroup(track_group) => callback(&track_group.borrow().base),
TableLevelBox::Track(track) => callback(&track.borrow().base),
}
}
pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
match self {
TableLevelBox::Caption(caption) => callback(&mut caption.borrow_mut().context.base),
TableLevelBox::Cell(cell) => callback(&mut cell.borrow_mut().context.base),
TableLevelBox::TrackGroup(track_group) => callback(&mut track_group.borrow_mut().base),
TableLevelBox::Track(track) => callback(&mut track.borrow_mut().base),
}
}
pub(crate) fn repair_style(
&self,
context: &SharedStyleContext<'_>,
node: &ServoThreadSafeLayoutNode,
new_style: &Arc<ComputedValues>,
) {
match self {
TableLevelBox::Caption(caption) => caption
.borrow_mut()
.context
.repair_style(context, node, new_style),
TableLevelBox::Cell(cell) => cell
.borrow_mut()
.context
.repair_style(context, node, new_style),
TableLevelBox::TrackGroup(track_group) => {
track_group.borrow_mut().repair_style(new_style);
},
TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style),
}
}
pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
match self {
Self::Caption(caption) => caption.borrow().context.attached_to_tree(layout_box),
Self::Cell(cell) => cell.borrow().context.attached_to_tree(layout_box),
Self::TrackGroup(_) | Self::Track(_) => {
},
}
}
pub(crate) fn downgrade(&self) -> WeakTableLevelBox {
match self {
Self::Caption(caption) => WeakTableLevelBox::Caption(caption.downgrade()),
Self::Cell(cell) => WeakTableLevelBox::Cell(cell.downgrade()),
Self::TrackGroup(track_group) => WeakTableLevelBox::TrackGroup(track_group.downgrade()),
Self::Track(track) => WeakTableLevelBox::Track(track.downgrade()),
}
}
}
#[derive(Clone, Debug, MallocSizeOf)]
pub(crate) enum WeakTableLevelBox {
Caption(WeakRefCell<TableCaption>),
Cell(WeakRefCell<TableSlotCell>),
TrackGroup(WeakRefCell<TableTrackGroup>),
Track(WeakRefCell<TableTrack>),
}
impl WeakTableLevelBox {
pub(crate) fn upgrade(&self) -> Option<TableLevelBox> {
Some(match self {
Self::Caption(caption) => TableLevelBox::Caption(caption.upgrade()?),
Self::Cell(cell) => TableLevelBox::Cell(cell.upgrade()?),
Self::TrackGroup(track_group) => TableLevelBox::TrackGroup(track_group.upgrade()?),
Self::Track(track) => TableLevelBox::Track(track.upgrade()?),
})
}
}