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::{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;
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 ) {
205 self.style = new_style.clone();
206 self.grid_style = context
207 .stylist
208 .style_for_anonymous::<ServoDangerousStyleElement>(
209 &context.guards,
210 &PseudoElement::ServoTableGrid,
211 new_style,
212 );
213 }
214
215 pub(crate) fn subtree_size(&self) -> usize {
216 self.slots
217 .iter()
218 .flat_map(|row| row.iter())
219 .map(TableSlot::subtree_size)
220 .sum()
221 }
222}
223
224type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
225pub type TableSlotOffset = Vector2D<usize, UnknownUnit>;
226
227#[derive(Debug, MallocSizeOf)]
228pub struct TableSlotCell {
229 pub(crate) context: IndependentFormattingContext,
232
233 colspan: usize,
235
236 rowspan: usize,
239}
240
241impl TableSlotCell {
242 pub fn mock_for_testing(id: usize, colspan: usize, rowspan: usize) -> Self {
243 let base = LayoutBoxBase::new(
244 BaseFragmentInfo::new_for_testing(id),
245 ComputedValues::initial_values_with_font_override(Font::initial_values()).to_arc(),
246 );
247 let contents = IndependentFormattingContextContents::Flow(BlockFormattingContext {
248 contents: BlockContainer::BlockLevelBoxes(Vec::new()),
249 contains_floats: false,
250 });
251 let propagated_data =
252 PropagatedBoxTreeData::default().disallowing_percentage_table_columns();
253 Self {
254 context: IndependentFormattingContext::new(base, contents, propagated_data),
255 colspan,
256 rowspan,
257 }
258 }
259
260 pub fn node_id(&self) -> usize {
262 self.context
263 .base
264 .base_fragment_info
265 .tag
266 .map_or(0, |tag| tag.node.0)
267 }
268}
269
270#[derive(MallocSizeOf)]
275pub enum TableSlot {
276 Cell(ArcRefCell<TableSlotCell>),
278
279 Spanned(Vec<TableSlotOffset>),
287
288 Empty,
292}
293
294impl std::fmt::Debug for TableSlot {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 match self {
297 Self::Cell(_) => f.debug_tuple("Cell").finish(),
298 Self::Spanned(spanned) => f.debug_tuple("Spanned").field(spanned).finish(),
299 Self::Empty => write!(f, "Empty"),
300 }
301 }
302}
303
304impl TableSlot {
305 fn new_spanned(offset: TableSlotOffset) -> Self {
306 Self::Spanned(vec![offset])
307 }
308
309 pub(crate) fn subtree_size(&self) -> usize {
310 match self {
311 TableSlot::Cell(cell) => cell.borrow().context.subtree_size(),
312 TableSlot::Spanned(..) | TableSlot::Empty => 0,
313 }
314 }
315}
316
317#[derive(Debug, MallocSizeOf)]
319pub struct TableTrack {
320 base: LayoutBoxBase,
322
323 group_index: Option<usize>,
326
327 is_anonymous: bool,
330
331 shared_background_style: SharedStyle,
335}
336
337impl TableTrack {
338 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
339 self.base.repair_style(new_style);
340 self.shared_background_style = SharedStyle::new(new_style.clone());
341 }
342}
343
344#[derive(Debug, MallocSizeOf, PartialEq)]
345pub enum TableTrackGroupType {
346 HeaderGroup,
347 FooterGroup,
348 RowGroup,
349 ColumnGroup,
350}
351
352#[derive(Debug, MallocSizeOf)]
353pub struct TableTrackGroup {
354 base: LayoutBoxBase,
356
357 group_type: TableTrackGroupType,
359
360 track_range: Range<usize>,
362
363 shared_background_style: SharedStyle,
367}
368
369impl TableTrackGroup {
370 pub(super) fn is_empty(&self) -> bool {
371 self.track_range.is_empty()
372 }
373
374 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
375 self.base.repair_style(new_style);
376 self.shared_background_style = SharedStyle::new(new_style.clone());
377 }
378}
379
380#[derive(Debug, MallocSizeOf)]
381pub struct TableCaption {
382 pub(crate) context: IndependentFormattingContext,
384}
385
386#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
388pub(crate) struct CollapsedBorder {
389 pub style_color: BorderStyleColor,
390 pub width: Au,
391}
392
393pub(crate) type CollapsedBorderLine = Vec<CollapsedBorder>;
395
396#[derive(Clone, Debug, MallocSizeOf)]
397pub(crate) struct SpecificTableGridInfo {
398 pub collapsed_borders: PhysicalVec<Vec<CollapsedBorderLine>>,
399 pub track_sizes: PhysicalVec<Vec<Au>>,
400}
401
402pub(crate) struct TableLayoutStyle<'a> {
403 table: &'a Table,
404 layout: Option<&'a TableLayout<'a>>,
405}
406
407#[derive(Debug, MallocSizeOf)]
411pub(crate) enum TableLevelBox {
412 Caption(ArcRefCell<TableCaption>),
413 Cell(ArcRefCell<TableSlotCell>),
414 TrackGroup(ArcRefCell<TableTrackGroup>),
415 Track(ArcRefCell<TableTrack>),
416}
417
418impl TableLevelBox {
419 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
420 match self {
421 TableLevelBox::Caption(caption) => callback(&caption.borrow().context.base),
422 TableLevelBox::Cell(cell) => callback(&cell.borrow().context.base),
423 TableLevelBox::TrackGroup(track_group) => callback(&track_group.borrow().base),
424 TableLevelBox::Track(track) => callback(&track.borrow().base),
425 }
426 }
427
428 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
429 match self {
430 TableLevelBox::Caption(caption) => callback(&mut caption.borrow_mut().context.base),
431 TableLevelBox::Cell(cell) => callback(&mut cell.borrow_mut().context.base),
432 TableLevelBox::TrackGroup(track_group) => callback(&mut track_group.borrow_mut().base),
433 TableLevelBox::Track(track) => callback(&mut track.borrow_mut().base),
434 }
435 }
436
437 pub(crate) fn repair_style(
438 &self,
439 context: &SharedStyleContext<'_>,
440 node: &ServoLayoutNode,
441 new_style: &Arc<ComputedValues>,
442 ) {
443 match self {
444 TableLevelBox::Caption(caption) => caption
445 .borrow_mut()
446 .context
447 .repair_style(context, node, new_style),
448 TableLevelBox::Cell(cell) => cell
449 .borrow_mut()
450 .context
451 .repair_style(context, node, new_style),
452 TableLevelBox::TrackGroup(track_group) => {
453 track_group.borrow_mut().repair_style(new_style);
454 },
455 TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style),
456 }
457 }
458
459 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
460 match self {
461 Self::Caption(caption) => caption.borrow().context.attached_to_tree(layout_box),
462 Self::Cell(cell) => cell.borrow().context.attached_to_tree(layout_box),
463 Self::TrackGroup(_) | Self::Track(_) => {
464 },
467 }
468 }
469
470 pub(crate) fn downgrade(&self) -> WeakTableLevelBox {
471 match self {
472 Self::Caption(caption) => WeakTableLevelBox::Caption(caption.downgrade()),
473 Self::Cell(cell) => WeakTableLevelBox::Cell(cell.downgrade()),
474 Self::TrackGroup(track_group) => WeakTableLevelBox::TrackGroup(track_group.downgrade()),
475 Self::Track(track) => WeakTableLevelBox::Track(track.downgrade()),
476 }
477 }
478}
479
480#[derive(Clone, Debug, MallocSizeOf)]
481pub(crate) enum WeakTableLevelBox {
482 Caption(WeakRefCell<TableCaption>),
483 Cell(WeakRefCell<TableSlotCell>),
484 TrackGroup(WeakRefCell<TableTrackGroup>),
485 Track(WeakRefCell<TableTrack>),
486}
487
488impl WeakTableLevelBox {
489 pub(crate) fn upgrade(&self) -> Option<TableLevelBox> {
490 Some(match self {
491 Self::Caption(caption) => TableLevelBox::Caption(caption.upgrade()?),
492 Self::Cell(cell) => TableLevelBox::Cell(cell.upgrade()?),
493 Self::TrackGroup(track_group) => TableLevelBox::TrackGroup(track_group.upgrade()?),
494 Self::Track(track) => TableLevelBox::Track(track.upgrade()?),
495 })
496 }
497}