Source code

Revision control

Other Tools

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