Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/service_worker/service_worker_context_request_handler. h" | 5 #include "content/browser/service_worker/service_worker_context_request_handler. h" |
| 6 | 6 |
| 7 #include "base/time/time.h" | 7 #include "base/time/time.h" |
| 8 #include "content/browser/service_worker/service_worker_context_core.h" | 8 #include "content/browser/service_worker/service_worker_context_core.h" |
| 9 #include "content/browser/service_worker/service_worker_provider_host.h" | 9 #include "content/browser/service_worker/service_worker_provider_host.h" |
| 10 #include "content/browser/service_worker/service_worker_read_from_cache_job.h" | 10 #include "content/browser/service_worker/service_worker_read_from_cache_job.h" |
| 11 #include "content/browser/service_worker/service_worker_storage.h" | 11 #include "content/browser/service_worker/service_worker_storage.h" |
| 12 #include "content/browser/service_worker/service_worker_version.h" | 12 #include "content/browser/service_worker/service_worker_version.h" |
| 13 #include "content/browser/service_worker/service_worker_write_to_cache_job.h" | 13 #include "content/browser/service_worker/service_worker_write_to_cache_job.h" |
| 14 #include "content/public/browser/resource_context.h" | |
| 15 #include "content/public/common/resource_response_info.h" | 14 #include "content/public/common/resource_response_info.h" |
| 16 #include "net/base/load_flags.h" | 15 #include "net/base/load_flags.h" |
| 16 #include "net/log/net_log.h" | |
| 17 #include "net/log/net_log_event_type.h" | |
| 18 #include "net/log/net_log_with_source.h" | |
| 17 #include "net/url_request/url_request.h" | 19 #include "net/url_request/url_request.h" |
| 20 #include "net/url_request/url_request_error_job.h" | |
| 18 | 21 |
| 19 namespace content { | 22 namespace content { |
| 20 | 23 |
| 24 namespace { | |
| 25 | |
| 26 bool IsInstalled(const ServiceWorkerVersion* version) { | |
| 27 switch (version->status()) { | |
| 28 case ServiceWorkerVersion::NEW: | |
| 29 case ServiceWorkerVersion::INSTALLING: | |
| 30 return false; | |
| 31 case ServiceWorkerVersion::INSTALLED: | |
| 32 case ServiceWorkerVersion::ACTIVATING: | |
| 33 case ServiceWorkerVersion::ACTIVATED: | |
| 34 return true; | |
| 35 case ServiceWorkerVersion::REDUNDANT: | |
| 36 return false; | |
| 37 } | |
| 38 NOTREACHED(); | |
| 39 return false; | |
| 40 } | |
| 41 } | |
|
horo
2017/01/10 07:09:27
} // namespace
falken
2017/01/10 08:17:46
Done.
| |
| 42 | |
| 21 ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler( | 43 ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler( |
| 22 base::WeakPtr<ServiceWorkerContextCore> context, | 44 base::WeakPtr<ServiceWorkerContextCore> context, |
| 23 base::WeakPtr<ServiceWorkerProviderHost> provider_host, | 45 base::WeakPtr<ServiceWorkerProviderHost> provider_host, |
| 24 base::WeakPtr<storage::BlobStorageContext> blob_storage_context, | 46 base::WeakPtr<storage::BlobStorageContext> blob_storage_context, |
| 25 ResourceType resource_type) | 47 ResourceType resource_type) |
| 26 : ServiceWorkerRequestHandler(context, | 48 : ServiceWorkerRequestHandler(context, |
| 27 provider_host, | 49 provider_host, |
| 28 blob_storage_context, | 50 blob_storage_context, |
| 29 resource_type), | 51 resource_type), |
| 30 version_(provider_host_->running_hosted_version()) { | 52 version_(provider_host_->running_hosted_version()) { |
| 31 DCHECK(provider_host_->IsHostToRunningServiceWorker()); | 53 DCHECK(provider_host_->IsHostToRunningServiceWorker()); |
| 32 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) | 54 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) |
| 33 version_->NotifyMainScriptRequestHandlerCreated(); | 55 version_->NotifyMainScriptRequestHandlerCreated(); |
| 34 } | 56 } |
| 35 | 57 |
| 36 ServiceWorkerContextRequestHandler::~ServiceWorkerContextRequestHandler() { | 58 ServiceWorkerContextRequestHandler::~ServiceWorkerContextRequestHandler() { |
| 37 } | 59 } |
| 38 | 60 |
| 61 // static | |
| 62 std::string ServiceWorkerContextRequestHandler::CreateJobStatusToString( | |
| 63 CreateJobStatus status) { | |
| 64 switch (status) { | |
| 65 case CreateJobStatus::UNINITIALIZED: | |
| 66 return "UNINITIALIZED"; | |
| 67 case CreateJobStatus::WRITE_JOB_FOR_REGISTER: | |
| 68 return "WRITE_JOB_FOR_REGISTER"; | |
| 69 case CreateJobStatus::WRITE_JOB_FOR_UPDATE: | |
| 70 return "WRITE_JOB_FOR_UPDATE"; | |
| 71 case CreateJobStatus::READ_JOB: | |
| 72 return "READ_JOB"; | |
| 73 case CreateJobStatus::READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT: | |
| 74 return "READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT"; | |
| 75 case CreateJobStatus::ERROR_NO_PROVIDER: | |
| 76 return "ERROR_NO_PROVIDER"; | |
| 77 case CreateJobStatus::ERROR_NO_VERSION: | |
| 78 return "ERROR_NO_VERSION"; | |
| 79 case CreateJobStatus::ERROR_REDUNDANT_VERSION: | |
| 80 return "ERROR_REDUNDANT_VERSION"; | |
| 81 case CreateJobStatus::ERROR_NO_CONTEXT: | |
| 82 return "ERROR_NO_CONTEXT"; | |
| 83 case CreateJobStatus::ERROR_REDIRECT: | |
| 84 return "ERROR_REDIRECT"; | |
| 85 case CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT: | |
| 86 return "ERROR_UNINSTALLED_SCRIPT_IMPORT"; | |
| 87 case CreateJobStatus::ERROR_OUT_OF_RESOURCE_IDS: | |
| 88 return "ERROR_OUT_OF_RESOURCE_IDS"; | |
| 89 case CreateJobStatus::NUM_TYPES: | |
| 90 NOTREACHED(); | |
| 91 } | |
| 92 NOTREACHED() << static_cast<int>(status); | |
| 93 return "UNKNOWN"; | |
| 94 } | |
| 95 | |
| 39 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob( | 96 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob( |
| 40 net::URLRequest* request, | 97 net::URLRequest* request, |
| 41 net::NetworkDelegate* network_delegate, | 98 net::NetworkDelegate* network_delegate, |
| 42 ResourceContext* resource_context) { | 99 ResourceContext* resource_context) { |
| 43 CreateJobStatus status = CreateJobStatus::DID_NOT_SET_STATUS; | |
| 44 net::URLRequestJob* job = | |
| 45 MaybeCreateJobImpl(&status, request, network_delegate, resource_context); | |
| 46 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) { | |
| 47 version_->NotifyMainScriptJobCreated(status); | |
| 48 } | |
| 49 return job; | |
| 50 } | |
| 51 | |
| 52 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJobImpl( | |
| 53 CreateJobStatus* out_status, | |
| 54 net::URLRequest* request, | |
| 55 net::NetworkDelegate* network_delegate, | |
| 56 ResourceContext* resource_context) { | |
| 57 if (!provider_host_) { | |
| 58 *out_status = CreateJobStatus::NO_PROVIDER; | |
| 59 return nullptr; | |
| 60 } | |
| 61 if (!version_) { | |
| 62 *out_status = CreateJobStatus::NO_VERSION; | |
| 63 return nullptr; | |
| 64 } | |
| 65 if (!context_) { | |
| 66 *out_status = CreateJobStatus::NO_CONTEXT; | |
| 67 return nullptr; | |
| 68 } | |
| 69 | |
| 70 // We currently have no use case for hijacking a redirected request. | |
| 71 if (request->url_chain().size() > 1) { | |
| 72 *out_status = CreateJobStatus::IGNORE_REDIRECT; | |
| 73 return nullptr; | |
| 74 } | |
| 75 | |
| 76 // We only use the script cache for main script loading and | 100 // We only use the script cache for main script loading and |
| 77 // importScripts(), even if a cached script is xhr'd, we don't | 101 // importScripts(), even if a cached script is xhr'd, we don't |
| 78 // retrieve it from the script cache. | 102 // retrieve it from the script cache. |
| 79 // TODO(michaeln): Get the desired behavior clarified in the spec, | 103 // TODO(michaeln): Get the desired behavior clarified in the spec, |
|
horo
2017/01/10 07:09:27
Remove or update this TODO comment.
falken
2017/01/10 08:17:46
I think it's still unresolved. I'll change michael
| |
| 80 // and make tweak the behavior here to match. | 104 // and make tweak the behavior here to match. |
| 81 if (resource_type_ != RESOURCE_TYPE_SERVICE_WORKER && | 105 if (resource_type_ != RESOURCE_TYPE_SERVICE_WORKER && |
| 82 resource_type_ != RESOURCE_TYPE_SCRIPT) { | 106 resource_type_ != RESOURCE_TYPE_SCRIPT) { |
| 83 *out_status = CreateJobStatus::IGNORE_NON_SCRIPT; | 107 // Fall back to network. |
| 84 return nullptr; | 108 return nullptr; |
| 85 } | 109 } |
| 86 | 110 |
| 87 if (ShouldAddToScriptCache(request->url())) { | 111 CreateJobStatus status = CreateJobStatus::UNINITIALIZED; |
| 88 ServiceWorkerRegistration* registration = | 112 net::URLRequestJob* job = |
| 89 context_->GetLiveRegistration(version_->registration_id()); | 113 MaybeCreateJobImpl(request, network_delegate, &status); |
| 90 DCHECK(registration); // We're registering or updating so must be there. | 114 const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER; |
| 115 // TODO(falken): Add UMA for CreateJobStatus. | |
| 116 if (is_main_script) | |
| 117 version_->NotifyMainScriptJobCreated(status); | |
| 91 | 118 |
| 92 int64_t resource_id = context_->storage()->NewResourceId(); | 119 // If a job couldn't be created, return an error job rather than falling back |
| 93 if (resource_id == kInvalidServiceWorkerResourceId) { | 120 // to network. Otherwise the renderer may receive the response from network |
| 94 *out_status = CreateJobStatus::OUT_OF_RESOURCE_IDS; | 121 // and start a service worker whose browser-side ServiceWorkerVersion is not |
| 122 // properly initialized. | |
| 123 if (!job) { | |
|
horo
2017/01/10 07:09:27
Early return.
if (job)
return;
...
falken
2017/01/10 08:17:46
Done.
| |
| 124 // As an exception, allow installed service workers to use importScripts() | |
| 125 // to import non-installed scripts. | |
| 126 // TODO(falken): This is a spec violation that should be deprecated and | |
| 127 // removed. See https://github.com/w3c/ServiceWorker/issues/1021 | |
| 128 if (status == CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT) { | |
| 129 // Fall back to network. | |
| 95 return nullptr; | 130 return nullptr; |
| 96 } | 131 } |
| 97 | 132 |
| 98 // Bypass the browser cache for initial installs and update | 133 std::string error_str(CreateJobStatusToString(status)); |
| 99 // checks after 24 hours have passed. | 134 request->net_log().AddEvent( |
| 100 int extra_load_flags = 0; | 135 net::NetLogEventType:: |
| 101 base::TimeDelta time_since_last_check = | 136 SERVICE_WORKER_SCRIPT_LOAD_UNHANDLED_REQUEST_ERROR, |
| 102 base::Time::Now() - registration->last_update_check(); | 137 net::NetLog::StringCallback("error", &error_str)); |
| 103 if (time_since_last_check > base::TimeDelta::FromHours( | |
| 104 kServiceWorkerScriptMaxCacheAgeInHours) || | |
| 105 version_->force_bypass_cache_for_scripts()) { | |
| 106 extra_load_flags = net::LOAD_BYPASS_CACHE; | |
| 107 } | |
| 108 | 138 |
| 109 ServiceWorkerVersion* stored_version = registration->waiting_version() | 139 return new net::URLRequestErrorJob(request, network_delegate, |
| 110 ? registration->waiting_version() | 140 net::Error::ERR_FAILED); |
| 111 : registration->active_version(); | |
| 112 int64_t incumbent_resource_id = kInvalidServiceWorkerResourceId; | |
| 113 if (stored_version && stored_version->script_url() == request->url()) { | |
| 114 incumbent_resource_id = | |
| 115 stored_version->script_cache_map()->LookupResourceId(request->url()); | |
| 116 } | |
| 117 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) | |
| 118 version_->embedded_worker()->OnURLJobCreatedForMainScript(); | |
| 119 *out_status = incumbent_resource_id == kInvalidServiceWorkerResourceId | |
| 120 ? CreateJobStatus::CREATED_WRITE_JOB_FOR_REGISTER | |
| 121 : CreateJobStatus::CREATED_WRITE_JOB_FOR_UPDATE; | |
| 122 return new ServiceWorkerWriteToCacheJob( | |
| 123 request, network_delegate, resource_type_, context_, version_.get(), | |
| 124 extra_load_flags, resource_id, incumbent_resource_id); | |
| 125 } | 141 } |
| 126 | 142 |
| 127 int64_t resource_id = kInvalidServiceWorkerResourceId; | 143 return job; |
| 128 if (ShouldReadFromScriptCache(request->url(), &resource_id)) { | 144 } |
| 129 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) | 145 |
| 130 version_->embedded_worker()->OnURLJobCreatedForMainScript(); | 146 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJobImpl( |
| 131 *out_status = CreateJobStatus::CREATED_READ_JOB; | 147 net::URLRequest* request, |
| 148 net::NetworkDelegate* network_delegate, | |
| 149 CreateJobStatus* out_status) { | |
| 150 if (!provider_host_) { | |
| 151 *out_status = CreateJobStatus::ERROR_NO_PROVIDER; | |
| 152 return nullptr; | |
| 153 } | |
| 154 if (!context_) { | |
| 155 *out_status = CreateJobStatus::ERROR_NO_CONTEXT; | |
| 156 return nullptr; | |
| 157 } | |
| 158 if (!version_) { | |
| 159 *out_status = CreateJobStatus::ERROR_NO_VERSION; | |
| 160 return nullptr; | |
| 161 } | |
| 162 // This could happen if browser-side has set the status to redundant but the | |
| 163 // worker has not yet stopped. The worker is already doomed so just reject the | |
| 164 // request. Handle it specially here because otherwise it'd be unclear whether | |
| 165 // "REDUNDANT" should count as installed or not installed when making | |
| 166 // decisions about how to handle the request and logging UMA. | |
| 167 if (version_->status() == ServiceWorkerVersion::REDUNDANT) { | |
| 168 *out_status = CreateJobStatus::ERROR_REDUNDANT_VERSION; | |
| 169 return nullptr; | |
| 170 } | |
| 171 | |
| 172 // We currently have no use case for hijacking a redirected request. | |
| 173 if (request->url_chain().size() > 1) { | |
| 174 *out_status = CreateJobStatus::ERROR_REDIRECT; | |
| 175 return nullptr; | |
| 176 } | |
| 177 | |
| 178 const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER; | |
| 179 int resource_id = | |
| 180 version_->script_cache_map()->LookupResourceId(request->url()); | |
| 181 if (resource_id != kInvalidServiceWorkerResourceId) { | |
| 182 if (IsInstalled(version_.get())) { | |
| 183 // An installed worker is loading a stored script. | |
| 184 if (is_main_script) | |
| 185 version_->embedded_worker()->OnURLJobCreatedForMainScript(); | |
| 186 *out_status = CreateJobStatus::READ_JOB; | |
| 187 } else { | |
| 188 // A new worker is loading a stored script. The script was already | |
| 189 // imported (or the main script is being recursively imported). | |
| 190 *out_status = CreateJobStatus::READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT; | |
| 191 } | |
| 132 return new ServiceWorkerReadFromCacheJob(request, network_delegate, | 192 return new ServiceWorkerReadFromCacheJob(request, network_delegate, |
| 133 resource_type_, context_, version_, | 193 resource_type_, context_, version_, |
| 134 resource_id); | 194 resource_id); |
| 135 } | 195 } |
| 136 | 196 |
| 137 // NULL means use the network. | 197 // An installed worker is importing a non-stored script. |
| 138 *out_status = CreateJobStatus::IGNORE_UNKNOWN; | 198 if (IsInstalled(version_.get())) { |
| 139 return nullptr; | 199 DCHECK(!is_main_script); |
| 140 } | 200 *out_status = CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT; |
| 201 return nullptr; | |
| 202 } | |
| 141 | 203 |
| 142 bool ServiceWorkerContextRequestHandler::ShouldAddToScriptCache( | 204 // A new worker is loading a script for the first time. Create a write job to |
| 143 const GURL& url) { | 205 // store the script. |
| 144 // We only write imports that occur during the initial eval. | 206 ServiceWorkerRegistration* registration = |
| 145 if (version_->status() != ServiceWorkerVersion::NEW && | 207 context_->GetLiveRegistration(version_->registration_id()); |
| 146 version_->status() != ServiceWorkerVersion::INSTALLING) { | 208 DCHECK(registration); // We're registering or updating so must be there. |
| 147 return false; | 209 |
| 210 resource_id = context_->storage()->NewResourceId(); | |
| 211 if (resource_id == kInvalidServiceWorkerResourceId) { | |
| 212 *out_status = CreateJobStatus::ERROR_OUT_OF_RESOURCE_IDS; | |
| 213 return nullptr; | |
| 148 } | 214 } |
| 149 return version_->script_cache_map()->LookupResourceId(url) == | |
| 150 kInvalidServiceWorkerResourceId; | |
| 151 } | |
| 152 | 215 |
| 153 bool ServiceWorkerContextRequestHandler::ShouldReadFromScriptCache( | 216 // Bypass the browser cache for initial installs and update checks after 24 |
| 154 const GURL& url, | 217 // hours have passed. |
| 155 int64_t* resource_id_out) { | 218 int extra_load_flags = 0; |
| 156 // We don't read from the script cache until the version is INSTALLED. | 219 base::TimeDelta time_since_last_check = |
| 157 if (version_->status() == ServiceWorkerVersion::NEW || | 220 base::Time::Now() - registration->last_update_check(); |
| 158 version_->status() == ServiceWorkerVersion::INSTALLING) | 221 if (time_since_last_check > |
| 159 return false; | 222 base::TimeDelta::FromHours(kServiceWorkerScriptMaxCacheAgeInHours) || |
| 160 *resource_id_out = version_->script_cache_map()->LookupResourceId(url); | 223 version_->force_bypass_cache_for_scripts()) { |
| 161 return *resource_id_out != kInvalidServiceWorkerResourceId; | 224 extra_load_flags = net::LOAD_BYPASS_CACHE; |
| 225 } | |
| 226 | |
| 227 ServiceWorkerVersion* stored_version = registration->waiting_version() | |
| 228 ? registration->waiting_version() | |
| 229 : registration->active_version(); | |
| 230 int64_t incumbent_resource_id = kInvalidServiceWorkerResourceId; | |
| 231 if (stored_version && stored_version->script_url() == request->url()) { | |
| 232 incumbent_resource_id = | |
| 233 stored_version->script_cache_map()->LookupResourceId(request->url()); | |
| 234 } | |
| 235 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) | |
|
horo
2017/01/10 07:09:27
if (is_main_script)
falken
2017/01/10 08:17:46
Done.
| |
| 236 version_->embedded_worker()->OnURLJobCreatedForMainScript(); | |
| 237 *out_status = incumbent_resource_id == kInvalidServiceWorkerResourceId | |
| 238 ? CreateJobStatus::WRITE_JOB_FOR_REGISTER | |
| 239 : CreateJobStatus::WRITE_JOB_FOR_UPDATE; | |
| 240 return new ServiceWorkerWriteToCacheJob( | |
| 241 request, network_delegate, resource_type_, context_, version_.get(), | |
| 242 extra_load_flags, resource_id, incumbent_resource_id); | |
| 162 } | 243 } |
| 163 | 244 |
| 164 } // namespace content | 245 } // namespace content |
| OLD | NEW |