LCOV - code coverage report
Current view: top level - netwerk/base - BackgroundFileSaver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 426 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 69 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "BackgroundFileSaver.h"
       8             : 
       9             : #include "ScopedNSSTypes.h"
      10             : #include "mozilla/Casting.h"
      11             : #include "mozilla/Logging.h"
      12             : #include "mozilla/Telemetry.h"
      13             : #include "nsCOMArray.h"
      14             : #include "nsDependentSubstring.h"
      15             : #include "nsIAsyncInputStream.h"
      16             : #include "nsIFile.h"
      17             : #include "nsIMutableArray.h"
      18             : #include "nsIPipe.h"
      19             : #include "nsIX509Cert.h"
      20             : #include "nsIX509CertDB.h"
      21             : #include "nsIX509CertList.h"
      22             : #include "nsNetUtil.h"
      23             : #include "nsThreadUtils.h"
      24             : #include "pk11pub.h"
      25             : #include "secoidt.h"
      26             : 
      27             : #ifdef XP_WIN
      28             : #include <windows.h>
      29             : #include <softpub.h>
      30             : #include <wintrust.h>
      31             : #endif // XP_WIN
      32             : 
      33             : namespace mozilla {
      34             : namespace net {
      35             : 
      36             : // MOZ_LOG=BackgroundFileSaver:5
      37             : static LazyLogModule prlog("BackgroundFileSaver");
      38             : #define LOG(args) MOZ_LOG(prlog, mozilla::LogLevel::Debug, args)
      39             : #define LOG_ENABLED() MOZ_LOG_TEST(prlog, mozilla::LogLevel::Debug)
      40             : 
      41             : ////////////////////////////////////////////////////////////////////////////////
      42             : //// Globals
      43             : 
      44             : /**
      45             :  * Buffer size for writing to the output file or reading from the input file.
      46             :  */
      47             : #define BUFFERED_IO_SIZE (1024 * 32)
      48             : 
      49             : /**
      50             :  * When this upper limit is reached, the original request is suspended.
      51             :  */
      52             : #define REQUEST_SUSPEND_AT (1024 * 1024 * 4)
      53             : 
      54             : /**
      55             :  * When this lower limit is reached, the original request is resumed.
      56             :  */
      57             : #define REQUEST_RESUME_AT (1024 * 1024 * 2)
      58             : 
      59             : ////////////////////////////////////////////////////////////////////////////////
      60             : //// NotifyTargetChangeRunnable
      61             : 
      62             : /**
      63             :  * Runnable object used to notify the control thread that file contents will now
      64             :  * be saved to the specified file.
      65             :  */
      66           0 : class NotifyTargetChangeRunnable final : public Runnable
      67             : {
      68             : public:
      69           0 :   NotifyTargetChangeRunnable(BackgroundFileSaver* aSaver, nsIFile* aTarget)
      70           0 :     : Runnable("net::NotifyTargetChangeRunnable")
      71             :     , mSaver(aSaver)
      72           0 :     , mTarget(aTarget)
      73             :   {
      74           0 :   }
      75             : 
      76           0 :   NS_IMETHOD Run() override
      77             :   {
      78           0 :     return mSaver->NotifyTargetChange(mTarget);
      79             :   }
      80             : 
      81             : private:
      82             :   RefPtr<BackgroundFileSaver> mSaver;
      83             :   nsCOMPtr<nsIFile> mTarget;
      84             : };
      85             : 
      86             : ////////////////////////////////////////////////////////////////////////////////
      87             : //// BackgroundFileSaver
      88             : 
      89             : uint32_t BackgroundFileSaver::sThreadCount = 0;
      90             : uint32_t BackgroundFileSaver::sTelemetryMaxThreadCount = 0;
      91             : 
      92           0 : BackgroundFileSaver::BackgroundFileSaver()
      93             : : mControlEventTarget(nullptr)
      94             : , mWorkerThread(nullptr)
      95             : , mPipeOutputStream(nullptr)
      96             : , mPipeInputStream(nullptr)
      97             : , mObserver(nullptr)
      98             : , mLock("BackgroundFileSaver.mLock")
      99             : , mWorkerThreadAttentionRequested(false)
     100             : , mFinishRequested(false)
     101             : , mComplete(false)
     102             : , mStatus(NS_OK)
     103             : , mAppend(false)
     104             : , mInitialTarget(nullptr)
     105             : , mInitialTargetKeepPartial(false)
     106             : , mRenamedTarget(nullptr)
     107             : , mRenamedTargetKeepPartial(false)
     108             : , mAsyncCopyContext(nullptr)
     109             : , mSha256Enabled(false)
     110             : , mSignatureInfoEnabled(false)
     111             : , mActualTarget(nullptr)
     112             : , mActualTargetKeepPartial(false)
     113           0 : , mDigestContext(nullptr)
     114             : {
     115           0 :   LOG(("Created BackgroundFileSaver [this = %p]", this));
     116           0 : }
     117             : 
     118           0 : BackgroundFileSaver::~BackgroundFileSaver()
     119             : {
     120           0 :   LOG(("Destroying BackgroundFileSaver [this = %p]", this));
     121           0 :   nsNSSShutDownPreventionLock lock;
     122           0 :   if (isAlreadyShutDown()) {
     123           0 :     return;
     124             :   }
     125           0 :   destructorSafeDestroyNSSReference();
     126           0 :   shutdown(ShutdownCalledFrom::Object);
     127           0 : }
     128             : 
     129             : void
     130           0 : BackgroundFileSaver::destructorSafeDestroyNSSReference()
     131             : {
     132           0 :   mDigestContext = nullptr;
     133           0 : }
     134             : 
     135             : void
     136           0 : BackgroundFileSaver::virtualDestroyNSSReference()
     137             : {
     138           0 :   destructorSafeDestroyNSSReference();
     139           0 : }
     140             : 
     141             : // Called on the control thread.
     142             : nsresult
     143           0 : BackgroundFileSaver::Init()
     144             : {
     145           0 :   MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
     146             : 
     147             :   nsresult rv;
     148             : 
     149           0 :   rv = NS_NewPipe2(getter_AddRefs(mPipeInputStream),
     150           0 :                    getter_AddRefs(mPipeOutputStream), true, true, 0,
     151           0 :                    HasInfiniteBuffer() ? UINT32_MAX : 0);
     152           0 :   NS_ENSURE_SUCCESS(rv, rv);
     153             : 
     154           0 :   mControlEventTarget = GetCurrentThreadEventTarget();
     155           0 :   NS_ENSURE_TRUE(mControlEventTarget, NS_ERROR_NOT_INITIALIZED);
     156             : 
     157           0 :   rv = NS_NewNamedThread("BgFileSaver", getter_AddRefs(mWorkerThread));
     158           0 :   NS_ENSURE_SUCCESS(rv, rv);
     159             : 
     160           0 :   sThreadCount++;
     161           0 :   if (sThreadCount > sTelemetryMaxThreadCount) {
     162           0 :     sTelemetryMaxThreadCount = sThreadCount;
     163             :   }
     164             : 
     165           0 :   return NS_OK;
     166             : }
     167             : 
     168             : // Called on the control thread.
     169             : NS_IMETHODIMP
     170           0 : BackgroundFileSaver::GetObserver(nsIBackgroundFileSaverObserver **aObserver)
     171             : {
     172           0 :   NS_ENSURE_ARG_POINTER(aObserver);
     173           0 :   *aObserver = mObserver;
     174           0 :   NS_IF_ADDREF(*aObserver);
     175           0 :   return NS_OK;
     176             : }
     177             : 
     178             : // Called on the control thread.
     179             : NS_IMETHODIMP
     180           0 : BackgroundFileSaver::SetObserver(nsIBackgroundFileSaverObserver *aObserver)
     181             : {
     182           0 :   mObserver = aObserver;
     183           0 :   return NS_OK;
     184             : }
     185             : 
     186             : // Called on the control thread.
     187             : NS_IMETHODIMP
     188           0 : BackgroundFileSaver::EnableAppend()
     189             : {
     190           0 :   MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
     191             : 
     192           0 :   MutexAutoLock lock(mLock);
     193           0 :   mAppend = true;
     194             : 
     195           0 :   return NS_OK;
     196             : }
     197             : 
     198             : // Called on the control thread.
     199             : NS_IMETHODIMP
     200           0 : BackgroundFileSaver::SetTarget(nsIFile *aTarget, bool aKeepPartial)
     201             : {
     202           0 :   NS_ENSURE_ARG(aTarget);
     203             :   {
     204           0 :     MutexAutoLock lock(mLock);
     205           0 :     if (!mInitialTarget) {
     206           0 :       aTarget->Clone(getter_AddRefs(mInitialTarget));
     207           0 :       mInitialTargetKeepPartial = aKeepPartial;
     208             :     } else {
     209           0 :       aTarget->Clone(getter_AddRefs(mRenamedTarget));
     210           0 :       mRenamedTargetKeepPartial = aKeepPartial;
     211             :     }
     212             :   }
     213             : 
     214             :   // After the worker thread wakes up because attention is requested, it will
     215             :   // rename or create the target file as requested, and start copying data.
     216           0 :   return GetWorkerThreadAttention(true);
     217             : }
     218             : 
     219             : // Called on the control thread.
     220             : NS_IMETHODIMP
     221           0 : BackgroundFileSaver::Finish(nsresult aStatus)
     222             : {
     223             :   nsresult rv;
     224             : 
     225             :   // This will cause the NS_AsyncCopy operation, if it's in progress, to consume
     226             :   // all the data that is still in the pipe, and then finish.
     227           0 :   rv = mPipeOutputStream->Close();
     228           0 :   NS_ENSURE_SUCCESS(rv, rv);
     229             : 
     230             :   // Ensure that, when we get attention from the worker thread, if no pending
     231             :   // rename operation is waiting, the operation will complete.
     232             :   {
     233           0 :     MutexAutoLock lock(mLock);
     234           0 :     mFinishRequested = true;
     235           0 :     if (NS_SUCCEEDED(mStatus)) {
     236           0 :       mStatus = aStatus;
     237             :     }
     238             :   }
     239             : 
     240             :   // After the worker thread wakes up because attention is requested, it will
     241             :   // process the completion conditions, detect that completion is requested, and
     242             :   // notify the main thread of the completion.  If this function was called with
     243             :   // a success code, we wait for the copy to finish before processing the
     244             :   // completion conditions, otherwise we interrupt the copy immediately.
     245           0 :   return GetWorkerThreadAttention(NS_FAILED(aStatus));
     246             : }
     247             : 
     248             : NS_IMETHODIMP
     249           0 : BackgroundFileSaver::EnableSha256()
     250             : {
     251           0 :   MOZ_ASSERT(NS_IsMainThread(),
     252             :              "Can't enable sha256 or initialize NSS off the main thread");
     253             :   // Ensure Personal Security Manager is initialized. This is required for
     254             :   // PK11_* operations to work.
     255             :   nsresult rv;
     256           0 :   nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &rv);
     257           0 :   NS_ENSURE_SUCCESS(rv, rv);
     258           0 :   mSha256Enabled = true;
     259           0 :   return NS_OK;
     260             : }
     261             : 
     262             : NS_IMETHODIMP
     263           0 : BackgroundFileSaver::GetSha256Hash(nsACString& aHash)
     264             : {
     265           0 :   MOZ_ASSERT(NS_IsMainThread(), "Can't inspect sha256 off the main thread");
     266             :   // We acquire a lock because mSha256 is written on the worker thread.
     267           0 :   MutexAutoLock lock(mLock);
     268           0 :   if (mSha256.IsEmpty()) {
     269           0 :     return NS_ERROR_NOT_AVAILABLE;
     270             :   }
     271           0 :   aHash = mSha256;
     272           0 :   return NS_OK;
     273             : }
     274             : 
     275             : NS_IMETHODIMP
     276           0 : BackgroundFileSaver::EnableSignatureInfo()
     277             : {
     278           0 :   MOZ_ASSERT(NS_IsMainThread(),
     279             :              "Can't enable signature extraction off the main thread");
     280             :   // Ensure Personal Security Manager is initialized.
     281             :   nsresult rv;
     282           0 :   nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &rv);
     283           0 :   NS_ENSURE_SUCCESS(rv, rv);
     284           0 :   mSignatureInfoEnabled = true;
     285           0 :   return NS_OK;
     286             : }
     287             : 
     288             : NS_IMETHODIMP
     289           0 : BackgroundFileSaver::GetSignatureInfo(nsIArray** aSignatureInfo)
     290             : {
     291           0 :   MOZ_ASSERT(NS_IsMainThread(), "Can't inspect signature off the main thread");
     292             :   // We acquire a lock because mSignatureInfo is written on the worker thread.
     293           0 :   MutexAutoLock lock(mLock);
     294           0 :   if (!mComplete || !mSignatureInfoEnabled) {
     295           0 :     return NS_ERROR_NOT_AVAILABLE;
     296             :   }
     297           0 :   nsCOMPtr<nsIMutableArray> sigArray = do_CreateInstance(NS_ARRAY_CONTRACTID);
     298           0 :   for (int i = 0; i < mSignatureInfo.Count(); ++i) {
     299           0 :     sigArray->AppendElement(mSignatureInfo[i], false);
     300             :   }
     301           0 :   *aSignatureInfo = sigArray;
     302           0 :   NS_IF_ADDREF(*aSignatureInfo);
     303           0 :   return NS_OK;
     304             : }
     305             : 
     306             : // Called on the control thread.
     307             : nsresult
     308           0 : BackgroundFileSaver::GetWorkerThreadAttention(bool aShouldInterruptCopy)
     309             : {
     310             :   nsresult rv;
     311             : 
     312           0 :   MutexAutoLock lock(mLock);
     313             : 
     314             :   // We only require attention one time.  If this function is called two times
     315             :   // before the worker thread wakes up, and the first has aShouldInterruptCopy
     316             :   // false and the second true, we won't forcibly interrupt the copy from the
     317             :   // control thread.  However, that never happens, because calling Finish with a
     318             :   // success code is the only case that may result in aShouldInterruptCopy being
     319             :   // false.  In that case, we won't call this function again, because consumers
     320             :   // should not invoke other methods on the control thread after calling Finish.
     321             :   // And in any case, Finish already closes one end of the pipe, causing the
     322             :   // copy to finish properly on its own.
     323           0 :   if (mWorkerThreadAttentionRequested) {
     324           0 :     return NS_OK;
     325             :   }
     326             : 
     327           0 :   if (!mAsyncCopyContext) {
     328             :     // Copy is not in progress, post an event to handle the change manually.
     329           0 :     rv = mWorkerThread->Dispatch(
     330           0 :       NewRunnableMethod("net::BackgroundFileSaver::ProcessAttention",
     331             :                         this,
     332             :                         &BackgroundFileSaver::ProcessAttention),
     333           0 :       NS_DISPATCH_NORMAL);
     334           0 :     NS_ENSURE_SUCCESS(rv, rv);
     335           0 :   } else if (aShouldInterruptCopy) {
     336             :     // Interrupt the copy.  The copy will be resumed, if needed, by the
     337             :     // ProcessAttention function, invoked by the AsyncCopyCallback function.
     338           0 :     NS_CancelAsyncCopy(mAsyncCopyContext, NS_ERROR_ABORT);
     339             :   }
     340             : 
     341             :   // Indicate that attention has been requested successfully, there is no need
     342             :   // to post another event until the worker thread processes the current one.
     343           0 :   mWorkerThreadAttentionRequested = true;
     344             : 
     345           0 :   return NS_OK;
     346             : }
     347             : 
     348             : // Called on the worker thread.
     349             : // static
     350             : void
     351           0 : BackgroundFileSaver::AsyncCopyCallback(void *aClosure, nsresult aStatus)
     352             : {
     353           0 :   BackgroundFileSaver *self = (BackgroundFileSaver *)aClosure;
     354             :   {
     355           0 :     MutexAutoLock lock(self->mLock);
     356             : 
     357             :     // Now that the copy was interrupted or terminated, any notification from
     358             :     // the control thread requires an event to be posted to the worker thread.
     359           0 :     self->mAsyncCopyContext = nullptr;
     360             : 
     361             :     // When detecting failures, ignore the status code we use to interrupt.
     362           0 :     if (NS_FAILED(aStatus) && aStatus != NS_ERROR_ABORT &&
     363           0 :         NS_SUCCEEDED(self->mStatus)) {
     364           0 :       self->mStatus = aStatus;
     365             :     }
     366             :   }
     367             : 
     368           0 :   (void)self->ProcessAttention();
     369             : 
     370             :   // We called NS_ADDREF_THIS when NS_AsyncCopy started, to keep the object
     371             :   // alive even if other references disappeared.  At this point, we've finished
     372             :   // using the object and can safely release our reference.
     373           0 :   NS_RELEASE(self);
     374           0 : }
     375             : 
     376             : // Called on the worker thread.
     377             : nsresult
     378           0 : BackgroundFileSaver::ProcessAttention()
     379             : {
     380             :   nsresult rv;
     381             : 
     382             :   // This function is called whenever the attention of the worker thread has
     383             :   // been requested.  This may happen in these cases:
     384             :   // * We are about to start the copy for the first time.  In this case, we are
     385             :   //   called from an event posted on the worker thread from the control thread
     386             :   //   by GetWorkerThreadAttention, and mAsyncCopyContext is null.
     387             :   // * We have interrupted the copy for some reason.  In this case, we are
     388             :   //   called by AsyncCopyCallback, and mAsyncCopyContext is null.
     389             :   // * We are currently executing ProcessStateChange, and attention is requested
     390             :   //   by the control thread, for example because SetTarget or Finish have been
     391             :   //   called.  In this case, we are called from from an event posted through
     392             :   //   GetWorkerThreadAttention.  While mAsyncCopyContext was always null when
     393             :   //   the event was posted, at this point mAsyncCopyContext may not be null
     394             :   //   anymore, because ProcessStateChange may have started the copy before the
     395             :   //   event that called this function was processed on the worker thread.
     396             :   // If mAsyncCopyContext is not null, we interrupt the copy and re-enter
     397             :   // through AsyncCopyCallback.  This allows us to check if, for instance, we
     398             :   // should rename the target file.  We will then restart the copy if needed.
     399           0 :   if (mAsyncCopyContext) {
     400           0 :     NS_CancelAsyncCopy(mAsyncCopyContext, NS_ERROR_ABORT);
     401           0 :     return NS_OK;
     402             :   }
     403             :   // Use the current shared state to determine the next operation to execute.
     404           0 :   rv = ProcessStateChange();
     405           0 :   if (NS_FAILED(rv)) {
     406             :     // If something failed while processing, terminate the operation now.
     407             :     {
     408           0 :       MutexAutoLock lock(mLock);
     409             : 
     410           0 :       if (NS_SUCCEEDED(mStatus)) {
     411           0 :         mStatus = rv;
     412             :       }
     413             :     }
     414             :     // Ensure we notify completion now that the operation failed.
     415           0 :     CheckCompletion();
     416             :   }
     417             : 
     418           0 :   return NS_OK;
     419             : }
     420             : 
     421             : // Called on the worker thread.
     422             : nsresult
     423           0 : BackgroundFileSaver::ProcessStateChange()
     424             : {
     425             :   nsresult rv;
     426             : 
     427             :   // We might have been notified because the operation is complete, verify.
     428           0 :   if (CheckCompletion()) {
     429           0 :     return NS_OK;
     430             :   }
     431             : 
     432             :   // Get a copy of the current shared state for the worker thread.
     433           0 :   nsCOMPtr<nsIFile> initialTarget;
     434             :   bool initialTargetKeepPartial;
     435           0 :   nsCOMPtr<nsIFile> renamedTarget;
     436             :   bool renamedTargetKeepPartial;
     437             :   bool sha256Enabled;
     438             :   bool append;
     439             :   {
     440           0 :     MutexAutoLock lock(mLock);
     441             : 
     442           0 :     initialTarget = mInitialTarget;
     443           0 :     initialTargetKeepPartial = mInitialTargetKeepPartial;
     444           0 :     renamedTarget = mRenamedTarget;
     445           0 :     renamedTargetKeepPartial = mRenamedTargetKeepPartial;
     446           0 :     sha256Enabled = mSha256Enabled;
     447           0 :     append = mAppend;
     448             : 
     449             :     // From now on, another attention event needs to be posted if state changes.
     450           0 :     mWorkerThreadAttentionRequested = false;
     451             :   }
     452             : 
     453             :   // The initial target can only be null if it has never been assigned.  In this
     454             :   // case, there is nothing to do since we never created any output file.
     455           0 :   if (!initialTarget) {
     456           0 :     return NS_OK;
     457             :   }
     458             : 
     459             :   // Determine if we are processing the attention request for the first time.
     460           0 :   bool isContinuation = !!mActualTarget;
     461           0 :   if (!isContinuation) {
     462             :     // Assign the target file for the first time.
     463           0 :     mActualTarget = initialTarget;
     464           0 :     mActualTargetKeepPartial = initialTargetKeepPartial;
     465             :   }
     466             : 
     467             :   // Verify whether we have actually been instructed to use a different file.
     468             :   // This may happen the first time this function is executed, if SetTarget was
     469             :   // called two times before the worker thread processed the attention request.
     470           0 :   bool equalToCurrent = false;
     471           0 :   if (renamedTarget) {
     472           0 :     rv = mActualTarget->Equals(renamedTarget, &equalToCurrent);
     473           0 :     NS_ENSURE_SUCCESS(rv, rv);
     474           0 :     if (!equalToCurrent)
     475             :     {
     476             :       // If we were asked to rename the file but the initial file did not exist,
     477             :       // we simply create the file in the renamed location.  We avoid this check
     478             :       // if we have already started writing the output file ourselves.
     479           0 :       bool exists = true;
     480           0 :       if (!isContinuation) {
     481           0 :         rv = mActualTarget->Exists(&exists);
     482           0 :         NS_ENSURE_SUCCESS(rv, rv);
     483             :       }
     484           0 :       if (exists) {
     485             :         // We are moving the previous target file to a different location.
     486           0 :         nsCOMPtr<nsIFile> renamedTargetParentDir;
     487           0 :         rv = renamedTarget->GetParent(getter_AddRefs(renamedTargetParentDir));
     488           0 :         NS_ENSURE_SUCCESS(rv, rv);
     489             : 
     490           0 :         nsAutoString renamedTargetName;
     491           0 :         rv = renamedTarget->GetLeafName(renamedTargetName);
     492           0 :         NS_ENSURE_SUCCESS(rv, rv);
     493             : 
     494             :         // We must delete any existing target file before moving the current
     495             :         // one.
     496           0 :         rv = renamedTarget->Exists(&exists);
     497           0 :         NS_ENSURE_SUCCESS(rv, rv);
     498           0 :         if (exists) {
     499           0 :           rv = renamedTarget->Remove(false);
     500           0 :           NS_ENSURE_SUCCESS(rv, rv);
     501             :         }
     502             : 
     503             :         // Move the file.  If this fails, we still reference the original file
     504             :         // in mActualTarget, so that it is deleted if requested.  If this
     505             :         // succeeds, the nsIFile instance referenced by mActualTarget mutates
     506             :         // and starts pointing to the new file, but we'll discard the reference.
     507           0 :         rv = mActualTarget->MoveTo(renamedTargetParentDir, renamedTargetName);
     508           0 :         NS_ENSURE_SUCCESS(rv, rv);
     509             :       }
     510             : 
     511             :       // Now we can update the actual target file name.
     512           0 :       mActualTarget = renamedTarget;
     513           0 :       mActualTargetKeepPartial = renamedTargetKeepPartial;
     514             :     }
     515             :   }
     516             : 
     517             :   // Notify if the target file name actually changed.
     518           0 :   if (!equalToCurrent) {
     519             :     // We must clone the nsIFile instance because mActualTarget is not
     520             :     // immutable, it may change if the target is renamed later.
     521           0 :     nsCOMPtr<nsIFile> actualTargetToNotify;
     522           0 :     rv = mActualTarget->Clone(getter_AddRefs(actualTargetToNotify));
     523           0 :     NS_ENSURE_SUCCESS(rv, rv);
     524             : 
     525             :     RefPtr<NotifyTargetChangeRunnable> event =
     526           0 :       new NotifyTargetChangeRunnable(this, actualTargetToNotify);
     527           0 :     NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
     528             : 
     529           0 :     rv = mControlEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
     530           0 :     NS_ENSURE_SUCCESS(rv, rv);
     531             :   }
     532             : 
     533           0 :   if (isContinuation) {
     534             :     // The pending rename operation might be the last task before finishing. We
     535             :     // may return here only if we have already created the target file.
     536           0 :     if (CheckCompletion()) {
     537           0 :       return NS_OK;
     538             :     }
     539             : 
     540             :     // Even if the operation did not complete, the pipe input stream may be
     541             :     // empty and may have been closed already.  We detect this case using the
     542             :     // Available property, because it never returns an error if there is more
     543             :     // data to be consumed.  If the pipe input stream is closed, we just exit
     544             :     // and wait for more calls like SetTarget or Finish to be invoked on the
     545             :     // control thread.  However, we still truncate the file or create the
     546             :     // initial digest context if we are expected to do that.
     547             :     uint64_t available;
     548           0 :     rv = mPipeInputStream->Available(&available);
     549           0 :     if (NS_FAILED(rv)) {
     550           0 :       return NS_OK;
     551             :     }
     552             :   }
     553             : 
     554             :   // Create the digest context if requested and NSS hasn't been shut down.
     555           0 :   if (sha256Enabled && !mDigestContext) {
     556           0 :     nsNSSShutDownPreventionLock lock;
     557           0 :     if (!isAlreadyShutDown()) {
     558           0 :       mDigestContext = UniquePK11Context(
     559           0 :         PK11_CreateDigestContext(SEC_OID_SHA256));
     560           0 :       NS_ENSURE_TRUE(mDigestContext, NS_ERROR_OUT_OF_MEMORY);
     561             :     }
     562             :   }
     563             : 
     564             :   // When we are requested to append to an existing file, we should read the
     565             :   // existing data and ensure we include it as part of the final hash.
     566           0 :   if (mDigestContext && append && !isContinuation) {
     567           0 :     nsCOMPtr<nsIInputStream> inputStream;
     568           0 :     rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
     569             :                                     mActualTarget,
     570           0 :                                     PR_RDONLY | nsIFile::OS_READAHEAD);
     571           0 :     if (rv != NS_ERROR_FILE_NOT_FOUND) {
     572           0 :       NS_ENSURE_SUCCESS(rv, rv);
     573             : 
     574             :       char buffer[BUFFERED_IO_SIZE];
     575             :       while (true) {
     576             :         uint32_t count;
     577           0 :         rv = inputStream->Read(buffer, BUFFERED_IO_SIZE, &count);
     578           0 :         NS_ENSURE_SUCCESS(rv, rv);
     579             : 
     580           0 :         if (count == 0) {
     581             :           // We reached the end of the file.
     582           0 :           break;
     583             :         }
     584             : 
     585           0 :         nsNSSShutDownPreventionLock lock;
     586           0 :         if (isAlreadyShutDown()) {
     587           0 :           return NS_ERROR_NOT_AVAILABLE;
     588             :         }
     589             : 
     590           0 :         nsresult rv = MapSECStatus(
     591             :           PK11_DigestOp(mDigestContext.get(),
     592           0 :                         BitwiseCast<unsigned char*, char*>(buffer),
     593           0 :                         count));
     594           0 :         NS_ENSURE_SUCCESS(rv, rv);
     595           0 :       }
     596             : 
     597           0 :       rv = inputStream->Close();
     598           0 :       NS_ENSURE_SUCCESS(rv, rv);
     599             :     }
     600             :   }
     601             : 
     602             :   // We will append to the initial target file only if it was requested by the
     603             :   // caller, but we'll always append on subsequent accesses to the target file.
     604             :   int32_t creationIoFlags;
     605           0 :   if (isContinuation) {
     606           0 :     creationIoFlags = PR_APPEND;
     607             :   } else {
     608           0 :     creationIoFlags = (append ? PR_APPEND : PR_TRUNCATE) | PR_CREATE_FILE;
     609             :   }
     610             : 
     611             :   // Create the target file, or append to it if we already started writing it.
     612             :   // The 0600 permissions are used while the file is being downloaded, and for
     613             :   // interrupted downloads. Those may be located in the system temporary
     614             :   // directory, as well as the target directory, and generally have a ".part"
     615             :   // extension. Those part files should never be group or world-writable even
     616             :   // if the umask allows it.
     617           0 :   nsCOMPtr<nsIOutputStream> outputStream;
     618           0 :   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
     619             :                                    mActualTarget,
     620           0 :                                    PR_WRONLY | creationIoFlags, 0600);
     621           0 :   NS_ENSURE_SUCCESS(rv, rv);
     622             : 
     623           0 :   outputStream = NS_BufferOutputStream(outputStream, BUFFERED_IO_SIZE);
     624           0 :   if (!outputStream) {
     625           0 :     return NS_ERROR_FAILURE;
     626             :   }
     627             : 
     628             :   // Wrap the output stream so that it feeds the digest context if needed.
     629           0 :   if (mDigestContext) {
     630             :     // No need to acquire the NSS lock here, DigestOutputStream must acquire it
     631             :     // in any case before each asynchronous write. Constructing the
     632             :     // DigestOutputStream cannot fail. Passing mDigestContext to
     633             :     // DigestOutputStream is safe, because BackgroundFileSaver always outlives
     634             :     // the outputStream. BackgroundFileSaver is reference-counted before the
     635             :     // call to AsyncCopy, and mDigestContext is never destroyed before
     636             :     // AsyncCopyCallback.
     637           0 :     outputStream = new DigestOutputStream(outputStream, mDigestContext.get());
     638             :   }
     639             : 
     640             :   // Start copying our input to the target file.  No errors can be raised past
     641             :   // this point if the copy starts, since they should be handled by the thread.
     642             :   {
     643           0 :     MutexAutoLock lock(mLock);
     644             : 
     645           0 :     rv = NS_AsyncCopy(mPipeInputStream, outputStream, mWorkerThread,
     646             :                       NS_ASYNCCOPY_VIA_READSEGMENTS, 4096, AsyncCopyCallback,
     647           0 :                       this, false, true, getter_AddRefs(mAsyncCopyContext),
     648           0 :                       GetProgressCallback());
     649           0 :     if (NS_FAILED(rv)) {
     650           0 :       NS_WARNING("NS_AsyncCopy failed.");
     651           0 :       mAsyncCopyContext = nullptr;
     652           0 :       return rv;
     653             :     }
     654             :   }
     655             : 
     656             :   // If the operation succeeded, we must ensure that we keep this object alive
     657             :   // for the entire duration of the copy, since only the raw pointer will be
     658             :   // provided as the argument of the AsyncCopyCallback function.  We can add the
     659             :   // reference now, after NS_AsyncCopy returned, because it always starts
     660             :   // processing asynchronously, and there is no risk that the callback is
     661             :   // invoked before we reach this point.  If the operation failed instead, then
     662             :   // AsyncCopyCallback will never be called.
     663           0 :   NS_ADDREF_THIS();
     664             : 
     665           0 :   return NS_OK;
     666             : }
     667             : 
     668             : // Called on the worker thread.
     669             : bool
     670           0 : BackgroundFileSaver::CheckCompletion()
     671             : {
     672             :   nsresult rv;
     673             : 
     674           0 :   MOZ_ASSERT(!mAsyncCopyContext,
     675             :              "Should not be copying when checking completion conditions.");
     676             : 
     677           0 :   bool failed = true;
     678             :   {
     679           0 :     MutexAutoLock lock(mLock);
     680             : 
     681           0 :     if (mComplete) {
     682           0 :       return true;
     683             :     }
     684             : 
     685             :     // If an error occurred, we don't need to do the checks in this code block,
     686             :     // and the operation can be completed immediately with a failure code.
     687           0 :     if (NS_SUCCEEDED(mStatus)) {
     688           0 :       failed = false;
     689             : 
     690             :       // We did not incur in an error, so we must determine if we can stop now.
     691             :       // If the Finish method has not been called, we can just continue now.
     692           0 :       if (!mFinishRequested) {
     693           0 :         return false;
     694             :       }
     695             : 
     696             :       // We can only stop when all the operations requested by the control
     697             :       // thread have been processed.  First, we check whether we have processed
     698             :       // the first SetTarget call, if any.  Then, we check whether we have
     699             :       // processed any rename requested by subsequent SetTarget calls.
     700           0 :       if ((mInitialTarget && !mActualTarget) ||
     701           0 :           (mRenamedTarget && mRenamedTarget != mActualTarget)) {
     702           0 :         return false;
     703             :       }
     704             : 
     705             :       // If we still have data to write to the output file, allow the copy
     706             :       // operation to resume.  The Available getter may return an error if one
     707             :       // of the pipe's streams has been already closed.
     708             :       uint64_t available;
     709           0 :       rv = mPipeInputStream->Available(&available);
     710           0 :       if (NS_SUCCEEDED(rv) && available != 0) {
     711           0 :         return false;
     712             :       }
     713             :     }
     714             : 
     715           0 :     mComplete = true;
     716             :   }
     717             : 
     718             :   // Ensure we notify completion now that the operation finished.
     719             :   // Do a best-effort attempt to remove the file if required.
     720           0 :   if (failed && mActualTarget && !mActualTargetKeepPartial) {
     721           0 :     (void)mActualTarget->Remove(false);
     722             :   }
     723             : 
     724             :   // Finish computing the hash
     725           0 :   if (!failed && mDigestContext) {
     726           0 :     nsNSSShutDownPreventionLock lock;
     727           0 :     if (!isAlreadyShutDown()) {
     728           0 :       Digest d;
     729           0 :       rv = d.End(SEC_OID_SHA256, mDigestContext);
     730           0 :       if (NS_SUCCEEDED(rv)) {
     731           0 :         MutexAutoLock lock(mLock);
     732             :         mSha256 =
     733           0 :           nsDependentCSubstring(BitwiseCast<char*, unsigned char*>(d.get().data),
     734           0 :                                 d.get().len);
     735             :       }
     736             :     }
     737             :   }
     738             : 
     739             :   // Compute the signature of the binary. ExtractSignatureInfo doesn't do
     740             :   // anything on non-Windows platforms except return an empty nsIArray.
     741           0 :   if (!failed && mActualTarget) {
     742           0 :     nsString filePath;
     743           0 :     mActualTarget->GetTarget(filePath);
     744           0 :     nsresult rv = ExtractSignatureInfo(filePath);
     745           0 :     if (NS_FAILED(rv)) {
     746           0 :       LOG(("Unable to extract signature information [this = %p].", this));
     747             :     } else {
     748           0 :       LOG(("Signature extraction success! [this = %p]", this));
     749             :     }
     750             :   }
     751             : 
     752             :   // Post an event to notify that the operation completed.
     753           0 :   if (NS_FAILED(mControlEventTarget->Dispatch(NewRunnableMethod("BackgroundFileSaver::NotifySaveComplete",
     754             :                                                                 this,
     755             :                                                                 &BackgroundFileSaver::NotifySaveComplete),
     756             :                                               NS_DISPATCH_NORMAL))) {
     757           0 :     NS_WARNING("Unable to post completion event to the control thread.");
     758             :   }
     759             : 
     760           0 :   return true;
     761             : }
     762             : 
     763             : // Called on the control thread.
     764             : nsresult
     765           0 : BackgroundFileSaver::NotifyTargetChange(nsIFile *aTarget)
     766             : {
     767           0 :   if (mObserver) {
     768           0 :     (void)mObserver->OnTargetChange(this, aTarget);
     769             :   }
     770             : 
     771           0 :   return NS_OK;
     772             : }
     773             : 
     774             : // Called on the control thread.
     775             : nsresult
     776           0 : BackgroundFileSaver::NotifySaveComplete()
     777             : {
     778           0 :   MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
     779             : 
     780             :   nsresult status;
     781             :   {
     782           0 :     MutexAutoLock lock(mLock);
     783           0 :     status = mStatus;
     784             :   }
     785             : 
     786           0 :   if (mObserver) {
     787           0 :     (void)mObserver->OnSaveComplete(this, status);
     788             :   }
     789             : 
     790             :   // At this point, the worker thread will not process any more events, and we
     791             :   // can shut it down.  Shutting down a thread may re-enter the event loop on
     792             :   // this thread.  This is not a problem in this case, since this function is
     793             :   // called by a top-level event itself, and we have already invoked the
     794             :   // completion observer callback.  Re-entering the loop can only delay the
     795             :   // final release and destruction of this saver object, since we are keeping a
     796             :   // reference to it through the event object.
     797           0 :   mWorkerThread->Shutdown();
     798             : 
     799           0 :   sThreadCount--;
     800             : 
     801             :   // When there are no more active downloads, we consider the download session
     802             :   // finished. We record the maximum number of concurrent downloads reached
     803             :   // during the session in a telemetry histogram, and we reset the maximum
     804             :   // thread counter for the next download session
     805           0 :   if (sThreadCount == 0) {
     806             :     Telemetry::Accumulate(Telemetry::BACKGROUNDFILESAVER_THREAD_COUNT,
     807           0 :                           sTelemetryMaxThreadCount);
     808           0 :     sTelemetryMaxThreadCount = 0;
     809             :   }
     810             : 
     811           0 :   return NS_OK;
     812             : }
     813             : 
     814             : nsresult
     815           0 : BackgroundFileSaver::ExtractSignatureInfo(const nsAString& filePath)
     816             : {
     817           0 :   MOZ_ASSERT(!NS_IsMainThread(), "Cannot extract signature on main thread");
     818             : 
     819           0 :   nsNSSShutDownPreventionLock nssLock;
     820           0 :   if (isAlreadyShutDown()) {
     821           0 :     return NS_ERROR_NOT_AVAILABLE;
     822             :   }
     823             :   {
     824           0 :     MutexAutoLock lock(mLock);
     825           0 :     if (!mSignatureInfoEnabled) {
     826           0 :       return NS_OK;
     827             :     }
     828             :   }
     829             :   nsresult rv;
     830           0 :   nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID, &rv);
     831           0 :   NS_ENSURE_SUCCESS(rv, rv);
     832             : #ifdef XP_WIN
     833             :   // Setup the file to check.
     834             :   WINTRUST_FILE_INFO fileToCheck = {0};
     835             :   fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO);
     836             :   fileToCheck.pcwszFilePath = filePath.Data();
     837             :   fileToCheck.hFile = nullptr;
     838             :   fileToCheck.pgKnownSubject = nullptr;
     839             : 
     840             :   // We want to check it is signed and trusted.
     841             :   WINTRUST_DATA trustData = {0};
     842             :   trustData.cbStruct = sizeof(trustData);
     843             :   trustData.pPolicyCallbackData = nullptr;
     844             :   trustData.pSIPClientData = nullptr;
     845             :   trustData.dwUIChoice = WTD_UI_NONE;
     846             :   trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
     847             :   trustData.dwUnionChoice = WTD_CHOICE_FILE;
     848             :   trustData.dwStateAction = WTD_STATEACTION_VERIFY;
     849             :   trustData.hWVTStateData = nullptr;
     850             :   trustData.pwszURLReference = nullptr;
     851             :   // Disallow revocation checks over the network
     852             :   trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
     853             :   // no UI
     854             :   trustData.dwUIContext = 0;
     855             :   trustData.pFile = &fileToCheck;
     856             : 
     857             :   // The WINTRUST_ACTION_GENERIC_VERIFY_V2 policy verifies that the certificate
     858             :   // chains up to a trusted root CA and has appropriate permissions to sign
     859             :   // code.
     860             :   GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
     861             :   // Check if the file is signed by something that is trusted. If the file is
     862             :   // not signed, this is a no-op.
     863             :   LONG ret = WinVerifyTrust(nullptr, &policyGUID, &trustData);
     864             :   CRYPT_PROVIDER_DATA* cryptoProviderData = nullptr;
     865             :   // According to the Windows documentation, we should check against 0 instead
     866             :   // of ERROR_SUCCESS, which is an HRESULT.
     867             :   if (ret == 0) {
     868             :     cryptoProviderData = WTHelperProvDataFromStateData(trustData.hWVTStateData);
     869             :   }
     870             :   if (cryptoProviderData) {
     871             :     // Lock because signature information is read on the main thread.
     872             :     MutexAutoLock lock(mLock);
     873             :     LOG(("Downloaded trusted and signed file [this = %p].", this));
     874             :     // A binary may have multiple signers. Each signer may have multiple certs
     875             :     // in the chain.
     876             :     for (DWORD i = 0; i < cryptoProviderData->csSigners; ++i) {
     877             :       const CERT_CHAIN_CONTEXT* certChainContext =
     878             :         cryptoProviderData->pasSigners[i].pChainContext;
     879             :       if (!certChainContext) {
     880             :         break;
     881             :       }
     882             :       for (DWORD j = 0; j < certChainContext->cChain; ++j) {
     883             :         const CERT_SIMPLE_CHAIN* certSimpleChain =
     884             :           certChainContext->rgpChain[j];
     885             :         if (!certSimpleChain) {
     886             :           break;
     887             :         }
     888             :         nsCOMPtr<nsIX509CertList> nssCertList =
     889             :           do_CreateInstance(NS_X509CERTLIST_CONTRACTID);
     890             :         if (!nssCertList) {
     891             :           break;
     892             :         }
     893             :         bool extractionSuccess = true;
     894             :         for (DWORD k = 0; k < certSimpleChain->cElement; ++k) {
     895             :           CERT_CHAIN_ELEMENT* certChainElement = certSimpleChain->rgpElement[k];
     896             :           if (certChainElement->pCertContext->dwCertEncodingType !=
     897             :             X509_ASN_ENCODING) {
     898             :               continue;
     899             :           }
     900             :           nsCOMPtr<nsIX509Cert> nssCert = nullptr;
     901             :           nsDependentCSubstring certDER(
     902             :             reinterpret_cast<char *>(
     903             :               certChainElement->pCertContext->pbCertEncoded),
     904             :             certChainElement->pCertContext->cbCertEncoded);
     905             :           rv = certDB->ConstructX509(certDER, getter_AddRefs(nssCert));
     906             :           if (!nssCert) {
     907             :             extractionSuccess = false;
     908             :             LOG(("Couldn't create NSS cert [this = %p]", this));
     909             :             break;
     910             :           }
     911             :           nssCertList->AddCert(nssCert);
     912             :           nsString subjectName;
     913             :           nssCert->GetSubjectName(subjectName);
     914             :           LOG(("Adding cert %s [this = %p]",
     915             :                NS_ConvertUTF16toUTF8(subjectName).get(), this));
     916             :         }
     917             :         if (extractionSuccess) {
     918             :           mSignatureInfo.AppendObject(nssCertList);
     919             :         }
     920             :       }
     921             :     }
     922             :     // Free the provider data if cryptoProviderData is not null.
     923             :     trustData.dwStateAction = WTD_STATEACTION_CLOSE;
     924             :     WinVerifyTrust(nullptr, &policyGUID, &trustData);
     925             :   } else {
     926             :     LOG(("Downloaded unsigned or untrusted file [this = %p].", this));
     927             :   }
     928             : #endif
     929           0 :   return NS_OK;
     930             : }
     931             : 
     932             : ////////////////////////////////////////////////////////////////////////////////
     933             : //// BackgroundFileSaverOutputStream
     934             : 
     935           0 : NS_IMPL_ISUPPORTS(BackgroundFileSaverOutputStream,
     936             :                   nsIBackgroundFileSaver,
     937             :                   nsIOutputStream,
     938             :                   nsIAsyncOutputStream,
     939             :                   nsIOutputStreamCallback)
     940             : 
     941           0 : BackgroundFileSaverOutputStream::BackgroundFileSaverOutputStream()
     942             : : BackgroundFileSaver()
     943           0 : , mAsyncWaitCallback(nullptr)
     944             : {
     945           0 : }
     946             : 
     947           0 : BackgroundFileSaverOutputStream::~BackgroundFileSaverOutputStream()
     948             : {
     949           0 : }
     950             : 
     951             : bool
     952           0 : BackgroundFileSaverOutputStream::HasInfiniteBuffer()
     953             : {
     954           0 :   return false;
     955             : }
     956             : 
     957             : nsAsyncCopyProgressFun
     958           0 : BackgroundFileSaverOutputStream::GetProgressCallback()
     959             : {
     960           0 :   return nullptr;
     961             : }
     962             : 
     963             : NS_IMETHODIMP
     964           0 : BackgroundFileSaverOutputStream::Close()
     965             : {
     966           0 :   return mPipeOutputStream->Close();
     967             : }
     968             : 
     969             : NS_IMETHODIMP
     970           0 : BackgroundFileSaverOutputStream::Flush()
     971             : {
     972           0 :   return mPipeOutputStream->Flush();
     973             : }
     974             : 
     975             : NS_IMETHODIMP
     976           0 : BackgroundFileSaverOutputStream::Write(const char *aBuf, uint32_t aCount,
     977             :                                        uint32_t *_retval)
     978             : {
     979           0 :   return mPipeOutputStream->Write(aBuf, aCount, _retval);
     980             : }
     981             : 
     982             : NS_IMETHODIMP
     983           0 : BackgroundFileSaverOutputStream::WriteFrom(nsIInputStream *aFromStream,
     984             :                                            uint32_t aCount, uint32_t *_retval)
     985             : {
     986           0 :   return mPipeOutputStream->WriteFrom(aFromStream, aCount, _retval);
     987             : }
     988             : 
     989             : NS_IMETHODIMP
     990           0 : BackgroundFileSaverOutputStream::WriteSegments(nsReadSegmentFun aReader,
     991             :                                                void *aClosure, uint32_t aCount,
     992             :                                                uint32_t *_retval)
     993             : {
     994           0 :   return mPipeOutputStream->WriteSegments(aReader, aClosure, aCount, _retval);
     995             : }
     996             : 
     997             : NS_IMETHODIMP
     998           0 : BackgroundFileSaverOutputStream::IsNonBlocking(bool *_retval)
     999             : {
    1000           0 :   return mPipeOutputStream->IsNonBlocking(_retval);
    1001             : }
    1002             : 
    1003             : NS_IMETHODIMP
    1004           0 : BackgroundFileSaverOutputStream::CloseWithStatus(nsresult reason)
    1005             : {
    1006           0 :   return mPipeOutputStream->CloseWithStatus(reason);
    1007             : }
    1008             : 
    1009             : NS_IMETHODIMP
    1010           0 : BackgroundFileSaverOutputStream::AsyncWait(nsIOutputStreamCallback *aCallback,
    1011             :                                            uint32_t aFlags,
    1012             :                                            uint32_t aRequestedCount,
    1013             :                                            nsIEventTarget *aEventTarget)
    1014             : {
    1015           0 :   NS_ENSURE_STATE(!mAsyncWaitCallback);
    1016             : 
    1017           0 :   mAsyncWaitCallback = aCallback;
    1018             : 
    1019           0 :   return mPipeOutputStream->AsyncWait(this, aFlags, aRequestedCount,
    1020           0 :                                       aEventTarget);
    1021             : }
    1022             : 
    1023             : NS_IMETHODIMP
    1024           0 : BackgroundFileSaverOutputStream::OnOutputStreamReady(
    1025             :                                  nsIAsyncOutputStream *aStream)
    1026             : {
    1027           0 :   NS_ENSURE_STATE(mAsyncWaitCallback);
    1028             : 
    1029           0 :   nsCOMPtr<nsIOutputStreamCallback> asyncWaitCallback = nullptr;
    1030           0 :   asyncWaitCallback.swap(mAsyncWaitCallback);
    1031             : 
    1032           0 :   return asyncWaitCallback->OnOutputStreamReady(this);
    1033             : }
    1034             : 
    1035             : ////////////////////////////////////////////////////////////////////////////////
    1036             : //// BackgroundFileSaverStreamListener
    1037             : 
    1038           0 : NS_IMPL_ISUPPORTS(BackgroundFileSaverStreamListener,
    1039             :                   nsIBackgroundFileSaver,
    1040             :                   nsIRequestObserver,
    1041             :                   nsIStreamListener)
    1042             : 
    1043           0 : BackgroundFileSaverStreamListener::BackgroundFileSaverStreamListener()
    1044             : : BackgroundFileSaver()
    1045             : , mSuspensionLock("BackgroundFileSaverStreamListener.mSuspensionLock")
    1046             : , mReceivedTooMuchData(false)
    1047             : , mRequest(nullptr)
    1048           0 : , mRequestSuspended(false)
    1049             : {
    1050           0 : }
    1051             : 
    1052           0 : BackgroundFileSaverStreamListener::~BackgroundFileSaverStreamListener()
    1053             : {
    1054           0 : }
    1055             : 
    1056             : bool
    1057           0 : BackgroundFileSaverStreamListener::HasInfiniteBuffer()
    1058             : {
    1059           0 :   return true;
    1060             : }
    1061             : 
    1062             : nsAsyncCopyProgressFun
    1063           0 : BackgroundFileSaverStreamListener::GetProgressCallback()
    1064             : {
    1065           0 :   return AsyncCopyProgressCallback;
    1066             : }
    1067             : 
    1068             : NS_IMETHODIMP
    1069           0 : BackgroundFileSaverStreamListener::OnStartRequest(nsIRequest *aRequest,
    1070             :                                                   nsISupports *aContext)
    1071             : {
    1072           0 :   NS_ENSURE_ARG(aRequest);
    1073             : 
    1074           0 :   return NS_OK;
    1075             : }
    1076             : 
    1077             : NS_IMETHODIMP
    1078           0 : BackgroundFileSaverStreamListener::OnStopRequest(nsIRequest *aRequest,
    1079             :                                                  nsISupports *aContext,
    1080             :                                                  nsresult aStatusCode)
    1081             : {
    1082             :   // If an error occurred, cancel the operation immediately.  On success, wait
    1083             :   // until the caller has determined whether the file should be renamed.
    1084           0 :   if (NS_FAILED(aStatusCode)) {
    1085           0 :     Finish(aStatusCode);
    1086             :   }
    1087             : 
    1088           0 :   return NS_OK;
    1089             : }
    1090             : 
    1091             : NS_IMETHODIMP
    1092           0 : BackgroundFileSaverStreamListener::OnDataAvailable(nsIRequest *aRequest,
    1093             :                                                    nsISupports *aContext,
    1094             :                                                    nsIInputStream *aInputStream,
    1095             :                                                    uint64_t aOffset,
    1096             :                                                    uint32_t aCount)
    1097             : {
    1098             :   nsresult rv;
    1099             : 
    1100           0 :   NS_ENSURE_ARG(aRequest);
    1101             : 
    1102             :   // Read the requested data.  Since the pipe has an infinite buffer, we don't
    1103             :   // expect any write error to occur here.
    1104             :   uint32_t writeCount;
    1105           0 :   rv = mPipeOutputStream->WriteFrom(aInputStream, aCount, &writeCount);
    1106           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1107             : 
    1108             :   // If reading from the input stream fails for any reason, the pipe will return
    1109             :   // a success code, but without reading all the data.  Since we should be able
    1110             :   // to read the requested data when OnDataAvailable is called, raise an error.
    1111           0 :   if (writeCount < aCount) {
    1112           0 :     NS_WARNING("Reading from the input stream should not have failed.");
    1113           0 :     return NS_ERROR_UNEXPECTED;
    1114             :   }
    1115             : 
    1116           0 :   bool stateChanged = false;
    1117             :   {
    1118           0 :     MutexAutoLock lock(mSuspensionLock);
    1119             : 
    1120           0 :     if (!mReceivedTooMuchData) {
    1121             :       uint64_t available;
    1122           0 :       nsresult rv = mPipeInputStream->Available(&available);
    1123           0 :       if (NS_SUCCEEDED(rv) && available > REQUEST_SUSPEND_AT) {
    1124           0 :         mReceivedTooMuchData = true;
    1125           0 :         mRequest = aRequest;
    1126           0 :         stateChanged = true;
    1127             :       }
    1128             :     }
    1129             :   }
    1130             : 
    1131           0 :   if (stateChanged) {
    1132           0 :     NotifySuspendOrResume();
    1133             :   }
    1134             : 
    1135           0 :   return NS_OK;
    1136             : }
    1137             : 
    1138             : // Called on the worker thread.
    1139             : // static
    1140             : void
    1141           0 : BackgroundFileSaverStreamListener::AsyncCopyProgressCallback(void *aClosure,
    1142             :                                                              uint32_t aCount)
    1143             : {
    1144             :   BackgroundFileSaverStreamListener *self =
    1145           0 :     (BackgroundFileSaverStreamListener *)aClosure;
    1146             : 
    1147             :   // Wait if the control thread is in the process of suspending or resuming.
    1148           0 :   MutexAutoLock lock(self->mSuspensionLock);
    1149             : 
    1150             :   // This function is called when some bytes are consumed by NS_AsyncCopy.  Each
    1151             :   // time this happens, verify if a suspended request should be resumed, because
    1152             :   // we have now consumed enough data.
    1153           0 :   if (self->mReceivedTooMuchData) {
    1154             :     uint64_t available;
    1155           0 :     nsresult rv = self->mPipeInputStream->Available(&available);
    1156           0 :     if (NS_FAILED(rv) || available < REQUEST_RESUME_AT) {
    1157           0 :       self->mReceivedTooMuchData = false;
    1158             : 
    1159             :       // Post an event to verify if the request should be resumed.
    1160           0 :       if (NS_FAILED(self->mControlEventTarget->Dispatch(NewRunnableMethod("BackgroundFileSaverStreamListener::NotifySuspendOrResume",
    1161             :                                                                           self,
    1162             :                                                                           &BackgroundFileSaverStreamListener::NotifySuspendOrResume),
    1163             :                                                         NS_DISPATCH_NORMAL))) {
    1164           0 :         NS_WARNING("Unable to post resume event to the control thread.");
    1165             :       }
    1166             :     }
    1167             :   }
    1168           0 : }
    1169             : 
    1170             : // Called on the control thread.
    1171             : nsresult
    1172           0 : BackgroundFileSaverStreamListener::NotifySuspendOrResume()
    1173             : {
    1174             :   // Prevent the worker thread from changing state while processing.
    1175           0 :   MutexAutoLock lock(mSuspensionLock);
    1176             : 
    1177           0 :   if (mReceivedTooMuchData) {
    1178           0 :     if (!mRequestSuspended) {
    1179             :       // Try to suspend the request.  If this fails, don't try to resume later.
    1180           0 :       if (NS_SUCCEEDED(mRequest->Suspend())) {
    1181           0 :         mRequestSuspended = true;
    1182             :       } else {
    1183           0 :         NS_WARNING("Unable to suspend the request.");
    1184             :       }
    1185             :     }
    1186             :   } else {
    1187           0 :     if (mRequestSuspended) {
    1188             :       // Resume the request only if we succeeded in suspending it.
    1189           0 :       if (NS_SUCCEEDED(mRequest->Resume())) {
    1190           0 :         mRequestSuspended = false;
    1191             :       } else {
    1192           0 :         NS_WARNING("Unable to resume the request.");
    1193             :       }
    1194             :     }
    1195             :   }
    1196             : 
    1197           0 :   return NS_OK;
    1198             : }
    1199             : 
    1200             : ////////////////////////////////////////////////////////////////////////////////
    1201             : //// DigestOutputStream
    1202           0 : NS_IMPL_ISUPPORTS(DigestOutputStream,
    1203             :                   nsIOutputStream)
    1204             : 
    1205           0 : DigestOutputStream::DigestOutputStream(nsIOutputStream* aStream,
    1206           0 :                                        PK11Context* aContext) :
    1207             :   mOutputStream(aStream)
    1208           0 :   , mDigestContext(aContext)
    1209             : {
    1210           0 :   MOZ_ASSERT(mDigestContext, "Can't have null digest context");
    1211           0 :   MOZ_ASSERT(mOutputStream, "Can't have null output stream");
    1212           0 : }
    1213             : 
    1214           0 : DigestOutputStream::~DigestOutputStream()
    1215             : {
    1216           0 :   nsNSSShutDownPreventionLock locker;
    1217           0 :   if (isAlreadyShutDown()) {
    1218           0 :     return;
    1219             :   }
    1220           0 :   shutdown(ShutdownCalledFrom::Object);
    1221           0 : }
    1222             : 
    1223             : NS_IMETHODIMP
    1224           0 : DigestOutputStream::Close()
    1225             : {
    1226           0 :   return mOutputStream->Close();
    1227             : }
    1228             : 
    1229             : NS_IMETHODIMP
    1230           0 : DigestOutputStream::Flush()
    1231             : {
    1232           0 :   return mOutputStream->Flush();
    1233             : }
    1234             : 
    1235             : NS_IMETHODIMP
    1236           0 : DigestOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* retval)
    1237             : {
    1238           0 :   nsNSSShutDownPreventionLock lock;
    1239           0 :   if (isAlreadyShutDown()) {
    1240           0 :     return NS_ERROR_NOT_AVAILABLE;
    1241             :   }
    1242             : 
    1243           0 :   nsresult rv = MapSECStatus(
    1244             :     PK11_DigestOp(mDigestContext,
    1245             :                   BitwiseCast<const unsigned char*, const char*>(aBuf),
    1246           0 :                   aCount));
    1247           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1248             : 
    1249           0 :   return mOutputStream->Write(aBuf, aCount, retval);
    1250             : }
    1251             : 
    1252             : NS_IMETHODIMP
    1253           0 : DigestOutputStream::WriteFrom(nsIInputStream* aFromStream,
    1254             :                               uint32_t aCount, uint32_t* retval)
    1255             : {
    1256             :   // Not supported. We could read the stream to a buf, call DigestOp on the
    1257             :   // result, seek back and pass the stream on, but it's not worth it since our
    1258             :   // application (NS_AsyncCopy) doesn't invoke this on the sink.
    1259           0 :   MOZ_CRASH("DigestOutputStream::WriteFrom not implemented");
    1260             : }
    1261             : 
    1262             : NS_IMETHODIMP
    1263           0 : DigestOutputStream::WriteSegments(nsReadSegmentFun aReader,
    1264             :                                   void *aClosure, uint32_t aCount,
    1265             :                                   uint32_t *retval)
    1266             : {
    1267           0 :   MOZ_CRASH("DigestOutputStream::WriteSegments not implemented");
    1268             : }
    1269             : 
    1270             : NS_IMETHODIMP
    1271           0 : DigestOutputStream::IsNonBlocking(bool *retval)
    1272             : {
    1273           0 :   return mOutputStream->IsNonBlocking(retval);
    1274             : }
    1275             : 
    1276             : #undef LOG_ENABLED
    1277             : 
    1278             : } // namespace net
    1279             : } // namespace mozilla

Generated by: LCOV version 1.13