LibGC+LibWeb: Add and use GC::weak_callback()

This commit is contained in:
Jelle Raaijmakers 2026-02-25 10:00:33 +01:00 committed by Tim Flynn
parent 2b78b84979
commit f9e837a832
4 changed files with 47 additions and 61 deletions

View file

@ -148,6 +148,17 @@ private:
NonnullRefPtr<WeakImpl> m_impl { WeakImpl::the_null_weak_impl };
};
// NOTE: Unlike GC::Function, captures in this callback are not visited by the garbage collector.
// Do not capture GC-managed objects without ensuring they are kept alive through other means.
template<typename T, typename Callback>
auto weak_callback(T& obj, Callback&& callback)
{
return [weak = Weak<T> { obj }, cb = forward<Callback>(callback)](auto&&... args) mutable {
if (weak)
cb(*weak, forward<decltype(args)>(args)...);
};
}
template<typename T, typename U>
inline bool operator==(Weak<T> const& a, Ptr<U> const& b)
{

View file

@ -1010,11 +1010,9 @@ void HTMLMediaElement::fetch_resource(URL::URL const& url_record, Function<void(
auto fetch_data = make_ref_counted<FetchData>();
fetch_data->url_record = url_record;
fetch_data->stream = Media::IncrementallyPopulatedStream::create_empty();
fetch_data->stream->set_data_request_callback([self = GC::Weak(*this), &fetch_data = *fetch_data](u64 offset) {
if (!self)
return;
self->restart_fetch_at_offset(fetch_data, offset);
});
fetch_data->stream->set_data_request_callback(GC::weak_callback(*this, [&fetch_data = *fetch_data](auto& self, u64 offset) {
self.restart_fetch_at_offset(fetch_data, offset);
}));
fetch_data->failure_callback = [&stream = *fetch_data->stream, failure_callback = move(failure_callback)](String error_message) {
// Ensure that we unblock any reads if we stop the fetch due to some failure.
stream.close();
@ -1431,10 +1429,9 @@ void HTMLMediaElement::on_metadata_parsed()
// NB: Register the duration change handler here so that we don't set the duration when the
// playback manager updates the duration after parsing.
m_playback_manager->on_duration_change = [weak_self = GC::Weak(*this)](AK::Duration duration) {
if (weak_self)
weak_self->set_duration(duration.to_seconds_f64());
};
m_playback_manager->on_duration_change = GC::weak_callback(*this, [](auto& self, AK::Duration duration) {
self.set_duration(duration.to_seconds_f64());
});
// 5. For video elements, set the videoWidth and videoHeight attributes, and queue a media element task given the media element to fire an event
// named resize at the media element.
@ -1509,41 +1506,32 @@ void HTMLMediaElement::set_up_playback_manager(NonnullRefPtr<FetchData> const& f
// -> If the media resource is found to have an audio track
// -> If the media resource is found to have a video track
m_playback_manager->on_track_added = [weak_self = GC::Weak(*this)](auto track_type, auto& track) {
if (!weak_self)
return;
if (track_type == Media::TrackType::Audio) {
weak_self->on_audio_track_added(track);
} else {
weak_self->on_video_track_added(track);
}
};
m_playback_manager->on_track_added = GC::weak_callback(*this, [](auto& self, auto track_type, auto& track) {
if (track_type == Media::TrackType::Audio)
self.on_audio_track_added(track);
else
self.on_video_track_added(track);
});
// -> Once enough of the media data has been fetched to determine the duration of the media resource, its dimensions, and other metadata
m_playback_manager->on_metadata_parsed = [weak_self = GC::Weak(*this)] {
if (!weak_self)
return;
weak_self->on_metadata_parsed();
};
m_playback_manager->on_metadata_parsed = GC::weak_callback(*this, [](auto& self) {
self.on_metadata_parsed();
});
// -> If the media data can be fetched but is found by inspection to be in an unsupported format, or can otherwise not be rendered at all
m_playback_manager->on_unsupported_format_error = [weak_self = GC::Weak(*this), fetch_data](auto&& error) mutable {
if (!weak_self)
return;
m_playback_manager->on_unsupported_format_error = GC::weak_callback(*this, [fetch_data](auto& self, auto&& error) {
// 1. The user agent should cancel the fetching process.
weak_self->cancel_the_fetching_process();
self.cancel_the_fetching_process();
// 2. Abort this subalgorithm, returning to the resource selection algorithm.
fetch_data->failure_callback(MUST(String::from_utf8(error.description())));
};
});
m_playback_manager->add_media_source(*fetch_data->stream);
m_playback_manager->on_playback_state_change = [weak_self = GC::Weak(*this)] {
if (weak_self)
weak_self->on_playback_manager_state_change();
};
m_playback_manager->on_playback_state_change = GC::weak_callback(*this, [](auto& self) {
self.on_playback_manager_state_change();
});
}
// https://html.spec.whatwg.org/multipage/media.html#media-data-processing-steps-list

View file

@ -33,10 +33,9 @@ GC_DEFINE_ALLOCATOR(HTMLTextAreaElement);
HTMLTextAreaElement::HTMLTextAreaElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: HTMLElement(document, move(qualified_name))
, m_input_event_timer(Core::Timer::create_single_shot(0, [weak_this = GC::Weak { *this }]() {
if (weak_this)
weak_this->queue_firing_input_event();
}))
, m_input_event_timer(Core::Timer::create_single_shot(0, GC::weak_callback(*this, [](auto& self) {
self.queue_firing_input_event();
})))
{
}

View file

@ -221,30 +221,18 @@ ErrorOr<void> WebSocket::establish_web_socket_connection(URL::URL const& url_rec
m_websocket = request_client->websocket_connect(url_record, origin_string, protocol_byte_strings, {}, additional_headers);
m_websocket->on_open = [weak_this = GC::Weak { *this }] {
if (!weak_this)
return;
auto& websocket = const_cast<WebSocket&>(*weak_this);
websocket.on_open();
};
m_websocket->on_message = [weak_this = GC::Weak { *this }](auto message) {
if (!weak_this)
return;
auto& websocket = const_cast<WebSocket&>(*weak_this);
websocket.on_message(move(message.data), message.is_text);
};
m_websocket->on_close = [weak_this = GC::Weak { *this }](auto code, auto reason, bool was_clean) {
if (!weak_this)
return;
auto& websocket = const_cast<WebSocket&>(*weak_this);
websocket.on_close(code, String::from_byte_string(reason).release_value_but_fixme_should_propagate_errors(), was_clean);
};
m_websocket->on_error = [weak_this = GC::Weak { *this }](auto) {
if (!weak_this)
return;
auto& websocket = const_cast<WebSocket&>(*weak_this);
websocket.on_error();
};
m_websocket->on_open = GC::weak_callback(*this, [](auto& self) {
self.on_open();
});
m_websocket->on_message = GC::weak_callback(*this, [](auto& self, auto message) {
self.on_message(move(message.data), message.is_text);
});
m_websocket->on_close = GC::weak_callback(*this, [](auto& self, auto code, auto reason, bool was_clean) {
self.on_close(code, String::from_byte_string(reason).release_value_but_fixme_should_propagate_errors(), was_clean);
});
m_websocket->on_error = GC::weak_callback(*this, [](auto& self, auto) {
self.on_error();
});
return {};
}