Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/file/MutableBlobStorage.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=8 sts=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 "EmptyBlobImpl.h"
8
#include "MutableBlobStorage.h"
9
#include "MemoryBlobImpl.h"
10
#include "mozilla/CheckedInt.h"
11
#include "mozilla/dom/ipc/TemporaryIPCBlobChild.h"
12
#include "mozilla/dom/WorkerPrivate.h"
13
#include "mozilla/ipc/BackgroundChild.h"
14
#include "mozilla/ipc/PBackgroundChild.h"
15
#include "mozilla/Preferences.h"
16
#include "mozilla/TaskQueue.h"
17
#include "File.h"
18
#include "nsAnonymousTemporaryFile.h"
19
#include "nsNetCID.h"
20
#include "nsProxyRelease.h"
21
22
0
#define BLOB_MEMORY_TEMPORARY_FILE 1048576
23
24
namespace mozilla {
25
namespace dom {
26
27
namespace {
28
29
// This class uses the callback to inform when the Blob is created or when the
30
// error must be propagated.
31
class BlobCreationDoneRunnable final : public Runnable
32
{
33
public:
34
  BlobCreationDoneRunnable(MutableBlobStorage* aBlobStorage,
35
                           MutableBlobStorageCallback* aCallback,
36
                           Blob* aBlob,
37
                           nsresult aRv)
38
    : Runnable("dom::BlobCreationDoneRunnable")
39
    , mBlobStorage(aBlobStorage)
40
    , mCallback(aCallback)
41
    , mBlob(aBlob)
42
    , mRv(aRv)
43
0
  {
44
0
    MOZ_ASSERT(aBlobStorage);
45
0
    MOZ_ASSERT(aCallback);
46
0
    MOZ_ASSERT((NS_FAILED(aRv) && !aBlob) ||
47
0
               (NS_SUCCEEDED(aRv) && aBlob));
48
0
  }
49
50
  NS_IMETHOD
51
  Run() override
52
0
  {
53
0
    MOZ_ASSERT(NS_IsMainThread());
54
0
    MOZ_ASSERT(mBlobStorage);
55
0
    mCallback->BlobStoreCompleted(mBlobStorage, mBlob, mRv);
56
0
    mCallback = nullptr;
57
0
    mBlob = nullptr;
58
0
    return NS_OK;
59
0
  }
60
61
private:
62
  ~BlobCreationDoneRunnable()
63
0
  {
64
0
    MOZ_ASSERT(mBlobStorage);
65
0
    // If something when wrong, we still have to release these objects in the
66
0
    // correct thread.
67
0
    NS_ProxyRelease(
68
0
      "BlobCreationDoneRunnable::mCallback",
69
0
      mBlobStorage->EventTarget(), mCallback.forget());
70
0
    NS_ProxyRelease(
71
0
      "BlobCreationDoneRunnable::mBlob",
72
0
      mBlobStorage->EventTarget(), mBlob.forget());
73
0
  }
74
75
  RefPtr<MutableBlobStorage> mBlobStorage;
76
  RefPtr<MutableBlobStorageCallback> mCallback;
77
  RefPtr<Blob> mBlob;
78
  nsresult mRv;
79
};
80
81
// Simple runnable to propagate the error to the BlobStorage.
82
class ErrorPropagationRunnable final : public Runnable
83
{
84
public:
85
  ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
86
    : Runnable("dom::ErrorPropagationRunnable")
87
    , mBlobStorage(aBlobStorage)
88
    , mRv(aRv)
89
0
  {}
90
91
  NS_IMETHOD
92
  Run() override
93
0
  {
94
0
    mBlobStorage->ErrorPropagated(mRv);
95
0
    return NS_OK;
96
0
  }
97
98
private:
99
  RefPtr<MutableBlobStorage> mBlobStorage;
100
  nsresult mRv;
101
};
102
103
// This runnable moves a buffer to the IO thread and there, it writes it into
104
// the temporary file, if its File Descriptor has not been already closed.
105
class WriteRunnable final : public Runnable
106
{
107
public:
108
  static WriteRunnable*
109
  CopyBuffer(MutableBlobStorage* aBlobStorage,
110
             const void* aData, uint32_t aLength)
111
0
  {
112
0
    MOZ_ASSERT(aBlobStorage);
113
0
    MOZ_ASSERT(aData);
114
0
115
0
    // We have to take a copy of this buffer.
116
0
    void* data = malloc(aLength);
117
0
    if (!data) {
118
0
      return nullptr;
119
0
    }
120
0
121
0
    memcpy((char*)data, aData, aLength);
122
0
    return new WriteRunnable(aBlobStorage, data, aLength);
123
0
  }
124
125
  static WriteRunnable*
126
  AdoptBuffer(MutableBlobStorage* aBlobStorage, void* aData, uint32_t aLength)
127
0
  {
128
0
    MOZ_ASSERT(NS_IsMainThread());
129
0
    MOZ_ASSERT(aBlobStorage);
130
0
    MOZ_ASSERT(aData);
131
0
132
0
    return new WriteRunnable(aBlobStorage, aData, aLength);
133
0
  }
134
135
  NS_IMETHOD
136
  Run() override
137
0
  {
138
0
    MOZ_ASSERT(!NS_IsMainThread());
139
0
    MOZ_ASSERT(mBlobStorage);
140
0
141
0
    PRFileDesc* fd = mBlobStorage->GetFD();
142
0
    if (!fd) {
143
0
      // The file descriptor has been closed in the meantime.
144
0
      return NS_OK;
145
0
    }
146
0
147
0
    int32_t written = PR_Write(fd, mData, mLength);
148
0
    if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
149
0
      mBlobStorage->CloseFD();
150
0
      return mBlobStorage->EventTarget()->Dispatch(
151
0
        new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE),
152
0
        NS_DISPATCH_NORMAL);
153
0
    }
154
0
155
0
    return NS_OK;
156
0
  }
157
158
private:
159
  WriteRunnable(MutableBlobStorage* aBlobStorage, void* aData, uint32_t aLength)
160
    : Runnable("dom::WriteRunnable")
161
    , mBlobStorage(aBlobStorage)
162
    , mData(aData)
163
    , mLength(aLength)
164
0
  {
165
0
    MOZ_ASSERT(mBlobStorage);
166
0
    MOZ_ASSERT(aData);
167
0
  }
168
169
  ~WriteRunnable()
170
0
  {
171
0
    free(mData);
172
0
  }
173
174
  RefPtr<MutableBlobStorage> mBlobStorage;
175
  void* mData;
176
  uint32_t mLength;
177
};
178
179
// This runnable closes the FD in case something goes wrong or the temporary
180
// file is not needed anymore.
181
class CloseFileRunnable final : public Runnable
182
{
183
public:
184
  explicit CloseFileRunnable(PRFileDesc* aFD)
185
    : Runnable("dom::CloseFileRunnable")
186
    , mFD(aFD)
187
0
  {}
188
189
  NS_IMETHOD
190
  Run() override
191
0
  {
192
0
    MOZ_ASSERT(!NS_IsMainThread());
193
0
    PR_Close(mFD);
194
0
    mFD = nullptr;
195
0
    return NS_OK;
196
0
  }
197
198
private:
199
  ~CloseFileRunnable()
200
0
  {
201
0
    if (mFD) {
202
0
      PR_Close(mFD);
203
0
    }
204
0
  }
205
206
  PRFileDesc* mFD;
207
};
208
209
// This runnable is dispatched to the main-thread from the IO thread and its
210
// task is to create the blob and inform the callback.
211
class CreateBlobRunnable final : public Runnable
212
                               , public TemporaryIPCBlobChildCallback
213
{
214
public:
215
  // We need to always declare refcounting because
216
  // TemporaryIPCBlobChildCallback has pure-virtual refcounting.
217
  NS_DECL_ISUPPORTS_INHERITED
218
219
  CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
220
                     already_AddRefed<nsISupports> aParent,
221
                     const nsACString& aContentType,
222
                     already_AddRefed<MutableBlobStorageCallback> aCallback)
223
    : Runnable("dom::CreateBlobRunnable")
224
    , mBlobStorage(aBlobStorage)
225
    , mParent(aParent)
226
    , mContentType(aContentType)
227
    , mCallback(aCallback)
228
0
  {
229
0
    MOZ_ASSERT(!NS_IsMainThread());
230
0
    MOZ_ASSERT(aBlobStorage);
231
0
  }
232
233
  NS_IMETHOD
234
  Run() override
235
0
  {
236
0
    MOZ_ASSERT(NS_IsMainThread());
237
0
    MOZ_ASSERT(mBlobStorage);
238
0
    mBlobStorage->AskForBlob(this, mContentType);
239
0
    return NS_OK;
240
0
  }
241
242
  void
243
  OperationSucceeded(BlobImpl* aBlobImpl) override
244
0
  {
245
0
    nsCOMPtr<nsISupports> parent(std::move(mParent));
246
0
    RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
247
0
248
0
    RefPtr<Blob> blob = Blob::Create(parent, aBlobImpl);
249
0
    callback->BlobStoreCompleted(mBlobStorage, blob, NS_OK);
250
0
  }
251
252
  void
253
  OperationFailed(nsresult aRv) override
254
0
  {
255
0
    RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
256
0
    callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv);
257
0
  }
258
259
private:
260
  ~CreateBlobRunnable()
261
0
  {
262
0
    MOZ_ASSERT(mBlobStorage);
263
0
    // If something when wrong, we still have to release data in the correct
264
0
    // thread.
265
0
    NS_ProxyRelease(
266
0
      "CreateBlobRunnable::mParent",
267
0
      mBlobStorage->EventTarget(), mParent.forget());
268
0
    NS_ProxyRelease(
269
0
      "CreateBlobRunnable::mCallback",
270
0
      mBlobStorage->EventTarget(), mCallback.forget());
271
0
  }
272
273
  RefPtr<MutableBlobStorage> mBlobStorage;
274
  nsCOMPtr<nsISupports> mParent;
275
  nsCString mContentType;
276
  RefPtr<MutableBlobStorageCallback> mCallback;
277
};
278
279
NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable)
280
281
// This task is used to know when the writing is completed. From the IO thread
282
// it dispatches a CreateBlobRunnable to the main-thread.
283
class LastRunnable final : public Runnable
284
{
285
public:
286
  LastRunnable(MutableBlobStorage* aBlobStorage,
287
               nsISupports* aParent,
288
               const nsACString& aContentType,
289
               MutableBlobStorageCallback* aCallback)
290
    : Runnable("dom::LastRunnable")
291
    , mBlobStorage(aBlobStorage)
292
    , mParent(aParent)
293
    , mContentType(aContentType)
294
    , mCallback(aCallback)
295
0
  {
296
0
    MOZ_ASSERT(NS_IsMainThread());
297
0
    MOZ_ASSERT(mBlobStorage);
298
0
    MOZ_ASSERT(aCallback);
299
0
  }
300
301
  NS_IMETHOD
302
  Run() override
303
0
  {
304
0
    MOZ_ASSERT(!NS_IsMainThread());
305
0
306
0
    RefPtr<Runnable> runnable =
307
0
      new CreateBlobRunnable(mBlobStorage, mParent.forget(),
308
0
                             mContentType, mCallback.forget());
309
0
    return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL);
310
0
  }
