зеркало из https://github.com/mozilla/gecko-dev.git
247 строки
6.5 KiB
C++
247 строки
6.5 KiB
C++
/* -*- 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 {
|
|
|
|
ServiceWorkerJob::Type
|
|
ServiceWorkerJob::GetType() const
|
|
{
|
|
return mType;
|
|
}
|
|
|
|
ServiceWorkerJob::State
|
|
ServiceWorkerJob::GetState() const
|
|
{
|
|
return mState;
|
|
}
|
|
|
|
bool
|
|
ServiceWorkerJob::Canceled() const
|
|
{
|
|
return mCanceled;
|
|
}
|
|
|
|
bool
|
|
ServiceWorkerJob::ResultCallbacksInvoked() const
|
|
{
|
|
return mResultCallbacksInvoked;
|
|
}
|
|
|
|
bool
|
|
ServiceWorkerJob::IsEquivalentTo(ServiceWorkerJob* aJob) const
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(aJob);
|
|
return mType == aJob->mType &&
|
|
mScope.Equals(aJob->mScope) &&
|
|
mScriptSpec.Equals(aJob->mScriptSpec) &&
|
|
mPrincipal->Equals(aJob->mPrincipal);
|
|
}
|
|
|
|
void
|
|
ServiceWorkerJob::AppendResultCallback(Callback* aCallback)
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
|
|
MOZ_DIAGNOSTIC_ASSERT(aCallback);
|
|
MOZ_DIAGNOSTIC_ASSERT(mFinalCallback != aCallback);
|
|
MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
|
|
MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
|
|
mResultCallbackList.AppendElement(aCallback);
|
|
}
|
|
|
|
void
|
|
ServiceWorkerJob::StealResultCallbacksFrom(ServiceWorkerJob* 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
|
|
ServiceWorkerJob::Start(Callback* aFinalCallback)
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(aFinalCallback);
|
|
MOZ_DIAGNOSTIC_ASSERT(!mFinalCallback);
|
|
MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
|
|
mFinalCallback = aFinalCallback;
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mState == State::Initial);
|
|
mState = State::Started;
|
|
|
|
nsCOMPtr<nsIRunnable> runnable =
|
|
NewRunnableMethod(this, &ServiceWorkerJob::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) {
|
|
// browser shutdown
|
|
return;
|
|
}
|
|
if (!swm->HasBackgroundActor()) {
|
|
// waiting to initialize
|
|
swm->AppendPendingOperation(runnable);
|
|
return;
|
|
}
|
|
|
|
// Otherwise start asynchronously. We should never run a job synchronously.
|
|
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
|
NS_DispatchToMainThread(runnable.forget())));
|
|
}
|
|
|
|
void
|
|
ServiceWorkerJob::Cancel()
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(!mCanceled);
|
|
mCanceled = true;
|
|
}
|
|
|
|
ServiceWorkerJob::ServiceWorkerJob(Type aType,
|
|
nsIPrincipal* aPrincipal,
|
|
const nsACString& aScope,
|
|
const nsACString& aScriptSpec)
|
|
: mType(aType)
|
|
, mPrincipal(aPrincipal)
|
|
, mScope(aScope)
|
|
, mScriptSpec(aScriptSpec)
|
|
, mState(State::Initial)
|
|
, mCanceled(false)
|
|
, mResultCallbacksInvoked(false)
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(mPrincipal);
|
|
MOZ_ASSERT(!mScope.IsEmpty());
|
|
// Some job types may have an empty script spec
|
|
}
|
|
|
|
ServiceWorkerJob::~ServiceWorkerJob()
|
|
{
|
|
AssertIsOnMainThread();
|
|
// Jobs must finish or never be started. Destroying an actively running
|
|
// job is an error.
|
|
MOZ_ASSERT(mState != State::Started);
|
|
MOZ_ASSERT_IF(mState == State::Finished, mResultCallbacksInvoked);
|
|
}
|
|
|
|
void
|
|
ServiceWorkerJob::InvokeResultCallbacks(ErrorResult& aRv)
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
|
|
mResultCallbacksInvoked = true;
|
|
|
|
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
|
|
ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv)
|
|
{
|
|
ErrorResult converted(aRv);
|
|
InvokeResultCallbacks(converted);
|
|
}
|
|
|
|
void
|
|
ServiceWorkerJob::Finish(ErrorResult& aRv)
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
// Avoid double-completion because it can result on operating on cleaned
|
|
// up data. This should not happen, though, so also assert to try to
|
|
// narrow down the causes.
|
|
MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
|
|
if (mState != State::Started) {
|
|
return;
|
|
}
|
|
|
|
// 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<ServiceWorkerJob> kungFuDeathGrip = this;
|
|
|
|
if (!mResultCallbacksInvoked) {
|
|
InvokeResultCallbacks(aRv);
|
|
}
|
|
|
|
mState = State::Finished;
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mFinalCallback);
|
|
if (mFinalCallback) {
|
|
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
|
|
ServiceWorkerJob::Finish(nsresult aRv)
|
|
{
|
|
ErrorResult converted(aRv);
|
|
Finish(converted);
|
|
}
|
|
|
|
} // namespace workers
|
|
} // namespace dom
|
|
} // namespace mozilla
|