blob: f5e8409fd2e8c85e032ccd08c28a88c8c9ab2875 [file] [log] [blame]
// Copyright 2014 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/notification_event_dispatcher_impl.h"
#include "base/callback.h"
#include "content/browser/notifications/platform_notification_context_impl.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_database_data.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/platform_notification_data.h"
namespace content {
namespace {
using NotificationClickDispatchCompleteCallback =
NotificationEventDispatcher::NotificationClickDispatchCompleteCallback;
// To be called when the notificationclick event has finished executing. Will
// post a task to call |dispatch_complete_callback| on the UI thread.
void NotificationClickEventFinished(
const NotificationClickDispatchCompleteCallback& dispatch_complete_callback,
const scoped_refptr<ServiceWorkerRegistration>& service_worker_registration,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
PersistentNotificationStatus status = PERSISTENT_NOTIFICATION_STATUS_SUCCESS;
switch (service_worker_status) {
case SERVICE_WORKER_OK:
// Success status was initialized above.
break;
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
status = PERSISTENT_NOTIFICATION_STATUS_EVENT_WAITUNTIL_REJECTED;
break;
case SERVICE_WORKER_ERROR_FAILED:
case SERVICE_WORKER_ERROR_ABORT:
case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
case SERVICE_WORKER_ERROR_NOT_FOUND:
case SERVICE_WORKER_ERROR_EXISTS:
case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED:
case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
case SERVICE_WORKER_ERROR_IPC_FAILED:
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_STATE:
case SERVICE_WORKER_ERROR_TIMEOUT:
case SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED:
case SERVICE_WORKER_ERROR_DISK_CACHE:
case SERVICE_WORKER_ERROR_REDUNDANT:
case SERVICE_WORKER_ERROR_DISALLOWED:
case SERVICE_WORKER_ERROR_MAX_VALUE:
status = PERSISTENT_NOTIFICATION_STATUS_SERVICE_WORKER_ERROR;
break;
}
BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(dispatch_complete_callback, status));
}
// Dispatches the notificationclick on |service_worker_registration| if the
// registration was available. Must be called on the IO thread.
void DispatchNotificationClickEventOnRegistration(
const NotificationDatabaseData& notification_database_data,
int action_index,
const NotificationClickDispatchCompleteCallback& dispatch_complete_callback,
ServiceWorkerStatusCode service_worker_status,
const scoped_refptr<ServiceWorkerRegistration>&
service_worker_registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status == SERVICE_WORKER_OK) {
base::Callback<void(ServiceWorkerStatusCode)> dispatch_event_callback =
base::Bind(&NotificationClickEventFinished,
dispatch_complete_callback,
service_worker_registration);
DCHECK(service_worker_registration->active_version());
service_worker_registration->active_version()->
DispatchNotificationClickEvent(
dispatch_event_callback,
notification_database_data.notification_id,
notification_database_data.notification_data,
action_index);
return;
}
PersistentNotificationStatus status = PERSISTENT_NOTIFICATION_STATUS_SUCCESS;
switch (service_worker_status) {
case SERVICE_WORKER_ERROR_NOT_FOUND:
status = PERSISTENT_NOTIFICATION_STATUS_NO_SERVICE_WORKER;
break;
case SERVICE_WORKER_ERROR_FAILED:
case SERVICE_WORKER_ERROR_ABORT:
case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
case SERVICE_WORKER_ERROR_EXISTS:
case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED:
case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
case SERVICE_WORKER_ERROR_IPC_FAILED:
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
case SERVICE_WORKER_ERROR_STATE:
case SERVICE_WORKER_ERROR_TIMEOUT:
case SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED:
case SERVICE_WORKER_ERROR_DISK_CACHE:
case SERVICE_WORKER_ERROR_REDUNDANT:
case SERVICE_WORKER_ERROR_DISALLOWED:
case SERVICE_WORKER_ERROR_MAX_VALUE:
status = PERSISTENT_NOTIFICATION_STATUS_SERVICE_WORKER_ERROR;
break;
case SERVICE_WORKER_OK:
NOTREACHED();
break;
}
BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(dispatch_complete_callback, status));
}
// Finds the ServiceWorkerRegistration associated with the |origin| and
// |service_worker_registration_id|. Must be called on the IO thread.
void FindServiceWorkerRegistration(
const GURL& origin,
int action_index,
const NotificationClickDispatchCompleteCallback& dispatch_complete_callback,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
bool success,
const NotificationDatabaseData& notification_database_data) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!success) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(dispatch_complete_callback,
PERSISTENT_NOTIFICATION_STATUS_DATABASE_ERROR));
return;
}
service_worker_context->FindReadyRegistrationForId(
notification_database_data.service_worker_registration_id,
origin,
base::Bind(&DispatchNotificationClickEventOnRegistration,
notification_database_data, action_index,
dispatch_complete_callback));
}
// Reads the data associated with the |persistent_notification_id| belonging to
// |origin| from the notification context.
void ReadNotificationDatabaseData(
int64_t persistent_notification_id,
const GURL& origin,
int action_index,
const NotificationClickDispatchCompleteCallback& dispatch_complete_callback,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
scoped_refptr<PlatformNotificationContextImpl> notification_context) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
notification_context->ReadNotificationData(
persistent_notification_id,
origin,
base::Bind(&FindServiceWorkerRegistration,
origin, action_index, dispatch_complete_callback,
service_worker_context));
}
} // namespace
// static
NotificationEventDispatcher* NotificationEventDispatcher::GetInstance() {
return NotificationEventDispatcherImpl::GetInstance();
}
NotificationEventDispatcherImpl*
NotificationEventDispatcherImpl::GetInstance() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return base::Singleton<NotificationEventDispatcherImpl>::get();
}
NotificationEventDispatcherImpl::NotificationEventDispatcherImpl() {}
NotificationEventDispatcherImpl::~NotificationEventDispatcherImpl() {}
void NotificationEventDispatcherImpl::DispatchNotificationClickEvent(
BrowserContext* browser_context,
int64_t persistent_notification_id,
const GURL& origin,
int action_index,
const NotificationClickDispatchCompleteCallback&
dispatch_complete_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_GT(persistent_notification_id, 0);
DCHECK(origin.is_valid());
StoragePartition* partition =
BrowserContext::GetStoragePartitionForSite(browser_context, origin);
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context =
static_cast<ServiceWorkerContextWrapper*>(
partition->GetServiceWorkerContext());
scoped_refptr<PlatformNotificationContextImpl> notification_context =
static_cast<PlatformNotificationContextImpl*>(
partition->GetPlatformNotificationContext());
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&ReadNotificationDatabaseData,
persistent_notification_id,
origin,
action_index,
dispatch_complete_callback,
service_worker_context,
notification_context));
}
} // namespace content