Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/presentation/PresentationTCPSessionTransport.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 "nsArrayUtils.h"
8
#include "nsIAsyncStreamCopier.h"
9
#include "nsIInputStreamPump.h"
10
#include "nsIMultiplexInputStream.h"
11
#include "nsIMutableArray.h"
12
#include "nsIOutputStream.h"
13
#include "nsIPresentationControlChannel.h"
14
#include "nsIScriptableInputStream.h"
15
#include "nsISocketTransport.h"
16
#include "nsISocketTransportService.h"
17
#include "nsISupportsPrimitives.h"
18
#include "nsNetUtil.h"
19
#include "nsQueryObject.h"
20
#include "nsServiceManagerUtils.h"
21
#include "nsStreamUtils.h"
22
#include "nsThreadUtils.h"
23
#include "PresentationLog.h"
24
#include "PresentationTCPSessionTransport.h"
25
26
0
#define BUFFER_SIZE 65536
27
28
using namespace mozilla;
29
using namespace mozilla::dom;
30
31
class CopierCallbacks final : public nsIRequestObserver
32
{
33
public:
34
  explicit CopierCallbacks(PresentationTCPSessionTransport* aTransport)
35
    : mOwner(aTransport)
36
0
  {}
37
38
  NS_DECL_ISUPPORTS
39
  NS_DECL_NSIREQUESTOBSERVER
40
private:
41
0
  ~CopierCallbacks() {}
42
43
  RefPtr<PresentationTCPSessionTransport> mOwner;
44
};
45
46
NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)
47
48
NS_IMETHODIMP
49
CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
50
0
{
51
0
  return NS_OK;
52
0
}
53
54
NS_IMETHODIMP
55
CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
56
0
{
57
0
  mOwner->NotifyCopyComplete(aStatus);
58
0
  return NS_OK;
59
0
}
60
61
NS_IMPL_CYCLE_COLLECTION(PresentationTCPSessionTransport, mTransport,
62
                         mSocketInputStream, mSocketOutputStream,
63
                         mInputStreamPump, mInputStreamScriptable,
64
                         mCallback)
65
66
NS_IMPL_CYCLE_COLLECTING_ADDREF(PresentationTCPSessionTransport)
67
NS_IMPL_CYCLE_COLLECTING_RELEASE(PresentationTCPSessionTransport)
68
69
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PresentationTCPSessionTransport)
70
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPresentationSessionTransport)
71
0
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
72
0
  NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionTransport)
73
0
  NS_INTERFACE_MAP_ENTRY(nsIPresentationSessionTransportBuilder)
74
0
  NS_INTERFACE_MAP_ENTRY(nsIPresentationTCPSessionTransportBuilder)
75
0
  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
76
0
  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
77
0
  NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
78
0
NS_INTERFACE_MAP_END
79
80
PresentationTCPSessionTransport::PresentationTCPSessionTransport()
81
  : mReadyState(ReadyState::CLOSED)
82
  , mAsyncCopierActive(false)
83
  , mCloseStatus(NS_OK)
84
  , mDataNotificationEnabled(false)
85
0
{
86
0
}
87
88
PresentationTCPSessionTransport::~PresentationTCPSessionTransport()
89
0
{
90
0
}
91
92
NS_IMETHODIMP
93
PresentationTCPSessionTransport::BuildTCPSenderTransport(nsISocketTransport* aTransport,
94
                                                         nsIPresentationSessionTransportBuilderListener* aListener)
95
0
{
96
0
  if (NS_WARN_IF(!aTransport)) {
97
0
    return NS_ERROR_INVALID_ARG;
98
0
  }
99
0
  mTransport = aTransport;
100
0
101
0
  if (NS_WARN_IF(!aListener)) {
102
0
    return NS_ERROR_INVALID_ARG;
103
0
  }
104
0
  mListener = aListener;
105
0
106
0
  nsresult rv = CreateStream();
107
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
108
0
    return rv;
109
0
  }
110
0
111
0
  mRole = nsIPresentationService::ROLE_CONTROLLER;
112
0
113
0
  nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
114
0
  nsCOMPtr<nsIRunnable> onSessionTransportRunnable =
115
0
    NewRunnableMethod<nsIPresentationSessionTransport*>(
116
0
      "nsIPresentationSessionTransportBuilderListener::OnSessionTransport",
117
0
      mListener,
118
0
      &nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
119
0
      sessionTransport);