311
312
private:
313
  ~LastRunnable()
314
0
  {
315
0
    MOZ_ASSERT(mBlobStorage);
316
0
    // If something when wrong, we still have to release data in the correct
317
0
    // thread.
318
0
    NS_ProxyRelease(
319
0
      "LastRunnable::mParent",
320
0
      mBlobStorage->EventTarget(), mParent.forget());
321
0
    NS_ProxyRelease(
322
0
      "LastRunnable::mCallback",
323
0
      mBlobStorage->EventTarget(), mCallback.forget());
324
0
  }
325
326
  RefPtr<MutableBlobStorage> mBlobStorage;
327
  nsCOMPtr<nsISupports> mParent;
328
  nsCString mContentType;
329
  RefPtr<MutableBlobStorageCallback> mCallback;
330
};
331
332
} // anonymous namespace
333
334
MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
335
                                       nsIEventTarget* aEventTarget,
336
                                       uint32_t aMaxMemory)
337
  : mMutex("MutableBlobStorage::mMutex")
338
  , mData(nullptr)
339
  , mDataLen(0)
340
  , mDataBufferLen(0)
341
  , mStorageState(aType == eOnlyInMemory ? eKeepInMemory : eInMemory)
342
  , mFD(nullptr)
343
  , mErrorResult(NS_OK)
