1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/notifications/notification_event_dispatcher_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/optional.h"
11 #include "build/build_config.h"
12 #include "content/browser/notifications/devtools_event_logging.h"
13 #include "content/browser/notifications/platform_notification_context_impl.h"
14 #include "content/browser/service_worker/service_worker_context_wrapper.h"
15 #include "content/browser/service_worker/service_worker_registration.h"
16 #include "content/browser/service_worker/service_worker_storage.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/browser_task_traits.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/storage_partition.h"
22 #include "content/public/common/persistent_notification_status.h"
23 #include "third_party/blink/public/common/notifications/platform_notification_data.h"
24 
25 namespace content {
26 namespace {
27 
28 using NotificationDispatchCompleteCallback =
29     base::OnceCallback<void(PersistentNotificationStatus)>;
30 using NotificationOperationCallback =
31     base::OnceCallback<void(const ServiceWorkerRegistration*,
32                             const NotificationDatabaseData&,
33                             NotificationDispatchCompleteCallback)>;
34 using NotificationOperationCallbackWithContext =
35     base::OnceCallback<void(const scoped_refptr<PlatformNotificationContext>&,
36                             BrowserContext* browser_context,
37                             const ServiceWorkerRegistration*,
38                             const NotificationDatabaseData&,
39                             NotificationDispatchCompleteCallback)>;
40 
41 // Derives a PersistentNotificationStatus from the ServiceWorkerStatusCode.
ConvertServiceWorkerStatus(blink::ServiceWorkerStatusCode service_worker_status)42 PersistentNotificationStatus ConvertServiceWorkerStatus(
43     blink::ServiceWorkerStatusCode service_worker_status) {
44 #if defined(OS_ANDROID)
45   // This LOG(INFO) deliberately exists to help track down the cause of
46   // https://crbug.com/534537, where notifications sometimes do not react to
47   // the user clicking on them. It should be removed once that's fixed.
48   LOG(INFO) << "The notification event has finished: "
49             << blink::ServiceWorkerStatusToString(service_worker_status);
50 #endif
51   switch (service_worker_status) {
52     case blink::ServiceWorkerStatusCode::kOk:
53       return PersistentNotificationStatus::kSuccess;
54     case blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected:
55       return PersistentNotificationStatus::kWaitUntilRejected;
56     case blink::ServiceWorkerStatusCode::kErrorFailed:
57     case blink::ServiceWorkerStatusCode::kErrorAbort:
58     case blink::ServiceWorkerStatusCode::kErrorStartWorkerFailed:
59     case blink::ServiceWorkerStatusCode::kErrorProcessNotFound:
60     case blink::ServiceWorkerStatusCode::kErrorNotFound:
61     case blink::ServiceWorkerStatusCode::kErrorExists:
62     case blink::ServiceWorkerStatusCode::kErrorInstallWorkerFailed:
63     case blink::ServiceWorkerStatusCode::kErrorActivateWorkerFailed:
64     case blink::ServiceWorkerStatusCode::kErrorIpcFailed:
65     case blink::ServiceWorkerStatusCode::kErrorNetwork:
66     case blink::ServiceWorkerStatusCode::kErrorSecurity:
67     case blink::ServiceWorkerStatusCode::kErrorState:
68     case blink::ServiceWorkerStatusCode::kErrorTimeout:
69     case blink::ServiceWorkerStatusCode::kErrorScriptEvaluateFailed:
70     case blink::ServiceWorkerStatusCode::kErrorDiskCache:
71     case blink::ServiceWorkerStatusCode::kErrorRedundant:
72     case blink::ServiceWorkerStatusCode::kErrorDisallowed:
73     case blink::ServiceWorkerStatusCode::kErrorInvalidArguments:
74     case blink::ServiceWorkerStatusCode::kErrorStorageDisconnected:
75       return PersistentNotificationStatus::kServiceWorkerError;
76   }
77   NOTREACHED();
78   return PersistentNotificationStatus::kServiceWorkerError;
79 }
80 
81 // To be called when a notification event has finished with a
82 // blink::ServiceWorkerStatusCode result. Will run or post a task to call
83 // |dispatch_complete_callback| on the UI thread with a
84 // PersistentNotificationStatus derived from the service worker status.
ServiceWorkerNotificationEventFinished(NotificationDispatchCompleteCallback dispatch_complete_callback,blink::ServiceWorkerStatusCode service_worker_status)85 void ServiceWorkerNotificationEventFinished(
86     NotificationDispatchCompleteCallback dispatch_complete_callback,
87     blink::ServiceWorkerStatusCode service_worker_status) {
88   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
89   RunOrPostTaskOnThread(
90       FROM_HERE, BrowserThread::UI,
91       base::BindOnce(std::move(dispatch_complete_callback),
92                      ConvertServiceWorkerStatus(service_worker_status)));
93 }
94 
95 // Dispatches the given notification action event on
96 // |service_worker_registration| if the registration was available. Must be
97 // called on the service worker core thread.
DispatchNotificationEventOnRegistration(const NotificationDatabaseData & notification_database_data,NotificationOperationCallback dispatch_event_action,NotificationDispatchCompleteCallback dispatch_complete_callback,blink::ServiceWorkerStatusCode service_worker_status,scoped_refptr<ServiceWorkerRegistration> service_worker_registration)98 void DispatchNotificationEventOnRegistration(
99     const NotificationDatabaseData& notification_database_data,
100     NotificationOperationCallback dispatch_event_action,
101     NotificationDispatchCompleteCallback dispatch_complete_callback,
102     blink::ServiceWorkerStatusCode service_worker_status,
103     scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
104   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
105 #if defined(OS_ANDROID)
106   // This LOG(INFO) deliberately exists to help track down the cause of
107   // https://crbug.com/534537, where notifications sometimes do not react to
108   // the user clicking on them. It should be removed once that's fixed.
109   LOG(INFO) << "Trying to dispatch notification for SW with status: "
110             << blink::ServiceWorkerStatusToString(service_worker_status);
111 #endif
112   if (service_worker_status == blink::ServiceWorkerStatusCode::kOk) {
113     DCHECK(service_worker_registration->active_version());
114 
115     std::move(dispatch_event_action)
116         .Run(service_worker_registration.get(), notification_database_data,
117              std::move(dispatch_complete_callback));
118     return;
119   }
120 
121   PersistentNotificationStatus status = PersistentNotificationStatus::kSuccess;
122   switch (service_worker_status) {
123     case blink::ServiceWorkerStatusCode::kErrorNotFound:
124       status = PersistentNotificationStatus::kServiceWorkerMissing;
125       break;
126     case blink::ServiceWorkerStatusCode::kErrorFailed:
127     case blink::ServiceWorkerStatusCode::kErrorAbort:
128     case blink::ServiceWorkerStatusCode::kErrorStartWorkerFailed:
129     case blink::ServiceWorkerStatusCode::kErrorProcessNotFound:
130     case blink::ServiceWorkerStatusCode::kErrorExists:
131     case blink::ServiceWorkerStatusCode::kErrorInstallWorkerFailed:
132     case blink::ServiceWorkerStatusCode::kErrorActivateWorkerFailed:
133     case blink::ServiceWorkerStatusCode::kErrorIpcFailed:
134     case blink::ServiceWorkerStatusCode::kErrorNetwork:
135     case blink::ServiceWorkerStatusCode::kErrorSecurity:
136     case blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected:
137     case blink::ServiceWorkerStatusCode::kErrorState:
138     case blink::ServiceWorkerStatusCode::kErrorTimeout:
139     case blink::ServiceWorkerStatusCode::kErrorScriptEvaluateFailed:
140     case blink::ServiceWorkerStatusCode::kErrorDiskCache:
141     case blink::ServiceWorkerStatusCode::kErrorRedundant:
142     case blink::ServiceWorkerStatusCode::kErrorDisallowed:
143     case blink::ServiceWorkerStatusCode::kErrorInvalidArguments:
144     case blink::ServiceWorkerStatusCode::kErrorStorageDisconnected:
145       status = PersistentNotificationStatus::kServiceWorkerError;
146       break;
147     case blink::ServiceWorkerStatusCode::kOk:
148       NOTREACHED();
149       break;
150   }
151 
152   GetUIThreadTaskRunner({})->PostTask(
153       FROM_HERE, base::BindOnce(std::move(dispatch_complete_callback), status));
154 }
155 
156 // Finds the ServiceWorkerRegistration associated with the |origin| and
157 // |service_worker_registration_id|. Must be called on the UI thread.
FindServiceWorkerRegistration(const url::Origin & origin,const scoped_refptr<ServiceWorkerContextWrapper> & service_worker_context,NotificationOperationCallback notification_action_callback,NotificationDispatchCompleteCallback dispatch_complete_callback,bool success,const NotificationDatabaseData & notification_database_data)158 void FindServiceWorkerRegistration(
159     const url::Origin& origin,
160     const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context,
161     NotificationOperationCallback notification_action_callback,
162     NotificationDispatchCompleteCallback dispatch_complete_callback,
163     bool success,
164     const NotificationDatabaseData& notification_database_data) {
165   DCHECK_CURRENTLY_ON(BrowserThread::UI);
166 #if defined(OS_ANDROID)
167   // This LOG(INFO) deliberately exists to help track down the cause of
168   // https://crbug.com/534537, where notifications sometimes do not react to
169   // the user clicking on them. It should be removed once that's fixed.
170   LOG(INFO) << "Lookup for ServiceWoker Registration: success: " << success;
171 #endif
172   if (!success) {
173     std::move(dispatch_complete_callback)
174         .Run(PersistentNotificationStatus::kDatabaseError);
175     return;
176   }
177 
178   RunOrPostTaskOnThread(
179       FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
180       base::BindOnce(&ServiceWorkerContextWrapper::FindReadyRegistrationForId,
181                      service_worker_context,
182                      notification_database_data.service_worker_registration_id,
183                      origin,
184                      base::BindOnce(&DispatchNotificationEventOnRegistration,
185                                     notification_database_data,
186                                     std::move(notification_action_callback),
187                                     std::move(dispatch_complete_callback))));
188 }
189 
190 // Reads the data associated with the |notification_id| belonging to |origin|
191 // from the notification context.
ReadNotificationDatabaseData(const std::string & notification_id,const GURL & origin,PlatformNotificationContext::Interaction interaction,const scoped_refptr<ServiceWorkerContextWrapper> & service_worker_context,const scoped_refptr<PlatformNotificationContext> & notification_context,NotificationOperationCallback notification_read_callback,NotificationDispatchCompleteCallback dispatch_complete_callback)192 void ReadNotificationDatabaseData(
193     const std::string& notification_id,
194     const GURL& origin,
195     PlatformNotificationContext::Interaction interaction,
196     const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context,
197     const scoped_refptr<PlatformNotificationContext>& notification_context,
198     NotificationOperationCallback notification_read_callback,
199     NotificationDispatchCompleteCallback dispatch_complete_callback) {
200   DCHECK_CURRENTLY_ON(BrowserThread::UI);
201   notification_context->ReadNotificationDataAndRecordInteraction(
202       notification_id, origin, interaction,
203       base::BindOnce(&FindServiceWorkerRegistration,
204                      url::Origin::Create(origin), service_worker_context,
205                      std::move(notification_read_callback),
206                      std::move(dispatch_complete_callback)));
207 }
208 
209 // -----------------------------------------------------------------------------
210 
211 // Dispatches the notificationclick event on |service_worker|.
212 // Must be called on the service worker core thread.
DispatchNotificationClickEventOnWorker(const scoped_refptr<ServiceWorkerVersion> & service_worker,const NotificationDatabaseData & notification_database_data,const base::Optional<int> & action_index,const base::Optional<base::string16> & reply,ServiceWorkerVersion::StatusCallback callback,blink::ServiceWorkerStatusCode start_worker_status)213 void DispatchNotificationClickEventOnWorker(
214     const scoped_refptr<ServiceWorkerVersion>& service_worker,
215     const NotificationDatabaseData& notification_database_data,
216     const base::Optional<int>& action_index,
217     const base::Optional<base::string16>& reply,
218     ServiceWorkerVersion::StatusCallback callback,
219     blink::ServiceWorkerStatusCode start_worker_status) {
220   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
221   if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
222     std::move(callback).Run(start_worker_status);
223     return;
224   }
225 
226   int request_id = service_worker->StartRequest(
227       ServiceWorkerMetrics::EventType::NOTIFICATION_CLICK, std::move(callback));
228 
229   int action_index_int = -1 /* no value */;
230   if (action_index.has_value())
231     action_index_int = action_index.value();
232 
233   service_worker->endpoint()->DispatchNotificationClickEvent(
234       notification_database_data.notification_id,
235       notification_database_data.notification_data, action_index_int, reply,
236       service_worker->CreateSimpleEventCallback(request_id));
237 }
238 
239 // Dispatches the notification click event on the |service_worker_registration|.
DoDispatchNotificationClickEvent(const base::Optional<int> & action_index,const base::Optional<base::string16> & reply,const scoped_refptr<PlatformNotificationContext> & notification_context,BrowserContext * browser_context,const ServiceWorkerRegistration * service_worker_registration,const NotificationDatabaseData & notification_database_data,NotificationDispatchCompleteCallback dispatch_complete_callback)240 void DoDispatchNotificationClickEvent(
241     const base::Optional<int>& action_index,
242     const base::Optional<base::string16>& reply,
243     const scoped_refptr<PlatformNotificationContext>& notification_context,
244     BrowserContext* browser_context,
245     const ServiceWorkerRegistration* service_worker_registration,
246     const NotificationDatabaseData& notification_database_data,
247     NotificationDispatchCompleteCallback dispatch_complete_callback) {
248   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
249 
250   RunOrPostTaskOnThread(
251       FROM_HERE, BrowserThread::UI,
252       base::BindOnce(&notifications::LogNotificationClickedEventToDevTools,
253                      browser_context, notification_database_data, action_index,
254                      reply));
255 
256   service_worker_registration->active_version()->RunAfterStartWorker(
257       ServiceWorkerMetrics::EventType::NOTIFICATION_CLICK,
258       base::BindOnce(
259           &DispatchNotificationClickEventOnWorker,
260           base::WrapRefCounted(service_worker_registration->active_version()),
261           notification_database_data, action_index, reply,
262           base::BindOnce(&ServiceWorkerNotificationEventFinished,
263                          std::move(dispatch_complete_callback))));
264 }
265 
266 // -----------------------------------------------------------------------------
267 
268 // Called when the notification data has been deleted to finish the notification
269 // close event.
OnPersistentNotificationDataDeleted(blink::ServiceWorkerStatusCode service_worker_status,NotificationDispatchCompleteCallback dispatch_complete_callback,bool success)270 void OnPersistentNotificationDataDeleted(
271     blink::ServiceWorkerStatusCode service_worker_status,
272     NotificationDispatchCompleteCallback dispatch_complete_callback,
273     bool success) {
274   DCHECK_CURRENTLY_ON(BrowserThread::UI);
275   PersistentNotificationStatus status =
276       success ? PersistentNotificationStatus::kSuccess
277               : PersistentNotificationStatus::kDatabaseError;
278   if (service_worker_status != blink::ServiceWorkerStatusCode::kOk)
279     status = ConvertServiceWorkerStatus(service_worker_status);
280   std::move(dispatch_complete_callback).Run(status);
281 }
282 
283 // Called when the persistent notification close event has been handled
284 // to remove the notification from the database.
DeleteNotificationDataFromDatabase(const std::string & notification_id,const GURL & origin,const scoped_refptr<PlatformNotificationContext> & notification_context,NotificationDispatchCompleteCallback dispatch_complete_callback,blink::ServiceWorkerStatusCode status_code)285 void DeleteNotificationDataFromDatabase(
286     const std::string& notification_id,
287     const GURL& origin,
288     const scoped_refptr<PlatformNotificationContext>& notification_context,
289     NotificationDispatchCompleteCallback dispatch_complete_callback,
290     blink::ServiceWorkerStatusCode status_code) {
291   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
292   RunOrPostTaskOnThread(
293       FROM_HERE, BrowserThread::UI,
294       base::BindOnce(
295           &PlatformNotificationContext::DeleteNotificationData,
296           notification_context, notification_id, origin,
297           /* close_notification= */ false,
298           base::BindOnce(&OnPersistentNotificationDataDeleted, status_code,
299                          std::move(dispatch_complete_callback))));
300 }
301 
302 // Dispatches the notificationclose event on |service_worker|.
303 // Must be called on the service worker core thread.
DispatchNotificationCloseEventOnWorker(const scoped_refptr<ServiceWorkerVersion> & service_worker,const NotificationDatabaseData & notification_database_data,ServiceWorkerVersion::StatusCallback callback,blink::ServiceWorkerStatusCode start_worker_status)304 void DispatchNotificationCloseEventOnWorker(
305     const scoped_refptr<ServiceWorkerVersion>& service_worker,
306     const NotificationDatabaseData& notification_database_data,
307     ServiceWorkerVersion::StatusCallback callback,
308     blink::ServiceWorkerStatusCode start_worker_status) {
309   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
310   if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
311     std::move(callback).Run(start_worker_status);
312     return;
313   }
314 
315   int request_id = service_worker->StartRequest(
316       ServiceWorkerMetrics::EventType::NOTIFICATION_CLOSE, std::move(callback));
317 
318   service_worker->endpoint()->DispatchNotificationCloseEvent(
319       notification_database_data.notification_id,
320       notification_database_data.notification_data,
321       service_worker->CreateSimpleEventCallback(request_id));
322 }
323 
324 // Dispatches the notification close event on the service worker registration.
DoDispatchNotificationCloseEvent(const std::string & notification_id,bool by_user,const scoped_refptr<PlatformNotificationContext> & notification_context,BrowserContext * browser_context,const ServiceWorkerRegistration * service_worker_registration,const NotificationDatabaseData & notification_database_data,NotificationDispatchCompleteCallback dispatch_complete_callback)325 void DoDispatchNotificationCloseEvent(
326     const std::string& notification_id,
327     bool by_user,
328     const scoped_refptr<PlatformNotificationContext>& notification_context,
329     BrowserContext* browser_context,
330     const ServiceWorkerRegistration* service_worker_registration,
331     const NotificationDatabaseData& notification_database_data,
332     NotificationDispatchCompleteCallback dispatch_complete_callback) {
333   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
334   if (by_user) {
335     service_worker_registration->active_version()->RunAfterStartWorker(
336         ServiceWorkerMetrics::EventType::NOTIFICATION_CLOSE,
337         base::BindOnce(
338             &DispatchNotificationCloseEventOnWorker,
339             base::WrapRefCounted(service_worker_registration->active_version()),
340             notification_database_data,
341             base::BindOnce(&DeleteNotificationDataFromDatabase, notification_id,
342                            notification_database_data.origin,
343                            notification_context,
344                            std::move(dispatch_complete_callback))));
345   } else {
346     DeleteNotificationDataFromDatabase(
347         notification_id, notification_database_data.origin,
348         notification_context, std::move(dispatch_complete_callback),
349         blink::ServiceWorkerStatusCode::kOk);
350   }
351 }
352 
353 // Dispatches any notification event. The actual, specific event dispatch should
354 // be done by the |notification_action_callback|.
DispatchNotificationEvent(BrowserContext * browser_context,const std::string & notification_id,const GURL & origin,const PlatformNotificationContext::Interaction interaction,NotificationOperationCallbackWithContext notification_action_callback,NotificationDispatchCompleteCallback dispatch_complete_callback)355 void DispatchNotificationEvent(
356     BrowserContext* browser_context,
357     const std::string& notification_id,
358     const GURL& origin,
359     const PlatformNotificationContext::Interaction interaction,
360     NotificationOperationCallbackWithContext notification_action_callback,
361     NotificationDispatchCompleteCallback dispatch_complete_callback) {
362   DCHECK_CURRENTLY_ON(BrowserThread::UI);
363   DCHECK(!notification_id.empty());
364   DCHECK(origin.is_valid());
365 
366   StoragePartition* partition =
367       BrowserContext::GetStoragePartitionForSite(browser_context, origin);
368 
369   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context =
370       static_cast<ServiceWorkerContextWrapper*>(
371           partition->GetServiceWorkerContext());
372   scoped_refptr<PlatformNotificationContext> notification_context =
373       partition->GetPlatformNotificationContext();
374 
375   ReadNotificationDatabaseData(
376       notification_id, origin, interaction, service_worker_context,
377       notification_context,
378       base::BindOnce(std::move(notification_action_callback),
379                      notification_context, browser_context),
380       std::move(dispatch_complete_callback));
381 }
382 
383 }  // namespace
384 
385 // static
GetInstance()386 NotificationEventDispatcher* NotificationEventDispatcher::GetInstance() {
387   return NotificationEventDispatcherImpl::GetInstance();
388 }
389 
390 NotificationEventDispatcherImpl*
GetInstance()391 NotificationEventDispatcherImpl::GetInstance() {
392   DCHECK_CURRENTLY_ON(BrowserThread::UI);
393   return base::Singleton<NotificationEventDispatcherImpl>::get();
394 }
395 
396 NotificationEventDispatcherImpl::NotificationEventDispatcherImpl() = default;
397 NotificationEventDispatcherImpl::~NotificationEventDispatcherImpl() = default;
398 
DispatchNotificationClickEvent(BrowserContext * browser_context,const std::string & notification_id,const GURL & origin,const base::Optional<int> & action_index,const base::Optional<base::string16> & reply,NotificationDispatchCompleteCallback dispatch_complete_callback)399 void NotificationEventDispatcherImpl::DispatchNotificationClickEvent(
400     BrowserContext* browser_context,
401     const std::string& notification_id,
402     const GURL& origin,
403     const base::Optional<int>& action_index,
404     const base::Optional<base::string16>& reply,
405     NotificationDispatchCompleteCallback dispatch_complete_callback) {
406   DCHECK_CURRENTLY_ON(BrowserThread::UI);
407 
408   PlatformNotificationContext::Interaction interaction =
409       action_index.has_value()
410           ? PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED
411           : PlatformNotificationContext::Interaction::CLICKED;
412 
413   DispatchNotificationEvent(
414       browser_context, notification_id, origin, interaction,
415       base::BindOnce(&DoDispatchNotificationClickEvent, action_index, reply),
416       std::move(dispatch_complete_callback));
417 }
418 
DispatchNotificationCloseEvent(BrowserContext * browser_context,const std::string & notification_id,const GURL & origin,bool by_user,NotificationDispatchCompleteCallback dispatch_complete_callback)419 void NotificationEventDispatcherImpl::DispatchNotificationCloseEvent(
420     BrowserContext* browser_context,
421     const std::string& notification_id,
422     const GURL& origin,
423     bool by_user,
424     NotificationDispatchCompleteCallback dispatch_complete_callback) {
425   DCHECK_CURRENTLY_ON(BrowserThread::UI);
426 
427   DispatchNotificationEvent(browser_context, notification_id, origin,
428                             PlatformNotificationContext::Interaction::CLOSED,
429                             base::BindOnce(&DoDispatchNotificationCloseEvent,
430                                            notification_id, by_user),
431                             std::move(dispatch_complete_callback));
432 }
433 
RegisterNonPersistentNotificationListener(const std::string & notification_id,mojo::PendingRemote<blink::mojom::NonPersistentNotificationListener> event_listener_remote)434 void NotificationEventDispatcherImpl::RegisterNonPersistentNotificationListener(
435     const std::string& notification_id,
436     mojo::PendingRemote<blink::mojom::NonPersistentNotificationListener>
437         event_listener_remote) {
438   mojo::Remote<blink::mojom::NonPersistentNotificationListener> bound_remote(
439       std::move(event_listener_remote));
440 
441   // Observe connection errors, which occur when the JavaScript object or the
442   // renderer hosting them goes away. (For example through navigation.) The
443   // listener gets freed together with |this|, thus the Unretained is safe.
444   bound_remote.set_disconnect_handler(base::BindOnce(
445       &NotificationEventDispatcherImpl::
446           HandleConnectionErrorForNonPersistentNotificationListener,
447       base::Unretained(this), notification_id));
448 
449   if (non_persistent_notification_listeners_.count(notification_id)) {
450     // Dispatch the close event for any previously displayed notification with
451     // the same notification id. This happens whenever a non-persistent
452     // notification is replaced (by creating another with the same tag), since
453     // from the JavaScript point of view there will be two notification objects,
454     // and the old one needs to receive a close event before the new one
455     // receives a show event.
456     non_persistent_notification_listeners_[notification_id]->OnClose(
457         base::DoNothing());
458     non_persistent_notification_listeners_.erase(notification_id);
459   }
460 
461   non_persistent_notification_listeners_.insert(
462       {notification_id, std::move(bound_remote)});
463 }
464 
DispatchNonPersistentShowEvent(const std::string & notification_id)465 void NotificationEventDispatcherImpl::DispatchNonPersistentShowEvent(
466     const std::string& notification_id) {
467   if (!non_persistent_notification_listeners_.count(notification_id))
468     return;
469   non_persistent_notification_listeners_[notification_id]->OnShow();
470 }
471 
DispatchNonPersistentClickEvent(const std::string & notification_id,NotificationClickEventCallback callback)472 void NotificationEventDispatcherImpl::DispatchNonPersistentClickEvent(
473     const std::string& notification_id,
474     NotificationClickEventCallback callback) {
475   if (!non_persistent_notification_listeners_.count(notification_id)) {
476     std::move(callback).Run(false /* success */);
477     return;
478   }
479 
480   non_persistent_notification_listeners_[notification_id]->OnClick(
481       base::BindOnce(std::move(callback), true /* success */));
482 }
483 
DispatchNonPersistentCloseEvent(const std::string & notification_id,base::OnceClosure completed_closure)484 void NotificationEventDispatcherImpl::DispatchNonPersistentCloseEvent(
485     const std::string& notification_id,
486     base::OnceClosure completed_closure) {
487   if (!non_persistent_notification_listeners_.count(notification_id)) {
488     std::move(completed_closure).Run();
489     return;
490   }
491   // Listeners get freed together with |this|, thus the Unretained is safe.
492   non_persistent_notification_listeners_[notification_id]->OnClose(
493       base::BindOnce(
494           &NotificationEventDispatcherImpl::OnNonPersistentCloseComplete,
495           base::Unretained(this), notification_id,
496           std::move(completed_closure)));
497 }
498 
OnNonPersistentCloseComplete(const std::string & notification_id,base::OnceClosure completed_closure)499 void NotificationEventDispatcherImpl::OnNonPersistentCloseComplete(
500     const std::string& notification_id,
501     base::OnceClosure completed_closure) {
502   non_persistent_notification_listeners_.erase(notification_id);
503   std::move(completed_closure).Run();
504 }
505 
506 void NotificationEventDispatcherImpl::
HandleConnectionErrorForNonPersistentNotificationListener(const std::string & notification_id)507     HandleConnectionErrorForNonPersistentNotificationListener(
508         const std::string& notification_id) {
509   DCHECK(non_persistent_notification_listeners_.count(notification_id));
510   non_persistent_notification_listeners_.erase(notification_id);
511 }
512 
513 }  // namespace content
514