// 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& 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_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, const NotificationClickDispatchCompleteCallback& dispatch_complete_callback, ServiceWorkerStatusCode service_worker_status, const scoped_refptr& service_worker_registration) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (service_worker_status == SERVICE_WORKER_OK) { base::Callback dispatch_event_callback = base::Bind(&NotificationClickEventFinished, dispatch_complete_callback, service_worker_registration); service_worker_registration->active_version()-> DispatchNotificationClickEvent( dispatch_event_callback, notification_database_data.notification_id, notification_database_data.notification_data); 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_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, const NotificationClickDispatchCompleteCallback& dispatch_complete_callback, scoped_refptr 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->FindRegistrationForId( notification_database_data.service_worker_registration_id, origin, base::Bind(&DispatchNotificationClickEventOnRegistration, notification_database_data, 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, const NotificationClickDispatchCompleteCallback& dispatch_complete_callback, scoped_refptr service_worker_context, scoped_refptr notification_context) { DCHECK_CURRENTLY_ON(BrowserThread::IO); notification_context->ReadNotificationData( persistent_notification_id, origin, base::Bind(&FindServiceWorkerRegistration, origin, dispatch_complete_callback, service_worker_context)); } } // namespace // static NotificationEventDispatcher* NotificationEventDispatcher::GetInstance() { return NotificationEventDispatcherImpl::GetInstance(); } NotificationEventDispatcherImpl* NotificationEventDispatcherImpl::GetInstance() { DCHECK_CURRENTLY_ON(BrowserThread::UI); return Singleton::get(); } NotificationEventDispatcherImpl::NotificationEventDispatcherImpl() {} NotificationEventDispatcherImpl::~NotificationEventDispatcherImpl() {} void NotificationEventDispatcherImpl::DispatchNotificationClickEvent( BrowserContext* browser_context, int64_t persistent_notification_id, const GURL& origin, 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 service_worker_context = static_cast( partition->GetServiceWorkerContext()); scoped_refptr notification_context = static_cast( partition->GetPlatformNotificationContext()); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&ReadNotificationDatabaseData, persistent_notification_id, origin, dispatch_complete_callback, service_worker_context, notification_context)); } } // namespace content