|  | // Copyright 2019 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "content/browser/notifications/platform_notification_service_proxy.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/check_op.h" | 
|  | #include "content/browser/notifications/devtools_event_logging.h" | 
|  | #include "content/browser/service_worker/service_worker_context_wrapper.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/browser_task_traits.h" | 
|  | #include "content/public/browser/browser_thread.h" | 
|  | #include "content/public/browser/notification_database_data.h" | 
|  | #include "content/public/browser/platform_notification_service.h" | 
|  | #include "content/public/common/content_client.h" | 
|  | #include "third_party/blink/public/common/storage_key/storage_key.h" | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | PlatformNotificationServiceProxy::PlatformNotificationServiceProxy( | 
|  | scoped_refptr<ServiceWorkerContextWrapper> service_worker_context, | 
|  | BrowserContext* browser_context) | 
|  | : service_worker_context_(service_worker_context), | 
|  | browser_context_(browser_context), | 
|  | notification_service_(browser_context->GetPlatformNotificationService()) { | 
|  | } | 
|  |  | 
|  | PlatformNotificationServiceProxy::~PlatformNotificationServiceProxy() = default; | 
|  |  | 
|  | void PlatformNotificationServiceProxy::Shutdown() { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | weak_ptr_factory_ui_.InvalidateWeakPtrs(); | 
|  | } | 
|  |  | 
|  | base::WeakPtr<PlatformNotificationServiceProxy> | 
|  | PlatformNotificationServiceProxy::AsWeakPtr() { | 
|  | return weak_ptr_factory_ui_.GetWeakPtr(); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::DoDisplayNotification( | 
|  | const NotificationDatabaseData& data, | 
|  | const GURL& service_worker_scope, | 
|  | DisplayResultCallback callback) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | if (notification_service_) { | 
|  | notification_service_->DisplayPersistentNotification( | 
|  | data.notification_id, service_worker_scope, data.origin, | 
|  | data.notification_data, | 
|  | data.notification_resources.value_or(blink::NotificationResources())); | 
|  | notifications::LogNotificationDisplayedEventToDevTools(browser_context_, | 
|  | data); | 
|  | } | 
|  | std::move(callback).Run(/* success= */ true, data.notification_id); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::VerifyServiceWorkerScope( | 
|  | const NotificationDatabaseData& data, | 
|  | DisplayResultCallback callback, | 
|  | blink::ServiceWorkerStatusCode status, | 
|  | scoped_refptr<ServiceWorkerRegistration> registration) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | base::OnceClosure task; | 
|  |  | 
|  | if (status == blink::ServiceWorkerStatusCode::kOk && | 
|  | registration->key().origin().GetURL() == data.origin) { | 
|  | DoDisplayNotification(data, registration->scope(), std::move(callback)); | 
|  | } else { | 
|  | std::move(callback).Run(/* success= */ false, /* notification_id= */ ""); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::DisplayNotification( | 
|  | const NotificationDatabaseData& data, | 
|  | DisplayResultCallback callback) { | 
|  | if (!service_worker_context_) { | 
|  | GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE}) | 
|  | ->PostTask(FROM_HERE, | 
|  | base::BindOnce( | 
|  | &PlatformNotificationServiceProxy::DoDisplayNotification, | 
|  | AsWeakPtr(), data, GURL(), std::move(callback))); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE}) | 
|  | ->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce( | 
|  | &ServiceWorkerContextWrapper::FindReadyRegistrationForId, | 
|  | service_worker_context_, data.service_worker_registration_id, | 
|  | blink::StorageKey(url::Origin::Create(data.origin)), | 
|  | base::BindOnce( | 
|  | &PlatformNotificationServiceProxy::VerifyServiceWorkerScope, | 
|  | weak_ptr_factory_io_.GetWeakPtr(), data, | 
|  | std::move(callback)))); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::CloseNotifications( | 
|  | const std::set<std::string>& notification_ids) { | 
|  | if (!notification_service_) | 
|  | return; | 
|  | GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE}) | 
|  | ->PostTask(FROM_HERE, | 
|  | base::BindOnce( | 
|  | &PlatformNotificationServiceProxy::DoCloseNotifications, | 
|  | AsWeakPtr(), notification_ids)); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::DoCloseNotifications( | 
|  | const std::set<std::string>& notification_ids) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | for (const std::string& notification_id : notification_ids) | 
|  | notification_service_->ClosePersistentNotification(notification_id); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::ScheduleTrigger(base::Time timestamp) { | 
|  | if (!notification_service_) | 
|  | return; | 
|  | GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE}) | 
|  | ->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&PlatformNotificationServiceProxy::DoScheduleTrigger, | 
|  | AsWeakPtr(), timestamp)); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::DoScheduleTrigger(base::Time timestamp) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | notification_service_->ScheduleTrigger(timestamp); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::ScheduleNotification( | 
|  | const NotificationDatabaseData& data) { | 
|  | DCHECK(data.notification_data.show_trigger_timestamp.has_value()); | 
|  | if (!notification_service_) | 
|  | return; | 
|  | GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE}) | 
|  | ->PostTask(FROM_HERE, | 
|  | base::BindOnce( | 
|  | &PlatformNotificationServiceProxy::DoScheduleNotification, | 
|  | AsWeakPtr(), data)); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::DoScheduleNotification( | 
|  | const NotificationDatabaseData& data) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | base::Time show_trigger_timestamp = | 
|  | data.notification_data.show_trigger_timestamp.value(); | 
|  | notifications::LogNotificationScheduledEventToDevTools( | 
|  | browser_context_, data, show_trigger_timestamp); | 
|  | notification_service_->ScheduleTrigger(show_trigger_timestamp); | 
|  | } | 
|  |  | 
|  | base::Time PlatformNotificationServiceProxy::GetNextTrigger() { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | if (!notification_service_) | 
|  | return base::Time::Max(); | 
|  | return notification_service_->ReadNextTriggerTimestamp(); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::RecordNotificationUkmEvent( | 
|  | const NotificationDatabaseData& data) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | if (!notification_service_) | 
|  | return; | 
|  | notification_service_->RecordNotificationUkmEvent(data); | 
|  | } | 
|  |  | 
|  | bool PlatformNotificationServiceProxy::ShouldLogClose(const GURL& origin) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | return notifications::ShouldLogNotificationEventToDevTools(browser_context_, | 
|  | origin); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::LogClose( | 
|  | const NotificationDatabaseData& data) { | 
|  | GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT}) | 
|  | ->PostTask(FROM_HERE, | 
|  | base::BindOnce(&PlatformNotificationServiceProxy::DoLogClose, | 
|  | AsWeakPtr(), data)); | 
|  | } | 
|  |  | 
|  | void PlatformNotificationServiceProxy::DoLogClose( | 
|  | const NotificationDatabaseData& data) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | notifications::LogNotificationClosedEventToDevTools(browser_context_, data); | 
|  | } | 
|  |  | 
|  | }  // namespace content |