Line data Source code
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 "IPCBlobInputStream.h"
8 : #include "IPCBlobInputStreamChild.h"
9 : #include "IPCBlobInputStreamStorage.h"
10 : #include "mozilla/ipc/InputStreamParams.h"
11 : #include "nsIAsyncInputStream.h"
12 :
13 : namespace mozilla {
14 : namespace dom {
15 :
16 : namespace {
17 :
18 0 : class CallbackRunnable final : public CancelableRunnable
19 : {
20 : public:
21 : static void
22 0 : Execute(nsIInputStreamCallback* aCallback,
23 : nsIEventTarget* aEventTarget,
24 : IPCBlobInputStream* aStream)
25 : {
26 : RefPtr<CallbackRunnable> runnable =
27 0 : new CallbackRunnable(aCallback, aStream);
28 :
29 0 : nsCOMPtr<nsIEventTarget> target = aEventTarget;
30 0 : if (!target) {
31 0 : target = NS_GetCurrentThread();
32 : }
33 :
34 0 : target->Dispatch(runnable, NS_DISPATCH_NORMAL);
35 0 : }
36 :
37 : NS_IMETHOD
38 0 : Run() override
39 : {
40 0 : mCallback->OnInputStreamReady(mStream);
41 0 : mCallback = nullptr;
42 0 : mStream = nullptr;
43 0 : return NS_OK;
44 : }
45 :
46 : private:
47 0 : CallbackRunnable(nsIInputStreamCallback* aCallback,
48 : IPCBlobInputStream* aStream)
49 0 : : CancelableRunnable("dom::CallbackRunnable")
50 : , mCallback(aCallback)
51 0 : , mStream(aStream)
52 : {
53 0 : MOZ_ASSERT(mCallback);
54 0 : MOZ_ASSERT(mStream);
55 0 : }
56 :
57 : nsCOMPtr<nsIInputStreamCallback> mCallback;
58 : RefPtr<IPCBlobInputStream> mStream;
59 : };
60 :
61 : } // anonymous
62 :
63 0 : NS_IMPL_ADDREF(IPCBlobInputStream);
64 0 : NS_IMPL_RELEASE(IPCBlobInputStream);
65 :
66 0 : NS_INTERFACE_MAP_BEGIN(IPCBlobInputStream)
67 0 : NS_INTERFACE_MAP_ENTRY(nsIInputStream)
68 0 : NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
69 0 : NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
70 0 : NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
71 0 : NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
72 0 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
73 0 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileMetadata, IsFileMetadata())
74 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
75 0 : NS_INTERFACE_MAP_END
76 :
77 0 : IPCBlobInputStream::IPCBlobInputStream(IPCBlobInputStreamChild* aActor)
78 : : mActor(aActor)
79 0 : , mState(eInit)
80 : {
81 0 : MOZ_ASSERT(aActor);
82 :
83 0 : if (XRE_IsParentProcess()) {
84 0 : nsCOMPtr<nsIInputStream> stream;
85 0 : IPCBlobInputStreamStorage::Get()->GetStream(mActor->ID(),
86 0 : getter_AddRefs(stream));
87 0 : if (stream) {
88 0 : mState = eRunning;
89 0 : mRemoteStream = stream;
90 : }
91 : }
92 0 : }
93 :
94 0 : IPCBlobInputStream::~IPCBlobInputStream()
95 : {
96 0 : Close();
97 0 : }
98 :
99 : // nsIInputStream interface
100 :
101 : NS_IMETHODIMP
102 0 : IPCBlobInputStream::Available(uint64_t* aLength)
103 : {
104 : // We don't have a remoteStream yet. Let's return the full known size.
105 0 : if (mState == eInit || mState == ePending) {
106 0 : *aLength = mActor->Size();
107 0 : return NS_OK;
108 : }
109 :
110 0 : if (mState == eRunning) {
111 0 : MOZ_ASSERT(mRemoteStream);
112 0 : return mRemoteStream->Available(aLength);
113 : }
114 :
115 0 : MOZ_ASSERT(mState == eClosed);
116 0 : return NS_BASE_STREAM_CLOSED;
117 : }
118 :
119 : NS_IMETHODIMP
120 0 : IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
121 : {
122 : // Read is not available is we don't have a remoteStream.
123 0 : if (mState == eInit || mState == ePending) {
124 0 : return NS_BASE_STREAM_WOULD_BLOCK;
125 : }
126 :
127 0 : if (mState == eRunning) {
128 0 : return mRemoteStream->Read(aBuffer, aCount, aReadCount);
129 : }
130 :
131 0 : MOZ_ASSERT(mState == eClosed);
132 0 : return NS_BASE_STREAM_CLOSED;
133 : }
134 :
135 : NS_IMETHODIMP
136 0 : IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
137 : uint32_t aCount, uint32_t *aResult)
138 : {
139 : // ReadSegments is not available is we don't have a remoteStream.
140 0 : if (mState == eInit || mState == ePending) {
141 0 : return NS_BASE_STREAM_WOULD_BLOCK;
142 : }
143 :
144 0 : if (mState == eRunning) {
145 0 : return mRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
146 : }
147 :
148 0 : MOZ_ASSERT(mState == eClosed);
149 0 : return NS_BASE_STREAM_CLOSED;
150 : }
151 :
152 : NS_IMETHODIMP
153 0 : IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
154 : {
155 0 : *aNonBlocking = true;
156 0 : return NS_OK;
157 : }
158 :
159 : NS_IMETHODIMP
160 0 : IPCBlobInputStream::Close()
161 : {
162 0 : if (mActor) {
163 0 : mActor->ForgetStream(this);
164 0 : mActor = nullptr;
165 : }
166 :
167 0 : if (mRemoteStream) {
168 0 : mRemoteStream->Close();
169 0 : mRemoteStream = nullptr;
170 : }
171 :
172 0 : mCallback = nullptr;
173 :
174 0 : mState = eClosed;
175 0 : return NS_OK;
176 : }
177 :
178 : // nsICloneableInputStream interface
179 :
180 : NS_IMETHODIMP
181 0 : IPCBlobInputStream::GetCloneable(bool* aCloneable)
182 : {
183 0 : *aCloneable = mState != eClosed;
184 0 : return NS_OK;
185 : }
186 :
187 : NS_IMETHODIMP
188 0 : IPCBlobInputStream::Clone(nsIInputStream** aResult)
189 : {
190 0 : if (mState == eClosed) {
191 0 : return NS_BASE_STREAM_CLOSED;
192 : }
193 :
194 0 : MOZ_ASSERT(mActor);
195 :
196 0 : nsCOMPtr<nsIInputStream> stream = mActor->CreateStream();
197 0 : if (!stream) {
198 0 : return NS_ERROR_FAILURE;
199 : }
200 :
201 0 : stream.forget(aResult);
202 0 : return NS_OK;
203 : }
204 :
205 : // nsIAsyncInputStream interface
206 :
207 : NS_IMETHODIMP
208 0 : IPCBlobInputStream::CloseWithStatus(nsresult aStatus)
209 : {
210 0 : return Close();
211 : }
212 :
213 : NS_IMETHODIMP
214 0 : IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
215 : uint32_t aFlags, uint32_t aRequestedCount,
216 : nsIEventTarget* aEventTarget)
217 : {
218 : // See IPCBlobInputStream.h for more information about this state machine.
219 :
220 0 : switch (mState) {
221 : // First call, we need to retrieve the stream from the parent actor.
222 : case eInit:
223 0 : MOZ_ASSERT(mActor);
224 :
225 0 : mCallback = aCallback;
226 0 : mCallbackEventTarget = aEventTarget;
227 0 : mState = ePending;
228 :
229 0 : mActor->StreamNeeded(this, aEventTarget);
230 0 : return NS_OK;
231 :
232 : // We are still waiting for the remote inputStream
233 : case ePending:
234 0 : if (mCallback && aCallback) {
235 0 : return NS_ERROR_FAILURE;
236 : }
237 :
238 0 : mCallback = aCallback;
239 0 : mCallbackEventTarget = aEventTarget;
240 0 : return NS_OK;
241 :
242 : // We have the remote inputStream, let's check if we can execute the callback.
243 : case eRunning:
244 0 : return MaybeExecuteCallback(aCallback, aEventTarget);
245 :
246 : // Stream is closed.
247 : default:
248 0 : MOZ_ASSERT(mState == eClosed);
249 0 : return NS_BASE_STREAM_CLOSED;
250 : }
251 : }
252 :
253 : void
254 0 : IPCBlobInputStream::StreamReady(nsIInputStream* aInputStream)
255 : {
256 : // We have been closed in the meantime.
257 0 : if (mState == eClosed) {
258 0 : if (aInputStream) {
259 0 : aInputStream->Close();
260 : }
261 0 : return;
262 : }
263 :
264 : // If aInputStream is null, it means that the serialization went wrong or the
265 : // stream is not available anymore. We keep the state as pending just to block
266 : // any additional operation.
267 :
268 0 : nsCOMPtr<nsIInputStreamCallback> callback;
269 0 : callback.swap(mCallback);
270 :
271 0 : nsCOMPtr<nsIEventTarget> callbackEventTarget;
272 0 : callbackEventTarget.swap(mCallbackEventTarget);
273 :
274 0 : if (aInputStream && callback) {
275 0 : MOZ_ASSERT(mState == ePending);
276 :
277 0 : mRemoteStream = aInputStream;
278 0 : mState = eRunning;
279 :
280 0 : MaybeExecuteCallback(callback, callbackEventTarget);
281 : }
282 : }
283 :
284 : nsresult
285 0 : IPCBlobInputStream::MaybeExecuteCallback(nsIInputStreamCallback* aCallback,
286 : nsIEventTarget* aCallbackEventTarget)
287 : {
288 0 : MOZ_ASSERT(mState == eRunning);
289 0 : MOZ_ASSERT(mRemoteStream);
290 :
291 : // If the stream supports nsIAsyncInputStream, we need to call its AsyncWait
292 : // and wait for OnInputStreamReady.
293 0 : nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mRemoteStream);
294 0 : if (asyncStream) {
295 : // If the callback has been already set, we return an error.
296 0 : if (mCallback && aCallback) {
297 0 : return NS_ERROR_FAILURE;
298 : }
299 :
300 0 : mCallback = aCallback;
301 0 : mCallbackEventTarget = aCallbackEventTarget;
302 :
303 0 : if (!mCallback) {
304 0 : return NS_OK;
305 : }
306 :
307 0 : RefPtr<nsIEventTarget> target = GetCurrentThreadEventTarget();
308 0 : return asyncStream->AsyncWait(this, 0, 0, target);
309 : }
310 :
311 0 : MOZ_ASSERT(!mCallback);
312 0 : MOZ_ASSERT(!mCallbackEventTarget);
313 :
314 0 : if (!aCallback) {
315 0 : return NS_OK;
316 : }
317 :
318 0 : CallbackRunnable::Execute(aCallback, aCallbackEventTarget, this);
319 0 : return NS_OK;
320 : }
321 :
322 : // nsIInputStreamCallback
323 :
324 : NS_IMETHODIMP
325 0 : IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
326 : {
327 : // We have been closed in the meantime.
328 0 : if (mState == eClosed) {
329 0 : return NS_OK;
330 : }
331 :
332 0 : MOZ_ASSERT(mState == eRunning);
333 0 : MOZ_ASSERT(mRemoteStream == aStream);
334 :
335 : // The callback has been canceled in the meantime.
336 0 : if (!mCallback) {
337 0 : return NS_OK;
338 : }
339 :
340 0 : CallbackRunnable::Execute(mCallback, mCallbackEventTarget, this);
341 :
342 0 : mCallback = nullptr;
343 0 : mCallbackEventTarget = nullptr;
344 :
345 0 : return NS_OK;
346 : }
347 :
348 : // nsIIPCSerializableInputStream
349 :
350 : void
351 0 : IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
352 : FileDescriptorArray& aFileDescriptors)
353 : {
354 0 : mozilla::ipc::IPCBlobInputStreamParams params;
355 0 : params.id() = mActor->ID();
356 :
357 0 : aParams = params;
358 0 : }
359 :
360 : bool
361 0 : IPCBlobInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
362 : const FileDescriptorArray& aFileDescriptors)
363 : {
364 0 : MOZ_CRASH("This should never be called.");
365 : return false;
366 : }
367 :
368 : mozilla::Maybe<uint64_t>
369 0 : IPCBlobInputStream::ExpectedSerializedLength()
370 : {
371 0 : return mozilla::Nothing();
372 : }
373 :
374 : // nsIFileMetadata
375 :
376 : bool
377 0 : IPCBlobInputStream::IsFileMetadata() const
378 : {
379 : // We are nsIFileMetadata only if we have the remote stream and that is a
380 : // nsIFileMetadata.
381 0 : nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
382 0 : return !!fileMetadata;
383 : }
384 :
385 : NS_IMETHODIMP
386 0 : IPCBlobInputStream::GetSize(int64_t* aRetval)
387 : {
388 0 : nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
389 0 : if (!fileMetadata) {
390 0 : return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
391 : }
392 :
393 0 : return fileMetadata->GetSize(aRetval);
394 : }
395 :
396 : NS_IMETHODIMP
397 0 : IPCBlobInputStream::GetLastModified(int64_t* aRetval)
398 : {
399 0 : nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
400 0 : if (!fileMetadata) {
401 0 : return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
402 : }
403 :
404 0 : return fileMetadata->GetLastModified(aRetval);
405 : }
406 :
407 : NS_IMETHODIMP
408 0 : IPCBlobInputStream::GetFileDescriptor(PRFileDesc** aRetval)
409 : {
410 0 : nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
411 0 : if (!fileMetadata) {
412 0 : return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
413 : }
414 :
415 0 : return fileMetadata->GetFileDescriptor(aRetval);
416 : }
417 :
418 : // nsISeekableStream
419 :
420 : bool
421 0 : IPCBlobInputStream::IsSeekableStream() const
422 : {
423 : // We are nsISeekableStream only if we have the remote stream and that is a
424 : // nsISeekableStream.
425 0 : nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mRemoteStream);
426 0 : return !!seekableStream;
427 : }
428 :
429 : NS_IMETHODIMP
430 0 : IPCBlobInputStream::Seek(int32_t aWhence, int64_t aOffset)
431 : {
432 0 : nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mRemoteStream);
433 0 : if (!seekableStream) {
434 0 : return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
435 : }
436 :
437 0 : return seekableStream->Seek(aWhence, aOffset);
438 : }
439 :
440 : NS_IMETHODIMP
441 0 : IPCBlobInputStream::Tell(int64_t *aResult)
442 : {
443 0 : nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mRemoteStream);
444 0 : if (!seekableStream) {
445 0 : return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
446 : }
447 :
448 0 : return seekableStream->Tell(aResult);
449 : }
450 :
451 : NS_IMETHODIMP
452 0 : IPCBlobInputStream::SetEOF()
453 : {
454 : // This is a read-only stream.
455 0 : return NS_ERROR_FAILURE;
456 : }
457 :
458 : } // namespace dom
459 : } // namespace mozilla
|