120
0
121
0
  NS_DispatchToCurrentThread(onSessionTransportRunnable.forget());
122
0
123
0
  nsCOMPtr<nsIRunnable> setReadyStateRunnable = NewRunnableMethod<ReadyState>(
124
0
    "dom::PresentationTCPSessionTransport::SetReadyState",
125
0
    this,
126
0
    &PresentationTCPSessionTransport::SetReadyState,
127
0
    ReadyState::OPEN);
128
0
  return NS_DispatchToCurrentThread(setReadyStateRunnable.forget());
129
0
}
130
131
NS_IMETHODIMP
132
PresentationTCPSessionTransport::BuildTCPReceiverTransport(nsIPresentationChannelDescription* aDescription,
133
                                                           nsIPresentationSessionTransportBuilderListener* aListener)
134
0
{
135
0
  if (NS_WARN_IF(!aDescription)) {
136
0
    return NS_ERROR_INVALID_ARG;
137
0
  }
138
0
139
0
  if (NS_WARN_IF(!aListener)) {
140
0
    return NS_ERROR_INVALID_ARG;
141
0
  }
142
0
  mListener = aListener;
143
0
144
0
  uint16_t serverPort;
145
0
  nsresult rv = aDescription->GetTcpPort(&serverPort);
146
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
147
0
    return rv;
148
0
  }
149
0
150
0
  nsCOMPtr<nsIArray> serverHosts;
151
0
  rv = aDescription->GetTcpAddress(getter_AddRefs(serverHosts));
152
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
153
0
    return rv;
154
0
  }
155
0
156
0
  // TODO bug 1228504 Take all IP addresses in PresentationChannelDescription
157
0
  // into account. And at the first stage Presentation API is only exposed on
158
0
  // Firefox OS where the first IP appears enough for most scenarios.
159
0
  nsCOMPtr<nsISupportsCString> supportStr = do_QueryElementAt(serverHosts, 0);
160
0
  if (NS_WARN_IF(!supportStr)) {
161
0
    return NS_ERROR_INVALID_ARG;
162
0
  }
163
0
164
0
  nsAutoCString serverHost;
165
0
  supportStr->GetData(serverHost);
166
0
  if (serverHost.IsEmpty()) {
167
0
    return NS_ERROR_INVALID_ARG;
168
0
  }
169
0
170
0
  PRES_DEBUG("%s:ServerHost[%s],ServerPort[%d]\n", __func__, serverHost.get(), serverPort);
171
0
172
0
  SetReadyState(ReadyState::CONNECTING);
173
0
174
0
  nsCOMPtr<nsISocketTransportService> sts =
175
0
    do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
176
0
  if (NS_WARN_IF(!sts)) {
177
0
    return NS_ERROR_NOT_AVAILABLE;
178
0
  }
179
0
  rv = sts->CreateTransport(nullptr, 0, serverHost, serverPort, nullptr,
180
0
                            getter_AddRefs(mTransport));
181
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
182
0
    return rv;
183
0
  }
184
0
185
0
  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
186
0
  mTransport->SetEventSink(this, mainTarget);
187
0
188
0
  rv = CreateStream();
189
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
190
0
    return rv;
191
0
  }
192
0
193
0
  mRole = nsIPresentationService::ROLE_RECEIVER;
194
0
195
0
  nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
196
0
  nsCOMPtr<nsIRunnable> runnable =
197
0
    NewRunnableMethod<nsIPresentationSessionTransport*>(
198
0
      "nsIPresentationSessionTransportBuilderListener::OnSessionTransport",
199
0
      mListener,
200
0
      &nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
201
0
      sessionTransport);
202
0
  return NS_DispatchToCurrentThread(runnable.forget());
203
0
}
204
205
nsresult
206
PresentationTCPSessionTransport::CreateStream()
207
0
{
208
0
  nsresult rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(mSocketInputStream));
209
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
210
0
    return rv;
211
0
  }
212
0
  rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(mSocketOutputStream));
213
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
214
0
    return rv;
215
0
  }
216
0
217
0
  // If the other side is not listening, we will get an |onInputStreamReady|
218
0
  // callback where |available| raises to indicate the connection was refused.
219
0
  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mSocketInputStream);
