blob: c21775c3492447ba4c613465531f80147e7dc3b0 [file] [log] [blame]
// Copyright 2017 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/stub_notification_display_service.h"
#include <algorithm>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "chrome/browser/notifications/notification_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "url/origin.h"
// static
std::unique_ptr<KeyedService> StubNotificationDisplayService::FactoryForTests(
content::BrowserContext* context) {
return std::make_unique<StubNotificationDisplayService>(
Profile::FromBrowserContext(context));
}
StubNotificationDisplayService::StubNotificationDisplayService(Profile* profile)
: NotificationDisplayServiceImpl(profile), profile_(profile) {}
StubNotificationDisplayService::~StubNotificationDisplayService() = default;
void StubNotificationDisplayService::SetNotificationAddedClosure(
base::RepeatingClosure closure) {
notification_added_closure_ = std::move(closure);
}
void StubNotificationDisplayService::SetNotificationClosedClosure(
base::RepeatingClosure closure) {
notification_closed_closure_ = std::move(closure);
}
std::vector<message_center::Notification>
StubNotificationDisplayService::GetDisplayedNotificationsForType(
NotificationHandler::Type type) const {
std::vector<message_center::Notification> notifications;
for (const auto& data : notifications_) {
if (data.type != type)
continue;
notifications.push_back(data.notification);
}
return notifications;
}
std::optional<message_center::Notification>
StubNotificationDisplayService::GetNotification(
const std::string& notification_id) {
auto iter = std::ranges::find(
notifications_, notification_id,
[](const NotificationData& data) { return data.notification.id(); });
if (iter == notifications_.end())
return std::nullopt;
return iter->notification;
}
const NotificationCommon::Metadata*
StubNotificationDisplayService::GetMetadataForNotification(
const message_center::Notification& notification) {
auto iter = std::ranges::find(
notifications_, notification.id(),
[](const NotificationData& data) { return data.notification.id(); });
if (iter == notifications_.end())
return nullptr;
return iter->metadata.get();
}
void StubNotificationDisplayService::SimulateClick(
NotificationHandler::Type notification_type,
const std::string& notification_id,
std::optional<int> action_index,
std::optional<std::u16string> reply) {
auto iter = FindNotification(notification_type, notification_id);
if (iter == notifications_.end())
return;
NotificationHandler* handler = GetNotificationHandler(notification_type);
if (notification_type == NotificationHandler::Type::TRANSIENT) {
DCHECK(!handler);
auto* delegate = iter->notification.delegate();
if (delegate)
delegate->Click(action_index, reply);
return;
}
DCHECK(handler);
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
handler->OnClick(profile_, iter->notification.origin_url(), notification_id,
action_index, reply, run_loop.QuitClosure());
run_loop.Run();
}
void StubNotificationDisplayService::SimulateSettingsClick(
NotificationHandler::Type notification_type,
const std::string& notification_id) {
auto iter = FindNotification(notification_type, notification_id);
if (iter == notifications_.end())
return;
NotificationHandler* handler = GetNotificationHandler(notification_type);
if (notification_type == NotificationHandler::Type::TRANSIENT) {
DCHECK(!handler);
if (iter->notification.delegate())
iter->notification.delegate()->SettingsClick();
} else {
DCHECK(handler);
handler->OpenSettings(profile_, iter->notification.origin_url());
}
}
void StubNotificationDisplayService::RemoveNotification(
NotificationHandler::Type notification_type,
const std::string& notification_id,
bool by_user,
bool silent) {
auto iter = FindNotification(notification_type, notification_id);
if (iter == notifications_.end())
return;
NotificationData data = std::move(*iter);
notifications_.erase(iter);
if (!silent) {
NotificationHandler* handler = GetNotificationHandler(notification_type);
if (notification_type == NotificationHandler::Type::TRANSIENT) {
DCHECK(!handler);
if (data.notification.delegate())
data.notification.delegate()->Close(by_user);
} else {
base::RunLoop run_loop;
handler->OnClose(profile_, data.notification.origin_url(),
notification_id, by_user, run_loop.QuitClosure());
run_loop.Run();
}
}
}
void StubNotificationDisplayService::RemoveAllNotifications(
NotificationHandler::Type notification_type,
bool by_user) {
NotificationHandler* handler = GetNotificationHandler(notification_type);
DCHECK_NE(!!handler,
notification_type == NotificationHandler::Type::TRANSIENT);
for (auto iter = notifications_.begin(); iter != notifications_.end();) {
if (iter->type == notification_type) {
NotificationData data = std::move(*iter);
iter = notifications_.erase(iter);
if (handler) {
base::RunLoop run_loop;
handler->OnClose(profile_, data.notification.origin_url(),
data.notification.id(), by_user,
run_loop.QuitClosure());
run_loop.Run();
} else if (data.notification.delegate()) {
data.notification.delegate()->Close(by_user);
}
} else {
iter++;
}
}
}
void StubNotificationDisplayService::SetProcessNotificationOperationDelegate(
const ProcessNotificationOperationCallback& delegate) {
process_notification_operation_delegate_ = delegate;
}
void StubNotificationDisplayService::Display(
NotificationHandler::Type notification_type,
const message_center::Notification& notification,
std::unique_ptr<NotificationCommon::Metadata> metadata) {
// This mimics notification replacement behaviour; the Close() method on a
// notification's delegate is not meant to be invoked in this situation.
RemoveNotification(notification_type, notification.id(), false /* by_user */,
true /* silent */);
NotificationHandler* handler = GetNotificationHandler(notification_type);
if (notification_type == NotificationHandler::Type::TRANSIENT) {
CHECK(!handler);
CHECK(notification.delegate());
} else {
handler->OnShow(profile_, notification.id());
}
notifications_.emplace_back(notification_type, notification,
std::move(metadata));
if (notification_added_closure_)
notification_added_closure_.Run();
}
void StubNotificationDisplayService::Close(
NotificationHandler::Type notification_type,
const std::string& notification_id) {
// Close the notification silently only for non-transient notifications,
// because some tests of transient (non-web/extension) notifications rely on
// the close event being dispatched, e.g. tests in WebUsbDetectorTest.
RemoveNotification(
notification_type, notification_id, false /* by_user */,
notification_type != NotificationHandler::Type::TRANSIENT /* silent */);
if (notification_closed_closure_)
notification_closed_closure_.Run();
}
void StubNotificationDisplayService::GetDisplayed(
DisplayedNotificationsCallback callback) {
std::set<std::string> notifications;
for (const auto& notification_data : notifications_) {
notifications.insert(notification_data.notification.id());
}
std::move(callback).Run(std::move(notifications),
true /* supports_synchronization */);
}
void StubNotificationDisplayService::GetDisplayedForOrigin(
const GURL& origin,
DisplayedNotificationsCallback callback) {
std::set<std::string> notifications;
for (const auto& notification_data : notifications_) {
if (url::IsSameOriginWith(notification_data.notification.origin_url(),
origin)) {
notifications.insert(notification_data.notification.id());
}
}
std::move(callback).Run(std::move(notifications),
true /* supports_synchronization */);
}
void StubNotificationDisplayService::ProcessNotificationOperation(
NotificationOperation operation,
NotificationHandler::Type notification_type,
const GURL& origin,
const std::string& notification_id,
const std::optional<int>& action_index,
const std::optional<std::u16string>& reply,
const std::optional<bool>& by_user,
base::OnceClosure on_complete_cb) {
if (process_notification_operation_delegate_) {
// TODO(b/375547360): run `on_complete_cb` when notification processing
// finishes.
process_notification_operation_delegate_.Run(operation, notification_type,
origin, notification_id,
action_index, reply, by_user);
return;
}
NotificationDisplayServiceImpl::ProcessNotificationOperation(
operation, notification_type, origin, notification_id, action_index,
reply, by_user, std::move(on_complete_cb));
}
StubNotificationDisplayService::NotificationData::NotificationData(
NotificationHandler::Type type,
const message_center::Notification& notification,
std::unique_ptr<NotificationCommon::Metadata> metadata)
: type(type), notification(notification), metadata(std::move(metadata)) {}
StubNotificationDisplayService::NotificationData::NotificationData(
NotificationData&& other)
: type(other.type),
notification(std::move(other.notification)),
metadata(std::move(other.metadata)) {}
StubNotificationDisplayService::NotificationData::~NotificationData() = default;
StubNotificationDisplayService::NotificationData&
StubNotificationDisplayService::NotificationData::operator=(
NotificationData&& other) {
type = other.type;
notification = std::move(other.notification);
metadata = std::move(other.metadata);
return *this;
}
std::vector<StubNotificationDisplayService::NotificationData>::iterator
StubNotificationDisplayService::FindNotification(
NotificationHandler::Type notification_type,
const std::string& notification_id) {
return std::ranges::find_if(
notifications_,
[notification_type, &notification_id](const NotificationData& data) {
return data.type == notification_type &&
data.notification.id() == notification_id;
});
}