servo/components/layout_2020/table/mod.rs
Martin Robinson 5c1723c983
rustdoc: Fix many rustdoc errors (#31147)
This fixes many rustdoc errors that occur due to raw URLs in rustdoc
comments as well as unescaped Rust code that should be in backticks.
2024-01-22 13:13:48 +00:00

165 lines
5.4 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! HTML Tables (╯°□°)╯︵ ┻━┻.
//!
//! See <https://html.spec.whatwg.org/multipage/table-processing-model> and
//! <https://drafts.csswg.org/css-tables>. This is heavily based on the latter specification, but
//! note that it is still an Editor's Draft, so there is no guarantee that what is implemented here
//! matches other browsers or the current specification.
mod construct;
mod layout;
pub(crate) use construct::AnonymousTableContent;
pub use construct::TableBuilder;
use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
use serde::Serialize;
use servo_arc::Arc;
use style::properties::ComputedValues;
use style_traits::dom::OpaqueNode;
use super::flow::BlockFormattingContext;
use crate::flow::BlockContainer;
use crate::fragment_tree::BaseFragmentInfo;
pub type TableSize = Size2D<usize, UnknownUnit>;
#[derive(Debug, Serialize)]
pub struct Table {
/// The style of this table.
#[serde(skip_serializing)]
style: Arc<ComputedValues>,
/// The content of the slots of this table.
pub slots: Vec<Vec<TableSlot>>,
/// The size of this table.
pub size: TableSize,
}
impl Table {
pub(crate) fn new(style: Arc<ComputedValues>) -> Self {
Self {
style,
slots: Vec::new(),
size: TableSize::zero(),
}
}
/// Return the slot at the given coordinates, if it exists in the table, otherwise
/// return None.
fn get_slot<'a>(&'a self, coords: TableSlotCoordinates) -> Option<&'a 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(ref offsets)) => Some(coords - offsets[0]),
_ => return None,
}
}
fn resolve_first_cell(&self, coords: TableSlotCoordinates) -> Option<&TableSlotCell> {
let resolved_coords = match self.resolve_first_cell_coords(coords) {
Some(coords) => coords,
None => return None,
};
let slot = self.get_slot(resolved_coords);
match slot {
Some(&TableSlot::Cell(ref cell)) => Some(cell),
_ => unreachable!(
"Spanned slot should not point to an empty cell or another spanned slot."
),
}
}
}
type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
pub type TableSlotOffset = Vector2D<usize, UnknownUnit>;
#[derive(Debug, Serialize)]
pub struct TableSlotCell {
/// The contents of this cell, with its own layout.
contents: BlockFormattingContext,
/// Number of columns that the cell is to span. Must be greater than zero.
colspan: usize,
/// Number of rows that the cell is to span. Zero means that the cell is to span all
/// the remaining rows in the row group.
rowspan: usize,
/// The style of this table cell.
#[serde(skip_serializing)]
style: Arc<ComputedValues>,
/// The [`BaseFragmentInfo`] of this cell.
base_fragment_info: BaseFragmentInfo,
}
impl TableSlotCell {
pub fn mock_for_testing(id: usize, colspan: usize, rowspan: usize) -> Self {
Self {
contents: BlockFormattingContext {
contents: BlockContainer::BlockLevelBoxes(Vec::new()),
contains_floats: false,
},
colspan,
rowspan,
style: ComputedValues::initial_values().to_arc(),
base_fragment_info: BaseFragmentInfo::new_for_node(OpaqueNode(id)),
}
}
/// Get the node id of this cell's [`BaseFragmentInfo`]. This is used for unit tests.
pub fn node_id(&self) -> usize {
self.base_fragment_info.tag.node.0
}
}
#[derive(Serialize)]
/// A single table slot. It may be an actual cell, or a reference
/// to a previous cell that is spanned here
///
/// In case of table model errors, it may be multiple references
pub enum TableSlot {
/// A table cell, with a colspan and a rowspan.
Cell(TableSlotCell),
/// This slot is spanned by one or more multiple cells earlier in the table, which are
/// found at the given negative coordinate offsets. The vector is in the order of most
/// recent to earliest cell.
///
/// If there is more than one cell that spans a slot, this is a table model error, but
/// we still keep track of it. See
/// <https://html.spec.whatwg.org/multipage/#table-model-error>
Spanned(Vec<TableSlotOffset>),
/// An empty spot in the table. This can happen when there is a gap in columns between
/// cells that are defined and one which should exist because of cell with a rowspan
/// from a previous row.
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])
}
}