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