Bug 726593 - Implement FileHandle. r=bent

This commit is contained in:
Jan Varga 2012-06-03 18:33:52 +02:00
parent 97add2a393
commit 6e3e5ab48b
112 changed files with 7804 additions and 390 deletions

View File

@ -166,6 +166,7 @@
@BINPATH@/components/dom_css.xpt
@BINPATH@/components/dom_devicestorage.xpt
@BINPATH@/components/dom_events.xpt
@BINPATH@/components/dom_file.xpt
@BINPATH@/components/dom_geolocation.xpt
@BINPATH@/components/dom_network.xpt
@BINPATH@/components/dom_notification.xpt

View File

@ -171,6 +171,7 @@
@BINPATH@/components/dom_css.xpt
@BINPATH@/components/dom_devicestorage.xpt
@BINPATH@/components/dom_events.xpt
@BINPATH@/components/dom_file.xpt
@BINPATH@/components/dom_geolocation.xpt
@BINPATH@/components/dom_network.xpt
@BINPATH@/components/dom_notification.xpt

View File

@ -550,8 +550,10 @@ public:
* @return boolean indicating whether a BOM was detected.
*/
static bool CheckForBOM(const unsigned char* aBuffer, PRUint32 aLength,
nsACString& aCharset, bool *bigEndian = nsnull);
nsACString& aCharset, bool *bigEndian = nsnull);
static nsresult GuessCharset(const char *aData, PRUint32 aDataLen,
nsACString &aCharset);
/**
* Determine whether aContent is in some way associated with aForm. If the

View File

@ -43,6 +43,19 @@ class nsDOMFileBase : public nsIDOMFile,
public:
typedef mozilla::dom::indexedDB::FileInfo FileInfo;
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType) = 0;
virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
GetSubBlobs() const { return nsnull; }
NS_DECL_NSIDOMBLOB
NS_DECL_NSIDOMFILE
NS_DECL_NSIXHRSENDABLE
NS_DECL_NSIMUTABLE
protected:
nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: mIsFile(true), mImmutable(false), mContentType(aContentType),
@ -60,8 +73,8 @@ public:
mContentType.SetIsVoid(false);
}
nsDOMFileBase(const nsAString& aContentType,
PRUint64 aStart, PRUint64 aLength)
nsDOMFileBase(const nsAString& aContentType, PRUint64 aStart,
PRUint64 aLength)
: mIsFile(false), mImmutable(false), mContentType(aContentType),
mStart(aStart), mLength(aLength)
{
@ -73,36 +86,35 @@ public:
virtual ~nsDOMFileBase() {}
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType) = 0;
virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
GetSubBlobs() const { return nsnull; }
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMBLOB
NS_DECL_NSIDOMFILE
NS_DECL_NSIXHRSENDABLE
NS_DECL_NSIMUTABLE
protected:
bool IsSizeUnknown()
bool IsSizeUnknown() const
{
return mLength == UINT64_MAX;
}
virtual bool IsStoredFile()
virtual bool IsStoredFile() const
{
return false;
}
virtual bool IsWholeFile()
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;
@ -115,13 +127,53 @@ protected:
nsTArray<nsRefPtr<FileInfo> > mFileInfos;
};
class nsDOMFileFile : public nsDOMFileBase,
class nsDOMFile : public nsDOMFileBase
{
public:
nsDOMFile(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: nsDOMFileBase(aName, aContentType, aLength)
{ }
nsDOMFile(const nsAString& aContentType, PRUint64 aLength)
: nsDOMFileBase(aContentType, aLength)
{ }
nsDOMFile(const nsAString& aContentType, PRUint64 aStart, PRUint64 aLength)
: nsDOMFileBase(aContentType, aStart, aLength)
{ }
NS_DECL_ISUPPORTS
};
class nsDOMFileCC : public nsDOMFileBase
{
public:
nsDOMFileCC(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: nsDOMFileBase(aName, aContentType, aLength)
{ }
nsDOMFileCC(const nsAString& aContentType, PRUint64 aLength)
: nsDOMFileBase(aContentType, aLength)
{ }
nsDOMFileCC(const nsAString& aContentType, PRUint64 aStart, PRUint64 aLength)
: nsDOMFileBase(aContentType, aStart, aLength)
{ }
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMFileCC, nsIDOMFile)
};
class nsDOMFileFile : public nsDOMFile,
public nsIJSNativeInitializer
{
public:
// Create as a file
nsDOMFileFile(nsIFile *aFile)
: nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
: nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX),
mFile(aFile), mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "must have file");
@ -130,10 +182,19 @@ public:
mFile->GetLeafName(mName);
}
// Create as a file
nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile *aFile)
: nsDOMFile(aName, aContentType, aLength),
mFile(aFile), mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "must have file");
}
// Create as a blob
nsDOMFileFile(nsIFile *aFile, const nsAString& aContentType,
nsISupports *aCacheToken)
: nsDOMFileBase(aContentType, UINT64_MAX),
: nsDOMFile(aContentType, UINT64_MAX),
mFile(aFile), mWholeFile(true), mStoredFile(false),
mCacheToken(aCacheToken)
{
@ -142,20 +203,19 @@ public:
// Create as a file with custom name
nsDOMFileFile(nsIFile *aFile, const nsAString& aName)
: nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
: nsDOMFile(aName, EmptyString(), UINT64_MAX),
mFile(aFile), mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "must have file");
// Lazily get the content type and size
mContentType.SetIsVoid(true);
mName.Assign(aName);
}
// Create as a stored file
nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile* aFile,
FileInfo* aFileInfo)
: nsDOMFileBase(aName, aContentType, aLength),
: nsDOMFile(aName, aContentType, aLength),
mFile(aFile), mWholeFile(true), mStoredFile(true)
{
NS_ASSERTION(mFile, "must have file");
@ -165,7 +225,7 @@ public:
// Create as a stored blob
nsDOMFileFile(const nsAString& aContentType, PRUint64 aLength,
nsIFile* aFile, FileInfo* aFileInfo)
: nsDOMFileBase(aContentType, aLength),
: nsDOMFile(aContentType, aLength),
mFile(aFile), mWholeFile(true), mStoredFile(true)
{
NS_ASSERTION(mFile, "must have file");
@ -174,7 +234,7 @@ public:
// Create as a file to be later initialized
nsDOMFileFile()
: nsDOMFileBase(EmptyString(), EmptyString(), UINT64_MAX),
: nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX),
mWholeFile(true), mStoredFile(false)
{
// Lazily get the content type and size
@ -206,7 +266,7 @@ protected:
// Create slice
nsDOMFileFile(const nsDOMFileFile* aOther, PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType)
: nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
: nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
mFile(aOther->mFile), mWholeFile(false),
mStoredFile(aOther->mStoredFile), mCacheToken(aOther->mCacheToken)
{
@ -216,32 +276,30 @@ protected:
if (mStoredFile) {
FileInfo* fileInfo;
if (!mozilla::dom::indexedDB::IndexedDatabaseManager::IsClosed()) {
mozilla::dom::indexedDB::IndexedDatabaseManager::FileMutex().Lock();
using mozilla::dom::indexedDB::IndexedDatabaseManager;
if (IndexedDatabaseManager::IsClosed()) {
fileInfo = aOther->GetFileInfo();
}
NS_ASSERTION(!aOther->mFileInfos.IsEmpty(),
"A stored file must have at least one file info!");
fileInfo = aOther->mFileInfos.ElementAt(0);
if (!mozilla::dom::indexedDB::IndexedDatabaseManager::IsClosed()) {
mozilla::dom::indexedDB::IndexedDatabaseManager::FileMutex().Unlock();
else {
mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
fileInfo = aOther->GetFileInfo();
}
mFileInfos.AppendElement(fileInfo);
}
}
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType);
virtual bool IsStoredFile()
virtual bool IsStoredFile() const
{
return mStoredFile;
}
virtual bool IsWholeFile()
virtual bool IsWholeFile() const
{
return mWholeFile;
}
@ -252,7 +310,7 @@ protected:
nsCOMPtr<nsISupports> mCacheToken;
};
class nsDOMMemoryFile : public nsDOMFileBase
class nsDOMMemoryFile : public nsDOMFile
{
public:
// Create as file
@ -260,7 +318,7 @@ public:
PRUint64 aLength,
const nsAString& aName,
const nsAString& aContentType)
: nsDOMFileBase(aName, aContentType, aLength),
: nsDOMFile(aName, aContentType, aLength),
mDataOwner(new DataOwner(aMemoryBuffer))
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
@ -270,7 +328,7 @@ public:
nsDOMMemoryFile(void *aMemoryBuffer,
PRUint64 aLength,
const nsAString& aContentType)
: nsDOMFileBase(aContentType, aLength),
: nsDOMFile(aContentType, aLength),
mDataOwner(new DataOwner(aMemoryBuffer))
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
@ -282,7 +340,7 @@ protected:
// Create slice
nsDOMMemoryFile(const nsDOMMemoryFile* aOther, PRUint64 aStart,
PRUint64 aLength, const nsAString& aContentType)
: nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
: nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
mDataOwner(aOther->mDataOwner)
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");

View File

@ -178,6 +178,10 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
#include "nsIDOMDocumentType.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsICharsetDetector.h"
#include "nsICharsetDetectionObserver.h"
#include "nsIPlatformCharset.h"
extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
const char** next, PRUnichar* result);
extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
@ -190,21 +194,6 @@ using namespace mozilla;
const char kLoadAsData[] = "loadAsData";
/**
* Default values for the ViewportInfo structure.
*/
static const float kViewportMinScale = 0.0;
static const float kViewportMaxScale = 10.0;
static const PRUint32 kViewportMinWidth = 200;
static const PRUint32 kViewportMaxWidth = 10000;
static const PRUint32 kViewportMinHeight = 223;
static const PRUint32 kViewportMaxHeight = 10000;
static const PRInt32 kViewportDefaultScreenWidth = 980;
static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull;
nsIXPConnect *nsContentUtils::sXPConnect;
nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
@ -260,6 +249,23 @@ nsIParser* nsContentUtils::sXMLFragmentParser = nsnull;
nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nsnull;
bool nsContentUtils::sFragmentParsingActive = false;
namespace {
/**
* Default values for the ViewportInfo structure.
*/
static const float kViewportMinScale = 0.0;
static const float kViewportMaxScale = 10.0;
static const PRUint32 kViewportMinWidth = 200;
static const PRUint32 kViewportMaxWidth = 10000;
static const PRUint32 kViewportMinHeight = 223;
static const PRUint32 kViewportMaxHeight = 10000;
static const PRInt32 kViewportDefaultScreenWidth = 980;
static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
static PLDHashTable sEventListenerManagersHash;
class EventListenerManagerMapEntry : public PLDHashEntryHdr
@ -301,14 +307,36 @@ EventListenerManagerHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
lm->~EventListenerManagerMapEntry();
}
class nsSameOriginChecker : public nsIChannelEventSink,
public nsIInterfaceRequestor
class SameOriginChecker : public nsIChannelEventSink,
public nsIInterfaceRequestor
{
NS_DECL_ISUPPORTS
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
};
class CharsetDetectionObserver : public nsICharsetDetectionObserver
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf)
{
mCharset = aCharset;
return NS_OK;
}
const nsACString& GetResult() const
{
return mCharset;
}
private:
nsCString mCharset;
};
} // anonymous namespace
/* static */
TimeDuration
nsContentUtils::HandlingUserInputTimeout()
@ -3536,6 +3564,89 @@ nsContentUtils::CheckForBOM(const unsigned char* aBuffer, PRUint32 aLength,
return found;
}
NS_IMPL_ISUPPORTS1(CharsetDetectionObserver,
nsICharsetDetectionObserver)
/* static */
nsresult
nsContentUtils::GuessCharset(const char *aData, PRUint32 aDataLen,
nsACString &aCharset)
{
// First try the universal charset detector
nsCOMPtr<nsICharsetDetector> detector =
do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
"universal_charset_detector");
if (!detector) {
// No universal charset detector, try the default charset detector
const nsAdoptingCString& detectorName =
Preferences::GetLocalizedCString("intl.charset.detector");
if (!detectorName.IsEmpty()) {
nsCAutoString detectorContractID;
detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
detectorContractID += detectorName;
detector = do_CreateInstance(detectorContractID.get());
}
}
nsresult rv;
// The charset detector doesn't work for empty (null) aData. Testing
// aDataLen instead of aData so that we catch potential errors.
if (detector && aDataLen) {
nsRefPtr<CharsetDetectionObserver> observer =
new CharsetDetectionObserver();
rv = detector->Init(observer);
NS_ENSURE_SUCCESS(rv, rv);
bool dummy;
rv = detector->DoIt(aData, aDataLen, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
rv = detector->Done();
NS_ENSURE_SUCCESS(rv, rv);
aCharset = observer->GetResult();
} else {
// no charset detector available, check the BOM
unsigned char sniffBuf[3];
PRUint32 numRead =
(aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
memcpy(sniffBuf, aData, numRead);
bool bigEndian;
if (CheckForBOM(sniffBuf, numRead, aCharset, &bigEndian) &&
aCharset.EqualsLiteral("UTF-16")) {
if (bigEndian) {
aCharset.AppendLiteral("BE");
}
else {
aCharset.AppendLiteral("LE");
}
}
}
if (aCharset.IsEmpty()) {
// no charset detected, default to the system charset
nsCOMPtr<nsIPlatformCharset> platformCharset =
do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
aCharset);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to get the system charset!");
}
}
}
if (aCharset.IsEmpty()) {
// no sniffed or default charset, assume UTF-8
aCharset.AssignLiteral("UTF-8");
}
return NS_OK;
}
/* static */
void
nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver)
@ -5517,7 +5628,7 @@ nsIInterfaceRequestor*
nsContentUtils::GetSameOriginChecker()
{
if (!sSameOriginChecker) {
sSameOriginChecker = new nsSameOriginChecker();
sSameOriginChecker = new SameOriginChecker();
NS_IF_ADDREF(sSameOriginChecker);
}
return sSameOriginChecker;
@ -5549,15 +5660,15 @@ nsContentUtils::CheckSameOrigin(nsIChannel *aOldChannel, nsIChannel *aNewChannel
return rv;
}
NS_IMPL_ISUPPORTS2(nsSameOriginChecker,
NS_IMPL_ISUPPORTS2(SameOriginChecker,
nsIChannelEventSink,
nsIInterfaceRequestor)
NS_IMETHODIMP
nsSameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags,
nsIAsyncVerifyRedirectCallback *cb)
SameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags,
nsIAsyncVerifyRedirectCallback *cb)
{
NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
@ -5570,7 +5681,7 @@ nsSameOriginChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
}
NS_IMETHODIMP
nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
SameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
{
return QueryInterface(aIID, aResult);
}

View File

@ -17,7 +17,7 @@
using namespace mozilla;
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFileBase,
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMMultipartFile, nsDOMFile,
nsIJSNativeInitializer)
NS_IMETHODIMP
@ -139,6 +139,15 @@ nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength,
return blob.forget();
}
/* static */ nsresult
nsDOMMultipartFile::NewFile(const nsAString& aName, nsISupports* *aNewObject)
{
nsCOMPtr<nsISupports> file =
do_QueryObject(new nsDOMMultipartFile(aName));
file.forget(aNewObject);
return NS_OK;
}
/* static */ nsresult
nsDOMMultipartFile::NewBlob(nsISupports* *aNewObject)
{

View File

@ -12,7 +12,7 @@
using namespace mozilla;
class nsDOMMultipartFile : public nsDOMFileBase,
class nsDOMMultipartFile : public nsDOMFile,
public nsIJSNativeInitializer
{
public:
@ -20,7 +20,7 @@ public:
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
const nsAString& aName,
const nsAString& aContentType)
: nsDOMFileBase(aName, aContentType, UINT64_MAX),
: nsDOMFile(aName, aContentType, UINT64_MAX),
mBlobs(aBlobs)
{
}
@ -28,14 +28,20 @@ public:
// Create as a blob
nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
const nsAString& aContentType)
: nsDOMFileBase(aContentType, UINT64_MAX),
: nsDOMFile(aContentType, UINT64_MAX),
mBlobs(aBlobs)
{
}
// Create as a file to be later initialized
nsDOMMultipartFile(const nsAString& aName)
: nsDOMFile(aName, EmptyString(), UINT64_MAX)
{
}
// Create as a blob to be later initialized
nsDOMMultipartFile()
: nsDOMFileBase(EmptyString(), UINT64_MAX)
: nsDOMFile(EmptyString(), UINT64_MAX)
{
}
@ -60,6 +66,9 @@ public:
NS_IMETHOD GetSize(PRUint64*);
NS_IMETHOD GetInternalStream(nsIInputStream**);
static nsresult
NewFile(const nsAString& aName, nsISupports* *aNewObject);
// DOMClassInfo constructor (for Blob([b1, "foo"], { type: "image/png" }))
static nsresult
NewBlob(nsISupports* *aNewObject);

View File

@ -103,23 +103,6 @@ nsresult DataOwnerAdapter::Create(DataOwner* aDataOwner,
////////////////////////////////////////////////////////////////////////////
// nsDOMFileBase implementation
DOMCI_DATA(File, nsDOMFileBase)
DOMCI_DATA(Blob, nsDOMFileBase)
NS_INTERFACE_MAP_BEGIN(nsDOMFileBase)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
NS_INTERFACE_MAP_ENTRY(nsIMutable)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
NS_INTERFACE_MAP_END
// Threadsafe when GetMutable() == false
NS_IMPL_THREADSAFE_ADDREF(nsDOMFileBase)
NS_IMPL_THREADSAFE_RELEASE(nsDOMFileBase)
NS_IMETHODIMP
nsDOMFileBase::GetName(nsAString &aFileName)
{
@ -302,7 +285,7 @@ nsDOMFileBase::GetFileId()
{
PRInt64 id = -1;
if (IsStoredFile() && IsWholeFile()) {
if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) {
if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
indexedDB::IndexedDatabaseManager::FileMutex().Lock();
}
@ -353,7 +336,14 @@ nsDOMFileBase::GetFileInfo(indexedDB::FileManager* aFileManager)
// A slice created from a stored file must keep the file info alive.
// However, we don't support sharing of slices yet, so the slice must be
// copied again. That's why we have to ignore the first file info.
PRUint32 startIndex = IsStoredFile() && !IsWholeFile() ? 1 : 0;
// Snapshots are handled in a similar way (they have to be copied).
PRUint32 startIndex;
if (IsStoredFile() && (!IsWholeFile() || IsSnapshot())) {
startIndex = 1;
}
else {
startIndex = 0;
}
MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
@ -419,10 +409,48 @@ nsDOMFileBase::SetMutable(bool aMutable)
return rv;
}
////////////////////////////////////////////////////////////////////////////
// nsDOMFile implementation
DOMCI_DATA(File, nsDOMFile)
DOMCI_DATA(Blob, nsDOMFile)
NS_INTERFACE_MAP_BEGIN(nsDOMFile)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
NS_INTERFACE_MAP_ENTRY(nsIMutable)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
NS_INTERFACE_MAP_END
// Threadsafe when GetMutable() == false
NS_IMPL_THREADSAFE_ADDREF(nsDOMFile)
NS_IMPL_THREADSAFE_RELEASE(nsDOMFile)
////////////////////////////////////////////////////////////////////////////
// nsDOMFileCC implementation
NS_IMPL_CYCLE_COLLECTION_0(nsDOMFileCC)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMFileCC)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
NS_INTERFACE_MAP_ENTRY(nsIMutable)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMFileCC)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMFileCC)
////////////////////////////////////////////////////////////////////////////
// nsDOMFileFile implementation
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMFileFile, nsDOMFileBase,
NS_IMPL_ISUPPORTS_INHERITED1(nsDOMFileFile, nsDOMFile,
nsIJSNativeInitializer)
already_AddRefed<nsIDOMBlob>

View File

@ -11,14 +11,12 @@
#include "nsDOMFile.h"
#include "nsDOMError.h"
#include "nsCharsetAlias.h"
#include "nsICharsetDetector.h"
#include "nsICharsetConverterManager.h"
#include "nsIConverterInputStream.h"
#include "nsIFile.h"
#include "nsIFileStreams.h"
#include "nsIInputStream.h"
#include "nsIMIMEService.h"
#include "nsIPlatformCharset.h"
#include "nsIUnicodeDecoder.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
@ -97,7 +95,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileReader)
NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
@ -112,15 +109,6 @@ nsDOMFileReader::RootResultArrayBuffer()
static_cast<nsDOMEventTargetHelper*>(this)), this);
}
//nsICharsetDetectionObserver
NS_IMETHODIMP
nsDOMFileReader::Notify(const char *aCharset, nsDetectionConfident aConf)
{
mCharset = aCharset;
return NS_OK;
}
//nsDOMFileReader constructors/initializers
nsDOMFileReader::nsDOMFileReader()
@ -457,7 +445,7 @@ nsDOMFileReader::GetAsText(const nsACString &aCharset,
if (!aCharset.IsEmpty()) {
charsetGuess = aCharset;
} else {
rv = GuessCharset(aFileData, aDataLen, charsetGuess);
rv = nsContentUtils::GuessCharset(aFileData, aDataLen, charsetGuess);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -540,80 +528,3 @@ nsDOMFileReader::ConvertStream(const char *aFileData,
return rv;
}
nsresult
nsDOMFileReader::GuessCharset(const char *aFileData,
PRUint32 aDataLen,
nsACString &aCharset)
{
// First try the universal charset detector
nsCOMPtr<nsICharsetDetector> detector
= do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
"universal_charset_detector");
if (!detector) {
// No universal charset detector, try the default charset detector
const nsAdoptingCString& detectorName =
Preferences::GetLocalizedCString("intl.charset.detector");
if (!detectorName.IsEmpty()) {
nsCAutoString detectorContractID;
detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
detectorContractID += detectorName;
detector = do_CreateInstance(detectorContractID.get());
}
}
nsresult rv;
// The charset detector doesn't work for empty (null) aFileData. Testing
// aDataLen instead of aFileData so that we catch potential errors.
if (detector && aDataLen != 0) {
mCharset.Truncate();
detector->Init(this);
bool done;
rv = detector->DoIt(aFileData, aDataLen, &done);
NS_ENSURE_SUCCESS(rv, rv);
rv = detector->Done();
NS_ENSURE_SUCCESS(rv, rv);
aCharset = mCharset;
} else {
// no charset detector available, check the BOM
unsigned char sniffBuf[3];
PRUint32 numRead = (aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
memcpy(sniffBuf, aFileData, numRead);
if (numRead >= 2 &&
sniffBuf[0] == 0xfe &&
sniffBuf[1] == 0xff) {
aCharset = "UTF-16BE";
} else if (numRead >= 2 &&
sniffBuf[0] == 0xff &&
sniffBuf[1] == 0xfe) {
aCharset = "UTF-16LE";
} else if (numRead >= 3 &&
sniffBuf[0] == 0xef &&
sniffBuf[1] == 0xbb &&
sniffBuf[2] == 0xbf) {
aCharset = "UTF-8";
}
}
if (aCharset.IsEmpty()) {
// no charset detected, default to the system charset
nsCOMPtr<nsIPlatformCharset> platformCharset =
do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
aCharset);
}
}
if (aCharset.IsEmpty()) {
// no sniffed or default charset, try UTF-8
aCharset.AssignLiteral("UTF-8");
}
return NS_OK;
}

