LibWeb: Invalidate descendants for container-* property changes
Whenever an ancestor's container-name or container-type changes, it affects the matching of any `@container` queries, and so can affect the style of any descendant.
This commit is contained in:
parent
67046049d9
commit
cb14d574de
4 changed files with 59 additions and 13 deletions
|
|
@ -95,6 +95,9 @@ RequiredInvalidationAfterStyleChange compute_property_invalidation(CSS::Property
|
|||
return invalidation;
|
||||
}
|
||||
|
||||
if (AK::first_is_one_of(property_id, CSS::PropertyID::ContainerName, CSS::PropertyID::ContainerType))
|
||||
invalidation.recompute_descendant_styles = true;
|
||||
|
||||
// OPTIMIZATION: Special handling for CSS `visibility`:
|
||||
if (property_id == CSS::PropertyID::Visibility) {
|
||||
// We don't need to relayout if the visibility changes from visible to hidden or vice versa. Only collapse requires relayout.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ struct RequiredInvalidationAfterStyleChange {
|
|||
bool relayout : 1 { false };
|
||||
bool rebuild_layout_tree : 1 { false };
|
||||
bool rebuild_accumulated_visual_contexts : 1 { false };
|
||||
// The element's change affects rule matching for descendants, without necessarily changing inherited style.
|
||||
bool recompute_descendant_styles : 1 { false };
|
||||
|
||||
void operator|=(RequiredInvalidationAfterStyleChange const& other)
|
||||
{
|
||||
|
|
@ -24,9 +26,10 @@ struct RequiredInvalidationAfterStyleChange {
|
|||
relayout |= other.relayout;
|
||||
rebuild_layout_tree |= other.rebuild_layout_tree;
|
||||
rebuild_accumulated_visual_contexts |= other.rebuild_accumulated_visual_contexts;
|
||||
recompute_descendant_styles |= other.recompute_descendant_styles;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_none() const { return !repaint && !rebuild_stacking_context_tree && !relayout && !rebuild_layout_tree && !rebuild_accumulated_visual_contexts; }
|
||||
[[nodiscard]] bool is_none() const { return !repaint && !rebuild_stacking_context_tree && !relayout && !rebuild_layout_tree && !rebuild_accumulated_visual_contexts && !recompute_descendant_styles; }
|
||||
[[nodiscard]] bool is_full() const { return repaint && rebuild_stacking_context_tree && relayout && rebuild_layout_tree; }
|
||||
static RequiredInvalidationAfterStyleChange full() { return { true, true, true, true, false }; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1809,7 +1809,13 @@ bool Document::layout_is_up_to_date() const
|
|||
&& m_svg_roots_needing_relayout.is_empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] static CSS::RequiredInvalidationAfterStyleChange update_style_recursively(Node& node, CSS::StyleComputer& style_computer, bool needs_inherited_style_update, bool recompute_elements_depending_on_custom_properties, bool parent_display_changed)
|
||||
[[nodiscard]] static CSS::RequiredInvalidationAfterStyleChange update_style_recursively(
|
||||
Node& node,
|
||||
CSS::StyleComputer& style_computer,
|
||||
bool needs_inherited_style_update,
|
||||
bool recompute_elements_depending_on_custom_properties,
|
||||
bool parent_display_changed,
|
||||
bool ancestor_needs_descendant_style_recompute)
|
||||
{
|
||||
bool const needs_full_style_update = node.document().needs_full_style_update();
|
||||
CSS::RequiredInvalidationAfterStyleChange invalidation;
|
||||
|
|
@ -1831,7 +1837,12 @@ bool Document::layout_is_up_to_date() const
|
|||
// include media conditions, or b) the data used to resolve media queries hasn't changed.
|
||||
bool const needs_style_update_due_to_if_media = element.style_uses_if_css_function();
|
||||
|
||||
if (needs_full_style_update || node.needs_style_update() || parent_display_changed || (recompute_elements_depending_on_custom_properties && (element.style_uses_var_css_function() || element.style_uses_inherit_css_function())) || needs_style_update_due_to_if_media) {
|
||||
if (needs_full_style_update
|
||||
|| node.needs_style_update()
|
||||
|| parent_display_changed
|
||||
|| ancestor_needs_descendant_style_recompute
|
||||
|| (recompute_elements_depending_on_custom_properties && (element.style_uses_var_css_function() || element.style_uses_inherit_css_function()))
|
||||
|| needs_style_update_due_to_if_media) {
|
||||
node_invalidation = element.recompute_style(did_change_custom_properties);
|
||||
} else if (needs_inherited_style_update) {
|
||||
node_invalidation = element.recompute_inherited_style();
|
||||
|
|
@ -1863,11 +1874,29 @@ bool Document::layout_is_up_to_date() const
|
|||
// NB: When display changes to/from flex/grid/contents, children may need to be blockified or un-blockified.
|
||||
// This requires a full style recompute, not just inherited style update.
|
||||
bool children_need_full_style_recompute = node_invalidation.rebuild_layout_tree;
|
||||
if (needs_full_style_update || node.child_needs_style_update() || children_need_inherited_style_update || recompute_elements_depending_on_custom_properties || children_need_full_style_recompute) {
|
||||
bool descendant_style_recompute_needed = ancestor_needs_descendant_style_recompute || node_invalidation.recompute_descendant_styles;
|
||||
if (needs_full_style_update
|
||||
|| node.child_needs_style_update()
|
||||
|| children_need_inherited_style_update
|
||||
|| recompute_elements_depending_on_custom_properties
|
||||
|| children_need_full_style_recompute
|
||||
|| descendant_style_recompute_needed) {
|
||||
if (node.is_element()) {
|
||||
if (auto shadow_root = static_cast<DOM::Element&>(node).shadow_root()) {
|
||||
if (needs_full_style_update || shadow_root->needs_style_update() || shadow_root->child_needs_style_update()) {
|
||||
auto subtree_invalidation = update_style_recursively(*shadow_root, style_computer, children_need_inherited_style_update, recompute_elements_depending_on_custom_properties, children_need_full_style_recompute);
|
||||
if (needs_full_style_update
|
||||
|| shadow_root->needs_style_update()
|
||||
|| shadow_root->child_needs_style_update()
|
||||
|| children_need_inherited_style_update
|
||||
|| recompute_elements_depending_on_custom_properties
|
||||
|| children_need_full_style_recompute
|
||||
|| descendant_style_recompute_needed) {
|
||||
auto subtree_invalidation = update_style_recursively(
|
||||
*shadow_root,
|
||||
style_computer,
|
||||
children_need_inherited_style_update,
|
||||
recompute_elements_depending_on_custom_properties,
|
||||
children_need_full_style_recompute,
|
||||
descendant_style_recompute_needed);
|
||||
if (!is_display_none)
|
||||
invalidation |= subtree_invalidation;
|
||||
}
|
||||
|
|
@ -1875,8 +1904,20 @@ bool Document::layout_is_up_to_date() const
|
|||
}
|
||||
|
||||
node.for_each_child([&](auto& child) {
|
||||
if (needs_full_style_update || child.needs_style_update() || children_need_inherited_style_update || child.child_needs_style_update() || recompute_elements_depending_on_custom_properties || children_need_full_style_recompute) {
|
||||
auto subtree_invalidation = update_style_recursively(child, style_computer, children_need_inherited_style_update, recompute_elements_depending_on_custom_properties, children_need_full_style_recompute);
|
||||
if (needs_full_style_update
|
||||
|| child.needs_style_update()
|
||||
|| children_need_inherited_style_update
|
||||
|| child.child_needs_style_update()
|
||||
|| recompute_elements_depending_on_custom_properties
|
||||
|| children_need_full_style_recompute
|
||||
|| descendant_style_recompute_needed) {
|
||||
auto subtree_invalidation = update_style_recursively(
|
||||
child,
|
||||
style_computer,
|
||||
children_need_inherited_style_update,
|
||||
recompute_elements_depending_on_custom_properties,
|
||||
children_need_full_style_recompute,
|
||||
descendant_style_recompute_needed);
|
||||
if (!is_display_none)
|
||||
invalidation |= subtree_invalidation;
|
||||
}
|
||||
|
|
@ -1933,7 +1974,7 @@ void Document::update_style()
|
|||
|
||||
build_registered_properties_cache();
|
||||
|
||||
auto invalidation = update_style_recursively(*this, style_computer(), false, false, false);
|
||||
auto invalidation = update_style_recursively(*this, style_computer(), false, false, false, false);
|
||||
if (!invalidation.is_none())
|
||||
set_needs_to_record_display_list();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ Harness status: OK
|
|||
|
||||
Found 3 tests
|
||||
|
||||
1 Pass
|
||||
2 Fail
|
||||
3 Pass
|
||||
Pass Initially selects #couter as --foo container, both match
|
||||
Fail Remove #couter --foo container-name, none match
|
||||
Fail Make #cinner a --foo container, #inner starts matching
|
||||
Pass Remove #couter --foo container-name, none match
|
||||
Pass Make #cinner a --foo container, #inner starts matching
|
||||
|
|
|
|||
Loading…
Reference in a new issue