Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/BackgroundFileSaver.h
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=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
/**
8
 * This file defines two implementations of the nsIBackgroundFileSaver
9
 * interface.  See the "test_backgroundfilesaver.js" file for usage examples.
10
 */
11
12
#ifndef BackgroundFileSaver_h__
13
#define BackgroundFileSaver_h__
14
15
#include "ScopedNSSTypes.h"
16
#include "mozilla/Mutex.h"
17
#include "nsCOMArray.h"
18
#include "nsCOMPtr.h"
19
#include "nsIAsyncOutputStream.h"
20
#include "nsIBackgroundFileSaver.h"
21
#include "nsIStreamListener.h"
22
#include "nsStreamUtils.h"
23
#include "nsString.h"
24
25
class nsIAsyncInputStream;
26
class nsIThread;
27
class nsIX509CertList;
28
29
namespace mozilla {
30
namespace net {
31
32
class DigestOutputStream;
33
34
////////////////////////////////////////////////////////////////////////////////
35
//// BackgroundFileSaver
36
37
class BackgroundFileSaver : public nsIBackgroundFileSaver
38
{
39
public:
40
  NS_DECL_NSIBACKGROUNDFILESAVER
41
42
  BackgroundFileSaver();
43
44
  /**
45
   * Initializes the pipe and the worker thread on XPCOM construction.
46
   *
47
   * This is called automatically by the XPCOM infrastructure, and if this
48
   * fails, the factory will delete this object without returning a reference.
49
   */
50
  nsresult Init();
51
52
  /**
53
   * Number of worker threads that are currently running.
54
   */
55
  static uint32_t sThreadCount;
56
57
  /**
58
   * Maximum number of worker threads reached during the current download session,
59
   * used for telemetry.
60
   *
61
   * When there are no more worker threads running, we consider the download
62
   * session finished, and this counter is reset.
63
   */
64
  static uint32_t sTelemetryMaxThreadCount;
65
66
67
protected:
68
  virtual ~BackgroundFileSaver();
69
70
  /**
71
   * Thread that constructed this object.
72
   */
73
  nsCOMPtr<nsIEventTarget> mControlEventTarget;
74
75
  /**
76
   * Thread to which the actual input/output is delegated.
77
   */
78
  nsCOMPtr<nsIThread> mWorkerThread;
79
80
  /**
81
   * Stream that receives data from derived classes.  The received data will be
82
   * available to the worker thread through mPipeInputStream. This is an
83
   * instance of nsPipeOutputStream, not BackgroundFileSaverOutputStream.
84
   */
85
  nsCOMPtr<nsIAsyncOutputStream> mPipeOutputStream;
86
87
  /**
88
   * Used during initialization, determines if the pipe is created with an
89
   * infinite buffer.  An infinite buffer is required if the derived class
90
   * implements nsIStreamListener, because this interface requires all the
91
   * provided data to be consumed synchronously.
92
   */
93
  virtual bool HasInfiniteBuffer() = 0;
94
95
  /**
96
   * Used by derived classes if they need to be called back while copying.
97
   */
98
  virtual nsAsyncCopyProgressFun GetProgressCallback() = 0;
99
100
  /**
101
   * Stream used by the worker thread to read the data to be saved.
102
   */
103
  nsCOMPtr<nsIAsyncInputStream> mPipeInputStream;
104
105
private:
106
  friend class NotifyTargetChangeRunnable;
107
108
  /**
109
   * Matches the nsIBackgroundFileSaver::observer property.
110
   *
111
   * @remarks This is a strong reference so that JavaScript callers don't need
112
   *          to worry about keeping another reference to the observer.
113
   */
114
  nsCOMPtr<nsIBackgroundFileSaverObserver> mObserver;
115
116
  //////////////////////////////////////////////////////////////////////////////
117
  //// Shared state between control and worker threads
118
119
  /**
120
   * Protects the shared state between control and worker threads.  This mutex
121
   * is always locked for a very short time, never during input/output.
122
   */
123
  mozilla::Mutex mLock;
124
125
  /**
126
   * True if the worker thread is already waiting to process a change in state.
127
   */
128
  bool mWorkerThreadAttentionRequested;
129
130
  /**
131
   * True if the operation should finish as soon as possibile.
132
   */
133
  bool mFinishRequested;
134
135
  /**
136
   * True if the operation completed, with either success or failure.
137
   */
138
  bool mComplete;
139
140
  /**
141
   * Holds the current file saver status.  This is a success status while the
142
   * object is working correctly, and remains such if the operation completes
143
   * successfully.  This becomes an error status when an error occurs on the
144
   * worker thread, or when the operation is canceled.
145
   */
146
  nsresult mStatus;
147
148
  /**
149
   * True if we should append data to the initial target file, instead of
150
   * overwriting it.
151
   */
152
  bool mAppend;
153
154
  /**
155
   * This is set by the first SetTarget call on the control thread, and contains
156
   * the target file name that will be used by the worker thread, as soon as it
157
   * is possible to update mActualTarget and open the file.  This is null if no
158
   * target was ever assigned to this object.
159
   */
160
  nsCOMPtr<nsIFile> mInitialTarget;
161
162
  /**
163
   * This is set by the first SetTarget call on the control thread, and
164
   * indicates whether mInitialTarget should be kept as partially completed,
165
   * rather than deleted, if the operation fails or is canceled.
166
   */
167
  bool mInitialTargetKeepPartial;
168
169
  /**
170
   * This is set by subsequent SetTarget calls on the control thread, and
171
   * contains the new target file name to which the worker thread will move the
172
   * target file, as soon as it can be done.  This is null if SetTarget was
173
   * called only once, or no target was ever assigned to this object.
174
   *
175
   * The target file can be renamed multiple times, though only the most recent
176
   * rename is guaranteed to be processed by the worker thread.
177
   */
178
  nsCOMPtr<nsIFile> mRenamedTarget;
179
180
  /**
181
   * This is set by subsequent SetTarget calls on the control thread, and
182
   * indicates whether mRenamedTarget should be kept as partially completed,
183
   * rather than deleted, if the operation fails or is canceled.
184
   */
185
  bool mRenamedTargetKeepPartial;
186
187
  /**
188
   * While NS_AsyncCopy is in progress, allows canceling it.  Null otherwise.
189
   * This is read by both threads but only written by the worker thread.
190
   */
191
  nsCOMPtr<nsISupports> mAsyncCopyContext;
192
193
  /**
194
   * The SHA 256 hash in raw bytes of the downloaded file. This is written
195
   * by the worker thread but can be read on the main thread.
196
   */
197
  nsCString mSha256;
198
199
  /**
200
   * Whether or not to compute the hash. Must be set on the main thread before
201
   * setTarget is called.
202
   */
203
  bool mSha256Enabled;
204
205
  /**
206
   * Store the signature info.
207
   */
208
  nsCOMArray<nsIX509CertList> mSignatureInfo;
209
210
  /**
211
   * Whether or not to extract the signature. Must be set on the main thread
212
   * before setTarget is called.
213
   */
214
  bool mSignatureInfoEnabled;
215
216
  //////////////////////////////////////////////////////////////////////////////
217
  //// State handled exclusively by the worker thread
218
219
  /**
220
   * Current target file associated to the input and output streams.
221
   */
222
  nsCOMPtr<nsIFile> mActualTarget;
223
224
  /**
225
   * Indicates whether mActualTarget should be kept as partially completed,
226
   * rather than deleted, if the operation fails or is canceled.
227
   */
228
  bool mActualTargetKeepPartial;
229
230
  /**
231
   * Used to calculate the file hash. This keeps state across file renames and
232
   * is lazily initialized in ProcessStateChange.
233
   */
234
  UniquePK11Context mDigestContext;
235
236
  //////////////////////////////////////////////////////////////////////////////
237
  //// Private methods
238
239
  /**
240
   * Called when NS_AsyncCopy completes.
241
   *
242
   * @param aClosure
243
   *        Populated with a raw pointer to the BackgroundFileSaver object.
244
   * @param aStatus
245
   *        Success or failure status specified when the copy was interrupted.
246
   */
247
  static void AsyncCopyCallback(void *aClosure, nsresult aStatus);
248
249
  /**
250
   * Called on the control thread after state changes, to ensure that the worker
251
   * thread will process the state change appropriately.
252
   *
253
   * @param aShouldInterruptCopy
254
   *        If true, the current NS_AsyncCopy, if any, is canceled.
255
   */
256
  nsresult GetWorkerThreadAttention(bool aShouldInterruptCopy);
257
258
  /**
259
   * Event called on the worker thread to begin processing a state change.
260
   */
261
  nsresult ProcessAttention();
262
263
  /**
264
   * Called by ProcessAttention to execute the operations corresponding to the
265
   * state change.  If this results in an error, ProcessAttention will force the
266
   * entire operation to be aborted.
267
   */
268
  nsresult ProcessStateChange();
269
270
  /**
271
   * Returns true if completion conditions are met on the worker thread.  The
272
   * first time this happens, posts the completion event to the control thread.
273
   */
274
  bool CheckCompletion();
275
276
  /**
277
   * Event called on the control thread to indicate that file contents will now
278
   * be saved to the specified file.
279
   */
280
  nsresult NotifyTargetChange(nsIFile *aTarget);
281
282
  /**
283
   * Event called on the control thread to send the final notification.
284
   */
285
  nsresult NotifySaveComplete();
286
287
  /**
288
   * Verifies the signature of the binary at the specified file path and stores
289
   * the signature data in mSignatureInfo. We extract only X.509 certificates,
290
   * since that is what Google's Safebrowsing protocol specifies.
291
   */
292
  nsresult ExtractSignatureInfo(const nsAString& filePath);
293
};
294
295
////////////////////////////////////////////////////////////////////////////////
296
//// BackgroundFileSaverOutputStream
297
298
class BackgroundFileSaverOutputStream : public BackgroundFileSaver
299
                                      , public nsIAsyncOutputStream
300
                                      , public nsIOutputStreamCallback
301
{
302
public:
303
  NS_DECL_THREADSAFE_ISUPPORTS
304
  NS_DECL_NSIOUTPUTSTREAM
305
  NS_DECL_NSIASYNCOUTPUTSTREAM
306
  NS_DECL_NSIOUTPUTSTREAMCALLBACK
307
308
  BackgroundFileSaverOutputStream();
309
310
protected:
311
  virtual bool HasInfiniteBuffer() override;
312
  virtual nsAsyncCopyProgressFun GetProgressCallback() override;
313
314
private:
315
0
  ~BackgroundFileSaverOutputStream() = default;
316
317
  /**
318
   * Original callback provided to our AsyncWait wrapper.
319
   */
320
  nsCOMPtr<nsIOutputStreamCallback> mAsyncWaitCallback;
321
};
322
323
////////////////////////////////////////////////////////////////////////////////
324
//// BackgroundFileSaverStreamListener. This class is instantiated by
325
// nsExternalHelperAppService, DownloadCore.jsm, and possibly others.
326
327
class BackgroundFileSaverStreamListener final : public BackgroundFileSaver
328
                                              , public nsIStreamListener
329
{
330
public:
331
  NS_DECL_THREADSAFE_ISUPPORTS
332
  NS_DECL_NSIREQUESTOBSERVER
333
  NS_DECL_NSISTREAMLISTENER
334
335
  BackgroundFileSaverStreamListener();
336
337
protected:
338
  virtual bool HasInfiniteBuffer() override;
339
  virtual nsAsyncCopyProgressFun GetProgressCallback() override;
340
341
private:
342
0
  ~BackgroundFileSaverStreamListener() = default;
343
344
  /**
345
   * Protects the state related to whether the request should be suspended.
346
   */
347
  mozilla::Mutex mSuspensionLock;
348
349
  /**
350
   * Whether we should suspend the request because we received too much data.
351
   */
352
  bool mReceivedTooMuchData;
353
354
  /**
355
   * Request for which we received too much data.  This is populated when
356
   * mReceivedTooMuchData becomes true for the first time.
357
   */
358
  nsCOMPtr<nsIRequest> mRequest;
359
360
  /**
361
   * Whether mRequest is currently suspended.
362
   */
363
  bool mRequestSuspended;
364
365
  /**
366
   * Called while NS_AsyncCopy is copying data.
367
   */
368
  static void AsyncCopyProgressCallback(void *aClosure, uint32_t aCount);
369
370
  /**
371
   * Called on the control thread to suspend or resume the request.
372
   */
373
  nsresult NotifySuspendOrResume();
374
};
375
376
// A wrapper around nsIOutputStream, so that we can compute hashes on the
377
// stream without copying and without polluting pristine NSS code with XPCOM
378
// interfaces.
379
class DigestOutputStream : public nsIOutputStream
380
{
381
public:
382
  NS_DECL_THREADSAFE_ISUPPORTS
383
  NS_DECL_NSIOUTPUTSTREAM
384
  // Constructor. Neither parameter may be null. The caller owns both.
385
  DigestOutputStream(nsIOutputStream* outputStream, PK11Context* aContext);
386
387
private:
388
0
  virtual ~DigestOutputStream() = default;
389
390
  // Calls to write are passed to this stream.
391
  nsCOMPtr<nsIOutputStream> mOutputStream;
392
  // Digest context used to compute the hash, owned by the caller.
393
  PK11Context* mDigestContext;
394
395
  // Don't accidentally copy construct.
396
  DigestOutputStream(const DigestOutputStream& d) = delete;
397
};
398
399
} // namespace net
400
} // namespace mozilla
401
402
#endif