Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(615)

Side by Side Diff: chrome/browser/notifications/notification_platform_bridge_linux.cc

Issue 2805543005: Linux native notifications: Handle clicks and closes (Closed)
Patch Set: Address peter@'s comments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/notifications/notification_platform_bridge_linux.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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> 7 #include <algorithm>
8 8
9 #include "base/memory/ptr_util.h" 9 #include "base/memory/ptr_util.h"
10 #include "base/stl_util.h" 10 #include "base/stl_util.h"
11 #include "base/strings/nullable_string16.h"
11 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/notifications/native_notification_display_service.h"
13 #include "chrome/browser/notifications/notification.h" 15 #include "chrome/browser/notifications/notification.h"
16 #include "chrome/browser/notifications/notification_display_service_factory.h"
17 #include "chrome/browser/profiles/profile_manager.h"
14 18
15 namespace { 19 namespace {
16 20
17 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; 21 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications";
18 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; 22 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications";
19 23
24 void AddActionToNotification(GVariantBuilder* actions_builder,
25 const char* action_id,
26 const char* button_label) {
27 g_variant_builder_add(actions_builder, "s", action_id);
28 g_variant_builder_add(actions_builder, "s", button_label);
29 }
30
20 // Callback used by GLib when the "Notify" message completes for the 31 // Callback used by GLib when the "Notify" message completes for the
21 // first time. 32 // first time.
22 void NotifyCompleteReceiver(GObject* source_object, 33 void NotifyCompleteReceiver(GObject* source_object,
23 GAsyncResult* result, 34 GAsyncResult* result,
24 gpointer user_data) { 35 gpointer user_data) {
25 GDBusProxy* proxy = G_DBUS_PROXY(source_object); 36 GDBusProxy* proxy = G_DBUS_PROXY(source_object);
26 GVariant* value = g_dbus_proxy_call_finish(proxy, result, nullptr); 37 GVariant* value = g_dbus_proxy_call_finish(proxy, result, nullptr);
27 if (!value) { 38 if (!value) {
28 // The message might have been cancelled, in which case 39 // The message might have been cancelled, in which case
29 // |user_data| points to a destroyed NotificationData. 40 // |user_data| points to a destroyed NotificationData.
30 return; 41 return;
31 } 42 }
32 auto* platform_bridge_linux = static_cast<NotificationPlatformBridgeLinux*>( 43 auto* platform_bridge_linux = static_cast<NotificationPlatformBridgeLinux*>(
33 g_browser_process->notification_platform_bridge()); 44 g_browser_process->notification_platform_bridge());
34 platform_bridge_linux->NotifyCompleteInternal(user_data, value); 45 platform_bridge_linux->NotifyCompleteInternal(user_data, value);
35 } 46 }
36 47
48 // Runs once the profile has been loaded in order to perform a given
49 // |operation| on a notification.
50 void ProfileLoadedCallback(NotificationCommon::Operation operation,
51 NotificationCommon::Type notification_type,
52 const std::string& origin,
53 const std::string& notification_id,
54 int action_index,
55 const base::NullableString16& reply,
56 Profile* profile) {
57 if (!profile)
58 return;
59
60 NotificationDisplayService* display_service =
61 NotificationDisplayServiceFactory::GetForProfile(profile);
62
63 static_cast<NativeNotificationDisplayService*>(display_service)
64 ->ProcessNotificationOperation(operation, notification_type, origin,
65 notification_id, action_index, reply);
66 }
67
37 } // namespace 68 } // namespace
38 69
39 // static 70 // static
40 NotificationPlatformBridge* NotificationPlatformBridge::Create() { 71 NotificationPlatformBridge* NotificationPlatformBridge::Create() {
41 GDBusProxy* notification_proxy = g_dbus_proxy_new_for_bus_sync( 72 GDBusProxy* notification_proxy = g_dbus_proxy_new_for_bus_sync(
42 G_BUS_TYPE_SESSION, 73 G_BUS_TYPE_SESSION,
43 static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | 74 static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
44 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
45 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), 75 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
46 nullptr, kFreedesktopNotificationsName, kFreedesktopNotificationsPath, 76 nullptr, kFreedesktopNotificationsName, kFreedesktopNotificationsPath,
47 kFreedesktopNotificationsName, nullptr, nullptr); 77 kFreedesktopNotificationsName, nullptr, nullptr);
48 if (!notification_proxy) 78 if (!notification_proxy)
49 return nullptr; 79 return nullptr;
50 return new NotificationPlatformBridgeLinux(notification_proxy); 80 return new NotificationPlatformBridgeLinux(notification_proxy);
51 } 81 }
52 82
53 struct NotificationPlatformBridgeLinux::NotificationData { 83 struct NotificationPlatformBridgeLinux::NotificationData {
54 NotificationData(const std::string& notification_id, 84 NotificationData(NotificationCommon::Type notification_type,
85 const std::string& notification_id,
55 const std::string& profile_id, 86 const std::string& profile_id,
56 bool is_incognito) 87 bool is_incognito,
57 : notification_id(notification_id), 88 const GURL& origin_url)
89 : notification_type(notification_type),
90 notification_id(notification_id),
58 profile_id(profile_id), 91 profile_id(profile_id),
59 is_incognito(is_incognito) {} 92 is_incognito(is_incognito),
93 origin_url(origin_url) {}
60 94
61 ~NotificationData() { 95 ~NotificationData() {
62 if (cancellable) 96 if (cancellable)
63 g_cancellable_cancel(cancellable); 97 g_cancellable_cancel(cancellable);
64 } 98 }
65 99
66 // The ID used by the notification server. Will be 0 until the 100 // The ID used by the notification server. Will be 0 until the
67 // first "Notify" message completes. 101 // first "Notify" message completes.
68 uint32_t dbus_id = 0; 102 uint32_t dbus_id = 0;
69 103
70 // Same parameters used by NotificationPlatformBridge::Display(). 104 // Same parameters used by NotificationPlatformBridge::Display().
105 const NotificationCommon::Type notification_type;
71 const std::string notification_id; 106 const std::string notification_id;
72 const std::string profile_id; 107 const std::string profile_id;
73 const bool is_incognito; 108 const bool is_incognito;
74 109
110 // A copy of the origin_url from the underlying
111 // message_center::Notification. Used to pass back to
112 // NativeNotificationDisplayService.
113 const GURL origin_url;
114
75 // Used to cancel the initial "Notify" message so we don't call 115 // Used to cancel the initial "Notify" message so we don't call
76 // NotificationPlatformBridgeLinux::NotifyCompleteInternal() with a 116 // NotificationPlatformBridgeLinux::NotifyCompleteInternal() with a
77 // destroyed Notification. 117 // destroyed Notification.
78 ScopedGObject<GCancellable> cancellable; 118 ScopedGObject<GCancellable> cancellable;
79 119
80 // If not null, the data to update the notification with once 120 // If not null, the data to update the notification with once
81 // |dbus_id| becomes available. 121 // |dbus_id| becomes available.
82 std::unique_ptr<Notification> update_data; 122 std::unique_ptr<Notification> update_data;
83 NotificationCommon::Type update_type = NotificationCommon::TYPE_MAX;
84 123
85 // If true, indicates the notification should be closed once 124 // If true, indicates the notification should be closed once
86 // |dbus_id| becomes available. 125 // |dbus_id| becomes available.
87 bool should_close = false; 126 bool should_close = false;
88 }; 127 };
89 128
90 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux( 129 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux(
91 GDBusProxy* notification_proxy) 130 GDBusProxy* notification_proxy)
92 : notification_proxy_(notification_proxy) {} 131 : notification_proxy_(notification_proxy) {
132 proxy_signal_handler_ = g_signal_connect(
133 notification_proxy_, "g-signal", G_CALLBACK(GSignalReceiverThunk), this);
134 }
93 135
94 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() {} 136 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() {
137 if (proxy_signal_handler_)
138 g_signal_handler_disconnect(notification_proxy_, proxy_signal_handler_);
139 }
95 140
96 void NotificationPlatformBridgeLinux::Display( 141 void NotificationPlatformBridgeLinux::Display(
97 NotificationCommon::Type notification_type, 142 NotificationCommon::Type notification_type,
98 const std::string& notification_id, 143 const std::string& notification_id,
99 const std::string& profile_id, 144 const std::string& profile_id,
100 bool is_incognito, 145 bool is_incognito,
101 const Notification& notification) { 146 const Notification& notification) {
102 NotificationData* data = FindNotificationData(notification_id, profile_id); 147 NotificationData* data =
148 FindNotificationData(notification_id, profile_id, is_incognito);
103 if (data) { 149 if (data) {
104 // Update an existing notification. 150 // Update an existing notification.
151 DCHECK_EQ(data->notification_type, notification_type);
105 if (data->dbus_id) { 152 if (data->dbus_id) {
106 NotifyNow(data->dbus_id, notification_type, notification, nullptr, 153 NotifyNow(data->dbus_id, notification_type, notification, nullptr,
107 nullptr, nullptr); 154 nullptr, nullptr);
108 } else { 155 } else {
109 data->update_type = notification_type;
110 data->update_data = base::MakeUnique<Notification>(notification); 156 data->update_data = base::MakeUnique<Notification>(notification);
111 } 157 }
112 } else { 158 } else {
113 // Send the notification for the first time. 159 // Send the notification for the first time.
114 data = new NotificationData(notification_id, profile_id, is_incognito); 160 data = new NotificationData(notification_type, notification_id, profile_id,
161 is_incognito, notification.origin_url());
115 data->cancellable.reset(g_cancellable_new()); 162 data->cancellable.reset(g_cancellable_new());
116 notifications_.emplace(data, base::WrapUnique(data)); 163 notifications_.emplace(data, base::WrapUnique(data));
117 NotifyNow(0, notification_type, notification, data->cancellable, 164 NotifyNow(0, notification_type, notification, data->cancellable,
118 NotifyCompleteReceiver, data); 165 NotifyCompleteReceiver, data);
119 } 166 }
120 } 167 }
121 168
122 void NotificationPlatformBridgeLinux::Close( 169 void NotificationPlatformBridgeLinux::Close(
123 const std::string& profile_id, 170 const std::string& profile_id,
124 const std::string& notification_id) { 171 const std::string& notification_id) {
125 NotificationData* data = FindNotificationData(notification_id, profile_id); 172 std::vector<NotificationData*> to_erase;
126 if (!data) 173 for (const auto& pair : notifications_) {
127 return; 174 NotificationData* data = pair.first;
128 if (data->dbus_id) { 175 if (data->notification_id == notification_id &&
129 CloseNow(data->dbus_id); 176 data->profile_id == profile_id) {
177 if (data->dbus_id) {
178 CloseNow(data->dbus_id);
179 to_erase.push_back(data);
180 } else {
181 data->should_close = true;
182 }
183 }
184 }
Peter Beverloo 2017/04/07 20:41:37 Filed crbug.com/709608 and will fix + cleanup.
185 for (NotificationData* data : to_erase)
130 notifications_.erase(data); 186 notifications_.erase(data);
131 } else {
132 data->should_close = true;
133 }
134 } 187 }
135 188
136 void NotificationPlatformBridgeLinux::GetDisplayed( 189 void NotificationPlatformBridgeLinux::GetDisplayed(
137 const std::string& profile_id, 190 const std::string& profile_id,
138 bool incognito, 191 bool incognito,
139 const DisplayedNotificationsCallback& callback) const { 192 const DisplayedNotificationsCallback& callback) const {
140 callback.Run(base::MakeUnique<std::set<std::string>>(), false); 193 callback.Run(base::MakeUnique<std::set<std::string>>(), false);
141 } 194 }
142 195
143 void NotificationPlatformBridgeLinux::NotifyCompleteInternal(gpointer user_data, 196 void NotificationPlatformBridgeLinux::NotifyCompleteInternal(gpointer user_data,
144 GVariant* value) { 197 GVariant* value) {
145 NotificationData* data = reinterpret_cast<NotificationData*>(user_data); 198 NotificationData* data = reinterpret_cast<NotificationData*>(user_data);
146 if (!base::ContainsKey(notifications_, data)) 199 if (!base::ContainsKey(notifications_, data))
147 return; 200 return;
148 data->cancellable.reset(); 201 data->cancellable.reset();
149 if (value && g_variant_is_of_type(value, G_VARIANT_TYPE("(u)"))) 202 if (value && g_variant_is_of_type(value, G_VARIANT_TYPE("(u)")))
150 g_variant_get(value, "(u)", &data->dbus_id); 203 g_variant_get(value, "(u)", &data->dbus_id);
151 204
152 if (!data->dbus_id) { 205 if (!data->dbus_id) {
153 // There was some sort of error with creating the notification. 206 // There was some sort of error with creating the notification.
154 notifications_.erase(data); 207 notifications_.erase(data);
155 } else if (data->should_close) { 208 } else if (data->should_close) {
156 CloseNow(data->dbus_id); 209 CloseNow(data->dbus_id);
157 notifications_.erase(data); 210 notifications_.erase(data);
158 } else if (data->update_data) { 211 } else if (data->update_data) {
159 NotifyNow(data->dbus_id, data->update_type, *data->update_data, nullptr, 212 NotifyNow(data->dbus_id, data->notification_type, *data->update_data,
160 nullptr, nullptr); 213 nullptr, nullptr, nullptr);
161 data->update_data.reset(); 214 data->update_data.reset();
162 } 215 }
163 } 216 }
164 217
165 void NotificationPlatformBridgeLinux::NotifyNow( 218 void NotificationPlatformBridgeLinux::NotifyNow(
166 uint32_t dbus_id, 219 uint32_t dbus_id,
167 NotificationCommon::Type notification_type, 220 NotificationCommon::Type notification_type,
168 const Notification& notification, 221 const Notification& notification,
169 GCancellable* cancellable, 222 GCancellable* cancellable,
170 GAsyncReadyCallback callback, 223 GAsyncReadyCallback callback,
171 gpointer user_data) { 224 gpointer user_data) {
172 // TODO(thomasanderson): Add a complete implementation. 225 // TODO(thomasanderson): Add a complete implementation.
226
227 GVariantBuilder actions_builder;
228 // Even-indexed elements in this array are action IDs passed back to
229 // us in GSignalReceiver. Odd-indexed ones contain the button text.
230 g_variant_builder_init(&actions_builder, G_VARIANT_TYPE("as"));
231 if (notification.clickable()) {
232 // Special case: the pair ("default", "") will not add a button,
233 // but instead makes the entire notification clickable.
234 AddActionToNotification(&actions_builder, "default", "");
235 }
236 // Always add a settings button.
237 AddActionToNotification(&actions_builder, "settings", "Settings");
238
173 const std::string title = base::UTF16ToUTF8(notification.title()); 239 const std::string title = base::UTF16ToUTF8(notification.title());
174 const std::string message = base::UTF16ToUTF8(notification.message()); 240 const std::string message = base::UTF16ToUTF8(notification.message());
241
175 GVariant* parameters = 242 GVariant* parameters =
176 g_variant_new("(susssasa{sv}i)", "", dbus_id, "", title.c_str(), 243 g_variant_new("(susssasa{sv}i)", "", dbus_id, "", title.c_str(),
177 message.c_str(), nullptr, nullptr, -1); 244 message.c_str(), &actions_builder, nullptr, -1);
178 g_dbus_proxy_call(notification_proxy_, "Notify", parameters, 245 g_dbus_proxy_call(notification_proxy_, "Notify", parameters,
179 G_DBUS_CALL_FLAGS_NONE, -1, cancellable, callback, 246 G_DBUS_CALL_FLAGS_NONE, -1, cancellable, callback,
180 user_data); 247 user_data);
181 } 248 }
182 249
183 void NotificationPlatformBridgeLinux::CloseNow(uint32_t dbus_id) { 250 void NotificationPlatformBridgeLinux::CloseNow(uint32_t dbus_id) {
184 g_dbus_proxy_call(notification_proxy_, "CloseNotification", 251 g_dbus_proxy_call(notification_proxy_, "CloseNotification",
185 g_variant_new("(u)", dbus_id), G_DBUS_CALL_FLAGS_NONE, -1, 252 g_variant_new("(u)", dbus_id), G_DBUS_CALL_FLAGS_NONE, -1,
186 nullptr, nullptr, nullptr); 253 nullptr, nullptr, nullptr);
187 } 254 }
188 255
189 NotificationPlatformBridgeLinux::NotificationData* 256 NotificationPlatformBridgeLinux::NotificationData*
190 NotificationPlatformBridgeLinux::FindNotificationData( 257 NotificationPlatformBridgeLinux::FindNotificationData(
191 const std::string& notification_id, 258 const std::string& notification_id,
192 const std::string& profile_id) { 259 const std::string& profile_id,
260 bool is_incognito) {
193 for (const auto& pair : notifications_) { 261 for (const auto& pair : notifications_) {
194 NotificationData* data = pair.first; 262 NotificationData* data = pair.first;
195 if (data->notification_id == notification_id && 263 if (data->notification_id == notification_id &&
196 data->profile_id == profile_id) { 264 data->profile_id == profile_id && data->is_incognito == is_incognito) {
197 return data; 265 return data;
198 } 266 }
199 } 267 }
200 268
201 return nullptr; 269 return nullptr;
202 } 270 }
271
272 NotificationPlatformBridgeLinux::NotificationData*
273 NotificationPlatformBridgeLinux::FindNotificationData(uint32_t dbus_id) {
274 for (const auto& pair : notifications_) {
275 NotificationData* data = pair.first;
276 if (data->dbus_id == dbus_id)
277 return data;
278 }
279
280 return nullptr;
281 }
282
283 void NotificationPlatformBridgeLinux::ForwardNotificationOperation(
284 uint32_t dbus_id,
285 NotificationCommon::Operation operation,
286 int action_index) {
287 NotificationData* data = FindNotificationData(dbus_id);
288 if (!data) {
289 // This notification either belongs to a different app or we
290 // already removed the NotificationData after sending a
291 // "CloseNotification" message.
292 return;
293 }
294
295 ProfileManager* profile_manager = g_browser_process->profile_manager();
296 DCHECK(profile_manager);
297
298 profile_manager->LoadProfile(
299 data->profile_id, data->is_incognito,
300 base::Bind(&ProfileLoadedCallback, operation, data->notification_type,
301 data->origin_url.spec(), data->notification_id, action_index,
302 base::NullableString16()));
303 }
304
305 void NotificationPlatformBridgeLinux::GSignalReceiver(GDBusProxy* proxy,
306 const char* sender_name,
307 const char* sender_signal,
308 GVariant* parameters) {
309 uint32_t dbus_id = 0;
310 if (strcmp("NotificationClosed", sender_signal) == 0 &&
311 g_variant_is_of_type(parameters, G_VARIANT_TYPE("(uu)"))) {
312 uint32_t reason;
313 g_variant_get(parameters, "(uu)", &dbus_id, &reason);
314 ForwardNotificationOperation(dbus_id, NotificationCommon::CLOSE, -1);
315 // std::unordered_map::erase(nullptr) is safe here.
316 notifications_.erase(FindNotificationData(dbus_id));
317 } else if (strcmp("ActionInvoked", sender_signal) == 0 &&
318 g_variant_is_of_type(parameters, G_VARIANT_TYPE("(us)"))) {
319 const gchar* action = nullptr;
320 g_variant_get(parameters, "(u&s)", &dbus_id, &action);
321 DCHECK(action);
322
323 if (strcmp(action, "default") == 0) {
324 ForwardNotificationOperation(dbus_id, NotificationCommon::CLICK, 0);
325 } else if (strcmp(action, "settings") == 0) {
326 ForwardNotificationOperation(dbus_id, NotificationCommon::SETTINGS, -1);
327 } else {
328 NOTIMPLEMENTED() << "No custom buttons just yet!";
329 }
330 }
331 }
OLDNEW
« no previous file with comments | « chrome/browser/notifications/notification_platform_bridge_linux.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698