diff --git a/Libraries/LibGC/Weak.h b/Libraries/LibGC/Weak.h index c07a98b0ac..4bf35e4a9b 100644 --- a/Libraries/LibGC/Weak.h +++ b/Libraries/LibGC/Weak.h @@ -148,6 +148,17 @@ private: NonnullRefPtr 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 +auto weak_callback(T& obj, Callback&& callback) +{ + return [weak = Weak { obj }, cb = forward(callback)](auto&&... args) mutable { + if (weak) + cb(*weak, forward(args)...); + }; +} + template inline bool operator==(Weak const& a, Ptr const& b) { diff --git a/Libraries/LibWeb/HTML/HTMLMediaElement.cpp b/Libraries/LibWeb/HTML/HTMLMediaElement.cpp index 96cc6df51b..69222edb09 100644 --- a/Libraries/LibWeb/HTML/HTMLMediaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLMediaElement.cpp @@ -1010,11 +1010,9 @@ void HTMLMediaElement::fetch_resource(URL::URL const& url_record, Function(); 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 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 diff --git a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index 87253eb941..34e5abbdd8 100644 --- a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -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(); + }))) { } diff --git a/Libraries/LibWeb/WebSockets/WebSocket.cpp b/Libraries/LibWeb/WebSockets/WebSocket.cpp index 22aa24b969..dfae9a6117 100644 --- a/Libraries/LibWeb/WebSockets/WebSocket.cpp +++ b/Libraries/LibWeb/WebSockets/WebSocket.cpp @@ -221,30 +221,18 @@ ErrorOr 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(*weak_this); - websocket.on_open(); - }; - m_websocket->on_message = [weak_this = GC::Weak { *this }](auto message) { - if (!weak_this) - return; - auto& websocket = const_cast(*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(*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(*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 {}; }