mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-20 08:18:55 +00:00
LibWeb: Implement CSS 'contain' property
This commit is contained in:
parent
c53c781745
commit
67ed676831
Notes:
github-actions[bot]
2025-01-28 11:25:39 +00:00
Author: https://github.com/Psychpsyo
Commit: 67ed676831
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3296
Reviewed-by: https://github.com/AtkinsSJ ✅
154 changed files with 4200 additions and 117 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -107,6 +107,16 @@
|
|||
"none",
|
||||
"all"
|
||||
],
|
||||
"contain": [
|
||||
"none",
|
||||
"strict",
|
||||
"content",
|
||||
"size",
|
||||
"inline-size",
|
||||
"layout",
|
||||
"style",
|
||||
"paint"
|
||||
],
|
||||
"content-visibility": [
|
||||
"visible",
|
||||
"auto",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1190,9 +1190,21 @@ static void propagate_scrollbar_width_to_viewport(Element& root_element, Layout:
|
|||
viewport_computed_values.set_scrollbar_width(root_element_computed_values.scrollbar_width());
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-overflow-3/#overflow-propagation
|
||||
static void propagate_overflow_to_viewport(Element& root_element, Layout::Viewport& viewport)
|
||||
{
|
||||
// https://drafts.csswg.org/css-overflow-3/#overflow-propagation
|
||||
// https://drafts.csswg.org/css-contain-2/#contain-property
|
||||
// Additionally, when any containments are active on either the HTML <html> or <body> elements, propagation of
|
||||
// properties from the <body> element to the initial containing block, the viewport, or the canvas background, is
|
||||
// disabled. Notably, this affects:
|
||||
// - 'overflow' and its longhands (see CSS Overflow 3 § 3.3 Overflow Viewport Propagation)
|
||||
if (root_element.is_html_html_element() && !root_element.computed_properties()->contain().is_empty())
|
||||
return;
|
||||
|
||||
auto* body_element = root_element.first_child_of_type<HTML::HTMLBodyElement>();
|
||||
if (body_element && !body_element->computed_properties()->contain().is_empty())
|
||||
return;
|
||||
|
||||
// UAs must apply the overflow-* values set on the root element to the viewport
|
||||
// when the root element’s display value is not none.
|
||||
auto overflow_origin_node = root_element.layout_node();
|
||||
|
|
|
@ -2741,6 +2741,148 @@ bool Element::is_relevant_to_the_user()
|
|||
return false;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#skips-its-contents
|
||||
bool Element::skips_its_contents()
|
||||
{
|
||||
// https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-hidden
|
||||
// The element skips its contents.
|
||||
if (computed_properties()->content_visibility() == CSS::ContentVisibility::Hidden)
|
||||
return true;
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto
|
||||
// If the element is not relevant to the user, it also skips its contents.
|
||||
if (computed_properties()->content_visibility() == CSS::ContentVisibility::Auto && !this->is_relevant_to_the_user()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-size
|
||||
bool Element::has_size_containment() const
|
||||
{
|
||||
// However, giving an element size containment has no effect if any of the following are true:
|
||||
|
||||
// - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none')
|
||||
if (!layout_node())
|
||||
return false;
|
||||
|
||||
// - if its inner display type is 'table'
|
||||
if (layout_node()->display().is_table_inside())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal table box
|
||||
if (layout_node()->display().is_internal_table())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal ruby box or a non-atomic inline-level box
|
||||
// FIXME: Implement this.
|
||||
|
||||
if (computed_properties()->contain().size_containment)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-inline-size
|
||||
bool Element::has_inline_size_containment() const
|
||||
{
|
||||
// Giving an element inline-size containment has no effect if any of the following are true:
|
||||
|
||||
// - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none')
|
||||
if (!layout_node())
|
||||
return false;
|
||||
|
||||
// - if its inner display type is 'table'
|
||||
if (layout_node()->display().is_table_inside())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal table box
|
||||
if (layout_node()->display().is_internal_table())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal ruby box or a non-atomic inline-level box
|
||||
// FIXME: Implement this.
|
||||
|
||||
if (computed_properties()->contain().inline_size_containment)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-layout
|
||||
bool Element::has_layout_containment() const
|
||||
{
|
||||
// However, giving an element layout containment has no effect if any of the following are true:
|
||||
|
||||
// - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none')
|
||||
if (!layout_node())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal table box other than 'table-cell'
|
||||
if (layout_node()->display().is_internal_table() && !layout_node()->display().is_table_cell())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal ruby box or a non-atomic inline-level box
|
||||
// FIXME: Implement this.
|
||||
|
||||
if (computed_properties()->contain().layout_containment)
|
||||
return true;
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto
|
||||
// Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and
|
||||
// paint containment for the element.
|
||||
if (computed_properties()->content_visibility() == CSS::ContentVisibility::Auto)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-style
|
||||
bool Element::has_style_containment() const
|
||||
{
|
||||
// However, giving an element style containment has no effect if any of the following are true:
|
||||
|
||||
// - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none')
|
||||
if (!layout_node())
|
||||
return false;
|
||||
|
||||
if (computed_properties()->contain().style_containment)
|
||||
return true;
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto
|
||||
// Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and
|
||||
// paint containment for the element.
|
||||
if (computed_properties()->content_visibility() == CSS::ContentVisibility::Auto)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-paint
|
||||
bool Element::has_paint_containment() const
|
||||
{
|
||||
// However, giving an element paint containment has no effect if any of the following are true:
|
||||
|
||||
// - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none')
|
||||
if (!layout_node())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal table box other than 'table-cell'
|
||||
if (layout_node()->display().is_internal_table() && !layout_node()->display().is_table_cell())
|
||||
return false;
|
||||
|
||||
// - if its principal box is an internal ruby box or a non-atomic inline-level box
|
||||
// FIXME: Implement this
|
||||
|
||||
if (computed_properties()->contain().paint_containment)
|
||||
return true;
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto
|
||||
// Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and
|
||||
// paint containment for the element.
|
||||
if (computed_properties()->content_visibility() == CSS::ContentVisibility::Auto)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Element::id_reference_exists(String const& id_reference) const
|
||||
{
|
||||
return document().get_element_by_id(id_reference);
|
||||
|
@ -3154,6 +3296,12 @@ void Element::resolve_counters(CSS::ComputedProperties& style)
|
|||
for (auto const& counter : counter_reset)
|
||||
ensure_counters_set().instantiate_a_counter(counter.name, unique_id(), counter.is_reversed, counter.value);
|
||||
|
||||
// FIXME: Take style containment into account
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-style
|
||||
// Giving an element style containment has the following effects:
|
||||
// 1. The 'counter-increment' and 'counter-set' properties must be scoped to the element’s sub-tree and create a
|
||||
// new counter.
|
||||
|
||||
// 3. Counter values are incremented (counter-increment).
|
||||
auto counter_increment = style.counter_data(CSS::PropertyID::CounterIncrement);
|
||||
for (auto const& counter : counter_increment)
|
||||
|
|
|
@ -398,6 +398,16 @@ public:
|
|||
void determine_proximity_to_the_viewport();
|
||||
bool is_relevant_to_the_user();
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#skips-its-contents
|
||||
bool skips_its_contents();
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-types
|
||||
bool has_size_containment() const;
|
||||
bool has_inline_size_containment() const;
|
||||
bool has_layout_containment() const;
|
||||
bool has_style_containment() const;
|
||||
bool has_paint_containment() const;
|
||||
|
||||
protected:
|
||||
Element(Document&, DOM::QualifiedName);
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <LibWeb/Bindings/HTMLHtmlElementPrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/HTML/HTMLBodyElement.h>
|
||||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
||||
|
@ -28,6 +29,18 @@ void HTMLHtmlElement::initialize(JS::Realm& realm)
|
|||
|
||||
bool HTMLHtmlElement::should_use_body_background_properties() const
|
||||
{
|
||||
// https://drafts.csswg.org/css-contain-2/#contain-property
|
||||
// Additionally, when any containments are active on either the HTML <html> or <body> elements, propagation of
|
||||
// properties from the <body> element to the initial containing block, the viewport, or the canvas background, is
|
||||
// disabled. Notably, this affects:
|
||||
// - 'background' and its longhands (see CSS Backgrounds 3 § 2.11.2 The Canvas Background and the HTML <body> Element)
|
||||
if (!computed_properties()->contain().is_empty())
|
||||
return false;
|
||||
|
||||
auto* body_element = first_child_of_type<HTML::HTMLBodyElement>();
|
||||
if (body_element && !body_element->computed_properties()->contain().is_empty())
|
||||
return false;
|
||||
|
||||
auto background_color = layout_node()->computed_values().background_color();
|
||||
auto const& background_layers = layout_node()->background_layers();
|
||||
|
||||
|
|
|
@ -28,6 +28,37 @@ Box::~Box()
|
|||
{
|
||||
}
|
||||
|
||||
Optional<CSSPixels> Box::natural_width() const
|
||||
{
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-size
|
||||
// Replaced elements must be treated as having a natural width and height of 0 and no natural aspect
|
||||
// ratio.
|
||||
if (dom_node() && dom_node()->is_element() && as<DOM::Element>(dom_node())->has_size_containment())
|
||||
return 0;
|
||||
|
||||
return m_natural_width;
|
||||
}
|
||||
Optional<CSSPixels> Box::natural_height() const
|
||||
{
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-size
|
||||
// Replaced elements must be treated as having a natural width and height of 0 and no natural aspect
|
||||
// ratio.
|
||||
if (dom_node() && dom_node()->is_element() && as<DOM::Element>(dom_node())->has_size_containment())
|
||||
return 0;
|
||||
|
||||
return m_natural_height;
|
||||
}
|
||||
Optional<CSSPixelFraction> Box::natural_aspect_ratio() const
|
||||
{
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-size
|
||||
// Replaced elements must be treated as having a natural width and height of 0 and no natural aspect
|
||||
// ratio.
|
||||
if (dom_node() && dom_node()->is_element() && as<DOM::Element>(dom_node())->has_size_containment())
|
||||
return {};
|
||||
|
||||
return m_natural_aspect_ratio;
|
||||
}
|
||||
|
||||
void Box::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
|
|
|
@ -25,9 +25,9 @@ public:
|
|||
Painting::PaintableBox* paintable_box();
|
||||
|
||||
// https://www.w3.org/TR/css-images-3/#natural-dimensions
|
||||
Optional<CSSPixels> natural_width() const { return m_natural_width; }
|
||||
Optional<CSSPixels> natural_height() const { return m_natural_height; }
|
||||
Optional<CSSPixelFraction> natural_aspect_ratio() const { return m_natural_aspect_ratio; }
|
||||
Optional<CSSPixels> natural_width() const;
|
||||
Optional<CSSPixels> natural_height() const;
|
||||
Optional<CSSPixelFraction> natural_aspect_ratio() const;
|
||||
|
||||
bool has_natural_width() const { return natural_width().has_value(); }
|
||||
bool has_natural_height() const { return natural_height().has_value(); }
|
||||
|
|
|
@ -94,7 +94,14 @@ bool FormattingContext::creates_block_formatting_context(Box const& box)
|
|||
if (box.display().is_flow_root_inside())
|
||||
return true;
|
||||
|
||||
// FIXME: Elements with contain: layout, content, or paint.
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-types
|
||||
// 1. The layout containment box establishes an independent formatting context.
|
||||
// 4. The paint containment box establishes an independent formatting context.
|
||||
if (box.dom_node() && box.dom_node()->is_element()) {
|
||||
auto element = as<DOM::Element>(box.dom_node());
|
||||
if (element->has_layout_containment() || element->has_paint_containment())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (box.parent()) {
|
||||
auto parent_display = box.parent()->display();
|
||||
|
|
|
@ -89,6 +89,17 @@ bool Node::can_contain_boxes_with_position_absolute() const
|
|||
if (computed_values().scale().has_value())
|
||||
return true;
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-types
|
||||
// 4. The layout containment box establishes an absolute positioning containing block and a fixed positioning
|
||||
// containing block.
|
||||
// 4. The paint containment box establishes an absolute positioning containing block and a fixed positioning
|
||||
// containing block.
|
||||
if (dom_node() && dom_node()->is_element()) {
|
||||
auto element = as<DOM::Element>(dom_node());
|
||||
if (element->has_layout_containment() || element->has_paint_containment())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -220,6 +231,15 @@ bool Node::establishes_stacking_context() const
|
|||
if (computed_values().isolation() == CSS::Isolation::Isolate)
|
||||
return true;
|
||||
|
||||
// https://drafts.csswg.org/css-contain-2/#containment-types
|
||||
// 5. The layout containment box creates a stacking context.
|
||||
// 3. The paint containment box creates a stacking context.
|
||||
if (dom_node() && dom_node()->is_element()) {
|
||||
auto element = as<DOM::Element>(dom_node());
|
||||
if (element->has_layout_containment() || element->has_paint_containment())
|
||||
return true;
|
||||
}
|
||||
|
||||
return computed_values().opacity() < 1.0f;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue