2021-09-02 02:12:49 +01:00
/*
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
2024-03-10 18:50:30 +00:00
* Copyright ( c ) 2024 , Tim Ledbetter < timledbetter @ gmail . com >
2021-09-02 02:12:49 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-09-25 16:15:49 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2021-09-02 02:12:49 +01:00
# include <LibWeb/DOM/AbortSignal.h>
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/EventDispatcher.h>
2021-10-01 01:09:11 +01:00
# include <LibWeb/HTML/EventHandler.h>
2024-02-26 17:52:32 +00:00
# include <LibWeb/HTML/Window.h>
# include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
2021-09-02 02:12:49 +01:00
namespace Web : : DOM {
2023-11-19 19:47:52 +01:00
JS_DEFINE_ALLOCATOR ( AbortSignal ) ;
2023-02-14 21:02:46 +01:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < AbortSignal > > AbortSignal : : construct_impl ( JS : : Realm & realm )
2021-09-02 02:12:49 +01:00
{
2023-08-13 13:05:26 +02:00
return realm . heap ( ) . allocate < AbortSignal > ( realm , realm ) ;
2022-09-25 16:15:49 -06:00
}
AbortSignal : : AbortSignal ( JS : : Realm & realm )
: EventTarget ( realm )
{
2023-01-10 06:28:20 -05:00
}
2023-08-07 08:41:28 +02:00
void AbortSignal : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( AbortSignal ) ;
2021-09-02 02:12:49 +01:00
}
// https://dom.spec.whatwg.org/#abortsignal-add
2023-09-25 18:45:20 +02:00
void AbortSignal : : add_abort_algorithm ( Function < void ( ) > abort_algorithm )
2021-09-02 02:12:49 +01:00
{
2021-12-10 20:05:12 +00:00
// 1. If signal is aborted, then return.
if ( aborted ( ) )
2021-09-02 02:12:49 +01:00
return ;
2021-12-10 20:05:12 +00:00
// 2. Append algorithm to signal’ s abort algorithms.
2023-09-25 18:45:20 +02:00
m_abort_algorithms . append ( JS : : create_heap_function ( vm ( ) . heap ( ) , move ( abort_algorithm ) ) ) ;
2021-09-02 02:12:49 +01:00
}
// https://dom.spec.whatwg.org/#abortsignal-signal-abort
2021-12-10 20:05:12 +00:00
void AbortSignal : : signal_abort ( JS : : Value reason )
2021-09-02 02:12:49 +01:00
{
2021-12-10 20:05:12 +00:00
// 1. If signal is aborted, then return.
if ( aborted ( ) )
2021-09-02 02:12:49 +01:00
return ;
2021-12-10 20:05:12 +00: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
2023-09-06 16:03:01 +12:00
m_abort_reason = WebIDL : : AbortError : : create ( realm ( ) , " Aborted without reason " _fly_string ) . ptr ( ) ;
2021-09-02 02:12:49 +01:00
2021-12-10 20:05:12 +00:00
// 3. For each algorithm in signal’ s abort algorithms: run algorithm.
2021-09-02 02:12:49 +01:00
for ( auto & algorithm : m_abort_algorithms )
2023-09-25 18:45:20 +02:00
algorithm - > function ( ) ( ) ;
2021-09-02 02:12:49 +01:00
2021-12-10 20:05:12 +00:00
// 4. Empty signal’ s abort algorithms.
2021-09-02 02:12:49 +01:00
m_abort_algorithms . clear ( ) ;
2021-12-10 20:05:12 +00:00
// 5. Fire an event named abort at signal.
2024-03-10 19:33:31 +00:00
auto abort_event = Event : : create ( realm ( ) , HTML : : EventNames : : abort ) ;
abort_event - > set_is_trusted ( true ) ;
dispatch_event ( abort_event ) ;
2024-03-10 18:50:30 +00:00
// 6. For each dependentSignal of signal’ s dependent signals, signal abort on dependentSignal with signal’ s abort reason.
for ( auto const & dependent_signal : m_dependent_signals )
dependent_signal - > signal_abort ( reason ) ;
2021-09-02 02:12:49 +01:00
}
2022-09-24 16:02:41 +01:00
void AbortSignal : : set_onabort ( WebIDL : : CallbackType * event_handler )
2021-10-01 01:09:11 +01:00
{
2022-08-08 14:12:01 +02:00
set_event_handler_attribute ( HTML : : EventNames : : abort , event_handler ) ;
2021-10-01 01:09:11 +01:00
}
2022-09-24 16:02:41 +01:00
WebIDL : : CallbackType * AbortSignal : : onabort ( )
2021-10-01 01:09:11 +01:00
{
return event_handler_attribute ( HTML : : EventNames : : abort ) ;
}
2021-12-10 19:48:51 +00: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 20:05:12 +00:00
void AbortSignal : : visit_edges ( JS : : Cell : : Visitor & visitor )
{
2022-08-28 13:42:07 +02:00
Base : : visit_edges ( visitor ) ;
2021-12-10 20:05:12 +00:00
visitor . visit ( m_abort_reason ) ;
2023-09-25 18:45:20 +02:00
for ( auto & algorithm : m_abort_algorithms )
visitor . visit ( algorithm ) ;
2024-03-10 18:50:30 +00:00
for ( auto & source_signal : m_source_signals )
visitor . visit ( source_signal ) ;
for ( auto & dependent_signal : m_dependent_signals )
visitor . visit ( dependent_signal ) ;
2021-12-10 20:05:12 +00:00
}
2024-02-20 20:51:29 +00:00
// https://dom.spec.whatwg.org/#dom-abortsignal-abort
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < AbortSignal > > AbortSignal : : abort ( JS : : VM & vm , JS : : Value reason )
{
// 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.
if ( reason . is_undefined ( ) )
reason = WebIDL : : AbortError : : create ( * vm . current_realm ( ) , " Aborted without reason " _fly_string ) . ptr ( ) ;
signal - > set_reason ( reason ) ;
// 3. Return signal.
return signal ;
}
2024-02-26 17:52:32 +00:00
// https://dom.spec.whatwg.org/#dom-abortsignal-timeout
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < AbortSignal > > AbortSignal : : timeout ( JS : : VM & vm , WebIDL : : UnsignedLongLong milliseconds )
{
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 ) ;
auto * window_or_worker = dynamic_cast < HTML : : WindowOrWorkerGlobalScopeMixin * > ( & global ) ;
VERIFY ( window_or_worker ) ;
// 3. Run steps after a timeout given global, "AbortSignal-timeout", milliseconds, and the following step:
window_or_worker - > run_steps_after_a_timeout ( milliseconds , [ & realm , & global , strong_signal = JS : : make_handle ( signal ) ] ( ) {
// 1. Queue a global task on the timer task source given global to signal abort given signal and a new "TimeoutError" DOMException.
HTML : : queue_global_task ( HTML : : Task : : Source : : TimerTask , global , [ & realm , & strong_signal ] ( ) mutable {
auto reason = WebIDL : : TimeoutError : : create ( realm , " Signal timed out " _fly_string ) ;
strong_signal - > signal_abort ( reason ) ;
} ) ;
} ) ;
// 4. Return signal.
return signal ;
}
2024-03-10 18:50:30 +00:00
// https://dom.spec.whatwg.org/#dom-abortsignal-any
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < AbortSignal > > AbortSignal : : any ( JS : : VM & vm , JS : : Value signals )
{
Vector < JS : : Handle < AbortSignal > > signals_list ;
auto iterator_record = TRY ( get_iterator ( vm , signals , JS : : IteratorHint : : Sync ) ) ;
while ( true ) {
auto next = TRY ( iterator_step_value ( vm , iterator_record ) ) ;
if ( ! next . has_value ( ) )
break ;
auto value = next . release_value ( ) ;
if ( ! value . is_object ( ) | | ! is < AbortSignal > ( value . as_object ( ) ) )
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAnObjectOfType , " AbortSignal " ) ;
auto & signal = static_cast < AbortSignal & > ( value . as_object ( ) ) ;
signals_list . append ( JS : : make_handle ( signal ) ) ;
}
// 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.
return create_dependent_abort_signal ( * vm . current_realm ( ) , signals_list ) ;
}
// https://dom.spec.whatwg.org/#create-a-dependent-abort-signal
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < AbortSignal > > AbortSignal : : create_dependent_abort_signal ( JS : : Realm & realm , Vector < JS : : Handle < AbortSignal > > const & signals )
{
// 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-02 02:12:49 +01:00
}