Bug 1346247 - Avoid race conditions when SW are updated - part 1 - PServiceWorkerUpdater actor, r=bkelly

This commit is contained in:
Andrea Marchesini 2017-03-28 11:48:38 +02:00
Родитель fc6d826908
Коммит c6334b35ae
15 изменённых файлов: 474 добавлений и 0 удалений

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

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBackground;
include protocol PServiceWorkerUpdater;
include PBackgroundSharedTypes;
include ServiceWorkerRegistrarTypes;
@ -15,6 +16,7 @@ namespace dom {
protocol PServiceWorkerManager
{
manager PBackground;
manages PServiceWorkerUpdater;
parent:
async Register(ServiceWorkerRegistrationData data);
@ -31,6 +33,9 @@ parent:
async Shutdown();
async PServiceWorkerUpdater(OriginAttributes originAttributes,
nsCString scope);
child:
async NotifyRegister(ServiceWorkerRegistrationData data);
async NotifySoftUpdate(OriginAttributes originAttributes, nsString scope);

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

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PServiceWorkerManager;
namespace mozilla {
namespace dom {
protocol PServiceWorkerUpdater
{
manager PServiceWorkerManager;
parent:
// This __delete__ is safe because it's called when Proceed() is received and
// no other IPC messages are received nor sent.
async __delete__();
child:
async Proceed(bool allowed);
};
} // namespace dom
} // namespace mozilla

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

@ -77,6 +77,7 @@
#include "ServiceWorkerEvents.h"
#include "ServiceWorkerUnregisterJob.h"
#include "ServiceWorkerUpdateJob.h"
#include "ServiceWorkerUpdaterChild.h"
#include "SharedWorker.h"
#include "WorkerInlines.h"
#include "WorkerPrivate.h"
@ -508,6 +509,107 @@ private:
{}
};
// This runnable is used for 2 different tasks:
// - to postpone the SoftUpdate() until the IPC SWM actor is created
// (aInternalMethod == false)
// - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is
// notified by the parent (aInternalMethod == true)
class SoftUpdateRunnable final : public Runnable
{
public:
SoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
const nsACString& aScope, bool aInternalMethod)
: mAttrs(aOriginAttributes)
, mScope(aScope)
, mInternalMethod(aInternalMethod)
{}
NS_IMETHOD Run() override
{
AssertIsOnMainThread();
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
return NS_ERROR_FAILURE;
}
if (mInternalMethod) {
swm->SoftUpdateInternal(mAttrs, mScope);
} else {
swm->SoftUpdate(mAttrs, mScope);
}
return NS_OK;
}
private:
~SoftUpdateRunnable()
{}
const OriginAttributes mAttrs;
const nsCString mScope;
bool mInternalMethod;
};
// This runnable is used for 3 different tasks:
// - to postpone the Update() until the IPC SWM actor is created
// (aType == ePostpone)
// - to call the 'real' Update when the ServiceWorkerUpdaterChild is
// notified by the parent (aType == eSuccess)
// - an error must be propagated (aType == eFailure)
class UpdateRunnable final : public Runnable
{
public:
enum Type {
ePostpone,
eSuccess,
eFailure,
};
UpdateRunnable(nsIPrincipal* aPrincipal,
const nsACString& aScope,
ServiceWorkerUpdateFinishCallback* aCallback,
Type aType)
: mPrincipal(aPrincipal)
, mScope(aScope)
, mCallback(aCallback)
, mType(aType)
{}
NS_IMETHOD Run() override
{
AssertIsOnMainThread();
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
return NS_ERROR_FAILURE;
}
if (mType == ePostpone) {
swm->Update(mPrincipal, mScope, mCallback);
return NS_OK;
}
if (mType == eSuccess) {
swm->UpdateInternal(mPrincipal, mScope, mCallback);
return NS_OK;
}
ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
mCallback->UpdateFailed(error);
return NS_OK;
}
private:
~UpdateRunnable()
{}
nsCOMPtr<nsIPrincipal> mPrincipal;
const nsCString mScope;
RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
Type mType;
};
} // namespace
// This function implements parts of the step 3 of the following algorithm:
@ -2726,6 +2828,32 @@ ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
return;
}
if (!mActor) {
RefPtr<Runnable> runnable =
new SoftUpdateRunnable(aOriginAttributes, aScope, false);
AppendPendingOperation(runnable);
return;
}
RefPtr<Runnable> runnable =
new SoftUpdateRunnable(aOriginAttributes, aScope, true);
ServiceWorkerUpdaterChild* actor =
new ServiceWorkerUpdaterChild(runnable, nullptr);
mActor->SendPServiceWorkerUpdaterConstructor(actor, aOriginAttributes,
nsCString(aScope));
}
void
ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
const nsACString& aScope)
{
AssertIsOnMainThread();
if (mShuttingDown) {
return;
}
nsCOMPtr<nsIURI> scopeURI;
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -2820,12 +2948,43 @@ public:
NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback)
};
} // anonymous namespace
void
ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
const nsACString& aScope,
ServiceWorkerUpdateFinishCallback* aCallback)
{
AssertIsOnMainThread();
if (!mActor) {
RefPtr<Runnable> runnable =
new UpdateRunnable(aPrincipal, aScope, aCallback,
UpdateRunnable::ePostpone);
AppendPendingOperation(runnable);
return;
}
RefPtr<Runnable> successRunnable =
new UpdateRunnable(aPrincipal, aScope, aCallback,
UpdateRunnable::eSuccess);
RefPtr<Runnable> failureRunnable =
new UpdateRunnable(aPrincipal, aScope, aCallback,
UpdateRunnable::eFailure);
ServiceWorkerUpdaterChild* actor =
new ServiceWorkerUpdaterChild(successRunnable, failureRunnable);
mActor->SendPServiceWorkerUpdaterConstructor(actor,
aPrincipal->OriginAttributesRef(),
nsCString(aScope));
}
void
ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
const nsACString& aScope,
ServiceWorkerUpdateFinishCallback* aCallback)
{
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aCallback);

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

