1 // Copyright 2017 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/service_worker/service_worker_new_script_loader.h"
6
7 #include <memory>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "content/browser/appcache/appcache_disk_cache_ops.h"
14 #include "content/browser/service_worker/service_worker_cache_writer.h"
15 #include "content/browser/service_worker/service_worker_consts.h"
16 #include "content/browser/service_worker/service_worker_context_core.h"
17 #include "content/browser/service_worker/service_worker_disk_cache.h"
18 #include "content/browser/service_worker/service_worker_loader_helpers.h"
19 #include "content/browser/service_worker/service_worker_storage.h"
20 #include "content/browser/service_worker/service_worker_version.h"
21 #include "content/browser/url_loader_factory_getter.h"
22 #include "content/common/service_worker/service_worker_utils.h"
23 #include "net/base/ip_endpoint.h"
24 #include "net/base/load_flags.h"
25 #include "net/base/net_errors.h"
26 #include "net/cert/cert_status_flags.h"
27 #include "net/http/http_response_info.h"
28 #include "services/network/public/mojom/url_response_head.mojom.h"
29 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
30
31 namespace content {
32
33 // We chose this size because the AppCache uses this.
34 const uint32_t ServiceWorkerNewScriptLoader::kReadBufferSize = 32768;
35
36 // This is for debugging https://crbug.com/959627.
37 // The purpose is to see where the IOBuffer comes from by checking |__vfptr|.
38 class ServiceWorkerNewScriptLoader::WrappedIOBuffer
39 : public net::WrappedIOBuffer {
40 public:
WrappedIOBuffer(const char * data)41 WrappedIOBuffer(const char* data) : net::WrappedIOBuffer(data) {}
42
43 private:
44 ~WrappedIOBuffer() override = default;
45
46 // This is to make sure that the vtable is not merged with other classes.
dummy()47 virtual void dummy() { NOTREACHED(); }
48 };
49
50 std::unique_ptr<ServiceWorkerNewScriptLoader>
CreateAndStart(int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & original_request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,scoped_refptr<ServiceWorkerVersion> version,scoped_refptr<network::SharedURLLoaderFactory> loader_factory,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation,int64_t cache_resource_id)51 ServiceWorkerNewScriptLoader::CreateAndStart(
52 int32_t routing_id,
53 int32_t request_id,
54 uint32_t options,
55 const network::ResourceRequest& original_request,
56 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
57 scoped_refptr<ServiceWorkerVersion> version,
58 scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
59 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
60 int64_t cache_resource_id) {
61 return base::WrapUnique(new ServiceWorkerNewScriptLoader(
62 routing_id, request_id, options, original_request, std::move(client),
63 version, loader_factory, traffic_annotation, cache_resource_id));
64 }
65
66 // TODO(nhiroki): We're doing multiple things in the ctor. Consider factors out
67 // some of them into a separate function.
ServiceWorkerNewScriptLoader(int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & original_request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,scoped_refptr<ServiceWorkerVersion> version,scoped_refptr<network::SharedURLLoaderFactory> loader_factory,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation,int64_t cache_resource_id)68 ServiceWorkerNewScriptLoader::ServiceWorkerNewScriptLoader(
69 int32_t routing_id,
70 int32_t request_id,
71 uint32_t options,
72 const network::ResourceRequest& original_request,
73 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
74 scoped_refptr<ServiceWorkerVersion> version,
75 scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
76 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
77 int64_t cache_resource_id)
78 : request_url_(original_request.url),
79 resource_type_(static_cast<blink::mojom::ResourceType>(
80 original_request.resource_type)),
81 original_options_(options),
82 version_(version),
83 network_watcher_(FROM_HERE,
84 mojo::SimpleWatcher::ArmingPolicy::MANUAL,
85 base::SequencedTaskRunnerHandle::Get()),
86 loader_factory_(std::move(loader_factory)),
87 client_(std::move(client)) {
88 DCHECK_NE(cache_resource_id, blink::mojom::kInvalidServiceWorkerResourceId);
89
90 network::ResourceRequest resource_request(original_request);
91 #if DCHECK_IS_ON()
92 CheckVersionStatusBeforeLoad();
93 #endif // DCHECK_IS_ON()
94
95 scoped_refptr<ServiceWorkerRegistration> registration =
96 version_->context()->GetLiveRegistration(version_->registration_id());
97 // ServiceWorkerVersion keeps the registration alive while the service
98 // worker is starting up, and it must be starting up here.
99 DCHECK(registration);
100 const bool is_main_script =
101 resource_type_ == blink::mojom::ResourceType::kServiceWorker;
102 if (is_main_script) {
103 // Request SSLInfo. It will be persisted in service worker storage and
104 // may be used by ServiceWorkerNavigationLoader for navigations handled
105 // by this service worker.
106 options |= network::mojom::kURLLoadOptionSendSSLInfoWithResponse;
107
108 resource_request.headers.SetHeader("Service-Worker", "script");
109 }
110
111 // Validate the browser cache if needed, e.g., updateViaCache demands it or 24
112 // hours passed since the last update check that hit network.
113 base::TimeDelta time_since_last_check =
114 base::Time::Now() - registration->last_update_check();
115 if (service_worker_loader_helpers::ShouldValidateBrowserCacheForScript(
116 is_main_script, version_->force_bypass_cache_for_scripts(),
117 registration->update_via_cache(), time_since_last_check)) {
118 resource_request.load_flags |= net::LOAD_VALIDATE_CACHE;
119 }
120
121 ServiceWorkerStorage* storage = version_->context()->storage();
122 cache_writer_ = ServiceWorkerCacheWriter::CreateForWriteBack(
123 storage->CreateResponseWriter(cache_resource_id));
124
125 version_->script_cache_map()->NotifyStartedCaching(request_url_,
126 cache_resource_id);
127
128 // Disable MIME sniffing. The spec requires the header list to have a
129 // JavaScript MIME type. Therefore, no sniffing is needed.
130 options &= ~network::mojom::kURLLoadOptionSniffMimeType;
131
132 loader_factory_->CreateLoaderAndStart(
133 network_loader_.BindNewPipeAndPassReceiver(), routing_id, request_id,
134 options, resource_request,
135 network_client_receiver_.BindNewPipeAndPassRemote(), traffic_annotation);
136 DCHECK_EQ(LoaderState::kNotStarted, network_loader_state_);
137 network_loader_state_ = LoaderState::kLoadingHeader;
138 }
139
~ServiceWorkerNewScriptLoader()140 ServiceWorkerNewScriptLoader::~ServiceWorkerNewScriptLoader() {
141 // This class is used as a SelfOwnedReceiver and its lifetime is tied to the
142 // corresponding mojo connection. There could be cases where the mojo
143 // connection is disconnected while writing the response to the storage.
144 // Complete this loader with ERR_FAILED in such cases to update the script
145 // cache map.
146 bool writers_completed = header_writer_state_ == WriterState::kCompleted &&
147 body_writer_state_ == WriterState::kCompleted;
148 if (network_loader_state_ == LoaderState::kCompleted && !writers_completed) {
149 DCHECK(client_);
150 CommitCompleted(network::URLLoaderCompletionStatus(net::ERR_FAILED),
151 ServiceWorkerConsts::kServiceWorkerInvalidVersionError);
152 }
153 }
154
FollowRedirect(const std::vector<std::string> & removed_headers,const net::HttpRequestHeaders & modified_headers,const base::Optional<GURL> & new_url)155 void ServiceWorkerNewScriptLoader::FollowRedirect(
156 const std::vector<std::string>& removed_headers,
157 const net::HttpRequestHeaders& modified_headers,
158 const base::Optional<GURL>& new_url) {
159 // Resource requests for service worker scripts should not follow redirects.
160 // See comments in OnReceiveRedirect().
161 NOTREACHED();
162 }
163
SetPriority(net::RequestPriority priority,int32_t intra_priority_value)164 void ServiceWorkerNewScriptLoader::SetPriority(net::RequestPriority priority,
165 int32_t intra_priority_value) {
166 if (network_loader_)
167 network_loader_->SetPriority(priority, intra_priority_value);
168 }
169
PauseReadingBodyFromNet()170 void ServiceWorkerNewScriptLoader::PauseReadingBodyFromNet() {
171 if (network_loader_)
172 network_loader_->PauseReadingBodyFromNet();
173 }
174
ResumeReadingBodyFromNet()175 void ServiceWorkerNewScriptLoader::ResumeReadingBodyFromNet() {
176 if (network_loader_)
177 network_loader_->ResumeReadingBodyFromNet();
178 }
179
180 // URLLoaderClient for network loader ------------------------------------------
181
OnReceiveResponse(network::mojom::URLResponseHeadPtr response_head)182 void ServiceWorkerNewScriptLoader::OnReceiveResponse(
183 network::mojom::URLResponseHeadPtr response_head) {
184 DCHECK_EQ(LoaderState::kLoadingHeader, network_loader_state_);
185 if (!version_->context() || version_->is_redundant()) {
186 CommitCompleted(network::URLLoaderCompletionStatus(net::ERR_FAILED),
187 ServiceWorkerConsts::kServiceWorkerInvalidVersionError);
188 return;
189 }
190
191 blink::ServiceWorkerStatusCode service_worker_state =
192 blink::ServiceWorkerStatusCode::kOk;
193 network::URLLoaderCompletionStatus completion_status;
194 std::string error_message;
195 std::unique_ptr<net::HttpResponseInfo> response_info =
196 service_worker_loader_helpers::CreateHttpResponseInfoAndCheckHeaders(
197 *response_head, &service_worker_state, &completion_status,
198 &error_message);
199 if (!response_info) {
200 DCHECK_NE(net::OK, completion_status.error_code);
201 CommitCompleted(completion_status, error_message);
202 return;
203 }
204
205 if (resource_type_ == blink::mojom::ResourceType::kServiceWorker) {
206 // Check the path restriction defined in the spec:
207 // https://w3c.github.io/ServiceWorker/#service-worker-script-response
208 std::string service_worker_allowed;
209 bool has_header = response_head->headers && response_head->headers->EnumerateHeader(
210 nullptr, ServiceWorkerConsts::kServiceWorkerAllowed,
211 &service_worker_allowed);
212 if (!ServiceWorkerUtils::IsPathRestrictionSatisfied(
213 version_->scope(), request_url_,
214 has_header ? &service_worker_allowed : nullptr, &error_message)) {
215 CommitCompleted(
216 network::URLLoaderCompletionStatus(net::ERR_INSECURE_RESPONSE),
217 error_message);
218 return;
219 }
220
221 version_->set_cross_origin_embedder_policy(
222 response_head->cross_origin_embedder_policy);
223
224 if (response_head->network_accessed)
225 version_->embedded_worker()->OnNetworkAccessedForScriptLoad();
226
227 version_->SetMainScriptResponse(
228 std::make_unique<ServiceWorkerVersion::MainScriptResponse>(
229 *response_head));
230 }
231
232 network_loader_state_ = LoaderState::kWaitingForBody;
233
234 WriteHeaders(
235 base::MakeRefCounted<HttpResponseInfoIOBuffer>(std::move(response_info)));
236
237 // Don't pass SSLInfo to the client when the original request doesn't ask
238 // to send it.
239 if (response_head->ssl_info.has_value() &&
240 !(original_options_ &
241 network::mojom::kURLLoadOptionSendSSLInfoWithResponse)) {
242 response_head->ssl_info.reset();
243 }
244 client_->OnReceiveResponse(std::move(response_head));
245 }
246
OnReceiveRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr response_head)247 void ServiceWorkerNewScriptLoader::OnReceiveRedirect(
248 const net::RedirectInfo& redirect_info,
249 network::mojom::URLResponseHeadPtr response_head) {
250 // Resource requests for service worker scripts should not follow redirects.
251 //
252 // Step 9.5: "Set request's redirect mode to "error"."
253 // https://w3c.github.io/ServiceWorker/#update-algorithm
254 //
255 // TODO(https://crbug.com/889798): Follow redirects for imported scripts.
256 CommitCompleted(network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT),
257 ServiceWorkerConsts::kServiceWorkerRedirectError);
258 }
259
OnUploadProgress(int64_t current_position,int64_t total_size,OnUploadProgressCallback ack_callback)260 void ServiceWorkerNewScriptLoader::OnUploadProgress(
261 int64_t current_position,
262 int64_t total_size,
263 OnUploadProgressCallback ack_callback) {
264 client_->OnUploadProgress(current_position, total_size,
265 std::move(ack_callback));
266 }
267
OnReceiveCachedMetadata(mojo_base::BigBuffer data)268 void ServiceWorkerNewScriptLoader::OnReceiveCachedMetadata(
269 mojo_base::BigBuffer data) {
270 client_->OnReceiveCachedMetadata(std::move(data));
271 }
272
OnTransferSizeUpdated(int32_t transfer_size_diff)273 void ServiceWorkerNewScriptLoader::OnTransferSizeUpdated(
274 int32_t transfer_size_diff) {
275 client_->OnTransferSizeUpdated(transfer_size_diff);
276 }
277
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle consumer)278 void ServiceWorkerNewScriptLoader::OnStartLoadingResponseBody(
279 mojo::ScopedDataPipeConsumerHandle consumer) {
280 DCHECK_EQ(LoaderState::kWaitingForBody, network_loader_state_);
281 // Create a pair of the consumer and producer for responding to the client.
282 mojo::ScopedDataPipeConsumerHandle client_consumer;
283 if (mojo::CreateDataPipe(nullptr, &client_producer_, &client_consumer) !=
284 MOJO_RESULT_OK) {
285 CommitCompleted(network::URLLoaderCompletionStatus(net::ERR_FAILED),
286 ServiceWorkerConsts::kServiceWorkerFetchScriptError);
287 return;
288 }
289
290 // Pass the consumer handle for responding with the response to the client.
291 client_->OnStartLoadingResponseBody(std::move(client_consumer));
292
293 network_consumer_ = std::move(consumer);
294 network_loader_state_ = LoaderState::kLoadingBody;
295 MaybeStartNetworkConsumerHandleWatcher();
296 }
297
OnComplete(const network::URLLoaderCompletionStatus & status)298 void ServiceWorkerNewScriptLoader::OnComplete(
299 const network::URLLoaderCompletionStatus& status) {
300 LoaderState previous_state = network_loader_state_;
301 network_loader_state_ = LoaderState::kCompleted;
302 if (status.error_code != net::OK) {
303 CommitCompleted(status,
304 ServiceWorkerConsts::kServiceWorkerFetchScriptError);
305 return;
306 }
307
308 DCHECK_EQ(LoaderState::kLoadingBody, previous_state);
309
310 switch (body_writer_state_) {
311 case WriterState::kNotStarted:
312 // The header is still being written. Wait until both the header and body
313 // are written. OnNetworkDataAvailable() will call CommitCompleted() after
314 // all data from |network_consumer_| is consumed.
315 DCHECK_EQ(WriterState::kWriting, header_writer_state_);
316 return;
317 case WriterState::kWriting:
318 // Wait until it's written. OnNetworkDataAvailable() will call
319 // CommitCompleted() after all data from |network_consumer_| is
320 // consumed.
321 DCHECK_EQ(WriterState::kCompleted, header_writer_state_);
322 return;
323 case WriterState::kCompleted:
324 DCHECK_EQ(WriterState::kCompleted, header_writer_state_);
325 CommitCompleted(network::URLLoaderCompletionStatus(net::OK),
326 std::string() /* status_message */);
327 return;
328 }
329 NOTREACHED();
330 }
331
332 // End of URLLoaderClient ------------------------------------------------------
333
334 #if DCHECK_IS_ON()
CheckVersionStatusBeforeLoad()335 void ServiceWorkerNewScriptLoader::CheckVersionStatusBeforeLoad() {
336 DCHECK(version_);
337
338 // ServiceWorkerNewScriptLoader is used for fetching the service worker main
339 // script (RESOURCE_TYPE_SERVICE_WORKER) during worker startup or
340 // importScripts() (RESOURCE_TYPE_SCRIPT).
341 // TODO(nhiroki): In the current implementation, importScripts() can be called
342 // in any ServiceWorkerVersion::Status except for REDUNDANT, but the spec
343 // defines importScripts() works only on the initial script evaluation and the
344 // install event. Update this check once importScripts() is fixed.
345 // (https://crbug.com/719052)
346 DCHECK((resource_type_ == blink::mojom::ResourceType::kServiceWorker &&
347 version_->status() == ServiceWorkerVersion::NEW) ||
348 (resource_type_ == blink::mojom::ResourceType::kScript &&
349 version_->status() != ServiceWorkerVersion::REDUNDANT));
350 }
351 #endif // DCHECK_IS_ON()
352
WriteHeaders(scoped_refptr<HttpResponseInfoIOBuffer> info_buffer)353 void ServiceWorkerNewScriptLoader::WriteHeaders(
354 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer) {
355 DCHECK_EQ(WriterState::kNotStarted, header_writer_state_);
356 header_writer_state_ = WriterState::kWriting;
357 net::Error error = cache_writer_->MaybeWriteHeaders(
358 info_buffer.get(),
359 base::BindOnce(&ServiceWorkerNewScriptLoader::OnWriteHeadersComplete,
360 weak_factory_.GetWeakPtr()));
361 if (error == net::ERR_IO_PENDING) {
362 // OnWriteHeadersComplete() will be called asynchronously.
363 return;
364 }
365 // MaybeWriteHeaders() doesn't run the callback if it finishes synchronously,
366 // so explicitly call it here.
367 OnWriteHeadersComplete(error);
368 }
369
OnWriteHeadersComplete(net::Error error)370 void ServiceWorkerNewScriptLoader::OnWriteHeadersComplete(net::Error error) {
371 DCHECK_EQ(WriterState::kWriting, header_writer_state_);
372 DCHECK_NE(net::ERR_IO_PENDING, error);
373 if (error != net::OK) {
374 ServiceWorkerMetrics::CountWriteResponseResult(
375 ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
376 CommitCompleted(network::URLLoaderCompletionStatus(error),
377 ServiceWorkerConsts::kDatabaseErrorMessage);
378 return;
379 }
380 header_writer_state_ = WriterState::kCompleted;
381
382 // If all other states are kCompleted the response body is empty, we can
383 // finish now.
384 if (network_loader_state_ == LoaderState::kCompleted &&
385 body_writer_state_ == WriterState::kCompleted) {
386 CommitCompleted(network::URLLoaderCompletionStatus(net::OK),
387 std::string() /* status_message */);
388 return;
389 }
390
391 MaybeStartNetworkConsumerHandleWatcher();
392 }
393
MaybeStartNetworkConsumerHandleWatcher()394 void ServiceWorkerNewScriptLoader::MaybeStartNetworkConsumerHandleWatcher() {
395 if (network_loader_state_ == LoaderState::kWaitingForBody) {
396 // OnStartLoadingResponseBody() or OnComplete() will continue the sequence.
397 return;
398 }
399 if (header_writer_state_ != WriterState::kCompleted) {
400 DCHECK_EQ(WriterState::kWriting, header_writer_state_);
401 // OnWriteHeadersComplete() will continue the sequence.
402 return;
403 }
404
405 DCHECK_EQ(WriterState::kNotStarted, body_writer_state_);
406 body_writer_state_ = WriterState::kWriting;
407
408 network_watcher_.Watch(
409 network_consumer_.get(),
410 MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
411 base::BindRepeating(&ServiceWorkerNewScriptLoader::OnNetworkDataAvailable,
412 weak_factory_.GetWeakPtr()));
413 network_watcher_.ArmOrNotify();
414 }
415
OnNetworkDataAvailable(MojoResult)416 void ServiceWorkerNewScriptLoader::OnNetworkDataAvailable(MojoResult) {
417 DCHECK_EQ(WriterState::kCompleted, header_writer_state_);
418 DCHECK_EQ(WriterState::kWriting, body_writer_state_);
419 DCHECK(network_consumer_.is_valid());
420 scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer;
421 uint32_t bytes_available = 0;
422 MojoResult result = network::MojoToNetPendingBuffer::BeginRead(
423 &network_consumer_, &pending_buffer, &bytes_available);
424 switch (result) {
425 case MOJO_RESULT_OK:
426 WriteData(std::move(pending_buffer), bytes_available);
427 return;
428 case MOJO_RESULT_FAILED_PRECONDITION:
429 // Call WriteData() with null buffer to let the cache writer know that
430 // body from the network reaches to the end.
431 WriteData(/*pending_buffer=*/nullptr, /*bytes_available=*/0);
432 return;
433 case MOJO_RESULT_SHOULD_WAIT:
434 network_watcher_.ArmOrNotify();
435 return;
436 }
437 NOTREACHED() << static_cast<int>(result);
438 }
439
WriteData(scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,uint32_t bytes_available)440 void ServiceWorkerNewScriptLoader::WriteData(
441 scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
442 uint32_t bytes_available) {
443 // Cap the buffer size up to |kReadBufferSize|. The remaining will be written
444 // next time.
445 uint32_t bytes_written = std::min<uint32_t>(kReadBufferSize, bytes_available);
446
447 auto buffer = base::MakeRefCounted<WrappedIOBuffer>(
448 pending_buffer ? pending_buffer->buffer() : nullptr);
449 MojoResult result = client_producer_->WriteData(
450 buffer->data(), &bytes_written, MOJO_WRITE_DATA_FLAG_NONE);
451 switch (result) {
452 case MOJO_RESULT_OK:
453 break;
454 case MOJO_RESULT_FAILED_PRECONDITION:
455 ServiceWorkerMetrics::CountWriteResponseResult(
456 ServiceWorkerMetrics::WRITE_DATA_ERROR);
457 CommitCompleted(network::URLLoaderCompletionStatus(net::ERR_FAILED),
458 ServiceWorkerConsts::kServiceWorkerFetchScriptError);
459 return;
460 case MOJO_RESULT_SHOULD_WAIT:
461 // No data was written to |client_producer_| because the pipe was full.
462 // Retry when the pipe becomes ready again.
463 pending_buffer->CompleteRead(0);
464 network_consumer_ = pending_buffer->ReleaseHandle();
465 network_watcher_.ArmOrNotify();
466 return;
467 default:
468 NOTREACHED() << static_cast<int>(result);
469 return;
470 }
471
472 // Write the buffer in the service worker script storage up to the size we
473 // successfully wrote to the data pipe (i.e., |bytes_written|).
474 // A null buffer and zero |bytes_written| are passed when this is the end of
475 // the body.
476 net::Error error = cache_writer_->MaybeWriteData(
477 buffer.get(), base::strict_cast<size_t>(bytes_written),
478 base::BindOnce(&ServiceWorkerNewScriptLoader::OnWriteDataComplete,
479 weak_factory_.GetWeakPtr(), pending_buffer,
480 bytes_written));
481 if (error == net::ERR_IO_PENDING) {
482 // OnWriteDataComplete() will be called asynchronously.
483 return;
484 }
485 // MaybeWriteData() doesn't run the callback if it finishes synchronously, so
486 // explicitly call it here.
487 OnWriteDataComplete(std::move(pending_buffer), bytes_written, error);
488 }
489
OnWriteDataComplete(scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,uint32_t bytes_written,net::Error error)490 void ServiceWorkerNewScriptLoader::OnWriteDataComplete(
491 scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
492 uint32_t bytes_written,
493 net::Error error) {
494 DCHECK_NE(net::ERR_IO_PENDING, error);
495 if (error != net::OK) {
496 ServiceWorkerMetrics::CountWriteResponseResult(
497 ServiceWorkerMetrics::WRITE_DATA_ERROR);
498 CommitCompleted(network::URLLoaderCompletionStatus(error),
499 ServiceWorkerConsts::kDatabaseErrorMessage);
500 return;
501 }
502 ServiceWorkerMetrics::CountWriteResponseResult(
503 ServiceWorkerMetrics::WRITE_OK);
504
505 if (bytes_written == 0) {
506 // Zero |bytes_written| with net::OK means that all data has been read from
507 // the network and the Mojo data pipe has been closed. Thus we can complete
508 // the request if OnComplete() has already been received.
509 DCHECK(!pending_buffer);
510 body_writer_state_ = WriterState::kCompleted;
511 if (network_loader_state_ == LoaderState::kCompleted) {
512 CommitCompleted(network::URLLoaderCompletionStatus(net::OK),
513 std::string() /* status_message */);
514 }
515 return;
516 }
517
518 DCHECK(pending_buffer);
519 pending_buffer->CompleteRead(bytes_written);
520 // Get the consumer handle from a previous read operation if we have one.
521 network_consumer_ = pending_buffer->ReleaseHandle();
522 network_watcher_.ArmOrNotify();
523 }
524
CommitCompleted(const network::URLLoaderCompletionStatus & status,const std::string & status_message)525 void ServiceWorkerNewScriptLoader::CommitCompleted(
526 const network::URLLoaderCompletionStatus& status,
527 const std::string& status_message) {
528 net::Error error_code = static_cast<net::Error>(status.error_code);
529 int bytes_written = -1;
530 if (error_code == net::OK) {
531 DCHECK_EQ(LoaderState::kCompleted, network_loader_state_);
532 DCHECK_EQ(WriterState::kCompleted, header_writer_state_);
533 DCHECK_EQ(WriterState::kCompleted, body_writer_state_);
534 DCHECK(cache_writer_->did_replace());
535 bytes_written = cache_writer_->bytes_written();
536 } else {
537 // AddMessageConsole must be called before notifying that an error occurred
538 // because the worker stops soon after receiving the error response.
539 // TODO(nhiroki): Consider replacing this hacky way with the new error code
540 // handling mechanism in URLLoader.
541 version_->AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kError,
542 status_message);
543 }
544 version_->script_cache_map()->NotifyFinishedCaching(
545 request_url_, bytes_written, error_code, status_message);
546
547 client_->OnComplete(status);
548 client_producer_.reset();
549
550 network_loader_.reset();
551 network_client_receiver_.reset();
552 network_consumer_.reset();
553 network_watcher_.Cancel();
554 cache_writer_.reset();
555 network_loader_state_ = LoaderState::kCompleted;
556 header_writer_state_ = WriterState::kCompleted;
557 body_writer_state_ = WriterState::kCompleted;
558 }
559
560 } // namespace content
561