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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "FileSnapshot.h"
8 
9 #include "IDBFileHandle.h"
10 #include "mozilla/Assertions.h"
11 #include "nsIIPCSerializableInputStream.h"
12 
13 namespace mozilla {
14 namespace dom {
15 namespace indexedDB {
16 
17 using namespace mozilla::ipc;
18 
19 namespace {
20 
21 class StreamWrapper final
22   : public nsIInputStream
23   , public nsIIPCSerializableInputStream
24 {
25   class CloseRunnable;
26 
27   nsCOMPtr<nsIEventTarget> mOwningThread;
28   nsCOMPtr<nsIInputStream> mInputStream;
29   RefPtr<IDBFileHandle> mFileHandle;
30   bool mFinished;
31 
32 public:
StreamWrapper(nsIInputStream * aInputStream,IDBFileHandle * aFileHandle)33   StreamWrapper(nsIInputStream* aInputStream,
34                 IDBFileHandle* aFileHandle)
35     : mOwningThread(NS_GetCurrentThread())
36     , mInputStream(aInputStream)
37     , mFileHandle(aFileHandle)
38     , mFinished(false)
39   {
40     AssertIsOnOwningThread();
41     MOZ_ASSERT(aInputStream);
42     MOZ_ASSERT(aFileHandle);
43     aFileHandle->AssertIsOnOwningThread();
44 
45     mFileHandle->OnNewRequest();
46   }
47 
48 private:
49   virtual ~StreamWrapper();
50 
51   bool
IsOnOwningThread() const52   IsOnOwningThread() const
53   {
54     MOZ_ASSERT(mOwningThread);
55 
56     bool current;
57     return NS_SUCCEEDED(mOwningThread->
58                         IsOnCurrentThread(&current)) && current;
59   }
60 
61   void
AssertIsOnOwningThread() const62   AssertIsOnOwningThread() const
63   {
64     MOZ_ASSERT(IsOnOwningThread());
65   }
66 
67   void
Finish()68   Finish()
69   {
70     AssertIsOnOwningThread();
71 
72     if (mFinished) {
73       return;
74     }
75 
76     mFinished = true;
77 
78     mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true);
79   }
80 
81   void
Destroy()82   Destroy()
83   {
84     if (IsOnOwningThread()) {
85       delete this;
86       return;
87     }
88 
89     nsCOMPtr<nsIRunnable> destroyRunnable =
90       NewNonOwningRunnableMethod(this, &StreamWrapper::Destroy);
91 
92     MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(destroyRunnable,
93                                                 NS_DISPATCH_NORMAL));
94   }
95 
96   NS_DECL_THREADSAFE_ISUPPORTS
97   NS_DECL_NSIINPUTSTREAM
98   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
99 };
100 
101 class StreamWrapper::CloseRunnable final
102   : public Runnable
103 {
104   friend class StreamWrapper;
105 
106   RefPtr<StreamWrapper> mStreamWrapper;
107 
108 public:
109   NS_DECL_ISUPPORTS_INHERITED
110 
111 private:
112   explicit
CloseRunnable(StreamWrapper * aStreamWrapper)113   CloseRunnable(StreamWrapper* aStreamWrapper)
114     : mStreamWrapper(aStreamWrapper)
115   { }
116 
~CloseRunnable()117   ~CloseRunnable()
118   { }
119 
120   NS_IMETHOD
121   Run() override;
122 };
123 
124 } // anonymous namespace
125 
BlobImplSnapshot(BlobImpl * aFileImpl,IDBFileHandle * aFileHandle)126 BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
127                                    IDBFileHandle* aFileHandle)
128   : mBlobImpl(aFileImpl)
129 {
130   MOZ_ASSERT(aFileImpl);
131   MOZ_ASSERT(aFileHandle);
132 
133   mFileHandle =
134     do_GetWeakReference(NS_ISUPPORTS_CAST(EventTarget*, aFileHandle));
135 }
136 
BlobImplSnapshot(BlobImpl * aFileImpl,nsIWeakReference * aFileHandle)137 BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
138                                    nsIWeakReference* aFileHandle)
139   : mBlobImpl(aFileImpl)
140   , mFileHandle(aFileHandle)
141 {
142   MOZ_ASSERT(aFileImpl);
143   MOZ_ASSERT(aFileHandle);
144 }
145 
~BlobImplSnapshot()146 BlobImplSnapshot::~BlobImplSnapshot()
147 {
148 }
149 
NS_IMPL_ISUPPORTS_INHERITED(BlobImplSnapshot,BlobImpl,PIBlobImplSnapshot)150 NS_IMPL_ISUPPORTS_INHERITED(BlobImplSnapshot, BlobImpl, PIBlobImplSnapshot)
151 
152 already_AddRefed<BlobImpl>
153 BlobImplSnapshot::CreateSlice(uint64_t aStart,
154                               uint64_t aLength,
155                               const nsAString& aContentType,
156                               ErrorResult& aRv)
157 {
158   RefPtr<BlobImpl> blobImpl =
159     mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
160 
161   if (NS_WARN_IF(aRv.Failed())) {
162     return nullptr;
163   }
164 
165   blobImpl = new BlobImplSnapshot(blobImpl, mFileHandle);
166   return blobImpl.forget();
167 }
168 
169 void
GetInternalStream(nsIInputStream ** aStream,ErrorResult & aRv)170 BlobImplSnapshot::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
171 {
172   nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
173   RefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
174   if (!fileHandle || !fileHandle->IsOpen()) {
175     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
176     return;
177   }
178 
179   nsCOMPtr<nsIInputStream> stream;
180   mBlobImpl->GetInternalStream(getter_AddRefs(stream), aRv);
181   if (NS_WARN_IF(aRv.Failed())) {
182     return;
183   }
184 
185   RefPtr<StreamWrapper> wrapper = new StreamWrapper(stream, fileHandle);
186 
187   wrapper.forget(aStream);
188 }
189 
190 BlobImpl*
GetBlobImpl() const191 BlobImplSnapshot::GetBlobImpl() const
192 {
193   nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
194   RefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
195   if (!fileHandle || !fileHandle->IsOpen()) {
196     return nullptr;
197   }
198 
199   return mBlobImpl;
200 }
201 
~StreamWrapper()202 StreamWrapper::~StreamWrapper()
203 {
204   AssertIsOnOwningThread();
205 
206   Finish();
207 }
208 
209 NS_IMPL_ADDREF(StreamWrapper)
NS_IMPL_RELEASE_WITH_DESTROY(StreamWrapper,Destroy ())210 NS_IMPL_RELEASE_WITH_DESTROY(StreamWrapper, Destroy())
211 NS_IMPL_QUERY_INTERFACE(StreamWrapper,
212                         nsIInputStream,
213                         nsIIPCSerializableInputStream)
214 
215 NS_IMETHODIMP
216 StreamWrapper::Close()
217 {
218   MOZ_ASSERT(!IsOnOwningThread());
219 
220   RefPtr<CloseRunnable> closeRunnable = new CloseRunnable(this);
221 
222   MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(closeRunnable,
223                                               NS_DISPATCH_NORMAL));
224 
225   return NS_OK;
226 }
227 
228 NS_IMETHODIMP
Available(uint64_t * _retval)229 StreamWrapper::Available(uint64_t* _retval)
230 {
231   // Can't assert here, this method is sometimes called on the owning thread
232   // (nsInputStreamChannel::OpenContentStream calls Available before setting
233   // the content length property).
234 
235   return mInputStream->Available(_retval);
236 }
237 
238 NS_IMETHODIMP
Read(char * aBuf,uint32_t aCount,uint32_t * _retval)239 StreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
240 {
241   MOZ_ASSERT(!IsOnOwningThread());
242   return mInputStream->Read(aBuf, aCount, _retval);
243 }
244 
245 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun aWriter,void * aClosure,uint32_t aCount,uint32_t * _retval)246 StreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
247                             uint32_t aCount, uint32_t* _retval)
248 {
249   MOZ_ASSERT(!IsOnOwningThread());
250   return mInputStream->ReadSegments(aWriter, aClosure, aCount, _retval);
251 }
252 
253 NS_IMETHODIMP
IsNonBlocking(bool * _retval)254 StreamWrapper::IsNonBlocking(bool* _retval)
255 {
256   return mInputStream->IsNonBlocking(_retval);
257 }
258 
259 void
Serialize(InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors)260 StreamWrapper::Serialize(InputStreamParams& aParams,
261                          FileDescriptorArray& aFileDescriptors)
262 {
263   nsCOMPtr<nsIIPCSerializableInputStream> stream =
264     do_QueryInterface(mInputStream);
265 
266   if (stream) {
267     stream->Serialize(aParams, aFileDescriptors);
268   }
269 }
270 
271 bool
Deserialize(const InputStreamParams & aParams,const FileDescriptorArray & aFileDescriptors)272 StreamWrapper::Deserialize(const InputStreamParams& aParams,
273                            const FileDescriptorArray& aFileDescriptors)
274 {
275   nsCOMPtr<nsIIPCSerializableInputStream> stream =
276     do_QueryInterface(mInputStream);
277 
278   if (stream) {
279     return stream->Deserialize(aParams, aFileDescriptors);
280   }
281 
282   return false;
283 }
284 
285 Maybe<uint64_t>
ExpectedSerializedLength()286 StreamWrapper::ExpectedSerializedLength()
287 {
288   nsCOMPtr<nsIIPCSerializableInputStream> stream =
289     do_QueryInterface(mInputStream);
290 
291   if (stream) {
292     return stream->ExpectedSerializedLength();
293   }
294   return Nothing();
295 }
296 
NS_IMPL_ISUPPORTS_INHERITED0(StreamWrapper::CloseRunnable,Runnable)297 NS_IMPL_ISUPPORTS_INHERITED0(StreamWrapper::CloseRunnable,
298                              Runnable)
299 
300 NS_IMETHODIMP
301 StreamWrapper::
302 CloseRunnable::Run()
303 {
304   mStreamWrapper->Finish();
305 
306   return NS_OK;
307 }
308 
309 } // namespace indexedDB
310 } // namespace dom
311 } // namespace mozilla
312