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

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

Issue 2821533003: Refactor NotificationPlatformBridgeLinux (Closed)
Patch Set: add additional 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
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/files/file_util.h" 9 #include "base/files/file_util.h"
10 #include "base/memory/ptr_util.h" 10 #include "base/run_loop.h"
11 #include "base/stl_util.h"
12 #include "base/strings/nullable_string16.h" 11 #include "base/strings/nullable_string16.h"
13 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h" 13 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h" 14 #include "base/strings/utf_string_conversions.h"
15 #include "base/synchronization/lock.h"
16 #include "base/task_scheduler/post_task.h" 16 #include "base/task_scheduler/post_task.h"
17 #include "chrome/browser/browser_process.h" 17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h" 18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/notifications/native_notification_display_service.h" 19 #include "chrome/browser/notifications/native_notification_display_service.h"
20 #include "chrome/browser/notifications/notification.h" 20 #include "chrome/browser/notifications/notification.h"
21 #include "chrome/browser/notifications/notification_display_service_factory.h" 21 #include "chrome/browser/notifications/notification_display_service_factory.h"
22 #include "chrome/browser/profiles/profile_manager.h" 22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/browser/shell_integration_linux.h" 23 #include "chrome/browser/shell_integration_linux.h"
24 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/notification_service.h" 25 #include "content/public/browser/notification_service.h"
26 #include "dbus/bus.h"
27 #include "dbus/message.h"
28 #include "dbus/object_proxy.h"
29 #include "ui/gfx/image/image_skia.h"
25 30
26 namespace { 31 namespace {
27 32
28 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; 33 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications";
29 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; 34 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications";
30 35
31 void AddActionToNotification(GVariantBuilder* actions_builder, 36 gfx::Image DeepCopyImage(const gfx::Image& image) {
32 const char* action_id, 37 std::unique_ptr<gfx::ImageSkia> image_skia(image.CopyImageSkia());
33 const char* button_label) { 38 return gfx::Image(*image_skia);
34 g_variant_builder_add(actions_builder, "s", action_id);
35 g_variant_builder_add(actions_builder, "s", button_label);
36 } 39 }
37 40
38 int NotificationPriorityToFdoUrgency(int priority) { 41 int NotificationPriorityToFdoUrgency(int priority) {
39 enum FdoUrgency { 42 enum FdoUrgency {
40 LOW = 0, 43 LOW = 0,
41 NORMAL = 1, 44 NORMAL = 1,
42 CRITICAL = 2, 45 CRITICAL = 2,
43 }; 46 };
44 switch (priority) { 47 switch (priority) {
45 case message_center::MIN_PRIORITY: 48 case message_center::MIN_PRIORITY:
46 case message_center::LOW_PRIORITY: 49 case message_center::LOW_PRIORITY:
47 return LOW; 50 return LOW;
48 case message_center::HIGH_PRIORITY: 51 case message_center::HIGH_PRIORITY:
49 case message_center::MAX_PRIORITY: 52 case message_center::MAX_PRIORITY:
50 return CRITICAL; 53 return CRITICAL;
51 default: 54 default:
52 NOTREACHED(); 55 NOTREACHED();
53 // fallthrough
54 case message_center::DEFAULT_PRIORITY: 56 case message_center::DEFAULT_PRIORITY:
55 return NORMAL; 57 return NORMAL;
56 } 58 }
57 } 59 }
58 60
59 // Callback used by GLib when the "Notify" message completes for the
60 // first time.
61 void NotifyCompleteReceiver(GObject* source_object,
62 GAsyncResult* result,
63 gpointer user_data) {
64 GDBusProxy* proxy = G_DBUS_PROXY(source_object);
65 GVariant* value = g_dbus_proxy_call_finish(proxy, result, nullptr);
66 if (!value) {
67 // The message might have been cancelled, in which case
68 // |user_data| points to a destroyed NotificationData.
69 return;
70 }
71 auto* platform_bridge_linux = static_cast<NotificationPlatformBridgeLinux*>(
72 g_browser_process->notification_platform_bridge());
73 platform_bridge_linux->NotifyCompleteInternal(user_data, value);
74 }
75
76 // Runs once the profile has been loaded in order to perform a given 61 // Runs once the profile has been loaded in order to perform a given
77 // |operation| on a notification. 62 // |operation| on a notification.
78 void ProfileLoadedCallback(NotificationCommon::Operation operation, 63 void ProfileLoadedCallback(NotificationCommon::Operation operation,
79 NotificationCommon::Type notification_type, 64 NotificationCommon::Type notification_type,
80 const std::string& origin, 65 const std::string& origin,
81 const std::string& notification_id, 66 const std::string& notification_id,
82 int action_index, 67 int action_index,
83 const base::NullableString16& reply, 68 const base::NullableString16& reply,
84 Profile* profile) { 69 Profile* profile) {
85 if (!profile) 70 if (!profile)
(...skipping 18 matching lines...) Expand all
104 if (!base::CreateTemporaryFile(&file_path)) 89 if (!base::CreateTemporaryFile(&file_path))
105 return base::FilePath(); 90 return base::FilePath();
106 if (base::WriteFile(file_path, data->front_as<char>(), data_len) != 91 if (base::WriteFile(file_path, data->front_as<char>(), data_len) !=
107 data_len) { 92 data_len) {
108 base::DeleteFile(file_path, false); 93 base::DeleteFile(file_path, false);
109 return base::FilePath(); 94 return base::FilePath();
110 } 95 }
111 return file_path; 96 return file_path;
112 } 97 }
113 98
114 void DeleteNotificationResourceFile(const base::FilePath& file_path) {
115 if (file_path.empty())
116 return;
117 base::PostTaskWithTraits(
118 FROM_HERE,
119 base::TaskTraits()
120 .MayBlock()
121 .WithPriority(base::TaskPriority::BACKGROUND)
122 .WithShutdownBehavior(base::TaskShutdownBehavior::BLOCK_SHUTDOWN),
123 base::BindOnce(base::IgnoreResult(base::DeleteFile), file_path, false));
124 }
125
126 } // namespace 99 } // namespace
127 100
128 // static 101 // static
129 NotificationPlatformBridge* NotificationPlatformBridge::Create() { 102 NotificationPlatformBridge* NotificationPlatformBridge::Create() {
130 GDBusProxy* notification_proxy = g_dbus_proxy_new_for_bus_sync( 103 return new NotificationPlatformBridgeLinux();
131 G_BUS_TYPE_SESSION,
132 static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
133 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
134 nullptr, kFreedesktopNotificationsName, kFreedesktopNotificationsPath,
135 kFreedesktopNotificationsName, nullptr, nullptr);
136 if (!notification_proxy)
137 return nullptr;
138 return new NotificationPlatformBridgeLinux(notification_proxy);
139 } 104 }
140 105
106 struct NotificationPlatformBridgeLinux::ResourceFile {
107 explicit ResourceFile(const base::FilePath& file_path)
108 : file_path(file_path) {}
109 ~ResourceFile() { base::DeleteFile(file_path, false); }
110 const base::FilePath file_path;
111 };
112
141 struct NotificationPlatformBridgeLinux::NotificationData { 113 struct NotificationPlatformBridgeLinux::NotificationData {
142 NotificationData(NotificationCommon::Type notification_type, 114 NotificationData(NotificationCommon::Type notification_type,
143 const std::string& notification_id, 115 const std::string& notification_id,
144 const std::string& profile_id, 116 const std::string& profile_id,
145 bool is_incognito, 117 bool is_incognito,
146 const GURL& origin_url) 118 const GURL& origin_url)
147 : notification_type(notification_type), 119 : notification_type(notification_type),
148 notification_id(notification_id), 120 notification_id(notification_id),
149 profile_id(profile_id), 121 profile_id(profile_id),
150 is_incognito(is_incognito), 122 is_incognito(is_incognito),
151 origin_url(origin_url), 123 origin_url(origin_url) {}
152 weak_factory(this) {}
153
154 ~NotificationData() {
155 if (cancellable)
156 g_cancellable_cancel(cancellable);
157 ResetResourceFiles();
158 }
159
160 void ResetResourceFiles() {
161 for (const base::FilePath& file : resource_files)
162 DeleteNotificationResourceFile(file);
163 resource_files.clear();
164 }
165 124
166 // The ID used by the notification server. Will be 0 until the 125 // The ID used by the notification server. Will be 0 until the
167 // first "Notify" message completes. 126 // first "Notify" message completes.
168 uint32_t dbus_id = 0; 127 uint32_t dbus_id = 0;
169 128
170 // Same parameters used by NotificationPlatformBridge::Display(). 129 // Same parameters used by NotificationPlatformBridge::Display().
171 NotificationCommon::Type notification_type; 130 NotificationCommon::Type notification_type;
172 const std::string notification_id; 131 const std::string notification_id;
173 const std::string profile_id; 132 const std::string profile_id;
174 const bool is_incognito; 133 const bool is_incognito;
175 134
176 // A copy of the origin_url from the underlying 135 // A copy of the origin_url from the underlying
177 // message_center::Notification. Used to pass back to 136 // message_center::Notification. Used to pass back to
178 // NativeNotificationDisplayService. 137 // NativeNotificationDisplayService.
179 const GURL origin_url; 138 const GURL origin_url;
180 139
181 // Used to keep track of the IDs of the buttons currently displayed 140 // Used to keep track of the IDs of the buttons currently displayed
182 // on this notification. The valid range of action IDs is 141 // on this notification. The valid range of action IDs is
183 // [action_start, action_end). 142 // [action_start, action_end).
184 size_t action_start = 0; 143 size_t action_start = 0;
185 size_t action_end = 0; 144 size_t action_end = 0;
186 145
187 // Temporary resource files associated with the notification that 146 // Temporary resource files associated with the notification that
188 // should be cleaned up when the notification is closed or on 147 // should be cleaned up when the notification is closed or on
189 // shutdown. 148 // shutdown.
190 std::vector<base::FilePath> resource_files; 149 std::vector<std::unique_ptr<ResourceFile>> resource_files;
191
192 // Used to cancel the initial "Notify" message so we don't call
193 // NotificationPlatformBridgeLinux::NotifyCompleteInternal() with a
194 // destroyed Notification.
195 ScopedGObject<GCancellable> cancellable;
196
197 // If not null, the data to update the notification with once
198 // |dbus_id| becomes available.
199 std::unique_ptr<Notification> update_data;
200 NotificationCommon::Type update_notification_type =
201 NotificationCommon::TYPE_MAX;
202
203 // If true, indicates the notification should be closed once
204 // |dbus_id| becomes available.
205 bool should_close = false;
206
207 base::WeakPtrFactory<NotificationData> weak_factory;
208 }; 150 };
209 151
210 struct NotificationPlatformBridgeLinux::ResourceFiles { 152 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux() {
211 explicit ResourceFiles(const base::FilePath& icon_file) 153 content::BrowserThread::PostTask(
212 : icon_file(icon_file) {} 154 content::BrowserThread::FILE, FROM_HERE,
213 ~ResourceFiles() { DeleteNotificationResourceFile(icon_file); } 155 base::BindOnce(&NotificationPlatformBridgeLinux::Init,
214 base::FilePath icon_file; 156 base::Unretained(this)));
Lei Zhang 2017/04/21 22:45:18 Do we know for sure if |this| will still be valid
Tom (Use chromium acct) 2017/04/24 20:46:14 Made NPBLI ref counted, so we can be sure it's sti
215 };
216
217 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux(
218 GDBusProxy* notification_proxy)
219 : notification_proxy_(notification_proxy), weak_factory_(this) {
220 proxy_signal_handler_ = g_signal_connect(
221 notification_proxy_, "g-signal", G_CALLBACK(GSignalReceiverThunk), this);
222 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
223 content::NotificationService::AllSources());
224 } 157 }
225 158
226 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() { 159 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() {
227 if (proxy_signal_handler_) 160 CleanUp();
Lei Zhang 2017/04/21 22:45:17 Since NotificationPlatformBridgeLinux hangs off of
Tom (Use chromium acct) 2017/04/24 20:46:14 No. Changed this to DCHECK instead
228 g_signal_handler_disconnect(notification_proxy_, proxy_signal_handler_);
229 } 161 }
230 162
231 void NotificationPlatformBridgeLinux::Display( 163 void NotificationPlatformBridgeLinux::Display(
232 NotificationCommon::Type notification_type, 164 NotificationCommon::Type notification_type,
233 const std::string& notification_id, 165 const std::string& notification_id,
234 const std::string& profile_id, 166 const std::string& profile_id,
235 bool is_incognito, 167 bool is_incognito,
236 const Notification& notification) { 168 const Notification& notification) {
Lei Zhang 2017/04/21 22:45:18 Can we check we are on the UI thread here? Ditto b
Tom (Use chromium acct) 2017/04/24 20:46:14 Done.
237 NotificationData* data = 169 auto notification_copy = base::MakeUnique<Notification>(notification);
238 FindNotificationData(notification_id, profile_id, is_incognito); 170 notification_copy->set_icon(DeepCopyImage(notification_copy->icon()));
239 if (data) { 171 notification_copy->set_image(gfx::Image());
240 // Update an existing notification. 172 notification_copy->set_small_image(gfx::Image());
241 if (data->dbus_id) { 173 for (size_t i = 0; i < notification_copy->buttons().size(); i++)
242 data->notification_type = notification_type; 174 notification_copy->SetButtonIcon(i, gfx::Image());
243 Notify(notification, data, nullptr, nullptr); 175
244 } else { 176 content::BrowserThread::PostTask(
245 data->update_notification_type = notification_type; 177 content::BrowserThread::FILE, FROM_HERE,
246 data->update_data = base::MakeUnique<Notification>(notification); 178 base::Bind(&NotificationPlatformBridgeLinux::DisplayNow,
247 } 179 base::Unretained(this), notification_type, notification_id,
Lei Zhang 2017/04/21 22:45:17 Similar question re: base::Unretained() usage. Dit
Tom (Use chromium acct) 2017/04/24 20:46:14 Done.
248 } else { 180 profile_id, is_incognito, base::Passed(&notification_copy)));
249 // Send the notification for the first time.
250 data = new NotificationData(notification_type, notification_id, profile_id,
251 is_incognito, notification.origin_url());
252 data->cancellable.reset(g_cancellable_new());
253 notifications_.emplace(data, base::WrapUnique(data));
254 Notify(notification, data, NotifyCompleteReceiver, data);
255 }
256 } 181 }
257 182
258 void NotificationPlatformBridgeLinux::Close( 183 void NotificationPlatformBridgeLinux::Close(
259 const std::string& profile_id, 184 const std::string& profile_id,
260 const std::string& notification_id) { 185 const std::string& notification_id) {
261 std::vector<NotificationData*> to_erase; 186 content::BrowserThread::PostTask(
262 for (const auto& pair : notifications_) { 187 content::BrowserThread::FILE, FROM_HERE,
263 NotificationData* data = pair.first; 188 base::Bind(&NotificationPlatformBridgeLinux::CloseNow,
264 if (data->notification_id == notification_id && 189 base::Unretained(this), profile_id, notification_id));
265 data->profile_id == profile_id) {
266 if (data->dbus_id) {
267 CloseNow(data->dbus_id);
268 to_erase.push_back(data);
269 } else {
270 data->should_close = true;
271 }
272 }
273 }
274 for (NotificationData* data : to_erase)
275 notifications_.erase(data);
276 } 190 }
277 191
278 void NotificationPlatformBridgeLinux::GetDisplayed( 192 void NotificationPlatformBridgeLinux::GetDisplayed(
279 const std::string& profile_id, 193 const std::string& profile_id,
280 bool incognito, 194 bool incognito,
281 const GetDisplayedNotificationsCallback& callback) const { 195 const GetDisplayedNotificationsCallback& callback) const {
282 // TODO(thomasanderson): implement. 196 content::BrowserThread::PostTask(
283 callback.Run(base::MakeUnique<std::set<std::string>>(), false); 197 content::BrowserThread::FILE, FROM_HERE,
198 base::Bind(&NotificationPlatformBridgeLinux::GetDisplayedNow,
199 base::Unretained(this), profile_id, incognito, callback));
284 } 200 }
285 201
286 void NotificationPlatformBridgeLinux::NotifyCompleteInternal(gpointer user_data, 202 void NotificationPlatformBridgeLinux::IsConnected(
287 GVariant* value) { 203 base::OnceCallback<void(bool)> callback) {
288 NotificationData* data = reinterpret_cast<NotificationData*>(user_data); 204 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
289 if (!base::ContainsKey(notifications_, data)) 205 base::AutoLock lock(connected_lock_);
290 return; 206 if (connected_.has_value()) {
291 data->cancellable.reset(); 207 content::BrowserThread::PostTask(
Lei Zhang 2017/04/21 22:45:17 If you PostTaskAndReply(WithResult) the you may be
Tom (Use chromium acct) 2017/04/24 20:46:14 Not sure I understand. The lock protects connecte
Lei Zhang 2017/04/25 18:55:57 To be clear, I was thinking if you always accessed
Tom (Use chromium acct) 2017/04/26 03:12:28 Oh I see what you mean now. Done.
292 if (value && g_variant_is_of_type(value, G_VARIANT_TYPE("(u)"))) 208 content::BrowserThread::UI, FROM_HERE,
293 g_variant_get(value, "(u)", &data->dbus_id); 209 base::BindOnce(std::move(callback), connected_.value()));
294 210 } else {
295 if (!data->dbus_id) { 211 on_connected_callbacks_.push_back(std::move(callback));
296 // There was some sort of error with creating the notification.
297 notifications_.erase(data);
298 } else if (data->should_close) {
299 CloseNow(data->dbus_id);
300 notifications_.erase(data);
301 } else if (data->update_data) {
302 data->notification_type = data->update_notification_type;
303 Notify(*data->update_data, data, nullptr, nullptr);
304 data->update_data.reset();
305 } 212 }
306 } 213 }
307 214
308 void NotificationPlatformBridgeLinux::Observe( 215 void NotificationPlatformBridgeLinux::Observe(
309 int type, 216 int type,
310 const content::NotificationSource& source, 217 const content::NotificationSource& source,
311 const content::NotificationDetails& details) { 218 const content::NotificationDetails& details) {
312 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); 219 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
313 // The browser process is about to exit. Clean up all notification 220 // The browser process is about to exit. Post the CleanUp() task
314 // resource files. 221 // while we still can.
222 CleanUp();
223 }
224
225 void NotificationPlatformBridgeLinux::Init() {
226 dbus::Bus::Options bus_options;
227 bus_options.bus_type = dbus::Bus::SESSION;
228 bus_options.connection_type = dbus::Bus::PRIVATE;
229 bus_ = make_scoped_refptr(new dbus::Bus(bus_options));
230
231 notification_proxy_ =
232 bus_->GetObjectProxy(kFreedesktopNotificationsName,
233 dbus::ObjectPath(kFreedesktopNotificationsPath));
234 OnConnected(notification_proxy_);
Lei Zhang 2017/04/21 22:45:17 So if |notification_proxy_| is NULL, then we defin
Tom (Use chromium acct) 2017/04/24 20:46:14 Done.
235 if (!notification_proxy_)
236 return;
237
238 auto on_signal_connected = [](const std::string&, const std::string&,
239 bool success) { DCHECK(success); };
240 notification_proxy_->ConnectToSignal(
241 kFreedesktopNotificationsName, "ActionInvoked",
242 base::Bind(&NotificationPlatformBridgeLinux::OnActionInvoked,
243 base::Unretained(this)),
244 base::Bind(on_signal_connected));
245 notification_proxy_->ConnectToSignal(
246 kFreedesktopNotificationsName, "NotificationClosed",
247 base::Bind(&NotificationPlatformBridgeLinux::OnNotificationClosed,
248 base::Unretained(this)),
249 base::Bind(on_signal_connected));
250 }
251
252 void NotificationPlatformBridgeLinux::CleanUp() {
253 content::BrowserThread::PostTask(
254 content::BrowserThread::FILE, FROM_HERE,
255 base::BindOnce(&NotificationPlatformBridgeLinux::CleanUpInternal,
256 base::Unretained(this)));
257 }
258
259 void NotificationPlatformBridgeLinux::CleanUpInternal() {
260 bus_->ShutdownAndBlock();
261 bus_ = nullptr;
262 notification_proxy_ = nullptr;
315 notifications_.clear(); 263 notifications_.clear();
316 } 264 }
317 265
318 void NotificationPlatformBridgeLinux::Notify(const Notification& notification, 266 void NotificationPlatformBridgeLinux::DisplayNow(
319 NotificationData* data, 267 NotificationCommon::Type notification_type,
320 GAsyncReadyCallback callback, 268 const std::string& notification_id,
321 gpointer user_data) { 269 const std::string& profile_id,
322 const scoped_refptr<base::RefCountedMemory> icon_data = 270 bool is_incognito,
323 notification.icon().As1xPNGBytes(); 271 std::unique_ptr<Notification> notification) {
324 if (!icon_data->size()) { 272 NotificationData* data =
325 NotifyNow(notification, data->weak_factory.GetWeakPtr(), callback, 273 FindNotificationData(notification_id, profile_id, is_incognito);
326 user_data, base::MakeUnique<ResourceFiles>(base::FilePath())); 274 if (data) {
275 // Update an existing notification.
276 data->notification_type = notification_type;
277 data->resource_files.clear();
327 } else { 278 } else {
328 base::PostTaskWithTraitsAndReplyWithResult( 279 // Send the notification for the first time.
329 FROM_HERE, 280 data = new NotificationData(notification_type, notification_id, profile_id,
330 base::TaskTraits() 281 is_incognito, notification->origin_url());
331 .MayBlock() 282 notifications_.emplace(data, base::WrapUnique(data));
332 .WithPriority(base::TaskPriority::USER_BLOCKING)
333 .WithShutdownBehavior(base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN),
334 base::Bind(
335 [](scoped_refptr<base::RefCountedMemory> icon) {
336 return base::MakeUnique<ResourceFiles>(WriteDataToTmpFile(icon));
337 },
338 icon_data),
339 base::Bind(&NotificationPlatformBridgeLinux::NotifyNow,
340 weak_factory_.GetWeakPtr(), notification,
341 data->weak_factory.GetWeakPtr(), callback, user_data));
342 } 283 }
343 }
344 284
345 void NotificationPlatformBridgeLinux::NotifyNow( 285 dbus::MethodCall method_call(kFreedesktopNotificationsName, "Notify");
346 const Notification& notification, 286 dbus::MessageWriter writer(&method_call);
347 base::WeakPtr<NotificationData> data,
348 GAsyncReadyCallback callback,
349 gpointer user_data,
350 std::unique_ptr<ResourceFiles> resource_files) {
351 if (!data)
352 return;
353 287
354 if (data->dbus_id) 288 // app_name passed implicitly via desktop-entry.
355 DCHECK(!data->cancellable); 289 writer.AppendString("");
356 290
357 data->ResetResourceFiles(); 291 writer.AppendUint32(data->dbus_id);
358 292
359 GVariantBuilder actions_builder; 293 // app_icon passed implicitly via desktop-entry.
360 // Even-indexed elements in this array are action IDs passed back to 294 writer.AppendString("");
361 // us in GSignalReceiver. Odd-indexed ones contain the button text. 295
362 g_variant_builder_init(&actions_builder, G_VARIANT_TYPE("as")); 296 const std::string title = base::UTF16ToUTF8(notification->title());
297 writer.AppendString(title);
298
299 const std::string message = base::UTF16ToUTF8(notification->message());
300 writer.AppendString(message);
301
302 // Even-indexed elements in this vector are action IDs passed back to
303 // us in OnActionInvoked(). Odd-indexed ones contain the button text.
304 std::vector<std::string> actions;
363 data->action_start = data->action_end; 305 data->action_start = data->action_end;
364 for (const auto& button_info : notification.buttons()) { 306 for (const auto& button_info : notification->buttons()) {
365 // FDO notification buttons can contain either an icon or a label, 307 // FDO notification buttons can contain either an icon or a label,
366 // but not both, and the type of all buttons must be the same (all 308 // but not both, and the type of all buttons must be the same (all
367 // labels or all icons), so always use labels. 309 // labels or all icons), so always use labels.
368 std::string id = base::SizeTToString(data->action_end++); 310 const std::string id = base::SizeTToString(data->action_end++);
369 const std::string label = base::UTF16ToUTF8(button_info.title); 311 const std::string label = base::UTF16ToUTF8(button_info.title);
370 AddActionToNotification(&actions_builder, id.c_str(), label.c_str()); 312 actions.push_back(id);
313 actions.push_back(label);
371 } 314 }
372 if (notification.clickable()) { 315 if (notification->clickable()) {
373 // Special case: the pair ("default", "") will not add a button, 316 // Special case: the pair ("default", "") will not add a button,
374 // but instead makes the entire notification clickable. 317 // but instead makes the entire notification clickable.
375 AddActionToNotification(&actions_builder, "default", ""); 318 actions.push_back("default");
319 actions.push_back("");
376 } 320 }
377 // Always add a settings button. 321 // Always add a settings button.
378 AddActionToNotification(&actions_builder, "settings", "Settings"); 322 actions.push_back("settings");
323 actions.push_back("Settings");
324 writer.AppendArrayOfStrings(actions);
379 325
380 GVariantBuilder hints_builder; 326 dbus::MessageWriter hints_writer(nullptr);
381 g_variant_builder_init(&hints_builder, G_VARIANT_TYPE("a{sv}")); 327 writer.OpenArray("{sv}", &hints_writer);
382 328 dbus::MessageWriter urgency_writer(nullptr);
383 g_variant_builder_add(&hints_builder, "{sv}", "urgency", 329 hints_writer.OpenDictEntry(&urgency_writer);
384 g_variant_new_byte(NotificationPriorityToFdoUrgency( 330 urgency_writer.AppendString("urgency");
385 notification.priority()))); 331 urgency_writer.AppendVariantOfUint32(
332 NotificationPriorityToFdoUrgency(notification->priority()));
333 hints_writer.CloseContainer(&urgency_writer);
386 334
387 std::unique_ptr<base::Environment> env = base::Environment::Create(); 335 std::unique_ptr<base::Environment> env = base::Environment::Create();
388 base::FilePath desktop_file( 336 base::FilePath desktop_file(
389 shell_integration_linux::GetDesktopName(env.get())); 337 shell_integration_linux::GetDesktopName(env.get()));
390 const char kDesktopFileSuffix[] = ".desktop"; 338 const char kDesktopFileSuffix[] = ".desktop";
391 DCHECK(base::EndsWith(desktop_file.value(), kDesktopFileSuffix, 339 DCHECK(base::EndsWith(desktop_file.value(), kDesktopFileSuffix,
392 base::CompareCase::SENSITIVE)); 340 base::CompareCase::SENSITIVE));
341 desktop_file = desktop_file.RemoveFinalExtension();
342 dbus::MessageWriter desktop_entry_writer(nullptr);
343 hints_writer.OpenDictEntry(&desktop_entry_writer);
344 desktop_entry_writer.AppendString("desktop-entry");
345 desktop_entry_writer.AppendVariantOfString(desktop_file.value());
346 hints_writer.CloseContainer(&desktop_entry_writer);
393 347
394 desktop_file = desktop_file.RemoveFinalExtension(); 348 base::FilePath icon_file =
395 g_variant_builder_add(&hints_builder, "{sv}", "desktop-entry", 349 WriteDataToTmpFile(notification->icon().As1xPNGBytes());
396 g_variant_new_string(desktop_file.value().c_str())); 350 if (!icon_file.empty()) {
397 351 dbus::MessageWriter image_path_writer(nullptr);
398 if (!resource_files->icon_file.empty()) { 352 hints_writer.OpenDictEntry(&image_path_writer);
399 g_variant_builder_add( 353 image_path_writer.AppendString("image-path");
400 &hints_builder, "{sv}", "image-path", 354 image_path_writer.AppendVariantOfString(icon_file.value());
401 g_variant_new_string(resource_files->icon_file.value().c_str())); 355 hints_writer.CloseContainer(&image_path_writer);
402 data->resource_files.push_back(resource_files->icon_file); 356 data->resource_files.push_back(base::MakeUnique<ResourceFile>(icon_file));
403 resource_files->icon_file.clear();
404 } 357 }
405 358
406 const std::string title = base::UTF16ToUTF8(notification.title()); 359 writer.CloseContainer(&hints_writer);
407 const std::string message = base::UTF16ToUTF8(notification.message());
408 360
409 GVariant* parameters = g_variant_new( 361 const int32_t kExpireTimeoutDefault = -1;
410 "(susssasa{sv}i)", "" /* app_name passed implicitly via desktop-entry */, 362 writer.AppendInt32(kExpireTimeoutDefault);
411 data->dbus_id, "" /* app_icon passed implicitly via desktop-entry */, 363
412 title.c_str(), message.c_str(), &actions_builder, &hints_builder, -1); 364 std::unique_ptr<dbus::Response> response =
413 g_dbus_proxy_call(notification_proxy_, "Notify", parameters, 365 notification_proxy_->CallMethodAndBlock(
414 G_DBUS_CALL_FLAGS_NONE, -1, data->cancellable, callback, 366 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
415 user_data); 367 if (response) {
368 dbus::MessageReader reader(response.get());
369 reader.PopUint32(&data->dbus_id);
370 }
371 if (!data->dbus_id) {
372 // There was some sort of error with creating the notification.
373 notifications_.erase(data);
374 }
416 } 375 }
417 376
418 void NotificationPlatformBridgeLinux::CloseNow(uint32_t dbus_id) { 377 void NotificationPlatformBridgeLinux::CloseNow(
419 g_dbus_proxy_call(notification_proxy_, "CloseNotification", 378 const std::string& profile_id,
420 g_variant_new("(u)", dbus_id), G_DBUS_CALL_FLAGS_NONE, -1, 379 const std::string& notification_id) {
421 nullptr, nullptr, nullptr); 380 std::vector<NotificationData*> to_erase;
381 for (const auto& pair : notifications_) {
382 NotificationData* data = pair.first;
383 if (data->notification_id == notification_id &&
384 data->profile_id == profile_id) {
385 dbus::MethodCall method_call(kFreedesktopNotificationsName,
386 "CloseNotification");
387 dbus::MessageWriter writer(&method_call);
388 writer.AppendUint32(data->dbus_id);
389 notification_proxy_->CallMethodAndBlock(
390 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
391 }
392 }
393 for (NotificationData* data : to_erase)
394 notifications_.erase(data);
395 }
396
397 void NotificationPlatformBridgeLinux::GetDisplayedNow(
398 const std::string& profile_id,
399 bool incognito,
400 const GetDisplayedNotificationsCallback& callback) const {
401 // TODO(thomasanderson): Implement.
402 content::BrowserThread::PostTask(
403 content::BrowserThread::UI, FROM_HERE,
404 base::Bind(callback,
405 base::Passed(base::MakeUnique<std::set<std::string>>()),
406 false));
422 } 407 }
423 408
424 NotificationPlatformBridgeLinux::NotificationData* 409 NotificationPlatformBridgeLinux::NotificationData*
425 NotificationPlatformBridgeLinux::FindNotificationData( 410 NotificationPlatformBridgeLinux::FindNotificationData(
426 const std::string& notification_id, 411 const std::string& notification_id,
427 const std::string& profile_id, 412 const std::string& profile_id,
428 bool is_incognito) { 413 bool is_incognito) {
429 for (const auto& pair : notifications_) { 414 for (const auto& pair : notifications_) {
430 NotificationData* data = pair.first; 415 NotificationData* data = pair.first;
431 if (data->notification_id == notification_id && 416 if (data->notification_id == notification_id &&
(...skipping 10 matching lines...) Expand all
442 for (const auto& pair : notifications_) { 427 for (const auto& pair : notifications_) {
443 NotificationData* data = pair.first; 428 NotificationData* data = pair.first;
444 if (data->dbus_id == dbus_id) 429 if (data->dbus_id == dbus_id)
445 return data; 430 return data;
446 } 431 }
447 432
448 return nullptr; 433 return nullptr;
449 } 434 }
450 435
451 void NotificationPlatformBridgeLinux::ForwardNotificationOperation( 436 void NotificationPlatformBridgeLinux::ForwardNotificationOperation(
452 uint32_t dbus_id, 437 NotificationData* data,
453 NotificationCommon::Operation operation, 438 NotificationCommon::Operation operation,
454 int action_index) { 439 int action_index) {
455 NotificationData* data = FindNotificationData(dbus_id); 440 content::BrowserThread::PostTask(
456 if (!data) { 441 content::BrowserThread::UI, FROM_HERE,
457 // This notification either belongs to a different app or we 442 base::Bind(
458 // already removed the NotificationData after sending a 443 [](NotificationCommon::Operation operation,
459 // "CloseNotification" message. 444 NotificationCommon::Type notification_type,
460 return; 445 const std::string& origin, const std::string& notification_id,
461 } 446 int action_index, const std::string& profile_id,
447 bool is_incognito) {
448 ProfileManager* profile_manager =
449 g_browser_process->profile_manager();
450 DCHECK(profile_manager);
462 451
463 ProfileManager* profile_manager = g_browser_process->profile_manager(); 452 profile_manager->LoadProfile(
464 DCHECK(profile_manager); 453 profile_id, is_incognito,
465 454 base::Bind(&ProfileLoadedCallback, operation, notification_type,
466 profile_manager->LoadProfile( 455 origin, notification_id, action_index,
467 data->profile_id, data->is_incognito, 456 base::NullableString16()));
468 base::Bind(&ProfileLoadedCallback, operation, data->notification_type, 457 },
469 data->origin_url.spec(), data->notification_id, action_index, 458 operation, data->notification_type, data->origin_url.spec(),
470 base::NullableString16())); 459 data->notification_id, action_index, data->profile_id,
460 data->is_incognito));
471 } 461 }
472 462
473 void NotificationPlatformBridgeLinux::GSignalReceiver(GDBusProxy* proxy, 463 void NotificationPlatformBridgeLinux::OnActionInvoked(dbus::Signal* signal) {
474 const char* sender_name, 464 dbus::MessageReader reader(signal);
475 const char* sender_signal, 465 uint32_t dbus_id;
476 GVariant* parameters) { 466 if (!reader.PopUint32(&dbus_id))
477 uint32_t dbus_id = 0; 467 return;
478 if (strcmp("NotificationClosed", sender_signal) == 0 && 468 std::string action;
479 g_variant_is_of_type(parameters, G_VARIANT_TYPE("(uu)"))) { 469 if (!reader.PopString(&action))
480 uint32_t reason; 470 return;
481 g_variant_get(parameters, "(uu)", &dbus_id, &reason);
482 ForwardNotificationOperation(dbus_id, NotificationCommon::CLOSE, -1);
483 // std::unordered_map::erase(nullptr) is safe here.
484 notifications_.erase(FindNotificationData(dbus_id));
485 } else if (strcmp("ActionInvoked", sender_signal) == 0 &&
486 g_variant_is_of_type(parameters, G_VARIANT_TYPE("(us)"))) {
487 const gchar* action = nullptr;
488 g_variant_get(parameters, "(u&s)", &dbus_id, &action);
489 DCHECK(action);
490 471
491 if (strcmp(action, "default") == 0) { 472 NotificationData* data = FindNotificationData(dbus_id);
492 ForwardNotificationOperation(dbus_id, NotificationCommon::CLICK, -1); 473 if (!data)
493 } else if (strcmp(action, "settings") == 0) { 474 return;
494 ForwardNotificationOperation(dbus_id, NotificationCommon::SETTINGS, -1); 475
495 } else { 476 if (action == "default") {
496 size_t id; 477 ForwardNotificationOperation(data, NotificationCommon::CLICK, -1);
497 if (!base::StringToSizeT(action, &id)) 478 } else if (action == "settings") {
498 return; 479 ForwardNotificationOperation(data, NotificationCommon::SETTINGS, -1);
499 NotificationData* data = FindNotificationData(dbus_id); 480 } else {
500 if (!data) 481 size_t id;
501 return; 482 if (!base::StringToSizeT(action, &id))
502 size_t n_buttons = data->action_end - data->action_start; 483 return;
503 size_t id_zero_based = id - data->action_start; 484 size_t n_buttons = data->action_end - data->action_start;
504 if (id_zero_based >= n_buttons) 485 size_t id_zero_based = id - data->action_start;
505 return; 486 if (id_zero_based >= n_buttons)
506 ForwardNotificationOperation(dbus_id, NotificationCommon::CLICK, 487 return;
507 id_zero_based); 488 ForwardNotificationOperation(data, NotificationCommon::CLICK,
508 } 489 id_zero_based);
509 } 490 }
510 } 491 }
492
493 void NotificationPlatformBridgeLinux::OnNotificationClosed(
494 dbus::Signal* signal) {
495 dbus::MessageReader reader(signal);
496 uint32_t dbus_id;
497 if (!reader.PopUint32(&dbus_id))
498 return;
499
500 NotificationData* data = FindNotificationData(dbus_id);
501 if (!data)
502 return;
503
504 ForwardNotificationOperation(data, NotificationCommon::CLOSE, -1);
505 notifications_.erase(data);
506 }
507
508 void NotificationPlatformBridgeLinux::OnConnected(bool success) {
509 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
510 {
511 base::AutoLock lock(connected_lock_);
512 connected_ = success;
513 for (auto& callback : on_connected_callbacks_) {
514 content::BrowserThread::PostTask(
515 content::BrowserThread::UI, FROM_HERE,
516 base::BindOnce(std::move(callback), success));
517 }
518 on_connected_callbacks_.clear();
519 }
520 if (!success)
521 CleanUp();
Lei Zhang 2017/04/21 22:45:17 Can this be CleanUpInternal() since it's already o
Tom (Use chromium acct) 2017/04/24 20:46:14 Done.
522 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698