BackgroundFileSaver.h 12 KB

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