servo/components/layout_2020/table/mod.rs
Martin Robinson aa073c3dca
layout: Implement support for line-height and vertical-align (#30902)
* layout: Implement support for `line-height` and `vertical-align`

This is an initial implementation of proper `line-height` and
`vertical-align` support. While this change includes the bulk of the
work there are still many missing pieces for full support. In particular
some big missing things are:

 - Flex containers do not properly compute their baselines. The idea is
   to tackle this in a followup change. This causes various flex tests
   to start failing because everything used to be top aligned.
 - The implementation of the line-height quirks (only active in quirks
   mode) are incomplete. While the quirk works in many cases, there are
   still some cases where it is handled incorrectly. This requires more
   redesign and refinement, better suited for a followup.
 - Most of the features are CSS 3 such as precision control of the
   baseline and first and last baselines are not implemented. This
   change gets us close to CSS 2.x support.

While there are many new test passes with this change some tests are
starting to fail. An accounting of new failures:

Tests failing also in Layout 2013:
 - /css/css2/positioning/toogle-abspos-on-relpos-inline-child.html (only passes in Chrome)
 - /css/CSS2/fonts/font-applies-to-001.xht (potentially an issue with font size)

Invalid tests:
 - /css/CSS2/visudet/inline-block-baseline-003.xht
 - /css/CSS2/visudet/inline-block-baseline-004.xht
 - These are are failing in all browsers. See https://bugs.chromium.org/p/chromium/issues/detail?id=1222151.

Missing table support:
 - /_mozilla/mozilla/table_valign_middle.html

Missing `font-size-adjust` support :
 - /css/css-fonts/font-size-adjust-zero-2.html (also failing in 2013)

Incomplete form field support :
- /html/rendering/widgets/the-select-element/option-add-label-quirks.html (label isn't rendered so button isn't the right size in quirks mode due to line height quirk)

Need support for calculating flexbox baseline:
 - /css/css-flexbox/fieldset-baseline-alignment.html
 - /css/css-flexbox/flex-inline.html
 - /css/css-flexbox/flexbox-baseline-multi-line-horiz-001.html
 - /css/css-flexbox/flexbox-baseline-single-item-001a.html
 - /css/css-flexbox/flexbox-baseline-single-item-001b.html

Failing because we don't create anonymous inline boxes for text children of blocks:
- /css/CSS2/linebox/anonymous-inline-inherit-001.html

Passes locally (potentially related to fonts):
 - /css/CSS2/css1/c414-flt-fit-004.xht
 - /css/css-transforms/transform-input-017.html
 - /html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-min-intrinsic-size.html
 - /css/css-fonts/first-available-font-005.html
 - /css/css-fonts/first-available-font-006.html

* Some cleanups after live review with @mukilan

Also update results.
2024-01-08 14:49:50 +00:00

119 lines
3.7 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/. */
//! Table layout.
//! See https://html.spec.whatwg.org/multipage/table-processing-model.
mod construct;
pub(crate) use construct::AnonymousTableContent;
pub use construct::TableBuilder;
use euclid::{Point2D, UnknownUnit, Vector2D};
use serde::Serialize;
use style::values::computed::Length;
use super::flow::BlockFormattingContext;
use crate::context::LayoutContext;
use crate::flow::BlockContainer;
use crate::formatting_contexts::IndependentLayout;
use crate::positioned::PositioningContext;
use crate::sizing::ContentSizes;
use crate::ContainingBlock;
#[derive(Debug, Default, Serialize)]
pub struct Table {
pub slots: Vec<Vec<TableSlot>>,
}
impl Table {
pub(crate) fn inline_content_sizes(&self) -> ContentSizes {
ContentSizes::zero()
}
pub(crate) fn layout(
&self,
_layout_context: &LayoutContext,
_positioning_context: &mut PositioningContext,
_containing_block: &ContainingBlock,
) -> IndependentLayout {
IndependentLayout {
fragments: Vec::new(),
content_block_size: Length::new(0.),
last_inflow_baseline_offset: None,
}
}
}
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,
// An id used for testing purposes.
pub id: u8,
}
impl TableSlotCell {
pub fn mock_for_testing(id: u8, colspan: usize, rowspan: usize) -> Self {
Self {
contents: BlockFormattingContext {
contents: BlockContainer::BlockLevelBoxes(Vec::new()),
contains_floats: false,
},
colspan,
rowspan,
id,
}
}
}
#[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])
}
}