LibWeb: Implement CSS 'contain' property

This commit is contained in:
Psychpsyo 2025-01-18 20:39:26 +01:00 committed by Sam Atkins
commit 67ed676831
Notes: github-actions[bot] 2025-01-28 11:25:39 +00:00
154 changed files with 4200 additions and 117 deletions

View file

@ -1360,6 +1360,74 @@ Optional<CSS::Isolation> ComputedProperties::isolation() const
return keyword_to_isolation(value.to_keyword());
}
CSS::Containment ComputedProperties::contain() const
{
CSS::Containment containment = {};
auto const& value = property(CSS::PropertyID::Contain);
switch (value.to_keyword()) {
case Keyword::None:
// This value indicates that the property has no effect. The element renders as normal, with no containment effects applied.
return {};
case Keyword::Strict:
// This value computes to 'size layout paint style', and thus turns on all forms of containment for the element.
containment.size_containment = true;
containment.layout_containment = true;
containment.paint_containment = true;
containment.style_containment = true;
break;
case Keyword::Content:
// This value computes to 'layout paint style', and thus turns on all forms of containment except size containment for the element.
containment.layout_containment = true;
containment.paint_containment = true;
containment.style_containment = true;
break;
case Keyword::Size:
containment.size_containment = true;
break;
case Keyword::InlineSize:
containment.inline_size_containment = true;
break;
case Keyword::Layout:
containment.layout_containment = true;
break;
case Keyword::Style:
containment.style_containment = true;
break;
case Keyword::Paint:
containment.paint_containment = true;
break;
default:
if (value.is_value_list()) {
auto& values = value.as_value_list().values();
for (auto const& item : values) {
switch (item->to_keyword()) {
case Keyword::Size:
containment.size_containment = true;
break;
case Keyword::InlineSize:
containment.inline_size_containment = true;
break;
case Keyword::Layout:
containment.layout_containment = true;
break;
case Keyword::Style:
containment.style_containment = true;
break;
case Keyword::Paint:
containment.paint_containment = true;
break;
default:
dbgln("`{}` is not supported in `contain` (yet?)", item->to_string(CSSStyleValue::SerializationMode::Normal));
break;
}
}
}
}
return containment;
}
Optional<CSS::MaskType> ComputedProperties::mask_type() const
{
auto const& value = property(CSS::PropertyID::MaskType);

View file

@ -161,6 +161,7 @@ public:
Optional<CSS::WritingMode> writing_mode() const;
Optional<CSS::UserSelect> user_select() const;
Optional<CSS::Isolation> isolation() const;
CSS::Containment contain() const;
static Vector<CSS::Transformation> transformations_for_style_value(CSSStyleValue const& value);
Vector<CSS::Transformation> transformations() const;

View file

@ -66,6 +66,18 @@ struct ObjectPosition {
CSS::LengthPercentage offset_y { Percentage(50) };
};
// https://drafts.csswg.org/css-contain-2/#containment-types
struct Containment {
// FIXME: It'd be nice if this was a single-byte bitfield instead of some bools.
bool size_containment = false;
bool inline_size_containment = false;
bool layout_containment = false;
bool style_containment = false;
bool paint_containment = false;
bool is_empty() const { return !(size_containment || inline_size_containment || layout_containment || style_containment || paint_containment); }
};
class InitialValues {
public:
static AspectRatio aspect_ratio() { return AspectRatio { true, {} }; }
@ -172,6 +184,7 @@ public:
static CSS::WritingMode writing_mode() { return CSS::WritingMode::HorizontalTb; }
static CSS::UserSelect user_select() { return CSS::UserSelect::Auto; }
static CSS::Isolation isolation() { return CSS::Isolation::Auto; }
static CSS::Containment contain() { return {}; }
// https://www.w3.org/TR/SVG/geometry.html
static LengthPercentage cx() { return CSS::Length::make_px(0); }
@ -428,6 +441,7 @@ public:
CSS::WritingMode writing_mode() const { return m_inherited.writing_mode; }
CSS::UserSelect user_select() const { return m_noninherited.user_select; }
CSS::Isolation isolation() const { return m_noninherited.isolation; }
CSS::Containment const& contain() const { return m_noninherited.contain; }
CSS::LengthBox const& inset() const { return m_noninherited.inset; }
const CSS::LengthBox& margin() const { return m_noninherited.margin; }
@ -681,6 +695,7 @@ protected:
CSS::UnicodeBidi unicode_bidi { InitialValues::unicode_bidi() };
CSS::UserSelect user_select { InitialValues::user_select() };
CSS::Isolation isolation { InitialValues::isolation() };
CSS::Containment contain { InitialValues::contain() };
Optional<CSS::Transformation> rotate;
Optional<CSS::Transformation> translate;
@ -855,6 +870,7 @@ public:
void set_writing_mode(CSS::WritingMode value) { m_inherited.writing_mode = value; }
void set_user_select(CSS::UserSelect value) { m_noninherited.user_select = value; }
void set_isolation(CSS::Isolation value) { m_noninherited.isolation = value; }
void set_contain(CSS::Containment value) { m_noninherited.contain = move(value); }
void set_fill(SVGPaint value) { m_inherited.fill = move(value); }
void set_stroke(SVGPaint value) { m_inherited.stroke = move(value); }

View file

@ -62,6 +62,11 @@ public:
bool is_table_cell() const { return is_internal() && internal() == DisplayInternal::TableCell; }
bool is_table_column_group() const { return is_internal() && internal() == DisplayInternal::TableColumnGroup; }
bool is_table_caption() const { return is_internal() && internal() == DisplayInternal::TableCaption; }
// https://drafts.csswg.org/css-display-3/#internal-table-element
bool is_internal_table() const
{
return is_internal() && (internal() == DisplayInternal::TableRowGroup || internal() == DisplayInternal::TableHeaderGroup || internal() == DisplayInternal::TableFooterGroup || internal() == DisplayInternal::TableRow || internal() == DisplayInternal::TableCell || internal() == DisplayInternal::TableColumnGroup || internal() == DisplayInternal::TableColumn);
}
bool is_none() const { return m_type == Type::Box && m_value.box == DisplayBox::None; }
bool is_contents() const { return m_type == Type::Box && m_value.box == DisplayBox::Contents; }

View file

@ -107,6 +107,16 @@
"none",
"all"
],
"contain": [
"none",
"strict",
"content",
"size",
"inline-size",
"layout",
"style",
"paint"
],
"content-visibility": [
"visible",
"auto",

View file

@ -210,6 +210,7 @@
"inline-end",
"inline-flex",
"inline-grid",
"inline-size",
"inline-start",
"inline-table",
"inset",
@ -235,6 +236,7 @@
"landscape",
"large",
"larger",
"layout",
"left",
"legacy",
"less",
@ -308,6 +310,7 @@
"p3",
"padding-box",
"paged",
"paint",
"paused",
"petite-caps",
"pixelated",
@ -366,6 +369,7 @@
"sideways-lr",
"sideways-rl",
"simplified",
"size",
"slashed-zero",
"slider-horizontal",
"slow",
@ -389,7 +393,9 @@
"static",
"sticky",
"stretch",
"strict",
"stroke-box",
"style",
"sub",
"subtractive",
"super",

View file

@ -923,6 +923,16 @@
"column-count"
]
},
"contain": {
"__comment": "FIXME: Some values of contain need to be mutually exclusive. The grammar this should adhere to is 'none | strict | content | [ [size | inline-size] || layout || style || paint ]'.",
"affects-stacking-context": true,
"animation-type": "none",
"inherited": false,
"initial": "none",
"valid-types": [
"contain"
]
},
"content": {
"animation-type": "discrete",
"inherited": false,