Backed out changeset 36e95068e103 (bug 1848160) for bc failures on browser_preference...
[gecko.git] / netwerk / base / BackgroundFileSaver.h
blob214aa31d10560950b87d1048b45f01bd4135b512
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/. */
7 /**
8 * This file defines two implementations of the nsIBackgroundFileSaver
9 * interface. See the "test_backgroundfilesaver.js" file for usage examples.
12 #ifndef BackgroundFileSaver_h__
13 #define BackgroundFileSaver_h__
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"
25 class nsIAsyncInputStream;
26 class nsISerialEventTarget;
28 namespace mozilla {
29 namespace net {
31 class DigestOutputStream;
33 ////////////////////////////////////////////////////////////////////////////////
34 //// BackgroundFileSaver
36 class BackgroundFileSaver : public nsIBackgroundFileSaver {
37 public:
38 NS_DECL_NSIBACKGROUNDFILESAVER
40 BackgroundFileSaver();
42 /**
43 * Initializes the pipe and the worker thread on XPCOM construction.
45 * This is called automatically by the XPCOM infrastructure, and if this
46 * fails, the factory will delete this object without returning a reference.
48 nsresult Init();
50 /**
51 * Number of worker threads that are currently running.
53 static uint32_t sThreadCount;
55 /**
56 * Maximum number of worker threads reached during the current download
57 * session, used for telemetry.
59 * When there are no more worker threads running, we consider the download
60 * session finished, and this counter is reset.
62 static uint32_t sTelemetryMaxThreadCount;
64 protected:
65 virtual ~BackgroundFileSaver();
67 /**
68 * Thread that constructed this object.
70 nsCOMPtr<nsIEventTarget> mControlEventTarget;
72 /**
73 * Thread to which the actual input/output is delegated.
75 nsCOMPtr<nsISerialEventTarget> mBackgroundET;
77 /**
78 * Stream that receives data from derived classes. The received data will be
79 * available to the worker thread through mPipeInputStream. This is an
80 * instance of nsPipeOutputStream, not BackgroundFileSaverOutputStream.
82 nsCOMPtr<nsIAsyncOutputStream> mPipeOutputStream;
84 /**
85 * Used during initialization, determines if the pipe is created with an
86 * infinite buffer. An infinite buffer is required if the derived class
87 * implements nsIStreamListener, because this interface requires all the
88 * provided data to be consumed synchronously.
90 virtual bool HasInfiniteBuffer() = 0;
92 /**
93 * Used by derived classes if they need to be called back while copying.
95 virtual nsAsyncCopyProgressFun GetProgressCallback() = 0;
97 /**
98 * Stream used by the worker thread to read the data to be saved.
100 nsCOMPtr<nsIAsyncInputStream> mPipeInputStream;
102 private:
103 friend class NotifyTargetChangeRunnable;
106 * Matches the nsIBackgroundFileSaver::observer property.
108 * @remarks This is a strong reference so that JavaScript callers don't need
109 * to worry about keeping another reference to the observer.
111 nsCOMPtr<nsIBackgroundFileSaverObserver> mObserver;
113 //////////////////////////////////////////////////////////////////////////////
114 //// Shared state between control and worker threads
117 * Protects the shared state between control and worker threads. This mutex
118 * is always locked for a very short time, never during input/output.
120 mozilla::Mutex mLock{"BackgroundFileSaver.mLock"};
123 * True if the worker thread is already waiting to process a change in state.
125 bool mWorkerThreadAttentionRequested MOZ_GUARDED_BY(mLock){false};
128 * True if the operation should finish as soon as possibile.
130 bool mFinishRequested MOZ_GUARDED_BY(mLock){false};
133 * True if the operation completed, with either success or failure.
135 bool mComplete MOZ_GUARDED_BY(mLock){false};
138 * Holds the current file saver status. This is a success status while the
139 * object is working correctly, and remains such if the operation completes
140 * successfully. This becomes an error status when an error occurs on the
141 * worker thread, or when the operation is canceled.
143 nsresult mStatus MOZ_GUARDED_BY(mLock){NS_OK};
146 * True if we should append data to the initial target file, instead of
147 * overwriting it.
149 bool mAppend MOZ_GUARDED_BY(mLock){false};
152 * This is set by the first SetTarget call on the control thread, and contains
153 * the target file name that will be used by the worker thread, as soon as it
154 * is possible to update mActualTarget and open the file. This is null if no
155 * target was ever assigned to this object.
157 nsCOMPtr<nsIFile> mInitialTarget MOZ_GUARDED_BY(mLock);
160 * This is set by the first SetTarget call on the control thread, and
161 * indicates whether mInitialTarget should be kept as partially completed,
162 * rather than deleted, if the operation fails or is canceled.
164 bool mInitialTargetKeepPartial MOZ_GUARDED_BY(mLock){false};
167 * This is set by subsequent SetTarget calls on the control thread, and
168 * contains the new target file name to which the worker thread will move the
169 * target file, as soon as it can be done. This is null if SetTarget was
170 * called only once, or no target was ever assigned to this object.
172 * The target file can be renamed multiple times, though only the most recent
173 * rename is guaranteed to be processed by the worker thread.
175 nsCOMPtr<nsIFile> mRenamedTarget MOZ_GUARDED_BY(mLock);
178 * This is set by subsequent SetTarget calls on the control thread, and
179 * indicates whether mRenamedTarget should be kept as partially completed,
180 * rather than deleted, if the operation fails or is canceled.
182 bool mRenamedTargetKeepPartial MOZ_GUARDED_BY(mLock){false};
185 * While NS_AsyncCopy is in progress, allows canceling it. Null otherwise.
186 * This is read by both threads but only written by the worker thread.
188 nsCOMPtr<nsISupports> mAsyncCopyContext MOZ_GUARDED_BY(mLock);
191 * The SHA 256 hash in raw bytes of the downloaded file. This is written
192 * by the worker thread but can be read on the main thread.
194 nsCString mSha256 MOZ_GUARDED_BY(mLock);
197 * Whether or not to compute the hash. Must be set on the main thread before
198 * setTarget is called.
200 bool mSha256Enabled MOZ_GUARDED_BY(mLock){false};
203 * Store the signature info.
205 nsTArray<nsTArray<nsTArray<uint8_t>>> mSignatureInfo MOZ_GUARDED_BY(mLock);
208 * Whether or not to extract the signature. Must be set on the main thread
209 * before setTarget is called.
211 bool mSignatureInfoEnabled MOZ_GUARDED_BY(mLock){false};
213 //////////////////////////////////////////////////////////////////////////////
214 //// State handled exclusively by the worker thread
217 * Current target file associated to the input and output streams.
219 nsCOMPtr<nsIFile> mActualTarget;
222 * Indicates whether mActualTarget should be kept as partially completed,
223 * rather than deleted, if the operation fails or is canceled.
225 bool mActualTargetKeepPartial{false};
228 * Used to calculate the file hash. This keeps state across file renames and
229 * is lazily initialized in ProcessStateChange.
231 Maybe<Digest> mDigest;
233 //////////////////////////////////////////////////////////////////////////////
234 //// Private methods
237 * Called when NS_AsyncCopy completes.
239 * @param aClosure
240 * Populated with a raw pointer to the BackgroundFileSaver object.
241 * @param aStatus
242 * Success or failure status specified when the copy was interrupted.
244 static void AsyncCopyCallback(void* aClosure, nsresult aStatus);
247 * Called on the control thread after state changes, to ensure that the worker
248 * thread will process the state change appropriately.
250 * @param aShouldInterruptCopy
251 * If true, the current NS_AsyncCopy, if any, is canceled.
253 nsresult GetWorkerThreadAttention(bool aShouldInterruptCopy);
256 * Event called on the worker thread to begin processing a state change.
258 nsresult ProcessAttention();
261 * Called by ProcessAttention to execute the operations corresponding to the
262 * state change. If this results in an error, ProcessAttention will force the
263 * entire operation to be aborted.
265 nsresult ProcessStateChange();
268 * Returns true if completion conditions are met on the worker thread. The
269 * first time this happens, posts the completion event to the control thread.
271 bool CheckCompletion();
274 * Event called on the control thread to indicate that file contents will now
275 * be saved to the specified file.
277 nsresult NotifyTargetChange(nsIFile* aTarget);
280 * Event called on the control thread to send the final notification.
282 nsresult NotifySaveComplete();
285 * Verifies the signature of the binary at the specified file path and stores
286 * the signature data in mSignatureInfo. We extract only X.509 certificates,
287 * since that is what Google's Safebrowsing protocol specifies.
289 nsresult ExtractSignatureInfo(const nsAString& filePath);
292 ////////////////////////////////////////////////////////////////////////////////
293 //// BackgroundFileSaverOutputStream
295 class BackgroundFileSaverOutputStream : public BackgroundFileSaver,
296 public nsIAsyncOutputStream,
297 public nsIOutputStreamCallback {
298 public:
299 NS_DECL_THREADSAFE_ISUPPORTS
300 NS_DECL_NSIOUTPUTSTREAM
301 NS_DECL_NSIASYNCOUTPUTSTREAM
302 NS_DECL_NSIOUTPUTSTREAMCALLBACK
304 BackgroundFileSaverOutputStream();
306 protected:
307 virtual bool HasInfiniteBuffer() override;
308 virtual nsAsyncCopyProgressFun GetProgressCallback() override;
310 private:
311 ~BackgroundFileSaverOutputStream() = default;
314 * Original callback provided to our AsyncWait wrapper.
316 nsCOMPtr<nsIOutputStreamCallback> mAsyncWaitCallback;
319 ////////////////////////////////////////////////////////////////////////////////
320 //// BackgroundFileSaverStreamListener. This class is instantiated by
321 // nsExternalHelperAppService, DownloadCore.sys.mjs, and possibly others.
323 class BackgroundFileSaverStreamListener final : public BackgroundFileSaver,
324 public nsIStreamListener {
325 public:
326 NS_DECL_THREADSAFE_ISUPPORTS
327 NS_DECL_NSIREQUESTOBSERVER
328 NS_DECL_NSISTREAMLISTENER
330 BackgroundFileSaverStreamListener() = default;
332 protected:
333 virtual bool HasInfiniteBuffer() override;
334 virtual nsAsyncCopyProgressFun GetProgressCallback() override;
336 private:
337 ~BackgroundFileSaverStreamListener() = default;
340 * Protects the state related to whether the request should be suspended.
342 mozilla::Mutex mSuspensionLock{
343 "BackgroundFileSaverStreamListener.mSuspensionLock"};
346 * Whether we should suspend the request because we received too much data.
348 bool mReceivedTooMuchData MOZ_GUARDED_BY(mSuspensionLock){false};
351 * Request for which we received too much data. This is populated when
352 * mReceivedTooMuchData becomes true for the first time.
354 nsCOMPtr<nsIRequest> mRequest MOZ_GUARDED_BY(mSuspensionLock);
357 * Whether mRequest is currently suspended.
359 bool mRequestSuspended MOZ_GUARDED_BY(mSuspensionLock){false};
362 * Called while NS_AsyncCopy is copying data.
364 static void AsyncCopyProgressCallback(void* aClosure, uint32_t aCount);
367 * Called on the control thread to suspend or resume the request.
369 nsresult NotifySuspendOrResume();
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* aStream, Digest& aDigest);
382 private:
383 virtual ~DigestOutputStream() = default;
385 // Calls to write are passed to this stream.
386 nsCOMPtr<nsIOutputStream> mOutputStream;
387 // Digest used to compute the hash, owned by the caller.
388 Digest& mDigest;
390 // Don't accidentally copy construct.
391 DigestOutputStream(const DigestOutputStream& d) = delete;
394 } // namespace net
395 } // namespace mozilla
397 #endif