michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDOMFile.h" michael@0: michael@0: #include "nsCExternalHandlerService.h" michael@0: #include "nsContentCID.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "nsError.h" michael@0: #include "nsICharsetDetector.h" michael@0: #include "nsIClassInfo.h" michael@0: #include "nsIConverterInputStream.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIFileStreams.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsIIPCSerializableInputStream.h" michael@0: #include "nsIMemoryReporter.h" michael@0: #include "nsIMIMEService.h" michael@0: #include "nsISeekableStream.h" michael@0: #include "nsIUnicharInputStream.h" michael@0: #include "nsIUnicodeDecoder.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIUUIDGenerator.h" michael@0: #include "nsHostObjectProtocolHandler.h" michael@0: #include "nsStringStream.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "mozilla/SHA1.h" michael@0: #include "mozilla/CheckedInt.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "mozilla/dom/FileListBinding.h" michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: // XXXkhuey the input stream that we pass out of a DOMFile michael@0: // can outlive the actual DOMFile object. Thus, we must michael@0: // ensure that the buffer underlying the stream we get michael@0: // from NS_NewByteInputStream is held alive as long as the michael@0: // stream is. We do that by passing back this class instead. michael@0: class DataOwnerAdapter MOZ_FINAL : public nsIInputStream, michael@0: public nsISeekableStream, michael@0: public nsIIPCSerializableInputStream michael@0: { michael@0: typedef nsDOMMemoryFile::DataOwner DataOwner; michael@0: public: michael@0: static nsresult Create(DataOwner* aDataOwner, michael@0: uint32_t aStart, michael@0: uint32_t aLength, michael@0: nsIInputStream** _retval); michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: // These are mandatory. michael@0: NS_FORWARD_NSIINPUTSTREAM(mStream->) michael@0: NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->) michael@0: michael@0: // This is optional. We use a conditional QI to keep it from being called michael@0: // if the underlying stream doesn't support it. michael@0: NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(mSerializableInputStream->) michael@0: michael@0: private: michael@0: DataOwnerAdapter(DataOwner* aDataOwner, michael@0: nsIInputStream* aStream) michael@0: : mDataOwner(aDataOwner), mStream(aStream), michael@0: mSeekableStream(do_QueryInterface(aStream)), michael@0: mSerializableInputStream(do_QueryInterface(aStream)) michael@0: { michael@0: NS_ASSERTION(mSeekableStream, "Somebody gave us the wrong stream!"); michael@0: } michael@0: michael@0: nsRefPtr mDataOwner; michael@0: nsCOMPtr mStream; michael@0: nsCOMPtr mSeekableStream; michael@0: nsCOMPtr mSerializableInputStream; michael@0: }; michael@0: michael@0: NS_IMPL_ADDREF(DataOwnerAdapter) michael@0: NS_IMPL_RELEASE(DataOwnerAdapter) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(DataOwnerAdapter) michael@0: NS_INTERFACE_MAP_ENTRY(nsIInputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsISeekableStream) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, michael@0: mSerializableInputStream) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: nsresult DataOwnerAdapter::Create(DataOwner* aDataOwner, michael@0: uint32_t aStart, michael@0: uint32_t aLength, michael@0: nsIInputStream** _retval) michael@0: { michael@0: nsresult rv; michael@0: NS_ASSERTION(aDataOwner, "Uh ..."); michael@0: michael@0: nsCOMPtr stream; michael@0: michael@0: rv = NS_NewByteInputStream(getter_AddRefs(stream), michael@0: static_cast(aDataOwner->mData) + michael@0: aStart, michael@0: (int32_t)aLength, michael@0: NS_ASSIGNMENT_DEPEND); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ADDREF(*_retval = new DataOwnerAdapter(aDataOwner, stream)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMFileBase implementation michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetName(nsAString &aFileName) michael@0: { michael@0: NS_ASSERTION(mIsFile, "Should only be called on files"); michael@0: aFileName = mName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetPath(nsAString &aPath) michael@0: { michael@0: NS_ASSERTION(mIsFile, "Should only be called on files"); michael@0: aPath = mPath; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetLastModifiedDate(JSContext* cx, JS::MutableHandle aLastModifiedDate) michael@0: { michael@0: JS::Rooted date(cx, JS_NewDateObjectMsec(cx, JS_Now() / PR_USEC_PER_MSEC)); michael@0: if (!date) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: aLastModifiedDate.setObject(*date); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetMozFullPath(nsAString &aFileName) michael@0: { michael@0: NS_ASSERTION(mIsFile, "Should only be called on files"); michael@0: michael@0: // It is unsafe to call IsCallerChrome on a non-main thread. If michael@0: // you hit the following assertion you need to figure out some other way to michael@0: // determine privileges and call GetMozFullPathInternal. michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: if (nsContentUtils::IsCallerChrome()) { michael@0: return GetMozFullPathInternal(aFileName); michael@0: } michael@0: aFileName.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetMozFullPathInternal(nsAString &aFileName) michael@0: { michael@0: if (!mIsFile) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aFileName.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetSize(uint64_t *aSize) michael@0: { michael@0: *aSize = mLength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetType(nsAString &aType) michael@0: { michael@0: aType = mContentType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) michael@0: { michael@0: NS_ASSERTION(mIsFile, "Should only be called on files"); michael@0: if (IsDateUnknown()) { michael@0: mLastModificationDate = PR_Now(); michael@0: } michael@0: *aLastModifiedDate = mLastModificationDate; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Makes sure that aStart and aEnd is less then or equal to aSize and greater michael@0: // than 0 michael@0: static void michael@0: ParseSize(int64_t aSize, int64_t& aStart, int64_t& aEnd) michael@0: { michael@0: CheckedInt64 newStartOffset = aStart; michael@0: if (aStart < -aSize) { michael@0: newStartOffset = 0; michael@0: } michael@0: else if (aStart < 0) { michael@0: newStartOffset += aSize; michael@0: } michael@0: else if (aStart > aSize) { michael@0: newStartOffset = aSize; michael@0: } michael@0: michael@0: CheckedInt64 newEndOffset = aEnd; michael@0: if (aEnd < -aSize) { michael@0: newEndOffset = 0; michael@0: } michael@0: else if (aEnd < 0) { michael@0: newEndOffset += aSize; michael@0: } michael@0: else if (aEnd > aSize) { michael@0: newEndOffset = aSize; michael@0: } michael@0: michael@0: if (!newStartOffset.isValid() || !newEndOffset.isValid() || michael@0: newStartOffset.value() >= newEndOffset.value()) { michael@0: aStart = aEnd = 0; michael@0: } michael@0: else { michael@0: aStart = newStartOffset.value(); michael@0: aEnd = newEndOffset.value(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::Slice(int64_t aStart, int64_t aEnd, michael@0: const nsAString& aContentType, uint8_t optional_argc, michael@0: nsIDOMBlob **aBlob) michael@0: { michael@0: *aBlob = nullptr; michael@0: michael@0: // Truncate aStart and aEnd so that we stay within this file. michael@0: uint64_t thisLength; michael@0: nsresult rv = GetSize(&thisLength); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (optional_argc < 2) { michael@0: aEnd = (int64_t)thisLength; michael@0: } michael@0: michael@0: ParseSize((int64_t)thisLength, aStart, aEnd); michael@0: michael@0: // Create the new file michael@0: *aBlob = CreateSlice((uint64_t)aStart, (uint64_t)(aEnd - aStart), michael@0: aContentType).take(); michael@0: michael@0: return *aBlob ? NS_OK : NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetInternalStream(nsIInputStream **aStream) michael@0: { michael@0: // Must be overridden michael@0: NS_NOTREACHED("Must override GetInternalStream"); michael@0: michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetInternalUrl(nsIPrincipal* aPrincipal, nsAString& aURL) michael@0: { michael@0: NS_ENSURE_STATE(aPrincipal); michael@0: michael@0: nsCString url; michael@0: nsresult rv = nsBlobProtocolHandler::AddDataEntry( michael@0: NS_LITERAL_CSTRING(BLOBURI_SCHEME), michael@0: static_cast(this), aPrincipal, url); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: CopyASCIItoUTF16(url, aURL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(int64_t) michael@0: nsDOMFileBase::GetFileId() michael@0: { michael@0: int64_t id = -1; michael@0: michael@0: if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) { michael@0: if (!indexedDB::IndexedDatabaseManager::IsClosed()) { michael@0: indexedDB::IndexedDatabaseManager::FileMutex().Lock(); michael@0: } michael@0: michael@0: NS_ASSERTION(!mFileInfos.IsEmpty(), michael@0: "A stored file must have at least one file info!"); michael@0: michael@0: nsRefPtr& fileInfo = mFileInfos.ElementAt(0); michael@0: if (fileInfo) { michael@0: id = fileInfo->Id(); michael@0: } michael@0: michael@0: if (!indexedDB::IndexedDatabaseManager::IsClosed()) { michael@0: indexedDB::IndexedDatabaseManager::FileMutex().Unlock(); michael@0: } michael@0: } michael@0: michael@0: return id; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsDOMFileBase::AddFileInfo(indexedDB::FileInfo* aFileInfo) michael@0: { michael@0: if (indexedDB::IndexedDatabaseManager::IsClosed()) { michael@0: NS_ERROR("Shouldn't be called after shutdown!"); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr fileInfo = aFileInfo; michael@0: michael@0: MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex()); michael@0: michael@0: NS_ASSERTION(!mFileInfos.Contains(aFileInfo), michael@0: "Adding the same file info agan?!"); michael@0: michael@0: nsRefPtr* element = mFileInfos.AppendElement(); michael@0: element->swap(fileInfo); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(indexedDB::FileInfo*) michael@0: nsDOMFileBase::GetFileInfo(indexedDB::FileManager* aFileManager) michael@0: { michael@0: if (indexedDB::IndexedDatabaseManager::IsClosed()) { michael@0: NS_ERROR("Shouldn't be called after shutdown!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // A slice created from a stored file must keep the file info alive. michael@0: // However, we don't support sharing of slices yet, so the slice must be michael@0: // copied again. That's why we have to ignore the first file info. michael@0: // Snapshots are handled in a similar way (they have to be copied). michael@0: uint32_t startIndex; michael@0: if (IsStoredFile() && (!IsWholeFile() || IsSnapshot())) { michael@0: startIndex = 1; michael@0: } michael@0: else { michael@0: startIndex = 0; michael@0: } michael@0: michael@0: MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex()); michael@0: michael@0: for (uint32_t i = startIndex; i < mFileInfos.Length(); i++) { michael@0: nsRefPtr& fileInfo = mFileInfos.ElementAt(i); michael@0: if (fileInfo->Manager() == aFileManager) { michael@0: return fileInfo; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetSendInfo(nsIInputStream** aBody, michael@0: uint64_t* aContentLength, michael@0: nsACString& aContentType, michael@0: nsACString& aCharset) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr stream; michael@0: rv = this->GetInternalStream(getter_AddRefs(stream)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = this->GetSize(aContentLength); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsString contentType; michael@0: rv = this->GetType(contentType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: CopyUTF16toUTF8(contentType, aContentType); michael@0: michael@0: aCharset.Truncate(); michael@0: michael@0: stream.forget(aBody); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::GetMutable(bool* aMutable) michael@0: { michael@0: *aMutable = !mImmutable; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileBase::SetMutable(bool aMutable) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: NS_ENSURE_ARG(!mImmutable || !aMutable); michael@0: michael@0: if (!mImmutable && !aMutable) { michael@0: // Force the content type and size to be cached michael@0: nsString dummyString; michael@0: rv = this->GetType(dummyString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint64_t dummyInt; michael@0: rv = this->GetSize(&dummyInt); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: mImmutable = !aMutable; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsDOMFileBase::IsMemoryFile(void) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMFile implementation michael@0: michael@0: DOMCI_DATA(File, nsDOMFile) michael@0: DOMCI_DATA(Blob, nsDOMFile) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsDOMFile) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMBlob) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) michael@0: NS_INTERFACE_MAP_ENTRY(nsIMutable) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: // Threadsafe when GetMutable() == false michael@0: NS_IMPL_ADDREF(nsDOMFile) michael@0: NS_IMPL_RELEASE(nsDOMFile) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMFileCC implementation michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileCC) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsDOMFileCC) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMFileCC) michael@0: // We don't have anything to traverse, but some of our subclasses do. michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileCC) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMBlob) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) michael@0: NS_INTERFACE_MAP_ENTRY(nsIMutable) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileCC) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileCC) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMFileFile implementation michael@0: michael@0: already_AddRefed michael@0: nsDOMFileFile::CreateSlice(uint64_t aStart, uint64_t aLength, michael@0: const nsAString& aContentType) michael@0: { michael@0: nsCOMPtr t = new nsDOMFileFile(this, aStart, aLength, aContentType); michael@0: return t.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileFile::GetMozFullPathInternal(nsAString &aFilename) michael@0: { michael@0: NS_ASSERTION(mIsFile, "Should only be called on files"); michael@0: return mFile->GetPath(aFilename); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileFile::GetLastModifiedDate(JSContext* cx, JS::MutableHandle aLastModifiedDate) michael@0: { michael@0: NS_ASSERTION(mIsFile, "Should only be called on files"); michael@0: michael@0: PRTime msecs; michael@0: if (IsDateUnknown()) { michael@0: nsresult rv = mFile->GetLastModifiedTime(&msecs); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mLastModificationDate = msecs; michael@0: } else { michael@0: msecs = mLastModificationDate; michael@0: } michael@0: michael@0: JSObject* date = JS_NewDateObjectMsec(cx, msecs); michael@0: if (date) { michael@0: aLastModifiedDate.setObject(*date); michael@0: } michael@0: else { michael@0: date = JS_NewDateObjectMsec(cx, JS_Now() / PR_USEC_PER_MSEC); michael@0: aLastModifiedDate.setObject(*date); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileFile::GetSize(uint64_t *aFileSize) michael@0: { michael@0: if (IsSizeUnknown()) { michael@0: NS_ASSERTION(mWholeFile, michael@0: "Should only use lazy size when using the whole file"); michael@0: int64_t fileSize; michael@0: nsresult rv = mFile->GetFileSize(&fileSize); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (fileSize < 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mLength = fileSize; michael@0: } michael@0: michael@0: *aFileSize = mLength; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileFile::GetType(nsAString &aType) michael@0: { michael@0: if (mContentType.IsVoid()) { michael@0: NS_ASSERTION(mWholeFile, michael@0: "Should only use lazy ContentType when using the whole file"); michael@0: nsresult rv; michael@0: nsCOMPtr mimeService = michael@0: do_GetService(NS_MIMESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString mimeType; michael@0: rv = mimeService->GetTypeFromFile(mFile, mimeType); michael@0: if (NS_FAILED(rv)) { michael@0: mimeType.Truncate(); michael@0: } michael@0: michael@0: AppendUTF8toUTF16(mimeType, mContentType); michael@0: mContentType.SetIsVoid(false); michael@0: } michael@0: michael@0: aType = mContentType; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileFile::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) michael@0: { michael@0: NS_ASSERTION(mIsFile, "Should only be called on files"); michael@0: if (IsDateUnknown()) { michael@0: PRTime msecs; michael@0: nsresult rv = mFile->GetLastModifiedTime(&msecs); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mLastModificationDate = msecs; michael@0: } michael@0: *aLastModifiedDate = mLastModificationDate; michael@0: return NS_OK; michael@0: } michael@0: michael@0: const uint32_t sFileStreamFlags = michael@0: nsIFileInputStream::CLOSE_ON_EOF | michael@0: nsIFileInputStream::REOPEN_ON_REWIND | michael@0: nsIFileInputStream::DEFER_OPEN; michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileFile::GetInternalStream(nsIInputStream **aStream) michael@0: { michael@0: return mWholeFile ? michael@0: NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags) : michael@0: NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength, michael@0: -1, -1, sFileStreamFlags); michael@0: } michael@0: michael@0: void michael@0: nsDOMFileFile::SetPath(const nsAString& aPath) michael@0: { michael@0: MOZ_ASSERT(aPath.IsEmpty() || michael@0: aPath[aPath.Length() - 1] == char16_t('/'), michael@0: "Path must end with a path separator"); michael@0: mPath = aPath; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMMemoryFile implementation michael@0: michael@0: already_AddRefed michael@0: nsDOMMemoryFile::CreateSlice(uint64_t aStart, uint64_t aLength, michael@0: const nsAString& aContentType) michael@0: { michael@0: nsCOMPtr t = michael@0: new nsDOMMemoryFile(this, aStart, aLength, aContentType); michael@0: return t.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMMemoryFile::GetInternalStream(nsIInputStream **aStream) michael@0: { michael@0: if (mLength > INT32_MAX) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return DataOwnerAdapter::Create(mDataOwner, mStart, mLength, aStream); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsDOMMemoryFile::IsMemoryFile(void) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: /* static */ StaticMutex michael@0: nsDOMMemoryFile::DataOwner::sDataOwnerMutex; michael@0: michael@0: /* static */ StaticAutoPtr > michael@0: nsDOMMemoryFile::DataOwner::sDataOwners; michael@0: michael@0: /* static */ bool michael@0: nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered; michael@0: michael@0: MOZ_DEFINE_MALLOC_SIZE_OF(DOMMemoryFileDataOwnerMallocSizeOf) michael@0: michael@0: class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL michael@0: : public nsIMemoryReporter michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback, michael@0: nsISupports *aClosure) michael@0: { michael@0: typedef nsDOMMemoryFile::DataOwner DataOwner; michael@0: michael@0: StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex); michael@0: michael@0: if (!DataOwner::sDataOwners) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: const size_t LARGE_OBJECT_MIN_SIZE = 8 * 1024; michael@0: size_t smallObjectsTotal = 0; michael@0: michael@0: for (DataOwner *owner = DataOwner::sDataOwners->getFirst(); michael@0: owner; owner = owner->getNext()) { michael@0: michael@0: size_t size = DOMMemoryFileDataOwnerMallocSizeOf(owner->mData); michael@0: michael@0: if (size < LARGE_OBJECT_MIN_SIZE) { michael@0: smallObjectsTotal += size; michael@0: } michael@0: else { michael@0: SHA1Sum sha1; michael@0: sha1.update(owner->mData, owner->mLength); michael@0: uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long. michael@0: sha1.finish(digest); michael@0: michael@0: nsAutoCString digestString; michael@0: for (size_t i = 0; i < sizeof(digest); i++) { michael@0: digestString.AppendPrintf("%02x", digest[i]); michael@0: } michael@0: michael@0: nsresult rv = aCallback->Callback( michael@0: /* process */ NS_LITERAL_CSTRING(""), michael@0: nsPrintfCString( michael@0: "explicit/dom/memory-file-data/large/file(length=%llu, sha1=%s)", michael@0: owner->mLength, digestString.get()), michael@0: KIND_HEAP, UNITS_BYTES, size, michael@0: nsPrintfCString( michael@0: "Memory used to back a memory file of length %llu bytes. The file " michael@0: "has a sha1 of %s.\n\n" michael@0: "Note that the allocator may round up a memory file's length -- " michael@0: "that is, an N-byte memory file may take up more than N bytes of " michael@0: "memory.", michael@0: owner->mLength, digestString.get()), michael@0: aClosure); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: if (smallObjectsTotal > 0) { michael@0: nsresult rv = aCallback->Callback( michael@0: /* process */ NS_LITERAL_CSTRING(""), michael@0: NS_LITERAL_CSTRING("explicit/dom/memory-file-data/small"), michael@0: KIND_HEAP, UNITS_BYTES, smallObjectsTotal, michael@0: nsPrintfCString( michael@0: "Memory used to back small memory files (less than %d bytes each).\n\n" michael@0: "Note that the allocator may round up a memory file's length -- " michael@0: "that is, an N-byte memory file may take up more than N bytes of " michael@0: "memory."), michael@0: aClosure); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsDOMMemoryFileDataOwnerMemoryReporter, nsIMemoryReporter) michael@0: michael@0: /* static */ void michael@0: nsDOMMemoryFile::DataOwner::EnsureMemoryReporterRegistered() michael@0: { michael@0: sDataOwnerMutex.AssertCurrentThreadOwns(); michael@0: if (sMemoryReporterRegistered) { michael@0: return; michael@0: } michael@0: michael@0: RegisterStrongMemoryReporter(new nsDOMMemoryFileDataOwnerMemoryReporter()); michael@0: michael@0: sMemoryReporterRegistered = true; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMFileList implementation michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMFileList) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileList) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFileList) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMFileList) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileList) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileList) michael@0: michael@0: JSObject* michael@0: nsDOMFileList::WrapObject(JSContext *cx) michael@0: { michael@0: return FileListBinding::Wrap(cx, this); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileList::GetLength(uint32_t* aLength) michael@0: { michael@0: *aLength = Length(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMFileList::Item(uint32_t aIndex, nsIDOMFile **aFile) michael@0: { michael@0: NS_IF_ADDREF(*aFile = Item(aIndex)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMFileInternalUrlHolder implementation michael@0: michael@0: nsDOMFileInternalUrlHolder::nsDOMFileInternalUrlHolder(nsIDOMBlob* aFile, michael@0: nsIPrincipal* aPrincipal michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: aFile->GetInternalUrl(aPrincipal, mUrl); michael@0: } michael@0: michael@0: nsDOMFileInternalUrlHolder::~nsDOMFileInternalUrlHolder() { michael@0: if (!mUrl.IsEmpty()) { michael@0: nsAutoCString narrowUrl; michael@0: CopyUTF16toUTF8(mUrl, narrowUrl); michael@0: nsBlobProtocolHandler::RemoveDataEntry(narrowUrl); michael@0: } michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////// michael@0: // nsDOMTemporaryFileBlob implementation michael@0: already_AddRefed michael@0: nsDOMTemporaryFileBlob::CreateSlice(uint64_t aStart, uint64_t aLength, michael@0: const nsAString& aContentType) michael@0: { michael@0: if (aStart + aLength > mLength) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr t = michael@0: new nsDOMTemporaryFileBlob(this, aStart + mStartPos, aLength, aContentType); michael@0: return t.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDOMTemporaryFileBlob::GetInternalStream(nsIInputStream **aStream) michael@0: { michael@0: nsCOMPtr stream = michael@0: new nsTemporaryFileInputStream(mFileDescOwner, mStartPos, mStartPos + mLength); michael@0: stream.forget(aStream); michael@0: return NS_OK; michael@0: }