View File

@ -16,8 +16,6 @@
#include "nsIJSNativeInitializer.h"
#include "prtime.h"
#include "nsITimer.h"
#include "nsICharsetDetector.h"
#include "nsICharsetDetectionObserver.h"
#include "nsIDOMFile.h"
#include "nsIDOMFileReader.h"
@ -34,8 +32,7 @@ class nsDOMFileReader : public mozilla::dom::FileIOObject,
public nsIDOMFileReader,
public nsIInterfaceRequestor,
public nsSupportsWeakReference,
public nsIJSNativeInitializer,
public nsICharsetDetectionObserver
public nsIJSNativeInitializer
{
public:
nsDOMFileReader();
@ -54,13 +51,10 @@ public:
NS_DECL_EVENT_HANDLER(loadend)
NS_DECL_EVENT_HANDLER(loadstart)
// nsIJSNativeInitializer
NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
// nsIJSNativeInitializer
NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
PRUint32 argc, jsval* argv);
// nsICharsetDetectionObserver
NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
// FileIOObject overrides
NS_IMETHOD DoAbort(nsAString& aEvent);
NS_IMETHOD DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
@ -88,7 +82,6 @@ protected:
nsresult GetAsText(const nsACString &aCharset,
const char *aFileData, PRUint32 aDataLen, nsAString &aResult);
nsresult GetAsDataURL(nsIDOMBlob *aFile, const char *aFileData, PRUint32 aDataLen, nsAString &aResult);
nsresult GuessCharset(const char *aFileData, PRUint32 aDataLen, nsACString &aCharset);
nsresult ConvertStream(const char *aFileData, PRUint32 aDataLen, const char *aCharset, nsAString &aResult);
void FreeFileData() {

View File

@ -616,7 +616,7 @@ protected:
// Non-null only when we are able to get a os-file representation of the
// response, i.e. when loading from a file, or when the http-stream
// caches into a file or is reading from a cached file.
nsRefPtr<nsDOMFileBase> mDOMFile;
nsRefPtr<nsDOMFile> mDOMFile;
// We stream data to mBuilder when response type is "blob" or "moz-blob"
// and mDOMFile is null.
nsRefPtr<nsDOMBlobBuilder> mBuilder;

View File

@ -193,4 +193,50 @@ private:
xpc_TryUnmarkWrappedGrayObject(tmp->mOn##_event##Listener->GetInner()); \
}
/* Use this macro to declare functions that forward the behavior of this
* interface to another object.
* This macro doesn't forward PreHandleEvent because sometimes subclasses
* want to override it.
*/
#define NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(_to) \
NS_SCRIPTABLE NS_IMETHOD AddEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture, bool wantsUntrusted, PRUint8 _argc) { \
return _to AddEventListener(type, listener, useCapture, wantsUntrusted, _argc); \
} \
NS_IMETHOD AddSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture, bool aWantsUntrusted, PRUint8 _argc) { \
return _to AddSystemEventListener(type, listener, aUseCapture, aWantsUntrusted, _argc); \
} \
NS_SCRIPTABLE NS_IMETHOD RemoveEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture) { \
return _to RemoveEventListener(type, listener, useCapture); \
} \
NS_IMETHOD RemoveSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture) { \
return _to RemoveSystemEventListener(type, listener, aUseCapture); \
} \
NS_SCRIPTABLE NS_IMETHOD DispatchEvent(nsIDOMEvent *evt, bool *_retval NS_OUTPARAM) { \
return _to DispatchEvent(evt, _retval); \
} \
virtual nsIDOMEventTarget * GetTargetForDOMEvent(void) { \
return _to GetTargetForDOMEvent(); \
} \
virtual nsIDOMEventTarget * GetTargetForEventTargetChain(void) { \
return _to GetTargetForEventTargetChain(); \
} \
virtual nsresult WillHandleEvent(nsEventChainPostVisitor & aVisitor) { \
return _to WillHandleEvent(aVisitor); \
} \
virtual nsresult PostHandleEvent(nsEventChainPostVisitor & aVisitor) { \
return _to PostHandleEvent(aVisitor); \
} \
virtual nsresult DispatchDOMEvent(nsEvent *aEvent, nsIDOMEvent *aDOMEvent, nsPresContext *aPresContext, nsEventStatus *aEventStatus) { \
return _to DispatchDOMEvent(aEvent, aDOMEvent, aPresContext, aEventStatus); \
} \
virtual nsEventListenerManager * GetListenerManager(bool aMayCreate) { \
return _to GetListenerManager(aMayCreate); \
} \
virtual nsIScriptContext * GetContextForEventHandlers(nsresult *aRv NS_OUTPARAM) { \
return _to GetContextForEventHandlers(aRv); \
} \
virtual JSContext * GetJSContextForEventHandlers(void) { \
return _to GetJSContextForEventHandlers(); \
}
#endif // nsDOMEventTargetHelper_h_

View File

@ -1042,7 +1042,7 @@ size_t sqlite3_quota_fread(
** the write if we exceed quota.
*/
size_t sqlite3_quota_fwrite(
void *pBuf, /* Take content to write from here */
const void *pBuf, /* Take content to write from here */
size_t size, /* Size of each element */
size_t nmemb, /* Number of elements */
quota_FILE *p /* Write to this quota_FILE objecct */
@ -1052,7 +1052,7 @@ size_t sqlite3_quota_fwrite(
sqlite3_int64 szNew;
quotaFile *pFile;
size_t rc;
iOfst = ftell(p->f);
iEnd = iOfst + size*nmemb;
pFile = p->pFile;
@ -1091,7 +1091,7 @@ size_t sqlite3_quota_fwrite(
pFile->iSize = iNewEnd;
quotaLeave();
}
return rc;
return rc;
}
/*
@ -1160,6 +1160,13 @@ long sqlite3_quota_ftell(quota_FILE *p){
return ftell(p->f);
}
/*
** Test the error indicator for the given file.
*/
int sqlite3_quota_ferror(quota_FILE *p){
return ferror(p->f);
}
/*
** Truncate a file to szNew bytes.
*/
@ -1237,6 +1244,25 @@ sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){
return p->pFile ? p->pFile->iSize : -1;
}
/*
** Determine the amount of data in bytes available for reading
** in the given file.
*/
long sqlite3_quota_file_available(quota_FILE *p){
FILE* f = p->f;
long pos1, pos2;
int rc;
pos1 = ftell(f);
if ( pos1 < 0 ) return -1;
rc = fseek(f, 0, SEEK_END);
if ( rc != 0 ) return -1;
pos2 = ftell(f);
if ( pos2 < 0 ) return -1;
rc = fseek(f, pos1, SEEK_SET);
if ( rc != 0 ) return -1;
return pos2 - pos1;
}
/*
** Remove a managed file. Update quotas accordingly.
*/

View File

@ -162,7 +162,7 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
** the sum of sizes of all files from going over quota.
*/
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
size_t sqlite3_quota_fwrite(const void*, size_t, size_t, quota_FILE*);
/*
** Flush all written content held in memory buffers out to disk.
@ -190,6 +190,13 @@ int sqlite3_quota_fseek(quota_FILE*, long, int);
void sqlite3_quota_rewind(quota_FILE*);
long sqlite3_quota_ftell(quota_FILE*);
/*
** Test the error indicator for the given file.
**
** Return non-zero if the error indicator is set.
*/
int sqlite3_quota_ferror(quota_FILE*);
/*
** Truncate a file previously opened by sqlite3_quota_fopen(). Return
** zero on success and non-zero on any kind of failure.
@ -198,7 +205,7 @@ long sqlite3_quota_ftell(quota_FILE*);
** Any attempt to "truncate" a file to a larger size results in
** undefined behavior.
*/
int sqlite3_quota_ftrunate(quota_FILE*, sqlite3_int64 newSize);
int sqlite3_quota_ftruncate(quota_FILE*, sqlite3_int64 newSize);
/*
** Return the last modification time of the opened file, in seconds
@ -232,6 +239,14 @@ sqlite3_int64 sqlite3_quota_file_size(quota_FILE*);
*/
sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*);
/*
** Determine the amount of data in bytes available for reading
** in the given file.
**
** Return -1 if the amount cannot be determined for some reason.
*/
long sqlite3_quota_file_available(quota_FILE*);
/*
** Delete a file from the disk, if that file is under quota management.
** Adjust quotas accordingly.

View File

@ -50,6 +50,7 @@ DIRS += \
battery \
contacts \
devicestorage \
file \
power \
settings \
sms \

View File

@ -17,8 +17,8 @@ using mozilla::dom::DOMRequest;
using mozilla::dom::DOMRequestService;
DOMRequest::DOMRequest(nsIDOMWindow* aWindow)
: mDone(false)
, mResult(JSVAL_VOID)
: mResult(JSVAL_VOID)
, mDone(false)
, mRooted(false)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
@ -34,14 +34,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMRequest,
nsDOMEventTargetHelper)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(success)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mError)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMRequest,
nsDOMEventTargetHelper)
tmp->mResult = JSVAL_VOID;
tmp->UnrootResultVal();
if (tmp->mRooted) {
tmp->mResult = JSVAL_VOID;
tmp->UnrootResultVal();
}
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(success)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mError)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
@ -98,35 +102,54 @@ DOMRequest::GetError(nsIDOMDOMError** aError)
void
DOMRequest::FireSuccess(jsval aResult)
{
NS_ABORT_IF_FALSE(!mDone, "Already fired success/error");
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
NS_ASSERTION(!mError, "mError shouldn't have been set!");
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
mDone = true;
RootResultVal();
if (JSVAL_IS_GCTHING(aResult)) {
RootResultVal();
}
mResult = aResult;
FireEvent(NS_LITERAL_STRING("success"));
FireEvent(NS_LITERAL_STRING("success"), false, false);
}
void
DOMRequest::FireError(const nsAString& aError)
{
NS_ABORT_IF_FALSE(!mDone, "Already fired success/error");
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
NS_ASSERTION(!mError, "mError shouldn't have been set!");
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
mDone = true;
mError = DOMError::CreateWithName(aError);
FireEvent(NS_LITERAL_STRING("error"));
FireEvent(NS_LITERAL_STRING("error"), true, true);
}
void
DOMRequest::FireEvent(const nsAString& aType)
DOMRequest::FireError(nsresult aError)
{
NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
NS_ASSERTION(!mError, "mError shouldn't have been set!");
NS_ASSERTION(mResult == JSVAL_VOID, "mResult shouldn't have been set!");
mDone = true;
mError = DOMError::CreateForNSResult(aError);
FireEvent(NS_LITERAL_STRING("error"), true, true);
}
void
DOMRequest::FireEvent(const nsAString& aType, bool aBubble, bool aCancelable)
{
if (NS_FAILED(CheckInnerWindowCorrectness())) {
return;
}
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nsnull, nsnull);
nsresult rv = event->InitEvent(aType, false, false);
nsresult rv = event->InitEvent(aType, aBubble, aCancelable);
if (NS_FAILED(rv)) {
return;
}
@ -140,6 +163,22 @@ DOMRequest::FireEvent(const nsAString& aType)
DispatchEvent(event, &dummy);
}
void
DOMRequest::RootResultVal()
{
NS_ASSERTION(!mRooted, "Don't call me if already rooted!");
NS_HOLD_JS_OBJECTS(this, DOMRequest);
mRooted = true;
}
void
DOMRequest::UnrootResultVal()
{
NS_ASSERTION(mRooted, "Don't call me if not rooted!");
NS_DROP_JS_OBJECTS(this, DOMRequest);
mRooted = false;
}
NS_IMPL_ISUPPORTS1(DOMRequestService, nsIDOMRequestService)
NS_IMETHODIMP

View File

@ -20,6 +20,12 @@ namespace dom {
class DOMRequest : public nsDOMEventTargetHelper,
public nsIDOMDOMRequest
{
protected:
jsval mResult;
nsCOMPtr<nsIDOMDOMError> mError;
bool mDone;
bool mRooted;
NS_DECL_EVENT_HANDLER(success)
NS_DECL_EVENT_HANDLER(error)
@ -33,37 +39,22 @@ public:
void FireSuccess(jsval aResult);
void FireError(const nsAString& aError);
void FireError(nsresult aError);
DOMRequest(nsIDOMWindow* aWindow);
virtual ~DOMRequest()
{
UnrootResultVal();
}
bool mDone;
jsval mResult;
nsCOMPtr<nsIDOMDOMError> mError;
bool mRooted;
private:
void FireEvent(const nsAString& aType);
void RootResultVal()
{
if (!mRooted) {
NS_HOLD_JS_OBJECTS(this, DOMRequest);
mRooted = true;
}
}
void UnrootResultVal()
{
if (mRooted) {
NS_DROP_JS_OBJECTS(this, DOMRequest);
mRooted = false;
UnrootResultVal();
}
}
protected:
void FireEvent(const nsAString& aType, bool aBubble, bool aCancelable);
virtual void RootResultVal();
virtual void UnrootResultVal();
};
class DOMRequestService : public nsIDOMRequestService

View File

@ -17,6 +17,7 @@ enum StructuredCloneTags {
SCTAG_DOM_BLOB,
SCTAG_DOM_FILE,
SCTAG_DOM_FILELIST,
SCTAG_DOM_FILEHANDLE,
// These tags are used for both main thread and workers.
SCTAG_DOM_IMAGEDATA,

View File

@ -107,3 +107,10 @@ DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_REGISTERED , "Factory not registered")
DOM_MSG_DEF(NS_ERROR_FACTORY_NOT_LOADED , "Factory not loaded")
DOM_MSG_DEF(NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT , "Factory does not support signatures")
DOM_MSG_DEF(NS_ERROR_FACTORY_EXISTS , "Factory already exists")
DOM4_MSG_DEF(UnknownError, "The operation failed for reasons unrelated to the file storage itself and not covered by any other error code.", NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR)
DOM4_MSG_DEF(LockedFileInactiveError, "A request was placed against a locked file which is currently not active, or which is finished.", NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR)
DOM4_MSG_DEF(ReadOnlyError, "A mutation operation was attempted in a READ_ONLY locked file.", NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR)
DOM4_MSG_DEF(InvalidStateError, "A mutation operation was attempted on a file storage that did not allow mutations.", NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR)
DOM4_MSG_DEF(AbortError, "A request was aborted, for example through a call to LockedFile.abort.", NS_ERROR_DOM_FILEHANDLE_ABORT_ERR)

View File

@ -464,6 +464,7 @@
#include "mozilla/dom/indexedDB/IDBWrapperCache.h"
#include "mozilla/dom/indexedDB/IDBFactory.h"
#include "mozilla/dom/indexedDB/IDBFileHandle.h"
#include "mozilla/dom/indexedDB/IDBRequest.h"
#include "mozilla/dom/indexedDB/IDBDatabase.h"
#include "mozilla/dom/indexedDB/IDBEvents.h"
@ -513,6 +514,10 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
#include "DOMError.h"
#include "DOMRequest.h"
#include "DOMFileHandle.h"
#include "FileRequest.h"
#include "LockedFile.h"
#include "mozilla/Likely.h"
#undef None // something included above defines this preprocessor symbol, maybe Xlib headers
@ -1573,6 +1578,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(IDBFactory, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(IDBFileHandle, FileHandle, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(IDBRequest, IDBEventTargetSH,
IDBEVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(IDBDatabase, IDBEventTargetSH,
@ -1638,6 +1645,13 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(DOMRequest, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(DOMFileHandle, FileHandle, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(FileRequest, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
};
// Objects that should be constructable through |new Name();|
@ -4330,6 +4344,11 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIIDBFactory)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(IDBFileHandle, nsIDOMFileHandle)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
DOM_CLASSINFO_MAP_ENTRY(nsIIDBFileHandle)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(IDBRequest, nsIIDBRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIIDBRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
@ -4459,6 +4478,20 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(DOMFileHandle, nsIDOMFileHandle)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(FileRequest, nsIDOMFileRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(LockedFile, nsIDOMLockedFile)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile)
DOM_CLASSINFO_MAP_END
#ifdef NS_DEBUG
{
PRUint32 i = ArrayLength(sClassInfoData);

View File

@ -487,6 +487,7 @@ DOMCI_CLASS(WebSocket)
DOMCI_CLASS(CloseEvent)
DOMCI_CLASS(IDBFactory)
DOMCI_CLASS(IDBFileHandle)
DOMCI_CLASS(IDBRequest)
DOMCI_CLASS(IDBDatabase)
DOMCI_CLASS(IDBObjectStore)
@ -526,3 +527,7 @@ DOMCI_CLASS(BluetoothAdapter)
DOMCI_CLASS(DOMError)
DOMCI_CLASS(DOMRequest)
DOMCI_CLASS(DOMFileHandle)
DOMCI_CLASS(FileRequest)
DOMCI_CLASS(LockedFile)

View File

@ -89,4 +89,11 @@
#define NS_ERROR_DOM_FILE_NOT_READABLE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILE, 1)
#define NS_ERROR_DOM_FILE_ABORT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILE, 2)
#define NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,1)
#define NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,2)
#define NS_ERROR_DOM_FILEHANDLE_LOCKEDFILE_INACTIVE_ERR \
NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,3)
#define NS_ERROR_DOM_FILEHANDLE_ABORT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,4)
#define NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM_FILEHANDLE,5)
#endif // nsDOMError_h__

View File

@ -96,6 +96,9 @@ enum DOM4ErrorTypeCodeMap {
/* File API errors http://dev.w3.org/2006/webapi/FileAPI/#ErrorAndException */
NotReadableError = 0,
/* FileHandle API errors */
LockedFileInactiveError = 0,
};
#define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},

View File

@ -55,8 +55,9 @@ nsDOMScriptObjectFactory::nsDOMScriptObjectFactory()
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_SVG);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_XPATH);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_INDEXEDDB);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_XPCONNECT);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_INDEXEDDB);
xs->RegisterExceptionProvider(provider, NS_ERROR_MODULE_DOM_FILEHANDLE);
}
NS_ASSERTION(!gExceptionProvider, "Registered twice?!");
@ -142,6 +143,10 @@ nsDOMScriptObjectFactory::Observe(nsISupports *aSubject,
NS_ERROR_MODULE_DOM_XPATH);
xs->UnregisterExceptionProvider(gExceptionProvider,
NS_ERROR_MODULE_XPCONNECT);
xs->UnregisterExceptionProvider(gExceptionProvider,
NS_ERROR_MODULE_DOM_INDEXEDDB);
xs->UnregisterExceptionProvider(gExceptionProvider,
NS_ERROR_MODULE_DOM_FILEHANDLE);
}
NS_RELEASE(gExceptionProvider);
@ -251,7 +256,8 @@ nsDOMExceptionProvider::GetException(nsresult result,
default:
MOZ_ASSERT(NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM ||
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_FILE ||
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_INDEXEDDB,
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_INDEXEDDB ||
NS_ERROR_GET_MODULE(result) == NS_ERROR_MODULE_DOM_FILEHANDLE,
"Trying to create an exception for the wrong error module.");
return NS_NewDOMException(result, aDefaultException, _retval);
}

View File

@ -63,6 +63,8 @@
#include "mozilla/dom/indexedDB/FileInfo.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
#include "sampler.h"
#include "nsDOMBlobBuilder.h"
#include "nsIDOMFileHandle.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -2270,14 +2272,99 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFileId(nsIDOMBlob* aBlob, PRInt64* aResult)
static nsresult
GetFileOrBlob(const nsAString& aName, const jsval& aBlobParts,
const jsval& aParameters, JSContext* aCx,
PRUint8 aOptionalArgCount, nsISupports** aResult)
{
if (!IsUniversalXPConnectCapable()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
*aResult = aBlob->GetFileId();
nsresult rv;
nsCOMPtr<nsISupports> file;
if (aName.IsVoid()) {
rv = nsDOMMultipartFile::NewBlob(getter_AddRefs(file));
}
else {
rv = nsDOMMultipartFile::NewFile(aName, getter_AddRefs(file));
}
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(file);
NS_ASSERTION(initializer, "what?");
jsval args[2] = { aBlobParts, aParameters };
rv = initializer->Initialize(nsnull, aCx, nsnull, aOptionalArgCount, args);
NS_ENSURE_SUCCESS(rv, rv);
file.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFile(const nsAString& aName, const jsval& aBlobParts,
const jsval& aParameters, JSContext* aCx,
PRUint8 aOptionalArgCount, nsIDOMFile** aResult)
{
nsCOMPtr<nsISupports> file;
nsresult rv = GetFileOrBlob(aName, aBlobParts, aParameters, aCx,
aOptionalArgCount, getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMFile> result = do_QueryInterface(file);
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetBlob(const jsval& aBlobParts, const jsval& aParameters,
JSContext* aCx, PRUint8 aOptionalArgCount,
nsIDOMBlob** aResult)
{
nsAutoString name;
name.SetIsVoid(true);
nsCOMPtr<nsISupports> blob;
nsresult rv = GetFileOrBlob(name, aBlobParts, aParameters, aCx,
aOptionalArgCount, getter_AddRefs(blob));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMBlob> result = do_QueryInterface(blob);
result.forget(aResult);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFileId(const jsval& aFile, JSContext* aCx,
PRInt64* aResult)
{
if (!JSVAL_IS_PRIMITIVE(aFile)) {
JSObject* obj = JSVAL_TO_OBJECT(aFile);
nsISupports* nativeObj =
nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj);
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(nativeObj);
if (blob) {
*aResult = blob->GetFileId();
return NS_OK;
}
nsCOMPtr<nsIDOMFileHandle> fileHandle = do_QueryInterface(nativeObj);
if (fileHandle) {
*aResult = fileHandle->GetFileId();
return NS_OK;
}
}
*aResult = -1;
return NS_OK;
}

View File

@ -17,8 +17,8 @@ interface nsIDOMDOMRequest : nsIDOMEventTarget
readonly attribute jsval result;
readonly attribute nsIDOMDOMError error;
attribute nsIDOMEventListener onsuccess;
attribute nsIDOMEventListener onerror;
attribute nsIDOMEventListener onsuccess;
attribute nsIDOMEventListener onerror;
};
[scriptable, builtinclass, uuid(eebcdf29-f8fa-4c36-bbc7-2146b1cbaf7b)]

View File

@ -5,6 +5,7 @@
DOM_SRCDIRS = \
dom/base \
dom/battery \
dom/file \
dom/power \
dom/network/src \
dom/settings \

128
dom/file/AsyncHelper.cpp Normal file
View File

@ -0,0 +1,128 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "AsyncHelper.h"
#include "nsIRequestObserver.h"
#include "nsNetUtil.h"
#include "FileService.h"
USING_FILE_NAMESPACE
NS_IMPL_THREADSAFE_ISUPPORTS2(AsyncHelper, nsIRunnable, nsIRequest)
nsresult
AsyncHelper::AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt)
{
nsresult rv;
if (aObserver) {
// build proxy for observer events
rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), aObserver);
NS_ENSURE_SUCCESS(rv, rv);
}
mCtxt = aCtxt;
FileService* service = FileService::GetOrCreate();
NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
nsIEventTarget* target = service->StreamTransportTarget();
rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::Run()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mObserver) {
mObserver->OnStartRequest(this, mCtxt);
}
mStatus = DoStreamWork(mStream);
if (mObserver) {
mObserver->OnStopRequest(this, mCtxt, mStatus);
}
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::GetName(nsACString& aName)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::IsPending(bool* _retval)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::GetStatus(nsresult* aStatus)
{
*aStatus = mStatus;
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::Cancel(nsresult aStatus)
{
return NS_OK;
}
NS_IMETHODIMP
AsyncHelper::Suspend()
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::Resume()
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::GetLoadGroup(nsILoadGroup** aLoadGroup)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::GetLoadFlags(nsLoadFlags* aLoadFlags)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AsyncHelper::SetLoadFlags(nsLoadFlags aLoadFlags)
{
NS_WARNING("Shouldn't be called!");
return NS_ERROR_NOT_IMPLEMENTED;
}

57
dom/file/AsyncHelper.h Normal file
View File

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_asynchelper_h__
#define mozilla_dom_file_asynchelper_h__
#include "FileCommon.h"
#include "nsIRequest.h"
#include "nsIRunnable.h"
class nsIRequestObserver;
BEGIN_FILE_NAMESPACE
/**
* Must be subclassed. The subclass must implement DoStreamWork.
* Async operations that are not supported in necko (truncate, flush, etc.)
* should use this helper. Call AsyncWork to invoke the operation.
*/
class AsyncHelper : public nsIRunnable,
public nsIRequest
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIREQUEST
nsresult
AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt);
protected:
AsyncHelper(nsISupports* aStream)
: mStream(aStream),
mStatus(NS_OK)
{ }
virtual ~AsyncHelper()
{ }
virtual nsresult
DoStreamWork(nsISupports* aStream) = 0;
private:
nsCOMPtr<nsISupports> mStream;
nsCOMPtr<nsIRequestObserver> mObserver;
nsCOMPtr<nsISupports> mCtxt;
nsresult mStatus;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_asynchelper_h__

View File

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "DOMFileHandle.h"
#include "nsIFileStreams.h"
#include "nsDOMClassInfoID.h"
#include "nsNetUtil.h"
#include "File.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
// static
already_AddRefed<DOMFileHandle>
DOMFileHandle::Create(nsPIDOMWindow* aWindow,
nsIFileStorage* aFileStorage,
nsIFile* aFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<DOMFileHandle> newFile(new DOMFileHandle);
newFile->BindToOwner(aWindow);
newFile->mFileStorage = aFileStorage;
nsresult rv = aFile->GetLeafName(newFile->mName);
NS_ENSURE_SUCCESS(rv, nsnull);
newFile->mFile = aFile;
newFile->mFileName = newFile->mName;
return newFile.forget();
}
already_AddRefed<nsISupports>
DOMFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
{
nsresult rv;
if (aReadOnly) {
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), aFile, -1, -1,
nsIFileInputStream::DEFER_OPEN);
NS_ENSURE_SUCCESS(rv, nsnull);
return stream.forget();
}
nsCOMPtr<nsIFileStream> stream;
rv = NS_NewLocalFileStream(getter_AddRefs(stream), aFile, -1, -1,
nsIFileStream::DEFER_OPEN);
NS_ENSURE_SUCCESS(rv, nsnull);
return stream.forget();
}
already_AddRefed<nsIDOMFile>
DOMFileHandle::CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize)
{
nsCOMPtr<nsIDOMFile> file =
new File(mName, mType, aFileSize, mFile, aLockedFile);
return file.forget();
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMFileHandle)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMFileHandle)
NS_INTERFACE_MAP_END_INHERITING(FileHandle)
NS_IMPL_ADDREF_INHERITED(DOMFileHandle, FileHandle)
NS_IMPL_RELEASE_INHERITED(DOMFileHandle, FileHandle)
DOMCI_DATA(DOMFileHandle, DOMFileHandle)

42
dom/file/DOMFileHandle.h Normal file
View File

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_domfilehandle_h__
#define mozilla_dom_file_domfilehandle_h__
#include "FileCommon.h"
#include "FileHandle.h"
BEGIN_FILE_NAMESPACE
class DOMFileHandle : public FileHandle
{
public:
NS_DECL_ISUPPORTS_INHERITED
static already_AddRefed<DOMFileHandle>
Create(nsPIDOMWindow* aWindow,
nsIFileStorage* aFileStorage,
nsIFile* aFile);
virtual already_AddRefed<nsISupports>
CreateStream(nsIFile* aFile, bool aReadOnly);
virtual already_AddRefed<nsIDOMFile>
CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize);
protected:
DOMFileHandle()
{ }
~DOMFileHandle()
{ }
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_domfilehandle_h__

86
dom/file/File.cpp Normal file
View File

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "File.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
using mozilla::dom::indexedDB::IndexedDatabaseManager;
// Create slice
File::File(const File* aOther, PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType)
: nsDOMFileCC(aContentType, aOther->mStart + aStart, aLength),
mFile(aOther->mFile), mLockedFile(aOther->mLockedFile),
mWholeFile(false), mStoredFile(aOther->mStoredFile)
{
NS_ASSERTION(mFile, "Null file!");
NS_ASSERTION(mLockedFile, "Null locked file!");
if (mStoredFile) {
FileInfo* fileInfo;
if (IndexedDatabaseManager::IsClosed()) {
fileInfo = aOther->GetFileInfo();
}
else {
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
fileInfo = aOther->GetFileInfo();
}
mFileInfos.AppendElement(fileInfo);
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(File)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(File, nsDOMFileCC)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mLockedFile,
nsIDOMLockedFile)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(File, nsDOMFileCC)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLockedFile)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(File)
NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC)
NS_IMPL_ADDREF_INHERITED(File, nsDOMFileCC)
NS_IMPL_RELEASE_INHERITED(File, nsDOMFileCC)
NS_IMETHODIMP
File::GetInternalStream(nsIInputStream **aStream)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv = mLockedFile->OpenInputStream(mWholeFile, mStart, mLength,
aStream);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
already_AddRefed<nsIDOMBlob>
File::CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIDOMBlob> t =
new File(this, aStart, aLength, aContentType);
return t.forget();
}
NS_IMETHODIMP
File::GetMozFullPathInternal(nsAString &aFilename)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mIsFile, "Should only be called on files");
return mFile->GetPath(aFilename);
}

96
dom/file/File.h Normal file
View File

@ -0,0 +1,96 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_file_h__
#define mozilla_dom_file_file_h__
#include "FileCommon.h"
#include "nsDOMFile.h"
#include "LockedFile.h"
BEGIN_FILE_NAMESPACE
class File : public nsDOMFileCC
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(File, nsDOMFileCC)
// Create as a file
File(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile* aFile, LockedFile* aLockedFile)
: nsDOMFileCC(aName, aContentType, aLength),
mFile(aFile), mLockedFile(aLockedFile),
mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "Null file!");
NS_ASSERTION(mLockedFile, "Null locked file!");
}
// Create as a stored file
File(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength, nsIFile* aFile, LockedFile* aLockedFile,
FileInfo* aFileInfo)
: nsDOMFileCC(aName, aContentType, aLength),
mFile(aFile), mLockedFile(aLockedFile),
mWholeFile(true), mStoredFile(true)
{
NS_ASSERTION(mFile, "Null file!");
NS_ASSERTION(mLockedFile, "Null locked file!");
mFileInfos.AppendElement(aFileInfo);
}
// Overrides
NS_IMETHOD
GetMozFullPathInternal(nsAString& aFullPath);
NS_IMETHOD
GetInternalStream(nsIInputStream** aStream);
protected:
// Create slice
File(const File* aOther, PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType);
virtual ~File()
{ }
virtual already_AddRefed<nsIDOMBlob>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType);
virtual bool
IsStoredFile() const
{
return mStoredFile;
}
virtual bool
IsWholeFile() const
{
return mWholeFile;
}
virtual bool
IsSnapshot() const
{
return true;
}
private:
nsCOMPtr<nsIFile> mFile;
nsRefPtr<LockedFile> mLockedFile;
bool mWholeFile;
bool mStoredFile;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_file_h__

25
dom/file/FileCommon.h Normal file
View File

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_filecommon_h__
#define mozilla_dom_file_filecommon_h__
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMEventTargetHelper.h"
#include "nsDebug.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#define BEGIN_FILE_NAMESPACE \
namespace mozilla { namespace dom { namespace file {
#define END_FILE_NAMESPACE \
} /* namespace file */ } /* namespace dom */ } /* namespace mozilla */
#define USING_FILE_NAMESPACE \
using namespace mozilla::dom::file;
#endif // mozilla_dom_file_filecommon_h__

180
dom/file/FileHandle.cpp Normal file
View File

@ -0,0 +1,180 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "FileHandle.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfoID.h"
#include "nsIDOMFile.h"
#include "nsIFileStorage.h"
#include "FileRequest.h"
#include "FileService.h"
#include "LockedFile.h"
#include "MetadataHelper.h"
USING_FILE_NAMESPACE
namespace {
class GetFileHelper : public MetadataHelper
{
public:
GetFileHelper(LockedFile* aLockedFile,
FileRequest* aFileRequest,
MetadataParameters* aParams,
FileHandle* aFileHandle)
: MetadataHelper(aLockedFile, aFileRequest, aParams),
mFileHandle(aFileHandle)
{ }
nsresult
GetSuccessResult(JSContext* aCx, jsval* aVal);
void
ReleaseObjects()
{
mFileHandle = nsnull;
MetadataHelper::ReleaseObjects();
}
private:
nsRefPtr<FileHandle> mFileHandle;
};
} // anonymous namespace
NS_IMPL_CYCLE_COLLECTION_CLASS(FileHandle)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileHandle,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFileStorage)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort)
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileHandle,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFileStorage)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileHandle)
NS_INTERFACE_MAP_ENTRY(nsIDOMFileHandle)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(FileHandle, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(FileHandle, nsDOMEventTargetHelper)
NS_IMPL_EVENT_HANDLER(FileHandle, abort);
NS_IMPL_EVENT_HANDLER(FileHandle, error);
NS_IMETHODIMP
FileHandle::GetName(nsAString& aName)
{
aName = mName;
return NS_OK;
}
NS_IMETHODIMP
FileHandle::GetType(nsAString& aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
FileHandle::Open(const nsAString& aMode,
PRUint8 aOptionalArgCount,
nsIDOMLockedFile** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (FileService::IsShuttingDown() || mFileStorage->IsStorageShuttingDown()) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
LockedFile::Mode mode;
if (aOptionalArgCount) {
if (aMode.EqualsLiteral("readwrite")) {
mode = LockedFile::READ_WRITE;
}
else if (aMode.EqualsLiteral("readonly")) {
mode = LockedFile::READ_ONLY;
}
else {
return NS_ERROR_TYPE_ERR;
}
}
else {
mode = LockedFile::READ_ONLY;
}
nsRefPtr<LockedFile> lockedFile = LockedFile::Create(this, mode);
NS_ENSURE_TRUE(lockedFile, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
lockedFile.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
FileHandle::GetFile(nsIDOMFileRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Do nothing if the window is closed
if (!GetOwner()) {
return NS_OK;
}
nsRefPtr<LockedFile> lockedFile =
LockedFile::Create(this, LockedFile::READ_ONLY, LockedFile::PARALLEL);
NS_ENSURE_TRUE(lockedFile, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
nsRefPtr<FileRequest> fileRequest =
FileRequest::Create(GetOwner(), lockedFile);
nsRefPtr<MetadataParameters> params = new MetadataParameters();
params->Init(true, false);
nsRefPtr<GetFileHelper> helper =
new GetFileHelper(lockedFile, fileRequest, params, this);
nsresult rv = helper->Enqueue();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
fileRequest.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP_(PRInt64)
FileHandle::GetFileId()
{
return -1;
}
NS_IMETHODIMP_(mozilla::dom::indexedDB::FileInfo*)
FileHandle::GetFileInfo()
{
return nsnull;
}
nsresult
GetFileHelper::GetSuccessResult(JSContext* aCx, jsval* aVal)
{
nsCOMPtr<nsIDOMFile> domFile =
mFileHandle->CreateFileObject(mLockedFile, mParams->Size());
nsresult rv =
nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), domFile,
&NS_GET_IID(nsIDOMFile), aVal);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return NS_OK;
}

86
dom/file/FileHandle.h Normal file
View File

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_filehandle_h__
#define mozilla_dom_file_filehandle_h__
#include "FileCommon.h"
#include "nsIDOMFileHandle.h"
#include "nsIFile.h"
#include "nsDOMEventTargetHelper.h"
class nsIDOMFile;
class nsIFileStorage;
BEGIN_FILE_NAMESPACE
class FileService;
class LockedFile;
class FinishHelper;
class FileHelper;
/**
* Must be subclassed. The subclass must implement CreateStream and
* CreateFileObject. Basically, every file storage implementation provides its
* own FileHandle implementation (for example IDBFileHandle provides IndexedDB
* specific implementation).
*/
class FileHandle : public nsDOMEventTargetHelper,
public nsIDOMFileHandle
{
friend class FileService;
friend class LockedFile;
friend class FinishHelper;
friend class FileHelper;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMFILEHANDLE
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileHandle, nsDOMEventTargetHelper)
const nsAString&
Name() const
{
return mName;
}
const nsAString&
Type() const
{
return mType;
}
virtual already_AddRefed<nsISupports>
CreateStream(nsIFile* aFile, bool aReadOnly) = 0;
virtual already_AddRefed<nsIDOMFile>
CreateFileObject(LockedFile* aLockedFile, PRUint32 aFileSize) = 0;
protected:
FileHandle()
{ }
~FileHandle()
{ }
nsCOMPtr<nsIFileStorage> mFileStorage;
nsString mName;
nsString mType;
nsCOMPtr<nsIFile> mFile;
nsString mFileName;
NS_DECL_EVENT_HANDLER(abort)
NS_DECL_EVENT_HANDLER(error)
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filehandle_h__

209
dom/file/FileHelper.cpp Normal file
View File

@ -0,0 +1,209 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "FileHelper.h"
#include "nsIFileStorage.h"
#include "nsNetError.h"
#include "nsProxyRelease.h"
#include "FileHandle.h"
#include "FileRequest.h"
#include "FileService.h"
USING_FILE_NAMESPACE
namespace {
LockedFile* gCurrentLockedFile = nsnull;
} // anonymous namespace
FileHelper::FileHelper(LockedFile* aLockedFile,
FileRequest* aFileRequest)
: mFileStorage(aLockedFile->mFileHandle->mFileStorage),
mLockedFile(aLockedFile),
mFileRequest(aFileRequest),
mResultCode(NS_OK),
mFinished(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
FileHelper::~FileHelper()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
NS_IMPL_THREADSAFE_ISUPPORTS1(FileHelper, nsIRequestObserver)
nsresult
FileHelper::Enqueue()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
FileService* service = FileService::GetOrCreate();
NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
nsresult rv = service->Enqueue(mLockedFile, this);
NS_ENSURE_SUCCESS(rv, rv);
if (mLockedFile) {
mLockedFile->OnNewRequest();
}
return NS_OK;
}
nsresult
FileHelper::AsyncRun(FileHelperListener* aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// Assign the listener early, so we can use it synchronously if the code
// below fails.
mListener = aListener;
nsresult rv;
nsCOMPtr<nsISupports> stream;
if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
rv = mLockedFile->CreateParallelStream(getter_AddRefs(stream));
}
else {
rv = mLockedFile->GetOrCreateStream(getter_AddRefs(stream));
}
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(stream, "This should never be null!");
rv = DoAsyncRun(stream);
}
if (NS_FAILED(rv)) {
mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
Finish();
}
return NS_OK;
}
NS_IMETHODIMP
FileHelper::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
return NS_OK;
}
NS_IMETHODIMP
FileHelper::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
nsresult aStatus)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (NS_FAILED(aStatus)) {
mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
Finish();
return NS_OK;
}
void
FileHelper::OnStreamProgress(PRUint64 aProgress, PRUint64 aProgressMax)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mLockedFile->IsAborted()) {
NS_ASSERTION(mRequest, "Should have a request!\n");
nsresult rv = mRequest->Cancel(NS_BINDING_ABORTED);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to cancel the request!");
}
return;
}
if (mFileRequest) {
mFileRequest->OnProgress(aProgress, aProgressMax);
}
}
// static
LockedFile*
FileHelper::GetCurrentLockedFile()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
return gCurrentLockedFile;
}
nsresult
FileHelper::GetSuccessResult(JSContext* aCx,
jsval* aVal)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
*aVal = JSVAL_VOID;
return NS_OK;
}
void
FileHelper::ReleaseObjects()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileStorage = nsnull;
mLockedFile = nsnull;
mFileRequest = nsnull;
mListener = nsnull;
mRequest = nsnull;
}
void
FileHelper::Finish()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mFinished) {
return;
}
mFinished = true;
if (mLockedFile->IsAborted()) {
// Always fire a "error" event with ABORT_ERR if the transaction was
// aborted, even if the request succeeded or failed with another error.
mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR;
}
LockedFile* oldLockedFile = gCurrentLockedFile;
gCurrentLockedFile = mLockedFile;
if (mFileRequest) {
nsresult rv = mFileRequest->NotifyHelperCompleted(this);
if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
mResultCode = rv;
}
}
NS_ASSERTION(gCurrentLockedFile == mLockedFile, "Should be unchanged!");
gCurrentLockedFile = oldLockedFile;
mLockedFile->OnRequestFinished();
mListener->OnFileHelperComplete(this);
ReleaseObjects();
NS_ASSERTION(!(mFileStorage || mLockedFile || mFileRequest || mListener ||
mRequest), "Subclass didn't call FileHelper::ReleaseObjects!");
}

107
dom/file/FileHelper.h Normal file
View File

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_filehelper_h__
#define mozilla_dom_file_filehelper_h__
#include "FileCommon.h"
#include "nsIRequestObserver.h"
class nsIFileStorage;
BEGIN_FILE_NAMESPACE
class FileHelper;
class FileRequest;
class FileOutputStreamWrapper;
class LockedFile;
class FileHelperListener
{
public:
NS_IMETHOD_(nsrefcnt)
AddRef() = 0;
NS_IMETHOD_(nsrefcnt)
Release() = 0;
virtual void
OnFileHelperComplete(FileHelper* aFileHelper) = 0;
};
/**
* Must be subclassed. The subclass must implement DoAsyncRun. It may then
* choose to implement GetSuccessResult to properly set the result of the
* success event. Call Enqueue to start the file operation.
*/
class FileHelper : public nsIRequestObserver
{
friend class FileRequest;
friend class FileOutputStreamWrapper;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
nsresult
Enqueue();
nsresult
AsyncRun(FileHelperListener* aListener);
void
OnStreamProgress(PRUint64 aProgress, PRUint64 aProgressMax);
void
OnStreamClose()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Finish();
}
void
OnStreamDestroy()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Finish();
}
static LockedFile*
GetCurrentLockedFile();
protected:
FileHelper(LockedFile* aLockedFile, FileRequest* aRequest);
virtual ~FileHelper();
virtual nsresult
DoAsyncRun(nsISupports* aStream) = 0;
virtual nsresult
GetSuccessResult(JSContext* aCx, jsval* aVal);
virtual void
ReleaseObjects();
void
Finish();
nsCOMPtr<nsIFileStorage> mFileStorage;
nsRefPtr<LockedFile> mLockedFile;
nsRefPtr<FileRequest> mFileRequest;
nsRefPtr<FileHelperListener> mListener;
nsCOMPtr<nsIRequest> mRequest;
private:
nsresult mResultCode;
bool mFinished;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filehelper_h__

169
dom/file/FileRequest.cpp Normal file
View File

