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 // Finishing a job is idempotent and potentially expected by the error
168 // handling path for ServiceWorkerUpdateJob, so this is not an error.
169 if (mState
!= State::Started
) {
173 // Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to
175 if (aRv
.Failed() && !aRv
.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR
) &&
176 !aRv
.ErrorCodeIs(NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR
) &&
177 !aRv
.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR
)) {
178 // Remove the old error code so we can replace it with a TypeError.
179 aRv
.SuppressException();
181 // Throw the type error with a generic error message. We use a stack
182 // reference to bypass the normal static analysis for "return right after
183 // throwing", since it's not the right check here: this ErrorResult came in
185 ErrorResult
& rv
= aRv
;
186 rv
.ThrowTypeError
<MSG_SW_INSTALL_ERROR
>(mScriptSpec
, mScope
);
189 // The final callback may drop the last ref to this object.
190 RefPtr
<ServiceWorkerJob
> kungFuDeathGrip
= this;
192 if (!mResultCallbacksInvoked
) {
193 InvokeResultCallbacks(aRv
);
196 mState
= State::Finished
;
198 MOZ_DIAGNOSTIC_ASSERT(mFinalCallback
);
199 if (mFinalCallback
) {
200 mFinalCallback
->JobFinished(this, aRv
);
201 mFinalCallback
= nullptr;
204 // The callback might not consume the error.
205 aRv
.SuppressException();
207 // Async release this object to ensure that our caller methods complete
209 NS_ReleaseOnMainThread("ServiceWorkerJobProxyRunnable",
210 kungFuDeathGrip
.forget(), true /* always proxy */);
213 void ServiceWorkerJob::Finish(nsresult aRv
) {
214 ErrorResult
converted(aRv
);
218 } // namespace mozilla::dom