Source code

Revision control

Other Tools

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