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 |