@ -0,0 +1,169 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "FileRequest.h"
#include "nsIJSContextStack.h"
#include "nsIPrivateDOMEvent.h"
#include "nsContentUtils.h"
#include "nsEventDispatcher.h"
#include "nsDOMProgressEvent.h"
#include "FileHelper.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
FileRequest::FileRequest(nsIDOMWindow* aWindow)
: DOMRequest(aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
FileRequest::~FileRequest()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
// static
already_AddRefed<FileRequest>
FileRequest::Create(nsIDOMWindow* aOwner,
LockedFile* aLockedFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<FileRequest> request = new FileRequest(aOwner);
request->mLockedFile = aLockedFile;
return request.forget();
}
nsresult
FileRequest::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
aVisitor.mCanHandle = true;
aVisitor.mParentTarget = mLockedFile;
return NS_OK;
}
nsresult
FileRequest::NotifyHelperCompleted(FileHelper* aFileHelper)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv = aFileHelper->mResultCode;
// If the request failed then fire error event and return.
if (NS_FAILED(rv)) {
FireError(rv);
return NS_OK;
}
// Otherwise we need to get the result from the helper.
jsval result;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_STATE(sc);
JSContext* cx = sc->GetNativeContext();
NS_ASSERTION(cx, "Failed to get a context!");
JSObject* global = sc->GetNativeGlobal();
NS_ASSERTION(global, "Failed to get global object!");
JSAutoRequest ar(cx);
JSAutoEnterCompartment ac;
if (ac.enter(cx, global)) {
rv = aFileHelper->GetSuccessResult(cx, &result);
if (NS_FAILED(rv)) {
NS_WARNING("GetSuccessResult failed!");
}
}
else {
NS_WARNING("Failed to enter correct compartment!");
rv = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
if (NS_SUCCEEDED(rv)) {
FireSuccess(result);
}
else {
FireError(rv);
}
return NS_OK;
}
NS_IMETHODIMP
FileRequest::GetLockedFile(nsIDOMLockedFile** aLockedFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIDOMLockedFile> lockedFile(mLockedFile);
lockedFile.forget(aLockedFile);
return NS_OK;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(FileRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileRequest, DOMRequest)
// Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because
// nsDOMEventTargetHelper does it for us.
NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(progress)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mLockedFile,
nsIDOMLockedFile)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileRequest, DOMRequest)
NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(progress)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLockedFile)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileRequest)
NS_INTERFACE_MAP_ENTRY(nsIDOMFileRequest)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
NS_IMPL_ADDREF_INHERITED(FileRequest, DOMRequest)
NS_IMPL_RELEASE_INHERITED(FileRequest, DOMRequest)
DOMCI_DATA(FileRequest, FileRequest)
NS_IMPL_EVENT_HANDLER(FileRequest, progress);
void
FileRequest::FireProgressEvent(PRUint64 aLoaded, PRUint64 aTotal)
{
if (NS_FAILED(CheckInnerWindowCorrectness())) {
return;
}
nsRefPtr<nsDOMProgressEvent> event = new nsDOMProgressEvent(nsnull, nsnull);
nsresult rv = event->InitProgressEvent(NS_LITERAL_STRING("progress"),
false, false, false, aLoaded, aTotal);
NS_ENSURE_SUCCESS(rv,);
rv = event->SetTrusted(true);
NS_ENSURE_SUCCESS(rv,);
bool dummy;
rv = DispatchEvent(static_cast<nsIDOMProgressEvent*>(event), &dummy);
NS_ENSURE_SUCCESS(rv,);
}
void
FileRequest::RootResultVal()
{
NS_ASSERTION(!mRooted, "Don't call me if already rooted!");
nsXPCOMCycleCollectionParticipant *participant;
CallQueryInterface(this, &participant);
nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(this, DOMRequest),
participant);
mRooted = true;
}

64
dom/file/FileRequest.h Normal file
View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_filerequest_h__
#define mozilla_dom_file_filerequest_h__
#include "FileCommon.h"
#include "nsIDOMFileRequest.h"
#include "DOMRequest.h"
BEGIN_FILE_NAMESPACE
class FileHelper;
class LockedFile;
class FileRequest : public mozilla::dom::DOMRequest,
public nsIDOMFileRequest
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMFILEREQUEST
NS_FORWARD_NSIDOMDOMREQUEST(DOMRequest::)
NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(DOMRequest::)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileRequest, DOMRequest)
static already_AddRefed<FileRequest>
Create(nsIDOMWindow* aOwner, LockedFile* aLockedFile);
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(nsEventChainPreVisitor& aVisitor);
void
OnProgress(PRUint64 aProgress, PRUint64 aProgressMax)
{
FireProgressEvent(aProgress, aProgressMax);
}
nsresult
NotifyHelperCompleted(FileHelper* aFileHelper);
private:
FileRequest(nsIDOMWindow* aWindow);
~FileRequest();
void
FireProgressEvent(PRUint64 aLoaded, PRUint64 aTotal);
virtual void
RootResultVal();
nsRefPtr<LockedFile> mLockedFile;
NS_DECL_EVENT_HANDLER(progress)
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filerequest_h__

528
dom/file/FileService.cpp Normal file
View File

@ -0,0 +1,528 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "FileService.h"
#include "nsIFile.h"
#include "nsIFileStorage.h"
#include "nsIObserverService.h"
#include "nsIStreamTransportService.h"
#include "nsNetCID.h"
#include "FileHandle.h"
#include "FileRequest.h"
USING_FILE_NAMESPACE
namespace {
FileService* gInstance = nsnull;
bool gShutdown = false;
} // anonymous namespace
FileService::FileService()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "More than one instance!");
}
FileService::~FileService()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "More than one instance!");
}
nsresult
FileService::Init()
{
mFileStorageInfos.Init();
nsresult rv;
mStreamTransportTarget =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStreamTransportService> sts =
do_QueryInterface(mStreamTransportTarget);
rv = sts->RaiseThreadLimit();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
FileService::Cleanup()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsIThread* thread = NS_GetCurrentThread();
while (mFileStorageInfos.Count()) {
if (!NS_ProcessNextEvent(thread)) {
NS_ERROR("Failed to process next event!");
break;
}
}
// Make sure the service is still accessible while any generated callbacks
// are processed.
nsresult rv = NS_ProcessPendingEvents(thread);
NS_ENSURE_SUCCESS(rv, rv);
if (!mCompleteCallbacks.IsEmpty()) {
// Run all callbacks manually now.
for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
mCompleteCallbacks[index].mCallback->Run();
}
mCompleteCallbacks.Clear();
// And make sure they get processed.
rv = NS_ProcessPendingEvents(thread);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// static
FileService*
FileService::GetOrCreate()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (gShutdown) {
NS_WARNING("Calling GetOrCreate() after shutdown!");
return nsnull;
}
if (!gInstance) {
nsRefPtr<FileService> service(new FileService);
nsresult rv = service->Init();
NS_ENSURE_SUCCESS(rv, nsnull);
nsCOMPtr<nsIObserverService> obs =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, nsnull);
rv = obs->AddObserver(service, "profile-before-change", false);
NS_ENSURE_SUCCESS(rv, nsnull);
// The observer service now owns us.
gInstance = service;
}
return gInstance;
}
// static
FileService*
FileService::Get()
{
// Does not return an owning reference.
return gInstance;
}
// static
void
FileService::Shutdown()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
gShutdown = true;
if (gInstance) {
if (NS_FAILED(gInstance->Cleanup())) {
NS_WARNING("Failed to shutdown file service!");
}
gInstance = nsnull;
}
}
// static
bool
FileService::IsShuttingDown()
{
return gShutdown;
}
nsresult
FileService::Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aLockedFile, "Null pointer!");
FileHandle* fileHandle = aLockedFile->mFileHandle;
if (fileHandle->mFileStorage->IsStorageInvalidated()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsISupports* storageId = fileHandle->mFileStorage->StorageId();
const nsAString& fileName = fileHandle->mFileName;
bool modeIsWrite = aLockedFile->mMode == LockedFile::READ_WRITE;
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
nsAutoPtr<FileStorageInfo> newFileStorageInfo(new FileStorageInfo());
mFileStorageInfos.Put(storageId, newFileStorageInfo);
fileStorageInfo = newFileStorageInfo.forget();
}
LockedFileQueue* existingLockedFileQueue =
fileStorageInfo->GetLockedFileQueue(aLockedFile);
if (existingLockedFileQueue) {
existingLockedFileQueue->Enqueue(aFileHelper);
return NS_OK;
}
bool lockedForReading = fileStorageInfo->IsFileLockedForReading(fileName);
bool lockedForWriting = fileStorageInfo->IsFileLockedForWriting(fileName);
if (modeIsWrite) {
if (!lockedForWriting) {
fileStorageInfo->LockFileForWriting(fileName);
}
}
else {
if (!lockedForReading) {
fileStorageInfo->LockFileForReading(fileName);
}
}
if (lockedForWriting || (lockedForReading && modeIsWrite)) {
fileStorageInfo->CreateDelayedEnqueueInfo(aLockedFile, aFileHelper);
}
else {
LockedFileQueue* lockedFileQueue =
fileStorageInfo->CreateLockedFileQueue(aLockedFile);
if (aFileHelper) {
// Enqueue() will queue the file helper if there's already something
// running. That can't fail, so no need to eventually remove
// fileStorageInfo from the hash table.
//
// If the file helper is free to run then AsyncRun() is called on the
// file helper. AsyncRun() is responsible for calling all necessary
// callbacks when something fails. We're propagating the error here,
// however there's no need to eventually remove fileStorageInfo from
// the hash table. Code behind AsyncRun() will take care of it. The last
// item in the code path is NotifyLockedFileCompleted() which removes
// fileStorageInfo from the hash table if there are no locked files for
// the file storage.
nsresult rv = lockedFileQueue->Enqueue(aFileHelper);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
void
FileService::NotifyLockedFileCompleted(LockedFile* aLockedFile)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aLockedFile, "Null pointer!");
FileHandle* fileHandle = aLockedFile->mFileHandle;
nsISupports* storageId = fileHandle->mFileStorage->StorageId();
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
NS_ERROR("We don't know anyting about this locked file?!");
return;
}
fileStorageInfo->RemoveLockedFileQueue(aLockedFile);
if (!fileStorageInfo->HasRunningLockedFiles()) {
mFileStorageInfos.Remove(storageId);
#ifdef DEBUG
storageId = nsnull;
#endif
// See if we need to fire any complete callbacks.
PRUint32 index = 0;
while (index < mCompleteCallbacks.Length()) {
if (MaybeFireCallback(mCompleteCallbacks[index])) {
mCompleteCallbacks.RemoveElementAt(index);
}
else {
index++;
}
}
}
}
bool
FileService::WaitForAllStoragesToComplete(
nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
nsIRunnable* aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aStorages.IsEmpty(), "No databases to wait on!");
NS_ASSERTION(aCallback, "Null pointer!");
StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
callback->mCallback = aCallback;
callback->mStorages.SwapElements(aStorages);
if (MaybeFireCallback(*callback)) {
mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
}
return true;
}
void
FileService::AbortLockedFilesForStorage(nsIFileStorage* aFileStorage)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aFileStorage, "Null pointer!");
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
return;
}
nsAutoTArray<nsRefPtr<LockedFile>, 10> lockedFiles;
fileStorageInfo->CollectRunningAndDelayedLockedFiles(aFileStorage,
lockedFiles);
for (PRUint32 index = 0; index < lockedFiles.Length(); index++) {
lockedFiles[index]->Abort();
}
}
bool
FileService::HasLockedFilesForStorage(nsIFileStorage* aFileStorage)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aFileStorage, "Null pointer!");
FileStorageInfo* fileStorageInfo;
if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
return false;
}
return fileStorageInfo->HasRunningLockedFiles(aFileStorage);
}
NS_IMPL_ISUPPORTS1(FileService, nsIObserver)
NS_IMETHODIMP
FileService::Observe(nsISupports* aSubject, const char* aTopic,
const PRUnichar* aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!strcmp(aTopic, "profile-before-change"), "Wrong topic!");
Shutdown();
return NS_OK;
}
bool
FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
for (PRUint32 index = 0; index < aCallback.mStorages.Length(); index++) {
if (mFileStorageInfos.Get(aCallback.mStorages[index]->StorageId(),
nsnull)) {
return false;
}
}
aCallback.mCallback->Run();
return true;
}
FileService::LockedFileQueue::LockedFileQueue(LockedFile* aLockedFile)
: mLockedFile(aLockedFile)
{
NS_ASSERTION(aLockedFile, "Null pointer!");
}
NS_IMPL_THREADSAFE_ADDREF(FileService::LockedFileQueue)
NS_IMPL_THREADSAFE_RELEASE(FileService::LockedFileQueue)
nsresult
FileService::LockedFileQueue::Enqueue(FileHelper* aFileHelper)
{
mQueue.AppendElement(aFileHelper);
nsresult rv;
if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
rv = aFileHelper->AsyncRun(this);
}
else {
rv = ProcessQueue();
}
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
FileService::LockedFileQueue::OnFileHelperComplete(FileHelper* aFileHelper)
{
if (mLockedFile->mRequestMode == LockedFile::PARALLEL) {
PRInt32 index = mQueue.IndexOf(aFileHelper);
NS_ASSERTION(index != -1, "We don't know anything about this helper!");
mQueue.RemoveElementAt(index);
}
else {
NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!");
mCurrentHelper = nsnull;
nsresult rv = ProcessQueue();
NS_ENSURE_SUCCESS(rv,);
}
}
nsresult
FileService::LockedFileQueue::ProcessQueue()
{
if (mQueue.IsEmpty() || mCurrentHelper) {
return NS_OK;
}
mCurrentHelper = mQueue[0];
mQueue.RemoveElementAt(0);
nsresult rv = mCurrentHelper->AsyncRun(this);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
FileService::LockedFileQueue*
FileService::FileStorageInfo::CreateLockedFileQueue(LockedFile* aLockedFile)
{
nsRefPtr<LockedFileQueue>* lockedFileQueue =
mLockedFileQueues.AppendElement();
*lockedFileQueue = new LockedFileQueue(aLockedFile);
return lockedFileQueue->get();
}
FileService::LockedFileQueue*
FileService::FileStorageInfo::GetLockedFileQueue(LockedFile* aLockedFile)
{
PRUint32 count = mLockedFileQueues.Length();
for (PRUint32 index = 0; index < count; index++) {
nsRefPtr<LockedFileQueue>& lockedFileQueue = mLockedFileQueues[index];
if (lockedFileQueue->mLockedFile == aLockedFile) {
return lockedFileQueue;
}
}
return nsnull;
}
void
FileService::FileStorageInfo::RemoveLockedFileQueue(LockedFile* aLockedFile)
{
PRUint32 lockedFileCount = mLockedFileQueues.Length();
// We can't just remove entries from lock hash tables, we have to rebuild
// them instead. Multiple LockedFile objects may lock the same file
// (one entry can represent multiple locks).
mFilesReading.Clear();
mFilesWriting.Clear();
for (PRUint32 index = 0, count = lockedFileCount; index < count; index++) {
LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
if (lockedFile == aLockedFile) {
NS_ASSERTION(count == lockedFileCount, "More than one match?!");
mLockedFileQueues.RemoveElementAt(index);
index--;
count--;
continue;
}
const nsAString& fileName = lockedFile->mFileHandle->mFileName;
if (lockedFile->mMode == LockedFile::READ_WRITE) {
if (!IsFileLockedForWriting(fileName)) {
LockFileForWriting(fileName);
}
}
else {
if (!IsFileLockedForReading(fileName)) {
LockFileForReading(fileName);
}
}
}
NS_ASSERTION(mLockedFileQueues.Length() == lockedFileCount - 1,
"Didn't find the locked file we were looking for!");
nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
for (PRUint32 index = 0; index < delayedEnqueueInfos.Length(); index++) {
DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mLockedFile,
delayedEnqueueInfo.mFileHelper))) {
NS_WARNING("Enqueue failed!");
}
}
}
bool
FileService::FileStorageInfo::HasRunningLockedFiles(
nsIFileStorage* aFileStorage)
{
for (PRUint32 index = 0; index < mLockedFileQueues.Length(); index++) {
LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
return true;
}
}
return false;
}
FileService::DelayedEnqueueInfo*
FileService::FileStorageInfo::CreateDelayedEnqueueInfo(LockedFile* aLockedFile,
FileHelper* aFileHelper)
{
DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
info->mLockedFile = aLockedFile;
info->mFileHelper = aFileHelper;
return info;
}
void
FileService::FileStorageInfo::CollectRunningAndDelayedLockedFiles(
nsIFileStorage* aFileStorage,
nsTArray<nsRefPtr<LockedFile> >& aLockedFiles)
{
for (PRUint32 index = 0; index < mLockedFileQueues.Length(); index++) {
LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
aLockedFiles.AppendElement(lockedFile);
}
}
for (PRUint32 index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
LockedFile* lockedFile = mDelayedEnqueueInfos[index].mLockedFile;
if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
aLockedFiles.AppendElement(lockedFile);
}
}
}

196
dom/file/FileService.h Normal file
View File

@ -0,0 +1,196 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_fileservice_h__
#define mozilla_dom_file_fileservice_h__
#include "FileCommon.h"
#include "nsIObserver.h"
#include "nsClassHashtable.h"
#include "mozilla/dom/file/FileHelper.h"
#include "mozilla/dom/file/LockedFile.h"
BEGIN_FILE_NAMESPACE
class FileService : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// Returns a non-owning reference!
static FileService*
GetOrCreate();
// Returns a non-owning reference!
static FileService*
Get();
static void
Shutdown();
// Returns true if we've begun the shutdown process.
static bool
IsShuttingDown();
nsresult
Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper);
void
NotifyLockedFileCompleted(LockedFile* aLockedFile);
bool
WaitForAllStoragesToComplete(nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
nsIRunnable* aCallback);
void
AbortLockedFilesForStorage(nsIFileStorage* aFileStorage);
bool
HasLockedFilesForStorage(nsIFileStorage* aFileStorage);
nsIEventTarget*
StreamTransportTarget()
{
NS_ASSERTION(mStreamTransportTarget, "This should never be null!");
return mStreamTransportTarget;
}
private:
class LockedFileQueue : public FileHelperListener
{
friend class FileService;
public:
NS_IMETHOD_(nsrefcnt)
AddRef();
NS_IMETHOD_(nsrefcnt)
Release();
inline nsresult
Enqueue(FileHelper* aFileHelper);
virtual void
OnFileHelperComplete(FileHelper* aFileHelper);
private:
inline
LockedFileQueue(LockedFile* aLockedFile);
nsresult
ProcessQueue();
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
nsRefPtr<LockedFile> mLockedFile;
nsTArray<nsRefPtr<FileHelper> > mQueue;
nsRefPtr<FileHelper> mCurrentHelper;
};
struct DelayedEnqueueInfo
{
nsRefPtr<LockedFile> mLockedFile;
nsRefPtr<FileHelper> mFileHelper;
};
class FileStorageInfo
{
friend class FileService;
public:
inline LockedFileQueue*
CreateLockedFileQueue(LockedFile* aLockedFile);
inline LockedFileQueue*
GetLockedFileQueue(LockedFile* aLockedFile);
void
RemoveLockedFileQueue(LockedFile* aLockedFile);
bool
HasRunningLockedFiles()
{
return !mLockedFileQueues.IsEmpty();
}
inline bool
HasRunningLockedFiles(nsIFileStorage* aFileStorage);
inline DelayedEnqueueInfo*
CreateDelayedEnqueueInfo(LockedFile* aLockedFile, FileHelper* aFileHelper);
inline void
CollectRunningAndDelayedLockedFiles(
nsIFileStorage* aFileStorage,
nsTArray<nsRefPtr<LockedFile> >& aLockedFiles);
void
LockFileForReading(const nsAString& aFileName)
{
mFilesReading.PutEntry(aFileName);
}
void
LockFileForWriting(const nsAString& aFileName)
{
mFilesWriting.PutEntry(aFileName);
}
bool
IsFileLockedForReading(const nsAString& aFileName)
{
return mFilesReading.Contains(aFileName);
}
bool
IsFileLockedForWriting(const nsAString& aFileName)
{
return mFilesWriting.Contains(aFileName);
}
private:
FileStorageInfo()
{
mFilesReading.Init();
mFilesWriting.Init();
}
nsTArray<nsRefPtr<LockedFileQueue> > mLockedFileQueues;
nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
nsTHashtable<nsStringHashKey> mFilesReading;
nsTHashtable<nsStringHashKey> mFilesWriting;
};
struct StoragesCompleteCallback
{
nsTArray<nsCOMPtr<nsIFileStorage> > mStorages;
nsCOMPtr<nsIRunnable> mCallback;
};
FileService();
~FileService();
nsresult
Init();
nsresult
Cleanup();
bool
MaybeFireCallback(StoragesCompleteCallback& aCallback);
nsCOMPtr<nsIEventTarget> mStreamTransportTarget;
nsClassHashtable<nsISupportsHashKey, FileStorageInfo> mFileStorageInfos;
nsTArray<StoragesCompleteCallback> mCompleteCallbacks;
};
END_FILE_NAMESPACE
#endif /* mozilla_dom_file_fileservice_h__ */

View File

