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> | 7 #include <algorithm> |
8 #include <memory> | 8 #include <memory> |
9 #include <set> | 9 #include <set> |
10 #include <unordered_map> | 10 #include <unordered_map> |
(...skipping 17 matching lines...) Expand all Loading... | |
28 #include "chrome/browser/notifications/native_notification_display_service.h" | 28 #include "chrome/browser/notifications/native_notification_display_service.h" |
29 #include "chrome/browser/notifications/notification.h" | 29 #include "chrome/browser/notifications/notification.h" |
30 #include "chrome/browser/notifications/notification_display_service_factory.h" | 30 #include "chrome/browser/notifications/notification_display_service_factory.h" |
31 #include "chrome/browser/profiles/profile_manager.h" | 31 #include "chrome/browser/profiles/profile_manager.h" |
32 #include "chrome/browser/shell_integration_linux.h" | 32 #include "chrome/browser/shell_integration_linux.h" |
33 #include "content/public/browser/browser_thread.h" | 33 #include "content/public/browser/browser_thread.h" |
34 #include "content/public/browser/notification_service.h" | 34 #include "content/public/browser/notification_service.h" |
35 #include "dbus/bus.h" | 35 #include "dbus/bus.h" |
36 #include "dbus/message.h" | 36 #include "dbus/message.h" |
37 #include "dbus/object_proxy.h" | 37 #include "dbus/object_proxy.h" |
38 #include "skia/ext/image_operations.h" | |
38 #include "ui/gfx/image/image_skia.h" | 39 #include "ui/gfx/image/image_skia.h" |
39 | 40 |
40 namespace { | 41 namespace { |
41 | 42 |
42 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; | 43 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; |
43 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; | 44 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; |
44 | 45 |
45 const char kDefaultButtonId[] = "default"; | 46 const char kDefaultButtonId[] = "default"; |
46 const char kSettingsButtonId[] = "settings"; | 47 const char kSettingsButtonId[] = "settings"; |
47 | 48 |
49 // Max image size; specified in the FDO notification specification. | |
50 const int kMaxImageWidth = 200; | |
51 const int kMaxImageHeight = 100; | |
52 | |
48 // The values in this enumeration correspond to those of the | 53 // The values in this enumeration correspond to those of the |
49 // Linux.NotificationPlatformBridge.InitializationStatus histogram, so | 54 // Linux.NotificationPlatformBridge.InitializationStatus histogram, so |
50 // the ordering should not be changed. New error codes should be | 55 // the ordering should not be changed. New error codes should be |
51 // added at the end, before NUM_ITEMS. | 56 // added at the end, before NUM_ITEMS. |
52 enum class ConnectionInitializationStatusCode { | 57 enum class ConnectionInitializationStatusCode { |
53 SUCCESS = 0, | 58 SUCCESS = 0, |
54 NATIVE_NOTIFICATIONS_NOT_SUPPORTED = 1, | 59 NATIVE_NOTIFICATIONS_NOT_SUPPORTED = 1, |
55 MISSING_REQUIRED_CAPABILITIES = 2, | 60 MISSING_REQUIRED_CAPABILITIES = 2, |
56 COULD_NOT_CONNECT_TO_SIGNALS = 3, | 61 COULD_NOT_CONNECT_TO_SIGNALS = 3, |
57 INCOMPATIBLE_SPEC_VERSION = 4, | 62 INCOMPATIBLE_SPEC_VERSION = 4, |
58 NUM_ITEMS | 63 NUM_ITEMS |
59 }; | 64 }; |
60 | 65 |
66 int ClampInt(int v, int lo, int hi) { | |
Lei Zhang
2017/05/11 00:54:56
return std::max(std::min(value, hi), low);
Lei Zhang
2017/05/11 00:54:56
BTW, std::clamp() will be in C++17, so we'll have
Tom (Use chromium acct)
2017/05/11 01:28:22
Done.
| |
67 if (v < lo) | |
68 return lo; | |
69 if (v > hi) | |
70 return hi; | |
71 return v; | |
72 } | |
73 | |
61 base::string16 CreateNotificationTitle(const Notification& notification) { | 74 base::string16 CreateNotificationTitle(const Notification& notification) { |
62 base::string16 title; | 75 base::string16 title; |
63 if (notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS) { | 76 if (notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS) { |
64 title += base::FormatPercent(notification.progress()); | 77 title += base::FormatPercent(notification.progress()); |
65 title += base::UTF8ToUTF16(" - "); | 78 title += base::UTF8ToUTF16(" - "); |
66 } | 79 } |
67 title += notification.title(); | 80 title += notification.title(); |
68 return title; | 81 return title; |
69 } | 82 } |
70 | 83 |
(...skipping 17 matching lines...) Expand all Loading... | |
88 case message_center::HIGH_PRIORITY: | 101 case message_center::HIGH_PRIORITY: |
89 case message_center::MAX_PRIORITY: | 102 case message_center::MAX_PRIORITY: |
90 return CRITICAL; | 103 return CRITICAL; |
91 default: | 104 default: |
92 NOTREACHED(); | 105 NOTREACHED(); |
93 case message_center::DEFAULT_PRIORITY: | 106 case message_center::DEFAULT_PRIORITY: |
94 return NORMAL; | 107 return NORMAL; |
95 } | 108 } |
96 } | 109 } |
97 | 110 |
111 // Constrain |image|'s size to |kMaxImageWidth|x|kMaxImageHeight|. If | |
112 // the image does not need to be resized, or the image is empty, | |
113 // returns |image| directly. | |
114 gfx::Image ResizeImageToFdoMaxSize(const gfx::Image& image) { | |
115 if (image.IsEmpty()) | |
116 return image; | |
117 int width = image.Width(); | |
118 int height = image.Height(); | |
119 if (width <= kMaxImageWidth && height <= kMaxImageHeight) { | |
120 return image; | |
121 } else { | |
Lei Zhang
2017/05/11 00:54:56
No else after return.
Tom (Use chromium acct)
2017/05/11 01:28:22
Done.
| |
122 const SkBitmap* image_bitmap = image.ToSkBitmap(); | |
123 double scale = std::min(static_cast<double>(kMaxImageWidth) / width, | |
124 static_cast<double>(kMaxImageHeight) / height); | |
125 width = ClampInt(scale * width, 1, kMaxImageWidth); | |
126 height = ClampInt(scale * height, 1, kMaxImageHeight); | |
127 return gfx::Image( | |
128 gfx::ImageSkia::CreateFrom1xBitmap(skia::ImageOperations::Resize( | |
129 *image_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, width, | |
130 height))); | |
131 } | |
132 } | |
133 | |
98 // Runs once the profile has been loaded in order to perform a given | 134 // Runs once the profile has been loaded in order to perform a given |
99 // |operation| on a notification. | 135 // |operation| on a notification. |
100 void ProfileLoadedCallback(NotificationCommon::Operation operation, | 136 void ProfileLoadedCallback(NotificationCommon::Operation operation, |
101 NotificationCommon::Type notification_type, | 137 NotificationCommon::Type notification_type, |
102 const std::string& origin, | 138 const std::string& origin, |
103 const std::string& notification_id, | 139 const std::string& notification_id, |
104 int action_index, | 140 int action_index, |
105 const base::NullableString16& reply, | 141 const base::NullableString16& reply, |
106 Profile* profile) { | 142 Profile* profile) { |
107 if (!profile) | 143 if (!profile) |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
202 bool is_incognito, | 238 bool is_incognito, |
203 const Notification& notification) override { | 239 const Notification& notification) override { |
204 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 240 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
205 // Notifications contain gfx::Image's which have reference counts | 241 // Notifications contain gfx::Image's which have reference counts |
206 // that are not thread safe. Because of this, we duplicate the | 242 // that are not thread safe. Because of this, we duplicate the |
207 // notification and its images. Wrap the notification in a | 243 // notification and its images. Wrap the notification in a |
208 // unique_ptr to transfer ownership of the notification (and the | 244 // unique_ptr to transfer ownership of the notification (and the |
209 // non-thread-safe reference counts) to the task runner thread. | 245 // non-thread-safe reference counts) to the task runner thread. |
210 auto notification_copy = base::MakeUnique<Notification>(notification); | 246 auto notification_copy = base::MakeUnique<Notification>(notification); |
211 notification_copy->set_icon(DeepCopyImage(notification_copy->icon())); | 247 notification_copy->set_icon(DeepCopyImage(notification_copy->icon())); |
212 notification_copy->set_image(gfx::Image()); | 248 notification_copy->set_image(body_images_supported_ |
249 ? DeepCopyImage(notification_copy->image()) | |
250 : gfx::Image()); | |
213 notification_copy->set_small_image(gfx::Image()); | 251 notification_copy->set_small_image(gfx::Image()); |
214 for (size_t i = 0; i < notification_copy->buttons().size(); i++) | 252 for (size_t i = 0; i < notification_copy->buttons().size(); i++) |
215 notification_copy->SetButtonIcon(i, gfx::Image()); | 253 notification_copy->SetButtonIcon(i, gfx::Image()); |
216 | 254 |
217 PostTaskToTaskRunnerThread(base::BindOnce( | 255 PostTaskToTaskRunnerThread(base::BindOnce( |
218 &NotificationPlatformBridgeLinuxImpl::DisplayOnTaskRunner, this, | 256 &NotificationPlatformBridgeLinuxImpl::DisplayOnTaskRunner, this, |
219 notification_type, notification_id, profile_id, is_incognito, | 257 notification_type, notification_id, profile_id, is_incognito, |
220 base::Passed(¬ification_copy))); | 258 base::Passed(¬ification_copy))); |
221 } | 259 } |
222 | 260 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
304 void Observe(int type, | 342 void Observe(int type, |
305 const content::NotificationSource& source, | 343 const content::NotificationSource& source, |
306 const content::NotificationDetails& details) override { | 344 const content::NotificationDetails& details) override { |
307 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 345 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
308 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); | 346 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); |
309 // The browser process is about to exit. Post the CleanUp() task | 347 // The browser process is about to exit. Post the CleanUp() task |
310 // while we still can. | 348 // while we still can. |
311 CleanUp(); | 349 CleanUp(); |
312 } | 350 } |
313 | 351 |
352 void SetBodyImagesSupported(bool body_images_supported) { | |
353 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
354 body_images_supported_ = body_images_supported; | |
355 } | |
356 | |
314 void PostTaskToUiThread(base::OnceClosure closure) const { | 357 void PostTaskToUiThread(base::OnceClosure closure) const { |
315 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 358 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
316 bool success = content::BrowserThread::PostTask( | 359 bool success = content::BrowserThread::PostTask( |
317 content::BrowserThread::UI, FROM_HERE, std::move(closure)); | 360 content::BrowserThread::UI, FROM_HERE, std::move(closure)); |
318 DCHECK(success); | 361 DCHECK(success); |
319 } | 362 } |
320 | 363 |
321 void PostTaskToTaskRunnerThread(base::OnceClosure closure) const { | 364 void PostTaskToTaskRunnerThread(base::OnceClosure closure) const { |
322 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 365 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
323 DCHECK(task_runner_); | 366 DCHECK(task_runner_); |
(...skipping 29 matching lines...) Expand all Loading... | |
353 notification_proxy_->CallMethodAndBlock( | 396 notification_proxy_->CallMethodAndBlock( |
354 &get_capabilities_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); | 397 &get_capabilities_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); |
355 if (capabilities_response) { | 398 if (capabilities_response) { |
356 dbus::MessageReader reader(capabilities_response.get()); | 399 dbus::MessageReader reader(capabilities_response.get()); |
357 std::vector<std::string> capabilities; | 400 std::vector<std::string> capabilities; |
358 reader.PopArrayOfStrings(&capabilities); | 401 reader.PopArrayOfStrings(&capabilities); |
359 for (const std::string& capability : capabilities) | 402 for (const std::string& capability : capabilities) |
360 capabilities_.insert(capability); | 403 capabilities_.insert(capability); |
361 } | 404 } |
362 RecordMetricsForCapabilities(); | 405 RecordMetricsForCapabilities(); |
406 PostTaskToUiThread(base::BindOnce( | |
407 &NotificationPlatformBridgeLinuxImpl::SetBodyImagesSupported, this, | |
408 base::ContainsKey(capabilities_, "body-images"))); | |
363 | 409 |
364 dbus::MethodCall get_server_information_call(kFreedesktopNotificationsName, | 410 dbus::MethodCall get_server_information_call(kFreedesktopNotificationsName, |
365 "GetServerInformation"); | 411 "GetServerInformation"); |
366 std::unique_ptr<dbus::Response> server_information_response = | 412 std::unique_ptr<dbus::Response> server_information_response = |
367 notification_proxy_->CallMethodAndBlock( | 413 notification_proxy_->CallMethodAndBlock( |
368 &get_server_information_call, | 414 &get_server_information_call, |
369 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); | 415 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); |
370 if (server_information_response) { | 416 if (server_information_response) { |
371 dbus::MessageReader reader(server_information_response.get()); | 417 dbus::MessageReader reader(server_information_response.get()); |
372 std::string spec_version; | 418 std::string spec_version; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
405 void CleanUpOnTaskRunner() { | 451 void CleanUpOnTaskRunner() { |
406 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 452 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
407 if (bus_) | 453 if (bus_) |
408 bus_->ShutdownAndBlock(); | 454 bus_->ShutdownAndBlock(); |
409 bus_ = nullptr; | 455 bus_ = nullptr; |
410 notification_proxy_ = nullptr; | 456 notification_proxy_ = nullptr; |
411 notifications_.clear(); | 457 notifications_.clear(); |
412 } | 458 } |
413 | 459 |
414 // Makes the "Notify" call to D-Bus. | 460 // Makes the "Notify" call to D-Bus. |
415 void DisplayOnTaskRunner(NotificationCommon::Type notification_type, | 461 void DisplayOnTaskRunner(NotificationCommon::Type notification_type, |
Lei Zhang
2017/05/11 01:00:00
BTW, this is now ~150 lines long. Time to break of
Tom (Use chromium acct)
2017/05/11 01:28:22
Acknowledged.
| |
416 const std::string& notification_id, | 462 const std::string& notification_id, |
417 const std::string& profile_id, | 463 const std::string& profile_id, |
418 bool is_incognito, | 464 bool is_incognito, |
419 std::unique_ptr<Notification> notification) { | 465 std::unique_ptr<Notification> notification) { |
420 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 466 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
421 NotificationData* data = | 467 NotificationData* data = |
422 FindNotificationData(notification_id, profile_id, is_incognito); | 468 FindNotificationData(notification_id, profile_id, is_incognito); |
423 if (data) { | 469 if (data) { |
424 // Update an existing notification. | 470 // Update an existing notification. |
425 data->notification_type = notification_type; | 471 data->notification_type = notification_type; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
461 body += "\n"; | 507 body += "\n"; |
462 const std::string title = base::UTF16ToUTF8(item.title); | 508 const std::string title = base::UTF16ToUTF8(item.title); |
463 const std::string message = base::UTF16ToUTF8(item.message); | 509 const std::string message = base::UTF16ToUTF8(item.message); |
464 // TODO(peter): Figure out the right way to internationalize | 510 // TODO(peter): Figure out the right way to internationalize |
465 // this for RTL languages. | 511 // this for RTL languages. |
466 if (body_markup) | 512 if (body_markup) |
467 body += "<b>" + title + "</b> " + message; | 513 body += "<b>" + title + "</b> " + message; |
468 else | 514 else |
469 body += title + " - " + message; | 515 body += title + " - " + message; |
470 } | 516 } |
517 } else if (notification->type() == | |
518 message_center::NOTIFICATION_TYPE_IMAGE && | |
519 base::ContainsKey(capabilities_, "body-images")) { | |
520 std::unique_ptr<ResourceFile> image_file = WriteDataToTmpFile( | |
521 ResizeImageToFdoMaxSize(notification->image()).As1xPNGBytes()); | |
522 if (image_file) { | |
523 if (!body.empty()) | |
524 body += "\n"; | |
525 body += | |
526 "<img src=\"" + image_file->file_path().value() + "\" alt=\"\"/>"; | |
527 data->resource_files.push_back(std::move(image_file)); | |
528 } | |
471 } | 529 } |
472 } | 530 } |
473 writer.AppendString(body); | 531 writer.AppendString(body); |
474 | 532 |
475 // Even-indexed elements in this vector are action IDs passed back to | 533 // Even-indexed elements in this vector are action IDs passed back to |
476 // us in OnActionInvoked(). Odd-indexed ones contain the button text. | 534 // us in OnActionInvoked(). Odd-indexed ones contain the button text. |
477 std::vector<std::string> actions; | 535 std::vector<std::string> actions; |
478 if (base::ContainsKey(capabilities_, "actions")) { | 536 if (base::ContainsKey(capabilities_, "actions")) { |
479 data->action_start = data->action_end; | 537 data->action_start = data->action_end; |
480 for (const auto& button_info : notification->buttons()) { | 538 for (const auto& button_info : notification->buttons()) { |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
743 | 801 |
744 scoped_refptr<base::SequencedTaskRunner> task_runner_; | 802 scoped_refptr<base::SequencedTaskRunner> task_runner_; |
745 | 803 |
746 content::NotificationRegistrar registrar_; | 804 content::NotificationRegistrar registrar_; |
747 | 805 |
748 // State necessary for OnConnectionInitializationFinished() and | 806 // State necessary for OnConnectionInitializationFinished() and |
749 // SetReadyCallback(). | 807 // SetReadyCallback(). |
750 base::Optional<bool> connected_; | 808 base::Optional<bool> connected_; |
751 std::vector<NotificationBridgeReadyCallback> on_connected_callbacks_; | 809 std::vector<NotificationBridgeReadyCallback> on_connected_callbacks_; |
752 | 810 |
811 // Notification servers very rarely have the 'body-images' | |
812 // capability, so try to avoid an image copy if possible. It is | |
813 // intialized to true so that the UI thread will conservatively copy | |
814 // the image before the GetCapabilities message completes. | |
815 bool body_images_supported_ = true; | |
816 | |
753 ////////////////////////////////////////////////////////////////////////////// | 817 ////////////////////////////////////////////////////////////////////////////// |
754 // Members used only on the task runner thread. | 818 // Members used only on the task runner thread. |
755 | 819 |
756 scoped_refptr<dbus::Bus> bus_; | 820 scoped_refptr<dbus::Bus> bus_; |
757 | 821 |
758 dbus::ObjectProxy* notification_proxy_ = nullptr; | 822 dbus::ObjectProxy* notification_proxy_ = nullptr; |
759 | 823 |
760 std::unordered_set<std::string> capabilities_; | 824 std::unordered_set<std::string> capabilities_; |
761 | 825 |
762 base::Version spec_version_; | 826 base::Version spec_version_; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
809 } | 873 } |
810 | 874 |
811 void NotificationPlatformBridgeLinux::SetReadyCallback( | 875 void NotificationPlatformBridgeLinux::SetReadyCallback( |
812 NotificationBridgeReadyCallback callback) { | 876 NotificationBridgeReadyCallback callback) { |
813 impl_->SetReadyCallback(std::move(callback)); | 877 impl_->SetReadyCallback(std::move(callback)); |
814 } | 878 } |
815 | 879 |
816 void NotificationPlatformBridgeLinux::CleanUp() { | 880 void NotificationPlatformBridgeLinux::CleanUp() { |
817 impl_->CleanUp(); | 881 impl_->CleanUp(); |
818 } | 882 } |
OLD | NEW |