344
  , mEventTarget(aEventTarget)
345
  , mMaxMemory(aMaxMemory)
346
0
{
347
0
  MOZ_ASSERT(NS_IsMainThread());
348
0
349
0
  if (!mEventTarget) {
350
0
    mEventTarget = GetMainThreadEventTarget();
351
0
  }
352
0
353
0
  if (aMaxMemory == 0 && aType == eCouldBeInTemporaryFile) {
354
0
    mMaxMemory = Preferences::GetUint("dom.blob.memoryToTemporaryFile",
355
0
                                      BLOB_MEMORY_TEMPORARY_FILE);
356
0
  }
357
0
358
0
  MOZ_ASSERT(mEventTarget);
359
0
}
360
361
MutableBlobStorage::~MutableBlobStorage()
362
0
{
363
0
  free(mData);
364
0
365
0
  if (mFD) {
366
0
    RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
367
0
    Unused << DispatchToIOThread(runnable.forget());
368
0
  }
369
0
370
0
  if (mTaskQueue) {
371
0
    mTaskQueue->BeginShutdown();
372
0
  }
373
0
374
0
  if (mActor) {
375
0
    NS_ProxyRelease("MutableBlobStorage::mActor",
376
0
                    EventTarget(), mActor.forget());
377
0
  }
378
0
}
379
380
void
381
MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
382
                                     const nsACString& aContentType,
383
                                     MutableBlobStorageCallback* aCallback)