220
0
  if (NS_WARN_IF(!asyncStream)) {
221
0
    return NS_ERROR_NOT_AVAILABLE;
222
0
  }
223
0
224
0
  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
225
0
  rv = asyncStream->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, mainTarget);
226
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
227
0
    return rv;
228
0
  }
229
0
230
0
  mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
231
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
232
0
    return rv;
233
0
  }
234
0
  rv = mInputStreamScriptable->Init(mSocketInputStream);
235
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
236
0
    return rv;
237
0
  }
238
0
239
0
  return NS_OK;
240
0
}
241
242
nsresult
243
PresentationTCPSessionTransport::CreateInputStreamPump()
244
0
{
245
0
  if (NS_WARN_IF(mInputStreamPump)) {
246
0
    return NS_OK;
247
0
  }
248
0
249
0
  nsresult rv;
250
0
  mInputStreamPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
251
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
252
0
    return rv;
253
0
  }
254
0
255
0
  rv = mInputStreamPump->Init(mSocketInputStream, 0, 0, false, nullptr);
256
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
257
0
    return rv;
258
0
  }
259
0
260
0
  rv = mInputStreamPump->AsyncRead(this, nullptr);
261
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
262
0
    return rv;
263
0
  }
264
0
265
0
  return NS_OK;
266
0
}
267
268
NS_IMETHODIMP
269
PresentationTCPSessionTransport::EnableDataNotification()
270
0
{
271
0
  if (NS_WARN_IF(!mCallback)) {
272
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
273
0
  }
274
0
275
0
  if (mDataNotificationEnabled) {
276
0
    return NS_OK;
277
0
  }
278
0
279
0
  mDataNotificationEnabled = true;
280
0
281
0
  if (IsReadyToNotifyData()) {
282
0
    return CreateInputStreamPump();
283
0
  }
284
0
285
0
  return NS_OK;
286
0
}
287
288
// nsIPresentationSessionTransportBuilderListener
289
NS_IMETHODIMP
290
PresentationTCPSessionTransport::GetCallback(nsIPresentationSessionTransportCallback** aCallback)
291
0
{
292
0
  nsCOMPtr<nsIPresentationSessionTransportCallback> callback = mCallback;
293
0
  callback.forget(aCallback);
294
0
  return NS_OK;
295
0
}
296
297
NS_IMETHODIMP
298
PresentationTCPSessionTransport::SetCallback(nsIPresentationSessionTransportCallback* aCallback)
299
0
{
300
0
  mCallback = aCallback;
301
0
302
0
  if (!!mCallback && ReadyState::OPEN == mReadyState) {
303
0
    // Notify the transport channel is ready.
304
0
    Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady()));
305
0
  }
306
0
307
0
  return NS_OK;
