Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "CacheLog.h"
6 : #include "CacheFileInputStream.h"
7 :
8 : #include "CacheFile.h"
9 : #include "nsStreamUtils.h"
10 : #include "nsThreadUtils.h"
11 : #include <algorithm>
12 :
13 : namespace mozilla {
14 : namespace net {
15 :
16 66 : NS_IMPL_ADDREF(CacheFileInputStream)
17 : NS_IMETHODIMP_(MozExternalRefCountType)
18 66 : CacheFileInputStream::Release()
19 : {
20 66 : NS_PRECONDITION(0 != mRefCnt, "dup release");
21 66 : nsrefcnt count = --mRefCnt;
22 66 : NS_LOG_RELEASE(this, count, "CacheFileInputStream");
23 :
24 66 : if (0 == count) {
25 4 : mRefCnt = 1;
26 4 : delete (this);
27 4 : return 0;
28 : }
29 :
30 62 : if (count == 1) {
31 4 : mFile->RemoveInput(this, mStatus);
32 : }
33 :
34 62 : return count;
35 : }
36 :
37 46 : NS_INTERFACE_MAP_BEGIN(CacheFileInputStream)
38 46 : NS_INTERFACE_MAP_ENTRY(nsIInputStream)
39 25 : NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
40 17 : NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
41 7 : NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
42 3 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
43 3 : NS_INTERFACE_MAP_END_THREADSAFE
44 :
45 4 : CacheFileInputStream::CacheFileInputStream(CacheFile *aFile,
46 : nsISupports *aEntry,
47 4 : bool aAlternativeData)
48 : : mFile(aFile)
49 : , mPos(0)
50 : , mStatus(NS_OK)
51 : , mClosed(false)
52 : , mInReadSegments(false)
53 : , mWaitingForUpdate(false)
54 : , mAlternativeData(aAlternativeData)
55 : , mListeningForChunk(-1)
56 : , mCallbackFlags(0)
57 4 : , mCacheEntryHandle(aEntry)
58 : {
59 4 : LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this));
60 :
61 4 : if (mAlternativeData) {
62 0 : mPos = mFile->mAltDataOffset;
63 : }
64 4 : }
65 :
66 12 : CacheFileInputStream::~CacheFileInputStream()
67 : {
68 4 : LOG(("CacheFileInputStream::~CacheFileInputStream() [this=%p]", this));
69 4 : MOZ_ASSERT(!mInReadSegments);
70 12 : }
71 :
72 : // nsIInputStream
73 : NS_IMETHODIMP
74 3 : CacheFileInputStream::Close()
75 : {
76 3 : LOG(("CacheFileInputStream::Close() [this=%p]", this));
77 3 : return CloseWithStatus(NS_OK);
78 : }
79 :
80 : NS_IMETHODIMP
81 8 : CacheFileInputStream::Available(uint64_t *_retval)
82 : {
83 16 : CacheFileAutoLock lock(mFile);
84 :
85 8 : if (mClosed) {
86 0 : LOG(("CacheFileInputStream::Available() - Stream is closed. [this=%p, "
87 : "status=0x%08" PRIx32 "]", this, static_cast<uint32_t>(mStatus)));
88 0 : return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
89 : }
90 :
91 8 : EnsureCorrectChunk(false);
92 8 : if (NS_FAILED(mStatus)) {
93 0 : LOG(("CacheFileInputStream::Available() - EnsureCorrectChunk failed. "
94 : "[this=%p, status=0x%08" PRIx32 "]", this, static_cast<uint32_t>(mStatus)));
95 0 : return mStatus;
96 : }
97 :
98 8 : nsresult rv = NS_OK;
99 8 : *_retval = 0;
100 :
101 8 : if (mChunk) {
102 8 : int64_t canRead = mFile->BytesFromChunk(mChunk->Index(), mAlternativeData);
103 8 : canRead -= (mPos % kChunkSize);
104 :
105 8 : if (canRead > 0) {
106 6 : *_retval = canRead;
107 2 : } else if (canRead == 0 && !mFile->OutputStreamExists(mAlternativeData)) {
108 2 : rv = NS_BASE_STREAM_CLOSED;
109 : }
110 : }
111 :
112 8 : LOG(("CacheFileInputStream::Available() [this=%p, retval=%" PRIu64 ", rv=0x%08" PRIx32 "]",
113 : this, *_retval, static_cast<uint32_t>(rv)));
114 :
115 8 : return rv;
116 : }
117 :
118 : NS_IMETHODIMP
119 2 : CacheFileInputStream::Read(char *aBuf, uint32_t aCount, uint32_t *_retval)
120 : {
121 2 : LOG(("CacheFileInputStream::Read() [this=%p, count=%d]", this, aCount));
122 2 : return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
123 : }
124 :
125 : NS_IMETHODIMP
126 6 : CacheFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
127 : uint32_t aCount, uint32_t *_retval)
128 : {
129 12 : CacheFileAutoLock lock(mFile);
130 :
131 6 : LOG(("CacheFileInputStream::ReadSegments() [this=%p, count=%d]",
132 : this, aCount));
133 :
134 : nsresult rv;
135 :
136 6 : *_retval = 0;
137 :
138 6 : if (mInReadSegments) {
139 0 : LOG(("CacheFileInputStream::ReadSegments() - Cannot be called while the "
140 : "stream is in ReadSegments!"));
141 0 : return NS_ERROR_UNEXPECTED;
142 : }
143 :
144 6 : if (mClosed) {
145 0 : LOG(("CacheFileInputStream::ReadSegments() - Stream is closed. [this=%p, "
146 : "status=0x%08" PRIx32 "]", this, static_cast<uint32_t>(mStatus)));
147 :
148 0 : if NS_FAILED(mStatus)
149 0 : return mStatus;
150 :
151 0 : return NS_OK;
152 : }
153 :
154 6 : EnsureCorrectChunk(false);
155 :
156 : while (true) {
157 6 : if (NS_FAILED(mStatus))
158 0 : return mStatus;
159 :
160 6 : if (!mChunk) {
161 0 : if (mListeningForChunk == -1) {
162 0 : return NS_OK;
163 : } else {
164 0 : return NS_BASE_STREAM_WOULD_BLOCK;
165 : }
166 : }
167 :
168 6 : CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
169 6 : int64_t canRead = CanRead(&hnd);
170 6 : if (NS_FAILED(mStatus)) {
171 0 : return mStatus;
172 : }
173 :
174 6 : if (canRead < 0) {
175 : // file was truncated ???
176 0 : MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
177 : rv = NS_OK;
178 6 : } else if (canRead > 0) {
179 6 : uint32_t toRead = std::min(static_cast<uint32_t>(canRead), aCount);
180 : uint32_t read;
181 6 : const char *buf = hnd.Buf() + (mPos - hnd.Offset());
182 :
183 6 : mInReadSegments = true;
184 6 : lock.Unlock();
185 :
186 6 : rv = aWriter(this, aClosure, buf, *_retval, toRead, &read);
187 :
188 6 : lock.Lock();
189 6 : mInReadSegments = false;
190 :
191 6 : if (NS_SUCCEEDED(rv)) {
192 2 : MOZ_ASSERT(read <= toRead,
193 : "writer should not write more than we asked it to write");
194 :
195 2 : *_retval += read;
196 2 : mPos += read;
197 2 : aCount -= read;
198 :
199 2 : if (!mClosed) {
200 2 : if (hnd.DataSize() != mChunk->DataSize()) {
201 : // New data was written to this chunk while the lock was released.
202 0 : continue;
203 : }
204 :
205 : // The last chunk is released after the caller closes this stream.
206 2 : EnsureCorrectChunk(false);
207 :
208 2 : if (mChunk && aCount) {
209 : // We have the next chunk! Go on.
210 0 : continue;
211 : }
212 : }
213 : }
214 :
215 6 : if (mClosed) {
216 : // The stream was closed from aWriter, do the cleanup.
217 0 : CleanUp();
218 : }
219 :
220 6 : rv = NS_OK;
221 : } else {
222 0 : if (mFile->OutputStreamExists(mAlternativeData)) {
223 0 : rv = NS_BASE_STREAM_WOULD_BLOCK;
224 : } else {
225 0 : rv = NS_OK;
226 : }
227 : }
228 :
229 6 : break;
230 0 : }
231 :
232 6 : LOG(("CacheFileInputStream::ReadSegments() [this=%p, rv=0x%08" PRIx32 ", retval=%d]",
233 : this, static_cast<uint32_t>(rv), *_retval));
234 :
235 6 : return rv;
236 : }
237 :
238 : NS_IMETHODIMP
239 7 : CacheFileInputStream::IsNonBlocking(bool *_retval)
240 : {
241 7 : *_retval = true;
242 7 : return NS_OK;
243 : }
244 :
245 : // nsIAsyncInputStream
246 : NS_IMETHODIMP
247 4 : CacheFileInputStream::CloseWithStatus(nsresult aStatus)
248 : {
249 8 : CacheFileAutoLock lock(mFile);
250 :
251 4 : LOG(("CacheFileInputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32 "]",
252 : this, static_cast<uint32_t>(aStatus)));
253 :
254 8 : return CloseWithStatusLocked(aStatus);
255 : }
256 :
257 : nsresult
258 4 : CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus)
259 : {
260 4 : LOG(("CacheFileInputStream::CloseWithStatusLocked() [this=%p, "
261 : "aStatus=0x%08" PRIx32 "]", this, static_cast<uint32_t>(aStatus)));
262 :
263 4 : if (mClosed) {
264 : // We notify listener and null out mCallback immediately after closing
265 : // the stream. If we're in ReadSegments we postpone notification until we
266 : // step out from ReadSegments. So if the stream is already closed the
267 : // following assertion must be true.
268 0 : MOZ_ASSERT(!mCallback || mInReadSegments);
269 :
270 0 : return NS_OK;
271 : }
272 :
273 4 : mClosed = true;
274 4 : mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
275 :
276 4 : if (!mInReadSegments) {
277 4 : CleanUp();
278 : }
279 :
280 4 : return NS_OK;
281 : }
282 :
283 : void
284 4 : CacheFileInputStream::CleanUp()
285 : {
286 4 : MOZ_ASSERT(!mInReadSegments);
287 4 : MOZ_ASSERT(mClosed);
288 :
289 4 : if (mChunk) {
290 3 : ReleaseChunk();
291 : }
292 :
293 : // TODO propagate error from input stream to other streams ???
294 :
295 4 : MaybeNotifyListener();
296 :
297 4 : mFile->ReleaseOutsideLock(mCacheEntryHandle.forget());
298 4 : }
299 :
300 : NS_IMETHODIMP
301 5 : CacheFileInputStream::AsyncWait(nsIInputStreamCallback *aCallback,
302 : uint32_t aFlags,
303 : uint32_t aRequestedCount,
304 : nsIEventTarget *aEventTarget)
305 : {
306 10 : CacheFileAutoLock lock(mFile);
307 :
308 5 : LOG(("CacheFileInputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
309 : "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
310 : aRequestedCount, aEventTarget));
311 :
312 5 : if (mInReadSegments) {
313 0 : LOG(("CacheFileInputStream::AsyncWait() - Cannot be called while the stream"
314 : " is in ReadSegments!"));
315 0 : MOZ_ASSERT(false, "Unexpected call. If it's a valid usage implement it. "
316 : "Otherwise fix the caller.");
317 : return NS_ERROR_UNEXPECTED;
318 : }
319 :
320 5 : mCallback = aCallback;
321 5 : mCallbackFlags = aFlags;
322 5 : mCallbackTarget = aEventTarget;
323 :
324 5 : if (!mCallback) {
325 0 : if (mWaitingForUpdate) {
326 0 : mChunk->CancelWait(this);
327 0 : mWaitingForUpdate = false;
328 : }
329 0 : return NS_OK;
330 : }
331 :
332 5 : if (mClosed) {
333 0 : NotifyListener();
334 0 : return NS_OK;
335 : }
336 :
337 5 : EnsureCorrectChunk(false);
338 :
339 5 : MaybeNotifyListener();
340 :
341 5 : return NS_OK;
342 : }
343 :
344 : // nsISeekableStream
345 : NS_IMETHODIMP
346 4 : CacheFileInputStream::Seek(int32_t whence, int64_t offset)
347 : {
348 8 : CacheFileAutoLock lock(mFile);
349 :
350 4 : LOG(("CacheFileInputStream::Seek() [this=%p, whence=%d, offset=%" PRId64 "]",
351 : this, whence, offset));
352 :
353 4 : if (mInReadSegments) {
354 0 : LOG(("CacheFileInputStream::Seek() - Cannot be called while the stream is "
355 : "in ReadSegments!"));
356 0 : return NS_ERROR_UNEXPECTED;
357 : }
358 :
359 4 : if (mClosed) {
360 0 : LOG(("CacheFileInputStream::Seek() - Stream is closed. [this=%p]", this));
361 0 : return NS_BASE_STREAM_CLOSED;
362 : }
363 :
364 4 : int64_t newPos = offset;
365 4 : switch (whence) {
366 : case NS_SEEK_SET:
367 4 : if (mAlternativeData) {
368 0 : newPos += mFile->mAltDataOffset;
369 : }
370 4 : break;
371 : case NS_SEEK_CUR:
372 0 : newPos += mPos;
373 0 : break;
374 : case NS_SEEK_END:
375 0 : if (mAlternativeData) {
376 0 : newPos += mFile->mDataSize;
377 : } else {
378 0 : newPos += mFile->mAltDataOffset;
379 : }
380 0 : break;
381 : default:
382 0 : NS_ERROR("invalid whence");
383 0 : return NS_ERROR_INVALID_ARG;
384 : }
385 4 : mPos = newPos;
386 4 : EnsureCorrectChunk(false);
387 :
388 4 : LOG(("CacheFileInputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
389 4 : return NS_OK;
390 : }
391 :
392 : NS_IMETHODIMP
393 10 : CacheFileInputStream::Tell(int64_t *_retval)
394 : {
395 20 : CacheFileAutoLock lock(mFile);
396 :
397 10 : if (mClosed) {
398 0 : LOG(("CacheFileInputStream::Tell() - Stream is closed. [this=%p]", this));
399 0 : return NS_BASE_STREAM_CLOSED;
400 : }
401 :
402 10 : *_retval = mPos;
403 :
404 10 : if (mAlternativeData) {
405 0 : *_retval -= mFile->mAltDataOffset;
406 : }
407 :
408 10 : LOG(("CacheFileInputStream::Tell() [this=%p, retval=%" PRId64 "]", this, *_retval));
409 10 : return NS_OK;
410 : }
411 :
412 : NS_IMETHODIMP
413 0 : CacheFileInputStream::SetEOF()
414 : {
415 0 : MOZ_ASSERT(false, "Don't call SetEOF on cache input stream");
416 : return NS_ERROR_NOT_IMPLEMENTED;
417 : }
418 :
419 : // CacheFileChunkListener
420 : nsresult
421 0 : CacheFileInputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
422 : {
423 0 : MOZ_CRASH("CacheFileInputStream::OnChunkRead should not be called!");
424 : return NS_ERROR_UNEXPECTED;
425 : }
426 :
427 : nsresult
428 0 : CacheFileInputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
429 : {
430 0 : MOZ_CRASH("CacheFileInputStream::OnChunkWritten should not be called!");
431 : return NS_ERROR_UNEXPECTED;
432 : }
433 :
434 : nsresult
435 2 : CacheFileInputStream::OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx,
436 : CacheFileChunk *aChunk)
437 : {
438 4 : CacheFileAutoLock lock(mFile);
439 :
440 2 : LOG(("CacheFileInputStream::OnChunkAvailable() [this=%p, result=0x%08" PRIx32 ", "
441 : "idx=%d, chunk=%p]", this, static_cast<uint32_t>(aResult), aChunkIdx, aChunk));
442 :
443 2 : MOZ_ASSERT(mListeningForChunk != -1);
444 :
445 2 : if (mListeningForChunk != static_cast<int64_t>(aChunkIdx)) {
446 : // This is not a chunk that we're waiting for
447 0 : LOG(("CacheFileInputStream::OnChunkAvailable() - Notification is for a "
448 : "different chunk. [this=%p, listeningForChunk=%" PRId64 "]",
449 : this, mListeningForChunk));
450 :
451 0 : return NS_OK;
452 : }
453 :
454 2 : MOZ_ASSERT(!mChunk);
455 2 : MOZ_ASSERT(!mWaitingForUpdate);
456 2 : MOZ_ASSERT(!mInReadSegments);
457 2 : mListeningForChunk = -1;
458 :
459 2 : if (mClosed) {
460 0 : MOZ_ASSERT(!mCallback);
461 :
462 0 : LOG(("CacheFileInputStream::OnChunkAvailable() - Stream is closed, "
463 : "ignoring notification. [this=%p]", this));
464 :
465 0 : return NS_OK;
466 : }
467 :
468 2 : if (NS_SUCCEEDED(aResult)) {
469 2 : mChunk = aChunk;
470 0 : } else if (aResult != NS_ERROR_NOT_AVAILABLE) {
471 : // Close the stream with error. The consumer will receive this error later
472 : // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
473 : // differently since it is returned when the requested chunk is not
474 : // available and there is no writer that could create it, i.e. it means that
475 : // we've reached the end of the file.
476 0 : CloseWithStatusLocked(aResult);
477 :
478 0 : return NS_OK;
479 : }
480 :
481 2 : MaybeNotifyListener();
482 :
483 2 : return NS_OK;
484 : }
485 :
486 : nsresult
487 0 : CacheFileInputStream::OnChunkUpdated(CacheFileChunk *aChunk)
488 : {
489 0 : CacheFileAutoLock lock(mFile);
490 :
491 0 : LOG(("CacheFileInputStream::OnChunkUpdated() [this=%p, idx=%d]",
492 : this, aChunk->Index()));
493 :
494 0 : if (!mWaitingForUpdate) {
495 0 : LOG(("CacheFileInputStream::OnChunkUpdated() - Ignoring notification since "
496 : "mWaitingforUpdate == false. [this=%p]", this));
497 :
498 0 : return NS_OK;
499 : }
500 : else {
501 0 : mWaitingForUpdate = false;
502 : }
503 :
504 0 : MOZ_ASSERT(mChunk == aChunk);
505 :
506 0 : MaybeNotifyListener();
507 :
508 0 : return NS_OK;
509 : }
510 :
511 : void
512 3 : CacheFileInputStream::ReleaseChunk()
513 : {
514 3 : mFile->AssertOwnsLock();
515 :
516 3 : LOG(("CacheFileInputStream::ReleaseChunk() [this=%p, idx=%d]",
517 : this, mChunk->Index()));
518 :
519 3 : MOZ_ASSERT(!mInReadSegments);
520 :
521 3 : if (mWaitingForUpdate) {
522 0 : LOG(("CacheFileInputStream::ReleaseChunk() - Canceling waiting for update. "
523 : "[this=%p]", this));
524 :
525 0 : mChunk->CancelWait(this);
526 0 : mWaitingForUpdate = false;
527 : }
528 :
529 3 : mFile->ReleaseOutsideLock(mChunk.forget());
530 3 : }
531 :
532 : void
533 25 : CacheFileInputStream::EnsureCorrectChunk(bool aReleaseOnly)
534 : {
535 25 : mFile->AssertOwnsLock();
536 :
537 25 : LOG(("CacheFileInputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
538 : this, aReleaseOnly));
539 :
540 : nsresult rv;
541 :
542 25 : uint32_t chunkIdx = mPos / kChunkSize;
543 :
544 25 : if (mInReadSegments) {
545 : // We must have correct chunk
546 0 : MOZ_ASSERT(mChunk);
547 0 : MOZ_ASSERT(mChunk->Index() == chunkIdx);
548 0 : return;
549 : }
550 :
551 25 : if (mChunk) {
552 21 : if (mChunk->Index() == chunkIdx) {
553 : // we have a correct chunk
554 21 : LOG(("CacheFileInputStream::EnsureCorrectChunk() - Have correct chunk "
555 : "[this=%p, idx=%d]", this, chunkIdx));
556 :
557 21 : return;
558 : } else {
559 0 : ReleaseChunk();
560 : }
561 : }
562 :
563 4 : MOZ_ASSERT(!mWaitingForUpdate);
564 :
565 4 : if (aReleaseOnly)
566 0 : return;
567 :
568 4 : if (mListeningForChunk == static_cast<int64_t>(chunkIdx)) {
569 : // We're already waiting for this chunk
570 0 : LOG(("CacheFileInputStream::EnsureCorrectChunk() - Already listening for "
571 : "chunk %" PRId64 " [this=%p]", mListeningForChunk, this));
572 :
573 0 : return;
574 : }
575 :
576 4 : rv = mFile->GetChunkLocked(chunkIdx, CacheFile::READER, this,
577 8 : getter_AddRefs(mChunk));
578 4 : if (NS_FAILED(rv)) {
579 1 : LOG(("CacheFileInputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
580 : "[this=%p, idx=%d, rv=0x%08" PRIx32 "]", this, chunkIdx,
581 : static_cast<uint32_t>(rv)));
582 1 : if (rv != NS_ERROR_NOT_AVAILABLE) {
583 : // Close the stream with error. The consumer will receive this error later
584 : // in Read(), Available() etc. We need to handle NS_ERROR_NOT_AVAILABLE
585 : // differently since it is returned when the requested chunk is not
586 : // available and there is no writer that could create it, i.e. it means
587 : // that we've reached the end of the file.
588 0 : CloseWithStatusLocked(rv);
589 :
590 0 : return;
591 : }
592 3 : } else if (!mChunk) {
593 2 : mListeningForChunk = static_cast<int64_t>(chunkIdx);
594 : }
595 :
596 4 : MaybeNotifyListener();
597 : }
598 :
599 : int64_t
600 11 : CacheFileInputStream::CanRead(CacheFileChunkReadHandle *aHandle)
601 : {
602 11 : mFile->AssertOwnsLock();
603 :
604 11 : MOZ_ASSERT(mChunk);
605 11 : MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
606 :
607 11 : int64_t retval = aHandle->Offset() + aHandle->DataSize();
608 :
609 11 : if (!mAlternativeData && mFile->mAltDataOffset != -1 &&
610 0 : mFile->mAltDataOffset < retval) {
611 0 : retval = mFile->mAltDataOffset;
612 : }
613 :
614 11 : retval -= mPos;
615 11 : if (retval <= 0 && NS_FAILED(mChunk->GetStatus())) {
616 0 : CloseWithStatusLocked(mChunk->GetStatus());
617 : }
618 :
619 11 : LOG(("CacheFileInputStream::CanRead() [this=%p, canRead=%" PRId64 "]",
620 : this, retval));
621 :
622 11 : return retval;
623 : }
624 :
625 : void
626 5 : CacheFileInputStream::NotifyListener()
627 : {
628 5 : mFile->AssertOwnsLock();
629 :
630 5 : LOG(("CacheFileInputStream::NotifyListener() [this=%p]", this));
631 :
632 5 : MOZ_ASSERT(mCallback);
633 5 : MOZ_ASSERT(!mInReadSegments);
634 :
635 5 : if (!mCallbackTarget) {
636 0 : mCallbackTarget = CacheFileIOManager::IOTarget();
637 0 : if (!mCallbackTarget) {
638 0 : LOG(("CacheFileInputStream::NotifyListener() - Cannot get Cache I/O "
639 : "thread! Using main thread for callback."));
640 0 : mCallbackTarget = GetMainThreadEventTarget();
641 : }
642 : }
643 :
644 : nsCOMPtr<nsIInputStreamCallback> asyncCallback =
645 10 : NS_NewInputStreamReadyEvent("CacheFileInputStream::NotifyListener",
646 10 : mCallback, mCallbackTarget);
647 :
648 5 : mCallback = nullptr;
649 5 : mCallbackTarget = nullptr;
650 :
651 5 : asyncCallback->OnInputStreamReady(this);
652 5 : }
653 :
654 : void
655 15 : CacheFileInputStream::MaybeNotifyListener()
656 : {
657 15 : mFile->AssertOwnsLock();
658 :
659 15 : LOG(("CacheFileInputStream::MaybeNotifyListener() [this=%p, mCallback=%p, "
660 : "mClosed=%d, mStatus=0x%08" PRIx32 ", mChunk=%p, mListeningForChunk=%" PRId64 ", "
661 : "mWaitingForUpdate=%d]", this, mCallback.get(), mClosed,
662 : static_cast<uint32_t>(mStatus), mChunk.get(), mListeningForChunk,
663 : mWaitingForUpdate));
664 :
665 15 : MOZ_ASSERT(!mInReadSegments);
666 :
667 15 : if (!mCallback)
668 20 : return;
669 :
670 5 : if (mClosed || NS_FAILED(mStatus)) {
671 0 : NotifyListener();
672 0 : return;
673 : }
674 :
675 5 : if (!mChunk) {
676 0 : if (mListeningForChunk == -1) {
677 : // EOF, should we notify even if mCallbackFlags == WAIT_CLOSURE_ONLY ??
678 0 : NotifyListener();
679 : }
680 0 : return;
681 : }
682 :
683 5 : MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
684 :
685 5 : if (mWaitingForUpdate)
686 0 : return;
687 :
688 10 : CacheFileChunkReadHandle hnd = mChunk->GetReadHandle();
689 5 : int64_t canRead = CanRead(&hnd);
690 5 : if (NS_FAILED(mStatus)) {
691 : // CanRead() called CloseWithStatusLocked() which called
692 : // MaybeNotifyListener() so the listener was already notified. Stop here.
693 0 : MOZ_ASSERT(!mCallback);
694 0 : return;
695 : }
696 :
697 5 : if (canRead > 0) {
698 5 : if (!(mCallbackFlags & WAIT_CLOSURE_ONLY))
699 5 : NotifyListener();
700 : }
701 0 : else if (canRead == 0) {
702 0 : if (!mFile->OutputStreamExists(mAlternativeData)) {
703 : // EOF
704 0 : NotifyListener();
705 : }
706 : else {
707 0 : mChunk->WaitForUpdate(this);
708 0 : mWaitingForUpdate = true;
709 : }
710 : }
711 : else {
712 : // Output have set EOF before mPos?
713 0 : MOZ_ASSERT(false, "SetEOF is currenty not implemented?!");
714 : NotifyListener();
715 : }
716 : }
717 :
718 : // Memory reporting
719 :
720 : size_t
721 0 : CacheFileInputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
722 : {
723 : // Everything the stream keeps a reference to is already reported somewhere else.
724 : // mFile reports itself.
725 : // mChunk reported as part of CacheFile.
726 : // mCallback is usually CacheFile or a class that is reported elsewhere.
727 0 : return mallocSizeOf(this);
728 : }
729 :
730 : } // namespace net
731 : } // namespace mozilla
|