384
0
{
385
0
  MOZ_ASSERT(NS_IsMainThread());
386
0
  MOZ_ASSERT(aCallback);
387
0
388
0
  MutexAutoLock lock(mMutex);
389
0
390
0
  // GetBlob can be called just once.
391
0
  MOZ_ASSERT(mStorageState != eClosed);
392
0
  StorageState previousState = mStorageState;
393
0
  mStorageState = eClosed;
394
0
395
0
  if (previousState == eInTemporaryFile) {
396
0
    if (NS_FAILED(mErrorResult)) {
397
0
      MOZ_ASSERT(!mActor);
398
0
399
0
      RefPtr<Runnable> runnable =
400
0
        new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
401
0
      EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
402
0
      return;
403
0
    }
404
0
405
0
    MOZ_ASSERT(mActor);
406
0
407
0
    // We want to wait until all the WriteRunnable are completed. The way we do
408
0
    // this is to go to the I/O thread and then we come back: the runnables are
409
0
    // executed in order and this LastRunnable will be... the last one.
410
0
    // This Runnable will also close the FD on the I/O thread.
411
0
    RefPtr<Runnable> runnable =
412
0
      new LastRunnable(this, aParent, aContentType, aCallback);
413
0
414
0
    // If the dispatching fails, we are shutting down and it's fine to do not
415
0
    // run the callback.
416
0
    Unused << DispatchToIOThread(runnable.forget());
417
0
    return;
418
0
  }
419
0
420
0
  // If we are waiting for the temporary file, it's better to wait...
421
0
  if (previousState == eWaitingForTemporaryFile) {
422
0
    mPendingParent = aParent;
423
0
    mPendingContentType = aContentType;
424
0
    mPendingCallback = aCallback;
425
0
    return;
426
0
  }
427
0
428
0
  RefPtr<BlobImpl> blobImpl;
429
0
430
0
  if (mData) {
431
0
    blobImpl = new MemoryBlobImpl(mData, mDataLen,
432
0
                                  NS_ConvertUTF8toUTF16(aContentType));
433
0
434
0
    mData = nullptr; // The MemoryBlobImpl takes ownership of the buffer
435
0
    mDataLen = 0;
436
0
    mDataBufferLen = 0;
437
0
  } else {
438
0
    blobImpl = new EmptyBlobImpl(NS_ConvertUTF8toUTF16(aContentType));
439
0
  }
440
0
441
0
  RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
442
0
  RefPtr<BlobCreationDoneRunnable> runnable =
443
0
    new BlobCreationDoneRunnable(this, aCallback, blob, NS_OK);
444
0
445
0
  nsresult error = EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
446
0
  if (NS_WARN_IF(NS_FAILED(error))) {
447
0
    return;
448
0
  }
449
0
}
450
451
nsresult
452
MutableBlobStorage::Append(const void* aData, uint32_t aLength)
453
0
{
454
0
  // This method can be called on any thread.
455
0
456
0
  MutexAutoLock lock(mMutex);
457
0
  MOZ_ASSERT(mStorageState != eClosed);
458
0
  NS_ENSURE_ARG_POINTER(aData);
459
0
460
0
  if (!aLength) {
461
0
    return NS_OK;
462
0
  }
463
0
464
0
  // If eInMemory is the current Storage state, we could maybe migrate to
465
0
  // a temporary file.
466
0
  if (mStorageState == eInMemory && ShouldBeTemporaryStorage(lock, aLength) &&
467
0
      !MaybeCreateTemporaryFile(lock)) {
468
0
    return NS_ERROR_FAILURE;
469
0
  }
470
0
471
0
  // If we are already in the temporaryFile mode, we have to dispatch a
472
0
  // runnable.
473
0
  if (mStorageState == eInTemporaryFile) {
474
0
    // If a previous operation failed, let's return that error now.
475
0
    if (NS_FAILED(mErrorResult)) {
476
0
      return mErrorResult;
477
0
    }
478
0
479
0
    RefPtr<WriteRunnable> runnable =
480
0
      WriteRunnable::CopyBuffer(this, aData, aLength);
481
0
    if (NS_WARN_IF(!runnable)) {
482
0
      return NS_ERROR_OUT_OF_MEMORY;
483
0
    }
484
0
485
0
    nsresult rv = DispatchToIOThread(runnable.forget());
486
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
487
0
      return rv;
488
0
    }
489
0
490
0
    mDataLen += aLength;
491
0
    return NS_OK;
492
0
  }
493
0
494
0
  // By default, we store in memory.
495
0
496
0
  uint64_t offset = mDataLen;
497
0
498
0
  if (!ExpandBufferSize(lock, aLength)) {
499
0
    return NS_ERROR_OUT_OF_MEMORY;
500
0
  }
501
0
502
0
  memcpy((char*)mData + offset, aData, aLength);
503
0
  return NS_OK;
504
0
}
505
506
bool
507
MutableBlobStorage::ExpandBufferSize(const MutexAutoLock& aProofOfLock,
508
                                     uint64_t aSize)
509
0
{
510
0
  MOZ_ASSERT(mStorageState < eInTemporaryFile);
511
0
512
0
  if (mDataBufferLen >= mDataLen + aSize) {
513
0
    mDataLen += aSize;
514
0
    return true;
515
0
  }
516
0
517
0
  // Start at 1 or we'll loop forever.
518
0
  CheckedUint32 bufferLen =
519
0
    std::max<uint32_t>(static_cast<uint32_t>(mDataBufferLen), 1);
520
0
  while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize) {
521
0
    bufferLen *= 2;
522
0
  }
523
0
524
0
  if (!bufferLen.isValid()) {
525
0
    return false;
526
0
  }
527
0
528
0
  void* data = realloc(mData, bufferLen.value());
529
0
  if (!data) {
530
0
    return false;
531
0
  }
532
0
533
0
  mData = data;
534
0
  mDataBufferLen = bufferLen.value();
535
0
  mDataLen += aSize;
536
0
  return true;
537
0
}
538
539
bool
540
MutableBlobStorage::ShouldBeTemporaryStorage(const MutexAutoLock& aProofOfLock,
541
                                             uint64_t aSize) const
