зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1227932 - Fix Service Workers SoftUpdate and registration.update code paths. r=ehsan
This patch splits the code paths for registration.update and soft update since they have different behaviour. Next, it changes ServiceWorkerRegisterJob to use just one callback and just prevents soft update from queuing a new task if another one is pending.
This commit is contained in:
Родитель
b5c0f9f920
Коммит
4390d49a7b
|
@ -375,6 +375,7 @@ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& a
|
|||
, mScope(aScope)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mLastUpdateCheckTime(0)
|
||||
, mUpdating(false)
|
||||
, mPendingUninstall(false)
|
||||
{}
|
||||
|
||||
|
@ -863,7 +864,7 @@ class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
|
|||
nsCString mScope;
|
||||
nsCString mScriptSpec;
|
||||
RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
|
||||
nsTArray<RefPtr<ServiceWorkerUpdateFinishCallback>> mCallbacks;
|
||||
RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
|
@ -892,6 +893,7 @@ public:
|
|||
: ServiceWorkerJob(aQueue)
|
||||
, mScope(aScope)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mCallback(aCallback)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mLoadGroup(aLoadGroup)
|
||||
, mJobType(REGISTER_JOB)
|
||||
|
@ -900,8 +902,6 @@ public:
|
|||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mLoadGroup);
|
||||
MOZ_ASSERT(aCallback);
|
||||
|
||||
mCallbacks.AppendElement(aCallback);
|
||||
}
|
||||
|
||||
// [[Update]]
|
||||
|
@ -910,13 +910,11 @@ public:
|
|||
ServiceWorkerUpdateFinishCallback* aCallback)
|
||||
: ServiceWorkerJob(aQueue)
|
||||
, mRegistration(aRegistration)
|
||||
, mCallback(aCallback)
|
||||
, mJobType(UPDATE_JOB)
|
||||
, mCanceled(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aCallback);
|
||||
|
||||
mCallbacks.AppendElement(aCallback);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -925,16 +923,6 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
AppendCallback(ServiceWorkerUpdateFinishCallback* aCallback)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(!mCallbacks.Contains(aCallback));
|
||||
|
||||
mCallbacks.AppendElement(aCallback);
|
||||
}
|
||||
|
||||
void
|
||||
Cancel()
|
||||
{
|
||||
|
@ -986,10 +974,6 @@ public:
|
|||
swm->StoreRegistration(mPrincipal, mRegistration);
|
||||
} else {
|
||||
MOZ_ASSERT(mJobType == UPDATE_JOB);
|
||||
MOZ_ASSERT(mRegistration);
|
||||
MOZ_ASSERT(mRegistration->mUpdateJob == nullptr);
|
||||
|
||||
mRegistration->mUpdateJob = this;
|
||||
}
|
||||
|
||||
Update();
|
||||
|
@ -1121,8 +1105,6 @@ public:
|
|||
Fail(ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mCallbacks.Length());
|
||||
|
||||
// With cancellation support, we may only be running with one reference
|
||||
// from another object like a stream loader or something.
|
||||
RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
|
||||
|
@ -1155,15 +1137,11 @@ public:
|
|||
aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < mCallbacks.Length(); ++i) {
|
||||
ErrorResult rv;
|
||||
aRv.CloneTo(rv);
|
||||
mCallbacks[i]->UpdateFailed(rv);
|
||||
rv.SuppressException();
|
||||
if (mCallback) {
|
||||
mCallback->UpdateFailed(aRv);
|
||||
mCallback = nullptr;
|
||||
}
|
||||
|
||||
mCallbacks[0]->UpdateFailed(aRv);
|
||||
|
||||
// In case the callback does not consume the exception
|
||||
aRv.SuppressException();
|
||||
|
||||
|
@ -1179,7 +1157,6 @@ public:
|
|||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->MaybeRemoveRegistration(mRegistration);
|
||||
// Ensures that the job can't do anything useful from this point on.
|
||||
mRegistration->mUpdateJob = nullptr;
|
||||
mRegistration = nullptr;
|
||||
Done(origStatus);
|
||||
}
|
||||
|
@ -1195,11 +1172,14 @@ public:
|
|||
void
|
||||
ContinueInstall()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
// mRegistration will be null if we have already Fail()ed.
|
||||
if (!mRegistration) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRegistration->mUpdating = false;
|
||||
|
||||
// Even if we are canceled, ensure integrity of mSetOfScopesBeingUpdated
|
||||
// first.
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
|
@ -1275,12 +1255,16 @@ private:
|
|||
void
|
||||
Update()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// Since Update() is called synchronously from Start(), we can assert this.
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
MOZ_ASSERT(mRegistration);
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::ContinueUpdate);
|
||||
NS_DispatchToMainThread(r);
|
||||
|
||||
mRegistration->mUpdating = true;
|
||||
}
|
||||
|
||||
// Aspects of (actually the whole algorithm) of [[Update]] after
|
||||
|
@ -1323,12 +1307,11 @@ private:
|
|||
Succeed()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mCallbacks.Length());
|
||||
|
||||
for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
|
||||
mCallbacks[i]->UpdateSucceeded(mRegistration);
|
||||
// We don't have a callback for soft updates.
|
||||
if (mCallback) {
|
||||
mCallback->UpdateSucceeded(mRegistration);
|
||||
mCallback = nullptr;
|
||||
}
|
||||
mCallbacks.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1386,13 +1369,13 @@ private:
|
|||
void
|
||||
Done(nsresult aStatus)
|
||||
{
|
||||
ServiceWorkerJob::Done(aStatus);
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (mJobType == UPDATE_JOB && mRegistration) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mRegistration->mUpdateJob);
|
||||
mRegistration->mUpdateJob = nullptr;
|
||||
if (mRegistration) {
|
||||
mRegistration->mUpdating = false;
|
||||
}
|
||||
|
||||
ServiceWorkerJob::Done(aStatus);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2664,23 +2647,6 @@ ServiceWorkerRegistrationInfo::NotifyListenersOnChange()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerRegistrationInfo::IsUpdating() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mUpdateJob != nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::AppendUpdateCallback(ServiceWorkerUpdateFinishCallback* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(mUpdateJob);
|
||||
|
||||
mUpdateJob->AppendCallback(aCallback);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::LoadRegistration(
|
||||
const ServiceWorkerRegistrationData& aRegistration)
|
||||
|
@ -3526,56 +3492,16 @@ ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerReg
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::SoftUpdate(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
|
||||
nsAutoCString scopeKey;
|
||||
nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
SoftUpdate(scopeKey, aScope, aCallback);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback)
|
||||
const nsACString& aScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsAutoCString scopeKey;
|
||||
aOriginAttributes.CreateSuffix(scopeKey);
|
||||
SoftUpdate(scopeKey, aScope, aCallback);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Empty callback. Only use when you really want to ignore errors.
|
||||
class EmptyUpdateFinishCallback final : public ServiceWorkerUpdateFinishCallback
|
||||
{
|
||||
public:
|
||||
void
|
||||
UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
|
||||
{}
|
||||
|
||||
void
|
||||
UpdateFailed(ErrorResult& aStatus) override
|
||||
{}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ServiceWorkerManager::SoftUpdate(const nsACString& aScopeKey,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback)
|
||||
{
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
GetRegistration(aScopeKey, aScope);
|
||||
GetRegistration(scopeKey, aScope);
|
||||
if (NS_WARN_IF(!registration)) {
|
||||
return;
|
||||
}
|
||||
|
@ -3601,28 +3527,71 @@ ServiceWorkerManager::SoftUpdate(const nsACString& aScopeKey,
|
|||
// "Set registration's registering script url to newestWorker's script url."
|
||||
registration->mScriptSpec = newest->ScriptSpec();
|
||||
|
||||
ServiceWorkerJobQueue* queue =
|
||||
GetOrCreateJobQueue(aScopeKey, aScope);
|
||||
MOZ_ASSERT(queue);
|
||||
// "If the registration queue for registration is empty, invoke Update algorithm,
|
||||
// or its equivalent, with client, registration as its argument."
|
||||
// TODO(catalinb): We don't implement the force bypass cache flag.
|
||||
// See: https://github.com/slightlyoff/ServiceWorker/issues/759
|
||||
if (!registration->mUpdating) {
|
||||
ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scopeKey, aScope);
|
||||
MOZ_ASSERT(queue);
|
||||
|
||||
RefPtr<ServiceWorkerUpdateFinishCallback> cb(aCallback);
|
||||
if (!cb) {
|
||||
cb = new EmptyUpdateFinishCallback();
|
||||
RefPtr<ServiceWorkerRegisterJob> job =
|
||||
new ServiceWorkerRegisterJob(queue, registration, nullptr);
|
||||
queue->Append(job);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(aCallback);
|
||||
|
||||
nsAutoCString scopeKey;
|
||||
nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
GetRegistration(scopeKey, aScope);
|
||||
if (NS_WARN_IF(!registration)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// "If registration's uninstalling flag is set, abort these steps."
|
||||
if (registration->mPendingUninstall) {
|
||||
return;
|
||||
}
|
||||
|
||||
// "Let newestWorker be the result of running Get Newest Worker algorithm
|
||||
// passing registration as its argument.
|
||||
// If newestWorker is null, return a promise rejected with "InvalidStateError"
|
||||
RefPtr<ServiceWorkerInfo> newest = registration->Newest();
|
||||
if (!newest) {
|
||||
ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
aCallback->UpdateFailed(error);
|
||||
|
||||
// In case the callback does not consume the exception
|
||||
error.SuppressException();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// "Set registration's registering script url to newestWorker's script url."
|
||||
registration->mScriptSpec = newest->ScriptSpec();
|
||||
|
||||
ServiceWorkerJobQueue* queue =
|
||||
GetOrCreateJobQueue(scopeKey, aScope);
|
||||
MOZ_ASSERT(queue);
|
||||
|
||||
// "Invoke Update algorithm, or its equivalent, with client, registration as
|
||||
// its argument."
|
||||
if (registration->IsUpdating()) {
|
||||
// This is used to reduce burst of update events. If there is an update
|
||||
// job in queue when we try to create a new one, drop current one and
|
||||
// merge the callback function to existing update job.
|
||||
// See. https://github.com/slightlyoff/ServiceWorker/issues/759
|
||||
registration->AppendUpdateCallback(cb);
|
||||
} else {
|
||||
RefPtr<ServiceWorkerRegisterJob> job =
|
||||
new ServiceWorkerRegisterJob(queue, registration, cb);
|
||||
queue->Append(job);
|
||||
}
|
||||
RefPtr<ServiceWorkerRegisterJob> job =
|
||||
new ServiceWorkerRegisterJob(queue, registration, aCallback);
|
||||
queue->Append(job);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -47,11 +47,9 @@ class ServiceWorker;
|
|||
class ServiceWorkerClientInfo;
|
||||
class ServiceWorkerInfo;
|
||||
class ServiceWorkerJob;
|
||||
class ServiceWorkerRegisterJob;
|
||||
class ServiceWorkerJobQueue;
|
||||
class ServiceWorkerManagerChild;
|
||||
class ServiceWorkerPrivate;
|
||||
class ServiceWorkerUpdateFinishCallback;
|
||||
|
||||
class ServiceWorkerRegistrationInfo final
|
||||
: public nsIServiceWorkerRegistrationInfo
|
||||
|
@ -79,7 +77,11 @@ public:
|
|||
|
||||
uint64_t mLastUpdateCheckTime;
|
||||
|
||||
RefPtr<ServiceWorkerRegisterJob> mUpdateJob;
|
||||
// According to the spec, Soft Update shouldn't queue an update job
|
||||
// if the registration queue is not empty. Because our job queue
|
||||
// works slightly different, we use a flag to determine if the registration
|
||||
// is already updating.
|
||||
bool mUpdating;
|
||||
|
||||
// When unregister() is called on a registration, it is not immediately
|
||||
// removed since documents may be controlled. It is marked as
|
||||
|
@ -149,12 +151,6 @@ public:
|
|||
|
||||
void
|
||||
NotifyListenersOnChange();
|
||||
|
||||
bool
|
||||
IsUpdating() const;
|
||||
|
||||
void
|
||||
AppendUpdateCallback(ServiceWorkerUpdateFinishCallback* aCallback);
|
||||
};
|
||||
|
||||
class ServiceWorkerUpdateFinishCallback
|
||||
|
@ -373,14 +369,13 @@ public:
|
|||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
SoftUpdate(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
|
||||
Update(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback);
|
||||
|
||||
void
|
||||
SoftUpdate(const OriginAttributes& aOriginAttributes,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
|
||||
const nsACString& aScope);
|
||||
|
||||
void
|
||||
PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
|
||||
|
@ -489,11 +484,6 @@ private:
|
|||
void
|
||||
MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
|
||||
|
||||
void
|
||||
SoftUpdate(const nsACString& aScopeKey,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
|
||||
|
||||
already_AddRefed<ServiceWorkerRegistrationInfo>
|
||||
GetRegistration(const nsACString& aScopeKey,
|
||||
const nsACString& aScope) const;
|
||||
|
|
|
@ -42,7 +42,7 @@ ServiceWorkerManagerChild::RecvNotifySoftUpdate(
|
|||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
|
||||
swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope), nullptr);
|
||||
swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -266,8 +266,7 @@ UpdateInternal(nsIPrincipal* aPrincipal,
|
|||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
|
||||
// The spec defines ServiceWorkerRegistration.update() exactly as Soft Update.
|
||||
swm->SoftUpdate(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
|
||||
swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
|
||||
}
|
||||
|
||||
class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
|
||||
|
@ -393,14 +392,22 @@ public:
|
|||
AssertIsOnMainThread();
|
||||
ErrorResult result;
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
// UpdateInternal may try to reject the promise synchronously leading
|
||||
// to a deadlock.
|
||||
{
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal();
|
||||
}
|
||||
MOZ_ASSERT(principal);
|
||||
|
||||
RefPtr<WorkerThreadUpdateCallback> cb =
|
||||
new WorkerThreadUpdateCallback(mPromiseProxy);
|
||||
UpdateInternal(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, cb);
|
||||
UpdateInternal(principal, mScope, cb);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче