1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/BackgroundFileSaver.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,418 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/** 1.11 + * This file defines two implementations of the nsIBackgroundFileSaver 1.12 + * interface. See the "test_backgroundfilesaver.js" file for usage examples. 1.13 + */ 1.14 + 1.15 +#ifndef BackgroundFileSaver_h__ 1.16 +#define BackgroundFileSaver_h__ 1.17 + 1.18 +#include "mozilla/Mutex.h" 1.19 +#include "nsCOMArray.h" 1.20 +#include "nsCOMPtr.h" 1.21 +#include "nsNSSShutDown.h" 1.22 +#include "nsIAsyncOutputStream.h" 1.23 +#include "nsIBackgroundFileSaver.h" 1.24 +#include "nsIStreamListener.h" 1.25 +#include "nsStreamUtils.h" 1.26 +#include "ScopedNSSTypes.h" 1.27 + 1.28 +class nsIAsyncInputStream; 1.29 +class nsIThread; 1.30 +class nsIX509CertList; 1.31 +class PRLogModuleInfo; 1.32 + 1.33 +namespace mozilla { 1.34 +namespace net { 1.35 + 1.36 +class DigestOutputStream; 1.37 + 1.38 +//////////////////////////////////////////////////////////////////////////////// 1.39 +//// BackgroundFileSaver 1.40 + 1.41 +class BackgroundFileSaver : public nsIBackgroundFileSaver, 1.42 + public nsNSSShutDownObject 1.43 +{ 1.44 +public: 1.45 + NS_DECL_NSIBACKGROUNDFILESAVER 1.46 + 1.47 + BackgroundFileSaver(); 1.48 + 1.49 + /** 1.50 + * Initializes the pipe and the worker thread on XPCOM construction. 1.51 + * 1.52 + * This is called automatically by the XPCOM infrastructure, and if this 1.53 + * fails, the factory will delete this object without returning a reference. 1.54 + */ 1.55 + nsresult Init(); 1.56 + 1.57 + /** 1.58 + * Used by nsNSSShutDownList to manage nsNSSShutDownObjects. 1.59 + */ 1.60 + void virtualDestroyNSSReference(); 1.61 + 1.62 + /** 1.63 + * Number of worker threads that are currently running. 1.64 + */ 1.65 + static uint32_t sThreadCount; 1.66 + 1.67 + /** 1.68 + * Maximum number of worker threads reached during the current download session, 1.69 + * used for telemetry. 1.70 + * 1.71 + * When there are no more worker threads running, we consider the download 1.72 + * session finished, and this counter is reset. 1.73 + */ 1.74 + static uint32_t sTelemetryMaxThreadCount; 1.75 + 1.76 + 1.77 +protected: 1.78 + virtual ~BackgroundFileSaver(); 1.79 + 1.80 + static PRLogModuleInfo *prlog; 1.81 + 1.82 + /** 1.83 + * Helper function for managing NSS objects (mDigestContext). 1.84 + */ 1.85 + void destructorSafeDestroyNSSReference(); 1.86 + 1.87 + /** 1.88 + * Thread that constructed this object. 1.89 + */ 1.90 + nsCOMPtr<nsIThread> mControlThread; 1.91 + 1.92 + /** 1.93 + * Thread to which the actual input/output is delegated. 1.94 + */ 1.95 + nsCOMPtr<nsIThread> mWorkerThread; 1.96 + 1.97 + /** 1.98 + * Stream that receives data from derived classes. The received data will be 1.99 + * available to the worker thread through mPipeInputStream. This is an 1.100 + * instance of nsPipeOutputStream, not BackgroundFileSaverOutputStream. 1.101 + */ 1.102 + nsCOMPtr<nsIAsyncOutputStream> mPipeOutputStream; 1.103 + 1.104 + /** 1.105 + * Used during initialization, determines if the pipe is created with an 1.106 + * infinite buffer. An infinite buffer is required if the derived class 1.107 + * implements nsIStreamListener, because this interface requires all the 1.108 + * provided data to be consumed synchronously. 1.109 + */ 1.110 + virtual bool HasInfiniteBuffer() = 0; 1.111 + 1.112 + /** 1.113 + * Used by derived classes if they need to be called back while copying. 1.114 + */ 1.115 + virtual nsAsyncCopyProgressFun GetProgressCallback() = 0; 1.116 + 1.117 + /** 1.118 + * Stream used by the worker thread to read the data to be saved. 1.119 + */ 1.120 + nsCOMPtr<nsIAsyncInputStream> mPipeInputStream; 1.121 + 1.122 +private: 1.123 + friend class NotifyTargetChangeRunnable; 1.124 + 1.125 + /** 1.126 + * Matches the nsIBackgroundFileSaver::observer property. 1.127 + * 1.128 + * @remarks This is a strong reference so that JavaScript callers don't need 1.129 + * to worry about keeping another reference to the observer. 1.130 + */ 1.131 + nsCOMPtr<nsIBackgroundFileSaverObserver> mObserver; 1.132 + 1.133 + ////////////////////////////////////////////////////////////////////////////// 1.134 + //// Shared state between control and worker threads 1.135 + 1.136 + /** 1.137 + * Protects the shared state between control and worker threads. This mutex 1.138 + * is always locked for a very short time, never during input/output. 1.139 + */ 1.140 + mozilla::Mutex mLock; 1.141 + 1.142 + /** 1.143 + * True if the worker thread is already waiting to process a change in state. 1.144 + */ 1.145 + bool mWorkerThreadAttentionRequested; 1.146 + 1.147 + /** 1.148 + * True if the operation should finish as soon as possibile. 1.149 + */ 1.150 + bool mFinishRequested; 1.151 + 1.152 + /** 1.153 + * True if the operation completed, with either success or failure. 1.154 + */ 1.155 + bool mComplete; 1.156 + 1.157 + /** 1.158 + * Holds the current file saver status. This is a success status while the 1.159 + * object is working correctly, and remains such if the operation completes 1.160 + * successfully. This becomes an error status when an error occurs on the 1.161 + * worker thread, or when the operation is canceled. 1.162 + */ 1.163 + nsresult mStatus; 1.164 + 1.165 + /** 1.166 + * True if we should append data to the initial target file, instead of 1.167 + * overwriting it. 1.168 + */ 1.169 + bool mAppend; 1.170 + 1.171 + /** 1.172 + * This is set by the first SetTarget call on the control thread, and contains 1.173 + * the target file name that will be used by the worker thread, as soon as it 1.174 + * is possible to update mActualTarget and open the file. This is null if no 1.175 + * target was ever assigned to this object. 1.176 + */ 1.177 + nsCOMPtr<nsIFile> mInitialTarget; 1.178 + 1.179 + /** 1.180 + * This is set by the first SetTarget call on the control thread, and 1.181 + * indicates whether mInitialTarget should be kept as partially completed, 1.182 + * rather than deleted, if the operation fails or is canceled. 1.183 + */ 1.184 + bool mInitialTargetKeepPartial; 1.185 + 1.186 + /** 1.187 + * This is set by subsequent SetTarget calls on the control thread, and 1.188 + * contains the new target file name to which the worker thread will move the 1.189 + * target file, as soon as it can be done. This is null if SetTarget was 1.190 + * called only once, or no target was ever assigned to this object. 1.191 + * 1.192 + * The target file can be renamed multiple times, though only the most recent 1.193 + * rename is guaranteed to be processed by the worker thread. 1.194 + */ 1.195 + nsCOMPtr<nsIFile> mRenamedTarget; 1.196 + 1.197 + /** 1.198 + * This is set by subsequent SetTarget calls on the control thread, and 1.199 + * indicates whether mRenamedTarget should be kept as partially completed, 1.200 + * rather than deleted, if the operation fails or is canceled. 1.201 + */ 1.202 + bool mRenamedTargetKeepPartial; 1.203 + 1.204 + /** 1.205 + * While NS_AsyncCopy is in progress, allows canceling it. Null otherwise. 1.206 + * This is read by both threads but only written by the worker thread. 1.207 + */ 1.208 + nsCOMPtr<nsISupports> mAsyncCopyContext; 1.209 + 1.210 + /** 1.211 + * The SHA 256 hash in raw bytes of the downloaded file. This is written 1.212 + * by the worker thread but can be read on the main thread. 1.213 + */ 1.214 + nsAutoCString mSha256; 1.215 + 1.216 + /** 1.217 + * Whether or not to compute the hash. Must be set on the main thread before 1.218 + * setTarget is called. 1.219 + */ 1.220 + bool mSha256Enabled; 1.221 + 1.222 + /** 1.223 + * Store the signature info. 1.224 + */ 1.225 + nsCOMArray<nsIX509CertList> mSignatureInfo; 1.226 + 1.227 + /** 1.228 + * Whether or not to extract the signature. Must be set on the main thread 1.229 + * before setTarget is called. 1.230 + */ 1.231 + bool mSignatureInfoEnabled; 1.232 + 1.233 + ////////////////////////////////////////////////////////////////////////////// 1.234 + //// State handled exclusively by the worker thread 1.235 + 1.236 + /** 1.237 + * Current target file associated to the input and output streams. 1.238 + */ 1.239 + nsCOMPtr<nsIFile> mActualTarget; 1.240 + 1.241 + /** 1.242 + * Indicates whether mActualTarget should be kept as partially completed, 1.243 + * rather than deleted, if the operation fails or is canceled. 1.244 + */ 1.245 + bool mActualTargetKeepPartial; 1.246 + 1.247 + /** 1.248 + * Used to calculate the file hash. This keeps state across file renames and 1.249 + * is lazily initialized in ProcessStateChange. 1.250 + */ 1.251 + ScopedPK11Context mDigestContext; 1.252 + 1.253 + ////////////////////////////////////////////////////////////////////////////// 1.254 + //// Private methods 1.255 + 1.256 + /** 1.257 + * Called when NS_AsyncCopy completes. 1.258 + * 1.259 + * @param aClosure 1.260 + * Populated with a raw pointer to the BackgroundFileSaver object. 1.261 + * @param aStatus 1.262 + * Success or failure status specified when the copy was interrupted. 1.263 + */ 1.264 + static void AsyncCopyCallback(void *aClosure, nsresult aStatus); 1.265 + 1.266 + /** 1.267 + * Called on the control thread after state changes, to ensure that the worker 1.268 + * thread will process the state change appropriately. 1.269 + * 1.270 + * @param aShouldInterruptCopy 1.271 + * If true, the current NS_AsyncCopy, if any, is canceled. 1.272 + */ 1.273 + nsresult GetWorkerThreadAttention(bool aShouldInterruptCopy); 1.274 + 1.275 + /** 1.276 + * Event called on the worker thread to begin processing a state change. 1.277 + */ 1.278 + nsresult ProcessAttention(); 1.279 + 1.280 + /** 1.281 + * Called by ProcessAttention to execute the operations corresponding to the 1.282 + * state change. If this results in an error, ProcessAttention will force the 1.283 + * entire operation to be aborted. 1.284 + */ 1.285 + nsresult ProcessStateChange(); 1.286 + 1.287 + /** 1.288 + * Returns true if completion conditions are met on the worker thread. The 1.289 + * first time this happens, posts the completion event to the control thread. 1.290 + */ 1.291 + bool CheckCompletion(); 1.292 + 1.293 + /** 1.294 + * Event called on the control thread to indicate that file contents will now 1.295 + * be saved to the specified file. 1.296 + */ 1.297 + nsresult NotifyTargetChange(nsIFile *aTarget); 1.298 + 1.299 + /** 1.300 + * Event called on the control thread to send the final notification. 1.301 + */ 1.302 + nsresult NotifySaveComplete(); 1.303 + 1.304 + /** 1.305 + * Verifies the signature of the binary at the specified file path and stores 1.306 + * the signature data in mSignatureInfo. We extract only X.509 certificates, 1.307 + * since that is what Google's Safebrowsing protocol specifies. 1.308 + */ 1.309 + nsresult ExtractSignatureInfo(const nsAString& filePath); 1.310 +}; 1.311 + 1.312 +//////////////////////////////////////////////////////////////////////////////// 1.313 +//// BackgroundFileSaverOutputStream 1.314 + 1.315 +class BackgroundFileSaverOutputStream : public BackgroundFileSaver 1.316 + , public nsIAsyncOutputStream 1.317 + , public nsIOutputStreamCallback 1.318 +{ 1.319 +public: 1.320 + NS_DECL_THREADSAFE_ISUPPORTS 1.321 + NS_DECL_NSIOUTPUTSTREAM 1.322 + NS_DECL_NSIASYNCOUTPUTSTREAM 1.323 + NS_DECL_NSIOUTPUTSTREAMCALLBACK 1.324 + 1.325 + BackgroundFileSaverOutputStream(); 1.326 + 1.327 +protected: 1.328 + virtual bool HasInfiniteBuffer() MOZ_OVERRIDE; 1.329 + virtual nsAsyncCopyProgressFun GetProgressCallback() MOZ_OVERRIDE; 1.330 + 1.331 +private: 1.332 + ~BackgroundFileSaverOutputStream(); 1.333 + 1.334 + /** 1.335 + * Original callback provided to our AsyncWait wrapper. 1.336 + */ 1.337 + nsCOMPtr<nsIOutputStreamCallback> mAsyncWaitCallback; 1.338 +}; 1.339 + 1.340 +//////////////////////////////////////////////////////////////////////////////// 1.341 +//// BackgroundFileSaverStreamListener. This class is instantiated by 1.342 +// nsExternalHelperAppService, DownloadCore.jsm, and possibly others. 1.343 + 1.344 +class BackgroundFileSaverStreamListener : public BackgroundFileSaver 1.345 + , public nsIStreamListener 1.346 +{ 1.347 +public: 1.348 + NS_DECL_THREADSAFE_ISUPPORTS 1.349 + NS_DECL_NSIREQUESTOBSERVER 1.350 + NS_DECL_NSISTREAMLISTENER 1.351 + 1.352 + BackgroundFileSaverStreamListener(); 1.353 + 1.354 +protected: 1.355 + virtual bool HasInfiniteBuffer() MOZ_OVERRIDE; 1.356 + virtual nsAsyncCopyProgressFun GetProgressCallback() MOZ_OVERRIDE; 1.357 + 1.358 +private: 1.359 + ~BackgroundFileSaverStreamListener(); 1.360 + 1.361 + /** 1.362 + * Protects the state related to whether the request should be suspended. 1.363 + */ 1.364 + mozilla::Mutex mSuspensionLock; 1.365 + 1.366 + /** 1.367 + * Whether we should suspend the request because we received too much data. 1.368 + */ 1.369 + bool mReceivedTooMuchData; 1.370 + 1.371 + /** 1.372 + * Request for which we received too much data. This is populated when 1.373 + * mReceivedTooMuchData becomes true for the first time. 1.374 + */ 1.375 + nsCOMPtr<nsIRequest> mRequest; 1.376 + 1.377 + /** 1.378 + * Whether mRequest is currently suspended. 1.379 + */ 1.380 + bool mRequestSuspended; 1.381 + 1.382 + /** 1.383 + * Called while NS_AsyncCopy is copying data. 1.384 + */ 1.385 + static void AsyncCopyProgressCallback(void *aClosure, uint32_t aCount); 1.386 + 1.387 + /** 1.388 + * Called on the control thread to suspend or resume the request. 1.389 + */ 1.390 + nsresult NotifySuspendOrResume(); 1.391 +}; 1.392 + 1.393 +// A wrapper around nsIOutputStream, so that we can compute hashes on the 1.394 +// stream without copying and without polluting pristine NSS code with XPCOM 1.395 +// interfaces. 1.396 +class DigestOutputStream : public nsNSSShutDownObject, 1.397 + public nsIOutputStream 1.398 +{ 1.399 +public: 1.400 + NS_DECL_THREADSAFE_ISUPPORTS 1.401 + NS_DECL_NSIOUTPUTSTREAM 1.402 + // Constructor. Neither parameter may be null. The caller owns both. 1.403 + DigestOutputStream(nsIOutputStream* outputStream, PK11Context* aContext); 1.404 + ~DigestOutputStream(); 1.405 + 1.406 + // We don't own any NSS objects here, so no need to clean up 1.407 + void virtualDestroyNSSReference() { } 1.408 + 1.409 +private: 1.410 + // Calls to write are passed to this stream. 1.411 + nsCOMPtr<nsIOutputStream> mOutputStream; 1.412 + // Digest context used to compute the hash, owned by the caller. 1.413 + PK11Context* mDigestContext; 1.414 + 1.415 + // Don't accidentally copy construct. 1.416 + DigestOutputStream(const DigestOutputStream& d); 1.417 +}; 1.418 +} // namespace net 1.419 +} // namespace mozilla 1.420 + 1.421 +#endif