| Index: content/browser/service_worker/service_worker_context_request_handler.cc
|
| diff --git a/content/browser/service_worker/service_worker_context_request_handler.cc b/content/browser/service_worker/service_worker_context_request_handler.cc
|
| index 39250cdebf9cf5fcd379af673edc55e1661df609..ed72f5f6b2352497c25e6922b5152ee50cd0a8ab 100644
|
| --- a/content/browser/service_worker/service_worker_context_request_handler.cc
|
| +++ b/content/browser/service_worker/service_worker_context_request_handler.cc
|
| @@ -11,13 +11,36 @@
|
| #include "content/browser/service_worker/service_worker_storage.h"
|
| #include "content/browser/service_worker/service_worker_version.h"
|
| #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
|
| -#include "content/public/browser/resource_context.h"
|
| #include "content/public/common/resource_response_info.h"
|
| #include "net/base/load_flags.h"
|
| +#include "net/log/net_log.h"
|
| +#include "net/log/net_log_event_type.h"
|
| +#include "net/log/net_log_with_source.h"
|
| #include "net/url_request/url_request.h"
|
| +#include "net/url_request/url_request_error_job.h"
|
|
|
| namespace content {
|
|
|
| +namespace {
|
| +
|
| +bool IsInstalled(const ServiceWorkerVersion* version) {
|
| + switch (version->status()) {
|
| + case ServiceWorkerVersion::NEW:
|
| + case ServiceWorkerVersion::INSTALLING:
|
| + return false;
|
| + case ServiceWorkerVersion::INSTALLED:
|
| + case ServiceWorkerVersion::ACTIVATING:
|
| + case ServiceWorkerVersion::ACTIVATED:
|
| + return true;
|
| + case ServiceWorkerVersion::REDUNDANT:
|
| + return false;
|
| + }
|
| + NOTREACHED();
|
| + return false;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler(
|
| base::WeakPtr<ServiceWorkerContextCore> context,
|
| base::WeakPtr<ServiceWorkerProviderHost> provider_host,
|
| @@ -36,129 +59,186 @@ ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler(
|
| ServiceWorkerContextRequestHandler::~ServiceWorkerContextRequestHandler() {
|
| }
|
|
|
| +// static
|
| +std::string ServiceWorkerContextRequestHandler::CreateJobStatusToString(
|
| + CreateJobStatus status) {
|
| + switch (status) {
|
| + case CreateJobStatus::UNINITIALIZED:
|
| + return "UNINITIALIZED";
|
| + case CreateJobStatus::WRITE_JOB_FOR_REGISTER:
|
| + return "WRITE_JOB_FOR_REGISTER";
|
| + case CreateJobStatus::WRITE_JOB_FOR_UPDATE:
|
| + return "WRITE_JOB_FOR_UPDATE";
|
| + case CreateJobStatus::READ_JOB:
|
| + return "READ_JOB";
|
| + case CreateJobStatus::READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT:
|
| + return "READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT";
|
| + case CreateJobStatus::ERROR_NO_PROVIDER:
|
| + return "ERROR_NO_PROVIDER";
|
| + case CreateJobStatus::ERROR_NO_VERSION:
|
| + return "ERROR_NO_VERSION";
|
| + case CreateJobStatus::ERROR_REDUNDANT_VERSION:
|
| + return "ERROR_REDUNDANT_VERSION";
|
| + case CreateJobStatus::ERROR_NO_CONTEXT:
|
| + return "ERROR_NO_CONTEXT";
|
| + case CreateJobStatus::ERROR_REDIRECT:
|
| + return "ERROR_REDIRECT";
|
| + case CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT:
|
| + return "ERROR_UNINSTALLED_SCRIPT_IMPORT";
|
| + case CreateJobStatus::ERROR_OUT_OF_RESOURCE_IDS:
|
| + return "ERROR_OUT_OF_RESOURCE_IDS";
|
| + case CreateJobStatus::NUM_TYPES:
|
| + NOTREACHED();
|
| + }
|
| + NOTREACHED() << static_cast<int>(status);
|
| + return "UNKNOWN";
|
| +}
|
| +
|
| net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob(
|
| net::URLRequest* request,
|
| net::NetworkDelegate* network_delegate,
|
| ResourceContext* resource_context) {
|
| - CreateJobStatus status = CreateJobStatus::DID_NOT_SET_STATUS;
|
| + // We only use the script cache for main script loading and
|
| + // importScripts(), even if a cached script is xhr'd, we don't
|
| + // retrieve it from the script cache.
|
| + // TODO(falken): Get the desired behavior clarified in the spec,
|
| + // and make tweak the behavior here to match.
|
| + if (resource_type_ != RESOURCE_TYPE_SERVICE_WORKER &&
|
| + resource_type_ != RESOURCE_TYPE_SCRIPT) {
|
| + // Fall back to network.
|
| + return nullptr;
|
| + }
|
| +
|
| + CreateJobStatus status = CreateJobStatus::UNINITIALIZED;
|
| net::URLRequestJob* job =
|
| - MaybeCreateJobImpl(&status, request, network_delegate, resource_context);
|
| - if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
|
| + MaybeCreateJobImpl(request, network_delegate, &status);
|
| + const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER;
|
| + // TODO(falken): Add UMA for CreateJobStatus.
|
| + if (is_main_script)
|
| version_->NotifyMainScriptJobCreated(status);
|
| + if (job)
|
| + return job;
|
| +
|
| + // If we got here, a job couldn't be created. Return an error job rather than
|
| + // falling back to network. Otherwise the renderer may receive the response
|
| + // from network and start a service worker whose browser-side
|
| + // ServiceWorkerVersion is not properly initialized.
|
| + //
|
| + // As an exception, allow installed service workers to use importScripts()
|
| + // to import non-installed scripts.
|
| + // TODO(falken): This is a spec violation that should be deprecated and
|
| + // removed. See https://github.com/w3c/ServiceWorker/issues/1021
|
| + if (status == CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT) {
|
| + // Fall back to network.
|
| + return nullptr;
|
| }
|
| - return job;
|
| +
|
| + std::string error_str(CreateJobStatusToString(status));
|
| + request->net_log().AddEvent(
|
| + net::NetLogEventType::SERVICE_WORKER_SCRIPT_LOAD_UNHANDLED_REQUEST_ERROR,
|
| + net::NetLog::StringCallback("error", &error_str));
|
| +
|
| + return new net::URLRequestErrorJob(request, network_delegate,
|
| + net::Error::ERR_FAILED);
|
| }
|
|
|
| net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJobImpl(
|
| - CreateJobStatus* out_status,
|
| net::URLRequest* request,
|
| net::NetworkDelegate* network_delegate,
|
| - ResourceContext* resource_context) {
|
| + CreateJobStatus* out_status) {
|
| if (!provider_host_) {
|
| - *out_status = CreateJobStatus::NO_PROVIDER;
|
| + *out_status = CreateJobStatus::ERROR_NO_PROVIDER;
|
| + return nullptr;
|
| + }
|
| + if (!context_) {
|
| + *out_status = CreateJobStatus::ERROR_NO_CONTEXT;
|
| return nullptr;
|
| }
|
| if (!version_) {
|
| - *out_status = CreateJobStatus::NO_VERSION;
|
| + *out_status = CreateJobStatus::ERROR_NO_VERSION;
|
| return nullptr;
|
| }
|
| - if (!context_) {
|
| - *out_status = CreateJobStatus::NO_CONTEXT;
|
| + // This could happen if browser-side has set the status to redundant but the
|
| + // worker has not yet stopped. The worker is already doomed so just reject the
|
| + // request. Handle it specially here because otherwise it'd be unclear whether
|
| + // "REDUNDANT" should count as installed or not installed when making
|
| + // decisions about how to handle the request and logging UMA.
|
| + if (version_->status() == ServiceWorkerVersion::REDUNDANT) {
|
| + *out_status = CreateJobStatus::ERROR_REDUNDANT_VERSION;
|
| return nullptr;
|
| }
|
|
|
| // We currently have no use case for hijacking a redirected request.
|
| if (request->url_chain().size() > 1) {
|
| - *out_status = CreateJobStatus::IGNORE_REDIRECT;
|
| + *out_status = CreateJobStatus::ERROR_REDIRECT;
|
| return nullptr;
|
| }
|
|
|
| - // We only use the script cache for main script loading and
|
| - // importScripts(), even if a cached script is xhr'd, we don't
|
| - // retrieve it from the script cache.
|
| - // TODO(michaeln): Get the desired behavior clarified in the spec,
|
| - // and make tweak the behavior here to match.
|
| - if (resource_type_ != RESOURCE_TYPE_SERVICE_WORKER &&
|
| - resource_type_ != RESOURCE_TYPE_SCRIPT) {
|
| - *out_status = CreateJobStatus::IGNORE_NON_SCRIPT;
|
| - return nullptr;
|
| - }
|
| -
|
| - if (ShouldAddToScriptCache(request->url())) {
|
| - ServiceWorkerRegistration* registration =
|
| - context_->GetLiveRegistration(version_->registration_id());
|
| - DCHECK(registration); // We're registering or updating so must be there.
|
| -
|
| - int64_t resource_id = context_->storage()->NewResourceId();
|
| - if (resource_id == kInvalidServiceWorkerResourceId) {
|
| - *out_status = CreateJobStatus::OUT_OF_RESOURCE_IDS;
|
| - return nullptr;
|
| - }
|
| -
|
| - // Bypass the browser cache for initial installs and update
|
| - // checks after 24 hours have passed.
|
| - int extra_load_flags = 0;
|
| - base::TimeDelta time_since_last_check =
|
| - base::Time::Now() - registration->last_update_check();
|
| - if (time_since_last_check > base::TimeDelta::FromHours(
|
| - kServiceWorkerScriptMaxCacheAgeInHours) ||
|
| - version_->force_bypass_cache_for_scripts()) {
|
| - extra_load_flags = net::LOAD_BYPASS_CACHE;
|
| - }
|
| -
|
| - ServiceWorkerVersion* stored_version = registration->waiting_version()
|
| - ? registration->waiting_version()
|
| - : registration->active_version();
|
| - int64_t incumbent_resource_id = kInvalidServiceWorkerResourceId;
|
| - if (stored_version && stored_version->script_url() == request->url()) {
|
| - incumbent_resource_id =
|
| - stored_version->script_cache_map()->LookupResourceId(request->url());
|
| + const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER;
|
| + int resource_id =
|
| + version_->script_cache_map()->LookupResourceId(request->url());
|
| + if (resource_id != kInvalidServiceWorkerResourceId) {
|
| + if (IsInstalled(version_.get())) {
|
| + // An installed worker is loading a stored script.
|
| + if (is_main_script)
|
| + version_->embedded_worker()->OnURLJobCreatedForMainScript();
|
| + *out_status = CreateJobStatus::READ_JOB;
|
| + } else {
|
| + // A new worker is loading a stored script. The script was already
|
| + // imported (or the main script is being recursively imported).
|
| + *out_status = CreateJobStatus::READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT;
|
| }
|
| - if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER)
|
| - version_->embedded_worker()->OnURLJobCreatedForMainScript();
|
| - *out_status = incumbent_resource_id == kInvalidServiceWorkerResourceId
|
| - ? CreateJobStatus::CREATED_WRITE_JOB_FOR_REGISTER
|
| - : CreateJobStatus::CREATED_WRITE_JOB_FOR_UPDATE;
|
| - return new ServiceWorkerWriteToCacheJob(
|
| - request, network_delegate, resource_type_, context_, version_.get(),
|
| - extra_load_flags, resource_id, incumbent_resource_id);
|
| - }
|
| -
|
| - int64_t resource_id = kInvalidServiceWorkerResourceId;
|
| - if (ShouldReadFromScriptCache(request->url(), &resource_id)) {
|
| - if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER)
|
| - version_->embedded_worker()->OnURLJobCreatedForMainScript();
|
| - *out_status = CreateJobStatus::CREATED_READ_JOB;
|
| return new ServiceWorkerReadFromCacheJob(request, network_delegate,
|
| resource_type_, context_, version_,
|
| resource_id);
|
| }
|
|
|
| - // NULL means use the network.
|
| - *out_status = CreateJobStatus::IGNORE_UNKNOWN;
|
| - return nullptr;
|
| -}
|
| + // An installed worker is importing a non-stored script.
|
| + if (IsInstalled(version_.get())) {
|
| + DCHECK(!is_main_script);
|
| + *out_status = CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT;
|
| + return nullptr;
|
| + }
|
|
|
| -bool ServiceWorkerContextRequestHandler::ShouldAddToScriptCache(
|
| - const GURL& url) {
|
| - // We only write imports that occur during the initial eval.
|
| - if (version_->status() != ServiceWorkerVersion::NEW &&
|
| - version_->status() != ServiceWorkerVersion::INSTALLING) {
|
| - return false;
|
| + // A new worker is loading a script for the first time. Create a write job to
|
| + // store the script.
|
| + ServiceWorkerRegistration* registration =
|
| + context_->GetLiveRegistration(version_->registration_id());
|
| + DCHECK(registration); // We're registering or updating so must be there.
|
| +
|
| + resource_id = context_->storage()->NewResourceId();
|
| + if (resource_id == kInvalidServiceWorkerResourceId) {
|
| + *out_status = CreateJobStatus::ERROR_OUT_OF_RESOURCE_IDS;
|
| + return nullptr;
|
| }
|
| - return version_->script_cache_map()->LookupResourceId(url) ==
|
| - kInvalidServiceWorkerResourceId;
|
| -}
|
|
|
| -bool ServiceWorkerContextRequestHandler::ShouldReadFromScriptCache(
|
| - const GURL& url,
|
| - int64_t* resource_id_out) {
|
| - // We don't read from the script cache until the version is INSTALLED.
|
| - if (version_->status() == ServiceWorkerVersion::NEW ||
|
| - version_->status() == ServiceWorkerVersion::INSTALLING)
|
| - return false;
|
| - *resource_id_out = version_->script_cache_map()->LookupResourceId(url);
|
| - return *resource_id_out != kInvalidServiceWorkerResourceId;
|
| + // Bypass the browser cache for initial installs and update checks after 24
|
| + // hours have passed.
|
| + int extra_load_flags = 0;
|
| + base::TimeDelta time_since_last_check =
|
| + base::Time::Now() - registration->last_update_check();
|
| + if (time_since_last_check >
|
| + base::TimeDelta::FromHours(kServiceWorkerScriptMaxCacheAgeInHours) ||
|
| + version_->force_bypass_cache_for_scripts()) {
|
| + extra_load_flags = net::LOAD_BYPASS_CACHE;
|
| + }
|
| +
|
| + ServiceWorkerVersion* stored_version = registration->waiting_version()
|
| + ? registration->waiting_version()
|
| + : registration->active_version();
|
| + int64_t incumbent_resource_id = kInvalidServiceWorkerResourceId;
|
| + if (stored_version && stored_version->script_url() == request->url()) {
|
| + incumbent_resource_id =
|
| + stored_version->script_cache_map()->LookupResourceId(request->url());
|
| + }
|
| + if (is_main_script)
|
| + version_->embedded_worker()->OnURLJobCreatedForMainScript();
|
| + *out_status = incumbent_resource_id == kInvalidServiceWorkerResourceId
|
| + ? CreateJobStatus::WRITE_JOB_FOR_REGISTER
|
| + : CreateJobStatus::WRITE_JOB_FOR_UPDATE;
|
| + return new ServiceWorkerWriteToCacheJob(
|
| + request, network_delegate, resource_type_, context_, version_.get(),
|
| + extra_load_flags, resource_id, incumbent_resource_id);
|
| }
|
|
|
| } // namespace content
|
|
|