| Index: chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| index 6a0f8974c804f7e3ef7e9dbbef6546c7fc14068c..f19184ac8c78ff35c57e1baf256a8a3c2d51b8a0 100644
|
| --- a/chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| +++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| @@ -4,8 +4,12 @@
|
|
|
| #include "chrome/browser/notifications/notification_platform_bridge_linux.h"
|
|
|
| +#include <algorithm>
|
| +
|
| #include "base/memory/ptr_util.h"
|
| +#include "base/stl_util.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| +#include "chrome/browser/browser_process.h"
|
| #include "chrome/browser/notifications/notification.h"
|
|
|
| namespace {
|
| @@ -13,6 +17,23 @@ namespace {
|
| const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications";
|
| const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications";
|
|
|
| +// Callback used by GLib when the "Notify" message completes for the
|
| +// first time.
|
| +void NotifyCompleteReceiver(GObject* source_object,
|
| + GAsyncResult* result,
|
| + gpointer user_data) {
|
| + GDBusProxy* proxy = G_DBUS_PROXY(source_object);
|
| + GVariant* value = g_dbus_proxy_call_finish(proxy, result, nullptr);
|
| + if (!value) {
|
| + // The message might have been cancelled, in which case
|
| + // |user_data| points to a destroyed NotificationData.
|
| + return;
|
| + }
|
| + auto* platform_bridge_linux = static_cast<NotificationPlatformBridgeLinux*>(
|
| + g_browser_process->notification_platform_bridge());
|
| + platform_bridge_linux->NotifyCompleteInternal(user_data, value);
|
| +}
|
| +
|
| } // namespace
|
|
|
| // static
|
| @@ -29,13 +50,48 @@ NotificationPlatformBridge* NotificationPlatformBridge::Create() {
|
| return new NotificationPlatformBridgeLinux(notification_proxy);
|
| }
|
|
|
| +struct NotificationPlatformBridgeLinux::NotificationData {
|
| + NotificationData(const std::string& notification_id,
|
| + const std::string& profile_id,
|
| + bool is_incognito)
|
| + : notification_id(notification_id),
|
| + profile_id(profile_id),
|
| + is_incognito(is_incognito) {}
|
| +
|
| + ~NotificationData() {
|
| + if (cancellable)
|
| + g_cancellable_cancel(cancellable);
|
| + }
|
| +
|
| + // The ID used by the notification server. Will be 0 until the
|
| + // first "Notify" message completes.
|
| + uint32_t dbus_id = 0;
|
| +
|
| + // Same parameters used by NotificationPlatformBridge::Display().
|
| + const std::string notification_id;
|
| + const std::string profile_id;
|
| + const bool is_incognito;
|
| +
|
| + // Used to cancel the initial "Notify" message so we don't call
|
| + // NotificationPlatformBridgeLinux::NotifyCompleteInternal() with a
|
| + // destroyed Notification.
|
| + ScopedGObject<GCancellable> cancellable;
|
| +
|
| + // If not null, the data to update the notification with once
|
| + // |dbus_id| becomes available.
|
| + std::unique_ptr<Notification> update_data;
|
| + NotificationCommon::Type update_type = NotificationCommon::TYPE_MAX;
|
| +
|
| + // If true, indicates the notification should be closed once
|
| + // |dbus_id| becomes available.
|
| + bool should_close = false;
|
| +};
|
| +
|
| NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux(
|
| GDBusProxy* notification_proxy)
|
| : notification_proxy_(notification_proxy) {}
|
|
|
| -NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() {
|
| - g_object_unref(notification_proxy_);
|
| -}
|
| +NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() {}
|
|
|
| void NotificationPlatformBridgeLinux::Display(
|
| NotificationCommon::Type notification_type,
|
| @@ -43,20 +99,38 @@ void NotificationPlatformBridgeLinux::Display(
|
| const std::string& profile_id,
|
| bool is_incognito,
|
| const Notification& notification) {
|
| - // TODO(thomasanderson): Add a complete implementation.
|
| - g_dbus_proxy_call(
|
| - notification_proxy_, "Notify",
|
| - g_variant_new("(susssasa{sv}i)", "", 0, "",
|
| - base::UTF16ToUTF8(notification.title()).c_str(),
|
| - base::UTF16ToUTF8(notification.message()).c_str(), nullptr,
|
| - nullptr, -1),
|
| - G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr, nullptr);
|
| + NotificationData* data = FindNotificationData(notification_id, profile_id);
|
| + if (data) {
|
| + // Update an existing notification.
|
| + if (data->dbus_id) {
|
| + NotifyNow(data->dbus_id, notification_type, notification, nullptr,
|
| + nullptr, nullptr);
|
| + } else {
|
| + data->update_type = notification_type;
|
| + data->update_data = base::MakeUnique<Notification>(notification);
|
| + }
|
| + } else {
|
| + // Send the notification for the first time.
|
| + data = new NotificationData(notification_id, profile_id, is_incognito);
|
| + data->cancellable.reset(g_cancellable_new());
|
| + notifications_.emplace(data, base::WrapUnique(data));
|
| + NotifyNow(0, notification_type, notification, data->cancellable,
|
| + NotifyCompleteReceiver, data);
|
| + }
|
| }
|
|
|
| void NotificationPlatformBridgeLinux::Close(
|
| const std::string& profile_id,
|
| const std::string& notification_id) {
|
| - NOTIMPLEMENTED();
|
| + NotificationData* data = FindNotificationData(notification_id, profile_id);
|
| + if (!data)
|
| + return;
|
| + if (data->dbus_id) {
|
| + CloseNow(data->dbus_id);
|
| + notifications_.erase(data);
|
| + } else {
|
| + data->should_close = true;
|
| + }
|
| }
|
|
|
| void NotificationPlatformBridgeLinux::GetDisplayed(
|
| @@ -65,3 +139,64 @@ void NotificationPlatformBridgeLinux::GetDisplayed(
|
| const DisplayedNotificationsCallback& callback) const {
|
| callback.Run(base::MakeUnique<std::set<std::string>>(), false);
|
| }
|
| +
|
| +void NotificationPlatformBridgeLinux::NotifyCompleteInternal(gpointer user_data,
|
| + GVariant* value) {
|
| + NotificationData* data = reinterpret_cast<NotificationData*>(user_data);
|
| + if (!base::ContainsKey(notifications_, data))
|
| + return;
|
| + data->cancellable.reset();
|
| + if (value && g_variant_is_of_type(value, G_VARIANT_TYPE("(u)")))
|
| + g_variant_get(value, "(u)", &data->dbus_id);
|
| +
|
| + if (!data->dbus_id) {
|
| + // There was some sort of error with creating the notification.
|
| + notifications_.erase(data);
|
| + } else if (data->should_close) {
|
| + CloseNow(data->dbus_id);
|
| + notifications_.erase(data);
|
| + } else if (data->update_data) {
|
| + NotifyNow(data->dbus_id, data->update_type, *data->update_data, nullptr,
|
| + nullptr, nullptr);
|
| + data->update_data.reset();
|
| + }
|
| +}
|
| +
|
| +void NotificationPlatformBridgeLinux::NotifyNow(
|
| + uint32_t dbus_id,
|
| + NotificationCommon::Type notification_type,
|
| + const Notification& notification,
|
| + GCancellable* cancellable,
|
| + GAsyncReadyCallback callback,
|
| + gpointer user_data) {
|
| + // TODO(thomasanderson): Add a complete implementation.
|
| + const std::string title = base::UTF16ToUTF8(notification.title());
|
| + const std::string message = base::UTF16ToUTF8(notification.message());
|
| + GVariant* parameters =
|
| + g_variant_new("(susssasa{sv}i)", "", dbus_id, "", title.c_str(),
|
| + message.c_str(), nullptr, nullptr, -1);
|
| + g_dbus_proxy_call(notification_proxy_, "Notify", parameters,
|
| + G_DBUS_CALL_FLAGS_NONE, -1, cancellable, callback,
|
| + user_data);
|
| +}
|
| +
|
| +void NotificationPlatformBridgeLinux::CloseNow(uint32_t dbus_id) {
|
| + g_dbus_proxy_call(notification_proxy_, "CloseNotification",
|
| + g_variant_new("(u)", dbus_id), G_DBUS_CALL_FLAGS_NONE, -1,
|
| + nullptr, nullptr, nullptr);
|
| +}
|
| +
|
| +NotificationPlatformBridgeLinux::NotificationData*
|
| +NotificationPlatformBridgeLinux::FindNotificationData(
|
| + const std::string& notification_id,
|
| + const std::string& profile_id) {
|
| + for (const auto& pair : notifications_) {
|
| + NotificationData* data = pair.first;
|
| + if (data->notification_id == notification_id &&
|
| + data->profile_id == profile_id) {
|
| + return data;
|
| + }
|
| + }
|
| +
|
| + return nullptr;
|
| +}
|
|
|