mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:17:45 +00:00
LibWeb: Only invalidate stacking context tree for opacity/z-index change
I came across some websites that change an elements CSS "opacity" in their :hover selectors. That caused us to relayout on hover, which we'd like to avoid. With this patch, we now check if a property only affects the stacking context tree, and if nothing layout-affecting has changed, we only invalidate the stacking context tree, causing it to be rebuilt on next paint or hit test. This makes :hover { opacity: ... } rules much faster. :^)
This commit is contained in:
parent
59afdb959f
commit
8c88ee1165
6 changed files with 54 additions and 0 deletions
|
@ -155,6 +155,34 @@ bool property_affects_layout(PropertyID property_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool property_affects_stacking_context(PropertyID property_id)
|
||||||
|
{
|
||||||
|
switch (property_id) {
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
properties.for_each_member([&](auto& name, auto& value) {
|
||||||
|
VERIFY(value.is_object());
|
||||||
|
|
||||||
|
bool affects_layout = true;
|
||||||
|
if (value.as_object().has("affects-stacking-context"))
|
||||||
|
affects_layout = value.as_object().get("affects-stacking-context").to_bool();
|
||||||
|
|
||||||
|
if (affects_layout) {
|
||||||
|
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> property_initial_value(PropertyID property_id)
|
NonnullRefPtr<StyleValue> property_initial_value(PropertyID property_id)
|
||||||
{
|
{
|
||||||
static Array<RefPtr<StyleValue>, to_underlying(last_property_id) + 1> initial_values;
|
static Array<RefPtr<StyleValue>, to_underlying(last_property_id) + 1> initial_values;
|
||||||
|
|
|
@ -90,6 +90,7 @@ bool property_accepts_value(PropertyID, StyleValue&);
|
||||||
size_t property_maximum_value_count(PropertyID);
|
size_t property_maximum_value_count(PropertyID);
|
||||||
|
|
||||||
bool property_affects_layout(PropertyID);
|
bool property_affects_layout(PropertyID);
|
||||||
|
bool property_affects_stacking_context(PropertyID);
|
||||||
|
|
||||||
constexpr PropertyID first_property_id = PropertyID::@first_property_id@;
|
constexpr PropertyID first_property_id = PropertyID::@first_property_id@;
|
||||||
constexpr PropertyID last_property_id = PropertyID::@last_property_id@;
|
constexpr PropertyID last_property_id = PropertyID::@last_property_id@;
|
||||||
|
|
|
@ -1034,6 +1034,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"opacity": {
|
"opacity": {
|
||||||
|
"affects-layout": false,
|
||||||
|
"affects-stacking-context": true,
|
||||||
"inherited": false,
|
"inherited": false,
|
||||||
"initial": "1",
|
"initial": "1",
|
||||||
"valid-types": [
|
"valid-types": [
|
||||||
|
@ -1459,6 +1461,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"z-index": {
|
"z-index": {
|
||||||
|
"affects-layout": false,
|
||||||
|
"affects-stacking-context": true,
|
||||||
"inherited": false,
|
"inherited": false,
|
||||||
"initial": "auto",
|
"initial": "auto",
|
||||||
"valid-types": [
|
"valid-types": [
|
||||||
|
|
|
@ -1558,4 +1558,10 @@ void Document::decrement_number_of_things_delaying_the_load_event(Badge<Document
|
||||||
page->client().page_did_update_resource_count(m_number_of_things_delaying_the_load_event);
|
page->client().page_did_update_resource_count(m_number_of_things_delaying_the_load_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::invalidate_stacking_context_tree()
|
||||||
|
{
|
||||||
|
if (auto* paint_box = this->paint_box())
|
||||||
|
const_cast<Painting::PaintableBox*>(paint_box)->invalidate_stacking_context();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,7 @@ public:
|
||||||
void set_needs_layout();
|
void set_needs_layout();
|
||||||
|
|
||||||
void invalidate_layout();
|
void invalidate_layout();
|
||||||
|
void invalidate_stacking_context_tree();
|
||||||
|
|
||||||
virtual bool is_child_allowed(const Node&) const override;
|
virtual bool is_child_allowed(const Node&) const override;
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
||||||
#include <LibWeb/HTML/Parser/HTMLParser.h>
|
#include <LibWeb/HTML/Parser/HTMLParser.h>
|
||||||
#include <LibWeb/Layout/BlockContainer.h>
|
#include <LibWeb/Layout/BlockContainer.h>
|
||||||
|
#include <LibWeb/Layout/InitialContainingBlock.h>
|
||||||
#include <LibWeb/Layout/InlineNode.h>
|
#include <LibWeb/Layout/InlineNode.h>
|
||||||
#include <LibWeb/Layout/ListItemBox.h>
|
#include <LibWeb/Layout/ListItemBox.h>
|
||||||
#include <LibWeb/Layout/TableBox.h>
|
#include <LibWeb/Layout/TableBox.h>
|
||||||
|
@ -278,12 +279,14 @@ void Element::did_remove_attribute(FlyString const& name)
|
||||||
enum class RequiredInvalidation {
|
enum class RequiredInvalidation {
|
||||||
None,
|
None,
|
||||||
RepaintOnly,
|
RepaintOnly,
|
||||||
|
RebuildStackingContextTree,
|
||||||
Relayout,
|
Relayout,
|
||||||
};
|
};
|
||||||
|
|
||||||
static RequiredInvalidation compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style)
|
static RequiredInvalidation compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style)
|
||||||
{
|
{
|
||||||
bool requires_repaint = false;
|
bool requires_repaint = false;
|
||||||
|
bool requires_stacking_context_tree_rebuild = false;
|
||||||
for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) {
|
for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) {
|
||||||
auto property_id = static_cast<CSS::PropertyID>(i);
|
auto property_id = static_cast<CSS::PropertyID>(i);
|
||||||
auto const& old_value = old_style.properties()[i];
|
auto const& old_value = old_style.properties()[i];
|
||||||
|
@ -296,8 +299,12 @@ static RequiredInvalidation compute_required_invalidation(CSS::StyleProperties c
|
||||||
continue;
|
continue;
|
||||||
if (CSS::property_affects_layout(property_id))
|
if (CSS::property_affects_layout(property_id))
|
||||||
return RequiredInvalidation::Relayout;
|
return RequiredInvalidation::Relayout;
|
||||||
|
if (CSS::property_affects_stacking_context(property_id))
|
||||||
|
requires_stacking_context_tree_rebuild = true;
|
||||||
requires_repaint = true;
|
requires_repaint = true;
|
||||||
}
|
}
|
||||||
|
if (requires_stacking_context_tree_rebuild)
|
||||||
|
return RequiredInvalidation::RebuildStackingContextTree;
|
||||||
if (requires_repaint)
|
if (requires_repaint)
|
||||||
return RequiredInvalidation::RepaintOnly;
|
return RequiredInvalidation::RepaintOnly;
|
||||||
return RequiredInvalidation::None;
|
return RequiredInvalidation::None;
|
||||||
|
@ -325,6 +332,13 @@ Element::NeedsRelayout Element::recompute_style()
|
||||||
return NeedsRelayout::No;
|
return NeedsRelayout::No;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (required_invalidation == RequiredInvalidation::RebuildStackingContextTree && layout_node()) {
|
||||||
|
layout_node()->apply_style(*m_computed_css_values);
|
||||||
|
document().invalidate_stacking_context_tree();
|
||||||
|
layout_node()->set_needs_display();
|
||||||
|
return NeedsRelayout::No;
|
||||||
|
}
|
||||||
|
|
||||||
return NeedsRelayout::Yes;
|
return NeedsRelayout::Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue