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/push_messaging/push_messaging_router.h"
6 
7 #include <string>
8 
9 #include "base/bind.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/task/post_task.h"
12 #include "content/browser/devtools/devtools_background_services_context_impl.h"
13 #include "content/browser/service_worker/service_worker_context_wrapper.h"
14 #include "content/browser/service_worker/service_worker_registration.h"
15 #include "content/browser/service_worker/service_worker_storage.h"
16 #include "content/browser/storage_partition_impl.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 "third_party/blink/public/common/service_worker/service_worker_status_code.h"
21 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
22 
23 namespace content {
24 
25 namespace {
26 
RunDeliverCallback(PushMessagingRouter::DeliverMessageCallback deliver_message_callback,blink::mojom::PushDeliveryStatus delivery_status)27 void RunDeliverCallback(
28     PushMessagingRouter::DeliverMessageCallback deliver_message_callback,
29     blink::mojom::PushDeliveryStatus delivery_status) {
30   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
31   // Use PostTask() instead of RunOrPostTaskOnThread() to ensure the callback
32   // is called asynchronously.
33   base::PostTask(
34       FROM_HERE, {BrowserThread::UI},
35       base::BindOnce(std::move(deliver_message_callback), delivery_status));
36 }
37 
38 }  // namespace
39 
40 // static
DeliverMessage(BrowserContext * browser_context,const GURL & origin,int64_t service_worker_registration_id,const std::string & message_id,base::Optional<std::string> payload,DeliverMessageCallback deliver_message_callback)41 void PushMessagingRouter::DeliverMessage(
42     BrowserContext* browser_context,
43     const GURL& origin,
44     int64_t service_worker_registration_id,
45     const std::string& message_id,
46     base::Optional<std::string> payload,
47     DeliverMessageCallback deliver_message_callback) {
48   DCHECK_CURRENTLY_ON(BrowserThread::UI);
49   StoragePartition* partition =
50       BrowserContext::GetStoragePartitionForSite(browser_context, origin);
51   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context =
52       static_cast<ServiceWorkerContextWrapper*>(
53           partition->GetServiceWorkerContext());
54   auto devtools_context =
55       base::WrapRefCounted<DevToolsBackgroundServicesContextImpl>(
56           service_worker_context->storage_partition()
57               ->GetDevToolsBackgroundServicesContext());
58   RunOrPostTaskOnThread(
59       FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
60       base::BindOnce(&PushMessagingRouter::FindServiceWorkerRegistration,
61                      std::move(service_worker_context),
62                      std::move(devtools_context), origin,
63                      service_worker_registration_id, message_id,
64                      std::move(payload), std::move(deliver_message_callback)));
65 }
66 
67 // static
FindServiceWorkerRegistration(scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,const GURL & origin,int64_t service_worker_registration_id,const std::string & message_id,base::Optional<std::string> payload,DeliverMessageCallback deliver_message_callback)68 void PushMessagingRouter::FindServiceWorkerRegistration(
69     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
70     scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,
71     const GURL& origin,
72     int64_t service_worker_registration_id,
73     const std::string& message_id,
74     base::Optional<std::string> payload,
75     DeliverMessageCallback deliver_message_callback) {
76   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
77   // Try to acquire the registration from storage. If it's already live we'll
78   // receive it right away. If not, it will be revived from storage.
79   service_worker_context->FindReadyRegistrationForId(
80       service_worker_registration_id, origin,
81       base::BindOnce(
82           &PushMessagingRouter::FindServiceWorkerRegistrationCallback,
83           std::move(devtools_context), message_id, std::move(payload),
84           std::move(deliver_message_callback)));
85 }
86 
87 // static
FindServiceWorkerRegistrationCallback(scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,const std::string & message_id,base::Optional<std::string> payload,DeliverMessageCallback deliver_message_callback,blink::ServiceWorkerStatusCode service_worker_status,scoped_refptr<ServiceWorkerRegistration> service_worker_registration)88 void PushMessagingRouter::FindServiceWorkerRegistrationCallback(
89     scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,
90     const std::string& message_id,
91     base::Optional<std::string> payload,
92     DeliverMessageCallback deliver_message_callback,
93     blink::ServiceWorkerStatusCode service_worker_status,
94     scoped_refptr<ServiceWorkerRegistration> service_worker_registration) {
95   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
96   UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus.FindServiceWorker",
97                             service_worker_status);
98   if (service_worker_status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
99     RunDeliverCallback(std::move(deliver_message_callback),
100                        blink::mojom::PushDeliveryStatus::NO_SERVICE_WORKER);
101     return;
102   }
103   if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
104     RunDeliverCallback(std::move(deliver_message_callback),
105                        blink::mojom::PushDeliveryStatus::SERVICE_WORKER_ERROR);
106     return;
107   }
108 
109   ServiceWorkerVersion* version = service_worker_registration->active_version();
110   DCHECK(version);
111 
112   // Hold on to the service worker registration in the callback to keep it
113   // alive until the callback dies. Otherwise the registration could be
114   // released when this method returns - before the event is delivered to the
115   // service worker.
116   version->RunAfterStartWorker(
117       ServiceWorkerMetrics::EventType::PUSH,
118       base::BindOnce(&PushMessagingRouter::DeliverMessageToWorker,
119                      base::WrapRefCounted(version),
120                      std::move(service_worker_registration),
121                      std::move(devtools_context), message_id,
122                      std::move(payload), std::move(deliver_message_callback)));
123 }
124 
125 // static
DeliverMessageToWorker(scoped_refptr<ServiceWorkerVersion> service_worker,scoped_refptr<ServiceWorkerRegistration> service_worker_registration,scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,const std::string & message_id,base::Optional<std::string> payload,DeliverMessageCallback deliver_message_callback,blink::ServiceWorkerStatusCode start_worker_status)126 void PushMessagingRouter::DeliverMessageToWorker(
127     scoped_refptr<ServiceWorkerVersion> service_worker,
128     scoped_refptr<ServiceWorkerRegistration> service_worker_registration,
129     scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,
130     const std::string& message_id,
131     base::Optional<std::string> payload,
132     DeliverMessageCallback deliver_message_callback,
133     blink::ServiceWorkerStatusCode start_worker_status) {
134   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
135   if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
136     DeliverMessageEnd(std::move(service_worker),
137                       std::move(service_worker_registration),
138                       std::move(devtools_context), message_id,
139                       std::move(deliver_message_callback), start_worker_status);
140     return;
141   }
142 
143   int request_id = service_worker->StartRequestWithCustomTimeout(
144       ServiceWorkerMetrics::EventType::PUSH,
145       base::BindOnce(&PushMessagingRouter::DeliverMessageEnd, service_worker,
146                      std::move(service_worker_registration), devtools_context,
147                      message_id, std::move(deliver_message_callback)),
148       base::TimeDelta::FromSeconds(blink::mojom::kPushEventTimeoutSeconds),
149       ServiceWorkerVersion::KILL_ON_TIMEOUT);
150 
151   service_worker->endpoint()->DispatchPushEvent(
152       payload, service_worker->CreateSimpleEventCallback(request_id));
153 
154   if (devtools_context->IsRecording(
155           DevToolsBackgroundService::kPushMessaging)) {
156     std::map<std::string, std::string> event_metadata;
157     if (payload)
158       event_metadata["Payload"] = *payload;
159     devtools_context->LogBackgroundServiceEventOnCoreThread(
160         service_worker->registration_id(), service_worker->script_origin(),
161         DevToolsBackgroundService::kPushMessaging, "Push event dispatched",
162         message_id, event_metadata);
163   }
164 }
165 
166 // static
DeliverMessageEnd(scoped_refptr<ServiceWorkerVersion> service_worker,scoped_refptr<ServiceWorkerRegistration> service_worker_registration,scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,const std::string & message_id,DeliverMessageCallback deliver_message_callback,blink::ServiceWorkerStatusCode service_worker_status)167 void PushMessagingRouter::DeliverMessageEnd(
168     scoped_refptr<ServiceWorkerVersion> service_worker,
169     scoped_refptr<ServiceWorkerRegistration> service_worker_registration,
170     scoped_refptr<DevToolsBackgroundServicesContextImpl> devtools_context,
171     const std::string& message_id,
172     DeliverMessageCallback deliver_message_callback,
173     blink::ServiceWorkerStatusCode service_worker_status) {
174   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
175   UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus.ServiceWorkerEvent",
176                             service_worker_status);
177   blink::mojom::PushDeliveryStatus delivery_status =
178       blink::mojom::PushDeliveryStatus::SERVICE_WORKER_ERROR;
179   std::string status_description;
180   switch (service_worker_status) {
181     case blink::ServiceWorkerStatusCode::kOk:
182       delivery_status = blink::mojom::PushDeliveryStatus::SUCCESS;
183       status_description = "Success";
184       break;
185     case blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected:
186       delivery_status =
187           blink::mojom::PushDeliveryStatus::EVENT_WAITUNTIL_REJECTED;
188       status_description = "waitUntil Rejected";
189       break;
190     case blink::ServiceWorkerStatusCode::kErrorTimeout:
191       delivery_status = blink::mojom::PushDeliveryStatus::TIMEOUT;
192       status_description = "Timeout";
193       break;
194     case blink::ServiceWorkerStatusCode::kErrorFailed:
195     case blink::ServiceWorkerStatusCode::kErrorAbort:
196     case blink::ServiceWorkerStatusCode::kErrorStartWorkerFailed:
197     case blink::ServiceWorkerStatusCode::kErrorProcessNotFound:
198     case blink::ServiceWorkerStatusCode::kErrorNotFound:
199     case blink::ServiceWorkerStatusCode::kErrorIpcFailed:
200     case blink::ServiceWorkerStatusCode::kErrorScriptEvaluateFailed:
201     case blink::ServiceWorkerStatusCode::kErrorDiskCache:
202     case blink::ServiceWorkerStatusCode::kErrorRedundant:
203     case blink::ServiceWorkerStatusCode::kErrorDisallowed:
204       delivery_status = blink::mojom::PushDeliveryStatus::SERVICE_WORKER_ERROR;
205       break;
206     case blink::ServiceWorkerStatusCode::kErrorExists:
207     case blink::ServiceWorkerStatusCode::kErrorInstallWorkerFailed:
208     case blink::ServiceWorkerStatusCode::kErrorActivateWorkerFailed:
209     case blink::ServiceWorkerStatusCode::kErrorNetwork:
210     case blink::ServiceWorkerStatusCode::kErrorSecurity:
211     case blink::ServiceWorkerStatusCode::kErrorState:
212     case blink::ServiceWorkerStatusCode::kErrorInvalidArguments:
213       NOTREACHED() << "Got unexpected error code: "
214                    << static_cast<uint32_t>(service_worker_status) << " "
215                    << blink::ServiceWorkerStatusToString(service_worker_status);
216       delivery_status = blink::mojom::PushDeliveryStatus::SERVICE_WORKER_ERROR;
217       break;
218   }
219   RunDeliverCallback(std::move(deliver_message_callback), delivery_status);
220 
221   if (devtools_context->IsRecording(
222           DevToolsBackgroundService::kPushMessaging) &&
223       delivery_status !=
224           blink::mojom::PushDeliveryStatus::SERVICE_WORKER_ERROR) {
225     devtools_context->LogBackgroundServiceEventOnCoreThread(
226         service_worker->registration_id(), service_worker->script_origin(),
227         DevToolsBackgroundService::kPushMessaging, "Push event completed",
228         message_id, {{"Status", status_description}});
229   }
230 }
231 
232 }  // namespace content
233