| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/notifications/notification_display_queue.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "chrome/browser/notifications/notification_display_service.h" |
| #include "url/origin.h" |
| |
| namespace { |
| |
| bool IsWebNotification(NotificationHandler::Type notification_type) { |
| return notification_type == NotificationHandler::Type::WEB_PERSISTENT || |
| notification_type == NotificationHandler::Type::WEB_NON_PERSISTENT || |
| notification_type == NotificationHandler::Type::EXTENSION; |
| } |
| |
| } // namespace |
| |
| NotificationDisplayQueue::NotificationDisplayQueue( |
| NotificationDisplayService* notification_display_service) |
| : notification_display_service_(notification_display_service) {} |
| |
| NotificationDisplayQueue::~NotificationDisplayQueue() = default; |
| |
| void NotificationDisplayQueue::OnBlockingStateChanged() { |
| MaybeDisplayQueuedNotifications(); |
| } |
| |
| bool NotificationDisplayQueue::ShouldEnqueueNotification( |
| NotificationHandler::Type notification_type, |
| const message_center::Notification& notification) const { |
| return IsWebNotification(notification_type) && |
| IsAnyNotificationBlockerActive(notification); |
| } |
| |
| void NotificationDisplayQueue::EnqueueNotification( |
| NotificationHandler::Type notification_type, |
| const message_center::Notification& notification, |
| std::unique_ptr<NotificationCommon::Metadata> metadata) { |
| bool replaced = |
| DoRemoveQueuedNotification(notification.id(), /*notify=*/false); |
| queued_notifications_.emplace_back(notification_type, notification, |
| std::move(metadata)); |
| // Notify blockers that a new notification has been blocked. |
| for (auto& blocker : blockers_) { |
| if (blocker->ShouldBlockNotification(notification)) |
| blocker->OnBlockedNotification(notification, replaced); |
| } |
| } |
| |
| void NotificationDisplayQueue::RemoveQueuedNotification( |
| const std::string& notification_id) { |
| DoRemoveQueuedNotification(notification_id, /*notify=*/true); |
| } |
| |
| std::set<std::string> NotificationDisplayQueue::GetQueuedNotificationIds() |
| const { |
| std::set<std::string> notification_ids; |
| for (const QueuedNotification& queued : queued_notifications_) { |
| notification_ids.insert(queued.notification.id()); |
| } |
| |
| return notification_ids; |
| } |
| |
| std::set<std::string> |
| NotificationDisplayQueue::GetQueuedNotificationIdsForOrigin( |
| const GURL& origin) const { |
| std::set<std::string> notification_ids; |
| for (const QueuedNotification& queued : queued_notifications_) { |
| if (url::IsSameOriginWith(queued.notification.origin_url(), origin)) { |
| notification_ids.insert(queued.notification.id()); |
| } |
| } |
| |
| return notification_ids; |
| } |
| |
| void NotificationDisplayQueue::SetNotificationBlockers( |
| NotificationBlockers blockers) { |
| // Remove old blockers from the observer. |
| for (auto& blocker : blockers_) |
| notification_blocker_observations_.RemoveObservation(blocker.get()); |
| |
| // Add new blockers and observe them. |
| blockers_ = std::move(blockers); |
| for (auto& blocker : blockers_) |
| notification_blocker_observations_.AddObservation(blocker.get()); |
| |
| // Update new state with new blockers. |
| MaybeDisplayQueuedNotifications(); |
| } |
| |
| void NotificationDisplayQueue::AddNotificationBlocker( |
| std::unique_ptr<NotificationBlocker> blocker) { |
| notification_blocker_observations_.AddObservation(blocker.get()); |
| blockers_.push_back(std::move(blocker)); |
| } |
| |
| bool NotificationDisplayQueue::DoRemoveQueuedNotification( |
| const std::string& notification_id, |
| bool notify) { |
| auto it = std::ranges::find(queued_notifications_, notification_id, |
| [](const QueuedNotification& queued) { |
| return queued.notification.id(); |
| }); |
| |
| if (it == queued_notifications_.end()) |
| return false; |
| |
| if (notify) { |
| for (auto& blocker : blockers_) { |
| if (blocker->ShouldBlockNotification(it->notification)) |
| blocker->OnClosedNotification(it->notification); |
| } |
| } |
| |
| queued_notifications_.erase(it); |
| return true; |
| } |
| |
| void NotificationDisplayQueue::MaybeDisplayQueuedNotifications() { |
| auto show_begin = std::stable_partition( |
| queued_notifications_.begin(), queued_notifications_.end(), |
| [&](const QueuedNotification& queued) { |
| return IsAnyNotificationBlockerActive(queued.notification); |
| }); |
| |
| std::vector<QueuedNotification> notifications; |
| notifications.insert(notifications.end(), std::make_move_iterator(show_begin), |
| std::make_move_iterator(queued_notifications_.end())); |
| |
| queued_notifications_.erase(show_begin, queued_notifications_.end()); |
| |
| for (QueuedNotification& queued : notifications) { |
| notification_display_service_->Display(queued.notification_type, |
| queued.notification, |
| std::move(queued.metadata)); |
| } |
| } |
| |
| bool NotificationDisplayQueue::IsAnyNotificationBlockerActive( |
| const message_center::Notification& notification) const { |
| return std::ranges::any_of( |
| blockers_, |
| [¬ification](const std::unique_ptr<NotificationBlocker>& blocker) { |
| return blocker->ShouldBlockNotification(notification); |
| }); |
| } |
| |
| NotificationDisplayQueue::QueuedNotification::QueuedNotification( |
| NotificationHandler::Type notification_type, |
| const message_center::Notification& notification, |
| std::unique_ptr<NotificationCommon::Metadata> metadata) |
| : notification_type(notification_type), |
| notification(notification), |
| metadata(std::move(metadata)) {} |
| |
| NotificationDisplayQueue::QueuedNotification::QueuedNotification( |
| QueuedNotification&&) = default; |
| |
| NotificationDisplayQueue::QueuedNotification& |
| NotificationDisplayQueue::QueuedNotification::operator=(QueuedNotification&&) = |
| default; |
| |
| NotificationDisplayQueue::QueuedNotification::~QueuedNotification() = default; |