зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1189659 - Part 3 - Use separate synchronization queues for service worker register jobs and install jobs. r=bkelly
This commit is contained in:
Родитель
0094835ad9
Коммит
8497aa95e5
|
@ -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
|
||||
|
Загрузка…
Ссылка в новой задаче