no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / serviceworkers / ServiceWorkerJob.cpp
blob980d48b66ca1322de08f8d4eee1f2988790f4d38
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ServiceWorkerJob.h"
9 #include "mozilla/dom/WorkerCommon.h"
10 #include "nsIPrincipal.h"
11 #include "nsProxyRelease.h"
12 #include "nsThreadUtils.h"
13 #include "ServiceWorkerManager.h"
15 namespace mozilla::dom {
17 ServiceWorkerJob::Type ServiceWorkerJob::GetType() const { return mType; }
19 ServiceWorkerJob::State ServiceWorkerJob::GetState() const { return mState; }
21 bool ServiceWorkerJob::Canceled() const { return mCanceled; }
23 bool ServiceWorkerJob::ResultCallbacksInvoked() const {
24 return mResultCallbacksInvoked;
27 bool ServiceWorkerJob::IsEquivalentTo(ServiceWorkerJob* aJob) const {
28 MOZ_ASSERT(NS_IsMainThread());
29 MOZ_ASSERT(aJob);
30 return mType == aJob->mType && mScope.Equals(aJob->mScope) &&
31 mScriptSpec.Equals(aJob->mScriptSpec) &&
32 mPrincipal->Equals(aJob->mPrincipal);
35 void ServiceWorkerJob::AppendResultCallback(Callback* aCallback) {
36 MOZ_ASSERT(NS_IsMainThread());
37 MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
38 MOZ_DIAGNOSTIC_ASSERT(aCallback);
39 MOZ_DIAGNOSTIC_ASSERT(mFinalCallback != aCallback);
40 MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
41 MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
42 mResultCallbackList.AppendElement(aCallback);
45 void ServiceWorkerJob::StealResultCallbacksFrom(ServiceWorkerJob* aJob) {
46 MOZ_ASSERT(NS_IsMainThread());
47 MOZ_ASSERT(aJob);
48 MOZ_ASSERT(aJob->mState == State::Initial);
50 // Take the callbacks from the other job immediately to avoid the
51 // any possibility of them existing on both jobs at once.
52 nsTArray<RefPtr<Callback>> callbackList =
53 std::move(aJob->mResultCallbackList);
55 for (RefPtr<Callback>& callback : callbackList) {
56 // Use AppendResultCallback() so that assertion checking is performed on
57 // each callback.
58 AppendResultCallback(callback);
62 void ServiceWorkerJob::Start(Callback* aFinalCallback) {
63 MOZ_ASSERT(NS_IsMainThread());
64 MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
66 MOZ_DIAGNOSTIC_ASSERT(aFinalCallback);
67 MOZ_DIAGNOSTIC_ASSERT(!mFinalCallback);
68 MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
69 mFinalCallback = aFinalCallback;
71 MOZ_DIAGNOSTIC_ASSERT(mState == State::Initial);
72 mState = State::Started;
74 nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
75 "ServiceWorkerJob::AsyncExecute", this, &ServiceWorkerJob::AsyncExecute);
77 // We may have to wait for the PBackground actor to be initialized
78 // before proceeding. We should always be able to get a ServiceWorkerManager,
79 // however, since Start() should not be called during shutdown.
80 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
81 if (!swm) {
82 // browser shutdown
83 return;
86 // Otherwise start asynchronously. We should never run a job synchronously.
87 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget())));
90 void ServiceWorkerJob::Cancel() {
91 MOZ_ASSERT(NS_IsMainThread());
92 MOZ_ASSERT(!mCanceled);
93 mCanceled = true;
95 if (GetState() != State::Started) {
96 MOZ_ASSERT(GetState() == State::Initial);
98 ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
99 InvokeResultCallbacks(error);
101 // The callbacks might not consume the error, which is fine.
102 error.SuppressException();
106 ServiceWorkerJob::ServiceWorkerJob(Type aType, nsIPrincipal* aPrincipal,
107 const nsACString& aScope,
108 nsCString aScriptSpec)
109 : mType(aType),
110 mPrincipal(aPrincipal),
111 mScope(aScope),
112 mScriptSpec(std::move(aScriptSpec)),
113 mState(State::Initial),
114 mCanceled(false),
115 mResultCallbacksInvoked(false) {
116 MOZ_ASSERT(NS_IsMainThread());
117 MOZ_ASSERT(mPrincipal);
118 MOZ_ASSERT(!mScope.IsEmpty());
120 // Empty script URL if and only if this is an unregister job.
121 MOZ_ASSERT((mType == Type::Unregister) == mScriptSpec.IsEmpty());
124 ServiceWorkerJob::~ServiceWorkerJob() {
125 MOZ_ASSERT(NS_IsMainThread());
126 // Jobs must finish or never be started. Destroying an actively running
127 // job is an error.
128 MOZ_ASSERT(mState != State::Started);
129 MOZ_ASSERT_IF(mState == State::Finished, mResultCallbacksInvoked);
132 void ServiceWorkerJob::InvokeResultCallbacks(ErrorResult& aRv) {
133 MOZ_ASSERT(NS_IsMainThread());
134 MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
135 MOZ_DIAGNOSTIC_ASSERT_IF(mState == State::Initial, Canceled());
137 MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
138 mResultCallbacksInvoked = true;
140 nsTArray<RefPtr<Callback>> callbackList = std::move(mResultCallbackList);
142 for (RefPtr<Callback>& callback : callbackList) {
143 // The callback might consume an exception on the ErrorResult, so we need
144 // to clone in order to maintain the error for the next callback.
145 ErrorResult rv;
146 aRv.CloneTo(rv);
148 if (GetState() == State::Started) {
149 callback->JobFinished(this, rv);
150 } else {
151 callback->JobDiscarded(rv);
154 // The callback might not consume the error.
155 rv.SuppressException();
159 void ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv) {
160 ErrorResult converted(aRv);
161 InvokeResultCallbacks(converted);
164 void ServiceWorkerJob::Finish(ErrorResult& aRv) {
165 MOZ_ASSERT(NS_IsMainThread());
167 // Avoid double-completion because it can result on operating on cleaned
168 // up data. This should not happen, though, so also assert to try to
169 // narrow down the causes.
170 MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
171 if (mState != State::Started) {
172 return;
175 // Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to
176 // script.
177 if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
178 !aRv.ErrorCodeIs(NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR) &&
179 !aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {
180 // Remove the old error code so we can replace it with a TypeError.
181 aRv.SuppressException();
183 // Throw the type error with a generic error message. We use a stack
184 // reference to bypass the normal static analysis for "return right after
185 // throwing", since it's not the right check here: this ErrorResult came in
186 // pre-thrown.
187 ErrorResult& rv = aRv;
188 rv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(mScriptSpec, mScope);
191 // The final callback may drop the last ref to this object.
192 RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
194 if (!mResultCallbacksInvoked) {
195 InvokeResultCallbacks(aRv);
198 mState = State::Finished;
200 MOZ_DIAGNOSTIC_ASSERT(mFinalCallback);
201 if (mFinalCallback) {
202 mFinalCallback->JobFinished(this, aRv);
203 mFinalCallback = nullptr;
206 // The callback might not consume the error.
207 aRv.SuppressException();
209 // Async release this object to ensure that our caller methods complete
210 // as well.
211 NS_ReleaseOnMainThread("ServiceWorkerJobProxyRunnable",
212 kungFuDeathGrip.forget(), true /* always proxy */);
215 void ServiceWorkerJob::Finish(nsresult aRv) {
216 ErrorResult converted(aRv);
217 Finish(converted);
220 } // namespace mozilla::dom