2024-06-21 22:53:05 +01:00
/*
* Copyright ( c ) 2024 , the Ladybird developers .
2024-12-06 18:12:29 -04:00
* Copyright ( c ) 2024 , Felipe Muñoz Mazur < felipe . munoz . mazur @ protonmail . com >
2024-06-21 22:53:05 +01:00
*
* 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>
2024-10-15 12:39:47 +02:00
# include <LibWeb/HTML/HTMLIFrameElement.h>
2024-06-21 22:53:05 +01:00
# include <LibWeb/HTML/Window.h>
namespace Web : : HTML {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( CloseWatcher ) ;
2024-06-21 22:53:05 +01:00
// https://html.spec.whatwg.org/multipage/interaction.html#establish-a-close-watcher
2024-11-15 04:01:23 +13:00
GC : : Ref < CloseWatcher > CloseWatcher : : establish ( HTML : : Window & window )
2024-06-21 22:53:05 +01:00
{
// 1. Assert: window's associated Document is fully active.
VERIFY ( window . associated_document ( ) . is_fully_active ( ) ) ;
// 2. Let closeWatcher be a new close watcher
2024-11-14 05:50:17 +13:00
auto close_watcher = window . realm ( ) . create < CloseWatcher > ( window . realm ( ) ) ;
2024-06-21 22:53:05 +01:00
// 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
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < CloseWatcher > > CloseWatcher : : construct_impl ( JS : : Realm & realm , CloseWatcherOptions const & options )
2024-06-21 22:53:05 +01:00
{
2025-01-21 09:12:05 -05:00
auto & window = as < HTML : : Window > ( realm . global_object ( ) ) ;
2024-10-15 12:39:47 +02:00
// NOTE: Not in spec explicitly, but this should account for detached iframes too. See /close-watcher/frame-removal.html WPT.
auto navigable = window . navigable ( ) ;
if ( navigable & & navigable - > has_been_destroyed ( ) )
return WebIDL : : InvalidStateError : : create ( realm , " The iframe has been detached " _string ) ;
// 1. If this's relevant global object's associated Document is not fully active, then return an "InvalidStateError" DOMException.
2024-06-21 22:53:05 +01:00
if ( ! window . associated_document ( ) . is_fully_active ( ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : InvalidStateError : : create ( realm , " The document is not fully active. " _string ) ;
2024-06-21 22:53:05 +01:00
// 2. Let close_watcher be the result of establishing a close watcher
auto close_watcher = establish ( window ) ;
// 3. If options["signal"] exists, then:
2024-12-06 18:12:29 -04:00
if ( auto signal = options . signal ) {
// 3.1 If options["signal"]'s aborted, then destroy closeWatcher.
if ( signal - > aborted ( ) ) {
close_watcher - > destroy ( ) ;
}
// 3.2 Add the following steps to options["signal"]:
2025-04-10 09:04:01 -04:00
( void ) signal - > add_abort_algorithm ( [ close_watcher ] {
2024-12-06 18:12:29 -04:00
// 3.2.1 Destroy closeWatcher.
close_watcher - > destroy ( ) ;
} ) ;
2024-06-21 22:53:05 +01:00
}
return close_watcher ;
}
CloseWatcher : : CloseWatcher ( JS : : Realm & realm )
: DOM : : EventTarget ( realm )
{
}
2025-01-16 20:37:18 +00:00
// 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 ) ;
}
2024-06-21 22:53:05 +01:00
// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close
2025-01-16 20:37:18 +00:00
bool CloseWatcher : : request_close ( bool require_history_action_activation )
2024-06-21 22:53:05 +01:00
{
2025-01-24 12:44:17 +00:00
// 1. If closeWatcher is not active, then return true.
2024-06-21 22:53:05 +01:00
if ( ! m_is_active )
return true ;
2025-01-16 20:37:18 +00:00
// 2. If the result of running closeWatcher's get enabled state is false, then return true.
if ( ! get_enabled_state ( ) )
return true ;
2025-01-24 12:44:17 +00:00
// 3. If closeWatcher's is running cancel action is true, then return true.
2024-06-21 22:53:05 +01:00
if ( m_is_running_cancel_action )
return true ;
2025-01-24 12:44:17 +00:00
// 4. Let window be closeWatcher's window.
2025-01-21 09:12:05 -05:00
auto & window = as < HTML : : Window > ( realm ( ) . global_object ( ) ) ;
2024-06-21 22:53:05 +01:00
2025-01-24 12:44:17 +00:00
// 5. If window's associated Document is not fully active, then return true.
2024-06-21 22:53:05 +01:00
if ( ! window . associated_document ( ) . is_fully_active ( ) )
return true ;
2025-01-16 20:37:18 +00:00
// 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,
2024-06-21 22:53:05 +01:00
// and window has history-action activation; otherwise false.
auto manager = window . close_watcher_manager ( ) ;
2025-01-16 20:37:18 +00:00
bool can_prevent_close = ! require_history_action_activation | | ( manager - > can_prevent_close ( ) & & window . has_history_action_activation ( ) ) ;
2025-01-24 12:44:17 +00:00
// 7. Set closeWatcher's is running cancel action to true.
2024-06-21 22:53:05 +01:00
m_is_running_cancel_action = true ;
2025-01-24 12:44:17 +00:00
// 8. Let shouldContinue be the result of running closeWatcher's cancel action given canPreventClose.
2024-06-21 22:53:05 +01:00
bool should_continue = dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : cancel , { . cancelable = can_prevent_close } ) ) ;
2025-01-24 12:44:17 +00:00
// 9. Set closeWatcher's is running cancel action to false.
2024-06-21 22:53:05 +01:00
m_is_running_cancel_action = false ;
2025-01-24 12:44:17 +00:00
// 10. If shouldContinue is false, then:
2024-06-21 22:53:05 +01:00
if ( ! should_continue ) {
2025-01-24 12:44:17 +00:00
// 10.1 Assert: canPreventClose is true.
2024-06-21 22:53:05 +01:00
VERIFY ( can_prevent_close ) ;
2025-01-24 12:44:17 +00:00
// 10.2 Consume history-action user activation given window.
2024-06-21 22:53:05 +01:00
window . consume_history_action_user_activation ( ) ;
2025-01-24 12:44:17 +00:00
// 10.3 Return false.
2024-06-21 22:53:05 +01:00
return false ;
}
2025-01-24 12:44:17 +00:00
// 11. Close closeWatcher.
2024-06-21 22:53:05 +01:00
close ( ) ;
2025-01-24 12:44:17 +00:00
// 12. Return true.
2024-06-21 22:53:05 +01:00
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 ;
2025-01-16 20:37:18 +00:00
// 2. If the result of running closeWatcher's get enabled state is false, then return.
if ( ! get_enabled_state ( ) )
return ;
2025-01-24 12:44:17 +00:00
// 3. If closeWatcher's window's associated Document is not fully active, then return.
2025-01-21 09:12:05 -05:00
if ( ! as < HTML : : Window > ( realm ( ) . global_object ( ) ) . associated_document ( ) . is_fully_active ( ) )
2024-06-21 22:53:05 +01:00
return ;
2025-01-24 12:44:17 +00:00
// 4. Destroy closeWatcher.
2024-06-21 22:53:05 +01:00
destroy ( ) ;
2025-01-24 12:44:17 +00:00
// 5. Run closeWatcher's close action.
2024-06-21 22:53:05 +01:00
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.
2025-01-21 09:12:05 -05:00
auto manager = as < HTML : : Window > ( realm ( ) . global_object ( ) ) . close_watcher_manager ( ) ;
2024-06-21 22:53:05 +01:00
// 2-3. Moved to CloseWatcherManager::remove
manager - > remove ( * this ) ;
m_is_active = false ;
}
void CloseWatcher : : initialize ( JS : : Realm & realm )
{
WEB_SET_PROTOTYPE_FOR_INTERFACE ( CloseWatcher ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2024-06-21 22:53:05 +01:00
}
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 ) ;
}
}