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; }; }