@ -164,10 +164,20 @@ public:
const nsACString& aScope,
ServiceWorkerUpdateFinishCallback* aCallback);
void
UpdateInternal(nsIPrincipal* aPrincipal,
const nsACString& aScope,
ServiceWorkerUpdateFinishCallback* aCallback);
void
SoftUpdate(const OriginAttributes& aOriginAttributes,
const nsACString& aScope);
void
SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
const nsACString& aScope);
void
PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
const nsAString& aScope);

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

@ -6,6 +6,7 @@
#include "ServiceWorkerManagerChild.h"
#include "ServiceWorkerManager.h"
#include "ServiceWorkerUpdaterChild.h"
#include "mozilla/Unused.h"
namespace mozilla {
@ -102,6 +103,20 @@ ServiceWorkerManagerChild::RecvNotifyRemoveAll()
return IPC_OK();
}
PServiceWorkerUpdaterChild*
ServiceWorkerManagerChild::AllocPServiceWorkerUpdaterChild(const OriginAttributes& aOriginAttributes,
const nsCString& aScope)
{
MOZ_CRASH("Do no use ServiceWorkerUpdaterChild IPC CTOR.");
}
bool
ServiceWorkerManagerChild::DeallocPServiceWorkerUpdaterChild(PServiceWorkerUpdaterChild* aActor)
{
delete aActor;
return true;
}
} // namespace workers
} // namespace dom
} // namespace mozilla

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

@ -46,6 +46,13 @@ public:
virtual mozilla::ipc::IPCResult RecvNotifyRemoveAll() override;
virtual PServiceWorkerUpdaterChild*
AllocPServiceWorkerUpdaterChild(const OriginAttributes& originAttributes,
const nsCString& scope) override;
virtual bool
DeallocPServiceWorkerUpdaterChild(PServiceWorkerUpdaterChild* aActor) override;
private:
ServiceWorkerManagerChild()
: mShuttingDown(false)

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

