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