diff --git a/Tests/LibWeb/Text/expected/css/update-pseudo-elements-on-hover.txt b/Tests/LibWeb/Text/expected/css/update-pseudo-elements-on-hover.txt
new file mode 100644
index 00000000000..4ed1a777f64
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/css/update-pseudo-elements-on-hover.txt
@@ -0,0 +1,3 @@
+Hi Not hovering: 16
+Hovering: 78
+Not hovering: 16
diff --git a/Tests/LibWeb/Text/input/css/update-pseudo-elements-on-hover.html b/Tests/LibWeb/Text/input/css/update-pseudo-elements-on-hover.html
new file mode 100644
index 00000000000..ddebf3f40bb
--- /dev/null
+++ b/Tests/LibWeb/Text/input/css/update-pseudo-elements-on-hover.html
@@ -0,0 +1,33 @@
+
+
+
+
+
diff --git a/Userland/Libraries/LibWeb/CSS/StyleInvalidation.h b/Userland/Libraries/LibWeb/CSS/StyleInvalidation.h
index c116781b875..4fa23d5fb9c 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleInvalidation.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleInvalidation.h
@@ -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 }; }
};
diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp
index 5816582d54d..9a21d30b64a 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Element.cpp
@@ -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(i));
+ if (style) {
+ pseudo_elements_dirty = true;
+ break;
+ }
+ }
+ }
+
+ if (pseudo_elements_dirty)
+ invalidation = CSS::RequiredInvalidationAfterStyleChange::full();
+ }
+
if (invalidation.is_none())
return invalidation;