diff -r 000000000000 -r 6474c204b198 content/base/public/nsDOMFile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/base/public/nsDOMFile.h Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,564 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsDOMFile_h__ +#define nsDOMFile_h__ + +#include "mozilla/Attributes.h" +#include "nsICharsetDetectionObserver.h" +#include "nsIFile.h" +#include "nsIDOMFile.h" +#include "nsIDOMFileList.h" +#include "nsIInputStream.h" +#include "nsIJSNativeInitializer.h" +#include "nsIMutable.h" +#include "nsCOMArray.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIXMLHttpRequest.h" +#include "nsAutoPtr.h" +#include "nsFileStreams.h" +#include "nsTemporaryFileInputStream.h" + +#include "mozilla/GuardObjects.h" +#include "mozilla/LinkedList.h" +#include +#include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/dom/DOMError.h" +#include "mozilla/dom/indexedDB/FileInfo.h" +#include "mozilla/dom/indexedDB/FileManager.h" +#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "nsWrapperCache.h" +#include "nsCycleCollectionParticipant.h" + +class nsIFile; +class nsIInputStream; +class nsIClassInfo; + +class nsDOMFileBase : public nsIDOMFile, + public nsIXHRSendable, + public nsIMutable +{ +public: + typedef mozilla::dom::indexedDB::FileInfo FileInfo; + + virtual already_AddRefed + CreateSlice(uint64_t aStart, uint64_t aLength, + const nsAString& aContentType) = 0; + + virtual const nsTArray >* + GetSubBlobs() const { return nullptr; } + + NS_DECL_NSIDOMBLOB + NS_DECL_NSIDOMFILE + NS_DECL_NSIXHRSENDABLE + NS_DECL_NSIMUTABLE + + void + SetLazyData(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength, uint64_t aLastModifiedDate) + { + NS_ASSERTION(aLength, "must have length"); + + mName = aName; + mContentType = aContentType; + mLength = aLength; + mLastModificationDate = aLastModifiedDate; + mIsFile = !aName.IsVoid(); + } + + bool IsSizeUnknown() const + { + return mLength == UINT64_MAX; + } + + bool IsDateUnknown() const + { + return mIsFile && mLastModificationDate == UINT64_MAX; + } + +protected: + nsDOMFileBase(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength, uint64_t aLastModifiedDate) + : mIsFile(true), mImmutable(false), mContentType(aContentType), + mName(aName), mStart(0), mLength(aLength), mLastModificationDate(aLastModifiedDate) + { + // Ensure non-null mContentType by default + mContentType.SetIsVoid(false); + } + + nsDOMFileBase(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength) + : mIsFile(true), mImmutable(false), mContentType(aContentType), + mName(aName), mStart(0), mLength(aLength), mLastModificationDate(UINT64_MAX) + { + // Ensure non-null mContentType by default + mContentType.SetIsVoid(false); + } + + nsDOMFileBase(const nsAString& aContentType, uint64_t aLength) + : mIsFile(false), mImmutable(false), mContentType(aContentType), + mStart(0), mLength(aLength), mLastModificationDate(UINT64_MAX) + { + // Ensure non-null mContentType by default + mContentType.SetIsVoid(false); + } + + nsDOMFileBase(const nsAString& aContentType, uint64_t aStart, + uint64_t aLength) + : mIsFile(false), mImmutable(false), mContentType(aContentType), + mStart(aStart), mLength(aLength), mLastModificationDate(UINT64_MAX) + { + NS_ASSERTION(aLength != UINT64_MAX, + "Must know length when creating slice"); + // Ensure non-null mContentType by default + mContentType.SetIsVoid(false); + } + + virtual ~nsDOMFileBase() {} + + virtual bool IsStoredFile() const + { + return false; + } + + virtual bool IsWholeFile() const + { + NS_NOTREACHED("Should only be called on dom blobs backed by files!"); + return false; + } + + virtual bool IsSnapshot() const + { + return false; + } + + FileInfo* GetFileInfo() const + { + NS_ASSERTION(IsStoredFile(), "Should only be called on stored files!"); + NS_ASSERTION(!mFileInfos.IsEmpty(), "Must have at least one file info!"); + + return mFileInfos.ElementAt(0); + } + + bool mIsFile; + bool mImmutable; + + nsString mContentType; + nsString mName; + nsString mPath; // The path relative to a directory chosen by the user + + uint64_t mStart; + uint64_t mLength; + + uint64_t mLastModificationDate; + + // Protected by IndexedDatabaseManager::FileMutex() + nsTArray > mFileInfos; +}; + +class nsDOMFile : public nsDOMFileBase +{ +public: + nsDOMFile(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength, uint64_t aLastModifiedDate) + : nsDOMFileBase(aName, aContentType, aLength, aLastModifiedDate) + { } + + nsDOMFile(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength) + : nsDOMFileBase(aName, aContentType, aLength) + { } + + nsDOMFile(const nsAString& aContentType, uint64_t aLength) + : nsDOMFileBase(aContentType, aLength) + { } + + nsDOMFile(const nsAString& aContentType, uint64_t aStart, uint64_t aLength) + : nsDOMFileBase(aContentType, aStart, aLength) + { } + + NS_DECL_THREADSAFE_ISUPPORTS +}; + +class nsDOMFileCC : public nsDOMFileBase +{ +public: + nsDOMFileCC(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength) + : nsDOMFileBase(aName, aContentType, aLength) + { } + + nsDOMFileCC(const nsAString& aContentType, uint64_t aLength) + : nsDOMFileBase(aContentType, aLength) + { } + + nsDOMFileCC(const nsAString& aContentType, uint64_t aStart, uint64_t aLength) + : nsDOMFileBase(aContentType, aStart, aLength) + { } + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMFileCC, nsIDOMFile) +}; + +class nsDOMFileFile : public nsDOMFile +{ +public: + // Create as a file + nsDOMFileFile(nsIFile *aFile) + : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX), + mFile(aFile), mWholeFile(true), mStoredFile(false) + { + NS_ASSERTION(mFile, "must have file"); + // Lazily get the content type and size + mContentType.SetIsVoid(true); + mFile->GetLeafName(mName); + } + + nsDOMFileFile(nsIFile *aFile, FileInfo *aFileInfo) + : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX), + mFile(aFile), mWholeFile(true), mStoredFile(true) + { + NS_ASSERTION(mFile, "must have file"); + NS_ASSERTION(aFileInfo, "must have file info"); + // Lazily get the content type and size + mContentType.SetIsVoid(true); + mFile->GetLeafName(mName); + + mFileInfos.AppendElement(aFileInfo); + } + + // Create as a file + nsDOMFileFile(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength, nsIFile *aFile) + : nsDOMFile(aName, aContentType, aLength, UINT64_MAX), + mFile(aFile), mWholeFile(true), mStoredFile(false) + { + NS_ASSERTION(mFile, "must have file"); + } + + nsDOMFileFile(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength, nsIFile *aFile, uint64_t aLastModificationDate) + : nsDOMFile(aName, aContentType, aLength, aLastModificationDate), + mFile(aFile), mWholeFile(true), mStoredFile(false) + { + NS_ASSERTION(mFile, "must have file"); + } + + // Create as a file with custom name + nsDOMFileFile(nsIFile *aFile, const nsAString& aName, + const nsAString& aContentType) + : nsDOMFile(aName, aContentType, UINT64_MAX, UINT64_MAX), + mFile(aFile), mWholeFile(true), mStoredFile(false) + { + NS_ASSERTION(mFile, "must have file"); + if (aContentType.IsEmpty()) { + // Lazily get the content type and size + mContentType.SetIsVoid(true); + } + } + + // Create as a stored file + nsDOMFileFile(const nsAString& aName, const nsAString& aContentType, + uint64_t aLength, nsIFile* aFile, + FileInfo* aFileInfo) + : nsDOMFile(aName, aContentType, aLength, UINT64_MAX), + mFile(aFile), mWholeFile(true), mStoredFile(true) + { + NS_ASSERTION(mFile, "must have file"); + mFileInfos.AppendElement(aFileInfo); + } + + // Create as a stored blob + nsDOMFileFile(const nsAString& aContentType, uint64_t aLength, + nsIFile* aFile, FileInfo* aFileInfo) + : nsDOMFile(aContentType, aLength), + mFile(aFile), mWholeFile(true), mStoredFile(true) + { + NS_ASSERTION(mFile, "must have file"); + mFileInfos.AppendElement(aFileInfo); + } + + // Create as a file to be later initialized + nsDOMFileFile() + : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX), + mWholeFile(true), mStoredFile(false) + { + // Lazily get the content type and size + mContentType.SetIsVoid(true); + mName.SetIsVoid(true); + } + + // Overrides + NS_IMETHOD GetSize(uint64_t* aSize) MOZ_OVERRIDE; + NS_IMETHOD GetType(nsAString& aType) MOZ_OVERRIDE; + NS_IMETHOD GetLastModifiedDate(JSContext* cx, JS::MutableHandle aLastModifiedDate) MOZ_OVERRIDE; + NS_IMETHOD GetMozLastModifiedDate(uint64_t* aLastModifiedDate) MOZ_OVERRIDE; + NS_IMETHOD GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE; + NS_IMETHOD GetInternalStream(nsIInputStream**) MOZ_OVERRIDE; + + void SetPath(const nsAString& aFullPath); + +protected: + // Create slice + nsDOMFileFile(const nsDOMFileFile* aOther, uint64_t aStart, uint64_t aLength, + const nsAString& aContentType) + : nsDOMFile(aContentType, aOther->mStart + aStart, aLength), + mFile(aOther->mFile), mWholeFile(false), + mStoredFile(aOther->mStoredFile) + { + NS_ASSERTION(mFile, "must have file"); + mImmutable = aOther->mImmutable; + + if (mStoredFile) { + FileInfo* fileInfo; + + using mozilla::dom::indexedDB::IndexedDatabaseManager; + + if (IndexedDatabaseManager::IsClosed()) { + fileInfo = aOther->GetFileInfo(); + } + else { + mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + fileInfo = aOther->GetFileInfo(); + } + + mFileInfos.AppendElement(fileInfo); + } + } + + virtual already_AddRefed + CreateSlice(uint64_t aStart, uint64_t aLength, + const nsAString& aContentType) MOZ_OVERRIDE; + + virtual bool IsStoredFile() const MOZ_OVERRIDE + { + return mStoredFile; + } + + virtual bool IsWholeFile() const MOZ_OVERRIDE + { + return mWholeFile; + } + + nsCOMPtr mFile; + bool mWholeFile; + bool mStoredFile; +}; + +/** + * This class may be used off the main thread, and in particular, its + * constructor and destructor may not run on the same thread. Be careful! + */ +class nsDOMMemoryFile : public nsDOMFile +{ +public: + // Create as file + nsDOMMemoryFile(void *aMemoryBuffer, + uint64_t aLength, + const nsAString& aName, + const nsAString& aContentType) + : nsDOMFile(aName, aContentType, aLength, UINT64_MAX), + mDataOwner(new DataOwner(aMemoryBuffer, aLength)) + { + NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data"); + } + + // Create as blob + nsDOMMemoryFile(void *aMemoryBuffer, + uint64_t aLength, + const nsAString& aContentType) + : nsDOMFile(aContentType, aLength), + mDataOwner(new DataOwner(aMemoryBuffer, aLength)) + { + NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data"); + } + + NS_IMETHOD GetInternalStream(nsIInputStream**) MOZ_OVERRIDE; + + NS_IMETHOD_(bool) IsMemoryFile(void) MOZ_OVERRIDE; + +protected: + // Create slice + nsDOMMemoryFile(const nsDOMMemoryFile* aOther, uint64_t aStart, + uint64_t aLength, const nsAString& aContentType) + : nsDOMFile(aContentType, aOther->mStart + aStart, aLength), + mDataOwner(aOther->mDataOwner) + { + NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data"); + mImmutable = aOther->mImmutable; + } + virtual already_AddRefed + CreateSlice(uint64_t aStart, uint64_t aLength, + const nsAString& aContentType) MOZ_OVERRIDE; + + // These classes need to see DataOwner. + friend class DataOwnerAdapter; + friend class nsDOMMemoryFileDataOwnerMemoryReporter; + + class DataOwner MOZ_FINAL : public mozilla::LinkedListElement { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataOwner) + DataOwner(void* aMemoryBuffer, uint64_t aLength) + : mData(aMemoryBuffer) + , mLength(aLength) + { + mozilla::StaticMutexAutoLock lock(sDataOwnerMutex); + + if (!sDataOwners) { + sDataOwners = new mozilla::LinkedList(); + EnsureMemoryReporterRegistered(); + } + sDataOwners->insertBack(this); + } + + private: + // Private destructor, to discourage deletion outside of Release(): + ~DataOwner() { + mozilla::StaticMutexAutoLock lock(sDataOwnerMutex); + + remove(); + if (sDataOwners->isEmpty()) { + // Free the linked list if it's empty. + sDataOwners = nullptr; + } + + moz_free(mData); + } + + public: + static void EnsureMemoryReporterRegistered(); + + // sDataOwners and sMemoryReporterRegistered may only be accessed while + // holding sDataOwnerMutex! You also must hold the mutex while touching + // elements of the linked list that DataOwner inherits from. + static mozilla::StaticMutex sDataOwnerMutex; + static mozilla::StaticAutoPtr > sDataOwners; + static bool sMemoryReporterRegistered; + + void* mData; + uint64_t mLength; + }; + + // Used when backed by a memory store + nsRefPtr mDataOwner; +}; + +class nsDOMFileList MOZ_FINAL : public nsIDOMFileList, + public nsWrapperCache +{ +public: + nsDOMFileList(nsISupports *aParent) : mParent(aParent) + { + SetIsDOMBinding(); + } + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMFileList) + + NS_DECL_NSIDOMFILELIST + + virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; + + nsISupports* GetParentObject() + { + return mParent; + } + + void Disconnect() + { + mParent = nullptr; + } + + bool Append(nsIDOMFile *aFile) { return mFiles.AppendObject(aFile); } + + bool Remove(uint32_t aIndex) { return mFiles.RemoveObjectAt(aIndex); } + void Clear() { return mFiles.Clear(); } + + static nsDOMFileList* FromSupports(nsISupports* aSupports) + { +#ifdef DEBUG + { + nsCOMPtr list_qi = do_QueryInterface(aSupports); + + // If this assertion fires the QI implementation for the object in + // question doesn't use the nsIDOMFileList pointer as the nsISupports + // pointer. That must be fixed, or we'll crash... + NS_ASSERTION(list_qi == static_cast(aSupports), + "Uh, fix QI!"); + } +#endif + + return static_cast(aSupports); + } + + nsIDOMFile* Item(uint32_t aIndex) + { + return mFiles.SafeObjectAt(aIndex); + } + nsIDOMFile* IndexedGetter(uint32_t aIndex, bool& aFound) + { + aFound = aIndex < static_cast(mFiles.Count()); + return aFound ? mFiles.ObjectAt(aIndex) : nullptr; + } + uint32_t Length() + { + return mFiles.Count(); + } + +private: + nsCOMArray mFiles; + nsISupports *mParent; +}; + +class MOZ_STACK_CLASS nsDOMFileInternalUrlHolder { +public: + nsDOMFileInternalUrlHolder(nsIDOMBlob* aFile, nsIPrincipal* aPrincipal + MOZ_GUARD_OBJECT_NOTIFIER_PARAM); + ~nsDOMFileInternalUrlHolder(); + nsAutoString mUrl; +private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; +// This class would take the ownership of aFD and the caller must not close it. +class nsDOMTemporaryFileBlob : public nsDOMFile +{ +public: + nsDOMTemporaryFileBlob(PRFileDesc* aFD, uint64_t aStartPos, uint64_t aLength, + const nsAString& aContentType) + : nsDOMFile(aContentType, aLength), + mLength(aLength), + mStartPos(aStartPos), + mContentType(aContentType) + { + mFileDescOwner = new nsTemporaryFileInputStream::FileDescOwner(aFD); + } + + ~nsDOMTemporaryFileBlob() { } + NS_IMETHOD GetInternalStream(nsIInputStream**) MOZ_OVERRIDE; + +protected: + nsDOMTemporaryFileBlob(const nsDOMTemporaryFileBlob* aOther, uint64_t aStart, uint64_t aLength, + const nsAString& aContentType) + : nsDOMFile(aContentType, aLength), + mLength(aLength), + mStartPos(aStart), + mFileDescOwner(aOther->mFileDescOwner), + mContentType(aContentType) { } + + virtual already_AddRefed + CreateSlice(uint64_t aStart, uint64_t aLength, + const nsAString& aContentType) MOZ_OVERRIDE; + +private: + uint64_t mLength; + uint64_t mStartPos; + nsRefPtr mFileDescOwner; + nsString mContentType; +}; + +#endif