@ -0,0 +1,397 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "FileStreamWrappers.h"
#include "nsIFileStorage.h"
#include "nsISeekableStream.h"
#include "nsIStandardFileStream.h"
#include "FileHelper.h"
USING_FILE_NAMESPACE
namespace {
class ProgressRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
ProgressRunnable(FileHelper* aFileHelper,
PRUint64 aProgress,
PRUint64 aProgressMax)
: mFileHelper(aFileHelper),
mProgress(aProgress),
mProgressMax(aProgressMax)
{
}
private:
nsRefPtr<FileHelper> mFileHelper;
PRUint64 mProgress;
PRUint64 mProgressMax;
};
class CloseRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
CloseRunnable(FileHelper* aFileHelper)
: mFileHelper(aFileHelper)
{ }
private:
nsRefPtr<FileHelper> mFileHelper;
};
class DestroyRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
DestroyRunnable(FileHelper* aFileHelper)
: mFileHelper(aFileHelper)
{ }
private:
nsRefPtr<FileHelper> mFileHelper;
};
} // anonymous namespace
FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags)
: mFileStream(aFileStream),
mFileHelper(aFileHelper),
mOffset(aOffset),
mLimit(aLimit),
mFlags(aFlags),
mFirstTime(true)
{
NS_ASSERTION(mFileStream, "Must have a file stream!");
NS_ASSERTION(mFileHelper, "Must have a file helper!");
}
FileStreamWrapper::~FileStreamWrapper()
{
if (mFlags & NOTIFY_DESTROY) {
if (NS_IsMainThread()) {
mFileHelper->OnStreamDestroy();
}
else {
nsCOMPtr<nsIRunnable> runnable =
new DestroyRunnable(mFileHelper);
nsresult rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
}
}
NS_IMPL_THREADSAFE_ISUPPORTS0(FileStreamWrapper)
FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags)
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
{
mInputStream = do_QueryInterface(mFileStream);
NS_ASSERTION(mInputStream, "This should always succeed!");
}
NS_IMPL_ISUPPORTS_INHERITED1(FileInputStreamWrapper,
FileStreamWrapper,
nsIInputStream)
NS_IMETHODIMP
FileInputStreamWrapper::Close()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
if (mFlags & NOTIFY_CLOSE) {
nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
mOffset = 0;
mLimit = 0;
return NS_OK;
}
NS_IMETHODIMP
FileInputStreamWrapper::Available(PRUint32* _retval)
{
// Performing sync IO on the main thread is generally not allowed.
// However, the input stream wrapper is also used to track reads performed by
// other APIs like FileReader, XHR, etc.
// In that case nsInputStreamChannel::OpenContentStream() calls Available()
// before setting the content length property. This property is not important
// to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED
// here. It causes OpenContentStream() to set the content length property to
// zero.
if (NS_IsMainThread()) {
return NS_BASE_STREAM_CLOSED;
}
return mInputStream->Available(_retval);
}
NS_IMETHODIMP
FileInputStreamWrapper::Read(char* aBuf, PRUint32 aCount, PRUint32* _retval)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (mFirstTime) {
mFirstTime = false;
if (mOffset != LL_MAXUINT) {
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
if (seekable) {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
}
mOffset = 0;
}
PRUint64 max = mLimit - mOffset;
if (max == 0) {
*_retval = 0;
return NS_OK;
}
if (aCount > max) {
aCount = max;
}
rv = mInputStream->Read(aBuf, aCount, _retval);
NS_ENSURE_SUCCESS(rv, rv);
mOffset += *_retval;
if (mFlags & NOTIFY_PROGRESS) {
nsCOMPtr<nsIRunnable> runnable =
new ProgressRunnable(mFileHelper, mOffset, mLimit);
rv = NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
return NS_OK;
}
NS_IMETHODIMP
FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
PRUint32 aCount, PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileInputStreamWrapper::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}
FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags)
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
#ifdef DEBUG
, mWriteThread(nsnull)
#endif
{
mOutputStream = do_QueryInterface(mFileStream);
NS_ASSERTION(mOutputStream, "This should always succeed!");
}
NS_IMPL_ISUPPORTS_INHERITED1(FileOutputStreamWrapper,
FileStreamWrapper,
nsIOutputStream)
NS_IMETHODIMP
FileOutputStreamWrapper::Close()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv = NS_OK;
if (!mFirstTime) {
// We must flush buffers of the stream on the same thread on which we wrote
// some data.
nsCOMPtr<nsIStandardFileStream> sstream = do_QueryInterface(mFileStream);
if (sstream) {
rv = sstream->FlushBuffers();
if (NS_FAILED(rv)) {
NS_WARNING("Failed to flush buffers of the stream!");
}
}
NS_ASSERTION(PR_GetCurrentThread() == mWriteThread,
"Unsetting thread locals on wrong thread!");
mFileHelper->mFileStorage->UnsetThreadLocals();
}
if (mFlags & NOTIFY_CLOSE) {
nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
mOffset = 0;
mLimit = 0;
return rv;
}
NS_IMETHODIMP
FileOutputStreamWrapper::Write(const char* aBuf, PRUint32 aCount,
PRUint32* _retval)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (mFirstTime) {
mFirstTime = false;
#ifdef DEBUG
mWriteThread = PR_GetCurrentThread();
#endif
mFileHelper->mFileStorage->SetThreadLocals();
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream);
if (seekable) {
if (mOffset == LL_MAXUINT) {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
}
else {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
}
NS_ENSURE_SUCCESS(rv, rv);
}
mOffset = 0;
}
PRUint64 max = mLimit - mOffset;
if (max == 0) {
*_retval = 0;
return NS_OK;
}
if (aCount > max) {
aCount = max;
}
rv = mOutputStream->Write(aBuf, aCount, _retval);
NS_ENSURE_SUCCESS(rv, rv);
mOffset += *_retval;
if (mFlags & NOTIFY_PROGRESS) {
nsCOMPtr<nsIRunnable> runnable =
new ProgressRunnable(mFileHelper, mOffset, mLimit);
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
}
return NS_OK;
}
NS_IMETHODIMP
FileOutputStreamWrapper::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream,
PRUint32 aCount, PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader,
void* aClosure, PRUint32 aCount,
PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileOutputStreamWrapper::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(ProgressRunnable, nsIRunnable)
NS_IMETHODIMP
ProgressRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamProgress(mProgress, mProgressMax);
mFileHelper = nsnull;
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(CloseRunnable, nsIRunnable)
NS_IMETHODIMP
CloseRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamClose();
mFileHelper = nsnull;
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(DestroyRunnable, nsIRunnable)
NS_IMETHODIMP
DestroyRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamDestroy();
mFileHelper = nsnull;
return NS_OK;
}

View File

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_filestreamwrappers_h__
#define mozilla_dom_file_filestreamwrappers_h__
#include "FileCommon.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
BEGIN_FILE_NAMESPACE
class FileHelper;
class FileStreamWrapper : public nsISupports
{
public:
NS_DECL_ISUPPORTS
FileStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags);
virtual ~FileStreamWrapper();
enum {
NOTIFY_PROGRESS = 1 << 0,
NOTIFY_CLOSE = 1 << 1,
NOTIFY_DESTROY = 1 << 2
};
protected:
nsCOMPtr<nsISupports> mFileStream;
nsRefPtr<FileHelper> mFileHelper;
PRUint64 mOffset;
PRUint64 mLimit;
PRUint32 mFlags;
bool mFirstTime;
};
class FileInputStreamWrapper : public FileStreamWrapper,
public nsIInputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIINPUTSTREAM
FileInputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags);
protected:
virtual ~FileInputStreamWrapper()
{ }
private:
nsCOMPtr<nsIInputStream> mInputStream;
};
class FileOutputStreamWrapper : public FileStreamWrapper,
public nsIOutputStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIOUTPUTSTREAM
FileOutputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
PRUint64 aOffset,
PRUint64 aLimit,
PRUint32 aFlags);
protected:
virtual ~FileOutputStreamWrapper()
{ }
private:
nsCOMPtr<nsIOutputStream> mOutputStream;
#ifdef DEBUG
void* mWriteThread;
#endif
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_filestreamwrappers_h__

1119
dom/file/LockedFile.cpp Normal file

File diff suppressed because it is too large Load Diff

155
dom/file/LockedFile.h Normal file
View File

@ -0,0 +1,155 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_lockedfile_h__
#define mozilla_dom_file_lockedfile_h__
#include "FileCommon.h"
#include "nsIDOMLockedFile.h"
#include "nsIRunnable.h"
#include "nsDOMEventTargetHelper.h"
class nsIInputStream;
BEGIN_FILE_NAMESPACE
class FileHandle;
class FileRequest;
class MetadataHelper;
class LockedFile : public nsDOMEventTargetHelper,
public nsIDOMLockedFile,
public nsIRunnable
{
friend class FinishHelper;
friend class FileService;
friend class FileHelper;
friend class MetadataHelper;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMLOCKEDFILE
NS_DECL_NSIRUNNABLE
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LockedFile, nsDOMEventTargetHelper)
enum Mode
{
READ_ONLY = 0,
READ_WRITE
};
enum RequestMode
{
NORMAL = 0, // Sequential
PARALLEL
};
enum ReadyState
{
INITIAL = 0,
LOADING,
FINISHING,
DONE
};
static already_AddRefed<LockedFile>
Create(FileHandle* aFileHandle,
Mode aMode,
RequestMode aRequestMode = NORMAL);
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(nsEventChainPreVisitor& aVisitor);
nsresult
CreateParallelStream(nsISupports** aStream);
nsresult
GetOrCreateStream(nsISupports** aStream);
bool
IsOpen() const;
bool
IsAborted() const
{
return mAborted;
}
FileHandle*
Handle() const
{
return mFileHandle;
}
nsresult
OpenInputStream(bool aWholeFile, PRUint64 aStart, PRUint64 aLength,
nsIInputStream** aResult);
private:
LockedFile();
~LockedFile();
void
OnNewRequest();
void
OnRequestFinished();
inline already_AddRefed<FileRequest>
GenerateFileRequest();
nsresult
WriteOrAppend(const jsval& aValue, JSContext* aCx,
nsIDOMFileRequest** _retval, bool aAppend);
nsresult
Finish();
nsRefPtr<FileHandle> mFileHandle;
ReadyState mReadyState;
Mode mMode;
RequestMode mRequestMode;
PRUint64 mLocation;
PRUint32 mPendingRequests;
NS_DECL_EVENT_HANDLER(complete)
NS_DECL_EVENT_HANDLER(abort)
NS_DECL_EVENT_HANDLER(error)
nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
nsCOMPtr<nsISupports> mStream;
bool mAborted;
bool mCreating;
};
class FinishHelper : public nsIRunnable
{
friend class LockedFile;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
private:
FinishHelper(LockedFile* aLockedFile);
~FinishHelper()
{ }
nsRefPtr<LockedFile> mLockedFile;
nsTArray<nsCOMPtr<nsISupports> > mParallelStreams;
nsCOMPtr<nsISupports> mStream;
bool mAborted;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_lockedfile_h__

58
dom/file/Makefile.in Normal file
View File

@ -0,0 +1,58 @@
# 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/.
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
LIBRARY_NAME = domfile_s
XPIDL_MODULE = dom_file
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
include $(topsrcdir)/dom/dom-config.mk
EXPORTS_NAMESPACES = mozilla/dom/file
CPPSRCS = \
AsyncHelper.cpp \
DOMFileHandle.cpp \
File.cpp \
FileHandle.cpp \
FileHelper.cpp \
FileRequest.cpp \
FileService.cpp \
FileStreamWrappers.cpp \
LockedFile.cpp \
MemoryStreams.cpp \
MetadataHelper.cpp \
$(NULL)
EXPORTS = \
nsIFileStorage.h \
$(NULL)
EXPORTS_mozilla/dom/file = \
DOMFileHandle.h \
File.h \
FileCommon.h \
FileHandle.h \
FileHelper.h \
FileService.h \
LockedFile.h \
$(NULL)
XPIDLSRCS = \
nsIDOMFileHandle.idl \
nsIDOMFileRequest.idl \
nsIDOMLockedFile.idl \
$(NULL)
TEST_DIRS += test
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "MemoryStreams.h"
#include "nsStreamUtils.h"
USING_FILE_NAMESPACE
// static
already_AddRefed<MemoryOutputStream>
MemoryOutputStream::Create(PRUint64 aSize)
{
NS_ASSERTION(aSize, "Passed zero size!");
NS_ENSURE_TRUE(aSize <= PR_UINT32_MAX, nsnull);
nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream();
char* dummy;
PRUint32 length = stream->mData.GetMutableData(&dummy, aSize, fallible_t());
NS_ENSURE_TRUE(length == aSize, nsnull);
return stream.forget();
}
NS_IMPL_THREADSAFE_ISUPPORTS1(MemoryOutputStream, nsIOutputStream)
NS_IMETHODIMP
MemoryOutputStream::Close()
{
mData.Truncate(mOffset);
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::Write(const char* aBuf, PRUint32 aCount, PRUint32* _retval)
{
return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval);
}
NS_IMETHODIMP
MemoryOutputStream::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, PRUint32 aCount,
PRUint32* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
PRUint32 aCount, PRUint32* _retval)
{
NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!");
PRUint32 maxCount = mData.Length() - mOffset;
if (maxCount == 0) {
*_retval = 0;
return NS_OK;
}
if (aCount > maxCount) {
aCount = maxCount;
}
nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0,
aCount, _retval);
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(*_retval <= aCount,
"Reader should not read more than we asked it to read!");
mOffset += *_retval;
}
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}

46
dom/file/MemoryStreams.h Normal file
View File

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_memorystreams_h__
#define mozilla_dom_file_memorystreams_h__
#include "FileCommon.h"
#include "nsIOutputStream.h"
BEGIN_FILE_NAMESPACE
class MemoryOutputStream : public nsIOutputStream
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
static already_AddRefed<MemoryOutputStream>
Create(PRUint64 aSize);
const nsCString&
Data() const
{
return mData;
}
private:
MemoryOutputStream()
: mOffset(0)
{ }
virtual ~MemoryOutputStream()
{ }
nsCString mData;
PRUint64 mOffset;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_memorystreams_h__

106
dom/file/MetadataHelper.cpp Normal file
View File

@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "MetadataHelper.h"
#include "LockedFile.h"
USING_FILE_NAMESPACE
nsresult
MetadataHelper::DoAsyncRun(nsISupports* aStream)
{
bool readWrite = mLockedFile->mMode == LockedFile::READ_WRITE;
nsRefPtr<AsyncMetadataGetter> getter =
new AsyncMetadataGetter(aStream, mParams, readWrite);
nsresult rv = getter->AsyncWork(this, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
MetadataHelper::GetSuccessResult(JSContext* aCx,
jsval* aVal)
{
JSObject* obj = JS_NewObject(aCx, nsnull, nsnull, nsnull);
NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
if (mParams->SizeRequested()) {
jsval val;
if (mParams->Size() <= JSVAL_INT_MAX) {
val = INT_TO_JSVAL(mParams->Size());
}
else {
double size = mParams->Size();
if (!JS_NewNumberValue(aCx, size, &val)) {
return NS_ERROR_FAILURE;
}
}
if (!JS_DefineProperty(aCx, obj, "size", val, nsnull, nsnull,
JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
if (mParams->LastModifiedRequested()) {
double msec = mParams->LastModified();
JSObject *date = JS_NewDateObjectMsec(aCx, msec);
NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
if (!JS_DefineProperty(aCx, obj, "lastModified", OBJECT_TO_JSVAL(date),
nsnull, nsnull, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
*aVal = OBJECT_TO_JSVAL(obj);
return NS_OK;
}
nsresult
MetadataHelper::AsyncMetadataGetter::DoStreamWork(nsISupports* aStream)
{
nsresult rv;
if (mReadWrite) {
// Force a flush (so all pending writes are flushed to the disk and file
// metadata is updated too).
nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
NS_ASSERTION(ostream, "This should always succeed!");
rv = ostream->Flush();
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(aStream);
if (mParams->SizeRequested()) {
PRInt64 size;
rv = metadata->GetSize(&size);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(size >= 0, NS_ERROR_FAILURE);
mParams->mSize = PRUint64(size);
}
if (mParams->LastModifiedRequested()) {
PRInt64 lastModified;
rv = metadata->GetLastModified(&lastModified);
NS_ENSURE_SUCCESS(rv, rv);
mParams->mLastModified = lastModified;
}
return NS_OK;
}

122
dom/file/MetadataHelper.h Normal file
View File

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_file_metadatahelper_h__
#define mozilla_dom_file_metadatahelper_h__
#include "FileCommon.h"
#include "nsIFileStreams.h"
#include "DictionaryHelpers.h"
#include "AsyncHelper.h"
#include "FileHelper.h"
class nsIFileStream;
BEGIN_FILE_NAMESPACE
class MetadataHelper;
class MetadataParameters
{
friend class MetadataHelper;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataParameters)
nsresult
Init(JSContext* aCx, const jsval* aVal)
{
return mConfig.Init(aCx, aVal);
}
void
Init(bool aRequestSize, bool aRequestLastModified)
{
mConfig.size = aRequestSize;
mConfig.lastModified = aRequestLastModified;
}
bool
IsConfigured() const
{
return mConfig.size || mConfig.lastModified;
}
bool
SizeRequested() const
{
return mConfig.size;
}
bool
LastModifiedRequested() const
{
return mConfig.lastModified;
}
PRUint64
Size() const
{
return mSize;
}
PRInt64
LastModified() const
{
return mLastModified;
}
private:
DOMFileMetadataParameters mConfig;
PRUint64 mSize;
PRInt64 mLastModified;
};
class MetadataHelper : public FileHelper
{
public:
MetadataHelper(LockedFile* aLockedFile,
FileRequest* aFileRequest,
MetadataParameters* aParams)
: FileHelper(aLockedFile, aFileRequest),
mParams(aParams)
{ }
nsresult
DoAsyncRun(nsISupports* aStream);
nsresult
GetSuccessResult(JSContext* aCx, jsval* aVal);
protected:
class AsyncMetadataGetter : public AsyncHelper
{
public:
AsyncMetadataGetter(nsISupports* aStream, MetadataParameters* aParams,
bool aReadWrite)
: AsyncHelper(aStream),
mParams(aParams), mReadWrite(aReadWrite)
{ }
protected:
nsresult
DoStreamWork(nsISupports* aStream);
private:
nsRefPtr<MetadataParameters> mParams;
bool mReadWrite;
};
nsRefPtr<MetadataParameters> mParams;
};
END_FILE_NAMESPACE
#endif // mozilla_dom_file_metadatahelper_h__

View File

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsISupports.idl"
%{C++
namespace mozilla {
namespace dom {
namespace indexedDB {
class FileInfo;
}
}
}
%}
[ptr] native FileInfo(mozilla::dom::indexedDB::FileInfo);
interface nsIDOMEventListener;
interface nsIDOMFileRequest;
interface nsIDOMLockedFile;
[scriptable, builtinclass, uuid(0dc9c73c-4e44-4430-8898-85f61a70b1d2)]
interface nsIDOMFileHandle : nsISupports
{
readonly attribute DOMString name;
readonly attribute DOMString type;
// mode can be either "readonly" or "readwrite"
[optional_argc]
nsIDOMLockedFile
open([optional /* "readonly" */] in DOMString mode);
nsIDOMFileRequest
getFile();
[notxpcom]
long long
getFileId();
[notxpcom]
FileInfo
getFileInfo();
attribute nsIDOMEventListener onabort;
attribute nsIDOMEventListener onerror;
};

View File

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsIDOMDOMRequest.idl"
interface nsIDOMEventListener;
interface nsIDOMLockedFile;
[scriptable, builtinclass, uuid(fe06b66e-fede-4d44-ab3a-403f60d6b593)]
interface nsIDOMFileRequest : nsIDOMDOMRequest
{
readonly attribute nsIDOMLockedFile lockedFile;
attribute nsIDOMEventListener onprogress;
};

View File

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsISupports.idl"
interface nsIDOMEventListener;
interface nsIDOMFileHandle;
interface nsIDOMFileRequest;
dictionary DOMFileMetadataParameters
{
boolean size;
boolean lastModified;
};
[scriptable, builtinclass, uuid(63055eeb-cc19-468b-bafa-7b7961796340)]
interface nsIDOMLockedFile : nsISupports
{
readonly attribute nsIDOMFileHandle fileHandle;
// "readonly" or "readwrite"
readonly attribute DOMString mode;
readonly attribute boolean active;
attribute unsigned long long location;
[implicit_jscontext]
nsIDOMFileRequest
getMetadata(/* DOMFileMetadataParameters */
[optional /* all */] in jsval parameters);
[implicit_jscontext]
nsIDOMFileRequest
readAsArrayBuffer(in unsigned long long size);
nsIDOMFileRequest
readAsText(in unsigned long long size,
[optional] in DOMString encoding);
[implicit_jscontext]
nsIDOMFileRequest
write(in jsval value);
[implicit_jscontext]
nsIDOMFileRequest
append(in jsval value);
nsIDOMFileRequest
truncate();
nsIDOMFileRequest
flush();
void
abort();
attribute nsIDOMEventListener oncomplete;
attribute nsIDOMEventListener onabort;
attribute nsIDOMEventListener onerror;
};

55
dom/file/nsIFileStorage.h Normal file
View File

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 nsIFileStorage_h__
#define nsIFileStorage_h__
#include "nsISupports.h"
#define NS_FILESTORAGE_IID \
{0xbba9c2ff, 0x85c9, 0x47c1, \
{ 0xaf, 0xce, 0x0a, 0x7e, 0x6f, 0x21, 0x50, 0x95 } }
class nsIFileStorage : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_FILESTORAGE_IID)
virtual nsISupports*
StorageId() = 0;
virtual bool
IsStorageInvalidated() = 0;
virtual bool
IsStorageShuttingDown() = 0;
virtual void
SetThreadLocals() = 0;
virtual void
UnsetThreadLocals() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIFileStorage, NS_FILESTORAGE_IID)
#define NS_DECL_NSIFILESTORAGE \
virtual nsISupports* \
StorageId(); \
\
virtual bool \
IsStorageInvalidated(); \
\
virtual bool \
IsStorageShuttingDown(); \
\
virtual void \
SetThreadLocals(); \
\
virtual void \
UnsetThreadLocals();
#endif // nsIFileStorage_h__

33
dom/file/test/Makefile.in Normal file
View File

@ -0,0 +1,33 @@
# 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/.
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = dom/file/test
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
TEST_FILES = \
helpers.js \
test_append_read_data.html \
test_getFileId.html \
test_lockedfile_lifetimes.html \
test_lockedfile_lifetimes_nested.html \
test_lockedfile_ordering.html \
test_overlapping_lockedfiles.html \
test_progress_events.html \
test_readonly_lockedfiles.html \
test_request_readyState.html \
test_stream_tracking.html \
test_success_events_after_abort.html \
test_truncate.html \
test_write_read_data.html \
$(NULL)
libs:: $(TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)

220
dom/file/test/helpers.js Normal file
View File

@ -0,0 +1,220 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const IndexedDatabaseKey = "IDB";
const DeviceStorageKey = "DS";
var fileStorages = [
{ key: IndexedDatabaseKey }
// { key: DeviceStorageKey }
];
var utils = SpecialPowers.getDOMWindowUtils(window);
var testGenerator = testSteps();
function runTest()
{
allowIndexedDB();
allowUnlimitedQuota();
SimpleTest.waitForExplicitFinish();
testGenerator.next();
}
function finishTest()
{
resetUnlimitedQuota();
resetIndexedDB();
SimpleTest.executeSoon(function() {
testGenerator.close();
SimpleTest.finish();
});
}
function grabEventAndContinueHandler(event)
{
testGenerator.send(event);
}
function continueToNextStep()
{
SimpleTest.executeSoon(function() {
testGenerator.next();
});
}
function errorHandler(event)
{
ok(false, "indexedDB error, code " + event.target.errorCode);
finishTest();
}
function unexpectedSuccessHandler()
{
ok(false, "Got success, but did not expect it!");
finishTest();
}
function ExpectError(name)
{
this._name = name;
}
ExpectError.prototype = {
handleEvent: function(event)
{
is(event.type, "error", "Got an error event");
is(event.target.error.name, this._name, "Expected error was thrown.");
event.preventDefault();
event.stopPropagation();
grabEventAndContinueHandler(event);
}
};
function addPermission(type, allow, url)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
let uri;
if (url) {
uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url, null, null);
}
else {
uri = SpecialPowers.getDocumentURIObject(window.document);
}
let permission;
if (allow) {
permission = Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
}
else {
permission = Components.interfaces.nsIPermissionManager.DENY_ACTION;
}
Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager)
.add(uri, type, permission);
}
function removePermission(permission, url)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
let uri;
if (url) {
uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url, null, null);
}
else {
uri = SpecialPowers.getDocumentURIObject(window.document);
}
Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager)
.remove(uri.host, permission);
}
function allowIndexedDB(url)
{
addPermission("indexedDB", true, url);
}
function resetIndexedDB(url)
{
removePermission("indexedDB", url);
}
function allowUnlimitedQuota(url)
{
addPermission("indexedDB-unlimited", true, url);
}
function resetUnlimitedQuota(url)
{
removePermission("indexedDB-unlimited", url);
}
function getFileHandle(fileStorageKey, name)
{
var requestService = SpecialPowers.getDOMRequestService();
var request = requestService.createRequest(window);
switch (fileStorageKey) {
case IndexedDatabaseKey:
var dbname = window.location.pathname;
mozIndexedDB.open(dbname, 1).onsuccess = function(event) {
var db = event.target.result;
db.mozCreateFileHandle(name).onsuccess = function(event) {
var fileHandle = event.target.result;
requestService.fireSuccess(request, fileHandle);
}
}
break;
case DeviceStorageKey:
var dbname = window.location.pathname;
mozIndexedDB.open(dbname, 1).onsuccess = function(event) {
var db = event.target.result;
db.mozCreateFileHandle(name).onsuccess = function(event) {
var fileHandle = event.target.result;
requestService.fireSuccess(request, fileHandle);
}
}
break;
}
return request;
}
function getBuffer(size)
{
let buffer = new ArrayBuffer(size);
is(buffer.byteLength, size, "Correct byte length");
return buffer;
}
function getRandomBuffer(size)
{
let buffer = getBuffer(size);
let view = new Uint8Array(buffer);
for (let i = 0; i < size; i++) {
view[i] = parseInt(Math.random() * 255)
}
return buffer;
}
function compareBuffers(buffer1, buffer2)
{
if (buffer1.byteLength != buffer2.byteLength) {
return false;
}
let view1 = new Uint8Array(buffer1);
let view2 = new Uint8Array(buffer2);
for (let i = 0; i < buffer1.byteLength; i++) {
if (view1[i] != view2[i]) {
return false;
}
}
return true;
}
function getBlob(type, buffer)
{
return utils.getBlob([buffer], {type: type});
}
function getRandomBlob(size)
{
return getBlob("binary/random", getRandomBuffer(size));
}
function getFileId(blob)
{
return utils.getFileId(blob);
}

