Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/file/ipc/IPCBlobInputStream.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 "IPCBlobInputStream.h"
8
#include "IPCBlobInputStreamChild.h"
9
#include "IPCBlobInputStreamStorage.h"
10
#include "mozilla/ipc/InputStreamParams.h"
11
#include "mozilla/SlicedInputStream.h"
12
#include "mozilla/NonBlockingAsyncInputStream.h"
13
#include "IPCBlobInputStreamThread.h"
14
#include "nsIAsyncInputStream.h"
15
#include "nsIAsyncOutputStream.h"
16
#include "nsIPipe.h"
17
#include "nsStreamUtils.h"
18
#include "nsStringStream.h"
19
20
namespace mozilla {
21
namespace dom {
22
23
namespace {
24
25
class InputStreamCallbackRunnable final : public CancelableRunnable
26
{
27
public:
28
  // Note that the execution can be synchronous in case the event target is
29
  // null.
30
  static void
31
  Execute(nsIInputStreamCallback* aCallback,
32
          nsIEventTarget* aEventTarget,
33
          IPCBlobInputStream* aStream)
34
0
  {
35
0
    MOZ_ASSERT(aCallback);
36
0
37
0
    RefPtr<InputStreamCallbackRunnable> runnable =
38
0
      new InputStreamCallbackRunnable(aCallback, aStream);
39
0
40
0
    nsCOMPtr<nsIEventTarget> target = aEventTarget;
41
0
    if (aEventTarget) {
42
0
      target->Dispatch(runnable, NS_DISPATCH_NORMAL);
43
0
    } else {
44
0
      runnable->Run();
45
0
    }
46
0
  }
47
48
  NS_IMETHOD
49
  Run() override
50
0
  {
51
0
    mCallback->OnInputStreamReady(mStream);
52
0
    mCallback = nullptr;
53
0
    mStream = nullptr;
54
0
    return NS_OK;
55
0
  }
56
57
private:
58
  InputStreamCallbackRunnable(nsIInputStreamCallback* aCallback,
59
                              IPCBlobInputStream* aStream)
60
    : CancelableRunnable("dom::InputStreamCallbackRunnable")
61
    , mCallback(aCallback)
62
    , mStream(aStream)
63
0
  {
64
0
    MOZ_ASSERT(mCallback);
65
0
    MOZ_ASSERT(mStream);
66
0
  }
67
68
  nsCOMPtr<nsIInputStreamCallback> mCallback;
69
  RefPtr<IPCBlobInputStream> mStream;
70
};
71
72
class FileMetadataCallbackRunnable final : public CancelableRunnable
73
{
74
public:
75
  static void
76
  Execute(nsIFileMetadataCallback* aCallback,
77
          nsIEventTarget* aEventTarget,
78
          IPCBlobInputStream* aStream)
79
0
  {
80
0
    MOZ_ASSERT(aCallback);
81
0
    MOZ_ASSERT(aEventTarget);
82
0
83
0
    RefPtr<FileMetadataCallbackRunnable> runnable =
84
0
      new FileMetadataCallbackRunnable(aCallback, aStream);
85
0
86
0
    nsCOMPtr<nsIEventTarget> target = aEventTarget;
87
0
    target->Dispatch(runnable, NS_DISPATCH_NORMAL);
88
0
  }
89
90
  NS_IMETHOD
91
  Run() override
92
0
  {
93
0
    mCallback->OnFileMetadataReady(mStream);
94
0
    mCallback = nullptr;
95
0
    mStream = nullptr;
96
0
    return NS_OK;
97
0
  }
98
99
private:
100
  FileMetadataCallbackRunnable(nsIFileMetadataCallback* aCallback,
101
                               IPCBlobInputStream* aStream)
102
    : CancelableRunnable("dom::FileMetadataCallbackRunnable")
103
    , mCallback(aCallback)
104
    , mStream(aStream)
105
0
  {
106
0
    MOZ_ASSERT(mCallback);
107
0
    MOZ_ASSERT(mStream);
108
0
  }
109
110
  nsCOMPtr<nsIFileMetadataCallback> mCallback;
111
  RefPtr<IPCBlobInputStream> mStream;
112
};
113
114
} // anonymous
115
116
NS_IMPL_ADDREF(IPCBlobInputStream);
117
NS_IMPL_RELEASE(IPCBlobInputStream);
118
119
0
NS_INTERFACE_MAP_BEGIN(IPCBlobInputStream)
120
0
  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
121
0
  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
122
0
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
123
0
  NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
124
0
  NS_INTERFACE_MAP_ENTRY(nsICloneableInputStreamWithRange)
125
0
  NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
126
0
  NS_INTERFACE_MAP_ENTRY(nsIFileMetadata)
127
0
  NS_INTERFACE_MAP_ENTRY(nsIAsyncFileMetadata)
128
0
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamLength)
129
0
  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStreamLength)
