Chromium Code Reviews| 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 |