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/. */
6 #include "CacheFileOutputStream.h"
9 #include "CacheEntry.h"
10 #include "nsStreamUtils.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/DebugOnly.h"
18 NS_IMPL_ADDREF(CacheFileOutputStream
)
19 NS_IMETHODIMP_(MozExternalRefCountType
)
20 CacheFileOutputStream::Release()
22 NS_PRECONDITION(0 != mRefCnt
, "dup release");
23 nsrefcnt count
= --mRefCnt
;
24 NS_LOG_RELEASE(this, count
, "CacheFileOutputStream");
29 CacheFileAutoLock
lock(mFile
);
30 mFile
->RemoveOutput(this, mStatus
);
39 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream
)
40 NS_INTERFACE_MAP_ENTRY(nsIOutputStream
)
41 NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream
)
42 NS_INTERFACE_MAP_ENTRY(nsISeekableStream
)
43 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener
)
44 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIOutputStream
)
45 NS_INTERFACE_MAP_END_THREADSAFE
47 CacheFileOutputStream::CacheFileOutputStream(CacheFile
*aFile
,
48 CacheOutputCloseListener
*aCloseListener
)
50 , mCloseListener(aCloseListener
)
56 LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
57 MOZ_COUNT_CTOR(CacheFileOutputStream
);
60 CacheFileOutputStream::~CacheFileOutputStream()
62 LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
63 MOZ_COUNT_DTOR(CacheFileOutputStream
);
68 CacheFileOutputStream::Close()
70 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
71 return CloseWithStatus(NS_OK
);
75 CacheFileOutputStream::Flush()
77 // TODO do we need to implement flush ???
78 LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
83 CacheFileOutputStream::Write(const char * aBuf
, uint32_t aCount
,
86 CacheFileAutoLock
lock(mFile
);
88 LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount
));
91 LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
92 "status=0x%08x]", this, mStatus
));
94 return NS_FAILED(mStatus
) ? mStatus
: NS_BASE_STREAM_CLOSED
;
97 if (CacheObserver::EntryIsTooBig(mPos
+ aCount
, !mFile
->mMemoryOnly
)) {
98 LOG(("CacheFileOutputStream::Write() - Entry is too big, failing and "
99 "dooming the entry. [this=%p]", this));
101 mFile
->DoomLocked(nullptr);
102 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG
);
103 return NS_ERROR_FILE_TOO_BIG
;
109 EnsureCorrectChunk(false);
110 if (NS_FAILED(mStatus
)) {
115 if (NS_FAILED(mStatus
)) {
119 uint32_t chunkOffset
= mPos
- (mPos
/ kChunkSize
) * kChunkSize
;
120 uint32_t canWrite
= kChunkSize
- chunkOffset
;
121 uint32_t thisWrite
= std::min(static_cast<uint32_t>(canWrite
), aCount
);
122 nsresult rv
= mChunk
->EnsureBufSize(chunkOffset
+ thisWrite
);
124 CloseWithStatusLocked(rv
);
127 memcpy(mChunk
->BufForWriting() + chunkOffset
, aBuf
, thisWrite
);
133 mChunk
->UpdateDataSize(chunkOffset
, thisWrite
, false);
136 EnsureCorrectChunk(true);
138 LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]",
145 CacheFileOutputStream::WriteFrom(nsIInputStream
*aFromStream
, uint32_t aCount
,
148 LOG(("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
149 ", count=%d]", this, aFromStream
, aCount
));
151 return NS_ERROR_NOT_IMPLEMENTED
;
155 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader
, void *aClosure
,
156 uint32_t aCount
, uint32_t *_retval
)
158 LOG(("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
159 "count=%d]", this, aCount
));
161 return NS_ERROR_NOT_IMPLEMENTED
;
165 CacheFileOutputStream::IsNonBlocking(bool *_retval
)
171 // nsIAsyncOutputStream
173 CacheFileOutputStream::CloseWithStatus(nsresult aStatus
)
175 CacheFileAutoLock
lock(mFile
);
177 LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
180 return CloseWithStatusLocked(aStatus
);
184 CacheFileOutputStream::CloseWithStatusLocked(nsresult aStatus
)
186 LOG(("CacheFileOutputStream::CloseWithStatusLocked() [this=%p, "
187 "aStatus=0x%08x]", this, aStatus
));
190 MOZ_ASSERT(!mCallback
);
195 mStatus
= NS_FAILED(aStatus
) ? aStatus
: NS_BASE_STREAM_CLOSED
;
205 mFile
->RemoveOutput(this, mStatus
);
211 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback
*aCallback
,
213 uint32_t aRequestedCount
,
214 nsIEventTarget
*aEventTarget
)
216 CacheFileAutoLock
lock(mFile
);
218 LOG(("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
219 "requestedCount=%d, eventTarget=%p]", this, aCallback
, aFlags
,
220 aRequestedCount
, aEventTarget
));
222 mCallback
= aCallback
;
223 mCallbackFlags
= aFlags
;
224 mCallbackTarget
= aEventTarget
;
229 // The stream is blocking so it is writable at any time
230 if (mClosed
|| !(aFlags
& WAIT_CLOSURE_ONLY
))
238 CacheFileOutputStream::Seek(int32_t whence
, int64_t offset
)
240 CacheFileAutoLock
lock(mFile
);
242 LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
243 this, whence
, offset
));
246 LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
247 return NS_BASE_STREAM_CLOSED
;
250 int64_t newPos
= offset
;
258 newPos
+= mFile
->mDataSize
;
261 NS_ERROR("invalid whence");
262 return NS_ERROR_INVALID_ARG
;
265 EnsureCorrectChunk(true);
267 LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%lld]", this, mPos
));
272 CacheFileOutputStream::Tell(int64_t *_retval
)
274 CacheFileAutoLock
lock(mFile
);
277 LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
278 return NS_BASE_STREAM_CLOSED
;
283 LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%lld]", this, *_retval
));
288 CacheFileOutputStream::SetEOF()
290 MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
291 // Right now we don't use SetEOF(). If we ever need this method, we need
292 // to think about what to do with input streams that already points beyond
294 return NS_ERROR_NOT_IMPLEMENTED
;
297 // CacheFileChunkListener
299 CacheFileOutputStream::OnChunkRead(nsresult aResult
, CacheFileChunk
*aChunk
)
301 MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
302 return NS_ERROR_UNEXPECTED
;
306 CacheFileOutputStream::OnChunkWritten(nsresult aResult
, CacheFileChunk
*aChunk
)
309 "CacheFileOutputStream::OnChunkWritten should not be called!");
310 return NS_ERROR_UNEXPECTED
;
314 CacheFileOutputStream::OnChunkAvailable(nsresult aResult
,
316 CacheFileChunk
*aChunk
)
319 "CacheFileOutputStream::OnChunkAvailable should not be called!");
320 return NS_ERROR_UNEXPECTED
;
324 CacheFileOutputStream::OnChunkUpdated(CacheFileChunk
*aChunk
)
327 "CacheFileOutputStream::OnChunkUpdated should not be called!");
328 return NS_ERROR_UNEXPECTED
;
331 void CacheFileOutputStream::NotifyCloseListener()
333 nsRefPtr
<CacheOutputCloseListener
> listener
;
334 listener
.swap(mCloseListener
);
338 listener
->OnOutputClosed();
342 CacheFileOutputStream::ReleaseChunk()
344 LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
345 this, mChunk
->Index()));
347 mFile
->ReleaseOutsideLock(mChunk
.forget().take());
351 CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly
)
353 mFile
->AssertOwnsLock();
355 LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
356 this, aReleaseOnly
));
358 uint32_t chunkIdx
= mPos
/ kChunkSize
;
361 if (mChunk
->Index() == chunkIdx
) {
362 // we have a correct chunk
363 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
364 "[this=%p, idx=%d]", this, chunkIdx
));
377 rv
= mFile
->GetChunkLocked(chunkIdx
, CacheFile::WRITER
, nullptr,
378 getter_AddRefs(mChunk
));
380 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
381 "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx
, rv
));
382 CloseWithStatusLocked(rv
);
387 CacheFileOutputStream::FillHole()
389 mFile
->AssertOwnsLock();
392 MOZ_ASSERT(mPos
/ kChunkSize
== mChunk
->Index());
394 uint32_t pos
= mPos
- (mPos
/ kChunkSize
) * kChunkSize
;
395 if (mChunk
->DataSize() >= pos
)
398 LOG(("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
399 "%d-%d [this=%p]", mChunk
->Index(), mChunk
->DataSize(), pos
- 1, this));
401 nsresult rv
= mChunk
->EnsureBufSize(pos
);
403 CloseWithStatusLocked(rv
);
407 memset(mChunk
->BufForWriting() + mChunk
->DataSize(), 0,
408 pos
- mChunk
->DataSize());
410 mChunk
->UpdateDataSize(mChunk
->DataSize(), pos
- mChunk
->DataSize(), false);
414 CacheFileOutputStream::NotifyListener()
416 mFile
->AssertOwnsLock();
418 LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
420 MOZ_ASSERT(mCallback
);
422 if (!mCallbackTarget
) {
423 mCallbackTarget
= CacheFileIOManager::IOTarget();
424 if (!mCallbackTarget
) {
425 LOG(("CacheFileOutputStream::NotifyListener() - Cannot get Cache I/O "
426 "thread! Using main thread for callback."));
427 mCallbackTarget
= do_GetMainThread();
431 nsCOMPtr
<nsIOutputStreamCallback
> asyncCallback
=
432 NS_NewOutputStreamReadyEvent(mCallback
, mCallbackTarget
);
435 mCallbackTarget
= nullptr;
437 asyncCallback
->OnOutputStreamReady(this);
443 CacheFileOutputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
445 // Everything the stream keeps a reference to is already reported somewhere else.
446 // mFile reports itself.
447 // mChunk reported as part of CacheFile.
448 // mCloseListener is CacheEntry, already reported.
449 // mCallback is usually CacheFile or a class that is reported elsewhere.
450 return mallocSizeOf(this);