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