Coverage Report

Created: 2018-09-25 14:53

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