tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BackgroundFileSaver.h (12725B)


      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 nsISerialEventTarget;
     27 
     28 namespace mozilla {
     29 namespace net {
     30 
     31 class DigestOutputStream;
     32 
     33 ////////////////////////////////////////////////////////////////////////////////
     34 //// BackgroundFileSaver
     35 
     36 class BackgroundFileSaver : public nsIBackgroundFileSaver {
     37 public:
     38  NS_DECL_NSIBACKGROUNDFILESAVER
     39 
     40  BackgroundFileSaver();
     41 
     42  /**
     43   * Initializes the pipe and the worker thread on XPCOM construction.
     44   *
     45   * This is called automatically by the XPCOM infrastructure, and if this
     46   * fails, the factory will delete this object without returning a reference.
     47   */
     48  nsresult Init();
     49 
     50  /**
     51   * Number of worker threads that are currently running.
     52   */
     53  static uint32_t sThreadCount;
     54 
     55  /**
     56   * Maximum number of worker threads reached during the current download
     57   * session, used for telemetry.
     58   *
     59   * When there are no more worker threads running, we consider the download
     60   * session finished, and this counter is reset.
     61   */
     62  static uint32_t sTelemetryMaxThreadCount;
     63 
     64 protected:
     65  virtual ~BackgroundFileSaver();
     66 
     67  /**
     68   * Thread that constructed this object.
     69   */
     70  nsCOMPtr<nsIEventTarget> mControlEventTarget;
     71 
     72  /**
     73   * Thread to which the actual input/output is delegated.
     74   */
     75  nsCOMPtr<nsISerialEventTarget> mBackgroundET;
     76 
     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.
     81   */
     82  nsCOMPtr<nsIAsyncOutputStream> mPipeOutputStream;
     83 
     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.
     89   */
     90  virtual bool HasInfiniteBuffer() = 0;
     91 
     92  /**
     93   * Used by derived classes if they need to be called back while copying.
     94   */
     95  virtual nsAsyncCopyProgressFun GetProgressCallback() = 0;
     96 
     97  /**
     98   * Stream used by the worker thread to read the data to be saved.
     99   */
    100  nsCOMPtr<nsIAsyncInputStream> mPipeInputStream;
    101 
    102 private:
    103  friend class NotifyTargetChangeRunnable;
    104 
    105  /**
    106   * Matches the nsIBackgroundFileSaver::observer property.
    107   *
    108   * @remarks This is a strong reference so that JavaScript callers don't need
    109   *          to worry about keeping another reference to the observer.
    110   */
    111  nsCOMPtr<nsIBackgroundFileSaverObserver> mObserver;
    112 
    113  //////////////////////////////////////////////////////////////////////////////
    114  //// Shared state between control and worker threads
    115 
    116  /**
    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.
    119   */
    120  mozilla::Mutex mLock{"BackgroundFileSaver.mLock"};
    121 
    122  /**
    123   * True if the worker thread is already waiting to process a change in state.
    124   */
    125  bool mWorkerThreadAttentionRequested MOZ_GUARDED_BY(mLock){false};
    126 
    127  /**
    128   * True if the operation should finish as soon as possibile.
    129   */
    130  bool mFinishRequested MOZ_GUARDED_BY(mLock){false};
    131 
    132  /**
    133   * True if the operation completed, with either success or failure.
    134   */
    135  bool mComplete MOZ_GUARDED_BY(mLock){false};
    136 
    137  /**
    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.
    142   */
    143  nsresult mStatus MOZ_GUARDED_BY(mLock){NS_OK};
    144 
    145  /**
    146   * True if we should append data to the initial target file, instead of
    147   * overwriting it.
    148   */
    149  bool mAppend MOZ_GUARDED_BY(mLock){false};
    150 
    151  /**
    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.
    156   */
    157  nsCOMPtr<nsIFile> mInitialTarget MOZ_GUARDED_BY(mLock);
    158 
    159  /**
    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.
    163   */
    164  bool mInitialTargetKeepPartial MOZ_GUARDED_BY(mLock){false};
    165 
    166  /**
    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.
    171   *
    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.
    174   */
    175  nsCOMPtr<nsIFile> mRenamedTarget MOZ_GUARDED_BY(mLock);
    176 
    177  /**
    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.
    181   */
    182  bool mRenamedTargetKeepPartial MOZ_GUARDED_BY(mLock){false};
    183 
    184  /**
    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.
    187   */
    188  nsCOMPtr<nsISupports> mAsyncCopyContext MOZ_GUARDED_BY(mLock);
    189 
    190  /**
    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.
    193   */
    194  nsCString mSha256 MOZ_GUARDED_BY(mLock);
    195 
    196  /**
    197   * Whether or not to compute the hash. Must be set on the main thread before
    198   * setTarget is called.
    199   */
    200  bool mSha256Enabled MOZ_GUARDED_BY(mLock){false};
    201 
    202  /**
    203   * Store the signature info.
    204   */
    205  nsTArray<nsTArray<nsTArray<uint8_t>>> mSignatureInfo MOZ_GUARDED_BY(mLock);
    206 
    207  /**
    208   * Whether or not to extract the signature. Must be set on the main thread
    209   * before setTarget is called.
    210   */
    211  bool mSignatureInfoEnabled MOZ_GUARDED_BY(mLock){false};
    212 
    213  //////////////////////////////////////////////////////////////////////////////
    214  //// State handled exclusively by the worker thread
    215 
    216  /**
    217   * Current target file associated to the input and output streams.
    218   */
    219  nsCOMPtr<nsIFile> mActualTarget;
    220 
    221  /**
    222   * Indicates whether mActualTarget should be kept as partially completed,
    223   * rather than deleted, if the operation fails or is canceled.
    224   */
    225  bool mActualTargetKeepPartial{false};
    226 
    227  /**
    228   * Used to calculate the file hash. This keeps state across file renames and
    229   * is lazily initialized in ProcessStateChange.
    230   */
    231  Maybe<Digest> mDigest;
    232 
    233  //////////////////////////////////////////////////////////////////////////////
    234  //// Private methods
    235 
    236  /**
    237   * Called when NS_AsyncCopy completes.
    238   *
    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.
    243   */
    244  static void AsyncCopyCallback(void* aClosure, nsresult aStatus);
    245 
    246  /**
    247   * Called on the control thread after state changes, to ensure that the worker
    248   * thread will process the state change appropriately.
    249   *
    250   * @param aShouldInterruptCopy
    251   *        If true, the current NS_AsyncCopy, if any, is canceled.
    252   */
    253  nsresult GetWorkerThreadAttention(bool aShouldInterruptCopy);
    254 
    255  /**
    256   * Event called on the worker thread to begin processing a state change.
    257   */
    258  nsresult ProcessAttention();
    259 
    260  /**
    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.
    264   */
    265  nsresult ProcessStateChange();
    266 
    267  /**
    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.
    270   */
    271  bool CheckCompletion();
    272 
    273  /**
    274   * Event called on the control thread to indicate that file contents will now
    275   * be saved to the specified file.
    276   */
    277  nsresult NotifyTargetChange(nsIFile* aTarget);
    278 
    279  /**
    280   * Event called on the control thread to send the final notification.
    281   */
    282  nsresult NotifySaveComplete();
    283 
    284  /**
    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.
    288   */
    289  nsresult ExtractSignatureInfo(const nsAString& filePath);
    290 };
    291 
    292 ////////////////////////////////////////////////////////////////////////////////
    293 //// BackgroundFileSaverOutputStream
    294 
    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
    303 
    304  BackgroundFileSaverOutputStream();
    305 
    306 protected:
    307  virtual bool HasInfiniteBuffer() override;
    308  virtual nsAsyncCopyProgressFun GetProgressCallback() override;
    309 
    310 private:
    311  ~BackgroundFileSaverOutputStream() = default;
    312 
    313  /**
    314   * Original callback provided to our AsyncWait wrapper.
    315   */
    316  nsCOMPtr<nsIOutputStreamCallback> mAsyncWaitCallback;
    317 };
    318 
    319 ////////////////////////////////////////////////////////////////////////////////
    320 //// BackgroundFileSaverStreamListener. This class is instantiated by
    321 // nsExternalHelperAppService, DownloadCore.sys.mjs, and possibly others.
    322 
    323 class BackgroundFileSaverStreamListener final : public BackgroundFileSaver,
    324                                                public nsIStreamListener {
    325 public:
    326  NS_DECL_THREADSAFE_ISUPPORTS
    327  NS_DECL_NSIREQUESTOBSERVER
    328  NS_DECL_NSISTREAMLISTENER
    329 
    330  BackgroundFileSaverStreamListener() = default;
    331 
    332 protected:
    333  virtual bool HasInfiniteBuffer() override;
    334  virtual nsAsyncCopyProgressFun GetProgressCallback() override;
    335 
    336 private:
    337  ~BackgroundFileSaverStreamListener() = default;
    338 
    339  /**
    340   * Protects the state related to whether the request should be suspended.
    341   */
    342  mozilla::Mutex mSuspensionLock{
    343      "BackgroundFileSaverStreamListener.mSuspensionLock"};
    344 
    345  /**
    346   * Whether we should suspend the request because we received too much data.
    347   */
    348  bool mReceivedTooMuchData MOZ_GUARDED_BY(mSuspensionLock){false};
    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 MOZ_GUARDED_BY(mSuspensionLock);
    355 
    356  /**
    357   * Whether mRequest is currently suspended.
    358   */
    359  bool mRequestSuspended MOZ_GUARDED_BY(mSuspensionLock){false};
    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* aStream, Digest& aDigest);
    381 
    382 private:
    383  virtual ~DigestOutputStream() = default;
    384 
    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;
    389 
    390  // Don't accidentally copy construct.
    391  DigestOutputStream(const DigestOutputStream& d) = delete;
    392 };
    393 
    394 }  // namespace net
    395 }  // namespace mozilla
    396 
    397 #endif