瀏覽代碼

LibWeb: Support content-visibility css

Edwin Hoksberg 1 年之前
父節點
當前提交
020b20d817

+ 9 - 0
Tests/LibWeb/Layout/expected/css-content-visibility.txt

@@ -0,0 +1,9 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (0,0) content-size 800x48 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x32 children: not-inline
+      BlockContainer <div> at (24,24) content-size 752x0 children: not-inline
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+  PaintableWithLines (BlockContainer<HTML>) [0,0 800x48]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x32]
+      PaintableWithLines (BlockContainer<DIV>) [8,8 784x32]

+ 9 - 0
Tests/LibWeb/Layout/input/css-content-visibility.html

@@ -0,0 +1,9 @@
+<!doctype html><style>
+div {
+    padding: 16px;
+    content-visibility: hidden;
+}
+div::before {
+    content: 'I am invisible';
+}
+</style><div>I am not visible<span>and nor am I

+ 2 - 1
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

+ 4 - 0
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<Color> 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; }

+ 5 - 0
Userland/Libraries/LibWeb/CSS/Enums.json

@@ -101,6 +101,11 @@
     "right",
     "both"
   ],
+  "content-visibility": [
+    "visible",
+    "auto",
+    "hidden"
+  ],
   "cursor": [
     "auto",
     "default",

+ 9 - 0
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",

+ 2 - 2
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();
     }
 

+ 6 - 0
Userland/Libraries/LibWeb/CSS/StyleProperties.cpp

@@ -744,6 +744,12 @@ StyleProperties::ContentDataAndQuoteNestingLevel StyleProperties::content(u32 in
     return { {}, quote_nesting_level };
 }
 
+Optional<CSS::ContentVisibility> StyleProperties::content_visibility() const
+{
+    auto value = property(CSS::PropertyID::ContentVisibility);
+    return value_id_to_content_visibility(value->to_identifier());
+}
+
 Optional<CSS::Cursor> StyleProperties::cursor() const
 {
     auto value = property(CSS::PropertyID::Cursor);

+ 1 - 0
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<CSS::ContentVisibility> content_visibility() const;
     Optional<CSS::Cursor> cursor() const;
     Optional<CSS::WhiteSpace> white_space() const;
     Optional<CSS::LineStyle> line_style(CSS::PropertyID) const;

+ 1 - 0
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -17,6 +17,7 @@
 #include <LibWeb/CSS/ResolvedCSSStyleDeclaration.h>
 #include <LibWeb/CSS/SelectorEngine.h>
 #include <LibWeb/CSS/StyleComputer.h>
+#include <LibWeb/CSS/StyleProperties.h>
 #include <LibWeb/CSS/StyleValues/IdentifierStyleValue.h>
 #include <LibWeb/CSS/StyleValues/NumberStyleValue.h>
 #include <LibWeb/DOM/Attr.h>

+ 4 - 0
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());

+ 17 - 4
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::Element>(dom_node) ? verify_cast<DOM::Element>(dom_node).shadow_root() : nullptr;
 
+    auto element_has_content_visibility_hidden = [&dom_node]() {
+        if (is<DOM::Element>(dom_node)) {
+            auto& element = static_cast<DOM::Element&>(dom_node);
+            return element.computed_css_values()->content_visibility() == CSS::ContentVisibility::Hidden;
+        }
+        return false;
+    }();
+
     // Add node for the ::before pseudo-element.
-    if (is<DOM::Element>(dom_node) && layout_node->can_have_children()) {
+    if (is<DOM::Element>(dom_node) && layout_node->can_have_children() && !element_has_content_visibility_hidden) {
         auto& element = static_cast<DOM::Element&>(dom_node);
         push_parent(verify_cast<NodeWithStyle>(*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<NodeWithStyle>(*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<HTML::HTMLSlotElement>(dom_node)) {
-        auto slottables = static_cast<HTML::HTMLSlotElement&>(dom_node).assigned_nodes_internal();
+        auto& slot_element = static_cast<HTML::HTMLSlotElement&>(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<NodeWithStyle>(*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::Element>(dom_node) && layout_node->can_have_children()) {
+    if (is<DOM::Element>(dom_node) && layout_node->can_have_children() && !element_has_content_visibility_hidden) {
         auto& element = static_cast<DOM::Element&>(dom_node);
         push_parent(verify_cast<NodeWithStyle>(*layout_node));
         create_pseudo_element_if_needed(element, CSS::Selector::PseudoElement::Type::After, AppendOrPrepend::Append);