View File

@ -0,0 +1,103 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const maxLocation = 18446744073709552000;
var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
for (let i = 0; i < 5; i++) {
testString += testString;
}
var testBuffer = getRandomBuffer(100000);
var testBlob = getBlob("binary/random", testBuffer);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let location = 0;
let lockedFile = fileHandle.open("readwrite");
is(lockedFile.location, location, "Correct location");
request = lockedFile.append(testString);
is(lockedFile.location, maxLocation, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = 0;
request = lockedFile.readAsText(testString.length);
location += testString.length
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultString = event.target.result;
ok(resultString == testString, "Correct string data");
request = lockedFile.append(testBuffer);
is(lockedFile.location, maxLocation, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = location;
request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
location += testBuffer.byteLength;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
request = lockedFile.append(testBlob);
is(lockedFile.location, maxLocation, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = location;
request = lockedFile.readAsArrayBuffer(testBlob.size);
location += testBlob.size;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
request = lockedFile.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let result = event.target.result;
is(result.size, location, "Correct size");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,31 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
let id = getFileId(null);
ok(id == -1, "Correct id");
id = getFileId(getRandomBlob(100));
ok(id == -1, "Correct id");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,49 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open();
continueToNextStep();
yield;
try {
lockedFile.getMetadata({ size: true });
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got exception.");
is(e.name, "LockedFileInactiveError", "Good error.");
is(e.code, 0, "Good error code.");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,61 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open();
let lockedFile2;
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
let thread = Components.classes["@mozilla.org/thread-manager;1"]
.getService()
.currentThread;
let eventHasRun;
thread.dispatch(function() {
eventHasRun = true;
lockedFile2 = fileHandle.open();
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
while (!eventHasRun) {
thread.processNextEvent(false);
}
ok(lockedFile2, "Non-null lockedFile2");
continueToNextStep();
yield;
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,54 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile1 = fileHandle.open("readwrite");
let lockedFile2 = fileHandle.open("readwrite");
let request1 = lockedFile2.write("2");
let request2 = lockedFile1.write("1");
lockedFile1.oncomplete = grabEventAndContinueHandler;
lockedFile2.oncomplete = grabEventAndContinueHandler;
yield;
yield;
let lockedFile3 = fileHandle.open("readonly");
let request3 = lockedFile3.readAsText(1);
request3.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result, "2", "Locked files were ordered properly.");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,65 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
for (let i = 0; i < 50; i++) {
let stepNumber = 0;
request = fileHandle.open("readwrite").append("string1");
request.onsuccess = function(event) {
is(stepNumber, 1, "This callback came first");
stepNumber++;
event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
}
request = fileHandle.open("readwrite").append("string2");
request.onsuccess = function(event) {
is(stepNumber, 2, "This callback came second");
stepNumber++;
event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
}
request = fileHandle.open("readwrite").append("string3");
request.onsuccess = function(event) {
is(stepNumber, 3, "This callback came third");
stepNumber++;
event.target.lockedFile.oncomplete = grabEventAndContinueHandler;
}
stepNumber++;
yield; yield; yield;;
is(stepNumber, 4, "All callbacks received");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,70 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testBuffer = getRandomBuffer(100000);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
let sum = 0;
request = lockedFile.write(testBuffer);
request.onprogress = function(event) {
let loaded = event.loaded;
let total = event.total;
ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
is(total, testBuffer.byteLength, "Correct total progress");
sum += event.loaded - sum;
}
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(sum, testBuffer.byteLength, "Correct loaded progress sum");
sum = 0;
lockedFile.location = 0;
request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
request.onprogress = function(event) {
let loaded = event.loaded;
let total = event.total;
ok(loaded >= 0 && loaded <= total, "Correct loaded progress");
is(total, testBuffer.byteLength, "Correct total progress");
sum += event.loaded - sum;
}
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(sum, testBuffer.byteLength, "Correct loaded progress sum");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,73 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
request = fileHandle.open("readwrite").write({});
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.lockedFile.mode, "readwrite", "Correct mode");
try {
fileHandle.open().write({});
ok(false, "Writing to a readonly locked file should fail!");
}
catch (e) {
ok(true, "Writing to a readonly locked file failed");
}
try {
fileHandle.open().append({});
ok(false, "Appending to a readonly locked file should fail!");
}
catch (e) {
ok(true, "Appending to a readonly locked file failed");
}
try {
fileHandle.open().truncate({});
ok(false, "Truncating a readonly locked file should fail!");
}
catch (e) {
ok(true, "Truncating a readonly locked file failed");
}
try {
fileHandle.open().flush({});
ok(false, "Flushing a readonly locked file should fail!");
}
catch (e) {
ok(true, "Flushing a readonly locked file failed");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,57 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
is(request.readyState, "pending", "Correct readyState");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
is(request.readyState, "done", "Correct readyState");
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
request = lockedFile.write("string");
is(request.readyState, "pending", "Correct readyState");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(request.readyState, "done", "Correct readyState");
lockedFile.location = 0;
request = lockedFile.readAsText(6);
request.onsuccess = grabEventAndContinueHandler;
is(request.readyState, "pending", "Correct readyState");
event = yield;
ok(event.target.result, "Got something");
is(request.readyState, "done", "Correct readyState");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,86 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testBuffer = getRandomBuffer(100000);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
request = lockedFile.write(testBuffer);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
request = fileHandle.getFile();
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let file = event.target.result;
let resultBuffer1;
let resultBuffer2;
let reader1 = new FileReader();
reader1.readAsArrayBuffer(file);
reader1.onerror = errorHandler;
reader1.onload = function(event)
{
resultBuffer1 = event.target.result;
let reader = new FileReader();
try {
reader.readAsArrayBuffer(file);
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got exception.");
is(e.name, "LockedFileInactiveError", "Good error.");
is(e.code, 0, "Good error code.");
}
}
let reader2 = new FileReader();
reader2.readAsArrayBuffer(file);
reader2.onerror = errorHandler;
reader2.onload = function(event)
{
resultBuffer2 = event.target.result;
}
lockedFile = event.target.lockedFile;
lockedFile.oncomplete = grabEventAndContinueHandler;
yield;
ok(compareBuffers(resultBuffer1, testBuffer), "Correct data");
ok(compareBuffers(resultBuffer2, testBuffer), "Correct data");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,66 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open();
lockedFile.oncomplete = unexpectedSuccessHandler;
lockedFile.onabort = grabEventAndContinueHandler;
let sawError = false;
request = lockedFile.getMetadata({ size: true });
request.onsuccess = unexpectedSuccessHandler;
request.onerror = function(event) {
is(event.target.error.name, "AbortError", "Good error");
sawError = true;
event.stopPropagation();
}
lockedFile.abort();
event = yield;
is(event.type, "abort", "Got abort event");
is(sawError, true, "Saw getMetadata() error");
// Make sure the success event isn't queued somehow.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var thread = Components.classes["@mozilla.org/thread-manager;1"]
.getService(Components.interfaces.nsIThreadManager)
.currentThread;
while (thread.hasPendingEvents()) {
thread.processNextEvent(false);
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,61 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testBuffer = getRandomBuffer(100000);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.bin");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
request = lockedFile.write(testBuffer);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(lockedFile.location, 100000, "Correct location");
for (let i = 0; i < 10; i++) {
let location = lockedFile.location - 10000
lockedFile.location = location;
request = lockedFile.truncate();
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(lockedFile.location, location, "Correct location");
request = lockedFile.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.target.result.size, location, "Correct size");
}
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,101 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>File Handle Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
var testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
for (let i = 0; i < 5; i++) {
testString += testString;
}
var testBuffer = getRandomBuffer(100000);
var testBlob = getBlob("binary/random", testBuffer);
for each (let fileStorage in fileStorages) {
let request = getFileHandle(fileStorage.key, "test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let location = 0;
let lockedFile = fileHandle.open("readwrite");
is(lockedFile.location, location, "Correct location");
request = lockedFile.write(testString);
location += testString.length;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location = 0;
request = lockedFile.readAsText(testString.length);
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultString = event.target.result;
ok(resultString == testString, "Correct string data");
request = lockedFile.write(testBuffer);
location += testBuffer.byteLength;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location -= testBuffer.byteLength;
request = lockedFile.readAsArrayBuffer(testBuffer.byteLength);
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
request = lockedFile.write(testBlob);
location += testBlob.size;
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
lockedFile.location -= testBlob.size;
request = lockedFile.readAsArrayBuffer(testBlob.size);
is(lockedFile.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
request = lockedFile.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let result = event.target.result;
is(result.size, location, "Correct size");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,321 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "FileStream.h"
#include "nsIFile.h"
#include "nsThreadUtils.h"
#include "test_quota.h"
USING_INDEXEDDB_NAMESPACE
NS_IMPL_THREADSAFE_ADDREF(FileStream)
NS_IMPL_THREADSAFE_RELEASE(FileStream)
NS_INTERFACE_MAP_BEGIN(FileStream)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardFileStream)
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
NS_INTERFACE_MAP_ENTRY(nsIStandardFileStream)
NS_INTERFACE_MAP_ENTRY(nsIFileMetadata)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
FileStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
{
// TODO: Add support for 64 bit file sizes, bug 752431
NS_ENSURE_TRUE(aOffset <= PR_INT32_MAX, NS_ERROR_INVALID_ARG);
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
int whence;
switch (aWhence) {
case nsISeekableStream::NS_SEEK_SET:
whence = SEEK_SET;
break;
case nsISeekableStream::NS_SEEK_CUR:
whence = SEEK_CUR;
break;
case nsISeekableStream::NS_SEEK_END:
whence = SEEK_END;
break;
default:
return NS_ERROR_INVALID_ARG;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fseek(mQuotaFile, aOffset, whence);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::Tell(PRInt64* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
long rc = sqlite3_quota_ftell(mQuotaFile);
NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
*aResult = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::SetEOF()
{
PRInt64 pos;
nsresult rv = Tell(&pos);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_ftruncate(mQuotaFile, pos);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::Close()
{
CleanUpOpen();
if (mQuotaFile) {
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fclose(mQuotaFile);
mQuotaFile = nsnull;
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
}
return NS_OK;
}
NS_IMETHODIMP
FileStream::Available(PRUint32* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
long rc = sqlite3_quota_file_available(mQuotaFile);
NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
*aResult = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
size_t bytesRead = sqlite3_quota_fread(aBuf, 1, aCount, mQuotaFile);
if (bytesRead < aCount && sqlite3_quota_ferror(mQuotaFile)) {
return NS_BASE_STREAM_OSERROR;
}
*aResult = bytesRead;
return NS_OK;
}
NS_IMETHODIMP
FileStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
PRUint32 aCount, PRUint32* aResult)
{
NS_NOTREACHED("Don't call me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::IsNonBlocking(bool *aNonBlocking)
{
*aNonBlocking = false;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Write(const char* aBuf, PRUint32 aCount, PRUint32 *aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
size_t bytesWritten = sqlite3_quota_fwrite(aBuf, 1, aCount, mQuotaFile);
if (bytesWritten < aCount) {
return NS_BASE_STREAM_OSERROR;
}
*aResult = bytesWritten;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Flush()
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fflush(mQuotaFile, 1);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
{
NS_NOTREACHED("Don't call me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::Init(nsIFile* aFile, const nsAString& aMode, PRInt32 aFlags)
{
NS_ASSERTION(!mQuotaFile && !mDeferredOpen, "Already initialized!");
nsresult rv = aFile->GetPath(mFilePath);
NS_ENSURE_SUCCESS(rv, rv);
mMode = aMode;
mFlags = aFlags;
if (mFlags & nsIStandardFileStream::FLAGS_DEFER_OPEN) {
mDeferredOpen = true;
return NS_OK;
}
return DoOpen();
}
NS_IMETHODIMP
FileStream::GetSize(PRInt64* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
// TODO: Use sqlite3_quota_file_size() here, bug 760783
PRInt64 rc = sqlite3_quota_file_truesize(mQuotaFile);
NS_ASSERTION(rc >= 0, "The file is not under quota management!");
*_retval = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::GetLastModified(PRInt64* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
time_t mtime;
int rc = sqlite3_quota_file_mtime(mQuotaFile, &mtime);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
*_retval = mtime * PR_MSEC_PER_SEC;
return NS_OK;
}
NS_IMETHODIMP
FileStream::FlushBuffers()
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fflush(mQuotaFile, 0);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
nsresult
FileStream::DoOpen()
{
NS_ASSERTION(!mFilePath.IsEmpty(), "Must have a file path");
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
quota_FILE* quotaFile =
sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(mFilePath).get(),
NS_ConvertUTF16toUTF8(mMode).get());
CleanUpOpen();
if (!quotaFile) {
return NS_BASE_STREAM_OSERROR;
}
mQuotaFile = quotaFile;
return NS_OK;
}

140
dom/indexedDB/FileStream.h Normal file
View File

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_indexeddb_filestream_h__
#define mozilla_dom_indexeddb_filestream_h__
#include "IndexedDatabase.h"
#include "nsIFileStreams.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsISeekableStream.h"
#include "nsIStandardFileStream.h"
class nsIFile;
struct quota_FILE;
BEGIN_INDEXEDDB_NAMESPACE
class FileStream : public nsISeekableStream,
public nsIInputStream,
public nsIOutputStream,
public nsIStandardFileStream,
public nsIFileMetadata
{
public:
FileStream()
: mFlags(0),
mDeferredOpen(false),
mQuotaFile(nsnull)
{ }
virtual ~FileStream()
{
Close();
}
NS_DECL_ISUPPORTS
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSISTANDARDFILESTREAM
NS_DECL_NSIFILEMETADATA
// nsIInputStream
NS_IMETHOD
Close();
NS_IMETHOD
Available(PRUint32* _retval);
NS_IMETHOD
Read(char* aBuf, PRUint32 aCount, PRUint32* _retval);
NS_IMETHOD
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, PRUint32 aCount,
PRUint32* _retval);
NS_IMETHOD
IsNonBlocking(bool* _retval);
// nsIOutputStream
// Close() already declared
NS_IMETHOD
Flush();
NS_IMETHOD
Write(const char* aBuf, PRUint32 aCount, PRUint32* _retval);
NS_IMETHOD
WriteFrom(nsIInputStream* aFromStream, PRUint32 aCount, PRUint32* _retval);
NS_IMETHOD
WriteSegments(nsReadSegmentFun aReader, void* aClosure, PRUint32 aCount,
PRUint32* _retval);
// IsNonBlocking() already declared
protected:
/**
* Cleans up data prepared in Init.
*/
void
CleanUpOpen()
{
mFilePath.Truncate();
mDeferredOpen = false;
}
/**
* Open the file. This is called either from Init
* or from DoPendingOpen (if FLAGS_DEFER_OPEN is used when initializing this
* stream). The default behavior of DoOpen is to open the file and save the
* file descriptor.
*/
virtual nsresult
DoOpen();
/**
* If there is a pending open, do it now. It's important for this to be
* inlined since we do it in almost every stream API call.
*/
nsresult
DoPendingOpen()
{
if (!mDeferredOpen) {
return NS_OK;
}
return DoOpen();
}
/**
* Data we need to do an open.
*/
nsString mFilePath;
nsString mMode;
/**
* Flags describing our behavior. See the IDL file for possible values.
*/
PRInt32 mFlags;
/**
* Whether we have a pending open (see FLAGS_DEFER_OPEN in the IDL file).
*/
bool mDeferredOpen;
/**
* File descriptor for opened file.
*/
quota_FILE* mQuotaFile;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_filestream_h__

View File

@ -1001,7 +1001,7 @@ ContinueObjectStoreHelper::GatherResultsFromStatement(
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
mDatabase->Manager(), mCloneReadInfo);
mDatabase, mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -1071,7 +1071,7 @@ ContinueIndexObjectHelper::GatherResultsFromStatement(
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3,
mDatabase->Manager(), mCloneReadInfo);
mDatabase, mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;

View File

@ -21,6 +21,7 @@
#include "CheckQuotaHelper.h"
#include "DatabaseInfo.h"
#include "IDBEvents.h"
#include "IDBFileHandle.h"
#include "IDBIndex.h"
#include "IDBObjectStore.h"
#include "IDBTransaction.h"
@ -90,6 +91,53 @@ private:
PRInt64 mObjectStoreId;
};
class CreateFileHelper : public AsyncConnectionHelper
{
public:
CreateFileHelper(IDBDatabase* aDatabase,
IDBRequest* aRequest,
const nsAString& aName,
const nsAString& aType)
: AsyncConnectionHelper(aDatabase, aRequest),
mName(aName), mType(aType)
{ }
~CreateFileHelper()
{ }
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
nsresult GetSuccessResult(JSContext* aCx,
jsval* aVal);
void ReleaseMainThreadObjects()
{
mFileInfo = nsnull;
AsyncConnectionHelper::ReleaseMainThreadObjects();
}
virtual ChildProcessSendResult MaybeSendResponseToChildProcess(
nsresult aResultCode)
MOZ_OVERRIDE
{
return Success_NotSent;
}
virtual nsresult UnpackResponseFromParentProcess(
const ResponseValue& aResponseValue)
MOZ_OVERRIDE
{
MOZ_NOT_REACHED("Should never get here!");
return NS_ERROR_UNEXPECTED;
}
private:
// In-params.
nsString mName;
nsString mType;
// Out-params.
nsRefPtr<FileInfo> mFileInfo;
};
NS_STACK_CLASS
class AutoRemoveObjectStore
{
@ -349,6 +397,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase)
NS_INTERFACE_MAP_ENTRY(nsIFileStorage)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase)
NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
@ -692,6 +741,36 @@ IDBDatabase::Transaction(const jsval& aStoreNames,
return NS_OK;
}
NS_IMETHODIMP
IDBDatabase::MozCreateFileHandle(const nsAString& aName,
const nsAString& aType,
nsIIDBRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (IndexedDatabaseManager::IsShuttingDown()) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (mClosed) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
nsRefPtr<IDBRequest> request = IDBRequest::Create(nsnull, this, nsnull);
nsRefPtr<CreateFileHelper> helper =
new CreateFileHelper(this, request, aName, aType);
IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
NS_ASSERTION(manager, "We should definitely have a manager here");
nsresult rv = helper->Dispatch(manager->IOThread());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
IDBDatabase::Close()
{
@ -703,6 +782,37 @@ IDBDatabase::Close()
return NS_OK;
}
nsISupports*
IDBDatabase::StorageId()
{
return Id();
}
bool
IDBDatabase::IsStorageInvalidated()
{
return IsInvalidated();
}
bool
IDBDatabase::IsStorageShuttingDown()
{
return IndexedDatabaseManager::IsShuttingDown();
}
void
IDBDatabase::SetThreadLocals()
{
NS_ASSERTION(GetOwner(), "Should have owner!");
IndexedDatabaseManager::SetCurrentWindow(GetOwner());
}
void
IDBDatabase::UnsetThreadLocals()
{
IndexedDatabaseManager::SetCurrentWindow(nsnull);
}
nsresult
IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
{
@ -846,3 +956,35 @@ DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
return NS_OK;
}
nsresult
CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
FileManager* fileManager = mDatabase->Manager();
mFileInfo = fileManager->GetNewFileInfo();
NS_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
NS_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory, mFileInfo->Id());
NS_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;
}
nsresult
CreateFileHelper::GetSuccessResult(JSContext* aCx,
jsval* aVal)
{
nsRefPtr<IDBFileHandle> fileHandle =
IDBFileHandle::Create(mDatabase, mName, mType, mFileInfo.forget());
NS_ENSURE_TRUE(fileHandle, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMFileHandle*, fileHandle),
aVal);
}

View File

@ -10,6 +10,7 @@
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
#include "nsIDocument.h"
#include "nsIFileStorage.h"
#include "nsIIDBDatabase.h"
#include "nsDOMEventTargetHelper.h"
#include "mozilla/dom/indexedDB/IDBWrapperCache.h"
@ -31,7 +32,8 @@ class IndexedDBDatabaseParent;
struct ObjectStoreInfoGuts;
class IDBDatabase : public IDBWrapperCache,
public nsIIDBDatabase
public nsIIDBDatabase,
public nsIFileStorage
{
friend class AsyncConnectionHelper;
friend class IndexedDatabaseManager;
@ -39,6 +41,7 @@ class IDBDatabase : public IDBWrapperCache,
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIIDBDATABASE
NS_DECL_NSIFILESTORAGE
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache)

View File

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "IDBFileHandle.h"
#include "nsIStandardFileStream.h"
#include "nsDOMClassInfoID.h"
#include "FileStream.h"
#include "mozilla/dom/file/File.h"
USING_INDEXEDDB_NAMESPACE
namespace {
inline
already_AddRefed<nsIFile>
GetFileFor(FileInfo* aFileInfo)
{
FileManager* fileManager = aFileInfo->Manager();
nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
NS_ENSURE_TRUE(directory, nsnull);
nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory,
aFileInfo->Id());
NS_ENSURE_TRUE(file, nsnull);
return file.forget();
}
} // anonymous namespace
// static
already_AddRefed<IDBFileHandle>
IDBFileHandle::Create(IDBDatabase* aDatabase,
const nsAString& aName,
const nsAString& aType,
already_AddRefed<FileInfo> aFileInfo)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<FileInfo> fileInfo(aFileInfo);
NS_ASSERTION(fileInfo, "Null pointer!");
nsRefPtr<IDBFileHandle> newFile = new IDBFileHandle();
newFile->BindToOwner(aDatabase);
newFile->mFileStorage = aDatabase;
newFile->mName = aName;
newFile->mType = aType;
newFile->mFile = GetFileFor(fileInfo);
NS_ENSURE_TRUE(newFile->mFile, nsnull);
newFile->mFileName.AppendInt(fileInfo->Id());
fileInfo.swap(newFile->mFileInfo);
return newFile.forget();
}
already_AddRefed<nsISupports>
IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
{
nsRefPtr<FileStream> stream = new FileStream();
nsString streamMode;
if (aReadOnly) {
streamMode.AssignLiteral("rb");
}
else {
streamMode.AssignLiteral("r+b");
}
nsresult rv = stream->Init(aFile, streamMode,
nsIStandardFileStream::FLAGS_DEFER_OPEN);
NS_ENSURE_SUCCESS(rv, nsnull);
nsCOMPtr<nsISupports> result =
NS_ISUPPORTS_CAST(nsIStandardFileStream*, stream);
return result.forget();
}
already_AddRefed<nsIDOMFile>
IDBFileHandle::CreateFileObject(mozilla::dom::file::LockedFile* aLockedFile,
PRUint32 aFileSize)
{
nsCOMPtr<nsIDOMFile> file = new mozilla::dom::file::File(
mName, mType, aFileSize, mFile, aLockedFile, mFileInfo);
return file.forget();
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileHandle)
NS_INTERFACE_MAP_ENTRY(nsIIDBFileHandle)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBFileHandle)
NS_INTERFACE_MAP_END_INHERITING(FileHandle)
NS_IMPL_ADDREF_INHERITED(IDBFileHandle, FileHandle)
NS_IMPL_RELEASE_INHERITED(IDBFileHandle, FileHandle)
DOMCI_DATA(IDBFileHandle, IDBFileHandle)
NS_IMETHODIMP
IDBFileHandle::GetDatabase(nsIIDBDatabase** aDatabase)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIIDBDatabase> database = do_QueryInterface(mFileStorage);
NS_ASSERTION(database, "This should always succeed!");
database.forget(aDatabase);
return NS_OK;
}

View File

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_indexeddb_idbfilehandle_h__
#define mozilla_dom_indexeddb_idbfilehandle_h__
#include "IndexedDatabase.h"
#include "nsIIDBFileHandle.h"
#include "mozilla/dom/file/FileHandle.h"
#include "mozilla/dom/indexedDB/FileInfo.h"
BEGIN_INDEXEDDB_NAMESPACE
class IDBFileHandle : public mozilla::dom::file::FileHandle,
public nsIIDBFileHandle
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIIDBFILEHANDLE
NS_IMETHOD_(PRInt64)
GetFileId()
{
return mFileInfo->Id();
}
NS_IMETHOD_(FileInfo*)
GetFileInfo()
{
return mFileInfo;
}
static already_AddRefed<IDBFileHandle>
Create(IDBDatabase* aDatabase, const nsAString& aName,
const nsAString& aType, already_AddRefed<FileInfo> aFileInfo);
virtual already_AddRefed<nsISupports>
CreateStream(nsIFile* aFile, bool aReadOnly);
virtual already_AddRefed<nsIDOMFile>
CreateFileObject(mozilla::dom::file::LockedFile* aLockedFile,
PRUint32 aFileSize);
private:
IDBFileHandle()
{ }
~IDBFileHandle()
{ }
nsRefPtr<FileInfo> mFileInfo;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_idbfilehandle_h__

View File

@ -1172,7 +1172,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
if (hasResult) {
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
mDatabase->Manager(), mCloneReadInfo);
mDatabase, mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1488,7 +1488,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
NS_ASSERTION(readInfo, "This shouldn't fail!");
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
mDatabase->Manager(), *readInfo);
mDatabase, *readInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -2023,7 +2023,7 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3,
mDatabase->Manager(), mCloneReadInfo);
mDatabase, mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// Now we need to make the query to get the next match.

View File

@ -9,6 +9,7 @@
#include "IDBObjectStore.h"
#include "nsIJSContextStack.h"
#include "nsIOutputStream.h"
#include "jsfriendapi.h"
#include "mozilla/dom/StructuredCloneTags.h"
@ -27,8 +28,10 @@
#include "xpcpublic.h"
#include "AsyncConnectionHelper.h"
#include "FileStream.h"
#include "IDBCursor.h"
#include "IDBEvents.h"
#include "IDBFileHandle.h"
#include "IDBIndex.h"
#include "IDBKeyRange.h"
#include "IDBTransaction.h"
@ -910,7 +913,7 @@ IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
mozIStorageStatement* aStatement,
PRUint32 aDataIndex,
PRUint32 aFileIdsIndex,
FileManager* aFileManager,
IDBDatabase* aDatabase,
StructuredCloneReadInfo& aInfo)
{
#ifdef DEBUG
@ -956,26 +959,28 @@ IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
rv = aStatement->GetIsNull(aFileIdsIndex, &isNull);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (isNull) {
return NS_OK;
if (!isNull) {
nsString ids;
rv = aStatement->GetString(aFileIdsIndex, ids);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsAutoTArray<PRInt64, 10> array;
rv = ConvertFileIdsToArray(ids, array);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
FileManager* fileManager = aDatabase->Manager();
for (PRUint32 i = 0; i < array.Length(); i++) {
const PRInt64& id = array[i];
nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(id);
NS_ASSERTION(fileInfo, "Null file info!");
aInfo.mFileInfos.AppendElement(fileInfo);
}
}
nsString ids;
rv = aStatement->GetString(aFileIdsIndex, ids);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsAutoTArray<PRInt64, 10> array;
rv = ConvertFileIdsToArray(ids, array);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
for (PRUint32 i = 0; i < array.Length(); i++) {
const PRInt64& id = array.ElementAt(i);
nsRefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(id);
NS_ASSERTION(fileInfo, "Null file info!");
aInfo.mFileInfos.AppendElement(fileInfo);
}
aInfo.mDatabase = aDatabase;
return NS_OK;
}
@ -1102,7 +1107,8 @@ IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
uint32_t aData,
void* aClosure)
{
if (aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE) {
if (aTag == SCTAG_DOM_FILEHANDLE || aTag == SCTAG_DOM_BLOB ||
aTag == SCTAG_DOM_FILE) {
StructuredCloneReadInfo* cloneReadInfo =
reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
@ -1112,15 +1118,50 @@ IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
}
nsRefPtr<FileInfo> fileInfo = cloneReadInfo->mFileInfos[aData];
nsRefPtr<FileManager> fileManager = fileInfo->Manager();
IDBDatabase* database = cloneReadInfo->mDatabase;
if (aTag == SCTAG_DOM_FILEHANDLE) {
nsCString type;
if (!StructuredCloneReadString(aReader, type)) {
return nsnull;
}
NS_ConvertUTF8toUTF16 convType(type);
nsCString name;
if (!StructuredCloneReadString(aReader, name)) {
return nsnull;
}
NS_ConvertUTF8toUTF16 convName(name);
nsRefPtr<IDBFileHandle> fileHandle = IDBFileHandle::Create(database,
convName, convType, fileInfo.forget());
jsval wrappedFileHandle;
nsresult rv =
nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx),
static_cast<nsIDOMFileHandle*>(fileHandle),
&NS_GET_IID(nsIDOMFileHandle),
&wrappedFileHandle);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to wrap native!");
return nsnull;
}
return JSVAL_TO_OBJECT(wrappedFileHandle);
}
FileManager* fileManager = database->Manager();
nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
if (!directory) {
NS_WARNING("Failed to get directory!");
return nsnull;
}
nsCOMPtr<nsIFile> nativeFile =
fileManager->GetFileForId(directory, fileInfo->Id());
if (!nativeFile) {
NS_WARNING("Failed to get file!");
return nsnull;
}
@ -1209,25 +1250,55 @@ IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
if (wrappedNative) {
nsISupports* supports = wrappedNative->Native();
IDBTransaction* transaction = cloneWriteInfo->mTransaction;
FileManager* fileManager = transaction->Database()->Manager();
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
if (blob) {
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
// Check if it is a blob created from this db or the blob was already
// stored in this db
nsRefPtr<FileInfo> fileInfo = transaction->GetFileInfo(blob);
nsCOMPtr<nsIInputStream> inputStream;
if (!fileInfo) {
fileInfo = blob->GetFileInfo(fileManager);
}
if (!fileInfo) {
fileInfo = fileManager->GetNewFileInfo();
if (!fileInfo) {
NS_WARNING("Failed to get new file info!");
return false;
}
if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) {
NS_WARNING("Failed to get internal steam!");
return false;
}
transaction->AddFileInfo(blob, fileInfo);
}
PRUint64 size;
if (NS_FAILED(blob->GetSize(&size))) {
NS_WARNING("Failed to get size!");
return false;
}
size = SwapBytes(size);
nsString type;
if (NS_FAILED(blob->GetType(type))) {
NS_WARNING("Failed to get type!");
return false;
}
NS_ConvertUTF16toUTF8 convType(type);
PRUint32 convTypeLength = SwapBytes(convType.Length());
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
cloneWriteInfo->mBlobs.Length()) ||
cloneWriteInfo->mFiles.Length()) ||
!JS_WriteBytes(aWriter, &size, sizeof(PRUint64)) ||
!JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
!JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
@ -1237,6 +1308,7 @@ IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
if (file) {
nsString name;
if (NS_FAILED(file->GetName(name))) {
NS_WARNING("Failed to get name!");
return false;
}
NS_ConvertUTF16toUTF8 convName(name);
@ -1248,7 +1320,51 @@ IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
}
}
cloneWriteInfo->mBlobs.AppendElement(blob);
StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement();
cloneFile->mFile = blob.forget();
cloneFile->mFileInfo = fileInfo.forget();
cloneFile->mInputStream = inputStream.forget();
return true;
}
nsCOMPtr<nsIDOMFileHandle> fileHandle = do_QueryInterface(supports);
if (fileHandle) {
nsRefPtr<FileInfo> fileInfo = fileHandle->GetFileInfo();
// Throw when trying to store non IDB file handles or IDB file handles
// across databases.
if (!fileInfo || fileInfo->Manager() != fileManager) {
return false;
}
nsString type;
if (NS_FAILED(fileHandle->GetType(type))) {
NS_WARNING("Failed to get type!");
return false;
}
NS_ConvertUTF16toUTF8 convType(type);
PRUint32 convTypeLength = SwapBytes(convType.Length());
nsString name;
if (NS_FAILED(fileHandle->GetName(name))) {
NS_WARNING("Failed to get name!");
return false;
}
NS_ConvertUTF16toUTF8 convName(name);
PRUint32 convNameLength = SwapBytes(convName.Length());
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILEHANDLE,
cloneWriteInfo->mFiles.Length()) ||
!JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
!JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
!JS_WriteBytes(aWriter, &convNameLength, sizeof(PRUint32)) ||
!JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
return false;
}
StructuredCloneFile* file = cloneWriteInfo->mFiles.AppendElement();
file->mFileInfo = fileInfo.forget();
return true;
}
@ -1465,6 +1581,7 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
}
aCloneWriteInfo.mOffsetToKeyProp = 0;
aCloneWriteInfo.mTransaction = mTransaction;
// We guard on rv being a success because we need to run the property
// deletion code below even if we should not be serializing the value
@ -2379,26 +2496,30 @@ IDBObjectStore::Count(const jsval& aKey,
}
inline nsresult
CopyData(nsIInputStream* aStream, quota_FILE* aFile)
CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
{
nsresult rv;
do {
char copyBuffer[FILE_COPY_BUFFER_SIZE];
PRUint32 numRead;
nsresult rv = aStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
rv = aInputStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
NS_ENSURE_SUCCESS(rv, rv);
if (numRead <= 0) {
break;
}
size_t numWrite = sqlite3_quota_fwrite(copyBuffer, 1, numRead, aFile);
PRUint32 numWrite;
rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
} while (true);
// Flush and sync
NS_ENSURE_TRUE(sqlite3_quota_fflush(aFile, 1) == 0,
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = aOutputStream->Flush();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -2570,50 +2691,28 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
nsAutoString fileIds;
for (PRUint32 index = 0; index < mCloneWriteInfo.mBlobs.Length(); index++) {
nsCOMPtr<nsIDOMBlob>& domBlob = mCloneWriteInfo.mBlobs[index];
PRUint32 length = mCloneWriteInfo.mFiles.Length();
for (PRUint32 index = 0; index < length; index++) {
StructuredCloneFile& cloneFile = mCloneWriteInfo.mFiles[index];
PRInt64 id = -1;
// Check if it is a blob created from this db or the blob was already
// stored in this db
nsRefPtr<FileInfo> fileInfo = domBlob->GetFileInfo(fileManager);
if (fileInfo) {
id = fileInfo->Id();
}
if (id == -1) {
fileInfo = fileManager->GetNewFileInfo();
NS_ENSURE_TRUE(fileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
id = fileInfo->Id();
mTransaction->OnNewFileInfo(fileInfo);
FileInfo* fileInfo = cloneFile.mFileInfo;
nsIInputStream* inputStream = cloneFile.mInputStream;
PRInt64 id = fileInfo->Id();
if (inputStream) {
// Copy it
nsCOMPtr<nsIInputStream> inputStream;
rv = domBlob->GetInternalStream(getter_AddRefs(inputStream));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<nsIFile> nativeFile = fileManager->GetFileForId(directory, id);
nsCOMPtr<nsIFile> nativeFile =
fileManager->GetFileForId(directory, id);
NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsString nativeFilePath;
rv = nativeFile->GetPath(nativeFilePath);
nsRefPtr<FileStream> outputStream = new FileStream();
rv = outputStream->Init(nativeFile, NS_LITERAL_STRING("wb"), 0);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
quota_FILE* file =
sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(nativeFilePath).get(), "wb");
NS_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = CopyData(inputStream, file);
NS_ENSURE_TRUE(sqlite3_quota_fclose(file) == 0,
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = CopyData(inputStream, outputStream);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
domBlob->AddFileInfo(fileInfo);
cloneFile.mFile->AddFileInfo(fileInfo);
}
if (index) {
@ -2777,7 +2876,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
if (hasResult) {
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
mDatabase->Manager(), mCloneReadInfo);
mDatabase, mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -3087,7 +3186,7 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
mDatabase->Manager(), mCloneReadInfo);
mDatabase, mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// Now we need to make the query to get the next match.
@ -3461,7 +3560,7 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
do {
StructuredCloneReadInfo cloneReadInfo;
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
mDatabase->Manager(), cloneReadInfo);
mDatabase, cloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer;
@ -3612,7 +3711,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_ASSERTION(readInfo, "Shouldn't fail if SetCapacity succeeded!");
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
mDatabase->Manager(), *readInfo);
mDatabase, *readInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

View File

@ -73,7 +73,7 @@ public:
GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
PRUint32 aDataIndex,
PRUint32 aFileIdsIndex,
FileManager* aFileManager,
IDBDatabase* aDatabase,
StructuredCloneReadInfo& aInfo);
static void

View File

@ -113,6 +113,8 @@ IDBTransaction::CreateInternal(IDBDatabase* aDatabase,
IndexedDBTransactionChild* actor = nsnull;
transaction->mCreatedFileInfos.Init();
if (IndexedDatabaseManager::IsMainProcess()) {
transaction->mCachedStatements.Init();
@ -470,10 +472,18 @@ IDBTransaction::GetOrCreateObjectStore(const nsAString& aName,
return retval.forget();
}
void
IDBTransaction::OnNewFileInfo(FileInfo* aFileInfo)
already_AddRefed<FileInfo>
IDBTransaction::GetFileInfo(nsIDOMBlob* aBlob)
{
mCreatedFileInfos.AppendElement(aFileInfo);
nsRefPtr<FileInfo> fileInfo;
mCreatedFileInfos.Get(aBlob, getter_AddRefs(fileInfo));
return fileInfo.forget();
}
void
IDBTransaction::AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo)
{
mCreatedFileInfos.Put(aBlob, aFileInfo);
}
void

View File

@ -152,7 +152,8 @@ public:
ObjectStoreInfo* aObjectStoreInfo,
bool aCreating);
void OnNewFileInfo(FileInfo* aFileInfo);
already_AddRefed<FileInfo> GetFileInfo(nsIDOMBlob* aBlob);
void AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo);
void ClearCreatedFileInfos();
@ -230,7 +231,7 @@ private:
nsTArray<nsRefPtr<IDBObjectStore> > mCreatedObjectStores;
nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
nsTArray<nsRefPtr<FileInfo> > mCreatedFileInfos;
nsRefPtrHashtable<nsISupportsHashKey, FileInfo> mCreatedFileInfos;
IndexedDBTransactionChild* mActorChild;
IndexedDBTransactionParent* mActorParent;

View File

@ -28,19 +28,34 @@
using namespace mozilla::dom::indexedDB;
class nsIDOMBlob;
class nsIInputStream;
BEGIN_INDEXEDDB_NAMESPACE
class FileInfo;
class IDBDatabase;
class IDBTransaction;
template <class T>
void SwapData(T& aData1, T& aData2)
{
T temp = aData2;
aData2 = aData1;
aData1 = temp;
}
struct SerializedStructuredCloneReadInfo;
struct StructuredCloneReadInfo
{
// In IndexedDatabaseInlines.h
inline StructuredCloneReadInfo();
void Swap(StructuredCloneReadInfo& aCloneReadInfo)
{
mCloneBuffer.swap(aCloneReadInfo.mCloneBuffer);
mFileInfos.SwapElements(aCloneReadInfo.mFileInfos);
SwapData(mDatabase, aCloneReadInfo.mDatabase);
}
// In IndexedDatabaseInlines.h
@ -49,6 +64,7 @@ struct StructuredCloneReadInfo
JSAutoStructuredCloneBuffer mCloneBuffer;
nsTArray<nsRefPtr<FileInfo> > mFileInfos;
IDBDatabase* mDatabase;
};
struct SerializedStructuredCloneReadInfo
@ -77,22 +93,41 @@ struct SerializedStructuredCloneReadInfo
size_t dataLength;
};
struct StructuredCloneFile
{
bool operator==(const StructuredCloneFile& aOther) const
{
return this->mFile == aOther.mFile &&
this->mFileInfo == aOther.mFileInfo &&
this->mInputStream == aOther.mInputStream;
}
nsCOMPtr<nsIDOMBlob> mFile;
nsRefPtr<FileInfo> mFileInfo;
nsCOMPtr<nsIInputStream> mInputStream;
};
struct SerializedStructuredCloneWriteInfo;
struct StructuredCloneWriteInfo
{
// In IndexedDatabaseInlines.h
inline StructuredCloneWriteInfo();
void Swap(StructuredCloneWriteInfo& aCloneWriteInfo)
{
mCloneBuffer.swap(aCloneWriteInfo.mCloneBuffer);
mBlobs.SwapElements(aCloneWriteInfo.mBlobs);
mOffsetToKeyProp = aCloneWriteInfo.mOffsetToKeyProp;
mFiles.SwapElements(aCloneWriteInfo.mFiles);
SwapData(mTransaction, aCloneWriteInfo.mTransaction);
SwapData(mOffsetToKeyProp, aCloneWriteInfo.mOffsetToKeyProp);
}
bool operator==(const StructuredCloneWriteInfo& aOther) const
{
return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() &&
this->mCloneBuffer.data() == aOther.mCloneBuffer.data() &&
this->mBlobs == aOther.mBlobs &&
this->mFiles == aOther.mFiles &&
this->mTransaction == aOther.mTransaction &&
this->mOffsetToKeyProp == aOther.mOffsetToKeyProp;
}
@ -101,7 +136,8 @@ struct StructuredCloneWriteInfo
SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther);
JSAutoStructuredCloneBuffer mCloneBuffer;
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
nsTArray<StructuredCloneFile> mFiles;
IDBTransaction* mTransaction;
PRUint64 mOffsetToKeyProp;
};

View File

@ -10,6 +10,13 @@
BEGIN_INDEXEDDB_NAMESPACE
inline
StructuredCloneWriteInfo::StructuredCloneWriteInfo()
: mTransaction(nsnull),
mOffsetToKeyProp(0)
{
}
inline
bool
StructuredCloneWriteInfo::SetFromSerialized(
@ -22,11 +29,17 @@ StructuredCloneWriteInfo::SetFromSerialized(
return false;
}
mBlobs.Clear();
mFiles.Clear();
mOffsetToKeyProp = aOther.offsetToKeyProp;
return true;
}
inline
StructuredCloneReadInfo::StructuredCloneReadInfo()
: mDatabase(nsnull)
{
}
inline
bool
StructuredCloneReadInfo::SetFromSerialized(

View File

@ -9,6 +9,7 @@
#include "nsIDOMScriptObjectFactory.h"
#include "nsIFile.h"
#include "nsIFileStorage.h"
#include "nsILocalFile.h"
#include "nsIObserverService.h"
#include "nsIScriptObjectPrincipal.h"
@ -17,6 +18,7 @@
#include "nsISimpleEnumerator.h"
#include "nsITimer.h"
#include "mozilla/dom/file/FileService.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
@ -56,6 +58,7 @@
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
using mozilla::Preferences;
using mozilla::dom::file::FileService;
static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@ -113,6 +116,7 @@ public:
NS_IMPL_THREADSAFE_ISUPPORTS1(QuotaCallback, mozIStorageQuotaCallback)
// Adds all databases in the hash to the given array.
template <class T>
PLDHashOperator
EnumerateToTArray(const nsACString& aKey,
nsTArray<IDBDatabase*>* aValue,
@ -123,8 +127,8 @@ EnumerateToTArray(const nsACString& aKey,
NS_ASSERTION(aValue, "Null pointer!");
NS_ASSERTION(aUserArg, "Null pointer!");
nsTArray<IDBDatabase*>* array =
static_cast<nsTArray<IDBDatabase*>*>(aUserArg);
nsTArray<T>* array =
static_cast<nsTArray<T>*>(aUserArg);
if (!array->AppendElements(*aValue)) {
NS_WARNING("Out of memory!");
@ -518,8 +522,10 @@ IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
NS_ASSERTION(aWindow, "Null pointer!");
nsAutoTArray<IDBDatabase*, 50> liveDatabases;
mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
&liveDatabases);
FileService* service = FileService::Get();
TransactionThreadPool* pool = TransactionThreadPool::Get();
for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
@ -529,6 +535,10 @@ IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
NS_WARNING("Failed to close database for dying window!");
}
if (service) {
service->AbortLockedFilesForStorage(database);
}
if (pool) {
pool->AbortTransactionsForDatabase(database);
}
@ -543,17 +553,20 @@ IndexedDatabaseManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
NS_ASSERTION(aWindow, "Null pointer!");
nsAutoTArray<IDBDatabase*, 50> liveDatabases;
mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
&liveDatabases);
FileService* service = FileService::Get();
TransactionThreadPool* pool = TransactionThreadPool::Get();
if (!pool) {
if (!service && !pool) {
return false;
}
for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
IDBDatabase*& database = liveDatabases[index];
if (database->GetOwner() == aWindow &&
pool->HasTransactionsForDatabase(database)) {
((service && service->HasLockedFilesForStorage(database)) ||
(pool && pool->HasTransactionsForDatabase(database)))) {
return true;
}
}
@ -587,21 +600,38 @@ IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase)
// transactions that have not completed. We need to wait for those
// before we dispatch the helper.
TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
if (!pool) {
NS_ERROR("IndexedDB is totally broken.");
return;
FileService* service = FileService::Get();
TransactionThreadPool* pool = TransactionThreadPool::Get();
PRUint32 count = !!service + !!pool;
nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
new WaitForTransactionsToFinishRunnable(op,
NS_MAX<PRUint32>(count, 1));
if (!count) {
runnable->Run();
}
else {
// Use the WaitForTransactionsToxFinishRunnable as the callback.
nsRefPtr<WaitForTransactionsToFinishRunnable> waitRunnable =
new WaitForTransactionsToFinishRunnable(op);
if (service) {
nsTArray<nsCOMPtr<nsIFileStorage> > array;
array.AppendElement(aDatabase);
nsAutoTArray<nsRefPtr<IDBDatabase>, 1> array;
array.AppendElement(aDatabase);
if (!service->WaitForAllStoragesToComplete(array, runnable)) {
NS_WARNING("Failed to wait for storages to complete!");
}
}
// Use the WaitForTransactionsToFinishRunnable as the callback.
if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) {
NS_WARNING("Failed to wait for transaction to complete!");
if (pool) {
nsTArray<nsRefPtr<IDBDatabase> > array;
array.AppendElement(aDatabase);
if (!pool->WaitForAllDatabasesToComplete(array, runnable)) {
NS_WARNING("Failed to wait for databases to complete!");
}
}
}
}
break;
@ -1249,6 +1279,39 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
}
if (sIsMainProcess) {
FileService* service = FileService::Get();
if (service) {
// This should only wait for IDB databases (file storages) to complete.
// Other file storages may still have running locked files.
// If the necko service (thread pool) gets the shutdown notification
// first then the sync loop won't be processed at all, otherwise it will
// lock the main thread until all IDB file storages are finished.
nsTArray<nsCOMPtr<nsIFileStorage> >
liveDatabases(mLiveDatabases.Count());
mLiveDatabases.EnumerateRead(
EnumerateToTArray<nsCOMPtr<nsIFileStorage> >,
&liveDatabases);
if (!liveDatabases.IsEmpty()) {
nsRefPtr<WaitForLockedFilesToFinishRunnable> runnable =
new WaitForLockedFilesToFinishRunnable();
if (!service->WaitForAllStoragesToComplete(liveDatabases,
runnable)) {
NS_WARNING("Failed to wait for databases to complete!");
}
nsIThread* thread = NS_GetCurrentThread();
while (runnable->IsBusy()) {
if (!NS_ProcessNextEvent(thread)) {
NS_ERROR("Failed to process next event!");
break;
}
}
}
}
// Make sure to join with our IO thread.
if (NS_FAILED(mIOThread->Shutdown())) {
NS_WARNING("Failed to shutdown IO thread!");
@ -1287,7 +1350,8 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
// Grab all live databases, for all origins.
nsAutoTArray<IDBDatabase*, 50> liveDatabases;
mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
&liveDatabases);
// Invalidate them all.
if (!liveDatabases.IsEmpty()) {
@ -1493,7 +1557,7 @@ IndexedDatabaseManager::AsyncUsageRunnable::GetUsageForDirectory(
rv = file->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(fileSize > 0, "Negative size?!");
NS_ASSERTION(fileSize >= 0, "Negative size?!");
IncrementUsage(aUsage, PRUint64(fileSize));
}
@ -1532,6 +1596,11 @@ IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(mOp && mOp->mHelper, "What?");
NS_ASSERTION(mCountdown, "Wrong countdown!");
if (--mCountdown) {
return NS_OK;
}
// Don't hold the callback alive longer than necessary.
nsRefPtr<AsyncConnectionHelper> helper;
@ -1547,6 +1616,18 @@ IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run()
return NS_OK;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable,
nsIRunnable)
NS_IMETHODIMP
IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mBusy = false;
return NS_OK;
}
IndexedDatabaseManager::SynchronizedOp::SynchronizedOp(const nsACString& aOrigin,
nsIAtom* aId)

