Coverage Report

Created: 2018-09-25 14:53

/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