@ -6,6 +6,7 @@
#include "ServiceWorkerManagerParent.h"
#include "ServiceWorkerManagerService.h"
#include "ServiceWorkerUpdaterParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/ipc/BackgroundParent.h"
@ -310,6 +311,38 @@ ServiceWorkerManagerParent::RecvShutdown()
return IPC_OK();
}
PServiceWorkerUpdaterParent*
ServiceWorkerManagerParent::AllocPServiceWorkerUpdaterParent(const OriginAttributes& aOriginAttributes,
const nsCString& aScope)
{
AssertIsOnBackgroundThread();
return new ServiceWorkerUpdaterParent();
}
mozilla::ipc::IPCResult
ServiceWorkerManagerParent::RecvPServiceWorkerUpdaterConstructor(PServiceWorkerUpdaterParent* aActor,
const OriginAttributes& aOriginAttributes,
const nsCString& aScope)
{
AssertIsOnBackgroundThread();
if (NS_WARN_IF(!mService)) {
return IPC_FAIL_NO_REASON(this);
}
mService->ProcessUpdaterActor(static_cast<ServiceWorkerUpdaterParent*>(aActor),
aOriginAttributes, aScope, mID);
return IPC_OK();
}
bool
ServiceWorkerManagerParent::DeallocPServiceWorkerUpdaterParent(PServiceWorkerUpdaterParent* aActor)
{
AssertIsOnBackgroundThread();
delete aActor;
return true;
}
void
ServiceWorkerManagerParent::ActorDestroy(ActorDestroyReason aWhy)
{

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

@ -56,6 +56,18 @@ private:
virtual mozilla::ipc::IPCResult RecvShutdown() override;
virtual PServiceWorkerUpdaterParent*
AllocPServiceWorkerUpdaterParent(const OriginAttributes& aOriginAttributes,
const nsCString& aScope) override;
virtual mozilla::ipc::IPCResult
RecvPServiceWorkerUpdaterConstructor(PServiceWorkerUpdaterParent* aActor,
const OriginAttributes& aOriginAttributes,
const nsCString& aScope) override;
virtual bool
DeallocPServiceWorkerUpdaterParent(PServiceWorkerUpdaterParent* aActor) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
RefPtr<ServiceWorkerManagerService> mService;

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

@ -7,6 +7,7 @@
#include "ServiceWorkerManagerService.h"
#include "ServiceWorkerManagerParent.h"
#include "ServiceWorkerRegistrar.h"
#include "ServiceWorkerUpdaterParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/Unused.h"
@ -232,6 +233,51 @@ ServiceWorkerManagerService::PropagateRemoveAll(uint64_t aParentID)
#endif
}
void
ServiceWorkerManagerService::ProcessUpdaterActor(ServiceWorkerUpdaterParent* aActor,
const OriginAttributes& aOriginAttributes,
const nsACString& aScope,
uint64_t aParentId)
{
AssertIsOnBackgroundThread();
nsAutoCString suffix;
aOriginAttributes.CreateSuffix(suffix);
nsCString scope(aScope);
scope.Append(suffix);
for (uint32_t i = 0; i < mPendingUpdaterActors.Length(); ++i) {
// We already have an actor doing this update on another process.
if (mPendingUpdaterActors[i].mScope.Equals(scope) &&
mPendingUpdaterActors[i].mParentId != aParentId) {
Unused << aActor->SendProceed(false);
return;
}
}
if (aActor->Proceed(this)) {
PendingUpdaterActor* pua = mPendingUpdaterActors.AppendElement();
pua->mActor = aActor;
pua->mScope = scope;
pua->mParentId = aParentId;
}
}
void
ServiceWorkerManagerService::UpdaterActorDestroyed(ServiceWorkerUpdaterParent* aActor)
{
for (uint32_t i = 0; i < mPendingUpdaterActors.Length(); ++i) {
// We already have an actor doing the update for this scope.
if (mPendingUpdaterActors[i].mActor == aActor) {
mPendingUpdaterActors.RemoveElementAt(i);
return;
}
}
MOZ_CRASH("The actor should be found");
}
} // namespace workers
} // namespace dom
} // namespace mozilla

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

