1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00
ladybird/Libraries/LibWeb/DOM/AbortSignal.cpp
Andreas Kling a6dfc74e93 LibWeb: Only set prototype once for object with IDL interface
Before this change, we were going through the chain of base classes for
each IDL interface object and having them set the prototype to their
prototype.

Instead of doing that, reorder things so that we set the right prototype
immediately in Foo::initialize(), and then don't bother in all the base
class overrides.

This knocks off a ~1% profile item on Speedometer 3.
2025-04-20 18:43:11 +02:00

232 lines
8.4 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2024, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/AbortSignalPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/AbortSignal.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
namespace Web::DOM {
GC_DEFINE_ALLOCATOR(AbortSignal);
WebIDL::ExceptionOr<GC::Ref<AbortSignal>> AbortSignal::construct_impl(JS::Realm& realm)
{
return realm.create<AbortSignal>(realm);
}
AbortSignal::AbortSignal(JS::Realm& realm)
: EventTarget(realm)
{
}
void AbortSignal::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(AbortSignal);
Base::initialize(realm);
}
// https://dom.spec.whatwg.org/#abortsignal-add
Optional<AbortSignal::AbortAlgorithmID> AbortSignal::add_abort_algorithm(Function<void()> abort_algorithm)
{
// 1. If signal is aborted, then return.
if (aborted())
return {};
// 2. Append algorithm to signals abort algorithms.
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 signals abort algorithms.
m_abort_algorithms.remove(id);
}
// https://dom.spec.whatwg.org/#abortsignal-signal-abort
void AbortSignal::signal_abort(JS::Value reason)
{
// 1. If signal is aborted, then return.
if (aborted())
return;
// 2. Set signals abort reason to reason if it is given; otherwise to a new "AbortError" DOMException.
if (!reason.is_undefined())
m_abort_reason = reason;
else
m_abort_reason = WebIDL::AbortError::create(realm(), "Aborted without reason"_string).ptr();
// 3. Let dependentSignalsToAbort be a new list.
Vector<GC::Root<AbortSignal>> dependent_signals_to_abort;
// 4. For each dependentSignal of signals dependent signals:
for (auto const& dependent_signal : m_dependent_signals) {
// 1. If dependentSignal is not aborted, then:
if (!dependent_signal->aborted()) {
// 1. Set dependentSignals abort reason to signals 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 signals abort algorithms: run algorithm.
for (auto const& algorithm : signal.m_abort_algorithms)
algorithm.value->function()();
// 2. Empty signals 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);
}
void AbortSignal::set_onabort(WebIDL::CallbackType* event_handler)
{
set_event_handler_attribute(HTML::EventNames::abort, event_handler);
}
WebIDL::CallbackType* AbortSignal::onabort()
{
return event_handler_attribute(HTML::EventNames::abort);
}
// https://dom.spec.whatwg.org/#dom-abortsignal-throwifaborted
JS::ThrowCompletionOr<void> AbortSignal::throw_if_aborted() const
{
// The throwIfAborted() method steps are to throw thiss abort reason, if this is aborted.
if (!aborted())
return {};
return JS::throw_completion(m_abort_reason);
}
void AbortSignal::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_abort_reason);
visitor.visit(m_abort_algorithms);
visitor.visit(m_source_signals);
visitor.visit(m_dependent_signals);
}
// https://dom.spec.whatwg.org/#dom-abortsignal-abort
WebIDL::ExceptionOr<GC::Ref<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 signals 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"_string).ptr();
signal->set_reason(reason);
// 3. Return signal.
return signal;
}
// https://dom.spec.whatwg.org/#dom-abortsignal-timeout
WebIDL::ExceptionOr<GC::Ref<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 signals relevant global object.
auto& global = HTML::relevant_global_object(signal);
auto& window_or_worker = as<HTML::WindowOrWorkerGlobalScopeMixin>(global);
// 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, 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, GC::create_function(realm.heap(), [&realm, signal]() mutable {
auto reason = WebIDL::TimeoutError::create(realm, "Signal timed out"_string);
signal->signal_abort(reason);
}));
});
// 4. Return signal.
return signal;
}
// https://dom.spec.whatwg.org/#dom-abortsignal-any
WebIDL::ExceptionOr<GC::Ref<AbortSignal>> AbortSignal::any(JS::VM& vm, Vector<GC::Root<AbortSignal>> const& signals)
{
// 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);
}
// https://dom.spec.whatwg.org/#create-a-dependent-abort-signal
WebIDL::ExceptionOr<GC::Ref<AbortSignal>> AbortSignal::create_dependent_abort_signal(JS::Realm& realm, Vector<GC::Root<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 resultSignals abort reason to signals abort reason and return resultSignal.
for (auto const& signal : signals) {
if (signal->aborted()) {
result_signal->set_reason(signal->reason());
return result_signal;
}
}
// 3. Set resultSignals dependent to true.
result_signal->set_dependent(true);
// 4. For each signal of signals:
for (auto const& signal : signals) {
// 1. If signals dependent is false, then:
if (!signal->dependent()) {
// 1. Append signal to resultSignals source signals.
result_signal->append_source_signal({ signal });
// 2. Append resultSignal to signals dependent signals.
signal->append_dependent_signal(result_signal);
}
// 2. Otherwise, for each sourceSignal of signals 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 resultSignals source signals.
result_signal->append_source_signal(source_signal);
// 3. Append resultSignal to sourceSignals dependent signals.
source_signal->append_dependent_signal(result_signal);
}
}
}
// 5. Return resultSignal
return result_signal;
}
}