130
0
  NS_INTERFACE_MAP_ENTRY(nsIIPCBlobInputStream)
131
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
132
0
NS_INTERFACE_MAP_END
133
134
IPCBlobInputStream::IPCBlobInputStream(IPCBlobInputStreamChild* aActor)
135
  : mActor(aActor)
136
  , mState(eInit)
137
  , mStart(0)
138
  , mLength(0)
139
  , mConsumed(false)
140
  , mMutex("IPCBlobInputStream::mMutex")
141
0
{
142
0
  MOZ_ASSERT(aActor);
143
0
144
0
  mLength = aActor->Size();
145
0
146
0
  if (XRE_IsParentProcess()) {
147
0
    nsCOMPtr<nsIInputStream> stream;
148
0
    IPCBlobInputStreamStorage::Get()->GetStream(mActor->ID(),
149
0
                                                0, mLength,
150
0
                                                getter_AddRefs(stream));
151
0
    if (stream) {
152
0
      mState = eRunning;
153
0
      mRemoteStream = stream;
154
0
    }
155
0
  }
156
0
}
157
158
IPCBlobInputStream::~IPCBlobInputStream()
159
0
{
160
0
  Close();
161
0
}
162
163
// nsIInputStream interface
164
165
NS_IMETHODIMP
166
IPCBlobInputStream::Available(uint64_t* aLength)
167
0
{
168
0
  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
169
0
  {
170
0
    MutexAutoLock lock(mMutex);
171
0
172
0
    // We don't have a remoteStream yet: let's return 0.
173
0
    if (mState == eInit || mState == ePending) {
174
0
      *aLength = 0;
175
0
      return NS_OK;
176
0
    }
177
0
178
0
    if (mState == eClosed) {
179
0
      return NS_BASE_STREAM_CLOSED;
180
0
    }
181
0
182
0
    MOZ_ASSERT(mState == eRunning);
183
0
    MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
184
0
185
0
    nsresult rv = EnsureAsyncRemoteStream(lock);
186
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
187
0
      return rv;
188
0
    }
189
0
190
0
    asyncRemoteStream = mAsyncRemoteStream;
191
0
  }
192
0
193
0
  MOZ_ASSERT(asyncRemoteStream);
194
0
  return asyncRemoteStream->Available(aLength);
195
0
}
196
197
NS_IMETHODIMP
198
IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
199
0
{
200
0
  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
201
0
  {
202
0
    MutexAutoLock lock(mMutex);
203
0
204
0
    // Read is not available is we don't have a remoteStream.
205
0
    if (mState == eInit || mState == ePending) {
206
0
      return NS_BASE_STREAM_WOULD_BLOCK;
207
0
    }
208
0
209
0
    if (mState == eClosed) {
210
0
      return NS_BASE_STREAM_CLOSED;
211
0
    }
212
0
213
0
    MOZ_ASSERT(mState == eRunning);
214
0
    MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
215
0
216
0
    nsresult rv = EnsureAsyncRemoteStream(lock);
217
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
218
0
      return rv;
219
0
    }
220
0
221
0
    asyncRemoteStream = mAsyncRemoteStream;
222
0
  }
223
0
224
0
  MOZ_ASSERT(asyncRemoteStream);
225
0
  nsresult rv = asyncRemoteStream->Read(aBuffer, aCount, aReadCount);
226
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
227
0
    return rv;
