blob: ecd0fe6553859112f4cb9912299f702c11e81958 [file] [log] [blame]
// 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_,
[&notification](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;