308
0
}
309
310
NS_IMETHODIMP
311
PresentationTCPSessionTransport::GetSelfAddress(nsINetAddr** aSelfAddress)
312
0
{
313
0
  if (NS_WARN_IF(!mTransport)) {
314
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
315
0
  }
316
0
317
0
  return mTransport->GetScriptableSelfAddr(aSelfAddress);
318
0
}
319
320
nsresult
321
PresentationTCPSessionTransport::EnsureCopying()
322
0
{
323
0
  if (mAsyncCopierActive) {
324
0
    return NS_OK;
325
0
  }
326
0
327
0
  mAsyncCopierActive = true;
328
0
329
0
  nsresult rv;
330
0
331
0
  nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
332
0
    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
333
0
  NS_ENSURE_SUCCESS(rv, rv);
334
0
335
0
  nsCOMPtr<nsIInputStream> stream = do_QueryInterface(multiplexStream);
336
0
337
0
  while (!mPendingData.IsEmpty()) {
338
0
    nsCOMPtr<nsIInputStream> stream = mPendingData[0];
339
0
    multiplexStream->AppendStream(stream);
340
0
    mPendingData.RemoveElementAt(0);
341
0
  }
342
0
343
0
  nsCOMPtr<nsIAsyncStreamCopier> copier =
344
0
    do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
345
0
  NS_ENSURE_SUCCESS(rv, rv);
346
0
347
0
  nsCOMPtr<nsISocketTransportService> sts =
348
0
      do_GetService("@mozilla.org/network/socket-transport-service;1");
349
0
350
0
  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
351
0
  rv = copier->Init(stream,
352
0
                    mSocketOutputStream,
353
0
                    target,
354
0
                    true, /* source buffered */
355
0
                    false, /* sink buffered */
356
0
                    BUFFER_SIZE,
357
0
                    false, /* close source */
358
0
                    false); /* close sink */
359
0
  NS_ENSURE_SUCCESS(rv, rv);
360
0
361
0
  RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
362
0
  rv = copier->AsyncCopy(callbacks, nullptr);
363
0
  NS_ENSURE_SUCCESS(rv, rv);
364
0
365
0
  return NS_OK;
366
0
}
367
368
void
369
PresentationTCPSessionTransport::NotifyCopyComplete(nsresult aStatus)
370
0
{
371
0
  mAsyncCopierActive = false;
372
0
373
0
  if (NS_WARN_IF(NS_FAILED(aStatus))) {
374
0
    if (mReadyState != ReadyState::CLOSED) {
375
0
      mCloseStatus = aStatus;
376
0
      SetReadyState(ReadyState::CLOSED);
377
0
    }
378
0
    return;
379
0
  }
380
0
381
0
  if (!mPendingData.IsEmpty()) {
382
0
    EnsureCopying();
383
0
    return;
384
0
  }
385
0
386
0
  if (mReadyState == ReadyState::CLOSING) {
387
0
    mSocketOutputStream->Close();
388
0
    mCloseStatus = NS_OK;
389
0
    SetReadyState(ReadyState::CLOSED);
390
0
  }
391
0
}
392
393
NS_IMETHODIMP
394
PresentationTCPSessionTransport::Send(const nsAString& aData)
395
0
{
396
0
  if (NS_WARN_IF(mReadyState != ReadyState::OPEN)) {
397
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
398
0
  }
399
0
400
0
  nsresult rv;
401
0
  nsCOMPtr<nsIStringInputStream> stream =
402
0
    do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
403
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
404
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
405
0
  }
406
0
407
0
  NS_ConvertUTF16toUTF8 msgString(aData);
408
0
  rv = stream->SetData(msgString.BeginReading(), msgString.Length());
409
0
  if(NS_WARN_IF(NS_FAILED(rv))) {
410
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
411
0
  }
412
0
413
0
  mPendingData.AppendElement(stream);
414
0
415
0
  EnsureCopying();
416
0
417
0
  return NS_OK;
418
0
}
419
420
NS_IMETHODIMP
421
PresentationTCPSessionTransport::SendBinaryMsg(const nsACString& aData)
422
0
{
423
0
  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
424
0
}
425
426
NS_IMETHODIMP
427
PresentationTCPSessionTransport::SendBlob(Blob* aBlob)
428
0
{
429
0
  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
430
0
}
431
432
NS_IMETHODIMP
433
PresentationTCPSessionTransport::Close(nsresult aReason)
434
0
{
435
0
  PRES_DEBUG("%s:reason[%" PRIx32 "]\n", __func__, static_cast<uint32_t>(aReason));
436
0
437
0
  if (mReadyState == ReadyState::CLOSED || mReadyState == ReadyState::CLOSING) {
438
0
    return NS_OK;
439
0
  }
440
0
441
0
  mCloseStatus = aReason;
442
0
  SetReadyState(ReadyState::CLOSING);
443
0
444
0
  if (!mAsyncCopierActive) {
445
0
    mPendingData.Clear();
446
0
    mSocketOutputStream->Close();
447
0
  }
448
0
449
0
  mSocketInputStream->Close();
450
0
  mDataNotificationEnabled = false;
451
0
452
0
  mListener = nullptr;
453
0
454
0
  return NS_OK;
455
0
}
456
457
void
458
PresentationTCPSessionTransport::SetReadyState(ReadyState aReadyState)
459
0
{
460
0
  mReadyState = aReadyState;
461
0
462
0
  if (mReadyState == ReadyState::OPEN) {
463
0
    if (IsReadyToNotifyData()) {
464
0
      CreateInputStreamPump();
465
0
    }
466
0
467
0
    if (NS_WARN_IF(!mCallback)) {
468
0
      return;
469
0
    }
470
0
471
0
    // Notify the transport channel is ready.
472
0
    Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady()));