228
0
  }
229
0
230
0
  {
231
0
    MutexAutoLock lock(mMutex);
232
0
    mConsumed = true;
233
0
  }
234
0
235
0
  return NS_OK;
236
0
}
237
238
NS_IMETHODIMP
239
IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
240
                                 uint32_t aCount, uint32_t *aResult)
241
0
{
242
0
  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
243
0
  {
244
0
    MutexAutoLock lock(mMutex);
245
0
246
0
    // ReadSegments is not available is we don't have a remoteStream.
247
0
    if (mState == eInit || mState == ePending) {
248
0
      return NS_BASE_STREAM_WOULD_BLOCK;
249
0
    }
250
0
251
0
    if (mState == eClosed) {
252
0
      return NS_BASE_STREAM_CLOSED;
253
0
    }
254
0
255
0
    MOZ_ASSERT(mState == eRunning);
256
0
    MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
257
0
258
0
    nsresult rv = EnsureAsyncRemoteStream(lock);
259
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
260
0
      return rv;
261
0
    }
262
0
263
0
    asyncRemoteStream = mAsyncRemoteStream;
264
0
  }
265
0
266
0
  MOZ_ASSERT(asyncRemoteStream);
267
0
  nsresult rv = asyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
268
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
269
0
    return rv;
270
0
  }
271
0
272
0
  // If some data has been read, we mark the stream as consumed.
273
0
  if (*aResult != 0) {
274
0
    MutexAutoLock lock(mMutex);
275
0
    mConsumed = true;
276
0
  }
277
0
278
0
  return NS_OK;
279
0
}
280
281
NS_IMETHODIMP
282
IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
283
0
{
284
0
  *aNonBlocking = true;
285
0
  return NS_OK;
286
0
}
287
288
NS_IMETHODIMP
289
IPCBlobInputStream::Close()
290
0
{
291
0
  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
292
0
  nsCOMPtr<nsIInputStream> remoteStream;
293
0
  {
294
0
    MutexAutoLock lock(mMutex);
295
0
296
0
    if (mActor) {
297
0
      mActor->ForgetStream(this);
298
0
      mActor = nullptr;
299
0
    }
300
0
301
0
    asyncRemoteStream.swap(mAsyncRemoteStream);
302
0
    remoteStream.swap(mRemoteStream);
303
0
304
0
    mInputStreamCallback = nullptr;
305
0
    mInputStreamCallbackEventTarget = nullptr;
306
0
307
0
    mFileMetadataCallback = nullptr;
308
0
    mFileMetadataCallbackEventTarget = nullptr;
309
0
310
0
    mState = eClosed;
311
0
  }
312
0
313
0
  if (asyncRemoteStream) {
314
0
    asyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
315
0
  }
316
0
317
0
  if (remoteStream) {
318
0
    remoteStream->Close();
319
0
  }
320
0
321
0
  return NS_OK;
322
0
}
323
324
// nsICloneableInputStream interface
325
326
NS_IMETHODIMP
327
IPCBlobInputStream::GetCloneable(bool* aCloneable)
328
0
{
329
0
  MutexAutoLock lock(mMutex);
330
0
  *aCloneable = mState != eClosed;
331
0
  return NS_OK;
332
0
}
333
334
NS_IMETHODIMP
335
IPCBlobInputStream::Clone(nsIInputStream** aResult)
336
0
{
337
0
  MutexAutoLock lock(mMutex);
338
0
339
0
  if (mState == eClosed) {
340
0
    return NS_BASE_STREAM_CLOSED;
341
0
  }
342
0
343
0
  MOZ_ASSERT(mActor);
344
0
345
0
  RefPtr<IPCBlobInputStream> stream = mActor->CreateStream();
346
0
  if (!stream) {
347
0
    return NS_ERROR_FAILURE;
348
0
  }
349
0
350
0
  stream->InitWithExistingRange(mStart, mLength, lock);
351
0
352
0
  stream.forget(aResult);
353
0
  return NS_OK;
354
0
}
355
356
// nsICloneableInputStreamWithRange interface
357
358
NS_IMETHODIMP
359
IPCBlobInputStream::CloneWithRange(uint64_t aStart, uint64_t aLength,
360
                                   nsIInputStream** aResult)
361
0
{
362
0
  MutexAutoLock lock(mMutex);
363
0
364
0
  if (mState == eClosed) {
365
0
    return NS_BASE_STREAM_CLOSED;
366
0
  }
367
0
368
0
  // Too short or out of range.
369
0
  if (aLength == 0 || aStart >= mLength) {
370
0
    return NS_NewCStringInputStream(aResult, EmptyCString());
371
0
  }
372
0
373
0
  MOZ_ASSERT(mActor);
374
0
375
0
  RefPtr<IPCBlobInputStream> stream = mActor->CreateStream();
376
0
  if (!stream) {
377
0
    return NS_ERROR_FAILURE;
378
0
  }
379
0
380
0
  CheckedInt<uint64_t> streamSize = mLength;
381
0
  streamSize -= aStart;
382
0
  if (!streamSize.isValid()) {
383
0
    return NS_ERROR_FAILURE;
384
0
  }
385
0
386
0
  if (aLength > streamSize.value()) {
387
0
    aLength = streamSize.value();
388
0
  }
389
0
390
0
  stream->InitWithExistingRange(aStart + mStart, aLength, lock);
391
0
392
0
  stream.forget(aResult);
393
0
  return NS_OK;
394
0
}
395
396
// nsIAsyncInputStream interface
397
398
NS_IMETHODIMP
399
IPCBlobInputStream::CloseWithStatus(nsresult aStatus)
400
0
{
401
0
  return Close();
402
0
}
403
404
NS_IMETHODIMP
405
IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
406
                              uint32_t aFlags, uint32_t aRequestedCount,
407
                              nsIEventTarget* aEventTarget)
408
0
{
409
0
  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
410
0
  {
411
0
    MutexAutoLock lock(mMutex);
412
0
413
0
    // See IPCBlobInputStream.h for more information about this state machine.
414
0
415
0
    switch (mState) {
416
0
    // First call, we need to retrieve the stream from the parent actor.
417
0
    case eInit:
418
0
      MOZ_ASSERT(mActor);
419
0
420
0
      mInputStreamCallback = aCallback;
421
0
      mInputStreamCallbackEventTarget = aEventTarget;
422
0
      mState = ePending;
423
0
424
0
      mActor->StreamNeeded(this, aEventTarget);
425
0
      return NS_OK;
426
0
427
0
    // We are still waiting for the remote inputStream
428
0
    case ePending: {
429
0
      if (mInputStreamCallback && aCallback) {
430
0
        return NS_ERROR_FAILURE;
431
0
      }
432
0
433
0
      mInputStreamCallback = aCallback;
434
0
      mInputStreamCallbackEventTarget = aEventTarget;
435
0
      return NS_OK;
436
0
    }
437
0
438
0
    // We have the remote inputStream, let's check if we can execute the callback.
439
0
    case eRunning: {
440
0
      if (mInputStreamCallback && aCallback) {
441
0
        return NS_ERROR_FAILURE;
442
0
      }
443
0
444
0
      nsresult rv = EnsureAsyncRemoteStream(lock);
445
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
446
0
        return rv;
447
0
      }
448
0
449
0
      mInputStreamCallback = aCallback;
450
0
      mInputStreamCallbackEventTarget = aEventTarget;
451
0
452
0
      asyncRemoteStream = mAsyncRemoteStream;
453
0
      break;
454
0
    }
455
0
456
0
    // Stream is closed.
457
0
    default:
458
0
      MOZ_ASSERT(mState == eClosed);
459
0
      return NS_BASE_STREAM_CLOSED;
460
0
    }
461
0
  }
462
0
463
0
  MOZ_ASSERT(asyncRemoteStream);
464
0
  return asyncRemoteStream->AsyncWait(aCallback ? this : nullptr,
465
0
                                      0, 0, aEventTarget);
