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(¤t)) && 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