Skip to content

Commit

Permalink
Add initial support for table box tree construction
Browse files Browse the repository at this point in the history
This is the first part of constructing the box tree for table layout. No
layout is actually done and the construction of tables is now hidden
behind a flag (in order to not regress WPT).  Notably, this does not
handle anonymous table part construction, when the DOM does not reflect
a fully-formed table. That's part two.

Progress toward servo#27459.

Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Manish Goregaokar <manishsmail@gmail.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
  • Loading branch information
3 people committed Dec 5, 2023
1 parent 63701b3 commit b62780a
Show file tree
Hide file tree
Showing 12 changed files with 923 additions and 20 deletions.
3 changes: 3 additions & 0 deletions components/config/prefs.rs
Expand Up @@ -457,6 +457,9 @@ mod gen {
enabled: bool,
},
legacy_layout: bool,
tables: {
enabled: bool,
},
#[serde(default = "default_layout_threads")]
threads: i64,
writing_mode: {
Expand Down
1 change: 1 addition & 0 deletions components/layout_2020/flexbox/construct.rs
Expand Up @@ -165,6 +165,7 @@ where
} => {
let display_inside = match display {
DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
DisplayGeneratingBox::LayoutInternal(_) => display.display_inside(),
};
let box_ = if info.style.get_box().position.is_absolutely_positioned() {
// https://drafts.csswg.org/css-flexbox/#abspos-items
Expand Down
3 changes: 3 additions & 0 deletions components/layout_2020/flow/construct.rs
Expand Up @@ -273,6 +273,9 @@ where
}
},
},
DisplayGeneratingBox::LayoutInternal(_) => {
unreachable!("The result of blockification should never be layout-internal value.");
},
}
}

Expand Down
5 changes: 5 additions & 0 deletions components/layout_2020/flow/inline.rs
Expand Up @@ -2248,6 +2248,11 @@ impl AbsolutelyPositionedLineItem {
block: Length::zero(),
}
},
Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(_)) => {
unreachable!(
"The result of blockification should never be a layout-internal value."
);
},
Display::Contents => {
panic!("display:contents does not generate an abspos box")
},
Expand Down
2 changes: 1 addition & 1 deletion components/layout_2020/flow/root.rs
Expand Up @@ -225,7 +225,7 @@ fn construct_for_root_element<'dom>(
unreachable!()
},
// The root element is blockified, ignore DisplayOutside
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
Display::GeneratingBox(display_generating_box) => display_generating_box.display_inside(),
};

let contents =
Expand Down
20 changes: 17 additions & 3 deletions components/layout_2020/formatting_contexts.rs
Expand Up @@ -21,6 +21,7 @@ use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes};
use crate::style_ext::DisplayInside;
use crate::table::Table;
use crate::ContainingBlock;