466
0
}
467
468
void
469
IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
470
0
{
471
0
  nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
472
0
473
0
  // If inputStream is null, it means that the serialization went wrong or the
474
0
  // stream is not available anymore. We keep the state as pending just to block
475
0
  // any additional operation.
476
0
477
0
  if (!inputStream) {
478
0
    return;
479
0
  }
480
0
481
0
  nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
482
0
  nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
483
0
  nsCOMPtr<nsIInputStreamCallback> inputStreamCallback;
484
0
  nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
485
0
  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
486
0
  {
487
0
    MutexAutoLock lock(mMutex);
488
0
489
0
    // We have been closed in the meantime.
490
0
    if (mState == eClosed) {
491
0
      if (inputStream) {
492
0
        MutexAutoUnlock unlock(mMutex);
493
0
        inputStream->Close();
494
0
      }
495
0
      return;
496
0
    }
497
0
498
0
    // Now it's the right time to apply a slice if needed.
499
0
    if (mStart > 0 || mLength < mActor->Size()) {
500
0
      inputStream =
501
0
        new SlicedInputStream(inputStream.forget(), mStart, mLength);
502
0
    }
503
0
504
0
    mRemoteStream = inputStream;
505
0
506
0
    MOZ_ASSERT(mState == ePending);
507
0
    mState = eRunning;
508
0
509
0
    fileMetadataCallback.swap(mFileMetadataCallback);
510
0
    fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
511
0
512
0
    inputStreamCallback = mInputStreamCallback ? this : nullptr;
513
0
    inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
514
0
515
0
    if (inputStreamCallback) {
516
0
      nsresult rv = EnsureAsyncRemoteStream(lock);
517
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
518
0
        return;
519
0
      }
520
0
521
0
      MOZ_ASSERT(mAsyncRemoteStream);
522
0
      asyncRemoteStream = mAsyncRemoteStream;
523
0
    }
524
0
  }
525
0
526
0
  if (fileMetadataCallback) {
527
0
    FileMetadataCallbackRunnable::Execute(fileMetadataCallback,
528
0
                                          fileMetadataCallbackEventTarget,
529
0
                                          this);
530
0
  }
531
0
532
0
  if (inputStreamCallback) {
533
0
    MOZ_ASSERT(asyncRemoteStream);
534
0
535
0
    nsresult rv = asyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
536
0
                                               inputStreamCallbackEventTarget);
537
0
    Unused << NS_WARN_IF(NS_FAILED(rv));
538
0
  }
539
0
}
540
541
void
542
IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength,
543
                                          const MutexAutoLock& aProofOfLock)
544
0
{
545
0
  MOZ_ASSERT(mActor->Size() >= aStart + aLength);
546
0
  mStart = aStart;
547
0
  mLength = aLength;
548
0
549
0
  // In the child, we slice in StreamReady() when we set mState to eRunning.
550
0
  // But in the parent, we start out eRunning, so it's necessary to slice the
551
0
  // stream as soon as we have the information during the initialization phase
552
0
  // because the stream is immediately consumable.
553
0
  if (mState == eRunning && mRemoteStream && XRE_IsParentProcess() &&
554
0
      (mStart > 0 || mLength < mActor->Size())) {
555
0
    mRemoteStream =
556
0
      new SlicedInputStream(mRemoteStream.forget(), mStart, mLength);
557
0
  }
558
0
}
559
560
// nsIInputStreamCallback
561
562
NS_IMETHODIMP
563
IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
564
0
{
565
0
  nsCOMPtr<nsIInputStreamCallback> callback;
566
0
  nsCOMPtr<nsIEventTarget> callbackEventTarget;
567
0
  {
568
0
    MutexAutoLock lock(mMutex);
569
0
570
0
    // We have been closed in the meantime.
571
0
    if (mState == eClosed) {
572
0
      return NS_OK;
573
0
    }
574
0
575
0
    MOZ_ASSERT(mState == eRunning);
576
0
    MOZ_ASSERT(mAsyncRemoteStream == aStream);
577
0
578
0
    // The callback has been canceled in the meantime.
579
0
    if (!mInputStreamCallback) {
580
0
      return NS_OK;
581
0
    }
582
0
583
0
    callback.swap(mInputStreamCallback);
584
0
    callbackEventTarget.swap(mInputStreamCallbackEventTarget);
585
0
  }
586
0
587
0
  // This must be the last operation because the execution of the callback can
588
0
  // be synchronous.
589
0
  MOZ_ASSERT(callback);
590
0
  InputStreamCallbackRunnable::Execute(callback, callbackEventTarget, this);
591
0
  return NS_OK;
592
0
}
593
594
// nsIIPCSerializableInputStream
595
596
void
597
IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
598
                              FileDescriptorArray& aFileDescriptors)
599
0
{
600
0
  MutexAutoLock lock(mMutex);
601
0
602
0
  mozilla::ipc::IPCBlobInputStreamParams params;
603
0
  params.id() = mActor->ID();
604
0
  params.start() = mStart;
605
0
  params.length() = mLength;
606
0
607
0
  aParams = params;
608
0
}
609
610
bool
611
IPCBlobInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
612
                                const FileDescriptorArray& aFileDescriptors)
613
0
{
614
0
  MOZ_CRASH("This should never be called.");
615
0
  return false;
616
0
}
617
618
mozilla::Maybe<uint64_t>
619
IPCBlobInputStream::ExpectedSerializedLength()
620
0
{
621
0
  return mozilla::Nothing();
622
0
}
623
624
// nsIAsyncFileMetadata
625
626
NS_IMETHODIMP
627
IPCBlobInputStream::AsyncFileMetadataWait(nsIFileMetadataCallback* aCallback,
628
                                          nsIEventTarget* aEventTarget)
629
0
{
630
0
  MOZ_ASSERT(!!aCallback == !!aEventTarget);
631
0
632
0
  // If we have the callback, we must have the event target.
633
0
  if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
634
0
    return NS_ERROR_FAILURE;
635
0
  }
636
0
637
0
  // See IPCBlobInputStream.h for more information about this state machine.
638
0
639
0
  {
640
0
    MutexAutoLock lock(mMutex);
641
0
642
0
    switch (mState) {
643
0
    // First call, we need to retrieve the stream from the parent actor.
644
0
    case eInit:
645
0
      MOZ_ASSERT(mActor);
646
0
647
0
      mFileMetadataCallback = aCallback;
648
0
      mFileMetadataCallbackEventTarget = aEventTarget;
649
0
      mState = ePending;
650
0
651
0
      mActor->StreamNeeded(this, aEventTarget);
652
0
      return NS_OK;
653
0
654
0
    // We are still waiting for the remote inputStream
655
0
    case ePending:
656
0
      if (mFileMetadataCallback && aCallback) {
657
0
        return NS_ERROR_FAILURE;
658
0
      }
659
0
660
0
      mFileMetadataCallback = aCallback;
661
0
      mFileMetadataCallbackEventTarget = aEventTarget;
662
0
      return NS_OK;
663
0
664
0
    // We have the remote inputStream, let's check if we can execute the callback.
665
0
    case eRunning:
666
0
      break;
667
0
668
0
    // Stream is closed.
669
0
    default:
670
0
      MOZ_ASSERT(mState == eClosed);
671
0
      return NS_BASE_STREAM_CLOSED;
672
0
    }
673
0
674
0
    MOZ_ASSERT(mState == eRunning);
675
0
  }
676
0
677
0
  FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
678
0
  return NS_OK;
