diff --git a/Userland/Libraries/LibWeb/HTML/CloseWatcher.h b/Userland/Libraries/LibWeb/HTML/CloseWatcher.h
index 4037ad0289..105acc7265 100644
--- a/Userland/Libraries/LibWeb/HTML/CloseWatcher.h
+++ b/Userland/Libraries/LibWeb/HTML/CloseWatcher.h
@@ -23,6 +23,7 @@ class CloseWatcher final : public DOM::EventTarget {
public:
static WebIDL::ExceptionOr> construct_impl(JS::Realm&, CloseWatcherOptions const& = {});
+ [[nodiscard]] static JS::NonnullGCPtr establish(HTML::Window&);
bool request_close();
void close();
@@ -38,7 +39,6 @@ public:
private:
CloseWatcher(JS::Realm&);
- [[nodiscard]] static JS::NonnullGCPtr establish(HTML::Window&);
virtual void initialize(JS::Realm&) override;
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp
index 3ae8b6ee79..5101bfead9 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.cpp
@@ -4,10 +4,13 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include
#include
#include
#include
#include
+#include
+#include
#include
#include
@@ -28,13 +31,24 @@ void HTMLDialogElement::initialize(JS::Realm& realm)
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLDialogElement);
}
+void HTMLDialogElement::visit_edges(JS::Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+
+ visitor.visit(m_close_watcher);
+}
+
void HTMLDialogElement::removed_from(Node* old_parent)
{
HTMLElement::removed_from(old_parent);
- // FIXME: 1. If removedNode's close watcher is not null, then:
- // 1. Destroy removedNode's close watcher.
- // 2. Set removedNode's close watcher to null.
+ // 1. If removedNode's close watcher is not null, then:
+ if (m_close_watcher) {
+ // 1.1. Destroy removedNode's close watcher.
+ m_close_watcher->destroy();
+ // 1.2. Set removedNode's close watcher to null.
+ m_close_watcher = nullptr;
+ }
// 2. If removedNode's node document's top layer contains removedNode, then remove an element from the top layer
// immediately given removedNode.
@@ -93,10 +107,31 @@ WebIDL::ExceptionOr HTMLDialogElement::show_modal()
if (!document().top_layer_elements().contains(*this))
document().add_an_element_to_the_top_layer(*this);
- // FIXME: 9. Set this's close watcher to the result of establishing a close watcher given this's relevant global object, with:
- // - cancelAction being to return the result of firing an event named cancel at this, with the cancelable
- // attribute initialized to true.
- // - closeAction being to close the dialog given this and null.
+ // 9. Set this's close watcher to the result of establishing a close watcher given this's relevant global object
+ m_close_watcher = CloseWatcher::establish(*document().window());
+ // - cancelAction given canPreventClose being to return the result of firing an event named cancel at this, with the cancelable attribute initialized to canPreventClose.
+ auto cancel_callback_function = JS::NativeFunction::create(
+ realm(), [this](JS::VM& vm) {
+ auto& event = verify_cast(vm.argument(0).as_object());
+ bool can_prevent_close = event.cancelable();
+ auto should_continue = dispatch_event(DOM::Event::create(realm(), HTML::EventNames::cancel, { .cancelable = can_prevent_close }));
+ if (!should_continue)
+ event.prevent_default();
+ return JS::js_undefined();
+ },
+ 0, "", &realm());
+ auto cancel_callback = realm().heap().allocate_without_realm(*cancel_callback_function, Bindings::host_defined_environment_settings_object(realm()));
+ m_close_watcher->add_event_listener_without_options(HTML::EventNames::cancel, DOM::IDLEventListener::create(realm(), cancel_callback));
+ // - closeAction being to close the dialog given this and null.
+ auto close_callback_function = JS::NativeFunction::create(
+ realm(), [this](JS::VM&) {
+ close_the_dialog({});
+
+ return JS::js_undefined();
+ },
+ 0, "", &realm());
+ auto close_callback = realm().heap().allocate_without_realm(*close_callback_function, Bindings::host_defined_environment_settings_object(realm()));
+ m_close_watcher->add_event_listener_without_options(HTML::EventNames::close, DOM::IDLEventListener::create(realm(), close_callback));
// FIXME: 10. Set this's previously focused element to the focused element.
@@ -165,9 +200,13 @@ void HTMLDialogElement::close_the_dialog(Optional result)
dispatch_event(close_event);
});
- // FIXME: 9. If subject's close watcher is not null, then:
- // 1. Destroy subject's close watcher.
- // 2. Set subject's close watcher to null.
+ // 9. If subject's close watcher is not null, then:
+ if (m_close_watcher) {
+ // 9.1 Destroy subject's close watcher.
+ m_close_watcher->destroy();
+ // 9.2 Set subject's close watcher to null.
+ m_close_watcher = nullptr;
+ }
}
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-focusing-steps
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h
index 5d8446e707..8055c71284 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLDialogElement.h
@@ -34,6 +34,7 @@ private:
HTMLDialogElement(DOM::Document&, DOM::QualifiedName);
virtual void initialize(JS::Realm&) override;
+ virtual void visit_edges(Cell::Visitor&) override;
void close_the_dialog(Optional result);
@@ -41,6 +42,7 @@ private:
String m_return_value;
bool m_is_modal { false };
+ JS::GCPtr m_close_watcher;
};
}