mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
LibWeb: Invalidate layout if pseudo-element style changes
Pseudo-elements' style is only computed while building the layout tree. This meant that previously, they would not have their style recomputed in some cases. (Such as when :hover is applied to an ancestor.) Now, when recomputing an element's style, we also return a full invalidation if one or more pseudo-elements would exist either before or after style recomputation. This heuristic produces some false positives, but no false negatives. Because pseudo-elements' style is computed during layout building, any computation done here is then thrown away. So this approach minimises the amount of wasted style computation. Plus it's simple, until we have data on what approach would be faster. This fixes the Acid2 nose becoming blue when the .nose div is hovered.
This commit is contained in:
parent
f23f0721bd
commit
7daf5cdaff
Notes:
github-actions[bot]
2024-07-30 07:46:33 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/7daf5cdaff0 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/888
4 changed files with 68 additions and 1 deletions
|
@ -0,0 +1,3 @@
|
|||
Hi Not hovering: 16
|
||||
Hovering: 78
|
||||
Not hovering: 16
|
|
@ -0,0 +1,33 @@
|
|||
<!doctype html>
|
||||
<style>
|
||||
.outer {
|
||||
height: 100px;
|
||||
}
|
||||
.inner {
|
||||
display: inline-block;
|
||||
}
|
||||
.inner::before {
|
||||
content: "Hi";
|
||||
background-color: red;
|
||||
}
|
||||
.outer:hover .inner::before {
|
||||
content: "Long text";
|
||||
background-color: lime;
|
||||
}
|
||||
</style>
|
||||
<script src="../include.js"></script>
|
||||
<div class="outer"><div class="inner"></div></div>
|
||||
<script>
|
||||
test(() => {
|
||||
const inner = document.querySelector('.inner');
|
||||
println('Not hovering: ' + inner.clientWidth);
|
||||
|
||||
// Move mouse over .outer
|
||||
internals.movePointerTo(80, 80);
|
||||
println('Hovering: ' + inner.clientWidth);
|
||||
|
||||
// Move mouse away again
|
||||
internals.movePointerTo(200, 200);
|
||||
println('Not hovering: ' + inner.clientWidth);
|
||||
});
|
||||
</script>
|
|
@ -25,6 +25,7 @@ struct RequiredInvalidationAfterStyleChange {
|
|||
}
|
||||
|
||||
[[nodiscard]] bool is_none() const { return !repaint && !rebuild_stacking_context_tree && !relayout && !rebuild_layout_tree; }
|
||||
[[nodiscard]] bool is_full() const { return repaint && rebuild_stacking_context_tree && relayout && rebuild_layout_tree; }
|
||||
static RequiredInvalidationAfterStyleChange full() { return { true, true, true, true }; }
|
||||
};
|
||||
|
||||
|
|
|
@ -534,7 +534,8 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style()
|
|||
set_needs_style_update(false);
|
||||
VERIFY(parent());
|
||||
|
||||
auto new_computed_css_values = document().style_computer().compute_style(*this);
|
||||
auto& style_computer = document().style_computer();
|
||||
auto new_computed_css_values = style_computer.compute_style(*this);
|
||||
|
||||
// Tables must not inherit -libweb-* values for text-align.
|
||||
// FIXME: Find the spec for this.
|
||||
|
@ -550,6 +551,35 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style()
|
|||
else
|
||||
invalidation = CSS::RequiredInvalidationAfterStyleChange::full();
|
||||
|
||||
// Any document change that can cause this element's style to change, could also affect its pseudo-elements.
|
||||
// So determine if any pseudo-elements currently exist, or should now exist, and if so, invalidate everything.
|
||||
// (If we're already invalidating everything, we don't need to do further checks for this.)
|
||||
if (!invalidation.is_full()) {
|
||||
bool pseudo_elements_dirty = false;
|
||||
|
||||
if (m_pseudo_element_data) {
|
||||
for (auto& pseudo_element : *m_pseudo_element_data) {
|
||||
if (pseudo_element.layout_node) {
|
||||
pseudo_elements_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pseudo_elements_dirty) {
|
||||
for (auto i = 0; i < to_underlying(CSS::Selector::PseudoElement::Type::KnownPseudoElementCount); i++) {
|
||||
auto style = style_computer.compute_pseudo_element_style_if_needed(*this, static_cast<CSS::Selector::PseudoElement::Type>(i));
|
||||
if (style) {
|
||||
pseudo_elements_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pseudo_elements_dirty)
|
||||
invalidation = CSS::RequiredInvalidationAfterStyleChange::full();
|
||||
}
|
||||
|
||||
if (invalidation.is_none())
|
||||
return invalidation;
|
||||
|
||||
|
|
Loading…
Reference in a new issue