| 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 | 8 |
| 9 #include "base/barrier_closure.h" | 9 #include "base/barrier_closure.h" |
| 10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 // static | 120 // static |
| 121 NotificationPlatformBridge* NotificationPlatformBridge::Create() { | 121 NotificationPlatformBridge* NotificationPlatformBridge::Create() { |
| 122 return new NotificationPlatformBridgeLinux(); | 122 return new NotificationPlatformBridgeLinux(); |
| 123 } | 123 } |
| 124 | 124 |
| 125 class NotificationPlatformBridgeLinuxImpl | 125 class NotificationPlatformBridgeLinuxImpl |
| 126 : public NotificationPlatformBridge, | 126 : public NotificationPlatformBridge, |
| 127 public content::NotificationObserver, | 127 public content::NotificationObserver, |
| 128 public base::RefCountedThreadSafe<NotificationPlatformBridgeLinuxImpl> { | 128 public base::RefCountedThreadSafe<NotificationPlatformBridgeLinuxImpl> { |
| 129 public: | 129 public: |
| 130 NotificationPlatformBridgeLinuxImpl() | 130 explicit NotificationPlatformBridgeLinuxImpl(scoped_refptr<dbus::Bus> bus) |
| 131 : task_runner_(base::CreateSingleThreadTaskRunnerWithTraits( | 131 : bus_(bus) { |
| 132 base::TaskTraits().MayBlock().WithPriority( | 132 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 133 base::TaskPriority::USER_BLOCKING))) { | 133 task_runner_ = base::CreateSingleThreadTaskRunnerWithTraits( |
| 134 base::TaskTraits().MayBlock().WithPriority( |
| 135 base::TaskPriority::USER_BLOCKING)); |
| 134 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, | 136 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, |
| 135 content::NotificationService::AllSources()); | 137 content::NotificationService::AllSources()); |
| 136 PostTaskToTaskRunnerThread( | 138 } |
| 137 base::BindOnce(&NotificationPlatformBridgeLinuxImpl::Init, this)); | 139 |
| 140 // InitOnTaskRunner() cannot be posted from within the constructor |
| 141 // because of a race condition. The reference count for |this| |
| 142 // starts out as 0. Posting the Init task would increment the count |
| 143 // to 1. If the task finishes before the constructor returns, the |
| 144 // count will go to 0 and the object would be prematurely |
| 145 // destructed. |
| 146 void Init() { |
| 147 PostTaskToTaskRunnerThread(base::BindOnce( |
| 148 &NotificationPlatformBridgeLinuxImpl::InitOnTaskRunner, this)); |
| 138 } | 149 } |
| 139 | 150 |
| 140 void Display(NotificationCommon::Type notification_type, | 151 void Display(NotificationCommon::Type notification_type, |
| 141 const std::string& notification_id, | 152 const std::string& notification_id, |
| 142 const std::string& profile_id, | 153 const std::string& profile_id, |
| 143 bool is_incognito, | 154 bool is_incognito, |
| 144 const Notification& notification) override { | 155 const Notification& notification) override { |
| 145 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 156 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 146 // Notifications contain gfx::Image's which have reference counts | 157 // Notifications contain gfx::Image's which have reference counts |
| 147 // that are not thread safe. Because of this, we duplicate the | 158 // that are not thread safe. Because of this, we duplicate the |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 181 | 192 |
| 182 void SetReadyCallback(NotificationBridgeReadyCallback callback) override { | 193 void SetReadyCallback(NotificationBridgeReadyCallback callback) override { |
| 183 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 194 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 184 if (connected_.has_value()) { | 195 if (connected_.has_value()) { |
| 185 std::move(callback).Run(connected_.value()); | 196 std::move(callback).Run(connected_.value()); |
| 186 } else { | 197 } else { |
| 187 on_connected_callbacks_.push_back(std::move(callback)); | 198 on_connected_callbacks_.push_back(std::move(callback)); |
| 188 } | 199 } |
| 189 } | 200 } |
| 190 | 201 |
| 202 void CleanUp() { |
| 203 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 204 PostTaskToTaskRunnerThread(base::BindOnce( |
| 205 &NotificationPlatformBridgeLinuxImpl::CleanUpOnTaskRunner, this)); |
| 206 } |
| 207 |
| 191 private: | 208 private: |
| 192 friend class base::RefCountedThreadSafe<NotificationPlatformBridgeLinuxImpl>; | 209 friend class base::RefCountedThreadSafe<NotificationPlatformBridgeLinuxImpl>; |
| 193 | 210 |
| 194 struct ResourceFile { | 211 struct ResourceFile { |
| 195 explicit ResourceFile(const base::FilePath& file_path) | 212 explicit ResourceFile(const base::FilePath& file_path) |
| 196 : file_path(file_path) {} | 213 : file_path(file_path) {} |
| 197 ~ResourceFile() { base::DeleteFile(file_path, false); } | 214 ~ResourceFile() { base::DeleteFile(file_path, false); } |
| 198 const base::FilePath file_path; | 215 const base::FilePath file_path; |
| 199 }; | 216 }; |
| 200 | 217 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 232 size_t action_end = 0; | 249 size_t action_end = 0; |
| 233 | 250 |
| 234 // Temporary resource files associated with the notification that | 251 // Temporary resource files associated with the notification that |
| 235 // should be cleaned up when the notification is closed or on | 252 // should be cleaned up when the notification is closed or on |
| 236 // shutdown. | 253 // shutdown. |
| 237 std::vector<std::unique_ptr<ResourceFile>> resource_files; | 254 std::vector<std::unique_ptr<ResourceFile>> resource_files; |
| 238 }; | 255 }; |
| 239 | 256 |
| 240 ~NotificationPlatformBridgeLinuxImpl() override { | 257 ~NotificationPlatformBridgeLinuxImpl() override { |
| 241 DCHECK(!bus_); | 258 DCHECK(!bus_); |
| 259 DCHECK(!notification_proxy_); |
| 242 DCHECK(notifications_.empty()); | 260 DCHECK(notifications_.empty()); |
| 243 } | 261 } |
| 244 | 262 |
| 245 void Observe(int type, | 263 void Observe(int type, |
| 246 const content::NotificationSource& source, | 264 const content::NotificationSource& source, |
| 247 const content::NotificationDetails& details) override { | 265 const content::NotificationDetails& details) override { |
| 248 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 266 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 249 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); | 267 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); |
| 250 // The browser process is about to exit. Post the CleanUp() task | 268 // The browser process is about to exit. Post the CleanUp() task |
| 251 // while we still can. | 269 // while we still can. |
| 252 CleanUp(); | 270 CleanUp(); |
| 253 } | 271 } |
| 254 | 272 |
| 255 void PostTaskToUiThread(base::OnceClosure closure) const { | 273 void PostTaskToUiThread(base::OnceClosure closure) const { |
| 256 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 274 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 257 bool success = content::BrowserThread::PostTask( | 275 bool success = content::BrowserThread::PostTask( |
| 258 content::BrowserThread::UI, FROM_HERE, std::move(closure)); | 276 content::BrowserThread::UI, FROM_HERE, std::move(closure)); |
| 259 DCHECK(success); | 277 DCHECK(success); |
| 260 } | 278 } |
| 261 | 279 |
| 262 void PostTaskToTaskRunnerThread(base::OnceClosure closure) const { | 280 void PostTaskToTaskRunnerThread(base::OnceClosure closure) const { |
| 263 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 281 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 264 DCHECK(task_runner_); | 282 DCHECK(task_runner_); |
| 265 bool success = task_runner_->PostTask(FROM_HERE, std::move(closure)); | 283 bool success = task_runner_->PostTask(FROM_HERE, std::move(closure)); |
| 266 DCHECK(success); | 284 DCHECK(success); |
| 267 } | 285 } |
| 268 | 286 |
| 269 // Sets up the D-Bus connection. | 287 // Sets up the D-Bus connection. |
| 270 void Init() { | 288 void InitOnTaskRunner() { |
| 271 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 289 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 272 dbus::Bus::Options bus_options; | 290 // |bus_| may be non-null in unit testing where a fake bus is used. |
| 273 bus_options.bus_type = dbus::Bus::SESSION; | 291 if (!bus_) { |
| 274 bus_options.connection_type = dbus::Bus::PRIVATE; | 292 dbus::Bus::Options bus_options; |
| 275 bus_options.dbus_task_runner = task_runner_; | 293 bus_options.bus_type = dbus::Bus::SESSION; |
| 276 bus_ = make_scoped_refptr(new dbus::Bus(bus_options)); | 294 bus_options.connection_type = dbus::Bus::PRIVATE; |
| 295 bus_options.dbus_task_runner = task_runner_; |
| 296 bus_ = make_scoped_refptr(new dbus::Bus(bus_options)); |
| 297 } |
| 277 | 298 |
| 278 notification_proxy_ = | 299 notification_proxy_ = |
| 279 bus_->GetObjectProxy(kFreedesktopNotificationsName, | 300 bus_->GetObjectProxy(kFreedesktopNotificationsName, |
| 280 dbus::ObjectPath(kFreedesktopNotificationsPath)); | 301 dbus::ObjectPath(kFreedesktopNotificationsPath)); |
| 281 if (!notification_proxy_) { | 302 if (!notification_proxy_) { |
| 282 OnConnectionInitializationFinishedOnTaskRunner(false); | 303 OnConnectionInitializationFinishedOnTaskRunner(false); |
| 283 return; | 304 return; |
| 284 } | 305 } |
| 285 | 306 |
| 286 connected_signals_barrier_ = base::BarrierClosure( | 307 connected_signals_barrier_ = base::BarrierClosure( |
| 287 2, base::Bind(&NotificationPlatformBridgeLinuxImpl:: | 308 2, base::Bind(&NotificationPlatformBridgeLinuxImpl:: |
| 288 OnConnectionInitializationFinishedOnTaskRunner, | 309 OnConnectionInitializationFinishedOnTaskRunner, |
| 289 this, true)); | 310 this, true)); |
| 290 notification_proxy_->ConnectToSignal( | 311 notification_proxy_->ConnectToSignal( |
| 291 kFreedesktopNotificationsName, "ActionInvoked", | 312 kFreedesktopNotificationsName, "ActionInvoked", |
| 292 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnActionInvoked, this), | 313 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnActionInvoked, this), |
| 293 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnSignalConnected, | 314 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnSignalConnected, |
| 294 this)); | 315 this)); |
| 295 notification_proxy_->ConnectToSignal( | 316 notification_proxy_->ConnectToSignal( |
| 296 kFreedesktopNotificationsName, "NotificationClosed", | 317 kFreedesktopNotificationsName, "NotificationClosed", |
| 297 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnNotificationClosed, | 318 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnNotificationClosed, |
| 298 this), | 319 this), |
| 299 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnSignalConnected, | 320 base::Bind(&NotificationPlatformBridgeLinuxImpl::OnSignalConnected, |
| 300 this)); | 321 this)); |
| 301 } | 322 } |
| 302 | 323 |
| 303 void CleanUp() { | |
| 304 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 305 if (cleanup_posted_) | |
| 306 return; | |
| 307 PostTaskToTaskRunnerThread(base::BindOnce( | |
| 308 &NotificationPlatformBridgeLinuxImpl::CleanUpOnTaskRunner, this)); | |
| 309 cleanup_posted_ = true; | |
| 310 } | |
| 311 | |
| 312 void CleanUpOnTaskRunner() { | 324 void CleanUpOnTaskRunner() { |
| 313 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 325 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 314 bus_->ShutdownAndBlock(); | 326 if (bus_) |
| 327 bus_->ShutdownAndBlock(); |
| 315 bus_ = nullptr; | 328 bus_ = nullptr; |
| 316 notification_proxy_ = nullptr; | 329 notification_proxy_ = nullptr; |
| 317 notifications_.clear(); | 330 notifications_.clear(); |
| 318 } | 331 } |
| 319 | 332 |
| 320 // Makes the "Notify" call to D-Bus. | 333 // Makes the "Notify" call to D-Bus. |
| 321 void DisplayOnTaskRunner(NotificationCommon::Type notification_type, | 334 void DisplayOnTaskRunner(NotificationCommon::Type notification_type, |
| 322 const std::string& notification_id, | 335 const std::string& notification_id, |
| 323 const std::string& profile_id, | 336 const std::string& profile_id, |
| 324 bool is_incognito, | 337 bool is_incognito, |
| (...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 579 | 592 |
| 580 scoped_refptr<base::SequencedTaskRunner> task_runner_; | 593 scoped_refptr<base::SequencedTaskRunner> task_runner_; |
| 581 | 594 |
| 582 content::NotificationRegistrar registrar_; | 595 content::NotificationRegistrar registrar_; |
| 583 | 596 |
| 584 // State necessary for OnConnectionInitializationFinished() and | 597 // State necessary for OnConnectionInitializationFinished() and |
| 585 // SetReadyCallback(). | 598 // SetReadyCallback(). |
| 586 base::Optional<bool> connected_; | 599 base::Optional<bool> connected_; |
| 587 std::vector<NotificationBridgeReadyCallback> on_connected_callbacks_; | 600 std::vector<NotificationBridgeReadyCallback> on_connected_callbacks_; |
| 588 | 601 |
| 589 bool cleanup_posted_ = false; | |
| 590 | |
| 591 ////////////////////////////////////////////////////////////////////////////// | 602 ////////////////////////////////////////////////////////////////////////////// |
| 592 // Members used only on the task runner thread. | 603 // Members used only on the task runner thread. |
| 593 | 604 |
| 594 scoped_refptr<dbus::Bus> bus_; | 605 scoped_refptr<dbus::Bus> bus_; |
| 595 | 606 |
| 596 dbus::ObjectProxy* notification_proxy_ = nullptr; | 607 dbus::ObjectProxy* notification_proxy_ = nullptr; |
| 597 | 608 |
| 598 base::Closure connected_signals_barrier_; | 609 base::Closure connected_signals_barrier_; |
| 599 | 610 |
| 600 // A std::set<std::unique_ptr<T>> doesn't work well because | 611 // A std::set<std::unique_ptr<T>> doesn't work well because |
| 601 // eg. std::set::erase(T) would require a std::unique_ptr<T> | 612 // eg. std::set::erase(T) would require a std::unique_ptr<T> |
| 602 // argument, so the data would get double-destructed. | 613 // argument, so the data would get double-destructed. |
| 603 template <typename T> | 614 template <typename T> |
| 604 using UnorderedUniqueSet = std::unordered_map<T*, std::unique_ptr<T>>; | 615 using UnorderedUniqueSet = std::unordered_map<T*, std::unique_ptr<T>>; |
| 605 | 616 |
| 606 UnorderedUniqueSet<NotificationData> notifications_; | 617 UnorderedUniqueSet<NotificationData> notifications_; |
| 607 | 618 |
| 608 DISALLOW_COPY_AND_ASSIGN(NotificationPlatformBridgeLinuxImpl); | 619 DISALLOW_COPY_AND_ASSIGN(NotificationPlatformBridgeLinuxImpl); |
| 609 }; | 620 }; |
| 610 | 621 |
| 611 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux() | 622 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux() |
| 612 : impl_(new NotificationPlatformBridgeLinuxImpl()) {} | 623 : NotificationPlatformBridgeLinux(nullptr) {} |
| 624 |
| 625 NotificationPlatformBridgeLinux::NotificationPlatformBridgeLinux( |
| 626 scoped_refptr<dbus::Bus> bus) |
| 627 : impl_(new NotificationPlatformBridgeLinuxImpl(bus)) { |
| 628 impl_->Init(); |
| 629 } |
| 613 | 630 |
| 614 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() = default; | 631 NotificationPlatformBridgeLinux::~NotificationPlatformBridgeLinux() = default; |
| 615 | 632 |
| 616 void NotificationPlatformBridgeLinux::Display( | 633 void NotificationPlatformBridgeLinux::Display( |
| 617 NotificationCommon::Type notification_type, | 634 NotificationCommon::Type notification_type, |
| 618 const std::string& notification_id, | 635 const std::string& notification_id, |
| 619 const std::string& profile_id, | 636 const std::string& profile_id, |
| 620 bool is_incognito, | 637 bool is_incognito, |
| 621 const Notification& notification) { | 638 const Notification& notification) { |
| 622 impl_->Display(notification_type, notification_id, profile_id, is_incognito, | 639 impl_->Display(notification_type, notification_id, profile_id, is_incognito, |
| (...skipping 10 matching lines...) Expand all Loading... |
| 633 const std::string& profile_id, | 650 const std::string& profile_id, |
| 634 bool incognito, | 651 bool incognito, |
| 635 const GetDisplayedNotificationsCallback& callback) const { | 652 const GetDisplayedNotificationsCallback& callback) const { |
| 636 impl_->GetDisplayed(profile_id, incognito, callback); | 653 impl_->GetDisplayed(profile_id, incognito, callback); |
| 637 } | 654 } |
| 638 | 655 |
| 639 void NotificationPlatformBridgeLinux::SetReadyCallback( | 656 void NotificationPlatformBridgeLinux::SetReadyCallback( |
| 640 NotificationBridgeReadyCallback callback) { | 657 NotificationBridgeReadyCallback callback) { |
| 641 impl_->SetReadyCallback(std::move(callback)); | 658 impl_->SetReadyCallback(std::move(callback)); |
| 642 } | 659 } |
| 660 |
| 661 void NotificationPlatformBridgeLinux::CleanUp() { |
| 662 impl_->CleanUp(); |
| 663 } |
| OLD | NEW |