Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
5 #include "CacheLog.h"
6 #include "CacheFileOutputStream.h"
8 #include "CacheFile.h"
9 #include "CacheEntry.h"
10 #include "nsStreamUtils.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include <algorithm>
15 namespace mozilla {
16 namespace net {
18 NS_IMPL_ADDREF(CacheFileOutputStream)
19 NS_IMETHODIMP_(MozExternalRefCountType)
20 CacheFileOutputStream::Release()
21 {
22 NS_PRECONDITION(0 != mRefCnt, "dup release");
23 nsrefcnt count = --mRefCnt;
24 NS_LOG_RELEASE(this, count, "CacheFileOutputStream");
26 if (0 == count) {
27 mRefCnt = 1;
28 {
29 CacheFileAutoLock lock(mFile);
30 mFile->RemoveOutput(this);
31 }
32 delete (this);
33 return 0;
34 }
36 return count;
37 }
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)
49 : mFile(aFile)
50 , mCloseListener(aCloseListener)
51 , mPos(0)
52 , mClosed(false)
53 , mStatus(NS_OK)
54 , mCallbackFlags(0)
55 {
56 LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
57 MOZ_COUNT_CTOR(CacheFileOutputStream);
58 }
60 CacheFileOutputStream::~CacheFileOutputStream()
61 {
62 LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
63 MOZ_COUNT_DTOR(CacheFileOutputStream);
64 }
66 // nsIOutputStream
67 NS_IMETHODIMP
68 CacheFileOutputStream::Close()
69 {
70 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
71 return CloseWithStatus(NS_OK);
72 }
74 NS_IMETHODIMP
75 CacheFileOutputStream::Flush()
76 {
77 // TODO do we need to implement flush ???
78 LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
79 return NS_OK;
80 }
82 NS_IMETHODIMP
83 CacheFileOutputStream::Write(const char * aBuf, uint32_t aCount,
84 uint32_t *_retval)
85 {
86 CacheFileAutoLock lock(mFile);
88 LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
90 if (mClosed) {
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;
95 }
97 *_retval = aCount;
99 while (aCount) {
100 EnsureCorrectChunk(false);
101 if (NS_FAILED(mStatus))
102 return mStatus;
104 FillHole();
106 uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
107 uint32_t canWrite = kChunkSize - chunkOffset;
108 uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount);
109 mChunk->EnsureBufSize(chunkOffset + thisWrite);
110 memcpy(mChunk->BufForWriting() + chunkOffset, aBuf, thisWrite);
112 mPos += thisWrite;
113 aBuf += thisWrite;
114 aCount -= thisWrite;
116 mChunk->UpdateDataSize(chunkOffset, thisWrite, false);
117 }
119 EnsureCorrectChunk(true);
121 LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]",
122 *_retval, this));
124 return NS_OK;
125 }
127 NS_IMETHODIMP
128 CacheFileOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount,
129 uint32_t *_retval)
130 {
131 LOG(("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
132 ", count=%d]", this, aFromStream, aCount));
134 return NS_ERROR_NOT_IMPLEMENTED;
135 }
137 NS_IMETHODIMP
138 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure,
139 uint32_t aCount, uint32_t *_retval)
140 {
141 LOG(("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
142 "count=%d]", this, aCount));
144 return NS_ERROR_NOT_IMPLEMENTED;
145 }
147 NS_IMETHODIMP
148 CacheFileOutputStream::IsNonBlocking(bool *_retval)
149 {
150 *_retval = false;
151 return NS_OK;
152 }
154 // nsIAsyncOutputStream
155 NS_IMETHODIMP
156 CacheFileOutputStream::CloseWithStatus(nsresult aStatus)
157 {
158 CacheFileAutoLock lock(mFile);
160 LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08x]",
161 this, aStatus));
163 if (mClosed) {
164 MOZ_ASSERT(!mCallback);
165 return NS_OK;
166 }
168 mClosed = true;
169 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
171 if (mChunk)
172 ReleaseChunk();
174 if (mCallback)
175 NotifyListener();
177 mFile->RemoveOutput(this);
179 return NS_OK;
180 }
182 NS_IMETHODIMP
183 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback *aCallback,
184 uint32_t aFlags,
185 uint32_t aRequestedCount,
186 nsIEventTarget *aEventTarget)
187 {
188 CacheFileAutoLock lock(mFile);
190 LOG(("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
191 "requestedCount=%d, eventTarget=%p]", this, aCallback, aFlags,
192 aRequestedCount, aEventTarget));
194 mCallback = aCallback;
195 mCallbackFlags = aFlags;
197 if (!mCallback)
198 return NS_OK;
200 // The stream is blocking so it is writable at any time
201 if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY))
202 NotifyListener();
204 return NS_OK;
205 }
207 // nsISeekableStream
208 NS_IMETHODIMP
209 CacheFileOutputStream::Seek(int32_t whence, int64_t offset)
210 {
211 CacheFileAutoLock lock(mFile);
213 LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%lld]",
214 this, whence, offset));
216 if (mClosed) {
217 LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
218 return NS_BASE_STREAM_CLOSED;
219 }
221 int64_t newPos = offset;
222 switch (whence) {
223 case NS_SEEK_SET:
224 break;
225 case NS_SEEK_CUR:
226 newPos += mPos;
227 break;
228 case NS_SEEK_END:
229 newPos += mFile->mDataSize;
230 break;
231 default:
232 NS_ERROR("invalid whence");
233 return NS_ERROR_INVALID_ARG;
234 }
235 mPos = newPos;
236 EnsureCorrectChunk(true);
238 LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%lld]", this, mPos));
239 return NS_OK;
240 }
242 NS_IMETHODIMP
243 CacheFileOutputStream::Tell(int64_t *_retval)
244 {
245 CacheFileAutoLock lock(mFile);
247 if (mClosed) {
248 LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
249 return NS_BASE_STREAM_CLOSED;
250 }
252 *_retval = mPos;
254 LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%lld]", this, *_retval));
255 return NS_OK;
256 }
258 NS_IMETHODIMP
259 CacheFileOutputStream::SetEOF()
260 {
261 MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
262 // Right now we don't use SetEOF(). If we ever need this method, we need
263 // to think about what to do with input streams that already points beyond
264 // new EOF.
265 return NS_ERROR_NOT_IMPLEMENTED;
266 }
268 // CacheFileChunkListener
269 nsresult
270 CacheFileOutputStream::OnChunkRead(nsresult aResult, CacheFileChunk *aChunk)
271 {
272 MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
273 return NS_ERROR_UNEXPECTED;
274 }
276 nsresult
277 CacheFileOutputStream::OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk)
278 {
279 MOZ_CRASH(
280 "CacheFileOutputStream::OnChunkWritten should not be called!");
281 return NS_ERROR_UNEXPECTED;
282 }
284 nsresult
285 CacheFileOutputStream::OnChunkAvailable(nsresult aResult,
286 uint32_t aChunkIdx,
287 CacheFileChunk *aChunk)
288 {
289 MOZ_CRASH(
290 "CacheFileOutputStream::OnChunkAvailable should not be called!");
291 return NS_ERROR_UNEXPECTED;
292 }
294 nsresult
295 CacheFileOutputStream::OnChunkUpdated(CacheFileChunk *aChunk)
296 {
297 MOZ_CRASH(
298 "CacheFileOutputStream::OnChunkUpdated should not be called!");
299 return NS_ERROR_UNEXPECTED;
300 }
302 void CacheFileOutputStream::NotifyCloseListener()
303 {
304 nsRefPtr<CacheOutputCloseListener> listener;
305 listener.swap(mCloseListener);
306 if (!listener)
307 return;
309 listener->OnOutputClosed();
310 }
312 void
313 CacheFileOutputStream::ReleaseChunk()
314 {
315 LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]",
316 this, mChunk->Index()));
318 mFile->ReleaseOutsideLock(mChunk.forget().take());
319 }
321 void
322 CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly)
323 {
324 mFile->AssertOwnsLock();
326 LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
327 this, aReleaseOnly));
329 uint32_t chunkIdx = mPos / kChunkSize;
331 if (mChunk) {
332 if (mChunk->Index() == chunkIdx) {
333 // we have a correct chunk
334 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
335 "[this=%p, idx=%d]", this, chunkIdx));
337 return;
338 }
339 else {
340 ReleaseChunk();
341 }
342 }
344 if (aReleaseOnly)
345 return;
347 nsresult rv;
348 rv = mFile->GetChunkLocked(chunkIdx, true, nullptr, getter_AddRefs(mChunk));
349 if (NS_FAILED(rv)) {
350 LOG(("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
351 "[this=%p, idx=%d, rv=0x%08x]", this, chunkIdx, rv));
352 mStatus = rv;
353 }
354 }
356 void
357 CacheFileOutputStream::FillHole()
358 {
359 mFile->AssertOwnsLock();
361 MOZ_ASSERT(mChunk);
362 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
364 uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize;
365 if (mChunk->DataSize() >= pos)
366 return;
368 LOG(("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
369 "%d-%d [this=%p]", mChunk->Index(), mChunk->DataSize(), pos - 1, this));
371 mChunk->EnsureBufSize(pos);
372 memset(mChunk->BufForWriting() + mChunk->DataSize(), 0,
373 pos - mChunk->DataSize());
375 mChunk->UpdateDataSize(mChunk->DataSize(), pos - mChunk->DataSize(), false);
376 }
378 void
379 CacheFileOutputStream::NotifyListener()
380 {
381 mFile->AssertOwnsLock();
383 LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
385 MOZ_ASSERT(mCallback);
387 if (!mCallbackTarget)
388 mCallbackTarget = NS_GetCurrentThread();
390 nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
391 NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
393 mCallback = nullptr;
394 mCallbackTarget = nullptr;
396 asyncCallback->OnOutputStreamReady(this);
397 }
399 // Memory reporting
401 size_t
402 CacheFileOutputStream::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
403 {
404 // Everything the stream keeps a reference to is already reported somewhere else.
405 // mFile reports itself.
406 // mChunk reported as part of CacheFile.
407 // mCloseListener is CacheEntry, already reported.
408 // mCallback is usually CacheFile or a class that is reported elsewhere.
409 return mallocSizeOf(this);
410 }
412 } // net
413 } // mozilla