Coverage Report

Created: 2025-08-28 06:26

/src/serenity/Userland/Libraries/LibWeb/CSS/StyleInvalidation.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibWeb/CSS/StyleInvalidation.h>
8
#include <LibWeb/CSS/StyleProperties.h>
9
10
namespace Web::CSS {
11
12
RequiredInvalidationAfterStyleChange compute_property_invalidation(CSS::PropertyID property_id, RefPtr<CSSStyleValue const> const& old_value, RefPtr<CSSStyleValue const> const& new_value)
13
0
{
14
0
    RequiredInvalidationAfterStyleChange invalidation;
15
16
0
    bool const property_value_changed = (!old_value || !new_value) || *old_value != *new_value;
17
0
    if (!property_value_changed)
18
0
        return invalidation;
19
20
    // NOTE: If the computed CSS display, content, or content-visibility property changes, we have to rebuild the entire layout tree.
21
    //       In the future, we should figure out ways to rebuild a smaller part of the tree.
22
0
    if (AK::first_is_one_of(property_id, CSS::PropertyID::Display, CSS::PropertyID::Content, CSS::PropertyID::ContentVisibility)) {
23
0
        return RequiredInvalidationAfterStyleChange::full();
24
0
    }
25
26
    // NOTE: If one of the overflow properties change, we rebuild the entire layout tree.
27
    //       This ensures that overflow propagation from root/body to viewport happens correctly.
28
    //       In the future, we can make this invalidation narrower.
29
0
    if (property_id == CSS::PropertyID::OverflowX || property_id == CSS::PropertyID::OverflowY) {
30
0
        return RequiredInvalidationAfterStyleChange::full();
31
0
    }
32
33
    // OPTIMIZATION: Special handling for CSS `visibility`:
34
0
    if (property_id == CSS::PropertyID::Visibility) {
35
        // We don't need to relayout if the visibility changes from visible to hidden or vice versa. Only collapse requires relayout.
36
0
        if ((old_value && old_value->to_keyword() == CSS::Keyword::Collapse) != (new_value && new_value->to_keyword() == CSS::Keyword::Collapse))
37
0
            invalidation.relayout = true;
38
        // Of course, we still have to repaint on any visibility change.
39
0
        invalidation.repaint = true;
40
0
    } else if (CSS::property_affects_layout(property_id)) {
41
0
        invalidation.relayout = true;
42
0
    }
43
44
0
    if (property_id == CSS::PropertyID::Opacity && old_value && new_value) {
45
        // OPTIMIZATION: An element creates a stacking context when its opacity changes from 1 to less than 1
46
        //               and stops to create one when opacity returns to 1. So stacking context tree rebuild is
47
        //               not required for opacity changes within the range below 1.
48
0
        auto old_value_opacity = CSS::StyleProperties::resolve_opacity_value(*old_value);
49
0
        auto new_value_opacity = CSS::StyleProperties::resolve_opacity_value(*new_value);
50
0
        if (old_value_opacity != new_value_opacity && (old_value_opacity == 1 || new_value_opacity == 1)) {
51
0
            invalidation.rebuild_stacking_context_tree = true;
52
0
        }
53
0
    } else if (CSS::property_affects_stacking_context(property_id)) {
54
0
        invalidation.rebuild_stacking_context_tree = true;
55
0
    }
56
0
    invalidation.repaint = true;
57
58
0
    return invalidation;
59
0
}
60
61
}