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

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

Issue 2876603004: Linux native notifications: Add attribution (Closed)
Patch Set: Rebase 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 | chrome/browser/notifications/notification_platform_bridge_linux_unittest.cc » ('j') | 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 13 matching lines...) Expand all
24 #include "base/task_scheduler/post_task.h" 24 #include "base/task_scheduler/post_task.h"
25 #include "base/version.h" 25 #include "base/version.h"
26 #include "chrome/browser/browser_process.h" 26 #include "chrome/browser/browser_process.h"
27 #include "chrome/browser/chrome_notification_types.h" 27 #include "chrome/browser/chrome_notification_types.h"
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 "chrome/grit/generated_resources.h" 33 #include "chrome/grit/generated_resources.h"
34 #include "components/url_formatter/elide_url.h"
34 #include "content/public/browser/browser_thread.h" 35 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/notification_service.h" 36 #include "content/public/browser/notification_service.h"
36 #include "dbus/bus.h" 37 #include "dbus/bus.h"
37 #include "dbus/message.h" 38 #include "dbus/message.h"
38 #include "dbus/object_proxy.h" 39 #include "dbus/object_proxy.h"
40 #include "skia/ext/image_operations.h"
39 #include "ui/base/l10n/l10n_util.h" 41 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/gfx/image/image_skia.h" 42 #include "ui/gfx/image/image_skia.h"
41 43
42 namespace { 44 namespace {
43 45
44 // DBus name / path. 46 // DBus name / path.
45 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; 47 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications";
46 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; 48 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications";
47 49
48 // DBus methods. 50 // DBus methods.
(...skipping 15 matching lines...) Expand all
64 const char kCapabilityBodyMarkup[] = "body-markup"; 66 const char kCapabilityBodyMarkup[] = "body-markup";
65 const char kCapabilityIconMulti[] = "icon-multi"; 67 const char kCapabilityIconMulti[] = "icon-multi";
66 const char kCapabilityIconStatic[] = "icon-static"; 68 const char kCapabilityIconStatic[] = "icon-static";
67 const char kCapabilityPersistence[] = "persistence"; 69 const char kCapabilityPersistence[] = "persistence";
68 const char kCapabilitySound[] = "sound"; 70 const char kCapabilitySound[] = "sound";
69 71
70 // Button IDs. 72 // Button IDs.
71 const char kDefaultButtonId[] = "default"; 73 const char kDefaultButtonId[] = "default";
72 const char kSettingsButtonId[] = "settings"; 74 const char kSettingsButtonId[] = "settings";
73 75
76 // Max image size; specified in the FDO notification specification.
77 const int kMaxImageWidth = 200;
78 const int kMaxImageHeight = 100;
79
74 // The values in this enumeration correspond to those of the 80 // The values in this enumeration correspond to those of the
75 // Linux.NotificationPlatformBridge.InitializationStatus histogram, so 81 // Linux.NotificationPlatformBridge.InitializationStatus histogram, so
76 // the ordering should not be changed. New error codes should be 82 // the ordering should not be changed. New error codes should be
77 // added at the end, before NUM_ITEMS. 83 // added at the end, before NUM_ITEMS.
78 enum class ConnectionInitializationStatusCode { 84 enum class ConnectionInitializationStatusCode {
79 SUCCESS = 0, 85 SUCCESS = 0,
80 NATIVE_NOTIFICATIONS_NOT_SUPPORTED = 1, 86 NATIVE_NOTIFICATIONS_NOT_SUPPORTED = 1,
81 MISSING_REQUIRED_CAPABILITIES = 2, 87 MISSING_REQUIRED_CAPABILITIES = 2,
82 COULD_NOT_CONNECT_TO_SIGNALS = 3, 88 COULD_NOT_CONNECT_TO_SIGNALS = 3,
83 INCOMPATIBLE_SPEC_VERSION = 4, 89 INCOMPATIBLE_SPEC_VERSION = 4,
84 NUM_ITEMS 90 NUM_ITEMS
85 }; 91 };
86 92
93 int ClampInt(int value, int low, int hi) {
94 return std::max(std::min(value, hi), low);
95 }
96
87 base::string16 CreateNotificationTitle(const Notification& notification) { 97 base::string16 CreateNotificationTitle(const Notification& notification) {
88 base::string16 title; 98 base::string16 title;
89 if (notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS) { 99 if (notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS) {
90 title += base::FormatPercent(notification.progress()); 100 title += base::FormatPercent(notification.progress());
91 title += base::UTF8ToUTF16(" - "); 101 title += base::UTF8ToUTF16(" - ");
92 } 102 }
93 title += notification.title(); 103 title += notification.title();
94 return title; 104 return title;
95 } 105 }
96 106
(...skipping 17 matching lines...) Expand all
114 case message_center::HIGH_PRIORITY: 124 case message_center::HIGH_PRIORITY:
115 case message_center::MAX_PRIORITY: 125 case message_center::MAX_PRIORITY:
116 return CRITICAL; 126 return CRITICAL;
117 default: 127 default:
118 NOTREACHED(); 128 NOTREACHED();
119 case message_center::DEFAULT_PRIORITY: 129 case message_center::DEFAULT_PRIORITY:
120 return NORMAL; 130 return NORMAL;
121 } 131 }
122 } 132 }
123 133
134 // Constrain |image|'s size to |kMaxImageWidth|x|kMaxImageHeight|. If
135 // the image does not need to be resized, or the image is empty,
136 // returns |image| directly.
137 gfx::Image ResizeImageToFdoMaxSize(const gfx::Image& image) {
yoshiki 2017/05/15 02:35:15 nit: Could you add a DCHECK to ensure it's not on
Tom (Use chromium acct) 2017/05/15 22:07:04 Done.
138 if (image.IsEmpty())
139 return image;
140 int width = image.Width();
141 int height = image.Height();
142 if (width <= kMaxImageWidth && height <= kMaxImageHeight) {
143 return image;
144 }
145 const SkBitmap* image_bitmap = image.ToSkBitmap();
146 double scale = std::min(static_cast<double>(kMaxImageWidth) / width,
147 static_cast<double>(kMaxImageHeight) / height);
148 width = ClampInt(scale * width, 1, kMaxImageWidth);
149 height = ClampInt(scale * height, 1, kMaxImageHeight);
150 return gfx::Image(
151 gfx::ImageSkia::CreateFrom1xBitmap(skia::ImageOperations::Resize(
152 *image_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, width,
153 height)));
154 }
155
124 // Runs once the profile has been loaded in order to perform a given 156 // Runs once the profile has been loaded in order to perform a given
125 // |operation| on a notification. 157 // |operation| on a notification.
126 void ProfileLoadedCallback(NotificationCommon::Operation operation, 158 void ProfileLoadedCallback(NotificationCommon::Operation operation,
127 NotificationCommon::Type notification_type, 159 NotificationCommon::Type notification_type,
128 const std::string& origin, 160 const std::string& origin,
129 const std::string& notification_id, 161 const std::string& notification_id,
130 int action_index, 162 int action_index,
131 const base::NullableString16& reply, 163 const base::NullableString16& reply,
132 Profile* profile) { 164 Profile* profile) {
133 if (!profile) 165 if (!profile)
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 bool is_incognito, 260 bool is_incognito,
229 const Notification& notification) override { 261 const Notification& notification) override {
230 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 262 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
231 // Notifications contain gfx::Image's which have reference counts 263 // Notifications contain gfx::Image's which have reference counts
232 // that are not thread safe. Because of this, we duplicate the 264 // that are not thread safe. Because of this, we duplicate the
233 // notification and its images. Wrap the notification in a 265 // notification and its images. Wrap the notification in a
234 // unique_ptr to transfer ownership of the notification (and the 266 // unique_ptr to transfer ownership of the notification (and the
235 // non-thread-safe reference counts) to the task runner thread. 267 // non-thread-safe reference counts) to the task runner thread.
236 auto notification_copy = base::MakeUnique<Notification>(notification); 268 auto notification_copy = base::MakeUnique<Notification>(notification);
237 notification_copy->set_icon(DeepCopyImage(notification_copy->icon())); 269 notification_copy->set_icon(DeepCopyImage(notification_copy->icon()));
238 notification_copy->set_image(gfx::Image()); 270 notification_copy->set_image(body_images_supported_
271 ? DeepCopyImage(notification_copy->image())
272 : gfx::Image());
239 notification_copy->set_small_image(gfx::Image()); 273 notification_copy->set_small_image(gfx::Image());
240 for (size_t i = 0; i < notification_copy->buttons().size(); i++) 274 for (size_t i = 0; i < notification_copy->buttons().size(); i++)
241 notification_copy->SetButtonIcon(i, gfx::Image()); 275 notification_copy->SetButtonIcon(i, gfx::Image());
242 276
243 PostTaskToTaskRunnerThread(base::BindOnce( 277 PostTaskToTaskRunnerThread(base::BindOnce(
244 &NotificationPlatformBridgeLinuxImpl::DisplayOnTaskRunner, this, 278 &NotificationPlatformBridgeLinuxImpl::DisplayOnTaskRunner, this,
245 notification_type, notification_id, profile_id, is_incognito, 279 notification_type, notification_id, profile_id, is_incognito,
246 base::Passed(&notification_copy))); 280 base::Passed(&notification_copy)));
247 } 281 }
248 282
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 void Observe(int type, 364 void Observe(int type,
331 const content::NotificationSource& source, 365 const content::NotificationSource& source,
332 const content::NotificationDetails& details) override { 366 const content::NotificationDetails& details) override {
333 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 367 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
334 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); 368 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
335 // The browser process is about to exit. Post the CleanUp() task 369 // The browser process is about to exit. Post the CleanUp() task
336 // while we still can. 370 // while we still can.
337 CleanUp(); 371 CleanUp();
338 } 372 }
339 373
374 void SetBodyImagesSupported(bool body_images_supported) {
375 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
376 body_images_supported_ = body_images_supported;
377 }
378
340 void PostTaskToUiThread(base::OnceClosure closure) const { 379 void PostTaskToUiThread(base::OnceClosure closure) const {
341 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 380 DCHECK(task_runner_->RunsTasksOnCurrentThread());
342 bool success = content::BrowserThread::PostTask( 381 bool success = content::BrowserThread::PostTask(
343 content::BrowserThread::UI, FROM_HERE, std::move(closure)); 382 content::BrowserThread::UI, FROM_HERE, std::move(closure));
344 DCHECK(success); 383 DCHECK(success);
345 } 384 }
346 385
347 void PostTaskToTaskRunnerThread(base::OnceClosure closure) const { 386 void PostTaskToTaskRunnerThread(base::OnceClosure closure) const {
348 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 387 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
349 DCHECK(task_runner_); 388 DCHECK(task_runner_);
(...skipping 29 matching lines...) Expand all
379 notification_proxy_->CallMethodAndBlock( 418 notification_proxy_->CallMethodAndBlock(
380 &get_capabilities_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); 419 &get_capabilities_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
381 if (capabilities_response) { 420 if (capabilities_response) {
382 dbus::MessageReader reader(capabilities_response.get()); 421 dbus::MessageReader reader(capabilities_response.get());
383 std::vector<std::string> capabilities; 422 std::vector<std::string> capabilities;
384 reader.PopArrayOfStrings(&capabilities); 423 reader.PopArrayOfStrings(&capabilities);
385 for (const std::string& capability : capabilities) 424 for (const std::string& capability : capabilities)
386 capabilities_.insert(capability); 425 capabilities_.insert(capability);
387 } 426 }
388 RecordMetricsForCapabilities(); 427 RecordMetricsForCapabilities();
428 PostTaskToUiThread(base::BindOnce(
429 &NotificationPlatformBridgeLinuxImpl::SetBodyImagesSupported, this,
430 base::ContainsKey(capabilities_, kCapabilityBodyImages)));
389 431
390 dbus::MethodCall get_server_information_call(kFreedesktopNotificationsName, 432 dbus::MethodCall get_server_information_call(kFreedesktopNotificationsName,
391 kMethodGetServerInformation); 433 kMethodGetServerInformation);
392 std::unique_ptr<dbus::Response> server_information_response = 434 std::unique_ptr<dbus::Response> server_information_response =
393 notification_proxy_->CallMethodAndBlock( 435 notification_proxy_->CallMethodAndBlock(
394 &get_server_information_call, 436 &get_server_information_call,
395 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); 437 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
396 if (server_information_response) { 438 if (server_information_response) {
397 dbus::MessageReader reader(server_information_response.get()); 439 dbus::MessageReader reader(server_information_response.get());
398 std::string spec_version; 440 std::string spec_version;
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 510
469 // app_icon passed implicitly via desktop-entry. 511 // app_icon passed implicitly via desktop-entry.
470 writer.AppendString(""); 512 writer.AppendString("");
471 513
472 writer.AppendString( 514 writer.AppendString(
473 base::UTF16ToUTF8(CreateNotificationTitle(*notification))); 515 base::UTF16ToUTF8(CreateNotificationTitle(*notification)));
474 516
475 std::string body; 517 std::string body;
476 if (base::ContainsKey(capabilities_, kCapabilityBody)) { 518 if (base::ContainsKey(capabilities_, kCapabilityBody)) {
477 body = base::UTF16ToUTF8(notification->message()); 519 body = base::UTF16ToUTF8(notification->message());
478 if (base::ContainsKey(capabilities_, kCapabilityBodyMarkup)) { 520 const bool body_markup =
521 base::ContainsKey(capabilities_, kCapabilityBodyMarkup);
522 if (body_markup) {
479 base::ReplaceSubstringsAfterOffset(&body, 0, "&", "&amp;"); 523 base::ReplaceSubstringsAfterOffset(&body, 0, "&", "&amp;");
480 base::ReplaceSubstringsAfterOffset(&body, 0, "<", "&lt;"); 524 base::ReplaceSubstringsAfterOffset(&body, 0, "<", "&lt;");
481 base::ReplaceSubstringsAfterOffset(&body, 0, ">", "&gt;"); 525 base::ReplaceSubstringsAfterOffset(&body, 0, ">", "&gt;");
482 } 526 }
527 if (notification->type() == message_center::NOTIFICATION_TYPE_MULTIPLE) {
528 for (const auto& item : notification->items()) {
529 if (!body.empty())
530 body += "\n";
531 const std::string title = base::UTF16ToUTF8(item.title);
532 const std::string message = base::UTF16ToUTF8(item.message);
533 // TODO(peter): Figure out the right way to internationalize
534 // this for RTL languages.
535 if (body_markup)
536 body += "<b>" + title + "</b> " + message;
537 else
538 body += title + " - " + message;
539 }
540 } else if (notification->type() ==
541 message_center::NOTIFICATION_TYPE_IMAGE &&
542 base::ContainsKey(capabilities_, "body-images")) {
543 std::unique_ptr<ResourceFile> image_file = WriteDataToTmpFile(
544 ResizeImageToFdoMaxSize(notification->image()).As1xPNGBytes());
545 if (image_file) {
546 if (!body.empty())
547 body += "\n";
548 body +=
549 "<img src=\"" + image_file->file_path().value() + "\" alt=\"\"/>";
550 data->resource_files.push_back(std::move(image_file));
551 }
552 }
553
554 // Attribute this notification to the origin, if any.
555 if (!notification->origin_url().is_empty()) {
Peter Beverloo 2017/05/15 16:19:41 If the notification has a context message, we shou
Peter Beverloo 2017/05/15 16:19:41 How certain are we that the attribution is going t
Tom (Use chromium acct) 2017/05/15 22:07:03 Done.
Tom (Use chromium acct) 2017/05/15 22:07:04 Ok, I moved the context to the beginning of the bo
556 if (!body.empty())
557 body += "\n";
558 std::string url_display_text =
559 base::UTF16ToUTF8(url_formatter::FormatUrlForSecurityDisplay(
560 notification->origin_url(),
561 url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS));
Peter Beverloo 2017/05/15 16:19:41 What would wrapped contents look like? For example
Tom (Use chromium acct) 2017/05/15 22:07:04 See the pictures on the bug to see how it looks on
562 std::string spec = notification->origin_url().spec();
563 if (base::ContainsKey(capabilities_, "body-hyperlinks")) {
564 body += "<a href=\"" + spec + "\">" + url_display_text + "</a>";
565 } else {
566 body += url_display_text;
567 }
568 }
483 } 569 }
484 writer.AppendString(body); 570 writer.AppendString(body);
485 571
486 // Even-indexed elements in this vector are action IDs passed back to 572 // Even-indexed elements in this vector are action IDs passed back to
487 // us in OnActionInvoked(). Odd-indexed ones contain the button text. 573 // us in OnActionInvoked(). Odd-indexed ones contain the button text.
488 std::vector<std::string> actions; 574 std::vector<std::string> actions;
489 if (base::ContainsKey(capabilities_, kCapabilityActions)) { 575 if (base::ContainsKey(capabilities_, kCapabilityActions)) {
490 data->action_start = data->action_end; 576 data->action_start = data->action_end;
491 for (const auto& button_info : notification->buttons()) { 577 for (const auto& button_info : notification->buttons()) {
492 // FDO notification buttons can contain either an icon or a label, 578 // FDO notification buttons can contain either an icon or a label,
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 ? "image_path" 628 ? "image_path"
543 : "image-path"); 629 : "image-path");
544 image_path_writer.AppendVariantOfString(icon_file->file_path().value()); 630 image_path_writer.AppendVariantOfString(icon_file->file_path().value());
545 hints_writer.CloseContainer(&image_path_writer); 631 hints_writer.CloseContainer(&image_path_writer);
546 data->resource_files.push_back(std::move(icon_file)); 632 data->resource_files.push_back(std::move(icon_file));
547 } 633 }
548 634
549 writer.CloseContainer(&hints_writer); 635 writer.CloseContainer(&hints_writer);
550 636
551 const int32_t kExpireTimeoutDefault = -1; 637 const int32_t kExpireTimeoutDefault = -1;
552 writer.AppendInt32(kExpireTimeoutDefault); 638 const int32_t kExpireTimeoutNever = 0;
639 writer.AppendInt32(notification->never_timeout() ? kExpireTimeoutNever
640 : kExpireTimeoutDefault);
553 641
554 std::unique_ptr<dbus::Response> response = 642 std::unique_ptr<dbus::Response> response =
555 notification_proxy_->CallMethodAndBlock( 643 notification_proxy_->CallMethodAndBlock(
556 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); 644 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
557 if (response) { 645 if (response) {
558 dbus::MessageReader reader(response.get()); 646 dbus::MessageReader reader(response.get());
559 reader.PopUint32(&data->dbus_id); 647 reader.PopUint32(&data->dbus_id);
560 } 648 }
561 if (!data->dbus_id) { 649 if (!data->dbus_id) {
562 // There was some sort of error with creating the notification. 650 // There was some sort of error with creating the notification.
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 846
759 scoped_refptr<base::SequencedTaskRunner> task_runner_; 847 scoped_refptr<base::SequencedTaskRunner> task_runner_;
760 848
761 content::NotificationRegistrar registrar_; 849 content::NotificationRegistrar registrar_;
762 850
763 // State necessary for OnConnectionInitializationFinished() and 851 // State necessary for OnConnectionInitializationFinished() and
764 // SetReadyCallback(). 852 // SetReadyCallback().
765 base::Optional<bool> connected_; 853 base::Optional<bool> connected_;
766 std::vector<NotificationBridgeReadyCallback> on_connected_callbacks_; 854 std::vector<NotificationBridgeReadyCallback> on_connected_callbacks_;
767 855
856 // Notification servers very rarely have the 'body-images'
857 // capability, so try to avoid an image copy if possible. It is
858 // intialized to true so that the UI thread will conservatively copy
859 // the image before the GetCapabilities message completes.
860 bool body_images_supported_ = true;
861
768 ////////////////////////////////////////////////////////////////////////////// 862 //////////////////////////////////////////////////////////////////////////////
769 // Members used only on the task runner thread. 863 // Members used only on the task runner thread.
770 864
771 scoped_refptr<dbus::Bus> bus_; 865 scoped_refptr<dbus::Bus> bus_;
772 866
773 dbus::ObjectProxy* notification_proxy_ = nullptr; 867 dbus::ObjectProxy* notification_proxy_ = nullptr;
774 868
775 std::unordered_set<std::string> capabilities_; 869 std::unordered_set<std::string> capabilities_;
776 870
777 base::Version spec_version_; 871 base::Version spec_version_;
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
824 } 918 }
825 919
826 void NotificationPlatformBridgeLinux::SetReadyCallback( 920 void NotificationPlatformBridgeLinux::SetReadyCallback(
827 NotificationBridgeReadyCallback callback) { 921 NotificationBridgeReadyCallback callback) {
828 impl_->SetReadyCallback(std::move(callback)); 922 impl_->SetReadyCallback(std::move(callback));
829 } 923 }
830 924
831 void NotificationPlatformBridgeLinux::CleanUp() { 925 void NotificationPlatformBridgeLinux::CleanUp() {
832 impl_->CleanUp(); 926 impl_->CleanUp();
833 } 927 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/notifications/notification_platform_bridge_linux_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698