Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* vim:set ts=4 sw=4 sts=4 et cin: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Mutex.h"
7 #include "mozilla/Attributes.h"
8 #include "nsStreamUtils.h"
9 #include "nsAutoPtr.h"
10 #include "nsCOMPtr.h"
11 #include "nsIPipe.h"
12 #include "nsIEventTarget.h"
13 #include "nsIRunnable.h"
14 #include "nsISafeOutputStream.h"
15 #include "nsString.h"
16 #include "nsIAsyncInputStream.h"
17 #include "nsIAsyncOutputStream.h"
18 #include "nsIBufferedStreams.h"
20 using namespace mozilla;
22 //-----------------------------------------------------------------------------
24 class nsInputStreamReadyEvent MOZ_FINAL : public nsIRunnable
25 , public nsIInputStreamCallback
26 {
27 public:
28 NS_DECL_THREADSAFE_ISUPPORTS
30 nsInputStreamReadyEvent(nsIInputStreamCallback *callback,
31 nsIEventTarget *target)
32 : mCallback(callback)
33 , mTarget(target)
34 {
35 }
37 private:
38 ~nsInputStreamReadyEvent()
39 {
40 if (!mCallback)
41 return;
42 //
43 // whoa!! looks like we never posted this event. take care to
44 // release mCallback on the correct thread. if mTarget lives on the
45 // calling thread, then we are ok. otherwise, we have to try to
46 // proxy the Release over the right thread. if that thread is dead,
47 // then there's nothing we can do... better to leak than crash.
48 //
49 bool val;
50 nsresult rv = mTarget->IsOnCurrentThread(&val);
51 if (NS_FAILED(rv) || !val) {
52 nsCOMPtr<nsIInputStreamCallback> event =
53 NS_NewInputStreamReadyEvent(mCallback, mTarget);
54 mCallback = nullptr;
55 if (event) {
56 rv = event->OnInputStreamReady(nullptr);
57 if (NS_FAILED(rv)) {
58 NS_NOTREACHED("leaking stream event");
59 nsISupports *sup = event;
60 NS_ADDREF(sup);
61 }
62 }
63 }
64 }
66 public:
67 NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream)
68 {
69 mStream = stream;
71 nsresult rv =
72 mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
73 if (NS_FAILED(rv)) {
74 NS_WARNING("Dispatch failed");
75 return NS_ERROR_FAILURE;
76 }
78 return NS_OK;
79 }
81 NS_IMETHOD Run()
82 {
83 if (mCallback) {
84 if (mStream)
85 mCallback->OnInputStreamReady(mStream);
86 mCallback = nullptr;
87 }
88 return NS_OK;
89 }
91 private:
92 nsCOMPtr<nsIAsyncInputStream> mStream;
93 nsCOMPtr<nsIInputStreamCallback> mCallback;
94 nsCOMPtr<nsIEventTarget> mTarget;
95 };
97 NS_IMPL_ISUPPORTS(nsInputStreamReadyEvent, nsIRunnable,
98 nsIInputStreamCallback)
100 //-----------------------------------------------------------------------------
102 class nsOutputStreamReadyEvent MOZ_FINAL : public nsIRunnable
103 , public nsIOutputStreamCallback
104 {
105 public:
106 NS_DECL_THREADSAFE_ISUPPORTS
108 nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
109 nsIEventTarget *target)
110 : mCallback(callback)
111 , mTarget(target)
112 {
113 }
115 private:
116 ~nsOutputStreamReadyEvent()
117 {
118 if (!mCallback)
119 return;
120 //
121 // whoa!! looks like we never posted this event. take care to
122 // release mCallback on the correct thread. if mTarget lives on the
123 // calling thread, then we are ok. otherwise, we have to try to
124 // proxy the Release over the right thread. if that thread is dead,
125 // then there's nothing we can do... better to leak than crash.
126 //
127 bool val;
128 nsresult rv = mTarget->IsOnCurrentThread(&val);
129 if (NS_FAILED(rv) || !val) {
130 nsCOMPtr<nsIOutputStreamCallback> event =
131 NS_NewOutputStreamReadyEvent(mCallback, mTarget);
132 mCallback = nullptr;
133 if (event) {
134 rv = event->OnOutputStreamReady(nullptr);
135 if (NS_FAILED(rv)) {
136 NS_NOTREACHED("leaking stream event");
137 nsISupports *sup = event;
138 NS_ADDREF(sup);
139 }
140 }
141 }
142 }
144 public:
145 NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream)
146 {
147 mStream = stream;
149 nsresult rv =
150 mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
151 if (NS_FAILED(rv)) {
152 NS_WARNING("PostEvent failed");
153 return NS_ERROR_FAILURE;
154 }
156 return NS_OK;
157 }
159 NS_IMETHOD Run()
160 {
161 if (mCallback) {
162 if (mStream)
163 mCallback->OnOutputStreamReady(mStream);
164 mCallback = nullptr;
165 }
166 return NS_OK;
167 }
169 private:
170 nsCOMPtr<nsIAsyncOutputStream> mStream;
171 nsCOMPtr<nsIOutputStreamCallback> mCallback;
172 nsCOMPtr<nsIEventTarget> mTarget;
173 };
175 NS_IMPL_ISUPPORTS(nsOutputStreamReadyEvent, nsIRunnable,
176 nsIOutputStreamCallback)
178 //-----------------------------------------------------------------------------
180 already_AddRefed<nsIInputStreamCallback>
181 NS_NewInputStreamReadyEvent(nsIInputStreamCallback *callback,
182 nsIEventTarget *target)
183 {
184 NS_ASSERTION(callback, "null callback");
185 NS_ASSERTION(target, "null target");
186 nsRefPtr<nsInputStreamReadyEvent> ev =
187 new nsInputStreamReadyEvent(callback, target);
188 return ev.forget();
189 }
191 already_AddRefed<nsIOutputStreamCallback>
192 NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
193 nsIEventTarget *target)
194 {
195 NS_ASSERTION(callback, "null callback");
196 NS_ASSERTION(target, "null target");
197 nsRefPtr<nsOutputStreamReadyEvent> ev =
198 new nsOutputStreamReadyEvent(callback, target);
199 return ev.forget();
200 }
202 //-----------------------------------------------------------------------------
203 // NS_AsyncCopy implementation
205 // abstract stream copier...
206 class nsAStreamCopier : public nsIInputStreamCallback
207 , public nsIOutputStreamCallback
208 , public nsIRunnable
209 {
210 public:
211 NS_DECL_THREADSAFE_ISUPPORTS
213 nsAStreamCopier()
214 : mLock("nsAStreamCopier.mLock")
215 , mCallback(nullptr)
216 , mProgressCallback(nullptr)
217 , mClosure(nullptr)
218 , mChunkSize(0)
219 , mEventInProcess(false)
220 , mEventIsPending(false)
221 , mCloseSource(true)
222 , mCloseSink(true)
223 , mCanceled(false)
224 , mCancelStatus(NS_OK)
225 {
226 }
228 // virtual since subclasses call superclass Release()
229 virtual ~nsAStreamCopier()
230 {
231 }
233 // kick off the async copy...
234 nsresult Start(nsIInputStream *source,
235 nsIOutputStream *sink,
236 nsIEventTarget *target,
237 nsAsyncCopyCallbackFun callback,
238 void *closure,
239 uint32_t chunksize,
240 bool closeSource,
241 bool closeSink,
242 nsAsyncCopyProgressFun progressCallback)
243 {
244 mSource = source;
245 mSink = sink;
246 mTarget = target;
247 mCallback = callback;
248 mClosure = closure;
249 mChunkSize = chunksize;
250 mCloseSource = closeSource;
251 mCloseSink = closeSink;
252 mProgressCallback = progressCallback;
254 mAsyncSource = do_QueryInterface(mSource);
255 mAsyncSink = do_QueryInterface(mSink);
257 return PostContinuationEvent();
258 }
260 // implemented by subclasses, returns number of bytes copied and
261 // sets source and sink condition before returning.
262 virtual uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0;
264 void Process()
265 {
266 if (!mSource || !mSink)
267 return;
269 nsresult sourceCondition, sinkCondition;
270 nsresult cancelStatus;
271 bool canceled;
272 {
273 MutexAutoLock lock(mLock);
274 canceled = mCanceled;
275 cancelStatus = mCancelStatus;
276 }
278 // Copy data from the source to the sink until we hit failure or have
279 // copied all the data.
280 for (;;) {
281 // Note: copyFailed will be true if the source or the sink have
282 // reported an error, or if we failed to write any bytes
283 // because we have consumed all of our data.
284 bool copyFailed = false;
285 if (!canceled) {
286 uint32_t n = DoCopy(&sourceCondition, &sinkCondition);
287 if (n > 0 && mProgressCallback) {
288 mProgressCallback(mClosure, n);
289 }
290 copyFailed = NS_FAILED(sourceCondition) ||
291 NS_FAILED(sinkCondition) || n == 0;
293 MutexAutoLock lock(mLock);
294 canceled = mCanceled;
295 cancelStatus = mCancelStatus;
296 }
297 if (copyFailed && !canceled) {
298 if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) {
299 // need to wait for more data from source. while waiting for
300 // more source data, be sure to observe failures on output end.
301 mAsyncSource->AsyncWait(this, 0, 0, nullptr);
303 if (mAsyncSink)
304 mAsyncSink->AsyncWait(this,
305 nsIAsyncOutputStream::WAIT_CLOSURE_ONLY,
306 0, nullptr);
307 break;
308 }
309 else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) {
310 // need to wait for more room in the sink. while waiting for
311 // more room in the sink, be sure to observer failures on the
312 // input end.
313 mAsyncSink->AsyncWait(this, 0, 0, nullptr);
315 if (mAsyncSource)
316 mAsyncSource->AsyncWait(this,
317 nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
318 0, nullptr);
319 break;
320 }
321 }
322 if (copyFailed || canceled) {
323 if (mCloseSource) {
324 // close source
325 if (mAsyncSource)
326 mAsyncSource->CloseWithStatus(canceled ? cancelStatus :
327 sinkCondition);
328 else
329 mSource->Close();
330 }
331 mAsyncSource = nullptr;
332 mSource = nullptr;
334 if (mCloseSink) {
335 // close sink
336 if (mAsyncSink)
337 mAsyncSink->CloseWithStatus(canceled ? cancelStatus :
338 sourceCondition);
339 else {
340 // If we have an nsISafeOutputStream, and our
341 // sourceCondition and sinkCondition are not set to a
342 // failure state, finish writing.
343 nsCOMPtr<nsISafeOutputStream> sostream =
344 do_QueryInterface(mSink);
345 if (sostream && NS_SUCCEEDED(sourceCondition) &&
346 NS_SUCCEEDED(sinkCondition))
347 sostream->Finish();
348 else
349 mSink->Close();
350 }
351 }
352 mAsyncSink = nullptr;
353 mSink = nullptr;
355 // notify state complete...
356 if (mCallback) {
357 nsresult status;
358 if (!canceled) {
359 status = sourceCondition;
360 if (NS_SUCCEEDED(status))
361 status = sinkCondition;
362 if (status == NS_BASE_STREAM_CLOSED)
363 status = NS_OK;
364 } else {
365 status = cancelStatus;
366 }
367 mCallback(mClosure, status);
368 }
369 break;
370 }
371 }
372 }
374 nsresult Cancel(nsresult aReason)
375 {
376 MutexAutoLock lock(mLock);
377 if (mCanceled)
378 return NS_ERROR_FAILURE;
380 if (NS_SUCCEEDED(aReason)) {
381 NS_WARNING("cancel with non-failure status code");
382 aReason = NS_BASE_STREAM_CLOSED;
383 }
385 mCanceled = true;
386 mCancelStatus = aReason;
387 return NS_OK;
388 }
390 NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source)
391 {
392 PostContinuationEvent();
393 return NS_OK;
394 }
396 NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink)
397 {
398 PostContinuationEvent();
399 return NS_OK;
400 }
402 // continuation event handler
403 NS_IMETHOD Run()
404 {
405 Process();
407 // clear "in process" flag and post any pending continuation event
408 MutexAutoLock lock(mLock);
409 mEventInProcess = false;
410 if (mEventIsPending) {
411 mEventIsPending = false;
412 PostContinuationEvent_Locked();
413 }
415 return NS_OK;
416 }
418 nsresult PostContinuationEvent()
419 {
420 // we cannot post a continuation event if there is currently
421 // an event in process. doing so could result in Process being
422 // run simultaneously on multiple threads, so we mark the event
423 // as pending, and if an event is already in process then we
424 // just let that existing event take care of posting the real
425 // continuation event.
427 MutexAutoLock lock(mLock);
428 return PostContinuationEvent_Locked();
429 }
431 nsresult PostContinuationEvent_Locked()
432 {
433 nsresult rv = NS_OK;
434 if (mEventInProcess)
435 mEventIsPending = true;
436 else {
437 rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
438 if (NS_SUCCEEDED(rv))
439 mEventInProcess = true;
440 else
441 NS_WARNING("unable to post continuation event");
442 }
443 return rv;
444 }
446 protected:
447 nsCOMPtr<nsIInputStream> mSource;
448 nsCOMPtr<nsIOutputStream> mSink;
449 nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
450 nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
451 nsCOMPtr<nsIEventTarget> mTarget;
452 Mutex mLock;
453 nsAsyncCopyCallbackFun mCallback;
454 nsAsyncCopyProgressFun mProgressCallback;
455 void *mClosure;
456 uint32_t mChunkSize;
457 bool mEventInProcess;
458 bool mEventIsPending;
459 bool mCloseSource;
460 bool mCloseSink;
461 bool mCanceled;
462 nsresult mCancelStatus;
463 };
465 NS_IMPL_ISUPPORTS(nsAStreamCopier,
466 nsIInputStreamCallback,
467 nsIOutputStreamCallback,
468 nsIRunnable)
470 class nsStreamCopierIB MOZ_FINAL : public nsAStreamCopier
471 {
472 public:
473 nsStreamCopierIB() : nsAStreamCopier() {}
474 virtual ~nsStreamCopierIB() {}
476 struct ReadSegmentsState {
477 nsIOutputStream *mSink;
478 nsresult mSinkCondition;
479 };
481 static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr,
482 void *closure,
483 const char *buffer,
484 uint32_t offset,
485 uint32_t count,
486 uint32_t *countWritten)
487 {
488 ReadSegmentsState *state = (ReadSegmentsState *) closure;
490 nsresult rv = state->mSink->Write(buffer, count, countWritten);
491 if (NS_FAILED(rv))
492 state->mSinkCondition = rv;
493 else if (*countWritten == 0)
494 state->mSinkCondition = NS_BASE_STREAM_CLOSED;
496 return state->mSinkCondition;
497 }
499 uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
500 {
501 ReadSegmentsState state;
502 state.mSink = mSink;
503 state.mSinkCondition = NS_OK;
505 uint32_t n;
506 *sourceCondition =
507 mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
508 *sinkCondition = state.mSinkCondition;
509 return n;
510 }
511 };
513 class nsStreamCopierOB MOZ_FINAL : public nsAStreamCopier
514 {
515 public:
516 nsStreamCopierOB() : nsAStreamCopier() {}
517 virtual ~nsStreamCopierOB() {}
519 struct WriteSegmentsState {
520 nsIInputStream *mSource;
521 nsresult mSourceCondition;
522 };
524 static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
525 void *closure,
526 char *buffer,
527 uint32_t offset,
528 uint32_t count,
529 uint32_t *countRead)
530 {
531 WriteSegmentsState *state = (WriteSegmentsState *) closure;
533 nsresult rv = state->mSource->Read(buffer, count, countRead);
534 if (NS_FAILED(rv))
535 state->mSourceCondition = rv;
536 else if (*countRead == 0)
537 state->mSourceCondition = NS_BASE_STREAM_CLOSED;
539 return state->mSourceCondition;
540 }
542 uint32_t DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
543 {
544 WriteSegmentsState state;
545 state.mSource = mSource;
546 state.mSourceCondition = NS_OK;
548 uint32_t n;
549 *sinkCondition =
550 mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
551 *sourceCondition = state.mSourceCondition;
552 return n;
553 }
554 };
556 //-----------------------------------------------------------------------------
558 nsresult
559 NS_AsyncCopy(nsIInputStream *source,
560 nsIOutputStream *sink,
561 nsIEventTarget *target,
562 nsAsyncCopyMode mode,
563 uint32_t chunkSize,
564 nsAsyncCopyCallbackFun callback,
565 void *closure,
566 bool closeSource,
567 bool closeSink,
568 nsISupports **aCopierCtx,
569 nsAsyncCopyProgressFun progressCallback)
570 {
571 NS_ASSERTION(target, "non-null target required");
573 nsresult rv;
574 nsAStreamCopier *copier;
576 if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS)
577 copier = new nsStreamCopierIB();
578 else
579 copier = new nsStreamCopierOB();
581 if (!copier)
582 return NS_ERROR_OUT_OF_MEMORY;
584 // Start() takes an owning ref to the copier...
585 NS_ADDREF(copier);
586 rv = copier->Start(source, sink, target, callback, closure, chunkSize,
587 closeSource, closeSink, progressCallback);
589 if (aCopierCtx) {
590 *aCopierCtx = static_cast<nsISupports*>(
591 static_cast<nsIRunnable*>(copier));
592 NS_ADDREF(*aCopierCtx);
593 }
594 NS_RELEASE(copier);
596 return rv;
597 }
599 //-----------------------------------------------------------------------------
601 nsresult
602 NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason)
603 {
604 nsAStreamCopier *copier = static_cast<nsAStreamCopier *>(
605 static_cast<nsIRunnable *>(aCopierCtx));
606 return copier->Cancel(aReason);
607 }
609 //-----------------------------------------------------------------------------
611 nsresult
612 NS_ConsumeStream(nsIInputStream *stream, uint32_t maxCount, nsACString &result)
613 {
614 nsresult rv = NS_OK;
615 result.Truncate();
617 while (maxCount) {
618 uint64_t avail64;
619 rv = stream->Available(&avail64);
620 if (NS_FAILED(rv)) {
621 if (rv == NS_BASE_STREAM_CLOSED)
622 rv = NS_OK;
623 break;
624 }
625 if (avail64 == 0)
626 break;
628 uint32_t avail = (uint32_t)XPCOM_MIN<uint64_t>(avail64, maxCount);
630 // resize result buffer
631 uint32_t length = result.Length();
632 if (avail > UINT32_MAX - length)
633 return NS_ERROR_FILE_TOO_BIG;
635 result.SetLength(length + avail);
636 if (result.Length() != (length + avail))
637 return NS_ERROR_OUT_OF_MEMORY;
638 char *buf = result.BeginWriting() + length;
640 uint32_t n;
641 rv = stream->Read(buf, avail, &n);
642 if (NS_FAILED(rv))
643 break;
644 if (n != avail)
645 result.SetLength(length + n);
646 if (n == 0)
647 break;
648 maxCount -= n;
649 }
651 return rv;
652 }
654 //-----------------------------------------------------------------------------
656 static NS_METHOD
657 TestInputStream(nsIInputStream *inStr,
658 void *closure,
659 const char *buffer,
660 uint32_t offset,
661 uint32_t count,
662 uint32_t *countWritten)
663 {
664 bool *result = static_cast<bool *>(closure);
665 *result = true;
666 return NS_ERROR_ABORT; // don't call me anymore
667 }
669 bool
670 NS_InputStreamIsBuffered(nsIInputStream *stream)
671 {
672 nsCOMPtr<nsIBufferedInputStream> bufferedIn = do_QueryInterface(stream);
673 if (bufferedIn) {
674 return true;
675 }
677 bool result = false;
678 uint32_t n;
679 nsresult rv = stream->ReadSegments(TestInputStream,
680 &result, 1, &n);
681 return result || NS_SUCCEEDED(rv);
682 }
684 static NS_METHOD
685 TestOutputStream(nsIOutputStream *outStr,
686 void *closure,
687 char *buffer,
688 uint32_t offset,
689 uint32_t count,
690 uint32_t *countRead)
691 {
692 bool *result = static_cast<bool *>(closure);
693 *result = true;
694 return NS_ERROR_ABORT; // don't call me anymore
695 }
697 bool
698 NS_OutputStreamIsBuffered(nsIOutputStream *stream)
699 {
700 nsCOMPtr<nsIBufferedOutputStream> bufferedOut = do_QueryInterface(stream);
701 if (bufferedOut) {
702 return true;
703 }
705 bool result = false;
706 uint32_t n;
707 stream->WriteSegments(TestOutputStream, &result, 1, &n);
708 return result;
709 }
711 //-----------------------------------------------------------------------------
713 NS_METHOD
714 NS_CopySegmentToStream(nsIInputStream *inStr,
715 void *closure,
716 const char *buffer,
717 uint32_t offset,
718 uint32_t count,
719 uint32_t *countWritten)
720 {
721 nsIOutputStream *outStr = static_cast<nsIOutputStream *>(closure);
722 *countWritten = 0;
723 while (count) {
724 uint32_t n;
725 nsresult rv = outStr->Write(buffer, count, &n);
726 if (NS_FAILED(rv))
727 return rv;
728 buffer += n;
729 count -= n;
730 *countWritten += n;
731 }
732 return NS_OK;
733 }
735 NS_METHOD
736 NS_CopySegmentToBuffer(nsIInputStream *inStr,
737 void *closure,
738 const char *buffer,
739 uint32_t offset,
740 uint32_t count,
741 uint32_t *countWritten)
742 {
743 char *toBuf = static_cast<char *>(closure);
744 memcpy(&toBuf[offset], buffer, count);
745 *countWritten = count;
746 return NS_OK;
747 }
749 NS_METHOD
750 NS_CopySegmentToBuffer(nsIOutputStream *outStr,
751 void *closure,
752 char *buffer,
753 uint32_t offset,
754 uint32_t count,
755 uint32_t *countRead)
756 {
757 const char* fromBuf = static_cast<const char*>(closure);
758 memcpy(buffer, &fromBuf[offset], count);
759 *countRead = count;
760 return NS_OK;
761 }
763 NS_METHOD
764 NS_DiscardSegment(nsIInputStream *inStr,
765 void *closure,
766 const char *buffer,
767 uint32_t offset,
768 uint32_t count,
769 uint32_t *countWritten)
770 {
771 *countWritten = count;
772 return NS_OK;
773 }
775 //-----------------------------------------------------------------------------
777 NS_METHOD
778 NS_WriteSegmentThunk(nsIInputStream *inStr,
779 void *closure,
780 const char *buffer,
781 uint32_t offset,
782 uint32_t count,
783 uint32_t *countWritten)
784 {
785 nsWriteSegmentThunk *thunk = static_cast<nsWriteSegmentThunk *>(closure);
786 return thunk->mFun(thunk->mStream, thunk->mClosure, buffer, offset, count,
787 countWritten);
788 }
790 NS_METHOD
791 NS_FillArray(FallibleTArray<char>& aDest, nsIInputStream *aInput,
792 uint32_t aKeep, uint32_t *aNewBytes)
793 {
794 MOZ_ASSERT(aInput, "null stream");
795 MOZ_ASSERT(aKeep <= aDest.Length(), "illegal keep count");
797 char* aBuffer = aDest.Elements();
798 int64_t keepOffset = int64_t(aDest.Length()) - aKeep;
799 if (0 != aKeep && keepOffset > 0) {
800 memmove(aBuffer, aBuffer + keepOffset, aKeep);
801 }
803 nsresult rv =
804 aInput->Read(aBuffer + aKeep, aDest.Capacity() - aKeep, aNewBytes);
805 if (NS_FAILED(rv)) {
806 *aNewBytes = 0;
807 }
808 // NOTE: we rely on the fact that the new slots are NOT initialized by
809 // SetLengthAndRetainStorage here, see nsTArrayElementTraits::Construct()
810 // in nsTArray.h:
811 aDest.SetLengthAndRetainStorage(aKeep + *aNewBytes);
813 MOZ_ASSERT(aDest.Length() <= aDest.Capacity(), "buffer overflow");
814 return rv;
815 }