542
0
{
543
0
  MOZ_ASSERT(mStorageState == eInMemory);
544
0
545
0
  CheckedUint32 bufferSize = mDataLen;
546
0
  bufferSize += aSize;
547
0
548
0
  if (!bufferSize.isValid()) {
549
0
    return false;
550
0
  }
551
0
552
0
  return bufferSize.value() >= mMaxMemory;
553
0
}
554
555
bool
556
MutableBlobStorage::MaybeCreateTemporaryFile(const MutexAutoLock& aProofOfLock)
557
0
{
558
0
  mStorageState = eWaitingForTemporaryFile;
559
0
560
0
  if (!NS_IsMainThread()) {
561
0
    RefPtr<MutableBlobStorage> self = this;
562
0
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
563
0
      "MutableBlobStorage::MaybeCreateTemporaryFile",
564
0
      [self]() { self->MaybeCreateTemporaryFileOnMainThread(); });
565
0
    EventTarget()->Dispatch(r.forget(), NS_DISPATCH_SYNC);
566
0
    return !!mActor;
567
0
  }
568
0
569
0
  MaybeCreateTemporaryFileOnMainThread();
570
0
  return !!mActor;
571
0
}
572
573
void
574
MutableBlobStorage::MaybeCreateTemporaryFileOnMainThread()
575
0
{
576
0
  MOZ_ASSERT(NS_IsMainThread());
577
0
  MOZ_ASSERT(!mActor);
578
0
579
0
  mozilla::ipc::PBackgroundChild* actorChild =
580
0
    mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
581
0
  if (NS_WARN_IF(!actorChild)) {
582
0
    return;
583
0
  }
584
0
585
0
  mActor = new TemporaryIPCBlobChild(this);
586
0
  actorChild->SendPTemporaryIPCBlobConstructor(mActor);
587
0
588
0
  // We need manually to increase the reference for this actor because the
589
0
  // IPC allocator method is not triggered. The Release() is called by IPDL
590
0
  // when the actor is deleted.
591
0
  mActor.get()->AddRef();
592
0
593
0
  // The actor will call us when the FileDescriptor is received.
594
0
}
595
596
void
597
MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
598
0
{
599
0
  MOZ_ASSERT(NS_IsMainThread());
600
0
601
0
  MutexAutoLock lock(mMutex);
602
0
  MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
603
0
             mStorageState == eClosed);
604
0
  MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
605
0
  MOZ_ASSERT(mActor);
606
0
  MOZ_ASSERT(aFD);
607
0
608
0
  // If the object has been already closed and we don't need to execute a
609
0
  // callback, we need just to close the file descriptor in the correct thread.
610
0
  if (mStorageState == eClosed && !mPendingCallback) {
611
0
    RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);
612
0
613
0
    // If this dispatching fails, CloseFileRunnable will close the FD in the
614
0
    // DTOR on the current thread.
615
0
    Unused << DispatchToIOThread(runnable.forget());
616
0
617
0
    // Let's inform the parent that we have nothing else to do.
618
0
    mActor->SendOperationFailed();
619
0
    mActor = nullptr;
620
0
    return;
621
0
  }
622
0
623
0
  // If we still receiving data, we can proceed in temporary-file mode.
624
0
  if (mStorageState == eWaitingForTemporaryFile) {
625
0
    mStorageState = eInTemporaryFile;
626
0
  }
627
0
628
0
  mFD = aFD;
629
0
  MOZ_ASSERT(NS_SUCCEEDED(mErrorResult));
630
0
631
0
  // This runnable takes the ownership of mData and it will write this buffer
632
0
  // into the temporary file.
633
0
  RefPtr<WriteRunnable> runnable =
634
0
    WriteRunnable::AdoptBuffer(this, mData, mDataLen);
635
0
  MOZ_ASSERT(runnable);
636
0
637
0
  mData = nullptr;
638
0
639
0
  nsresult rv = DispatchToIOThread(runnable.forget());
640
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
641
0
    // Shutting down, we cannot continue.
