1 // Copyright 2019 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_updated_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 "base/task/post_task.h"
14 #include "content/browser/renderer_host/frame_tree_node.h"
15 #include "content/browser/service_worker/service_worker_cache_writer.h"
16 #include "content/browser/service_worker/service_worker_consts.h"
17 #include "content/browser/service_worker/service_worker_context_core.h"
18 #include "content/browser/service_worker/service_worker_context_wrapper.h"
19 #include "content/browser/service_worker/service_worker_loader_helpers.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 "content/public/browser/browser_task_traits.h"
24 #include "content/public/browser/content_browser_client.h"
25 #include "content/public/browser/url_loader_throttles.h"
26 #include "content/public/common/content_client.h"
27 #include "net/base/ip_endpoint.h"
28 #include "net/base/load_flags.h"
29 #include "net/base/net_errors.h"
30 #include "net/cert/cert_status_flags.h"
31 #include "services/network/public/mojom/url_response_head.mojom.h"
32 #include "third_party/blink/public/common/loader/throttling_url_loader.h"
33 
34 namespace content {
35 
36 // We chose this size because the AppCache uses this.
37 const uint32_t ServiceWorkerUpdatedScriptLoader::kReadBufferSize = 32768;
38 
39 ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::LoaderOnUI::
40     LoaderOnUI() = default;
41 ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::LoaderOnUI::
42     ~LoaderOnUI() = default;
43 ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::
ThrottlingURLLoaderCoreWrapper()44     ThrottlingURLLoaderCoreWrapper()
45     : loader_on_ui_(new LoaderOnUI()) {}
46 ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::
47     ~ThrottlingURLLoaderCoreWrapper() = default;
48 
49 // static
50 std::unique_ptr<
51     ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper>
52 ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::
CreateLoaderAndStart(std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_loader_factory,BrowserContextGetter browser_context_getter,int32_t routing_id,int32_t request_id,uint32_t options,const network::ResourceRequest & resource_request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,const net::NetworkTrafficAnnotationTag & traffic_annotation)53     CreateLoaderAndStart(
54         std::unique_ptr<network::PendingSharedURLLoaderFactory>
55             pending_loader_factory,
56         BrowserContextGetter browser_context_getter,
57         int32_t routing_id,
58         int32_t request_id,
59         uint32_t options,
60         const network::ResourceRequest& resource_request,
61         mojo::PendingRemote<network::mojom::URLLoaderClient> client,
62         const net::NetworkTrafficAnnotationTag& traffic_annotation) {
63   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
64   auto wrapper = base::WrapUnique(new ThrottlingURLLoaderCoreWrapper());
65 
66   RunOrPostTaskOnThread(
67       FROM_HERE, BrowserThread::UI,
68       base::BindOnce(&ThrottlingURLLoaderCoreWrapper::StartInternalOnUI,
69                      std::move(pending_loader_factory),
70                      std::move(browser_context_getter), routing_id, request_id,
71                      options, network::ResourceRequest(resource_request),
72                      std::move(client),
73                      net::NetworkTrafficAnnotationTag(traffic_annotation),
74                      base::Unretained(wrapper->loader_on_ui_.get())));
75   return wrapper;
76 }
77 
78 // static
79 void ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::
StartInternalOnUI(std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_loader_factory,BrowserContextGetter browser_context_getter,int32_t routing_id,int32_t request_id,uint32_t options,network::ResourceRequest resource_request,mojo::PendingRemote<network::mojom::URLLoaderClient> client_remote,net::NetworkTrafficAnnotationTag traffic_annotation,LoaderOnUI * loader_on_ui)80     StartInternalOnUI(
81         std::unique_ptr<network::PendingSharedURLLoaderFactory>
82             pending_loader_factory,
83         BrowserContextGetter browser_context_getter,
84         int32_t routing_id,
85         int32_t request_id,
86         uint32_t options,
87         network::ResourceRequest resource_request,
88         mojo::PendingRemote<network::mojom::URLLoaderClient> client_remote,
89         net::NetworkTrafficAnnotationTag traffic_annotation,
90         LoaderOnUI* loader_on_ui) {
91   DCHECK_CURRENTLY_ON(BrowserThread::UI);
92   BrowserContext* browser_context = browser_context_getter.Run();
93   if (!browser_context)
94     return;
95 
96   // Service worker update checking doesn't have a relevant frame and tab, so
97   // that |wc_getter| returns nullptr and the frame id is set to
98   // kNoFrameTreeNodeId.
99   base::RepeatingCallback<WebContents*()> wc_getter =
100       base::BindRepeating([]() -> WebContents* { return nullptr; });
101   std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles =
102       CreateContentBrowserURLLoaderThrottles(
103           resource_request, browser_context, std::move(wc_getter),
104           /*navigation_ui_data=*/nullptr, RenderFrameHost::kNoFrameTreeNodeId);
105 
106   mojo::Remote<network::mojom::URLLoaderClient> client(
107       std::move(client_remote));
108   auto loader = blink::ThrottlingURLLoader::CreateLoaderAndStart(
109       network::SharedURLLoaderFactory::Create(
110           std::move(pending_loader_factory)),
111       std::move(throttles), routing_id, request_id, options, &resource_request,
112       client.get(), traffic_annotation, base::ThreadTaskRunnerHandle::Get());
113   loader_on_ui->loader = std::move(loader);
114   loader_on_ui->client = std::move(client);
115 }
116 
117 void ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::
SetPriority(net::RequestPriority priority,int32_t intra_priority_value)118     SetPriority(net::RequestPriority priority, int32_t intra_priority_value) {
119   RunOrPostTaskOnThread(
120       FROM_HERE, BrowserThread::UI,
121       base::BindOnce(
122           [](LoaderOnUI* loader_on_ui, net::RequestPriority priority,
123              int32_t intra_priority_value) {
124             DCHECK(loader_on_ui->loader);
125             loader_on_ui->loader->SetPriority(priority, intra_priority_value);
126           },
127           base::Unretained(loader_on_ui_.get()), priority,
128           intra_priority_value));
129 }
130 
131 void ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::
PauseReadingBodyFromNet()132     PauseReadingBodyFromNet() {
133   RunOrPostTaskOnThread(FROM_HERE, BrowserThread::UI,
134                         base::BindOnce(
135                             [](LoaderOnUI* loader_on_ui) {
136                               DCHECK(loader_on_ui->loader);
137                               loader_on_ui->loader->PauseReadingBodyFromNet();
138                             },
139                             base::Unretained(loader_on_ui_.get())));
140 }
141 
142 void ServiceWorkerUpdatedScriptLoader::ThrottlingURLLoaderCoreWrapper::
ResumeReadingBodyFromNet()143     ResumeReadingBodyFromNet() {
144   RunOrPostTaskOnThread(FROM_HERE, BrowserThread::UI,
145                         base::BindOnce(
146                             [](LoaderOnUI* loader_on_ui) {
147                               DCHECK(loader_on_ui->loader);
148                               loader_on_ui->loader->ResumeReadingBodyFromNet();
149                             },
150                             base::Unretained(loader_on_ui_.get())));
151 }
152 
153 // This is for debugging https://crbug.com/959627.
154 // The purpose is to see where the IOBuffer comes from by checking |__vfptr|.
155 class ServiceWorkerUpdatedScriptLoader::WrappedIOBuffer
156     : public net::WrappedIOBuffer {
157  public:
WrappedIOBuffer(const char * data)158   WrappedIOBuffer(const char* data) : net::WrappedIOBuffer(data) {}
159 
160  private:
161   ~WrappedIOBuffer() override = default;
162 
163   // This is to make sure that the vtable is not merged with other classes.
dummy()164   virtual void dummy() { NOTREACHED(); }
165 };
166 
167 std::unique_ptr<ServiceWorkerUpdatedScriptLoader>
CreateAndStart(uint32_t options,const network::ResourceRequest & original_request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,scoped_refptr<ServiceWorkerVersion> version)168 ServiceWorkerUpdatedScriptLoader::CreateAndStart(
169     uint32_t options,
170     const network::ResourceRequest& original_request,
171     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
172     scoped_refptr<ServiceWorkerVersion> version) {
173   return base::WrapUnique(new ServiceWorkerUpdatedScriptLoader(
174       options, original_request, std::move(client), version));
175 }
176 
ServiceWorkerUpdatedScriptLoader(uint32_t options,const network::ResourceRequest & original_request,mojo::PendingRemote<network::mojom::URLLoaderClient> client,scoped_refptr<ServiceWorkerVersion> version)177 ServiceWorkerUpdatedScriptLoader::ServiceWorkerUpdatedScriptLoader(
178     uint32_t options,
179     const network::ResourceRequest& original_request,
180     mojo::PendingRemote<network::mojom::URLLoaderClient> client,
181     scoped_refptr<ServiceWorkerVersion> version)
182     : request_url_(original_request.url),
183       request_destination_(original_request.destination),
184       options_(options),
185       version_(std::move(version)),
186       network_watcher_(FROM_HERE,
187                        mojo::SimpleWatcher::ArmingPolicy::MANUAL,
188                        base::SequencedTaskRunnerHandle::Get()),
189       client_(std::move(client)),
190       client_producer_watcher_(FROM_HERE,
191                                mojo::SimpleWatcher::ArmingPolicy::MANUAL,
192                                base::SequencedTaskRunnerHandle::Get()),
193       request_start_(base::TimeTicks::Now()) {
194 #if DCHECK_IS_ON()
195   service_worker_loader_helpers::CheckVersionStatusBeforeWorkerScriptLoad(
196       version_->status(), request_destination_);
197 #endif  // DCHECK_IS_ON()
198 
199   DCHECK(client_);
200   ServiceWorkerUpdateChecker::ComparedScriptInfo info =
201       version_->TakeComparedScriptInfo(request_url_);
202   if (info.result == ServiceWorkerSingleScriptUpdateChecker::Result::kFailed) {
203     DCHECK(!info.paused_state);
204     // A network error received during update checking. This replays it.
205     CommitCompleted(info.failure_info->network_status,
206                     info.failure_info->error_message);
207     return;
208   }
209 
210   cache_writer_ = std::move(info.paused_state->cache_writer);
211   DCHECK(cache_writer_);
212 
213   network_loader_ = std::move(info.paused_state->network_loader);
214   pending_network_client_receiver_ =
215       std::move(info.paused_state->network_client_receiver);
216 
217   network_loader_state_ = info.paused_state->network_loader_state;
218   DCHECK(network_loader_state_ == LoaderState::kLoadingBody ||
219          network_loader_state_ == LoaderState::kCompleted);
220 
221   body_writer_state_ = info.paused_state->body_writer_state;
222   DCHECK(body_writer_state_ == WriterState::kWriting ||
223          body_writer_state_ == WriterState::kCompleted);
224 
225   version_->script_cache_map()->NotifyStartedCaching(
226       request_url_, cache_writer_->writer_resource_id());
227 
228   // Resume the cache writer and observe its writes, so all data written
229   // is sent to |client_|.
230   cache_writer_->set_write_observer(this);
231   net::Error error = cache_writer_->Resume(base::BindOnce(
232       &ServiceWorkerUpdatedScriptLoader::OnCacheWriterResumed,
233       weak_factory_.GetWeakPtr(), info.paused_state->pending_network_buffer,
234       info.paused_state->consumed_bytes));
235 
236   if (error != net::ERR_IO_PENDING) {
237     OnCacheWriterResumed(info.paused_state->pending_network_buffer,
238                          info.paused_state->consumed_bytes, error);
239   }
240 }
241 
242 ServiceWorkerUpdatedScriptLoader::~ServiceWorkerUpdatedScriptLoader() = default;
243 
FollowRedirect(const std::vector<std::string> & removed_headers,const net::HttpRequestHeaders & modified_headers,const net::HttpRequestHeaders & modified_cors_exempt_headers,const base::Optional<GURL> & new_url)244 void ServiceWorkerUpdatedScriptLoader::FollowRedirect(
245     const std::vector<std::string>& removed_headers,
246     const net::HttpRequestHeaders& modified_headers,
247     const net::HttpRequestHeaders& modified_cors_exempt_headers,
248     const base::Optional<GURL>& new_url) {
249   // Resource requests for service worker scripts should not follow redirects.
250   // See comments in OnReceiveRedirect().
251   NOTREACHED();
252 }
253 
SetPriority(net::RequestPriority priority,int32_t intra_priority_value)254 void ServiceWorkerUpdatedScriptLoader::SetPriority(
255     net::RequestPriority priority,
256     int32_t intra_priority_value) {
257   if (network_loader_)
258     network_loader_->SetPriority(priority, intra_priority_value);
259 }
260 
PauseReadingBodyFromNet()261 void ServiceWorkerUpdatedScriptLoader::PauseReadingBodyFromNet() {
262   if (network_loader_)
263     network_loader_->PauseReadingBodyFromNet();
264 }
265 
ResumeReadingBodyFromNet()266 void ServiceWorkerUpdatedScriptLoader::ResumeReadingBodyFromNet() {
267   if (network_loader_)
268     network_loader_->ResumeReadingBodyFromNet();
269 }
270 
271 // URLLoaderClient for network loader ------------------------------------------
272 
OnReceiveResponse(network::mojom::URLResponseHeadPtr response_head)273 void ServiceWorkerUpdatedScriptLoader::OnReceiveResponse(
274     network::mojom::URLResponseHeadPtr response_head) {
275   NOTREACHED();
276 }
277 
OnReceiveRedirect(const net::RedirectInfo & redirect_info,network::mojom::URLResponseHeadPtr response_head)278 void ServiceWorkerUpdatedScriptLoader::OnReceiveRedirect(
279     const net::RedirectInfo& redirect_info,
280     network::mojom::URLResponseHeadPtr response_head) {
281   NOTREACHED();
282 }
283 
OnUploadProgress(int64_t current_position,int64_t total_size,OnUploadProgressCallback ack_callback)284 void ServiceWorkerUpdatedScriptLoader::OnUploadProgress(
285     int64_t current_position,
286     int64_t total_size,
287     OnUploadProgressCallback ack_callback) {
288   NOTREACHED();
289 }
290 
OnReceiveCachedMetadata(mojo_base::BigBuffer data)291 void ServiceWorkerUpdatedScriptLoader::OnReceiveCachedMetadata(
292     mojo_base::BigBuffer data) {
293   client_->OnReceiveCachedMetadata(std::move(data));
294 }
295 
OnTransferSizeUpdated(int32_t transfer_size_diff)296 void ServiceWorkerUpdatedScriptLoader::OnTransferSizeUpdated(
297     int32_t transfer_size_diff) {
298   client_->OnTransferSizeUpdated(transfer_size_diff);
299 }
300 
OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle consumer)301 void ServiceWorkerUpdatedScriptLoader::OnStartLoadingResponseBody(
302     mojo::ScopedDataPipeConsumerHandle consumer) {
303   NOTREACHED();
304 }
305 
OnComplete(const network::URLLoaderCompletionStatus & status)306 void ServiceWorkerUpdatedScriptLoader::OnComplete(
307     const network::URLLoaderCompletionStatus& status) {
308   LoaderState previous_state = network_loader_state_;
309   network_loader_state_ = LoaderState::kCompleted;
310   if (status.error_code != net::OK) {
311     CommitCompleted(status,
312                     ServiceWorkerConsts::kServiceWorkerFetchScriptError);
313     return;
314   }
315 
316   DCHECK_EQ(LoaderState::kLoadingBody, previous_state);
317   switch (body_writer_state_) {
318     case WriterState::kNotStarted:
319       NOTREACHED();
320       return;
321     case WriterState::kWriting:
322       // Wait until it's written. OnNetworkDataAvailable() will call
323       // CommitCompleted() after all data from |network_consumer_| is
324       // consumed.
325       return;
326     case WriterState::kCompleted:
327       CommitCompleted(network::URLLoaderCompletionStatus(net::OK),
328                       std::string() /* status_message */);
329       return;
330   }
331   NOTREACHED();
332 }
333 
334 // End of URLLoaderClient ------------------------------------------------------
335 
WillWriteResponseHead(const network::mojom::URLResponseHead & response_head)336 int ServiceWorkerUpdatedScriptLoader::WillWriteResponseHead(
337     const network::mojom::URLResponseHead& response_head) {
338   auto client_response = response_head.Clone();
339   client_response->request_start = request_start_;
340 
341   if (request_destination_ ==
342       network::mojom::RequestDestination::kServiceWorker) {
343     version_->SetMainScriptResponse(
344         std::make_unique<ServiceWorkerVersion::MainScriptResponse>(
345             *client_response));
346   }
347 
348   // Don't pass SSLInfo to the client when the original request doesn't ask
349   // to send it.
350   if (client_response->ssl_info.has_value() &&
351       !(options_ & network::mojom::kURLLoadOptionSendSSLInfoWithResponse)) {
352     client_response->ssl_info.reset();
353   }
354 
355   client_->OnReceiveResponse(std::move(client_response));
356 
357   mojo::ScopedDataPipeConsumerHandle client_consumer;
358   if (mojo::CreateDataPipe(nullptr, &client_producer_, &client_consumer) !=
359       MOJO_RESULT_OK) {
360     // Reports error to cache writer and finally the loader would process this
361     // failure in OnCacheWriterResumed()
362     return net::ERR_INSUFFICIENT_RESOURCES;
363   }
364 
365   // Pass the consumer handle to the client.
366   client_->OnStartLoadingResponseBody(std::move(client_consumer));
367   client_producer_watcher_.Watch(
368       client_producer_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
369       base::BindRepeating(&ServiceWorkerUpdatedScriptLoader::OnClientWritable,
370                           weak_factory_.GetWeakPtr()));
371   return net::OK;
372 }
373 
OnClientWritable(MojoResult)374 void ServiceWorkerUpdatedScriptLoader::OnClientWritable(MojoResult) {
375   DCHECK(data_to_send_);
376   DCHECK_GE(data_length_, bytes_sent_to_client_);
377   DCHECK(client_producer_);
378 
379   // Cap the buffer size up to |kReadBufferSize|. The remaining will be written
380   // next time.
381   uint32_t bytes_newly_sent =
382       std::min<uint32_t>(kReadBufferSize, data_length_ - bytes_sent_to_client_);
383 
384   MojoResult result =
385       client_producer_->WriteData(data_to_send_->data() + bytes_sent_to_client_,
386                                   &bytes_newly_sent, MOJO_WRITE_DATA_FLAG_NONE);
387 
388   if (result == MOJO_RESULT_SHOULD_WAIT) {
389     // No data was written to |client_producer_| because the pipe was full.
390     // Retry when the pipe becomes ready again.
391     client_producer_watcher_.ArmOrNotify();
392     return;
393   }
394 
395   if (result != MOJO_RESULT_OK) {
396     ServiceWorkerMetrics::CountWriteResponseResult(
397         ServiceWorkerMetrics::WRITE_DATA_ERROR);
398     CommitCompleted(network::URLLoaderCompletionStatus(net::ERR_FAILED),
399                     ServiceWorkerConsts::kServiceWorkerFetchScriptError);
400     return;
401   }
402 
403   bytes_sent_to_client_ += bytes_newly_sent;
404   if (bytes_sent_to_client_ != data_length_) {
405     // Not all data is sent. Send the rest in another task.
406     client_producer_watcher_.ArmOrNotify();
407     return;
408   }
409   std::move(write_observer_complete_callback_).Run(net::OK);
410 }
411 
WillWriteData(scoped_refptr<net::IOBuffer> data,int length,base::OnceCallback<void (net::Error)> callback)412 int ServiceWorkerUpdatedScriptLoader::WillWriteData(
413     scoped_refptr<net::IOBuffer> data,
414     int length,
415     base::OnceCallback<void(net::Error)> callback) {
416   DCHECK(!write_observer_complete_callback_);
417   DCHECK(client_producer_);
418 
419   data_to_send_ = std::move(data);
420   data_length_ = length;
421   bytes_sent_to_client_ = 0;
422   write_observer_complete_callback_ = std::move(callback);
423   client_producer_watcher_.ArmOrNotify();
424   return net::ERR_IO_PENDING;
425 }
426 
OnCacheWriterResumed(scoped_refptr<network::MojoToNetPendingBuffer> pending_network_buffer,uint32_t consumed_bytes,net::Error error)427 void ServiceWorkerUpdatedScriptLoader::OnCacheWriterResumed(
428     scoped_refptr<network::MojoToNetPendingBuffer> pending_network_buffer,
429     uint32_t consumed_bytes,
430     net::Error error) {
431   DCHECK_NE(error, net::ERR_IO_PENDING);
432   // Stop observing write operations in cache writer as further data are
433   // from network which would be processed by OnNetworkDataAvailable().
434   cache_writer_->set_write_observer(nullptr);
435 
436   if (error != net::OK) {
437     CommitCompleted(network::URLLoaderCompletionStatus(error),
438                     ServiceWorkerConsts::kDatabaseErrorMessage);
439     return;
440   }
441   // If the script has no body or all the body has already been read when it
442   // was paused, we don't have to wait for more data from network.
443   if (body_writer_state_ == WriterState::kCompleted) {
444     CommitCompleted(network::URLLoaderCompletionStatus(net::OK), std::string());
445     return;
446   }
447 
448   // The data in the pending buffer has been processed during resuming. At this
449   // point, this completes the pending read and releases the Mojo handle to
450   // continue with reading the rest of the body.
451   DCHECK(pending_network_buffer);
452   pending_network_buffer->CompleteRead(consumed_bytes);
453   network_consumer_ = pending_network_buffer->ReleaseHandle();
454 
455   // Continue to load the rest of the body from the network.
456   DCHECK_EQ(body_writer_state_, WriterState::kWriting);
457   DCHECK(network_consumer_);
458   network_client_receiver_.Bind(std::move(pending_network_client_receiver_));
459   network_watcher_.Watch(
460       network_consumer_.get(),
461       MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
462       base::BindRepeating(
463           &ServiceWorkerUpdatedScriptLoader::OnNetworkDataAvailable,
464           weak_factory_.GetWeakPtr()));
465   network_watcher_.ArmOrNotify();
466 }
467 
OnNetworkDataAvailable(MojoResult)468 void ServiceWorkerUpdatedScriptLoader::OnNetworkDataAvailable(MojoResult) {
469   DCHECK_EQ(WriterState::kWriting, body_writer_state_);
470   DCHECK(network_consumer_.is_valid());
471   scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer;
472   uint32_t bytes_available = 0;
473   MojoResult result = network::MojoToNetPendingBuffer::BeginRead(
474       &network_consumer_, &pending_buffer, &bytes_available);
475   switch (result) {
476     case MOJO_RESULT_OK:
477       WriteData(std::move(pending_buffer), bytes_available);
478       return;
479     case MOJO_RESULT_FAILED_PRECONDITION:
480       // Call WriteData() with null buffer to let the cache writer know that
481       // body from the network reaches to the end.
482       WriteData(/*pending_buffer=*/nullptr, /*bytes_available=*/0);
483       return;
484     case MOJO_RESULT_SHOULD_WAIT:
485       network_watcher_.ArmOrNotify();
486       return;
487   }
488   NOTREACHED() << static_cast<int>(result);
489 }
490 
WriteData(scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,uint32_t bytes_available)491 void ServiceWorkerUpdatedScriptLoader::WriteData(
492     scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
493     uint32_t bytes_available) {
494   // Cap the buffer size up to |kReadBufferSize|. The remaining will be written
495   // next time.
496   uint32_t bytes_written = std::min<uint32_t>(kReadBufferSize, bytes_available);
497 
498   auto buffer = base::MakeRefCounted<WrappedIOBuffer>(
499       pending_buffer ? pending_buffer->buffer() : nullptr);
500   MojoResult result = client_producer_->WriteData(
501       buffer->data(), &bytes_written, MOJO_WRITE_DATA_FLAG_NONE);
502   switch (result) {
503     case MOJO_RESULT_OK:
504       break;
505     case MOJO_RESULT_FAILED_PRECONDITION:
506       ServiceWorkerMetrics::CountWriteResponseResult(
507           ServiceWorkerMetrics::WRITE_DATA_ERROR);
508       CommitCompleted(network::URLLoaderCompletionStatus(net::ERR_FAILED),
509                       ServiceWorkerConsts::kServiceWorkerFetchScriptError);
510       return;
511     case MOJO_RESULT_SHOULD_WAIT:
512       // No data was written to |client_producer_| because the pipe was full.
513       // Retry when the pipe becomes ready again.
514       pending_buffer->CompleteRead(0);
515       network_consumer_ = pending_buffer->ReleaseHandle();
516       network_watcher_.ArmOrNotify();
517       return;
518     default:
519       NOTREACHED() << static_cast<int>(result);
520       return;
521   }
522 
523   // Write the buffer in the service worker script storage up to the size we
524   // successfully wrote to the data pipe (i.e., |bytes_written|).
525   // A null buffer and zero |bytes_written| are passed when this is the end of
526   // the body.
527   net::Error error = cache_writer_->MaybeWriteData(
528       buffer.get(), base::strict_cast<size_t>(bytes_written),
529       base::BindOnce(&ServiceWorkerUpdatedScriptLoader::OnWriteDataComplete,
530                      weak_factory_.GetWeakPtr(), pending_buffer,
531                      bytes_written));
532   if (error == net::ERR_IO_PENDING) {
533     // OnWriteDataComplete() will be called asynchronously.
534     return;
535   }
536   // MaybeWriteData() doesn't run the callback if it finishes synchronously, so
537   // explicitly call it here.
538   OnWriteDataComplete(std::move(pending_buffer), bytes_written, error);
539 }
540 
OnWriteDataComplete(scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,uint32_t bytes_written,net::Error error)541 void ServiceWorkerUpdatedScriptLoader::OnWriteDataComplete(
542     scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
543     uint32_t bytes_written,
544     net::Error error) {
545   DCHECK_NE(net::ERR_IO_PENDING, error);
546   if (error != net::OK) {
547     ServiceWorkerMetrics::CountWriteResponseResult(
548         ServiceWorkerMetrics::WRITE_DATA_ERROR);
549     CommitCompleted(network::URLLoaderCompletionStatus(error),
550                     ServiceWorkerConsts::kDatabaseErrorMessage);
551     return;
552   }
553   ServiceWorkerMetrics::CountWriteResponseResult(
554       ServiceWorkerMetrics::WRITE_OK);
555 
556   if (bytes_written == 0) {
557     // Zero |bytes_written| with net::OK means that all data has been read from
558     // the network and the Mojo data pipe has been closed. Thus we can complete
559     // the request if OnComplete() has already been received.
560     DCHECK(!pending_buffer);
561     body_writer_state_ = WriterState::kCompleted;
562     if (network_loader_state_ == LoaderState::kCompleted) {
563       CommitCompleted(network::URLLoaderCompletionStatus(net::OK),
564                       std::string() /* status_message */);
565     }
566     return;
567   }
568 
569   DCHECK(pending_buffer);
570   pending_buffer->CompleteRead(bytes_written);
571   // Get the consumer handle from a previous read operation if we have one.
572   network_consumer_ = pending_buffer->ReleaseHandle();
573   network_watcher_.ArmOrNotify();
574 }
575 
CommitCompleted(const network::URLLoaderCompletionStatus & status,const std::string & status_message)576 void ServiceWorkerUpdatedScriptLoader::CommitCompleted(
577     const network::URLLoaderCompletionStatus& status,
578     const std::string& status_message) {
579   net::Error error_code = static_cast<net::Error>(status.error_code);
580   int bytes_written = -1;
581   if (error_code == net::OK) {
582     DCHECK(cache_writer_);
583     DCHECK_EQ(LoaderState::kCompleted, network_loader_state_);
584     DCHECK_EQ(WriterState::kCompleted, body_writer_state_);
585     // If all the calls to WriteHeaders/WriteData succeeded, but the incumbent
586     // entry wasn't actually replaced because the new entry was equivalent, the
587     // new version didn't actually install because it already exists.
588     if (!cache_writer_->did_replace()) {
589       version_->SetStartWorkerStatusCode(
590           blink::ServiceWorkerStatusCode::kErrorExists);
591       error_code = net::ERR_FILE_EXISTS;
592     }
593     bytes_written = cache_writer_->bytes_written();
594   } else {
595     // AddMessageConsole must be called before notifying that an error occurred
596     // because the worker stops soon after receiving the error response.
597     // TODO(nhiroki): Consider replacing this hacky way with the new error code
598     // handling mechanism in URLLoader.
599     version_->AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kError,
600                                   status_message);
601   }
602 
603   // Cache writer could be nullptr when update checking observed a network error
604   // and this loader hasn't started the caching yet.
605   if (cache_writer_) {
606     version_->script_cache_map()->NotifyFinishedCaching(
607         request_url_, bytes_written, error_code, status_message);
608   }
609 
610   client_->OnComplete(status);
611   client_producer_.reset();
612   client_producer_watcher_.Cancel();
613 
614   network_loader_.reset();
615   network_client_receiver_.reset();
616   network_consumer_.reset();
617   network_watcher_.Cancel();
618   cache_writer_.reset();
619   network_loader_state_ = LoaderState::kCompleted;
620   body_writer_state_ = WriterState::kCompleted;
621 }
622 
623 }  // namespace content
624