/// https://drafts.csswg.org/css-display/#independent-formatting-context
Expand Down Expand Up @@ -54,6 +55,7 @@ pub(crate) struct ReplacedFormattingContext {
pub(crate) enum NonReplacedFormattingContextContents {
Flow(BlockFormattingContext),
Flex(FlexContainer),
Table(Table),
// Other layout modes go here
}

Expand All @@ -73,15 +75,15 @@ impl IndependentFormattingContext {
propagated_text_decoration_line: TextDecorationLine,
) -> Self {
match contents.try_into() {
Ok(non_replaced) => {
Ok(non_replaced_contents) => {
let contents = match display_inside {
DisplayInside::Flow { is_list_item } |
DisplayInside::FlowRoot { is_list_item } => {
NonReplacedFormattingContextContents::Flow(
BlockFormattingContext::construct(
context,
node_and_style_info,
non_replaced,
non_replaced_contents,
propagated_text_decoration_line,
is_list_item,
),
Expand All @@ -91,7 +93,15 @@ impl IndependentFormattingContext {
NonReplacedFormattingContextContents::Flex(FlexContainer::construct(
context,
node_and_style_info,
non_replaced,
non_replaced_contents,
propagated_text_decoration_line,
))
},
DisplayInside::Table => {
NonReplacedFormattingContextContents::Table(Table::construct(
context,
node_and_style_info,
non_replaced_contents,
propagated_text_decoration_line,
))
},
Expand Down Expand Up @@ -190,6 +200,9 @@ impl NonReplacedFormattingContext {
NonReplacedFormattingContextContents::Flex(fc) => {
fc.layout(layout_context, positioning_context, containing_block)
},
NonReplacedFormattingContextContents::Table(table) => {
table.layout(layout_context, positioning_context, containing_block)
},
}
}

Expand All @@ -213,6 +226,7 @@ impl NonReplacedFormattingContextContents {
.contents
.inline_content_sizes(layout_context, writing_mode),
Self::Flex(inner) => inner.inline_content_sizes(),
Self::Table(table) => table.inline_content_sizes(),
}
}
}
1 change: 1 addition & 0 deletions components/layout_2020/lib.rs
Expand Up @@ -23,6 +23,7 @@ pub mod query;
mod replaced;
mod sizing;
mod style_ext;
pub mod table;
pub mod traversal;

pub use flow::BoxTree;
Expand Down
100 changes: 84 additions & 16 deletions components/layout_2020/style_ext.rs
Expand Up @@ -2,6 +2,7 @@
* 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/. */

use servo_config::pref;
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
use style::computed_values::position::T as ComputedPosition;
use style::computed_values::transform_style::T as ComputedTransformStyle;
Expand Down Expand Up @@ -36,8 +37,19 @@ pub(crate) enum DisplayGeneratingBox {
outside: DisplayOutside,
inside: DisplayInside,
},
// Layout-internal display types go here:
// https://drafts.csswg.org/css-display-3/#layout-specific-display
LayoutInternal(DisplayLayoutInternal),
}

impl DisplayGeneratingBox {
pub(crate) fn display_inside(&self) -> DisplayInside {
match *self {
DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
DisplayGeneratingBox::LayoutInternal(layout_internal) => {
layout_internal.display_inside()
},
}
}
}

#[derive(Clone, Copy, Eq, PartialEq)]
Expand All @@ -53,6 +65,32 @@ pub(crate) enum DisplayInside {
Flow { is_list_item: bool },
FlowRoot { is_list_item: bool },
Flex,
Table,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
/// https://drafts.csswg.org/css-display-3/#layout-specific-display
pub(crate) enum DisplayLayoutInternal {
TableCaption,
TableCell,
TableColumn,
TableColumnGroup,
TableFooterGroup,
TableHeaderGroup,
TableRow,
TableRowGroup,
}

impl DisplayLayoutInternal {
/// https://drafts.csswg.org/css-display-3/#layout-specific-displa
pub(crate) fn display_inside(&self) -> DisplayInside {
// When we add ruby, the display_inside of ruby must be Flow.
// TODO: this should be unreachable for everything but
// table cell and caption, once we have box tree fixups.
DisplayInside::FlowRoot {
is_list_item: false,
}
}
}

/// Percentages resolved but not `auto` margins
Expand Down Expand Up @@ -492,6 +530,48 @@ impl ComputedValuesExt for ComputedValues {

impl From<stylo::Display> for Display {
fn from(packed: stylo::Display) -> Self {
let outside = packed.outside();
let inside = packed.inside();

let outside = match outside {
stylo::DisplayOutside::Block => DisplayOutside::Block,
stylo::DisplayOutside::Inline => DisplayOutside::Inline,
stylo::DisplayOutside::TableCaption if pref!(layout.tables.enabled) => {
return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
DisplayLayoutInternal::TableCaption,
));
},
stylo::DisplayOutside::TableCaption => DisplayOutside::Block,
stylo::DisplayOutside::InternalTable if pref!(layout.tables.enabled) => {
let internal = match inside {
stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
stylo::DisplayInside::TableColumnGroup => {
DisplayLayoutInternal::TableColumnGroup
},
stylo::DisplayInside::TableHeaderGroup => {
DisplayLayoutInternal::TableHeaderGroup
},
stylo::DisplayInside::TableFooterGroup => {
DisplayLayoutInternal::TableFooterGroup
},
stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
_ => unreachable!("Non-internal DisplayInside found"),
};
return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
},
stylo::DisplayOutside::InternalTable => {
DisplayOutside::Block
},
// This should not be a value of DisplayInside, but oh well
// special-case display: contents because we still want it to work despite the early return
stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
return Display::Contents
},
stylo::DisplayOutside::None => return Display::None,
};

let inside = match packed.inside() {
stylo::DisplayInside::Flow => DisplayInside::Flow {
is_list_item: packed.is_list_item(),
Expand All @@ -505,7 +585,7 @@ impl From<stylo::Display> for Display {
stylo::DisplayInside::None => return Display::None,
stylo::DisplayInside::Contents => return Display::Contents,

// TODO: Implement support for tables.
stylo::DisplayInside::Table if pref!(layout.tables.enabled) => DisplayInside::Table,
stylo::DisplayInside::Table |
stylo::DisplayInside::TableRowGroup |
stylo::DisplayInside::TableColumn |
Expand All @@ -514,20 +594,8 @@ impl From<stylo::Display> for Display {
stylo::DisplayInside::TableFooterGroup |
stylo::DisplayInside::TableRow |
stylo::DisplayInside::TableCell => DisplayInside::Flow {
is_list_item: packed.is_list_item(),
},
};
let outside = match packed.outside() {
stylo::DisplayOutside::Block => DisplayOutside::Block,
stylo::DisplayOutside::Inline => DisplayOutside::Inline,

// This should not be a value of DisplayInside, but oh well
stylo::DisplayOutside::None => return Display::None,

// TODO: Implement support for tables.
stylo::DisplayOutside::TableCaption | stylo::DisplayOutside::InternalTable => {
DisplayOutside::Block
},
is_list_item: packed.is_list_item(),
}
};
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
}
Expand Down

0 comments on commit b62780a

Please sign in to comment.