Fixed crash with multiple notifications

Sometimes an invalid function pointer was called with an invalid context
This commit is contained in:
LAGonauta 2021-02-27 16:30:36 -03:00
parent f2c689fa12
commit 0f555da7a9
2 changed files with 46 additions and 41 deletions

View file

@ -15,37 +15,48 @@
template<class T> template<class T>
struct Callback { struct Callback {
private:
winrtEventToken* token;
public:
T callback; T callback;
void* context; void* context;
void(*free)(void*); void(*free)(void*);
winrtEventToken* token;
Callback(T callback, void* context, void(*free)(void*), winrtEventToken* token)
Callback(T callback, void* context, void(*free)(void*))
{ {
this->callback = callback; this->callback = callback;
this->free = free; this->free = free;
this->context = context; this->context = context;
this->token = token;
} }
~Callback() ~Callback()
{
Clear();
}
void Clear()
{ {
if (this->callback && this->context && this->free) if (this->callback && this->context && this->free)
{ {
this->free(this->context); this->free(this->context);
} }
if (this->token) {
g_object_unref(this->token);
}
this->callback = nullptr; this->callback = nullptr;
this->context = nullptr; this->context = nullptr;
this->free = nullptr; this->free = nullptr;
this->token = nullptr; this->token = nullptr;
} }
void SetToken(winrtEventToken* token) {
this->token = token;
g_object_ref(this->token);
}
winrtEventToken* GetToken() {
return this->token;
}
// delete copy // delete copy
Callback(const Callback&) = delete; Callback(const Callback&) = delete;
Callback& operator=(const Callback&) = delete; Callback& operator=(const Callback&) = delete;
@ -77,32 +88,29 @@ static void winrt_windows_ui_notifications_toast_notification_finalize(GObject*
for (const auto& item : priv->notification->activated) for (const auto& item : priv->notification->activated)
{ {
auto token = item->token; auto token = item->GetToken();
if (winrt_event_token_operator_bool(token)) if (winrt_event_token_operator_bool(token))
{ {
priv->notification->data.Activated(*winrt_event_token_get_internal(token)); priv->notification->data.Activated(*winrt_event_token_get_internal(token));
} }
g_object_unref(token);
} }
for (const auto& item : priv->notification->failed) for (const auto& item : priv->notification->failed)
{ {
auto token = item->token; auto token = item->GetToken();
if (winrt_event_token_operator_bool(token)) if (winrt_event_token_operator_bool(token))
{ {
priv->notification->data.Failed(*winrt_event_token_get_internal(token)); priv->notification->data.Failed(*winrt_event_token_get_internal(token));
} }
g_object_unref(token);
} }
for (const auto& item : priv->notification->dismissed) for (const auto& item : priv->notification->dismissed)
{ {
auto token = item->token; auto token = item->GetToken();
if (winrt_event_token_operator_bool(token)) if (winrt_event_token_operator_bool(token))
{ {
priv->notification->data.Dismissed(*winrt_event_token_get_internal(token)); priv->notification->data.Dismissed(*winrt_event_token_get_internal(token));
} }
g_object_unref(token);
} }
delete priv->notification; delete priv->notification;
@ -237,6 +245,7 @@ winrtEventToken* winrt_windows_ui_notifications_toast_notification_Activated(win
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self); winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
auto callback_data = std::make_shared<Callback<NotificationCallbackActivated>>(callback, context, free);
auto token = priv->notification->data.Activated([=](auto sender, winrt::Windows::Foundation::IInspectable inspectable) auto token = priv->notification->data.Activated([=](auto sender, winrt::Windows::Foundation::IInspectable inspectable)
{ {
std::wstring arguments; std::wstring arguments;
@ -263,11 +272,10 @@ winrtEventToken* winrt_windows_ui_notifications_toast_notification_Activated(win
callback(wsview_to_char(arguments), nullptr /* user_input */ , 0 /* user_input.size() */, context); callback(wsview_to_char(arguments), nullptr /* user_input */ , 0 /* user_input.size() */, context);
}); });
auto new_token = winrt_event_token_new_from_token(&token); callback_data->SetToken(winrt_event_token_new_from_token(&token));
g_object_ref(new_token);
priv->notification->activated.push_back(std::make_shared<Callback<NotificationCallbackActivated>>(callback, context, free, new_token)); priv->notification->activated.push_back(callback_data);
return new_token; return callback_data->GetToken();
} }
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Failed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackFailed callback, void* context, void(*free)(void*)) winrtEventToken* winrt_windows_ui_notifications_toast_notification_Failed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackFailed callback, void* context, void(*free)(void*))
@ -277,16 +285,16 @@ winrtEventToken* winrt_windows_ui_notifications_toast_notification_Failed(winrtW
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self); winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
auto callback_data = std::make_shared<Callback<NotificationCallbackFailed>>(callback, context, free);
auto token = priv->notification->data.Failed([=](auto sender, auto toastFailedEventArgs) auto token = priv->notification->data.Failed([=](auto sender, auto toastFailedEventArgs)
{ {
callback(context); callback_data->callback(callback_data->context);
}); });
auto new_token = winrt_event_token_new_from_token(&token); callback_data->SetToken(winrt_event_token_new_from_token(&token));
g_object_ref(new_token);
priv->notification->failed.push_back(std::make_shared<Callback<NotificationCallbackFailed>>(callback, context, free, new_token)); priv->notification->failed.push_back(callback_data);
return new_token; return callback_data->GetToken();
} }
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Dismissed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackDismissed callback, void* context, void(*free)(void*)) winrtEventToken* winrt_windows_ui_notifications_toast_notification_Dismissed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackDismissed callback, void* context, void(*free)(void*))
@ -296,17 +304,17 @@ winrtEventToken* winrt_windows_ui_notifications_toast_notification_Dismissed(win
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self); winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
auto callback_data = std::make_shared<Callback<NotificationCallbackDismissed>>(callback, context, free);
auto token = priv->notification->data.Dismissed([=](auto sender, winrt::Windows::UI::Notifications::ToastDismissedEventArgs dismissed) auto token = priv->notification->data.Dismissed([=](auto sender, winrt::Windows::UI::Notifications::ToastDismissedEventArgs dismissed)
{ {
auto reason = dismissed.Reason(); auto reason = dismissed.Reason();
callback(static_cast<winrtWindowsUINotificationsToastDismissalReason>(reason), context); callback_data->callback(static_cast<winrtWindowsUINotificationsToastDismissalReason>(reason), callback_data->context);
}); });
auto new_token = winrt_event_token_new_from_token(&token); callback_data->SetToken(winrt_event_token_new_from_token(&token));
g_object_ref(new_token);
priv->notification->dismissed.push_back(std::make_shared<Callback<NotificationCallbackDismissed>>(callback, context, free, new_token)); priv->notification->dismissed.push_back(callback_data);
return new_token; return callback_data->GetToken();
} }
// TODO: refactor `Remove{Activated,Failed,Dismissed}` methods into one to deduplicate code // TODO: refactor `Remove{Activated,Failed,Dismissed}` methods into one to deduplicate code
@ -317,13 +325,12 @@ void winrt_windows_ui_notifications_toast_notification_RemoveActivated(winrtWind
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self); winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
priv->notification->activated.remove_if([&](const auto& callback) { priv->notification->activated.remove_if([&](const auto& callback) {
if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->token)) if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->GetToken()))
{ {
if (winrt_event_token_operator_bool(callback->token)) if (winrt_event_token_operator_bool(callback->GetToken()))
{ {
priv->notification->data.Activated(*winrt_event_token_get_internal(callback->token)); priv->notification->data.Activated(*winrt_event_token_get_internal(callback->GetToken()));
} }
g_object_unref(callback->token);
return true; return true;
} }
return false; return false;
@ -337,13 +344,12 @@ void winrt_windows_ui_notifications_toast_notification_RemoveFailed(winrtWindows
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self); winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
priv->notification->failed.remove_if([&](const auto& callback) { priv->notification->failed.remove_if([&](const auto& callback) {
if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->token)) if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->GetToken()))
{ {
if (winrt_event_token_operator_bool(callback->token)) if (winrt_event_token_operator_bool(callback->GetToken()))
{ {
priv->notification->data.Failed(*winrt_event_token_get_internal(callback->token)); priv->notification->data.Failed(*winrt_event_token_get_internal(callback->GetToken()));
} }
g_object_unref(callback->token);
return true; return true;
} }
return false; return false;
@ -357,13 +363,12 @@ void winrt_windows_ui_notifications_toast_notification_RemoveDismissed(winrtWind
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self); winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
priv->notification->dismissed.remove_if([&](const auto& callback) { priv->notification->dismissed.remove_if([&](const auto& callback) {
if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->token)) if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->GetToken()))
{ {
if (winrt_event_token_operator_bool(callback->token)) if (winrt_event_token_operator_bool(callback->GetToken()))
{ {
priv->notification->data.Dismissed(*winrt_event_token_get_internal(callback->token)); priv->notification->data.Dismissed(*winrt_event_token_get_internal(callback->GetToken()));
} }
g_object_unref(callback->token);
return true; return true;
} }
return false; return false;