at (24,24) content-size 752x0 children: not-inline
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+ PaintableWithLines (BlockContainer) [0,0 800x48]
+ PaintableWithLines (BlockContainer) [8,8 784x32]
+ PaintableWithLines (BlockContainer
) [8,8 784x32]
\ No newline at end of file
diff --git a/Tests/LibWeb/Layout/input/css-content-visibility.html b/Tests/LibWeb/Layout/input/css-content-visibility.html
new file mode 100644
index 00000000000..5ce60621d4e
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/css-content-visibility.html
@@ -0,0 +1,9 @@
+
I am not visibleand nor am I
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt
index 97a22823cb9..fc2a41b3184 100644
--- a/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt
+++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt
@@ -87,6 +87,7 @@ color: rgb(0, 0, 0)
column-count: auto
column-gap: auto
content: normal
+content-visibility: visible
cursor: auto
cx: 0px
cy: 0px
@@ -119,7 +120,7 @@ grid-row-start: auto
grid-template-areas:
grid-template-columns:
grid-template-rows:
-height: 2057px
+height: 2074px
image-rendering: auto
inline-size: auto
inset-block-end: auto
diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
index 6a2cd9e6f52..1201095f2b1 100644
--- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h
+++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
@@ -100,6 +100,7 @@ public:
static CSS::CaptionSide caption_side() { return CSS::CaptionSide::Top; }
static CSS::Clear clear() { return CSS::Clear::None; }
static CSS::Clip clip() { return CSS::Clip::make_auto(); }
+ static CSS::ContentVisibility content_visibility() { return CSS::ContentVisibility::Visible; }
static CSS::Cursor cursor() { return CSS::Cursor::Auto; }
static CSS::WhiteSpace white_space() { return CSS::WhiteSpace::Normal; }
static CSS::TextAlign text_align() { return CSS::TextAlign::Left; }
@@ -352,6 +353,7 @@ public:
CSS::CaptionSide caption_side() const { return m_inherited.caption_side; }
CSS::Clear clear() const { return m_noninherited.clear; }
CSS::Clip clip() const { return m_noninherited.clip; }
+ CSS::ContentVisibility content_visibility() const { return m_inherited.content_visibility; }
CSS::Cursor cursor() const { return m_inherited.cursor; }
CSS::ContentData content() const { return m_noninherited.content; }
CSS::PointerEvents pointer_events() const { return m_inherited.pointer_events; }
@@ -507,6 +509,7 @@ protected:
Color color { InitialValues::color() };
Optional accent_color {};
Color webkit_text_fill_color { InitialValues::color() };
+ CSS::ContentVisibility content_visibility { InitialValues::content_visibility() };
CSS::Cursor cursor { InitialValues::cursor() };
CSS::ImageRendering image_rendering { InitialValues::image_rendering() };
CSS::PointerEvents pointer_events { InitialValues::pointer_events() };
@@ -654,6 +657,7 @@ public:
void set_color(Color color) { m_inherited.color = color; }
void set_clip(CSS::Clip const& clip) { m_noninherited.clip = clip; }
void set_content(ContentData const& content) { m_noninherited.content = content; }
+ void set_content_visibility(CSS::ContentVisibility content_visibility) { m_inherited.content_visibility = content_visibility; }
void set_cursor(CSS::Cursor cursor) { m_inherited.cursor = cursor; }
void set_image_rendering(CSS::ImageRendering value) { m_inherited.image_rendering = value; }
void set_pointer_events(CSS::PointerEvents value) { m_inherited.pointer_events = value; }
diff --git a/Userland/Libraries/LibWeb/CSS/Enums.json b/Userland/Libraries/LibWeb/CSS/Enums.json
index 7417904f0a8..59d2d925f69 100644
--- a/Userland/Libraries/LibWeb/CSS/Enums.json
+++ b/Userland/Libraries/LibWeb/CSS/Enums.json
@@ -101,6 +101,11 @@
"right",
"both"
],
+ "content-visibility": [
+ "visible",
+ "auto",
+ "hidden"
+ ],
"cursor": [
"auto",
"default",
diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json
index 42fac029af7..3dc61948690 100644
--- a/Userland/Libraries/LibWeb/CSS/Properties.json
+++ b/Userland/Libraries/LibWeb/CSS/Properties.json
@@ -897,6 +897,15 @@
"no-close-quote"
]
},
+ "content-visibility": {
+ "animation-type": "none",
+ "__comment": "FIXME: Implement animation https://drafts.csswg.org/css-contain/#content-visibility-animation",
+ "inherited": false,
+ "initial": "visible",
+ "valid-types": [
+ "content-visibility"
+ ]
+ },
"cursor": {
"affects-layout": false,
"animation-type": "discrete",
diff --git a/Userland/Libraries/LibWeb/CSS/StyleInvalidation.cpp b/Userland/Libraries/LibWeb/CSS/StyleInvalidation.cpp
index fd02111a3bd..a9e663641e8 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleInvalidation.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleInvalidation.cpp
@@ -17,9 +17,9 @@ RequiredInvalidationAfterStyleChange compute_property_invalidation(CSS::Property
if (!property_value_changed)
return invalidation;
- // NOTE: If the computed CSS display property changes, we have to rebuild the entire layout tree.
+ // NOTE: If the computed CSS display/content-visibility property changes, we have to rebuild the entire layout tree.
// In the future, we should figure out ways to rebuild a smaller part of the tree.
- if (property_id == CSS::PropertyID::Display) {
+ if (property_id == CSS::PropertyID::Display || property_id == CSS::PropertyID::ContentVisibility) {
return RequiredInvalidationAfterStyleChange::full();
}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
index 992f105725f..83ab51ec56a 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
@@ -744,6 +744,12 @@ StyleProperties::ContentDataAndQuoteNestingLevel StyleProperties::content(u32 in
return { {}, quote_nesting_level };
}
+Optional StyleProperties::content_visibility() const
+{
+ auto value = property(CSS::PropertyID::ContentVisibility);
+ return value_id_to_content_visibility(value->to_identifier());
+}
+
Optional StyleProperties::cursor() const
{
auto value = property(CSS::PropertyID::Cursor);
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
index 70af730d665..c8e80082277 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
@@ -86,6 +86,7 @@ public:
u32 final_quote_nesting_level { 0 };
};
ContentDataAndQuoteNestingLevel content(u32 initial_quote_nesting_level) const;
+ Optional content_visibility() const;
Optional cursor() const;
Optional white_space() const;
Optional line_style(CSS::PropertyID) const;
diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp
index e774f1940d5..0134a1f9155 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Element.cpp
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp
index acd5a669c34..d94955f8ea5 100644
--- a/Userland/Libraries/LibWeb/Layout/Node.cpp
+++ b/Userland/Libraries/LibWeb/Layout/Node.cpp
@@ -613,6 +613,10 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
if (overflow_y.has_value())
computed_values.set_overflow_y(overflow_y.value());
+ auto content_visibility = computed_style.content_visibility();
+ if (content_visibility.has_value())
+ computed_values.set_content_visibility(content_visibility.value());
+
auto cursor = computed_style.cursor();
if (cursor.has_value())
computed_values.set_cursor(cursor.value());
diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
index 5d8126f6e9e..5b637f7d3e7 100644
--- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
+++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
@@ -371,15 +371,23 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
auto shadow_root = is(dom_node) ? verify_cast(dom_node).shadow_root() : nullptr;
+ auto element_has_content_visibility_hidden = [&dom_node]() {
+ if (is(dom_node)) {
+ auto& element = static_cast(dom_node);
+ return element.computed_css_values()->content_visibility() == CSS::ContentVisibility::Hidden;
+ }
+ return false;
+ }();
+
// Add node for the ::before pseudo-element.
- if (is(dom_node) && layout_node->can_have_children()) {
+ if (is(dom_node) && layout_node->can_have_children() && !element_has_content_visibility_hidden) {
auto& element = static_cast(dom_node);
push_parent(verify_cast(*layout_node));
create_pseudo_element_if_needed(element, CSS::Selector::PseudoElement::Type::Before, AppendOrPrepend::Prepend);
pop_parent();
}
- if ((dom_node.has_children() || shadow_root) && layout_node->can_have_children()) {
+ if ((dom_node.has_children() || shadow_root) && layout_node->can_have_children() && !element_has_content_visibility_hidden) {
push_parent(verify_cast(*layout_node));
if (shadow_root) {
for (auto* node = shadow_root->first_child(); node; node = node->next_sibling()) {
@@ -411,7 +419,12 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
}
if (is(dom_node)) {
- auto slottables = static_cast(dom_node).assigned_nodes_internal();
+ auto& slot_element = static_cast(dom_node);
+
+ if (slot_element.computed_css_values()->content_visibility() == CSS::ContentVisibility::Hidden)
+ return;
+
+ auto slottables = slot_element.assigned_nodes_internal();
push_parent(verify_cast(*layout_node));
for (auto const& slottable : slottables)
@@ -499,7 +512,7 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
}
// Add nodes for the ::after pseudo-element.
- if (is(dom_node) && layout_node->can_have_children()) {
+ if (is(dom_node) && layout_node->can_have_children() && !element_has_content_visibility_hidden) {
auto& element = static_cast(dom_node);
push_parent(verify_cast(*layout_node));
create_pseudo_element_if_needed(element, CSS::Selector::PseudoElement::Type::After, AppendOrPrepend::Append);