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