642
0
    return;
643
0
  }
644
0
645
0
  // If we are closed, it means that GetBlobWhenReady() has been called when we
646
0
  // were already waiting for a temporary file-descriptor. Finally we are here,
647
0
  // AdoptBuffer runnable is going to write the current buffer into this file.
648
0
  // After that, there is nothing else to write, and we dispatch LastRunnable
649
0
  // which ends up calling mPendingCallback via CreateBlobRunnable.
650
0
  if (mStorageState == eClosed) {
651
0
    MOZ_ASSERT(mPendingCallback);
652
0
653
0
    RefPtr<Runnable> runnable =
654
0
      new LastRunnable(this, mPendingParent, mPendingContentType,
655
0
                       mPendingCallback);
656
0
    Unused << DispatchToIOThread(runnable.forget());
657
0
658
0
    mPendingParent = nullptr;
659
0
    mPendingCallback = nullptr;
660
0
  }
661
0
}
662
663
void
664
MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
665
                               const nsACString& aContentType)
666
0
{
667
0
  MOZ_ASSERT(NS_IsMainThread());
668
0
669
0
  MutexAutoLock lock(mMutex);
670
0
  MOZ_ASSERT(mStorageState == eClosed);
671
0
  MOZ_ASSERT(mFD);
672
0
  MOZ_ASSERT(mActor);
673
0
  MOZ_ASSERT(aCallback);
674
0
675
0
  // Let's pass the FileDescriptor to the parent actor in order to keep the file
676
0
  // locked on windows.
677
0
  mActor->AskForBlob(aCallback, aContentType, mFD);
678
0
679
0
  // The previous operation has duplicated the file descriptor. Now we can close
680
0
  // mFD. The parent will take care of closing the duplicated file descriptor on
681
0
  // its side.
682
0
  RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
683
0
  Unused << DispatchToIOThread(runnable.forget());
684
0
685
0
  mFD = nullptr;
686
0
  mActor = nullptr;
687
0
}
688
689
void
690
MutableBlobStorage::ErrorPropagated(nsresult aRv)
691
0
{
692
0
  MOZ_ASSERT(NS_IsMainThread());
693
0
694
0
  MutexAutoLock lock(mMutex);
695
0
  mErrorResult = aRv;
696
0
697
0
  if (mActor) {
698
0
    mActor->SendOperationFailed();
699
0
    mActor = nullptr;
700
0
  }
701
0
}
702
703
nsresult
704
MutableBlobStorage::DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable)
705
0
{
706
0
  if (!mTaskQueue) {
707
0
    nsCOMPtr<nsIEventTarget> target
708
0
      = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
709
0
    MOZ_ASSERT(target);
710
0
711
0
    mTaskQueue = new TaskQueue(target.forget());
712
0
  }
713
0
714
0
  nsCOMPtr<nsIRunnable> runnable(aRunnable);
715
0
  nsresult rv = mTaskQueue->Dispatch(runnable.forget());
716
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
717
0
    return rv;
718
0
  }
719
0
720
0
  return NS_OK;
721
0
}
722
723
size_t
724
MutableBlobStorage::SizeOfCurrentMemoryBuffer()
725
0
{
726
0
  MOZ_ASSERT(NS_IsMainThread());
727
0
  MutexAutoLock lock(mMutex);
728
0
  return mStorageState < eInTemporaryFile ? mDataLen : 0;
729
0
}
730
731
PRFileDesc*
732
MutableBlobStorage::GetFD()
733
0
{
734
0
  MOZ_ASSERT(!NS_IsMainThread());
735
0
  MutexAutoLock lock(mMutex);
736
0
  return mFD;
737
0
}
738
739
void
740
MutableBlobStorage::CloseFD()
741
0
{
742
0
  MOZ_ASSERT(!NS_IsMainThread());
743
0
  MutexAutoLock lock(mMutex);
744
0
  MOZ_ASSERT(mFD);
745
0
746
0
  PR_Close(mFD);
747
0
  mFD = nullptr;
748
0
}
749
750
} // dom namespace
751
} // mozilla namespace