679
0
}
680
681
// nsIFileMetadata
682
683
NS_IMETHODIMP
684
IPCBlobInputStream::GetSize(int64_t* aRetval)
685
0
{
686
0
  nsCOMPtr<nsIFileMetadata> fileMetadata;
687
0
  {
688
0
    MutexAutoLock lock(mMutex);
689
0
    fileMetadata = do_QueryInterface(mRemoteStream);
690
0
    if (!fileMetadata) {
691
0
      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
692
0
    }
693
0
  }
694
0
695
0
  return fileMetadata->GetSize(aRetval);
696
0
}
697
698
NS_IMETHODIMP
699
IPCBlobInputStream::GetLastModified(int64_t* aRetval)
700
0
{
701
0
  nsCOMPtr<nsIFileMetadata> fileMetadata;
702
0
  {
703
0
    MutexAutoLock lock(mMutex);
704
0
    fileMetadata = do_QueryInterface(mRemoteStream);
705
0
    if (!fileMetadata) {
706
0
      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
707
0
    }
708
0
  }
709
0
710
0
  return fileMetadata->GetLastModified(aRetval);
711
0
}
712
713
NS_IMETHODIMP
714
IPCBlobInputStream::GetFileDescriptor(PRFileDesc** aRetval)
715
0
{
716
0
  nsCOMPtr<nsIFileMetadata> fileMetadata;
717
0
  {
718
0
    MutexAutoLock lock(mMutex);
719
0
    fileMetadata = do_QueryInterface(mRemoteStream);
720
0
    if (!fileMetadata) {
721
0
      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
722
0
    }
723
0
  }
724
0
725
0
  return fileMetadata->GetFileDescriptor(aRetval);
726
0
}
727
728
nsresult
729
IPCBlobInputStream::EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock)
730
0
{
731
0
  // We already have an async remote stream.
732
0
  if (mAsyncRemoteStream) {
733
0
    return NS_OK;
734
0
  }
735
0
736
0
  if (!mRemoteStream) {
737
0
    return NS_ERROR_FAILURE;
738
0
  }
739
0
740
0
  // If the stream is blocking, we want to make it unblocking using a pipe.
741
0
  bool nonBlocking = false;
742
0
  nsresult rv = mRemoteStream->IsNonBlocking(&nonBlocking);
743
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
744
0
    return rv;
745
0
  }
746
0
747
0
  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mRemoteStream);
748
0
749
0
  // If non-blocking and non-async, let's use NonBlockingAsyncInputStream.
750
0
  if (nonBlocking && !asyncStream) {
751
0
    rv = NonBlockingAsyncInputStream::Create(mRemoteStream.forget(),
752
0
                                             getter_AddRefs(asyncStream));
753
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
754
0
      return rv;
755
0
    }
756
0
757
0
    MOZ_ASSERT(asyncStream);
758
0
  }
759
0
760
0
  if (!asyncStream) {
761
0
    // Let's make the stream async using the DOMFile thread.
762
0
    nsCOMPtr<nsIAsyncInputStream> pipeIn;
763
0
    nsCOMPtr<nsIAsyncOutputStream> pipeOut;
764
0
    rv = NS_NewPipe2(getter_AddRefs(pipeIn),
765
0
                     getter_AddRefs(pipeOut),
766
0
                     true, true);
767
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
768
0
      return rv;
769
0
    }
770
0
771
0
    RefPtr<IPCBlobInputStreamThread> thread =
772
0
      IPCBlobInputStreamThread::GetOrCreate();
773
0
    if (NS_WARN_IF(!thread)) {
774
0
      return NS_ERROR_FAILURE;
775
0
    }
776
0
777
0
    rv = NS_AsyncCopy(mRemoteStream, pipeOut, thread,
778
0
                      NS_ASYNCCOPY_VIA_WRITESEGMENTS);
779
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
780
0
      return rv;
781
0
    }
782
0
783
0
    asyncStream = pipeIn;
784
0
  }
785
0
786
0
  MOZ_ASSERT(asyncStream);
787
0
  mAsyncRemoteStream = asyncStream;
788
0
  mRemoteStream = nullptr;
789
0
790
0
  return NS_OK;
791
0
}
792
793
// nsIInputStreamLength
794
795
NS_IMETHODIMP
796
IPCBlobInputStream::Length(int64_t* aLength)
797
0
{
798
0
  MutexAutoLock lock(mMutex);
799
0
800
0
  if (mState == eClosed) {
801
0
    return NS_BASE_STREAM_CLOSED;
802
0
  }
803
0
804
0
  if (mConsumed) {
805
0
    return NS_ERROR_NOT_AVAILABLE;
806
0
  }
807
0
808
0
  return NS_BASE_STREAM_WOULD_BLOCK;
809
0
}
810
811
// nsIAsyncInputStreamLength
812
813
NS_IMETHODIMP
814
IPCBlobInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
815
                                    nsIEventTarget* aEventTarget)
