1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "InputStreamLengthWrapper.h"
8 #include "mozilla/ipc/InputStreamUtils.h"
9 #include "nsISeekableStream.h"
10 #include "nsStreamUtils.h"
11 
12 namespace mozilla {
13 
14 using namespace ipc;
15 
16 NS_IMPL_ADDREF(InputStreamLengthWrapper);
17 NS_IMPL_RELEASE(InputStreamLengthWrapper);
18 
19 NS_INTERFACE_MAP_BEGIN(InputStreamLengthWrapper)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)20   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
21   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
22                                      mWeakCloneableInputStream || !mInputStream)
23   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(
24       nsIIPCSerializableInputStream,
25       mWeakIPCSerializableInputStream || !mInputStream)
26   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
27                                      mWeakSeekableInputStream || !mInputStream)
28   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITellableStream,
29                                      mWeakTellableInputStream || !mInputStream)
30   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
31                                      mWeakAsyncInputStream || !mInputStream)
32   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
33                                      mWeakAsyncInputStream || !mInputStream)
34   NS_INTERFACE_MAP_ENTRY(nsIInputStreamLength)
35   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
36 NS_INTERFACE_MAP_END
37 
38 /* static */
39 already_AddRefed<nsIInputStream> InputStreamLengthWrapper::MaybeWrap(
40     already_AddRefed<nsIInputStream> aInputStream, int64_t aLength) {
41   nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
42   MOZ_ASSERT(inputStream);
43 
44   nsCOMPtr<nsIInputStreamLength> length = do_QueryInterface(inputStream);
45   if (length) {
46     return inputStream.forget();
47   }
48 
49   nsCOMPtr<nsIAsyncInputStreamLength> asyncLength =
50       do_QueryInterface(inputStream);
51   if (asyncLength) {
52     return inputStream.forget();
53   }
54 
55   nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(inputStream);
56   if (!asyncStream) {
57     return inputStream.forget();
58   }
59 
60   inputStream = new InputStreamLengthWrapper(inputStream.forget(), aLength);
61   return inputStream.forget();
62 }
63 
InputStreamLengthWrapper(already_AddRefed<nsIInputStream> aInputStream,int64_t aLength)64 InputStreamLengthWrapper::InputStreamLengthWrapper(
65     already_AddRefed<nsIInputStream> aInputStream, int64_t aLength)
66     : mWeakCloneableInputStream(nullptr),
67       mWeakIPCSerializableInputStream(nullptr),
68       mWeakSeekableInputStream(nullptr),
69       mWeakTellableInputStream(nullptr),
70       mWeakAsyncInputStream(nullptr),
71       mLength(aLength),
72       mConsumed(false),
73       mMutex("InputStreamLengthWrapper::mMutex") {
74   MOZ_ASSERT(mLength >= 0);
75 
76   nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
77   SetSourceStream(inputStream.forget());
78 }
79 
InputStreamLengthWrapper()80 InputStreamLengthWrapper::InputStreamLengthWrapper()
81     : mWeakCloneableInputStream(nullptr),
82       mWeakIPCSerializableInputStream(nullptr),
83       mWeakSeekableInputStream(nullptr),
84       mWeakTellableInputStream(nullptr),
85       mWeakAsyncInputStream(nullptr),
86       mLength(-1),
87       mConsumed(false),
88       mMutex("InputStreamLengthWrapper::mMutex") {}
89 
90 InputStreamLengthWrapper::~InputStreamLengthWrapper() = default;
91 
SetSourceStream(already_AddRefed<nsIInputStream> aInputStream)92 void InputStreamLengthWrapper::SetSourceStream(
93     already_AddRefed<nsIInputStream> aInputStream) {
94   MOZ_ASSERT(!mInputStream);
95 
96   mInputStream = std::move(aInputStream);
97 
98   nsCOMPtr<nsICloneableInputStream> cloneableStream =
99       do_QueryInterface(mInputStream);
100   if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
101     mWeakCloneableInputStream = cloneableStream;
102   }
103 
104   nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
105       do_QueryInterface(mInputStream);
106   if (serializableStream && SameCOMIdentity(mInputStream, serializableStream)) {
107     mWeakIPCSerializableInputStream = serializableStream;
108   }
109 
110   nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mInputStream);
111   if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
112     mWeakSeekableInputStream = seekableStream;
113   }
114 
115   nsCOMPtr<nsITellableStream> tellableStream = do_QueryInterface(mInputStream);
116   if (tellableStream && SameCOMIdentity(mInputStream, tellableStream)) {
117     mWeakTellableInputStream = tellableStream;
118   }
119 
120   nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
121       do_QueryInterface(mInputStream);
122   if (asyncInputStream && SameCOMIdentity(mInputStream, asyncInputStream)) {
123     mWeakAsyncInputStream = asyncInputStream;
124   }
125 }
126 
127 // nsIInputStream interface
128 
129 NS_IMETHODIMP
Close()130 InputStreamLengthWrapper::Close() {
131   NS_ENSURE_STATE(mInputStream);
132   return mInputStream->Close();
133 }
134 
135 NS_IMETHODIMP
Available(uint64_t * aLength)136 InputStreamLengthWrapper::Available(uint64_t* aLength) {
137   NS_ENSURE_STATE(mInputStream);
138   return mInputStream->Available(aLength);
139 }
140 
141 NS_IMETHODIMP
Read(char * aBuffer,uint32_t aCount,uint32_t * aReadCount)142 InputStreamLengthWrapper::Read(char* aBuffer, uint32_t aCount,
143                                uint32_t* aReadCount) {
144   NS_ENSURE_STATE(mInputStream);
145   mConsumed = true;
146   return mInputStream->Read(aBuffer, aCount, aReadCount);
147 }
148 
149 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun aWriter,void * aClosure,uint32_t aCount,uint32_t * aResult)150 InputStreamLengthWrapper::ReadSegments(nsWriteSegmentFun aWriter,
151                                        void* aClosure, uint32_t aCount,
152                                        uint32_t* aResult) {
153   return NS_ERROR_NOT_IMPLEMENTED;
154 }
155 
156 NS_IMETHODIMP
IsNonBlocking(bool * aNonBlocking)157 InputStreamLengthWrapper::IsNonBlocking(bool* aNonBlocking) {
158   NS_ENSURE_STATE(mInputStream);
159   return mInputStream->IsNonBlocking(aNonBlocking);
160 }
161 
162 // nsICloneableInputStream interface
163 
164 NS_IMETHODIMP
GetCloneable(bool * aCloneable)165 InputStreamLengthWrapper::GetCloneable(bool* aCloneable) {
166   NS_ENSURE_STATE(mInputStream);
167   NS_ENSURE_STATE(mWeakCloneableInputStream);
168   mWeakCloneableInputStream->GetCloneable(aCloneable);
169   return NS_OK;
170 }
171 
172 NS_IMETHODIMP
Clone(nsIInputStream ** aResult)173 InputStreamLengthWrapper::Clone(nsIInputStream** aResult) {
174   NS_ENSURE_STATE(mInputStream);
175   NS_ENSURE_STATE(mWeakCloneableInputStream);
176 
177   nsCOMPtr<nsIInputStream> clonedStream;
178   nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
179   if (NS_WARN_IF(NS_FAILED(rv))) {
180     return rv;
181   }
182 
183   nsCOMPtr<nsIInputStream> stream =
184       new InputStreamLengthWrapper(clonedStream.forget(), mLength);
185 
186   stream.forget(aResult);
187   return NS_OK;
188 }
189 
190 // nsIAsyncInputStream interface
191 
192 NS_IMETHODIMP
CloseWithStatus(nsresult aStatus)193 InputStreamLengthWrapper::CloseWithStatus(nsresult aStatus) {
194   NS_ENSURE_STATE(mInputStream);
195   NS_ENSURE_STATE(mWeakAsyncInputStream);
196 
197   mConsumed = true;
198   return mWeakAsyncInputStream->CloseWithStatus(aStatus);
199 }
200 
201 NS_IMETHODIMP
AsyncWait(nsIInputStreamCallback * aCallback,uint32_t aFlags,uint32_t aRequestedCount,nsIEventTarget * aEventTarget)202 InputStreamLengthWrapper::AsyncWait(nsIInputStreamCallback* aCallback,
203                                     uint32_t aFlags, uint32_t aRequestedCount,
204                                     nsIEventTarget* aEventTarget) {
205   NS_ENSURE_STATE(mInputStream);
206   NS_ENSURE_STATE(mWeakAsyncInputStream);
207 
208   nsCOMPtr<nsIInputStreamCallback> callback = this;
209   {
210     MutexAutoLock lock(mMutex);
211 
212     if (mAsyncWaitCallback && aCallback) {
213       return NS_ERROR_FAILURE;
214     }
215 
216     bool hadCallback = !!mAsyncWaitCallback;
217     mAsyncWaitCallback = aCallback;
218 
219     if (!mAsyncWaitCallback) {
220       if (!hadCallback) {
221         // No pending operation.
222         return NS_OK;
223       }
224 
225       // Abort current operation.
226       callback = nullptr;
227     }
228   }
229 
230   return mWeakAsyncInputStream->AsyncWait(callback, aFlags, aRequestedCount,
231                                           aEventTarget);
232 }
233 
234 // nsIInputStreamCallback
235 
236 NS_IMETHODIMP
OnInputStreamReady(nsIAsyncInputStream * aStream)237 InputStreamLengthWrapper::OnInputStreamReady(nsIAsyncInputStream* aStream) {
238   MOZ_ASSERT(mInputStream);
239   MOZ_ASSERT(mWeakAsyncInputStream);
240   MOZ_ASSERT(mWeakAsyncInputStream == aStream);
241 
242   nsCOMPtr<nsIInputStreamCallback> callback;
243   {
244     MutexAutoLock lock(mMutex);
245     // We have been canceled in the meanwhile.
246     if (!mAsyncWaitCallback) {
247       return NS_OK;
248     }
249 
250     callback.swap(mAsyncWaitCallback);
251   }
252 
253   MOZ_ASSERT(callback);
254   return callback->OnInputStreamReady(this);
255 }
256 
257 // nsIIPCSerializableInputStream
258 
Serialize(mozilla::ipc::InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,mozilla::ipc::ParentToChildStreamActorManager * aManager)259 void InputStreamLengthWrapper::Serialize(
260     mozilla::ipc::InputStreamParams& aParams,
261     FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
262     uint32_t aMaxSize, uint32_t* aSizeUsed,
263     mozilla::ipc::ParentToChildStreamActorManager* aManager) {
264   SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
265                     aSizeUsed, aManager);
266 }
267 
Serialize(mozilla::ipc::InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,mozilla::ipc::ChildToParentStreamActorManager * aManager)268 void InputStreamLengthWrapper::Serialize(
269     mozilla::ipc::InputStreamParams& aParams,
270     FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
271     uint32_t aMaxSize, uint32_t* aSizeUsed,
272     mozilla::ipc::ChildToParentStreamActorManager* aManager) {
273   SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
274                     aSizeUsed, aManager);
275 }
276 
277 template <typename M>
SerializeInternal(mozilla::ipc::InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,M * aManager)278 void InputStreamLengthWrapper::SerializeInternal(
279     mozilla::ipc::InputStreamParams& aParams,
280     FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
281     uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) {
282   MOZ_ASSERT(mInputStream);
283   MOZ_ASSERT(mWeakIPCSerializableInputStream);
284 
285   InputStreamLengthWrapperParams params;
286   InputStreamHelper::SerializeInputStream(mInputStream, params.stream(),
287                                           aFileDescriptors, aDelayedStart,
288                                           aMaxSize, aSizeUsed, aManager);
289   params.length() = mLength;
290   params.consumed() = mConsumed;
291 
292   aParams = params;
293 }
294 
Deserialize(const mozilla::ipc::InputStreamParams & aParams,const FileDescriptorArray & aFileDescriptors)295 bool InputStreamLengthWrapper::Deserialize(
296     const mozilla::ipc::InputStreamParams& aParams,
297     const FileDescriptorArray& aFileDescriptors) {
298   MOZ_ASSERT(!mInputStream);
299   MOZ_ASSERT(!mWeakIPCSerializableInputStream);
300 
301   if (aParams.type() != InputStreamParams::TInputStreamLengthWrapperParams) {
302     NS_ERROR("Received unknown parameters from the other process!");
303     return false;
304   }
305 
306   const InputStreamLengthWrapperParams& params =
307       aParams.get_InputStreamLengthWrapperParams();
308 
309   nsCOMPtr<nsIInputStream> stream = InputStreamHelper::DeserializeInputStream(
310       params.stream(), aFileDescriptors);
311   if (!stream) {
312     NS_WARNING("Deserialize failed!");
313     return false;
314   }
315 
316   SetSourceStream(stream.forget());
317 
318   mLength = params.length();
319   mConsumed = params.consumed();
320 
321   return true;
322 }
323 
324 // nsISeekableStream
325 
326 NS_IMETHODIMP
Seek(int32_t aWhence,int64_t aOffset)327 InputStreamLengthWrapper::Seek(int32_t aWhence, int64_t aOffset) {
328   NS_ENSURE_STATE(mInputStream);
329   NS_ENSURE_STATE(mWeakSeekableInputStream);
330 
331   mConsumed = true;
332   return mWeakSeekableInputStream->Seek(aWhence, aOffset);
333 }
334 
335 NS_IMETHODIMP
SetEOF()336 InputStreamLengthWrapper::SetEOF() {
337   NS_ENSURE_STATE(mInputStream);
338   NS_ENSURE_STATE(mWeakSeekableInputStream);
339 
340   mConsumed = true;
341   return mWeakSeekableInputStream->SetEOF();
342 }
343 
344 // nsITellableStream
345 
346 NS_IMETHODIMP
Tell(int64_t * aResult)347 InputStreamLengthWrapper::Tell(int64_t* aResult) {
348   NS_ENSURE_STATE(mInputStream);
349   NS_ENSURE_STATE(mWeakTellableInputStream);
350 
351   return mWeakTellableInputStream->Tell(aResult);
352 }
353 
354 // nsIInputStreamLength
355 
356 NS_IMETHODIMP
Length(int64_t * aLength)357 InputStreamLengthWrapper::Length(int64_t* aLength) {
358   NS_ENSURE_STATE(mInputStream);
359   *aLength = mLength;
360   return NS_OK;
361 }
362 
363 }  // namespace mozilla
364