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