netwerk/base/src/BackgroundFileSaver.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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.
    10  */
    12 #ifndef BackgroundFileSaver_h__
    13 #define BackgroundFileSaver_h__
    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"
    25 class nsIAsyncInputStream;
    26 class nsIThread;
    27 class nsIX509CertList;
    28 class PRLogModuleInfo;
    30 namespace mozilla {
    31 namespace net {
    33 class DigestOutputStream;
    35 ////////////////////////////////////////////////////////////////////////////////
    36 //// BackgroundFileSaver
    38 class BackgroundFileSaver : public nsIBackgroundFileSaver,
    39                             public nsNSSShutDownObject
    40 {
    41 public:
    42   NS_DECL_NSIBACKGROUNDFILESAVER
    44   BackgroundFileSaver();
    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();
    54   /**
    55    * Used by nsNSSShutDownList to manage nsNSSShutDownObjects.
    56    */
    57   void virtualDestroyNSSReference();
    59   /**
    60    * Number of worker threads that are currently running.
    61    */
    62   static uint32_t sThreadCount;
    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;
    74 protected:
    75   virtual ~BackgroundFileSaver();
    77   static PRLogModuleInfo *prlog;
    79   /**
    80    * Helper function for managing NSS objects (mDigestContext).
    81    */
    82   void destructorSafeDestroyNSSReference();
    84   /**
    85    * Thread that constructed this object.
    86    */
    87   nsCOMPtr<nsIThread> mControlThread;
    89   /**
    90    * Thread to which the actual input/output is delegated.
    91    */
    92   nsCOMPtr<nsIThread> mWorkerThread;
    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;
   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;
   109   /**
   110    * Used by derived classes if they need to be called back while copying.
   111    */
   112   virtual nsAsyncCopyProgressFun GetProgressCallback() = 0;
   114   /**
   115    * Stream used by the worker thread to read the data to be saved.
   116    */
   117   nsCOMPtr<nsIAsyncInputStream> mPipeInputStream;
   119 private:
   120   friend class NotifyTargetChangeRunnable;
   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;
   130   //////////////////////////////////////////////////////////////////////////////
   131   //// Shared state between control and worker threads
   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;
   139   /**
   140    * True if the worker thread is already waiting to process a change in state.
   141    */
   142   bool mWorkerThreadAttentionRequested;
   144   /**
   145    * True if the operation should finish as soon as possibile.
   146    */
   147   bool mFinishRequested;
   149   /**
   150    * True if the operation completed, with either success or failure.
   151    */
   152   bool mComplete;
   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;
   162   /**
   163    * True if we should append data to the initial target file, instead of
   164    * overwriting it.
   165    */
   166   bool mAppend;
   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;
   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;
   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;
   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;
   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;
   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;
   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;
   219   /**
   220    * Store the signature info.
   221    */
   222   nsCOMArray<nsIX509CertList> mSignatureInfo;
   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;
   230   //////////////////////////////////////////////////////////////////////////////
   231   //// State handled exclusively by the worker thread
   233   /**
   234    * Current target file associated to the input and output streams.
   235    */
   236   nsCOMPtr<nsIFile> mActualTarget;
   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;
   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;
   250   //////////////////////////////////////////////////////////////////////////////
   251   //// Private methods
   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);
   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);
   272   /**
   273    * Event called on the worker thread to begin processing a state change.
   274    */
   275   nsresult ProcessAttention();
   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();
   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();
   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);
   296   /**
   297    * Event called on the control thread to send the final notification.
   298    */
   299   nsresult NotifySaveComplete();
   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 };
   309 ////////////////////////////////////////////////////////////////////////////////
   310 //// BackgroundFileSaverOutputStream
   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
   322   BackgroundFileSaverOutputStream();
   324 protected:
   325   virtual bool HasInfiniteBuffer() MOZ_OVERRIDE;
   326   virtual nsAsyncCopyProgressFun GetProgressCallback() MOZ_OVERRIDE;
   328 private:
   329   ~BackgroundFileSaverOutputStream();
   331   /**
   332    * Original callback provided to our AsyncWait wrapper.
   333    */
   334   nsCOMPtr<nsIOutputStreamCallback> mAsyncWaitCallback;
   335 };
   337 ////////////////////////////////////////////////////////////////////////////////
   338 //// BackgroundFileSaverStreamListener. This class is instantiated by
   339 // nsExternalHelperAppService, DownloadCore.jsm, and possibly others.
   341 class BackgroundFileSaverStreamListener : public BackgroundFileSaver
   342                                         , public nsIStreamListener
   343 {
   344 public:
   345   NS_DECL_THREADSAFE_ISUPPORTS
   346   NS_DECL_NSIREQUESTOBSERVER
   347   NS_DECL_NSISTREAMLISTENER
   349   BackgroundFileSaverStreamListener();
   351 protected:
   352   virtual bool HasInfiniteBuffer() MOZ_OVERRIDE;
   353   virtual nsAsyncCopyProgressFun GetProgressCallback() MOZ_OVERRIDE;
   355 private:
   356   ~BackgroundFileSaverStreamListener();
   358   /**
   359    * Protects the state related to whether the request should be suspended.
   360    */
   361   mozilla::Mutex mSuspensionLock;
   363   /**
   364    * Whether we should suspend the request because we received too much data.
   365    */
   366   bool mReceivedTooMuchData;
   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;
   374   /**
   375    * Whether mRequest is currently suspended.
   376    */
   377   bool mRequestSuspended;
   379   /**
   380    * Called while NS_AsyncCopy is copying data.
   381    */
   382   static void AsyncCopyProgressCallback(void *aClosure, uint32_t aCount);
   384   /**
   385    * Called on the control thread to suspend or resume the request.
   386    */
   387   nsresult NotifySuspendOrResume();
   388 };
   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();
   403   // We don't own any NSS objects here, so no need to clean up
   404   void virtualDestroyNSSReference() { }
   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;
   412   // Don't accidentally copy construct.
   413   DigestOutputStream(const DigestOutputStream& d);
   414 };
   415 } // namespace net
   416 } // namespace mozilla
   418 #endif

mercurial