1 // Copyright 2013 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/renderer/service_worker/web_service_worker_provider_impl.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/trace_event/trace_event.h"
13 #include "content/common/service_worker/service_worker_utils.h"
14 #include "content/renderer/service_worker/service_worker_provider_context.h"
15 #include "content/renderer/service_worker/service_worker_type_converters.h"
16 #include "content/renderer/worker/fetch_client_settings_object_helpers.h"
17 #include "third_party/blink/public/common/messaging/message_port_channel.h"
18 #include "third_party/blink/public/mojom/service_worker/service_worker_container_type.mojom.h"
19 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
20 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
21 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h"
22 #include "third_party/blink/public/platform/web_url.h"
23
24 using blink::WebURL;
25
26 namespace content {
27
28 namespace {
29
30 const char kLostConnectionErrorMessage[] =
31 "Lost connection to the service worker system.";
32
33 } // anonymous namespace
34
WebServiceWorkerProviderImpl(ServiceWorkerProviderContext * context)35 WebServiceWorkerProviderImpl::WebServiceWorkerProviderImpl(
36 ServiceWorkerProviderContext* context)
37 : context_(context), provider_client_(nullptr) {
38 DCHECK(context_);
39 DCHECK_EQ(context_->container_type(),
40 blink::mojom::ServiceWorkerContainerType::kForWindow);
41 context_->SetWebServiceWorkerProvider(weak_factory_.GetWeakPtr());
42 }
43
44 WebServiceWorkerProviderImpl::~WebServiceWorkerProviderImpl() = default;
45
SetClient(blink::WebServiceWorkerProviderClient * client)46 void WebServiceWorkerProviderImpl::SetClient(
47 blink::WebServiceWorkerProviderClient* client) {
48 provider_client_ = client;
49 if (!provider_client_)
50 return;
51
52 blink::mojom::ServiceWorkerObjectInfoPtr controller =
53 context_->TakeController();
54 if (!controller)
55 return;
56 DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
57 controller->version_id);
58 SetController(std::move(controller), context_->used_features(),
59 false /* notify_controllerchange */);
60 }
61
RegisterServiceWorker(const WebURL & web_pattern,const WebURL & web_script_url,blink::mojom::ScriptType script_type,blink::mojom::ServiceWorkerUpdateViaCache update_via_cache,const blink::WebFetchClientSettingsObject & fetch_client_settings_object,std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks)62 void WebServiceWorkerProviderImpl::RegisterServiceWorker(
63 const WebURL& web_pattern,
64 const WebURL& web_script_url,
65 blink::mojom::ScriptType script_type,
66 blink::mojom::ServiceWorkerUpdateViaCache update_via_cache,
67 const blink::WebFetchClientSettingsObject& fetch_client_settings_object,
68 std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks) {
69 DCHECK(callbacks);
70
71 GURL pattern(web_pattern);
72 GURL script_url(web_script_url);
73 const std::string error_prefix("Failed to register a ServiceWorker: ");
74 if (pattern.possibly_invalid_spec().size() > url::kMaxURLChars ||
75 script_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
76 callbacks->OnError(blink::WebServiceWorkerError(
77 blink::mojom::ServiceWorkerErrorType::kSecurity,
78 blink::WebString::FromASCII(
79 error_prefix + "The provided scriptURL or scope is too long.")));
80 return;
81 }
82
83 if (!context_->container_host()) {
84 callbacks->OnError(blink::WebServiceWorkerError(
85 blink::mojom::ServiceWorkerErrorType::kAbort,
86 blink::WebString::FromASCII(error_prefix +
87 kLostConnectionErrorMessage)));
88 return;
89 }
90
91 TRACE_EVENT_ASYNC_BEGIN2(
92 "ServiceWorker", "WebServiceWorkerProviderImpl::RegisterServiceWorker",
93 this, "Scope", pattern.spec(), "Script URL", script_url.spec());
94
95 // TODO(asamidoi): Create this options in
96 // ServiceWorkerContainer::RegisterServiceWorker() and pass it as an argument
97 // in this function instead of blink::mojom::ScriptType and
98 // blink::mojom::ServiceWorkerUpdateViaCache.
99 auto options = blink::mojom::ServiceWorkerRegistrationOptions::New(
100 pattern, script_type, update_via_cache);
101 context_->container_host()->Register(
102 script_url, std::move(options),
103 FetchClientSettingsObjectFromWebToMojom(fetch_client_settings_object),
104 base::BindOnce(&WebServiceWorkerProviderImpl::OnRegistered,
105 weak_factory_.GetWeakPtr(), std::move(callbacks)));
106 }
107
GetRegistration(const blink::WebURL & web_document_url,std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks)108 void WebServiceWorkerProviderImpl::GetRegistration(
109 const blink::WebURL& web_document_url,
110 std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks) {
111 DCHECK(callbacks);
112 GURL document_url(web_document_url);
113 const std::string error_prefix("Failed to get a ServiceWorkerRegistration: ");
114 if (document_url.possibly_invalid_spec().size() > url::kMaxURLChars) {
115 callbacks->OnError(blink::WebServiceWorkerError(
116 blink::mojom::ServiceWorkerErrorType::kSecurity,
117 blink::WebString::FromASCII(error_prefix +
118 "The provided documentURL is too long.")));
119 return;
120 }
121
122 if (!context_->container_host()) {
123 callbacks->OnError(blink::WebServiceWorkerError(
124 blink::mojom::ServiceWorkerErrorType::kAbort,
125 blink::WebString::FromASCII(error_prefix +
126 kLostConnectionErrorMessage)));
127 return;
128 }
129
130 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
131 "WebServiceWorkerProviderImpl::GetRegistration",
132 this, "Document URL", document_url.spec());
133 context_->container_host()->GetRegistration(
134 document_url,
135 base::BindOnce(&WebServiceWorkerProviderImpl::OnDidGetRegistration,
136 weak_factory_.GetWeakPtr(), std::move(callbacks)));
137 }
138
GetRegistrations(std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks)139 void WebServiceWorkerProviderImpl::GetRegistrations(
140 std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks) {
141 DCHECK(callbacks);
142 if (!context_->container_host()) {
143 const std::string error_prefix(
144 "Failed to get ServiceWorkerRegistration objects: ");
145 callbacks->OnError(blink::WebServiceWorkerError(
146 blink::mojom::ServiceWorkerErrorType::kAbort,
147 blink::WebString::FromASCII(error_prefix +
148 kLostConnectionErrorMessage)));
149 return;
150 }
151
152 TRACE_EVENT_ASYNC_BEGIN0(
153 "ServiceWorker", "WebServiceWorkerProviderImpl::GetRegistrations", this);
154 context_->container_host()->GetRegistrations(
155 base::BindOnce(&WebServiceWorkerProviderImpl::OnDidGetRegistrations,
156 weak_factory_.GetWeakPtr(), std::move(callbacks)));
157 }
158
GetRegistrationForReady(GetRegistrationForReadyCallback callback)159 void WebServiceWorkerProviderImpl::GetRegistrationForReady(
160 GetRegistrationForReadyCallback callback) {
161 if (!context_->container_host()) {
162 return;
163 }
164
165 TRACE_EVENT_ASYNC_BEGIN0(
166 "ServiceWorker", "WebServiceWorkerProviderImpl::GetRegistrationForReady",
167 this);
168 context_->container_host()->GetRegistrationForReady(base::BindOnce(
169 &WebServiceWorkerProviderImpl::OnDidGetRegistrationForReady,
170 weak_factory_.GetWeakPtr(), std::move(callback)));
171 }
172
ValidateScopeAndScriptURL(const blink::WebURL & scope,const blink::WebURL & script_url,blink::WebString * error_message)173 bool WebServiceWorkerProviderImpl::ValidateScopeAndScriptURL(
174 const blink::WebURL& scope,
175 const blink::WebURL& script_url,
176 blink::WebString* error_message) {
177 std::string error;
178 bool has_error = ServiceWorkerUtils::ContainsDisallowedCharacter(
179 scope, script_url, &error);
180 if (has_error)
181 *error_message = blink::WebString::FromUTF8(error);
182 return !has_error;
183 }
184
SetController(blink::mojom::ServiceWorkerObjectInfoPtr controller,const std::set<blink::mojom::WebFeature> & features,bool should_notify_controller_change)185 void WebServiceWorkerProviderImpl::SetController(
186 blink::mojom::ServiceWorkerObjectInfoPtr controller,
187 const std::set<blink::mojom::WebFeature>& features,
188 bool should_notify_controller_change) {
189 if (!provider_client_)
190 return;
191
192 for (blink::mojom::WebFeature feature : features)
193 provider_client_->CountFeature(feature);
194 provider_client_->SetController(
195 std::move(controller).To<blink::WebServiceWorkerObjectInfo>(),
196 should_notify_controller_change);
197 }
198
PostMessageToClient(blink::mojom::ServiceWorkerObjectInfoPtr source,blink::TransferableMessage message)199 void WebServiceWorkerProviderImpl::PostMessageToClient(
200 blink::mojom::ServiceWorkerObjectInfoPtr source,
201 blink::TransferableMessage message) {
202 if (!provider_client_)
203 return;
204
205 provider_client_->ReceiveMessage(
206 std::move(source).To<blink::WebServiceWorkerObjectInfo>(),
207 std::move(message));
208 }
209
CountFeature(blink::mojom::WebFeature feature)210 void WebServiceWorkerProviderImpl::CountFeature(
211 blink::mojom::WebFeature feature) {
212 if (!provider_client_)
213 return;
214 provider_client_->CountFeature(feature);
215 }
216
OnRegistered(std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks,blink::mojom::ServiceWorkerErrorType error,const base::Optional<std::string> & error_msg,blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration)217 void WebServiceWorkerProviderImpl::OnRegistered(
218 std::unique_ptr<WebServiceWorkerRegistrationCallbacks> callbacks,
219 blink::mojom::ServiceWorkerErrorType error,
220 const base::Optional<std::string>& error_msg,
221 blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration) {
222 TRACE_EVENT_ASYNC_END2(
223 "ServiceWorker", "WebServiceWorkerProviderImpl::RegisterServiceWorker",
224 this, "Error", ServiceWorkerUtils::MojoEnumToString(error), "Message",
225 error_msg ? *error_msg : "Success");
226 if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
227 DCHECK(error_msg);
228 DCHECK(!registration);
229 callbacks->OnError(blink::WebServiceWorkerError(
230 error, blink::WebString::FromASCII(*error_msg)));
231 return;
232 }
233
234 DCHECK(!error_msg);
235 DCHECK(registration);
236 DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
237 registration->registration_id);
238 callbacks->OnSuccess(
239 std::move(registration)
240 .To<blink::WebServiceWorkerRegistrationObjectInfo>());
241 }
242
OnDidGetRegistration(std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks,blink::mojom::ServiceWorkerErrorType error,const base::Optional<std::string> & error_msg,blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration)243 void WebServiceWorkerProviderImpl::OnDidGetRegistration(
244 std::unique_ptr<WebServiceWorkerGetRegistrationCallbacks> callbacks,
245 blink::mojom::ServiceWorkerErrorType error,
246 const base::Optional<std::string>& error_msg,
247 blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration) {
248 TRACE_EVENT_ASYNC_END2("ServiceWorker",
249 "WebServiceWorkerProviderImpl::GetRegistration", this,
250 "Error", ServiceWorkerUtils::MojoEnumToString(error),
251 "Message", error_msg ? *error_msg : "Success");
252 if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
253 DCHECK(error_msg);
254 DCHECK(!registration);
255 callbacks->OnError(blink::WebServiceWorkerError(
256 error, blink::WebString::FromASCII(*error_msg)));
257 return;
258 }
259
260 DCHECK(!error_msg);
261 // |registration| is nullptr if there is no registration at the scope or it's
262 // uninstalling.
263 DCHECK(!registration ||
264 registration->registration_id !=
265 blink::mojom::kInvalidServiceWorkerRegistrationId);
266 callbacks->OnSuccess(
267 std::move(registration)
268 .To<blink::WebServiceWorkerRegistrationObjectInfo>());
269 }
270
OnDidGetRegistrations(std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks,blink::mojom::ServiceWorkerErrorType error,const base::Optional<std::string> & error_msg,base::Optional<std::vector<blink::mojom::ServiceWorkerRegistrationObjectInfoPtr>> infos)271 void WebServiceWorkerProviderImpl::OnDidGetRegistrations(
272 std::unique_ptr<WebServiceWorkerGetRegistrationsCallbacks> callbacks,
273 blink::mojom::ServiceWorkerErrorType error,
274 const base::Optional<std::string>& error_msg,
275 base::Optional<
276 std::vector<blink::mojom::ServiceWorkerRegistrationObjectInfoPtr>>
277 infos) {
278 TRACE_EVENT_ASYNC_END2("ServiceWorker",
279 "WebServiceWorkerProviderImpl::GetRegistrations", this,
280 "Error", ServiceWorkerUtils::MojoEnumToString(error),
281 "Message", error_msg ? *error_msg : "Success");
282 if (error != blink::mojom::ServiceWorkerErrorType::kNone) {
283 DCHECK(error_msg);
284 DCHECK(!infos);
285 callbacks->OnError(blink::WebServiceWorkerError(
286 error, blink::WebString::FromASCII(*error_msg)));
287 return;
288 }
289
290 DCHECK(!error_msg);
291 DCHECK(infos);
292 blink::WebVector<blink::WebServiceWorkerRegistrationObjectInfo> registrations;
293 registrations.reserve(infos->size());
294 for (size_t i = 0; i < infos->size(); ++i) {
295 DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
296 (*infos)[i]->registration_id);
297 registrations.emplace_back(
298 std::move((*infos)[i])
299 .To<blink::WebServiceWorkerRegistrationObjectInfo>());
300 }
301 callbacks->OnSuccess(std::move(registrations));
302 }
303
OnDidGetRegistrationForReady(GetRegistrationForReadyCallback callback,blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration)304 void WebServiceWorkerProviderImpl::OnDidGetRegistrationForReady(
305 GetRegistrationForReadyCallback callback,
306 blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration) {
307 TRACE_EVENT_ASYNC_END0(
308 "ServiceWorker", "WebServiceWorkerProviderImpl::GetRegistrationForReady",
309 this);
310 // TODO(leonhsl): Currently the only reason that we allow nullable
311 // |registration| is: impl of the mojo method
312 // GetRegistrationForReady() needs to respond some non-sense params even if it
313 // has found that the request is a bad message and has called
314 // mojo::ReportBadMessage(), this is forced by Mojo, please see
315 // content::ServiceWorkerContainerHost::GetRegistrationForReady(). We'll find
316 // a better solution once the discussion at
317 // https://groups.google.com/a/chromium.org/forum/#!topic/chromium-mojo/NNsogKNurlA
318 // settled.
319 CHECK(registration);
320 DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
321 registration->registration_id);
322 std::move(callback).Run(
323 std::move(registration)
324 .To<blink::WebServiceWorkerRegistrationObjectInfo>());
325 }
326
327 } // namespace content
328