Bug 1226441 - Part 2: Delay functional event dispatch until service worker is activated; r=catalinb

This commit is contained in:
Ben Kelly 2015-11-24 10:47:59 -05:00 committed by Ehsan Akhgari
parent da0a7ade62
commit 2f321a65d7
4 changed files with 76 additions and 0 deletions

View File

@ -4601,6 +4601,7 @@ private:
void
ServiceWorkerInfo::UpdateState(ServiceWorkerState aState)
{
AssertIsOnMainThread();
#ifdef DEBUG
// Any state can directly transition to redundant, but everything else is
// ordered.
@ -4613,6 +4614,13 @@ ServiceWorkerInfo::UpdateState(ServiceWorkerState aState)
// Activated can only go to redundant.
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
#endif
// Flush any pending functional events to the worker when it transitions to the
// activated state.
// TODO: Do we care that these events will race with the propagation of the
// state change?
if (aState == ServiceWorkerState::Activated && mState != aState) {
mServiceWorkerPrivate->Activated();
}
mState = aState;
nsCOMPtr<nsIRunnable> r = new ChangeStateUpdater(mInstances, mState);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r.forget())));

View File

@ -283,6 +283,7 @@ public:
void
SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState)
{
AssertIsOnMainThread();
mState = aState;
}

View File

@ -590,6 +590,14 @@ ServiceWorkerPrivate::SendPushEvent(const Maybe<nsTArray<uint8_t>>& aData,
mKeepAliveToken,
aData,
regInfo);
if (mInfo->State() == ServiceWorkerState::Activating) {
mPendingFunctionalEvents.AppendElement(r.forget());
return NS_OK;
}
MOZ_ASSERT(mInfo->State() == ServiceWorkerState::Activated);
AutoJSAPI jsapi;
jsapi.Init();
if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
@ -1057,6 +1065,17 @@ public:
return DispatchFetchEvent(aCx, aWorkerPrivate);
}
NS_IMETHOD
Cancel() override
{
nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n");
}
WorkerRunnable::Cancel();
return NS_OK;
}
private:
~FetchEventRunnable() {}
@ -1246,6 +1265,8 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
bool aIsReload)
{
AssertIsOnMainThread();
// if the ServiceWorker script fails to load for some reason, just resume
// the original channel.
nsCOMPtr<nsIRunnable> failRunnable =
@ -1279,6 +1300,13 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
return rv;
}
if (mInfo->State() == ServiceWorkerState::Activating) {
mPendingFunctionalEvents.AppendElement(r.forget());
return NS_OK;
}
MOZ_ASSERT(mInfo->State() == ServiceWorkerState::Activated);
AutoJSAPI jsapi;
jsapi.Init();
if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
@ -1425,6 +1453,15 @@ ServiceWorkerPrivate::TerminateWorker()
NS_WARN_IF(!mWorkerPrivate->Terminate(jsapi.cx()));
mWorkerPrivate = nullptr;
mSupportsArray.Clear();
// Any pending events are never going to fire on this worker. Cancel
// them so that intercepted channels can be reset and other resources
// cleaned up.
nsTArray<RefPtr<WorkerRunnable>> pendingEvents;
mPendingFunctionalEvents.SwapElements(pendingEvents);
for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
pendingEvents[i]->Cancel();
}
}
}
@ -1447,6 +1484,29 @@ ServiceWorkerPrivate::NoteStoppedControllingDocuments()
TerminateWorker();
}
void
ServiceWorkerPrivate::Activated()
{
AssertIsOnMainThread();
// If we had to queue up events due to the worker activating, that means
// the worker must be currently running. We should be called synchronously
// when the worker becomes activated.
MOZ_ASSERT_IF(!mPendingFunctionalEvents.IsEmpty(), mWorkerPrivate);
nsTArray<RefPtr<WorkerRunnable>> pendingEvents;
mPendingFunctionalEvents.SwapElements(pendingEvents);
for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
RefPtr<WorkerRunnable> r = pendingEvents[i].forget();
AutoJSAPI jsapi;
jsapi.Init();
if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
NS_WARNING("Failed to dispatch pending functional event!");
}
}
}
/* static */ void
ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate)
{

View File

@ -134,6 +134,9 @@ public:
return mWorkerPrivate;
}
void
Activated();
private:
enum WakeUpReason {
FetchEvent = 0,
@ -197,6 +200,10 @@ private:
// |StoreISupports| and |RemoveISupports|. Note that the array is also
// cleared whenever the worker is terminated.
nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
// Array of function event worker runnables that are pending due to
// the worker activating. Main thread only.
nsTArray<RefPtr<WorkerRunnable>> mPendingFunctionalEvents;
};
} // namespace workers