1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file. 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/browser/service_worker/service_worker_write_to_cache_job.h" 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/debug/trace_event.h" 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/browser/service_worker/service_worker_context_core.h" 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/browser/service_worker/service_worker_disk_cache.h" 10116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "content/browser/service_worker/service_worker_metrics.h" 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/base/io_buffer.h" 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/base/net_errors.h" 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/http/http_request_headers.h" 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/http/http_response_headers.h" 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/http/http_util.h" 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/url_request/url_request.h" 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/url_request/url_request_context.h" 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "net/url_request/url_request_status.h" 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)namespace content { 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob( 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request, 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::NetworkDelegate* network_delegate, 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ResourceType resource_type, 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::WeakPtr<ServiceWorkerContextCore> context, 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ServiceWorkerVersion* version, 286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) int extra_load_flags, 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int64 response_id) 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : net::URLRequestJob(request, network_delegate), 31116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch resource_type_(resource_type), 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) context_(context), 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) url_(request->url()), 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) response_id_(response_id), 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) version_(version), 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) has_been_killed_(false), 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) did_notify_started_(false), 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) did_notify_finished_(false), 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_factory_(this) { 406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) InitNetRequest(extra_load_flags); 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() { 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(did_notify_started_, did_notify_finished_); 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::Start() { 481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker", 491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::ExecutingJob", 501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this, 511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "URL", request_->url().spec()); 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!context_) { 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NotifyStartError(net::URLRequestStatus( 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, net::ERR_FAILED)); 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) version_->script_cache_map()->NotifyStartedCaching( 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) url_, response_id_); 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) did_notify_started_ = true; 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) StartNetRequest(); 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::Kill() { 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (has_been_killed_) 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_factory_.InvalidateWeakPtrs(); 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) has_been_killed_ = true; 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net_request_.reset(); 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (did_notify_started_ && !did_notify_finished_) { 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) version_->script_cache_map()->NotifyFinishedCaching( 711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci url_, 721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED)); 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) did_notify_finished_ = true; 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) writer_.reset(); 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) context_.reset(); 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestJob::Kill(); 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const { 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (writer_ && writer_->IsWritePending()) 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return net::LOAD_STATE_WAITING_FOR_APPCACHE; 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (net_request_) 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return net_request_->GetLoadState().state; 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return net::LOAD_STATE_IDLE; 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) { 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!http_info()) 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return http_info()->headers->GetCharset(charset); 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const { 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!http_info()) 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return http_info()->headers->GetMimeType(mime_type); 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::GetResponseInfo( 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::HttpResponseInfo* info) { 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!http_info()) 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *info = *http_info(); 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)int ServiceWorkerWriteToCacheJob::GetResponseCode() const { 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!http_info()) 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return -1; 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return http_info()->headers->response_code(); 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders( 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const net::HttpRequestHeaders& headers) { 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::string value; 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value)); 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net_request_->SetExtraRequestHeaders(headers); 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool ServiceWorkerWriteToCacheJob::ReadRawData( 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::IOBuffer* buf, 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int buf_size, 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int *bytes_read) { 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read); 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SetStatus(status); 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (status.is_io_pending()) 127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return false; 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // No more data to process, the job is complete. 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) io_buffer_ = NULL; 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) version_->script_cache_map()->NotifyFinishedCaching( 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci url_, net::URLRequestStatus()); 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) did_notify_finished_ = true; 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return status.is_success(); 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const { 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return http_info_.get(); 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::InitNetRequest( 1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) int extra_load_flags) { 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(request()); 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net_request_ = request()->context()->CreateRequest( 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) request()->url(), 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) request()->priority(), 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this, 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this->GetCookieStore()); 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net_request_->set_first_party_for_cookies( 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) request()->first_party_for_cookies()); 151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net_request_->SetReferrer(request()->referrer()); 1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (extra_load_flags) 1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) net_request_->SetLoadFlags(net_request_->load_flags() | extra_load_flags); 154116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) { 156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // This will get copied into net_request_ when URLRequest::StartJob calls 157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders. 158116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch request()->SetExtraRequestHeaderByName("Service-Worker", "script", true); 159116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::StartNetRequest() { 1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker", 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::ExecutingJob", 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this, 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "NetRequest"); 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net_request_->Start(); // We'll continue in OnResponseStarted. 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData( 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::IOBuffer* buf, 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int buf_size, 173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int *bytes_read) { 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_GT(buf_size, 0); 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(bytes_read); 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *bytes_read = 0; 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) io_buffer_ = buf; 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int net_bytes_read = 0; 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!net_request_->Read(buf, buf_size, &net_bytes_read)) { 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (net_request_->status().is_io_pending()) 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return net_request_->status(); 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(!net_request_->status().is_success()); 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return net_request_->status(); 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (net_bytes_read != 0) { 188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) WriteDataToCache(net_bytes_read); 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(GetStatus().is_io_pending()); 190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return GetStatus(); 191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(net_request_->status().is_success()); 194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return net_request_->status(); 195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() { 198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!context_) { 199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, net::ERR_FAILED)); 201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker", 2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::ExecutingJob", 2051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this, 2061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "WriteHeadersToCache"); 207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) writer_ = context_->storage()->CreateResponseWriter(response_id_); 208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) info_buffer_ = new HttpResponseInfoIOBuffer( 209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) new net::HttpResponseInfo(net_request_->response_info())); 210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) writer_->WriteInfo( 2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci info_buffer_.get(), 212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete, 213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_factory_.GetWeakPtr())); 214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); 215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) { 218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status 219cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (result < 0) { 220116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ServiceWorkerMetrics::CountWriteResponseResult( 221116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ServiceWorkerMetrics::WRITE_HEADERS_ERROR); 222cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, result)); 224cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 225cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 226cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) http_info_.reset(info_buffer_->http_info.release()); 227cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) info_buffer_ = NULL; 2281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker", 2291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::ExecutingJob", 2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this, 2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "WriteHeadersToCacheCompleted"); 232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NotifyHeadersComplete(); 233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) { 236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_NE(0, amount_to_write); 2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT_ASYNC_STEP_INTO1("ServiceWorker", 2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::ExecutingJob", 2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this, 2401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "WriteDataToCache", 2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "Amount to write", amount_to_write); 242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); 243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) writer_->WriteData( 2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci io_buffer_.get(), 2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci amount_to_write, 246cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete, 247cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_factory_.GetWeakPtr())); 248cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 249cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 250cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) { 251cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_NE(0, result); 252cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) io_buffer_ = NULL; 253cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!context_) { 254cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 255cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, net::ERR_FAILED)); 256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 257cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 258cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (result < 0) { 259116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ServiceWorkerMetrics::CountWriteResponseResult( 260116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ServiceWorkerMetrics::WRITE_DATA_ERROR); 261cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 262cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, result)); 263cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 265116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ServiceWorkerMetrics::CountWriteResponseResult( 266116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ServiceWorkerMetrics::WRITE_OK); 267cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status 268cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NotifyReadComplete(result); 2691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT_ASYNC_END0("ServiceWorker", 2701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::ExecutingJob", 2711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this); 272cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 273cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 274cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnReceivedRedirect( 275cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request, 2766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) const net::RedirectInfo& redirect_info, 277cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool* defer_redirect) { 278cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(net_request_, request); 2791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT0("ServiceWorker", 2801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::OnReceivedRedirect"); 281cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Script resources can't redirect. 282cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 2831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci net::URLRequestStatus::FAILED, net::ERR_UNSAFE_REDIRECT)); 284cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 285cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 286cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnAuthRequired( 287cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request, 288cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::AuthChallengeInfo* auth_info) { 289cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(net_request_, request); 2901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT0("ServiceWorker", 2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::OnAuthRequired"); 292cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(michaeln): Pass this thru to our jobs client. 293cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 294cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, net::ERR_FAILED)); 295cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 296cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 297cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnCertificateRequested( 298cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request, 299cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::SSLCertRequestInfo* cert_request_info) { 300cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(net_request_, request); 3011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT0("ServiceWorker", 3021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::OnCertificateRequested"); 303cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(michaeln): Pass this thru to our jobs client. 304cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // see NotifyCertificateRequested. 305cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 306cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, net::ERR_FAILED)); 307cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 308cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 309cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError( 310cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request, 311cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const net::SSLInfo& ssl_info, 312cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool fatal) { 313cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(net_request_, request); 3141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT0("ServiceWorker", 3151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::OnSSLCertificateError"); 316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(michaeln): Pass this thru to our jobs client, 317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // see NotifySSLCertificateError. 318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 319cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, net::ERR_FAILED)); 320cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 321cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 322cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart( 323cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request, 324cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) bool* defer) { 325cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(net_request_, request); 3261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT0("ServiceWorker", 3271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart"); 328cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NotifyBeforeNetworkStart(defer); 329cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 330cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 331cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnResponseStarted( 332cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request) { 333cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(net_request_, request); 334cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!request->status().is_success()) { 335cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(request->status()); 336cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 337cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 338cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (request->GetResponseCode() / 100 != 2) { 339cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 340cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequestStatus::FAILED, net::ERR_FAILED)); 341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // TODO(michaeln): Instead of error'ing immediately, send the net 342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // response to our consumer, just don't cache it? 343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 344cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 3456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) // To prevent most user-uploaded content from being used as a serviceworker. 3466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (version_->script_url() == url_) { 3476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) std::string mime_type; 3486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) request->GetMimeType(&mime_type); 3496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (mime_type != "application/x-javascript" && 3506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) mime_type != "text/javascript" && 3516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) mime_type != "application/javascript") { 3526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) AsyncNotifyDoneHelper(net::URLRequestStatus( 3531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci net::URLRequestStatus::FAILED, net::ERR_INSECURE_RESPONSE)); 3546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return; 3556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 3566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) } 357cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) WriteHeadersToCache(); 358cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 359cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 360cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::OnReadCompleted( 361cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) net::URLRequest* request, 362cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int bytes_read) { 363cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK_EQ(net_request_, request); 364cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!request->status().is_success()) { 365cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(request->status()); 366cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 367cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 368cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (bytes_read > 0) { 369cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) WriteDataToCache(bytes_read); 370cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 371cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 3721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker", 3731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "ServiceWorkerWriteToCacheJob::ExecutingJob", 3741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this, 3751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "WriteHeadersToCache"); 376cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // We're done with all. 377cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncNotifyDoneHelper(request->status()); 378cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 379cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 380cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 381cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper( 382cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const net::URLRequestStatus& status) { 383cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) DCHECK(!status.is_io_pending()); 3841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci version_->script_cache_map()->NotifyFinishedCaching(url_, status); 385cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) did_notify_finished_ = true; 386cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SetStatus(status); 387cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NotifyDone(status); 388cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 389cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 390cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} // namespace content 391