1#![allow(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(crate) use construct::AnonymousTableContent;
76pub use construct::TableBuilder;
77use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
78use malloc_size_of_derive::MallocSizeOf;
79use script::layout_dom::{ServoLayoutElement, ServoThreadSafeLayoutNode};
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::SharedStyle;
88use crate::cell::{ArcRefCell, WeakRefCell};
89use crate::dom::WeakLayoutBox;
90use crate::flow::BlockContainer;
91use crate::formatting_contexts::IndependentFormattingContext;
92use crate::fragment_tree::BaseFragmentInfo;
93use crate::geom::PhysicalVec;
94use crate::layout_box_base::LayoutBoxBase;
95use crate::style_ext::BorderStyleColor;
96use crate::table::layout::TableLayout;
97
98pub type TableSize = Size2D<usize, UnknownUnit>;
99
100#[derive(Debug, MallocSizeOf)]
101pub struct Table {
102 style: Arc<ComputedValues>,
106
107 grid_style: Arc<ComputedValues>,
110
111 grid_base_fragment_info: BaseFragmentInfo,
114
115 pub captions: Vec<ArcRefCell<TableCaption>>,
117
118 pub column_groups: Vec<ArcRefCell<TableTrackGroup>>,
120
121 pub columns: Vec<ArcRefCell<TableTrack>>,
124
125 pub row_groups: Vec<ArcRefCell<TableTrackGroup>>,
127
128 pub rows: Vec<ArcRefCell<TableTrack>>,
130
131 pub slots: Vec<Vec<TableSlot>>,
133
134 pub size: TableSize,
136
137 anonymous: bool,
139
140 percentage_columns_allowed_for_inline_content_sizes: bool,
142}
143
144impl Table {
145 pub(crate) fn new(
146 style: Arc<ComputedValues>,
147 grid_style: Arc<ComputedValues>,
148 base_fragment_info: BaseFragmentInfo,
149 percentage_columns_allowed_for_inline_content_sizes: bool,
150 ) -> Self {
151 Self {
152 style,
153 grid_style,
154 grid_base_fragment_info: base_fragment_info,
155 captions: Vec::new(),
156 column_groups: Vec::new(),
157 columns: Vec::new(),
158 row_groups: Vec::new(),
159 rows: Vec::new(),
160 slots: Vec::new(),
161 size: TableSize::zero(),
162 anonymous: false,
163 percentage_columns_allowed_for_inline_content_sizes,
164 }
165 }
166
167 fn get_slot(&self, coords: TableSlotCoordinates) -> Option<&TableSlot> {
170 self.slots.get(coords.y)?.get(coords.x)
171 }
172
173 fn resolve_first_cell_coords(
174 &self,
175 coords: TableSlotCoordinates,
176 ) -> Option<TableSlotCoordinates> {
177 match self.get_slot(coords) {
178 Some(&TableSlot::Cell(_)) => Some(coords),
179 Some(TableSlot::Spanned(offsets)) => Some(coords - offsets[0]),
180 _ => None,
181 }
182 }
183
184 fn resolve_first_cell(
185 &self,
186 coords: TableSlotCoordinates,
187 ) -> Option<AtomicRef<'_, TableSlotCell>> {
188 let resolved_coords = self.resolve_first_cell_coords(coords)?;
189 let slot = self.get_slot(resolved_coords);
190 match slot {
191 Some(TableSlot::Cell(cell)) => Some(cell.borrow()),
192 _ => unreachable!(
193 "Spanned slot should not point to an empty cell or another spanned slot."
194 ),
195 }
196 }
197
198 pub(crate) fn repair_style(
199 &mut self,
200 context: &SharedStyleContext,
201 new_style: &Arc<ComputedValues>,
202 ) {
203 self.style = new_style.clone();
204 self.grid_style = context.stylist.style_for_anonymous::<ServoLayoutElement>(
205 &context.guards,
206 &PseudoElement::ServoTableGrid,
207 new_style,
208 );
209 }
210}
211
212type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
213pub type TableSlotOffset = Vector2D<usize, UnknownUnit>;
214
215#[derive(Debug, MallocSizeOf)]
216pub struct TableSlotCell {
217 base: LayoutBoxBase,
219
220 contents: BlockFormattingContext,
222
223 colspan: usize,
225
226 rowspan: usize,
229}
230
231impl TableSlotCell {
232 pub fn mock_for_testing(id: usize, colspan: usize, rowspan: usize) -> Self {
233 Self {
234 base: LayoutBoxBase::new(
235 BaseFragmentInfo::new_for_testing(id),
236 ComputedValues::initial_values_with_font_override(Font::initial_values()).to_arc(),
237 ),
238 contents: BlockFormattingContext {
239 contents: BlockContainer::BlockLevelBoxes(Vec::new()),
240 contains_floats: false,
241 },
242 colspan,
243 rowspan,
244 }
245 }
246
247 pub fn node_id(&self) -> usize {
249 self.base.base_fragment_info.tag.map_or(0, |tag| tag.node.0)
250 }
251
252 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
253 self.base.repair_style(new_style);
254 }
255}
256
257#[derive(MallocSizeOf)]
262pub enum TableSlot {
263 Cell(ArcRefCell<TableSlotCell>),
265
266 Spanned(Vec<TableSlotOffset>),
274
275 Empty,
279}
280
281impl std::fmt::Debug for TableSlot {
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 match self {
284 Self::Cell(_) => f.debug_tuple("Cell").finish(),
285 Self::Spanned(spanned) => f.debug_tuple("Spanned").field(spanned).finish(),
286 Self::Empty => write!(f, "Empty"),
287 }
288 }
289}
290
291impl TableSlot {
292 fn new_spanned(offset: TableSlotOffset) -> Self {
293 Self::Spanned(vec![offset])
294 }
295}
296
297#[derive(Debug, MallocSizeOf)]
299pub struct TableTrack {
300 base: LayoutBoxBase,
302
303 group_index: Option<usize>,
306
307 is_anonymous: bool,
310
311 shared_background_style: SharedStyle,
315}
316
317impl TableTrack {
318 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
319 self.base.repair_style(new_style);
320 self.shared_background_style = SharedStyle::new(new_style.clone());
321 }
322}
323
324#[derive(Debug, MallocSizeOf, PartialEq)]
325pub enum TableTrackGroupType {
326 HeaderGroup,
327 FooterGroup,
328 RowGroup,
329 ColumnGroup,
330}
331
332#[derive(Debug, MallocSizeOf)]
333pub struct TableTrackGroup {
334 base: LayoutBoxBase,
336
337 group_type: TableTrackGroupType,
339
340 track_range: Range<usize>,
342
343 shared_background_style: SharedStyle,
347}
348
349impl TableTrackGroup {
350 pub(super) fn is_empty(&self) -> bool {
351 self.track_range.is_empty()
352 }
353
354 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
355 self.base.repair_style(new_style);
356 self.shared_background_style = SharedStyle::new(new_style.clone());
357 }
358}
359
360#[derive(Debug, MallocSizeOf)]
361pub struct TableCaption {
362 context: IndependentFormattingContext,
364}
365
366#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
368pub(crate) struct CollapsedBorder {
369 pub style_color: BorderStyleColor,
370 pub width: Au,
371}
372
373pub(crate) type CollapsedBorderLine = Vec<CollapsedBorder>;
375
376#[derive(Clone, Debug, MallocSizeOf)]
377pub(crate) struct SpecificTableGridInfo {
378 pub collapsed_borders: PhysicalVec<Vec<CollapsedBorderLine>>,
379 pub track_sizes: PhysicalVec<Vec<Au>>,
380}
381
382pub(crate) struct TableLayoutStyle<'a> {
383 table: &'a Table,
384 layout: Option<&'a TableLayout<'a>>,
385}
386
387#[derive(Debug, MallocSizeOf)]
391pub(crate) enum TableLevelBox {
392 Caption(ArcRefCell<TableCaption>),
393 Cell(ArcRefCell<TableSlotCell>),
394 TrackGroup(ArcRefCell<TableTrackGroup>),
395 Track(ArcRefCell<TableTrack>),
396}
397
398impl TableLevelBox {
399 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
400 match self {
401 TableLevelBox::Caption(caption) => callback(&caption.borrow().context.base),
402 TableLevelBox::Cell(cell) => callback(&cell.borrow().base),
403 TableLevelBox::TrackGroup(track_group) => callback(&track_group.borrow().base),
404 TableLevelBox::Track(track) => callback(&track.borrow().base),
405 }
406 }
407
408 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
409 match self {
410 TableLevelBox::Caption(caption) => callback(&mut caption.borrow_mut().context.base),
411 TableLevelBox::Cell(cell) => callback(&mut cell.borrow_mut().base),
412 TableLevelBox::TrackGroup(track_group) => callback(&mut track_group.borrow_mut().base),
413 TableLevelBox::Track(track) => callback(&mut track.borrow_mut().base),
414 }
415 }
416
417 pub(crate) fn repair_style(
418 &self,
419 context: &SharedStyleContext<'_>,
420 node: &ServoThreadSafeLayoutNode,
421 new_style: &Arc<ComputedValues>,
422 ) {
423 match self {
424 TableLevelBox::Caption(caption) => caption
425 .borrow_mut()
426 .context
427 .repair_style(context, node, new_style),
428 TableLevelBox::Cell(cell) => cell.borrow_mut().repair_style(new_style),
429 TableLevelBox::TrackGroup(track_group) => {
430 track_group.borrow_mut().repair_style(new_style);
431 },
432 TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style),
433 }
434 }
435
436 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
437 match self {
438 Self::Caption(caption) => caption.borrow().context.attached_to_tree(layout_box),
439 Self::Cell(cell) => cell.borrow().contents.attached_to_tree(layout_box),
440 Self::TrackGroup(_) | Self::Track(_) => {
441 },
444 }
445 }
446
447 pub(crate) fn downgrade(&self) -> WeakTableLevelBox {
448 match self {
449 Self::Caption(caption) => WeakTableLevelBox::Caption(caption.downgrade()),
450 Self::Cell(cell) => WeakTableLevelBox::Cell(cell.downgrade()),
451 Self::TrackGroup(track_group) => WeakTableLevelBox::TrackGroup(track_group.downgrade()),
452 Self::Track(track) => WeakTableLevelBox::Track(track.downgrade()),
453 }
454 }
455}
456
457#[derive(Clone, Debug, MallocSizeOf)]
458pub(crate) enum WeakTableLevelBox {
459 Caption(WeakRefCell<TableCaption>),
460 Cell(WeakRefCell<TableSlotCell>),
461 TrackGroup(WeakRefCell<TableTrackGroup>),
462 Track(WeakRefCell<TableTrack>),
463}
464
465impl WeakTableLevelBox {
466 pub(crate) fn upgrade(&self) -> Option<TableLevelBox> {
467 Some(match self {
468 Self::Caption(caption) => TableLevelBox::Caption(caption.upgrade()?),
469 Self::Cell(cell) => TableLevelBox::Cell(cell.upgrade()?),
470 Self::TrackGroup(track_group) => TableLevelBox::TrackGroup(track_group.upgrade()?),
471 Self::Track(track) => TableLevelBox::Track(track.upgrade()?),
472 })
473 }
474}