LibWeb: Optimize getComputedStyle() to avoid layout when possible

Previously, getComputedStyle() would always call update_layout() for
most properties. This was expensive since layout involves a full tree
traversal even when only style information is needed.

This change introduces a more granular approach:
- Properties needing layout computation (used values like width/height)
  still call update_layout()
- Properties needing a layout node for resolved value computation
  (colors, border widths, etc.) also call update_layout()
- All other properties now only call update_style()

The set of properties needing layout node for resolution is now defined
in Properties.json via the "needs-layout-node-for-resolved-value" flag,
rather than being hardcoded. This is generated into a new function
property_needs_layout_node_for_resolved_value().
This commit is contained in:
Andreas Kling
2026-01-26 00:36:52 +01:00
committed by Andreas Kling
parent 3b90eb1d49
commit 35839af2d2
Notes: github-actions[bot] 2026-02-06 11:06:37 +00:00
5 changed files with 74 additions and 16 deletions

View File

@@ -296,6 +296,7 @@ size_t property_maximum_value_count(PropertyID);
bool property_affects_layout(PropertyID);
bool property_affects_stacking_context(PropertyID);
bool property_needs_layout_for_getcomputedstyle(PropertyID);
bool property_needs_layout_node_for_resolved_value(PropertyID);
constexpr PropertyID first_property_id = PropertyID::@first_property_id@;
constexpr PropertyID last_property_id = PropertyID::@last_property_id@;
@@ -737,6 +738,32 @@ bool property_needs_layout_for_getcomputedstyle(PropertyID property_id)
}
}
bool property_needs_layout_node_for_resolved_value(PropertyID property_id)
{
switch (property_id) {
)~~~");
properties.for_each_member([&](auto& name, auto& value) {
VERIFY(value.is_object());
if (is_legacy_alias(value.as_object()))
return;
if (value.as_object().get_bool("needs-layout-node-for-resolved-value"sv).value_or(false)) {
auto member_generator = generator.fork();
member_generator.set("name:titlecase", title_casify(name));
member_generator.append(R"~~~(
case PropertyID::@name:titlecase@:
)~~~");
}
});
generator.append(R"~~~(
return true;
default:
return false;
}
}
NonnullRefPtr<StyleValue const> property_initial_value(PropertyID property_id)
{
static Array<RefPtr<StyleValue const>, to_underlying(last_property_id) + 1> initial_values;