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::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.stylist.style_for_anonymous::<ServoLayoutElement>(
207 &context.guards,
208 &PseudoElement::ServoTableGrid,
209 new_style,
210 );
211 }
212}
213
214type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
215pub type TableSlotOffset = Vector2D<usize, UnknownUnit>;
216
217#[derive(Debug, MallocSizeOf)]
218pub struct TableSlotCell {
219 pub(crate) context: IndependentFormattingContext,
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 let base = LayoutBoxBase::new(
234 BaseFragmentInfo::new_for_testing(id),
235 ComputedValues::initial_values_with_font_override(Font::initial_values()).to_arc(),
236 );
237 let contents = IndependentFormattingContextContents::Flow(BlockFormattingContext {
238 contents: BlockContainer::BlockLevelBoxes(Vec::new()),
239 contains_floats: false,
240 });
241 let propagated_data =
242 PropagatedBoxTreeData::default().disallowing_percentage_table_columns();
243 Self {
244 context: IndependentFormattingContext::new(base, contents, propagated_data),
245 colspan,
246 rowspan,
247 }
248 }
249
250 pub fn node_id(&self) -> usize {
252 self.context
253 .base
254 .base_fragment_info
255 .tag
256 .map_or(0, |tag| tag.node.0)
257 }
258}
259
260#[derive(MallocSizeOf)]
265pub enum TableSlot {
266 Cell(ArcRefCell<TableSlotCell>),
268
269 Spanned(Vec<TableSlotOffset>),
277
278 Empty,
282}
283
284impl std::fmt::Debug for TableSlot {
285 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286 match self {
287 Self::Cell(_) => f.debug_tuple("Cell").finish(),
288 Self::Spanned(spanned) => f.debug_tuple("Spanned").field(spanned).finish(),
289 Self::Empty => write!(f, "Empty"),
290 }
291 }
292}
293
294impl TableSlot {
295 fn new_spanned(offset: TableSlotOffset) -> Self {
296 Self::Spanned(vec![offset])
297 }
298}
299
300#[derive(Debug, MallocSizeOf)]
302pub struct TableTrack {
303 base: LayoutBoxBase,
305
306 group_index: Option<usize>,
309
310 is_anonymous: bool,
313
314 shared_background_style: SharedStyle,
318}
319
320impl TableTrack {
321 fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
322 self.base.repair_style(new_style);
323 self.shared_background_style = SharedStyle::new(new_style.clone());
324 }
325}
326
327#[derive(Debug, MallocSizeOf, PartialEq)]
328pub enum TableTrackGroupType {
329 HeaderGroup,
330 FooterGroup,
331 RowGroup,
332 ColumnGroup,
333}
334
335#[derive(Debug, MallocSizeOf)]
336pub struct TableTrackGroup {
337 base: LayoutBoxBase,
339
340 group_type: TableTrackGroupType,
342
343 track_range: Range<usize>,
345
346 shared_background_style: SharedStyle,
350}
351
352impl TableTrackGroup {
353 pub(super) fn is_empty(&self) -> bool {
354 self.track_range.is_empty()
355 }
356
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)]
364pub struct TableCaption {
365 pub(crate) context: IndependentFormattingContext,
367}
368
369#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
371pub(crate) struct CollapsedBorder {
372 pub style_color: BorderStyleColor,
373 pub width: Au,
374}
375
376pub(crate) type CollapsedBorderLine = Vec<CollapsedBorder>;
378
379#[derive(Clone, Debug, MallocSizeOf)]
380pub(crate) struct SpecificTableGridInfo {
381 pub collapsed_borders: PhysicalVec<Vec<CollapsedBorderLine>>,
382 pub track_sizes: PhysicalVec<Vec<Au>>,
383}
384
385pub(crate) struct TableLayoutStyle<'a> {
386 table: &'a Table,
387 layout: Option<&'a TableLayout<'a>>,
388}
389
390#[derive(Debug, MallocSizeOf)]
394pub(crate) enum TableLevelBox {
395 Caption(ArcRefCell<TableCaption>),
396 Cell(ArcRefCell<TableSlotCell>),
397 TrackGroup(ArcRefCell<TableTrackGroup>),
398 Track(ArcRefCell<TableTrack>),
399}
400
401impl TableLevelBox {
402 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
403 match self {
404 TableLevelBox::Caption(caption) => callback(&caption.borrow().context.base),
405 TableLevelBox::Cell(cell) => callback(&cell.borrow().context.base),
406 TableLevelBox::TrackGroup(track_group) => callback(&track_group.borrow().base),
407 TableLevelBox::Track(track) => callback(&track.borrow().base),
408 }
409 }
410
411 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
412 match self {
413 TableLevelBox::Caption(caption) => callback(&mut caption.borrow_mut().context.base),
414 TableLevelBox::Cell(cell) => callback(&mut cell.borrow_mut().context.base),
415 TableLevelBox::TrackGroup(track_group) => callback(&mut track_group.borrow_mut().base),
416 TableLevelBox::Track(track) => callback(&mut track.borrow_mut().base),
417 }
418 }
419
420 pub(crate) fn repair_style(
421 &self,
422 context: &SharedStyleContext<'_>,
423 node: &ServoThreadSafeLayoutNode,
424 new_style: &Arc<ComputedValues>,
425 ) {
426 match self {
427 TableLevelBox::Caption(caption) => caption
428 .borrow_mut()
429 .context
430 .repair_style(context, node, new_style),
431 TableLevelBox::Cell(cell) => cell
432 .borrow_mut()
433 .context
434 .repair_style(context, node, new_style),
435 TableLevelBox::TrackGroup(track_group) => {
436 track_group.borrow_mut().repair_style(new_style);
437 },
438 TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style),
439 }
440 }
441
442 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
443 match self {
444 Self::Caption(caption) => caption.borrow().context.attached_to_tree(layout_box),
445 Self::Cell(cell) => cell.borrow().context.attached_to_tree(layout_box),
446 Self::TrackGroup(_) | Self::Track(_) => {
447 },
450 }
451 }
452
453 pub(crate) fn downgrade(&self) -> WeakTableLevelBox {
454 match self {
455 Self::Caption(caption) => WeakTableLevelBox::Caption(caption.downgrade()),
456 Self::Cell(cell) => WeakTableLevelBox::Cell(cell.downgrade()),
457 Self::TrackGroup(track_group) => WeakTableLevelBox::TrackGroup(track_group.downgrade()),
458 Self::Track(track) => WeakTableLevelBox::Track(track.downgrade()),
459 }
460 }
461}
462
463#[derive(Clone, Debug, MallocSizeOf)]
464pub(crate) enum WeakTableLevelBox {
465 Caption(WeakRefCell<TableCaption>),
466 Cell(WeakRefCell<TableSlotCell>),
467 TrackGroup(WeakRefCell<TableTrackGroup>),
468 Track(WeakRefCell<TableTrack>),
469}
470
471impl WeakTableLevelBox {
472 pub(crate) fn upgrade(&self) -> Option<TableLevelBox> {
473 Some(match self {
474 Self::Caption(caption) => TableLevelBox::Caption(caption.upgrade()?),
475 Self::Cell(cell) => TableLevelBox::Cell(cell.upgrade()?),
476 Self::TrackGroup(track_group) => TableLevelBox::TrackGroup(track_group.upgrade()?),
477 Self::Track(track) => TableLevelBox::Track(track.upgrade()?),
478 })
479 }
480}