diff --git a/Libraries/LibWeb/HTML/HTMLElement.cpp b/Libraries/LibWeb/HTML/HTMLElement.cpp
index 93207f6f4f1..52ac5b37481 100644
--- a/Libraries/LibWeb/HTML/HTMLElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLElement.cpp
@@ -619,6 +619,27 @@ void HTMLElement::attribute_changed(FlyString const& name, Optional cons
}
ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
+
+ [&]() {
+ // https://html.spec.whatwg.org/multipage/popover.html#the-popover-attribute:concept-element-attributes-change-ext
+ // https://whatpr.org/html/9457/popover.html#the-popover-attribute:concept-element-attributes-change-ext
+ // The following attribute change steps, given element, localName, oldValue, value, and namespace, are used for all HTML elements:
+
+ // 1. If namespace is not null, then return.
+ if (namespace_.has_value())
+ return;
+
+ // 2. If localName is not popover, then return.
+ if (name != HTML::AttributeNames::popover)
+ return;
+
+ // 3. If element's popover visibility state is in the showing state
+ // and oldValue and value are in different states,
+ // then run the hide popover algorithm given element, true, true, false, and true.
+ if (m_popover_visibility_state == PopoverVisibilityState::Showing
+ && popover_value_to_state(old_value) != popover_value_to_state(value))
+ MUST(hide_popover(FocusPreviousElement::Yes, FireEvents::Yes, ThrowExceptions::No, IgnoreDomState::Yes));
+ }();
}
WebIDL::ExceptionOr HTMLElement::cloned(Web::DOM::Node& copy, bool clone_children) const
@@ -957,13 +978,8 @@ WebIDL::ExceptionOr> HTMLElement::attach_internals()
return { internals };
}
-// https://html.spec.whatwg.org/multipage/popover.html#dom-popover
-Optional HTMLElement::popover() const
+Optional HTMLElement::popover_value_to_state(Optional value)
{
- // FIXME: This should probably be `Reflect` in the IDL.
- // The popover IDL attribute must reflect the popover attribute, limited to only known values.
- auto value = get_attribute(HTML::AttributeNames::popover);
-
if (!value.has_value())
return {};
@@ -975,6 +991,15 @@ Optional HTMLElement::popover() const
return "manual"_string;
}
+// https://html.spec.whatwg.org/multipage/popover.html#dom-popover
+Optional HTMLElement::popover() const
+{
+ // FIXME: This should probably be `Reflect` in the IDL.
+ // The popover IDL attribute must reflect the popover attribute, limited to only known values.
+ auto value = get_attribute(HTML::AttributeNames::popover);
+ return popover_value_to_state(value);
+}
+
// https://html.spec.whatwg.org/multipage/popover.html#dom-popover
WebIDL::ExceptionOr HTMLElement::set_popover(Optional value)
{
diff --git a/Libraries/LibWeb/HTML/HTMLElement.h b/Libraries/LibWeb/HTML/HTMLElement.h
index 05073a971e6..e373f84ac56 100644
--- a/Libraries/LibWeb/HTML/HTMLElement.h
+++ b/Libraries/LibWeb/HTML/HTMLElement.h
@@ -168,6 +168,8 @@ private:
void queue_a_popover_toggle_event_task(String old_state, String new_state);
+ static Optional popover_value_to_state(Optional value);
+
// https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
GC::Ptr m_attached_internals;
diff --git a/Tests/LibWeb/Text/expected/popover-crashes.txt b/Tests/LibWeb/Text/expected/popover-crashes.txt
index e358a96903c..52f2729b493 100644
--- a/Tests/LibWeb/Text/expected/popover-crashes.txt
+++ b/Tests/LibWeb/Text/expected/popover-crashes.txt
@@ -1,2 +1,3 @@
Didn't crash when showing recently hidden popover
Didn't crash when removing visible popover
+Didn't crash when removing popover with changed attribute
diff --git a/Tests/LibWeb/Text/input/popover-crashes.html b/Tests/LibWeb/Text/input/popover-crashes.html
index 4df1906348e..7e268cb13d6 100644
--- a/Tests/LibWeb/Text/input/popover-crashes.html
+++ b/Tests/LibWeb/Text/input/popover-crashes.html
@@ -1,6 +1,7 @@
+