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

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

Issue 2872053002: Linux native notifications: Support image notifications (Closed)
Patch Set: Resize on task runner Created 3 years, 7 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 #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
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
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
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(&notification_copy))); 258 base::Passed(&notification_copy)));
221 } 259 }
222 260
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698