Bug 1462772 P4 Route ServiceWorker state changes through the ServiceWorkerRegistration. r=mrbkap

This commit is contained in:
Ben Kelly 2018-07-02 07:44:18 -07:00
parent f3b6e1d527
commit aa573bca77
9 changed files with 129 additions and 39 deletions

View File

@ -12,6 +12,7 @@
#include "ServiceWorkerImpl.h"
#include "ServiceWorkerManager.h"
#include "ServiceWorkerPrivate.h"
#include "ServiceWorkerRegistration.h"
#include "mozilla/dom/DOMPrefs.h"
#include "mozilla/dom/ClientIPCTypes.h"
@ -74,6 +75,7 @@ ServiceWorker::ServiceWorker(nsIGlobalObject* aGlobal,
: DOMEventTargetHelper(aGlobal)
, mDescriptor(aDescriptor)
, mInner(aInner)
, mLastNotifiedState(ServiceWorkerState::Installing)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_DIAGNOSTIC_ASSERT(aGlobal);
@ -147,18 +149,24 @@ ServiceWorker::State() const
void
ServiceWorker::SetState(ServiceWorkerState aState)
{
ServiceWorkerState oldState = mDescriptor.State();
NS_ENSURE_TRUE_VOID(aState >= mDescriptor.State());
mDescriptor.SetState(aState);
if (oldState == aState) {
}
void
ServiceWorker::MaybeDispatchStateChangeEvent()
{
if (mDescriptor.State() <= mLastNotifiedState || !GetParentObject()) {
return;
}
mLastNotifiedState = mDescriptor.State();
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
// Once we have transitioned to the redundant state then no
// more statechange events will occur. We can allow the DOM
// object to GC if script is not holding it alive.
if (mDescriptor.State() == ServiceWorkerState::Redundant) {
if (mLastNotifiedState == ServiceWorkerState::Redundant) {
IgnoreKeepAliveIfHasListenersFor(NS_LITERAL_STRING("statechange"));
}
}
@ -236,6 +244,14 @@ ServiceWorker::MaybeAttachToRegistration(ServiceWorkerRegistration* aRegistratio
MOZ_DIAGNOSTIC_ASSERT(aRegistration);
MOZ_DIAGNOSTIC_ASSERT(!mRegistration);
// If the registration no longer actually references this ServiceWorker
// then we must be in the redundant state.
if (!aRegistration->Descriptor().HasWorker(mDescriptor)) {
SetState(ServiceWorkerState::Redundant);
MaybeDispatchStateChangeEvent();
return;
}
mRegistration = aRegistration;
}

View File

@ -89,6 +89,9 @@ public:
void
SetState(ServiceWorkerState aState);
void
MaybeDispatchStateChangeEvent();
void
GetScriptURL(nsString& aURL) const;
@ -117,6 +120,7 @@ private:
RefPtr<Inner> mInner;
RefPtr<ServiceWorkerRegistration> mRegistration;
ServiceWorkerState mLastNotifiedState;
};
NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorker, NS_DOM_SERVICEWORKER_IID)

View File

@ -12,7 +12,6 @@ namespace dom {
ServiceWorkerImpl::~ServiceWorkerImpl()
{
MOZ_DIAGNOSTIC_ASSERT(!mOuter);
mInfo->RemoveListener(this);
}
void
@ -21,11 +20,6 @@ ServiceWorkerImpl::AddServiceWorker(ServiceWorker* aWorker)
MOZ_DIAGNOSTIC_ASSERT(!mOuter);
MOZ_DIAGNOSTIC_ASSERT(aWorker);
mOuter = aWorker;
// Wait to attach to the info as a listener until we have the outer
// set. This is important because the info will try to set the
// state immediately.
mInfo->AddListener(this);
}
void

View File

@ -140,20 +140,71 @@ ServiceWorkerRegistration::UpdateState(const ServiceWorkerRegistrationDescriptor
nsCOMPtr<nsIGlobalObject> global = GetParentObject();
// Move the currently exposed workers into a separate list
// of "old" workers. We will then potentially add them
// back to the registration properties below based on the
// given descriptor. Any that are not restored will need
// to be moved to the redundant state.
AutoTArray<RefPtr<ServiceWorker>, 3> oldWorkerList({
mInstallingWorker.forget(),
mWaitingWorker.forget(),
mActiveWorker.forget(),
});
// Its important that all state changes are actually applied before
// dispatching any statechange events. Each ServiceWorker object
// should be in the correct state and the ServiceWorkerRegistration
// properties need to be set correctly as well. To accomplish this
// we use a ScopeExit to dispatch any statechange events.
auto scopeExit = MakeScopeExit([&] {
// Check to see if any of the "old" workers was completely discarded.
// Set these workers to the redundant state.
for (auto& oldWorker : oldWorkerList) {
if (!oldWorker ||
oldWorker == mInstallingWorker ||
oldWorker == mWaitingWorker ||
oldWorker == mActiveWorker) {
continue;
}
oldWorker->SetState(ServiceWorkerState::Redundant);
}
// Check each worker to see if it needs a statechange event dispatched.
if (mInstallingWorker) {
mInstallingWorker->MaybeDispatchStateChangeEvent();
}
if (mWaitingWorker) {
mWaitingWorker->MaybeDispatchStateChangeEvent();
}
if (mActiveWorker) {
mActiveWorker->MaybeDispatchStateChangeEvent();
}
// We also check the "old" workers to see if they need a statechange
// event as well. Note, these may overlap with the known worker properties
// above, but MaybeDispatchStateChangeEvent() will ignore duplicated calls.
for (auto& oldWorker : oldWorkerList) {
if (!oldWorker) {
continue;
}
oldWorker->MaybeDispatchStateChangeEvent();
}
});
// Clear all workers if the registration has been detached from the global.
// Also, we cannot expose ServiceWorker objects on worker threads yet, so
// do the same on when off-main-thread. This main thread check should be
// removed as part of bug 1113522.
if (!global || !NS_IsMainThread()) {
mInstallingWorker = nullptr;
mWaitingWorker = nullptr;
mActiveWorker = nullptr;
return;
}
Maybe<ServiceWorkerDescriptor> active = aDescriptor.GetActive();
if (active.isSome()) {
mActiveWorker = global->GetOrCreateServiceWorker(active.ref());
mActiveWorker->SetState(active.ref().State());
} else {
mActiveWorker = nullptr;
}
@ -161,6 +212,7 @@ ServiceWorkerRegistration::UpdateState(const ServiceWorkerRegistrationDescriptor
Maybe<ServiceWorkerDescriptor> waiting = aDescriptor.GetWaiting();
if (waiting.isSome()) {
mWaitingWorker = global->GetOrCreateServiceWorker(waiting.ref());
mWaitingWorker->SetState(waiting.ref().State());
} else {
mWaitingWorker = nullptr;
}
@ -168,6 +220,7 @@ ServiceWorkerRegistration::UpdateState(const ServiceWorkerRegistrationDescriptor
Maybe<ServiceWorkerDescriptor> installing = aDescriptor.GetInstalling();
if (installing.isSome()) {
mInstallingWorker = global->GetOrCreateServiceWorker(installing.ref());
mInstallingWorker->SetState(installing.ref().State());
} else {
mInstallingWorker = nullptr;
}
@ -345,5 +398,11 @@ ServiceWorkerRegistration::GetNotifications(const GetNotificationOptions& aOptio
return Notification::WorkerGet(worker, aOptions, scope, aRv);
}
const ServiceWorkerRegistrationDescriptor&
ServiceWorkerRegistration::Descriptor() const
{
return mDescriptor;
}
} // dom namespace
} // mozilla namespace

View File

@ -116,6 +116,9 @@ public:
GetNotifications(const GetNotificationOptions& aOptions,
ErrorResult& aRv);
const ServiceWorkerRegistrationDescriptor&
Descriptor() const;
private:
ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
const ServiceWorkerRegistrationDescriptor& aDescriptor,

View File

@ -197,6 +197,17 @@ ServiceWorkerRegistrationDescriptor::Newest() const
return result;
}
bool
ServiceWorkerRegistrationDescriptor::HasWorker(const ServiceWorkerDescriptor& aDescriptor) const
{
Maybe<ServiceWorkerDescriptor> installing = GetInstalling();
Maybe<ServiceWorkerDescriptor> waiting = GetWaiting();
Maybe<ServiceWorkerDescriptor> active = GetActive();
return (installing.isSome() && installing.ref().Matches(aDescriptor)) ||
(waiting.isSome() && waiting.ref().Matches(aDescriptor)) ||
(active.isSome() && active.ref().Matches(aDescriptor));
}
namespace {
bool

View File

@ -91,6 +91,9 @@ public:
Maybe<ServiceWorkerDescriptor>
Newest() const;
bool
HasWorker(const ServiceWorkerDescriptor& aDescriptor) const;
bool
IsValid() const;

View File

@ -105,8 +105,23 @@ ServiceWorkerRegistrationMainThread::UpdateFound()
void
ServiceWorkerRegistrationMainThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
{
mDescriptor = aDescriptor;
mOuter->UpdateState(aDescriptor);
NS_ENSURE_TRUE_VOID(mOuter);
nsIGlobalObject* global = mOuter->GetParentObject();
NS_ENSURE_TRUE_VOID(global);
RefPtr<ServiceWorkerRegistrationMainThread> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"ServiceWorkerRegistrationMainThread::UpdateState",
[self, desc = std::move(aDescriptor)] () mutable {
self->mDescriptor = std::move(desc);
NS_ENSURE_TRUE_VOID(self->mOuter);
self->mOuter->UpdateState(self->mDescriptor);
});
Unused <<
global->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget(),
NS_DISPATCH_NORMAL);
}
void

