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