123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /**
- * This file defines two implementations of the nsIBackgroundFileSaver
- * interface. See the "test_backgroundfilesaver.js" file for usage examples.
- */
- #ifndef BackgroundFileSaver_h__
- #define BackgroundFileSaver_h__
- #include "ScopedNSSTypes.h"
- #include "mozilla/Mutex.h"
- #include "nsCOMArray.h"
- #include "nsCOMPtr.h"
- #include "nsIAsyncOutputStream.h"
- #include "nsIBackgroundFileSaver.h"
- #include "nsIStreamListener.h"
- #include "nsNSSShutDown.h"
- #include "nsStreamUtils.h"
- #include "nsString.h"
- class nsIAsyncInputStream;
- class nsIThread;
- class nsIX509CertList;
- namespace mozilla {
- namespace net {
- class DigestOutputStream;
- ////////////////////////////////////////////////////////////////////////////////
- //// BackgroundFileSaver
- class BackgroundFileSaver : public nsIBackgroundFileSaver,
- public nsNSSShutDownObject
- {
- public:
- NS_DECL_NSIBACKGROUNDFILESAVER
- BackgroundFileSaver();
- /**
- * Initializes the pipe and the worker thread on XPCOM construction.
- *
- * This is called automatically by the XPCOM infrastructure, and if this
- * fails, the factory will delete this object without returning a reference.
- */
- nsresult Init();
- /**
- * Used by nsNSSShutDownList to manage nsNSSShutDownObjects.
- */
- void virtualDestroyNSSReference() override;
- /**
- * Number of worker threads that are currently running.
- */
- static uint32_t sThreadCount;
- protected:
- virtual ~BackgroundFileSaver();
- /**
- * Helper function for managing NSS objects (mDigestContext).
- */
- void destructorSafeDestroyNSSReference();
- /**
- * Thread that constructed this object.
- */
- nsCOMPtr<nsIThread> mControlThread;
- /**
- * Thread to which the actual input/output is delegated.
- */
- nsCOMPtr<nsIThread> mWorkerThread;
- /**
- * Stream that receives data from derived classes. The received data will be
- * available to the worker thread through mPipeInputStream. This is an
- * instance of nsPipeOutputStream, not BackgroundFileSaverOutputStream.
- */
- nsCOMPtr<nsIAsyncOutputStream> mPipeOutputStream;
- /**
- * Used during initialization, determines if the pipe is created with an
- * infinite buffer. An infinite buffer is required if the derived class
- * implements nsIStreamListener, because this interface requires all the
- * provided data to be consumed synchronously.
- */
- virtual bool HasInfiniteBuffer() = 0;
- /**
- * Used by derived classes if they need to be called back while copying.
- */
- virtual nsAsyncCopyProgressFun GetProgressCallback() = 0;
- /**
- * Stream used by the worker thread to read the data to be saved.
- */
- nsCOMPtr<nsIAsyncInputStream> mPipeInputStream;
- private:
- friend class NotifyTargetChangeRunnable;
- /**
- * Matches the nsIBackgroundFileSaver::observer property.
- *
- * @remarks This is a strong reference so that JavaScript callers don't need
- * to worry about keeping another reference to the observer.
- */
- nsCOMPtr<nsIBackgroundFileSaverObserver> mObserver;
- //////////////////////////////////////////////////////////////////////////////
- //// Shared state between control and worker threads
- /**
- * Protects the shared state between control and worker threads. This mutex
- * is always locked for a very short time, never during input/output.
- */
- mozilla::Mutex mLock;
- /**
- * True if the worker thread is already waiting to process a change in state.
- */
- bool mWorkerThreadAttentionRequested;
- /**
- * True if the operation should finish as soon as possibile.
- */
- bool mFinishRequested;
- /**
- * True if the operation completed, with either success or failure.
- */
- bool mComplete;
- /**
- * Holds the current file saver status. This is a success status while the
- * object is working correctly, and remains such if the operation completes
- * successfully. This becomes an error status when an error occurs on the
- * worker thread, or when the operation is canceled.
- */
- nsresult mStatus;
- /**
- * True if we should append data to the initial target file, instead of
- * overwriting it.
- */
- bool mAppend;
- /**
- * This is set by the first SetTarget call on the control thread, and contains
- * the target file name that will be used by the worker thread, as soon as it
- * is possible to update mActualTarget and open the file. This is null if no
- * target was ever assigned to this object.
- */
- nsCOMPtr<nsIFile> mInitialTarget;
- /**
- * This is set by the first SetTarget call on the control thread, and
- * indicates whether mInitialTarget should be kept as partially completed,
- * rather than deleted, if the operation fails or is canceled.
- */
- bool mInitialTargetKeepPartial;
- /**
- * This is set by subsequent SetTarget calls on the control thread, and
- * contains the new target file name to which the worker thread will move the
- * target file, as soon as it can be done. This is null if SetTarget was
- * called only once, or no target was ever assigned to this object.
- *
- * The target file can be renamed multiple times, though only the most recent
- * rename is guaranteed to be processed by the worker thread.
- */
- nsCOMPtr<nsIFile> mRenamedTarget;
- /**
- * This is set by subsequent SetTarget calls on the control thread, and
- * indicates whether mRenamedTarget should be kept as partially completed,
- * rather than deleted, if the operation fails or is canceled.
- */
- bool mRenamedTargetKeepPartial;
- /**
- * While NS_AsyncCopy is in progress, allows canceling it. Null otherwise.
- * This is read by both threads but only written by the worker thread.
- */
- nsCOMPtr<nsISupports> mAsyncCopyContext;
- /**
- * The SHA 256 hash in raw bytes of the downloaded file. This is written
- * by the worker thread but can be read on the main thread.
- */
- nsCString mSha256;
- /**
- * Whether or not to compute the hash. Must be set on the main thread before
- * setTarget is called.
- */
- bool mSha256Enabled;
- /**
- * Store the signature info.
- */
- nsCOMArray<nsIX509CertList> mSignatureInfo;
- /**
- * Whether or not to extract the signature. Must be set on the main thread
- * before setTarget is called.
- */
- bool mSignatureInfoEnabled;
- //////////////////////////////////////////////////////////////////////////////
- //// State handled exclusively by the worker thread
- /**
- * Current target file associated to the input and output streams.
- */
- nsCOMPtr<nsIFile> mActualTarget;
- /**
- * Indicates whether mActualTarget should be kept as partially completed,
- * rather than deleted, if the operation fails or is canceled.
- */
- bool mActualTargetKeepPartial;
- /**
- * Used to calculate the file hash. This keeps state across file renames and
- * is lazily initialized in ProcessStateChange.
- */
- UniquePK11Context mDigestContext;
- //////////////////////////////////////////////////////////////////////////////
- //// Private methods
- /**
- * Called when NS_AsyncCopy completes.
- *
- * @param aClosure
- * Populated with a raw pointer to the BackgroundFileSaver object.
- * @param aStatus
- * Success or failure status specified when the copy was interrupted.
- */
- static void AsyncCopyCallback(void *aClosure, nsresult aStatus);
- /**
- * Called on the control thread after state changes, to ensure that the worker
- * thread will process the state change appropriately.
- *
- * @param aShouldInterruptCopy
- * If true, the current NS_AsyncCopy, if any, is canceled.
- */
- nsresult GetWorkerThreadAttention(bool aShouldInterruptCopy);
- /**
- * Event called on the worker thread to begin processing a state change.
- */
- nsresult ProcessAttention();
- /**
- * Called by ProcessAttention to execute the operations corresponding to the
- * state change. If this results in an error, ProcessAttention will force the
- * entire operation to be aborted.
- */
- nsresult ProcessStateChange();
- /**
- * Returns true if completion conditions are met on the worker thread. The
- * first time this happens, posts the completion event to the control thread.
- */
- bool CheckCompletion();
- /**
- * Event called on the control thread to indicate that file contents will now
- * be saved to the specified file.
- */
- nsresult NotifyTargetChange(nsIFile *aTarget);
- /**
- * Event called on the control thread to send the final notification.
- */
- nsresult NotifySaveComplete();
- /**
- * Verifies the signature of the binary at the specified file path and stores
- * the signature data in mSignatureInfo. We extract only X.509 certificates,
- * since that is what Google's Safebrowsing protocol specifies.
- */
- nsresult ExtractSignatureInfo(const nsAString& filePath);
- };
- ////////////////////////////////////////////////////////////////////////////////
- //// BackgroundFileSaverOutputStream
- class BackgroundFileSaverOutputStream : public BackgroundFileSaver
- , public nsIAsyncOutputStream
- , public nsIOutputStreamCallback
- {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIOUTPUTSTREAM
- NS_DECL_NSIASYNCOUTPUTSTREAM
- NS_DECL_NSIOUTPUTSTREAMCALLBACK
- BackgroundFileSaverOutputStream();
- protected:
- virtual bool HasInfiniteBuffer() override;
- virtual nsAsyncCopyProgressFun GetProgressCallback() override;
- private:
- ~BackgroundFileSaverOutputStream();
- /**
- * Original callback provided to our AsyncWait wrapper.
- */
- nsCOMPtr<nsIOutputStreamCallback> mAsyncWaitCallback;
- };
- ////////////////////////////////////////////////////////////////////////////////
- //// BackgroundFileSaverStreamListener. This class is instantiated by
- // nsExternalHelperAppService, DownloadCore.jsm, and possibly others.
- class BackgroundFileSaverStreamListener final : public BackgroundFileSaver
- , public nsIStreamListener
- {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIREQUESTOBSERVER
- NS_DECL_NSISTREAMLISTENER
- BackgroundFileSaverStreamListener();
- protected:
- virtual bool HasInfiniteBuffer() override;
- virtual nsAsyncCopyProgressFun GetProgressCallback() override;
- private:
- ~BackgroundFileSaverStreamListener();
- /**
- * Protects the state related to whether the request should be suspended.
- */
- mozilla::Mutex mSuspensionLock;
- /**
- * Whether we should suspend the request because we received too much data.
- */
- bool mReceivedTooMuchData;
- /**
- * Request for which we received too much data. This is populated when
- * mReceivedTooMuchData becomes true for the first time.
- */
- nsCOMPtr<nsIRequest> mRequest;
- /**
- * Whether mRequest is currently suspended.
- */
- bool mRequestSuspended;
- /**
- * Called while NS_AsyncCopy is copying data.
- */
- static void AsyncCopyProgressCallback(void *aClosure, uint32_t aCount);
- /**
- * Called on the control thread to suspend or resume the request.
- */
- nsresult NotifySuspendOrResume();
- };
- // A wrapper around nsIOutputStream, so that we can compute hashes on the
- // stream without copying and without polluting pristine NSS code with XPCOM
- // interfaces.
- class DigestOutputStream : public nsNSSShutDownObject,
- public nsIOutputStream
- {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSIOUTPUTSTREAM
- // Constructor. Neither parameter may be null. The caller owns both.
- DigestOutputStream(nsIOutputStream* outputStream, PK11Context* aContext);
- // We don't own any NSS objects here, so no need to clean up
- void virtualDestroyNSSReference() override { }
- private:
- ~DigestOutputStream();
- // Calls to write are passed to this stream.
- nsCOMPtr<nsIOutputStream> mOutputStream;
- // Digest context used to compute the hash, owned by the caller.
- PK11Context* mDigestContext;
- // Don't accidentally copy construct.
- DigestOutputStream(const DigestOutputStream& d);
- };
- } // namespace net
- } // namespace mozilla
- #endif
|