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..a6c4a5546281e51293ef9673cd7986b7340706ba 100644 |
--- a/chrome/browser/notifications/notification_platform_bridge_linux.cc |
+++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc |
@@ -4,8 +4,11 @@ |
#include "chrome/browser/notifications/notification_platform_bridge_linux.h" |
+#include <algorithm> |
+ |
#include "base/memory/ptr_util.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "chrome/browser/browser_process.h" |
#include "chrome/browser/notifications/notification.h" |
namespace { |
@@ -13,6 +16,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 +49,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 +98,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 +138,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 (notifications_.find(data) == notifications_.end()) |
Lei Zhang
2017/04/06 19:59:33
!base::ContainsKey(notifications_, data) ?
Tom (Use chromium acct)
2017/04/06 22:28:56
Done.
|
+ 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; |
+} |