void ServiceWorkerUpdateJob::ContinueUpdateAfterScriptEval(bool aScriptEvaluationResult) { AssertIsOnMainThread(); RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); if (Canceled() || !swm) { FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); return; } // Step 7.5 of the Update algorithm verifying that the script evaluated // successfully. if (NS_WARN_IF(!aScriptEvaluationResult)) { ErrorResult error; NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec); NS_ConvertUTF8toUTF16 scope(mRegistration->mScope); error.ThrowTypeError<MSG_SW_SCRIPT_THREW>(scriptSpec, scope); FailUpdateJob(error); return; } Install(swm); }
void ServiceWorkerUpdateJob::ContinueAfterInstallEvent(bool aInstallEventSuccess) { if (Canceled()) { return FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); } MOZ_ASSERT(mRegistration->GetInstalling()); // Continue executing the Install algorithm at step 12. // "If installFailed is true" if (NS_WARN_IF(!aInstallEventSuccess)) { // The installing worker is cleaned up by FailUpdateJob(). FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); return; } mRegistration->TransitionInstallingToWaiting(); Finish(NS_OK); // Step 20 calls for explicitly waiting for queued event tasks to fire. Instead, // we simply queue a runnable to execute Activate. This ensures the events are // flushed from the queue before proceeding. // Step 22 of the Install algorithm. Activate is executed after the completion // of this job. The controlling client and skipWaiting checks are performed // in TryToActivate(). mRegistration->TryToActivateAsync(); }
void ServiceWorkerUpdateJob::Update() { AssertIsOnMainThread(); MOZ_ASSERT(!Canceled()); // SetRegistration() must be called before Update(). MOZ_ASSERT(mRegistration); MOZ_ASSERT(!mRegistration->GetInstalling()); // Begin the script download and comparison steps starting at step 5 // of the Update algorithm. RefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest(); nsAutoString cacheName; // If the script has not changed, we need to perform a byte-for-byte // comparison. if (workerInfo && workerInfo->ScriptSpec().Equals(mScriptSpec)) { cacheName = workerInfo->CacheName(); } RefPtr<CompareCallback> callback = new CompareCallback(this); nsresult rv = serviceWorkerScriptCache::Compare(mRegistration, mPrincipal, cacheName, NS_ConvertUTF8toUTF16(mScriptSpec), callback, mLoadGroup); if (NS_WARN_IF(NS_FAILED(rv))) { FailUpdateJob(rv); return; } }
void ServiceWorkerUpdateJob::AsyncExecute() { AssertIsOnMainThread(); MOZ_ASSERT(GetType() == Type::Update); RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); if (Canceled() || !swm) { FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); return; } // Begin step 1 of the Update algorithm. // // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#update-algorithm RefPtr<ServiceWorkerRegistrationInfo> registration = swm->GetRegistration(mPrincipal, mScope); if (!registration || registration->mPendingUninstall) { ErrorResult rv; rv.ThrowTypeError<MSG_SW_UPDATE_BAD_REGISTRATION>(NS_ConvertUTF8toUTF16(mScope), NS_LITERAL_STRING("uninstalled")); FailUpdateJob(rv); return; } // If a Register job with a new script executed ahead of us in the job queue, // then our update for the old script no longer makes sense. Simply abort // in this case. RefPtr<ServiceWorkerInfo> newest = registration->Newest(); if (newest && !mScriptSpec.Equals(newest->ScriptSpec())) { ErrorResult rv; rv.ThrowTypeError<MSG_SW_UPDATE_BAD_REGISTRATION>(NS_ConvertUTF8toUTF16(mScope), NS_LITERAL_STRING("changed")); FailUpdateJob(rv); return; } SetRegistration(registration); Update(); }
void ServiceWorkerUpdateJob::ContinueAfterInstallEvent(bool aInstallEventSuccess) { if (Canceled()) { return FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); } // If we haven't been canceled we should have a registration. There appears // to be a path where it gets cleared before we call into here. Assert // to try to catch this condition, but don't crash in release. MOZ_DIAGNOSTIC_ASSERT(mRegistration); if (!mRegistration) { return FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); } // Continue executing the Install algorithm at step 12. // "If installFailed is true" if (NS_WARN_IF(!aInstallEventSuccess)) { // The installing worker is cleaned up by FailUpdateJob(). FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); return; } MOZ_DIAGNOSTIC_ASSERT(mRegistration->GetInstalling()); mRegistration->TransitionInstallingToWaiting(); Finish(NS_OK); // Step 20 calls for explicitly waiting for queued event tasks to fire. Instead, // we simply queue a runnable to execute Activate. This ensures the events are // flushed from the queue before proceeding. // Step 22 of the Install algorithm. Activate is executed after the completion // of this job. The controlling client and skipWaiting checks are performed // in TryToActivate(). mRegistration->TryToActivateAsync(); }
void ServiceWorkerRegisterJob::AsyncExecute() { AssertIsOnMainThread(); if (Canceled()) { FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); return; } RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); RefPtr<ServiceWorkerRegistrationInfo> registration = swm->GetRegistration(mPrincipal, mScope); if (registration) { // If we are resurrecting an uninstalling registration, then persist // it to disk again. We preemptively removed it earlier during // unregister so that closing the window by shutting down the browser // results in the registration being gone on restart. if (registration->mPendingUninstall) { swm->StoreRegistration(mPrincipal, registration); } registration->mPendingUninstall = false; RefPtr<ServiceWorkerInfo> newest = registration->Newest(); if (newest && mScriptSpec.Equals(newest->ScriptSpec())) { SetRegistration(registration); Finish(NS_OK); return; } } else { registration = swm->CreateNewRegistration(mScope, mPrincipal); } SetRegistration(registration); Update(); }
void ServiceWorkerUpdateJob::ComparisonResult(nsresult aStatus, bool aInCacheAndEqual, const nsAString& aNewCacheName, const nsACString& aMaxScope, nsLoadFlags aLoadFlags) { AssertIsOnMainThread(); RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); if (NS_WARN_IF(Canceled() || !swm)) { FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); return; } // Handle failure of the download or comparison. This is part of Update // step 5 as "If the algorithm asynchronously completes with null, then:". if (NS_WARN_IF(NS_FAILED(aStatus))) { FailUpdateJob(aStatus); return; } // The spec validates the response before performing the byte-for-byte check. // Here we perform the comparison in another module and then validate the // script URL and scope. Make sure to do this validation before accepting // an byte-for-byte match since the service-worker-allowed header might have // changed since the last time it was installed. // This is step 2 the "validate response" section of Update algorithm step 5. // Step 1 is performed in the serviceWorkerScriptCache code. nsCOMPtr<nsIURI> scriptURI; nsresult rv = NS_NewURI(getter_AddRefs(scriptURI), mScriptSpec); if (NS_WARN_IF(NS_FAILED(rv))) { FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR); return; } nsCOMPtr<nsIURI> maxScopeURI; if (!aMaxScope.IsEmpty()) { rv = NS_NewURI(getter_AddRefs(maxScopeURI), aMaxScope, nullptr, scriptURI); if (NS_WARN_IF(NS_FAILED(rv))) { FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR); return; } } mLoadFlags = aLoadFlags; nsAutoCString defaultAllowedPrefix; rv = GetRequiredScopeStringPrefix(scriptURI, defaultAllowedPrefix, eUseDirectory); if (NS_WARN_IF(NS_FAILED(rv))) { FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR); return; } nsAutoCString maxPrefix(defaultAllowedPrefix); if (maxScopeURI) { rv = GetRequiredScopeStringPrefix(maxScopeURI, maxPrefix, eUsePath); if (NS_WARN_IF(NS_FAILED(rv))) { FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR); return; } } if (!StringBeginsWith(mRegistration->mScope, maxPrefix)) { nsAutoString message; NS_ConvertUTF8toUTF16 reportScope(mRegistration->mScope); NS_ConvertUTF8toUTF16 reportMaxPrefix(maxPrefix); const char16_t* params[] = { reportScope.get(), reportMaxPrefix.get() }; rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, "ServiceWorkerScopePathMismatch", params, message); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to format localized string"); swm->ReportToAllClients(mScope, message, EmptyString(), EmptyString(), 0, 0, nsIScriptError::errorFlag); FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR); return; } // The response has been validated, so now we can consider if its a // byte-for-byte match. This is step 6 of the Update algorithm. if (aInCacheAndEqual) { Finish(NS_OK); return; } Telemetry::Accumulate(Telemetry::SERVICE_WORKER_UPDATED, 1); // Begin step 7 of the Update algorithm to evaluate the new script. RefPtr<ServiceWorkerInfo> sw = new ServiceWorkerInfo(mRegistration->mPrincipal, mRegistration->mScope, mScriptSpec, aNewCacheName, mLoadFlags); mRegistration->SetEvaluating(sw); nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle( new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>( "ServiceWorkerUpdateJob", this)); RefPtr<LifeCycleEventCallback> callback = new ContinueUpdateRunnable(handle); ServiceWorkerPrivate* workerPrivate = sw->WorkerPrivate(); MOZ_ASSERT(workerPrivate); rv = workerPrivate->CheckScriptEvaluation(callback); if (NS_WARN_IF(NS_FAILED(rv))) { FailUpdateJob(NS_ERROR_DOM_ABORT_ERR); return; } }
void ServiceWorkerUpdateJob::FailUpdateJob(nsresult aRv) { ErrorResult rv(aRv); FailUpdateJob(rv); }