Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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/. */
7 #include "nsDOMFile.h"
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"
39 #include "mozilla/dom/FileListBinding.h"
40 using namespace mozilla;
41 using namespace mozilla::dom;
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);
59 NS_DECL_THREADSAFE_ISUPPORTS
61 // These are mandatory.
62 NS_FORWARD_NSIINPUTSTREAM(mStream->)
63 NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->)
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->)
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 }
79 nsRefPtr<DataOwner> mDataOwner;
80 nsCOMPtr<nsIInputStream> mStream;
81 nsCOMPtr<nsISeekableStream> mSeekableStream;
82 nsCOMPtr<nsIIPCSerializableInputStream> mSerializableInputStream;
83 };
85 NS_IMPL_ADDREF(DataOwnerAdapter)
86 NS_IMPL_RELEASE(DataOwnerAdapter)
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
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 ...");
104 nsCOMPtr<nsIInputStream> stream;
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);
113 NS_ADDREF(*_retval = new DataOwnerAdapter(aDataOwner, stream));
115 return NS_OK;
116 }
118 ////////////////////////////////////////////////////////////////////////////
119 // nsDOMFileBase implementation
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 }
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 }
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 }
148 NS_IMETHODIMP
149 nsDOMFileBase::GetMozFullPath(nsAString &aFileName)
150 {
151 NS_ASSERTION(mIsFile, "Should only be called on files");
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!");
158 if (nsContentUtils::IsCallerChrome()) {
159 return GetMozFullPathInternal(aFileName);
160 }
161 aFileName.Truncate();
162 return NS_OK;
163 }
165 NS_IMETHODIMP
166 nsDOMFileBase::GetMozFullPathInternal(nsAString &aFileName)
167 {
168 if (!mIsFile) {
169 return NS_ERROR_FAILURE;
170 }
172 aFileName.Truncate();
173 return NS_OK;
174 }
176 NS_IMETHODIMP
177 nsDOMFileBase::GetSize(uint64_t *aSize)
178 {
179 *aSize = mLength;
180 return NS_OK;
181 }
183 NS_IMETHODIMP
184 nsDOMFileBase::GetType(nsAString &aType)
185 {
186 aType = mContentType;
187 return NS_OK;
188 }
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 }
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 }
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 }
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 }
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;
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);
250 if (optional_argc < 2) {
251 aEnd = (int64_t)thisLength;
252 }
254 ParseSize((int64_t)thisLength, aStart, aEnd);
256 // Create the new file
257 *aBlob = CreateSlice((uint64_t)aStart, (uint64_t)(aEnd - aStart),
258 aContentType).take();
260 return *aBlob ? NS_OK : NS_ERROR_UNEXPECTED;
261 }
263 NS_IMETHODIMP
264 nsDOMFileBase::GetInternalStream(nsIInputStream **aStream)
265 {
266 // Must be overridden
267 NS_NOTREACHED("Must override GetInternalStream");
269 return NS_ERROR_NOT_IMPLEMENTED;
270 }
272 NS_IMETHODIMP
273 nsDOMFileBase::GetInternalUrl(nsIPrincipal* aPrincipal, nsAString& aURL)
274 {
275 NS_ENSURE_STATE(aPrincipal);
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 }
285 CopyASCIItoUTF16(url, aURL);
286 return NS_OK;
287 }
289 NS_IMETHODIMP_(int64_t)
290 nsDOMFileBase::GetFileId()
291 {
292 int64_t id = -1;
294 if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) {
295 if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
296 indexedDB::IndexedDatabaseManager::FileMutex().Lock();
297 }
299 NS_ASSERTION(!mFileInfos.IsEmpty(),
300 "A stored file must have at least one file info!");
302 nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(0);
303 if (fileInfo) {
304 id = fileInfo->Id();
305 }
307 if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
308 indexedDB::IndexedDatabaseManager::FileMutex().Unlock();
309 }
310 }
312 return id;
313 }
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 }
323 nsRefPtr<indexedDB::FileInfo> fileInfo = aFileInfo;
325 MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
327 NS_ASSERTION(!mFileInfos.Contains(aFileInfo),
328 "Adding the same file info agan?!");
330 nsRefPtr<indexedDB::FileInfo>* element = mFileInfos.AppendElement();
331 element->swap(fileInfo);
332 }
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 }
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 }
354 MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
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 }
363 return nullptr;
364 }
366 NS_IMETHODIMP
367 nsDOMFileBase::GetSendInfo(nsIInputStream** aBody,
368 uint64_t* aContentLength,
369 nsACString& aContentType,
370 nsACString& aCharset)
371 {
372 nsresult rv;
374 nsCOMPtr<nsIInputStream> stream;
375 rv = this->GetInternalStream(getter_AddRefs(stream));
376 NS_ENSURE_SUCCESS(rv, rv);
378 rv = this->GetSize(aContentLength);
379 NS_ENSURE_SUCCESS(rv, rv);
381 nsString contentType;
382 rv = this->GetType(contentType);
383 NS_ENSURE_SUCCESS(rv, rv);
385 CopyUTF16toUTF8(contentType, aContentType);
387 aCharset.Truncate();
389 stream.forget(aBody);
390 return NS_OK;
391 }
393 NS_IMETHODIMP
394 nsDOMFileBase::GetMutable(bool* aMutable)
395 {
396 *aMutable = !mImmutable;
397 return NS_OK;
398 }
400 NS_IMETHODIMP
401 nsDOMFileBase::SetMutable(bool aMutable)
402 {
403 nsresult rv = NS_OK;
405 NS_ENSURE_ARG(!mImmutable || !aMutable);
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);
413 uint64_t dummyInt;
414 rv = this->GetSize(&dummyInt);
415 NS_ENSURE_SUCCESS(rv, rv);
416 }
418 mImmutable = !aMutable;
419 return rv;
420 }
422 NS_IMETHODIMP_(bool)
423 nsDOMFileBase::IsMemoryFile(void)
424 {
425 return false;
426 }
428 ////////////////////////////////////////////////////////////////////////////
429 // nsDOMFile implementation
431 DOMCI_DATA(File, nsDOMFile)
432 DOMCI_DATA(Blob, nsDOMFile)
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
444 // Threadsafe when GetMutable() == false
445 NS_IMPL_ADDREF(nsDOMFile)
446 NS_IMPL_RELEASE(nsDOMFile)
448 ////////////////////////////////////////////////////////////////////////////
449 // nsDOMFileCC implementation
451 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileCC)
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
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
468 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileCC)
469 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileCC)
471 ////////////////////////////////////////////////////////////////////////////
472 // nsDOMFileFile implementation
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 }
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 }
489 NS_IMETHODIMP
490 nsDOMFileFile::GetLastModifiedDate(JSContext* cx, JS::MutableHandle<JS::Value> aLastModifiedDate)
491 {
492 NS_ASSERTION(mIsFile, "Should only be called on files");
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 }
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 }
512 return NS_OK;
513 }
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);
525 if (fileSize < 0) {
526 return NS_ERROR_FAILURE;
527 }
529 mLength = fileSize;
530 }
532 *aFileSize = mLength;
534 return NS_OK;
535 }
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);
548 nsAutoCString mimeType;
549 rv = mimeService->GetTypeFromFile(mFile, mimeType);
550 if (NS_FAILED(rv)) {
551 mimeType.Truncate();
552 }
554 AppendUTF8toUTF16(mimeType, mContentType);
555 mContentType.SetIsVoid(false);
556 }
558 aType = mContentType;
560 return NS_OK;
561 }
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 }
577 const uint32_t sFileStreamFlags =
578 nsIFileInputStream::CLOSE_ON_EOF |
579 nsIFileInputStream::REOPEN_ON_REWIND |
580 nsIFileInputStream::DEFER_OPEN;
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 }
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 }
600 ////////////////////////////////////////////////////////////////////////////
601 // nsDOMMemoryFile implementation
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 }
612 NS_IMETHODIMP
613 nsDOMMemoryFile::GetInternalStream(nsIInputStream **aStream)
614 {
615 if (mLength > INT32_MAX)
616 return NS_ERROR_FAILURE;
618 return DataOwnerAdapter::Create(mDataOwner, mStart, mLength, aStream);
619 }
621 NS_IMETHODIMP_(bool)
622 nsDOMMemoryFile::IsMemoryFile(void)
623 {
624 return true;
625 }
627 /* static */ StaticMutex
628 nsDOMMemoryFile::DataOwner::sDataOwnerMutex;
630 /* static */ StaticAutoPtr<LinkedList<nsDOMMemoryFile::DataOwner> >
631 nsDOMMemoryFile::DataOwner::sDataOwners;
633 /* static */ bool
634 nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered;
636 MOZ_DEFINE_MALLOC_SIZE_OF(DOMMemoryFileDataOwnerMallocSizeOf)
638 class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL
639 : public nsIMemoryReporter
640 {
641 public:
642 NS_DECL_THREADSAFE_ISUPPORTS
644 NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback,
645 nsISupports *aClosure)
646 {
647 typedef nsDOMMemoryFile::DataOwner DataOwner;
649 StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
651 if (!DataOwner::sDataOwners) {
652 return NS_OK;
653 }
655 const size_t LARGE_OBJECT_MIN_SIZE = 8 * 1024;
656 size_t smallObjectsTotal = 0;
658 for (DataOwner *owner = DataOwner::sDataOwners->getFirst();
659 owner; owner = owner->getNext()) {
661 size_t size = DOMMemoryFileDataOwnerMallocSizeOf(owner->mData);
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);
672 nsAutoCString digestString;
673 for (size_t i = 0; i < sizeof(digest); i++) {
674 digestString.AppendPrintf("%02x", digest[i]);
675 }
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 }
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 }
709 return NS_OK;
710 }
711 };
713 NS_IMPL_ISUPPORTS(nsDOMMemoryFileDataOwnerMemoryReporter, nsIMemoryReporter)
715 /* static */ void
716 nsDOMMemoryFile::DataOwner::EnsureMemoryReporterRegistered()
717 {
718 sDataOwnerMutex.AssertCurrentThreadOwns();
719 if (sMemoryReporterRegistered) {
720 return;
721 }
723 RegisterStrongMemoryReporter(new nsDOMMemoryFileDataOwnerMemoryReporter());
725 sMemoryReporterRegistered = true;
726 }
728 ////////////////////////////////////////////////////////////////////////////
729 // nsDOMFileList implementation
731 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMFileList)
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
739 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileList)
740 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileList)
742 JSObject*
743 nsDOMFileList::WrapObject(JSContext *cx)
744 {
745 return FileListBinding::Wrap(cx, this);
746 }
748 NS_IMETHODIMP
749 nsDOMFileList::GetLength(uint32_t* aLength)
750 {
751 *aLength = Length();
753 return NS_OK;
754 }
756 NS_IMETHODIMP
757 nsDOMFileList::Item(uint32_t aIndex, nsIDOMFile **aFile)
758 {
759 NS_IF_ADDREF(*aFile = Item(aIndex));
761 return NS_OK;
762 }
764 ////////////////////////////////////////////////////////////////////////////
765 // nsDOMFileInternalUrlHolder implementation
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 }
774 nsDOMFileInternalUrlHolder::~nsDOMFileInternalUrlHolder() {
775 if (!mUrl.IsEmpty()) {
776 nsAutoCString narrowUrl;
777 CopyUTF16toUTF8(mUrl, narrowUrl);
778 nsBlobProtocolHandler::RemoveDataEntry(narrowUrl);
779 }
780 }
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;
791 nsCOMPtr<nsIDOMBlob> t =
792 new nsDOMTemporaryFileBlob(this, aStart + mStartPos, aLength, aContentType);
793 return t.forget();
794 }
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 }