View File

@ -56,8 +56,6 @@ ServiceWorkerRegistrationInfo::Clear()
RefPtr<ServiceWorkerInfo> waiting = mWaitingWorker.forget();
RefPtr<ServiceWorkerInfo> active = mActiveWorker.forget();
UpdateRegistrationState();
if (installing) {
installing->UpdateState(ServiceWorkerState::Redundant);
installing->UpdateRedundantTime();
@ -77,6 +75,7 @@ ServiceWorkerRegistrationInfo::Clear()
active->WorkerPrivate()->NoteDeadServiceWorkerInfo();
}
UpdateRegistrationState();
NotifyChromeRegistrationListeners();
}
@ -366,6 +365,7 @@ ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
UpdateRegistrationState();
NotifyChromeRegistrationListeners();
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
@ -418,16 +418,10 @@ ServiceWorkerRegistrationInfo::UpdateRegistrationState()
mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
RefPtr<ServiceWorkerRegistrationInfo> self(this);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
"ServiceWorkerRegistrationInfo::UpdateRegistrationState",
[self = std::move(self)] {
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (swm) {
swm->UpdateRegistrationListeners(self);
}
});
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
NS_ENSURE_TRUE_VOID(swm);
swm->UpdateRegistrationListeners(this);
}
void
@ -571,12 +565,10 @@ ServiceWorkerRegistrationInfo::ClearInstalling()
}
RefPtr<ServiceWorkerInfo> installing = mInstallingWorker.forget();
UpdateRegistrationState();
installing->UpdateState(ServiceWorkerState::Redundant);
installing->UpdateRedundantTime();
UpdateRegistrationState();
NotifyChromeRegistrationListeners();
}
@ -588,11 +580,9 @@ ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling()
MOZ_ASSERT(!mInstallingWorker);
mInstallingWorker = mEvaluatingWorker.forget();
UpdateRegistrationState();
mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
UpdateRegistrationState();
NotifyChromeRegistrationListeners();
}
@ -609,12 +599,10 @@ ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting()
}
mWaitingWorker = mInstallingWorker.forget();
UpdateRegistrationState();
mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
mWaitingWorker->UpdateInstalledTime();
UpdateRegistrationState();
NotifyChromeRegistrationListeners();
// TODO: When bug 1426401 is implemented we will need to call
@ -650,7 +638,6 @@ ServiceWorkerRegistrationInfo::SetActive(ServiceWorkerInfo* aServiceWorker)
// We don't need to update activated time when we load registration from
// registrar.
UpdateRegistrationState();
NotifyChromeRegistrationListeners();
}
@ -669,9 +656,6 @@ ServiceWorkerRegistrationInfo::TransitionWaitingToActive()
// We are transitioning from waiting to active normally, so go to
// the activating state.
mActiveWorker = mWaitingWorker.forget();
UpdateRegistrationState();
mActiveWorker->UpdateState(ServiceWorkerState::Activating);
nsCOMPtr<nsIRunnable> r =
@ -684,6 +668,7 @@ ServiceWorkerRegistrationInfo::TransitionWaitingToActive()
});
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
UpdateRegistrationState();
NotifyChromeRegistrationListeners();
}