summaryrefslogtreecommitdiffstats
path: root/dom/workers/ServiceWorkerJob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/ServiceWorkerJob.cpp')
-rw-r--r--dom/workers/ServiceWorkerJob.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/dom/workers/ServiceWorkerJob.cpp b/dom/workers/ServiceWorkerJob.cpp
new file mode 100644
index 000000000..3d0a8e2cd
--- /dev/null
+++ b/dom/workers/ServiceWorkerJob.cpp
@@ -0,0 +1,246 @@
+/* -*- 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