Line data Source code
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/. */
6 :
7 : #include "ServiceWorkerJob.h"
8 :
9 : #include "nsProxyRelease.h"
10 : #include "nsThreadUtils.h"
11 : #include "Workers.h"
12 :
13 : namespace mozilla {
14 : namespace dom {
15 : namespace workers {
16 :
17 : ServiceWorkerJob::Type
18 0 : ServiceWorkerJob::GetType() const
19 : {
20 0 : return mType;
21 : }
22 :
23 : ServiceWorkerJob::State
24 0 : ServiceWorkerJob::GetState() const
25 : {
26 0 : return mState;
27 : }
28 :
29 : bool
30 0 : ServiceWorkerJob::Canceled() const
31 : {
32 0 : return mCanceled;
33 : }
34 :
35 : bool
36 0 : ServiceWorkerJob::ResultCallbacksInvoked() const
37 : {
38 0 : return mResultCallbacksInvoked;
39 : }
40 :
41 : bool
42 0 : ServiceWorkerJob::IsEquivalentTo(ServiceWorkerJob* aJob) const
43 : {
44 0 : AssertIsOnMainThread();
45 0 : MOZ_ASSERT(aJob);
46 0 : return mType == aJob->mType &&
47 0 : mScope.Equals(aJob->mScope) &&
48 0 : mScriptSpec.Equals(aJob->mScriptSpec) &&
49 0 : mPrincipal->Equals(aJob->mPrincipal);
50 : }
51 :
52 : void
53 0 : ServiceWorkerJob::AppendResultCallback(Callback* aCallback)
54 : {
55 0 : AssertIsOnMainThread();
56 0 : MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
57 0 : MOZ_DIAGNOSTIC_ASSERT(aCallback);
58 0 : MOZ_DIAGNOSTIC_ASSERT(mFinalCallback != aCallback);
59 0 : MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
60 0 : MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
61 0 : mResultCallbackList.AppendElement(aCallback);
62 0 : }
63 :
64 : void
65 0 : ServiceWorkerJob::StealResultCallbacksFrom(ServiceWorkerJob* aJob)
66 : {
67 0 : AssertIsOnMainThread();
68 0 : MOZ_ASSERT(aJob);
69 0 : MOZ_ASSERT(aJob->mState == State::Initial);
70 :
71 : // Take the callbacks from the other job immediately to avoid the
72 : // any possibility of them existing on both jobs at once.
73 0 : nsTArray<RefPtr<Callback>> callbackList;
74 0 : callbackList.SwapElements(aJob->mResultCallbackList);
75 :
76 0 : for (RefPtr<Callback>& callback : callbackList) {
77 : // Use AppendResultCallback() so that assertion checking is performed on
78 : // each callback.
79 0 : AppendResultCallback(callback);
80 : }
81 0 : }
82 :
83 : void
84 0 : ServiceWorkerJob::Start(Callback* aFinalCallback)
85 : {
86 0 : AssertIsOnMainThread();
87 0 : MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
88 :
89 0 : MOZ_DIAGNOSTIC_ASSERT(aFinalCallback);
90 0 : MOZ_DIAGNOSTIC_ASSERT(!mFinalCallback);
91 0 : MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
92 0 : mFinalCallback = aFinalCallback;
93 :
94 0 : MOZ_DIAGNOSTIC_ASSERT(mState == State::Initial);
95 0 : mState = State::Started;
96 :
97 : nsCOMPtr<nsIRunnable> runnable =
98 0 : NewRunnableMethod("ServiceWorkerJob::AsyncExecute",
99 0 : this, &ServiceWorkerJob::AsyncExecute);
100 :
101 : // We may have to wait for the PBackground actor to be initialized
102 : // before proceeding. We should always be able to get a ServiceWorkerManager,
103 : // however, since Start() should not be called during shutdown.
104 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
105 0 : if (!swm) {
106 : // browser shutdown
107 0 : return;
108 : }
109 0 : if (!swm->HasBackgroundActor()) {
110 : // waiting to initialize
111 0 : swm->AppendPendingOperation(runnable);
112 0 : return;
113 : }
114 :
115 : // Otherwise start asynchronously. We should never run a job synchronously.
116 0 : MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
117 : NS_DispatchToMainThread(runnable.forget())));
118 : }
119 :
120 : void
121 0 : ServiceWorkerJob::Cancel()
122 : {
123 0 : AssertIsOnMainThread();
124 0 : MOZ_ASSERT(!mCanceled);
125 0 : mCanceled = true;
126 0 : }
127 :
128 0 : ServiceWorkerJob::ServiceWorkerJob(Type aType,
129 : nsIPrincipal* aPrincipal,
130 : const nsACString& aScope,
131 0 : const nsACString& aScriptSpec)
132 : : mType(aType)
133 : , mPrincipal(aPrincipal)
134 : , mScope(aScope)
135 : , mScriptSpec(aScriptSpec)
136 : , mState(State::Initial)
137 : , mCanceled(false)
138 0 : , mResultCallbacksInvoked(false)
139 : {
140 0 : AssertIsOnMainThread();
141 0 : MOZ_ASSERT(mPrincipal);
142 0 : MOZ_ASSERT(!mScope.IsEmpty());
143 : // Some job types may have an empty script spec
144 0 : }
145 :
146 0 : ServiceWorkerJob::~ServiceWorkerJob()
147 : {
148 0 : AssertIsOnMainThread();
149 : // Jobs must finish or never be started. Destroying an actively running
150 : // job is an error.
151 0 : MOZ_ASSERT(mState != State::Started);
152 0 : MOZ_ASSERT_IF(mState == State::Finished, mResultCallbacksInvoked);
153 0 : }
154 :
155 : void
156 0 : ServiceWorkerJob::InvokeResultCallbacks(ErrorResult& aRv)
157 : {
158 0 : AssertIsOnMainThread();
159 0 : MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
160 :
161 0 : MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
162 0 : mResultCallbacksInvoked = true;
163 :
164 0 : nsTArray<RefPtr<Callback>> callbackList;
165 0 : callbackList.SwapElements(mResultCallbackList);
166 :
167 0 : for (RefPtr<Callback>& callback : callbackList) {
168 : // The callback might consume an exception on the ErrorResult, so we need
169 : // to clone in order to maintain the error for the next callback.
170 0 : ErrorResult rv;
171 0 : aRv.CloneTo(rv);
172 :
173 0 : callback->JobFinished(this, rv);
174 :
175 : // The callback might not consume the error.
176 0 : rv.SuppressException();
177 : }
178 0 : }
179 :
180 : void
181 0 : ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv)
182 : {
183 0 : ErrorResult converted(aRv);
184 0 : InvokeResultCallbacks(converted);
185 0 : }
186 :
187 : void
188 0 : ServiceWorkerJob::Finish(ErrorResult& aRv)
189 : {
190 0 : AssertIsOnMainThread();
191 :
192 : // Avoid double-completion because it can result on operating on cleaned
193 : // up data. This should not happen, though, so also assert to try to
194 : // narrow down the causes.
195 0 : MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
196 0 : if (mState != State::Started) {
197 0 : return;
198 : }
199 :
200 : // Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to script.
201 0 : if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
202 0 : !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR) &&
203 0 : !aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {
204 :
205 : // Remove the old error code so we can replace it with a TypeError.
206 0 : aRv.SuppressException();
207 :
208 0 : NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
209 0 : NS_ConvertUTF8toUTF16 scope(mScope);
210 :
211 : // Throw the type error with a generic error message.
212 0 : aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
213 : }
214 :
215 : // The final callback may drop the last ref to this object.
216 0 : RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
217 :
218 0 : if (!mResultCallbacksInvoked) {
219 0 : InvokeResultCallbacks(aRv);
220 : }
221 :
222 0 : mState = State::Finished;
223 :
224 0 : MOZ_DIAGNOSTIC_ASSERT(mFinalCallback);
225 0 : if (mFinalCallback) {
226 0 : mFinalCallback->JobFinished(this, aRv);
227 0 : mFinalCallback = nullptr;
228 : }
229 :
230 : // The callback might not consume the error.
231 0 : aRv.SuppressException();
232 :
233 : // Async release this object to ensure that our caller methods complete
234 : // as well.
235 : NS_ReleaseOnMainThread("ServiceWorkerJob",
236 0 : kungFuDeathGrip.forget(), true /* always proxy */);
237 : }
238 :
239 : void
240 0 : ServiceWorkerJob::Finish(nsresult aRv)
241 : {
242 0 : ErrorResult converted(aRv);
243 0 : Finish(converted);
244 0 : }
245 :
246 : } // namespace workers
247 : } // namespace dom
248 : } // namespace mozilla
|