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