AK+Everywhere: Add and use weak_callback()
We have a common pattern of creating a `WeakPtr<T>` from a reference and passing that into a lambda, to then take the strong ref when the lambda is executed. Add `weak_callback(Weakable, lambda)` that returns a lambda that only invokes the callback if a strong ref exists, and passes it as the first argument.
This commit is contained in:
parent
3d0b2349e6
commit
2b78b84979
9 changed files with 140 additions and 50 deletions
|
|
@ -28,6 +28,7 @@ class Utf16StringData;
|
|||
|
||||
enum class TrailingCodePointTransformation : u8;
|
||||
|
||||
class AtomicRefCountedBase;
|
||||
class BigEndianInputBitStream;
|
||||
class BigEndianOutputBitStream;
|
||||
class Bitmap;
|
||||
|
|
@ -86,6 +87,9 @@ using Bytes = Span<u8>;
|
|||
template<typename T, AK::MemoryOrder DefaultMemoryOrder>
|
||||
class Atomic;
|
||||
|
||||
template<typename T>
|
||||
class AtomicRefCounted;
|
||||
|
||||
template<typename T, typename TSizeCalculationPolicy = DefaultSizeCalculationPolicy>
|
||||
class SinglyLinkedList;
|
||||
|
||||
|
|
@ -176,6 +180,8 @@ class [[nodiscard]] ErrorOr;
|
|||
#if USING_AK_GLOBALLY
|
||||
using AK::Array;
|
||||
using AK::Atomic;
|
||||
using AK::AtomicRefCounted;
|
||||
using AK::AtomicRefCountedBase;
|
||||
using AK::Badge;
|
||||
using AK::BigEndianInputBitStream;
|
||||
using AK::BigEndianOutputBitStream;
|
||||
|
|
|
|||
12
AK/WeakPtr.h
12
AK/WeakPtr.h
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/Weakable.h>
|
||||
|
||||
namespace AK {
|
||||
|
|
@ -173,6 +174,16 @@ private:
|
|||
RefPtr<WeakLink> m_link;
|
||||
};
|
||||
|
||||
template<typename T, typename Callback>
|
||||
auto weak_callback(T& obj, Callback&& callback)
|
||||
requires(!IsBaseOf<AtomicRefCountedBase, T>)
|
||||
{
|
||||
return [weak = obj.template make_weak_ptr<T>(), cb = forward<Callback>(callback)](auto&&... args) {
|
||||
if (weak)
|
||||
cb(*weak, forward<decltype(args)>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct Formatter<WeakPtr<T>> : Formatter<T const*> {
|
||||
ErrorOr<void> format(FormatBuilder& builder, WeakPtr<T> const& value)
|
||||
|
|
@ -193,5 +204,6 @@ struct Traits<WeakPtr<T>> : public DefaultTraits<WeakPtr<T>> {
|
|||
}
|
||||
|
||||
#if USING_AK_GLOBALLY
|
||||
using AK::weak_callback;
|
||||
using AK::WeakPtr;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,10 +45,9 @@ FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr<TabActor>
|
|||
|
||||
devtools.delegate().listen_for_console_messages(
|
||||
tab->description(),
|
||||
[weak_self = make_weak_ptr<FrameActor>()](WebView::ConsoleOutput console_output) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->on_console_message(move(console_output));
|
||||
});
|
||||
weak_callback(*this, [](auto& self, WebView::ConsoleOutput console_output) {
|
||||
self.on_console_message(move(console_output));
|
||||
}));
|
||||
|
||||
// FIXME: We should adopt WebContent to inform us when style sheets are available or removed.
|
||||
devtools.delegate().retrieve_style_sheets(tab->description(),
|
||||
|
|
@ -58,33 +57,27 @@ FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr<TabActor>
|
|||
|
||||
devtools.delegate().listen_for_network_events(
|
||||
tab->description(),
|
||||
[weak_self = make_weak_ptr<FrameActor>()](DevToolsDelegate::NetworkRequestData data) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->on_network_request_started(move(data));
|
||||
},
|
||||
[weak_self = make_weak_ptr<FrameActor>()](DevToolsDelegate::NetworkResponseData data) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->on_network_response_headers_received(move(data));
|
||||
},
|
||||
[weak_self = make_weak_ptr<FrameActor>()](u64 request_id, ByteBuffer data) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->on_network_response_body_received(request_id, move(data));
|
||||
},
|
||||
[weak_self = make_weak_ptr<FrameActor>()](DevToolsDelegate::NetworkRequestCompleteData data) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->on_network_request_finished(move(data));
|
||||
});
|
||||
weak_callback(*this, [](auto& self, DevToolsDelegate::NetworkRequestData data) {
|
||||
self.on_network_request_started(move(data));
|
||||
}),
|
||||
weak_callback(*this, [](auto& self, DevToolsDelegate::NetworkResponseData data) {
|
||||
self.on_network_response_headers_received(move(data));
|
||||
}),
|
||||
weak_callback(*this, [](auto& self, u64 request_id, ByteBuffer data) {
|
||||
self.on_network_response_body_received(request_id, move(data));
|
||||
}),
|
||||
weak_callback(*this, [](auto& self, DevToolsDelegate::NetworkRequestCompleteData data) {
|
||||
self.on_network_request_finished(move(data));
|
||||
}));
|
||||
|
||||
devtools.delegate().listen_for_navigation_events(
|
||||
tab->description(),
|
||||
[weak_self = make_weak_ptr<FrameActor>()](String url) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->on_navigation_started(move(url));
|
||||
},
|
||||
[weak_self = make_weak_ptr<FrameActor>()](String url, String title) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->on_navigation_finished(move(url), move(title));
|
||||
});
|
||||
weak_callback(*this, [](auto& self, String url) {
|
||||
self.on_navigation_started(move(url));
|
||||
}),
|
||||
weak_callback(*this, [](auto& self, String url, String title) {
|
||||
self.on_navigation_finished(move(url), move(title));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -122,10 +122,9 @@ PageStyleActor::PageStyleActor(DevToolsServer& devtools, String name, WeakPtr<In
|
|||
{
|
||||
if (auto tab = InspectorActor::tab_for(m_inspector)) {
|
||||
devtools.delegate().listen_for_dom_properties(tab->description(),
|
||||
[weak_self = make_weak_ptr<PageStyleActor>()](WebView::DOMNodeProperties const& properties) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->received_dom_node_properties(properties);
|
||||
});
|
||||
weak_callback(*this, [](auto& self, WebView::DOMNodeProperties const& properties) {
|
||||
self.received_dom_node_properties(properties);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,9 @@ StyleSheetsActor::StyleSheetsActor(DevToolsServer& devtools, String name, WeakPt
|
|||
if (auto tab = m_tab.strong_ref()) {
|
||||
devtools.delegate().listen_for_style_sheet_sources(
|
||||
tab->description(),
|
||||
[weak_self = make_weak_ptr<StyleSheetsActor>()](Web::CSS::StyleSheetIdentifier const& style_sheet, String source) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->style_sheet_source_received(style_sheet, move(source));
|
||||
});
|
||||
weak_callback(*this, [](auto& self, Web::CSS::StyleSheetIdentifier const& style_sheet, String source) {
|
||||
self.style_sheet_source_received(style_sheet, move(source));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,9 @@ WalkerActor::WalkerActor(DevToolsServer& devtools, String name, WeakPtr<TabActor
|
|||
|
||||
if (auto tab = m_tab.strong_ref()) {
|
||||
devtools.delegate().listen_for_dom_mutations(tab->description(),
|
||||
[weak_self = make_weak_ptr<WalkerActor>()](WebView::Mutation mutation) {
|
||||
if (auto self = weak_self.strong_ref())
|
||||
self->new_dom_node_mutation(move(mutation));
|
||||
});
|
||||
weak_callback(*this, [](auto& self, WebView::Mutation mutation) {
|
||||
self.new_dom_node_mutation(move(mutation));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,21 +69,21 @@ void ImageStyleValue::load_any_resources(DOM::Document& document)
|
|||
|
||||
if (m_resource_request) {
|
||||
m_resource_request->add_callbacks(
|
||||
[this, weak_this = make_weak_ptr()] {
|
||||
if (!weak_this || !m_document)
|
||||
weak_callback(*this, [](auto& self) {
|
||||
if (!self.m_document)
|
||||
return;
|
||||
|
||||
for (auto* client : m_clients)
|
||||
client->image_style_value_did_update(*this);
|
||||
for (auto* client : self.m_clients)
|
||||
client->image_style_value_did_update(self);
|
||||
|
||||
auto image_data = m_resource_request->image_data();
|
||||
auto image_data = self.m_resource_request->image_data();
|
||||
if (image_data->is_animated() && image_data->frame_count() > 1) {
|
||||
m_timer = Platform::Timer::create(m_document->heap());
|
||||
m_timer->set_interval(image_data->frame_duration(0));
|
||||
m_timer->on_timeout = GC::create_function(m_document->heap(), [this] { animate(); });
|
||||
m_timer->start();
|
||||
self.m_timer = Platform::Timer::create(self.m_document->heap());
|
||||
self.m_timer->set_interval(image_data->frame_duration(0));
|
||||
self.m_timer->on_timeout = GC::create_function(self.m_document->heap(), [ptr = &self] { ptr->animate(); });
|
||||
self.m_timer->start();
|
||||
}
|
||||
},
|
||||
}),
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <AK/IDAllocator.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/Proxy.h>
|
||||
#include <LibCore/Socket.h>
|
||||
|
|
|
|||
|
|
@ -20,10 +20,22 @@ class SimpleWeakable : public Weakable<SimpleWeakable>
|
|||
public:
|
||||
SimpleWeakable() = default;
|
||||
|
||||
int member() const { return m_member; }
|
||||
|
||||
private:
|
||||
int m_member { 123 };
|
||||
};
|
||||
|
||||
class NonRefCountedWeakable : public Weakable<NonRefCountedWeakable> {
|
||||
public:
|
||||
NonRefCountedWeakable() = default;
|
||||
|
||||
int value() const { return m_value; }
|
||||
|
||||
private:
|
||||
int m_value { 456 };
|
||||
};
|
||||
|
||||
#if defined(AK_COMPILER_CLANG)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
|
@ -64,3 +76,72 @@ TEST_CASE(weakptr_move)
|
|||
|
||||
EXPECT_EQ(weak2.is_null(), true);
|
||||
}
|
||||
|
||||
TEST_CASE(weak_callback_ref_counted)
|
||||
{
|
||||
bool was_called = false;
|
||||
|
||||
{
|
||||
auto simple = adopt_ref(*new SimpleWeakable);
|
||||
auto cb = weak_callback(*simple, [&was_called](auto& self) {
|
||||
was_called = true;
|
||||
EXPECT_EQ(self.member(), 123);
|
||||
});
|
||||
|
||||
cb();
|
||||
EXPECT(was_called);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(weak_callback_ref_counted_dead)
|
||||
{
|
||||
auto cb = [&] {
|
||||
auto simple = adopt_ref(*new SimpleWeakable);
|
||||
return weak_callback(*simple, [](auto&) {
|
||||
VERIFY_NOT_REACHED();
|
||||
});
|
||||
}();
|
||||
|
||||
cb();
|
||||
}
|
||||
|
||||
TEST_CASE(weak_callback_ref_counted_with_args)
|
||||
{
|
||||
int received_value = 0;
|
||||
|
||||
auto simple = adopt_ref(*new SimpleWeakable);
|
||||
auto cb = weak_callback(*simple, [&received_value](auto& self, int value) {
|
||||
received_value = value + self.member();
|
||||
});
|
||||
|
||||
cb(42);
|
||||
EXPECT_EQ(received_value, 42 + 123);
|
||||
}
|
||||
|
||||
TEST_CASE(weak_callback_non_ref_counted)
|
||||
{
|
||||
bool was_called = false;
|
||||
|
||||
{
|
||||
NonRefCountedWeakable obj;
|
||||
auto cb = weak_callback(obj, [&was_called](auto& self) {
|
||||
was_called = true;
|
||||
EXPECT_EQ(self.value(), 456);
|
||||
});
|
||||
|
||||
cb();
|
||||
EXPECT(was_called);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(weak_callback_non_ref_counted_dead)
|
||||
{
|
||||
auto cb = [&] {
|
||||
NonRefCountedWeakable obj;
|
||||
return weak_callback(obj, [](auto&) {
|
||||
VERIFY_NOT_REACHED();
|
||||
});
|
||||
}();
|
||||
|
||||
cb();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue