layout: Add support for table captions (#32657)

This adds initial support for table captions. To do this, the idea of
the table wrapper becomes a bit more concrete. Even so, the wrapper is
still reponsible for allocating space for the grid's border and padding,
as those properties are specified on the wrapper and not grid in CSS.

In order to account for this weirdness of HTML/CSS captions and grid are
now laid out and placed with a negative offset in the table wrapper
content rect.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2024-07-03 20:24:19 +02:00 committed by GitHub
parent f8e4ae6040
commit 959ffad99a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
76 changed files with 551 additions and 322 deletions

View file

@ -15,8 +15,8 @@ use style::str::char_is_whitespace;
use style::values::specified::TextDecorationLine;
use super::{
Table, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset, TableTrack,
TableTrackGroup, TableTrackGroupType,
Table, TableCaption, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset,
TableTrack, TableTrackGroup, TableTrackGroupType,
};
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, NodeExt};
@ -73,12 +73,14 @@ impl Table {
pub(crate) fn construct<'dom>(
context: &LayoutContext,
info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
grid_style: Arc<ComputedValues>,
contents: NonReplacedContents,
propagated_text_decoration_line: TextDecorationLine,
) -> Self {
let text_decoration_line =
propagated_text_decoration_line | info.style.clone_text_decoration_line();
let mut traversal = TableBuilderTraversal::new(context, info, text_decoration_line);
let mut traversal =
TableBuilderTraversal::new(context, info, grid_style, text_decoration_line);
contents.traverse(context, info, &mut traversal);
traversal.finish()
}
@ -92,7 +94,7 @@ impl Table {
where
Node: crate::dom::NodeExt<'dom>,
{
let anonymous_style = context
let grid_and_wrapper_style = context
.shared_context()
.stylist
.style_for_anonymous::<Node::ConcreteElement>(
@ -100,10 +102,14 @@ impl Table {
&PseudoElement::ServoAnonymousTable,
&parent_info.style,
);
let anonymous_info = parent_info.new_anonymous(anonymous_style.clone());
let anonymous_info = parent_info.new_anonymous(grid_and_wrapper_style.clone());
let mut table_builder =
TableBuilderTraversal::new(context, &anonymous_info, propagated_text_decoration_line);
let mut table_builder = TableBuilderTraversal::new(
context,
&anonymous_info,
grid_and_wrapper_style.clone(),
propagated_text_decoration_line,
);
for content in contents {
match content {
@ -128,7 +134,7 @@ impl Table {
IndependentFormattingContext::NonReplaced(NonReplacedFormattingContext {
base_fragment_info: (&anonymous_info).into(),
style: anonymous_style,
style: grid_and_wrapper_style,
content_sizes: None,
contents: NonReplacedFormattingContextContents::Table(table),
})
@ -229,15 +235,23 @@ pub struct TableBuilder {
}
impl TableBuilder {
pub(super) fn new(style: Arc<ComputedValues>) -> Self {
pub(super) fn new(
style: Arc<ComputedValues>,
grid_style: Arc<ComputedValues>,
base_fragment_info: BaseFragmentInfo,
) -> Self {
Self {
table: Table::new(style),
table: Table::new(style, grid_style, base_fragment_info),
incoming_rowspans: Vec::new(),
}
}
pub fn new_for_tests() -> Self {
Self::new(ComputedValues::initial_values().to_arc())
Self::new(
ComputedValues::initial_values().to_arc(),
ComputedValues::initial_values().to_arc(),
BaseFragmentInfo::anonymous(),
)
}
pub fn last_row_index_in_row_group_at_row_n(&self, n: usize) -> usize {
@ -622,13 +636,14 @@ where
pub(crate) fn new(
context: &'style LayoutContext<'style>,
info: &'style NodeAndStyleInfo<Node>,
grid_style: Arc<ComputedValues>,
text_decoration_line: TextDecorationLine,
) -> Self {
TableBuilderTraversal {
context,
info,
current_text_decoration_line: text_decoration_line,
builder: TableBuilder::new(info.style.clone()),
builder: TableBuilder::new(info.style.clone(), grid_style, info.into()),
current_anonymous_row_content: Vec::new(),
current_row_group_index: None,
}
@ -825,9 +840,28 @@ where
::std::mem::forget(box_slot);
},
DisplayLayoutInternal::TableCaption => {
// TODO: Handle table captions.
let contents = match contents.try_into() {
Ok(non_replaced_contents) => {
BlockFormattingContext::construct(
self.context,
info,
non_replaced_contents,
self.current_text_decoration_line,
false, /* is_list_item */
)
},
Err(_replaced) => {
unreachable!("Replaced should not have a LayoutInternal display type.");
},
};
self.builder.table.captions.push(TableCaption {
contents,
style: info.style.clone(),
base_fragment_info: info.into(),
});
// We are doing this until we have actually set a Box for this `BoxSlot`.
::std::mem::forget(box_slot);
::std::mem::forget(box_slot)
},
DisplayLayoutInternal::TableCell => {
self.current_anonymous_row_content