diff --git a/Libraries/LibWeb/HTML/CloseWatcher.cpp b/Libraries/LibWeb/HTML/CloseWatcher.cpp index 0a07312ab68..86edb65bbda 100644 --- a/Libraries/LibWeb/HTML/CloseWatcher.cpp +++ b/Libraries/LibWeb/HTML/CloseWatcher.cpp @@ -79,14 +79,23 @@ CloseWatcher::CloseWatcher(JS::Realm& realm) { } +// https://html.spec.whatwg.org/multipage/interaction.html#dom-closewatcher-requestclose +void CloseWatcher::request_close_for_bindings() +{ + // The requestClose() method steps are to request to close this's internal close watcher with false. + request_close(false); +} + // https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close -bool CloseWatcher::request_close() +bool CloseWatcher::request_close(bool require_history_action_activation) { // 1. If closeWatcher is not active, then return true. if (!m_is_active) return true; - // FIXME: 2. If the result of running closeWatcher's get enabled state is false, then return true. + // 2. If the result of running closeWatcher's get enabled state is false, then return true. + if (!get_enabled_state()) + return true; // 3. If closeWatcher's is running cancel action is true, then return true. if (m_is_running_cancel_action) @@ -99,10 +108,10 @@ bool CloseWatcher::request_close() if (!window.associated_document().is_fully_active()) return true; - // 6. Let canPreventClose be true if window's close watcher manager's groups's size is less than window's close watcher manager's allowed number of groups, + // 6. Let canPreventClose be true if requireHistoryActionActivation is false, or if window's close watcher manager's groups's size is less than window's close watcher manager's allowed number of groups, // and window has history-action activation; otherwise false. auto manager = window.close_watcher_manager(); - bool can_prevent_close = manager->can_prevent_close() && window.has_history_action_activation(); + bool can_prevent_close = !require_history_action_activation || (manager->can_prevent_close() && window.has_history_action_activation()); // 7. Set closeWatcher's is running cancel action to true. m_is_running_cancel_action = true; // 8. Let shouldContinue be the result of running closeWatcher's cancel action given canPreventClose. @@ -133,7 +142,9 @@ void CloseWatcher::close() if (!m_is_active) return; - // FIXME: 2. If the result of running closeWatcher's get enabled state is false, then return. + // 2. If the result of running closeWatcher's get enabled state is false, then return. + if (!get_enabled_state()) + return; // 3. If closeWatcher's window's associated Document is not fully active, then return. if (!as(realm().global_object()).associated_document().is_fully_active()) diff --git a/Libraries/LibWeb/HTML/CloseWatcher.h b/Libraries/LibWeb/HTML/CloseWatcher.h index 6a83d734c5a..9b68dc665c6 100644 --- a/Libraries/LibWeb/HTML/CloseWatcher.h +++ b/Libraries/LibWeb/HTML/CloseWatcher.h @@ -25,10 +25,15 @@ public: static WebIDL::ExceptionOr> construct_impl(JS::Realm&, CloseWatcherOptions const& = {}); [[nodiscard]] static GC::Ref establish(HTML::Window&); - bool request_close(); + void request_close_for_bindings(); void close(); void destroy(); + bool request_close(bool require_history_action_activation); + + bool get_enabled_state() const { return m_is_enabled; } + void set_enabled(bool enabled) { m_is_enabled = enabled; } + virtual ~CloseWatcher() override = default; void set_oncancel(WebIDL::CallbackType*); @@ -44,6 +49,7 @@ private: bool m_is_running_cancel_action { false }; bool m_is_active { true }; + bool m_is_enabled { true }; }; } diff --git a/Libraries/LibWeb/HTML/CloseWatcher.idl b/Libraries/LibWeb/HTML/CloseWatcher.idl index f09de528df3..b95d0763747 100644 --- a/Libraries/LibWeb/HTML/CloseWatcher.idl +++ b/Libraries/LibWeb/HTML/CloseWatcher.idl @@ -6,7 +6,7 @@ interface CloseWatcher : EventTarget { constructor(optional CloseWatcherOptions options = {}); - undefined requestClose(); + [ImplementedAs=request_close_for_bindings] undefined requestClose(); undefined close(); undefined destroy(); diff --git a/Libraries/LibWeb/HTML/CloseWatcherManager.cpp b/Libraries/LibWeb/HTML/CloseWatcherManager.cpp index 8ffef5b0d8e..50d0fc6456e 100644 --- a/Libraries/LibWeb/HTML/CloseWatcherManager.cpp +++ b/Libraries/LibWeb/HTML/CloseWatcherManager.cpp @@ -75,10 +75,11 @@ bool CloseWatcherManager::process_close_watchers() } // 2.2 For each closeWatcher of group, in reverse order: for (auto it = group_copy.rbegin(); it != group_copy.rend(); ++it) { - // FIXME: 2.2.1 If the result of running closeWatcher's get enabled state is true, set processedACloseWatcher to true. - processed_a_close_watcher = true; + // 2.2.1 If the result of running closeWatcher's get enabled state is true, set processedACloseWatcher to true. + if ((*it)->get_enabled_state()) + processed_a_close_watcher = true; // 2.2.2 Let shouldProceed be the result of requesting to close closeWatcher with true. - bool should_proceed = (*it)->request_close(); + bool should_proceed = (*it)->request_close(true); // 2.2.3 If shouldProceed is false, then break; if (!should_proceed) break; diff --git a/Libraries/LibWeb/HTML/HTMLDialogElement.cpp b/Libraries/LibWeb/HTML/HTMLDialogElement.cpp index 4b9fa0710fd..ee8f1e30f7e 100644 --- a/Libraries/LibWeb/HTML/HTMLDialogElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLDialogElement.cpp @@ -132,7 +132,8 @@ WebIDL::ExceptionOr HTMLDialogElement::show() // FIXME: 7. Assert: this's node document's open dialogs list does not contain this. // FIXME: 8. Add this to this's node document's open dialogs list. - // FIXME: 9. Set the dialog close watcher with this. + // 9. Set the dialog close watcher with this. + set_close_watcher(); // FIXME: 10. Set this's previously focused element to the focused element. // FIXME: 11. Let document be this's node document. // FIXME: 12. Let hideUntil be the result of running topmost popover ancestor given this, document's showing hint popover list, null, and false. @@ -235,6 +236,29 @@ void HTMLDialogElement::close(Optional return_value) close_the_dialog(move(return_value)); } +// https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-requestclose +void HTMLDialogElement::request_close(Optional return_value) +{ + // 1. If this does not have an open attribute, then return. + if (!has_attribute(AttributeNames::open)) + return; + // 2. Assert: this's close watcher is not null. + VERIFY(m_close_watcher); + // 3. Set dialog's enable close watcher for requestClose() to true. + // ADHOC: Implemented slightly differently to the spec, as the spec is unnecessarily complex. + m_close_watcher->set_enabled(true); + // 4. If returnValue is not given, then set it to null. + // 5. Set this's request close return value to returnValue. + m_request_close_return_value = return_value; + // 6. Request to close dialog's close watcher with false. + m_close_watcher->request_close(false); + // 7. Set dialog's enable close watcher for requestClose() to false. + // ADHOC: Implemented slightly differently to the spec, as the spec is unnecessarily complex. + // FIXME: This should be set based on dialog closedby state, when implemented. + if (m_close_watcher) + m_close_watcher->set_enabled(m_is_modal); +} + // https://html.spec.whatwg.org/multipage/interactive-elements.html#dom-dialog-returnvalue String HTMLDialogElement::return_value() const { @@ -286,7 +310,8 @@ void HTMLDialogElement::close_the_dialog(Optional result) if (result.has_value()) set_return_value(result.release_value()); - // FIXME: 11. Set the request close return value to null. + // 11. Set the request close return value to null. + m_request_close_return_value = {}; // FIXME: 12. If subject's previously focused element is not null, then: // 1. Let element be subject's previously focused element. @@ -327,17 +352,20 @@ void HTMLDialogElement::set_close_watcher() 0, "", &realm()); auto cancel_callback = realm().heap().allocate(*cancel_callback_function, 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 dialog and FIXME: dialog's request close return value. + // - closeAction being to close the dialog given dialog and dialog's request close return value. auto close_callback_function = JS::NativeFunction::create( realm(), [this](JS::VM&) { - close_the_dialog({}); + close_the_dialog(m_request_close_return_value); return JS::js_undefined(); }, 0, "", &realm()); auto close_callback = realm().heap().allocate(*close_callback_function, realm()); m_close_watcher->add_event_listener_without_options(HTML::EventNames::close, DOM::IDLEventListener::create(realm(), close_callback)); - // FIXME: - getEnabledState being to return true if dialog's enable close watcher for requestClose() is true or dialog's computed closed-by state is not None; otherwise false. + // - getEnabledState being to return true if dialog's enable close watcher for requestClose() is true or dialog's computed closed-by state is not None; otherwise false. + // ADHOC: Implemented slightly differently to the spec, as the spec is unnecessarily complex. + // FIXME: This should be set based on dialog closedby state, when implemented. + m_close_watcher->set_enabled(m_is_modal); } // https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-focusing-steps diff --git a/Libraries/LibWeb/HTML/HTMLDialogElement.h b/Libraries/LibWeb/HTML/HTMLDialogElement.h index f908ebc9dc4..de4e3f54aff 100644 --- a/Libraries/LibWeb/HTML/HTMLDialogElement.h +++ b/Libraries/LibWeb/HTML/HTMLDialogElement.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -28,6 +29,7 @@ public: WebIDL::ExceptionOr show(); WebIDL::ExceptionOr show_modal(); void close(Optional return_value); + void request_close(Optional return_value); // https://www.w3.org/TR/html-aria/#el-dialog virtual Optional default_role() const override { return ARIA::Role::dialog; } @@ -50,6 +52,7 @@ private: String m_return_value; bool m_is_modal { false }; + Optional m_request_close_return_value; GC::Ptr m_close_watcher; // https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-toggle-task-tracker diff --git a/Libraries/LibWeb/HTML/HTMLDialogElement.idl b/Libraries/LibWeb/HTML/HTMLDialogElement.idl index 6b8c7f67ee9..751c498997c 100644 --- a/Libraries/LibWeb/HTML/HTMLDialogElement.idl +++ b/Libraries/LibWeb/HTML/HTMLDialogElement.idl @@ -12,6 +12,6 @@ interface HTMLDialogElement : HTMLElement { [CEReactions] undefined show(); [CEReactions] undefined showModal(); [CEReactions] undefined close(optional DOMString returnValue); - [FIXME, CEReactions] undefined requestClose(optional DOMString returnValue); + [CEReactions] undefined requestClose(optional DOMString returnValue); }; diff --git a/Libraries/LibWeb/HTML/HTMLElement.cpp b/Libraries/LibWeb/HTML/HTMLElement.cpp index 52ac5b37481..fbdfc60e459 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -1177,7 +1177,8 @@ WebIDL::ExceptionOr HTMLElement::show_popover(ThrowExceptions throw_except 0, "", &realm()); auto close_callback = realm().heap().allocate(*close_callback_function, realm()); m_popover_close_watcher->add_event_listener_without_options(HTML::EventNames::close, DOM::IDLEventListener::create(realm(), close_callback)); - // FIXME: - getEnabledState being to return true. + // - getEnabledState being to return true. + m_popover_close_watcher->set_enabled(true); } // FIXME: 19. Set element's previously focused element to null. // FIXME: 20. Let originallyFocusedElement be document's focused area of the document's DOM anchor.