1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "PartiallySeekableInputStream.h"
7 #include "mozilla/ipc/InputStreamUtils.h"
8 #include "nsISeekableStream.h"
9 #include "nsStreamUtils.h"
10
11 namespace mozilla {
12 namespace net {
13
14 NS_IMPL_ADDREF(PartiallySeekableInputStream);
15 NS_IMPL_RELEASE(PartiallySeekableInputStream);
16
17 NS_INTERFACE_MAP_BEGIN(PartiallySeekableInputStream)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)18 NS_INTERFACE_MAP_ENTRY(nsIInputStream)
19 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
20 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
21 mWeakCloneableInputStream)
22 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
23 mWeakIPCSerializableInputStream)
24 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, mWeakAsyncInputStream)
25 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
26 mWeakAsyncInputStream)
27 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
28 NS_INTERFACE_MAP_END
29
30 PartiallySeekableInputStream::PartiallySeekableInputStream(
31 already_AddRefed<nsIInputStream> aInputStream, uint64_t aBufferSize)
32 : mInputStream(Move(aInputStream)),
33 mWeakCloneableInputStream(nullptr),
34 mWeakIPCSerializableInputStream(nullptr),
35 mWeakAsyncInputStream(nullptr),
36 mBufferSize(aBufferSize),
37 mPos(0),
38 mClosed(false) {
39 Init();
40 }
41
PartiallySeekableInputStream(already_AddRefed<nsIInputStream> aClonedBaseStream,PartiallySeekableInputStream * aClonedFrom)42 PartiallySeekableInputStream::PartiallySeekableInputStream(
43 already_AddRefed<nsIInputStream> aClonedBaseStream,
44 PartiallySeekableInputStream* aClonedFrom)
45 : mInputStream(Move(aClonedBaseStream)),
46 mWeakCloneableInputStream(nullptr),
47 mWeakIPCSerializableInputStream(nullptr),
48 mWeakAsyncInputStream(nullptr),
49 mCachedBuffer(aClonedFrom->mCachedBuffer),
50 mBufferSize(aClonedFrom->mBufferSize),
51 mPos(aClonedFrom->mPos),
52 mClosed(aClonedFrom->mClosed) {
53 Init();
54 }
55
Init()56 void PartiallySeekableInputStream::Init() {
57 MOZ_ASSERT(mInputStream);
58
59 #ifdef DEBUG
60 nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mInputStream);
61 MOZ_ASSERT(!seekableStream);
62 #endif
63
64 nsCOMPtr<nsICloneableInputStream> cloneableStream =
65 do_QueryInterface(mInputStream);
66 if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
67 mWeakCloneableInputStream = cloneableStream;
68 }
69
70 nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
71 do_QueryInterface(mInputStream);
72 if (serializableStream && SameCOMIdentity(mInputStream, serializableStream)) {
73 mWeakIPCSerializableInputStream = serializableStream;
74 }
75
76 nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
77 do_QueryInterface(mInputStream);
78 if (asyncInputStream && SameCOMIdentity(mInputStream, asyncInputStream)) {
79 mWeakAsyncInputStream = asyncInputStream;
80 }
81 }
82
83 NS_IMETHODIMP
Close()84 PartiallySeekableInputStream::Close() {
85 mInputStream->Close();
86 mCachedBuffer.Clear();
87 mPos = 0;
88 mClosed = true;
89 return NS_OK;
90 }
91
92 // nsIInputStream interface
93
94 NS_IMETHODIMP
Available(uint64_t * aLength)95 PartiallySeekableInputStream::Available(uint64_t* aLength) {
96 if (mClosed) {
97 return NS_BASE_STREAM_CLOSED;
98 }
99
100 nsresult rv = mInputStream->Available(aLength);
101 if (NS_WARN_IF(NS_FAILED(rv))) {
102 return rv;
103 }
104
105 if (mPos < mCachedBuffer.Length()) {
106 *aLength += mCachedBuffer.Length() - mPos;
107 }
108
109 return NS_OK;
110 }
111
112 NS_IMETHODIMP
Read(char * aBuffer,uint32_t aCount,uint32_t * aReadCount)113 PartiallySeekableInputStream::Read(char* aBuffer, uint32_t aCount,
114 uint32_t* aReadCount) {
115 *aReadCount = 0;
116
117 if (mClosed) {
118 return NS_OK;
119 }
120
121 uint32_t byteRead = 0;
122
123 if (mPos < mCachedBuffer.Length()) {
124 // We are reading from the cached buffer.
125 byteRead = XPCOM_MIN(mCachedBuffer.Length() - mPos, (uint64_t)aCount);
126 memcpy(aBuffer, mCachedBuffer.Elements() + mPos, byteRead);
127 *aReadCount = byteRead;
128 mPos += byteRead;
129 }
130
131 if (byteRead < aCount) {
132 MOZ_ASSERT(mPos >= mCachedBuffer.Length());
133 MOZ_ASSERT_IF(mPos > mCachedBuffer.Length(),
134 mCachedBuffer.Length() == mBufferSize);
135
136 // We can read from the stream.
137 uint32_t byteWritten;
138 nsresult rv =
139 mInputStream->Read(aBuffer + byteRead, aCount - byteRead, &byteWritten);
140 if (NS_WARN_IF(NS_FAILED(rv)) || byteWritten == 0) {
141 return rv;
142 }
143
144 *aReadCount += byteWritten;
145
146 // Maybe we have to cache something.
147 if (mPos < mBufferSize) {
148 uint32_t size = XPCOM_MIN(mPos + byteWritten, mBufferSize);
149 mCachedBuffer.SetLength(size);
150 memcpy(mCachedBuffer.Elements() + mPos, aBuffer + byteRead, size - mPos);
151 }
152
153 mPos += byteWritten;
154 }
155
156 return NS_OK;
157 }
158
159 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun aWriter,void * aClosure,uint32_t aCount,uint32_t * aResult)160 PartiallySeekableInputStream::ReadSegments(nsWriteSegmentFun aWriter,
161 void* aClosure, uint32_t aCount,
162 uint32_t* aResult) {
163 return NS_ERROR_NOT_IMPLEMENTED;
164 }
165
166 NS_IMETHODIMP
IsNonBlocking(bool * aNonBlocking)167 PartiallySeekableInputStream::IsNonBlocking(bool* aNonBlocking) {
168 return mInputStream->IsNonBlocking(aNonBlocking);
169 }
170
171 // nsICloneableInputStream interface
172
173 NS_IMETHODIMP
GetCloneable(bool * aCloneable)174 PartiallySeekableInputStream::GetCloneable(bool* aCloneable) {
175 NS_ENSURE_STATE(mWeakCloneableInputStream);
176
177 return mWeakCloneableInputStream->GetCloneable(aCloneable);
178 }
179
180 NS_IMETHODIMP
Clone(nsIInputStream ** aResult)181 PartiallySeekableInputStream::Clone(nsIInputStream** aResult) {
182 NS_ENSURE_STATE(mWeakCloneableInputStream);
183
184 nsCOMPtr<nsIInputStream> clonedStream;
185 nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
186 if (NS_WARN_IF(NS_FAILED(rv))) {
187 return rv;
188 }
189
190 nsCOMPtr<nsIInputStream> stream =
191 new PartiallySeekableInputStream(clonedStream.forget(), this);
192
193 stream.forget(aResult);
194 return NS_OK;
195 }
196
197 // nsIAsyncInputStream interface
198
199 NS_IMETHODIMP
CloseWithStatus(nsresult aStatus)200 PartiallySeekableInputStream::CloseWithStatus(nsresult aStatus) {
201 NS_ENSURE_STATE(mWeakAsyncInputStream);
202
203 return mWeakAsyncInputStream->CloseWithStatus(aStatus);
204 }
205
206 NS_IMETHODIMP
AsyncWait(nsIInputStreamCallback * aCallback,uint32_t aFlags,uint32_t aRequestedCount,nsIEventTarget * aEventTarget)207 PartiallySeekableInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
208 uint32_t aFlags,
209 uint32_t aRequestedCount,
210 nsIEventTarget* aEventTarget) {
211 if (mClosed) {
212 return NS_BASE_STREAM_CLOSED;
213 }
214
215 NS_ENSURE_STATE(mWeakAsyncInputStream);
216
217 if (mAsyncWaitCallback && aCallback) {
218 return NS_ERROR_FAILURE;
219 }
220
221 mAsyncWaitCallback = aCallback;
222
223 if (!mAsyncWaitCallback) {
224 return NS_OK;
225 }
226
227 return mWeakAsyncInputStream->AsyncWait(this, aFlags, aRequestedCount,
228 aEventTarget);
229 }
230
231 // nsIInputStreamCallback
232
233 NS_IMETHODIMP
OnInputStreamReady(nsIAsyncInputStream * aStream)234 PartiallySeekableInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream) {
235 MOZ_ASSERT(mWeakAsyncInputStream);
236 MOZ_ASSERT(mWeakAsyncInputStream == aStream);
237
238 // We have been canceled in the meanwhile.
239 if (!mAsyncWaitCallback) {
240 return NS_OK;
241 }
242
243 nsCOMPtr<nsIInputStreamCallback> callback = mAsyncWaitCallback;
244
245 mAsyncWaitCallback = nullptr;
246
247 return callback->OnInputStreamReady(this);
248 }
249
250 // nsIIPCSerializableInputStream
251
Serialize(mozilla::ipc::InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors)252 void PartiallySeekableInputStream::Serialize(
253 mozilla::ipc::InputStreamParams& aParams,
254 FileDescriptorArray& aFileDescriptors) {
255 MOZ_ASSERT(mWeakIPCSerializableInputStream);
256 MOZ_DIAGNOSTIC_ASSERT(mCachedBuffer.IsEmpty());
257 mozilla::ipc::InputStreamHelper::SerializeInputStream(mInputStream, aParams,
258 aFileDescriptors);
259 }
260
Deserialize(const mozilla::ipc::InputStreamParams & aParams,const FileDescriptorArray & aFileDescriptors)261 bool PartiallySeekableInputStream::Deserialize(
262 const mozilla::ipc::InputStreamParams& aParams,
263 const FileDescriptorArray& aFileDescriptors) {
264 MOZ_CRASH("This method should never be called!");
265 return false;
266 }
267
268 mozilla::Maybe<uint64_t>
ExpectedSerializedLength()269 PartiallySeekableInputStream::ExpectedSerializedLength() {
270 if (!mWeakIPCSerializableInputStream) {
271 return mozilla::Nothing();
272 }
273
274 return mWeakIPCSerializableInputStream->ExpectedSerializedLength();
275 }
276
277 // nsISeekableStream
278
279 NS_IMETHODIMP
Seek(int32_t aWhence,int64_t aOffset)280 PartiallySeekableInputStream::Seek(int32_t aWhence, int64_t aOffset) {
281 if (mClosed) {
282 return NS_BASE_STREAM_CLOSED;
283 }
284
285 int64_t offset;
286
287 switch (aWhence) {
288 case NS_SEEK_SET:
289 offset = aOffset;
290 break;
291 case NS_SEEK_CUR:
292 offset = mPos + aOffset;
293 break;
294 case NS_SEEK_END: {
295 return NS_ERROR_NOT_IMPLEMENTED;
296 }
297 default:
298 return NS_ERROR_ILLEGAL_VALUE;
299 }
300
301 if (offset < 0) {
302 return NS_ERROR_ILLEGAL_VALUE;
303 }
304
305 if ((uint64_t)offset >= mCachedBuffer.Length() || mPos > mBufferSize) {
306 return NS_ERROR_NOT_IMPLEMENTED;
307 }
308
309 mPos = offset;
310 return NS_OK;
311 }
312
313 NS_IMETHODIMP
Tell(int64_t * aResult)314 PartiallySeekableInputStream::Tell(int64_t* aResult) {
315 if (mClosed) {
316 return NS_BASE_STREAM_CLOSED;
317 }
318
319 *aResult = mPos;
320 return NS_OK;
321 }
322
323 NS_IMETHODIMP
SetEOF()324 PartiallySeekableInputStream::SetEOF() { return Close(); }
325
326 } // namespace net
327 } // namespace mozilla
328