@ -26,6 +26,7 @@ class ServiceWorkerRegistrationData;
namespace workers {
class ServiceWorkerManagerParent;
class ServiceWorkerUpdaterParent;
class ServiceWorkerManagerService final
{
@ -53,11 +54,27 @@ public:
void PropagateRemoveAll(uint64_t aParentID);
void ProcessUpdaterActor(ServiceWorkerUpdaterParent* aActor,
const OriginAttributes& aOriginAttributes,
const nsACString& aScope,
uint64_t aParentID);
void UpdaterActorDestroyed(ServiceWorkerUpdaterParent* aActor);
private:
ServiceWorkerManagerService();
~ServiceWorkerManagerService();
nsTHashtable<nsPtrHashKey<ServiceWorkerManagerParent>> mAgents;
struct PendingUpdaterActor
{
nsCString mScope;
ServiceWorkerUpdaterParent* mActor;
uint64_t mParentId;
};
nsTArray<PendingUpdaterActor> mPendingUpdaterActors;
};
} // namespace workers

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

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ServiceWorkerUpdaterChild.h"
namespace mozilla {
namespace dom {
namespace workers {
ServiceWorkerUpdaterChild::ServiceWorkerUpdaterChild(Runnable* aSuccessRunnable,
Runnable* aFailureRunnable)
: mSuccessRunnable(aSuccessRunnable)
, mFailureRunnable(aFailureRunnable)
{
MOZ_ASSERT(aSuccessRunnable);
}
mozilla::ipc::IPCResult
ServiceWorkerUpdaterChild::RecvProceed(const bool& aAllowed)
{
if (aAllowed) {
mSuccessRunnable->Run();
} else if (mFailureRunnable) {
mFailureRunnable->Run();
}
Unused << Send__delete__(this);
return IPC_OK();
}
} // namespace workers
} // namespace dom
} // namespace mozilla

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

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_ServiceWorkerUpdaterChild_h
#define mozilla_dom_ServiceWorkerUpdaterChild_h
#include "mozilla/dom/PServiceWorkerUpdaterChild.h"
#include "mozilla/BasePrincipal.h"
namespace mozilla {
namespace dom {
namespace workers {
class ServiceWorkerUpdaterChild final : public PServiceWorkerUpdaterChild
{
public:
ServiceWorkerUpdaterChild(Runnable* aSuccessRunnable,
Runnable* aFailureRunnable);
mozilla::ipc::IPCResult
RecvProceed(const bool& aAllowed) override;
private:
RefPtr<Runnable> mSuccessRunnable;
RefPtr<Runnable> mFailureRunnable;
};
} // namespace workers
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ServiceWorkerUpdaterChild_h

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

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ServiceWorkerUpdaterParent.h"
#include "ServiceWorkerManagerService.h"
namespace mozilla {
namespace dom {
namespace workers {
bool
ServiceWorkerUpdaterParent::Proceed(ServiceWorkerManagerService* aService)
{
if (!SendProceed(true)) {
return false;
}
mService = aService;
return true;
}
void
ServiceWorkerUpdaterParent::ActorDestroy(ActorDestroyReason aWhy)
{
if (mService) {
mService->UpdaterActorDestroyed(this);
}
}
} // namespace workers
} // namespace dom
} // namespace mozilla

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

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_ServiceWorkerUpdaterParent_h
#define mozilla_dom_ServiceWorkerUpdaterParent_h
#include "mozilla/dom/PServiceWorkerUpdaterParent.h"
#include "mozilla/BasePrincipal.h"
namespace mozilla {
namespace dom {
namespace workers {
class ServiceWorkerManagerService;
class ServiceWorkerUpdaterParent final : public PServiceWorkerUpdaterParent
{
public:
void
ActorDestroy(ActorDestroyReason aWhy) override;
bool
Proceed(ServiceWorkerManagerService* aService);
private:
RefPtr<ServiceWorkerManagerService> mService;
};
} // namespace workers
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_ServiceWorkerUpdaterParent_h

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

@ -76,6 +76,8 @@ UNIFIED_SOURCES += [
'ServiceWorkerScriptCache.cpp',
'ServiceWorkerUnregisterJob.cpp',
'ServiceWorkerUpdateJob.cpp',
'ServiceWorkerUpdaterChild.cpp',
'ServiceWorkerUpdaterParent.cpp',
'ServiceWorkerWindowClient.cpp',
'SharedWorker.cpp',
'WorkerDebuggerManager.cpp',
@ -90,6 +92,7 @@ UNIFIED_SOURCES += [
IPDL_SOURCES += [
'PServiceWorkerManager.ipdl',
'PServiceWorkerUpdater.ipdl',
'ServiceWorkerRegistrarTypes.ipdlh',
]