mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibWeb: Implement dialog element's close watcher
Dialog elements now correctly establish a close watcher when shown modally. This means modal dialogs now correctly close with an escape key press.
This commit is contained in:
parent
8969f2e34a
commit
d86a6e1bec
Notes:
sideshowbarker
2024-07-17 11:30:54 +09:00
Author: https://github.com/lukewarlow Commit: https://github.com/LadybirdBrowser/ladybird/commit/d86a6e1bec Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/250 Reviewed-by: https://github.com/awesomekling
3 changed files with 52 additions and 11 deletions
|
@ -23,6 +23,7 @@ class CloseWatcher final : public DOM::EventTarget {
|
|||
|
||||
public:
|
||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<CloseWatcher>> construct_impl(JS::Realm&, CloseWatcherOptions const& = {});
|
||||
[[nodiscard]] static JS::NonnullGCPtr<CloseWatcher> establish(HTML::Window&);
|
||||
|
||||
bool request_close();
|
||||
void close();
|
||||
|
@ -38,7 +39,6 @@ public:
|
|||
|
||||
private:
|
||||
CloseWatcher(JS::Realm&);
|
||||
[[nodiscard]] static JS::NonnullGCPtr<CloseWatcher> establish(HTML::Window&);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
#include <LibWeb/Bindings/HTMLDialogElementPrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Event.h>
|
||||
#include <LibWeb/DOM/IDLEventListener.h>
|
||||
#include <LibWeb/HTML/CloseWatcher.h>
|
||||
#include <LibWeb/HTML/Focus.h>
|
||||
#include <LibWeb/HTML/HTMLDialogElement.h>
|
||||
|
||||
|
@ -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<void> 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<DOM::Event>(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<WebIDL::CallbackType>(*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<WebIDL::CallbackType>(*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<String> 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
|
||||
|
|
|
@ -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<String> result);
|
||||
|
||||
|
@ -41,6 +42,7 @@ private:
|
|||
|
||||
String m_return_value;
|
||||
bool m_is_modal { false };
|
||||
JS::GCPtr<CloseWatcher> m_close_watcher;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue