dom/file/FileStreamWrappers.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:2258798a8e06
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 "FileStreamWrappers.h"
8
9 #include "nsIFileStorage.h"
10 #include "nsISeekableStream.h"
11 #include "mozilla/Attributes.h"
12
13 #include "FileHelper.h"
14
15 USING_FILE_NAMESPACE
16
17 namespace {
18
19 class ProgressRunnable MOZ_FINAL : public nsIRunnable
20 {
21 public:
22 NS_DECL_THREADSAFE_ISUPPORTS
23 NS_DECL_NSIRUNNABLE
24
25 ProgressRunnable(FileHelper* aFileHelper,
26 uint64_t aProgress,
27 uint64_t aProgressMax)
28 : mFileHelper(aFileHelper),
29 mProgress(aProgress),
30 mProgressMax(aProgressMax)
31 {
32 }
33
34 private:
35 nsRefPtr<FileHelper> mFileHelper;
36 uint64_t mProgress;
37 uint64_t mProgressMax;
38 };
39
40 class CloseRunnable MOZ_FINAL : public nsIRunnable
41 {
42 public:
43 NS_DECL_THREADSAFE_ISUPPORTS
44 NS_DECL_NSIRUNNABLE
45
46 CloseRunnable(FileHelper* aFileHelper)
47 : mFileHelper(aFileHelper)
48 { }
49
50 private:
51 nsRefPtr<FileHelper> mFileHelper;
52 };
53
54 class DestroyRunnable MOZ_FINAL : public nsIRunnable
55 {
56 public:
57 NS_DECL_THREADSAFE_ISUPPORTS
58 NS_DECL_NSIRUNNABLE
59
60 DestroyRunnable(FileHelper* aFileHelper)
61 : mFileHelper(aFileHelper)
62 { }
63
64 private:
65 nsRefPtr<FileHelper> mFileHelper;
66 };
67
68 } // anonymous namespace
69
70 FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream,
71 FileHelper* aFileHelper,
72 uint64_t aOffset,
73 uint64_t aLimit,
74 uint32_t aFlags)
75 : mFileStream(aFileStream),
76 mFileHelper(aFileHelper),
77 mOffset(aOffset),
78 mLimit(aLimit),
79 mFlags(aFlags),
80 mFirstTime(true)
81 {
82 NS_ASSERTION(mFileStream, "Must have a file stream!");
83 NS_ASSERTION(mFileHelper, "Must have a file helper!");
84 }
85
86 FileStreamWrapper::~FileStreamWrapper()
87 {
88 if (mFlags & NOTIFY_DESTROY) {
89 if (NS_IsMainThread()) {
90 mFileHelper->OnStreamDestroy();
91 }
92 else {
93 nsCOMPtr<nsIRunnable> runnable =
94 new DestroyRunnable(mFileHelper);
95
96 nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
97 if (NS_FAILED(rv)) {
98 NS_WARNING("Failed to dispatch to the main thread!");
99 }
100 }
101 }
102 }
103
104 NS_IMPL_ISUPPORTS0(FileStreamWrapper)
105
106 FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream,
107 FileHelper* aFileHelper,
108 uint64_t aOffset,
109 uint64_t aLimit,
110 uint32_t aFlags)
111 : FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
112 {
113 mInputStream = do_QueryInterface(mFileStream);
114 NS_ASSERTION(mInputStream, "This should always succeed!");
115 }
116
117 NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper,
118 FileStreamWrapper,
119 nsIInputStream)
120
121 NS_IMETHODIMP
122 FileInputStreamWrapper::Close()
123 {
124 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
125
126 if (mFlags & NOTIFY_CLOSE) {
127 nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
128
129 if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
130 NS_WARNING("Failed to dispatch to the main thread!");
131 }
132 }
133
134 mOffset = 0;
135 mLimit = 0;
136
137 return NS_OK;
138 }
139
140 NS_IMETHODIMP
141 FileInputStreamWrapper::Available(uint64_t* _retval)
142 {
143 // Performing sync IO on the main thread is generally not allowed.
144 // However, the input stream wrapper is also used to track reads performed by
145 // other APIs like FileReader, XHR, etc.
146 // In that case nsInputStreamChannel::OpenContentStream() calls Available()
147 // before setting the content length property. This property is not important
148 // to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED
149 // here. It causes OpenContentStream() to set the content length property to
150 // zero.
151
152 if (NS_IsMainThread()) {
153 return NS_BASE_STREAM_CLOSED;
154 }
155
156 return mInputStream->Available(_retval);
157 }
158
159 NS_IMETHODIMP
160 FileInputStreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
161 {
162 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
163
164 nsresult rv;
165
166 if (mFirstTime) {
167 mFirstTime = false;
168
169 if (mOffset != UINT64_MAX) {
170 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
171 if (seekable) {
172 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
173 NS_ENSURE_SUCCESS(rv, rv);
174 }
175 }
176
177 mOffset = 0;
178 }
179
180 uint64_t max = mLimit - mOffset;
181 if (max == 0) {
182 *_retval = 0;
183 return NS_OK;
184 }
185
186 if (aCount > max) {
187 aCount = max;
188 }
189
190 rv = mInputStream->Read(aBuf, aCount, _retval);
191 NS_ENSURE_SUCCESS(rv, rv);
192
193 mOffset += *_retval;
194
195 if (mFlags & NOTIFY_PROGRESS) {
196 nsCOMPtr<nsIRunnable> runnable =
197 new ProgressRunnable(mFileHelper, mOffset, mLimit);
198
199 rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
200 if (NS_FAILED(rv)) {
201 NS_WARNING("Failed to dispatch to the main thread!");
202 }
203 }
204
205 return NS_OK;
206 }
207
208 NS_IMETHODIMP
209 FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
210 uint32_t aCount, uint32_t* _retval)
211 {
212 return NS_ERROR_NOT_IMPLEMENTED;
213 }
214
215 NS_IMETHODIMP
216 FileInputStreamWrapper::IsNonBlocking(bool* _retval)
217 {
218 *_retval = false;
219 return NS_OK;
220 }
221
222 FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream,
223 FileHelper* aFileHelper,
224 uint64_t aOffset,
225 uint64_t aLimit,
226 uint32_t aFlags)
227 : FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
228 #ifdef DEBUG
229 , mWriteThread(nullptr)
230 #endif
231 {
232 mOutputStream = do_QueryInterface(mFileStream);
233 NS_ASSERTION(mOutputStream, "This should always succeed!");
234 }
235
236 NS_IMPL_ISUPPORTS_INHERITED(FileOutputStreamWrapper,
237 FileStreamWrapper,
238 nsIOutputStream)
239
240 NS_IMETHODIMP
241 FileOutputStreamWrapper::Close()
242 {
243 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
244
245 nsresult rv = NS_OK;
246
247 if (!mFirstTime) {
248 NS_ASSERTION(PR_GetCurrentThread() == mWriteThread,
249 "Unsetting thread locals on wrong thread!");
250 mFileHelper->mFileStorage->UnsetThreadLocals();
251 }
252
253 if (mFlags & NOTIFY_CLOSE) {
254 nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
255
256 if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
257 NS_WARNING("Failed to dispatch to the main thread!");
258 }
259 }
260
261 mOffset = 0;
262 mLimit = 0;
263
264 return rv;
265 }
266
267 NS_IMETHODIMP
268 FileOutputStreamWrapper::Write(const char* aBuf, uint32_t aCount,
269 uint32_t* _retval)
270 {
271 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
272
273 nsresult rv;
274
275 if (mFirstTime) {
276 mFirstTime = false;
277
278 #ifdef DEBUG
279 mWriteThread = PR_GetCurrentThread();
280 #endif
281 mFileHelper->mFileStorage->SetThreadLocals();
282
283 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream);
284 if (seekable) {
285 if (mOffset == UINT64_MAX) {
286 rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
287 }
288 else {
289 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
290 }
291 NS_ENSURE_SUCCESS(rv, rv);
292 }
293
294 mOffset = 0;
295 }
296
297 uint64_t max = mLimit - mOffset;
298 if (max == 0) {
299 *_retval = 0;
300 return NS_OK;
301 }
302
303 if (aCount > max) {
304 aCount = max;
305 }
306
307 rv = mOutputStream->Write(aBuf, aCount, _retval);
308 NS_ENSURE_SUCCESS(rv, rv);
309
310 mOffset += *_retval;
311
312 if (mFlags & NOTIFY_PROGRESS) {
313 nsCOMPtr<nsIRunnable> runnable =
314 new ProgressRunnable(mFileHelper, mOffset, mLimit);
315
316 NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
317 }
318
319 return NS_OK;
320 }
321
322 NS_IMETHODIMP
323 FileOutputStreamWrapper::Flush()
324 {
325 return NS_OK;
326 }
327
328 NS_IMETHODIMP
329 FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream,
330 uint32_t aCount, uint32_t* _retval)
331 {
332 return NS_ERROR_NOT_IMPLEMENTED;
333 }
334
335 NS_IMETHODIMP
336 FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader,
337 void* aClosure, uint32_t aCount,
338 uint32_t* _retval)
339 {
340 return NS_ERROR_NOT_IMPLEMENTED;
341 }
342
343 NS_IMETHODIMP
344 FileOutputStreamWrapper::IsNonBlocking(bool* _retval)
345 {
346 *_retval = false;
347 return NS_OK;
348 }
349
350 NS_IMPL_ISUPPORTS(ProgressRunnable, nsIRunnable)
351
352 NS_IMETHODIMP
353 ProgressRunnable::Run()
354 {
355 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
356
357 mFileHelper->OnStreamProgress(mProgress, mProgressMax);
358 mFileHelper = nullptr;
359
360 return NS_OK;
361 }
362
363 NS_IMPL_ISUPPORTS(CloseRunnable, nsIRunnable)
364
365 NS_IMETHODIMP
366 CloseRunnable::Run()
367 {
368 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
369
370 mFileHelper->OnStreamClose();
371 mFileHelper = nullptr;
372
373 return NS_OK;
374 }
375
376 NS_IMPL_ISUPPORTS(DestroyRunnable, nsIRunnable)
377
378 NS_IMETHODIMP
379 DestroyRunnable::Run()
380 {
381 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
382
383 mFileHelper->OnStreamDestroy();
384 mFileHelper = nullptr;
385
386 return NS_OK;
387 }

mercurial