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 19390 : nsInputStreamReadyEvent(nsIInputStreamCallback *callback,
59 : nsIEventTarget *target)
60 : : mCallback(callback)
61 19390 : , mTarget(target)
62 : {
63 19390 : }
64 :
65 : private:
66 19390 : ~nsInputStreamReadyEvent()
67 38780 : {
68 19390 : 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 19390 : }
94 :
95 : public:
96 19382 : NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *stream)
97 : {
98 19382 : mStream = stream;
99 :
100 : nsresult rv =
101 19382 : mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
102 19382 : if (NS_FAILED(rv)) {
103 0 : NS_WARNING("Dispatch failed");
104 0 : return NS_ERROR_FAILURE;
105 : }
106 :
107 19382 : return NS_OK;
108 : }
109 :
110 19382 : NS_IMETHOD Run()
111 : {
112 19382 : if (mCallback) {
113 19382 : if (mStream)
114 19382 : mCallback->OnInputStreamReady(mStream);
115 19382 : mCallback = nsnull;
116 : }
117 19382 : return NS_OK;
118 : }
119 :
120 : private:
121 : nsCOMPtr<nsIAsyncInputStream> mStream;
122 : nsCOMPtr<nsIInputStreamCallback> mCallback;
123 : nsCOMPtr<nsIEventTarget> mTarget;
124 : };
125 :
126 334820 : 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 8368 : nsresult rv = mTarget->IsOnCurrentThread(&val);
158 8368 : 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 9908 : NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream)
176 : {
177 9908 : mStream = stream;
178 :
179 : nsresult rv =
180 9908 : mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
181 9908 : if (NS_FAILED(rv)) {
182 0 : NS_WARNING("PostEvent failed");
183 0 : return NS_ERROR_FAILURE;
184 : }
185 :
186 9908 : return NS_OK;
187 : }
188 :
189 9908 : NS_IMETHOD Run()
190 : {
191 9908 : if (mCallback) {
192 9908 : if (mStream)
193 9908 : mCallback->OnOutputStreamReady(mStream);
194 9908 : mCallback = nsnull;
195 : }
196 9908 : return NS_OK;
197 : }
198 :
199 : private:
200 : nsCOMPtr<nsIAsyncOutputStream> mStream;
201 : nsCOMPtr<nsIOutputStreamCallback> mCallback;
202 : nsCOMPtr<nsIEventTarget> mTarget;
203 : };
204 :
205 264855 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamReadyEvent, nsIRunnable,
206 : nsIOutputStreamCallback)
207 :
208 : //-----------------------------------------------------------------------------
209 :
210 : nsresult
211 19390 : NS_NewInputStreamReadyEvent(nsIInputStreamCallback **event,
212 : nsIInputStreamCallback *callback,
213 : nsIEventTarget *target)
214 : {
215 19390 : NS_ASSERTION(callback, "null callback");
216 19390 : NS_ASSERTION(target, "null target");
217 19390 : nsInputStreamReadyEvent *ev = new nsInputStreamReadyEvent(callback, target);
218 19390 : if (!ev)
219 0 : return NS_ERROR_OUT_OF_MEMORY;
220 19390 : NS_ADDREF(*event = ev);
221 19390 : 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 6487 : 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 6487 : , mCancelStatus(NS_OK)
260 : {
261 6487 : }
262 :
263 : // virtual since subclasses call superclass Release()
264 6486 : virtual ~nsAStreamCopier()
265 6486 : {
266 12972 : }
267 :
268 : // kick off the async copy...
269 6487 : 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 6487 : mSource = source;
279 6487 : mSink = sink;
280 6487 : mTarget = target;
281 6487 : mCallback = callback;
282 6487 : mClosure = closure;
283 6487 : mChunkSize = chunksize;
284 6487 : mCloseSource = closeSource;
285 6487 : mCloseSink = closeSink;
286 :
287 6487 : mAsyncSource = do_QueryInterface(mSource);
288 6487 : mAsyncSink = do_QueryInterface(mSink);
289 :
290 6487 : 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 24976 : void Process()
298 : {
299 24976 : if (!mSource || !mSink)
300 5813 : return;
301 :
302 : nsresult sourceCondition, sinkCondition;
303 : nsresult cancelStatus;
304 : bool canceled;
305 : {
306 38326 : MutexAutoLock lock(mLock);
307 19163 : canceled = mCanceled;
308 19163 : 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 7091 : 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 26254 : bool copyFailed = false;
318 26254 : if (!canceled) {
319 26250 : PRUint32 n = DoCopy(&sourceCondition, &sinkCondition);
320 26250 : copyFailed = NS_FAILED(sourceCondition) ||
321 26250 : NS_FAILED(sinkCondition) || n == 0;
322 :
323 52500 : MutexAutoLock lock(mLock);
324 26250 : canceled = mCanceled;
325 26250 : cancelStatus = mCancelStatus;
326 : }
327 26254 : if (copyFailed && !canceled) {
328 19159 : 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 12656 : mAsyncSource->AsyncWait(this, 0, 0, nsnull);
332 :
333 12656 : if (mAsyncSink)
334 12656 : mAsyncSink->AsyncWait(this,
335 : nsIAsyncOutputStream::WAIT_CLOSURE_ONLY,
336 12656 : 0, nsnull);
337 12656 : break;
338 : }
339 6503 : 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 25 : mAsyncSink->AsyncWait(this, 0, 0, nsnull);
344 :
345 25 : if (mAsyncSource)
346 25 : mAsyncSource->AsyncWait(this,
347 : nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
348 25 : 0, nsnull);
349 25 : break;
350 : }
351 : }
352 13573 : if (copyFailed || canceled) {
353 6482 : if (mCloseSource) {
354 : // close source
355 6476 : if (mAsyncSource)
356 5816 : mAsyncSource->CloseWithStatus(canceled ? cancelStatus :
357 5816 : sinkCondition);
358 : else
359 660 : mSource->Close();
360 : }
361 6482 : mAsyncSource = nsnull;
362 6482 : mSource = nsnull;
363 :
364 6482 : if (mCloseSink) {
365 : // close sink
366 6476 : if (mAsyncSink)
367 6098 : mAsyncSink->CloseWithStatus(canceled ? cancelStatus :
368 6098 : 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 756 : do_QueryInterface(mSink);
375 641 : if (sostream && NS_SUCCEEDED(sourceCondition) &&
376 263 : NS_SUCCEEDED(sinkCondition))
377 263 : sostream->Finish();
378 : else
379 115 : mSink->Close();
380 : }
381 : }
382 6482 : mAsyncSink = nsnull;
383 6482 : mSink = nsnull;
384 :
385 : // notify state complete...
386 6482 : if (mCallback) {
387 : nsresult status;
388 391 : if (!canceled) {
389 387 : status = sourceCondition;
390 387 : if (NS_SUCCEEDED(status))
391 387 : status = sinkCondition;
392 387 : if (status == NS_BASE_STREAM_CLOSED)
393 0 : status = NS_OK;
394 : } else {
395 4 : status = cancelStatus;
396 : }
397 391 : mCallback(mClosure, status);
398 : }
399 6482 : break;
400 : }
401 : }
402 : }
403 :
404 8 : nsresult Cancel(nsresult aReason)
405 : {
406 16 : MutexAutoLock lock(mLock);
407 8 : if (mCanceled)
408 0 : return NS_ERROR_FAILURE;
409 :
410 8 : if (NS_SUCCEEDED(aReason)) {
411 0 : NS_WARNING("cancel with non-failure status code");
412 0 : aReason = NS_BASE_STREAM_CLOSED;
413 : }
414 :
415 8 : mCanceled = true;
416 8 : mCancelStatus = aReason;
417 8 : return NS_OK;
418 : }
419 :
420 12642 : NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source)
421 : {
422 12642 : PostContinuationEvent();
423 12642 : return NS_OK;
424 : }
425 :
426 5851 : NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink)
427 : {
428 5851 : PostContinuationEvent();
429 5851 : return NS_OK;
430 : }
431 :
432 : // continuation event handler
433 24976 : NS_IMETHOD Run()
434 : {
435 24976 : Process();
436 :
437 : // clear "in process" flag and post any pending continuation event
438 49952 : MutexAutoLock lock(mLock);
439 24976 : mEventInProcess = false;
440 24976 : if (mEventIsPending) {
441 2948 : mEventIsPending = false;
442 2948 : PostContinuationEvent_Locked();
443 : }
444 :
445 24976 : return NS_OK;
446 : }
447 :
448 24980 : 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 49960 : MutexAutoLock lock(mLock);
458 24980 : return PostContinuationEvent_Locked();
459 : }
460 :
461 27928 : nsresult PostContinuationEvent_Locked()
462 : {
463 27928 : nsresult rv = NS_OK;
464 27928 : if (mEventInProcess)
465 2948 : mEventIsPending = true;
466 : else {
467 24980 : rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL);
468 24980 : if (NS_SUCCEEDED(rv))
469 24976 : mEventInProcess = true;
470 : else
471 4 : NS_WARNING("unable to post continuation event");
472 : }
473 27928 : 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 338777 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsAStreamCopier,
495 : nsIInputStreamCallback,
496 : nsIOutputStreamCallback,
497 : nsIRunnable)
498 :
499 : class nsStreamCopierIB : public nsAStreamCopier
500 : {
501 : public:
502 3301 : nsStreamCopierIB() : nsAStreamCopier() {}
503 13200 : virtual ~nsStreamCopierIB() {}
504 :
505 : struct ReadSegmentsState {
506 : nsIOutputStream *mSink;
507 : nsresult mSinkCondition;
508 : };
509 :
510 7001 : static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr,
511 : void *closure,
512 : const char *buffer,
513 : PRUint32 offset,
514 : PRUint32 count,
515 : PRUint32 *countWritten)
516 : {
517 7001 : ReadSegmentsState *state = (ReadSegmentsState *) closure;
518 :
519 7001 : nsresult rv = state->mSink->Write(buffer, count, countWritten);
520 7001 : if (NS_FAILED(rv))
521 30 : state->mSinkCondition = rv;
522 6971 : else if (*countWritten == 0)
523 0 : state->mSinkCondition = NS_BASE_STREAM_CLOSED;
524 :
525 7001 : return state->mSinkCondition;
526 : }
527 :
528 19355 : PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
529 : {
530 : ReadSegmentsState state;
531 19355 : state.mSink = mSink;
532 19355 : state.mSinkCondition = NS_OK;
533 :
534 : PRUint32 n;
535 : *sourceCondition =
536 19355 : mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n);
537 19355 : *sinkCondition = state.mSinkCondition;
538 19355 : return n;
539 : }
540 : };
541 :
542 : class nsStreamCopierOB : public nsAStreamCopier
543 : {
544 : public:
545 3186 : nsStreamCopierOB() : nsAStreamCopier() {}
546 12744 : virtual ~nsStreamCopierOB() {}
547 :
548 : struct WriteSegmentsState {
549 : nsIInputStream *mSource;
550 : nsresult mSourceCondition;
551 : };
552 :
553 10441 : static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr,
554 : void *closure,
555 : char *buffer,
556 : PRUint32 offset,
557 : PRUint32 count,
558 : PRUint32 *countRead)
559 : {
560 10441 : WriteSegmentsState *state = (WriteSegmentsState *) closure;
561 :
562 10441 : nsresult rv = state->mSource->Read(buffer, count, countRead);
563 10441 : if (NS_FAILED(rv))
564 3446 : state->mSourceCondition = rv;
565 6995 : else if (*countRead == 0)
566 3123 : state->mSourceCondition = NS_BASE_STREAM_CLOSED;
567 :
568 10441 : return state->mSourceCondition;
569 : }
570 :
571 6895 : PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition)
572 : {
573 : WriteSegmentsState state;
574 6895 : state.mSource = mSource;
575 6895 : state.mSourceCondition = NS_OK;
576 :
577 : PRUint32 n;
578 : *sinkCondition =
579 6895 : mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n);
580 6895 : *sourceCondition = state.mSourceCondition;
581 6895 : return n;
582 : }
583 : };
584 :
585 : //-----------------------------------------------------------------------------
586 :
587 : nsresult
588 6487 : 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 6487 : NS_ASSERTION(target, "non-null target required");
600 :
601 : nsresult rv;
602 : nsAStreamCopier *copier;
603 :
604 6487 : if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS)
605 3301 : copier = new nsStreamCopierIB();
606 : else
607 3186 : copier = new nsStreamCopierOB();
608 :
609 6487 : if (!copier)
610 0 : return NS_ERROR_OUT_OF_MEMORY;
611 :
612 : // Start() takes an owning ref to the copier...
613 6487 : NS_ADDREF(copier);
614 : rv = copier->Start(source, sink, target, callback, closure, chunkSize,
615 6487 : closeSource, closeSink);
616 :
617 6487 : if (aCopierCtx) {
618 : *aCopierCtx = static_cast<nsISupports*>(
619 395 : static_cast<nsIRunnable*>(copier));
620 395 : NS_ADDREF(*aCopierCtx);
621 : }
622 6487 : NS_RELEASE(copier);
623 :
624 6487 : return rv;
625 : }
626 :
627 : //-----------------------------------------------------------------------------
628 :
629 : nsresult
630 8 : NS_CancelAsyncCopy(nsISupports *aCopierCtx, nsresult aReason)
631 : {
632 : nsAStreamCopier *copier = static_cast<nsAStreamCopier *>(
633 8 : static_cast<nsIRunnable *>(aCopierCtx));
634 8 : 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 4074 : TestInputStream(nsIInputStream *inStr,
683 : void *closure,
684 : const char *buffer,
685 : PRUint32 offset,
686 : PRUint32 count,
687 : PRUint32 *countWritten)
688 : {
689 4074 : bool *result = static_cast<bool *>(closure);
690 4074 : *result = true;
691 4074 : return NS_ERROR_ABORT; // don't call me anymore
692 : }
693 :
694 : bool
695 4603 : NS_InputStreamIsBuffered(nsIInputStream *stream)
696 : {
697 4603 : bool result = false;
698 : PRUint32 n;
699 : nsresult rv = stream->ReadSegments(TestInputStream,
700 4603 : &result, 1, &n);
701 4603 : 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 382 : NS_OutputStreamIsBuffered(nsIOutputStream *stream)
719 : {
720 382 : bool result = false;
721 : PRUint32 n;
722 382 : stream->WriteSegments(TestOutputStream, &result, 1, &n);
723 382 : 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 4060575 : NS_CopySegmentToBuffer(nsIInputStream *inStr,
752 : void *closure,
753 : const char *buffer,
754 : PRUint32 offset,
755 : PRUint32 count,
756 : PRUint32 *countWritten)
757 : {
758 4060575 : char *toBuf = static_cast<char *>(closure);
759 4060575 : memcpy(&toBuf[offset], buffer, count);
760 4060575 : *countWritten = count;
761 4060575 : 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 : }
|