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());
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());
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
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();
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
);
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
)
110 mPrincipal(aPrincipal
),
112 mScriptSpec(std::move(aScriptSpec
)),
113 mState(State::Initial
),
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
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.
148 if (GetState() == State::Started
) {
149 callback
->JobFinished(this, rv
);
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
) {
175 // Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to
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
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
211 NS_ReleaseOnMainThread("ServiceWorkerJobProxyRunnable",
212 kungFuDeathGrip
.forget(), true /* always proxy */);
215 void ServiceWorkerJob::Finish(nsresult aRv
) {
216 ErrorResult
converted(aRv
);
220 } // namespace mozilla::dom