Bug 1256428 P1 Add ServiceWorkerJob2 base class. r=jdm

This commit is contained in:
Ben Kelly 2016-04-06 13:27:22 -07:00
Родитель 0ff5ceefd5
Коммит 9aff747125
4 изменённых файлов: 369 добавлений и 0 удалений

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

@ -0,0 +1,218 @@
/* -*- 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 "ServiceWorkerJob.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "Workers.h"
namespace mozilla {
namespace dom {
namespace workers {
ServiceWorkerJob2::Type
ServiceWorkerJob2::GetType() const
{
return mType;
}
ServiceWorkerJob2::State
ServiceWorkerJob2::GetState() const
{
return mState;
}
bool
ServiceWorkerJob2::Canceled() const
{
return mCanceled;
}
bool
ServiceWorkerJob2::IsEquivalentTo(ServiceWorkerJob2* aJob) const
{
AssertIsOnMainThread();
MOZ_ASSERT(aJob);
return mType == aJob->mType &&
mScope.Equals(aJob->mScope) &&
mScriptSpec.Equals(aJob->mScriptSpec) &&
mPrincipal->Equals(aJob->mPrincipal);
}
void
ServiceWorkerJob2::AppendResultCallback(Callback* aCallback)
{
AssertIsOnMainThread();
MOZ_ASSERT(mState != State::Finished);
MOZ_ASSERT(aCallback);
MOZ_ASSERT(mFinalCallback != aCallback);
MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
// TODO: handle the case where InvokeResultCallback() has already been called
mResultCallbackList.AppendElement(aCallback);
}
void
ServiceWorkerJob2::StealResultCallbacksFrom(ServiceWorkerJob2* aJob)
{
AssertIsOnMainThread();
MOZ_ASSERT(aJob);
MOZ_ASSERT(aJob->mState == State::Initial);
// Take the callbacks from the other job immediately to avoid the
// any possibility of them existing on both jobs at once.
nsTArray<RefPtr<Callback>> callbackList;
callbackList.SwapElements(aJob->mResultCallbackList);
for (RefPtr<Callback>& callback : callbackList) {
// Use AppendResultCallback() so that assertion checking is performed on
// each callback.
AppendResultCallback(callback);
}
}
void
ServiceWorkerJob2::Start(Callback* aFinalCallback)
{
AssertIsOnMainThread();
MOZ_ASSERT(!mCanceled);
MOZ_ASSERT(aFinalCallback);
MOZ_ASSERT(!mFinalCallback);
MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
mFinalCallback = aFinalCallback;
MOZ_ASSERT(mState == State::Initial);
mState = State::Started;
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &ServiceWorkerJob2::AsyncExecute);
// We may have to wait for the PBackground actor to be initialized
// before proceeding. We should always be able to get a ServiceWorkerManager,
// however, since Start() should not be called during shutdown.
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm->HasBackgroundActor()) {
swm->AppendPendingOperation(runnable);
return;
}
// Otherwise start asynchronously. We should never run a job synchronously.
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
NS_DispatchToMainThread(runnable.forget())));
}
void
ServiceWorkerJob2::Cancel()
{
AssertIsOnMainThread();
MOZ_ASSERT(!mCanceled);
mCanceled = true;
}
ServiceWorkerJob2::ServiceWorkerJob2(Type aType,
nsIPrincipal* aPrincipal,
const nsACString& aScope,
const nsACString& aScriptSpec)
: mType(aType)
, mPrincipal(aPrincipal)
, mScope(aScope)
, mScriptSpec(aScriptSpec)
, mState(State::Initial)
, mCanceled(false)
{
AssertIsOnMainThread();
MOZ_ASSERT(mPrincipal);
MOZ_ASSERT(!mScope.IsEmpty());
// Some job types may have an empty script spec
}
ServiceWorkerJob2::~ServiceWorkerJob2()
{
AssertIsOnMainThread();
// Jobs must finish or never be started. Destroying an actively running
// job is an error.
MOZ_ASSERT(mState != State::Started);
}
void
ServiceWorkerJob2::InvokeResultCallbacks(ErrorResult& aRv)
{
AssertIsOnMainThread();
MOZ_ASSERT(mState == State::Started);
nsTArray<RefPtr<Callback>> callbackList;
callbackList.SwapElements(mResultCallbackList);
for (RefPtr<Callback>& callback : callbackList) {
// The callback might consume an exception on the ErrorResult, so we need
// to clone in order to maintain the error for the next callback.
ErrorResult rv;
aRv.CloneTo(rv);
callback->JobFinished(this, rv);
// The callback might not consume the error.
rv.SuppressException();
}
}
void
ServiceWorkerJob2::InvokeResultCallbacks(nsresult aRv)
{
ErrorResult converted(aRv);
InvokeResultCallbacks(converted);
}
void
ServiceWorkerJob2::Finish(ErrorResult& aRv)
{
AssertIsOnMainThread();
MOZ_ASSERT(mState == State::Started);
// Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to script.
if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
!aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR) &&
!aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {
// Remove the old error code so we can replace it with a TypeError.
aRv.SuppressException();
NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
NS_ConvertUTF8toUTF16 scope(mScope);
// Throw the type error with a generic error message.
aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
}
// The final callback may drop the last ref to this object.
RefPtr<ServiceWorkerJob2> kungFuDeathGrip = this;
InvokeResultCallbacks(aRv);
mState = State::Finished;
mFinalCallback->JobFinished(this, aRv);
mFinalCallback = nullptr;
// The callback might not consume the error.
aRv.SuppressException();
// Async release this object to ensure that our caller methods complete
// as well.
NS_ReleaseOnMainThread(kungFuDeathGrip.forget(), true /* always proxy */);
}
void
ServiceWorkerJob2::Finish(nsresult aRv)
{
ErrorResult converted(aRv);
Finish(converted);
}
} // namespace workers
} // namespace dom
} // namespace mozilla

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

