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 16 matching lines...) Expand all Loading... | |
| 27 #include "chrome/browser/notifications/native_notification_display_service.h" | 27 #include "chrome/browser/notifications/native_notification_display_service.h" |
| 28 #include "chrome/browser/notifications/notification.h" | 28 #include "chrome/browser/notifications/notification.h" |
| 29 #include "chrome/browser/notifications/notification_display_service_factory.h" | 29 #include "chrome/browser/notifications/notification_display_service_factory.h" |
| 30 #include "chrome/browser/profiles/profile_manager.h" | 30 #include "chrome/browser/profiles/profile_manager.h" |
| 31 #include "chrome/browser/shell_integration_linux.h" | 31 #include "chrome/browser/shell_integration_linux.h" |
| 32 #include "content/public/browser/browser_thread.h" | 32 #include "content/public/browser/browser_thread.h" |
| 33 #include "content/public/browser/notification_service.h" | 33 #include "content/public/browser/notification_service.h" |
| 34 #include "dbus/bus.h" | 34 #include "dbus/bus.h" |
| 35 #include "dbus/message.h" | 35 #include "dbus/message.h" |
| 36 #include "dbus/object_proxy.h" | 36 #include "dbus/object_proxy.h" |
| 37 #include "skia/ext/image_operations.h" | |
| 37 #include "ui/gfx/image/image_skia.h" | 38 #include "ui/gfx/image/image_skia.h" |
| 38 | 39 |
| 39 namespace { | 40 namespace { |
| 40 | 41 |
| 41 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; | 42 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; |
| 42 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; | 43 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; |
| 43 | 44 |
| 44 const char kDefaultButtonId[] = "default"; | 45 const char kDefaultButtonId[] = "default"; |
| 45 const char kSettingsButtonId[] = "settings"; | 46 const char kSettingsButtonId[] = "settings"; |
| 46 | 47 |
| 48 // Specified in FDO notification specification. | |
| 49 const int kMaxImageWidth = 200; | |
| 50 const int kMaxImageHeight = 100; | |
|
Peter Beverloo
2017/05/10 12:54:40
This is tiny! Developers have to provide images wi
Tom (Use chromium acct)
2017/05/11 00:07:04
Yeah, but the only notification server that actual
| |
| 51 | |
| 47 // The values in this enumeration correspond to those of the | 52 // The values in this enumeration correspond to those of the |
| 48 // Linux.NotificationPlatformBridge.InitializationStatus histogram, so | 53 // Linux.NotificationPlatformBridge.InitializationStatus histogram, so |
| 49 // the ordering should not be changed. New error codes should be | 54 // the ordering should not be changed. New error codes should be |
| 50 // added at the end, before NUM_ITEMS. | 55 // added at the end, before NUM_ITEMS. |
| 51 enum class ConnectionInitializationStatusCode { | 56 enum class ConnectionInitializationStatusCode { |
| 52 SUCCESS = 0, | 57 SUCCESS = 0, |
| 53 NATIVE_NOTIFICATIONS_NOT_SUPPORTED = 1, | 58 NATIVE_NOTIFICATIONS_NOT_SUPPORTED = 1, |
| 54 MISSING_REQUIRED_CAPABILITIES = 2, | 59 MISSING_REQUIRED_CAPABILITIES = 2, |
| 55 COULD_NOT_CONNECT_TO_SIGNALS = 3, | 60 COULD_NOT_CONNECT_TO_SIGNALS = 3, |
| 56 INCOMPATIBLE_SPEC_VERSION = 4, | 61 INCOMPATIBLE_SPEC_VERSION = 4, |
| 57 NUM_ITEMS | 62 NUM_ITEMS |
| 58 }; | 63 }; |
| 59 | 64 |
| 65 int ClampInt(int v, int lo, int hi) { | |
| 66 if (v < lo) | |
| 67 return lo; | |
| 68 if (v > hi) | |
| 69 return hi; | |
| 70 return v; | |
| 71 } | |
| 72 | |
| 60 gfx::Image DeepCopyImage(const gfx::Image& image) { | 73 gfx::Image DeepCopyImage(const gfx::Image& image) { |
| 61 if (image.IsEmpty()) | 74 if (image.IsEmpty()) |
| 62 return gfx::Image(); | 75 return gfx::Image(); |
| 63 std::unique_ptr<gfx::ImageSkia> image_skia(image.CopyImageSkia()); | 76 std::unique_ptr<gfx::ImageSkia> image_skia(image.CopyImageSkia()); |
| 64 return gfx::Image(*image_skia); | 77 return gfx::Image(*image_skia); |
| 65 } | 78 } |
| 66 | 79 |
| 67 int NotificationPriorityToFdoUrgency(int priority) { | 80 int NotificationPriorityToFdoUrgency(int priority) { |
| 68 enum FdoUrgency { | 81 enum FdoUrgency { |
| 69 LOW = 0, | 82 LOW = 0, |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 191 bool is_incognito, | 204 bool is_incognito, |
| 192 const Notification& notification) override { | 205 const Notification& notification) override { |
| 193 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 206 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 194 // Notifications contain gfx::Image's which have reference counts | 207 // Notifications contain gfx::Image's which have reference counts |
| 195 // that are not thread safe. Because of this, we duplicate the | 208 // that are not thread safe. Because of this, we duplicate the |
| 196 // notification and its images. Wrap the notification in a | 209 // notification and its images. Wrap the notification in a |
| 197 // unique_ptr to transfer ownership of the notification (and the | 210 // unique_ptr to transfer ownership of the notification (and the |
| 198 // non-thread-safe reference counts) to the task runner thread. | 211 // non-thread-safe reference counts) to the task runner thread. |
| 199 auto notification_copy = base::MakeUnique<Notification>(notification); | 212 auto notification_copy = base::MakeUnique<Notification>(notification); |
| 200 notification_copy->set_icon(DeepCopyImage(notification_copy->icon())); | 213 notification_copy->set_icon(DeepCopyImage(notification_copy->icon())); |
| 201 notification_copy->set_image(gfx::Image()); | 214 if (body_images_supported_ != BODY_IMAGES_NOT_SUPPORTED && |
| 215 !notification_copy->image().IsEmpty()) { | |
| 216 int width = notification_copy->image().Width(); | |
| 217 int height = notification_copy->image().Height(); | |
| 218 if (width <= kMaxImageWidth && height <= kMaxImageHeight) { | |
| 219 notification_copy->set_image(DeepCopyImage(notification_copy->image())); | |
| 220 } else { | |
| 221 const SkBitmap* image_bitmap = notification_copy->image().ToSkBitmap(); | |
| 222 double scale = std::min(static_cast<double>(kMaxImageWidth) / width, | |
| 223 static_cast<double>(kMaxImageHeight) / height); | |
| 224 width = ClampInt(scale * width, 1, kMaxImageWidth); | |
| 225 height = ClampInt(scale * height, 1, kMaxImageHeight); | |
| 226 notification_copy->set_image(gfx::Image( | |
| 227 gfx::ImageSkia::CreateFrom1xBitmap(skia::ImageOperations::Resize( | |
| 228 *image_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, width, | |
| 229 height)))); | |
|
Peter Beverloo
2017/05/10 12:54:40
Since these images potentially can be huge, we sho
Tom (Use chromium acct)
2017/05/11 00:07:04
Done.
| |
| 230 } | |
| 231 } | |
| 202 notification_copy->set_small_image(gfx::Image()); | 232 notification_copy->set_small_image(gfx::Image()); |
| 203 for (size_t i = 0; i < notification_copy->buttons().size(); i++) | 233 for (size_t i = 0; i < notification_copy->buttons().size(); i++) |
| 204 notification_copy->SetButtonIcon(i, gfx::Image()); | 234 notification_copy->SetButtonIcon(i, gfx::Image()); |
| 205 | 235 |
| 206 PostTaskToTaskRunnerThread(base::BindOnce( | 236 PostTaskToTaskRunnerThread(base::BindOnce( |
| 207 &NotificationPlatformBridgeLinuxImpl::DisplayOnTaskRunner, this, | 237 &NotificationPlatformBridgeLinuxImpl::DisplayOnTaskRunner, this, |
| 208 notification_type, notification_id, profile_id, is_incognito, | 238 notification_type, notification_id, profile_id, is_incognito, |
| 209 base::Passed(¬ification_copy))); | 239 base::Passed(¬ification_copy))); |
| 210 } | 240 } |
| 211 | 241 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 342 notification_proxy_->CallMethodAndBlock( | 372 notification_proxy_->CallMethodAndBlock( |
| 343 &get_capabilities_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); | 373 &get_capabilities_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); |
| 344 if (capabilities_response) { | 374 if (capabilities_response) { |
| 345 dbus::MessageReader reader(capabilities_response.get()); | 375 dbus::MessageReader reader(capabilities_response.get()); |
| 346 std::vector<std::string> capabilities; | 376 std::vector<std::string> capabilities; |
| 347 reader.PopArrayOfStrings(&capabilities); | 377 reader.PopArrayOfStrings(&capabilities); |
| 348 for (const std::string& capability : capabilities) | 378 for (const std::string& capability : capabilities) |
| 349 capabilities_.insert(capability); | 379 capabilities_.insert(capability); |
| 350 } | 380 } |
| 351 RecordMetricsForCapabilities(); | 381 RecordMetricsForCapabilities(); |
| 382 body_images_supported_ = base::ContainsKey(capabilities_, "body-images") | |
| 383 ? BODY_IMAGES_SUPPORTED | |
| 384 : BODY_IMAGES_NOT_SUPPORTED; | |
| 352 | 385 |
| 353 dbus::MethodCall get_server_information_call(kFreedesktopNotificationsName, | 386 dbus::MethodCall get_server_information_call(kFreedesktopNotificationsName, |
| 354 "GetServerInformation"); | 387 "GetServerInformation"); |
| 355 std::unique_ptr<dbus::Response> server_information_response = | 388 std::unique_ptr<dbus::Response> server_information_response = |
| 356 notification_proxy_->CallMethodAndBlock( | 389 notification_proxy_->CallMethodAndBlock( |
| 357 &get_server_information_call, | 390 &get_server_information_call, |
| 358 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); | 391 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); |
| 359 if (server_information_response) { | 392 if (server_information_response) { |
| 360 dbus::MessageReader reader(server_information_response.get()); | 393 dbus::MessageReader reader(server_information_response.get()); |
| 361 std::string spec_version; | 394 std::string spec_version; |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 450 for (const auto& item : notification->items()) { | 483 for (const auto& item : notification->items()) { |
| 451 if (!body.empty()) | 484 if (!body.empty()) |
| 452 body += "\n"; | 485 body += "\n"; |
| 453 const std::string title = base::UTF16ToUTF8(item.title); | 486 const std::string title = base::UTF16ToUTF8(item.title); |
| 454 const std::string message = base::UTF16ToUTF8(item.message); | 487 const std::string message = base::UTF16ToUTF8(item.message); |
| 455 if (body_markup) | 488 if (body_markup) |
| 456 body += "<b>" + title + "</b> " + message; | 489 body += "<b>" + title + "</b> " + message; |
| 457 else | 490 else |
| 458 body += title + " - " + message; | 491 body += title + " - " + message; |
| 459 } | 492 } |
| 493 } else if (notification->type() == | |
| 494 message_center::NOTIFICATION_TYPE_IMAGE) { | |
| 495 std::unique_ptr<ResourceFile> image_file = | |
| 496 WriteDataToTmpFile(notification->image().As1xPNGBytes()); | |
| 497 if (image_file) { | |
| 498 if (!body.empty()) | |
| 499 body += "\n"; | |
| 500 body += | |
| 501 "<img src=\"" + image_file->file_path().value() + "\" alt=\"\"/>"; | |
|
Peter Beverloo
2017/05/10 12:54:40
Is a data: URL an option here? If the toast requir
Tom (Use chromium acct)
2017/05/11 00:07:04
No, the spec says "Images referenced must always b
| |
| 502 data->resource_files.push_back(std::move(image_file)); | |
| 503 } | |
| 460 } | 504 } |
| 461 } | 505 } |
| 462 writer.AppendString(body); | 506 writer.AppendString(body); |
| 463 | 507 |
| 464 // Even-indexed elements in this vector are action IDs passed back to | 508 // Even-indexed elements in this vector are action IDs passed back to |
| 465 // us in OnActionInvoked(). Odd-indexed ones contain the button text. | 509 // us in OnActionInvoked(). Odd-indexed ones contain the button text. |
| 466 std::vector<std::string> actions; | 510 std::vector<std::string> actions; |
| 467 if (base::ContainsKey(capabilities_, "actions")) { | 511 if (base::ContainsKey(capabilities_, "actions")) { |
| 468 data->action_start = data->action_end; | 512 data->action_start = data->action_end; |
| 469 for (const auto& button_info : notification->buttons()) { | 513 for (const auto& button_info : notification->buttons()) { |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 761 base::Closure connected_signals_barrier_; | 805 base::Closure connected_signals_barrier_; |
| 762 | 806 |
| 763 // A std::set<std::unique_ptr<T>> doesn't work well because | 807 // A std::set<std::unique_ptr<T>> doesn't work well because |
| 764 // eg. std::set::erase(T) would require a std::unique_ptr<T> | 808 // eg. std::set::erase(T) would require a std::unique_ptr<T> |
| 765 // argument, so the data would get double-destructed. | 809 // argument, so the data would get double-destructed. |
| 766 template <typename T> | 810 template <typename T> |
| 767 using UnorderedUniqueSet = std::unordered_map<T*, std::unique_ptr<T>>; | 811 using UnorderedUniqueSet = std::unordered_map<T*, std::unique_ptr<T>>; |
| 768 | 812 |
| 769 UnorderedUniqueSet<NotificationData> notifications_; | 813 UnorderedUniqueSet<NotificationData> notifications_; |
| 770 | 814 |
| 815 ////////////////////////////////////////////////////////////////////////////// | |
| 816 // Members used on both the UI thread and the task runner thread. | |
| 817 | |
| 818 // Notification servers very rarely have the 'body-images' | |
| 819 // capability, so try to avoid an image copy/resize if possible. | |
| 820 // Reads and writes to |body_images_supported_| should be atomic, so | |
| 821 // no locking is necessary. | |
| 822 enum { | |
| 823 BODY_IMAGES_UNKNOWN, | |
| 824 BODY_IMAGES_SUPPORTED, | |
| 825 BODY_IMAGES_NOT_SUPPORTED, | |
| 826 } body_images_supported_ = BODY_IMAGES_UNKNOWN; | |
|
Peter Beverloo
2017/05/10 12:54:40
It looks like you're considering base::Optional<bo
Tom (Use chromium acct)
2017/05/11 00:07:04
Done.
| |
| 827 | |
| 771 DISALLOW_COPY_AND_ASSIGN(NotificationPlatformBridgeLinuxImpl); | 828 DISALLOW_COPY_AND_ASSIGN(NotificationPlatformBridgeLinuxImpl); |
| 772 }; | 829 }; |
| 773 | 830 |
| 774 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux() | 831 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux() |
| 775 : NotificationPlatformBridgeLinux(nullptr) {} | 832 : NotificationPlatformBridgeLinux(nullptr) {} |
| 776 | 833 |
| 777 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux( | 834 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux( |
| 778 scoped_refptr<dbus::Bus> bus) | 835 scoped_refptr<dbus::Bus> bus) |
| 779 : impl_(new NotificationPlatformBridgeLinuxImpl(bus)) { | 836 : impl_(new NotificationPlatformBridgeLinuxImpl(bus)) { |
| 780 impl_->Init(); | 837 impl_->Init(); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 806 } | 863 } |
| 807 | 864 |
| 808 void NotificationPlatformBridgeLinux::SetReadyCallback( | 865 void NotificationPlatformBridgeLinux::SetReadyCallback( |
| 809 NotificationBridgeReadyCallback callback) { | 866 NotificationBridgeReadyCallback callback) { |
| 810 impl_->SetReadyCallback(std::move(callback)); | 867 impl_->SetReadyCallback(std::move(callback)); |
| 811 } | 868 } |
| 812 | 869 |
| 813 void NotificationPlatformBridgeLinux::CleanUp() { | 870 void NotificationPlatformBridgeLinux::CleanUp() { |
| 814 impl_->CleanUp(); | 871 impl_->CleanUp(); |
| 815 } | 872 } |
| OLD | NEW |