816
0
{
817
0
  MutexAutoLock lock(mMutex);
818
0
819
0
  if (mState == eClosed) {
820
0
    return NS_BASE_STREAM_CLOSED;
821
0
  }
822
0
823
0
  if (mConsumed) {
824
0
    return NS_ERROR_NOT_AVAILABLE;
825
0
  }
826
0
827
0
  // If we have the callback, we must have the event target.
828
0
  if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
829
0
    return NS_ERROR_FAILURE;
830
0
  }
831
0
832
0
  MOZ_ASSERT(mActor);
833
0
834
0
  mLengthCallback = aCallback;
835
0
  mLengthCallbackEventTarget = aEventTarget;
836
0
837
0
  if (aCallback) {
838
0
    mActor->LengthNeeded(this, aEventTarget);
839
0
  }
840
0
841
0
  return NS_OK;
842
0
}
843
844
namespace {
845
846
class InputStreamLengthCallbackRunnable final : public CancelableRunnable
847
{
848
public:
849
  static void
850
  Execute(nsIInputStreamLengthCallback* aCallback,
851
          nsIEventTarget* aEventTarget,
852
          IPCBlobInputStream* aStream,
853
          int64_t aLength)
854
0
  {
855
0
    MOZ_ASSERT(aCallback);
856
0
    MOZ_ASSERT(aEventTarget);
857
0
858
0
    RefPtr<InputStreamLengthCallbackRunnable> runnable =
859
0
      new InputStreamLengthCallbackRunnable(aCallback, aStream, aLength);
860
0
861
0
    nsCOMPtr<nsIEventTarget> target = aEventTarget;
862
0
    target->Dispatch(runnable, NS_DISPATCH_NORMAL);
863
0
  }
864
865
  NS_IMETHOD
866
  Run() override
867
0
  {
868
0
    mCallback->OnInputStreamLengthReady(mStream, mLength);
869
0
    mCallback = nullptr;
870
0
    mStream = nullptr;
871
0
    return NS_OK;
872
0
  }
873
874
private:
875
  InputStreamLengthCallbackRunnable(nsIInputStreamLengthCallback* aCallback,
876
                                    IPCBlobInputStream* aStream,
877
                                    int64_t aLength)
878
    : CancelableRunnable("dom::InputStreamLengthCallbackRunnable")
879
    , mCallback(aCallback)
880
    , mStream(aStream)
881
    , mLength(aLength)
882
0
  {
883
0
    MOZ_ASSERT(mCallback);
884
0
    MOZ_ASSERT(mStream);
885
0
  }
886
887
  nsCOMPtr<nsIInputStreamLengthCallback> mCallback;
888
  RefPtr<IPCBlobInputStream> mStream;
889
  int64_t mLength;
890
};
891
892
} // anonymous
893
894
void
895
IPCBlobInputStream::LengthReady(int64_t aLength)
896
0
{
897
0
  nsCOMPtr<nsIInputStreamLengthCallback> lengthCallback;
898
0
  nsCOMPtr<nsIEventTarget> lengthCallbackEventTarget;
899
0
900
0
  {
901
0
    MutexAutoLock lock(mMutex);
902
0
903
0
    // We have been closed in the meantime.
904
0
    if (mState == eClosed || mConsumed) {
905
0
      return;
906
0
    }
907
0
908
0
    if (mStart > 0) {
909
0
      aLength -= mStart;
910
0
    }
911
0
912
0
    if (mLength < mActor->Size()) {
913
0
      // If the remote stream must be sliced, we must return here the correct
914
0
      // value.
915
0
      aLength = XPCOM_MIN(aLength, (int64_t)mLength);
916
0
    }
917
0
918
0
    lengthCallback.swap(mLengthCallback);
919
0
    lengthCallbackEventTarget.swap(mLengthCallbackEventTarget);
920
0
  }
921
0
922
0
  if (lengthCallback) {
923
0
    InputStreamLengthCallbackRunnable::Execute(lengthCallback,
924
0
                                               lengthCallbackEventTarget,
925
0
                                               this,
926
0
                                               aLength);
927
0
  }
928
0
}
929
930
} // namespace dom
931
} // namespace mozilla