|
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 } |