2021-09-01 22:12:49 -03:00
/*
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
2024-09-08 17:10:52 -03:00
* Copyright ( c ) 2024 , Tim Ledbetter < tim . ledbetter @ ladybird . org >
2021-09-01 22:12:49 -03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2026-05-06 03:33:35 -03:00
# include <LibJS/Runtime/ExternalMemory.h>
2026-04-18 05:54:06 -03:00
# include <LibWeb/Bindings/AbortSignal.h>
2022-09-25 19:15:49 -03:00
# include <LibWeb/Bindings/Intrinsics.h>
2021-09-01 22:12:49 -03:00
# include <LibWeb/DOM/AbortSignal.h>
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/EventDispatcher.h>
2021-09-30 21:09:11 -03:00
# include <LibWeb/HTML/EventHandler.h>
2024-02-26 14:52:32 -03:00
# include <LibWeb/HTML/Window.h>
# include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
2021-09-01 22:12:49 -03:00
namespace Web : : DOM {
2024-11-14 12:01:23 -03:00
GC_DEFINE_ALLOCATOR ( AbortSignal ) ;
2023-11-19 15:47:52 -03:00
2024-11-14 12:01:23 -03:00
WebIDL : : ExceptionOr < GC : : Ref < AbortSignal > > AbortSignal : : construct_impl ( JS : : Realm & realm )
2021-09-01 22:12:49 -03:00
{
2024-11-13 13:50:17 -03:00
return realm . create < AbortSignal > ( realm ) ;
2022-09-25 19:15:49 -03:00
}
AbortSignal : : AbortSignal ( JS : : Realm & realm )
: EventTarget ( realm )
{
2023-01-10 08:28:20 -03:00
}
2023-08-07 03:41:28 -03:00
void AbortSignal : : initialize ( JS : : Realm & realm )
2023-01-10 08:28:20 -03:00
{
2024-03-16 09:13:08 -03:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( AbortSignal ) ;
2025-04-20 11:22:57 -03:00
Base : : initialize ( realm ) ;
2021-09-01 22:12:49 -03:00
}
// https://dom.spec.whatwg.org/#abortsignal-add
2025-04-10 10:04:01 -03:00
Optional < AbortSignal : : AbortAlgorithmID > AbortSignal : : add_abort_algorithm ( Function < void ( ) > abort_algorithm )
2021-09-01 22:12:49 -03:00
{
2021-12-10 17:05:12 -03:00
// 1. If signal is aborted, then return.
if ( aborted ( ) )
2025-04-10 10:04:01 -03:00
return { } ;
2021-09-01 22:12:49 -03:00
2021-12-10 17:05:12 -03:00
// 2. Append algorithm to signal’ s abort algorithms.
2025-04-10 10:04:01 -03:00
m_abort_algorithms . set ( + + m_next_abort_algorithm_id , GC : : create_function ( vm ( ) . heap ( ) , move ( abort_algorithm ) ) ) ;
return m_next_abort_algorithm_id ;
}
// https://dom.spec.whatwg.org/#abortsignal-remove
void AbortSignal : : remove_abort_algorithm ( AbortAlgorithmID id )
{
// To remove an algorithm algorithm from an AbortSignal signal, remove algorithm from signal’ s abort algorithms.
m_abort_algorithms . remove ( id ) ;
2021-09-01 22:12:49 -03:00
}
// https://dom.spec.whatwg.org/#abortsignal-signal-abort
2021-12-10 17:05:12 -03:00
void AbortSignal : : signal_abort ( JS : : Value reason )
2021-09-01 22:12:49 -03:00
{
2021-12-10 17:05:12 -03:00
// 1. If signal is aborted, then return.
if ( aborted ( ) )
2021-09-01 22:12:49 -03:00
return ;
2021-12-10 17:05:12 -03:00
// 2. Set signal’ s abort reason to reason if it is given; otherwise to a new "AbortError" DOMException.
if ( ! reason . is_undefined ( ) )
m_abort_reason = reason ;
else
2025-08-07 20:31:52 -03:00
m_abort_reason = WebIDL : : AbortError : : create ( realm ( ) , " Aborted without reason " _utf16 ) . ptr ( ) ;
2021-09-01 22:12:49 -03:00
2024-09-08 17:10:52 -03:00
// 3. Let dependentSignalsToAbort be a new list.
2024-11-14 12:01:23 -03:00
Vector < GC : : Root < AbortSignal > > dependent_signals_to_abort ;
2024-09-08 17:10:52 -03:00
// 4. For each dependentSignal of signal’ s dependent signals:
for ( auto const & dependent_signal : m_dependent_signals ) {
// 1. If dependentSignal is not aborted, then:
if ( ! dependent_signal - > aborted ( ) ) {
// 1. Set dependentSignal’ s abort reason to signal’ s abort reason.
dependent_signal - > set_reason ( m_abort_reason ) ;
// 2. Append dependentSignal to dependentSignalsToAbort.
dependent_signals_to_abort . append ( * dependent_signal ) ;
}
}
// https://dom.spec.whatwg.org/#run-the-abort-steps
auto run_the_abort_steps = [ ] ( auto & signal ) {
// 1. For each algorithm in signal’ s abort algorithms: run algorithm.
2025-04-10 10:04:01 -03:00
for ( auto const & algorithm : signal . m_abort_algorithms )
algorithm . value - > function ( ) ( ) ;
2024-09-08 17:10:52 -03:00
// 2. Empty signal’ s abort algorithms.
signal . m_abort_algorithms . clear ( ) ;
// 3. Fire an event named abort at signal.
auto abort_event = Event : : create ( signal . realm ( ) , HTML : : EventNames : : abort ) ;
abort_event - > set_is_trusted ( true ) ;
signal . dispatch_event ( abort_event ) ;
} ;
// 5. Run the abort steps for signal.
run_the_abort_steps ( * this ) ;
// 6. For each dependentSignal of dependentSignalsToAbort, run the abort steps for dependentSignal.
for ( auto const & dependent_signal : dependent_signals_to_abort )
run_the_abort_steps ( * dependent_signal ) ;
2021-09-01 22:12:49 -03:00
}
2022-09-24 12:02:41 -03:00
void AbortSignal : : set_onabort ( WebIDL : : CallbackType * event_handler )
2021-09-30 21:09:11 -03:00
{
2022-08-08 09:12:01 -03:00
set_event_handler_attribute ( HTML : : EventNames : : abort , event_handler ) ;
2021-09-30 21:09:11 -03:00
}
2022-09-24 12:02:41 -03:00
WebIDL : : CallbackType * AbortSignal : : onabort ( )
2021-09-30 21:09:11 -03:00
{
return event_handler_attribute ( HTML : : EventNames : : abort ) ;
}
2021-12-10 16:48:51 -03:00
// https://dom.spec.whatwg.org/#dom-abortsignal-throwifaborted
JS : : ThrowCompletionOr < void > AbortSignal : : throw_if_aborted ( ) const
{
// The throwIfAborted() method steps are to throw this’ s abort reason, if this is aborted.
if ( ! aborted ( ) )
return { } ;
return JS : : throw_completion ( m_abort_reason ) ;
}
2021-12-10 17:05:12 -03:00
void AbortSignal : : visit_edges ( JS : : Cell : : Visitor & visitor )
{
2022-08-28 08:42:07 -03:00
Base : : visit_edges ( visitor ) ;
2021-12-10 17:05:12 -03:00
visitor . visit ( m_abort_reason ) ;
2024-04-15 08:58:21 -03:00
visitor . visit ( m_abort_algorithms ) ;
visitor . visit ( m_source_signals ) ;
visitor . visit ( m_dependent_signals ) ;
2021-12-10 17:05:12 -03:00
}
2026-05-06 03:33:35 -03:00
size_t AbortSignal : : external_memory_size ( ) const
{
auto size = Base : : external_memory_size ( ) ;
size = JS : : saturating_add_external_memory_size ( size , JS : : hash_map_external_memory_size ( m_abort_algorithms ) ) ;
size = JS : : saturating_add_external_memory_size ( size , JS : : vector_external_memory_size ( m_source_signals ) ) ;
size = JS : : saturating_add_external_memory_size ( size , JS : : vector_external_memory_size ( m_dependent_signals ) ) ;
return size ;
}
2024-02-20 17:51:29 -03:00
// https://dom.spec.whatwg.org/#dom-abortsignal-abort
2026-05-20 15:58:46 -03:00
WebIDL : : ExceptionOr < GC : : Ref < AbortSignal > > AbortSignal : : abort ( JS : : VM & vm , Optional < JS : : Value > maybe_reason )
2024-02-20 17:51:29 -03:00
{
// 1. Let signal be a new AbortSignal object.
auto signal = TRY ( construct_impl ( * vm . current_realm ( ) ) ) ;
// 2. Set signal’ s abort reason to reason if it is given; otherwise to a new "AbortError" DOMException.
2026-05-20 15:58:46 -03:00
auto reason = maybe_reason . value_or ( JS : : js_undefined ( ) ) ;
2024-02-20 17:51:29 -03:00
if ( reason . is_undefined ( ) )
2025-08-07 20:31:52 -03:00
reason = WebIDL : : AbortError : : create ( * vm . current_realm ( ) , " Aborted without reason " _utf16 ) . ptr ( ) ;
2024-02-20 17:51:29 -03:00
signal - > set_reason ( reason ) ;
// 3. Return signal.
return signal ;
}
2024-02-26 14:52:32 -03:00
// https://dom.spec.whatwg.org/#dom-abortsignal-timeout
2024-11-14 12:01:23 -03:00
WebIDL : : ExceptionOr < GC : : Ref < AbortSignal > > AbortSignal : : timeout ( JS : : VM & vm , WebIDL : : UnsignedLongLong milliseconds )
2024-02-26 14:52:32 -03:00
{
auto & realm = * vm . current_realm ( ) ;
// 1. Let signal be a new AbortSignal object.
auto signal = TRY ( construct_impl ( realm ) ) ;
// 2. Let global be signal’ s relevant global object.
auto & global = HTML : : relevant_global_object ( signal ) ;
2025-02-02 12:42:57 -03:00
auto & window_or_worker = as < HTML : : WindowOrWorkerGlobalScopeMixin > ( global ) ;
2024-02-26 14:52:32 -03:00
// 3. Run steps after a timeout given global, "AbortSignal-timeout", milliseconds, and the following step:
2025-02-02 12:42:57 -03:00
window_or_worker . run_steps_after_a_timeout ( milliseconds , [ & realm , & global , signal ] ( ) {
2024-02-26 14:52:32 -03:00
// 1. Queue a global task on the timer task source given global to signal abort given signal and a new "TimeoutError" DOMException.
2024-11-14 12:01:23 -03:00
HTML : : queue_global_task ( HTML : : Task : : Source : : TimerTask , global , GC : : create_function ( realm . heap ( ) , [ & realm , signal ] ( ) mutable {
2025-08-07 20:31:52 -03:00
auto reason = WebIDL : : TimeoutError : : create ( realm , " Signal timed out " _utf16 ) ;
2024-04-03 07:50:39 -03:00
signal - > signal_abort ( reason ) ;
2024-04-16 17:04:01 -03:00
} ) ) ;
2024-02-26 14:52:32 -03:00
} ) ;
// 4. Return signal.
return signal ;
}
2024-03-10 15:50:30 -03:00
// https://dom.spec.whatwg.org/#dom-abortsignal-any
2026-05-20 15:58:46 -03:00
WebIDL : : ExceptionOr < GC : : Ref < AbortSignal > > AbortSignal : : any ( JS : : VM & vm , ReadonlySpan < GC : : Ref < AbortSignal > > signals )
2024-03-10 15:50:30 -03:00
{
// The static any(signals) method steps are to return the result of creating a dependent abort signal from signals using AbortSignal and the current realm.
2024-05-13 14:52:46 -03:00
return create_dependent_abort_signal ( * vm . current_realm ( ) , signals ) ;
2024-03-10 15:50:30 -03:00
}
// https://dom.spec.whatwg.org/#create-a-dependent-abort-signal
2026-05-20 15:58:46 -03:00
WebIDL : : ExceptionOr < GC : : Ref < AbortSignal > > AbortSignal : : create_dependent_abort_signal ( JS : : Realm & realm , ReadonlySpan < GC : : Ref < AbortSignal > > signals )
2024-03-10 15:50:30 -03:00
{
// 1. Let resultSignal be a new object implementing signalInterface using realm.
auto result_signal = TRY ( construct_impl ( realm ) ) ;
// 2. For each signal of signals: if signal is aborted, then set resultSignal’ s abort reason to signal’ s abort reason and return resultSignal.
for ( auto const & signal : signals ) {
if ( signal - > aborted ( ) ) {
result_signal - > set_reason ( signal - > reason ( ) ) ;
return result_signal ;
}
}
// 3. Set resultSignal’ s dependent to true.
result_signal - > set_dependent ( true ) ;
// 4. For each signal of signals:
for ( auto const & signal : signals ) {
// 1. If signal’ s dependent is false, then:
if ( ! signal - > dependent ( ) ) {
// 1. Append signal to resultSignal’ s source signals.
result_signal - > append_source_signal ( { signal } ) ;
// 2. Append resultSignal to signal’ s dependent signals.
signal - > append_dependent_signal ( result_signal ) ;
}
// 2. Otherwise, for each sourceSignal of signal’ s source signals:
else {
for ( auto const & source_signal : signal - > source_signals ( ) ) {
// 1. Assert: sourceSignal is not aborted and not dependent.
VERIFY ( source_signal ) ;
VERIFY ( ! source_signal - > aborted ( ) ) ;
VERIFY ( ! source_signal - > dependent ( ) ) ;
// 2. Append sourceSignal to resultSignal’ s source signals.
result_signal - > append_source_signal ( source_signal ) ;
// 3. Append resultSignal to sourceSignal’ s dependent signals.
source_signal - > append_dependent_signal ( result_signal ) ;
}
}
}
// 5. Return resultSignal
return result_signal ;
}
2021-09-01 22:12:49 -03:00
}