mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-02 22:24:26 +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. (cherry picked from commit d86a6e1bec858a35935ba6839c154ba2482d33e6)
This commit is contained in:
parent
47bc8ecb0e
commit
27ffaad882
|
@ -23,6 +23,7 @@ class CloseWatcher final : public DOM::EventTarget {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<CloseWatcher>> construct_impl(JS::Realm&, CloseWatcherOptions const& = {});
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<CloseWatcher>> construct_impl(JS::Realm&, CloseWatcherOptions const& = {});
|
||||||
|
[[nodiscard]] static JS::NonnullGCPtr<CloseWatcher> establish(HTML::Window&);
|
||||||
|
|
||||||
bool request_close();
|
bool request_close();
|
||||||
void close();
|
void close();
|
||||||
|
@ -38,7 +39,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CloseWatcher(JS::Realm&);
|
CloseWatcher(JS::Realm&);
|
||||||
[[nodiscard]] static JS::NonnullGCPtr<CloseWatcher> establish(HTML::Window&);
|
|
||||||
|
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,13 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/NativeFunction.h>
|
||||||
#include <LibWeb/Bindings/HTMLDialogElementPrototype.h>
|
#include <LibWeb/Bindings/HTMLDialogElementPrototype.h>
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/Event.h>
|
#include <LibWeb/DOM/Event.h>
|
||||||
|
#include <LibWeb/DOM/IDLEventListener.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcher.h>
|
||||||
#include <LibWeb/HTML/Focus.h>
|
#include <LibWeb/HTML/Focus.h>
|
||||||
#include <LibWeb/HTML/HTMLDialogElement.h>
|
#include <LibWeb/HTML/HTMLDialogElement.h>
|
||||||
|
|
||||||
|
@ -28,13 +31,24 @@ void HTMLDialogElement::initialize(JS::Realm& realm)
|
||||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLDialogElement);
|
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)
|
void HTMLDialogElement::removed_from(Node* old_parent)
|
||||||
{
|
{
|
||||||
HTMLElement::removed_from(old_parent);
|
HTMLElement::removed_from(old_parent);
|
||||||
|
|
||||||
// FIXME: 1. If removedNode's close watcher is not null, then:
|
// 1. If removedNode's close watcher is not null, then:
|
||||||
// 1. Destroy removedNode's close watcher.
|
if (m_close_watcher) {
|
||||||
// 2. Set removedNode's close watcher to null.
|
// 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
|
// 2. If removedNode's node document's top layer contains removedNode, then remove an element from the top layer
|
||||||
// immediately given removedNode.
|
// immediately given removedNode.
|
||||||
|
@ -93,10 +107,31 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_modal()
|
||||||
if (!document().top_layer_elements().contains(*this))
|
if (!document().top_layer_elements().contains(*this))
|
||||||
document().add_an_element_to_the_top_layer(*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:
|
// 9. Set this's close watcher to the result of establishing a close watcher given this's relevant global object
|
||||||
// - cancelAction being to return the result of firing an event named cancel at this, with the cancelable
|
m_close_watcher = CloseWatcher::establish(*document().window());
|
||||||
// attribute initialized to true.
|
// - cancelAction given canPreventClose being to return the result of firing an event named cancel at this, with the cancelable attribute initialized to canPreventClose.
|
||||||
// - closeAction being to close the dialog given this and null.
|
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.
|
// 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);
|
dispatch_event(close_event);
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: 9. If subject's close watcher is not null, then:
|
// 9. If subject's close watcher is not null, then:
|
||||||
// 1. Destroy subject's close watcher.
|
if (m_close_watcher) {
|
||||||
// 2. Set subject's close watcher to null.
|
// 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
|
// https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-focusing-steps
|
||||||
|
|
|
@ -34,6 +34,7 @@ private:
|
||||||
HTMLDialogElement(DOM::Document&, DOM::QualifiedName);
|
HTMLDialogElement(DOM::Document&, DOM::QualifiedName);
|
||||||
|
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
void close_the_dialog(Optional<String> result);
|
void close_the_dialog(Optional<String> result);
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ private:
|
||||||
|
|
||||||
String m_return_value;
|
String m_return_value;
|
||||||
bool m_is_modal { false };
|
bool m_is_modal { false };
|
||||||
|
JS::GCPtr<CloseWatcher> m_close_watcher;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue