Bug 771288 - Multiprocess FileHandle support (FileHandle on PBackground); r=baku

--HG--
rename : dom/filehandle/FileHandle.cpp => dom/filehandle/FileHandleBase.cpp
rename : dom/filehandle/FileHandle.h => dom/filehandle/FileHandleBase.h
rename : dom/filehandle/FileRequest.h => dom/filehandle/FileRequestBase.h
rename : dom/filehandle/MutableFile.cpp => dom/filehandle/MutableFileBase.cpp
rename : dom/filehandle/MutableFile.h => dom/filehandle/MutableFileBase.h
This commit is contained in:
Jan Varga 2015-09-09 13:15:05 +02:00
parent 4852256c3a
commit 2a061d3c3b
87 changed files with 8849 additions and 5249 deletions

View File

@ -536,8 +536,6 @@ public:
uint64_t aLength, int64_t aLastModifiedDate,
BlobDirState aDirState) override
{
NS_ASSERTION(aLength, "must have length");
mName = aName;
mContentType = aContentType;
mLength = aLength;

View File

@ -0,0 +1,743 @@
/* 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 "ActorsChild.h"
#include "BackgroundChildImpl.h"
#include "FileHandleBase.h"
#include "FileRequestBase.h"
#include "js/Date.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "MutableFileBase.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsString.h"
#include "xpcpublic.h"
namespace mozilla {
namespace dom {
/*******************************************************************************
* Helpers
******************************************************************************/
namespace {
class MOZ_STACK_CLASS AutoSetCurrentFileHandle final
{
typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
FileHandleBase* const mFileHandle;
FileHandleBase* mPreviousFileHandle;
FileHandleBase** mThreadLocalSlot;
public:
explicit AutoSetCurrentFileHandle(FileHandleBase* aFileHandle)
: mFileHandle(aFileHandle)
, mPreviousFileHandle(nullptr)
, mThreadLocalSlot(nullptr)
{
if (aFileHandle) {
BackgroundChildImpl::ThreadLocal* threadLocal =
BackgroundChildImpl::GetThreadLocalForCurrentThread();
MOZ_ASSERT(threadLocal);
// Hang onto this location for resetting later.
mThreadLocalSlot = &threadLocal->mCurrentFileHandle;
// Save the current value.
mPreviousFileHandle = *mThreadLocalSlot;
// Set the new value.
*mThreadLocalSlot = aFileHandle;
}
}
~AutoSetCurrentFileHandle()
{
MOZ_ASSERT_IF(mThreadLocalSlot, mFileHandle);
MOZ_ASSERT_IF(mThreadLocalSlot, *mThreadLocalSlot == mFileHandle);
if (mThreadLocalSlot) {
// Reset old value.
*mThreadLocalSlot = mPreviousFileHandle;
}
}
FileHandleBase*
FileHandle() const
{
return mFileHandle;
}
};
class MOZ_STACK_CLASS ResultHelper final
: public FileRequestBase::ResultCallback
{
FileRequestBase* mFileRequest;
AutoSetCurrentFileHandle mAutoFileHandle;
union
{
File* mFile;
const nsCString* mString;
const FileRequestMetadata* mMetadata;
const JS::Handle<JS::Value>* mJSValHandle;
} mResult;
enum
{
ResultTypeFile,
ResultTypeString,
ResultTypeMetadata,
ResultTypeJSValHandle,
} mResultType;
public:
ResultHelper(FileRequestBase* aFileRequest,
FileHandleBase* aFileHandle,
File* aResult)
: mFileRequest(aFileRequest)
, mAutoFileHandle(aFileHandle)
, mResultType(ResultTypeFile)
{
MOZ_ASSERT(aFileRequest);
MOZ_ASSERT(aFileHandle);
MOZ_ASSERT(aResult);
mResult.mFile = aResult;
}
ResultHelper(FileRequestBase* aFileRequest,
FileHandleBase* aFileHandle,
const nsCString* aResult)
: mFileRequest(aFileRequest)
, mAutoFileHandle(aFileHandle)
, mResultType(ResultTypeString)
{
MOZ_ASSERT(aFileRequest);
MOZ_ASSERT(aFileHandle);
MOZ_ASSERT(aResult);
mResult.mString = aResult;
}
ResultHelper(FileRequestBase* aFileRequest,
FileHandleBase* aFileHandle,
const FileRequestMetadata* aResult)
: mFileRequest(aFileRequest)
, mAutoFileHandle(aFileHandle)
, mResultType(ResultTypeMetadata)
{
MOZ_ASSERT(aFileRequest);
MOZ_ASSERT(aFileHandle);
MOZ_ASSERT(aResult);
mResult.mMetadata = aResult;
}
ResultHelper(FileRequestBase* aFileRequest,
FileHandleBase* aFileHandle,
const JS::Handle<JS::Value>* aResult)
: mFileRequest(aFileRequest)
, mAutoFileHandle(aFileHandle)
, mResultType(ResultTypeJSValHandle)
{
MOZ_ASSERT(aFileRequest);
MOZ_ASSERT(aFileHandle);
MOZ_ASSERT(aResult);
mResult.mJSValHandle = aResult;
}
FileRequestBase*
FileRequest() const
{
return mFileRequest;
}
FileHandleBase*
FileHandle() const
{
return mAutoFileHandle.FileHandle();
}
virtual nsresult
GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) override
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(mFileRequest);
switch (mResultType) {
case ResultTypeFile:
return GetResult(aCx, mResult.mFile, aResult);
case ResultTypeString:
return GetResult(aCx, mResult.mString, aResult);
case ResultTypeMetadata:
return GetResult(aCx, mResult.mMetadata, aResult);
case ResultTypeJSValHandle:
aResult.set(*mResult.mJSValHandle);
return NS_OK;
default:
MOZ_CRASH("Unknown result type!");
}
MOZ_CRASH("Should never get here!");
}
private:
nsresult
GetResult(JSContext* aCx,
File* aFile,
JS::MutableHandle<JS::Value> aResult)
{
bool ok = GetOrCreateDOMReflector(aCx, aFile, aResult);
if (NS_WARN_IF(!ok)) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
return NS_OK;
}
nsresult
GetResult(JSContext* aCx,
const nsCString* aString,
JS::MutableHandle<JS::Value> aResult)
{
const nsCString& data = *aString;
nsresult rv;
if (!mFileRequest->HasEncoding()) {
JS::Rooted<JSObject*> arrayBuffer(aCx);
rv = nsContentUtils::CreateArrayBuffer(aCx, data, arrayBuffer.address());
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
aResult.setObject(*arrayBuffer);
return NS_OK;
}
nsAutoCString encoding;
// The BOM sniffing is baked into the "decode" part of the Encoding
// Standard, which the File API references.
if (!nsContentUtils::CheckForBOM(
reinterpret_cast<const unsigned char *>(data.get()),
data.Length(),
encoding)) {
// BOM sniffing failed. Try the API argument.
if (!EncodingUtils::FindEncodingForLabel(mFileRequest->GetEncoding(),
encoding)) {
// API argument failed. Since we are dealing with a file system file,
// we don't have a meaningful type attribute for the blob available,
// so proceeding to the next step, which is defaulting to UTF-8.
encoding.AssignLiteral("UTF-8");
}
}
nsString tmpString;
rv = nsContentUtils::ConvertStringFromEncoding(encoding, data, tmpString);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
if (NS_WARN_IF(!xpc::StringToJsval(aCx, tmpString, aResult))) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
return NS_OK;
}
nsresult
GetResult(JSContext* aCx,
const FileRequestMetadata* aMetadata,
JS::MutableHandle<JS::Value> aResult)
{
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
const FileRequestSize& size = aMetadata->size();
if (size.type() != FileRequestSize::Tvoid_t) {
MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t);
JS::Rooted<JS::Value> number(aCx, JS_NumberValue(size.get_uint64_t()));
if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "size", number, 0))) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
}
const FileRequestLastModified& lastModified = aMetadata->lastModified();
if (lastModified.type() != FileRequestLastModified::Tvoid_t) {
MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t);
JS::Rooted<JSObject*> date(aCx,
JS::NewDateObject(aCx, JS::TimeClip(lastModified.get_int64_t())));
if (NS_WARN_IF(!date)) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModified", date, 0))) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
}
aResult.setObject(*obj);
return NS_OK;
}
};
already_AddRefed<File>
ConvertActorToFile(FileHandleBase* aFileHandle,
const FileRequestGetFileResponse& aResponse)
{
auto* actor = static_cast<BlobChild*>(aResponse.fileChild());
MutableFileBase* mutableFile = aFileHandle->MutableFile();
MOZ_ASSERT(mutableFile);
const FileRequestMetadata& metadata = aResponse.metadata();
const FileRequestSize& size = metadata.size();
MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t);
const FileRequestLastModified& lastModified = metadata.lastModified();
MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t);
actor->SetMysteryBlobInfo(mutableFile->Name(),
mutableFile->Type(),
size.get_uint64_t(),
lastModified.get_int64_t(),
BlobDirState::eUnknownIfDir);
nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
MOZ_ASSERT(blobImpl);
nsRefPtr<File> file = mutableFile->CreateFileFor(blobImpl, aFileHandle);
return file.forget();
}
void
HandleSuccess(ResultHelper* aResultHelper)
{
MOZ_ASSERT(aResultHelper);
nsRefPtr<FileRequestBase> fileRequest = aResultHelper->FileRequest();
MOZ_ASSERT(fileRequest);
fileRequest->AssertIsOnOwningThread();
nsRefPtr<FileHandleBase> fileHandle = aResultHelper->FileHandle();
MOZ_ASSERT(fileHandle);
if (fileHandle->IsAborted()) {
fileRequest->SetError(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR);
return;
}
MOZ_ASSERT(fileHandle->IsOpen());
fileRequest->SetResultCallback(aResultHelper);
MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
}
void
HandleError(FileRequestBase* aFileRequest,
nsresult aErrorCode,
FileHandleBase* aFileHandle)
{
MOZ_ASSERT(aFileRequest);
aFileRequest->AssertIsOnOwningThread();
MOZ_ASSERT(NS_FAILED(aErrorCode));
MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_FILEHANDLE);
MOZ_ASSERT(aFileHandle);
nsRefPtr<FileRequestBase> fileRequest = aFileRequest;
nsRefPtr<FileHandleBase> fileHandle = aFileHandle;
AutoSetCurrentFileHandle ascfh(aFileHandle);
fileRequest->SetError(aErrorCode);
MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted());
}
} // anonymous namespace
/*******************************************************************************
* BackgroundMutableFileChildBase
******************************************************************************/
BackgroundMutableFileChildBase::BackgroundMutableFileChildBase(
DEBUGONLY(PRThread* aOwningThread))
: ThreadObject(DEBUGONLY(aOwningThread))
, mMutableFile(nullptr)
{
AssertIsOnOwningThread();
MOZ_COUNT_CTOR(BackgroundMutableFileChildBase);
}
BackgroundMutableFileChildBase::~BackgroundMutableFileChildBase()
{
AssertIsOnOwningThread();
MOZ_COUNT_DTOR(BackgroundMutableFileChildBase);
}
void
BackgroundMutableFileChildBase::EnsureDOMObject()
{
AssertIsOnOwningThread();
if (mTemporaryStrongMutableFile) {
return;
}
mTemporaryStrongMutableFile = CreateMutableFile();
MOZ_ASSERT(mTemporaryStrongMutableFile);
mTemporaryStrongMutableFile->AssertIsOnOwningThread();
mMutableFile = mTemporaryStrongMutableFile;
}
void
BackgroundMutableFileChildBase::ReleaseDOMObject()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mTemporaryStrongMutableFile);
mTemporaryStrongMutableFile->AssertIsOnOwningThread();
MOZ_ASSERT(mMutableFile == mTemporaryStrongMutableFile);
mTemporaryStrongMutableFile = nullptr;
}
void
BackgroundMutableFileChildBase::SendDeleteMeInternal()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mTemporaryStrongMutableFile);
if (mMutableFile) {
mMutableFile->ClearBackgroundActor();
mMutableFile = nullptr;
MOZ_ALWAYS_TRUE(PBackgroundMutableFileChild::SendDeleteMe());
}
}
void
BackgroundMutableFileChildBase::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnOwningThread();
if (mMutableFile) {
mMutableFile->ClearBackgroundActor();
DEBUGONLY(mMutableFile = nullptr;)
}
}
PBackgroundFileHandleChild*
BackgroundMutableFileChildBase::AllocPBackgroundFileHandleChild(
const FileMode& aMode)
{
MOZ_CRASH("PBackgroundFileHandleChild actors should be manually "
"constructed!");
}
bool
BackgroundMutableFileChildBase::DeallocPBackgroundFileHandleChild(
PBackgroundFileHandleChild* aActor)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
delete static_cast<BackgroundFileHandleChild*>(aActor);
return true;
}
/*******************************************************************************
* BackgroundFileHandleChild
******************************************************************************/
BackgroundFileHandleChild::BackgroundFileHandleChild(
DEBUGONLY(PRThread* aOwningThread,)
FileHandleBase* aFileHandle)
: ThreadObject(DEBUGONLY(aOwningThread))
, mTemporaryStrongFileHandle(aFileHandle)
, mFileHandle(aFileHandle)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFileHandle);
aFileHandle->AssertIsOnOwningThread();
MOZ_COUNT_CTOR(BackgroundFileHandleChild);
}
BackgroundFileHandleChild::~BackgroundFileHandleChild()
{
AssertIsOnOwningThread();
MOZ_COUNT_DTOR(BackgroundFileHandleChild);
}
void
BackgroundFileHandleChild::SendDeleteMeInternal()
{
AssertIsOnOwningThread();
if (mFileHandle) {
NoteActorDestroyed();
MOZ_ALWAYS_TRUE(PBackgroundFileHandleChild::SendDeleteMe());
}
}
void
BackgroundFileHandleChild::NoteActorDestroyed()
{
AssertIsOnOwningThread();
MOZ_ASSERT_IF(mTemporaryStrongFileHandle, mFileHandle);
if (mFileHandle) {
mFileHandle->ClearBackgroundActor();
// Normally this would be DEBUG-only but NoteActorDestroyed is also called
// from SendDeleteMeInternal. In that case we're going to receive an actual
// ActorDestroy call later and we don't want to touch a dead object.
mTemporaryStrongFileHandle = nullptr;
mFileHandle = nullptr;
}
}
void
BackgroundFileHandleChild::NoteComplete()
{
AssertIsOnOwningThread();
MOZ_ASSERT_IF(mFileHandle, mTemporaryStrongFileHandle);
mTemporaryStrongFileHandle = nullptr;
}
void
BackgroundFileHandleChild::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnOwningThread();
NoteActorDestroyed();
}
bool
BackgroundFileHandleChild::RecvComplete(const bool& aAborted)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mFileHandle);
mFileHandle->HandleCompleteOrAbort(aAborted);
NoteComplete();
return true;
}
PBackgroundFileRequestChild*
BackgroundFileHandleChild::AllocPBackgroundFileRequestChild(
const FileRequestParams& aParams)
{
MOZ_CRASH("PBackgroundFileRequestChild actors should be manually "
"constructed!");
}
bool
BackgroundFileHandleChild::DeallocPBackgroundFileRequestChild(
PBackgroundFileRequestChild* aActor)
{
MOZ_ASSERT(aActor);
delete static_cast<BackgroundFileRequestChild*>(aActor);
return true;
}
/*******************************************************************************
* BackgroundFileRequestChild
******************************************************************************/
BackgroundFileRequestChild::BackgroundFileRequestChild(
DEBUGONLY(PRThread* aOwningThread,)
FileRequestBase* aFileRequest)
: ThreadObject(DEBUGONLY(aOwningThread))
, mFileRequest(aFileRequest)
, mFileHandle(aFileRequest->FileHandle())
, mActorDestroyed(false)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFileRequest);
aFileRequest->AssertIsOnOwningThread();
MOZ_ASSERT(mFileHandle);
mFileHandle->AssertIsOnOwningThread();
MOZ_COUNT_CTOR(BackgroundFileRequestChild);
}
BackgroundFileRequestChild::~BackgroundFileRequestChild()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mFileHandle);
MOZ_COUNT_DTOR(BackgroundFileRequestChild);
}
void
BackgroundFileRequestChild::HandleResponse(nsresult aResponse)
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_FAILED(aResponse));
MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_FILEHANDLE);
MOZ_ASSERT(mFileHandle);
HandleError(mFileRequest, aResponse, mFileHandle);
}
void
BackgroundFileRequestChild::HandleResponse(
const FileRequestGetFileResponse& aResponse)
{
AssertIsOnOwningThread();
nsRefPtr<File> file = ConvertActorToFile(mFileHandle, aResponse);
ResultHelper helper(mFileRequest, mFileHandle, file);
HandleSuccess(&helper);
}
void
BackgroundFileRequestChild::HandleResponse(const nsCString& aResponse)
{
AssertIsOnOwningThread();
ResultHelper helper(mFileRequest, mFileHandle, &aResponse);
HandleSuccess(&helper);
}
void
BackgroundFileRequestChild::HandleResponse(const FileRequestMetadata& aResponse)
{
AssertIsOnOwningThread();
ResultHelper helper(mFileRequest, mFileHandle, &aResponse);
HandleSuccess(&helper);
}
void
BackgroundFileRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse)
{
AssertIsOnOwningThread();
ResultHelper helper(mFileRequest, mFileHandle, &aResponse);
HandleSuccess(&helper);
}
void
BackgroundFileRequestChild::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mActorDestroyed);
mActorDestroyed = true;
if (mFileHandle) {
mFileHandle->AssertIsOnOwningThread();
mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */
aWhy == Deletion);
DEBUGONLY(mFileHandle = nullptr;)
}
}
bool
BackgroundFileRequestChild::Recv__delete__(const FileRequestResponse& aResponse)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mFileRequest);
MOZ_ASSERT(mFileHandle);
if (mFileHandle->IsAborted()) {
// Always handle an "error" with ABORT_ERR if the file handle was aborted,
// even if the request succeeded or failed with another error.
HandleResponse(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR);
} else {
switch (aResponse.type()) {
case FileRequestResponse::Tnsresult:
HandleResponse(aResponse.get_nsresult());
break;
case FileRequestResponse::TFileRequestGetFileResponse:
HandleResponse(aResponse.get_FileRequestGetFileResponse());
break;
case FileRequestResponse::TFileRequestReadResponse:
HandleResponse(aResponse.get_FileRequestReadResponse().data());
break;
case FileRequestResponse::TFileRequestWriteResponse:
HandleResponse(JS::UndefinedHandleValue);
break;
case FileRequestResponse::TFileRequestTruncateResponse:
HandleResponse(JS::UndefinedHandleValue);
break;
case FileRequestResponse::TFileRequestFlushResponse:
HandleResponse(JS::UndefinedHandleValue);
break;
case FileRequestResponse::TFileRequestGetMetadataResponse:
HandleResponse(aResponse.get_FileRequestGetMetadataResponse()
.metadata());
break;
default:
MOZ_CRASH("Unknown response type!");
}
}
mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true);
// Null this out so that we don't try to call OnRequestFinished() again in
// ActorDestroy.
mFileHandle = nullptr;
return true;
}
bool
BackgroundFileRequestChild::RecvProgress(const uint64_t& aProgress,
const uint64_t& aProgressMax)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mFileRequest);
mFileRequest->OnProgress(aProgress, aProgressMax);
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,175 @@
/* 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_filehandle_ActorsChild_h
#define mozilla_dom_filehandle_ActorsChild_h
#include "js/RootingAPI.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/FileHandleCommon.h"
#include "mozilla/dom/PBackgroundFileHandleChild.h"
#include "mozilla/dom/PBackgroundFileRequestChild.h"
#include "mozilla/dom/PBackgroundMutableFileChild.h"
#include "nsAutoPtr.h"
class nsCString;
namespace mozilla {
namespace dom {
class FileHandleBase;
class FileRequestBase;
class MutableFileBase;
class BackgroundMutableFileChildBase
: public ThreadObject
, public PBackgroundMutableFileChild
{
protected:
friend class MutableFileBase;
nsRefPtr<MutableFileBase> mTemporaryStrongMutableFile;
MutableFileBase* mMutableFile;
public:
void
EnsureDOMObject();
MutableFileBase*
GetDOMObject() const
{
AssertIsOnOwningThread();
return mMutableFile;
}
void
ReleaseDOMObject();
protected:
BackgroundMutableFileChildBase(DEBUGONLY(PRThread* aOwningThread));
~BackgroundMutableFileChildBase();
void
SendDeleteMeInternal();
virtual already_AddRefed<MutableFileBase>
CreateMutableFile() = 0;
// IPDL methods are only called by IPDL.
virtual void
ActorDestroy(ActorDestroyReason aWhy) override;
virtual PBackgroundFileHandleChild*
AllocPBackgroundFileHandleChild(const FileMode& aMode) override;
virtual bool
DeallocPBackgroundFileHandleChild(PBackgroundFileHandleChild* aActor)
override;
bool
SendDeleteMe() = delete;
};
class BackgroundFileHandleChild
: public ThreadObject
, public PBackgroundFileHandleChild
{
friend class BackgroundMutableFileChildBase;
friend class MutableFileBase;
// mTemporaryStrongFileHandle is strong and is only valid until the end of
// NoteComplete() member function or until the NoteActorDestroyed() member
// function is called.
nsRefPtr<FileHandleBase> mTemporaryStrongFileHandle;
// mFileHandle is weak and is valid until the NoteActorDestroyed() member
// function is called.
FileHandleBase* mFileHandle;
public:
BackgroundFileHandleChild(DEBUGONLY(PRThread* aOwningThread,)
FileHandleBase* aFileHandle);
void
SendDeleteMeInternal();
private:
~BackgroundFileHandleChild();
void
NoteActorDestroyed();
void
NoteComplete();
// IPDL methods are only called by IPDL.
virtual void
ActorDestroy(ActorDestroyReason aWhy) override;
bool
RecvComplete(const bool& aAborted) override;
virtual PBackgroundFileRequestChild*
AllocPBackgroundFileRequestChild(const FileRequestParams& aParams)
override;
virtual bool
DeallocPBackgroundFileRequestChild(PBackgroundFileRequestChild* aActor)
override;
bool
SendDeleteMe() = delete;
};
class BackgroundFileRequestChild final
: public ThreadObject
, public PBackgroundFileRequestChild
{
friend class BackgroundFileHandleChild;
friend class FileHandleBase;
nsRefPtr<FileRequestBase> mFileRequest;
nsRefPtr<FileHandleBase> mFileHandle;
bool mActorDestroyed;
private:
// Only created by FileHandleBase.
BackgroundFileRequestChild(DEBUGONLY(PRThread* aOwningThread,)
FileRequestBase* aFileRequest);
// Only destroyed by BackgroundFileHandleChild.
~BackgroundFileRequestChild();
void
HandleResponse(nsresult aResponse);
void
HandleResponse(const FileRequestGetFileResponse& aResponse);
void
HandleResponse(const nsCString& aResponse);
void
HandleResponse(const FileRequestMetadata& aResponse);
void
HandleResponse(JS::Handle<JS::Value> aResponse);
// IPDL methods are only called by IPDL.
virtual void
ActorDestroy(ActorDestroyReason aWhy) override;
virtual bool
Recv__delete__(const FileRequestResponse& aResponse) override;
virtual bool
RecvProgress(const uint64_t& aProgress,
const uint64_t& aProgressMax) override;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_filehandle_ActorsChild_h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,221 @@
/* 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_filehandle_ActorsParent_h
#define mozilla_dom_filehandle_ActorsParent_h
#include "mozilla/dom/FileHandleStorage.h"
#include "mozilla/dom/PBackgroundMutableFileParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsCOMPtr.h"
#include "nsHashKeys.h"
#include "nsString.h"
#include "nsTHashtable.h"
template <class> struct already_AddRefed;
class nsIFile;
class nsIRunnable;
class nsIThreadPool;
template <class> class nsTArray;
namespace mozilla {
namespace ipc {
class PBackgroundParent;
} // namespace ipc
namespace dom {
class BlobImpl;
class FileHandle;
class FileHandleOp;
class FileHandleThreadPool final
{
class FileHandleQueue;
struct DelayedEnqueueInfo;
class DirectoryInfo;
struct StoragesCompleteCallback;
nsCOMPtr<nsIThreadPool> mThreadPool;
nsCOMPtr<nsIEventTarget> mOwningThread;
nsClassHashtable<nsCStringHashKey, DirectoryInfo> mDirectoryInfos;
nsTArray<nsAutoPtr<StoragesCompleteCallback>> mCompleteCallbacks;
bool mShutdownRequested;
bool mShutdownComplete;
public:
static already_AddRefed<FileHandleThreadPool>
Create();
#ifdef DEBUG
void
AssertIsOnOwningThread() const;
nsIEventTarget*
GetThreadPoolEventTarget() const;
#else
void
AssertIsOnOwningThread() const
{ }
#endif
void
Enqueue(FileHandle* aFileHandle,
FileHandleOp* aFileHandleOp,
bool aFinish);
NS_INLINE_DECL_REFCOUNTING(FileHandleThreadPool)
void
WaitForDirectoriesToComplete(nsTArray<nsCString>&& aDirectoryIds,
nsIRunnable* aCallback);
void
Shutdown();
private:
FileHandleThreadPool();
// Reference counted.
~FileHandleThreadPool();
nsresult
Init();
void
Cleanup();
void
FinishFileHandle(FileHandle* aFileHandle);
bool
MaybeFireCallback(StoragesCompleteCallback* aCallback);
};
class BackgroundMutableFileParentBase
: public PBackgroundMutableFileParent
{
nsTHashtable<nsPtrHashKey<FileHandle>> mFileHandles;
nsCString mDirectoryId;
nsString mFileName;
FileHandleStorage mStorage;
bool mInvalidated;
bool mActorWasAlive;
bool mActorDestroyed;
protected:
nsCOMPtr<nsIFile> mFile;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BackgroundMutableFileParentBase)
void
Invalidate();
FileHandleStorage
Storage() const
{
return mStorage;
}
const nsCString&
DirectoryId() const
{
return mDirectoryId;
}
const nsString&
FileName() const
{
return mFileName;
}
bool
RegisterFileHandle(FileHandle* aFileHandle);
void
UnregisterFileHandle(FileHandle* aFileHandle);
void
SetActorAlive();
bool
IsActorDestroyed() const
{
mozilla::ipc::AssertIsOnBackgroundThread();
return mActorWasAlive && mActorDestroyed;
}
bool
IsInvalidated() const
{
mozilla::ipc::AssertIsOnBackgroundThread();
return mInvalidated;
}
virtual void
NoteActiveState()
{ }
virtual void
NoteInactiveState()
{ }
virtual mozilla::ipc::PBackgroundParent*
GetBackgroundParent() const = 0;
virtual already_AddRefed<nsISupports>
CreateStream(bool aReadOnly);
virtual already_AddRefed<BlobImpl>
CreateBlobImpl()
{
return nullptr;
}
protected:
BackgroundMutableFileParentBase(FileHandleStorage aStorage,
const nsACString& aDirectoryId,
const nsAString& aFileName,
nsIFile* aFile);
// Reference counted.
~BackgroundMutableFileParentBase();
// IPDL methods are only called by IPDL.
virtual void
ActorDestroy(ActorDestroyReason aWhy) override;
virtual PBackgroundFileHandleParent*
AllocPBackgroundFileHandleParent(const FileMode& aMode) override;
virtual bool
RecvPBackgroundFileHandleConstructor(PBackgroundFileHandleParent* aActor,
const FileMode& aMode) override;
virtual bool
DeallocPBackgroundFileHandleParent(PBackgroundFileHandleParent* aActor)
override;
virtual bool
RecvDeleteMe() override;
virtual bool
RecvGetFileId(int64_t* aFileId) override;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_filehandle_ActorsParent_h

View File

@ -1,131 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "FileService.h"
#include "MainThreadUtils.h"
#include "nsDebug.h"
#include "nsIEventTarget.h"
#include "nsIRequestObserver.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(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, aCtxt);
NS_ENSURE_SUCCESS(rv, rv);
}
FileService* service = FileService::GetOrCreate();
NS_ENSURE_TRUE(service, NS_ERROR_FAILURE);
nsIEventTarget* target = service->ThreadPoolTarget();
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, nullptr);
}
mStatus = DoStreamWork(mStream);
if (mObserver) {
mObserver->OnStopRequest(this, nullptr, 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;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,57 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_AsyncHelper_h
#define mozilla_dom_AsyncHelper_h
#include "nsCOMPtr.h"
#include "nsIRequest.h"
#include "nsIRunnable.h"
class nsIRequestObserver;
namespace mozilla {
namespace dom {
/**
* 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_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIREQUEST
nsresult
AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt);
protected:
explicit AsyncHelper(nsISupports* aStream)
: mStream(aStream),
mStatus(NS_OK)
{ }
virtual ~AsyncHelper()
{ }
virtual nsresult
DoStreamWork(nsISupports* aStream) = 0;
private:
nsCOMPtr<nsISupports> mStream;
nsCOMPtr<nsIRequestObserver> mObserver;
nsresult mStatus;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_AsyncHelper_h

View File

@ -1,901 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "AsyncHelper.h"
#include "FileHelper.h"
#include "FileRequest.h"
#include "FileService.h"
#include "FileStreamWrappers.h"
#include "MemoryStreams.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/File.h"
#include "MutableFile.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIEventTarget.h"
#include "nsISeekableStream.h"
#include "nsNetUtil.h"
#include "nsIAsyncStreamCopier.h"
#include "nsString.h"
#include "nsStringStream.h"
#include "nsThreadUtils.h"
#include "xpcpublic.h"
#define STREAM_COPY_BLOCK_SIZE 32768
namespace mozilla {
namespace dom {
namespace {
class ReadHelper : public FileHelper
{
public:
ReadHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest,
uint64_t aLocation,
uint64_t aSize)
: FileHelper(aFileHandle, aFileRequest),
mLocation(aLocation), mSize(aSize)
{
MOZ_ASSERT(mSize, "Passed zero size!");
}
nsresult
Init();
nsresult
DoAsyncRun(nsISupports* aStream) override;
nsresult
GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal) override;
protected:
uint64_t mLocation;
uint64_t mSize;
nsRefPtr<MemoryOutputStream> mStream;
};
class ReadTextHelper : public ReadHelper
{
public:
ReadTextHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest,
uint64_t aLocation,
uint64_t aSize,
const nsAString& aEncoding)
: ReadHelper(aFileHandle, aFileRequest, aLocation, aSize),
mEncoding(aEncoding)
{ }
nsresult
GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal) override;
private:
nsString mEncoding;
};
class WriteHelper : public FileHelper
{
public:
WriteHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest,
uint64_t aLocation,
nsIInputStream* aStream,
uint64_t aLength)
: FileHelper(aFileHandle, aFileRequest),
mLocation(aLocation), mStream(aStream), mLength(aLength)
{
MOZ_ASSERT(mLength, "Passed zero length!");
}
nsresult
DoAsyncRun(nsISupports* aStream);
private:
uint64_t mLocation;
nsCOMPtr<nsIInputStream> mStream;
uint64_t mLength;
};
class TruncateHelper : public FileHelper
{
public:
TruncateHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest,
uint64_t aOffset)
: FileHelper(aFileHandle, aFileRequest),
mOffset(aOffset)
{ }
nsresult
DoAsyncRun(nsISupports* aStream);
private:
class AsyncTruncator : public AsyncHelper
{
public:
AsyncTruncator(nsISupports* aStream, int64_t aOffset)
: AsyncHelper(aStream),
mOffset(aOffset)
{ }
protected:
nsresult
DoStreamWork(nsISupports* aStream) override;
uint64_t mOffset;
};
uint64_t mOffset;
};
class FlushHelper : public FileHelper
{
public:
FlushHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest)
: FileHelper(aFileHandle, aFileRequest)
{ }
nsresult
DoAsyncRun(nsISupports* aStream);
private:
class AsyncFlusher : public AsyncHelper
{
public:
explicit AsyncFlusher(nsISupports* aStream)
: AsyncHelper(aStream)
{ }
protected:
nsresult
DoStreamWork(nsISupports* aStream) override;
};
};
class OpenStreamHelper : public FileHelper
{
public:
OpenStreamHelper(FileHandleBase* aFileHandle,
bool aWholeFile,
uint64_t aStart,
uint64_t aLength)
: FileHelper(aFileHandle, nullptr),
mWholeFile(aWholeFile), mStart(aStart), mLength(aLength)
{ }
nsresult
DoAsyncRun(nsISupports* aStream);
nsCOMPtr<nsIInputStream>&
Result()
{
return mStream;
}
private:
bool mWholeFile;
uint64_t mStart;
uint64_t mLength;
nsCOMPtr<nsIInputStream> mStream;
};
} // namespace
FileHandleBase::FileHandleBase(FileMode aMode,
RequestMode aRequestMode)
: mReadyState(INITIAL),
mMode(aMode),
mRequestMode(aRequestMode),
mLocation(0),
mPendingRequests(0),
mAborted(false),
mCreating(false)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
}
FileHandleBase::~FileHandleBase()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
}
void
FileHandleBase::OnNewRequest()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
if (!mPendingRequests) {
MOZ_ASSERT(mReadyState == INITIAL, "Reusing a file handle!");
mReadyState = LOADING;
}
++mPendingRequests;
}
void
FileHandleBase::OnRequestFinished()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(mPendingRequests, "Mismatched calls!");
--mPendingRequests;
if (!mPendingRequests) {
MOZ_ASSERT(mAborted || mReadyState == LOADING, "Bad state!");
mReadyState = FileHandleBase::FINISHING;
Finish();
}
}
nsresult
FileHandleBase::CreateParallelStream(nsISupports** aStream)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MutableFileBase* mutableFile = MutableFile();
if (mutableFile->IsInvalid()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsISupports> stream =
mutableFile->CreateStream(mMode == FileMode::Readonly);
NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
mParallelStreams.AppendElement(stream);
stream.forget(aStream);
return NS_OK;
}
nsresult
FileHandleBase::GetOrCreateStream(nsISupports** aStream)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MutableFileBase* mutableFile = MutableFile();
if (mutableFile->IsInvalid()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!mStream) {
nsCOMPtr<nsISupports> stream =
mutableFile->CreateStream(mMode == FileMode::Readonly);
NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
stream.swap(mStream);
}
nsCOMPtr<nsISupports> stream(mStream);
stream.forget(aStream);
return NS_OK;
}
bool
FileHandleBase::IsOpen() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// If we haven't started anything then we're open.
if (mReadyState == INITIAL) {
MOZ_ASSERT(FileHelper::GetCurrentFileHandle() != this,
"This should be some other file handle (or null)!");
return true;
}
// If we've already started then we need to check to see if we still have the
// mCreating flag set. If we do (i.e. we haven't returned to the event loop
// from the time we were created) then we are open. Otherwise check the
// currently running file handles to see if it's the same. We only allow other
// requests to be made if this file handle is currently running.
if (mReadyState == LOADING) {
if (mCreating) {
return true;
}
if (FileHelper::GetCurrentFileHandle() == this) {
return true;
}
}
return false;
}
already_AddRefed<FileRequestBase>
FileHandleBase::Read(uint64_t aSize, bool aHasEncoding,
const nsAString& aEncoding, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// State and argument checking for read
if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
nsRefPtr<ReadHelper> helper;
if (aHasEncoding) {
helper = new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding);
} else {
helper = new ReadHelper(this, fileRequest, mLocation, aSize);
}
if (NS_WARN_IF(NS_FAILED(helper->Init())) ||
NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
mLocation += aSize;
return fileRequest.forget();
}
already_AddRefed<FileRequestBase>
FileHandleBase::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// State checking for write
if (!CheckStateForWrite(aRv)) {
return nullptr;
}
// Getting location and additional state checking for truncate
uint64_t location;
if (aSize.WasPassed()) {
// Just in case someone calls us from C++
MOZ_ASSERT(aSize.Value() != UINT64_MAX, "Passed wrong size!");
location = aSize.Value();
} else {
if (mLocation == UINT64_MAX) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return nullptr;
}
location = mLocation;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
nsRefPtr<TruncateHelper> helper =
new TruncateHelper(this, fileRequest, location);
if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
if (aSize.WasPassed()) {
mLocation = aSize.Value();
}
return fileRequest.forget();
}
already_AddRefed<FileRequestBase>
FileHandleBase::Flush(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// State checking for write
if (!CheckStateForWrite(aRv)) {
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest);
if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
return fileRequest.forget();
}
void
FileHandleBase::Abort(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// This method is special enough for not using generic state checking methods.
// We can't use IsOpen here since we need it to be possible to call Abort()
// even from outside of transaction callbacks.
if (mReadyState != FileHandleBase::INITIAL &&
mReadyState != FileHandleBase::LOADING) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return;
}
bool needToFinish = mReadyState == INITIAL;
mAborted = true;
mReadyState = DONE;
// Fire the abort event if there are no outstanding requests. Otherwise the
// abort event will be fired when all outstanding requests finish.
if (needToFinish) {
aRv = Finish();
}
}
void
FileHandleBase::OnReturnToEventLoop()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// We're back at the event loop, no longer newborn.
mCreating = false;
// Maybe set the readyState to DONE if there were no requests generated.
if (mReadyState == INITIAL) {
mReadyState = DONE;
if (NS_FAILED(Finish())) {
NS_WARNING("Failed to finish!");
}
}
}
nsresult
FileHandleBase::OpenInputStream(bool aWholeFile, uint64_t aStart,
uint64_t aLength, nsIInputStream** aResult)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(mRequestMode == PARALLEL,
"Don't call me in other than parallel mode!");
// Common state checking
ErrorResult error;
if (!CheckState(error)) {
return error.StealNSResult();
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return NS_OK;
}
nsRefPtr<OpenStreamHelper> helper =
new OpenStreamHelper(this, aWholeFile, aStart, aLength);
nsresult rv = helper->Enqueue();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
nsCOMPtr<nsIInputStream>& result = helper->Result();
NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
result.forget(aResult);
return NS_OK;
}
bool
FileHandleBase::CheckState(ErrorResult& aRv)
{
if (!IsOpen()) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
return false;
}
return true;
}
bool
FileHandleBase::CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv)
{
// Common state checking
if (!CheckState(aRv)) {
return false;
}
// Additional state checking for read
if (mLocation == UINT64_MAX) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return false;
}
// Argument checking for read
if (!aSize) {
aRv.ThrowTypeError(MSG_INVALID_READ_SIZE);
return false;
}
return true;
}
bool
FileHandleBase::CheckStateForWrite(ErrorResult& aRv)
{
// Common state checking
if (!CheckState(aRv)) {
return false;
}
// Additional state checking for write
if (mMode != FileMode::Readwrite) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
return false;
}
return true;
}
already_AddRefed<FileRequestBase>
FileHandleBase::WriteInternal(nsIInputStream* aInputStream,
uint64_t aInputLength, bool aAppend,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
DebugOnly<ErrorResult> error;
MOZ_ASSERT(CheckStateForWrite(error));
MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(aInputLength);
MOZ_ASSERT(CheckWindow());
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
uint64_t location = aAppend ? UINT64_MAX : mLocation;
nsRefPtr<WriteHelper> helper =
new WriteHelper(this, fileRequest, location, aInputStream, aInputLength);
if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
if (aAppend) {
mLocation = UINT64_MAX;
}
else {
mLocation += aInputLength;
}
return fileRequest.forget();
}
nsresult
FileHandleBase::Finish()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
nsRefPtr<FinishHelper> helper(new FinishHelper(this));
FileService* service = FileService::Get();
MOZ_ASSERT(service, "This should never be null");
nsIEventTarget* target = service->ThreadPoolTarget();
nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// static
already_AddRefed<nsIInputStream>
FileHandleBase::GetInputStream(const ArrayBuffer& aValue,
uint64_t* aInputLength, ErrorResult& aRv)
{
aValue.ComputeLengthAndData();
const char* data = reinterpret_cast<const char*>(aValue.Data());
uint32_t length = aValue.Length();
nsCOMPtr<nsIInputStream> stream;
aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
NS_ASSIGNMENT_COPY);
if (aRv.Failed()) {
return nullptr;
}
*aInputLength = length;
return stream.forget();
}
// static
already_AddRefed<nsIInputStream>
FileHandleBase::GetInputStream(const Blob& aValue, uint64_t* aInputLength,
ErrorResult& aRv)
{
Blob& file = const_cast<Blob&>(aValue);
uint64_t length = file.GetSize(aRv);
if (aRv.Failed()) {
return nullptr;
}
nsCOMPtr<nsIInputStream> stream;
file.GetInternalStream(getter_AddRefs(stream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
*aInputLength = length;
return stream.forget();
}
// static
already_AddRefed<nsIInputStream>
FileHandleBase::GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
ErrorResult& aRv)
{
NS_ConvertUTF16toUTF8 cstr(aValue);
nsCOMPtr<nsIInputStream> stream;
aRv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr);
if (aRv.Failed()) {
return nullptr;
}
*aInputLength = cstr.Length();
return stream.forget();
}
FinishHelper::FinishHelper(FileHandleBase* aFileHandle)
: mFileHandle(aFileHandle),
mAborted(aFileHandle->mAborted)
{
mParallelStreams.SwapElements(aFileHandle->mParallelStreams);
mStream.swap(aFileHandle->mStream);
}
NS_IMPL_ISUPPORTS(FinishHelper, nsIRunnable)
NS_IMETHODIMP
FinishHelper::Run()
{
if (NS_IsMainThread()) {
mFileHandle->mReadyState = FileHandleBase::DONE;
FileService* service = FileService::Get();
if (service) {
service->NotifyFileHandleCompleted(mFileHandle);
}
nsresult rv = mFileHandle->OnCompleteOrAbort(mAborted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mFileHandle = nullptr;
return NS_OK;
}
if (mFileHandle->MutableFile()->IsInvalid()) {
mAborted = true;
}
for (uint32_t index = 0; index < mParallelStreams.Length(); index++) {
nsCOMPtr<nsIInputStream> stream =
do_QueryInterface(mParallelStreams[index]);
if (NS_FAILED(stream->Close())) {
NS_WARNING("Failed to close stream!");
}
mParallelStreams[index] = nullptr;
}
if (mStream) {
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
if (NS_FAILED(stream->Close())) {
NS_WARNING("Failed to close stream!");
}
mStream = nullptr;
}
return NS_DispatchToMainThread(this);
}
nsresult
ReadHelper::Init()
{
mStream = MemoryOutputStream::Create(mSize);
NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE);
return NS_OK;
}
nsresult
ReadHelper::DoAsyncRun(nsISupports* aStream)
{
MOZ_ASSERT(aStream, "Passed a null stream!");
uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
nsCOMPtr<nsIInputStream> istream =
new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags);
FileService* service = FileService::Get();
MOZ_ASSERT(service, "This should never be null");
nsIEventTarget* target = service->ThreadPoolTarget();
nsCOMPtr<nsIAsyncStreamCopier> copier;
nsresult rv =
NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target,
false, true, STREAM_COPY_BLOCK_SIZE);
NS_ENSURE_SUCCESS(rv, rv);
rv = copier->AsyncCopy(this, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
mRequest = do_QueryInterface(copier);
return NS_OK;
}
nsresult
ReadHelper::GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal)
{
JS::Rooted<JSObject*> arrayBuffer(aCx);
nsresult rv =
nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address());
NS_ENSURE_SUCCESS(rv, rv);
aVal.setObject(*arrayBuffer);
return NS_OK;
}
nsresult
ReadTextHelper::GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal)
{
nsAutoCString encoding;
const nsCString& data = mStream->Data();
// The BOM sniffing is baked into the "decode" part of the Encoding
// Standard, which the File API references.
if (!nsContentUtils::CheckForBOM(
reinterpret_cast<const unsigned char *>(data.get()),
data.Length(),
encoding)) {
// BOM sniffing failed. Try the API argument.
if (!EncodingUtils::FindEncodingForLabel(mEncoding, encoding)) {
// API argument failed. Since we are dealing with a file system file,
// we don't have a meaningful type attribute for the blob available,
// so proceeding to the next step, which is defaulting to UTF-8.
encoding.AssignLiteral("UTF-8");
}
}
nsString tmpString;
nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data,
tmpString);
NS_ENSURE_SUCCESS(rv, rv);
if (!xpc::StringToJsval(aCx, tmpString, aVal)) {
NS_WARNING("Failed to convert string!");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
WriteHelper::DoAsyncRun(nsISupports* aStream)
{
MOZ_ASSERT(aStream, "Passed a null stream!");
uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS;
nsCOMPtr<nsIOutputStream> ostream =
new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags);
FileService* service = FileService::Get();
MOZ_ASSERT(service, "This should never be null");
nsIEventTarget* target = service->ThreadPoolTarget();
nsCOMPtr<nsIAsyncStreamCopier> copier;
nsresult rv =
NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target,
true, false, STREAM_COPY_BLOCK_SIZE);
NS_ENSURE_SUCCESS(rv, rv);
rv = copier->AsyncCopy(this, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
mRequest = do_QueryInterface(copier);
return NS_OK;
}
nsresult
TruncateHelper::DoAsyncRun(nsISupports* aStream)
{
MOZ_ASSERT(aStream, "Passed a null stream!");
nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset);
nsresult rv = truncator->AsyncWork(this, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream)
{
nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream);
nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
NS_ENSURE_SUCCESS(rv, rv);
rv = sstream->SetEOF();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
FlushHelper::DoAsyncRun(nsISupports* aStream)
{
MOZ_ASSERT(aStream, "Passed a null stream!");
nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream);
nsresult rv = flusher->AsyncWork(this, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream)
{
nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream);
nsresult rv = ostream->Flush();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
OpenStreamHelper::DoAsyncRun(nsISupports* aStream)
{
MOZ_ASSERT(aStream, "Passed a null stream!");
uint32_t flags = FileStreamWrapper::NOTIFY_CLOSE |
FileStreamWrapper::NOTIFY_DESTROY;
mStream = mWholeFile ?
new FileInputStreamWrapper(aStream, this, 0, mLength, flags) :
new FileInputStreamWrapper(aStream, this, mStart, mLength, flags);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,274 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_FileHandle_h
#define mozilla_dom_FileHandle_h
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/FileModeBinding.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/ErrorResult.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIInputStream.h"
#include "nsIRunnable.h"
#include "nsTArray.h"
class nsAString;
namespace mozilla {
namespace dom {
class Blob;
class FileHelper;
class FileRequestBase;
class FileService;
class FinishHelper;
class MetadataHelper;
class MutableFileBase;
/**
* This class provides a base for FileHandle implementations.
*/
class FileHandleBase
{
public:
enum RequestMode
{
NORMAL = 0, // Sequential
PARALLEL
};
enum ReadyState
{
INITIAL = 0,
LOADING,
FINISHING,
DONE
};
private:
friend class FileHelper;
friend class FileService;
friend class FinishHelper;
friend class MetadataHelper;
ReadyState mReadyState;
FileMode mMode;
RequestMode mRequestMode;
uint64_t mLocation;
uint32_t mPendingRequests;
nsTArray<nsCOMPtr<nsISupports>> mParallelStreams;
nsCOMPtr<nsISupports> mStream;
bool mAborted;
bool mCreating;
public:
NS_IMETHOD_(MozExternalRefCountType)
AddRef() = 0;
NS_IMETHOD_(MozExternalRefCountType)
Release() = 0;
nsresult
CreateParallelStream(nsISupports** aStream);
nsresult
GetOrCreateStream(nsISupports** aStream);
bool
IsOpen() const;
bool
IsAborted() const
{
return mAborted;
}
void
SetCreating()
{
mCreating = true;
}
virtual MutableFileBase*
MutableFile() const = 0;
nsresult
OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength,
nsIInputStream** aResult);
// Shared WebIDL (IndexedDB FileHandle and FileSystem FileHandle)
FileMode
Mode() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
return mMode;
}
bool
Active() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
return IsOpen();
}
Nullable<uint64_t>
GetLocation() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
if (mLocation == UINT64_MAX) {
return Nullable<uint64_t>();
}
return Nullable<uint64_t>(mLocation);
}
void
SetLocation(const Nullable<uint64_t>& aLocation)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// Null means the end-of-file.
if (aLocation.IsNull()) {
mLocation = UINT64_MAX;
} else {
mLocation = aLocation.Value();
}
}
already_AddRefed<FileRequestBase>
Read(uint64_t aSize, bool aHasEncoding, const nsAString& aEncoding,
ErrorResult& aRv);
already_AddRefed<FileRequestBase>
Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv);
already_AddRefed<FileRequestBase>
Flush(ErrorResult& aRv);
void
Abort(ErrorResult& aRv);
protected:
FileHandleBase(FileMode aMode,
RequestMode aRequestMode);
~FileHandleBase();
void
OnNewRequest();
void
OnRequestFinished();
void
OnReturnToEventLoop();
virtual nsresult
OnCompleteOrAbort(bool aAborted) = 0;
bool
CheckState(ErrorResult& aRv);
bool
CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv);
bool
CheckStateForWrite(ErrorResult& aRv);
virtual bool
CheckWindow() = 0;
virtual already_AddRefed<FileRequestBase>
GenerateFileRequest() = 0;
template<class T>
already_AddRefed<FileRequestBase>
WriteOrAppend(const T& aValue, bool aAppend, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
// State checking for write
if (!CheckStateForWrite(aRv)) {
return nullptr;
}
// Additional state checking for write
if (!aAppend && mLocation == UINT64_MAX) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return nullptr;
}
uint64_t length;
nsCOMPtr<nsIInputStream> stream = GetInputStream(aValue, &length, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (!length) {
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
return WriteInternal(stream, length, aAppend, aRv);
}
already_AddRefed<FileRequestBase>
WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength,
bool aAppend, ErrorResult& aRv);
nsresult
Finish();
static already_AddRefed<nsIInputStream>
GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength,
ErrorResult& aRv);
static already_AddRefed<nsIInputStream>
GetInputStream(const Blob& aValue, uint64_t* aInputLength,
ErrorResult& aRv);
static already_AddRefed<nsIInputStream>
GetInputStream(const nsAString& aValue, uint64_t* aInputLength,
ErrorResult& aRv);
};
class FinishHelper final : public nsIRunnable
{
friend class FileHandleBase;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
private:
explicit FinishHelper(FileHandleBase* aFileHandle);
~FinishHelper()
{ }
nsRefPtr<FileHandleBase> mFileHandle;
nsTArray<nsCOMPtr<nsISupports>> mParallelStreams;
nsCOMPtr<nsISupports> mStream;
bool mAborted;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileHandle_h

View File

@ -0,0 +1,636 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "FileHandleBase.h"
#include "ActorsChild.h"
#include "BackgroundChildImpl.h"
#include "FileRequestBase.h"
#include "mozilla/Assertions.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/PBackgroundFileHandle.h"
#include "mozilla/dom/UnionConversions.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "MutableFileBase.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
using namespace mozilla::ipc;
FileHandleBase::FileHandleBase(DEBUGONLY(PRThread* aOwningThread,)
FileMode aMode)
: RefCountedThreadObject(DEBUGONLY(aOwningThread))
, mBackgroundActor(nullptr)
, mLocation(0)
, mPendingRequestCount(0)
, mReadyState(INITIAL)
, mMode(aMode)
, mAborted(false)
, mCreating(false)
DEBUGONLY(, mSentFinishOrAbort(false))
DEBUGONLY(, mFiredCompleteOrAbort(false))
{
AssertIsOnOwningThread();
}
FileHandleBase::~FileHandleBase()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mPendingRequestCount);
MOZ_ASSERT(!mCreating);
MOZ_ASSERT(mSentFinishOrAbort);
MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
if (mBackgroundActor) {
mBackgroundActor->SendDeleteMeInternal();
MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
}
}
// static
FileHandleBase*
FileHandleBase::GetCurrent()
{
MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
BackgroundChildImpl::ThreadLocal* threadLocal =
BackgroundChildImpl::GetThreadLocalForCurrentThread();
MOZ_ASSERT(threadLocal);
return threadLocal->mCurrentFileHandle;
}
void
FileHandleBase::SetBackgroundActor(BackgroundFileHandleChild* aActor)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(!mBackgroundActor);
mBackgroundActor = aActor;
}
void
FileHandleBase::StartRequest(FileRequestBase* aFileRequest,
const FileRequestParams& aParams)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFileRequest);
MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
BackgroundFileRequestChild* actor =
new BackgroundFileRequestChild(DEBUGONLY(mBackgroundActor->OwningThread(),)
aFileRequest);
mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
// Balanced in BackgroundFileRequestChild::Recv__delete__().
OnNewRequest();
}
void
FileHandleBase::OnNewRequest()
{
AssertIsOnOwningThread();
if (!mPendingRequestCount) {
MOZ_ASSERT(mReadyState == INITIAL);
mReadyState = LOADING;
}
++mPendingRequestCount;
}
void
FileHandleBase::OnRequestFinished(bool aActorDestroyedNormally)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mPendingRequestCount);
--mPendingRequestCount;
if (!mPendingRequestCount && !MutableFile()->IsInvalidated()) {
mReadyState = FINISHING;
if (aActorDestroyedNormally) {
if (!mAborted) {
SendFinish();
} else {
SendAbort();
}
} else {
// Don't try to send any more messages to the parent if the request actor
// was killed.
#ifdef DEBUG
MOZ_ASSERT(!mSentFinishOrAbort);
mSentFinishOrAbort = true;
#endif
}
}
}
bool
FileHandleBase::IsOpen() const
{
AssertIsOnOwningThread();
// If we haven't started anything then we're open.
if (mReadyState == INITIAL) {
return true;
}
// If we've already started then we need to check to see if we still have the
// mCreating flag set. If we do (i.e. we haven't returned to the event loop
// from the time we were created) then we are open. Otherwise check the
// currently running file handles to see if it's the same. We only allow other
// requests to be made if this file handle is currently running.
if (mReadyState == LOADING && (mCreating || GetCurrent() == this)) {
return true;
}
return false;
}
void
FileHandleBase::Abort()
{
AssertIsOnOwningThread();
if (IsFinishingOrDone()) {
// Already started (and maybe finished) the finish or abort so there is
// nothing to do here.
return;
}
const bool isInvalidated = MutableFile()->IsInvalidated();
bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
#ifdef DEBUG
if (isInvalidated) {
mSentFinishOrAbort = true;
}
#endif
mAborted = true;
mReadyState = DONE;
// Fire the abort event if there are no outstanding requests. Otherwise the
// abort event will be fired when all outstanding requests finish.
if (needToSendAbort) {
SendAbort();
}
}
already_AddRefed<FileRequestBase>
FileHandleBase::Read(uint64_t aSize, bool aHasEncoding,
const nsAString& aEncoding, ErrorResult& aRv)
{
AssertIsOnOwningThread();
// State and argument checking for read
if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
FileRequestReadParams params;
params.offset() = mLocation;
params.size() = aSize;
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
if (aHasEncoding) {
fileRequest->SetEncoding(aEncoding);
}
StartRequest(fileRequest, params);
mLocation += aSize;
return fileRequest.forget();
}
already_AddRefed<FileRequestBase>
FileHandleBase::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
{
AssertIsOnOwningThread();
// State checking for write
if (!CheckStateForWrite(aRv)) {
return nullptr;
}
// Getting location and additional state checking for truncate
uint64_t location;
if (aSize.WasPassed()) {
// Just in case someone calls us from C++
MOZ_ASSERT(aSize.Value() != UINT64_MAX, "Passed wrong size!");
location = aSize.Value();
} else {
if (mLocation == UINT64_MAX) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return nullptr;
}
location = mLocation;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
FileRequestTruncateParams params;
params.offset() = location;
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
StartRequest(fileRequest, params);
if (aSize.WasPassed()) {
mLocation = aSize.Value();
}
return fileRequest.forget();
}
already_AddRefed<FileRequestBase>
FileHandleBase::Flush(ErrorResult& aRv)
{
AssertIsOnOwningThread();
// State checking for write
if (!CheckStateForWrite(aRv)) {
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
FileRequestFlushParams params;
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
StartRequest(fileRequest, params);
return fileRequest.forget();
}
void
FileHandleBase::Abort(ErrorResult& aRv)
{
AssertIsOnOwningThread();
// This method is special enough for not using generic state checking methods.
if (IsFinishingOrDone()) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return;
}
Abort();
}
void
FileHandleBase::HandleCompleteOrAbort(bool aAborted)
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mFiredCompleteOrAbort);
mReadyState = DONE;
DEBUGONLY(mFiredCompleteOrAbort = true;)
}
void
FileHandleBase::OnReturnToEventLoop()
{
AssertIsOnOwningThread();
// We're back at the event loop, no longer newborn.
mCreating = false;
// Maybe finish if there were no requests generated.
if (mReadyState == INITIAL) {
mReadyState = DONE;
SendFinish();
}
}
bool
FileHandleBase::CheckState(ErrorResult& aRv)
{
if (!IsOpen()) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
return false;
}
return true;
}
bool
FileHandleBase::CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv)
{
// Common state checking
if (!CheckState(aRv)) {
return false;
}
// Additional state checking for read
if (mLocation == UINT64_MAX) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return false;
}
// Argument checking for read
if (!aSize) {
aRv.ThrowTypeError(MSG_INVALID_READ_SIZE);
return false;
}
return true;
}
bool
FileHandleBase::CheckStateForWrite(ErrorResult& aRv)
{
// Common state checking
if (!CheckState(aRv)) {
return false;
}
// Additional state checking for write
if (mMode != FileMode::Readwrite) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
return false;
}
return true;
}
bool
FileHandleBase::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv)
{
// State checking for write
if (!CheckStateForWrite(aRv)) {
return false;
}
// Additional state checking for write
if (!aAppend && mLocation == UINT64_MAX) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return false;
}
return true;
}
already_AddRefed<FileRequestBase>
FileHandleBase::WriteOrAppend(
const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
bool aAppend,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
if (aValue.IsString()) {
return WriteOrAppend(aValue.GetAsString(), aAppend, aRv);
}
if (aValue.IsArrayBuffer()) {
return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv);
}
if (aValue.IsArrayBufferView()) {
return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv);
}
MOZ_ASSERT(aValue.IsBlob());
return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv);
}
already_AddRefed<FileRequestBase>
FileHandleBase::WriteOrAppend(const nsAString& aValue,
bool aAppend,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
// State checking for write or append
if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
return nullptr;
}
NS_ConvertUTF16toUTF8 cstr(aValue);
uint64_t dataLength = cstr.Length();;
if (!dataLength) {
return nullptr;
}
FileRequestStringData stringData(cstr);
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
return WriteInternal(stringData, dataLength, aAppend, aRv);
}
already_AddRefed<FileRequestBase>
FileHandleBase::WriteOrAppend(const ArrayBuffer& aValue,
bool aAppend,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
// State checking for write or append
if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
return nullptr;
}
aValue.ComputeLengthAndData();
uint64_t dataLength = aValue.Length();;
if (!dataLength) {
return nullptr;
}
const char* data = reinterpret_cast<const char*>(aValue.Data());
FileRequestStringData stringData;
if (NS_WARN_IF(!stringData.string().Assign(data, aValue.Length(),
fallible_t()))) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
return WriteInternal(stringData, dataLength, aAppend, aRv);
}
already_AddRefed<FileRequestBase>
FileHandleBase::WriteOrAppend(const ArrayBufferView& aValue,
bool aAppend,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
// State checking for write or append
if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
return nullptr;
}
aValue.ComputeLengthAndData();
uint64_t dataLength = aValue.Length();;
if (!dataLength) {
return nullptr;
}
const char* data = reinterpret_cast<const char*>(aValue.Data());
FileRequestStringData stringData;
if (NS_WARN_IF(!stringData.string().Assign(data, aValue.Length(),
fallible_t()))) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
return WriteInternal(stringData, dataLength, aAppend, aRv);
}
already_AddRefed<FileRequestBase>
FileHandleBase::WriteOrAppend(Blob& aValue,
bool aAppend,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
// State checking for write or append
if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
return nullptr;
}
ErrorResult rv;
uint64_t dataLength = aValue.GetSize(rv);
if (NS_WARN_IF(rv.Failed())) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
if (!dataLength) {
return nullptr;
}
PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(backgroundActor);
PBlobChild* blobActor =
BackgroundChild::GetOrCreateActorForBlob(backgroundActor, &aValue);
if (NS_WARN_IF(!blobActor)) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
FileRequestBlobData blobData;
blobData.blobChild() = blobActor;
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
return WriteInternal(blobData, dataLength, aAppend, aRv);
}
already_AddRefed<FileRequestBase>
FileHandleBase::WriteInternal(const FileRequestData& aData,
uint64_t aDataLength,
bool aAppend,
ErrorResult& aRv)
{
AssertIsOnOwningThread();
DebugOnly<ErrorResult> error;
MOZ_ASSERT(CheckStateForWrite(error));
MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
MOZ_ASSERT(aDataLength);
MOZ_ASSERT(CheckWindow());
FileRequestWriteParams params;
params.offset() = aAppend ? UINT64_MAX : mLocation;
params.data() = aData;
params.dataLength() = aDataLength;
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
MOZ_ASSERT(fileRequest);
StartRequest(fileRequest, params);
if (aAppend) {
mLocation = UINT64_MAX;
}
else {
mLocation += aDataLength;
}
return fileRequest.forget();
}
void
FileHandleBase::SendFinish()
{
AssertIsOnOwningThread();
MOZ_ASSERT(!mAborted);
MOZ_ASSERT(IsFinishingOrDone());
MOZ_ASSERT(!mSentFinishOrAbort);
MOZ_ASSERT(!mPendingRequestCount);
MOZ_ASSERT(mBackgroundActor);
mBackgroundActor->SendFinish();
DEBUGONLY(mSentFinishOrAbort = true;)
}
void
FileHandleBase::SendAbort()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mAborted);
MOZ_ASSERT(IsFinishingOrDone());
MOZ_ASSERT(!mSentFinishOrAbort);
MOZ_ASSERT(mBackgroundActor);
mBackgroundActor->SendAbort();
DEBUGONLY(mSentFinishOrAbort = true;)
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,246 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_FileHandle_h
#define mozilla_dom_FileHandle_h
#include "FileHandleCommon.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/FileModeBinding.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/TypedArray.h"
template <class> struct already_AddRefed;
class nsAString;
struct PRThread;
namespace mozilla {
class ErrorResult;
namespace dom {
class BackgroundFileHandleChild;
class Blob;
class FileRequestBase;
class FileRequestData;
class FileRequestParams;
class MutableFileBase;
class StringOrArrayBufferOrArrayBufferViewOrBlob;
/**
* This class provides a base for FileHandle implementations.
*/
class FileHandleBase
: public RefCountedThreadObject
{
public:
enum ReadyState
{
INITIAL = 0,
LOADING,
FINISHING,
DONE
};
private:
BackgroundFileHandleChild* mBackgroundActor;
uint64_t mLocation;
uint32_t mPendingRequestCount;
ReadyState mReadyState;
FileMode mMode;
bool mAborted;
bool mCreating;
DEBUGONLY(bool mSentFinishOrAbort;)
DEBUGONLY(bool mFiredCompleteOrAbort;)
public:
static FileHandleBase*
GetCurrent();
void
SetBackgroundActor(BackgroundFileHandleChild* aActor);
void
ClearBackgroundActor()
{
AssertIsOnOwningThread();
mBackgroundActor = nullptr;
}
void
StartRequest(FileRequestBase* aFileRequest, const FileRequestParams& aParams);
void
OnNewRequest();
void
OnRequestFinished(bool aActorDestroyedNormally);
bool
IsOpen() const;
bool
IsFinishingOrDone() const
{
AssertIsOnOwningThread();
return mReadyState == FINISHING || mReadyState == DONE;
}
bool
IsDone() const
{
AssertIsOnOwningThread();
return mReadyState == DONE;
}
bool
IsAborted() const
{
AssertIsOnOwningThread();
return mAborted;
}
void
SetCreating()
{
mCreating = true;
}
void
Abort();
// Shared WebIDL (IndexedDB FileHandle and FileSystem FileHandle)
FileMode
Mode() const
{
AssertIsOnOwningThread();
return mMode;
}
bool
Active() const
{
AssertIsOnOwningThread();
return IsOpen();
}
Nullable<uint64_t>
GetLocation() const
{
AssertIsOnOwningThread();
if (mLocation == UINT64_MAX) {
return Nullable<uint64_t>();
}
return Nullable<uint64_t>(mLocation);
}
void
SetLocation(const Nullable<uint64_t>& aLocation)
{
AssertIsOnOwningThread();
// Null means the end-of-file.
if (aLocation.IsNull()) {
mLocation = UINT64_MAX;
} else {
mLocation = aLocation.Value();
}
}
already_AddRefed<FileRequestBase>
Read(uint64_t aSize, bool aHasEncoding, const nsAString& aEncoding,
ErrorResult& aRv);
already_AddRefed<FileRequestBase>
Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv);
already_AddRefed<FileRequestBase>
Flush(ErrorResult& aRv);
void
Abort(ErrorResult& aRv);
// Must be overridden in subclasses.
virtual MutableFileBase*
MutableFile() const = 0;
// May be overridden in subclasses.
virtual void
HandleCompleteOrAbort(bool aAborted);
protected:
FileHandleBase(DEBUGONLY(PRThread* aOwningThread,)
FileMode aMode);
~FileHandleBase();
void
OnReturnToEventLoop();
bool
CheckState(ErrorResult& aRv);
bool
CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv);
bool
CheckStateForWrite(ErrorResult& aRv);
bool
CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv);
already_AddRefed<FileRequestBase>
WriteOrAppend(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
bool aAppend,
ErrorResult& aRv);
// Must be overridden in subclasses.
virtual bool
CheckWindow() = 0;
// Must be overridden in subclasses.
virtual already_AddRefed<FileRequestBase>
GenerateFileRequest() = 0;
private:
already_AddRefed<FileRequestBase>
WriteOrAppend(const nsAString& aValue, bool aAppend, ErrorResult& aRv);
already_AddRefed<FileRequestBase>
WriteOrAppend(const ArrayBuffer& aValue, bool aAppend, ErrorResult& aRv);
already_AddRefed<FileRequestBase>
WriteOrAppend(const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv);
already_AddRefed<FileRequestBase>
WriteOrAppend(Blob& aValue, bool aAppend, ErrorResult& aRv);
already_AddRefed<FileRequestBase>
WriteInternal(const FileRequestData& aData, uint64_t aDataLength,
bool aAppend, ErrorResult& aRv);
void
SendFinish();
void
SendAbort();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileHandle_h

View File

@ -0,0 +1,34 @@
/* -*- 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 "FileHandleCommon.h"
#include "mozilla/Assertions.h"
#include "prthread.h"
namespace mozilla {
namespace dom {
#ifdef DEBUG
void
ThreadObject::AssertIsOnOwningThread() const
{
MOZ_ASSERT(mOwningThread);
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
}
PRThread*
ThreadObject::OwningThread() const
{
MOZ_ASSERT(mOwningThread);
return mOwningThread;
}
#endif // DEBUG
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,73 @@
/* 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_FileHandleCommon_h
#define mozilla_dom_FileHandleCommon_h
#include "nscore.h"
#ifdef DEBUG
#define DEBUGONLY(...) __VA_ARGS__
#else
#define DEBUGONLY(...) /* nothing */
#endif
struct PRThread;
namespace mozilla {
namespace dom {
class RefCountedObject
{
public:
NS_IMETHOD_(MozExternalRefCountType)
AddRef() = 0;
NS_IMETHOD_(MozExternalRefCountType)
Release() = 0;
protected:
virtual ~RefCountedObject()
{ }
};
class ThreadObject
{
DEBUGONLY(PRThread* mOwningThread;)
public:
explicit ThreadObject(DEBUGONLY(PRThread* aOwningThread))
DEBUGONLY(: mOwningThread(aOwningThread))
{ }
virtual ~ThreadObject()
{ }
#ifdef DEBUG
void
AssertIsOnOwningThread() const;
PRThread*
OwningThread() const;
#else
void
AssertIsOnOwningThread() const
{ }
#endif
};
class RefCountedThreadObject
: public RefCountedObject
, public ThreadObject
{
public:
explicit RefCountedThreadObject(DEBUGONLY(PRThread* aOwningThread))
: ThreadObject(DEBUGONLY(aOwningThread))
{ }
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileHandleCommon_h

View File

@ -4,23 +4,21 @@
* 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 "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#ifndef mozilla_dom_FileHandleStorage_h
#define mozilla_dom_FileHandleStorage_h
namespace mozilla {
namespace dom {
FileRequestBase::FileRequestBase()
enum FileHandleStorage
{
MOZ_ASSERT(NS_IsMainThread());
}
FileRequestBase::~FileRequestBase()
{
MOZ_ASSERT(NS_IsMainThread());
}
FILE_HANDLE_STORAGE_IDB = 0,
// A placeholder for bug 997471
//FILE_HANDLE_STORAGE_FS
FILE_HANDLE_STORAGE_MAX
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileHandleStorage_h

View File

@ -1,235 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "FileHandle.h"
#include "FileRequest.h"
#include "FileService.h"
#include "js/Value.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "MutableFile.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIRequest.h"
namespace mozilla {
namespace dom {
namespace {
FileHandleBase* gCurrentFileHandle = nullptr;
} // namespace
FileHelper::FileHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest)
: mMutableFile(aFileHandle->MutableFile()),
mFileHandle(aFileHandle),
mFileRequest(aFileRequest),
mResultCode(NS_OK),
mFinished(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
FileHelper::~FileHelper()
{
MOZ_ASSERT(!mMutableFile && !mFileHandle && !mFileRequest && !mListener &&
!mRequest, "Should have cleared this!");
}
NS_IMPL_ISUPPORTS(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(mFileHandle, this);
NS_ENSURE_SUCCESS(rv, rv);
if (mFileHandle) {
mFileHandle->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 (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) {
rv = mFileHandle->CreateParallelStream(getter_AddRefs(stream));
}
else {
rv = mFileHandle->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)) {
if (aStatus == NS_ERROR_FILE_NO_DEVICE_SPACE) {
mResultCode = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR;
}
else {
mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
}
Finish();
return NS_OK;
}
void
FileHelper::OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mFileHandle->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
FileHandleBase*
FileHelper::GetCurrentFileHandle()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
return gCurrentFileHandle;
}
nsresult
FileHelper::GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
aVal.setUndefined();
return NS_OK;
}
void
FileHelper::ReleaseObjects()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mMutableFile = nullptr;
mFileHandle = nullptr;
mFileRequest = nullptr;
mListener = nullptr;
mRequest = nullptr;
}
void
FileHelper::Finish()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mFinished) {
return;
}
mFinished = true;
if (mFileHandle->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;
}
FileHandleBase* oldFileHandle = gCurrentFileHandle;
gCurrentFileHandle = mFileHandle;
if (mFileRequest) {
nsresult rv = mFileRequest->NotifyHelperCompleted(this);
if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
mResultCode = rv;
}
}
MOZ_ASSERT(gCurrentFileHandle == mFileHandle, "Should be unchanged!");
gCurrentFileHandle = oldFileHandle;
mFileHandle->OnRequestFinished();
mListener->OnFileHelperComplete(this);
ReleaseObjects();
MOZ_ASSERT(!(mMutableFile || mFileHandle || mFileRequest || mListener ||
mRequest), "Subclass didn't call FileHelper::ReleaseObjects!");
}
void
FileHelper::OnStreamClose()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Finish();
}
void
FileHelper::OnStreamDestroy()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Finish();
}
} // namespace dom
} // namespace mozilla

View File

@ -1,106 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_FileHelper_h
#define mozilla_dom_FileHelper_h
#include "js/TypeDecls.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIRequestObserver.h"
namespace mozilla {
namespace dom {
class FileHandleBase;
class FileHelper;
class FileRequestBase;
class FileOutputStreamWrapper;
class MutableFileBase;
class FileHelperListener
{
public:
NS_IMETHOD_(MozExternalRefCountType)
AddRef() = 0;
NS_IMETHOD_(MozExternalRefCountType)
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 FileOutputStreamWrapper;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
nsresult
ResultCode() const
{
return mResultCode;
}
nsresult
Enqueue();
nsresult
AsyncRun(FileHelperListener* aListener);
virtual nsresult
GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal);
void
OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax);
void
OnStreamClose();
void
OnStreamDestroy();
static FileHandleBase*
GetCurrentFileHandle();
protected:
FileHelper(FileHandleBase* aFileHandle, FileRequestBase* aRequest);
virtual ~FileHelper();
virtual nsresult
DoAsyncRun(nsISupports* aStream) = 0;
virtual void
ReleaseObjects();
void
Finish();
nsRefPtr<MutableFileBase> mMutableFile;
nsRefPtr<FileHandleBase> mFileHandle;
nsRefPtr<FileRequestBase> mFileRequest;
nsRefPtr<FileHelperListener> mListener;
nsCOMPtr<nsIRequest> mRequest;
private:
nsresult mResultCode;
bool mFinished;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileHelper_h

View File

@ -1,44 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_FileRequest_h
#define mozilla_dom_FileRequest_h
#include "nscore.h"
namespace mozilla {
namespace dom {
class FileHelper;
/**
* This class provides a base for FileRequest implementations.
*/
class FileRequestBase
{
public:
NS_IMETHOD_(MozExternalRefCountType)
AddRef() = 0;
NS_IMETHOD_(MozExternalRefCountType)
Release() = 0;
virtual void
OnProgress(uint64_t aProgress, uint64_t aProgressMax) = 0;
virtual nsresult
NotifyHelperCompleted(FileHelper* aFileHelper) = 0;
protected:
FileRequestBase();
virtual ~FileRequestBase();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileRequest_h

View File

@ -0,0 +1,93 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_FileRequest_h
#define mozilla_dom_FileRequest_h
#include "FileHandleCommon.h"
#include "js/TypeDecls.h"
#include "nsString.h"
struct PRThread;
namespace mozilla {
namespace dom {
class FileHandleBase;
/**
* This class provides a base for FileRequest implementations.
*/
class FileRequestBase
: public RefCountedThreadObject
{
nsString mEncoding;
bool mHasEncoding;
public:
class ResultCallback;
void
SetEncoding(const nsAString& aEncoding)
{
mEncoding = aEncoding;
mHasEncoding = true;
}
const nsAString&
GetEncoding() const
{
return mEncoding;
}
bool
HasEncoding() const
{
return mHasEncoding;
}
virtual FileHandleBase*
FileHandle() const = 0;
virtual void
OnProgress(uint64_t aProgress, uint64_t aProgressMax) = 0;
virtual void
SetResultCallback(ResultCallback* aCallback) = 0;
virtual void
SetError(nsresult aError) = 0;
protected:
FileRequestBase(DEBUGONLY(PRThread* aOwningThread))
: RefCountedThreadObject(DEBUGONLY(aOwningThread))
, mHasEncoding(false)
{
AssertIsOnOwningThread();
}
virtual ~FileRequestBase()
{
AssertIsOnOwningThread();
}
};
class NS_NO_VTABLE FileRequestBase::ResultCallback
{
public:
virtual nsresult
GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) = 0;
protected:
ResultCallback()
{ }
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileRequest_h

View File

@ -1,479 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "FileHandle.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "MutableFile.h"
#include "nsError.h"
#include "nsIEventTarget.h"
#include "nsNetCID.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadPool.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
namespace {
const uint32_t kThreadLimit = 5;
const uint32_t kIdleThreadLimit = 1;
const uint32_t kIdleThreadTimeoutMs = 30000;
StaticAutoPtr<FileService> gInstance;
bool gShutdown = false;
} // namespace
FileService::FileService()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!gInstance);
}
FileService::~FileService()
{
MOZ_ASSERT(NS_IsMainThread());
}
nsresult
FileService::Init()
{
mThreadPool = new nsThreadPool();
nsresult rv = mThreadPool->SetName(NS_LITERAL_CSTRING("FileHandleTrans"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mThreadPool->SetThreadLimit(kThreadLimit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
FileService::Cleanup()
{
MOZ_ASSERT(NS_IsMainThread());
nsIThread* thread = NS_GetCurrentThread();
MOZ_ASSERT(thread);
nsresult rv = mThreadPool->Shutdown();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Make sure the service is still accessible while any generated callbacks
// are processed.
rv = NS_ProcessPendingEvents(thread);
NS_ENSURE_SUCCESS(rv, rv);
if (!mCompleteCallbacks.IsEmpty()) {
// Run all callbacks manually now.
for (uint32_t 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 nullptr;
}
if (!gInstance) {
nsAutoPtr<FileService> service(new FileService());
nsresult rv = service->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
gInstance = service.forget();
}
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 = nullptr;
}
}
// static
bool
FileService::IsShuttingDown()
{
return gShutdown;
}
nsresult
FileService::Enqueue(FileHandleBase* aFileHandle, FileHelper* aFileHelper)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aFileHandle, "Null pointer!");
MutableFileBase* mutableFile = aFileHandle->MutableFile();
if (mutableFile->IsInvalid()) {
return NS_ERROR_NOT_AVAILABLE;
}
const nsACString& storageId = mutableFile->mStorageId;
const nsAString& fileName = mutableFile->mFileName;
bool modeIsWrite = aFileHandle->mMode == FileMode::Readwrite;
StorageInfo* storageInfo;
if (!mStorageInfos.Get(storageId, &storageInfo)) {
nsAutoPtr<StorageInfo> newStorageInfo(new StorageInfo());
mStorageInfos.Put(storageId, newStorageInfo);
storageInfo = newStorageInfo.forget();
}
FileHandleQueue* existingFileHandleQueue =
storageInfo->GetFileHandleQueue(aFileHandle);
if (existingFileHandleQueue) {
existingFileHandleQueue->Enqueue(aFileHelper);
return NS_OK;
}
bool lockedForReading = storageInfo->IsFileLockedForReading(fileName);
bool lockedForWriting = storageInfo->IsFileLockedForWriting(fileName);
if (modeIsWrite) {
if (!lockedForWriting) {
storageInfo->LockFileForWriting(fileName);
}
}
else {
if (!lockedForReading) {
storageInfo->LockFileForReading(fileName);
}
}
if (lockedForWriting || (lockedForReading && modeIsWrite)) {
storageInfo->CreateDelayedEnqueueInfo(aFileHandle, aFileHelper);
}
else {
FileHandleQueue* fileHandleQueue =
storageInfo->CreateFileHandleQueue(aFileHandle);
if (aFileHelper) {
// Enqueue() will queue the file helper if there's already something
// running. That can't fail, so no need to eventually remove
// storageInfo 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 storageInfo from
// the hash table. Code behind AsyncRun() will take care of it. The last
// item in the code path is NotifyFileHandleCompleted() which removes
// storageInfo from the hash table if there are no file handles for
// the file storage.
nsresult rv = fileHandleQueue->Enqueue(aFileHelper);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
void
FileService::NotifyFileHandleCompleted(FileHandleBase* aFileHandle)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aFileHandle, "Null pointer!");
MutableFileBase* mutableFile = aFileHandle->MutableFile();
const nsACString& storageId = mutableFile->mStorageId;
StorageInfo* storageInfo;
if (!mStorageInfos.Get(storageId, &storageInfo)) {
NS_ERROR("We don't know anyting about this file handle?!");
return;
}
storageInfo->RemoveFileHandleQueue(aFileHandle);
if (!storageInfo->HasRunningFileHandles()) {
mStorageInfos.Remove(storageId);
// See if we need to fire any complete callbacks.
uint32_t index = 0;
while (index < mCompleteCallbacks.Length()) {
if (MaybeFireCallback(mCompleteCallbacks[index])) {
mCompleteCallbacks.RemoveElementAt(index);
}
else {
index++;
}
}
}
}
void
FileService::WaitForStoragesToComplete(nsTArray<nsCString>& aStorageIds,
nsIRunnable* aCallback)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aStorageIds.IsEmpty());
MOZ_ASSERT(aCallback);
StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
callback->mCallback = aCallback;
callback->mStorageIds.SwapElements(aStorageIds);
if (MaybeFireCallback(*callback)) {
mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
}
}
nsIEventTarget*
FileService::ThreadPoolTarget() const
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mThreadPool);
return mThreadPool;
}
bool
FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
for (uint32_t index = 0; index < aCallback.mStorageIds.Length(); index++) {
if (mStorageInfos.Get(aCallback.mStorageIds[index], nullptr)) {
return false;
}
}
aCallback.mCallback->Run();
return true;
}
FileService::FileHandleQueue::FileHandleQueue(FileHandleBase* aFileHandle)
: mFileHandle(aFileHandle)
{
MOZ_ASSERT(aFileHandle, "Null pointer!");
}
FileService::FileHandleQueue::~FileHandleQueue()
{
}
NS_IMPL_ADDREF(FileService::FileHandleQueue)
NS_IMPL_RELEASE(FileService::FileHandleQueue)
nsresult
FileService::FileHandleQueue::Enqueue(FileHelper* aFileHelper)
{
mQueue.AppendElement(aFileHelper);
nsresult rv;
if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) {
rv = aFileHelper->AsyncRun(this);
}
else {
rv = ProcessQueue();
}
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
FileService::
FileHandleQueue::OnFileHelperComplete(FileHelper* aFileHelper)
{
if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) {
int32_t 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 = nullptr;
nsresult rv = ProcessQueue();
if (NS_FAILED(rv)) {
return;
}
}
}
nsresult
FileService::FileHandleQueue::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::DelayedEnqueueInfo::DelayedEnqueueInfo()
{
}
FileService::DelayedEnqueueInfo::~DelayedEnqueueInfo()
{
}
FileService::FileHandleQueue*
FileService::StorageInfo::CreateFileHandleQueue(FileHandleBase* aFileHandle)
{
nsRefPtr<FileHandleQueue>* fileHandleQueue =
mFileHandleQueues.AppendElement();
*fileHandleQueue = new FileHandleQueue(aFileHandle);
return fileHandleQueue->get();
}
FileService::FileHandleQueue*
FileService::StorageInfo::GetFileHandleQueue(FileHandleBase* aFileHandle)
{
uint32_t count = mFileHandleQueues.Length();
for (uint32_t index = 0; index < count; index++) {
nsRefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index];
if (fileHandleQueue->mFileHandle == aFileHandle) {
return fileHandleQueue;
}
}
return nullptr;
}
void
FileService::StorageInfo::RemoveFileHandleQueue(FileHandleBase* aFileHandle)
{
for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) {
if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) {
MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHelper, "Should be null!");
mDelayedEnqueueInfos.RemoveElementAt(index);
return;
}
}
uint32_t fileHandleCount = mFileHandleQueues.Length();
// We can't just remove entries from lock hash tables, we have to rebuild
// them instead. Multiple FileHandle objects may lock the same file
// (one entry can represent multiple locks).
mFilesReading.Clear();
mFilesWriting.Clear();
for (uint32_t index = 0, count = fileHandleCount; index < count; index++) {
FileHandleBase* fileHandle = mFileHandleQueues[index]->mFileHandle;
if (fileHandle == aFileHandle) {
MOZ_ASSERT(count == fileHandleCount, "More than one match?!");
mFileHandleQueues.RemoveElementAt(index);
index--;
count--;
continue;
}
const nsAString& fileName = fileHandle->MutableFile()->mFileName;
if (fileHandle->mMode == FileMode::Readwrite) {
if (!IsFileLockedForWriting(fileName)) {
LockFileForWriting(fileName);
}
}
else {
if (!IsFileLockedForReading(fileName)) {
LockFileForReading(fileName);
}
}
}
MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1,
"Didn't find the file handle we were looking for!");
nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos;
delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos);
for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) {
DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index];
if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mFileHandle,
delayedEnqueueInfo.mFileHelper))) {
NS_WARNING("Enqueue failed!");
}
}
}
FileService::DelayedEnqueueInfo*
FileService::StorageInfo::CreateDelayedEnqueueInfo(FileHandleBase* aFileHandle,
FileHelper* aFileHelper)
{
DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement();
info->mFileHandle = aFileHandle;
info->mFileHelper = aFileHelper;
return info;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,193 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_FileService_h
#define mozilla_dom_FileService_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/FileHelper.h"
#include "mozilla/StaticPtr.h"
#include "nsClassHashtable.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
class nsAString;
class nsIEventTarget;
class nsIRunnable;
class nsThreadPool;
namespace mozilla {
namespace dom {
class FileHandleBase;
class FileService final
{
friend class nsAutoPtr<FileService>;
friend class StaticAutoPtr<FileService>;
public:
// 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(FileHandleBase* aFileHandle, FileHelper* aFileHelper);
void
NotifyFileHandleCompleted(FileHandleBase* aFileHandle);
void
WaitForStoragesToComplete(nsTArray<nsCString>& aStorageIds,
nsIRunnable* aCallback);
nsIEventTarget*
ThreadPoolTarget() const;
private:
class FileHandleQueue final : public FileHelperListener
{
friend class FileService;
public:
NS_IMETHOD_(MozExternalRefCountType)
AddRef() override;
NS_IMETHOD_(MozExternalRefCountType)
Release() override;
inline nsresult
Enqueue(FileHelper* aFileHelper);
virtual void
OnFileHelperComplete(FileHelper* aFileHelper) override;
private:
inline
explicit FileHandleQueue(FileHandleBase* aFileHandle);
~FileHandleQueue();
nsresult
ProcessQueue();
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
nsRefPtr<FileHandleBase> mFileHandle;
nsTArray<nsRefPtr<FileHelper> > mQueue;
nsRefPtr<FileHelper> mCurrentHelper;
};
struct DelayedEnqueueInfo
{
DelayedEnqueueInfo();
~DelayedEnqueueInfo();
nsRefPtr<FileHandleBase> mFileHandle;
nsRefPtr<FileHelper> mFileHelper;
};
class StorageInfo
{
friend class FileService;
public:
inline FileHandleQueue*
CreateFileHandleQueue(FileHandleBase* aFileHandle);
inline FileHandleQueue*
GetFileHandleQueue(FileHandleBase* aFileHandle);
void
RemoveFileHandleQueue(FileHandleBase* aFileHandle);
bool
HasRunningFileHandles()
{
return !mFileHandleQueues.IsEmpty();
}
inline DelayedEnqueueInfo*
CreateDelayedEnqueueInfo(FileHandleBase* aFileHandle,
FileHelper* aFileHelper);
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:
StorageInfo()
{
}
nsTArray<nsRefPtr<FileHandleQueue>> mFileHandleQueues;
nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos;
nsTHashtable<nsStringHashKey> mFilesReading;
nsTHashtable<nsStringHashKey> mFilesWriting;
};
struct StoragesCompleteCallback
{
nsTArray<nsCString> mStorageIds;
nsCOMPtr<nsIRunnable> mCallback;
};
FileService();
~FileService();
nsresult
Init();
nsresult
Cleanup();
bool
MaybeFireCallback(StoragesCompleteCallback& aCallback);
nsRefPtr<nsThreadPool> mThreadPool;
nsClassHashtable<nsCStringHashKey, StorageInfo> mStorageInfos;
nsTArray<StoragesCompleteCallback> mCompleteCallbacks;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileService_h

View File

@ -1,415 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "FileHelper.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/ipc/InputStreamParams.h"
#include "MutableFile.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIRunnable.h"
#include "nsISeekableStream.h"
#include "nsThreadUtils.h"
#include "nsQueryObject.h"
#ifdef DEBUG
#include "nsXULAppAPI.h"
#endif
namespace mozilla {
namespace dom {
namespace {
class ProgressRunnable final : public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
ProgressRunnable(FileHelper* aFileHelper,
uint64_t aProgress,
uint64_t aProgressMax)
: mFileHelper(aFileHelper),
mProgress(aProgress),
mProgressMax(aProgressMax)
{
}
private:
~ProgressRunnable() {}
nsRefPtr<FileHelper> mFileHelper;
uint64_t mProgress;
uint64_t mProgressMax;
};
class CloseRunnable final : public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
explicit CloseRunnable(FileHelper* aFileHelper)
: mFileHelper(aFileHelper)
{ }
private:
~CloseRunnable() {}
nsRefPtr<FileHelper> mFileHelper;
};
class DestroyRunnable final : public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
explicit DestroyRunnable(FileHelper* aFileHelper)
: mFileHelper(aFileHelper)
{ }
private:
~DestroyRunnable() {}
nsRefPtr<FileHelper> mFileHelper;
};
} // namespace
FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
uint64_t aOffset,
uint64_t aLimit,
uint32_t 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);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
}
}
NS_IMPL_ISUPPORTS0(FileStreamWrapper)
FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
uint64_t aOffset,
uint64_t aLimit,
uint32_t aFlags)
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
{
mInputStream = do_QueryInterface(mFileStream);
NS_ASSERTION(mInputStream, "This should always succeed!");
}
NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper,
FileStreamWrapper,
nsIInputStream,
nsIIPCSerializableInputStream)
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_WARNING("Failed to dispatch to the main thread!");
}
}
mOffset = 0;
mLimit = 0;
return NS_OK;
}
NS_IMETHODIMP
FileInputStreamWrapper::Available(uint64_t* _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, uint32_t aCount, uint32_t* _retval)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (mFirstTime) {
mFirstTime = false;
if (mOffset != UINT64_MAX) {
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
if (seekable) {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
}
mOffset = 0;
}
uint64_t 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);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
return NS_OK;
}
NS_IMETHODIMP
FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileInputStreamWrapper::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}
void
FileInputStreamWrapper::Serialize(InputStreamParams& aParams,
FileDescriptorArray& /* aFDs */)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIInputStream> thisStream = do_QueryObject(this);
aParams = mozilla::ipc::SameProcessInputStreamParams(
reinterpret_cast<intptr_t>(thisStream.forget().take()));
}
bool
FileInputStreamWrapper::Deserialize(const InputStreamParams& /* aParams */,
const FileDescriptorArray& /* aFDs */)
{
MOZ_CRASH("Should never get here!");
}
FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
uint64_t aOffset,
uint64_t aLimit,
uint32_t aFlags)
: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags)
{
mOutputStream = do_QueryInterface(mFileStream);
NS_ASSERTION(mOutputStream, "This should always succeed!");
}
NS_IMPL_ISUPPORTS_INHERITED(FileOutputStreamWrapper,
FileStreamWrapper,
nsIOutputStream)
NS_IMETHODIMP
FileOutputStreamWrapper::Close()
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv = NS_OK;
if (mFlags & NOTIFY_CLOSE) {
nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper);
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
NS_WARNING("Failed to dispatch to the main thread!");
}
}
mOffset = 0;
mLimit = 0;
return rv;
}
NS_IMETHODIMP
FileOutputStreamWrapper::Write(const char* aBuf, uint32_t aCount,
uint32_t* _retval)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (mFirstTime) {
mFirstTime = false;
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream);
if (seekable) {
if (mOffset == UINT64_MAX) {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
}
else {
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
}
NS_ENSURE_SUCCESS(rv, rv);
}
mOffset = 0;
}
uint64_t 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);
}
return NS_OK;
}
NS_IMETHODIMP
FileOutputStreamWrapper::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream,
uint32_t aCount, uint32_t* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader,
void* aClosure, uint32_t aCount,
uint32_t* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileOutputStreamWrapper::IsNonBlocking(bool* _retval)
{
*_retval = false;
return NS_OK;
}
NS_IMPL_ISUPPORTS(ProgressRunnable, nsIRunnable)
NS_IMETHODIMP
ProgressRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamProgress(mProgress, mProgressMax);
mFileHelper = nullptr;
return NS_OK;
}
NS_IMPL_ISUPPORTS(CloseRunnable, nsIRunnable)
NS_IMETHODIMP
CloseRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamClose();
mFileHelper = nullptr;
return NS_OK;
}
NS_IMPL_ISUPPORTS(DestroyRunnable, nsIRunnable)
NS_IMETHODIMP
DestroyRunnable::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mFileHelper->OnStreamDestroy();
mFileHelper = nullptr;
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,102 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_FileStreamWrappers_h
#define mozilla_dom_FileStreamWrappers_h
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIIPCSerializableInputStream.h"
namespace mozilla {
namespace ipc {
class InputStreamParams;
} // namespace ipc
namespace dom {
class FileHelper;
class FileStreamWrapper : public nsISupports
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
FileStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
uint64_t aOffset,
uint64_t aLimit,
uint32_t aFlags);
enum {
NOTIFY_PROGRESS = 1 << 0,
NOTIFY_CLOSE = 1 << 1,
NOTIFY_DESTROY = 1 << 2
};
protected:
virtual ~FileStreamWrapper();
nsCOMPtr<nsISupports> mFileStream;
nsRefPtr<FileHelper> mFileHelper;
uint64_t mOffset;
uint64_t mLimit;
uint32_t mFlags;
bool mFirstTime;
};
class FileInputStreamWrapper : public FileStreamWrapper,
public nsIInputStream,
public nsIIPCSerializableInputStream
{
typedef mozilla::ipc::InputStreamParams InputStreamParams;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
FileInputStreamWrapper(nsISupports* aFileStream,
FileHelper* aFileHelper,
uint64_t aOffset,
uint64_t aLimit,
uint32_t 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,
uint64_t aOffset,
uint64_t aLimit,
uint32_t aFlags);
protected:
virtual ~FileOutputStreamWrapper()
{ }
private:
nsCOMPtr<nsIOutputStream> mOutputStream;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileStreamWrappers_h

View File

@ -1,98 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsStreamUtils.h"
namespace mozilla {
namespace dom {
// static
already_AddRefed<MemoryOutputStream>
MemoryOutputStream::Create(uint64_t aSize)
{
NS_ASSERTION(aSize, "Passed zero size!");
NS_ENSURE_TRUE(aSize <= UINT32_MAX, nullptr);
nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream();
char* dummy;
uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible);
NS_ENSURE_TRUE(length == aSize, nullptr);
return stream.forget();
}
NS_IMPL_ISUPPORTS(MemoryOutputStream, nsIOutputStream)
NS_IMETHODIMP
MemoryOutputStream::Close()
{
mData.Truncate(mOffset);
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval)
{
return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval);
}
NS_IMETHODIMP
MemoryOutputStream::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
uint32_t* _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
uint32_t aCount, uint32_t* _retval)
{
NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!");
uint32_t 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;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_MemoryStreams_h
#define mozilla_dom_MemoryStreams_h
#include "nsIOutputStream.h"
#include "nsString.h"
template <class> struct already_AddRefed;
namespace mozilla {
namespace dom {
class MemoryOutputStream : public nsIOutputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOUTPUTSTREAM
static already_AddRefed<MemoryOutputStream>
Create(uint64_t aSize);
const nsCString&
Data() const
{
return mData;
}
private:
MemoryOutputStream()
: mOffset(0)
{ }
virtual ~MemoryOutputStream()
{ }
nsCString mData;
uint64_t mOffset;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MemoryStreams_h

View File

@ -1,107 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "FileHandle.h"
#include "js/Value.h"
#include "js/RootingAPI.h"
#include "jsapi.h"
#include "js/Date.h"
#include "mozilla/dom/FileModeBinding.h"
#include "nsDebug.h"
#include "nsIFileStreams.h"
#include "nsIOutputStream.h"
namespace mozilla {
namespace dom {
nsresult
MetadataHelper::DoAsyncRun(nsISupports* aStream)
{
bool readWrite = mFileHandle->mMode == FileMode::Readwrite;
nsRefPtr<AsyncMetadataGetter> getter =
new AsyncMetadataGetter(aStream, mParams, readWrite);
nsresult rv = getter->AsyncWork(this, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
MetadataHelper::GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal)
{
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
if (mParams->SizeRequested()) {
JS::Rooted<JS::Value> val(aCx, JS_NumberValue(mParams->Size()));
if (!JS_DefineProperty(aCx, obj, "size", val, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
if (mParams->LastModifiedRequested()) {
double msec = mParams->LastModified();
JSObject *date = JS::NewDateObject(aCx, JS::TimeClip(msec));
NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
JS::Rooted<JS::Value> dateRoot(aCx, JS::ObjectValue(*date));
if (!JS_DefineProperty(aCx, obj, "lastModified", dateRoot,
JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
aVal.setObject(*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()) {
int64_t size;
rv = metadata->GetSize(&size);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(size >= 0, NS_ERROR_FAILURE);
mParams->mSize = uint64_t(size);
}
if (mParams->LastModifiedRequested()) {
int64_t lastModified;
rv = metadata->GetLastModified(&lastModified);
NS_ENSURE_SUCCESS(rv, rv);
mParams->mLastModified = lastModified;
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,118 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_MetadataHelper_h
#define mozilla_dom_MetadataHelper_h
#include "FileHelper.h"
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/AsyncHelper.h"
#include "nsAutoPtr.h"
namespace mozilla {
namespace dom {
class MetadataHelper;
class MetadataParameters final
{
friend class MetadataHelper;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataParameters)
MetadataParameters(bool aSizeRequested, bool aLastModifiedRequested)
: mSizeRequested(aSizeRequested)
, mLastModifiedRequested(aLastModifiedRequested)
{
}
bool
IsConfigured() const
{
return mSizeRequested || mLastModifiedRequested;
}
bool
SizeRequested() const
{
return mSizeRequested;
}
bool
LastModifiedRequested() const
{
return mLastModifiedRequested;
}
uint64_t
Size() const
{
return mSize;
}
int64_t
LastModified() const
{
return mLastModified;
}
private:
// Private destructor, to discourage deletion outside of Release():
~MetadataParameters()
{
}
uint64_t mSize;
int64_t mLastModified;
bool mSizeRequested;
bool mLastModifiedRequested;
};
class MetadataHelper : public FileHelper
{
public:
MetadataHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest,
MetadataParameters* aParams)
: FileHelper(aFileHandle, aFileRequest),
mParams(aParams)
{ }
nsresult
DoAsyncRun(nsISupports* aStream) override;
nsresult
GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal) override;
protected:
class AsyncMetadataGetter : public AsyncHelper
{
public:
AsyncMetadataGetter(nsISupports* aStream, MetadataParameters* aParams,
bool aReadWrite)
: AsyncHelper(aStream),
mParams(aParams), mReadWrite(aReadWrite)
{ }
protected:
nsresult
DoStreamWork(nsISupports* aStream) override;
private:
nsRefPtr<MetadataParameters> mParams;
bool mReadWrite;
};
nsRefPtr<MetadataParameters> mParams;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MetadataHelper_h

View File

@ -1,52 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "MutableFile.h"
#include "nsDebug.h"
#include "nsIFile.h"
#include "nsIFileStreams.h"
#include "nsIInputStream.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace dom {
MutableFileBase::MutableFileBase()
{
}
MutableFileBase::~MutableFileBase()
{
}
// virtual
already_AddRefed<nsISupports>
MutableFileBase::CreateStream(bool aReadOnly)
{
nsresult rv;
if (aReadOnly) {
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1,
nsIFileInputStream::DEFER_OPEN);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return stream.forget();
}
nsCOMPtr<nsIFileStream> stream;
rv = NS_NewLocalFileStream(getter_AddRefs(stream), mFile, -1, -1,
nsIFileStream::DEFER_OPEN);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return stream.forget();
}
} // namespace dom
} // namespace mozilla

View File

@ -1,66 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_MutableFile_h
#define mozilla_dom_MutableFile_h
#include "nsCOMPtr.h"
#include "nsString.h"
class nsIFile;
class nsIOfflineStorage;
namespace mozilla {
namespace dom {
class FileService;
/**
* This class provides a base for MutableFile implementations.
* The subclasses can override implementation of IsInvalid and CreateStream.
* (for example IDBMutableFile provides IndexedDB specific implementation).
*/
class MutableFileBase
{
friend class FileService;
public:
NS_IMETHOD_(MozExternalRefCountType)
AddRef() = 0;
NS_IMETHOD_(MozExternalRefCountType)
Release() = 0;
virtual bool
IsInvalid()
{
return false;
}
// A temporary method that will be removed along with nsIOfflineStorage
// interface.
virtual nsIOfflineStorage*
Storage() = 0;
virtual already_AddRefed<nsISupports>
CreateStream(bool aReadOnly);
protected:
MutableFileBase();
virtual ~MutableFileBase();
nsCOMPtr<nsIFile> mFile;
nsCString mStorageId;
nsString mFileName;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MutableFile_h

View File

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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 "MutableFileBase.h"
#include "ActorsChild.h"
#include "mozilla/Assertions.h"
#include "prthread.h"
namespace mozilla {
namespace dom {
MutableFileBase::MutableFileBase(DEBUGONLY(PRThread* aOwningThread,)
BackgroundMutableFileChildBase* aActor)
: RefCountedThreadObject(DEBUGONLY(aOwningThread))
, mBackgroundActor(aActor)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
}
MutableFileBase::~MutableFileBase()
{
AssertIsOnOwningThread();
if (mBackgroundActor) {
mBackgroundActor->SendDeleteMeInternal();
MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
}
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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_MutableFile_h
#define mozilla_dom_MutableFile_h
#include "mozilla/dom/FileHandleCommon.h"
#include "nscore.h"
template <class> struct already_AddRefed;
class nsISupports;
class nsString;
struct PRThread;
namespace mozilla {
namespace dom {
class BackgroundMutableFileChildBase;
class BlobImpl;
class File;
class FileHandleBase;
/**
* This class provides a base for MutableFile implementations.
* (for example IDBMutableFile provides IndexedDB specific implementation).
*/
class MutableFileBase
: public RefCountedThreadObject
{
protected:
BackgroundMutableFileChildBase* mBackgroundActor;
public:
BackgroundMutableFileChildBase*
GetBackgroundActor() const
{
AssertIsOnOwningThread();
return mBackgroundActor;
}
void
ClearBackgroundActor()
{
AssertIsOnOwningThread();
mBackgroundActor = nullptr;
}
virtual const nsString&
Name() const = 0;
virtual const nsString&
Type() const = 0;
virtual bool
IsInvalidated() = 0;
virtual already_AddRefed<File>
CreateFileFor(BlobImpl* aBlobImpl,
FileHandleBase* aFileHandle) = 0;
protected:
MutableFileBase(DEBUGONLY(PRThread* aOwningThread,)
BackgroundMutableFileChildBase* aActor);
virtual ~MutableFileBase();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MutableFile_h

View File

@ -0,0 +1,91 @@
/* 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 protocol PBackgroundFileRequest;
include protocol PBackgroundMutableFile;
include protocol PBlob;
namespace mozilla {
namespace dom {
struct FileRequestGetMetadataParams
{
bool size;
bool lastModified;
};
struct FileRequestReadParams
{
uint64_t offset;
uint64_t size;
};
struct FileRequestStringData
{
nsCString string;
};
struct FileRequestBlobData
{
PBlob blob;
};
union FileRequestData
{
FileRequestStringData;
FileRequestBlobData;
};
struct FileRequestWriteParams
{
uint64_t offset;
FileRequestData data;
uint64_t dataLength;
};
struct FileRequestTruncateParams
{
uint64_t offset;
};
struct FileRequestFlushParams
{
};
struct FileRequestGetFileParams
{
};
union FileRequestParams
{
FileRequestGetMetadataParams;
FileRequestReadParams;
FileRequestWriteParams;
FileRequestTruncateParams;
FileRequestFlushParams;
FileRequestGetFileParams;
};
protocol PBackgroundFileHandle
{
manager PBackgroundMutableFile;
manages PBackgroundFileRequest;
parent:
DeleteMe();
Finish();
Abort();
PBackgroundFileRequest(FileRequestParams params);
child:
__delete__();
Complete(bool aborted);
};
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,83 @@
/* 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 protocol PBackgroundFileHandle;
include protocol PBlob;
using struct mozilla::void_t
from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace dom {
union FileRequestSize
{
void_t;
uint64_t;
};
union FileRequestLastModified
{
void_t;
int64_t;
};
struct FileRequestMetadata
{
FileRequestSize size;
FileRequestLastModified lastModified;
};
struct FileRequestGetMetadataResponse
{
FileRequestMetadata metadata;
};
struct FileRequestReadResponse
{
nsCString data;
};
struct FileRequestWriteResponse
{
};
struct FileRequestTruncateResponse
{
};
struct FileRequestFlushResponse
{
};
struct FileRequestGetFileResponse
{
PBlob file;
FileRequestMetadata metadata;
};
union FileRequestResponse
{
nsresult;
FileRequestGetMetadataResponse;
FileRequestReadResponse;
FileRequestWriteResponse;
FileRequestTruncateResponse;
FileRequestFlushResponse;
FileRequestGetFileResponse;
};
protocol PBackgroundFileRequest
{
manager PBackgroundFileHandle;
child:
__delete__(FileRequestResponse response);
Progress(uint64_t progress,
uint64_t progressMax);
};
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,36 @@
/* 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 protocol PBackgroundFileHandle;
include protocol PBackgroundIDBDatabase;
include "mozilla/dom/filehandle/SerializationHelpers.h";
using mozilla::dom::FileMode
from "mozilla/dom/FileModeBinding.h";
namespace mozilla {
namespace dom {
sync protocol PBackgroundMutableFile
{
manager PBackgroundIDBDatabase;
manages PBackgroundFileHandle;
parent:
DeleteMe();
PBackgroundFileHandle(FileMode mode);
// Use only for testing!
sync GetFileId()
returns (int64_t fileId);
child:
__delete__();
};
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,23 @@
/* 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_filehandle_SerializationHelpers_h
#define mozilla_dom_filehandle_SerializationHelpers_h
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/FileModeBinding.h"
namespace IPC {
template <>
struct ParamTraits<mozilla::dom::FileMode> :
public ContiguousEnumSerializer<mozilla::dom::FileMode,
mozilla::dom::FileMode::Readonly,
mozilla::dom::FileMode::EndGuard_>
{ };
} // namespace IPC
#endif // mozilla_dom_filehandle_SerializationHelpers_h

View File

@ -4,26 +4,32 @@
# 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/.
EXPORTS.mozilla.dom.filehandle += [
'ActorsChild.h',
'ActorsParent.h',
'SerializationHelpers.h',
]
EXPORTS.mozilla.dom += [
'AsyncHelper.h',
'FileHandle.h',
'FileHelper.h',
'FileRequest.h',
'FileService.h',
'MetadataHelper.h',
'MutableFile.h',
'FileHandleBase.h',
'FileHandleCommon.h',
'FileHandleStorage.h',
'FileRequestBase.h',
'MutableFileBase.h',
]
UNIFIED_SOURCES += [
'AsyncHelper.cpp',
'FileHandle.cpp',
'FileHelper.cpp',
'FileRequest.cpp',
'FileService.cpp',
'FileStreamWrappers.cpp',
'MemoryStreams.cpp',
'MetadataHelper.cpp',
'MutableFile.cpp',
'ActorsChild.cpp',
'ActorsParent.cpp',
'FileHandleBase.cpp',
'FileHandleCommon.cpp',
'MutableFileBase.cpp',
]
IPDL_SOURCES += [
'PBackgroundFileHandle.ipdl',
'PBackgroundFileRequest.ipdl',
'PBackgroundMutableFile.ipdl',
]
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -12,6 +12,7 @@
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "IDBIndex.h"
#include "IDBMutableFile.h"
#include "IDBObjectStore.h"
#include "IDBMutableFile.h"
#include "IDBRequest.h"
@ -597,42 +598,83 @@ ConvertActorsToBlobs(IDBDatabase* aDatabase,
{
MOZ_ASSERT(aFiles.IsEmpty());
const nsTArray<PBlobChild*>& blobs = aCloneReadInfo.blobsChild();
const nsTArray<intptr_t>& fileInfos = aCloneReadInfo.fileInfos();
MOZ_ASSERT_IF(IndexedDatabaseManager::IsMainProcess(),
blobs.Length() == fileInfos.Length());
MOZ_ASSERT_IF(!IndexedDatabaseManager::IsMainProcess(), fileInfos.IsEmpty());
const nsTArray<BlobOrMutableFile>& blobs = aCloneReadInfo.blobs();
if (!blobs.IsEmpty()) {
const uint32_t count = blobs.Length();
aFiles.SetCapacity(count);
for (uint32_t index = 0; index < count; index++) {
BlobChild* actor = static_cast<BlobChild*>(blobs[index]);
const BlobOrMutableFile& blobOrMutableFile = blobs[index];
nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
MOZ_ASSERT(blobImpl);
switch (blobOrMutableFile.type()) {
case BlobOrMutableFile::TPBlobChild: {
auto* actor =
static_cast<BlobChild*>(blobOrMutableFile.get_PBlobChild());
nsRefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
MOZ_ASSERT(blobImpl);
nsRefPtr<FileInfo> fileInfo;
if (!fileInfos.IsEmpty()) {
fileInfo = dont_AddRef(reinterpret_cast<FileInfo*>(fileInfos[index]));
nsRefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl);
MOZ_ASSERT(fileInfo);
MOZ_ASSERT(fileInfo->Id() > 0);
aDatabase->NoteReceivedBlob(blob);
blob->AddFileInfo(fileInfo);
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mMutable = false;
file->mBlob.swap(blob);
break;
}
case BlobOrMutableFile::TNullableMutableFile: {
const NullableMutableFile& nullableMutableFile =
blobOrMutableFile.get_NullableMutableFile();
switch (nullableMutableFile.type()) {
case NullableMutableFile::Tnull_t: {
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mMutable = true;
break;
}
case NullableMutableFile::TPBackgroundMutableFileChild: {
auto* actor =
static_cast<BackgroundMutableFileChild*>(
nullableMutableFile.get_PBackgroundMutableFileChild());
MOZ_ASSERT(actor);
actor->EnsureDOMObject();
auto* mutableFile =
static_cast<IDBMutableFile*>(actor->GetDOMObject());
MOZ_ASSERT(mutableFile);
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mMutable = true;
file->mMutableFile = mutableFile;
actor->ReleaseDOMObject();
break;
}
default:
MOZ_CRASH("Should never get here!");
}
break;
}
default:
MOZ_CRASH("Should never get here!");
}
aDatabase->NoteReceivedBlob(blob);
StructuredCloneFile* file = aFiles.AppendElement();
MOZ_ASSERT(file);
file->mBlob.swap(blob);
file->mFileInfo.swap(fileInfo);
}
}
}
@ -1125,6 +1167,13 @@ BackgroundFactoryChild::AssertIsOnOwningThread() const
MOZ_ASSERT(current);
}
nsIEventTarget*
BackgroundFactoryChild::OwningThread() const
{
MOZ_ASSERT(mOwningThread);
return mOwningThread;
}
#endif // DEBUG
void
@ -1526,7 +1575,7 @@ BackgroundDatabaseChild::EnsureDOMObject()
MOZ_ASSERT(mSpec);
auto request = mOpenRequestActor->GetDOMObject();
auto request = mOpenRequestActor->GetOpenDBRequest();
MOZ_ASSERT(request);
auto factory =
@ -1593,6 +1642,24 @@ BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild(
return true;
}
PBackgroundIDBDatabaseRequestChild*
BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseRequestChild(
const DatabaseRequestParams& aParams)
{
MOZ_CRASH("PBackgroundIDBDatabaseRequestChild actors should be manually "
"constructed!");
}
bool
BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseRequestChild(
PBackgroundIDBDatabaseRequestChild* aActor)
{
MOZ_ASSERT(aActor);
delete static_cast<BackgroundDatabaseRequestChild*>(aActor);
return true;
}
PBackgroundIDBTransactionChild*
BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild(
const nsTArray<nsString>& aObjectStoreNames,
@ -1698,6 +1765,34 @@ BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild(
return true;
}
PBackgroundMutableFileChild*
BackgroundDatabaseChild::AllocPBackgroundMutableFileChild(const nsString& aName,
const nsString& aType)
{
AssertIsOnOwningThread();
#ifdef DEBUG
nsCOMPtr<nsIThread> owningThread = do_QueryInterface(OwningThread());
PRThread* owningPRThread;
owningThread->GetPRThread(&owningPRThread);
#endif
return new BackgroundMutableFileChild(DEBUGONLY(owningPRThread,)
aName,
aType);
}
bool
BackgroundDatabaseChild::DeallocPBackgroundMutableFileChild(
PBackgroundMutableFileChild* aActor)
{
MOZ_ASSERT(aActor);
delete static_cast<BackgroundMutableFileChild*>(aActor);
return true;
}
bool
BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion,
const NullableVersion& aNewVersion)
@ -1790,6 +1885,91 @@ BackgroundDatabaseChild::RecvInvalidate()
return true;
}
/*******************************************************************************
* BackgroundDatabaseRequestChild
******************************************************************************/
BackgroundDatabaseRequestChild::BackgroundDatabaseRequestChild(
IDBDatabase* aDatabase,
IDBRequest* aRequest)
: BackgroundRequestChildBase(aRequest)
, mDatabase(aDatabase)
{
// Can't assert owning thread here because IPDL has not yet set our manager!
MOZ_ASSERT(aDatabase);
aDatabase->AssertIsOnOwningThread();
MOZ_ASSERT(aRequest);
MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseRequestChild);
}
BackgroundDatabaseRequestChild::~BackgroundDatabaseRequestChild()
{
MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseRequestChild);
}
bool
BackgroundDatabaseRequestChild::HandleResponse(nsresult aResponse)
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_FAILED(aResponse));
MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
mRequest->Reset();
DispatchErrorEvent(mRequest, aResponse);
return true;
}
bool
BackgroundDatabaseRequestChild::HandleResponse(
const CreateFileRequestResponse& aResponse)
{
AssertIsOnOwningThread();
mRequest->Reset();
auto mutableFileActor =
static_cast<BackgroundMutableFileChild*>(aResponse.mutableFileChild());
MOZ_ASSERT(mutableFileActor);
mutableFileActor->EnsureDOMObject();
auto mutableFile =
static_cast<IDBMutableFile*>(mutableFileActor->GetDOMObject());
MOZ_ASSERT(mutableFile);
ResultHelper helper(mRequest, nullptr, mutableFile);
DispatchSuccessEvent(&helper);
mutableFileActor->ReleaseDOMObject();
return true;
}
bool
BackgroundDatabaseRequestChild::Recv__delete__(
const DatabaseRequestResponse& aResponse)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mRequest);
switch (aResponse.type()) {
case DatabaseRequestResponse::Tnsresult:
return HandleResponse(aResponse.get_nsresult());
case DatabaseRequestResponse::TCreateFileRequestResponse:
return HandleResponse(aResponse.get_CreateFileRequestResponse());
default:
MOZ_CRASH("Unknown response type!");
}
MOZ_CRASH("Should never get here!");
}
/*******************************************************************************
* BackgroundTransactionBase
******************************************************************************/
@ -2094,6 +2274,40 @@ BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild(
return true;
}
/*******************************************************************************
* BackgroundMutableFileChild
******************************************************************************/
BackgroundMutableFileChild::BackgroundMutableFileChild(
DEBUGONLY(PRThread* aOwningThread,)
const nsAString& aName,
const nsAString& aType)
: BackgroundMutableFileChildBase(DEBUGONLY(aOwningThread))
, mName(aName)
, mType(aType)
{
// Can't assert owning thread here because IPDL has not yet set our manager!
MOZ_COUNT_CTOR(indexedDB::BackgroundMutableFileChild);
}
BackgroundMutableFileChild::~BackgroundMutableFileChild()
{
MOZ_COUNT_DTOR(indexedDB::BackgroundMutableFileChild);
}
already_AddRefed<MutableFileBase>
BackgroundMutableFileChild::CreateMutableFile()
{
auto database =
static_cast<BackgroundDatabaseChild*>(Manager())->GetDOMObject();
MOZ_ASSERT(database);
nsRefPtr<IDBMutableFile> mutableFile =
new IDBMutableFile(database, this, mName, mType);
return mutableFile.forget();
}
/*******************************************************************************
* BackgroundRequestChild
******************************************************************************/
@ -2116,16 +2330,6 @@ BackgroundRequestChild::~BackgroundRequestChild()
MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
}
void
BackgroundRequestChild::HoldFileInfosUntilComplete(
nsTArray<nsRefPtr<FileInfo>>& aFileInfos)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mFileInfos.IsEmpty());
mFileInfos.SwapElements(aFileInfos);
}
void
BackgroundRequestChild::HandleResponse(nsresult aResponse)
{
@ -2168,7 +2372,6 @@ BackgroundRequestChild::HandleResponse(
const_cast<SerializedStructuredCloneReadInfo&>(aResponse);
StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo));
cloneReadInfo.mDatabase = mTransaction->Database();
ConvertActorsToBlobs(mTransaction->Database(),
aResponse,
@ -2203,8 +2406,6 @@ BackgroundRequestChild::HandleResponse(
*cloneReadInfo = Move(serializedCloneInfo);
cloneReadInfo->mDatabase = mTransaction->Database();
ConvertActorsToBlobs(database,
serializedCloneInfo,
cloneReadInfo->mFiles);
@ -2814,26 +3015,6 @@ BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse)
return true;
}
// XXX This doesn't belong here. However, we're not yet porting MutableFile
// stuff to PBackground so this is necessary for the time being.
void
DispatchMutableFileResult(IDBRequest* aRequest,
nsresult aResultCode,
IDBMutableFile* aMutableFile)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequest);
MOZ_ASSERT_IF(NS_SUCCEEDED(aResultCode), aMutableFile);
if (NS_SUCCEEDED(aResultCode)) {
ResultHelper helper(aRequest, nullptr, aMutableFile);
DispatchSuccessEvent(&helper);
} else {
DispatchErrorEvent(aRequest, aResultCode);
}
}
NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedActionRunnable,
nsIRunnable,
nsICancelableRunnable)

View File

@ -10,8 +10,10 @@
#include "IDBTransaction.h"
#include "js/RootingAPI.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/filehandle/ActorsChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h"
@ -36,7 +38,6 @@ class BackgroundChildImpl;
namespace dom {
namespace indexedDB {
class FileInfo;
class IDBCursor;
class IDBDatabase;
class IDBFactory;
@ -155,11 +156,15 @@ class BackgroundFactoryChild final
#endif
public:
#ifdef DEBUG
void
AssertIsOnOwningThread() const;
nsIEventTarget*
OwningThread() const;
#else
void
AssertIsOnOwningThread() const
#ifdef DEBUG
;
#else
{ }
#endif
@ -321,6 +326,14 @@ public:
static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread();
}
#ifdef DEBUG
nsIEventTarget*
OwningThread() const
{
return static_cast<BackgroundFactoryChild*>(Manager())->OwningThread();
}
#endif
const DatabaseSpec*
Spec() const
{
@ -365,6 +378,15 @@ private:
PBackgroundIDBDatabaseFileChild* aActor)
override;
virtual PBackgroundIDBDatabaseRequestChild*
AllocPBackgroundIDBDatabaseRequestChild(const DatabaseRequestParams& aParams)
override;
virtual bool
DeallocPBackgroundIDBDatabaseRequestChild(
PBackgroundIDBDatabaseRequestChild* aActor)
override;
virtual PBackgroundIDBTransactionChild*
AllocPBackgroundIDBTransactionChild(
const nsTArray<nsString>& aObjectStoreNames,
@ -397,6 +419,14 @@ private:
PBackgroundIDBVersionChangeTransactionChild* aActor)
override;
virtual PBackgroundMutableFileChild*
AllocPBackgroundMutableFileChild(const nsString& aName,
const nsString& aType) override;
virtual bool
DeallocPBackgroundMutableFileChild(PBackgroundMutableFileChild* aActor)
override;
virtual bool
RecvVersionChange(const uint64_t& aOldVersion,
const NullableVersion& aNewVersion)
@ -409,6 +439,34 @@ private:
SendDeleteMe() = delete;
};
class BackgroundDatabaseRequestChild final
: public BackgroundRequestChildBase
, public PBackgroundIDBDatabaseRequestChild
{
friend class BackgroundDatabaseChild;
friend class IDBDatabase;
nsRefPtr<IDBDatabase> mDatabase;
private:
// Only created by IDBDatabase.
BackgroundDatabaseRequestChild(IDBDatabase* aDatabase,
IDBRequest* aRequest);
// Only destroyed by BackgroundDatabaseChild.
~BackgroundDatabaseRequestChild();
bool
HandleResponse(nsresult aResponse);
bool
HandleResponse(const CreateFileRequestResponse& aResponse);
// IPDL methods are only called by IPDL.
virtual bool
Recv__delete__(const DatabaseRequestResponse& aResponse) override;
};
class BackgroundVersionChangeTransactionChild;
class BackgroundTransactionBase
@ -565,6 +623,28 @@ private:
SendDeleteMe() = delete;
};
class BackgroundMutableFileChild final
: public mozilla::dom::BackgroundMutableFileChildBase
{
friend class BackgroundDatabaseChild;
nsString mName;
nsString mType;
private:
// Only constructed by BackgroundDatabaseChild.
BackgroundMutableFileChild(DEBUGONLY(PRThread* aOwningThread,)
const nsAString& aName,
const nsAString& aType);
// Only destroyed by BackgroundDatabaseChild.
~BackgroundMutableFileChild();
// BackgroundMutableFileChildBase
virtual already_AddRefed<MutableFileBase>
CreateMutableFile() override;
};
class BackgroundRequestChild final
: public BackgroundRequestChildBase
, public PBackgroundIDBRequestChild
@ -574,11 +654,6 @@ class BackgroundRequestChild final
friend class IDBTransaction;
nsRefPtr<IDBTransaction> mTransaction;
nsTArray<nsRefPtr<FileInfo>> mFileInfos;
public:
void
HoldFileInfosUntilComplete(nsTArray<nsRefPtr<FileInfo>>& aFileInfos);
private:
// Only created by IDBTransaction.
@ -754,13 +829,6 @@ private:
SendDeleteMe() = delete;
};
// XXX This doesn't belong here. However, we're not yet porting MutableFile
// stuff to PBackground so this is necessary for the time being.
void
DispatchMutableFileResult(IDBRequest* aRequest,
nsresult aResultCode,
IDBMutableFile* aMutableFile);
} // namespace indexedDB
} // namespace dom
} // namespace mozilla

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ namespace mozilla {
namespace dom {
class Element;
class FileHandleThreadPool;
namespace quota {
@ -52,6 +53,9 @@ DeallocPIndexedDBPermissionRequestParent(
already_AddRefed<mozilla::dom::quota::Client>
CreateQuotaClient();
FileHandleThreadPool*
GetFileHandleThreadPool();
} // namespace indexedDB
} // namespace dom
} // namespace mozilla

View File

@ -133,7 +133,8 @@ FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount,
int32_t aDelta)
{
// XXX This can go away once DOM objects no longer hold FileInfo objects...
// Looking at you, IDBMutableFile...
// Looking at you, BlobImplBase...
// BlobImplBase is being addressed in bug 1068975.
if (IndexedDatabaseManager::IsClosed()) {
MOZ_ASSERT(&aRefCount == &mRefCnt);
MOZ_ASSERT(aDelta == 1 || aDelta == -1);

View File

@ -7,155 +7,291 @@
#include "FileSnapshot.h"
#include "IDBFileHandle.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MetadataHelper.h"
#ifdef DEBUG
#include "nsXULAppAPI.h"
#endif
#include "nsIIPCSerializableInputStream.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
// Create as a stored file
BlobImplSnapshot::BlobImplSnapshot(const nsAString& aName,
const nsAString& aContentType,
MetadataParameters* aMetadataParams,
nsIFile* aFile,
IDBFileHandle* aFileHandle,
FileInfo* aFileInfo)
: BlobImplBase(aName,
aContentType,
aMetadataParams->Size(),
aMetadataParams->LastModified())
, mFile(aFile)
, mWholeFile(true)
{
AssertSanity();
MOZ_ASSERT(aMetadataParams);
MOZ_ASSERT(aMetadataParams->Size() != UINT64_MAX);
MOZ_ASSERT(aMetadataParams->LastModified() != INT64_MAX);
MOZ_ASSERT(aFile);
MOZ_ASSERT(aFileHandle);
MOZ_ASSERT(aFileInfo);
using namespace mozilla::ipc;
mFileInfos.AppendElement(aFileInfo);
namespace {
class StreamWrapper final
: public nsIInputStream
, public nsIIPCSerializableInputStream
{
class CloseRunnable;
nsCOMPtr<nsIEventTarget> mOwningThread;
nsCOMPtr<nsIInputStream> mInputStream;
nsRefPtr<IDBFileHandle> mFileHandle;
bool mFinished;
public:
StreamWrapper(nsIInputStream* aInputStream,
IDBFileHandle* aFileHandle)
: mOwningThread(NS_GetCurrentThread())
, mInputStream(aInputStream)
, mFileHandle(aFileHandle)
, mFinished(false)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(aFileHandle);
aFileHandle->AssertIsOnOwningThread();
mFileHandle->OnNewRequest();
}
private:
virtual ~StreamWrapper();
bool
IsOnOwningThread() const
{
MOZ_ASSERT(mOwningThread);
bool current;
return NS_SUCCEEDED(mOwningThread->
IsOnCurrentThread(&current)) && current;
}
void
AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
void
Finish()
{
AssertIsOnOwningThread();
if (mFinished) {
return;
}
mFinished = true;
mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true);
}
void
Destroy()
{
if (IsOnOwningThread()) {
delete this;
return;
}
nsCOMPtr<nsIRunnable> destroyRunnable =
NS_NewNonOwningRunnableMethod(this, &StreamWrapper::Destroy);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(destroyRunnable,
NS_DISPATCH_NORMAL)));
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
};
class StreamWrapper::CloseRunnable final
: public nsRunnable
{
friend class StreamWrapper;
nsRefPtr<StreamWrapper> mStreamWrapper;
public:
NS_DECL_ISUPPORTS_INHERITED
private:
explicit
CloseRunnable(StreamWrapper* aStreamWrapper)
: mStreamWrapper(aStreamWrapper)
{ }
~CloseRunnable()
{ }
NS_IMETHOD
Run() override;
};
} // anonymous namespace
BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
IDBFileHandle* aFileHandle)
: mBlobImpl(aFileImpl)
{
MOZ_ASSERT(aFileImpl);
MOZ_ASSERT(aFileHandle);
mFileHandle =
do_GetWeakReference(NS_ISUPPORTS_CAST(EventTarget*, aFileHandle));
}
// Create slice
BlobImplSnapshot::BlobImplSnapshot(const BlobImplSnapshot* aOther,
uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType)
: BlobImplBase(aContentType, aOther->mStart + aStart, aLength)
, mFile(aOther->mFile)
, mFileHandle(aOther->mFileHandle)
, mWholeFile(false)
BlobImplSnapshot::BlobImplSnapshot(BlobImpl* aFileImpl,
nsIWeakReference* aFileHandle)
: mBlobImpl(aFileImpl)
, mFileHandle(aFileHandle)
{
AssertSanity();
MOZ_ASSERT(aOther);
FileInfo* fileInfo;
if (IndexedDatabaseManager::IsClosed()) {
fileInfo = aOther->GetFileInfo();
} else {
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
fileInfo = aOther->GetFileInfo();
}
mFileInfos.AppendElement(fileInfo);
MOZ_ASSERT(aFileImpl);
MOZ_ASSERT(aFileHandle);
}
BlobImplSnapshot::~BlobImplSnapshot()
{
}
#ifdef DEBUG
// static
void
BlobImplSnapshot::AssertSanity()
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
}
#endif // DEBUG
NS_IMPL_ISUPPORTS_INHERITED(BlobImplSnapshot, BlobImpl, PIBlobImplSnapshot)
void
BlobImplSnapshot::GetInternalStream(nsIInputStream** aStream,
ErrorResult& aRv)
{
AssertSanity();
nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
nsRefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
if (!fileHandle) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
return;
}
aRv = fileHandle->OpenInputStream(mWholeFile, mStart, mLength, aStream);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
already_AddRefed<BlobImpl>
BlobImplSnapshot::CreateSlice(uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType,
ErrorResult& aRv)
{
AssertSanity();
nsRefPtr<BlobImpl> blobImpl =
mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
nsRefPtr<BlobImpl> impl =
new BlobImplSnapshot(this, aStart, aLength, aContentType);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return impl.forget();
blobImpl = new BlobImplSnapshot(blobImpl, mFileHandle);
return blobImpl.forget();
}
void
BlobImplSnapshot::GetMozFullPathInternal(nsAString& aFilename,
ErrorResult& aRv) const
BlobImplSnapshot::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
{
AssertSanity();
MOZ_ASSERT(mIsFile);
nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
nsRefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
if (!fileHandle || !fileHandle->IsOpen()) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
return;
}
aRv = mFile->GetPath(aFilename);
nsCOMPtr<nsIInputStream> stream;
mBlobImpl->GetInternalStream(getter_AddRefs(stream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
nsRefPtr<StreamWrapper> wrapper = new StreamWrapper(stream, fileHandle);
wrapper.forget(aStream);
}
BlobImpl*
BlobImplSnapshot::GetBlobImpl() const
{
nsCOMPtr<EventTarget> et = do_QueryReferent(mFileHandle);
nsRefPtr<IDBFileHandle> fileHandle = static_cast<IDBFileHandle*>(et.get());
if (!fileHandle || !fileHandle->IsOpen()) {
return nullptr;
}
return mBlobImpl;
}
StreamWrapper::~StreamWrapper()
{
AssertIsOnOwningThread();
Finish();
}
NS_IMPL_ADDREF(StreamWrapper)
NS_IMPL_RELEASE_WITH_DESTROY(StreamWrapper, Destroy())
NS_IMPL_QUERY_INTERFACE(StreamWrapper,
nsIInputStream,
nsIIPCSerializableInputStream)
NS_IMETHODIMP
StreamWrapper::Close()
{
MOZ_ASSERT(!IsOnOwningThread());
nsRefPtr<CloseRunnable> closeRunnable = new CloseRunnable(this);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(closeRunnable,
NS_DISPATCH_NORMAL)));
return NS_OK;
}
NS_IMETHODIMP
StreamWrapper::Available(uint64_t* _retval)
{
// Can't assert here, this method is sometimes called on the owning thread
// (nsInputStreamChannel::OpenContentStream calls Available before setting
// the content length property).
return mInputStream->Available(_retval);
}
NS_IMETHODIMP
StreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
{
MOZ_ASSERT(!IsOnOwningThread());
return mInputStream->Read(aBuf, aCount, _retval);
}
NS_IMETHODIMP
StreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* _retval)
{
MOZ_ASSERT(!IsOnOwningThread());
return mInputStream->ReadSegments(aWriter, aClosure, aCount, _retval);
}
NS_IMETHODIMP
StreamWrapper::IsNonBlocking(bool* _retval)
{
return mInputStream->IsNonBlocking(_retval);
}
void
StreamWrapper::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors)
{
nsCOMPtr<nsIIPCSerializableInputStream> stream =
do_QueryInterface(mInputStream);
if (stream) {
stream->Serialize(aParams, aFileDescriptors);
}
}
bool
BlobImplSnapshot::IsStoredFile() const
StreamWrapper::Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray& aFileDescriptors)
{
AssertSanity();
nsCOMPtr<nsIIPCSerializableInputStream> stream =
do_QueryInterface(mInputStream);
return true;
if (stream) {
return stream->Deserialize(aParams, aFileDescriptors);
}
return false;
}
bool
BlobImplSnapshot::IsWholeFile() const
NS_IMPL_ISUPPORTS_INHERITED0(StreamWrapper::CloseRunnable,
nsRunnable)
NS_IMETHODIMP
StreamWrapper::
CloseRunnable::Run()
{
AssertSanity();
mStreamWrapper->Finish();
return mWholeFile;
}
bool
BlobImplSnapshot::IsSnapshot() const
{
AssertSanity();
return true;
return NS_OK;
}
} // namespace indexedDB

View File

@ -10,7 +10,6 @@
#include "mozilla/Attributes.h"
#include "mozilla/dom/File.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsISupports.h"
#include "nsWeakPtr.h"
@ -21,67 +20,91 @@ class PIBlobImplSnapshot : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(FILEIMPLSNAPSHOT_IID)
virtual mozilla::dom::BlobImpl*
GetBlobImpl() const = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(PIBlobImplSnapshot, FILEIMPLSNAPSHOT_IID)
namespace mozilla {
namespace dom {
class MetadataParameters;
namespace indexedDB {
class IDBFileHandle;
class BlobImplSnapshot final
: public BlobImplBase
: public BlobImpl
, public PIBlobImplSnapshot
{
typedef mozilla::dom::MetadataParameters MetadataParameters;
nsCOMPtr<nsIFile> mFile;
nsRefPtr<BlobImpl> mBlobImpl;
nsWeakPtr mFileHandle;
bool mWholeFile;
public:
// Create as a stored file
BlobImplSnapshot(const nsAString& aName,
const nsAString& aContentType,
MetadataParameters* aMetadataParams,
nsIFile* aFile,
IDBFileHandle* aFileHandle,
FileInfo* aFileInfo);
BlobImplSnapshot(BlobImpl* aImpl,
IDBFileHandle* aFileHandle);
NS_DECL_ISUPPORTS_INHERITED
private:
// Create slice
BlobImplSnapshot(const BlobImplSnapshot* aOther,
uint64_t aStart,
uint64_t aLength,
const nsAString& aContentType);
BlobImplSnapshot(BlobImpl* aImpl,
nsIWeakReference* aFileHandle);
~BlobImplSnapshot();
static void
AssertSanity()
#ifdef DEBUG
;
#else
{ }
#endif
// BlobImpl
virtual void
GetMozFullPathInternal(nsAString& aFullPath, ErrorResult& aRv) const override;
virtual void
GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override;
virtual bool MayBeClonedToOtherThreads() const override
GetName(nsAString& aName) override
{
return false;
mBlobImpl->GetName(aName);
}
virtual void
GetPath(nsAString& aPath, ErrorResult& aRv) override
{
mBlobImpl->GetPath(aPath, aRv);
}
virtual int64_t
GetLastModified(ErrorResult& aRv) override
{
return mBlobImpl->GetLastModified(aRv);
}
virtual void
SetLastModified(int64_t aLastModified) override
{
mBlobImpl->SetLastModified(aLastModified);
}
virtual void
GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override
{
mBlobImpl->GetMozFullPath(aName, aRv);
}
virtual void
GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override
{
mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
}
virtual uint64_t
GetSize(ErrorResult& aRv) override
{
return mBlobImpl->GetSize(aRv);
}
virtual void
GetType(nsAString& aType) override
{
mBlobImpl->GetType(aType);
}
virtual uint64_t
GetSerialNumber() const override
{
return mBlobImpl->GetSerialNumber();
}
virtual already_AddRefed<BlobImpl>
@ -90,14 +113,125 @@ private:
const nsAString& aContentType,
ErrorResult& aRv) override;
virtual bool
IsStoredFile() const override;
virtual const nsTArray<nsRefPtr<BlobImpl>>*
GetSubBlobImpls() const override
{
return mBlobImpl->GetSubBlobImpls();
}
virtual void
GetInternalStream(nsIInputStream** aStream,
ErrorResult& aRv) override;
virtual int64_t
GetFileId() override
{
return mBlobImpl->GetFileId();
}
virtual void
AddFileInfo(FileInfo* aFileInfo) override
{
return mBlobImpl->AddFileInfo(aFileInfo);
}
virtual FileInfo*
GetFileInfo(FileManager* aFileManager) override
{
return mBlobImpl->GetFileInfo(aFileManager);
}
virtual nsresult
GetSendInfo(nsIInputStream** aBody,
uint64_t* aContentLength,
nsACString& aContentType,
nsACString& aCharset) override
{
return mBlobImpl->GetSendInfo(aBody,
aContentLength,
aContentType,
aCharset);
}
virtual nsresult
GetMutable(bool* aMutable) const override
{
return mBlobImpl->GetMutable(aMutable);
}
virtual nsresult
SetMutable(bool aMutable) override
{
return mBlobImpl->SetMutable(aMutable);
}
virtual void
SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate,
BlobDirState aDirState) override
{
MOZ_CRASH("This should never be called!");
}
virtual bool
IsWholeFile() const override;
IsMemoryFile() const override
{
return mBlobImpl->IsMemoryFile();
}
virtual bool
IsSnapshot() const override;
IsSizeUnknown() const override
{
return mBlobImpl->IsSizeUnknown();
}
virtual bool
IsDateUnknown() const override
{
return mBlobImpl->IsDateUnknown();
}
virtual bool
IsFile() const override
{
return mBlobImpl->IsFile();
}
virtual void
LookupAndCacheIsDirectory() override
{
mBlobImpl->LookupAndCacheIsDirectory();
}
virtual void
SetIsDirectory(bool aIsDir) override
{
return mBlobImpl->SetIsDirectory(aIsDir);
}
virtual bool
IsDirectory() const override
{
return mBlobImpl->IsDirectory();
}
virtual BlobDirState
GetDirState() const override
{
return mBlobImpl->GetDirState();
}
virtual bool
MayBeClonedToOtherThreads() const override
{
return false;
}
// PIBlobImplSnapshot
virtual BlobImpl*
GetBlobImpl() const override;
};
} // namespace indexedDB

View File

@ -92,53 +92,6 @@ private:
NS_DECL_NSICANCELABLERUNNABLE
};
// XXX This should either be ported to PBackground or removed someday.
class CreateFileHelper final
: public nsRunnable
{
nsRefPtr<IDBDatabase> mDatabase;
nsRefPtr<IDBRequest> mRequest;
nsRefPtr<FileInfo> mFileInfo;
const nsString mName;
const nsString mType;
const nsString mDatabaseName;
const nsCString mOrigin;
const PersistenceType mPersistenceType;
nsresult mResultCode;
public:
static nsresult
CreateAndDispatch(IDBDatabase* aDatabase,
IDBRequest* aRequest,
const nsAString& aName,
const nsAString& aType);
NS_DECL_ISUPPORTS_INHERITED
private:
CreateFileHelper(IDBDatabase* aDatabase,
IDBRequest* aRequest,
const nsAString& aName,
const nsAString& aType,
const nsACString& aOrigin);
~CreateFileHelper()
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
}
nsresult
DoDatabaseWork();
void
DoMainThreadWork(nsresult aResultCode);
NS_DECL_NSIRUNNABLE
};
class DatabaseFile final
: public PBackgroundIDBDatabaseFileChild
{
@ -260,18 +213,19 @@ private:
NS_DECL_NSIOBSERVER
};
IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache,
IDBDatabase::IDBDatabase(IDBOpenDBRequest* aRequest,
IDBFactory* aFactory,
BackgroundDatabaseChild* aActor,
DatabaseSpec* aSpec)
: IDBWrapperCache(aOwnerCache)
: IDBWrapperCache(aRequest)
, mFactory(aFactory)
, mSpec(aSpec)
, mBackgroundActor(aActor)
, mFileHandleDisabled(aRequest->IsFileHandleDisabled())
, mClosed(false)
, mInvalidated(false)
{
MOZ_ASSERT(aOwnerCache);
MOZ_ASSERT(aRequest);
MOZ_ASSERT(aFactory);
aFactory->AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
@ -286,21 +240,21 @@ IDBDatabase::~IDBDatabase()
// static
already_AddRefed<IDBDatabase>
IDBDatabase::Create(IDBWrapperCache* aOwnerCache,
IDBDatabase::Create(IDBOpenDBRequest* aRequest,
IDBFactory* aFactory,
BackgroundDatabaseChild* aActor,
DatabaseSpec* aSpec)
{
MOZ_ASSERT(aOwnerCache);
MOZ_ASSERT(aRequest);
MOZ_ASSERT(aFactory);
aFactory->AssertIsOnOwningThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(aSpec);
nsRefPtr<IDBDatabase> db =
new IDBDatabase(aOwnerCache, aFactory, aActor, aSpec);
new IDBDatabase(aRequest, aFactory, aActor, aSpec);
db->SetScriptOwner(aOwnerCache->GetScriptOwner());
db->SetScriptOwner(aRequest->GetScriptOwner());
if (NS_IsMainThread()) {
if (nsPIDOMWindow* window = aFactory->GetParentObject()) {
@ -346,6 +300,13 @@ IDBDatabase::AssertIsOnOwningThread() const
mFactory->AssertIsOnOwningThread();
}
PRThread*
IDBDatabase::OwningThread() const
{
MOZ_ASSERT(mFactory);
return mFactory->OwningThread();
}
#endif // DEBUG
void
@ -822,11 +783,7 @@ IDBDatabase::CreateMutableFile(const nsAString& aName,
const Optional<nsAString>& aType,
ErrorResult& aRv)
{
if (!IndexedDatabaseManager::IsMainProcess() || !NS_IsMainThread()) {
IDB_WARNING("Not supported!");
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return nullptr;
}
AssertIsOnOwningThread();
if (QuotaManager::IsShuttingDown()) {
IDB_REPORT_INTERNAL_ERR();
@ -834,24 +791,33 @@ IDBDatabase::CreateMutableFile(const nsAString& aName,
return nullptr;
}
if (mClosed) {
if (mClosed || mFileHandleDisabled) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
return nullptr;
}
nsRefPtr<IDBRequest> request = IDBRequest::Create(this, nullptr);
nsString type;
if (aType.WasPassed()) {
type = aType.Value();
}
mFactory->IncrementParentLoggingRequestSerialNumber();
CreateFileParams params(nsString(aName), type);
aRv = CreateFileHelper::CreateAndDispatch(this, request, aName, type);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nsRefPtr<IDBRequest> request = IDBRequest::Create(this, nullptr);
MOZ_ASSERT(request);
BackgroundDatabaseRequestChild* actor =
new BackgroundDatabaseRequestChild(this, request);
IDB_LOG_MARK("IndexedDB %s: Child Request[%llu]: "
"database(%s).createMutableFile(%s)",
"IndexedDB %s: C R[%llu]: IDBDatabase.createMutableFile()",
IDB_LOG_ID_STRING(),
request->LoggingSerialNumber(),
IDB_LOG_STRINGIFY(this),
NS_ConvertUTF16toUTF8(aName).get());
mBackgroundActor->SendPBackgroundIDBDatabaseRequestConstructor(actor, params);
return request.forget();
}
@ -1149,8 +1115,7 @@ IDBDatabase::GetQuotaInfo(nsACString& aOrigin,
{
using mozilla::dom::quota::QuotaManager;
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(NS_IsMainThread(), "This can't work off the main thread!");
if (aPersistenceType) {
*aPersistenceType = mSpec->metadata().persistenceType();
@ -1266,9 +1231,9 @@ IDBDatabase::ExpireFileActors(bool aExpireAll)
void
IDBDatabase::NoteLiveMutableFile(IDBMutableFile* aMutableFile)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
MOZ_ASSERT(aMutableFile);
aMutableFile->AssertIsOnOwningThread();
MOZ_ASSERT(!mLiveMutableFiles.Contains(aMutableFile));
mLiveMutableFiles.AppendElement(aMutableFile);
@ -1277,45 +1242,22 @@ IDBDatabase::NoteLiveMutableFile(IDBMutableFile* aMutableFile)
void
IDBDatabase::NoteFinishedMutableFile(IDBMutableFile* aMutableFile)
{
// This should always happen in the main process but occasionally it is called
// after the IndexedDatabaseManager has already shut down.
// MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
MOZ_ASSERT(aMutableFile);
aMutableFile->AssertIsOnOwningThread();
// It's ok if this is called more than once, so don't assert that aMutableFile
// is in the list already.
// It's ok if this is called after we cleared the array, so don't assert that
// aMutableFile is in the list.
mLiveMutableFiles.RemoveElement(aMutableFile);
}
void
IDBDatabase::OnNewFileHandle()
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBackgroundActor);
mBackgroundActor->SendNewFileHandle();
}
void
IDBDatabase::OnFileHandleFinished()
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBackgroundActor);
mBackgroundActor->SendFileHandleFinished();
}
void
IDBDatabase::InvalidateMutableFiles()
{
if (!mLiveMutableFiles.IsEmpty()) {
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
if (!mLiveMutableFiles.IsEmpty()) {
for (uint32_t count = mLiveMutableFiles.Length(), index = 0;
index < count;
index++) {
@ -1446,189 +1388,6 @@ CancelableRunnableWrapper::Cancel()
return NS_ERROR_UNEXPECTED;
}
CreateFileHelper::CreateFileHelper(IDBDatabase* aDatabase,
IDBRequest* aRequest,
const nsAString& aName,
const nsAString& aType,
const nsACString& aOrigin)
: mDatabase(aDatabase)
, mRequest(aRequest)
, mName(aName)
, mType(aType)
, mDatabaseName(aDatabase->Name())
, mOrigin(aOrigin)
, mPersistenceType(aDatabase->Spec()->metadata().persistenceType())
, mResultCode(NS_OK)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aDatabase);
MOZ_ASSERT(aRequest);
MOZ_ASSERT(mPersistenceType != PERSISTENCE_TYPE_INVALID);
}
// static
nsresult
CreateFileHelper::CreateAndDispatch(IDBDatabase* aDatabase,
IDBRequest* aRequest,
const nsAString& aName,
const nsAString& aType)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aDatabase);
aDatabase->AssertIsOnOwningThread();
MOZ_ASSERT(aDatabase->Factory());
MOZ_ASSERT(aRequest);
MOZ_ASSERT(!QuotaManager::IsShuttingDown());
nsCString origin;
nsresult rv = aDatabase->GetQuotaInfo(origin, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(!origin.IsEmpty());
nsRefPtr<CreateFileHelper> helper =
new CreateFileHelper(aDatabase, aRequest, aName, aType, origin);
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
nsCOMPtr<nsIEventTarget> ioThread = quotaManager->IOThread();
MOZ_ASSERT(ioThread);
if (NS_WARN_IF(NS_FAILED(ioThread->Dispatch(helper, NS_DISPATCH_NORMAL)))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
return NS_OK;
}
nsresult
CreateFileHelper::DoDatabaseWork()
{
AssertIsOnIOThread();
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(!mFileInfo);
PROFILER_LABEL("IndexedDB",
"CreateFileHelper::DoDatabaseWork",
js::ProfileEntry::Category::STORAGE);
if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
NS_WARNING("Refusing to create file because disk space is low!");
return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
}
IndexedDatabaseManager* idbManager = IndexedDatabaseManager::Get();
MOZ_ASSERT(idbManager);
nsRefPtr<FileManager> fileManager =
idbManager->GetFileManager(mPersistenceType, mOrigin, mDatabaseName);
MOZ_ASSERT(fileManager);
nsRefPtr<FileInfo> fileInfo = fileManager->GetNewFileInfo();
if (NS_WARN_IF(!fileInfo)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
const int64_t fileId = fileInfo->Id();
nsCOMPtr<nsIFile> journalDirectory = fileManager->EnsureJournalDirectory();
if (NS_WARN_IF(!journalDirectory)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsCOMPtr<nsIFile> journalFile =
fileManager->GetFileForId(journalDirectory, fileId);
if (NS_WARN_IF(!journalFile)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsresult rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIFile> fileDirectory = fileManager->GetDirectory();
if (NS_WARN_IF(!fileDirectory)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsCOMPtr<nsIFile> file = fileManager->GetFileForId(fileDirectory, fileId);
if (NS_WARN_IF(!file)) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mFileInfo.swap(fileInfo);
return NS_OK;
}
void
CreateFileHelper::DoMainThreadWork(nsresult aResultCode)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
if (mDatabase->IsInvalidated()) {
IDB_REPORT_INTERNAL_ERR();
aResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsRefPtr<IDBMutableFile> mutableFile;
if (NS_SUCCEEDED(aResultCode)) {
mutableFile =
IDBMutableFile::Create(mDatabase, mName, mType, mFileInfo.forget());
MOZ_ASSERT(mutableFile);
}
DispatchMutableFileResult(mRequest, aResultCode, mutableFile);
}
NS_IMPL_ISUPPORTS_INHERITED0(CreateFileHelper, nsRunnable)
NS_IMETHODIMP
CreateFileHelper::Run()
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
if (NS_IsMainThread()) {
DoMainThreadWork(mResultCode);
mDatabase = nullptr;
mRequest = nullptr;
mFileInfo = nullptr;
return NS_OK;
}
AssertIsOnIOThread();
MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
nsresult rv = DoDatabaseWork();
if (NS_WARN_IF(NS_FAILED(rv))) {
mResultCode = rv;
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
return NS_OK;
}
// static
void
IDBDatabase::

View File

@ -42,6 +42,7 @@ class FileManager;
class IDBFactory;
class IDBMutableFile;
class IDBObjectStore;
class IDBOpenDBRequest;
class IDBRequest;
class IDBTransaction;
class PBackgroundIDBDatabaseFileChild;
@ -84,21 +85,26 @@ class IDBDatabase final
// Weak refs, IDBMutableFile strongly owns this IDBDatabase object.
nsTArray<IDBMutableFile*> mLiveMutableFiles;
const bool mFileHandleDisabled;
bool mClosed;
bool mInvalidated;
public:
static already_AddRefed<IDBDatabase>
Create(IDBWrapperCache* aOwnerCache,
Create(IDBOpenDBRequest* aRequest,
IDBFactory* aFactory,
BackgroundDatabaseChild* aActor,
DatabaseSpec* aSpec);
#ifdef DEBUG
void
AssertIsOnOwningThread() const;
PRThread*
OwningThread() const;
#else
void
AssertIsOnOwningThread() const
#ifdef DEBUG
;
#else
{ }
#endif
@ -189,22 +195,22 @@ public:
DelayedMaybeExpireFileActors();
// XXX This doesn't really belong here... It's only needed for IDBMutableFile
// serialization and should be removed someday.
// serialization and should be removed or fixed someday.
nsresult
GetQuotaInfo(nsACString& aOrigin, PersistenceType* aPersistenceType);
bool
IsFileHandleDisabled() const
{
return mFileHandleDisabled;
}
void
NoteLiveMutableFile(IDBMutableFile* aMutableFile);
void
NoteFinishedMutableFile(IDBMutableFile* aMutableFile);
void
OnNewFileHandle();
void
OnFileHandleFinished();
nsPIDOMWindow*
GetParentObject() const;
@ -280,7 +286,7 @@ public:
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
private:
IDBDatabase(IDBWrapperCache* aOwnerCache,
IDBDatabase(IDBOpenDBRequest* aRequest,
IDBFactory* aFactory,
BackgroundDatabaseChild* aActor,
DatabaseSpec* aSpec);

View File

@ -454,6 +454,13 @@ IDBFactory::AssertIsOnOwningThread() const
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
}
PRThread*
IDBFactory::OwningThread() const
{
MOZ_ASSERT(mOwningThread);
return mOwningThread;
}
#endif // DEBUG
bool

View File

@ -109,11 +109,15 @@ public:
AllowedForPrincipal(nsIPrincipal* aPrincipal,
bool* aIsSystemPrincipal = nullptr);
#ifdef DEBUG
void
AssertIsOnOwningThread() const;
PRThread*
OwningThread() const;
#else
void
AssertIsOnOwningThread() const
#ifdef DEBUG
;
#else
{ }
#endif

View File

@ -8,9 +8,9 @@
#include "IDBEvents.h"
#include "IDBMutableFile.h"
#include "mozilla/dom/FileService.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/IDBFileHandleBinding.h"
#include "mozilla/dom/MetadataHelper.h"
#include "mozilla/dom/filehandle/ActorsChild.h"
#include "mozilla/EventDispatcher.h"
#include "nsServiceManagerUtils.h"
#include "nsWidgetsCID.h"
@ -20,71 +20,115 @@ namespace dom {
namespace indexedDB {
IDBFileHandle::IDBFileHandle(FileMode aMode,
RequestMode aRequestMode,
IDBMutableFile* aMutableFile)
: FileHandleBase(aMode, aRequestMode)
: FileHandleBase(DEBUGONLY(aMutableFile->OwningThread(),)
aMode)
, mMutableFile(aMutableFile)
{
AssertIsOnOwningThread();
}
IDBFileHandle::~IDBFileHandle()
{
AssertIsOnOwningThread();
mMutableFile->UnregisterFileHandle(this);
}
// static
already_AddRefed<IDBFileHandle>
IDBFileHandle::Create(FileMode aMode,
RequestMode aRequestMode,
IDBMutableFile* aMutableFile)
IDBFileHandle::Create(IDBMutableFile* aMutableFile,
FileMode aMode)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aMutableFile);
aMutableFile->AssertIsOnOwningThread();
MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
nsRefPtr<IDBFileHandle> fileHandle =
new IDBFileHandle(aMode, aRequestMode, aMutableFile);
new IDBFileHandle(aMode, aMutableFile);
fileHandle->BindToOwner(aMutableFile);
// XXX Fix!
MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
nsContentUtils::RunInMetastableState(runnable.forget());
fileHandle->SetCreating();
FileService* service = FileService::GetOrCreate();
if (NS_WARN_IF(!service)) {
return nullptr;
}
nsresult rv = service->Enqueue(fileHandle, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
aMutableFile->Database()->OnNewFileHandle();
aMutableFile->RegisterFileHandle(fileHandle);
return fileHandle.forget();
}
mozilla::dom::MutableFileBase*
IDBFileHandle::MutableFile() const
already_AddRefed<IDBFileRequest>
IDBFileHandle::GetMetadata(const IDBFileMetadataParameters& aParameters,
ErrorResult& aRv)
{
return mMutableFile;
AssertIsOnOwningThread();
// Common state checking
if (!CheckState(aRv)) {
return nullptr;
}
// Argument checking for get metadata.
if (!aParameters.mSize && !aParameters.mLastModified) {
aRv.ThrowTypeError(MSG_METADATA_NOT_CONFIGURED);
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
FileRequestGetMetadataParams params;
params.size() = aParameters.mSize;
params.lastModified() = aParameters.mLastModified;
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
StartRequest(fileRequest, params);
return fileRequest.forget().downcast<IDBFileRequest>();
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBFileHandle, DOMEventTargetHelper,
mMutableFile)
NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileHandle)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFileHandle)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBFileHandle,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutableFile)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBFileHandle,
DOMEventTargetHelper)
// Don't unlink mMutableFile!
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMETHODIMP
IDBFileHandle::Run()
{
AssertIsOnOwningThread();
OnReturnToEventLoop();
return NS_OK;
}
nsresult
IDBFileHandle::PreHandleEvent(EventChainPreVisitor& aVisitor)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
AssertIsOnOwningThread();
aVisitor.mCanHandle = true;
aVisitor.mParentTarget = mMutableFile;
@ -95,55 +139,26 @@ IDBFileHandle::PreHandleEvent(EventChainPreVisitor& aVisitor)
JSObject*
IDBFileHandle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
AssertIsOnOwningThread();
return IDBFileHandleBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<IDBFileRequest>
IDBFileHandle::GetMetadata(const IDBFileMetadataParameters& aParameters,
ErrorResult& aRv)
mozilla::dom::MutableFileBase*
IDBFileHandle::MutableFile() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
AssertIsOnOwningThread();
// Common state checking
if (!CheckState(aRv)) {
return nullptr;
}
// Do nothing if the window is closed
if (!CheckWindow()) {
return nullptr;
}
nsRefPtr<MetadataParameters> params =
new MetadataParameters(aParameters.mSize, aParameters.mLastModified);
if (!params->IsConfigured()) {
aRv.ThrowTypeError(MSG_METADATA_NOT_CONFIGURED);
return nullptr;
}
nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest();
nsRefPtr<MetadataHelper> helper =
new MetadataHelper(this, fileRequest, params);
if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) {
aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
return fileRequest.forget().downcast<IDBFileRequest>();
return mMutableFile;
}
NS_IMETHODIMP
IDBFileHandle::Run()
void
IDBFileHandle::HandleCompleteOrAbort(bool aAborted)
{
OnReturnToEventLoop();
return NS_OK;
}
AssertIsOnOwningThread();
FileHandleBase::HandleCompleteOrAbort(aAborted);
nsresult
IDBFileHandle::OnCompleteOrAbort(bool aAborted)
{
nsCOMPtr<nsIDOMEvent> event;
if (aAborted) {
event = CreateGenericEvent(this, nsDependentString(kAbortEventType),
@ -153,29 +168,27 @@ IDBFileHandle::OnCompleteOrAbort(bool aAborted)
eDoesNotBubble, eNotCancelable);
}
if (NS_WARN_IF(!event)) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
return;
}
bool dummy;
if (NS_FAILED(DispatchEvent(event, &dummy))) {
NS_WARNING("Dispatch failed!");
NS_WARNING("DispatchEvent failed!");
}
mMutableFile->Database()->OnFileHandleFinished();
return NS_OK;
}
bool
IDBFileHandle::CheckWindow()
{
AssertIsOnOwningThread();
return GetOwner();
}
already_AddRefed<mozilla::dom::FileRequestBase>
IDBFileHandle::GenerateFileRequest()
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
AssertIsOnOwningThread();
return IDBFileRequest::Create(GetOwner(), this,
/* aWrapAsDOMRequest */ false);

View File

@ -9,12 +9,11 @@
#include "IDBFileRequest.h"
#include "js/TypeDecls.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/FileHandle.h"
#include "mozilla/dom/FileHandleBase.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIRunnable.h"
#include "nsWeakReference.h"
class nsPIDOMWindow;
@ -26,53 +25,41 @@ struct IDBFileMetadataParameters;
namespace indexedDB {
class IDBFileRequest;
class IDBMutableFile;
class IDBFileHandle final : public DOMEventTargetHelper,
public nsIRunnable,
public FileHandleBase,
public nsSupportsWeakReference
class IDBFileHandle final
: public DOMEventTargetHelper
, public nsIRunnable
, public FileHandleBase
, public nsSupportsWeakReference
{
nsRefPtr<IDBMutableFile> mMutableFile;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBFileHandle, DOMEventTargetHelper)
static already_AddRefed<IDBFileHandle>
Create(FileMode aMode,
RequestMode aRequestMode,
IDBMutableFile* aMutableFile);
virtual MutableFileBase*
MutableFile() const override;
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(EventChainPreVisitor& aVisitor) override;
// WrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
Create(IDBMutableFile* aMutableFile,
FileMode aMode);
// WebIDL
nsPIDOMWindow*
GetParentObject() const
{
AssertIsOnOwningThread();
return GetOwner();
}
IDBMutableFile*
GetMutableFile() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
AssertIsOnOwningThread();
return mMutableFile;
}
IDBMutableFile*
GetFileHandle() const
{
AssertIsOnOwningThread();
return GetMutableFile();
}
@ -82,39 +69,44 @@ public:
already_AddRefed<IDBFileRequest>
ReadAsArrayBuffer(uint64_t aSize, ErrorResult& aRv)
{
AssertIsOnOwningThread();
return Read(aSize, false, NullString(), aRv).downcast<IDBFileRequest>();
}
already_AddRefed<IDBFileRequest>
ReadAsText(uint64_t aSize, const nsAString& aEncoding, ErrorResult& aRv)
{
AssertIsOnOwningThread();
return Read(aSize, true, aEncoding, aRv).downcast<IDBFileRequest>();
}
template<class T>
already_AddRefed<IDBFileRequest>
Write(const T& aValue, ErrorResult& aRv)
Write(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
ErrorResult& aRv)
{
return
WriteOrAppend(aValue, false, aRv).template downcast<IDBFileRequest>();
AssertIsOnOwningThread();
return WriteOrAppend(aValue, false, aRv).downcast<IDBFileRequest>();
}
template<class T>
already_AddRefed<IDBFileRequest>
Append(const T& aValue, ErrorResult& aRv)
Append(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue,
ErrorResult& aRv)
{
return WriteOrAppend(aValue, true, aRv).template downcast<IDBFileRequest>();
AssertIsOnOwningThread();
return WriteOrAppend(aValue, true, aRv).downcast<IDBFileRequest>();
}
already_AddRefed<IDBFileRequest>
Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv)
{
AssertIsOnOwningThread();
return FileHandleBase::Truncate(aSize, aRv).downcast<IDBFileRequest>();
}
already_AddRefed<IDBFileRequest>
Flush(ErrorResult& aRv)
{
AssertIsOnOwningThread();
return FileHandleBase::Flush(aRv).downcast<IDBFileRequest>();
}
@ -122,22 +114,36 @@ public:
IMPL_EVENT_HANDLER(abort)
IMPL_EVENT_HANDLER(error)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBFileHandle, DOMEventTargetHelper)
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(EventChainPreVisitor& aVisitor) override;
// WrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// FileHandleBase
virtual MutableFileBase*
MutableFile() const override;
virtual void
HandleCompleteOrAbort(bool aAborted) override;
private:
IDBFileHandle(FileMode aMode,
RequestMode aRequestMode,
IDBMutableFile* aMutableFile);
~IDBFileHandle();
virtual nsresult
OnCompleteOrAbort(bool aAborted) override;
// FileHandleBase
virtual bool
CheckWindow() override;
virtual already_AddRefed<FileRequestBase>
GenerateFileRequest() override;
nsRefPtr<IDBMutableFile> mMutableFile;
};
} // namespace indexedDB

View File

@ -9,30 +9,34 @@
#include "IDBFileHandle.h"
#include "js/RootingAPI.h"
#include "jsapi.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/FileHelper.h"
#include "mozilla/dom/IDBFileRequestBinding.h"
#include "mozilla/dom/ProgressEvent.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/EventDispatcher.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIDOMEvent.h"
#include "nsIScriptContext.h"
#include "nsLiteralString.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
IDBFileRequest::IDBFileRequest(nsPIDOMWindow* aWindow)
: DOMRequest(aWindow), mWrapAsDOMRequest(false)
IDBFileRequest::IDBFileRequest(nsPIDOMWindow* aWindow,
IDBFileHandle* aFileHandle,
bool aWrapAsDOMRequest)
: DOMRequest(aWindow)
, FileRequestBase(DEBUGONLY(aFileHandle->OwningThread()))
, mFileHandle(aFileHandle)
, mWrapAsDOMRequest(aWrapAsDOMRequest)
{
AssertIsOnOwningThread();
}
IDBFileRequest::~IDBFileRequest()
{
AssertIsOnOwningThread();
}
// static
@ -40,103 +44,98 @@ already_AddRefed<IDBFileRequest>
IDBFileRequest::Create(nsPIDOMWindow* aOwner, IDBFileHandle* aFileHandle,
bool aWrapAsDOMRequest)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aFileHandle);
aFileHandle->AssertIsOnOwningThread();
nsRefPtr<IDBFileRequest> request = new IDBFileRequest(aOwner);
request->mFileHandle = aFileHandle;
request->mWrapAsDOMRequest = aWrapAsDOMRequest;
nsRefPtr<IDBFileRequest> request =
new IDBFileRequest(aOwner, aFileHandle, aWrapAsDOMRequest);
return request.forget();
}
NS_IMPL_ADDREF_INHERITED(IDBFileRequest, DOMRequest)
NS_IMPL_RELEASE_INHERITED(IDBFileRequest, DOMRequest)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBFileRequest, DOMRequest,
mFileHandle)
nsresult
IDBFileRequest::PreHandleEvent(EventChainPreVisitor& aVisitor)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
AssertIsOnOwningThread();
aVisitor.mCanHandle = true;
aVisitor.mParentTarget = mFileHandle;
return NS_OK;
}
void
IDBFileRequest::OnProgress(uint64_t aProgress, uint64_t aProgressMax)
{
FireProgressEvent(aProgress, aProgressMax);
}
nsresult
IDBFileRequest::NotifyHelperCompleted(FileHelper* aFileHelper)
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
nsresult rv = aFileHelper->ResultCode();
// 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.
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_STATE(sc);
AutoJSContext cx;
MOZ_ASSERT(cx, "Failed to get a context!");
JS::Rooted<JS::Value> result(cx);
JS::Rooted<JSObject*> global(cx, sc->GetWindowProxy());
MOZ_ASSERT(global, "Failed to get global object!");
JSAutoCompartment ac(cx, global);
rv = aFileHelper->GetSuccessResult(cx, &result);
if (NS_FAILED(rv)) {
NS_WARNING("GetSuccessResult failed!");
}
if (NS_SUCCEEDED(rv)) {
FireSuccess(result);
}
else {
FireError(rv);
}
return NS_OK;
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBFileRequest, DOMRequest,
mFileHandle)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBFileRequest)
NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
NS_IMPL_ADDREF_INHERITED(IDBFileRequest, DOMRequest)
NS_IMPL_RELEASE_INHERITED(IDBFileRequest, DOMRequest)
// virtual
JSObject*
IDBFileRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
AssertIsOnOwningThread();
if (mWrapAsDOMRequest) {
return DOMRequest::WrapObject(aCx, aGivenProto);
}
return IDBFileRequestBinding::Wrap(aCx, this, aGivenProto);
}
IDBFileHandle*
IDBFileRequest::GetFileHandle() const
mozilla::dom::FileHandleBase*
IDBFileRequest::FileHandle() const
{
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
AssertIsOnOwningThread();
return static_cast<IDBFileHandle*>(mFileHandle.get());
return mFileHandle;
}
void
IDBFileRequest::OnProgress(uint64_t aProgress, uint64_t aProgressMax)
{
AssertIsOnOwningThread();
FireProgressEvent(aProgress, aProgressMax);
}
void
IDBFileRequest::SetResultCallback(ResultCallback* aCallback)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aCallback);
AutoJSAPI autoJS;
if (NS_WARN_IF(!autoJS.Init(GetOwner()))) {
FireError(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return;
}
JSContext* cx = autoJS.cx();
JS::Rooted<JS::Value> result(cx);
nsresult rv = aCallback->GetResult(cx, &result);
if (NS_WARN_IF(NS_FAILED(rv))) {
FireError(rv);
} else {
FireSuccess(result);
}
}
void
IDBFileRequest::SetError(nsresult aError)
{
AssertIsOnOwningThread();
FireError(aError);
}
void
IDBFileRequest::FireProgressEvent(uint64_t aLoaded, uint64_t aTotal)
{
AssertIsOnOwningThread();
if (NS_FAILED(CheckInnerWindowCorrectness())) {
return;
}

View File

@ -10,10 +10,11 @@
#include "DOMRequest.h"
#include "js/TypeDecls.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/FileRequest.h"
#include "mozilla/dom/FileRequestBase.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
template <class> struct already_AddRefed;
class nsPIDOMWindow;
namespace mozilla {
@ -28,52 +29,65 @@ class IDBFileHandle;
class IDBFileRequest final : public DOMRequest,
public FileRequestBase
{
nsRefPtr<IDBFileHandle> mFileHandle;
bool mWrapAsDOMRequest;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBFileRequest, DOMRequest)
static already_AddRefed<IDBFileRequest>
Create(nsPIDOMWindow* aOwner, IDBFileHandle* aFileHandle,
bool aWrapAsDOMRequest);
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(EventChainPreVisitor& aVisitor) override;
// FileRequest
virtual void
OnProgress(uint64_t aProgress, uint64_t aProgressMax) override;
virtual nsresult
NotifyHelperCompleted(FileHelper* aFileHelper) override;
// nsWrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// WebIDL
IDBFileHandle*
GetFileHandle() const;
GetFileHandle() const
{
AssertIsOnOwningThread();
return mFileHandle;
}
IDBFileHandle*
GetLockedFile() const
{
AssertIsOnOwningThread();
return GetFileHandle();
}
IMPL_EVENT_HANDLER(progress)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBFileRequest, DOMRequest)
// nsIDOMEventTarget
virtual nsresult
PreHandleEvent(EventChainPreVisitor& aVisitor) override;
// nsWrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// FileRequestBase
virtual FileHandleBase*
FileHandle() const override;
virtual void
OnProgress(uint64_t aProgress, uint64_t aProgressMax) override;
virtual void
SetResultCallback(ResultCallback* aCallback) override;
virtual void
SetError(nsresult aError) override;
private:
explicit IDBFileRequest(nsPIDOMWindow* aWindow);
IDBFileRequest(nsPIDOMWindow* aWindow,
IDBFileHandle* aFileHandle,
bool aWrapAsDOMRequest);
~IDBFileRequest();
void
FireProgressEvent(uint64_t aLoaded, uint64_t aTotal);
nsRefPtr<IDBFileHandle> mFileHandle;
bool mWrapAsDOMRequest;
};
} // namespace indexedDB

View File

@ -6,8 +6,9 @@
#include "IDBMutableFile.h"
#include "FileSnapshot.h"
#include "ActorsChild.h"
#include "FileInfo.h"
#include "FileSnapshot.h"
#include "IDBDatabase.h"
#include "IDBFactory.h"
#include "IDBFileHandle.h"
@ -16,9 +17,9 @@
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/FileService.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/IDBMutableFileBinding.h"
#include "mozilla/dom/MetadataHelper.h"
#include "mozilla/dom/filehandle/ActorsChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
#include "mozilla/dom/quota/FileStreams.h"
#include "mozilla/dom/quota/QuotaManager.h"
@ -27,186 +28,191 @@
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsIInputStream.h"
#include "nsIPrincipal.h"
#include "ReportInternalError.h"
namespace mozilla {
namespace dom {
namespace indexedDB {
using namespace mozilla::dom::quota;
using namespace mozilla::ipc;
namespace {
class GetFileHelper : public MetadataHelper
{
public:
GetFileHelper(FileHandleBase* aFileHandle,
FileRequestBase* aFileRequest,
MetadataParameters* aParams,
IDBMutableFile* aMutableFile)
: MetadataHelper(aFileHandle, aFileRequest, aParams),
mMutableFile(aMutableFile)
{ }
virtual nsresult
GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal) override;
virtual void
ReleaseObjects() override
{
mMutableFile = nullptr;
MetadataHelper::ReleaseObjects();
}
private:
nsRefPtr<IDBMutableFile> mMutableFile;
};
already_AddRefed<nsIFile>
GetFileFor(FileInfo* aFileInfo)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aFileInfo);
FileManager* fileManager = aFileInfo->Manager();
MOZ_ASSERT(fileManager);
nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
if (NS_WARN_IF(!directory)) {
return nullptr;
}
nsCOMPtr<nsIFile> file =
fileManager->GetFileForId(directory, aFileInfo->Id());
if (NS_WARN_IF(!file)) {
return nullptr;
}
return file.forget();
}
} // namespace
IDBMutableFile::IDBMutableFile(IDBDatabase* aDatabase,
BackgroundMutableFileChild* aActor,
const nsAString& aName,
const nsAString& aType,
already_AddRefed<FileInfo> aFileInfo,
const nsACString& aGroup,
const nsACString& aOrigin,
const nsACString& aStorageId,
PersistenceType aPersistenceType,
already_AddRefed<nsIFile> aFile)
const nsAString& aType)
: DOMEventTargetHelper(aDatabase)
, MutableFileBase(DEBUGONLY(aDatabase->OwningThread(),)
aActor)
, mDatabase(aDatabase)
, mFileInfo(aFileInfo)
, mGroup(aGroup)
, mOrigin(aOrigin)
, mPersistenceType(aPersistenceType)
, mName(aName)
, mType(aType)
, mInvalidated(false)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDatabase);
MOZ_ASSERT(mFileInfo);
mName = aName;
mType = aType;
mFile = aFile;
mStorageId = aStorageId;
mFileName.AppendInt(mFileInfo->Id());
MOZ_ASSERT(mFile);
AssertIsOnOwningThread();
MOZ_ASSERT(aDatabase);
aDatabase->AssertIsOnOwningThread();
mDatabase->NoteLiveMutableFile(this);
}
IDBMutableFile::~IDBMutableFile()
{
// XXX This is always in the main process but it sometimes happens too late in
// shutdown and the IndexedDatabaseManager has already been torn down.
// MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
if (mDatabase) {
mDatabase->NoteFinishedMutableFile(this);
}
mDatabase->NoteFinishedMutableFile(this);
}
// static
already_AddRefed<IDBMutableFile>
IDBMutableFile::Create(IDBDatabase* aDatabase,
const nsAString& aName,
const nsAString& aType,
already_AddRefed<FileInfo> aFileInfo)
int64_t
IDBMutableFile::GetFileId() const
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
nsRefPtr<FileInfo> fileInfo(aFileInfo);
MOZ_ASSERT(fileInfo);
PrincipalInfo* principalInfo = aDatabase->Factory()->GetPrincipalInfo();
MOZ_ASSERT(principalInfo);
nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(*principalInfo);
if (NS_WARN_IF(!principal)) {
return nullptr;
int64_t fileId;
if (!mBackgroundActor ||
NS_WARN_IF(!mBackgroundActor->SendGetFileId(&fileId))) {
return -1;
}
nsCString group;
nsCString origin;
if (NS_WARN_IF(NS_FAILED(QuotaManager::GetInfoFromPrincipal(principal,
&group,
&origin,
nullptr)))) {
return nullptr;
}
const DatabaseSpec* spec = aDatabase->Spec();
MOZ_ASSERT(spec);
const DatabaseMetadata& metadata = spec->metadata();
PersistenceType persistenceType = metadata.persistenceType();
nsCString storageId;
QuotaManager::GetStorageId(persistenceType,
origin,
Client::IDB,
storageId);
storageId.Append('*');
storageId.Append(NS_ConvertUTF16toUTF8(metadata.name()));
nsCOMPtr<nsIFile> file = GetFileFor(fileInfo);
if (NS_WARN_IF(!file)) {
return nullptr;
}
nsRefPtr<IDBMutableFile> newFile =
new IDBMutableFile(aDatabase,
aName,
aType,
fileInfo.forget(),
group,
origin,
storageId,
persistenceType,
file.forget());
return newFile.forget();
return fileId;
}
void
IDBMutableFile::Invalidate()
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
MOZ_ASSERT(!mInvalidated);
mInvalidated = true;
AbortFileHandles();
}
void
IDBMutableFile::RegisterFileHandle(IDBFileHandle* aFileHandle)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFileHandle);
aFileHandle->AssertIsOnOwningThread();
MOZ_ASSERT(!mFileHandles.Contains(aFileHandle));
mFileHandles.PutEntry(aFileHandle);
}
void
IDBMutableFile::UnregisterFileHandle(IDBFileHandle* aFileHandle)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFileHandle);
aFileHandle->AssertIsOnOwningThread();
MOZ_ASSERT(mFileHandles.Contains(aFileHandle));
mFileHandles.RemoveEntry(aFileHandle);
}
void
IDBMutableFile::AbortFileHandles()
{
AssertIsOnOwningThread();
class MOZ_STACK_CLASS Helper final
{
public:
static void
AbortFileHandles(nsTHashtable<nsPtrHashKey<IDBFileHandle>>& aTable)
{
if (!aTable.Count()) {
return;
}
nsTArray<nsRefPtr<IDBFileHandle>> fileHandlesToAbort;
fileHandlesToAbort.SetCapacity(aTable.Count());
for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
IDBFileHandle* fileHandle = iter.Get()->GetKey();
MOZ_ASSERT(fileHandle);
fileHandle->AssertIsOnOwningThread();
if (!fileHandle->IsDone()) {
fileHandlesToAbort.AppendElement(iter.Get()->GetKey());
}
}
MOZ_ASSERT(fileHandlesToAbort.Length() <= aTable.Count());
if (fileHandlesToAbort.IsEmpty()) {
return;
}
for (nsRefPtr<IDBFileHandle>& fileHandle : fileHandlesToAbort) {
MOZ_ASSERT(fileHandle);
fileHandle->Abort();
}
}
};
Helper::AbortFileHandles(mFileHandles);
}
IDBDatabase*
IDBMutableFile::Database() const
{
AssertIsOnOwningThread();
return mDatabase;
}
already_AddRefed<IDBFileHandle>
IDBMutableFile::Open(FileMode aMode, ErrorResult& aError)
{
AssertIsOnOwningThread();
if (QuotaManager::IsShuttingDown() ||
mDatabase->IsClosed() ||
!GetOwner()) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
nsRefPtr<IDBFileHandle> fileHandle =
IDBFileHandle::Create(this, aMode);
if (NS_WARN_IF(!fileHandle)) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
BackgroundFileHandleChild* actor =
new BackgroundFileHandleChild(DEBUGONLY(mBackgroundActor->OwningThread(),)
fileHandle);
MOZ_ALWAYS_TRUE(
mBackgroundActor->SendPBackgroundFileHandleConstructor(actor, aMode));
fileHandle->SetBackgroundActor(actor);
return fileHandle.forget();
}
already_AddRefed<DOMRequest>
IDBMutableFile::GetFile(ErrorResult& aError)
{
nsRefPtr<IDBFileHandle> fileHandle = Open(FileMode::Readonly, aError);
if (NS_WARN_IF(aError.Failed())) {
return nullptr;
}
FileRequestGetFileParams params;
nsRefPtr<IDBFileRequest> request =
IDBFileRequest::Create(GetOwner(),
fileHandle,
/* aWrapAsDOMRequest */ true);
fileHandle->StartRequest(request, params);
return request.forget();
}
NS_IMPL_ADDREF_INHERITED(IDBMutableFile, DOMEventTargetHelper)
@ -219,193 +225,60 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IDBMutableFile)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBMutableFile,
DOMEventTargetHelper)
tmp->AssertIsOnOwningThread();
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBMutableFile,
DOMEventTargetHelper)
MOZ_ASSERT(tmp->mDatabase);
tmp->mDatabase->NoteFinishedMutableFile(tmp);
tmp->AssertIsOnOwningThread();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDatabase)
// Don't unlink mDatabase!
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
bool
IDBMutableFile::IsInvalid()
{
return mInvalidated;
}
nsIOfflineStorage*
IDBMutableFile::Storage()
{
MOZ_CRASH("Don't call me!");
}
already_AddRefed<nsISupports>
IDBMutableFile::CreateStream(bool aReadOnly)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
nsCOMPtr<nsISupports> result;
if (aReadOnly) {
nsRefPtr<FileInputStream> stream =
FileInputStream::Create(mPersistenceType,
mGroup,
mOrigin,
mFile,
-1,
-1,
nsIFileInputStream::DEFER_OPEN);
result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream);
} else {
nsRefPtr<FileStream> stream =
FileStream::Create(mPersistenceType,
mGroup,
mOrigin,
mFile,
-1,
-1,
nsIFileStream::DEFER_OPEN);
result = NS_ISUPPORTS_CAST(nsIFileStream*, stream);
}
if (NS_WARN_IF(!result)) {
return nullptr;
}
return result.forget();
}
JSObject*
IDBMutableFile::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
return IDBMutableFileBinding::Wrap(aCx, this, aGivenProto);
}
IDBDatabase*
IDBMutableFile::Database() const
const nsString&
IDBMutableFile::Name() const
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
return mDatabase;
return mName;
}
already_AddRefed<IDBFileHandle>
IDBMutableFile::Open(FileMode aMode, ErrorResult& aError)
const nsString&
IDBMutableFile::Type() const
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
if (QuotaManager::IsShuttingDown() || FileService::IsShuttingDown()) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
if (mDatabase->IsClosed()) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return nullptr;
}
MOZ_ASSERT(GetOwner());
nsRefPtr<IDBFileHandle> fileHandle =
IDBFileHandle::Create(aMode, FileHandleBase::NORMAL, this);
if (!fileHandle) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
return fileHandle.forget();
return mType;
}
int64_t
IDBMutableFile::GetFileId() const
bool
IDBMutableFile::IsInvalidated()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mFileInfo);
AssertIsOnOwningThread();
return mFileInfo->Id();
return mInvalidated;
}
already_AddRefed<File>
IDBMutableFile::CreateFileObject(IDBFileHandle* aFileHandle,
MetadataParameters* aMetadataParams)
IDBMutableFile::CreateFileFor(BlobImpl* aBlobImpl,
FileHandleBase* aFileHandle)
{
nsRefPtr<BlobImpl> impl =
new BlobImplSnapshot(mName,
mType,
aMetadataParams,
mFile,
aFileHandle,
mFileInfo);
AssertIsOnOwningThread();
nsRefPtr<File> file = File::Create(GetOwner(), impl);
MOZ_ASSERT(file);
nsRefPtr<BlobImpl> blobImplSnapshot =
new BlobImplSnapshot(aBlobImpl, static_cast<IDBFileHandle*>(aFileHandle));
nsRefPtr<File> file = File::Create(GetOwner(), blobImplSnapshot);
return file.forget();
}
already_AddRefed<DOMRequest>
IDBMutableFile::GetFile(ErrorResult& aError)
{
MOZ_ASSERT(NS_IsMainThread());
if (QuotaManager::IsShuttingDown() || FileService::IsShuttingDown()) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
if (mDatabase->IsClosed()) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
return nullptr;
}
MOZ_ASSERT(GetOwner());
nsRefPtr<IDBFileHandle> fileHandle =
IDBFileHandle::Create(FileMode::Readonly, FileHandleBase::PARALLEL, this);
nsRefPtr<IDBFileRequest> request =
IDBFileRequest::Create(GetOwner(),
fileHandle,
/* aWrapAsDOMRequest */ true);
nsRefPtr<MetadataParameters> params = new MetadataParameters(true, true);
nsRefPtr<GetFileHelper> helper =
new GetFileHelper(fileHandle, request, params, this);
nsresult rv = helper->Enqueue();
if (NS_FAILED(rv)) {
aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
return nullptr;
}
return request.forget();
}
nsresult
GetFileHelper::GetSuccessResult(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal)
{
MOZ_ASSERT(NS_IsMainThread());
auto fileHandle = static_cast<IDBFileHandle*>(mFileHandle.get());
nsRefPtr<File> domFile =
mMutableFile->CreateFileObject(fileHandle, mParams);
if (!ToJSValue(aCx, domFile, aVal)) {
return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR;
}
return NS_OK;
}
} // namespace indexedDB
} // namespace dom
} // namespace mozilla

View File

@ -10,13 +10,14 @@
#include "js/TypeDecls.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/FileModeBinding.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/FileModeBinding.h"
#include "mozilla/dom/MutableFile.h"
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/dom/MutableFileBase.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
#include "nsString.h"
#include "nsTHashtable.h"
class nsPIDOMWindow;
@ -28,11 +29,10 @@ namespace dom {
class DOMRequest;
class File;
class MetadataParameters;
namespace indexedDB {
class FileInfo;
class BackgroundMutableFileChild;
class IDBDatabase;
class IDBFileHandle;
@ -40,71 +40,43 @@ class IDBMutableFile final
: public DOMEventTargetHelper
, public MutableFileBase
{
typedef mozilla::dom::MetadataParameters MetadataParameters;
typedef mozilla::dom::quota::PersistenceType PersistenceType;
nsRefPtr<IDBDatabase> mDatabase;
nsTHashtable<nsPtrHashKey<IDBFileHandle>> mFileHandles;
nsString mName;
nsString mType;
nsRefPtr<IDBDatabase> mDatabase;
nsRefPtr<FileInfo> mFileInfo;
const nsCString mGroup;
const nsCString mOrigin;
const PersistenceType mPersistenceType;
Atomic<bool> mInvalidated;
public:
static already_AddRefed<IDBMutableFile>
Create(IDBDatabase* aDatabase,
const nsAString& aName,
const nsAString& aType,
already_AddRefed<FileInfo> aFileInfo);
IDBMutableFile(IDBDatabase* aDatabase,
BackgroundMutableFileChild* aActor,
const nsAString& aName,
const nsAString& aType);
const nsAString&
Name() const
void
SetLazyData(const nsAString& aName,
const nsAString& aType)
{
return mName;
}
const nsAString&
Type() const
{
return mType;
mName = aName;
mType = aType;
}
int64_t
GetFileId() const;
FileInfo*
GetFileInfo() const
{
return mFileInfo;
}
already_AddRefed<File>
CreateFileObject(IDBFileHandle* aFileHandle,
MetadataParameters* aMetadataParams);
void
Invalidate();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, DOMEventTargetHelper)
void
RegisterFileHandle(IDBFileHandle* aFileHandle);
virtual bool
IsInvalid() override;
void
UnregisterFileHandle(IDBFileHandle* aFileHandle);
virtual nsIOfflineStorage*
Storage() override;
virtual already_AddRefed<nsISupports>
CreateStream(bool aReadOnly) override;
// nsWrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void
AbortFileHandles();
// WebIDL
nsPIDOMWindow*
@ -137,17 +109,28 @@ public:
IMPL_EVENT_HANDLER(abort)
IMPL_EVENT_HANDLER(error)
private:
IDBMutableFile(IDBDatabase* aDatabase,
const nsAString& aName,
const nsAString& aType,
already_AddRefed<FileInfo> aFileInfo,
const nsACString& aGroup,
const nsACString& aOrigin,
const nsACString& aStorageId,
PersistenceType aPersistenceType,
already_AddRefed<nsIFile> aFile);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, DOMEventTargetHelper)
// nsWrapperCache
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// MutableFileBase
virtual const nsString&
Name() const override;
virtual const nsString&
Type() const override;
virtual bool
IsInvalidated() override;
virtual already_AddRefed<File>
CreateFileFor(BlobImpl* aBlobImpl,
FileHandleBase* aFileHandle) override;
private:
~IDBMutableFile();
};

View File

@ -61,20 +61,21 @@ using namespace mozilla::ipc;
struct IDBObjectStore::StructuredCloneWriteInfo
{
struct BlobOrFileInfo
struct BlobOrMutableFile
{
nsRefPtr<Blob> mBlob;
nsRefPtr<FileInfo> mFileInfo;
nsRefPtr<IDBMutableFile> mMutableFile;
bool
operator==(const BlobOrFileInfo& aOther) const
operator==(const BlobOrMutableFile& aOther) const
{
return this->mBlob == aOther.mBlob && this->mFileInfo == aOther.mFileInfo;
return this->mBlob == aOther.mBlob &&
this->mMutableFile == aOther.mMutableFile;
}
};
JSAutoStructuredCloneBuffer mCloneBuffer;
nsTArray<BlobOrFileInfo> mBlobOrFileInfos;
nsTArray<BlobOrMutableFile> mBlobOrMutableFiles;
IDBDatabase* mDatabase;
uint64_t mOffsetToKeyProp;
@ -96,7 +97,7 @@ struct IDBObjectStore::StructuredCloneWriteInfo
MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
mBlobOrFileInfos.SwapElements(aCloneWriteInfo.mBlobOrFileInfos);
mBlobOrMutableFiles.SwapElements(aCloneWriteInfo.mBlobOrMutableFiles);
aCloneWriteInfo.mOffsetToKeyProp = 0;
}
@ -110,7 +111,7 @@ struct IDBObjectStore::StructuredCloneWriteInfo
{
return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() &&
this->mCloneBuffer.data() == aOther.mCloneBuffer.data() &&
this->mBlobOrFileInfos == aOther.mBlobOrFileInfos &&
this->mBlobOrMutableFiles == aOther.mBlobOrMutableFiles &&
this->mDatabase == aOther.mDatabase &&
this->mOffsetToKeyProp == aOther.mOffsetToKeyProp;
}
@ -129,7 +130,7 @@ struct IDBObjectStore::StructuredCloneWriteInfo
}
}
mBlobOrFileInfos.Clear();
mBlobOrMutableFiles.Clear();
mOffsetToKeyProp = aOther.offsetToKeyProp();
return true;
@ -234,6 +235,10 @@ StructuredCloneWriteCallback(JSContext* aCx,
IDBMutableFile* mutableFile;
if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, aObj, mutableFile))) {
if (cloneWriteInfo->mDatabase->IsFileHandleDisabled()) {
return false;
}
IDBDatabase* database = mutableFile->Database();
MOZ_ASSERT(database);
@ -266,15 +271,12 @@ StructuredCloneWriteCallback(JSContext* aCx,
}
}
nsRefPtr<FileInfo> fileInfo = mutableFile->GetFileInfo();
MOZ_ASSERT(fileInfo);
if (cloneWriteInfo->mBlobOrFileInfos.Length() > size_t(UINT32_MAX)) {
if (cloneWriteInfo->mBlobOrMutableFiles.Length() > size_t(UINT32_MAX)) {
MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
return false;
}
const uint32_t index = uint32_t(cloneWriteInfo->mBlobOrFileInfos.Length());
const uint32_t index = cloneWriteInfo->mBlobOrMutableFiles.Length();
NS_ConvertUTF16toUTF8 convType(mutableFile->Type());
uint32_t convTypeLength =
@ -292,10 +294,10 @@ StructuredCloneWriteCallback(JSContext* aCx,
return false;
}
IDBObjectStore::StructuredCloneWriteInfo::BlobOrFileInfo*
newBlobOrFileInfo =
cloneWriteInfo->mBlobOrFileInfos.AppendElement();
newBlobOrFileInfo->mFileInfo.swap(fileInfo);
IDBObjectStore::StructuredCloneWriteInfo::BlobOrMutableFile*
newBlobOrMutableFile =
cloneWriteInfo->mBlobOrMutableFiles.AppendElement();
newBlobOrMutableFile->mMutableFile = mutableFile;
return true;
}
@ -316,14 +318,13 @@ StructuredCloneWriteCallback(JSContext* aCx,
uint32_t convTypeLength =
NativeEndian::swapToLittleEndian(convType.Length());
if (cloneWriteInfo->mBlobOrFileInfos.Length() > size_t(UINT32_MAX)) {
if (cloneWriteInfo->mBlobOrMutableFiles.Length() > size_t(UINT32_MAX)) {
MOZ_ASSERT(false,
"Fix the structured clone data to use a bigger type!");
return false;
}
const uint32_t index =
uint32_t(cloneWriteInfo->mBlobOrFileInfos.Length());
const uint32_t index = cloneWriteInfo->mBlobOrMutableFiles.Length();
if (!JS_WriteUint32Pair(aWriter,
blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
@ -349,17 +350,17 @@ StructuredCloneWriteCallback(JSContext* aCx,
uint32_t convNameLength =
NativeEndian::swapToLittleEndian(convName.Length());
if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) ||
if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) ||
!JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
!JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
return false;
}
}
IDBObjectStore::StructuredCloneWriteInfo::BlobOrFileInfo*
newBlobOrFileInfo =
cloneWriteInfo->mBlobOrFileInfos.AppendElement();
newBlobOrFileInfo->mBlob = blob;
IDBObjectStore::StructuredCloneWriteInfo::BlobOrMutableFile*
newBlobOrMutableFile =
cloneWriteInfo->mBlobOrMutableFiles.AppendElement();
newBlobOrMutableFile->mBlob = blob;
return true;
}
@ -430,6 +431,16 @@ ActorFromRemoteBlobImpl(BlobImpl* aImpl)
return nullptr;
}
bool
ResolveMysteryMutableFile(IDBMutableFile* aMutableFile,
const nsString& aName,
const nsString& aType)
{
MOZ_ASSERT(aMutableFile);
aMutableFile->SetLazyData(aName, aType);
return true;
}
bool
ResolveMysteryFile(BlobImpl* aImpl,
const nsString& aName,
@ -573,31 +584,29 @@ class ValueDeserializationHelper
public:
static bool
CreateAndWrapMutableFile(JSContext* aCx,
IDBDatabase* aDatabase,
StructuredCloneFile& aFile,
const MutableFileData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aDatabase);
MOZ_ASSERT(aFile.mFileInfo);
MOZ_ASSERT(aCx);
MOZ_ASSERT(aFile.mMutable);
if (!IndexedDatabaseManager::IsMainProcess() || !NS_IsMainThread()) {
if (!aFile.mMutableFile || !NS_IsMainThread()) {
return false;
}
nsRefPtr<IDBMutableFile> mutableFile =
IDBMutableFile::Create(aDatabase,
aData.name,
aData.type,
aFile.mFileInfo.forget());
MOZ_ASSERT(mutableFile);
JS::Rooted<JSObject*> result(aCx, mutableFile->WrapObject(aCx, nullptr));
if (NS_WARN_IF(!result)) {
if (NS_WARN_IF(!ResolveMysteryMutableFile(aFile.mMutableFile,
aData.name,
aData.type))) {
return false;
}
aResult.set(result);
JS::Rooted<JS::Value> wrappedMutableFile(aCx);
if (!ToJSValue(aCx, aFile.mMutableFile, &wrappedMutableFile)) {
return false;
}
aResult.set(&wrappedMutableFile.toObject());
return true;
}
@ -608,9 +617,11 @@ public:
const BlobOrFileData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
aData.tag == SCTAG_DOM_BLOB);
MOZ_ASSERT(!aFile.mMutable);
MOZ_ASSERT(aFile.mBlob);
// It can happen that this IDB is chrome code, so there is no parent, but
@ -679,13 +690,10 @@ class IndexDeserializationHelper
public:
static bool
CreateAndWrapMutableFile(JSContext* aCx,
IDBDatabase* aDatabase,
StructuredCloneFile& aFile,
const MutableFileData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(!aDatabase);
// MutableFile can't be used in index creation, so just make a dummy object.
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
@ -769,6 +777,64 @@ public:
}
};
// We don't need to upgrade database on B2G. See the comment in ActorsParent.cpp,
// UpgradeSchemaFrom18_0To19_0()
#if !defined(MOZ_B2G)
class UpgradeDeserializationHelper
{
public:
static bool
CreateAndWrapMutableFile(JSContext* aCx,
StructuredCloneFile& aFile,
const MutableFileData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(!aFile.mMutable);
aFile.mMutable = true;
// Just make a dummy object. The file_ids upgrade function is only
// interested in the |mMutable| flag.
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
return false;
}
aResult.set(obj);
return true;
}
static bool
CreateAndWrapBlobOrFile(JSContext* aCx,
IDBDatabase* aDatabase,
StructuredCloneFile& aFile,
const BlobOrFileData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(!aFile.mMutable);
MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
aData.tag == SCTAG_DOM_BLOB);
// Just make a dummy object. The file_ids upgrade function is only interested
// in the |mMutable| flag.
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
return false;
}
aResult.set(obj);
return true;
}
};
#endif // MOZ_B2G
template <class Traits>
JSObject*
CommonStructuredCloneReadCallback(JSContext* aCx,
@ -808,7 +874,6 @@ CommonStructuredCloneReadCallback(JSContext* aCx,
}
if (NS_WARN_IF(!Traits::CreateAndWrapMutableFile(aCx,
cloneReadInfo->mDatabase,
file,
data,
&result))) {
@ -1051,6 +1116,8 @@ IDBObjectStore::DeserializeValue(JSContext* aCx,
nullptr
};
// FIXME: Consider to use StructuredCloneHelper here and in other
// deserializing methods.
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
@ -1097,6 +1164,51 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
return true;
}
#if !defined(MOZ_B2G)
// static
bool
IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
StructuredCloneReadInfo& aCloneReadInfo,
JS::MutableHandle<JS::Value> aValue)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aCx);
if (aCloneReadInfo.mData.IsEmpty()) {
aValue.setUndefined();
return true;
}
size_t dataLen = aCloneReadInfo.mData.Length();
uint64_t* data =
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
aCloneReadInfo.mData.Elements()));
MOZ_ASSERT(!(dataLen % sizeof(*data)));
JSAutoRequest ar(aCx);
static JSStructuredCloneCallbacks callbacks = {
CommonStructuredCloneReadCallback<UpgradeDeserializationHelper>,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
};
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
}
return true;
}
#endif // MOZ_B2G
#ifdef DEBUG
void
@ -1237,17 +1349,15 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
commonParams.key() = key;
commonParams.indexUpdateInfos().SwapElements(updateInfo);
// Convert any blobs or fileIds into DatabaseFileOrMutableFileId.
nsTArray<StructuredCloneWriteInfo::BlobOrFileInfo>& blobOrFileInfos =
cloneWriteInfo.mBlobOrFileInfos;
// Convert any blobs or mutable files into DatabaseOrMutableFile.
nsTArray<StructuredCloneWriteInfo::BlobOrMutableFile>& blobOrMutableFiles =
cloneWriteInfo.mBlobOrMutableFiles;
FallibleTArray<nsRefPtr<FileInfo>> fileInfosToKeepAlive;
if (!blobOrMutableFiles.IsEmpty()) {
const uint32_t count = blobOrMutableFiles.Length();
if (!blobOrFileInfos.IsEmpty()) {
const uint32_t count = blobOrFileInfos.Length();
FallibleTArray<DatabaseFileOrMutableFileId> fileActorOrMutableFileIds;
if (NS_WARN_IF(!fileActorOrMutableFileIds.SetCapacity(count, fallible))) {
FallibleTArray<DatabaseOrMutableFile> fileOrMutableFileActors;
if (NS_WARN_IF(!fileOrMutableFileActors.SetCapacity(count, fallible))) {
aRv = NS_ERROR_OUT_OF_MEMORY;
return nullptr;
}
@ -1255,41 +1365,37 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
IDBDatabase* database = mTransaction->Database();
for (uint32_t index = 0; index < count; index++) {
StructuredCloneWriteInfo::BlobOrFileInfo& blobOrFileInfo =
blobOrFileInfos[index];
MOZ_ASSERT((blobOrFileInfo.mBlob && !blobOrFileInfo.mFileInfo) ||
(!blobOrFileInfo.mBlob && blobOrFileInfo.mFileInfo));
StructuredCloneWriteInfo::BlobOrMutableFile& blobOrMutableFile =
blobOrMutableFiles[index];
MOZ_ASSERT((blobOrMutableFile.mBlob && !blobOrMutableFile.mMutableFile) ||
(!blobOrMutableFile.mBlob && blobOrMutableFile.mMutableFile));
if (blobOrFileInfo.mBlob) {
if (blobOrMutableFile.mBlob) {
PBackgroundIDBDatabaseFileChild* fileActor =
database->GetOrCreateFileActorForBlob(blobOrFileInfo.mBlob);
database->GetOrCreateFileActorForBlob(blobOrMutableFile.mBlob);
if (NS_WARN_IF(!fileActor)) {
IDB_REPORT_INTERNAL_ERR();
aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
return nullptr;
}
MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileActor,
fallible));
MOZ_ALWAYS_TRUE(fileOrMutableFileActors.AppendElement(fileActor,
fallible));
} else {
const int64_t fileId = blobOrFileInfo.mFileInfo->Id();
MOZ_ASSERT(fileId > 0);
MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileId,
fallible));
nsRefPtr<FileInfo>* newFileInfo =
fileInfosToKeepAlive.AppendElement(fallible);
if (NS_WARN_IF(!newFileInfo)) {
aRv = NS_ERROR_OUT_OF_MEMORY;
PBackgroundMutableFileChild* mutableFileActor =
blobOrMutableFile.mMutableFile->GetBackgroundActor();
if (NS_WARN_IF(!mutableFileActor)) {
IDB_REPORT_INTERNAL_ERR();
aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
return nullptr;
}
newFileInfo->swap(blobOrFileInfo.mFileInfo);
MOZ_ALWAYS_TRUE(fileOrMutableFileActors.AppendElement(mutableFileActor,
fallible));
}
}
commonParams.files().SwapElements(fileActorOrMutableFileIds);
commonParams.files().SwapElements(fileOrMutableFileActors);
}
RequestParams params;
@ -1328,16 +1434,7 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
}
}
BackgroundRequestChild* actor = mTransaction->StartRequest(request, params);
MOZ_ASSERT(actor);
if (!fileInfosToKeepAlive.IsEmpty()) {
nsTArray<nsRefPtr<FileInfo>> fileInfos;
fileInfosToKeepAlive.SwapElements(fileInfos);
actor->HoldFileInfosUntilComplete(fileInfos);
MOZ_ASSERT(fileInfos.IsEmpty());
}
mTransaction->StartRequest(request, params);
return request.forget();
}

View File

@ -93,6 +93,13 @@ public:
StructuredCloneReadInfo& aCloneReadInfo,
JS::MutableHandle<JS::Value> aValue);
#if !defined(MOZ_B2G)
static bool
DeserializeUpgradeValue(JSContext* aCx,
StructuredCloneReadInfo& aCloneReadInfo,
JS::MutableHandle<JS::Value> aValue);
#endif
static const JSClass*
DummyPropClass()
{

View File

@ -495,9 +495,12 @@ private:
Notify(JSContext* aCx, Status aStatus) override;
};
IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner)
IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory,
nsPIDOMWindow* aOwner,
bool aFileHandleDisabled)
: IDBRequest(aOwner)
, mFactory(aFactory)
, mFileHandleDisabled(aFileHandleDisabled)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFactory);
@ -521,7 +524,10 @@ IDBOpenDBRequest::CreateForWindow(IDBFactory* aFactory,
MOZ_ASSERT(aOwner);
MOZ_ASSERT(aScriptOwner);
nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aFactory, aOwner);
bool fileHandleDisabled = !IndexedDatabaseManager::IsFileHandleEnabled();
nsRefPtr<IDBOpenDBRequest> request =
new IDBOpenDBRequest(aFactory, aOwner, fileHandleDisabled);
CaptureCaller(request->mFilename, &request->mLineNo, &request->mColumn);
request->SetScriptOwner(aScriptOwner);
@ -538,7 +544,10 @@ IDBOpenDBRequest::CreateForJS(IDBFactory* aFactory,
aFactory->AssertIsOnOwningThread();
MOZ_ASSERT(aScriptOwner);
nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aFactory, nullptr);
bool fileHandleDisabled = !IndexedDatabaseManager::IsFileHandleEnabled();
nsRefPtr<IDBOpenDBRequest> request =
new IDBOpenDBRequest(aFactory, nullptr, fileHandleDisabled);
CaptureCaller(request->mFilename, &request->mLineNo, &request->mColumn);
request->SetScriptOwner(aScriptOwner);

View File

@ -233,6 +233,8 @@ class IDBOpenDBRequest final
nsAutoPtr<WorkerFeature> mWorkerFeature;
const bool mFileHandleDisabled;
public:
static already_AddRefed<IDBOpenDBRequest>
CreateForWindow(IDBFactory* aFactory,
@ -243,6 +245,12 @@ public:
CreateForJS(IDBFactory* aFactory,
JS::Handle<JSObject*> aScriptOwner);
bool
IsFileHandleDisabled() const
{
return mFileHandleDisabled;
}
void
SetTransaction(IDBTransaction* aTransaction);
@ -270,7 +278,9 @@ public:
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
private:
IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner);
IDBOpenDBRequest(IDBFactory* aFactory,
nsPIDOMWindow* aOwner,
bool aFileHandleDisabled);
~IDBOpenDBRequest();
};

View File

@ -21,12 +21,15 @@ namespace indexedDB {
class FileInfo;
class IDBDatabase;
class IDBMutableFile;
class SerializedStructuredCloneReadInfo;
struct StructuredCloneFile
{
nsRefPtr<Blob> mBlob;
nsRefPtr<IDBMutableFile> mMutableFile;
nsRefPtr<FileInfo> mFileInfo;
bool mMutable;
// In IndexedDatabaseInlines.h
inline

View File

@ -12,6 +12,7 @@
#endif
#include "FileInfo.h"
#include "IDBMutableFile.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
#include "mozilla/dom/File.h"
#include "nsIInputStream.h"
@ -22,6 +23,7 @@ namespace indexedDB {
inline
StructuredCloneFile::StructuredCloneFile()
: mMutable(false)
{
MOZ_COUNT_CTOR(StructuredCloneFile);
}
@ -37,7 +39,9 @@ bool
StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const
{
return this->mBlob == aOther.mBlob &&
this->mFileInfo == aOther.mFileInfo;
this->mMutableFile == aOther.mMutableFile &&
this->mFileInfo == aOther.mFileInfo &&
this->mMutable == aOther.mMutable;
}
inline

View File

@ -126,6 +126,7 @@ const uint32_t kDeleteTimeoutMs = 1000;
const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
const char kPrefExperimental[] = IDB_PREF_BRANCH_ROOT "experimental";
const char kPrefFileHandle[] = "dom.fileHandle.enabled";
#define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."
@ -146,6 +147,7 @@ Atomic<bool> gInitialized(false);
Atomic<bool> gClosed(false);
Atomic<bool> gTestingMode(false);
Atomic<bool> gExperimentalFeaturesEnabled(false);
Atomic<bool> gFileHandleEnabled(false);
class DeleteFilesRunnable final
: public nsIRunnable
@ -381,6 +383,9 @@ IndexedDatabaseManager::Init()
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kPrefExperimental,
&gExperimentalFeaturesEnabled);
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kPrefFileHandle,
&gFileHandleEnabled);
// By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
// guarantees (unlike synchronous = OFF) atomicity and consistency, but not
@ -446,6 +451,9 @@ IndexedDatabaseManager::Destroy()
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kPrefExperimental,
&gExperimentalFeaturesEnabled);
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
kPrefFileHandle,
&gFileHandleEnabled);
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
@ -742,6 +750,17 @@ IndexedDatabaseManager::ExperimentalFeaturesEnabled()
return gExperimentalFeaturesEnabled;
}
// static
bool
IndexedDatabaseManager::IsFileHandleEnabled()
{
MOZ_ASSERT(gDBManager,
"IsFileHandleEnabled() called before indexedDB has been "
"initialized!");
return gFileHandleEnabled;
}
already_AddRefed<FileManager>
IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
const nsACString& aOrigin,