View File

@ -310,12 +310,14 @@ private:
class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsIRunnable
{
public:
WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp)
: mOp(aOp)
WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp,
PRUint32 aCountdown)
: mOp(aOp), mCountdown(aCountdown)
{
NS_ASSERTION(mOp, "Why don't we have a runnable?");
NS_ASSERTION(mOp->mDatabases.IsEmpty(), "We're here too early!");
NS_ASSERTION(mOp->mHelper, "What are we supposed to do when we're done?");
NS_ASSERTION(mCountdown, "Wrong countdown!");
}
NS_DECL_ISUPPORTS
@ -324,6 +326,26 @@ private:
private:
// The IndexedDatabaseManager holds this alive.
SynchronizedOp* mOp;
PRUint32 mCountdown;
};
class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsIRunnable
{
public:
WaitForLockedFilesToFinishRunnable()
: mBusy(true)
{ }
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
bool IsBusy() const
{
return mBusy;
}
private:
bool mBusy;
};
class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable

View File

@ -24,10 +24,12 @@ CPPSRCS = \
DatabaseInfo.cpp \
FileInfo.cpp \
FileManager.cpp \
FileStream.cpp \
IDBCursor.cpp \
IDBDatabase.cpp \
IDBEvents.cpp \
IDBFactory.cpp \
IDBFileHandle.cpp \
IDBIndex.cpp \
IDBKeyRange.cpp \
IDBObjectStore.cpp \
@ -45,6 +47,8 @@ EXPORTS_mozilla/dom/indexedDB = \
IDBCursor.h \
IDBDatabase.h \
IDBEvents.h \
IDBFactory.h \
IDBFileHandle.h \
IDBIndex.h \
IDBKeyRange.h \
IDBObjectStore.h \
@ -53,7 +57,6 @@ EXPORTS_mozilla/dom/indexedDB = \
IDBWrapperCache.h \
IndexedDatabase.h \
IndexedDatabaseManager.h \
IDBFactory.h \
Key.h \
FileManager.h \
FileInfo.h \
@ -77,6 +80,7 @@ XPIDLSRCS = \
nsIIDBCursorWithValue.idl \
nsIIDBDatabase.idl \
nsIIDBFactory.idl \
nsIIDBFileHandle.idl \
nsIIDBIndex.idl \
nsIIDBKeyRange.idl \
nsIIDBObjectStore.idl \
@ -85,6 +89,7 @@ XPIDLSRCS = \
nsIIDBVersionChangeEvent.idl \
nsIIDBOpenDBRequest.idl \
nsIIndexedDatabaseManager.idl \
nsIStandardFileStream.idl \
$(NULL)
DIRS += ipc

