| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/service_worker/service_worker_register_job.h" |
| |
| #include <stdint.h> |
| |
| #include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/browser/service_worker/embedded_worker_status.h" |
| #include "content/browser/service_worker/service_worker_context_core.h" |
| #include "content/browser/service_worker/service_worker_job_coordinator.h" |
| #include "content/browser/service_worker/service_worker_metrics.h" |
| #include "content/browser/service_worker/service_worker_registration.h" |
| #include "content/browser/service_worker/service_worker_storage.h" |
| #include "content/browser/service_worker/service_worker_write_to_cache_job.h" |
| #include "content/common/service_worker/service_worker_messages.h" |
| #include "content/common/service_worker/service_worker_types.h" |
| #include "content/common/service_worker/service_worker_utils.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "net/base/net_errors.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| void RunSoon(const base::Closure& closure) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure); |
| } |
| |
| } // namespace |
| |
| typedef ServiceWorkerRegisterJobBase::RegistrationJobType RegistrationJobType; |
| |
| ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( |
| base::WeakPtr<ServiceWorkerContextCore> context, |
| const GURL& pattern, |
| const GURL& script_url) |
| : context_(context), |
| job_type_(REGISTRATION_JOB), |
| pattern_(pattern), |
| script_url_(script_url), |
| phase_(INITIAL), |
| doom_installing_worker_(false), |
| is_promise_resolved_(false), |
| should_uninstall_on_failure_(false), |
| force_bypass_cache_(false), |
| skip_script_comparison_(false), |
| promise_resolved_status_(SERVICE_WORKER_OK), |
| weak_factory_(this) {} |
| |
| ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( |
| base::WeakPtr<ServiceWorkerContextCore> context, |
| ServiceWorkerRegistration* registration, |
| bool force_bypass_cache, |
| bool skip_script_comparison) |
| : context_(context), |
| job_type_(UPDATE_JOB), |
| pattern_(registration->pattern()), |
| phase_(INITIAL), |
| doom_installing_worker_(false), |
| is_promise_resolved_(false), |
| should_uninstall_on_failure_(false), |
| force_bypass_cache_(force_bypass_cache), |
| skip_script_comparison_(skip_script_comparison), |
| promise_resolved_status_(SERVICE_WORKER_OK), |
| weak_factory_(this) { |
| internal_.registration = registration; |
| } |
| |
| ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() { |
| DCHECK(!context_ || |
| phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT) |
| << "Jobs should only be interrupted during shutdown."; |
| } |
| |
| void ServiceWorkerRegisterJob::AddCallback( |
| const RegistrationCallback& callback, |
| ServiceWorkerProviderHost* provider_host) { |
| if (!is_promise_resolved_) { |
| callbacks_.push_back(callback); |
| if (provider_host) |
| provider_host->AddScopedProcessReferenceToPattern(pattern_); |
| return; |
| } |
| RunSoon(base::Bind(callback, promise_resolved_status_, |
| promise_resolved_status_message_, |
| base::RetainedRef(promise_resolved_registration_))); |
| } |
| |
| void ServiceWorkerRegisterJob::Start() { |
| BrowserThread::PostAfterStartupTask( |
| FROM_HERE, base::ThreadTaskRunnerHandle::Get(), |
| base::Bind(&ServiceWorkerRegisterJob::StartImpl, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ServiceWorkerRegisterJob::StartImpl() { |
| SetPhase(START); |
| ServiceWorkerStorage::FindRegistrationCallback next_step; |
| if (job_type_ == REGISTRATION_JOB) { |
| next_step = base::Bind( |
| &ServiceWorkerRegisterJob::ContinueWithRegistration, |
| weak_factory_.GetWeakPtr()); |
| } else { |
| next_step = base::Bind( |
| &ServiceWorkerRegisterJob::ContinueWithUpdate, |
| weak_factory_.GetWeakPtr()); |
| } |
| |
| scoped_refptr<ServiceWorkerRegistration> registration = |
| context_->storage()->GetUninstallingRegistration(pattern_); |
| if (registration.get()) |
| RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration)); |
| else |
| context_->storage()->FindRegistrationForPattern(pattern_, next_step); |
| } |
| |
| void ServiceWorkerRegisterJob::Abort() { |
| SetPhase(ABORT); |
| CompleteInternal(SERVICE_WORKER_ERROR_ABORT, std::string()); |
| // Don't have to call FinishJob() because the caller takes care of removing |
| // the jobs from the queue. |
| } |
| |
| bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) const { |
| if (job->GetType() != job_type_) |
| return false; |
| ServiceWorkerRegisterJob* register_job = |
| static_cast<ServiceWorkerRegisterJob*>(job); |
| if (job_type_ == UPDATE_JOB) |
| return register_job->pattern_ == pattern_; |
| DCHECK_EQ(REGISTRATION_JOB, job_type_); |
| return register_job->pattern_ == pattern_ && |
| register_job->script_url_ == script_url_; |
| } |
| |
| RegistrationJobType ServiceWorkerRegisterJob::GetType() const { |
| return job_type_; |
| } |
| |
| void ServiceWorkerRegisterJob::DoomInstallingWorker() { |
| doom_installing_worker_ = true; |
| if (phase_ == INSTALL) |
| Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED, std::string()); |
| } |
| |
| ServiceWorkerRegisterJob::Internal::Internal() {} |
| |
| ServiceWorkerRegisterJob::Internal::~Internal() {} |
| |
| void ServiceWorkerRegisterJob::set_registration( |
| scoped_refptr<ServiceWorkerRegistration> registration) { |
| DCHECK(phase_ == START || phase_ == REGISTER) << phase_; |
| DCHECK(!internal_.registration.get()); |
| internal_.registration = std::move(registration); |
| } |
| |
| ServiceWorkerRegistration* ServiceWorkerRegisterJob::registration() { |
| DCHECK(phase_ >= REGISTER || job_type_ == UPDATE_JOB) << phase_; |
| return internal_.registration.get(); |
| } |
| |
| void ServiceWorkerRegisterJob::set_new_version( |
| ServiceWorkerVersion* version) { |
| DCHECK(phase_ == UPDATE) << phase_; |
| DCHECK(!internal_.new_version.get()); |
| internal_.new_version = version; |
| } |
| |
| ServiceWorkerVersion* ServiceWorkerRegisterJob::new_version() { |
| DCHECK(phase_ >= UPDATE) << phase_; |
| return internal_.new_version.get(); |
| } |
| |
| void ServiceWorkerRegisterJob::SetPhase(Phase phase) { |
| switch (phase) { |
| case INITIAL: |
| NOTREACHED(); |
| break; |
| case START: |
| DCHECK(phase_ == INITIAL) << phase_; |
| break; |
| case REGISTER: |
| DCHECK(phase_ == START) << phase_; |
| break; |
| case UPDATE: |
| DCHECK(phase_ == START || phase_ == REGISTER) << phase_; |
| break; |
| case INSTALL: |
| DCHECK(phase_ == UPDATE) << phase_; |
| break; |
| case STORE: |
| DCHECK(phase_ == INSTALL) << phase_; |
| break; |
| case COMPLETE: |
| DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_; |
| break; |
| case ABORT: |
| break; |
| } |
| phase_ = phase; |
| } |
| |
| // This function corresponds to the steps in [[Register]] following |
| // "Let registration be the result of running the [[GetRegistration]] algorithm. |
| // Throughout this file, comments in quotes are excerpts from the spec. |
| void ServiceWorkerRegisterJob::ContinueWithRegistration( |
| ServiceWorkerStatusCode status, |
| scoped_refptr<ServiceWorkerRegistration> existing_registration) { |
| DCHECK_EQ(REGISTRATION_JOB, job_type_); |
| if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) { |
| Complete(status); |
| return; |
| } |
| |
| if (!existing_registration.get() || existing_registration->is_uninstalled()) { |
| RegisterAndContinue(); |
| return; |
| } |
| |
| DCHECK(existing_registration->GetNewestVersion()); |
| // "If scriptURL is equal to registration.[[ScriptURL]], then:" |
| if (existing_registration->GetNewestVersion()->script_url() == script_url_) { |
| // "Set registration.[[Uninstalling]] to false." |
| existing_registration->AbortPendingClear(base::Bind( |
| &ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl, |
| weak_factory_.GetWeakPtr(), |
| existing_registration)); |
| return; |
| } |
| |
| if (existing_registration->is_uninstalling()) { |
| existing_registration->AbortPendingClear(base::Bind( |
| &ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration, |
| weak_factory_.GetWeakPtr(), |
| existing_registration)); |
| return; |
| } |
| |
| // "Return the result of running the [[Update]] algorithm, or its equivalent, |
| // passing registration as the argument." |
| set_registration(existing_registration); |
| UpdateAndContinue(); |
| } |
| |
| void ServiceWorkerRegisterJob::ContinueWithUpdate( |
| ServiceWorkerStatusCode status, |
| scoped_refptr<ServiceWorkerRegistration> existing_registration) { |
| DCHECK_EQ(UPDATE_JOB, job_type_); |
| if (status != SERVICE_WORKER_OK) { |
| Complete(status); |
| return; |
| } |
| |
| if (existing_registration.get() != registration()) { |
| Complete(SERVICE_WORKER_ERROR_NOT_FOUND); |
| return; |
| } |
| |
| // A previous job may have unregistered this registration. |
| if (registration()->is_uninstalling() || |
| !registration()->GetNewestVersion()) { |
| Complete(SERVICE_WORKER_ERROR_NOT_FOUND); |
| return; |
| } |
| |
| DCHECK(script_url_.is_empty()); |
| script_url_ = registration()->GetNewestVersion()->script_url(); |
| |
| // TODO(michaeln): If the last update check was less than 24 hours |
| // ago, depending on the freshness of the cached worker script we |
| // may be able to complete the update job right here. |
| |
| UpdateAndContinue(); |
| } |
| |
| // Creates a new ServiceWorkerRegistration. |
| void ServiceWorkerRegisterJob::RegisterAndContinue() { |
| SetPhase(REGISTER); |
| |
| int64_t registration_id = context_->storage()->NewRegistrationId(); |
| if (registration_id == kInvalidServiceWorkerRegistrationId) { |
| Complete(SERVICE_WORKER_ERROR_ABORT); |
| return; |
| } |
| |
| set_registration( |
| new ServiceWorkerRegistration(pattern_, registration_id, context_)); |
| AddRegistrationToMatchingProviderHosts(registration()); |
| UpdateAndContinue(); |
| } |
| |
| void ServiceWorkerRegisterJob::ContinueWithUninstallingRegistration( |
| scoped_refptr<ServiceWorkerRegistration> existing_registration, |
| ServiceWorkerStatusCode status) { |
| if (status != SERVICE_WORKER_OK) { |
| Complete(status); |
| return; |
| } |
| should_uninstall_on_failure_ = true; |
| set_registration(existing_registration); |
| UpdateAndContinue(); |
| } |
| |
| void ServiceWorkerRegisterJob::ContinueWithRegistrationForSameScriptUrl( |
| scoped_refptr<ServiceWorkerRegistration> existing_registration, |
| ServiceWorkerStatusCode status) { |
| if (status != SERVICE_WORKER_OK) { |
| Complete(status); |
| return; |
| } |
| set_registration(existing_registration); |
| |
| // "If newestWorker is not null, and scriptURL is equal to |
| // newestWorker.scriptURL, then: |
| // Return a promise resolved with registration." |
| // We resolve only if there's an active version. If there's not, |
| // then there is either no version or only a waiting version from |
| // the last browser session; it makes sense to proceed with registration in |
| // either case. |
| DCHECK(!existing_registration->installing_version()); |
| if (existing_registration->active_version()) { |
| ResolvePromise(status, std::string(), existing_registration.get()); |
| Complete(SERVICE_WORKER_OK); |
| return; |
| } |
| |
| // "Return the result of running the [[Update]] algorithm, or its equivalent, |
| // passing registration as the argument." |
| UpdateAndContinue(); |
| } |
| |
| // This function corresponds to the spec's [[Update]] algorithm. |
| void ServiceWorkerRegisterJob::UpdateAndContinue() { |
| SetPhase(UPDATE); |
| context_->storage()->NotifyInstallingRegistration(registration()); |
| |
| int64_t version_id = context_->storage()->NewVersionId(); |
| if (version_id == kInvalidServiceWorkerVersionId) { |
| Complete(SERVICE_WORKER_ERROR_ABORT); |
| return; |
| } |
| |
| // "Let worker be a new ServiceWorker object..." and start |
| // the worker. |
| set_new_version(new ServiceWorkerVersion(registration(), script_url_, |
| version_id, context_)); |
| new_version()->set_force_bypass_cache_for_scripts(force_bypass_cache_); |
| if (registration()->has_installed_version() && !skip_script_comparison_) { |
| new_version()->set_pause_after_download(true); |
| new_version()->embedded_worker()->AddListener(this); |
| } else { |
| new_version()->set_pause_after_download(false); |
| } |
| new_version()->StartWorker( |
| ServiceWorkerMetrics::EventType::INSTALL, |
| base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ServiceWorkerRegisterJob::OnStartWorkerFinished( |
| ServiceWorkerStatusCode status) { |
| BumpLastUpdateCheckTimeIfNeeded(); |
| |
| if (status == SERVICE_WORKER_OK) { |
| InstallAndContinue(); |
| return; |
| } |
| |
| // "If serviceWorker fails to start up..." then reject the promise with an |
| // error and abort. |
| if (status == SERVICE_WORKER_ERROR_TIMEOUT) { |
| Complete(status, "Timed out while trying to start the Service Worker."); |
| return; |
| } |
| |
| const net::URLRequestStatus& main_script_status = |
| new_version()->script_cache_map()->main_script_status(); |
| std::string message; |
| if (main_script_status.status() != net::URLRequestStatus::SUCCESS) { |
| message = new_version()->script_cache_map()->main_script_status_message(); |
| if (message.empty()) |
| message = kFetchScriptError; |
| } |
| Complete(status, message); |
| } |
| |
| // This function corresponds to the spec's [[Install]] algorithm. |
| void ServiceWorkerRegisterJob::InstallAndContinue() { |
| SetPhase(INSTALL); |
| |
| // "Set registration.installingWorker to worker." |
| DCHECK(!registration()->installing_version()); |
| registration()->SetInstallingVersion(new_version()); |
| |
| // "Run the Update State algorithm passing registration's installing worker |
| // and installing as the arguments." |
| new_version()->SetStatus(ServiceWorkerVersion::INSTALLING); |
| |
| // "Resolve registrationPromise with registration." |
| ResolvePromise(SERVICE_WORKER_OK, std::string(), registration()); |
| |
| // "Fire a simple event named updatefound..." |
| registration()->NotifyUpdateFound(); |
| |
| // "Fire an event named install..." |
| new_version()->RunAfterStartWorker( |
| ServiceWorkerMetrics::EventType::INSTALL, |
| base::Bind(&ServiceWorkerRegisterJob::DispatchInstallEvent, |
| weak_factory_.GetWeakPtr()), |
| base::Bind(&ServiceWorkerRegisterJob::OnInstallFailed, |
| weak_factory_.GetWeakPtr())); |
| |
| // A subsequent registration job may terminate our installing worker. It can |
| // only do so after we've started the worker and dispatched the install |
| // event, as those are atomic substeps in the [[Install]] algorithm. |
| if (doom_installing_worker_) |
| Complete(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED); |
| } |
| |
| void ServiceWorkerRegisterJob::DispatchInstallEvent() { |
| DCHECK_EQ(ServiceWorkerVersion::INSTALLING, new_version()->status()) |
| << new_version()->status(); |
| DCHECK_EQ(EmbeddedWorkerStatus::RUNNING, new_version()->running_status()) |
| << "Worker stopped too soon after it was started."; |
| int request_id = new_version()->StartRequest( |
| ServiceWorkerMetrics::EventType::INSTALL, |
| base::Bind(&ServiceWorkerRegisterJob::OnInstallFailed, |
| weak_factory_.GetWeakPtr())); |
| new_version() |
| ->RegisterRequestCallback<ServiceWorkerHostMsg_InstallEventFinished>( |
| request_id, base::Bind(&ServiceWorkerRegisterJob::OnInstallFinished, |
| weak_factory_.GetWeakPtr())); |
| new_version()->DispatchEvent({request_id}, |
| ServiceWorkerMsg_InstallEvent(request_id)); |
| } |
| |
| void ServiceWorkerRegisterJob::OnInstallFinished( |
| int request_id, |
| blink::WebServiceWorkerEventResult result, |
| bool has_fetch_handler) { |
| new_version()->FinishRequest( |
| request_id, result == blink::WebServiceWorkerEventResultCompleted); |
| |
| ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; |
| switch (result) { |
| case blink::WebServiceWorkerEventResultCompleted: |
| status = SERVICE_WORKER_OK; |
| break; |
| case blink::WebServiceWorkerEventResultRejected: |
| status = SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| if (status != SERVICE_WORKER_OK) { |
| OnInstallFailed(status); |
| return; |
| } |
| |
| ServiceWorkerMetrics::RecordInstallEventStatus(status); |
| |
| SetPhase(STORE); |
| DCHECK(!registration()->last_update_check().is_null()); |
| new_version()->set_has_fetch_handler(has_fetch_handler); |
| context_->storage()->StoreRegistration( |
| registration(), |
| new_version(), |
| base::Bind(&ServiceWorkerRegisterJob::OnStoreRegistrationComplete, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ServiceWorkerRegisterJob::OnInstallFailed(ServiceWorkerStatusCode status) { |
| ServiceWorkerMetrics::RecordInstallEventStatus(status); |
| |
| if (status != SERVICE_WORKER_OK) { |
| Complete(status, std::string("ServiceWorker failed to install: ") + |
| ServiceWorkerStatusToString(status)); |
| } else { |
| NOTREACHED() << "OnInstallFailed should not handle SERVICE_WORKER_OK"; |
| } |
| } |
| |
| void ServiceWorkerRegisterJob::OnStoreRegistrationComplete( |
| ServiceWorkerStatusCode status) { |
| if (status != SERVICE_WORKER_OK) { |
| Complete(status); |
| return; |
| } |
| |
| // "9. If registration.waitingWorker is not null, then:..." |
| if (registration()->waiting_version()) { |
| // 1. Set redundantWorker to registration’s waiting worker. |
| // 2. Terminate redundantWorker. |
| registration()->waiting_version()->StopWorker( |
| base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); |
| // TODO(falken): Move this further down. The spec says to set status to |
| // 'redundant' after promoting the new version to .waiting attribute and |
| // 'installed' status. |
| registration()->waiting_version()->SetStatus( |
| ServiceWorkerVersion::REDUNDANT); |
| } |
| |
| // "10. Set registration.waitingWorker to registration.installingWorker." |
| // "11. Set registration.installingWorker to null." |
| registration()->SetWaitingVersion(new_version()); |
| |
| // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker |
| // and "installed" as the arguments." |
| new_version()->SetStatus(ServiceWorkerVersion::INSTALLED); |
| |
| // "If registration's waiting worker's skip waiting flag is set:" then |
| // activate the worker immediately otherwise "wait until no service worker |
| // client is using registration as their service worker registration." |
| registration()->ActivateWaitingVersionWhenReady(); |
| |
| Complete(SERVICE_WORKER_OK); |
| } |
| |
| void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) { |
| Complete(status, std::string()); |
| } |
| |
| void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status, |
| const std::string& status_message) { |
| CompleteInternal(status, status_message); |
| context_->job_coordinator()->FinishJob(pattern_, this); |
| } |
| |
| void ServiceWorkerRegisterJob::CompleteInternal( |
| ServiceWorkerStatusCode status, |
| const std::string& status_message) { |
| SetPhase(COMPLETE); |
| |
| if (new_version()) { |
| new_version()->set_pause_after_download(false); |
| new_version()->embedded_worker()->RemoveListener(this); |
| } |
| |
| if (status != SERVICE_WORKER_OK) { |
| if (registration()) { |
| if (should_uninstall_on_failure_) |
| registration()->ClearWhenReady(); |
| if (new_version()) { |
| if (status == SERVICE_WORKER_ERROR_EXISTS) |
| new_version()->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS); |
| else |
| new_version()->ReportError(status, status_message); |
| registration()->UnsetVersion(new_version()); |
| new_version()->Doom(); |
| } |
| if (!registration()->waiting_version() && |
| !registration()->active_version()) { |
| registration()->NotifyRegistrationFailed(); |
| context_->storage()->DeleteRegistration( |
| registration()->id(), |
| registration()->pattern().GetOrigin(), |
| base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); |
| } |
| } |
| if (!is_promise_resolved_) |
| ResolvePromise(status, status_message, NULL); |
| } |
| DCHECK(callbacks_.empty()); |
| if (registration()) { |
| context_->storage()->NotifyDoneInstallingRegistration( |
| registration(), new_version(), status); |
| if (registration()->has_installed_version()) |
| registration()->set_is_uninstalled(false); |
| } |
| } |
| |
| void ServiceWorkerRegisterJob::ResolvePromise( |
| ServiceWorkerStatusCode status, |
| const std::string& status_message, |
| ServiceWorkerRegistration* registration) { |
| DCHECK(!is_promise_resolved_); |
| |
| is_promise_resolved_ = true; |
| promise_resolved_status_ = status; |
| promise_resolved_status_message_ = status_message, |
| promise_resolved_registration_ = registration; |
| for (std::vector<RegistrationCallback>::iterator it = callbacks_.begin(); |
| it != callbacks_.end(); |
| ++it) { |
| it->Run(status, status_message, registration); |
| } |
| callbacks_.clear(); |
| } |
| |
| void ServiceWorkerRegisterJob::AddRegistrationToMatchingProviderHosts( |
| ServiceWorkerRegistration* registration) { |
| DCHECK(registration); |
| for (std::unique_ptr<ServiceWorkerContextCore::ProviderHostIterator> it = |
| context_->GetProviderHostIterator(); |
| !it->IsAtEnd(); it->Advance()) { |
| ServiceWorkerProviderHost* host = it->GetProviderHost(); |
| if (host->IsHostToRunningServiceWorker()) |
| continue; |
| if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(), |
| host->document_url())) |
| continue; |
| host->AddMatchingRegistration(registration); |
| } |
| } |
| |
| void ServiceWorkerRegisterJob::OnScriptLoaded() { |
| DCHECK(new_version()->pause_after_download()); |
| new_version()->set_pause_after_download(false); |
| net::URLRequestStatus status = |
| new_version()->script_cache_map()->main_script_status(); |
| if (!status.is_success()) { |
| // OnScriptLoaded signifies a successful network load, which translates into |
| // a script cache error only in the byte-for-byte identical case. |
| DCHECK_EQ(status.error(), |
| ServiceWorkerWriteToCacheJob::kIdenticalScriptError); |
| |
| BumpLastUpdateCheckTimeIfNeeded(); |
| ResolvePromise(SERVICE_WORKER_OK, std::string(), registration()); |
| Complete(SERVICE_WORKER_ERROR_EXISTS, |
| "The updated worker is identical to the incumbent."); |
| return; |
| } |
| |
| new_version()->embedded_worker()->ResumeAfterDownload(); |
| } |
| |
| void ServiceWorkerRegisterJob::BumpLastUpdateCheckTimeIfNeeded() { |
| // Bump the last update check time only when the register/update job fetched |
| // the version having bypassed the network cache. We assume that the |
| // BYPASS_CACHE flag evicts an existing cache entry, so even if the install |
| // ultimately failed for whatever reason, we know the version in the HTTP |
| // cache is not stale, so it's OK to bump the update check time. |
| if (new_version()->embedded_worker()->network_accessed_for_script() || |
| new_version()->force_bypass_cache_for_scripts() || |
| registration()->last_update_check().is_null()) { |
| registration()->set_last_update_check(base::Time::Now()); |
| |
| if (registration()->has_installed_version()) |
| context_->storage()->UpdateLastUpdateCheckTime(registration()); |
| } |
| } |
| |
| } // namespace content |