Bug 1189659 - Part 3 - Use separate synchronization queues for service worker register jobs and install jobs. r=bkelly

This commit is contained in:
Catalin Badea 2015-11-26 19:03:10 +02:00
Родитель 0094835ad9
Коммит 8497aa95e5
3 изменённых файлов: 375 добавлений и 278 удалений

Просмотреть файл

@ -147,22 +147,38 @@ struct ServiceWorkerManager::PendingOperation final
class ServiceWorkerJob : public nsISupports
{
friend class ServiceWorkerJobQueue;
public:
NS_DECL_ISUPPORTS
enum Type
{
RegisterJob,
UpdateJob,
InstallJob,
UnregisterJob
};
virtual void Start() = 0;
bool
IsRegisterOrInstallJob() const
{
return mJobType == RegisterJob || mJobType == UpdateJob ||
mJobType == InstallJob;
}
protected:
// The queue keeps the jobs alive, so they can hold a rawptr back to the
// queue.
ServiceWorkerJobQueue* mQueue;
public:
NS_DECL_ISUPPORTS
Type mJobType;
virtual void Start() = 0;
virtual bool
IsRegisterJob() const { return false; }
protected:
explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue)
explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue, Type aJobType)
: mQueue(aQueue)
, mJobType(aJobType)
{}
virtual ~ServiceWorkerJob()
@ -176,30 +192,44 @@ class ServiceWorkerJobQueue final
{
friend class ServiceWorkerJob;
nsTArray<RefPtr<ServiceWorkerJob>> mJobs;
const nsCString mOriginAttributesSuffix;
bool mPopping;
public:
explicit ServiceWorkerJobQueue(const nsACString& aScopeKey)
: mOriginAttributesSuffix(aScopeKey)
, mPopping(false)
struct QueueData final
{
QueueData()
: mPopping(false)
{ }
~ServiceWorkerJobQueue()
~QueueData()
{
if (!mJobs.IsEmpty()) {
NS_WARNING("Pending/running jobs still around on shutdown!");
}
}
nsTArray<RefPtr<ServiceWorkerJob>> mJobs;
bool mPopping;
};
const nsCString mOriginAttributesSuffix;
QueueData mRegistrationJobQueue;
QueueData mInstallationJobQueue;
public:
explicit ServiceWorkerJobQueue(const nsACString& aScopeKey)
: mOriginAttributesSuffix(aScopeKey)
{}
~ServiceWorkerJobQueue()
{ }
void
Append(ServiceWorkerJob* aJob)
{
MOZ_ASSERT(aJob);
MOZ_ASSERT(!mJobs.Contains(aJob));
bool wasEmpty = mJobs.IsEmpty();
mJobs.AppendElement(aJob);
QueueData& queue = GetQueue(aJob->mJobType);
MOZ_ASSERT(!queue.mJobs.Contains(aJob));
bool wasEmpty = queue.mJobs.IsEmpty();
queue.mJobs.AppendElement(aJob);
if (wasEmpty) {
aJob->Start();
}
@ -208,29 +238,47 @@ public:
void
CancelJobs();
// Only used by HandleError, keep it that way!
ServiceWorkerJob*
Peek()
{
if (mJobs.IsEmpty()) {
return nullptr;
}
return mJobs[0];
}
private:
void
Pop()
CancelJobs(QueueData& aQueue);
// Internal helper function used to assign jobs to the correct queue.
QueueData&
GetQueue(ServiceWorkerJob::Type aType)
{
MOZ_ASSERT(!mPopping,
switch (aType) {
case ServiceWorkerJob::Type::RegisterJob:
case ServiceWorkerJob::Type::UpdateJob:
case ServiceWorkerJob::Type::UnregisterJob:
return mRegistrationJobQueue;
case ServiceWorkerJob::Type::InstallJob:
return mInstallationJobQueue;
default:
MOZ_CRASH("Invalid job queue type.");
return mRegistrationJobQueue;
}
}
bool
IsEmpty()
{
return mRegistrationJobQueue.mJobs.IsEmpty() &&
mInstallationJobQueue.mJobs.IsEmpty();
}
void
Pop(QueueData& aQueue)
{
MOZ_ASSERT(!aQueue.mPopping,
"Pop() called recursively, did you write a job which calls Done() synchronously from Start()?");
AutoRestore<bool> savePopping(mPopping);
mPopping = true;
MOZ_ASSERT(!mJobs.IsEmpty());
mJobs.RemoveElementAt(0);
if (!mJobs.IsEmpty()) {
mJobs[0]->Start();
} else {
AutoRestore<bool> savePopping(aQueue.mPopping);
aQueue.mPopping = true;
MOZ_ASSERT(!aQueue.mJobs.IsEmpty());
aQueue.mJobs.RemoveElementAt(0);
if (!aQueue.mJobs.IsEmpty()) {
aQueue.mJobs[0]->Start();
} else if (IsEmpty()) {
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
MOZ_ASSERT(swm);
swm->MaybeRemoveRegistrationInfo(mOriginAttributesSuffix);
@ -240,9 +288,11 @@ private:
void
Done(ServiceWorkerJob* aJob)
{
MOZ_ASSERT(!mJobs.IsEmpty());
MOZ_ASSERT(mJobs[0] == aJob);
Pop();
MOZ_ASSERT(aJob);
QueueData& queue = GetQueue(aJob->mJobType);
MOZ_ASSERT(!queue.mJobs.IsEmpty());
MOZ_ASSERT(queue.mJobs[0] == aJob);
Pop(queue);
}
};
@ -555,14 +605,14 @@ public:
NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
class ServiceWorkerRegisterJob;
class ServiceWorkerInstallJob;
class ContinueInstallTask final : public ContinueLifecycleTask
{
RefPtr<ServiceWorkerRegisterJob> mJob;
RefPtr<ServiceWorkerInstallJob> mJob;
public:
explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob)
explicit ContinueInstallTask(ServiceWorkerInstallJob* aJob)
: mJob(aJob)
{}
@ -862,30 +912,267 @@ private:
} // namespace
class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
public serviceWorkerScriptCache::CompareCallback
class ServiceWorkerJobBase : public ServiceWorkerJob
{
public:
ServiceWorkerJobBase(ServiceWorkerJobQueue* aQueue,
ServiceWorkerJob::Type aJobType,
ServiceWorkerUpdateFinishCallback* aCallback)
: ServiceWorkerJobBase(aQueue, aJobType, aCallback, nullptr, nullptr)
{ }
ServiceWorkerJobBase(ServiceWorkerJobQueue* aQueue,
ServiceWorkerJob::Type aJobType,
ServiceWorkerUpdateFinishCallback* aCallback,
ServiceWorkerRegistrationInfo* aRegistration,
ServiceWorkerInfo* aServiceWorkerInfo)
: ServiceWorkerJob(aQueue, aJobType)
, mCallback(aCallback)
, mCanceled(false)
, mRegistration(aRegistration)
, mUpdateAndInstallInfo(aServiceWorkerInfo)
{
AssertIsOnMainThread();
}
void
Cancel()
{
mQueue = nullptr;
mCanceled = true;
}
protected:
RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
bool mCanceled;
RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
~ServiceWorkerJobBase()
{ }
// This MUST only be called when the job is still performing actions related
// to registration or update. After the spec resolves the update promise, use
// Done() with the failure code instead.
// Callers MUST hold a strong ref before calling this!
void
Fail(ErrorResult& aRv)
{
AssertIsOnMainThread();
MOZ_ASSERT(mRegistration);
// With cancellation support, we may only be running with one reference
// from another object like a stream loader or something.
RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
// Save off the plain error code to pass to Done() where its logged to
// stderr as a warning.
nsresult origStatus = static_cast<nsresult>(aRv.ErrorCodeAsInt());
// Ensure that we only surface SecurityErr or TypeErr to script.
if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
!aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR)) {
// Remove the old error code so we can replace it with a TypeError.
aRv.SuppressException();
NS_ConvertUTF8toUTF16 scriptSpec(mRegistration->mScriptSpec);
NS_ConvertUTF8toUTF16 scope(mRegistration->mScope);
// Throw the type error with a generic error message.
aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
}
if (mCallback) {
mCallback->UpdateFailed(aRv);
mCallback = nullptr;
}
// In case the callback does not consume the exception
aRv.SuppressException();
mUpdateAndInstallInfo = nullptr;
if (mRegistration->mInstallingWorker) {
nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
mRegistration->mInstallingWorker->CacheName());
if (NS_FAILED(rv)) {
NS_WARNING("Failed to purge the installing worker cache.");
}
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
swm->MaybeRemoveRegistration(mRegistration);
// Ensures that the job can't do anything useful from this point on.
mRegistration = nullptr;
Done(origStatus);
}
void
Fail(nsresult aRv)
{
ErrorResult rv(aRv);
Fail(rv);
}
void
Succeed()
{
AssertIsOnMainThread();
// We don't have a callback for soft updates.
if (mCallback) {
mCallback->UpdateSucceeded(mRegistration);
mCallback = nullptr;
}
}
};
class ServiceWorkerInstallJob final : public ServiceWorkerJobBase
{
friend class ContinueInstallTask;
public:
ServiceWorkerInstallJob(ServiceWorkerJobQueue* aQueue,
ServiceWorkerUpdateFinishCallback* aCallback,
ServiceWorkerRegistrationInfo* aRegistration,
ServiceWorkerInfo* aServiceWorkerInfo)
: ServiceWorkerJobBase(aQueue, Type::InstallJob, aCallback,
aRegistration, aServiceWorkerInfo)
{
MOZ_ASSERT(aRegistration);
}
void
Start()
{
AssertIsOnMainThread();
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &ServiceWorkerInstallJob::Install);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
}
void
Install()
{
RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
if (mCanceled) {
return Fail(NS_ERROR_DOM_ABORT_ERR);
}
MOZ_ASSERT(mRegistration);
// Begin [[Install]] atomic step 3.
if (mRegistration->mInstallingWorker) {
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker();
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER);
mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget();
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
mRegistration->NotifyListenersOnChange();
Succeed();
// The job should NOT call fail from this point on.
// Step 8 "Queue a task..." for updatefound.
nsCOMPtr<nsIRunnable> upr =
NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
swm,
&ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
mRegistration);
NS_DispatchToMainThread(upr);
// Call ContinueAfterInstallEvent(false) on main thread if the SW
// script fails to load.
nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool>
(this, &ServiceWorkerInstallJob::ContinueAfterInstallEvent, false);
nsMainThreadPtrHandle<ContinueLifecycleTask> installTask(
new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
RefPtr<LifeCycleEventCallback> callback = new ContinueLifecycleRunnable(installTask);
// This triggers Step 4.7 "Queue a task to run the following substeps..."
// which sends the install event to the worker.
ServiceWorkerPrivate* workerPrivate =
mRegistration->mInstallingWorker->WorkerPrivate();
nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
callback, failRunnable);
if (NS_WARN_IF(NS_FAILED(rv))) {
ContinueAfterInstallEvent(false /* aSuccess */);
}
}
void
ContinueAfterInstallEvent(bool aInstallEventSuccess)
{
if (mCanceled) {
return Done(NS_ERROR_DOM_ABORT_ERR);
}
if (!mRegistration->mInstallingWorker) {
NS_WARNING("mInstallingWorker was null.");
return Done(NS_ERROR_DOM_ABORT_ERR);
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
// "If installFailed is true"
if (NS_WARN_IF(!aInstallEventSuccess)) {
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
mRegistration->mInstallingWorker = nullptr;
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER);
swm->MaybeRemoveRegistration(mRegistration);
return Done(NS_ERROR_DOM_ABORT_ERR);
}
// "If registration's waiting worker is not null"
if (mRegistration->mWaitingWorker) {
mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker();
mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
nsresult rv =
serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
mRegistration->mWaitingWorker->CacheName());
if (NS_FAILED(rv)) {
NS_WARNING("Failed to purge the old waiting cache.");
}
}
mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
mRegistration->NotifyListenersOnChange();
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
// "If registration's waiting worker's skip waiting flag is set"
if (mRegistration->mWaitingWorker->SkipWaitingFlag()) {
mRegistration->PurgeActiveWorker();
}
Done(NS_OK);
// Activate() is invoked out of band of atomic.
mRegistration->TryToActivate();
}
};
class ServiceWorkerRegisterJob final : public ServiceWorkerJobBase,
public serviceWorkerScriptCache::CompareCallback
{
friend class ContinueUpdateRunnable;
nsCString mScope;
nsCString mScriptSpec;
RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
nsCOMPtr<nsIPrincipal> mPrincipal;
RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
nsCOMPtr<nsILoadGroup> mLoadGroup;
~ServiceWorkerRegisterJob()
{ }
enum
{
REGISTER_JOB = 0,
UPDATE_JOB = 1,
} mJobType;
bool mCanceled;
public:
NS_DECL_ISUPPORTS_INHERITED
@ -896,14 +1183,11 @@ public:
ServiceWorkerUpdateFinishCallback* aCallback,
nsIPrincipal* aPrincipal,
nsILoadGroup* aLoadGroup)
: ServiceWorkerJob(aQueue)
: ServiceWorkerJobBase(aQueue, Type::RegisterJob, aCallback)
, mScope(aScope)
, mScriptSpec(aScriptSpec)
, mCallback(aCallback)
, mPrincipal(aPrincipal)
, mLoadGroup(aLoadGroup)
, mJobType(REGISTER_JOB)
, mCanceled(false)
{
AssertIsOnMainThread();
MOZ_ASSERT(mLoadGroup);
@ -914,28 +1198,12 @@ public:
ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
ServiceWorkerRegistrationInfo* aRegistration,
ServiceWorkerUpdateFinishCallback* aCallback)
: ServiceWorkerJob(aQueue)
, mRegistration(aRegistration)
, mCallback(aCallback)
, mJobType(UPDATE_JOB)
, mCanceled(false)
: ServiceWorkerJobBase(aQueue, Type::UpdateJob, aCallback,
aRegistration, nullptr)
{
AssertIsOnMainThread();
}
bool
IsRegisterJob() const override
{
return true;
}
void
Cancel()
{
mQueue = nullptr;
mCanceled = true;
}
void
Start() override
{
@ -950,7 +1218,7 @@ public:
return;
}
if (mJobType == REGISTER_JOB) {
if (mJobType == RegisterJob) {
mRegistration = swm->GetRegistration(mPrincipal, mScope);
if (mRegistration) {
@ -979,7 +1247,7 @@ public:
mRegistration->NotifyListenersOnChange();
swm->StoreRegistration(mPrincipal, mRegistration);
} else {
MOZ_ASSERT(mJobType == UPDATE_JOB);
MOZ_ASSERT(mJobType == UpdateJob);
}
Update();
@ -1080,78 +1348,9 @@ public:
}
}
// This MUST only be called when the job is still performing actions related
// to registration or update. After the spec resolves the update promise, use
// Done() with the failure code instead.
// Callers MUST hold a strong ref before calling this!
void
Fail(ErrorResult& aRv)
{
AssertIsOnMainThread();
// With cancellation support, we may only be running with one reference
// from another object like a stream loader or something.
RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
// Save off the plain error code to pass to Done() where its logged to
// stderr as a warning.
nsresult origStatus = static_cast<nsresult>(aRv.ErrorCodeAsInt());
// Ensure that we only surface SecurityErr or TypeErr to script.
if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
!aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR)) {
// Remove the old error code so we can replace it with a TypeError.
aRv.SuppressException();
// Depending on how the job was created and where we are in the
// state machine the spec and scope may be stored in different ways.
// Extract the current scope and script spec.
nsString scriptSpec;
nsString scope;
if (mRegistration) {
CopyUTF8toUTF16(mRegistration->mScriptSpec, scriptSpec);
CopyUTF8toUTF16(mRegistration->mScope, scope);
} else {
CopyUTF8toUTF16(mScriptSpec, scriptSpec);
CopyUTF8toUTF16(mScope, scope);
}
// Throw the type error with a generic error message.
aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
}
if (mCallback) {
mCallback->UpdateFailed(aRv);
mCallback = nullptr;
}
// In case the callback does not consume the exception
aRv.SuppressException();
mUpdateAndInstallInfo = nullptr;
if (mRegistration->mInstallingWorker) {
nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
mRegistration->mInstallingWorker->CacheName());
if (NS_FAILED(rv)) {
NS_WARNING("Failed to purge the installing worker cache.");
}
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
swm->MaybeRemoveRegistration(mRegistration);
// Ensures that the job can't do anything useful from this point on.
mRegistration = nullptr;
Done(origStatus);
}
void
Fail(nsresult aRv)
{
ErrorResult rv(aRv);
Fail(rv);
}
private:
// This will perform steps 27 and 28 from [[Update]]
// Remove the job from the registration queue and invoke [[Install]]
void
ContinueInstall(bool aScriptEvaluationResult)
{
@ -1159,8 +1358,6 @@ private:
MOZ_ASSERT(mRegistration);
mRegistration->mUpdating = false;
// This is effectively the end of Step 4.3 of the [[Update]] algorithm.
// The invocation of [[Install]] is not part of the atomic block.
RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
if (mCanceled) {
return Fail(NS_ERROR_DOM_ABORT_ERR);
@ -1175,52 +1372,11 @@ private:
return Fail(error);
}
// Begin [[Install]] atomic step 4.
if (mRegistration->mInstallingWorker) {
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker();
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER);
mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget();
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
mRegistration->NotifyListenersOnChange();
Succeed();
// The job should NOT call fail from this point on.
// Step 4.6 "Queue a task..." for updatefound.
nsCOMPtr<nsIRunnable> upr =
NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
swm,
&ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
mRegistration);
NS_DispatchToMainThread(upr);
// Call ContinueAfterInstallEvent(false) on main thread if the SW
// script fails to load.
nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool>
(this, &ServiceWorkerRegisterJob::ContinueAfterInstallEvent, false);
nsMainThreadPtrHandle<ContinueLifecycleTask> installTask(
new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
RefPtr<LifeCycleEventCallback> callback = new ContinueLifecycleRunnable(installTask);
// This triggers Step 4.7 "Queue a task to run the following substeps..."
// which sends the install event to the worker.
ServiceWorkerPrivate* workerPrivate =
mRegistration->mInstallingWorker->WorkerPrivate();
rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
callback, failRunnable);
if (NS_WARN_IF(NS_FAILED(rv))) {
ContinueAfterInstallEvent(false /* aSuccess */);
}
RefPtr<ServiceWorkerInstallJob> job =
new ServiceWorkerInstallJob(mQueue, mCallback,
mRegistration, mUpdateAndInstallInfo);
mQueue->Append(job);
Done(NS_OK);
}
void
@ -1251,7 +1407,7 @@ private:
if (mRegistration->mInstallingWorker) {
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
// This will terminate the installing worker thread.
mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker();
mRegistration->mInstallingWorker = nullptr;
}
@ -1274,69 +1430,6 @@ private:
}
}
void
Succeed()
{
AssertIsOnMainThread();
// We don't have a callback for soft updates.
if (mCallback) {
mCallback->UpdateSucceeded(mRegistration);
mCallback = nullptr;
}
}
void
ContinueAfterInstallEvent(bool aInstallEventSuccess)
{
if (mCanceled) {
return Done(NS_ERROR_DOM_ABORT_ERR);
}
if (!mRegistration->mInstallingWorker) {
NS_WARNING("mInstallingWorker was null.");
return Done(NS_ERROR_DOM_ABORT_ERR);
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
// "If installFailed is true"
if (NS_WARN_IF(!aInstallEventSuccess)) {
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
mRegistration->mInstallingWorker = nullptr;
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER);
swm->MaybeRemoveRegistration(mRegistration);
return Done(NS_ERROR_DOM_ABORT_ERR);
}
// "If registration's waiting worker is not null"
if (mRegistration->mWaitingWorker) {
mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker();
mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
mRegistration->mWaitingWorker->CacheName());
if (NS_FAILED(rv)) {
NS_WARNING("Failed to purge the old waiting cache.");
}
}
mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
mRegistration->NotifyListenersOnChange();
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
// "If registration's waiting worker's skip waiting flag is set"
if (mRegistration->mWaitingWorker->SkipWaitingFlag()) {
mRegistration->PurgeActiveWorker();
}
Done(NS_OK);
// Activate() is invoked out of band of atomic.
mRegistration->TryToActivate();
}
void
Done(nsresult aStatus)
{
@ -1355,23 +1448,31 @@ NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerRegisterJob, ServiceWorkerJob);
void
ServiceWorkerJobQueue::CancelJobs()
{
if (mJobs.IsEmpty()) {
// The order doesn't matter. Cancel() just sets a flag on these jobs.
CancelJobs(mRegistrationJobQueue);
CancelJobs(mInstallationJobQueue);
}
void
ServiceWorkerJobQueue::CancelJobs(QueueData& aQueue)
{
if (aQueue.mJobs.IsEmpty()) {
return;
}
// We have to treat the first job specially. It is the running job and needs
// to be notified correctly.
RefPtr<ServiceWorkerJob> runningJob = mJobs[0];
RefPtr<ServiceWorkerJob> runningJob = aQueue.mJobs[0];
// We can just let an Unregister job run to completion.
if (runningJob->IsRegisterJob()) {
ServiceWorkerRegisterJob* job = static_cast<ServiceWorkerRegisterJob*>(runningJob.get());
if (runningJob->IsRegisterOrInstallJob()) {
ServiceWorkerJobBase* job = static_cast<ServiceWorkerJobBase*>(runningJob.get());
job->Cancel();
}
// Get rid of everything. Non-main thread objects may still be holding a ref
// to the running register job. Since we called Cancel() on it, the job's
// main thread functions will just exit.
mJobs.Clear();
aQueue.mJobs.Clear();
}
NS_IMETHODIMP
@ -2186,7 +2287,7 @@ public:
const nsACString& aScope,
nsIServiceWorkerUnregisterCallback* aCallback,
nsIPrincipal* aPrincipal)
: ServiceWorkerJob(aQueue)
: ServiceWorkerJob(aQueue, Type::UnregisterJob)
, mScope(aScope)
, mCallback(aCallback)
, mPrincipal(aPrincipal)

Просмотреть файл

@ -312,7 +312,9 @@ class ServiceWorkerManager final
friend class GetRegistrationsRunnable;
friend class GetRegistrationRunnable;
friend class ServiceWorkerJobQueue;
friend class ServiceWorkerInstallJob;
friend class ServiceWorkerRegisterJob;
friend class ServiceWorkerJobBase;
friend class ServiceWorkerRegistrationInfo;
friend class ServiceWorkerUnregisterJob;

Просмотреть файл

@ -1,6 +0,0 @@
[register-wait-forever-in-install-worker.https.html]
type: testharness
expected: TIMEOUT
[register worker that calls waitUntil with a promise that never resolves in oninstall]
expected: TIMEOUT