1 : /* vim:set ts=4 sw=4 sts=4 et cin: */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Mutex.h"
40 : #include "nsStreamUtils.h"
41 : #include "nsCOMPtr.h"
42 : #include "nsIPipe.h"
43 : #include "nsIEventTarget.h"
44 : #include "nsIRunnable.h"
45 : #include "nsISafeOutputStream.h"
46 : #include "nsString.h"
47 :
48 : using namespace mozilla;
49 :
50 : //-----------------------------------------------------------------------------
51 :
52 : class nsInputStreamReadyEvent : public nsIRunnable
53 : , public nsIInputStreamCallback
54 : {
55 : public:
56 : NS_DECL_ISUPPORTS
57 :
58 19374 : nsInputStreamReadyEvent(nsIInputStreamCallback *callback,
59 : nsIEventTarget *target)
60 : : mCallback(callback)
61 19374 : , mTarget(target)
62 : {
63 19374 : }
64 :
65 : private:
66 19374 : ~nsInputStreamReadyEvent()
67 38748 : {
68 19374 : if (!mCallback)
69 : return;
70 : //
71 : // whoa!! looks like we never posted this event. take care to
72 : // release mCallback on the correct thread. if mTarget lives on the
73 : // calling thread, then we are ok. otherwise, we have to try to
74 : // proxy the Release over the right thread. if that thread is dead,
75 : // then there's nothing we can do... better to leak than crash.
76 : //
77 : bool val;
78 8 : nsresult rv = mTarget->IsOnCurrentThread(&val);
79 8 : if (NS_FAILED(rv) || !val) {
80 0 : nsCOMPtr<nsIInputStreamCallback> event;
81 0 : NS_NewInputStreamReadyEvent(getter_AddRefs(event), mCallback,
82 0 : mTarget);
83 0 : mCallback = 0;
84 0 : if (event) {
85 0 : rv = event->OnInputStreamReady(nsnull);
86 0 : if (NS_FAILED(rv)) {
87 0 : NS_NOTREACHED("leaking stream event");
88 0 : nsISupports *sup = event;
89 0 : NS_ADDREF(sup);
90 : }
91 : }
92 : }
93 19374 : }
94 :
95 : public:
96 19366 : NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream)
97 : {
98 19366 : mStream = stream;
99 :
100 : nsresult rv =
101 19366 : mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
102 19366 : if (NS_FAILED(rv)) {
103 0 : NS_WARNING("Dispatch failed");
104 0 : return NS_ERROR_FAILURE;
105 : }
106 :
107 19366 : return NS_OK;
108 : }
109 :
110 19366 : NS_IMETHOD Run()
111 : {
112 19366 : if (mCallback) {
113 19366 : if (mStream)
114 19366 : mCallback->OnInputStreamReady(mStream);
115 19366 : mCallback = nsnull;
116 : }
117 19366 : return NS_OK;
118 : }
119 :
120 : private:
121 : nsCOMPtr<nsIAsyncInputStream> mStream;
122 : nsCOMPtr<nsIInputStreamCallback> mCallback;
123 : nsCOMPtr<nsIEventTarget> mTarget;
124 : };
125 :
126 334209 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamReadyEvent, nsIRunnable,
127 : nsIInputStreamCallback)
128 :
129 : //-----------------------------------------------------------------------------
130 :
131 : class nsOutputStreamReadyEvent : public nsIRunnable
132 : , public nsIOutputStreamCallback
133 : {
134 : public:
135 : NS_DECL_ISUPPORTS
136 :
137 18277 : nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback,
138 : nsIEventTarget *target)
139 : : mCallback(callback)
140 18277 : , mTarget(target)
141 : {
142 18277 : }
143 :
144 : private:
145 18276 : ~nsOutputStreamReadyEvent()
146 36552 : {
147 18276 : if (!mCallback)
148 : return;
149 : //
150 : // whoa!! looks like we never posted this event. take care to
151 : // release mCallback on the correct thread. if mTarget lives on the
152 : // calling thread, then we are ok. otherwise, we have to try to
153 : // proxy the Release over the right thread. if that thread is dead,
154 : // then there's nothing we can do... better to leak than crash.
155 : //
156 : bool val;
157 8369 : nsresult rv = mTarget->IsOnCurrentThread(&val);
158 8369 : if (NS_FAILED(rv) || !val) {
159 0 : nsCOMPtr<nsIOutputStreamCallback> event;
160 0 : NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mCallback,
161 0 : mTarget);
162 0 : mCallback = 0;
163 0 : if (event) {
164 0 : rv = event->OnOutputStreamReady(nsnull);
165 0 : if (NS_FAILED(rv)) {
166 0 : NS_NOTREACHED("leaking stream event");
167 0 : nsISupports *sup = event;
168 0 : NS_ADDREF(sup);
169 : }
170 : }
171 : }
172 18276 : }
173 :
174 : public:
175 9907 : NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream)
176 : {
177 9907 : mStream = stream;
178 :
179 : nsresult rv =
180 9907 : mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
181 9907 : if (NS_FAILED(rv)) {
182 0 : NS_WARNING("PostEvent failed");
183 0 : return NS_ERROR_FAILURE;
184 : }
185 :
186 9907 : return NS_OK;
187 : }
188 :
189 9907 : NS_IMETHOD Run()
190 : {
191 9907 : if (mCallback) {
192 9907 : if (mStream)
193 9907 : mCallback->OnOutputStreamReady(mStream);
194 9907 : mCallback = nsnull;
195 : }
196 9907 : return NS_OK;
197 : }
198 :
199 : private:
200 : nsCOMPtr<nsIAsyncOutputStream> mStream;
201 : nsCOMPtr<nsIOutputStreamCallback> mCallback;
202 : nsCOMPtr<nsIEventTarget> mTarget;
203 : };
204 :
205 264865 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamReadyEvent, nsIRunnable,
206 : nsIOutputStreamCallback)
207 :
208 : //-----------------------------------------------------------------------------
209 :
210 : nsresult
211 19374 : NS_NewInputStreamReadyEvent(nsIInputStreamCallback **event,
212 : nsIInputStreamCallback *callback,
213 : nsIEventTarget *target)
214 : {
215 19374 : NS_ASSERTION(callback, "null callback");
216 19374 : NS_ASSERTION(target, "null target");
217 19374 : nsInputStreamReadyEvent *ev = new nsInputStreamReadyEvent(callback, target);
218 19374 : if (!ev)
219 0 : return NS_ERROR_OUT_OF_MEMORY;
220 19374 : NS_ADDREF(*event = ev);
221 19374 : return NS_OK;
222 : }
223 :
224 : nsresult
225 18277 : NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **event,
226 : nsIOutputStreamCallback *callback,
227 : nsIEventTarget *target)
228 : {
229 18277 : NS_ASSERTION(callback, "null callback");
230 18277 : NS_ASSERTION(target, "null target");
231 18277 : nsOutputStreamReadyEvent *ev = new nsOutputStreamReadyEvent(callback, target);
232 18277 : if (!ev)
233 0 : return NS_ERROR_OUT_OF_MEMORY;
234 18277 : NS_ADDREF(*event = ev);
235 18277 : return NS_OK;
236 : }
237 :
238 : //-----------------------------------------------------------------------------
239 : // NS_AsyncCopy implementation
240 :
241 : // abstract stream copier...
242 : class nsAStreamCopier : public nsIInputStreamCallback
243 : , public nsIOutputStreamCallback
244 : , public nsIRunnable
245 : {
246 : public:
247 : NS_DECL_ISUPPORTS
248 :
249 6493 : nsAStreamCopier()
250 : : mLock("nsAStreamCopier.mLock")
251 : , mCallback(nsnull)
252 : , mClosure(nsnull)
253 : , mChunkSize(0)
254 : , mEventInProcess(false)
255 : , mEventIsPending(false)
256 : , mCloseSource(true)
257 : , mCloseSink(true)
258 : , mCanceled(false)
259 6493 : , mCancelStatus(NS_OK)
260 : {
261 6493 : }
262 :
263 : // virtual since subclasses call superclass Release()
264 6492 : virtual ~nsAStreamCopier()
265 6492 : {
266 12984 : }
267 :
268 : // kick off the async copy...
269 6493 : nsresult Start(nsIInputStream *source,
270 : nsIOutputStream *sink,
271 : nsIEventTarget *target,
272 : nsAsyncCopyCallbackFun callback,
273 : void *closure,
274 : PRUint32 chunksize,
275 : bool closeSource,
276 : bool closeSink)
277 : {
278 6493 : mSource = source;
279 6493 : mSink = sink;
280 6493 : mTarget = target;
281 6493 : mCallback = callback;
282 6493 : mClosure = closure;
283 6493 : mChunkSize = chunksize;
284 6493 : mCloseSource = closeSource;
285 6493 : mCloseSink = closeSink;
286 :
287 6493 : mAsyncSource = do_QueryInterface(mSource);
288 6493 : mAsyncSink = do_QueryInterface(mSink);
289 :
290 6493 : return PostContinuationEvent();
291 : }
292 :
293 : // implemented by subclasses, returns number of bytes copied and
294 : // sets source and sink condition before returning.
295 : virtual PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0;
296 :
297 25084 : void Process()
298 : {
299 25084 : if (!mSource || !mSink)
300 5816 : return;
301 :
302 : nsresult sourceCondition, sinkCondition;
303 : nsresult cancelStatus;
304 : bool canceled;
305 : {
306 38536 : MutexAutoLock lock(mLock);
307 19268 : canceled = mCanceled;
308 19268 : cancelStatus = mCancelStatus;
309 : }
310 :
311 : // Copy data from the source to the sink until we hit failure or have
312 : // copied all the data.
313 7094 : for (;;) {
314 : // Note: copyFailed will be true if the source or the sink have
315 : // reported an error, or if we failed to write any bytes
316 : // because we have consumed all of our data.
317 26362 : bool copyFailed = false;
318 26362 : if (!canceled) {
319 26358 : PRUint32 n = DoCopy(&sourceCondition, &sinkCondition);
320 26358 : copyFailed = NS_FAILED(sourceCondition) ||
321 26358 : NS_FAILED(sinkCondition) || n == 0;
322 :
323 52716 : MutexAutoLock lock(mLock);
324 26358 : canceled = mCanceled;
325 26358 : cancelStatus = mCancelStatus;
326 : }
327 26362 : if (copyFailed && !canceled) {
328 19264 : if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) {
329 : // need to wait for more data from source. while waiting for
330 : // more source data, be sure to observe failures on output end.
331 12738 : mAsyncSource->AsyncWait(this, 0, 0, nsnull);
332 :
333 12738 : if (mAsyncSink)
334 12738 : mAsyncSink->AsyncWait(this,
335 : nsIAsyncOutputStream::WAIT_CLOSURE_ONLY,
336 12738 : 0, nsnull);
337 12738 : break;
338 : }
339 6526 : else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) {
340 : // need to wait for more room in the sink. while waiting for
341 : // more room in the sink, be sure to observer failures on the
342 : // input end.
343 41 : mAsyncSink->AsyncWait(this, 0, 0, nsnull);
344 :
345 41 : if (mAsyncSource)
346 41 : mAsyncSource->AsyncWait(this,
347 : nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
348 41 : 0, nsnull);
349 41 : break;
350 : }
351 : }
352 13583 : if (copyFailed || canceled) {
353 6489 : if (mCloseSource) {
354 : // close source
355 6483 : if (mAsyncSource)
356 5818 : mAsyncSource->CloseWithStatus(canceled ? cancelStatus :
357 5818 : sinkCondition);
358 : else
359 665 : mSource->Close();
360 : }
361 6489 : mAsyncSource = nsnull;
362 6489 : mSource = nsnull;
363 :
364 6489 : if (mCloseSink) {
365 : // close sink
366 6483 : if (mAsyncSink)
367 6100 : mAsyncSink->CloseWithStatus(canceled ? cancelStatus :
368 6100 : sourceCondition);
369 : else {
370 : // If we have an nsISafeOutputStream, and our
371 : // sourceCondition and sinkCondition are not set to a
372 : // failure state, finish writing.
373 : nsCOMPtr<nsISafeOutputStream> sostream =
374 766 : do_QueryInterface(mSink);
375 651 : if (sostream && NS_SUCCEEDED(sourceCondition) &&
376 268 : NS_SUCCEEDED(sinkCondition))
377 268 : sostream->Finish();
378 : else
379 115 : mSink->Close();
380 : }
381 : }
382 6489 : mAsyncSink = nsnull;
383 6489 : mSink = nsnull;
384 :
385 : // notify state complete...
386 6489 : if (mCallback) {
387 : nsresult status;
388 396 : if (!canceled) {
389 392 : status = sourceCondition;
390 392 : if (NS_SUCCEEDED(status))
391 392 : status = sinkCondition;
392 392 : if (status == NS_BASE_STREAM_CLOSED)
393 0 : status = NS_OK;
394 : } else {
395 4 : status = cancelStatus;
396 : }
397 396 : mCallback(mClosure, status);
398 : }
399 6489 : break;
400 : }
401 : }
402 : }
403 :
404 7 : nsresult Cancel(nsresult aReason)
405 : {
406 14 : MutexAutoLock lock(mLock);
407 7 : if (mCanceled)
408 0 : return NS_ERROR_FAILURE;
409 :
410 7 : if (NS_SUCCEEDED(aReason)) {
411 0 : NS_WARNING("cancel with non-failure status code");
412 0 : aReason = NS_BASE_STREAM_CLOSED;
413 : }
414 :
415 7 : mCanceled = true;
416 7 : mCancelStatus = aReason;
417 7 : return NS_OK;
418 : }
419 :
420 12725 : NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source)
421 : {
422 12725 : PostContinuationEvent();
423 12725 : return NS_OK;
424 : }
425 :
426 5869 : NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink)
427 : {
428 5869 : PostContinuationEvent();
429 5869 : return NS_OK;
430 : }
431 :
432 : // continuation event handler
433 25084 : NS_IMETHOD Run()
434 : {
435 25084 : Process();
436 :
437 : // clear "in process" flag and post any pending continuation event
438 50168 : MutexAutoLock lock(mLock);
439 25084 : mEventInProcess = false;
440 25084 : if (mEventIsPending) {
441 2950 : mEventIsPending = false;
442 2950 : PostContinuationEvent_Locked();
443 : }
444 :
445 25084 : return NS_OK;
446 : }
447 :
448 25087 : nsresult PostContinuationEvent()
449 : {
450 : // we cannot post a continuation event if there is currently
451 : // an event in process. doing so could result in Process being
452 : // run simultaneously on multiple threads, so we mark the event
453 : // as pending, and if an event is already in process then we
454 : // just let that existing event take care of posting the real
455 : // continuation event.
456 :
457 50174 : MutexAutoLock lock(mLock);
458 25087 : return PostContinuationEvent_Locked();
459 : }
460 :
461 28037 : nsresult PostContinuationEvent_Locked()
462 : {
463 28037 : nsresult rv = NS_OK;
464 28037 : if (mEventInProcess)
465 2950 : mEventIsPending = true;
466 : else {
467 25087 : rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
468 25087 : if (NS_SUCCEEDED(rv))
469 25084 : mEventInProcess = true;
470 : else
471 3 : NS_WARNING("unable to post continuation event");
472 : }
473 28037 : return rv;
474 : }
475 :
476 : protected:
477 : nsCOMPtr<nsIInputStream> mSource;
478 : nsCOMPtr<nsIOutputStream> mSink;
479 : nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
480 : nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
481 : nsCOMPtr<nsIEventTarget> mTarget;
482 : Mutex mLock;
483 : nsAsyncCopyCallbackFun mCallback;
484 : void *mClosure;
485 : PRUint32 mChunkSize;
486 : bool mEventInProcess;
487 : bool mEventIsPending;
488 : bool mCloseSource;
489 : bool mCloseSink;
490 : bool mCanceled;
491 : nsresult mCancelStatus;
492 : };
493 :
494 340732 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsAStreamCopier,
495 : nsIInputStreamCallback,
496 : nsIOutputStreamCallback,
497 : nsIRunnable)
498 :
499 : class nsStreamCopierIB : public nsAStreamCopier
500 : {
501 : public:
502 3306 : nsStreamCopierIB() : nsAStreamCopier() {}
503 13220 : virtual ~nsStreamCopierIB() {}
504 :
505 : struct ReadSegmentsState {
506 : nsIOutputStream *mSink;
507 : nsresult mSinkCondition;
508 : };
509 :
510 7054 : static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr,
511 : void *closure,
512 : const char *buffer,
513 : PRUint32 offset,
514 : PRUint32 count,
515 : PRUint32 *countWritten)
516 : {
517 7054 : ReadSegmentsState *state = (ReadSegmentsState *) closure;
518 :
519 7054 : nsresult rv = state->mSink->Write(buffer, count, countWritten);
520 7054 : if (NS_FAILED(rv))
521 26 : state->mSinkCondition = rv;
522 7028 : else if (*countWritten == 0)
523 0 : state->mSinkCondition = NS_BASE_STREAM_CLOSED;
524 :
525 7054 : return state->mSinkCondition;
526 : }
527 :
528 19408 : PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
529 : {
530 : ReadSegmentsState state;
531 19408 : state.mSink = mSink;
532 19408 : state.mSinkCondition = NS_OK;
533 :
534 : PRUint32 n;
535 : *sourceCondition =
536 19408 : mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
537 19408 : *sinkCondition = state.mSinkCondition;
538 19408 : return n;
539 : }
540 : };
541 :
542 : class nsStreamCopierOB : public nsAStreamCopier
543 : {
544 : public:
545 3187 : nsStreamCopierOB() : nsAStreamCopier() {}
546 12748 : virtual ~nsStreamCopierOB() {}
547 :
548 : struct WriteSegmentsState {
549 : nsIInputStream *mSource;
550 : nsresult mSourceCondition;
551 : };
552 :
553 10513 : static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
554 : void *closure,
555 : char *buffer,
556 : PRUint32 offset,
557 : PRUint32 count,
558 : PRUint32 *countRead)
559 : {
560 10513 : WriteSegmentsState *state = (WriteSegmentsState *) closure;
561 :
562 10513 : nsresult rv = state->mSource->Read(buffer, count, countRead);
563 10513 : if (NS_FAILED(rv))
564 3480 : state->mSourceCondition = rv;
565 7033 : else if (*countRead == 0)
566 3123 : state->mSourceCondition = NS_BASE_STREAM_CLOSED;
567 :
568 10513 : return state->mSourceCondition;
569 : }
570 :
571 6950 : PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
572 : {
573 : WriteSegmentsState state;
574 6950 : state.mSource = mSource;
575 6950 : state.mSourceCondition = NS_OK;
576 :
577 : PRUint32 n;
578 : *sinkCondition =
579 6950 : mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
580 6950 : *sourceCondition = state.mSourceCondition;
581 6950 : return n;
582 : }
583 : };
584 :
585 : //-----------------------------------------------------------------------------
586 :
587 : nsresult
588 6493 : NS_AsyncCopy(nsIInputStream *source,
589 : nsIOutputStream *sink,
590 : nsIEventTarget *target,
591 : nsAsyncCopyMode mode,
592 : PRUint32 chunkSize,
593 : nsAsyncCopyCallbackFun callback,
594 : void *closure,
595 : bool closeSource,
596 : bool closeSink,
597 : nsISupports **aCopierCtx)
598 : {
599 6493 : NS_ASSERTION(target, "non-null target required");
600 :
601 : nsresult rv;
602 : nsAStreamCopier *copier;
603 :
604 6493 : if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS)
605 3306 : copier = new nsStreamCopierIB();
606 : else
607 3187 : copier = new nsStreamCopierOB();
608 :
609 6493 : if (!copier)
610 0 : return NS_ERROR_OUT_OF_MEMORY;
611 :
612 : // Start() takes an owning ref to the copier...
613 6493 : NS_ADDREF(copier);
614 : rv = copier->Start(source, sink, target, callback, closure, chunkSize,
615 6493 : closeSource, closeSink);
616 :
617 6493 : if (aCopierCtx) {
618 : *aCopierCtx = static_cast<nsISupports*>(
619 399 : static_cast<nsIRunnable*>(copier));
620 399 : NS_ADDREF(*aCopierCtx);
621 : }
622 6493 : NS_RELEASE(copier);
623 :
624 6493 : return rv;
625 : }
626 :
627 : //-----------------------------------------------------------------------------
628 :
629 : nsresult
630 7 : NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason)
631 : {
632 : nsAStreamCopier *copier = static_cast<nsAStreamCopier *>(
633 7 : static_cast<nsIRunnable *>(aCopierCtx));
634 7 : return copier->Cancel(aReason);
635 : }
636 :
637 : //-----------------------------------------------------------------------------
638 :
639 : nsresult
640 406 : NS_ConsumeStream(nsIInputStream *stream, PRUint32 maxCount, nsACString &result)
641 : {
642 406 : nsresult rv = NS_OK;
643 406 : result.Truncate();
644 :
645 1218 : while (maxCount) {
646 : PRUint32 avail;
647 414 : rv = stream->Available(&avail);
648 414 : if (NS_FAILED(rv)) {
649 0 : if (rv == NS_BASE_STREAM_CLOSED)
650 0 : rv = NS_OK;
651 0 : break;
652 : }
653 414 : if (avail == 0)
654 8 : break;
655 406 : if (avail > maxCount)
656 285 : avail = maxCount;
657 :
658 : // resize result buffer
659 406 : PRUint32 length = result.Length();
660 406 : result.SetLength(length + avail);
661 406 : if (result.Length() != (length + avail))
662 0 : return NS_ERROR_OUT_OF_MEMORY;
663 406 : char *buf = result.BeginWriting() + length;
664 :
665 : PRUint32 n;
666 406 : rv = stream->Read(buf, avail, &n);
667 406 : if (NS_FAILED(rv))
668 0 : break;
669 406 : if (n != avail)
670 0 : result.SetLength(length + n);
671 406 : if (n == 0)
672 0 : break;
673 406 : maxCount -= n;
674 : }
675 :
676 406 : return rv;
677 : }
678 :
679 : //-----------------------------------------------------------------------------
680 :
681 : static NS_METHOD
682 4078 : TestInputStream(nsIInputStream *inStr,
683 : void *closure,
684 : const char *buffer,
685 : PRUint32 offset,
686 : PRUint32 count,
687 : PRUint32 *countWritten)
688 : {
689 4078 : bool *result = static_cast<bool *>(closure);
690 4078 : *result = true;
691 4078 : return NS_ERROR_ABORT; // don't call me anymore
692 : }
693 :
694 : bool
695 4607 : NS_InputStreamIsBuffered(nsIInputStream *stream)
696 : {
697 4607 : bool result = false;
698 : PRUint32 n;
699 : nsresult rv = stream->ReadSegments(TestInputStream,
700 4607 : &result, 1, &n);
701 4607 : return result || NS_SUCCEEDED(rv);
702 : }
703 :
704 : static NS_METHOD
705 0 : TestOutputStream(nsIOutputStream *outStr,
706 : void *closure,
707 : char *buffer,
708 : PRUint32 offset,
709 : PRUint32 count,
710 : PRUint32 *countRead)
711 : {
712 0 : bool *result = static_cast<bool *>(closure);
713 0 : *result = true;
714 0 : return NS_ERROR_ABORT; // don't call me anymore
715 : }
716 :
717 : bool
718 386 : NS_OutputStreamIsBuffered(nsIOutputStream *stream)
719 : {
720 386 : bool result = false;
721 : PRUint32 n;
722 386 : stream->WriteSegments(TestOutputStream, &result, 1, &n);
723 386 : return result;
724 : }
725 :
726 : //-----------------------------------------------------------------------------
727 :
728 : NS_METHOD
729 17 : NS_CopySegmentToStream(nsIInputStream *inStr,
730 : void *closure,
731 : const char *buffer,
732 : PRUint32 offset,
733 : PRUint32 count,
734 : PRUint32 *countWritten)
735 : {
736 17 : nsIOutputStream *outStr = static_cast<nsIOutputStream *>(closure);
737 17 : *countWritten = 0;
738 51 : while (count) {
739 : PRUint32 n;
740 17 : nsresult rv = outStr->Write(buffer, count, &n);
741 17 : if (NS_FAILED(rv))
742 0 : return rv;
743 17 : buffer += n;
744 17 : count -= n;
745 17 : *countWritten += n;
746 : }
747 17 : return NS_OK;
748 : }
749 :
750 : NS_METHOD
751 4060698 : NS_CopySegmentToBuffer(nsIInputStream *inStr,
752 : void *closure,
753 : const char *buffer,
754 : PRUint32 offset,
755 : PRUint32 count,
756 : PRUint32 *countWritten)
757 : {
758 4060698 : char *toBuf = static_cast<char *>(closure);
759 4060698 : memcpy(&toBuf[offset], buffer, count);
760 4060698 : *countWritten = count;
761 4060698 : return NS_OK;
762 : }
763 :
764 : NS_METHOD
765 39 : NS_DiscardSegment(nsIInputStream *inStr,
766 : void *closure,
767 : const char *buffer,
768 : PRUint32 offset,
769 : PRUint32 count,
770 : PRUint32 *countWritten)
771 : {
772 39 : *countWritten = count;
773 39 : return NS_OK;
774 : }
775 :
776 : //-----------------------------------------------------------------------------
777 :
778 : NS_METHOD
779 17 : NS_WriteSegmentThunk(nsIInputStream *inStr,
780 : void *closure,
781 : const char *buffer,
782 : PRUint32 offset,
783 : PRUint32 count,
784 : PRUint32 *countWritten)
785 : {
786 17 : nsWriteSegmentThunk *thunk = static_cast<nsWriteSegmentThunk *>(closure);
787 : return thunk->mFun(thunk->mStream, thunk->mClosure, buffer, offset, count,
788 17 : countWritten);
789 : }
|