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