xpcom/io/nsStreamUtils.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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

mercurial