mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
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:
parent
4852256c3a
commit
2a061d3c3b
@ -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;
|
||||
|
743
dom/filehandle/ActorsChild.cpp
Normal file
743
dom/filehandle/ActorsChild.cpp
Normal 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
|
175
dom/filehandle/ActorsChild.h
Normal file
175
dom/filehandle/ActorsChild.h
Normal 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
|
2664
dom/filehandle/ActorsParent.cpp
Normal file
2664
dom/filehandle/ActorsParent.cpp
Normal file
File diff suppressed because it is too large
Load Diff
221
dom/filehandle/ActorsParent.h
Normal file
221
dom/filehandle/ActorsParent.h
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
636
dom/filehandle/FileHandleBase.cpp
Normal file
636
dom/filehandle/FileHandleBase.cpp
Normal 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
|
246
dom/filehandle/FileHandleBase.h
Normal file
246
dom/filehandle/FileHandleBase.h
Normal 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
|
34
dom/filehandle/FileHandleCommon.cpp
Normal file
34
dom/filehandle/FileHandleCommon.cpp
Normal 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
|
73
dom/filehandle/FileHandleCommon.h
Normal file
73
dom/filehandle/FileHandleCommon.h
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
93
dom/filehandle/FileRequestBase.h
Normal file
93
dom/filehandle/FileRequestBase.h
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
36
dom/filehandle/MutableFileBase.cpp
Normal file
36
dom/filehandle/MutableFileBase.cpp
Normal 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
|
76
dom/filehandle/MutableFileBase.h
Normal file
76
dom/filehandle/MutableFileBase.h
Normal 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
|
91
dom/filehandle/PBackgroundFileHandle.ipdl
Normal file
91
dom/filehandle/PBackgroundFileHandle.ipdl
Normal 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
|
83
dom/filehandle/PBackgroundFileRequest.ipdl
Normal file
83
dom/filehandle/PBackgroundFileRequest.ipdl
Normal 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
|
36
dom/filehandle/PBackgroundMutableFile.ipdl
Normal file
36
dom/filehandle/PBackgroundMutableFile.ipdl
Normal 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
|
23
dom/filehandle/SerializationHelpers.h
Normal file
23
dom/filehandle/SerializationHelpers.h
Normal 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
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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(¤t)) && 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
|
||||
|
@ -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
|
||||
|
@ -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::
|
||||
|
@ -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);
|
||||
|
@ -454,6 +454,13 @@ IDBFactory::AssertIsOnOwningThread() const
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
|
||||
}
|
||||
|
||||
PRThread*
|
||||
IDBFactory::OwningThread() const
|
||||
{
|
||||
MOZ_ASSERT(mOwningThread);
|
||||
return mOwningThread;
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
bool
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -118,6 +118,9 @@ public:
|
||||
return ExperimentalFeaturesEnabled();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsFileHandleEnabled();
|
||||
|
||||
already_AddRefed<FileManager>
|
||||
GetFileManager(PersistenceType aPersistenceType,
|
||||
const nsACString& aOrigin,
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
include protocol PBackgroundIDBTransaction;
|
||||
include protocol PBackgroundIDBVersionChangeTransaction;
|
||||
include protocol PBackgroundMutableFile;
|
||||
include protocol PBlob;
|
||||
|
||||
include PBackgroundIDBSharedTypes;
|
||||
|
@ -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
|
||||
|
33
dom/indexedDB/PBackgroundIDBDatabaseRequest.ipdl
Normal file
33
dom/indexedDB/PBackgroundIDBDatabaseRequest.ipdl
Normal 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
|
@ -39,7 +39,7 @@ union FactoryRequestParams
|
||||
DeleteDatabaseRequestParams;
|
||||
};
|
||||
|
||||
protocol PBackgroundIDBFactory
|
||||
sync protocol PBackgroundIDBFactory
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
include protocol PBackgroundIDBTransaction;
|
||||
include protocol PBackgroundIDBVersionChangeTransaction;
|
||||
include protocol PBackgroundMutableFile;
|
||||
include protocol PBlob;
|
||||
|
||||
include PBackgroundIDBSharedTypes;
|
||||
|
@ -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
|
||||
|
@ -6,6 +6,7 @@ include protocol PBackgroundIDBCursor;
|
||||
include protocol PBackgroundIDBDatabase;
|
||||
include protocol PBackgroundIDBDatabaseFile;
|
||||
include protocol PBackgroundIDBRequest;
|
||||
include protocol PBackgroundMutableFile;
|
||||
|
||||
include PBackgroundIDBSharedTypes;
|
||||
|
||||
|
@ -6,6 +6,7 @@ include protocol PBackgroundIDBCursor;
|
||||
include protocol PBackgroundIDBDatabase;
|
||||
include protocol PBackgroundIDBDatabaseFile;
|
||||
include protocol PBackgroundIDBRequest;
|
||||
include protocol PBackgroundMutableFile;
|
||||
include protocol PBlob;
|
||||
|
||||
include PBackgroundIDBSharedTypes;
|
||||
|
@ -77,6 +77,7 @@ IPDL_SOURCES += [
|
||||
'PBackgroundIDBCursor.ipdl',
|
||||
'PBackgroundIDBDatabase.ipdl',
|
||||
'PBackgroundIDBDatabaseFile.ipdl',
|
||||
'PBackgroundIDBDatabaseRequest.ipdl',
|
||||
'PBackgroundIDBFactory.ipdl',
|
||||
'PBackgroundIDBFactoryRequest.ipdl',
|
||||
'PBackgroundIDBRequest.ipdl',
|
||||
|
@ -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);
|
||||
|
@ -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_ + "]: '" +
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
204
dom/indexedDB/test/test_filehandle_disabled_pref.html
Normal file
204
dom/indexedDB/test/test_filehandle_disabled_pref.html
Normal 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>
|
@ -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>
|
BIN
dom/indexedDB/test/unit/mutableFileUpgrade_profile.zip
Normal file
BIN
dom/indexedDB/test/unit/mutableFileUpgrade_profile.zip
Normal file
Binary file not shown.
98
dom/indexedDB/test/unit/test_filehandle_append_read_data.js
Normal file
98
dom/indexedDB/test/unit/test_filehandle_append_read_data.js
Normal 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;
|
||||
}
|
122
dom/indexedDB/test/unit/test_mutableFileUpgrade.js
Normal file
122
dom/indexedDB/test/unit/test_mutableFileUpgrade.js
Normal 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;
|
||||
}
|
@ -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"]
|
||||
|
@ -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]
|
||||
|
140
dom/ipc/Blob.cpp
140
dom/ipc/Blob.cpp
@ -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());
|
||||
|
@ -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[];
|
||||
};
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user