473
0
  } else if (mReadyState == ReadyState::CLOSED && mCallback) {
474
0
    if (NS_WARN_IF(!mCallback)) {
475
0
      return;
476
0
    }
477
0
478
0
    // Notify the transport channel has been shut down.
479
0
    Unused <<
480
0
      NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportClosed(mCloseStatus)));
481
0
    mCallback = nullptr;
482
0
  }
483
0
}
484
485
// nsITransportEventSink
486
NS_IMETHODIMP
487
PresentationTCPSessionTransport::OnTransportStatus(nsITransport* aTransport,
488
                                                   nsresult aStatus,
489
                                                   int64_t aProgress,
490
                                                   int64_t aProgressMax)
491
0
{
492
0
  PRES_DEBUG("%s:aStatus[%" PRIx32 "]\n", __func__, static_cast<uint32_t>(aStatus));
493
0
494
0
  MOZ_ASSERT(NS_IsMainThread());
495
0
496
0
  if (aStatus != NS_NET_STATUS_CONNECTED_TO) {
497
0
    return NS_OK;
498
0
  }
499
0
500
0
  SetReadyState(ReadyState::OPEN);
501
0
502
0
  return NS_OK;
503
0
}
504
505
// nsIInputStreamCallback
506
NS_IMETHODIMP
507
PresentationTCPSessionTransport::OnInputStreamReady(nsIAsyncInputStream* aStream)
508
0
{
509
0
  MOZ_ASSERT(NS_IsMainThread());
510
0
511
0
  // Only used for detecting if the connection was refused.
512
0
  uint64_t dummy;
513
0
  nsresult rv = aStream->Available(&dummy);
514
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
515
0
    if (mReadyState != ReadyState::CLOSED) {
516
0
      mCloseStatus = NS_ERROR_CONNECTION_REFUSED;
517
0
      SetReadyState(ReadyState::CLOSED);
518
0
    }
519
0
  }
520
0
521
0
  return NS_OK;
522
0
}
523
524
// nsIRequestObserver
525
NS_IMETHODIMP
526
PresentationTCPSessionTransport::OnStartRequest(nsIRequest* aRequest,
527
                                                nsISupports* aContext)
528
0
{
529
0
  // Do nothing.
530
0
  return NS_OK;
531
0
}
532
533
NS_IMETHODIMP
534
PresentationTCPSessionTransport::OnStopRequest(nsIRequest* aRequest,
535
                                               nsISupports* aContext,
536
                                               nsresult aStatusCode)
537
0
{
538
0
  PRES_DEBUG("%s:aStatusCode[%" PRIx32 "]\n", __func__, static_cast<uint32_t>(aStatusCode));
539
0
540
0
  MOZ_ASSERT(NS_IsMainThread());
541
0
542
0
  mInputStreamPump = nullptr;
543
0
544
0
  if (mAsyncCopierActive && NS_SUCCEEDED(aStatusCode)) {
545
0
    // If we have some buffered output still, and status is not an error, the
546
0
    // other side has done a half-close, but we don't want to be in the close
547
0
    // state until we are done sending everything that was buffered. We also
548
0
    // don't want to call |NotifyTransportClosed| yet.
549
0
    return NS_OK;
550
0
  }
551
0
552
0
  // We call this even if there is no error.
553
0
  if (mReadyState != ReadyState::CLOSED) {
554
0
    mCloseStatus = aStatusCode;
555
0
    SetReadyState(ReadyState::CLOSED);
556
0
  }
557
0
  return NS_OK;
558
0
}
559
560
// nsIStreamListener
561
NS_IMETHODIMP
562
PresentationTCPSessionTransport::OnDataAvailable(nsIRequest* aRequest,
563
                                                 nsISupports* aContext,
564
                                                 nsIInputStream* aStream,
565
                                                 uint64_t aOffset,
566
                                                 uint32_t aCount)
567
0
{
568
0
  MOZ_ASSERT(NS_IsMainThread());
569
0
570
0
  if (NS_WARN_IF(!mCallback)) {
571
0
    return NS_ERROR_NOT_AVAILABLE;
572
0
  }
573
0
574
0
  nsCString data;
575
0
  nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
576
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
577
0
    return rv;
578
0
  }
579
0
580
0
  // Pass the incoming data to the listener.
581
0
  return mCallback->NotifyData(data, false);
582
0
}