View File

@ -216,8 +216,14 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction)
mTransactionsInProgress.Remove(databaseId);
// See if we need to fire any complete callbacks.
for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
MaybeFireCallback(index);
PRUint32 index = 0;
while (index < mCompleteCallbacks.Length()) {
if (MaybeFireCallback(mCompleteCallbacks[index])) {
mCompleteCallbacks.RemoveElementAt(index);
}
else {
index++;
}
}
}
else {
@ -459,7 +465,10 @@ TransactionThreadPool::WaitForAllDatabasesToComplete(
NS_ERROR("This should never fail!");
}
MaybeFireCallback(mCompleteCallbacks.Length() - 1);
if (MaybeFireCallback(*callback)) {
mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
}
return true;
}
@ -540,25 +549,20 @@ TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase)
return false;
}
void
TransactionThreadPool::MaybeFireCallback(PRUint32 aCallbackIndex)
bool
TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback& aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
DatabasesCompleteCallback& callback = mCompleteCallbacks[aCallbackIndex];
bool freeToRun = true;
for (PRUint32 index = 0; index < callback.mDatabases.Length(); index++) {
if (mTransactionsInProgress.Get(callback.mDatabases[index]->Id(), nsnull)) {
freeToRun = false;
break;
for (PRUint32 index = 0; index < aCallback.mDatabases.Length(); index++) {
if (mTransactionsInProgress.Get(aCallback.mDatabases[index]->Id(),
nsnull)) {
return false;
}
}
if (freeToRun) {
callback.mCallback->Run();
mCompleteCallbacks.RemoveElementAt(aCallbackIndex);
}
aCallback.mCallback->Run();
return true;
}
TransactionThreadPool::

View File

@ -129,7 +129,7 @@ protected:
aInfo.finishRunnable);
}
void MaybeFireCallback(PRUint32 aCallbackIndex);
bool MaybeFireCallback(DatabasesCompleteCallback& aCallback);
nsCOMPtr<nsIThreadPool> mThreadPool;

View File

@ -23,7 +23,7 @@ dictionary IDBObjectStoreParameters
* http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBDatabase
* for more information.
*/
[scriptable, builtinclass, uuid(bedee48a-f47f-44f2-ba1e-d8fe595bbfee)]
[scriptable, builtinclass, uuid(4543dbad-2b37-4741-8729-73b08b7ee37e)]
interface nsIIDBDatabase : nsISupports
{
readonly attribute DOMString name;
@ -35,7 +35,7 @@ interface nsIIDBDatabase : nsISupports
[implicit_jscontext]
nsIIDBObjectStore
createObjectStore([Null(Stringify)] in DOMString name,
/* nsIIDBObjectStoreParameters */
/* IDBObjectStoreParameters */
[optional /* none */] in jsval options);
void
@ -47,6 +47,10 @@ interface nsIIDBDatabase : nsISupports
transaction(in jsval storeNames, // js array of strings
[optional /* "readonly" */] in DOMString mode);
nsIIDBRequest
mozCreateFileHandle(in DOMString name,
[optional] in DOMString type);
void
close();

View File

@ -0,0 +1,15 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsISupports.idl"
interface nsIIDBDatabase;
[scriptable, builtinclass, uuid(7b05f6bb-26b0-4c12-a9a1-e31dd933deb8)]
interface nsIIDBFileHandle : nsISupports
{
readonly attribute nsIIDBDatabase database;
};

View File

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
#include "nsISupports.idl"
interface nsIFile;
/**
* A stream that allows you to read from a file or stream to a file
* using standard file APIs.
*/
[scriptable, uuid(ebbbb779-92a3-4b2a-b7cf-6efbe904c453)]
interface nsIStandardFileStream : nsISupports
{
/**
* If this is set, the file will be opened (i.e., a call to
* fopen done) only when we do an actual operation on the stream,
* or more specifically, when one of the following is called:
* - Seek
* - Tell
* - SetEOF
* - Available
* - Read
* - Write
* - Flush
* - GetSize
* - GetLastModified
* - Sync
*
* FLAGS_DEFER_OPEN is useful if we use the stream on a background
* thread, so that the opening and possible |stat|ing of the file
* happens there as well.
*
* @note Using this flag results in the file not being opened
* during the call to Init. This means that any errors that might
* happen when this flag is not set would happen during the
* first read. Also, the file is not locked when Init is called,
* so it might be deleted before we try to read from it.
*/
const long FLAGS_DEFER_OPEN = 1 << 0;
/**
* @param file file to read from or stream to
* @param mode file open mode (see fopen documentation)
* @param flags flags specifying various behaviors of the class
* (see enumerations in the class)
*/
void init(in nsIFile file,
in AString mode,
in long flags);
/**
* Flush all written content held in memory buffers out to disk.
* This is the equivalent of fflush()
*/
void flushBuffers();
};

View File

@ -56,6 +56,8 @@ TEST_FILES = \
test_file_resurrection_transaction_abort.html \
test_file_sharing.html \
test_file_transaction_abort.html \
test_filehandle_serialization.html \
test_filehandle_store_snapshot.html \
test_getAll.html \
test_global_data.html \
test_index_empty_keyPath.html \

View File

@ -48,12 +48,12 @@ function compareBuffers(buffer1, buffer2)
function getBlob(type, view)
{
return new Blob([view], {type: type});
return utils.getBlob([view], {type: type});
}
function getFile(name, type, view)
{
return new Blob([view], {type: type});
return utils.getFile(name, [view], {type: type});
}
function getRandomBlob(size)
@ -212,6 +212,11 @@ function scheduleGC()
SpecialPowers.exactGC(window, continueToNextStep);
}
function getFileId(file)
{
return utils.getFileId(file);
}
function hasFileInfo(name, id)
{
return utils.getFileReferences(name, id);

View File

@ -11,10 +11,15 @@ function executeSoon(aFun)
}
if (!window.runTest) {
window.runTest = function()
window.runTest = function(limitedQuota)
{
allowIndexedDB();
allowUnlimitedQuota();
if (limitedQuota) {
denyUnlimitedQuota();
}
else {
allowUnlimitedQuota();
}
SimpleTest.waitForExplicitFinish();
testGenerator.next();

View File

@ -61,6 +61,9 @@
refResult = result;
continue;
}
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
isnot(result.mozFullPath, refResult.mozFullPath, "Different os files");
}
for (let i = 1; i < databases.length; i++) {
@ -82,6 +85,9 @@
let result = event.target.result;
verifyBlob(result, refResult, 2);
yield;
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
isnot(result.mozFullPath, refResult.mozFullPath, "Different os files");
}
is(bufferCache.length, 2, "Correct length");

View File

@ -12,8 +12,6 @@
<script type="text/javascript;version=1.7">
function testSteps()
{
denyUnlimitedQuota();
const READ_WRITE = IDBTransaction.READ_WRITE;
const DEFAULT_QUOTA_MB = 50;
@ -32,16 +30,13 @@
let event = yield;
is(event.type, "upgradeneeded", "Got correct event type");
let db = event.target.result;
let objectStore = db.createObjectStore(objectStoreName, { });
objectStore.add(testData.value, testData.key);
let usage = getUsageSync();
let size = (DEFAULT_QUOTA_MB + 1) * 1024 * 1024 - usage;
let size = (DEFAULT_QUOTA_MB + 1) * 1024 * 1024 - getUsageSync();
fileData.file = getNullFile("random.bin", size);
event = yield;
@ -70,6 +65,6 @@
</head>
<body onload="runTest();"></body>
<body onload="runTest(true);"></body>
</html>

View File

@ -0,0 +1,66 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = IDBTransaction.READ_WRITE;
const DEFAULT_QUOTA_MB = 50;
const name = window.location.pathname;
const description = "My Test Database";
let request = mozIndexedDB.open(name, 1, description);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
is(event.type, "upgradeneeded", "Got correct event type");
let db = event.target.result;
db.onerror = errorHandler;
event = yield;
request = db.mozCreateFileHandle("test.bin", "binary");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
let blob = getNullBlob((50 + 1) * 1024 * 1024 - getUsageSync());
request = lockedFile.write(blob);
request.onerror = new ExpectError("UnknownError");
request.onsuccess = unexpectedSuccessHandler;
event = yield;
lockedFile.oncomplete = grabEventAndContinueHandler;
event = yield;
is(event.type, "complete", "Got correct event type");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest(true);"></body>
</html>

View File

@ -0,0 +1,101 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = IDBTransaction.READ_WRITE;
const databaseInfo = [
{ name: window.location.pathname + "1", description: "Test Database 1" },
{ name: window.location.pathname + "2", description: "Test Database 2" }
];
const objectStoreName = "Blobs";
const testFile = getRandomFile("random.bin", 100000);
let databases = [];
for each (let info in databaseInfo) {
let request = mozIndexedDB.open(info.name, 1, info.description);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
is(event.type, "upgradeneeded", "Got correct event type");
let db = event.target.result;
db.onerror = errorHandler;
db.createObjectStore(objectStoreName, { });
event = yield;
is(event.type, "success", "Got correct event type");
databases.push(db);
}
let db1 = databases[0];
let request = db1.mozCreateFileHandle("random.bin", "binary/random");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let fileHandle = event.target.result;
is(getFileId(fileHandle), 1, "Correct file id");
is(fileHandle.name, "random.bin", "Correct name");
is(fileHandle.type, "binary/random", "Correct type");
let trans = db1.transaction([objectStoreName], READ_WRITE);
let objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(fileHandle, 42);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
request = objectStore.get(42);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let result = event.target.result;
is(getFileId(result), 1, "Correct file id");
is(result.name, fileHandle.name, "Correct name");
is(result.type, fileHandle.type, "Correct type");
let db2 = databases[1];
trans = db2.transaction([objectStoreName], READ_WRITE);
objectStore = trans.objectStore(objectStoreName);
try {
objectStore.add(fileHandle, 42);
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got exception.");
is(e.name, "DataCloneError", "Good error.");
is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,92 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const READ_WRITE = IDBTransaction.READ_WRITE;
const name = window.location.pathname;
const description = "My Test Database";
const objectStoreName = "Blobs";
const testFile = getRandomFile("random.bin", 100000);
let request = mozIndexedDB.open(name, 1, description);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
is(event.type, "upgradeneeded", "Got correct event type");
let db = event.target.result;
db.onerror = errorHandler;
let objectStore = db.createObjectStore(objectStoreName, { });
event = yield;
is(event.type, "success", "Got correct event type");
request = db.mozCreateFileHandle("random.bin", "binary/random");
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let fileHandle = event.target.result;
fileHandle.onerror = errorHandler;
let lockedFile = fileHandle.open("readwrite");
is(getFileId(fileHandle), 1, "Correct file id");
request = lockedFile.write(testFile);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
request = fileHandle.getFile();
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let file = event.target.result;
lockedFile = event.target.lockedFile;
is(lockedFile.active, true, "Correct active state");
let trans = db.transaction([objectStoreName], READ_WRITE);
let objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(file, 42);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(lockedFile.active, false, "Correct open state");
request = objectStore.get(42);
request.onsuccess = grabEventAndContinueHandler;
event = yield;
verifyBlob(event.target.result, testFile, 2);
yield;
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

Some files were not shown because too many files have changed in this diff Show More