/src/mozilla-central/netwerk/protocol/file/nsFileChannel.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim:set ts=2 sw=2 sts=2 et cin: */ |
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 |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsIOService.h" |
8 | | #include "nsFileChannel.h" |
9 | | #include "nsBaseContentStream.h" |
10 | | #include "nsDirectoryIndexStream.h" |
11 | | #include "nsThreadUtils.h" |
12 | | #include "nsTransportUtils.h" |
13 | | #include "nsStreamUtils.h" |
14 | | #include "nsMimeTypes.h" |
15 | | #include "nsNetUtil.h" |
16 | | #include "nsNetCID.h" |
17 | | #include "nsIOutputStream.h" |
18 | | #include "nsIFileStreams.h" |
19 | | #include "nsFileProtocolHandler.h" |
20 | | #include "nsProxyRelease.h" |
21 | | #include "nsAutoPtr.h" |
22 | | #include "nsIContentPolicy.h" |
23 | | #include "nsContentUtils.h" |
24 | | |
25 | | #include "nsIFileURL.h" |
26 | | #include "nsIURIMutator.h" |
27 | | #include "nsIFile.h" |
28 | | #include "nsIMIMEService.h" |
29 | | #include "prio.h" |
30 | | #include <algorithm> |
31 | | |
32 | | #include "mozilla/Unused.h" |
33 | | |
34 | | using namespace mozilla; |
35 | | using namespace mozilla::net; |
36 | | |
37 | | //----------------------------------------------------------------------------- |
38 | | |
39 | | class nsFileCopyEvent : public Runnable { |
40 | | public: |
41 | | nsFileCopyEvent(nsIOutputStream* dest, nsIInputStream* source, int64_t len) |
42 | | : mozilla::Runnable("nsFileCopyEvent") |
43 | | , mDest(dest) |
44 | | , mSource(source) |
45 | | , mLen(len) |
46 | | , mStatus(NS_OK) |
47 | | , mInterruptStatus(NS_OK) |
48 | 0 | { |
49 | 0 | } |
50 | | |
51 | | // Read the current status of the file copy operation. |
52 | 0 | nsresult Status() { return mStatus; } |
53 | | |
54 | | // Call this method to perform the file copy synchronously. |
55 | | void DoCopy(); |
56 | | |
57 | | // Call this method to perform the file copy on a background thread. The |
58 | | // callback is dispatched when the file copy completes. |
59 | | nsresult Dispatch(nsIRunnable *callback, |
60 | | nsITransportEventSink *sink, |
61 | | nsIEventTarget *target); |
62 | | |
63 | | // Call this method to interrupt a file copy operation that is occuring on |
64 | | // a background thread. The status parameter passed to this function must |
65 | | // be a failure code and is set as the status of this file copy operation. |
66 | 0 | void Interrupt(nsresult status) { |
67 | 0 | NS_ASSERTION(NS_FAILED(status), "must be a failure code"); |
68 | 0 | mInterruptStatus = status; |
69 | 0 | } |
70 | | |
71 | 0 | NS_IMETHOD Run() override { |
72 | 0 | DoCopy(); |
73 | 0 | return NS_OK; |
74 | 0 | } |
75 | | |
76 | | private: |
77 | | nsCOMPtr<nsIEventTarget> mCallbackTarget; |
78 | | nsCOMPtr<nsIRunnable> mCallback; |
79 | | nsCOMPtr<nsITransportEventSink> mSink; |
80 | | nsCOMPtr<nsIOutputStream> mDest; |
81 | | nsCOMPtr<nsIInputStream> mSource; |
82 | | int64_t mLen; |
83 | | nsresult mStatus; // modified on i/o thread only |
84 | | nsresult mInterruptStatus; // modified on main thread only |
85 | | }; |
86 | | |
87 | | void |
88 | | nsFileCopyEvent::DoCopy() |
89 | 0 | { |
90 | 0 | // We'll copy in chunks this large by default. This size affects how |
91 | 0 | // frequently we'll check for interrupts. |
92 | 0 | const int32_t chunk = nsIOService::gDefaultSegmentSize * nsIOService::gDefaultSegmentCount; |
93 | 0 |
|
94 | 0 | nsresult rv = NS_OK; |
95 | 0 |
|
96 | 0 | int64_t len = mLen, progress = 0; |
97 | 0 | while (len) { |
98 | 0 | // If we've been interrupted, then stop copying. |
99 | 0 | rv = mInterruptStatus; |
100 | 0 | if (NS_FAILED(rv)) |
101 | 0 | break; |
102 | 0 | |
103 | 0 | int32_t num = std::min((int32_t) len, chunk); |
104 | 0 |
|
105 | 0 | uint32_t result; |
106 | 0 | rv = mSource->ReadSegments(NS_CopySegmentToStream, mDest, num, &result); |
107 | 0 | if (NS_FAILED(rv)) |
108 | 0 | break; |
109 | 0 | if (result != (uint32_t) num) { |
110 | 0 | rv = NS_ERROR_FILE_DISK_FULL; // stopped prematurely (out of disk space) |
111 | 0 | break; |
112 | 0 | } |
113 | 0 | |
114 | 0 | // Dispatch progress notification |
115 | 0 | if (mSink) { |
116 | 0 | progress += num; |
117 | 0 | mSink->OnTransportStatus(nullptr, NS_NET_STATUS_WRITING, progress, |
118 | 0 | mLen); |
119 | 0 | } |
120 | 0 |
|
121 | 0 | len -= num; |
122 | 0 | } |
123 | 0 |
|
124 | 0 | if (NS_FAILED(rv)) |
125 | 0 | mStatus = rv; |
126 | 0 |
|
127 | 0 | // Close the output stream before notifying our callback so that others may |
128 | 0 | // freely "play" with the file. |
129 | 0 | mDest->Close(); |
130 | 0 |
|
131 | 0 | // Notify completion |
132 | 0 | if (mCallback) { |
133 | 0 | mCallbackTarget->Dispatch(mCallback, NS_DISPATCH_NORMAL); |
134 | 0 |
|
135 | 0 | // Release the callback on the target thread to avoid destroying stuff on |
136 | 0 | // the wrong thread. |
137 | 0 | NS_ProxyRelease( |
138 | 0 | "nsFileCopyEvent::mCallback", mCallbackTarget, mCallback.forget()); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | nsresult |
143 | | nsFileCopyEvent::Dispatch(nsIRunnable *callback, |
144 | | nsITransportEventSink *sink, |
145 | | nsIEventTarget *target) |
146 | 0 | { |
147 | 0 | // Use the supplied event target for all asynchronous operations. |
148 | 0 |
|
149 | 0 | mCallback = callback; |
150 | 0 | mCallbackTarget = target; |
151 | 0 |
|
152 | 0 | // Build a coalescing proxy for progress events |
153 | 0 | nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(mSink), sink, target); |
154 | 0 |
|
155 | 0 | if (NS_FAILED(rv)) |
156 | 0 | return rv; |
157 | 0 | |
158 | 0 | // Dispatch ourselves to I/O thread pool... |
159 | 0 | nsCOMPtr<nsIEventTarget> pool = |
160 | 0 | do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); |
161 | 0 | if (NS_FAILED(rv)) |
162 | 0 | return rv; |
163 | 0 | |
164 | 0 | return pool->Dispatch(this, NS_DISPATCH_NORMAL); |
165 | 0 | } |
166 | | |
167 | | //----------------------------------------------------------------------------- |
168 | | |
169 | | // This is a dummy input stream that when read, performs the file copy. The |
170 | | // copy happens on a background thread via mCopyEvent. |
171 | | |
172 | | class nsFileUploadContentStream : public nsBaseContentStream { |
173 | | public: |
174 | | NS_INLINE_DECL_REFCOUNTING_INHERITED(nsFileUploadContentStream, |
175 | | nsBaseContentStream) |
176 | | |
177 | | nsFileUploadContentStream(bool nonBlocking, |
178 | | nsIOutputStream *dest, |
179 | | nsIInputStream *source, |
180 | | int64_t len, |
181 | | nsITransportEventSink *sink) |
182 | | : nsBaseContentStream(nonBlocking) |
183 | | , mCopyEvent(new nsFileCopyEvent(dest, source, len)) |
184 | 0 | , mSink(sink) { |
185 | 0 | } |
186 | | |
187 | 0 | bool IsInitialized() { |
188 | 0 | return mCopyEvent != nullptr; |
189 | 0 | } |
190 | | |
191 | | NS_IMETHOD ReadSegments(nsWriteSegmentFun fun, void *closure, |
192 | | uint32_t count, uint32_t *result) override; |
193 | | NS_IMETHOD AsyncWait(nsIInputStreamCallback *callback, uint32_t flags, |
194 | | uint32_t count, nsIEventTarget *target) override; |
195 | | |
196 | | private: |
197 | 0 | virtual ~nsFileUploadContentStream() = default; |
198 | | |
199 | | void OnCopyComplete(); |
200 | | |
201 | | RefPtr<nsFileCopyEvent> mCopyEvent; |
202 | | nsCOMPtr<nsITransportEventSink> mSink; |
203 | | }; |
204 | | |
205 | | NS_IMETHODIMP |
206 | | nsFileUploadContentStream::ReadSegments(nsWriteSegmentFun fun, void *closure, |
207 | | uint32_t count, uint32_t *result) |
208 | 0 | { |
209 | 0 | *result = 0; // nothing is ever actually read from this stream |
210 | 0 |
|
211 | 0 | if (IsClosed()) |
212 | 0 | return NS_OK; |
213 | 0 | |
214 | 0 | if (IsNonBlocking()) { |
215 | 0 | // Inform the caller that they will have to wait for the copy operation to |
216 | 0 | // complete asynchronously. We'll kick of the copy operation once they |
217 | 0 | // call AsyncWait. |
218 | 0 | return NS_BASE_STREAM_WOULD_BLOCK; |
219 | 0 | } |
220 | 0 | |
221 | 0 | // Perform copy synchronously, and then close out the stream. |
222 | 0 | mCopyEvent->DoCopy(); |
223 | 0 | nsresult status = mCopyEvent->Status(); |
224 | 0 | CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED); |
225 | 0 | return status; |
226 | 0 | } |
227 | | |
228 | | NS_IMETHODIMP |
229 | | nsFileUploadContentStream::AsyncWait(nsIInputStreamCallback *callback, |
230 | | uint32_t flags, uint32_t count, |
231 | | nsIEventTarget *target) |
232 | 0 | { |
233 | 0 | nsresult rv = nsBaseContentStream::AsyncWait(callback, flags, count, target); |
234 | 0 | if (NS_FAILED(rv) || IsClosed()) |
235 | 0 | return rv; |
236 | 0 | |
237 | 0 | if (IsNonBlocking()) { |
238 | 0 | nsCOMPtr<nsIRunnable> callback = |
239 | 0 | NewRunnableMethod("nsFileUploadContentStream::OnCopyComplete", |
240 | 0 | this, |
241 | 0 | &nsFileUploadContentStream::OnCopyComplete); |
242 | 0 | mCopyEvent->Dispatch(callback, mSink, target); |
243 | 0 | } |
244 | 0 |
|
245 | 0 | return NS_OK; |
246 | 0 | } |
247 | | |
248 | | void |
249 | | nsFileUploadContentStream::OnCopyComplete() |
250 | 0 | { |
251 | 0 | // This method is being called to indicate that we are done copying. |
252 | 0 | nsresult status = mCopyEvent->Status(); |
253 | 0 |
|
254 | 0 | CloseWithStatus(NS_FAILED(status) ? status : NS_BASE_STREAM_CLOSED); |
255 | 0 | } |
256 | | |
257 | | //----------------------------------------------------------------------------- |
258 | | |
259 | | nsFileChannel::nsFileChannel(nsIURI *uri) |
260 | | : mUploadLength(0) |
261 | | , mFileURI(uri) |
262 | 0 | { |
263 | 0 | } |
264 | | |
265 | | nsresult |
266 | | nsFileChannel::Init() |
267 | 0 | { |
268 | 0 | NS_ENSURE_STATE(mLoadInfo); |
269 | 0 |
|
270 | 0 | nsresult rv; |
271 | 0 |
|
272 | 0 | rv = nsBaseChannel::Init(); |
273 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
274 | 0 |
|
275 | 0 | // If we have a link file, we should resolve its target right away. |
276 | 0 | // This is to protect against a same origin attack where the same link file |
277 | 0 | // can point to different resources right after the first resource is loaded. |
278 | 0 | nsCOMPtr<nsIFile> file; |
279 | 0 | nsCOMPtr <nsIURI> targetURI; |
280 | | #ifdef XP_WIN |
281 | | nsAutoString fileTarget; |
282 | | #else |
283 | | nsAutoCString fileTarget; |
284 | 0 | #endif |
285 | 0 | nsCOMPtr<nsIFile> resolvedFile; |
286 | 0 | bool symLink; |
287 | 0 | nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mFileURI); |
288 | 0 | if (fileURL && |
289 | 0 | NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) && |
290 | 0 | NS_SUCCEEDED(file->IsSymlink(&symLink)) && |
291 | 0 | symLink && |
292 | | #ifdef XP_WIN |
293 | | NS_SUCCEEDED(file->GetTarget(fileTarget)) && |
294 | | NS_SUCCEEDED(NS_NewLocalFile(fileTarget, true, |
295 | | getter_AddRefs(resolvedFile))) && |
296 | | #else |
297 | 0 | NS_SUCCEEDED(file->GetNativeTarget(fileTarget)) && |
298 | 0 | NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, true, |
299 | 0 | getter_AddRefs(resolvedFile))) && |
300 | 0 | #endif |
301 | 0 | NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI), |
302 | 0 | resolvedFile, nullptr))) { |
303 | 0 | // Make an effort to match up the query strings. |
304 | 0 | nsCOMPtr<nsIURL> origURL = do_QueryInterface(mFileURI); |
305 | 0 | nsCOMPtr<nsIURL> targetURL = do_QueryInterface(targetURI); |
306 | 0 | nsAutoCString queryString; |
307 | 0 | if (origURL && targetURL && NS_SUCCEEDED(origURL->GetQuery(queryString))) { |
308 | 0 | Unused << NS_MutateURI(targetURI) |
309 | 0 | .SetQuery(queryString) |
310 | 0 | .Finalize(targetURI); |
311 | 0 | } |
312 | 0 |
|
313 | 0 | SetURI(targetURI); |
314 | 0 | SetOriginalURI(mFileURI); |
315 | 0 | mLoadInfo->SetResultPrincipalURI(targetURI); |
316 | 0 | } else { |
317 | 0 | SetURI(mFileURI); |
318 | 0 | } |
319 | 0 |
|
320 | 0 | return NS_OK; |
321 | 0 | } |
322 | | |
323 | | nsresult |
324 | | nsFileChannel::MakeFileInputStream(nsIFile *file, |
325 | | nsCOMPtr<nsIInputStream> &stream, |
326 | | nsCString &contentType, |
327 | | bool async) |
328 | 0 | { |
329 | 0 | // we accept that this might result in a disk hit to stat the file |
330 | 0 | bool isDir; |
331 | 0 | nsresult rv = file->IsDirectory(&isDir); |
332 | 0 | if (NS_FAILED(rv)) { |
333 | 0 | // canonicalize error message |
334 | 0 | if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) |
335 | 0 | rv = NS_ERROR_FILE_NOT_FOUND; |
336 | 0 |
|
337 | 0 | if (async && (NS_ERROR_FILE_NOT_FOUND == rv)) { |
338 | 0 | // We don't return "Not Found" errors here. Since we could not find |
339 | 0 | // the file, it's not a directory anyway. |
340 | 0 | isDir = false; |
341 | 0 | } else { |
342 | 0 | return rv; |
343 | 0 | } |
344 | 0 | } |
345 | 0 | |
346 | 0 | if (isDir) { |
347 | 0 | rv = nsDirectoryIndexStream::Create(file, getter_AddRefs(stream)); |
348 | 0 | if (NS_SUCCEEDED(rv) && !HasContentTypeHint()) |
349 | 0 | contentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT); |
350 | 0 | } else { |
351 | 0 | rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file, -1, -1, |
352 | 0 | async? nsIFileInputStream::DEFER_OPEN : 0); |
353 | 0 | if (NS_SUCCEEDED(rv) && !HasContentTypeHint()) { |
354 | 0 | // Use file extension to infer content type |
355 | 0 | nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv); |
356 | 0 | if (NS_SUCCEEDED(rv)) { |
357 | 0 | mime->GetTypeFromFile(file, contentType); |
358 | 0 | } |
359 | 0 | } |
360 | 0 | } |
361 | 0 | return rv; |
362 | 0 | } |
363 | | |
364 | | nsresult |
365 | | nsFileChannel::OpenContentStream(bool async, nsIInputStream **result, |
366 | | nsIChannel** channel) |
367 | 0 | { |
368 | 0 | // NOTE: the resulting file is a clone, so it is safe to pass it to the |
369 | 0 | // file input stream which will be read on a background thread. |
370 | 0 | nsCOMPtr<nsIFile> file; |
371 | 0 | nsresult rv = GetFile(getter_AddRefs(file)); |
372 | 0 | if (NS_FAILED(rv)) |
373 | 0 | return rv; |
374 | 0 | |
375 | 0 | nsCOMPtr<nsIFileProtocolHandler> fileHandler; |
376 | 0 | rv = NS_GetFileProtocolHandler(getter_AddRefs(fileHandler)); |
377 | 0 | if (NS_FAILED(rv)) |
378 | 0 | return rv; |
379 | 0 | |
380 | 0 | nsCOMPtr<nsIURI> newURI; |
381 | 0 | rv = fileHandler->ReadURLFile(file, getter_AddRefs(newURI)); |
382 | 0 | if (NS_SUCCEEDED(rv)) { |
383 | 0 | nsCOMPtr<nsIChannel> newChannel; |
384 | 0 | rv = NS_NewChannel(getter_AddRefs(newChannel), |
385 | 0 | newURI, |
386 | 0 | nsContentUtils::GetSystemPrincipal(), |
387 | 0 | nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, |
388 | 0 | nsIContentPolicy::TYPE_OTHER); |
389 | 0 |
|
390 | 0 | if (NS_FAILED(rv)) |
391 | 0 | return rv; |
392 | 0 | |
393 | 0 | *result = nullptr; |
394 | 0 | newChannel.forget(channel); |
395 | 0 | return NS_OK; |
396 | 0 | } |
397 | 0 | |
398 | 0 | nsCOMPtr<nsIInputStream> stream; |
399 | 0 |
|
400 | 0 | if (mUploadStream) { |
401 | 0 | // Pass back a nsFileUploadContentStream instance that knows how to perform |
402 | 0 | // the file copy when "read" (the resulting stream in this case does not |
403 | 0 | // actually return any data). |
404 | 0 |
|
405 | 0 | nsCOMPtr<nsIOutputStream> fileStream; |
406 | 0 | rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileStream), file, |
407 | 0 | PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, |
408 | 0 | PR_IRUSR | PR_IWUSR); |
409 | 0 | if (NS_FAILED(rv)) |
410 | 0 | return rv; |
411 | 0 | |
412 | 0 | RefPtr<nsFileUploadContentStream> uploadStream = |
413 | 0 | new nsFileUploadContentStream(async, fileStream, mUploadStream, |
414 | 0 | mUploadLength, this); |
415 | 0 | if (!uploadStream || !uploadStream->IsInitialized()) { |
416 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
417 | 0 | } |
418 | 0 | stream = uploadStream.forget(); |
419 | 0 |
|
420 | 0 | mContentLength = 0; |
421 | 0 |
|
422 | 0 | // Since there isn't any content to speak of we just set the content-type |
423 | 0 | // to something other than "unknown" to avoid triggering the content-type |
424 | 0 | // sniffer code in nsBaseChannel. |
425 | 0 | // However, don't override explicitly set types. |
426 | 0 | if (!HasContentTypeHint()) |
427 | 0 | SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM)); |
428 | 0 | } else { |
429 | 0 | nsAutoCString contentType; |
430 | 0 | rv = MakeFileInputStream(file, stream, contentType, async); |
431 | 0 | if (NS_FAILED(rv)) |
432 | 0 | return rv; |
433 | 0 | |
434 | 0 | EnableSynthesizedProgressEvents(true); |
435 | 0 |
|
436 | 0 | // fixup content length and type |
437 | 0 | if (mContentLength < 0) { |
438 | 0 | int64_t size; |
439 | 0 | rv = file->GetFileSize(&size); |
440 | 0 | if (NS_FAILED(rv)) { |
441 | 0 | if (async && |
442 | 0 | (NS_ERROR_FILE_NOT_FOUND == rv || |
443 | 0 | NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv)) { |
444 | 0 | size = 0; |
445 | 0 | } else { |
446 | 0 | return rv; |
447 | 0 | } |
448 | 0 | } |
449 | 0 | mContentLength = size; |
450 | 0 | } |
451 | 0 | if (!contentType.IsEmpty()) |
452 | 0 | SetContentType(contentType); |
453 | 0 | } |
454 | 0 |
|
455 | 0 | *result = nullptr; |
456 | 0 | stream.swap(*result); |
457 | 0 | return NS_OK; |
458 | 0 | } |
459 | | |
460 | | //----------------------------------------------------------------------------- |
461 | | // nsFileChannel::nsISupports |
462 | | |
463 | | NS_IMPL_ISUPPORTS_INHERITED(nsFileChannel, |
464 | | nsBaseChannel, |
465 | | nsIUploadChannel, |
466 | | nsIFileChannel) |
467 | | |
468 | | //----------------------------------------------------------------------------- |
469 | | // nsFileChannel::nsIFileChannel |
470 | | |
471 | | NS_IMETHODIMP |
472 | | nsFileChannel::GetFile(nsIFile **file) |
473 | 0 | { |
474 | 0 | nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(URI()); |
475 | 0 | NS_ENSURE_STATE(fileURL); |
476 | 0 |
|
477 | 0 | // This returns a cloned nsIFile |
478 | 0 | return fileURL->GetFile(file); |
479 | 0 | } |
480 | | |
481 | | //----------------------------------------------------------------------------- |
482 | | // nsFileChannel::nsIUploadChannel |
483 | | |
484 | | NS_IMETHODIMP |
485 | | nsFileChannel::SetUploadStream(nsIInputStream *stream, |
486 | | const nsACString &contentType, |
487 | | int64_t contentLength) |
488 | 0 | { |
489 | 0 | NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS); |
490 | 0 |
|
491 | 0 | if ((mUploadStream = stream)) { |
492 | 0 | mUploadLength = contentLength; |
493 | 0 | if (mUploadLength < 0) { |
494 | 0 | // Make sure we know how much data we are uploading. |
495 | 0 | uint64_t avail; |
496 | 0 | nsresult rv = mUploadStream->Available(&avail); |
497 | 0 | if (NS_FAILED(rv)) |
498 | 0 | return rv; |
499 | 0 | // if this doesn't fit in the javascript MAX_SAFE_INTEGER |
500 | 0 | // pretend we don't know the size |
501 | 0 | mUploadLength = InScriptableRange(avail) ? avail : -1; |
502 | 0 | } |
503 | 0 | } else { |
504 | 0 | mUploadLength = -1; |
505 | 0 | } |
506 | 0 | return NS_OK; |
507 | 0 | } |
508 | | |
509 | | NS_IMETHODIMP |
510 | | nsFileChannel::GetUploadStream(nsIInputStream **result) |
511 | 0 | { |
512 | 0 | NS_IF_ADDREF(*result = mUploadStream); |
513 | 0 | return NS_OK; |
514 | 0 | } |