1#![expect(rustdoc::private_intra_doc_links)]
5
6mod construct;
69mod layout;
70
71use std::ops::Range;
72
73use app_units::Au;
74use atomic_refcell::AtomicRef;
75pub use construct::TableBuilder;
76pub(crate) use construct::{AnonymousTableContent, AnonymousTableContentElement};
77use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
78use malloc_size_of_derive::MallocSizeOf;
79use script::layout_dom::{ServoDangerousStyleElement, ServoLayoutNode};
80use servo_arc::Arc;
81use style::context::SharedStyleContext;
82use style::properties::ComputedValues;
83use style::properties::style_structs::Font;
84use style::selector_parser::PseudoElement;
85
86use super::flow::BlockFormattingContext;
87use crate::cell::{ArcRefCell, WeakRefCell};
88use crate::dom::WeakLayoutBox;
89use crate::flow::BlockContainer;
90use crate::formatting_contexts::{
91 IndependentFormattingContext, IndependentFormattingContextContents,
92};
93use crate::fragment_tree::{BaseFragmentInfo, Fragment};
94use crate::geom::PhysicalVec;
95use crate::layout_box_base::LayoutBoxBase;
96use crate::style_ext::BorderStyleColor;
97use crate::table::layout::TableLayout;
98use crate::{PropagatedBoxTreeData, SharedStyle};
99
100pub type TableSize = Size2D<usize, UnknownUnit>;
101
102#[derive(Debug, MallocSizeOf)]
103pub struct Table {
104 style: Arc<ComputedValues>,
108
109 grid_style: Arc<ComputedValues>,
112
113 grid_base_fragment_info: BaseFragmentInfo,
116
117 pub captions: Vec<ArcRefCell<TableCaption>>,
119
120 pub column_groups: Vec<ArcRefCell<TableTrackGroup>>,
122
123 pub columns: Vec<ArcRefCell<TableTrack>>,
126
127 pub row_groups: Vec<ArcRefCell<TableTrackGroup>>,
129
130 pub rows: Vec<ArcRefCell<TableTrack>>,
132
133 pub slots: Vec<Vec<TableSlot>>,
135
136 pub size: TableSize,
138
139 anonymous: bool,
141
142 percentage_columns_allowed_for_inline_content_sizes: bool,
144}
145
146impl Table {
147 pub(crate) fn new(
148 style: Arc<ComputedValues>,
149 grid_style: Arc<ComputedValues>,
150 base_fragment_info: BaseFragmentInfo,
151 percentage_columns_allowed_for_inline_content_sizes: bool,
152 ) -> Self {
153 Self {
154 style,
155 grid_style,
156 grid_base_fragment_info: base_fragment_info,
157 captions: Vec::new(),
158 column_groups: Vec::new(),
159 columns: Vec::new(),
160 row_groups: Vec::new(),
161 rows: Vec::new(),
162 slots: Vec::new(),
163 size: TableSize::zero(),
164 anonymous: false,
165 percentage_columns_allowed_for_inline_content_sizes,
166 }
167 }
168
169 fn get_slot(&self, coords: TableSlotCoordinates) -> Option<&TableSlot> {
172 self.slots.get(coords.y)?.get(coords.x)
173 }
174
175 fn resolve_first_cell_coords(
176 &self,
177 coords: TableSlotCoordinates,
178 ) -> Option<TableSlotCoordinates> {
179 match self.get_slot(coords) {
180 Some(&TableSlot::Cell(_)) => Some(coords),
181 Some(TableSlot::Spanned(offsets)) => Some(coords - offsets[0]),
182 _ => None,
183 }
184 }
185
186 fn resolve_first_cell(
187 &self,
188 coords: TableSlotCoordinates,
189 ) -> Option<AtomicRef<'_, TableSlotCell>> {
190 let resolved_coords = self.resolve_first_cell_coords(coords)?;
191 let slot = self.get_slot(resolved_coords);
192 match slot {
193 Some(TableSlot::Cell(cell)) => Some(cell.borrow()),
194 _ => unreachable!(
195 "Spanned slot should not point to an empty cell or another spanned slot."
196 ),
197 }
198 }
199
200 pub(crate) fn repair_style(
201 &mut self,
202 context: &SharedStyleContext,
203 new_style: &Arc<ComputedValues>,
204 base: &LayoutBoxBase,
205 ) {
206 self.style = new_style.clone();
207 let new_grid_style = context
208 .stylist
209 .style_for_anonymous::<ServoDangerousStyleElement>(
210 &context.guards,
211 &PseudoElement::ServoTableGrid,
212 new_style,
213 );
214
215 for fragment in base.fragments.borrow().iter() {
220 if let Some(children) = fragment.children() {
221 for child_fragment in &*children {
222 if let Fragment::Box(box_fragment) = child_fragment &&
223 Arc::ptr_eq(&self.grid_style, &box_fragment.style.borrow())
224 {
225 child_fragment.repair_style(&new_grid_style);
226 }
227 }
228 }
229 fragment.repair_style(new_style);
230 }
231 self.grid_style = new_grid_style;
232 }
233
234 pub(crate) fn subtree_size(&self) -> usize {
235 self.slots
236 .iter()
237 .flat_map(|row| row.iter())
238 .map(TableSlot::subtree_size)
239 .sum()
240 }
241}
242
243type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
244pub type TableSlotOffset = Vector2D<usize, UnknownUnit>;
245
246#[derive(Debug, MallocSizeOf)]
247pub struct TableSlotCell {
248 pub(crate) context: IndependentFormattingContext,
251
252 colspan: usize,
254
255 rowspan: usize,
258}
259
260impl TableSlotCell {
261 pub fn mock_for_testing(id: usize, colspan: usize, rowspan: usize) -> Self {
262 let base = LayoutBoxBase::new(
263 BaseFragmentInfo::new_for_testing(id),
264 ComputedValues::initial_values_with_font_override(Font::initial_values()).to_arc(),
265 );
266 let contents = IndependentFormattingContextContents::Flow(BlockFormattingContext {
267 contents: BlockContainer::BlockLevelBoxes(Vec::new()),
268 contains_floats: false,
269 });
270 let propagated_data =
271 PropagatedBoxTreeData::default().disallowing_percentage_table_columns();
272 Self {
273 context: IndependentFormattingContext::new(base, contents, propagated_data),
274 colspan,
275 rowspan,
276 }
277 }
278
279 pub fn node_id(&self) -> usize {
281 self.context
282 .base
283 .base_fragment_info
284 .tag
285 .map_or(0, |tag| tag.node.0)
286 }
287}
288
289#[derive(MallocSizeOf)]
294pub enum TableSlot {
295 Cell(ArcRefCell<TableSlotCell>),
297
298 Spanned(Vec<TableSlotOffset>),
306
307 Empty,
311}
312
313impl std::fmt::Debug for TableSlot {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 match self {
316 Self::Cell(_) => f.debug_tuple("Cell").finish(),
317 Self::Spanned(spanned) => f.debug_tuple("Spanned").field(spanned).finish(),
318 Self::Empty => write!(f, "Empty"),
319 }
320 }
321}
322
323impl TableSlot {
324 fn new_spanned(offset: TableSlotOffset) -> Self {
325 Self::Spanned(vec![offset])
326 }
327
328 pub(crate) fn subtree_size(&self) -> usize {
329 match self {
330 TableSlot::Cell(cell) => cell.borrow().context.subtree_size(),
331 TableSlot::Spanned(..) | TableSlot::Empty => 0,
332 }
333 }
334}
335
336#[derive(Debug, MallocSizeOf)]
338pub struct TableTrack {
339 base: LayoutBoxBase,
341
342 group_index: Option<usize>,
345
346 is_anonymous: bool,
349
350 shared_background_style: SharedStyle,
354}
355
356impl TableTrack {
357 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
358 self.base.repair_style(new_style);
359 self.shared_background_style = SharedStyle::new(new_style.clone());
360 }
361}
362
363#[derive(Debug, MallocSizeOf, PartialEq)]
364pub enum TableTrackGroupType {
365 HeaderGroup,
366 FooterGroup,
367 RowGroup,
368 ColumnGroup,
369}
370
371#[derive(Debug, MallocSizeOf)]
372pub struct TableTrackGroup {
373 base: LayoutBoxBase,
375
376 group_type: TableTrackGroupType,
378
379 track_range: Range<usize>,
381
382 shared_background_style: SharedStyle,
386}
387
388impl TableTrackGroup {
389 pub(super) fn is_empty(&self) -> bool {
390 self.track_range.is_empty()
391 }
392
393 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
394 self.base.repair_style(new_style);
395 self.shared_background_style = SharedStyle::new(new_style.clone());
396 }
397}
398
399#[derive(Debug, MallocSizeOf)]
400pub struct TableCaption {
401 pub(crate) context: IndependentFormattingContext,
403}
404
405#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
407pub(crate) struct CollapsedBorder {
408 pub style_color: BorderStyleColor,
409 pub width: Au,
410}
411
412pub(crate) type CollapsedBorderLine = Vec<CollapsedBorder>;
414
415#[derive(Clone, Debug, MallocSizeOf)]
416pub(crate) struct SpecificTableGridInfo {
417 pub collapsed_borders: PhysicalVec<Vec<CollapsedBorderLine>>,
418 pub track_sizes: PhysicalVec<Vec<Au>>,
419}
420
421pub(crate) struct TableLayoutStyle<'a> {
422 table: &'a Table,
423 layout: Option<&'a TableLayout<'a>>,
424}
425
426#[derive(Debug, MallocSizeOf)]
430pub(crate) enum TableLevelBox {
431 Caption(ArcRefCell<TableCaption>),
432 Cell(ArcRefCell<TableSlotCell>),
433 TrackGroup(ArcRefCell<TableTrackGroup>),
434 Track(ArcRefCell<TableTrack>),
435}
436
437impl TableLevelBox {
438 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
439 match self {
440 TableLevelBox::Caption(caption) => callback(&caption.borrow().context.base),
441 TableLevelBox::Cell(cell) => callback(&cell.borrow().context.base),
442 TableLevelBox::TrackGroup(track_group) => callback(&track_group.borrow().base),
443 TableLevelBox::Track(track) => callback(&track.borrow().base),
444 }
445 }
446
447 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
448 match self {
449 TableLevelBox::Caption(caption) => callback(&mut caption.borrow_mut().context.base),
450 TableLevelBox::Cell(cell) => callback(&mut cell.borrow_mut().context.base),
451 TableLevelBox::TrackGroup(track_group) => callback(&mut track_group.borrow_mut().base),
452 TableLevelBox::Track(track) => callback(&mut track.borrow_mut().base),
453 }
454 }
455
456 pub(crate) fn repair_style(
457 &self,
458 context: &SharedStyleContext<'_>,
459 node: &ServoLayoutNode,
460 new_style: &Arc<ComputedValues>,
461 ) {
462 match self {
463 TableLevelBox::Caption(caption) => caption
464 .borrow_mut()
465 .context
466 .repair_style(context, node, new_style),
467 TableLevelBox::Cell(cell) => cell
468 .borrow_mut()
469 .context
470 .repair_style(context, node, new_style),
471 TableLevelBox::TrackGroup(track_group) => {
472 track_group.borrow_mut().repair_style(new_style);
473 },
474 TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style),
475 }
476 }
477
478 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
479 match self {
480 Self::Caption(caption) => caption.borrow().context.attached_to_tree(layout_box),
481 Self::Cell(cell) => cell.borrow().context.attached_to_tree(layout_box),
482 Self::TrackGroup(_) | Self::Track(_) => {
483 },
486 }
487 }
488
489 pub(crate) fn downgrade(&self) -> WeakTableLevelBox {
490 match self {
491 Self::Caption(caption) => WeakTableLevelBox::Caption(caption.downgrade()),
492 Self::Cell(cell) => WeakTableLevelBox::Cell(cell.downgrade()),
493 Self::TrackGroup(track_group) => WeakTableLevelBox::TrackGroup(track_group.downgrade()),
494 Self::Track(track) => WeakTableLevelBox::Track(track.downgrade()),
495 }
496 }
497}
498
499#[derive(Clone, Debug, MallocSizeOf)]
500pub(crate) enum WeakTableLevelBox {
501 Caption(WeakRefCell<TableCaption>),
502 Cell(WeakRefCell<TableSlotCell>),
503 TrackGroup(WeakRefCell<TableTrackGroup>),
504 Track(WeakRefCell<TableTrack>),
505}
506
507impl WeakTableLevelBox {
508 pub(crate) fn upgrade(&self) -> Option<TableLevelBox> {
509 Some(match self {
510 Self::Caption(caption) => TableLevelBox::Caption(caption.upgrade()?),
511 Self::Cell(cell) => TableLevelBox::Cell(cell.upgrade()?),
512 Self::TrackGroup(track_group) => TableLevelBox::TrackGroup(track_group.upgrade()?),
513 Self::Track(track) => TableLevelBox::Track(track.upgrade()?),
514 })
515 }
516}