blob: cd8c01bca2250e26dee93425250086feea599938 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/notifications/blink_notification_service_impl.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/strings/string16.h"
#include "content/browser/notifications/notification_event_dispatcher_impl.h"
#include "content/browser/notifications/platform_notification_context_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/notification_database_data.h"
#include "content/public/browser/permission_manager.h"
#include "content/public/browser/permission_type.h"
#include "content/public/browser/platform_notification_service.h"
#include "content/public/common/content_client.h"
#include "content/public/common/notification_resources.h"
#include "content/public/common/platform_notification_data.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
#include "url/gurl.h"
namespace content {
namespace {
// Returns the implementation of the PlatformNotificationService. May be NULL.
PlatformNotificationService* Service() {
return GetContentClient()->browser()->GetPlatformNotificationService();
}
} // namespace
using blink::mojom::PersistentNotificationError;
BlinkNotificationServiceImpl::BlinkNotificationServiceImpl(
PlatformNotificationContextImpl* notification_context,
BrowserContext* browser_context,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
const url::Origin& origin,
mojo::InterfaceRequest<blink::mojom::NotificationService> request)
: notification_context_(notification_context),
browser_context_(browser_context),
service_worker_context_(std::move(service_worker_context)),
origin_(origin),
binding_(this, std::move(request)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(notification_context_);
DCHECK(browser_context_);
binding_.set_connection_error_handler(base::BindOnce(
&BlinkNotificationServiceImpl::OnConnectionError,
base::Unretained(this) /* the channel is owned by |this| */));
}
BlinkNotificationServiceImpl::~BlinkNotificationServiceImpl() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void BlinkNotificationServiceImpl::GetPermissionStatus(
GetPermissionStatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!Service()) {
std::move(callback).Run(blink::mojom::PermissionStatus::DENIED);
return;
}
std::move(callback).Run(CheckPermissionStatus());
}
void BlinkNotificationServiceImpl::OnConnectionError() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
notification_context_->RemoveService(this);
// |this| has now been deleted.
}
void BlinkNotificationServiceImpl::DisplayNonPersistentNotification(
const std::string& token,
const PlatformNotificationData& platform_notification_data,
const NotificationResources& notification_resources,
blink::mojom::NonPersistentNotificationListenerPtr event_listener_ptr) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!Service())
return;
if (CheckPermissionStatus() != blink::mojom::PermissionStatus::GRANTED)
return;
std::string notification_id =
notification_context_->notification_id_generator()
->GenerateForNonPersistentNotification(origin_, token);
NotificationEventDispatcherImpl* event_dispatcher =
NotificationEventDispatcherImpl::GetInstance();
event_dispatcher->RegisterNonPersistentNotificationListener(
notification_id, std::move(event_listener_ptr));
Service()->DisplayNotification(browser_context_, notification_id,
origin_.GetURL(), platform_notification_data,
notification_resources);
}
void BlinkNotificationServiceImpl::CloseNonPersistentNotification(
const std::string& token) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!Service())
return;
if (CheckPermissionStatus() != blink::mojom::PermissionStatus::GRANTED)
return;
std::string notification_id =
notification_context_->notification_id_generator()
->GenerateForNonPersistentNotification(origin_, token);
Service()->CloseNotification(browser_context_, notification_id);
// TODO(https://crbug.com/442141): Pass a callback here to focus the tab
// which created the notification, unless the event is canceled.
NotificationEventDispatcherImpl::GetInstance()
->DispatchNonPersistentCloseEvent(notification_id, base::DoNothing());
}
blink::mojom::PermissionStatus
BlinkNotificationServiceImpl::CheckPermissionStatus() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!browser_context_->GetPermissionManager())
return blink::mojom::PermissionStatus::DENIED;
return browser_context_->GetPermissionManager()->GetPermissionStatus(
PermissionType::NOTIFICATIONS, origin_.GetURL(), origin_.GetURL());
}
void BlinkNotificationServiceImpl::DisplayPersistentNotification(
int64_t service_worker_registration_id,
const PlatformNotificationData& platform_notification_data,
const NotificationResources& notification_resources,
DisplayPersistentNotificationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!Service()) {
std::move(callback).Run(PersistentNotificationError::INTERNAL_ERROR);
return;
}
if (CheckPermissionStatus() != blink::mojom::PermissionStatus::GRANTED) {
std::move(callback).Run(PersistentNotificationError::PERMISSION_DENIED);
return;
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&BlinkNotificationServiceImpl::
DisplayPersistentNotificationOnIOThread,
weak_factory_for_io_.GetWeakPtr(),
service_worker_registration_id, platform_notification_data,
notification_resources, std::move(callback)));
}
void BlinkNotificationServiceImpl::DisplayPersistentNotificationOnIOThread(
int64_t service_worker_registration_id,
const PlatformNotificationData& platform_notification_data,
const NotificationResources& notification_resources,
DisplayPersistentNotificationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(awdf): Necessary to validate resources here?
NotificationDatabaseData database_data;
database_data.origin = origin_.GetURL();
database_data.service_worker_registration_id = service_worker_registration_id;
database_data.notification_data = platform_notification_data;
notification_context_->WriteNotificationData(
origin_.GetURL(), database_data,
base::AdaptCallbackForRepeating(base::BindOnce(
&BlinkNotificationServiceImpl::
DisplayPersistentNotificationWithIdOnIOThread,
weak_factory_for_io_.GetWeakPtr(), service_worker_registration_id,
platform_notification_data, notification_resources,
std::move(callback))));
}
void BlinkNotificationServiceImpl::
DisplayPersistentNotificationWithIdOnIOThread(
int64_t service_worker_registration_id,
const PlatformNotificationData& platform_notification_data,
const NotificationResources& notification_resources,
DisplayPersistentNotificationCallback callback,
bool success,
const std::string& notification_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!success) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(callback),
PersistentNotificationError::INTERNAL_ERROR));
return;
}
service_worker_context_->FindReadyRegistrationForId(
service_worker_registration_id, origin_.GetURL(),
base::BindOnce(
&BlinkNotificationServiceImpl::
DisplayPersistentNotificationWithServiceWorkerOnIOThread,
weak_factory_for_io_.GetWeakPtr(), notification_id,
platform_notification_data, notification_resources,
std::move(callback)));
}
void BlinkNotificationServiceImpl::
DisplayPersistentNotificationWithServiceWorkerOnIOThread(
const std::string& notification_id,
const PlatformNotificationData& platform_notification_data,
const NotificationResources& notification_resources,
DisplayPersistentNotificationCallback callback,
blink::ServiceWorkerStatusCode service_worker_status,
scoped_refptr<ServiceWorkerRegistration> registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
PersistentNotificationError error =
PersistentNotificationError::INTERNAL_ERROR;
// Display the notification if the Service Worker's origin matches the origin
// of the notification's sender.
if (service_worker_status == blink::ServiceWorkerStatusCode::kOk &&
registration->pattern().GetOrigin() == origin_.GetURL()) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
&PlatformNotificationService::DisplayPersistentNotification,
base::Unretained(Service()), browser_context_, notification_id,
registration->pattern(), origin_.GetURL(),
platform_notification_data, notification_resources));
error = PersistentNotificationError::NONE;
}
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(callback), error));
}
void BlinkNotificationServiceImpl::ClosePersistentNotification(
const std::string& notification_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!Service())
return;
if (CheckPermissionStatus() != blink::mojom::PermissionStatus::GRANTED)
return;
Service()->ClosePersistentNotification(browser_context_, notification_id);
// Deleting the data associated with |notification_id| from the notification
// database has to be done on the IO thread, but there's no reason to postpone
// removing the notification from the user's display until that's done.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&PlatformNotificationContextImpl::DeleteNotificationData,
notification_context_, notification_id, origin_.GetURL(),
base::DoNothing()));
}
void BlinkNotificationServiceImpl::GetNotifications(
int64_t service_worker_registration_id,
const std::string& filter_tag,
GetNotificationsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!Service() ||
CheckPermissionStatus() != blink::mojom::PermissionStatus::GRANTED) {
// No permission has been granted for the given origin. It is harmless to
// try to get notifications without permission, so return empty vectors
// indicating that no (accessible) notifications exist at this time.
std::move(callback).Run(std::vector<std::string>(),
std::vector<PlatformNotificationData>());
return;
}
auto read_notification_data_callback = base::BindOnce(
&BlinkNotificationServiceImpl::DidGetNotificationsOnIOThread,
weak_factory_for_io_.GetWeakPtr(), filter_tag, std::move(callback));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&PlatformNotificationContextImpl::
ReadAllNotificationDataForServiceWorkerRegistration,
notification_context_, origin_.GetURL(),
service_worker_registration_id,
base::AdaptCallbackForRepeating(
std::move(read_notification_data_callback))));
}
void BlinkNotificationServiceImpl::DidGetNotificationsOnIOThread(
const std::string& filter_tag,
GetNotificationsCallback callback,
bool success,
const std::vector<NotificationDatabaseData>& notifications) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::vector<std::string> ids;
std::vector<PlatformNotificationData> datas;
for (const NotificationDatabaseData& database_data : notifications) {
// An empty filter tag matches all, else we need an exact match.
if (filter_tag.empty() ||
filter_tag == database_data.notification_data.tag) {
ids.push_back(database_data.notification_id);
datas.push_back(database_data.notification_data);
}
}
// Make sure to invoke the |callback| on the UI thread again.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(std::move(callback), std::move(ids), std::move(datas)));
}
} // namespace content