OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/notifications/notification_platform_bridge_linux.h" | 5 #include "chrome/browser/notifications/notification_platform_bridge_linux.h" |
6 | 6 |
| 7 #include <algorithm> |
| 8 |
7 #include "base/memory/ptr_util.h" | 9 #include "base/memory/ptr_util.h" |
| 10 #include "base/stl_util.h" |
8 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 12 #include "chrome/browser/browser_process.h" |
9 #include "chrome/browser/notifications/notification.h" | 13 #include "chrome/browser/notifications/notification.h" |
10 | 14 |
11 namespace { | 15 namespace { |
12 | 16 |
13 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; | 17 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; |
14 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; | 18 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; |
15 | 19 |
| 20 // Callback used by GLib when the "Notify" message completes for the |
| 21 // first time. |
| 22 void NotifyCompleteReceiver(GObject* source_object, |
| 23 GAsyncResult* result, |
| 24 gpointer user_data) { |
| 25 GDBusProxy* proxy = G_DBUS_PROXY(source_object); |
| 26 GVariant* value = g_dbus_proxy_call_finish(proxy, result, nullptr); |
| 27 if (!value) { |
| 28 // The message might have been cancelled, in which case |
| 29 // |user_data| points to a destroyed NotificationData. |
| 30 return; |
| 31 } |
| 32 auto* platform_bridge_linux = static_cast<NotificationPlatformBridgeLinux*>( |
| 33 g_browser_process->notification_platform_bridge()); |
| 34 platform_bridge_linux->NotifyCompleteInternal(user_data, value); |
| 35 } |
| 36 |
16 } // namespace | 37 } // namespace |
17 | 38 |
18 // static | 39 // static |
19 NotificationPlatformBridge* NotificationPlatformBridge::Create() { | 40 NotificationPlatformBridge* NotificationPlatformBridge::Create() { |
20 GDBusProxy* notification_proxy = g_dbus_proxy_new_for_bus_sync( | 41 GDBusProxy* notification_proxy = g_dbus_proxy_new_for_bus_sync( |
21 G_BUS_TYPE_SESSION, | 42 G_BUS_TYPE_SESSION, |
22 static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | | 43 static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | |
23 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | | 44 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | |
24 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), | 45 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), |
25 nullptr, kFreedesktopNotificationsName, kFreedesktopNotificationsPath, | 46 nullptr, kFreedesktopNotificationsName, kFreedesktopNotificationsPath, |
26 kFreedesktopNotificationsName, nullptr, nullptr); | 47 kFreedesktopNotificationsName, nullptr, nullptr); |
27 if (!notification_proxy) | 48 if (!notification_proxy) |
28 return nullptr; | 49 return nullptr; |
29 return new NotificationPlatformBridgeLinux(notification_proxy); | 50 return new NotificationPlatformBridgeLinux(notification_proxy); |
30 } | 51 } |
31 | 52 |
| 53 struct NotificationPlatformBridgeLinux::NotificationData { |
| 54 NotificationData(const std::string& notification_id, |
| 55 const std::string& profile_id, |
| 56 bool is_incognito) |
| 57 : notification_id(notification_id), |
| 58 profile_id(profile_id), |
| 59 is_incognito(is_incognito) {} |
| 60 |
| 61 ~NotificationData() { |
| 62 if (cancellable) |
| 63 g_cancellable_cancel(cancellable); |
| 64 } |
| 65 |
| 66 // The ID used by the notification server. Will be 0 until the |
| 67 // first "Notify" message completes. |
| 68 uint32_t dbus_id = 0; |
| 69 |
| 70 // Same parameters used by NotificationPlatformBridge::Display(). |
| 71 const std::string notification_id; |
| 72 const std::string profile_id; |
| 73 const bool is_incognito; |
| 74 |
| 75 // Used to cancel the initial "Notify" message so we don't call |
| 76 // NotificationPlatformBridgeLinux::NotifyCompleteInternal() with a |
| 77 // destroyed Notification. |
| 78 ScopedGObject<GCancellable> cancellable; |
| 79 |
| 80 // If not null, the data to update the notification with once |
| 81 // |dbus_id| becomes available. |
| 82 std::unique_ptr<Notification> update_data; |
| 83 NotificationCommon::Type update_type = NotificationCommon::TYPE_MAX; |
| 84 |
| 85 // If true, indicates the notification should be closed once |
| 86 // |dbus_id| becomes available. |
| 87 bool should_close = false; |
| 88 }; |
| 89 |
32 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux( | 90 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux( |
33 GDBusProxy* notification_proxy) | 91 GDBusProxy* notification_proxy) |
34 : notification_proxy_(notification_proxy) {} | 92 : notification_proxy_(notification_proxy) {} |
35 | 93 |
36 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() { | 94 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() {} |
37 g_object_unref(notification_proxy_); | |
38 } | |
39 | 95 |
40 void NotificationPlatformBridgeLinux::Display( | 96 void NotificationPlatformBridgeLinux::Display( |
41 NotificationCommon::Type notification_type, | 97 NotificationCommon::Type notification_type, |
42 const std::string& notification_id, | 98 const std::string& notification_id, |
43 const std::string& profile_id, | 99 const std::string& profile_id, |
44 bool is_incognito, | 100 bool is_incognito, |
45 const Notification& notification) { | 101 const Notification& notification) { |
46 // TODO(thomasanderson): Add a complete implementation. | 102 NotificationData* data = FindNotificationData(notification_id, profile_id); |
47 g_dbus_proxy_call( | 103 if (data) { |
48 notification_proxy_, "Notify", | 104 // Update an existing notification. |
49 g_variant_new("(susssasa{sv}i)", "", 0, "", | 105 if (data->dbus_id) { |
50 base::UTF16ToUTF8(notification.title()).c_str(), | 106 NotifyNow(data->dbus_id, notification_type, notification, nullptr, |
51 base::UTF16ToUTF8(notification.message()).c_str(), nullptr, | 107 nullptr, nullptr); |
52 nullptr, -1), | 108 } else { |
53 G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr, nullptr); | 109 data->update_type = notification_type; |
| 110 data->update_data = base::MakeUnique<Notification>(notification); |
| 111 } |
| 112 } else { |
| 113 // Send the notification for the first time. |
| 114 data = new NotificationData(notification_id, profile_id, is_incognito); |
| 115 data->cancellable.reset(g_cancellable_new()); |
| 116 notifications_.emplace(data, base::WrapUnique(data)); |
| 117 NotifyNow(0, notification_type, notification, data->cancellable, |
| 118 NotifyCompleteReceiver, data); |
| 119 } |
54 } | 120 } |
55 | 121 |
56 void NotificationPlatformBridgeLinux::Close( | 122 void NotificationPlatformBridgeLinux::Close( |
57 const std::string& profile_id, | 123 const std::string& profile_id, |
58 const std::string& notification_id) { | 124 const std::string& notification_id) { |
59 NOTIMPLEMENTED(); | 125 NotificationData* data = FindNotificationData(notification_id, profile_id); |
| 126 if (!data) |
| 127 return; |
| 128 if (data->dbus_id) { |
| 129 CloseNow(data->dbus_id); |
| 130 notifications_.erase(data); |
| 131 } else { |
| 132 data->should_close = true; |
| 133 } |
60 } | 134 } |
61 | 135 |
62 void NotificationPlatformBridgeLinux::GetDisplayed( | 136 void NotificationPlatformBridgeLinux::GetDisplayed( |
63 const std::string& profile_id, | 137 const std::string& profile_id, |
64 bool incognito, | 138 bool incognito, |
65 const DisplayedNotificationsCallback& callback) const { | 139 const DisplayedNotificationsCallback& callback) const { |
66 callback.Run(base::MakeUnique<std::set<std::string>>(), false); | 140 callback.Run(base::MakeUnique<std::set<std::string>>(), false); |
67 } | 141 } |
| 142 |
| 143 void NotificationPlatformBridgeLinux::NotifyCompleteInternal(gpointer user_data, |
| 144 GVariant* value) { |
| 145 NotificationData* data = reinterpret_cast<NotificationData*>(user_data); |
| 146 if (!base::ContainsKey(notifications_, data)) |
| 147 return; |
| 148 data->cancellable.reset(); |
| 149 if (value && g_variant_is_of_type(value, G_VARIANT_TYPE("(u)"))) |
| 150 g_variant_get(value, "(u)", &data->dbus_id); |
| 151 |
| 152 if (!data->dbus_id) { |
| 153 // There was some sort of error with creating the notification. |
| 154 notifications_.erase(data); |
| 155 } else if (data->should_close) { |
| 156 CloseNow(data->dbus_id); |
| 157 notifications_.erase(data); |
| 158 } else if (data->update_data) { |
| 159 NotifyNow(data->dbus_id, data->update_type, *data->update_data, nullptr, |
| 160 nullptr, nullptr); |
| 161 data->update_data.reset(); |
| 162 } |
| 163 } |
| 164 |
| 165 void NotificationPlatformBridgeLinux::NotifyNow( |
| 166 uint32_t dbus_id, |
| 167 NotificationCommon::Type notification_type, |
| 168 const Notification& notification, |
| 169 GCancellable* cancellable, |
| 170 GAsyncReadyCallback callback, |
| 171 gpointer user_data) { |
| 172 // TODO(thomasanderson): Add a complete implementation. |
| 173 const std::string title = base::UTF16ToUTF8(notification.title()); |
| 174 const std::string message = base::UTF16ToUTF8(notification.message()); |
| 175 GVariant* parameters = |
| 176 g_variant_new("(susssasa{sv}i)", "", dbus_id, "", title.c_str(), |
| 177 message.c_str(), nullptr, nullptr, -1); |
| 178 g_dbus_proxy_call(notification_proxy_, "Notify", parameters, |
| 179 G_DBUS_CALL_FLAGS_NONE, -1, cancellable, callback, |
| 180 user_data); |
| 181 } |
| 182 |
| 183 void NotificationPlatformBridgeLinux::CloseNow(uint32_t dbus_id) { |
| 184 g_dbus_proxy_call(notification_proxy_, "CloseNotification", |
| 185 g_variant_new("(u)", dbus_id), G_DBUS_CALL_FLAGS_NONE, -1, |
| 186 nullptr, nullptr, nullptr); |
| 187 } |
| 188 |
| 189 NotificationPlatformBridgeLinux::NotificationData* |
| 190 NotificationPlatformBridgeLinux::FindNotificationData( |
| 191 const std::string& notification_id, |
| 192 const std::string& profile_id) { |
| 193 for (const auto& pair : notifications_) { |
| 194 NotificationData* data = pair.first; |
| 195 if (data->notification_id == notification_id && |
| 196 data->profile_id == profile_id) { |
| 197 return data; |
| 198 } |
| 199 } |
| 200 |
| 201 return nullptr; |
| 202 } |
OLD | NEW |