View File

@ -118,6 +118,9 @@ public:
return ExperimentalFeaturesEnabled();
}
static bool
IsFileHandleEnabled();
already_AddRefed<FileManager>
GetFileManager(PersistenceType aPersistenceType,
const nsACString& aOrigin,

View File

@ -4,6 +4,7 @@
include protocol PBackgroundIDBTransaction;
include protocol PBackgroundIDBVersionChangeTransaction;
include protocol PBackgroundMutableFile;
include protocol PBlob;
include PBackgroundIDBSharedTypes;

View File

@ -3,9 +3,11 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBackgroundIDBDatabaseFile;
include protocol PBackgroundIDBDatabaseRequest;
include protocol PBackgroundIDBFactory;
include protocol PBackgroundIDBTransaction;
include protocol PBackgroundIDBVersionChangeTransaction;
include protocol PBackgroundMutableFile;
include protocol PBlob;
include InputStreamParams;
@ -23,19 +25,32 @@ namespace mozilla {
namespace dom {
namespace indexedDB {
struct CreateFileParams
{
nsString name;
nsString type;
};
union DatabaseRequestParams
{
CreateFileParams;
};
union NullableVersion
{
null_t;
uint64_t;
};
protocol PBackgroundIDBDatabase
sync protocol PBackgroundIDBDatabase
{
manager PBackgroundIDBFactory;
manages PBackgroundIDBDatabaseFile;
manages PBackgroundIDBDatabaseRequest;
manages PBackgroundIDBTransaction;
manages PBackgroundIDBVersionChangeTransaction;
manages PBackgroundMutableFile;
parent:
DeleteMe();
@ -44,12 +59,10 @@ parent:
Close();
NewFileHandle();
FileHandleFinished();
PBackgroundIDBDatabaseFile(PBlob blob);
PBackgroundIDBDatabaseRequest(DatabaseRequestParams params);
PBackgroundIDBTransaction(nsString[] objectStoreNames, Mode mode);
child:
@ -63,6 +76,8 @@ child:
uint64_t requestedVersion,
int64_t nextObjectStoreId,
int64_t nextIndexId);
PBackgroundMutableFile(nsString name, nsString type);
};
} // namespace indexedDB

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/. */
include protocol PBackgroundIDBDatabase;
include protocol PBackgroundMutableFile;
namespace mozilla {
namespace dom {
namespace indexedDB {
struct CreateFileRequestResponse
{
PBackgroundMutableFile mutableFile;
};
union DatabaseRequestResponse
{
nsresult;
CreateFileRequestResponse;
};
protocol PBackgroundIDBDatabaseRequest
{
manager PBackgroundIDBDatabase;
child:
__delete__(DatabaseRequestResponse response);
};
} // namespace indexedDB
} // namespace dom
} // namespace mozilla

View File

@ -39,7 +39,7 @@ union FactoryRequestParams
DeleteDatabaseRequestParams;
};
protocol PBackgroundIDBFactory
sync protocol PBackgroundIDBFactory
{
manager PBackground;

View File

@ -4,6 +4,7 @@
include protocol PBackgroundIDBTransaction;
include protocol PBackgroundIDBVersionChangeTransaction;
include protocol PBackgroundMutableFile;
include protocol PBlob;
include PBackgroundIDBSharedTypes;

View File

@ -2,13 +2,17 @@
* 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 protocol PBlob;
include protocol PBackgroundIDBDatabaseFile;
include protocol PBackgroundMutableFile;
include protocol PBlob;
include DOMTypes;
include "mozilla/dom/indexedDB/SerializationHelpers.h";
using struct mozilla::null_t
from "ipc/IPCMessageUtils.h";
using struct mozilla::void_t
from "ipc/IPCMessageUtils.h";
@ -37,13 +41,22 @@ struct SerializedKeyRange
bool isOnly;
};
union NullableMutableFile
{
null_t;
PBackgroundMutableFile;
};
union BlobOrMutableFile
{
PBlob;
NullableMutableFile;
};
struct SerializedStructuredCloneReadInfo
{
uint8_t[] data;
PBlob[] blobs;
// This will only be valid for parent process actors.
intptr_t[] fileInfos;
BlobOrMutableFile[] blobs;
};
struct SerializedStructuredCloneWriteInfo
@ -141,11 +154,10 @@ union OpenCursorParams
IndexOpenKeyCursorParams;
};
// XXX Remove this once MutableFile has been ported to PBackground.
union DatabaseFileOrMutableFileId
union DatabaseOrMutableFile
{
PBackgroundIDBDatabaseFile;
int64_t;
PBackgroundMutableFile;
};
struct ObjectStoreAddPutParams
@ -154,7 +166,7 @@ struct ObjectStoreAddPutParams
SerializedStructuredCloneWriteInfo cloneInfo;
Key key;
IndexUpdateInfo[] indexUpdateInfos;
DatabaseFileOrMutableFileId[] files;
DatabaseOrMutableFile[] files;
};
struct ObjectStoreAddParams

View File

@ -6,6 +6,7 @@ include protocol PBackgroundIDBCursor;
include protocol PBackgroundIDBDatabase;
include protocol PBackgroundIDBDatabaseFile;
include protocol PBackgroundIDBRequest;
include protocol PBackgroundMutableFile;
include PBackgroundIDBSharedTypes;

View File

@ -6,6 +6,7 @@ include protocol PBackgroundIDBCursor;
include protocol PBackgroundIDBDatabase;
include protocol PBackgroundIDBDatabaseFile;
include protocol PBackgroundIDBRequest;
include protocol PBackgroundMutableFile;
include protocol PBlob;
include PBackgroundIDBSharedTypes;

View File

@ -77,6 +77,7 @@ IPDL_SOURCES += [
'PBackgroundIDBCursor.ipdl',
'PBackgroundIDBDatabase.ipdl',
'PBackgroundIDBDatabaseFile.ipdl',
'PBackgroundIDBDatabaseRequest.ipdl',
'PBackgroundIDBFactory.ipdl',
'PBackgroundIDBFactoryRequest.ipdl',
'PBackgroundIDBRequest.ipdl',

View File

@ -172,6 +172,16 @@ function verifyBlobArray(blobs1, blobs2, expectedFileIds)
expectedFileIds[verifiedCount], blobReadHandler);
}
function verifyMutableFile(mutableFile1, file2)
{
ok(mutableFile1 instanceof IDBMutableFile, "Instance of IDBMutableFile");
is(mutableFile1.name, file2.name, "Correct name");
is(mutableFile1.type, file2.type, "Correct type");
executeSoon(function() {
testGenerator.next();
});
}
function grabFileUsageAndContinueHandler(usage, fileUsage)
{
testGenerator.send(fileUsage);

View File

@ -471,6 +471,16 @@ function workerScript() {
return false;
}
self.getRandomBuffer = function(_size_) {
let buffer = new ArrayBuffer(_size_);
is(buffer.byteLength, _size_, "Correct byte length");
let view = new Uint8Array(buffer);
for (let i = 0; i < _size_; i++) {
view[i] = parseInt(Math.random() * 255)
}
return buffer;
};
self.onerror = function(_message_, _file_, _line_) {
ok(false,
"Worker: uncaught exception [" + _file_ + ":" + _line_ + "]: '" +

View File

@ -39,6 +39,7 @@ support-files =
unit/test_deleteDatabase.js
unit/test_deleteDatabase_interactions.js
unit/test_event_source.js
unit/test_filehandle_append_read_data.js
unit/test_getAll.js
unit/test_globalObjects_ipc.js
unit/test_globalObjects_other.js
@ -197,65 +198,46 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_file_transaction_abort.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_append_read_data.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_compat.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_getFile.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_iteration.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_lifetimes.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_lifetimes_nested.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_location.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_ordering.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_overlapping.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_progress_events.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_readonly_exceptions.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_request_readyState.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_serialization.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_store_snapshot.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_stream_tracking.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_success_events_after_abort.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_truncate.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_workers.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_filehandle_write_read_data.html]
# FileHandle is not supported in child processes.
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
[test_getAll.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_get_filehandle.html]
[test_filehandle_compat.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_disabled_pref.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_getFile.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_iteration.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_lifetimes.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_lifetimes_nested.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_location.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_ordering.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_overlapping.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_progress_events.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_readonly_exceptions.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_request_readyState.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_serialization.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_store_snapshot.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_stream_tracking.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_success_events_after_abort.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_truncate.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_workers.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_filehandle_write_read_data.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_getAll.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_globalObjects_content.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116

View File

@ -9,97 +9,7 @@
<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 name = window.location.pathname;
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 = new Blob([testBuffer], {type: "binary/random"});
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield undefined;
let db = event.target.result;
db.onerror = errorHandler;
request = db.createMutableFile("test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let mutableFile = event.target.result;
mutableFile.onerror = errorHandler;
let location = 0;
let fileHandle = mutableFile.open("readwrite");
is(fileHandle.location, location, "Correct location");
request = fileHandle.append(testString);
ok(fileHandle.location === null, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
fileHandle.location = 0;
request = fileHandle.readAsText(testString.length);
location += testString.length
is(fileHandle.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let resultString = event.target.result;
ok(resultString == testString, "Correct string data");
request = fileHandle.append(testBuffer);
ok(fileHandle.location === null, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
fileHandle.location = location;
request = fileHandle.readAsArrayBuffer(testBuffer.byteLength);
location += testBuffer.byteLength;
is(fileHandle.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
request = fileHandle.append(testBlob);
ok(fileHandle.location === null, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
fileHandle.location = location;
request = fileHandle.readAsArrayBuffer(testBlob.size);
location += testBlob.size;
is(fileHandle.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
request = fileHandle.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let result = event.target.result;
is(result.size, location, "Correct size");
finishTest();
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="unit/test_filehandle_append_read_data.js"></script>
<script type="text/javascript;version=1.7" src="file.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>

View File

@ -35,6 +35,9 @@
request = lockedFile.getMetadata({ size: true });
ok(request.lockedFile === lockedFile, "Correct property");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
finishTest();
yield undefined;

View File

@ -0,0 +1,204 @@
<!--
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 databaseName = window.location.pathname;
const databaseVersion = 1;
const objectStoreName = "foo";
const mutableFileName = "bar";
const mutableFileKey = 42;
info("opening database");
let request = indexedDB.open(databaseName, databaseVersion);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
request.onsuccess = unexpectedSuccessHandler;
let event = yield undefined;
info("creating object store");
let db = event.target.result;
db.onerror = errorHandler;
db.onversionchange = function(event) {
is(event.oldVersion, databaseVersion, "got correct oldVersion");
is(event.newVersion, null, "got correct newVersion");
db.close();
};
let objectStore = db.createObjectStore(objectStoreName,
{ autoIncrement: true });
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
info("creating mutable file");
request = db.createMutableFile(mutableFileName);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let mutableFile = event.target.result;
verifyMutableFile(mutableFile, getFile(mutableFileName, "", ""));
yield undefined;
objectStore = db.transaction([objectStoreName], "readwrite")
.objectStore(objectStoreName);
info("adding mutable file");
request = objectStore.add(mutableFile, mutableFileKey);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
info("getting mutable file");
request = objectStore.get(mutableFileKey);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
verifyMutableFile(event.target.result,
getFile(mutableFileName, "", ""));
yield undefined;
info("opening database");
request = indexedDB.open(databaseName, databaseVersion);
request.onerror = errorHandler;
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let db2 = event.target.result;
db2.onerror = errorHandler;
db2.onversionchange = function(event) {
is(event.oldVersion, databaseVersion, "got correct oldVersion");
is(event.newVersion, null, "got correct newVersion");
db2.close();
};
objectStore = db2.transaction([objectStoreName], "readwrite")
.objectStore(objectStoreName);
info("adding mutable file");
request = objectStore.add(mutableFile);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
info("getting mutable file");
request = objectStore.get(event.target.result);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
verifyMutableFile(event.target.result,
getFile(mutableFileName, "", ""));
yield undefined;
info("setting file handle pref");
SpecialPowers.pushPrefEnv({ set: [["dom.fileHandle.enabled", false]] },
continueToNextStep);
yield undefined;
info("opening database");
request = indexedDB.open(databaseName, databaseVersion);
request.onerror = errorHandler;
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let db3 = event.target.result;
db3.onerror = errorHandler;
db3.onversionchange = function(event) {
is(event.oldVersion, databaseVersion, "got correct oldVersion");
is(event.newVersion, null, "got correct newVersion");
db3.close();
};
info("creating mutable file");
try {
db3.createMutableFile(mutableFileName);
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got exception.");
is(e.name, "InvalidStateError", "Good error.");
is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
}
objectStore = db3.transaction([objectStoreName], "readwrite")
.objectStore(objectStoreName);
info("adding mutable file");
try {
objectStore.add(mutableFile);
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.");
}
info("getting mutable file");
request = objectStore.get(mutableFileKey);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
try {
let result = request.result;
ok(false, "Should have thrown!");
}
catch (e) {
ok(e instanceof DOMException, "Got exception.");
is(e.name, "InvalidStateError", "Good error.");
is(e.code, DOMException.INVALID_STATE_ERR, "Good error code.");
}
info("deleting database");
request = indexedDB.deleteDatabase(databaseName);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
info("resetting file handle pref");
SpecialPowers.popPrefEnv(continueToNextStep);
yield undefined;
finishTest();
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
<script type="text/javascript;version=1.7" src="file.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -1,53 +0,0 @@
<!--
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 name = window.location.pathname;
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield undefined;
let db = event.target.result;
db.onerror = errorHandler;
if (SpecialPowers.isMainProcess()) {
request = db.createMutableFile("random.bin", "binary/random");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let mutableFile = event.target.result;
ok(mutableFile, "Got mutablefile");
}
else {
try {
db.createMutableFile("random.bin", "binary/random");
ok(false, "Should have thrown!");
}
catch (ex) {
ok(true, "CreateMutableFile threw");
}
}
finishTest();
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

Binary file not shown.

View File

@ -0,0 +1,98 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
let disableWorkerTest = "FileHandle doesn't work in workers yet";
var testGenerator = testSteps();
function testSteps()
{
const name = this.window ? window.location.pathname : "Splendid Test";
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 = new Blob([testBuffer], {type: "binary/random"});
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield undefined;
let db = event.target.result;
db.onerror = errorHandler;
request = db.createMutableFile("test.txt");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let mutableFile = event.target.result;
mutableFile.onerror = errorHandler;
let location = 0;
let fileHandle = mutableFile.open("readwrite");
is(fileHandle.location, location, "Correct location");
request = fileHandle.append(testString);
ok(fileHandle.location === null, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
fileHandle.location = 0;
request = fileHandle.readAsText(testString.length);
location += testString.length
is(fileHandle.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let resultString = event.target.result;
ok(resultString == testString, "Correct string data");
request = fileHandle.append(testBuffer);
ok(fileHandle.location === null, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
fileHandle.location = location;
request = fileHandle.readAsArrayBuffer(testBuffer.byteLength);
location += testBuffer.byteLength;
is(fileHandle.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct array buffer data");
request = fileHandle.append(testBlob);
ok(fileHandle.location === null, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
fileHandle.location = location;
request = fileHandle.readAsArrayBuffer(testBlob.size);
location += testBlob.size;
is(fileHandle.location, location, "Correct location");
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
resultBuffer = event.target.result;
ok(compareBuffers(resultBuffer, testBuffer), "Correct blob data");
request = fileHandle.getMetadata({ size: true });
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let result = event.target.result;
is(result.size, location, "Correct size");
finishTest();
yield undefined;
}

View File

@ -0,0 +1,122 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function testSteps()
{
const dbNames = [
"No files",
"Blobs and mutable files"
]
const version = 1;
const objectStoreName = "test";
clearAllDatabases(continueToNextStepSync);
yield undefined;
installPackagedProfile("mutableFileUpgrade_profile");
let request = indexedDB.open(dbNames[0], version);
request.onerror = errorHandler;
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield undefined;
is(event.type, "success", "Correct event type");
let db = event.target.result;
db.onerror = errorHandler;
request = db.transaction([objectStoreName])
.objectStore(objectStoreName)
.get(1);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
is(event.target.result, "text", "Correct result");
request = indexedDB.open(dbNames[1], version);
request.onerror = errorHandler;
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
is(event.type, "success", "Correct event type");
db = event.target.result;
db.onerror = errorHandler;
request = db.transaction([objectStoreName])
.objectStore(objectStoreName)
.get(1);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
is(event.target.result, "text", "Correct result");
request = db.transaction([objectStoreName])
.objectStore(objectStoreName)
.get(2);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
verifyBlob(event.target.result, getBlob("blob0"));
yield undefined;
request = db.transaction([objectStoreName])
.objectStore(objectStoreName)
.get(3);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
let result = event.target.result;
verifyBlob(result[0], getBlob("blob1"));
yield undefined;
verifyBlob(result[1], getBlob("blob2"));
yield undefined;
request = db.transaction([objectStoreName])
.objectStore(objectStoreName)
.get(4);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
verifyMutableFile(event.target.result, getFile("mutablefile0", "", ""));
yield undefined;
request = db.transaction([objectStoreName])
.objectStore(objectStoreName)
.get(5);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
result = event.target.result;
verifyMutableFile(result[0], getFile("mutablefile1", "", ""));
yield undefined;
verifyMutableFile(result[1], getFile("mutablefile2", "", ""));
yield undefined;
request = db.transaction([objectStoreName])
.objectStore(objectStoreName)
.get(6);
request.onsuccess = grabEventAndContinueHandler;
event = yield undefined;
result = event.target.result;
verifyBlob(result[0], getBlob("blob3"));
yield undefined;
verifyMutableFile(result[1], getFile("mutablefile3", "", ""));
yield undefined;
finishTest();
yield undefined;
}

View File

@ -11,6 +11,8 @@ if (!("self" in this)) {
const DOMException = Ci.nsIDOMDOMException;
var bufferCache = [];
function is(a, b, msg) {
do_check_eq(a, b, Components.stack.caller);
}
@ -333,6 +335,99 @@ function installPackagedProfile(packageName)
zipReader.close();
}
function getBlob(str)
{
return new Blob([str], {type: "type/text"});
}
function getFile(name, type, str)
{
return new File([str], name, {type: type});
}
function getFileReader()
{
return SpecialPowers.Cc["@mozilla.org/files/filereader;1"]
.createInstance(SpecialPowers.Ci.nsIDOMFileReader);
}
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 verifyBuffers(buffer1, buffer2)
{
ok(compareBuffers(buffer1, buffer2), "Correct blob data");
}
function verifyBlob(blob1, blob2)
{
is(blob1 instanceof Components.interfaces.nsIDOMBlob, true,
"Instance of nsIDOMBlob");
is(blob1 instanceof File, blob2 instanceof File,
"Instance of DOM File");
is(blob1.size, blob2.size, "Correct size");
is(blob1.type, blob2.type, "Correct type");
if (blob2 instanceof File) {
is(blob1.name, blob2.name, "Correct name");
}
let buffer1;
let buffer2;
for (let i = 0; i < bufferCache.length; i++) {
if (bufferCache[i].blob == blob2) {
buffer2 = bufferCache[i].buffer;
break;
}
}
if (!buffer2) {
let reader = getFileReader();
reader.readAsArrayBuffer(blob2);
reader.onload = function(event) {
buffer2 = event.target.result;
bufferCache.push({ blob: blob2, buffer: buffer2 });
if (buffer1) {
verifyBuffers(buffer1, buffer2);
testGenerator.next();
}
}
}
let reader = getFileReader();
reader.readAsArrayBuffer(blob1);
reader.onload = function(event) {
buffer1 = event.target.result;
if (buffer2) {
verifyBuffers(buffer1, buffer2);
testGenerator.next();
}
}
}
function verifyMutableFile(mutableFile1, file2)
{
is(mutableFile1 instanceof IDBMutableFile, true,
"Instance of IDBMutableFile");
is(mutableFile1.name, file2.name, "Correct name");
is(mutableFile1.type, file2.type, "Correct type");
executeSoon(function() {
testGenerator.next();
});
}
var SpecialPowers = {
isMainProcess: function() {
return Components.classes["@mozilla.org/xre/app-info;1"]

View File

@ -10,6 +10,7 @@ skip-if = toolkit == 'gonk'
support-files =
bug1056939_profile.zip
defaultStorageUpgrade_profile.zip
mutableFileUpgrade_profile.zip
GlobalObjectsChild.js
GlobalObjectsComponent.js
GlobalObjectsComponent.manifest
@ -32,6 +33,7 @@ skip-if = toolkit == 'android'
skip-if = true
[test_lowDiskSpace.js]
[test_metadataRestore.js]
[test_mutableFileUpgrade.js]
[test_readwriteflush_disabled.js]
[test_schema18upgrade.js]
[test_temporary_storage.js]

View File

@ -710,64 +710,6 @@ private:
}
};
// This is only needed for IndexedDB BlobImplSnapshot.
class SameProcessInputStreamBlobImpl final
: public BlobImplBase
{
nsCOMPtr<nsIInputStream> mInputStream;
public:
SameProcessInputStreamBlobImpl(const nsAString& aContentType,
uint64_t aLength,
nsIInputStream* aInputStream)
: BlobImplBase(aContentType, aLength)
, mInputStream(aInputStream)
{
MOZ_ASSERT(aLength != UINT64_MAX);
MOZ_ASSERT(aInputStream);
mImmutable = true;
}
SameProcessInputStreamBlobImpl(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate,
nsIInputStream* aInputStream)
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
BlobDirState::eIsNotDir)
, mInputStream(aInputStream)
{
MOZ_ASSERT(aLength != UINT64_MAX);
MOZ_ASSERT(aLastModifiedDate != INT64_MAX);
MOZ_ASSERT(aInputStream);
mImmutable = true;
}
private:
virtual already_AddRefed<BlobImpl>
CreateSlice(uint64_t /* aStart */,
uint64_t /* aLength */,
const nsAString& /* aContentType */,
ErrorResult& /* aRv */) override
{
MOZ_CRASH("Not implemented");
}
virtual void
GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override
{
if (NS_WARN_IF(!aStream)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsCOMPtr<nsIInputStream> inputStream = mInputStream;
inputStream.forget(aStream);
}
};
struct MOZ_STACK_CLASS CreateBlobImplMetadata final
{
nsString mContentType;
@ -877,40 +819,6 @@ CreateBlobImpl(const nsTArray<uint8_t>& aMemoryData,
return blobImpl.forget();
}
already_AddRefed<BlobImpl>
CreateBlobImpl(intptr_t aAddRefedInputStream,
const CreateBlobImplMetadata& aMetadata)
{
MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
MOZ_ASSERT(aMetadata.mIsSameProcessActor);
MOZ_ASSERT(aAddRefedInputStream);
nsCOMPtr<nsIInputStream> inputStream =
dont_AddRef(
reinterpret_cast<nsIInputStream*>(aAddRefedInputStream));
nsRefPtr<BlobImpl> blobImpl;
if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
blobImpl =
new SameProcessInputStreamBlobImpl(aMetadata.mName,
aMetadata.mContentType,
aMetadata.mLength,
aMetadata.mLastModifiedDate,
inputStream);
} else {
blobImpl =
new SameProcessInputStreamBlobImpl(aMetadata.mContentType,
aMetadata.mLength,
inputStream);
}
DebugOnly<bool> isMutable;
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
return blobImpl.forget();
}
already_AddRefed<BlobImpl>
CreateBlobImpl(const nsTArray<BlobData>& aBlobData,
CreateBlobImplMetadata& aMetadata);
@ -934,11 +842,6 @@ CreateBlobImplFromBlobData(const BlobData& aBlobData,
break;
}
case BlobData::Tintptr_t: {
blobImpl = CreateBlobImpl(aBlobData.get_intptr_t(), aMetadata);
break;
}
case BlobData::TArrayOfBlobData: {
blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata);
break;
@ -3361,6 +3264,16 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlobImpl);
// If the blob represents a wrapper around real blob implementation (so called
// snapshot) then we need to get the real one.
if (nsCOMPtr<PIBlobImplSnapshot> snapshot = do_QueryInterface(aBlobImpl)) {
aBlobImpl = snapshot->GetBlobImpl();
if (!aBlobImpl) {
// The snapshot is not valid anymore.
return nullptr;
}
}
// If the blob represents a remote blob then we can simply pass its actor back
// here.
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) {
@ -3381,18 +3294,7 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
AnyBlobConstructorParams blobParams;
nsCOMPtr<nsIInputStream> snapshotInputStream;
if (gProcessType == GeckoProcessType_Default) {
nsCOMPtr<PIBlobImplSnapshot> snapshot = do_QueryInterface(aBlobImpl);
if (snapshot) {
ErrorResult rv;
aBlobImpl->GetInternalStream(getter_AddRefs(snapshotInputStream), rv);
MOZ_ALWAYS_TRUE(!rv.Failed());
}
}
if (gProcessType == GeckoProcessType_Default && !snapshotInputStream) {
nsRefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
auto addRefedBlobImpl =
reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
@ -3400,12 +3302,7 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
} else {
BlobData blobData;
if (snapshotInputStream) {
blobData =
reinterpret_cast<intptr_t>(snapshotInputStream.forget().take());
} else {
BlobDataFromBlobImpl(aBlobImpl, blobData);
}
BlobDataFromBlobImpl(aBlobImpl, blobData);
nsString contentType;
aBlobImpl->GetType(contentType);
@ -3914,6 +3811,8 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
MOZ_ASSERT(aManager);
MOZ_ASSERT(aBlobImpl);
MOZ_ASSERT(!nsCOMPtr<PIBlobImplSnapshot>(do_QueryInterface(aBlobImpl)));
// If the blob represents a remote blob for this manager then we can simply
// pass its actor back here.
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) {
@ -3928,20 +3827,9 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
return nullptr;
}
const bool isSameProcessActor = ActorManagerIsSameProcess(aManager);
AnyBlobConstructorParams blobParams;
bool isSnapshot;
if (isSameProcessActor) {
nsCOMPtr<PIBlobImplSnapshot> snapshot = do_QueryInterface(aBlobImpl);
isSnapshot = !!snapshot;
} else {
isSnapshot = false;
}
if (isSameProcessActor && !isSnapshot) {
if (ActorManagerIsSameProcess(aManager)) {
nsRefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
auto addRefedBlobImpl =
reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());

View File

@ -38,9 +38,6 @@ union BlobData
// For memory-backed blobs.
uint8_t[];
// For file snapshots, this is an nsIInputStream.
intptr_t;
// For multiplex blobs.
BlobData[];
};

View File

@ -26,17 +26,9 @@ interface IDBFileHandle : EventTarget
optional DOMString? encoding = null);
[Throws]
IDBFileRequest? write(ArrayBuffer value);
IDBFileRequest? write((DOMString or ArrayBuffer or ArrayBufferView or Blob) value);
[Throws]
IDBFileRequest? write(Blob value);
[Throws]
IDBFileRequest? write(DOMString value);
[Throws]
IDBFileRequest? append(ArrayBuffer value);
[Throws]
IDBFileRequest? append(Blob value);
[Throws]
IDBFileRequest? append(DOMString value);
IDBFileRequest? append((DOMString or ArrayBuffer or ArrayBufferView or Blob) value);
[Throws]
IDBFileRequest? truncate(optional unsigned long long size);
[Throws]

View File

@ -70,6 +70,7 @@ using mozilla::dom::PNuwaChild;
BackgroundChildImpl::
ThreadLocal::ThreadLocal()
: mCurrentFileHandle(nullptr)
{
// May happen on any thread!
MOZ_COUNT_CTOR(mozilla::ipc::BackgroundChildImpl::ThreadLocal);

View File

@ -11,6 +11,9 @@
namespace mozilla {
namespace dom {
class FileHandleBase;
namespace indexedDB {
class ThreadLocal;
@ -150,6 +153,7 @@ class BackgroundChildImpl::ThreadLocal final
public:
nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
mozilla::dom::FileHandleBase* mCurrentFileHandle;
public:
ThreadLocal();

View File

@ -124,6 +124,9 @@ pref("dom.indexedDB.logging.details", true);
// Enable profiler marks for indexedDB events.
pref("dom.indexedDB.logging.profiler-marks", false);
// Whether or not File Handle is enabled.
pref("dom.fileHandle.enabled", true);
// Whether or not the Permissions API is enabled.
#ifdef NIGHTLY_BUILD
pref("dom.permissions.enabled", true);