mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-11 13:36:13 +03:00
LibWeb: Implement CloseWatcher API
This implements most of the CloseWatcher API from the html spec. AbortSignal support is unimplemented. Integration with dialogs and popovers is also unimplemented.
This commit is contained in:
parent
527632f416
commit
b216046234
Notes:
sideshowbarker
2024-07-17 07:31:31 +09:00
Author: https://github.com/lukewarlow Commit: https://github.com/LadybirdBrowser/ladybird/commit/b216046234 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/240 Reviewed-by: https://github.com/awesomekling
@ -42,6 +42,7 @@ static bool is_platform_object(Type const& type)
|
|||||||
"CanvasGradient"sv,
|
"CanvasGradient"sv,
|
||||||
"CanvasPattern"sv,
|
"CanvasPattern"sv,
|
||||||
"CanvasRenderingContext2D"sv,
|
"CanvasRenderingContext2D"sv,
|
||||||
|
"CloseWatcher"sv,
|
||||||
"CryptoKey"sv,
|
"CryptoKey"sv,
|
||||||
"Document"sv,
|
"Document"sv,
|
||||||
"DocumentType"sv,
|
"DocumentType"sv,
|
||||||
|
@ -22,6 +22,8 @@ source_set("HTML") {
|
|||||||
"CanvasPattern.cpp",
|
"CanvasPattern.cpp",
|
||||||
"CanvasRenderingContext2D.cpp",
|
"CanvasRenderingContext2D.cpp",
|
||||||
"CloseEvent.cpp",
|
"CloseEvent.cpp",
|
||||||
|
"CloseWatcher.cpp",
|
||||||
|
"CloseWatcherManager.cpp",
|
||||||
"DOMParser.cpp",
|
"DOMParser.cpp",
|
||||||
"DOMStringMap.cpp",
|
"DOMStringMap.cpp",
|
||||||
"DataTransfer.cpp",
|
"DataTransfer.cpp",
|
||||||
|
@ -114,6 +114,7 @@ standard_idl_files = [
|
|||||||
"//Userland/Libraries/LibWeb/HTML/CanvasPattern.idl",
|
"//Userland/Libraries/LibWeb/HTML/CanvasPattern.idl",
|
||||||
"//Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl",
|
"//Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl",
|
||||||
"//Userland/Libraries/LibWeb/HTML/CloseEvent.idl",
|
"//Userland/Libraries/LibWeb/HTML/CloseEvent.idl",
|
||||||
|
"//Userland/Libraries/LibWeb/HTML/CloseWatcher.idl",
|
||||||
"//Userland/Libraries/LibWeb/HTML/CustomElements/CustomElementRegistry.idl",
|
"//Userland/Libraries/LibWeb/HTML/CustomElements/CustomElementRegistry.idl",
|
||||||
"//Userland/Libraries/LibWeb/HTML/DataTransfer.idl",
|
"//Userland/Libraries/LibWeb/HTML/DataTransfer.idl",
|
||||||
"//Userland/Libraries/LibWeb/HTML/DOMParser.idl",
|
"//Userland/Libraries/LibWeb/HTML/DOMParser.idl",
|
||||||
|
@ -259,6 +259,8 @@ set(SOURCES
|
|||||||
HTML/CanvasPattern.cpp
|
HTML/CanvasPattern.cpp
|
||||||
HTML/CanvasRenderingContext2D.cpp
|
HTML/CanvasRenderingContext2D.cpp
|
||||||
HTML/CloseEvent.cpp
|
HTML/CloseEvent.cpp
|
||||||
|
HTML/CloseWatcher.cpp
|
||||||
|
HTML/CloseWatcherManager.cpp
|
||||||
HTML/CORSSettingAttribute.cpp
|
HTML/CORSSettingAttribute.cpp
|
||||||
HTML/CrossOrigin/AbstractOperations.cpp
|
HTML/CrossOrigin/AbstractOperations.cpp
|
||||||
HTML/CrossOrigin/Reporting.cpp
|
HTML/CrossOrigin/Reporting.cpp
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <LibWeb/DOM/EventDispatcher.h>
|
#include <LibWeb/DOM/EventDispatcher.h>
|
||||||
#include <LibWeb/DOM/EventTarget.h>
|
#include <LibWeb/DOM/EventTarget.h>
|
||||||
#include <LibWeb/DOM/IDLEventListener.h>
|
#include <LibWeb/DOM/IDLEventListener.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcherManager.h>
|
||||||
#include <LibWeb/HTML/ErrorEvent.h>
|
#include <LibWeb/HTML/ErrorEvent.h>
|
||||||
#include <LibWeb/HTML/EventHandler.h>
|
#include <LibWeb/HTML/EventHandler.h>
|
||||||
#include <LibWeb/HTML/EventNames.h>
|
#include <LibWeb/HTML/EventNames.h>
|
||||||
@ -796,7 +797,9 @@ bool EventTarget::dispatch_event(Event& event)
|
|||||||
// FIXME: 3. Extend windows with the active window of each of document's ancestor navigables.
|
// FIXME: 3. Extend windows with the active window of each of document's ancestor navigables.
|
||||||
// FIXME: 4. Extend windows with the active window of each of document's descendant navigables,
|
// FIXME: 4. Extend windows with the active window of each of document's descendant navigables,
|
||||||
// filtered to include only those navigables whose active document's origin is same origin with document's origin.
|
// filtered to include only those navigables whose active document's origin is same origin with document's origin.
|
||||||
// FIXME: 5. For each window in windows, set window's last activation timestamp to the current high resolution time.
|
// FIXME: 5. For each window in windows:
|
||||||
|
// FIXME: 5.1 Set window's last activation timestamp to the current high resolution time.
|
||||||
|
// FIXME: 5.2 Notify the close watcher manager about user activation given window.
|
||||||
|
|
||||||
// FIXME: This is ad-hoc, but works for now.
|
// FIXME: This is ad-hoc, but works for now.
|
||||||
if (is_activation_triggering_input_event()) {
|
if (is_activation_triggering_input_event()) {
|
||||||
@ -804,11 +807,15 @@ bool EventTarget::dispatch_event(Event& event)
|
|||||||
auto current_time = HighResolutionTime::relative_high_resolution_time(unsafe_shared_time, realm().global_object());
|
auto current_time = HighResolutionTime::relative_high_resolution_time(unsafe_shared_time, realm().global_object());
|
||||||
|
|
||||||
if (is<HTML::Window>(this)) {
|
if (is<HTML::Window>(this)) {
|
||||||
static_cast<HTML::Window*>(this)->set_last_activation_timestamp(current_time);
|
auto* window = static_cast<HTML::Window*>(this);
|
||||||
|
window->set_last_activation_timestamp(current_time);
|
||||||
|
window->close_watcher_manager()->notify_about_user_activation();
|
||||||
} else if (is<DOM::Element>(this)) {
|
} else if (is<DOM::Element>(this)) {
|
||||||
auto const* element = static_cast<DOM::Element const*>(this);
|
auto const* element = static_cast<DOM::Element const*>(this);
|
||||||
if (auto window = element->document().window())
|
if (auto window = element->document().window()) {
|
||||||
window->set_last_activation_timestamp(current_time);
|
window->set_last_activation_timestamp(current_time);
|
||||||
|
window->close_watcher_manager()->notify_about_user_activation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,6 +344,8 @@ class BrowsingContextGroup;
|
|||||||
class CanvasRenderingContext2D;
|
class CanvasRenderingContext2D;
|
||||||
class ClassicScript;
|
class ClassicScript;
|
||||||
class CloseEvent;
|
class CloseEvent;
|
||||||
|
class CloseWatcher;
|
||||||
|
class CloseWatcherManager;
|
||||||
class CustomElementDefinition;
|
class CustomElementDefinition;
|
||||||
class CustomElementRegistry;
|
class CustomElementRegistry;
|
||||||
class DecodedImageData;
|
class DecodedImageData;
|
||||||
|
167
Userland/Libraries/LibWeb/HTML/CloseWatcher.cpp
Normal file
167
Userland/Libraries/LibWeb/HTML/CloseWatcher.cpp
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the Ladybird developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/TypeCasts.h>
|
||||||
|
#include <LibWeb/Bindings/CloseWatcherPrototype.h>
|
||||||
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
|
#include <LibWeb/DOM/Document.h>
|
||||||
|
#include <LibWeb/DOM/EventDispatcher.h>
|
||||||
|
#include <LibWeb/DOM/IDLEventListener.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcher.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcherManager.h>
|
||||||
|
#include <LibWeb/HTML/EventHandler.h>
|
||||||
|
#include <LibWeb/HTML/Window.h>
|
||||||
|
|
||||||
|
namespace Web::HTML {
|
||||||
|
|
||||||
|
JS_DEFINE_ALLOCATOR(CloseWatcher);
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#establish-a-close-watcher
|
||||||
|
JS::NonnullGCPtr<CloseWatcher> CloseWatcher::establish(HTML::Window& window)
|
||||||
|
{
|
||||||
|
// 1. Assert: window's associated Document is fully active.
|
||||||
|
VERIFY(window.associated_document().is_fully_active());
|
||||||
|
|
||||||
|
// 2. Let closeWatcher be a new close watcher
|
||||||
|
auto close_watcher = window.heap().allocate<CloseWatcher>(window.realm(), window.realm());
|
||||||
|
|
||||||
|
// 3. Let manager be window's associated close watcher manager
|
||||||
|
auto manager = window.close_watcher_manager();
|
||||||
|
|
||||||
|
// 4 - 6. Moved to CloseWatcherManager::add
|
||||||
|
manager->add(close_watcher);
|
||||||
|
|
||||||
|
// 7. Return close_watcher.
|
||||||
|
return close_watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#dom-closewatcher
|
||||||
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<CloseWatcher>> CloseWatcher::construct_impl(JS::Realm& realm, CloseWatcherOptions const& options)
|
||||||
|
{
|
||||||
|
// 1. If this's relevant global object's associated Document is not fully active, then return an "InvalidStateError" DOMException.
|
||||||
|
// FIXME: Not in spec explicitly, but this should account for detached iframes too. See /close-watcher/frame-removal.html WPT.
|
||||||
|
auto& window = verify_cast<HTML::Window>(realm.global_object());
|
||||||
|
if (!window.associated_document().is_fully_active())
|
||||||
|
return WebIDL::InvalidStateError::create(realm, "The document is not fully active."_fly_string);
|
||||||
|
|
||||||
|
// 2. Let close_watcher be the result of establishing a close watcher
|
||||||
|
auto close_watcher = establish(window);
|
||||||
|
|
||||||
|
// 3. If options["signal"] exists, then:
|
||||||
|
if (options.signal) {
|
||||||
|
// FIXME: 3.1 If options["signal"]'s aborted, then destroy closeWatcher.
|
||||||
|
// FIXME: 3.2 Add the following steps to options["signal"]:
|
||||||
|
}
|
||||||
|
|
||||||
|
return close_watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWatcher::CloseWatcher(JS::Realm& realm)
|
||||||
|
: DOM::EventTarget(realm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close
|
||||||
|
bool CloseWatcher::request_close()
|
||||||
|
{
|
||||||
|
// 1. If closeWatcher is not active, then return.
|
||||||
|
if (!m_is_active)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 2. If closeWatcher's is running cancel action is true, then return true.
|
||||||
|
if (m_is_running_cancel_action)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 3. Let window be closeWatcher's window.
|
||||||
|
auto& window = verify_cast<HTML::Window>(realm().global_object());
|
||||||
|
|
||||||
|
// 4. If window's associated Document is not fully active, then return true.
|
||||||
|
if (!window.associated_document().is_fully_active())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 5. 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,
|
||||||
|
// 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();
|
||||||
|
// 6. Set closeWatcher's is running cancel action to true.
|
||||||
|
m_is_running_cancel_action = true;
|
||||||
|
// 7. Let shouldContinue be the result of running closeWatcher's cancel action given canPreventClose.
|
||||||
|
bool should_continue = dispatch_event(DOM::Event::create(realm(), HTML::EventNames::cancel, { .cancelable = can_prevent_close }));
|
||||||
|
// 8. Set closeWatcher's is running cancel action to false.
|
||||||
|
m_is_running_cancel_action = false;
|
||||||
|
// 9. If shouldContinue is false, then:
|
||||||
|
if (!should_continue) {
|
||||||
|
// 9.1 Assert: canPreventClose is true.
|
||||||
|
VERIFY(can_prevent_close);
|
||||||
|
// 9.2 Consume history-action user activation given window.
|
||||||
|
window.consume_history_action_user_activation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. Close closeWatcher.
|
||||||
|
close();
|
||||||
|
|
||||||
|
// 11. Return true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-close
|
||||||
|
void CloseWatcher::close()
|
||||||
|
{
|
||||||
|
// 1. If closeWatcher is not active, then return.
|
||||||
|
if (!m_is_active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 2. If closeWatcher's window's associated Document is not fully active, then return.
|
||||||
|
if (!verify_cast<HTML::Window>(realm().global_object()).associated_document().is_fully_active())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 3. Destroy closeWatcher.
|
||||||
|
destroy();
|
||||||
|
|
||||||
|
// 4. Run closeWatcher's close action.
|
||||||
|
dispatch_event(DOM::Event::create(realm(), HTML::EventNames::close));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-destroy
|
||||||
|
void CloseWatcher::destroy()
|
||||||
|
{
|
||||||
|
// 1. Let manager be closeWatcher's window's close watcher manager.
|
||||||
|
auto manager = verify_cast<HTML::Window>(realm().global_object()).close_watcher_manager();
|
||||||
|
|
||||||
|
// 2-3. Moved to CloseWatcherManager::remove
|
||||||
|
manager->remove(*this);
|
||||||
|
|
||||||
|
m_is_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseWatcher::initialize(JS::Realm& realm)
|
||||||
|
{
|
||||||
|
Base::initialize(realm);
|
||||||
|
WEB_SET_PROTOTYPE_FOR_INTERFACE(CloseWatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseWatcher::set_oncancel(WebIDL::CallbackType* event_handler)
|
||||||
|
{
|
||||||
|
set_event_handler_attribute(HTML::EventNames::cancel, event_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebIDL::CallbackType* CloseWatcher::oncancel()
|
||||||
|
{
|
||||||
|
return event_handler_attribute(HTML::EventNames::cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseWatcher::set_onclose(WebIDL::CallbackType* event_handler)
|
||||||
|
{
|
||||||
|
set_event_handler_attribute(HTML::EventNames::close, event_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebIDL::CallbackType* CloseWatcher::onclose()
|
||||||
|
{
|
||||||
|
return event_handler_attribute(HTML::EventNames::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
Userland/Libraries/LibWeb/HTML/CloseWatcher.h
Normal file
49
Userland/Libraries/LibWeb/HTML/CloseWatcher.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the Ladybird developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Forward.h>
|
||||||
|
#include <LibWeb/DOM/EventTarget.h>
|
||||||
|
|
||||||
|
namespace Web::HTML {
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#closewatcheroptions
|
||||||
|
struct CloseWatcherOptions {
|
||||||
|
JS::GCPtr<DOM::AbortSignal> signal;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#the-closewatcher-interface
|
||||||
|
class CloseWatcher final : public DOM::EventTarget {
|
||||||
|
WEB_PLATFORM_OBJECT(CloseWatcher, DOM::EventTarget);
|
||||||
|
JS_DECLARE_ALLOCATOR(CloseWatcher);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<CloseWatcher>> construct_impl(JS::Realm&, CloseWatcherOptions const& = {});
|
||||||
|
|
||||||
|
bool request_close();
|
||||||
|
void close();
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
virtual ~CloseWatcher() override = default;
|
||||||
|
|
||||||
|
void set_oncancel(WebIDL::CallbackType*);
|
||||||
|
WebIDL::CallbackType* oncancel();
|
||||||
|
|
||||||
|
void set_onclose(WebIDL::CallbackType*);
|
||||||
|
WebIDL::CallbackType* onclose();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CloseWatcher(JS::Realm&);
|
||||||
|
[[nodiscard]] static JS::NonnullGCPtr<CloseWatcher> establish(HTML::Window&);
|
||||||
|
|
||||||
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
|
||||||
|
bool m_is_running_cancel_action { false };
|
||||||
|
bool m_is_active { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
19
Userland/Libraries/LibWeb/HTML/CloseWatcher.idl
Normal file
19
Userland/Libraries/LibWeb/HTML/CloseWatcher.idl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#import <DOM/EventTarget.idl>
|
||||||
|
#import <DOM/EventHandler.idl>
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#closewatcher
|
||||||
|
[Exposed=Window]
|
||||||
|
interface CloseWatcher : EventTarget {
|
||||||
|
constructor(optional CloseWatcherOptions options = {});
|
||||||
|
|
||||||
|
undefined requestClose();
|
||||||
|
undefined close();
|
||||||
|
undefined destroy();
|
||||||
|
|
||||||
|
attribute EventHandler oncancel;
|
||||||
|
attribute EventHandler onclose;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary CloseWatcherOptions {
|
||||||
|
AbortSignal signal;
|
||||||
|
};
|
120
Userland/Libraries/LibWeb/HTML/CloseWatcherManager.cpp
Normal file
120
Userland/Libraries/LibWeb/HTML/CloseWatcherManager.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the Ladybird developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/TypeCasts.h>
|
||||||
|
#include <LibWeb/DOM/Document.h>
|
||||||
|
#include <LibWeb/DOM/EventDispatcher.h>
|
||||||
|
#include <LibWeb/DOM/IDLEventListener.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcher.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcherManager.h>
|
||||||
|
#include <LibWeb/HTML/Window.h>
|
||||||
|
|
||||||
|
namespace Web::HTML {
|
||||||
|
|
||||||
|
JS_DEFINE_ALLOCATOR(CloseWatcherManager);
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<CloseWatcherManager> CloseWatcherManager::create(JS::Realm& realm)
|
||||||
|
{
|
||||||
|
return realm.heap().allocate<CloseWatcherManager>(realm, realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWatcherManager::CloseWatcherManager(JS::Realm& realm)
|
||||||
|
: PlatformObject(realm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseWatcherManager::add(JS::NonnullGCPtr<CloseWatcher> close_watcher)
|
||||||
|
{
|
||||||
|
// If manager's groups's size is less than manager's allowed number of groups
|
||||||
|
if (m_groups.size() < m_allowed_number_of_groups) {
|
||||||
|
// then append « closeWatcher » to manager's groups.
|
||||||
|
JS::MarkedVector<JS::NonnullGCPtr<CloseWatcher>> new_group(realm().heap());
|
||||||
|
new_group.append(close_watcher);
|
||||||
|
m_groups.append(move(new_group));
|
||||||
|
} else {
|
||||||
|
// Assert: manager's groups's size is at least 1 in this branch, since manager's allowed number of groups is always at least 1.
|
||||||
|
VERIFY(!m_groups.is_empty());
|
||||||
|
// Append closeWatcher to manager's groups's last item.
|
||||||
|
m_groups.last().append(close_watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set manager's next user interaction allows a new group to true.
|
||||||
|
m_next_user_interaction_allows_a_new_group = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseWatcherManager::remove(CloseWatcher const& close_watcher)
|
||||||
|
{
|
||||||
|
// 2. For each group of manager's groups: remove closeWatcher from group
|
||||||
|
for (auto& group : m_groups) {
|
||||||
|
group.remove_first_matching([&close_watcher](JS::NonnullGCPtr<CloseWatcher>& entry) {
|
||||||
|
return entry.ptr() == &close_watcher;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 3. Remove any item from manager's group that is empty
|
||||||
|
m_groups.remove_all_matching([](auto& group) { return group.is_empty(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#process-close-watchers
|
||||||
|
bool CloseWatcherManager::process_close_watchers()
|
||||||
|
{
|
||||||
|
// 1. Let processedACloseWatcher be false.
|
||||||
|
bool processed_a_close_watcher = false;
|
||||||
|
// 2. If window's close watcher manager's groups is not empty:
|
||||||
|
if (!m_groups.is_empty()) {
|
||||||
|
// 2.1 Let group be the last item in window's close watcher manager's groups.
|
||||||
|
auto& group = m_groups.last();
|
||||||
|
// Ambiguous spec wording. We copy the groups to avoid modifying the original while iterating.
|
||||||
|
// See https://github.com/whatwg/html/issues/10240
|
||||||
|
JS::MarkedVector<JS::NonnullGCPtr<CloseWatcher>> group_copy(realm().heap());
|
||||||
|
group_copy.ensure_capacity(group.size());
|
||||||
|
for (auto& close_watcher : group) {
|
||||||
|
group_copy.append(close_watcher);
|
||||||
|
}
|
||||||
|
// 2.2 For each closeWatcher of group, in reverse order:
|
||||||
|
for (auto it = group_copy.rbegin(); it != group_copy.rend(); ++it) {
|
||||||
|
// 2.1.1 Set processedACloseWatcher to true.
|
||||||
|
processed_a_close_watcher = true;
|
||||||
|
// 2.1.2 Let shouldProceed be the result of requesting to close closeWatcher.
|
||||||
|
bool should_proceed = (*it)->request_close();
|
||||||
|
// 2.1.3 If shouldProceed is false, then break;
|
||||||
|
if (!should_proceed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3. If window's close watcher manager's allowed number of groups is greater than 1, decrement it by 1.
|
||||||
|
if (m_allowed_number_of_groups > 1)
|
||||||
|
m_allowed_number_of_groups--;
|
||||||
|
|
||||||
|
// 4. Return processedACloseWatcher.
|
||||||
|
return processed_a_close_watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#notify-the-close-watcher-manager-about-user-activation
|
||||||
|
void CloseWatcherManager::notify_about_user_activation()
|
||||||
|
{
|
||||||
|
// 1. Let manager be window's close watcher manager.
|
||||||
|
// 2. If manager's next user interaction allows a new group is true, then increment manager's allowed number of groups.
|
||||||
|
if (m_next_user_interaction_allows_a_new_group)
|
||||||
|
m_allowed_number_of_groups++;
|
||||||
|
// 3. Set manager's next user interaction allows a new group to false.
|
||||||
|
m_next_user_interaction_allows_a_new_group = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close
|
||||||
|
bool CloseWatcherManager::can_prevent_close()
|
||||||
|
{
|
||||||
|
// 5. 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...
|
||||||
|
return m_groups.size() < m_allowed_number_of_groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseWatcherManager::visit_edges(JS::Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
|
||||||
|
visitor.visit(m_groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
Userland/Libraries/LibWeb/HTML/CloseWatcherManager.h
Normal file
40
Userland/Libraries/LibWeb/HTML/CloseWatcherManager.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, the Ladybird developers.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Forward.h>
|
||||||
|
#include <LibWeb/DOM/EventTarget.h>
|
||||||
|
|
||||||
|
namespace Web::HTML {
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-manager
|
||||||
|
class CloseWatcherManager final : public Bindings::PlatformObject {
|
||||||
|
WEB_PLATFORM_OBJECT(CloseWatcherManager, Bindings::PlatformObject);
|
||||||
|
JS_DECLARE_ALLOCATOR(CloseWatcherManager);
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] static JS::NonnullGCPtr<CloseWatcherManager> create(JS::Realm&);
|
||||||
|
|
||||||
|
void add(JS::NonnullGCPtr<CloseWatcher>);
|
||||||
|
void remove(CloseWatcher const&);
|
||||||
|
|
||||||
|
bool process_close_watchers();
|
||||||
|
|
||||||
|
void notify_about_user_activation();
|
||||||
|
bool can_prevent_close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit CloseWatcherManager(JS::Realm&);
|
||||||
|
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
Vector<Vector<JS::NonnullGCPtr<CloseWatcher>>> m_groups;
|
||||||
|
uint32_t m_allowed_number_of_groups { 1 };
|
||||||
|
bool m_next_user_interaction_allows_a_new_group { true };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,7 @@
|
|||||||
#include <LibWeb/DOM/HTMLCollection.h>
|
#include <LibWeb/DOM/HTMLCollection.h>
|
||||||
#include <LibWeb/DOMURL/DOMURL.h>
|
#include <LibWeb/DOMURL/DOMURL.h>
|
||||||
#include <LibWeb/HTML/BrowsingContext.h>
|
#include <LibWeb/HTML/BrowsingContext.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcherManager.h>
|
||||||
#include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
|
#include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
|
||||||
#include <LibWeb/HTML/DocumentState.h>
|
#include <LibWeb/HTML/DocumentState.h>
|
||||||
#include <LibWeb/HTML/EventHandler.h>
|
#include <LibWeb/HTML/EventHandler.h>
|
||||||
@ -129,6 +130,7 @@ void Window::visit_edges(JS::Cell::Visitor& visitor)
|
|||||||
visitor.visit(m_pdf_viewer_mime_type_objects);
|
visitor.visit(m_pdf_viewer_mime_type_objects);
|
||||||
visitor.visit(m_count_queuing_strategy_size_function);
|
visitor.visit(m_count_queuing_strategy_size_function);
|
||||||
visitor.visit(m_byte_length_queuing_strategy_size_function);
|
visitor.visit(m_byte_length_queuing_strategy_size_function);
|
||||||
|
visitor.visit(m_close_watcher_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::finalize()
|
void Window::finalize()
|
||||||
@ -973,6 +975,16 @@ JS::NonnullGCPtr<Navigator> Window::navigator()
|
|||||||
return JS::NonnullGCPtr { *m_navigator };
|
return JS::NonnullGCPtr { *m_navigator };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-manager
|
||||||
|
JS::NonnullGCPtr<CloseWatcherManager> Window::close_watcher_manager()
|
||||||
|
{
|
||||||
|
auto& realm = this->realm();
|
||||||
|
|
||||||
|
if (!m_close_watcher_manager)
|
||||||
|
m_close_watcher_manager = heap().allocate<CloseWatcherManager>(realm, realm);
|
||||||
|
return JS::NonnullGCPtr { *m_close_watcher_manager };
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-alert
|
// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-alert
|
||||||
void Window::alert(String const& message)
|
void Window::alert(String const& message)
|
||||||
{
|
{
|
||||||
|
@ -162,6 +162,7 @@ public:
|
|||||||
WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> open(Optional<String> const& url, Optional<String> const& target, Optional<String> const& features);
|
WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> open(Optional<String> const& url, Optional<String> const& target, Optional<String> const& features);
|
||||||
|
|
||||||
[[nodiscard]] JS::NonnullGCPtr<Navigator> navigator();
|
[[nodiscard]] JS::NonnullGCPtr<Navigator> navigator();
|
||||||
|
[[nodiscard]] JS::NonnullGCPtr<CloseWatcherManager> close_watcher_manager();
|
||||||
|
|
||||||
void alert(String const& message = {});
|
void alert(String const& message = {});
|
||||||
bool confirm(Optional<String> const& message);
|
bool confirm(Optional<String> const& message);
|
||||||
@ -269,6 +270,7 @@ private:
|
|||||||
JS::GCPtr<CSS::Screen> m_screen;
|
JS::GCPtr<CSS::Screen> m_screen;
|
||||||
JS::GCPtr<Navigator> m_navigator;
|
JS::GCPtr<Navigator> m_navigator;
|
||||||
JS::GCPtr<Location> m_location;
|
JS::GCPtr<Location> m_location;
|
||||||
|
JS::GCPtr<CloseWatcherManager> m_close_watcher_manager;
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#window-navigation-api
|
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#window-navigation-api
|
||||||
JS::GCPtr<Navigation> m_navigation;
|
JS::GCPtr<Navigation> m_navigation;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <LibWeb/DOM/Range.h>
|
#include <LibWeb/DOM/Range.h>
|
||||||
#include <LibWeb/DOM/Text.h>
|
#include <LibWeb/DOM/Text.h>
|
||||||
#include <LibWeb/HTML/BrowsingContext.h>
|
#include <LibWeb/HTML/BrowsingContext.h>
|
||||||
|
#include <LibWeb/HTML/CloseWatcherManager.h>
|
||||||
#include <LibWeb/HTML/Focus.h>
|
#include <LibWeb/HTML/Focus.h>
|
||||||
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
||||||
#include <LibWeb/HTML/HTMLFormElement.h>
|
#include <LibWeb/HTML/HTMLFormElement.h>
|
||||||
@ -778,6 +779,9 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code
|
|||||||
return focus_next_element();
|
return focus_next_element();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key == UIEvents::KeyCode::Key_Escape)
|
||||||
|
return document->window()->close_watcher_manager()->process_close_watchers();
|
||||||
|
|
||||||
auto& realm = document->realm();
|
auto& realm = document->realm();
|
||||||
|
|
||||||
if (auto selection = document->get_selection()) {
|
if (auto selection = document->get_selection()) {
|
||||||
|
@ -98,6 +98,7 @@ libweb_js_bindings(HTML/CanvasGradient)
|
|||||||
libweb_js_bindings(HTML/CanvasPattern)
|
libweb_js_bindings(HTML/CanvasPattern)
|
||||||
libweb_js_bindings(HTML/CanvasRenderingContext2D)
|
libweb_js_bindings(HTML/CanvasRenderingContext2D)
|
||||||
libweb_js_bindings(HTML/CloseEvent)
|
libweb_js_bindings(HTML/CloseEvent)
|
||||||
|
libweb_js_bindings(HTML/CloseWatcher)
|
||||||
libweb_js_bindings(HTML/CustomElements/CustomElementRegistry)
|
libweb_js_bindings(HTML/CustomElements/CustomElementRegistry)
|
||||||
libweb_js_bindings(HTML/DOMParser)
|
libweb_js_bindings(HTML/DOMParser)
|
||||||
libweb_js_bindings(HTML/DOMStringMap)
|
libweb_js_bindings(HTML/DOMStringMap)
|
||||||
|
Loading…
Reference in New Issue
Block a user