@ -0,0 +1,149 @@
/* -*- 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_workers_serviceworkerjob_h
#define mozilla_dom_workers_serviceworkerjob_h
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsTArray.h"
class nsIPrincipal;
namespace mozilla {
class ErrorResult;
namespace dom {
namespace workers {
class ServiceWorkerJob2
{
public:
// Implement this interface to receive notification when a job completes.
class Callback
{
public:
// Called once when the job completes. If the job is started, then this
// will be called. If a job is never executed due to browser shutdown,
// then this method will never be called. This method is always called
// on the main thread asynchronously after Start() completes.
virtual void JobFinished(ServiceWorkerJob2* aJob, ErrorResult& aStatus) = 0;
NS_IMETHOD_(MozExternalRefCountType)
AddRef(void) = 0;
NS_IMETHOD_(MozExternalRefCountType)
Release(void) = 0;
};
enum class Type
{
Register,
Update,
Unregister
};
enum class State
{
Initial,
Started,
Finished
};
Type
GetType() const;
State
GetState() const;
// Determine if the job has been canceled. This does not change the
// current State, but indicates that the job should progress to Finished
// as soon as possible.
bool
Canceled() const;
bool
IsEquivalentTo(ServiceWorkerJob2* aJob) const;
// Add a callback that will be invoked when the job's result is available.
// Some job types will invoke this before the job is actually finished.
// If an early callback does not occur, then it will be called automatically
// when Finish() is called. These callbacks will be invoked while the job
// state is Started.
void
AppendResultCallback(Callback* aCallback);
// This takes ownership of any result callbacks associated with the given job
// and then appends them to this job's callback list.
void
StealResultCallbacksFrom(ServiceWorkerJob2* aJob);
// Start the job. All work will be performed asynchronously on
// the main thread. The Finish() method must be called exactly
// once after this point. A final callback must be provided. It
// will be invoked after all other callbacks have been processed.
void
Start(Callback* aFinalCallback);
// Set an internal flag indicating that a started job should finish as
// soon as possible.
void
Cancel();
protected:
ServiceWorkerJob2(Type aType,
nsIPrincipal* aPrincipal,
const nsACString& aScope,
const nsACString& aScriptSpec);
virtual ~ServiceWorkerJob2();
// Invoke the result callbacks immediately. The job must be in the
// Started state. The callbacks are cleared after being invoked,
// so subsequent method calls have no effect.
void
InvokeResultCallbacks(ErrorResult& aRv);
// Convenience method that converts to ErrorResult and calls real method.
void
InvokeResultCallbacks(nsresult aRv);
// Indicate that the job has completed. The must be called exactly
// once after Start() has initiated job execution. It may not be
// called until Start() has returned.
void
Finish(ErrorResult& aRv);
// Convenience method that converts to ErrorResult and calls real method.
void
Finish(nsresult aRv);
// Specific job types should define AsyncExecute to begin their work.
// All errors and successes must result in Finish() being called.
virtual void
AsyncExecute() = 0;
const Type mType;
nsCOMPtr<nsIPrincipal> mPrincipal;
const nsCString mScope;
const nsCString mScriptSpec;
private:
RefPtr<Callback> mFinalCallback;
nsTArray<RefPtr<Callback>> mResultCallbackList;
State mState;
bool mCanceled;
public:
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJob2)
};
} // namespace workers
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_workers_serviceworkerjob_h

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

@ -330,6 +330,7 @@ class ServiceWorkerManager final
friend class ServiceWorkerJobQueue;
friend class ServiceWorkerInstallJob;
friend class ServiceWorkerRegisterJob;
friend class ServiceWorkerJob2;
friend class ServiceWorkerJobBase;
friend class ServiceWorkerScriptJobBase;
friend class ServiceWorkerRegistrationInfo;

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

@ -66,6 +66,7 @@ UNIFIED_SOURCES += [
'ServiceWorkerClients.cpp',
'ServiceWorkerContainer.cpp',
'ServiceWorkerEvents.cpp',
'ServiceWorkerJob.cpp',
'ServiceWorkerManager.cpp',
'ServiceWorkerManagerChild.cpp',
'ServiceWorkerManagerParent.cpp',