|
1 /* -*- Mode: C++; tab-width: 2; 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 |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "nsDOMFile.h" |
|
8 |
|
9 #include "nsCExternalHandlerService.h" |
|
10 #include "nsContentCID.h" |
|
11 #include "nsContentUtils.h" |
|
12 #include "nsDOMClassInfoID.h" |
|
13 #include "nsError.h" |
|
14 #include "nsICharsetDetector.h" |
|
15 #include "nsIClassInfo.h" |
|
16 #include "nsIConverterInputStream.h" |
|
17 #include "nsIDocument.h" |
|
18 #include "nsIFileStreams.h" |
|
19 #include "nsIInputStream.h" |
|
20 #include "nsIIPCSerializableInputStream.h" |
|
21 #include "nsIMemoryReporter.h" |
|
22 #include "nsIMIMEService.h" |
|
23 #include "nsISeekableStream.h" |
|
24 #include "nsIUnicharInputStream.h" |
|
25 #include "nsIUnicodeDecoder.h" |
|
26 #include "nsNetCID.h" |
|
27 #include "nsNetUtil.h" |
|
28 #include "nsIUUIDGenerator.h" |
|
29 #include "nsHostObjectProtocolHandler.h" |
|
30 #include "nsStringStream.h" |
|
31 #include "nsJSUtils.h" |
|
32 #include "nsPrintfCString.h" |
|
33 #include "mozilla/SHA1.h" |
|
34 #include "mozilla/CheckedInt.h" |
|
35 #include "mozilla/Preferences.h" |
|
36 #include "mozilla/Attributes.h" |
|
37 #include "nsThreadUtils.h" |
|
38 |
|
39 #include "mozilla/dom/FileListBinding.h" |
|
40 using namespace mozilla; |
|
41 using namespace mozilla::dom; |
|
42 |
|
43 // XXXkhuey the input stream that we pass out of a DOMFile |
|
44 // can outlive the actual DOMFile object. Thus, we must |
|
45 // ensure that the buffer underlying the stream we get |
|
46 // from NS_NewByteInputStream is held alive as long as the |
|
47 // stream is. We do that by passing back this class instead. |
|
48 class DataOwnerAdapter MOZ_FINAL : public nsIInputStream, |
|
49 public nsISeekableStream, |
|
50 public nsIIPCSerializableInputStream |
|
51 { |
|
52 typedef nsDOMMemoryFile::DataOwner DataOwner; |
|
53 public: |
|
54 static nsresult Create(DataOwner* aDataOwner, |
|
55 uint32_t aStart, |
|
56 uint32_t aLength, |
|
57 nsIInputStream** _retval); |
|
58 |
|
59 NS_DECL_THREADSAFE_ISUPPORTS |
|
60 |
|
61 // These are mandatory. |
|
62 NS_FORWARD_NSIINPUTSTREAM(mStream->) |
|
63 NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->) |
|
64 |
|
65 // This is optional. We use a conditional QI to keep it from being called |
|
66 // if the underlying stream doesn't support it. |
|
67 NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(mSerializableInputStream->) |
|
68 |
|
69 private: |
|
70 DataOwnerAdapter(DataOwner* aDataOwner, |
|
71 nsIInputStream* aStream) |
|
72 : mDataOwner(aDataOwner), mStream(aStream), |
|
73 mSeekableStream(do_QueryInterface(aStream)), |
|
74 mSerializableInputStream(do_QueryInterface(aStream)) |
|
75 { |
|
76 NS_ASSERTION(mSeekableStream, "Somebody gave us the wrong stream!"); |
|
77 } |
|
78 |
|
79 nsRefPtr<DataOwner> mDataOwner; |
|
80 nsCOMPtr<nsIInputStream> mStream; |
|
81 nsCOMPtr<nsISeekableStream> mSeekableStream; |
|
82 nsCOMPtr<nsIIPCSerializableInputStream> mSerializableInputStream; |
|
83 }; |
|
84 |
|
85 NS_IMPL_ADDREF(DataOwnerAdapter) |
|
86 NS_IMPL_RELEASE(DataOwnerAdapter) |
|
87 |
|
88 NS_INTERFACE_MAP_BEGIN(DataOwnerAdapter) |
|
89 NS_INTERFACE_MAP_ENTRY(nsIInputStream) |
|
90 NS_INTERFACE_MAP_ENTRY(nsISeekableStream) |
|
91 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, |
|
92 mSerializableInputStream) |
|
93 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) |
|
94 NS_INTERFACE_MAP_END |
|
95 |
|
96 nsresult DataOwnerAdapter::Create(DataOwner* aDataOwner, |
|
97 uint32_t aStart, |
|
98 uint32_t aLength, |
|
99 nsIInputStream** _retval) |
|
100 { |
|
101 nsresult rv; |
|
102 NS_ASSERTION(aDataOwner, "Uh ..."); |
|
103 |
|
104 nsCOMPtr<nsIInputStream> stream; |
|
105 |
|
106 rv = NS_NewByteInputStream(getter_AddRefs(stream), |
|
107 static_cast<const char*>(aDataOwner->mData) + |
|
108 aStart, |
|
109 (int32_t)aLength, |
|
110 NS_ASSIGNMENT_DEPEND); |
|
111 NS_ENSURE_SUCCESS(rv, rv); |
|
112 |
|
113 NS_ADDREF(*_retval = new DataOwnerAdapter(aDataOwner, stream)); |
|
114 |
|
115 return NS_OK; |
|
116 } |
|
117 |
|
118 //////////////////////////////////////////////////////////////////////////// |
|
119 // nsDOMFileBase implementation |
|
120 |
|
121 NS_IMETHODIMP |
|
122 nsDOMFileBase::GetName(nsAString &aFileName) |
|
123 { |
|
124 NS_ASSERTION(mIsFile, "Should only be called on files"); |
|
125 aFileName = mName; |
|
126 return NS_OK; |
|
127 } |
|
128 |
|
129 NS_IMETHODIMP |
|
130 nsDOMFileBase::GetPath(nsAString &aPath) |
|
131 { |
|
132 NS_ASSERTION(mIsFile, "Should only be called on files"); |
|
133 aPath = mPath; |
|
134 return NS_OK; |
|
135 } |
|
136 |
|
137 NS_IMETHODIMP |
|
138 nsDOMFileBase::GetLastModifiedDate(JSContext* cx, JS::MutableHandle<JS::Value> aLastModifiedDate) |
|
139 { |
|
140 JS::Rooted<JSObject*> date(cx, JS_NewDateObjectMsec(cx, JS_Now() / PR_USEC_PER_MSEC)); |
|
141 if (!date) { |
|
142 return NS_ERROR_OUT_OF_MEMORY; |
|
143 } |
|
144 aLastModifiedDate.setObject(*date); |
|
145 return NS_OK; |
|
146 } |
|
147 |
|
148 NS_IMETHODIMP |
|
149 nsDOMFileBase::GetMozFullPath(nsAString &aFileName) |
|
150 { |
|
151 NS_ASSERTION(mIsFile, "Should only be called on files"); |
|
152 |
|
153 // It is unsafe to call IsCallerChrome on a non-main thread. If |
|
154 // you hit the following assertion you need to figure out some other way to |
|
155 // determine privileges and call GetMozFullPathInternal. |
|
156 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
157 |
|
158 if (nsContentUtils::IsCallerChrome()) { |
|
159 return GetMozFullPathInternal(aFileName); |
|
160 } |
|
161 aFileName.Truncate(); |
|
162 return NS_OK; |
|
163 } |
|
164 |
|
165 NS_IMETHODIMP |
|
166 nsDOMFileBase::GetMozFullPathInternal(nsAString &aFileName) |
|
167 { |
|
168 if (!mIsFile) { |
|
169 return NS_ERROR_FAILURE; |
|
170 } |
|
171 |
|
172 aFileName.Truncate(); |
|
173 return NS_OK; |
|
174 } |
|
175 |
|
176 NS_IMETHODIMP |
|
177 nsDOMFileBase::GetSize(uint64_t *aSize) |
|
178 { |
|
179 *aSize = mLength; |
|
180 return NS_OK; |
|
181 } |
|
182 |
|
183 NS_IMETHODIMP |
|
184 nsDOMFileBase::GetType(nsAString &aType) |
|
185 { |
|
186 aType = mContentType; |
|
187 return NS_OK; |
|
188 } |
|
189 |
|
190 NS_IMETHODIMP |
|
191 nsDOMFileBase::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) |
|
192 { |
|
193 NS_ASSERTION(mIsFile, "Should only be called on files"); |
|
194 if (IsDateUnknown()) { |
|
195 mLastModificationDate = PR_Now(); |
|
196 } |
|
197 *aLastModifiedDate = mLastModificationDate; |
|
198 return NS_OK; |
|
199 } |
|
200 |
|
201 // Makes sure that aStart and aEnd is less then or equal to aSize and greater |
|
202 // than 0 |
|
203 static void |
|
204 ParseSize(int64_t aSize, int64_t& aStart, int64_t& aEnd) |
|
205 { |
|
206 CheckedInt64 newStartOffset = aStart; |
|
207 if (aStart < -aSize) { |
|
208 newStartOffset = 0; |
|
209 } |
|
210 else if (aStart < 0) { |
|
211 newStartOffset += aSize; |
|
212 } |
|
213 else if (aStart > aSize) { |
|
214 newStartOffset = aSize; |
|
215 } |
|
216 |
|
217 CheckedInt64 newEndOffset = aEnd; |
|
218 if (aEnd < -aSize) { |
|
219 newEndOffset = 0; |
|
220 } |
|
221 else if (aEnd < 0) { |
|
222 newEndOffset += aSize; |
|
223 } |
|
224 else if (aEnd > aSize) { |
|
225 newEndOffset = aSize; |
|
226 } |
|
227 |
|
228 if (!newStartOffset.isValid() || !newEndOffset.isValid() || |
|
229 newStartOffset.value() >= newEndOffset.value()) { |
|
230 aStart = aEnd = 0; |
|
231 } |
|
232 else { |
|
233 aStart = newStartOffset.value(); |
|
234 aEnd = newEndOffset.value(); |
|
235 } |
|
236 } |
|
237 |
|
238 NS_IMETHODIMP |
|
239 nsDOMFileBase::Slice(int64_t aStart, int64_t aEnd, |
|
240 const nsAString& aContentType, uint8_t optional_argc, |
|
241 nsIDOMBlob **aBlob) |
|
242 { |
|
243 *aBlob = nullptr; |
|
244 |
|
245 // Truncate aStart and aEnd so that we stay within this file. |
|
246 uint64_t thisLength; |
|
247 nsresult rv = GetSize(&thisLength); |
|
248 NS_ENSURE_SUCCESS(rv, rv); |
|
249 |
|
250 if (optional_argc < 2) { |
|
251 aEnd = (int64_t)thisLength; |
|
252 } |
|
253 |
|
254 ParseSize((int64_t)thisLength, aStart, aEnd); |
|
255 |
|
256 // Create the new file |
|
257 *aBlob = CreateSlice((uint64_t)aStart, (uint64_t)(aEnd - aStart), |
|
258 aContentType).take(); |
|
259 |
|
260 return *aBlob ? NS_OK : NS_ERROR_UNEXPECTED; |
|
261 } |
|
262 |
|
263 NS_IMETHODIMP |
|
264 nsDOMFileBase::GetInternalStream(nsIInputStream **aStream) |
|
265 { |
|
266 // Must be overridden |
|
267 NS_NOTREACHED("Must override GetInternalStream"); |
|
268 |
|
269 return NS_ERROR_NOT_IMPLEMENTED; |
|
270 } |
|
271 |
|
272 NS_IMETHODIMP |
|
273 nsDOMFileBase::GetInternalUrl(nsIPrincipal* aPrincipal, nsAString& aURL) |
|
274 { |
|
275 NS_ENSURE_STATE(aPrincipal); |
|
276 |
|
277 nsCString url; |
|
278 nsresult rv = nsBlobProtocolHandler::AddDataEntry( |
|
279 NS_LITERAL_CSTRING(BLOBURI_SCHEME), |
|
280 static_cast<nsIDOMBlob*>(this), aPrincipal, url); |
|
281 if (NS_FAILED(rv)) { |
|
282 return rv; |
|
283 } |
|
284 |
|
285 CopyASCIItoUTF16(url, aURL); |
|
286 return NS_OK; |
|
287 } |
|
288 |
|
289 NS_IMETHODIMP_(int64_t) |
|
290 nsDOMFileBase::GetFileId() |
|
291 { |
|
292 int64_t id = -1; |
|
293 |
|
294 if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) { |
|
295 if (!indexedDB::IndexedDatabaseManager::IsClosed()) { |
|
296 indexedDB::IndexedDatabaseManager::FileMutex().Lock(); |
|
297 } |
|
298 |
|
299 NS_ASSERTION(!mFileInfos.IsEmpty(), |
|
300 "A stored file must have at least one file info!"); |
|
301 |
|
302 nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(0); |
|
303 if (fileInfo) { |
|
304 id = fileInfo->Id(); |
|
305 } |
|
306 |
|
307 if (!indexedDB::IndexedDatabaseManager::IsClosed()) { |
|
308 indexedDB::IndexedDatabaseManager::FileMutex().Unlock(); |
|
309 } |
|
310 } |
|
311 |
|
312 return id; |
|
313 } |
|
314 |
|
315 NS_IMETHODIMP_(void) |
|
316 nsDOMFileBase::AddFileInfo(indexedDB::FileInfo* aFileInfo) |
|
317 { |
|
318 if (indexedDB::IndexedDatabaseManager::IsClosed()) { |
|
319 NS_ERROR("Shouldn't be called after shutdown!"); |
|
320 return; |
|
321 } |
|
322 |
|
323 nsRefPtr<indexedDB::FileInfo> fileInfo = aFileInfo; |
|
324 |
|
325 MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex()); |
|
326 |
|
327 NS_ASSERTION(!mFileInfos.Contains(aFileInfo), |
|
328 "Adding the same file info agan?!"); |
|
329 |
|
330 nsRefPtr<indexedDB::FileInfo>* element = mFileInfos.AppendElement(); |
|
331 element->swap(fileInfo); |
|
332 } |
|
333 |
|
334 NS_IMETHODIMP_(indexedDB::FileInfo*) |
|
335 nsDOMFileBase::GetFileInfo(indexedDB::FileManager* aFileManager) |
|
336 { |
|
337 if (indexedDB::IndexedDatabaseManager::IsClosed()) { |
|
338 NS_ERROR("Shouldn't be called after shutdown!"); |
|
339 return nullptr; |
|
340 } |
|
341 |
|
342 // A slice created from a stored file must keep the file info alive. |
|
343 // However, we don't support sharing of slices yet, so the slice must be |
|
344 // copied again. That's why we have to ignore the first file info. |
|
345 // Snapshots are handled in a similar way (they have to be copied). |
|
346 uint32_t startIndex; |
|
347 if (IsStoredFile() && (!IsWholeFile() || IsSnapshot())) { |
|
348 startIndex = 1; |
|
349 } |
|
350 else { |
|
351 startIndex = 0; |
|
352 } |
|
353 |
|
354 MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex()); |
|
355 |
|
356 for (uint32_t i = startIndex; i < mFileInfos.Length(); i++) { |
|
357 nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(i); |
|
358 if (fileInfo->Manager() == aFileManager) { |
|
359 return fileInfo; |
|
360 } |
|
361 } |
|
362 |
|
363 return nullptr; |
|
364 } |
|
365 |
|
366 NS_IMETHODIMP |
|
367 nsDOMFileBase::GetSendInfo(nsIInputStream** aBody, |
|
368 uint64_t* aContentLength, |
|
369 nsACString& aContentType, |
|
370 nsACString& aCharset) |
|
371 { |
|
372 nsresult rv; |
|
373 |
|
374 nsCOMPtr<nsIInputStream> stream; |
|
375 rv = this->GetInternalStream(getter_AddRefs(stream)); |
|
376 NS_ENSURE_SUCCESS(rv, rv); |
|
377 |
|
378 rv = this->GetSize(aContentLength); |
|
379 NS_ENSURE_SUCCESS(rv, rv); |
|
380 |
|
381 nsString contentType; |
|
382 rv = this->GetType(contentType); |
|
383 NS_ENSURE_SUCCESS(rv, rv); |
|
384 |
|
385 CopyUTF16toUTF8(contentType, aContentType); |
|
386 |
|
387 aCharset.Truncate(); |
|
388 |
|
389 stream.forget(aBody); |
|
390 return NS_OK; |
|
391 } |
|
392 |
|
393 NS_IMETHODIMP |
|
394 nsDOMFileBase::GetMutable(bool* aMutable) |
|
395 { |
|
396 *aMutable = !mImmutable; |
|
397 return NS_OK; |
|
398 } |
|
399 |
|
400 NS_IMETHODIMP |
|
401 nsDOMFileBase::SetMutable(bool aMutable) |
|
402 { |
|
403 nsresult rv = NS_OK; |
|
404 |
|
405 NS_ENSURE_ARG(!mImmutable || !aMutable); |
|
406 |
|
407 if (!mImmutable && !aMutable) { |
|
408 // Force the content type and size to be cached |
|
409 nsString dummyString; |
|
410 rv = this->GetType(dummyString); |
|
411 NS_ENSURE_SUCCESS(rv, rv); |
|
412 |
|
413 uint64_t dummyInt; |
|
414 rv = this->GetSize(&dummyInt); |
|
415 NS_ENSURE_SUCCESS(rv, rv); |
|
416 } |
|
417 |
|
418 mImmutable = !aMutable; |
|
419 return rv; |
|
420 } |
|
421 |
|
422 NS_IMETHODIMP_(bool) |
|
423 nsDOMFileBase::IsMemoryFile(void) |
|
424 { |
|
425 return false; |
|
426 } |
|
427 |
|
428 //////////////////////////////////////////////////////////////////////////// |
|
429 // nsDOMFile implementation |
|
430 |
|
431 DOMCI_DATA(File, nsDOMFile) |
|
432 DOMCI_DATA(Blob, nsDOMFile) |
|
433 |
|
434 NS_INTERFACE_MAP_BEGIN(nsDOMFile) |
|
435 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile) |
|
436 NS_INTERFACE_MAP_ENTRY(nsIDOMBlob) |
|
437 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile) |
|
438 NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) |
|
439 NS_INTERFACE_MAP_ENTRY(nsIMutable) |
|
440 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile) |
|
441 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile) |
|
442 NS_INTERFACE_MAP_END |
|
443 |
|
444 // Threadsafe when GetMutable() == false |
|
445 NS_IMPL_ADDREF(nsDOMFile) |
|
446 NS_IMPL_RELEASE(nsDOMFile) |
|
447 |
|
448 //////////////////////////////////////////////////////////////////////////// |
|
449 // nsDOMFileCC implementation |
|
450 |
|
451 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileCC) |
|
452 |
|
453 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsDOMFileCC) |
|
454 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMFileCC) |
|
455 // We don't have anything to traverse, but some of our subclasses do. |
|
456 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
457 |
|
458 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileCC) |
|
459 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile) |
|
460 NS_INTERFACE_MAP_ENTRY(nsIDOMBlob) |
|
461 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile) |
|
462 NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) |
|
463 NS_INTERFACE_MAP_ENTRY(nsIMutable) |
|
464 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile) |
|
465 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile) |
|
466 NS_INTERFACE_MAP_END |
|
467 |
|
468 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileCC) |
|
469 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileCC) |
|
470 |
|
471 //////////////////////////////////////////////////////////////////////////// |
|
472 // nsDOMFileFile implementation |
|
473 |
|
474 already_AddRefed<nsIDOMBlob> |
|
475 nsDOMFileFile::CreateSlice(uint64_t aStart, uint64_t aLength, |
|
476 const nsAString& aContentType) |
|
477 { |
|
478 nsCOMPtr<nsIDOMBlob> t = new nsDOMFileFile(this, aStart, aLength, aContentType); |
|
479 return t.forget(); |
|
480 } |
|
481 |
|
482 NS_IMETHODIMP |
|
483 nsDOMFileFile::GetMozFullPathInternal(nsAString &aFilename) |
|
484 { |
|
485 NS_ASSERTION(mIsFile, "Should only be called on files"); |
|
486 return mFile->GetPath(aFilename); |
|
487 } |
|
488 |
|
489 NS_IMETHODIMP |
|
490 nsDOMFileFile::GetLastModifiedDate(JSContext* cx, JS::MutableHandle<JS::Value> aLastModifiedDate) |
|
491 { |
|
492 NS_ASSERTION(mIsFile, "Should only be called on files"); |
|
493 |
|
494 PRTime msecs; |
|
495 if (IsDateUnknown()) { |
|
496 nsresult rv = mFile->GetLastModifiedTime(&msecs); |
|
497 NS_ENSURE_SUCCESS(rv, rv); |
|
498 mLastModificationDate = msecs; |
|
499 } else { |
|
500 msecs = mLastModificationDate; |
|
501 } |
|
502 |
|
503 JSObject* date = JS_NewDateObjectMsec(cx, msecs); |
|
504 if (date) { |
|
505 aLastModifiedDate.setObject(*date); |
|
506 } |
|
507 else { |
|
508 date = JS_NewDateObjectMsec(cx, JS_Now() / PR_USEC_PER_MSEC); |
|
509 aLastModifiedDate.setObject(*date); |
|
510 } |
|
511 |
|
512 return NS_OK; |
|
513 } |
|
514 |
|
515 NS_IMETHODIMP |
|
516 nsDOMFileFile::GetSize(uint64_t *aFileSize) |
|
517 { |
|
518 if (IsSizeUnknown()) { |
|
519 NS_ASSERTION(mWholeFile, |
|
520 "Should only use lazy size when using the whole file"); |
|
521 int64_t fileSize; |
|
522 nsresult rv = mFile->GetFileSize(&fileSize); |
|
523 NS_ENSURE_SUCCESS(rv, rv); |
|
524 |
|
525 if (fileSize < 0) { |
|
526 return NS_ERROR_FAILURE; |
|
527 } |
|
528 |
|
529 mLength = fileSize; |
|
530 } |
|
531 |
|
532 *aFileSize = mLength; |
|
533 |
|
534 return NS_OK; |
|
535 } |
|
536 |
|
537 NS_IMETHODIMP |
|
538 nsDOMFileFile::GetType(nsAString &aType) |
|
539 { |
|
540 if (mContentType.IsVoid()) { |
|
541 NS_ASSERTION(mWholeFile, |
|
542 "Should only use lazy ContentType when using the whole file"); |
|
543 nsresult rv; |
|
544 nsCOMPtr<nsIMIMEService> mimeService = |
|
545 do_GetService(NS_MIMESERVICE_CONTRACTID, &rv); |
|
546 NS_ENSURE_SUCCESS(rv, rv); |
|
547 |
|
548 nsAutoCString mimeType; |
|
549 rv = mimeService->GetTypeFromFile(mFile, mimeType); |
|
550 if (NS_FAILED(rv)) { |
|
551 mimeType.Truncate(); |
|
552 } |
|
553 |
|
554 AppendUTF8toUTF16(mimeType, mContentType); |
|
555 mContentType.SetIsVoid(false); |
|
556 } |
|
557 |
|
558 aType = mContentType; |
|
559 |
|
560 return NS_OK; |
|
561 } |
|
562 |
|
563 NS_IMETHODIMP |
|
564 nsDOMFileFile::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) |
|
565 { |
|
566 NS_ASSERTION(mIsFile, "Should only be called on files"); |
|
567 if (IsDateUnknown()) { |
|
568 PRTime msecs; |
|
569 nsresult rv = mFile->GetLastModifiedTime(&msecs); |
|
570 NS_ENSURE_SUCCESS(rv, rv); |
|
571 mLastModificationDate = msecs; |
|
572 } |
|
573 *aLastModifiedDate = mLastModificationDate; |
|
574 return NS_OK; |
|
575 } |
|
576 |
|
577 const uint32_t sFileStreamFlags = |
|
578 nsIFileInputStream::CLOSE_ON_EOF | |
|
579 nsIFileInputStream::REOPEN_ON_REWIND | |
|
580 nsIFileInputStream::DEFER_OPEN; |
|
581 |
|
582 NS_IMETHODIMP |
|
583 nsDOMFileFile::GetInternalStream(nsIInputStream **aStream) |
|
584 { |
|
585 return mWholeFile ? |
|
586 NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags) : |
|
587 NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength, |
|
588 -1, -1, sFileStreamFlags); |
|
589 } |
|
590 |
|
591 void |
|
592 nsDOMFileFile::SetPath(const nsAString& aPath) |
|
593 { |
|
594 MOZ_ASSERT(aPath.IsEmpty() || |
|
595 aPath[aPath.Length() - 1] == char16_t('/'), |
|
596 "Path must end with a path separator"); |
|
597 mPath = aPath; |
|
598 } |
|
599 |
|
600 //////////////////////////////////////////////////////////////////////////// |
|
601 // nsDOMMemoryFile implementation |
|
602 |
|
603 already_AddRefed<nsIDOMBlob> |
|
604 nsDOMMemoryFile::CreateSlice(uint64_t aStart, uint64_t aLength, |
|
605 const nsAString& aContentType) |
|
606 { |
|
607 nsCOMPtr<nsIDOMBlob> t = |
|
608 new nsDOMMemoryFile(this, aStart, aLength, aContentType); |
|
609 return t.forget(); |
|
610 } |
|
611 |
|
612 NS_IMETHODIMP |
|
613 nsDOMMemoryFile::GetInternalStream(nsIInputStream **aStream) |
|
614 { |
|
615 if (mLength > INT32_MAX) |
|
616 return NS_ERROR_FAILURE; |
|
617 |
|
618 return DataOwnerAdapter::Create(mDataOwner, mStart, mLength, aStream); |
|
619 } |
|
620 |
|
621 NS_IMETHODIMP_(bool) |
|
622 nsDOMMemoryFile::IsMemoryFile(void) |
|
623 { |
|
624 return true; |
|
625 } |
|
626 |
|
627 /* static */ StaticMutex |
|
628 nsDOMMemoryFile::DataOwner::sDataOwnerMutex; |
|
629 |
|
630 /* static */ StaticAutoPtr<LinkedList<nsDOMMemoryFile::DataOwner> > |
|
631 nsDOMMemoryFile::DataOwner::sDataOwners; |
|
632 |
|
633 /* static */ bool |
|
634 nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered; |
|
635 |
|
636 MOZ_DEFINE_MALLOC_SIZE_OF(DOMMemoryFileDataOwnerMallocSizeOf) |
|
637 |
|
638 class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL |
|
639 : public nsIMemoryReporter |
|
640 { |
|
641 public: |
|
642 NS_DECL_THREADSAFE_ISUPPORTS |
|
643 |
|
644 NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback, |
|
645 nsISupports *aClosure) |
|
646 { |
|
647 typedef nsDOMMemoryFile::DataOwner DataOwner; |
|
648 |
|
649 StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex); |
|
650 |
|
651 if (!DataOwner::sDataOwners) { |
|
652 return NS_OK; |
|
653 } |
|
654 |
|
655 const size_t LARGE_OBJECT_MIN_SIZE = 8 * 1024; |
|
656 size_t smallObjectsTotal = 0; |
|
657 |
|
658 for (DataOwner *owner = DataOwner::sDataOwners->getFirst(); |
|
659 owner; owner = owner->getNext()) { |
|
660 |
|
661 size_t size = DOMMemoryFileDataOwnerMallocSizeOf(owner->mData); |
|
662 |
|
663 if (size < LARGE_OBJECT_MIN_SIZE) { |
|
664 smallObjectsTotal += size; |
|
665 } |
|
666 else { |
|
667 SHA1Sum sha1; |
|
668 sha1.update(owner->mData, owner->mLength); |
|
669 uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long. |
|
670 sha1.finish(digest); |
|
671 |
|
672 nsAutoCString digestString; |
|
673 for (size_t i = 0; i < sizeof(digest); i++) { |
|
674 digestString.AppendPrintf("%02x", digest[i]); |
|
675 } |
|
676 |
|
677 nsresult rv = aCallback->Callback( |
|
678 /* process */ NS_LITERAL_CSTRING(""), |
|
679 nsPrintfCString( |
|
680 "explicit/dom/memory-file-data/large/file(length=%llu, sha1=%s)", |
|
681 owner->mLength, digestString.get()), |
|
682 KIND_HEAP, UNITS_BYTES, size, |
|
683 nsPrintfCString( |
|
684 "Memory used to back a memory file of length %llu bytes. The file " |
|
685 "has a sha1 of %s.\n\n" |
|
686 "Note that the allocator may round up a memory file's length -- " |
|
687 "that is, an N-byte memory file may take up more than N bytes of " |
|
688 "memory.", |
|
689 owner->mLength, digestString.get()), |
|
690 aClosure); |
|
691 NS_ENSURE_SUCCESS(rv, rv); |
|
692 } |
|
693 } |
|
694 |
|
695 if (smallObjectsTotal > 0) { |
|
696 nsresult rv = aCallback->Callback( |
|
697 /* process */ NS_LITERAL_CSTRING(""), |
|
698 NS_LITERAL_CSTRING("explicit/dom/memory-file-data/small"), |
|
699 KIND_HEAP, UNITS_BYTES, smallObjectsTotal, |
|
700 nsPrintfCString( |
|
701 "Memory used to back small memory files (less than %d bytes each).\n\n" |
|
702 "Note that the allocator may round up a memory file's length -- " |
|
703 "that is, an N-byte memory file may take up more than N bytes of " |
|
704 "memory."), |
|
705 aClosure); |
|
706 NS_ENSURE_SUCCESS(rv, rv); |
|
707 } |
|
708 |
|
709 return NS_OK; |
|
710 } |
|
711 }; |
|
712 |
|
713 NS_IMPL_ISUPPORTS(nsDOMMemoryFileDataOwnerMemoryReporter, nsIMemoryReporter) |
|
714 |
|
715 /* static */ void |
|
716 nsDOMMemoryFile::DataOwner::EnsureMemoryReporterRegistered() |
|
717 { |
|
718 sDataOwnerMutex.AssertCurrentThreadOwns(); |
|
719 if (sMemoryReporterRegistered) { |
|
720 return; |
|
721 } |
|
722 |
|
723 RegisterStrongMemoryReporter(new nsDOMMemoryFileDataOwnerMemoryReporter()); |
|
724 |
|
725 sMemoryReporterRegistered = true; |
|
726 } |
|
727 |
|
728 //////////////////////////////////////////////////////////////////////////// |
|
729 // nsDOMFileList implementation |
|
730 |
|
731 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMFileList) |
|
732 |
|
733 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileList) |
|
734 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
735 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFileList) |
|
736 NS_INTERFACE_MAP_ENTRY(nsIDOMFileList) |
|
737 NS_INTERFACE_MAP_END |
|
738 |
|
739 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileList) |
|
740 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileList) |
|
741 |
|
742 JSObject* |
|
743 nsDOMFileList::WrapObject(JSContext *cx) |
|
744 { |
|
745 return FileListBinding::Wrap(cx, this); |
|
746 } |
|
747 |
|
748 NS_IMETHODIMP |
|
749 nsDOMFileList::GetLength(uint32_t* aLength) |
|
750 { |
|
751 *aLength = Length(); |
|
752 |
|
753 return NS_OK; |
|
754 } |
|
755 |
|
756 NS_IMETHODIMP |
|
757 nsDOMFileList::Item(uint32_t aIndex, nsIDOMFile **aFile) |
|
758 { |
|
759 NS_IF_ADDREF(*aFile = Item(aIndex)); |
|
760 |
|
761 return NS_OK; |
|
762 } |
|
763 |
|
764 //////////////////////////////////////////////////////////////////////////// |
|
765 // nsDOMFileInternalUrlHolder implementation |
|
766 |
|
767 nsDOMFileInternalUrlHolder::nsDOMFileInternalUrlHolder(nsIDOMBlob* aFile, |
|
768 nsIPrincipal* aPrincipal |
|
769 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) { |
|
770 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
771 aFile->GetInternalUrl(aPrincipal, mUrl); |
|
772 } |
|
773 |
|
774 nsDOMFileInternalUrlHolder::~nsDOMFileInternalUrlHolder() { |
|
775 if (!mUrl.IsEmpty()) { |
|
776 nsAutoCString narrowUrl; |
|
777 CopyUTF16toUTF8(mUrl, narrowUrl); |
|
778 nsBlobProtocolHandler::RemoveDataEntry(narrowUrl); |
|
779 } |
|
780 } |
|
781 |
|
782 //////////////////////////////////////////////////////////////////////////// |
|
783 // nsDOMTemporaryFileBlob implementation |
|
784 already_AddRefed<nsIDOMBlob> |
|
785 nsDOMTemporaryFileBlob::CreateSlice(uint64_t aStart, uint64_t aLength, |
|
786 const nsAString& aContentType) |
|
787 { |
|
788 if (aStart + aLength > mLength) |
|
789 return nullptr; |
|
790 |
|
791 nsCOMPtr<nsIDOMBlob> t = |
|
792 new nsDOMTemporaryFileBlob(this, aStart + mStartPos, aLength, aContentType); |
|
793 return t.forget(); |
|
794 } |
|
795 |
|
796 NS_IMETHODIMP |
|
797 nsDOMTemporaryFileBlob::GetInternalStream(nsIInputStream **aStream) |
|
798 { |
|
799 nsCOMPtr<nsIInputStream> stream = |
|
800 new nsTemporaryFileInputStream(mFileDescOwner, mStartPos, mStartPos + mLength); |
|
801 stream.forget(aStream); |
|
802 return NS_OK; |
|
803 } |