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(¬ifications::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