Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 : #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 0 : explicit CopierCallbacks(PresentationTCPSessionTransport* aTransport)
35 0 : : 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 0 : NS_IMPL_ISUPPORTS(CopierCallbacks, nsIRequestObserver)
47 :
48 : NS_IMETHODIMP
49 0 : CopierCallbacks::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
50 : {
51 0 : return NS_OK;
52 : }
53 :
54 : NS_IMETHODIMP
55 0 : CopierCallbacks::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
56 : {
57 0 : mOwner->NotifyCopyComplete(aStatus);
58 0 : return NS_OK;
59 : }
60 :
61 0 : NS_IMPL_CYCLE_COLLECTION(PresentationTCPSessionTransport, mTransport,
62 : mSocketInputStream, mSocketOutputStream,
63 : mInputStreamPump, mInputStreamScriptable,
64 : mMultiplexStream, mMultiplexStreamCopier, mCallback)
65 :
66 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(PresentationTCPSessionTransport)
67 0 : 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 0 : PresentationTCPSessionTransport::PresentationTCPSessionTransport()
81 : : mReadyState(ReadyState::CLOSED)
82 : , mAsyncCopierActive(false)
83 : , mCloseStatus(NS_OK)
84 0 : , mDataNotificationEnabled(false)
85 : {
86 0 : }
87 :
88 0 : PresentationTCPSessionTransport::~PresentationTCPSessionTransport()
89 : {
90 0 : }
91 :
92 : NS_IMETHODIMP
93 0 : PresentationTCPSessionTransport::BuildTCPSenderTransport(nsISocketTransport* aTransport,
94 : nsIPresentationSessionTransportBuilderListener* aListener)
95 : {
96 0 : if (NS_WARN_IF(!aTransport)) {
97 0 : return NS_ERROR_INVALID_ARG;
98 : }
99 0 : mTransport = aTransport;
100 :
101 0 : if (NS_WARN_IF(!aListener)) {
102 0 : return NS_ERROR_INVALID_ARG;
103 : }
104 0 : mListener = aListener;
105 :
106 0 : nsresult rv = CreateStream();
107 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
108 0 : return rv;
109 : }
110 :
111 0 : mRole = nsIPresentationService::ROLE_CONTROLLER;
112 :
113 0 : nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
114 : nsCOMPtr<nsIRunnable> onSessionTransportRunnable =
115 0 : NewRunnableMethod<nsIPresentationSessionTransport*>(
116 : "nsIPresentationSessionTransportBuilderListener::OnSessionTransport",
117 : mListener,
118 : &nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
119 0 : sessionTransport);
120 :
121 0 : NS_DispatchToCurrentThread(onSessionTransportRunnable.forget());
122 :
123 0 : nsCOMPtr<nsIRunnable> setReadyStateRunnable = NewRunnableMethod<ReadyState>(
124 : "dom::PresentationTCPSessionTransport::SetReadyState",
125 : this,
126 : &PresentationTCPSessionTransport::SetReadyState,
127 0 : ReadyState::OPEN);
128 0 : return NS_DispatchToCurrentThread(setReadyStateRunnable.forget());
129 : }
130 :
131 : NS_IMETHODIMP
132 0 : PresentationTCPSessionTransport::BuildTCPReceiverTransport(nsIPresentationChannelDescription* aDescription,
133 : nsIPresentationSessionTransportBuilderListener* aListener)
134 : {
135 0 : if (NS_WARN_IF(!aDescription)) {
136 0 : return NS_ERROR_INVALID_ARG;
137 : }
138 :
139 0 : if (NS_WARN_IF(!aListener)) {
140 0 : return NS_ERROR_INVALID_ARG;
141 : }
142 0 : mListener = aListener;
143 :
144 : uint16_t serverPort;
145 0 : nsresult rv = aDescription->GetTcpPort(&serverPort);
146 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
147 0 : return rv;
148 : }
149 :
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 : }
155 :
156 : // TODO bug 1228504 Take all IP addresses in PresentationChannelDescription
157 : // into account. And at the first stage Presentation API is only exposed on
158 : // 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 : }
163 :
164 0 : nsAutoCString serverHost;
165 0 : supportStr->GetData(serverHost);
166 0 : if (serverHost.IsEmpty()) {
167 0 : return NS_ERROR_INVALID_ARG;
168 : }
169 :
170 0 : PRES_DEBUG("%s:ServerHost[%s],ServerPort[%d]\n", __func__, serverHost.get(), serverPort);
171 :
172 0 : SetReadyState(ReadyState::CONNECTING);
173 :
174 : 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 : }
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 : }
184 :
185 0 : nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
186 0 : mTransport->SetEventSink(this, mainTarget);
187 :
188 0 : rv = CreateStream();
189 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
190 0 : return rv;
191 : }
192 :
193 0 : mRole = nsIPresentationService::ROLE_RECEIVER;
194 :
195 0 : nsCOMPtr<nsIPresentationSessionTransport> sessionTransport = do_QueryObject(this);
196 : nsCOMPtr<nsIRunnable> runnable =
197 0 : NewRunnableMethod<nsIPresentationSessionTransport*>(
198 : "nsIPresentationSessionTransportBuilderListener::OnSessionTransport",
199 : mListener,
200 : &nsIPresentationSessionTransportBuilderListener::OnSessionTransport,
201 0 : sessionTransport);
202 0 : return NS_DispatchToCurrentThread(runnable.forget());
203 : }
204 :
205 : nsresult
206 0 : PresentationTCPSessionTransport::CreateStream()
207 : {
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 : }
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 : }
216 :
217 : // If the other side is not listening, we will get an |onInputStreamReady|
218 : // 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 : }
223 :
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 : }
229 :
230 0 : mInputStreamScriptable = do_CreateInstance("@mozilla.org/scriptableinputstream;1", &rv);
231 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
232 0 : return rv;
233 : }
234 0 : rv = mInputStreamScriptable->Init(mSocketInputStream);
235 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
236 0 : return rv;
237 : }
238 :
239 0 : mMultiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
240 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
241 0 : return rv;
242 : }
243 :
244 0 : mMultiplexStreamCopier = do_CreateInstance("@mozilla.org/network/async-stream-copier;1", &rv);
245 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
246 0 : return rv;
247 : }
248 :
249 : nsCOMPtr<nsISocketTransportService> sts =
250 0 : do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
251 0 : if (NS_WARN_IF(!sts)) {
252 0 : return NS_ERROR_NOT_AVAILABLE;
253 : }
254 :
255 0 : nsCOMPtr<nsIEventTarget> target = do_QueryInterface(sts);
256 0 : rv = mMultiplexStreamCopier->Init(mMultiplexStream,
257 : mSocketOutputStream,
258 : target,
259 : true, /* source buffered */
260 : false, /* sink buffered */
261 : BUFFER_SIZE,
262 : false, /* close source */
263 0 : false); /* close sink */
264 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
265 0 : return rv;
266 : }
267 :
268 0 : return NS_OK;
269 : }
270 :
271 : nsresult
272 0 : PresentationTCPSessionTransport::CreateInputStreamPump()
273 : {
274 0 : if (NS_WARN_IF(mInputStreamPump)) {
275 0 : return NS_OK;
276 : }
277 :
278 : nsresult rv;
279 0 : mInputStreamPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
280 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
281 0 : return rv;
282 : }
283 :
284 0 : rv = mInputStreamPump->Init(mSocketInputStream, -1, -1, 0, 0, false, nullptr);
285 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
286 0 : return rv;
287 : }
288 :
289 0 : rv = mInputStreamPump->AsyncRead(this, nullptr);
290 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
291 0 : return rv;
292 : }
293 :
294 0 : return NS_OK;
295 : }
296 :
297 : NS_IMETHODIMP
298 0 : PresentationTCPSessionTransport::EnableDataNotification()
299 : {
300 0 : if (NS_WARN_IF(!mCallback)) {
301 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
302 : }
303 :
304 0 : if (mDataNotificationEnabled) {
305 0 : return NS_OK;
306 : }
307 :
308 0 : mDataNotificationEnabled = true;
309 :
310 0 : if (IsReadyToNotifyData()) {
311 0 : return CreateInputStreamPump();
312 : }
313 :
314 0 : return NS_OK;
315 : }
316 :
317 : // nsIPresentationSessionTransportBuilderListener
318 : NS_IMETHODIMP
319 0 : PresentationTCPSessionTransport::GetCallback(nsIPresentationSessionTransportCallback** aCallback)
320 : {
321 0 : nsCOMPtr<nsIPresentationSessionTransportCallback> callback = mCallback;
322 0 : callback.forget(aCallback);
323 0 : return NS_OK;
324 : }
325 :
326 : NS_IMETHODIMP
327 0 : PresentationTCPSessionTransport::SetCallback(nsIPresentationSessionTransportCallback* aCallback)
328 : {
329 0 : mCallback = aCallback;
330 :
331 0 : if (!!mCallback && ReadyState::OPEN == mReadyState) {
332 : // Notify the transport channel is ready.
333 0 : Unused << NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportReady()));
334 : }
335 :
336 0 : return NS_OK;
337 : }
338 :
339 : NS_IMETHODIMP
340 0 : PresentationTCPSessionTransport::GetSelfAddress(nsINetAddr** aSelfAddress)
341 : {
342 0 : if (NS_WARN_IF(!mTransport)) {
343 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
344 : }
345 :
346 0 : return mTransport->GetScriptableSelfAddr(aSelfAddress);
347 : }
348 :
349 : void
350 0 : PresentationTCPSessionTransport::EnsureCopying()
351 : {
352 0 : if (mAsyncCopierActive) {
353 0 : return;
354 : }
355 :
356 0 : mAsyncCopierActive = true;
357 0 : RefPtr<CopierCallbacks> callbacks = new CopierCallbacks(this);
358 0 : Unused << NS_WARN_IF(NS_FAILED(mMultiplexStreamCopier->AsyncCopy(callbacks, nullptr)));
359 : }
360 :
361 : void
362 0 : PresentationTCPSessionTransport::NotifyCopyComplete(nsresult aStatus)
363 : {
364 0 : mAsyncCopierActive = false;
365 0 : mMultiplexStream->RemoveStream(0);
366 0 : if (NS_WARN_IF(NS_FAILED(aStatus))) {
367 0 : if (mReadyState != ReadyState::CLOSED) {
368 0 : mCloseStatus = aStatus;
369 0 : SetReadyState(ReadyState::CLOSED);
370 : }
371 0 : return;
372 : }
373 :
374 : uint32_t count;
375 0 : nsresult rv = mMultiplexStream->GetCount(&count);
376 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
377 0 : return;
378 : }
379 :
380 0 : if (count) {
381 0 : EnsureCopying();
382 0 : return;
383 : }
384 :
385 0 : if (mReadyState == ReadyState::CLOSING) {
386 0 : mSocketOutputStream->Close();
387 0 : mCloseStatus = NS_OK;
388 0 : SetReadyState(ReadyState::CLOSED);
389 : }
390 : }
391 :
392 : NS_IMETHODIMP
393 0 : PresentationTCPSessionTransport::Send(const nsAString& aData)
394 : {
395 0 : if (NS_WARN_IF(mReadyState != ReadyState::OPEN)) {
396 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
397 : }
398 :
399 : nsresult rv;
400 : nsCOMPtr<nsIStringInputStream> stream =
401 0 : do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
402 0 : if(NS_WARN_IF(NS_FAILED(rv))) {
403 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
404 : }
405 :
406 0 : NS_ConvertUTF16toUTF8 msgString(aData);
407 0 : rv = stream->SetData(msgString.BeginReading(), msgString.Length());
408 0 : if(NS_WARN_IF(NS_FAILED(rv))) {
409 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
410 : }
411 :
412 0 : mMultiplexStream->AppendStream(stream);
413 :
414 0 : EnsureCopying();
415 :
416 0 : return NS_OK;
417 : }
418 :
419 : NS_IMETHODIMP
420 0 : PresentationTCPSessionTransport::SendBinaryMsg(const nsACString& aData)
421 : {
422 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
423 : }
424 :
425 : NS_IMETHODIMP
426 0 : PresentationTCPSessionTransport::SendBlob(nsIDOMBlob* aBlob)
427 : {
428 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
429 : }
430 :
431 : NS_IMETHODIMP
432 0 : PresentationTCPSessionTransport::Close(nsresult aReason)
433 : {
434 0 : PRES_DEBUG("%s:reason[%" PRIx32 "]\n", __func__, static_cast<uint32_t>(aReason));
435 :
436 0 : if (mReadyState == ReadyState::CLOSED || mReadyState == ReadyState::CLOSING) {
437 0 : return NS_OK;
438 : }
439 :
440 0 : mCloseStatus = aReason;
441 0 : SetReadyState(ReadyState::CLOSING);
442 :
443 0 : uint32_t count = 0;
444 0 : mMultiplexStream->GetCount(&count);
445 0 : if (!count) {
446 0 : mSocketOutputStream->Close();
447 : }
448 :
449 0 : mSocketInputStream->Close();
450 0 : mDataNotificationEnabled = false;
451 :
452 0 : mListener = nullptr;
453 :
454 0 : return NS_OK;
455 : }
456 :
457 : void
458 0 : PresentationTCPSessionTransport::SetReadyState(ReadyState aReadyState)
459 : {
460 0 : mReadyState = aReadyState;
461 :
462 0 : if (mReadyState == ReadyState::OPEN) {
463 0 : if (IsReadyToNotifyData()) {
464 0 : CreateInputStreamPump();
465 : }
466 :
467 0 : if (NS_WARN_IF(!mCallback)) {
468 0 : return;
469 : }
470 :
471 : // 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 : }
477 :
478 : // Notify the transport channel has been shut down.
479 : Unused <<
480 0 : NS_WARN_IF(NS_FAILED(mCallback->NotifyTransportClosed(mCloseStatus)));
481 0 : mCallback = nullptr;
482 : }
483 : }
484 :
485 : // nsITransportEventSink
486 : NS_IMETHODIMP
487 0 : PresentationTCPSessionTransport::OnTransportStatus(nsITransport* aTransport,
488 : nsresult aStatus,
489 : int64_t aProgress,
490 : int64_t aProgressMax)
491 : {
492 0 : PRES_DEBUG("%s:aStatus[%" PRIx32 "]\n", __func__, static_cast<uint32_t>(aStatus));
493 :
494 0 : MOZ_ASSERT(NS_IsMainThread());
495 :
496 0 : if (aStatus != NS_NET_STATUS_CONNECTED_TO) {
497 0 : return NS_OK;
498 : }
499 :
500 0 : SetReadyState(ReadyState::OPEN);
501 :
502 0 : return NS_OK;
503 : }
504 :
505 : // nsIInputStreamCallback
506 : NS_IMETHODIMP
507 0 : PresentationTCPSessionTransport::OnInputStreamReady(nsIAsyncInputStream* aStream)
508 : {
509 0 : MOZ_ASSERT(NS_IsMainThread());
510 :
511 : // Only used for detecting if the connection was refused.
512 : 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 : }
519 : }
520 :
521 0 : return NS_OK;
522 : }
523 :
524 : // nsIRequestObserver
525 : NS_IMETHODIMP
526 0 : PresentationTCPSessionTransport::OnStartRequest(nsIRequest* aRequest,
527 : nsISupports* aContext)
528 : {
529 : // Do nothing.
530 0 : return NS_OK;
531 : }
532 :
533 : NS_IMETHODIMP
534 0 : PresentationTCPSessionTransport::OnStopRequest(nsIRequest* aRequest,
535 : nsISupports* aContext,
536 : nsresult aStatusCode)
537 : {
538 0 : PRES_DEBUG("%s:aStatusCode[%" PRIx32 "]\n", __func__, static_cast<uint32_t>(aStatusCode));
539 :
540 0 : MOZ_ASSERT(NS_IsMainThread());
541 :
542 : uint32_t count;
543 0 : nsresult rv = mMultiplexStream->GetCount(&count);
544 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
545 0 : return rv;
546 : }
547 :
548 0 : mInputStreamPump = nullptr;
549 :
550 0 : if (count != 0 && NS_SUCCEEDED(aStatusCode)) {
551 : // If we have some buffered output still, and status is not an error, the
552 : // other side has done a half-close, but we don't want to be in the close
553 : // state until we are done sending everything that was buffered. We also
554 : // don't want to call |NotifyTransportClosed| yet.
555 0 : return NS_OK;
556 : }
557 :
558 : // We call this even if there is no error.
559 0 : if (mReadyState != ReadyState::CLOSED) {
560 0 : mCloseStatus = aStatusCode;
561 0 : SetReadyState(ReadyState::CLOSED);
562 : }
563 0 : return NS_OK;
564 : }
565 :
566 : // nsIStreamListener
567 : NS_IMETHODIMP
568 0 : PresentationTCPSessionTransport::OnDataAvailable(nsIRequest* aRequest,
569 : nsISupports* aContext,
570 : nsIInputStream* aStream,
571 : uint64_t aOffset,
572 : uint32_t aCount)
573 : {
574 0 : MOZ_ASSERT(NS_IsMainThread());
575 :
576 0 : if (NS_WARN_IF(!mCallback)) {
577 0 : return NS_ERROR_NOT_AVAILABLE;
578 : }
579 :
580 0 : nsCString data;
581 0 : nsresult rv = mInputStreamScriptable->ReadBytes(aCount, data);
582 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
583 0 : return rv;
584 : }
585 :
586 : // Pass the incoming data to the listener.
587 0 : return mCallback->NotifyData(data, false);
588 : }
|