Source code

Revision control

Other Tools

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 "IDBDatabase.h"
10
#include "IDBFileHandle.h"
11
#include "IDBMutableFile.h"
12
#include "mozilla/Assertions.h"
13
#include "mozilla/Mutex.h"
14
#include "nsIAsyncInputStream.h"
15
#include "nsICloneableInputStream.h"
16
#include "nsIIPCSerializableInputStream.h"
17
18
namespace mozilla {
19
namespace dom {
20
namespace indexedDB {
21
22
using namespace mozilla::ipc;
23
24
namespace {
25
26
class StreamWrapper final : public nsIAsyncInputStream,
27
public nsIInputStreamCallback,
28
public nsICloneableInputStream,
29
public nsIIPCSerializableInputStream {
30
class CloseRunnable;
31
32
nsCOMPtr<nsIEventTarget> mOwningThread;
33
nsCOMPtr<nsIInputStream> mInputStream;
34
RefPtr<IDBFileHandle> mFileHandle;
35
bool mFinished;
36
37
// This is needed to call OnInputStreamReady() with the correct inputStream.
38
// It is protected by mutex.
39
nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
40
41
Mutex mMutex;
42
43
public:
44
StreamWrapper(nsIInputStream* aInputStream, IDBFileHandle* aFileHandle)
45
: mOwningThread(aFileHandle->GetMutableFile()->Database()->EventTarget()),
46
mInputStream(aInputStream),
47
mFileHandle(aFileHandle),
48
mFinished(false),
49
mMutex("StreamWrapper::mMutex") {
50
AssertIsOnOwningThread();
51
MOZ_ASSERT(aInputStream);
52
MOZ_ASSERT(aFileHandle);
53
aFileHandle->AssertIsOnOwningThread();
54
55
mFileHandle->OnNewRequest();
56
}
57
58
private:
59
virtual ~StreamWrapper();
60
61
template <typename M>
62
void SerializeInternal(InputStreamParams& aParams,
63
FileDescriptorArray& aFileDescriptors,
64
bool aDelayedStart, uint32_t aMaxSize,
65
uint32_t* aSizeUsed, M* aManager);
66
67
bool IsOnOwningThread() const {
68
MOZ_ASSERT(mOwningThread);
69
70
bool current;
71
return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)) && current;
72
}
73
74
void AssertIsOnOwningThread() const { MOZ_ASSERT(IsOnOwningThread()); }
75
76
void Finish() {
77
AssertIsOnOwningThread();
78
79
if (mFinished) {
80
return;
81
}
82
83
mFinished = true;
84
85
mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true);
86
}
87
88
void Destroy() {
89
if (IsOnOwningThread()) {
90
delete this;
91
return;
92
}
93
94
RefPtr<Runnable> destroyRunnable = NewNonOwningRunnableMethod(
95
"StreamWrapper::Destroy", this, &StreamWrapper::Destroy);
96
97
MOZ_ALWAYS_SUCCEEDS(
98
mOwningThread->Dispatch(destroyRunnable, NS_DISPATCH_NORMAL));
99
}
100
101
bool IsCloneableInputStream() const {
102
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mInputStream);
103
return !!stream;
104
}
105
106
bool IsIPCSerializableInputStream() const {
107
nsCOMPtr<nsIIPCSerializableInputStream> stream =
108
do_QueryInterface(mInputStream);
109
return !!stream;
110
}
111
112
bool IsAsyncInputStream() const {
113
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
114
return !!stream;
115
}
116
117
NS_DECL_THREADSAFE_ISUPPORTS
118
NS_DECL_NSIINPUTSTREAM
119
NS_DECL_NSIASYNCINPUTSTREAM
120
NS_DECL_NSIINPUTSTREAMCALLBACK
121
NS_DECL_NSICLONEABLEINPUTSTREAM
122
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
123
};
124
125
class StreamWrapper::CloseRunnable final : public Runnable {
126
friend class StreamWrapper;
127
128
RefPtr<StreamWrapper> mStreamWrapper;
129
130
public:
131
NS_INLINE_DECL_REFCOUNTING_INHERITED(CloseRunnable, Runnable)
132
133
private:
134
explicit CloseRunnable(StreamWrapper* aStreamWrapper)
135
: Runnable("StreamWrapper::CloseRunnable"),
136
mStreamWrapper(aStreamWrapper) {}
137
138
~CloseRunnable() = default;
139
140
NS_IMETHOD
141
Run() override;
142
};
143
144
} // anonymous namespace
145
146
BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
147
IDBFileHandle* aFileHandle)
148
: mBlobImpl(aFileImpl) {
149
MOZ_ASSERT(aFileImpl);
150
MOZ_ASSERT(aFileHandle);
151
152
mFileHandle =
153
do_GetWeakReference(NS_ISUPPORTS_CAST(EventTarget*, aFileHandle));
154
}
155
156
BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
157
nsIWeakReference* aFileHandle)
158
: mBlobImpl(aFileImpl), mFileHandle(aFileHandle) {
159
MOZ_ASSERT(aFileImpl);
160
MOZ_ASSERT(aFileHandle);
161
}
162
163
NS_IMPL_ISUPPORTS_INHERITED(BlobImplSnapshot, BlobImpl, PIBlobImplSnapshot)
164
165
already_AddRefed<BlobImpl> BlobImplSnapshot::CreateSlice(
166
uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
167
ErrorResult& aRv) {
168
RefPtr<BlobImpl> blobImpl =
169
mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
170
171
if (NS_WARN_IF(aRv.Failed())) {
172
return nullptr;
173
}
174
175
blobImpl = new BlobImplSnapshot(blobImpl, mFileHandle);
176
return blobImpl.forget();
177
}
178
179
void BlobImplSnapshot::CreateInputStream(nsIInputStream** aStream,
180
ErrorResult& aRv) {
181
nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
182
RefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
183
if (!fileHandle || !fileHandle->IsOpen()) {
184
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
185
return;
186
}
187
188
nsCOMPtr<nsIInputStream> stream;
189
mBlobImpl->CreateInputStream(getter_AddRefs(stream), aRv);
190
if (NS_WARN_IF(aRv.Failed())) {
191
return;
192
}
193
194
RefPtr<StreamWrapper> wrapper = new StreamWrapper(stream, fileHandle);
195
196
wrapper.forget(aStream);
197
}
198
199
BlobImpl* BlobImplSnapshot::GetBlobImpl() const {
200
nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
201
RefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
202
if (!fileHandle || !fileHandle->IsOpen()) {
203
return nullptr;
204
}
205
206
return mBlobImpl;
207
}
208
209
void BlobImplSnapshot::GetBlobImplType(nsAString& aBlobImplType) const {
210
aBlobImplType.AssignLiteral("BlobImplSnapshot[");
211
212
nsAutoString blobImplType;
213
mBlobImpl->GetBlobImplType(blobImplType);
214
aBlobImplType.Append(blobImplType);
215
216
aBlobImplType.AppendLiteral("]");
217
}
218
219
StreamWrapper::~StreamWrapper() {
220
AssertIsOnOwningThread();
221
222
Finish();
223
}
224
225
NS_IMPL_ADDREF(StreamWrapper)
226
NS_IMPL_RELEASE_WITH_DESTROY(StreamWrapper, Destroy())
227
228
NS_INTERFACE_MAP_BEGIN(StreamWrapper)
229
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
230
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream())
231
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
232
IsAsyncInputStream())
233
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
234
IsCloneableInputStream())
235
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
236
IsIPCSerializableInputStream())
237
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
238
NS_INTERFACE_MAP_END
239
240
NS_IMETHODIMP
241
StreamWrapper::Close() {
242
RefPtr<CloseRunnable> closeRunnable = new CloseRunnable(this);
243
244
MOZ_ALWAYS_SUCCEEDS(
245
mOwningThread->Dispatch(closeRunnable, NS_DISPATCH_NORMAL));
246
247
return NS_OK;
248
}
249
250
NS_IMETHODIMP
251
StreamWrapper::Available(uint64_t* _retval) {
252
return mInputStream->Available(_retval);
253
}
254
255
NS_IMETHODIMP
256
StreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
257
return mInputStream->Read(aBuf, aCount, _retval);
258
}
259
260
NS_IMETHODIMP
261
StreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
262
uint32_t aCount, uint32_t* _retval) {
263
return mInputStream->ReadSegments(aWriter, aClosure, aCount, _retval);
264
}
265
266
NS_IMETHODIMP
267
StreamWrapper::IsNonBlocking(bool* _retval) {
268
return mInputStream->IsNonBlocking(_retval);
269
}
270
271
void StreamWrapper::Serialize(InputStreamParams& aParams,
272
FileDescriptorArray& aFileDescriptors,
273
bool aDelayedStart, uint32_t aMaxSize,
274
uint32_t* aSizeUsed,
275
ParentToChildStreamActorManager* aManager) {
276
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
277
aSizeUsed, aManager);
278
}
279
280
void StreamWrapper::Serialize(InputStreamParams& aParams,
281
FileDescriptorArray& aFileDescriptors,
282
bool aDelayedStart, uint32_t aMaxSize,
283
uint32_t* aSizeUsed,
284
ChildToParentStreamActorManager* aManager) {
285
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
286
aSizeUsed, aManager);
287
}
288
289
template <typename M>
290
void StreamWrapper::SerializeInternal(InputStreamParams& aParams,
291
FileDescriptorArray& aFileDescriptors,
292
bool aDelayedStart, uint32_t aMaxSize,
293
uint32_t* aSizeUsed, M* aManager) {
294
MOZ_ASSERT(aSizeUsed);
295
*aSizeUsed = 0;
296
297
nsCOMPtr<nsIIPCSerializableInputStream> stream =
298
do_QueryInterface(mInputStream);
299
300
if (stream) {
301
stream->Serialize(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
302
aSizeUsed, aManager);
303
}
304
}
305
306
bool StreamWrapper::Deserialize(const InputStreamParams& aParams,
307
const FileDescriptorArray& aFileDescriptors) {
308
MOZ_CRASH("This method should never be called");
309
return false;
310
}
311
312
NS_IMETHODIMP
313
StreamWrapper::CloseWithStatus(nsresult aStatus) {
314
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
315
if (!stream) {
316
return NS_ERROR_NO_INTERFACE;
317
}
318
319
nsresult rv = stream->CloseWithStatus(aStatus);
320
if (NS_WARN_IF(NS_FAILED(rv))) {
321
return rv;
322
}
323
324
return Close();
325
}
326
327
NS_IMETHODIMP
328
StreamWrapper::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags,
329
uint32_t aRequestedCount,
330
nsIEventTarget* aEventTarget) {
331
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
332
if (!stream) {
333
return NS_ERROR_NO_INTERFACE;
334
}
335
336
nsCOMPtr<nsIInputStreamCallback> callback = aCallback ? this : nullptr;
337
{
338
MutexAutoLock lock(mMutex);
339
340
if (mAsyncWaitCallback && aCallback) {
341
return NS_ERROR_FAILURE;
342
}
343
344
mAsyncWaitCallback = aCallback;
345
}
346
347
return stream->AsyncWait(callback, aFlags, aRequestedCount, aEventTarget);
348
}
349
350
// nsIInputStreamCallback
351
352
NS_IMETHODIMP
353
StreamWrapper::OnInputStreamReady(nsIAsyncInputStream* aStream) {
354
nsCOMPtr<nsIAsyncInputStream> stream = do_QueryInterface(mInputStream);
355
if (!stream) {
356
return NS_ERROR_NO_INTERFACE;
357
}
358
359
nsCOMPtr<nsIInputStreamCallback> callback;
360
{
361
MutexAutoLock lock(mMutex);
362
363
// We have been canceled in the meanwhile.
364
if (!mAsyncWaitCallback) {
365
return NS_OK;
366
}
367
368
callback.swap(mAsyncWaitCallback);
369
}
370
371
MOZ_ASSERT(callback);
372
return callback->OnInputStreamReady(this);
373
}
374
375
// nsICloneableInputStream
376
377
NS_IMETHODIMP
378
StreamWrapper::GetCloneable(bool* aCloneable) {
379
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mInputStream);
380
if (!stream) {
381
*aCloneable = false;
382
return NS_ERROR_NO_INTERFACE;
383
}
384
385
return stream->GetCloneable(aCloneable);
386
}
387
388
NS_IMETHODIMP
389
StreamWrapper::Clone(nsIInputStream** aResult) {
390
nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mInputStream);
391
if (!stream) {
392
return NS_ERROR_NO_INTERFACE;
393
}
394
395
return stream->Clone(aResult);
396
}
397
398
NS_IMETHODIMP
399
StreamWrapper::CloseRunnable::Run() {
400
mStreamWrapper->Finish();
401
402
return NS_OK;
403
}
404
405
} // namespace indexedDB
406
} // namespace dom
407
} // namespace mozilla