Bug 1258221 - patch 2 - Port FileSystem API and DeviceStorage API to PBackground, r=smaug

This commit is contained in:
Andrea Marchesini 2016-04-09 19:17:02 +01:00
parent c6067c0a80
commit 9345155089
35 changed files with 1803 additions and 1083 deletions

View File

@ -9,13 +9,22 @@
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/PFileSystemParams.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
namespace mozilla {
using namespace ipc;
namespace dom {
/**
* CreateDirectoryTask
*/
/* static */ already_AddRefed<CreateDirectoryTask>
CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
@ -44,28 +53,6 @@ CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
return task.forget();
}
/* static */ already_AddRefed<CreateDirectoryTask>
CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<CreateDirectoryTask> task =
new CreateDirectoryTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath)
: FileSystemTaskBase(aFileSystem)
@ -75,20 +62,9 @@ CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
MOZ_ASSERT(aFileSystem);
}
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
}
CreateDirectoryTask::~CreateDirectoryTask()
{
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
"mPromise should be released on main thread!");
MOZ_ASSERT(NS_IsMainThread());
}
already_AddRefed<Promise>
@ -113,11 +89,92 @@ CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
}
FileSystemResponseValue
CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
void
CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
const FileSystemDirectoryResponse& r =
aValue.get_FileSystemDirectoryResponse();
aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(r.realPath()), true,
getter_AddRefs(mTargetPath));
NS_WARN_IF(aRv.Failed());
}
void
CreateDirectoryTask::HandlerCallback()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
mTargetPath,
Directory::eNotDOMRootDirectory,
mFileSystem);
MOZ_ASSERT(dir);
mPromise->MaybeResolve(dir);
mPromise = nullptr;
}
void
CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
}
/**
* CreateDirectoryTaskParent
*/
/* static */ already_AddRefed<CreateDirectoryTaskParent>
CreateDirectoryTaskParent::Create(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
RefPtr<CreateDirectoryTaskParent> task =
new CreateDirectoryTaskParent(aFileSystem, aParam, aParent);
aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aParam.realPath()), true,
getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
CreateDirectoryTaskParent::CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
}
FileSystemResponseValue
CreateDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
{
AssertIsOnBackgroundThread();
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
@ -127,20 +184,8 @@ CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
return FileSystemDirectoryResponse(path);
}
void
CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemDirectoryResponse r = aValue;
NS_ConvertUTF16toUTF8 path(r.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
NS_WARN_IF(aRv.Failed());
}
nsresult
CreateDirectoryTask::Work()
CreateDirectoryTaskParent::IOWork()
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
@ -169,33 +214,9 @@ CreateDirectoryTask::Work()
}
void
CreateDirectoryTask::HandlerCallback()
CreateDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
mTargetPath,
Directory::eNotDOMRootDirectory,
mFileSystem);
MOZ_ASSERT(dir);
mPromise->MaybeResolve(dir);
mPromise = nullptr;
}
void
CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("create");
aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
}
} // namespace dom

View File

@ -11,9 +11,12 @@
#include "nsAutoPtr.h"
#include "mozilla/ErrorResult.h"
#define CREATE_DIRECTORY_TASK_PERMISSION "create"
namespace mozilla {
namespace dom {
class FileSystemCreateDirectoryParams;
class Promise;
class CreateDirectoryTask final : public FileSystemTaskBase
@ -24,12 +27,6 @@ public:
nsIFile* aTargetPath,
ErrorResult& aRv);
static already_AddRefed<CreateDirectoryTask>
Create(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual
~CreateDirectoryTask();
@ -39,36 +36,54 @@ public:
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
virtual void
HandlerCallback() override;
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
virtual nsresult
Work() override;
virtual void
HandlerCallback() override;
private:
CreateDirectoryTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath);
CreateDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent);
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
};
class CreateDirectoryTaskParent final : public FileSystemTaskParentBase
{
public:
static already_AddRefed<CreateDirectoryTaskParent>
Create(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
protected:
virtual nsresult
IOWork() override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
private:
CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent);
nsCOMPtr<nsIFile> mTargetPath;
};
} // namespace dom
} // namespace mozilla

View File

@ -5,6 +5,8 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CreateFileTask.h"
#include "CreateDirectoryTask.h"
#include "RemoveTask.h"
#include <algorithm>
@ -12,18 +14,30 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/PFileSystemParams.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsIFile.h"
#include "nsNetUtil.h"
#include "nsIOutputStream.h"
#include "nsStringGlue.h"
#define GET_PERMISSION_ACCESS_TYPE(aAccess) \
if (mReplace) { \
aAccess.AssignLiteral(REMOVE_TASK_PERMISSION); \
return; \
} \
aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
namespace mozilla {
namespace dom {
uint32_t CreateFileTask::sOutputBufferSize = 0;
/**
*CreateFileTask
*/
/* static */ already_AddRefed<CreateFileTask>
CreateFileTask::Create(FileSystemBase* aFileSystem,
@ -41,17 +55,8 @@ CreateFileTask::Create(FileSystemBase* aFileSystem,
// aTargetPath can be null. In this case SetError will be called.
task->GetOutputBufferSize();
if (aBlobData) {
if (XRE_IsParentProcess()) {
aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
} else {
task->mBlobData = aBlobData;
}
task->mBlobImpl = aBlobData->Impl();
}
task->mArrayData.SwapElements(aArrayData);
@ -71,49 +76,6 @@ CreateFileTask::Create(FileSystemBase* aFileSystem,
return task.forget();
}
/* static */ already_AddRefed<CreateFileTask>
CreateFileTask::Create(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<CreateFileTask> task =
new CreateFileTask(aFileSystem, aParam, aParent);
task->GetOutputBufferSize();
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mReplace = aParam.replace();
auto& data = aParam.data();
if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
task->mArrayData = data;
return task.forget();
}
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
MOZ_ASSERT(blobImpl, "blobData should not be null.");
ErrorResult rv;
blobImpl->GetInternalStream(getter_AddRefs(task->mBlobStream), rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
return task.forget();
}
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
bool aReplace)
@ -125,25 +87,9 @@ CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
MOZ_ASSERT(aFileSystem);
}
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mReplace(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
}
CreateFileTask::~CreateFileTask()
{
MOZ_ASSERT((!mPromise && !mBlobData) || NS_IsMainThread(),
"mPromise and mBlobData should be released on main thread!");
if (mBlobStream) {
mBlobStream->Close();
}
MOZ_ASSERT(NS_IsMainThread());
}
already_AddRefed<Promise>
@ -166,12 +112,21 @@ CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
return param;
}
// If we are here, PBackground must be up and running: this method is called
// when the task has been already started by FileSystemPermissionRequest
// class and this happens only when PBackground actor has already been
// created.
PBackgroundChild* actor =
mozilla::ipc::BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(actor);
param.replace() = mReplace;
if (mBlobData) {
BlobChild* actor =
ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
if (actor) {
param.data() = actor;
if (mBlobImpl) {
PBlobChild* blobActor =
mozilla::ipc::BackgroundChild::GetOrCreateActorForBlobImpl(actor,
mBlobImpl);
if (blobActor) {
param.data() = blobActor;
}
} else {
param.data() = mArrayData;
@ -179,11 +134,108 @@ CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
return param;
}
FileSystemResponseValue
CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
void
CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
const FileSystemFileResponse& r = aValue.get_FileSystemFileResponse();
NS_ConvertUTF16toUTF8 path(r.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
void
CreateFileTask::HandlerCallback()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
mTargetPath);
mPromise->MaybeResolve(file);
mPromise = nullptr;
}
void
CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const
{
GET_PERMISSION_ACCESS_TYPE(aAccess)
}
/**
* CreateFileTaskParent
*/
uint32_t CreateFileTaskParent::sOutputBufferSize = 0;
/* static */ already_AddRefed<CreateFileTaskParent>
CreateFileTaskParent::Create(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
RefPtr<CreateFileTaskParent> task =
new CreateFileTaskParent(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mReplace = aParam.replace();
const FileSystemFileDataValue& data = aParam.data();
if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
task->mArrayData = data;
return task.forget();
}
MOZ_ASSERT(data.type() == FileSystemFileDataValue::TPBlobParent);
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
task->mBlobImpl = bp->GetBlobImpl();
MOZ_ASSERT(task->mBlobImpl, "blobData should not be null.");
return task.forget();
}
CreateFileTaskParent::CreateFileTaskParent(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
, mReplace(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
}
FileSystemResponseValue
CreateFileTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
{
AssertIsOnBackgroundThread();
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
@ -193,22 +245,8 @@ CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
return FileSystemFileResponse(path);
}
void
CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemFileResponse r = aValue;
NS_ConvertUTF16toUTF8 path(r.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
nsresult
CreateFileTask::Work()
CreateFileTaskParent::IOWork()
{
class MOZ_RAII AutoClose final
{
@ -280,6 +318,7 @@ CreateFileTask::Work()
}
AutoClose acOutputStream(outputStream);
MOZ_ASSERT(sOutputBufferSize);
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
@ -291,11 +330,17 @@ CreateFileTask::Work()
AutoClose acBufferedOutputStream(bufferedOutputStream);
if (mBlobStream) {
// Write the file content from blob data.
// Write the file content from blob data.
if (mBlobImpl) {
ErrorResult error;
nsCOMPtr<nsIInputStream> blobStream;
mBlobImpl->GetInternalStream(getter_AddRefs(blobStream), error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
uint64_t bufSize = 0;
rv = mBlobStream->Available(&bufSize);
rv = blobStream->Available(&bufSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -303,15 +348,14 @@ CreateFileTask::Work()
while (bufSize && !mFileSystem->IsShutdown()) {
uint32_t written = 0;
uint32_t writeSize = bufSize < UINT32_MAX ? bufSize : UINT32_MAX;
rv = bufferedOutputStream->WriteFrom(mBlobStream, writeSize, &written);
rv = bufferedOutputStream->WriteFrom(blobStream, writeSize, &written);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bufSize -= written;
}
mBlobStream->Close();
mBlobStream = nullptr;
blobStream->Close();
if (mFileSystem->IsShutdown()) {
return NS_ERROR_FAILURE;
@ -338,49 +382,23 @@ CreateFileTask::Work()
return NS_OK;
}
void
CreateFileTask::HandlerCallback()
nsresult
CreateFileTaskParent::MainThreadWork()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
mBlobData = nullptr;
return;
MOZ_ASSERT(NS_IsMainThread());
if (!sOutputBufferSize) {
sOutputBufferSize =
mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
mBlobData = nullptr;
return;
}
RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
mTargetPath);
mPromise->MaybeResolve(file);
mPromise = nullptr;
mBlobData = nullptr;
return FileSystemTaskParentBase::MainThreadWork();
}
void
CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const
CreateFileTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
if (mReplace) {
aAccess.AssignLiteral("write");
return;
}
aAccess.AssignLiteral("create");
}
void
CreateFileTask::GetOutputBufferSize() const
{
if (sOutputBufferSize || !XRE_IsParentProcess()) {
return;
}
sOutputBufferSize =
mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
GET_PERMISSION_ACCESS_TYPE(aAccess)
}
} // namespace dom

View File

@ -31,12 +31,6 @@ public:
bool replace,
ErrorResult& aRv);
static already_AddRefed<CreateFileTask>
Create(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual
~CreateFileTask();
@ -51,16 +45,10 @@ protected:
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
virtual nsresult
Work() override;
virtual void
HandlerCallback() override;
@ -69,22 +57,58 @@ private:
nsIFile* aFile,
bool aReplace);
CreateFileTask(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent);
void
GetOutputBufferSize() const;
static uint32_t sOutputBufferSize;
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
// Not thread-safe and should be released on main thread.
RefPtr<Blob> mBlobData;
RefPtr<BlobImpl> mBlobImpl;
nsCOMPtr<nsIInputStream> mBlobStream;
// This is going to be the content of the file, received by createFile()
// params.
InfallibleTArray<uint8_t> mArrayData;
bool mReplace;
};
class CreateFileTaskParent final : public FileSystemTaskParentBase
{
public:
static already_AddRefed<CreateFileTaskParent>
Create(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual bool
NeedToGoToMainThread() const override { return true; }
virtual nsresult
MainThreadWork() override;
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
protected:
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual nsresult
IOWork() override;
private:
CreateFileTaskParent(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent);
static uint32_t sOutputBufferSize;
nsCOMPtr<nsIFile> mTargetPath;
RefPtr<BlobImpl> mBlobImpl;
// This is going to be the content of the file, received by createFile()
// params.
InfallibleTArray<uint8_t> mArrayData;
bool mReplace;
};

View File

@ -23,15 +23,21 @@ namespace dom {
DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
const nsAString& aStorageName)
: mWindowId(0)
: mStorageType(aStorageType)
, mStorageName(aStorageName)
, mWindowId(0)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
mPermissionCheckType = ePermissionCheckByTestingPref;
mStorageType = aStorageType;
mStorageName = aStorageName;
mRequiresPermissionChecks =
!mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
if (NS_IsMainThread()) {
if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
mPermissionCheckType = ePermissionCheckNotRequired;
} else {
mPermissionCheckType = ePermissionCheckRequired;
}
} else {
AssertIsOnBackgroundThread();
}
// Get the permission name required to access the file system.
nsresult rv =
@ -53,18 +59,23 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
// DeviceStorageTypeChecker is a singleton object and must be initialized on
// the main thread. We initialize it here so that we can use it on the worker
// thread.
DebugOnly<DeviceStorageTypeChecker*> typeChecker
= DeviceStorageTypeChecker::CreateOrGet();
MOZ_ASSERT(typeChecker);
if (NS_IsMainThread()) {
DebugOnly<DeviceStorageTypeChecker*> typeChecker =
DeviceStorageTypeChecker::CreateOrGet();
MOZ_ASSERT(typeChecker);
}
}
DeviceStorageFileSystem::~DeviceStorageFileSystem()
{
AssertIsOnOwningThread();
}
already_AddRefed<FileSystemBase>
DeviceStorageFileSystem::Clone()
{
AssertIsOnOwningThread();
RefPtr<DeviceStorageFileSystem> fs =
new DeviceStorageFileSystem(mStorageType, mStorageName);
@ -77,7 +88,9 @@ void
DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
AssertIsOnOwningThread();
MOZ_ASSERT(aDeviceStorage);
nsCOMPtr<nsPIDOMWindowInner> window = aDeviceStorage->GetOwner();
MOZ_ASSERT(window->IsInnerWindow());
mWindowId = window->WindowID();
@ -86,7 +99,7 @@ DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
void
DeviceStorageFileSystem::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
AssertIsOnOwningThread();
mShutdown = true;
}
@ -94,6 +107,8 @@ nsISupports*
DeviceStorageFileSystem::GetParentObject() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
AssertIsOnOwningThread();
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
MOZ_ASSERT_IF(!mShutdown, window);
return window ? window->AsInner() : nullptr;
@ -102,14 +117,15 @@ DeviceStorageFileSystem::GetParentObject() const
void
DeviceStorageFileSystem::GetRootName(nsAString& aRetval) const
{
AssertIsOnOwningThread();
aRetval = mStorageName;
}
bool
DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Should be on parent process!");
MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!");
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aFile);
nsCOMPtr<nsIFile> rootPath;
@ -134,7 +150,7 @@ DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
bool
DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
AssertIsOnOwningThread();
MOZ_ASSERT(aDir);
ErrorResult rv;
@ -157,6 +173,8 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
void
DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const
{
AssertIsOnOwningThread();
// Generate the string representation of the file system.
aString.AssignLiteral("devicestorage-");
aString.Append(mStorageType);
@ -164,5 +182,16 @@ DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const
aString.Append(mStorageName);
}
nsresult
DeviceStorageFileSystem::MainThreadWork()
{
MOZ_ASSERT(NS_IsMainThread());
DebugOnly<DeviceStorageTypeChecker*> typeChecker =
DeviceStorageTypeChecker::CreateOrGet();
MOZ_ASSERT(typeChecker);
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -48,6 +48,12 @@ public:
virtual void
SerializeDOMPath(nsAString& aSerializedString) const override;
virtual bool
NeedToGoToMainThread() const override { return true; }
virtual nsresult
MainThreadWork() override;
private:
virtual
~DeviceStorageFileSystem();

View File

@ -17,6 +17,9 @@ namespace dom {
already_AddRefed<FileSystemBase>
FileSystemBase::DeserializeDOMPath(const nsAString& aString)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) {
// The string representation of devicestorage file system is of the format:
// devicestorage-StorageType-StorageName
@ -39,34 +42,41 @@ FileSystemBase::DeserializeDOMPath(const nsAString& aString)
return f.forget();
}
return RefPtr<OSFileSystem>(new OSFileSystem(aString)).forget();
return RefPtr<OSFileSystemParent>(new OSFileSystemParent(aString)).forget();
}
FileSystemBase::FileSystemBase()
: mShutdown(false)
, mRequiresPermissionChecks(true)
, mPermissionCheckType(eNotSet)
#ifdef DEBUG
, mOwningThread(PR_GetCurrentThread())
#endif
{
}
FileSystemBase::~FileSystemBase()
{
AssertIsOnOwningThread();
}
void
FileSystemBase::Shutdown()
{
AssertIsOnOwningThread();
mShutdown = true;
}
nsISupports*
FileSystemBase::GetParentObject() const
{
AssertIsOnOwningThread();
return nullptr;
}
bool
FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFile, "aFile Should not be null.");
MOZ_ASSERT(aPath);
@ -89,12 +99,14 @@ FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const
bool
FileSystemBase::IsSafeFile(nsIFile* aFile) const
{
AssertIsOnOwningThread();
return false;
}
bool
FileSystemBase::IsSafeDirectory(Directory* aDir) const
{
AssertIsOnOwningThread();
return false;
}
@ -104,6 +116,7 @@ FileSystemBase::GetDOMPath(nsIFile* aFile,
nsAString& aRetval,
ErrorResult& aRv) const
{
AssertIsOnOwningThread();
MOZ_ASSERT(aFile);
if (aType == Directory::eDOMRootDirectory) {
@ -171,5 +184,12 @@ FileSystemBase::GetDOMPath(nsIFile* aFile,
}
}
void
FileSystemBase::AssertIsOnOwningThread() const
{
MOZ_ASSERT(mOwningThread);
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
}
} // namespace dom
} // namespace mozilla

View File

@ -18,8 +18,8 @@ class BlobImpl;
class FileSystemBase
{
NS_INLINE_DECL_REFCOUNTING(FileSystemBase)
public:
NS_INLINE_DECL_REFCOUNTING(FileSystemBase)
// Create file system object from its string representation.
static already_AddRefed<FileSystemBase>
@ -87,16 +87,53 @@ public:
return mPermission;
}
bool
RequiresPermissionChecks() const
// The decision about doing or not doing the permission check cannot be done
// everywhere because, for some FileSystemBase implementation, this depends on
// a preference.
// This enum describes all the possible decisions. The implementation will do
// the check on the main-thread in the child and in the parent process when
// needed.
// Note: the permission check should not fail in PBackground because that
// means that the child has been compromised. If this happens the child
// process is killed.
enum ePermissionCheckType {
// When on the main-thread, we must check if we have
// device.storage.prompt.testing set to true.
ePermissionCheckByTestingPref,
// No permission check must be done.
ePermissionCheckNotRequired,
// Permission check is required.
ePermissionCheckRequired,
// This is the default value. We crash if this is let like this.
eNotSet
};
ePermissionCheckType
PermissionCheckType() const
{
return mRequiresPermissionChecks;
MOZ_ASSERT(mPermissionCheckType != eNotSet);
return mPermissionCheckType;
}
// IPC initialization
// See how these 2 methods are used in FileSystemTaskBase.
virtual bool
NeedToGoToMainThread() const { return false; }
virtual nsresult
MainThreadWork() { return NS_ERROR_FAILURE; }
// CC methods
virtual void Unlink() {}
virtual void Traverse(nsCycleCollectionTraversalCallback &cb) {}
void
AssertIsOnOwningThread() const;
protected:
virtual ~FileSystemBase();
@ -119,7 +156,11 @@ protected:
// The permission name required to access the file system.
nsCString mPermission;
bool mRequiresPermissionChecks;
ePermissionCheckType mPermissionCheckType;
#ifdef DEBUG
PRThread* mOwningThread;
#endif
};
} // namespace dom

View File

@ -8,6 +8,8 @@
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemTaskBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsIDocument.h"
#include "nsPIDOMWindow.h"
#include "nsContentPermissionHelper.h"
@ -16,14 +18,15 @@ namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable,
nsIContentPermissionRequest)
nsIContentPermissionRequest,
nsIIPCBackgroundChildCreateCallback)
// static
void
/* static */ void
FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
{
MOZ_ASSERT(aTask, "aTask should not be null!");
MOZ_ASSERT(NS_IsMainThread());
RefPtr<FileSystemPermissionRequest> request =
new FileSystemPermissionRequest(aTask);
NS_DispatchToCurrentThread(request);
@ -98,7 +101,7 @@ FileSystemPermissionRequest::Cancel()
{
MOZ_ASSERT(NS_IsMainThread());
mTask->SetError(NS_ERROR_DOM_SECURITY_ERR);
mTask->Start();
ScheduleTask();
return NS_OK;
}
@ -107,7 +110,7 @@ FileSystemPermissionRequest::Allow(JS::HandleValue aChoices)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aChoices.isUndefined());
mTask->Start();
ScheduleTask();
return NS_OK;
}
@ -122,7 +125,13 @@ FileSystemPermissionRequest::Run()
return NS_OK;
}
if (!filesystem->RequiresPermissionChecks()) {
if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckNotRequired) {
Allow(JS::UndefinedHandleValue);
return NS_OK;
}
if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckByTestingPref &&
mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
Allow(JS::UndefinedHandleValue);
return NS_OK;
}
@ -146,5 +155,32 @@ FileSystemPermissionRequest::GetRequester(nsIContentPermissionRequester** aReque
return NS_OK;
}
void
FileSystemPermissionRequest::ActorFailed()
{
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
}
void
FileSystemPermissionRequest::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
{
mTask->Start();
}
void
FileSystemPermissionRequest::ScheduleTask()
{
PBackgroundChild* actor =
mozilla::ipc::BackgroundChild::GetForCurrentThread();
if (actor) {
ActorCreated(actor);
} else {
if (NS_WARN_IF(
!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
MOZ_CRASH();
}
}
}
} /* namespace dom */
} /* namespace mozilla */

View File

@ -10,6 +10,7 @@
#include "nsAutoPtr.h"
#include "nsIRunnable.h"
#include "nsIContentPermissionPrompt.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
#include "nsString.h"
class nsPIDOMWindowInner;
@ -22,6 +23,7 @@ class FileSystemTaskBase;
class FileSystemPermissionRequest final
: public nsIContentPermissionRequest
, public nsIRunnable
, public nsIIPCBackgroundChildCreateCallback
{
public:
// Request permission for the given task.
@ -31,12 +33,19 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICONTENTPERMISSIONREQUEST
NS_DECL_NSIRUNNABLE
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
private:
explicit FileSystemPermissionRequest(FileSystemTaskBase* aTask);
virtual
~FileSystemPermissionRequest();
// Once the permission check has been done, we must run the task using IPC and
// PBackground. This method checks if the PBackground thread is ready to
// receive the task and in case waits for ActorCreated() to be called.
void
ScheduleTask();
nsCString mPermissionType;
nsCString mPermissionAccess;
RefPtr<FileSystemTaskBase> mTask;

View File

@ -18,18 +18,22 @@ namespace mozilla {
namespace dom {
FileSystemRequestParent::FileSystemRequestParent()
: mDestroyed(false)
{
AssertIsOnBackgroundThread();
}
FileSystemRequestParent::~FileSystemRequestParent()
{
AssertIsOnBackgroundThread();
}
#define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name) \
case FileSystemParams::TFileSystem##name##Params: { \
const FileSystem##name##Params& p = aParams; \
mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem()); \
task = name##Task::Create(mFileSystem, p, this, rv); \
MOZ_ASSERT(mFileSystem); \
mTask = name##TaskParent::Create(mFileSystem, p, this, rv); \
if (NS_WARN_IF(rv.Failed())) { \
return false; \
} \
@ -37,11 +41,10 @@ FileSystemRequestParent::~FileSystemRequestParent()
}
bool
FileSystemRequestParent::Dispatch(ContentParent* aParent,
const FileSystemParams& aParams)
FileSystemRequestParent::Initialize(const FileSystemParams& aParams)
{
MOZ_ASSERT(aParent, "aParent should not be null.");
RefPtr<FileSystemTaskBase> task;
AssertIsOnBackgroundThread();
ErrorResult rv;
switch (aParams.type()) {
@ -58,39 +61,47 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
}
}
if (NS_WARN_IF(!task || !mFileSystem)) {
if (NS_WARN_IF(!mTask || !mFileSystem)) {
// Should never reach here.
return false;
}
if (mFileSystem->RequiresPermissionChecks()) {
// Check the content process permission.
if (mFileSystem->PermissionCheckType() != FileSystemBase::ePermissionCheckNotRequired) {
nsAutoCString access;
mTask->GetPermissionAccessType(access);
nsCString access;
task->GetPermissionAccessType(access);
nsAutoCString permissionName;
permissionName = mFileSystem->GetPermission();
permissionName.Append('-');
permissionName.Append(access);
if (!AssertAppProcessPermission(aParent, permissionName.get())) {
return false;
}
mPermissionName = mFileSystem->GetPermission();
mPermissionName.Append('-');
mPermissionName.Append(access);
}
task->Start();
return true;
}
void
FileSystemRequestParent::ActorDestroy(ActorDestroyReason why)
FileSystemRequestParent::Start()
{
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(mFileSystem);
MOZ_ASSERT(mTask);
mTask->Start();
}
void
FileSystemRequestParent::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mDestroyed);
if (!mFileSystem) {
return;
}
mFileSystem->Shutdown();
mFileSystem = nullptr;
mTask = nullptr;
mDestroyed = true;
}
} // namespace dom

View File

@ -8,39 +8,57 @@
#define mozilla_dom_FileSystemRequestParent_h
#include "mozilla/dom/PFileSystemRequestParent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/FileSystemBase.h"
namespace mozilla {
namespace dom {
class FileSystemBase;
class FileSystemParams;
class FileSystemTaskParentBase;
class FileSystemRequestParent final
: public PFileSystemRequestParent
class FileSystemRequestParent final : public PFileSystemRequestParent
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemRequestParent)
public:
FileSystemRequestParent();
bool
IsRunning()
const nsCString&
PermissionName() const
{
return state() == PFileSystemRequest::__Start;
return mPermissionName;
}
FileSystemBase::ePermissionCheckType
PermissionCheckType() const
{
return mFileSystem ? mFileSystem->PermissionCheckType()
: FileSystemBase::eNotSet;
}
bool
Dispatch(ContentParent* aParent, const FileSystemParams& aParams);
Initialize(const FileSystemParams& aParams);
void
Start();
bool Destroyed() const
{
return mDestroyed;
}
virtual void
ActorDestroy(ActorDestroyReason why) override;
private:
// Private destructor, to discourage deletion outside of Release():
virtual
~FileSystemRequestParent();
RefPtr<FileSystemBase> mFileSystem;
RefPtr<FileSystemTaskParentBase> mTask;
nsCString mPermissionName;
bool mDestroyed;
};
} // namespace dom

View File

@ -7,14 +7,13 @@
#include "mozilla/dom/FileSystemTaskBase.h"
#include "nsNetCID.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemRequestParent.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PContent.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/unused.h"
#include "nsProxyRelease.h"
@ -23,27 +22,65 @@ namespace dom {
namespace {
class FileSystemReleaseRunnable final : public nsRunnable
nsresult
FileSystemErrorFromNsError(const nsresult& aErrorValue)
{
public:
explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
: mDoomed(nullptr)
{
aDoomed.swap(mDoomed);
uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
module == NS_ERROR_MODULE_DOM_FILE ||
module == NS_ERROR_MODULE_DOM) {
return aErrorValue;
}
NS_IMETHOD Run()
{
mDoomed->Release();
return NS_OK;
}
switch (aErrorValue) {
case NS_OK:
return NS_OK;
private:
FileSystemBase* MOZ_OWNING_REF mDoomed;
};
case NS_ERROR_FILE_INVALID_PATH:
case NS_ERROR_FILE_UNRECOGNIZED_PATH:
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
case NS_ERROR_FILE_DESTINATION_NOT_DIR:
return NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
case NS_ERROR_FILE_ACCESS_DENIED:
case NS_ERROR_FILE_DIR_NOT_EMPTY:
return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
case NS_ERROR_NOT_AVAILABLE:
return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
case NS_ERROR_FILE_ALREADY_EXISTS:
return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
case NS_ERROR_FILE_NOT_DIRECTORY:
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
case NS_ERROR_UNEXPECTED:
default:
return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
}
}
nsresult
DispatchToIOThread(nsIRunnable* aRunnable)
{
MOZ_ASSERT(aRunnable);
nsCOMPtr<nsIEventTarget> target
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
}
} // anonymous namespace
/**
* FileSystemTaskBase class
*/
FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
: mErrorValue(NS_OK)
, mFileSystem(aFileSystem)
@ -52,56 +89,30 @@ FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
}
FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem,
const FileSystemParams& aParam,
FileSystemRequestParent* aParent)
: mErrorValue(NS_OK)
, mFileSystem(aFileSystem)
, mRequestParent(aParent)
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
}
FileSystemTaskBase::~FileSystemTaskBase()
{
if (!NS_IsMainThread()) {
RefPtr<FileSystemReleaseRunnable> runnable =
new FileSystemReleaseRunnable(mFileSystem);
MOZ_ASSERT(!mFileSystem);
NS_DispatchToMainThread(runnable);
}
mFileSystem->AssertIsOnOwningThread();
}
FileSystemBase*
FileSystemTaskBase::GetFileSystem() const
{
mFileSystem->AssertIsOnOwningThread();
return mFileSystem.get();
}
void
FileSystemTaskBase::Start()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
mFileSystem->AssertIsOnOwningThread();
if (HasError()) {
NS_DispatchToMainThread(this);
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &FileSystemTaskBase::HandlerCallback);
NS_DispatchToMainThread(runnable);
return;
}
if (XRE_IsParentProcess()) {
// Run in parent process.
// Start worker thread.
nsCOMPtr<nsIEventTarget> target
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
NS_ASSERTION(target, "Must have stream transport service.");
target->Dispatch(this, NS_DISPATCH_NORMAL);
return;
}
// Run in child process.
if (mFileSystem->IsShutdown()) {
return;
}
@ -117,72 +128,24 @@ FileSystemTaskBase::Start()
// Retain a reference so the task object isn't deleted without IPDL's
// knowledge. The reference will be released by
// mozilla::dom::ContentChild::DeallocPFileSystemRequestChild.
// mozilla::ipc::BackgroundChildImpl::DeallocPFileSystemRequestChild.
NS_ADDREF_THIS();
ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this,
params);
}
// If we are here, PBackground must be up and running, because Start() is
// called only by FileSystemPermissionRequest, and that class takes care of
// PBackground initialization.
PBackgroundChild* actor =
mozilla::ipc::BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(actor);
NS_IMETHODIMP
FileSystemTaskBase::Run()
{
if (!NS_IsMainThread()) {
// Run worker thread tasks
nsresult rv = Work();
if (NS_FAILED(rv)) {
SetError(rv);
}
// Dispatch itself to main thread
NS_DispatchToMainThread(this);
return NS_OK;
}
// Run main thread tasks
HandleResult();
return NS_OK;
}
void
FileSystemTaskBase::HandleResult()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
return;
}
if (mRequestParent && mRequestParent->IsRunning()) {
Unused << mRequestParent->Send__delete__(mRequestParent,
GetRequestResult());
} else {
HandlerCallback();
}
}
FileSystemResponseValue
FileSystemTaskBase::GetRequestResult() const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (!HasError()) {
ErrorResult rv;
FileSystemResponseValue value = GetSuccessRequestResult(rv);
if (NS_WARN_IF(rv.Failed())) {
return FileSystemErrorResponse(rv.StealNSResult());
}
return value;
}
return FileSystemErrorResponse(mErrorValue);
actor->SendPFileSystemRequestConstructor(this, params);
}
void
FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(!XRE_IsParentProcess(),
"Only call from child process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
mFileSystem->AssertIsOnOwningThread();
if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
FileSystemErrorResponse r = aValue;
mErrorValue = r.error();
@ -196,6 +159,8 @@ FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
bool
FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue)
{
mFileSystem->AssertIsOnOwningThread();
SetRequestResult(aValue);
HandlerCallback();
return true;
@ -204,51 +169,160 @@ FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue)
void
FileSystemTaskBase::SetError(const nsresult& aErrorValue)
{
uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
module == NS_ERROR_MODULE_DOM_FILE ||
module == NS_ERROR_MODULE_DOM) {
mErrorValue = aErrorValue;
mErrorValue = FileSystemErrorFromNsError(aErrorValue);
}
/**
* FileSystemTaskParentBase class
*/
FileSystemTaskParentBase::FileSystemTaskParentBase(FileSystemBase* aFileSystem,
const FileSystemParams& aParam,
FileSystemRequestParent* aParent)
: mErrorValue(NS_OK)
, mFileSystem(aFileSystem)
, mRequestParent(aParent)
, mBackgroundEventTarget(NS_GetCurrentThread())
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
MOZ_ASSERT(aParent);
MOZ_ASSERT(mBackgroundEventTarget);
AssertIsOnBackgroundThread();
}
FileSystemTaskParentBase::~FileSystemTaskParentBase()
{
// This task can be released on different threads because we dispatch it (as
// runnable) to main-thread, I/O and then back to the PBackground thread.
NS_ProxyRelease(mBackgroundEventTarget, mFileSystem.forget());
NS_ProxyRelease(mBackgroundEventTarget, mRequestParent.forget());
}
void
FileSystemTaskParentBase::Start()
{
AssertIsOnBackgroundThread();
mFileSystem->AssertIsOnOwningThread();
if (NeedToGoToMainThread()) {
nsresult rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
NS_WARN_IF(NS_FAILED(rv));
return;
}
switch (aErrorValue) {
case NS_OK:
mErrorValue = NS_OK;
return;
nsresult rv = DispatchToIOThread(this);
NS_WARN_IF(NS_FAILED(rv));
}
case NS_ERROR_FILE_INVALID_PATH:
case NS_ERROR_FILE_UNRECOGNIZED_PATH:
mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
return;
void
FileSystemTaskParentBase::HandleResult()
{
AssertIsOnBackgroundThread();
mFileSystem->AssertIsOnOwningThread();
case NS_ERROR_FILE_DESTINATION_NOT_DIR:
mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
return;
case NS_ERROR_FILE_ACCESS_DENIED:
case NS_ERROR_FILE_DIR_NOT_EMPTY:
mErrorValue = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
return;
case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
case NS_ERROR_NOT_AVAILABLE:
mErrorValue = NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
return;
case NS_ERROR_FILE_ALREADY_EXISTS:
mErrorValue = NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
return;
case NS_ERROR_FILE_NOT_DIRECTORY:
mErrorValue = NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
return;
case NS_ERROR_UNEXPECTED:
default:
mErrorValue = NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
return;
if (mFileSystem->IsShutdown()) {
return;
}
MOZ_ASSERT(mRequestParent);
Unused << mRequestParent->Send__delete__(mRequestParent, GetRequestResult());
}
FileSystemResponseValue
FileSystemTaskParentBase::GetRequestResult() const
{
AssertIsOnBackgroundThread();
mFileSystem->AssertIsOnOwningThread();
if (HasError()) {
return FileSystemErrorResponse(mErrorValue);
}
ErrorResult rv;
FileSystemResponseValue value = GetSuccessRequestResult(rv);
if (NS_WARN_IF(rv.Failed())) {
return FileSystemErrorResponse(rv.StealNSResult());
}
return value;
}
void
FileSystemTaskParentBase::SetError(const nsresult& aErrorValue)
{
mErrorValue = FileSystemErrorFromNsError(aErrorValue);
}
bool
FileSystemTaskParentBase::NeedToGoToMainThread() const
{
return mFileSystem->NeedToGoToMainThread();
}
nsresult
FileSystemTaskParentBase::MainThreadWork()
{
MOZ_ASSERT(NS_IsMainThread());
return mFileSystem->MainThreadWork();
}
NS_IMETHODIMP
FileSystemTaskParentBase::Run()
{
// This method can run in 3 different threads. Here why:
// 1. if we are on the main-thread it's because the task must do something
// here. If no errors are returned we go the step 2.
// 2. We can be here directly if the task doesn't have nothing to do on the
// main-thread. We are are on the I/O thread and we call IOWork().
// 3. Both step 1 (in case of error) and step 2 end up here where return the
// value back to the PBackground thread.
if (NS_IsMainThread()) {
MOZ_ASSERT(NeedToGoToMainThread());
nsresult rv = MainThreadWork();
if (NS_WARN_IF(NS_FAILED(rv))) {
SetError(rv);
// Something when wrong. Let's go to the Background thread directly
// skipping the I/O thread step.
rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// Next step must happen on the I/O thread.
rv = DispatchToIOThread(this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// Run I/O thread tasks
if (!IsOnBackgroundThread()) {
nsresult rv = IOWork();
if (NS_WARN_IF(NS_FAILED(rv))) {
SetError(rv);
}
// Let's go back to PBackground thread to finish the work.
rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
// If we are here, it's because the I/O work has been done and we have to
// handle the result back via IPC.
AssertIsOnBackgroundThread();
HandleResult();
return NS_OK;
}
} // namespace dom

View File

@ -10,111 +10,115 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/FileSystemRequestParent.h"
#include "mozilla/dom/PFileSystemRequestChild.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
class BlobImpl;
class BlobParent;
class FileSystemBase;
class FileSystemParams;
class PBlobParent;
/*
* The base class to implement a Task class.
* The task is used to handle the OOP (out of process) operations.
* The file system operations can only be performed in the parent process. When
* performing such a parent-process-only operation, a task will delivered the
* operation to the parent process if needed.
* The file system operations can only be performed in the parent process. In
* order to avoid duplicated code, we used PBackground for child-parent and
* parent-parent communications.
*
* The following diagram illustrates the how a API call from the content page
* starts a task and gets call back results.
*
* The left block is the call sequence inside the child process, while the
* right block is the call sequence inside the parent process.
* The left block is the call sequence inside any process loading content, while
* the right block is the call sequence only inside the parent process.
*
* There are two types of API call. One is from the content page of the child
* process and we mark the steps as (1) to (8). The other is from the content
* page of the parent process and we mark the steps as (1') to (4').
* Page
* |
* | (1)
* ______|_______________________ | __________________________________
* | | | | | |
* | V | IPC | PBackground thread on |
* | [new FileSystemTaskBase()] | | | the parent process |
* | | | | | |
* | | (2) | | | |
* | V | | | |
* | [FileSystemPermissionRequest------------------\ |
* | ::RequestForTask()] <------------------------/ |
* | | | | | |
* | | (3) | | |
* | V | (4) | |
* | [GetRequestParams]------------------->[new FileSystemTaskParentBase()] |
* | | | | |
* | | | | | (5) _____________ |
* | | | | | | | |
* | | | | | | I/O Thread | |
* | | | | | | | |
* | | | | ---------> [IOWork] | |
* | | IPC | | | | |
* | | | | | | (6) | |
* | | | | -------------- | |
* | | | | | |_____________| |
* | | | | | |
* | | | | V |
* | | | | [HandleResult] |
* | | | | | |
* | | | | (7) |
* | | (8) | V |
* | [SetRequestResult]<--------------------[GetRequestResult] |
* | | | | |
* | | (9) | | | |
* | V | | | |
* |[HandlerCallback] | IPC | |
* |_______|______________________| | |__________________________________|
* | |
* V
* Page
*
* Page Page
* | |
* | (1) | (1')
* ______|________________ | _____________________|_____________
* | | | | | | |
* | | Task in | | | Task in | |
* | | Child Process | | | Parent Process | |
* | V | IPC | V |
* [new FileSystemTaskBase()] | | [new FileSystemTaskBase()] |
* | | | | | | |
* | | (2) | | | (2') |
* | V | (3) | | |
* | [GetRequestParams]------------->[new FileSystemTaskBase(...)] |
* | | | | | |
* | | | | | (4) | |
* | | | | | V |
* | | | | -----------> [Work] |
* | | IPC | | |
* | | | | (5) | (3') |
* | | | | V |
* | | | | --------[HandleResult] |
* | | | | | | |
* | | | | (6) | |
* | | (7) | V | |
* | [SetRequestResult]<-------------[GetRequestResult] | |
* | | | | | (4') |
* | | (8) | | | | |
* | V | | | V |
* |[HandlerCallback] | IPC | [HandlerCallback] |
* |_______|_______________| | |_________________________|_________|
* | | |
* V V
* Page Page
*
* 1. From child process page
* Child:
* 1. From the process that is handling the request
* Child/Parent (it can be in any process):
* (1) Call FileSystem API from content page with JS. Create a task and run.
* The base constructor [FileSystemTaskBase()] of the task should be called.
* (2) Forward the task to the parent process through the IPC and call
* (2) The FileSystemTaskBase object is given to
* [FileSystemPermissionRequest::RequestForTask()] that will perform a
* permission check step if needed (See ePermissionCheckType enum). The real
* operation is done on the parent process but it's hidden by
* [nsContentPermissionUtils::AskPermission()]. If the permission check is not
* needed or if the page has the right permission, the
* FileSystemPermissionRequest will start the task (only once PBackground
* actor is fully initialized).
* (3) Forward the task to the parent process through the IPC and call
* [GetRequestParams] to prepare the parameters of the IPC.
* Parent:
* (3) The parent process receives IPC and handle it in
* FileystemRequestParent.
* Get the IPC parameters and create a task to run the IPC task. The base
* constructor [FileSystemTaskBase(aParam, aParent)] of the task should be
* called to set the task as an IPC task.
* (4) The task operation will be performed in the member function of [Work].
* A worker thread will be created to run that function. If error occurs
* (4) The parent process receives IPC and handle it in
* FileystemRequestParent. Get the IPC parameters and create a task to run the
* IPC task. The base constructor [FileSystemTaskParentBase(aParam, aParent)]
* For security reasons, we do an additional permission check if needed. In
* the check fails, the child process will be killed.
* of the task should be called to set the task as an IPC task.
* (5) The task operation will be performed in the member function of [IOWork].
* A I/O thread will be created to run that function. If error occurs
* during the operation, call [SetError] to record the error and then abort.
* (5) After finishing the task operation, call [HandleResult] to send the
* (6) After finishing the task operation, call [HandleResult] to send the
* result back to the child process though the IPC.
* (6) Call [GetRequestResult] request result to prepare the parameters of the
* (7) Call [GetRequestResult] request result to prepare the parameters of the
* IPC. Because the formats of the error result for different task are the
* same, FileSystemTaskBase can handle the error message without interfering.
* Each task only needs to implement its specific success result preparation
* function -[GetSuccessRequestResult].
* Child:
* (7) The child process receives IPC and calls [SetRequestResult] to get the
* Child/Parent:
* (8) The process receives IPC and calls [SetRequestResult] to get the
* task result. Each task needs to implement its specific success result
* parsing function [SetSuccessRequestResult] to get the success result.
* (8) Call [HandlerCallback] to send the task result to the content page.
* 2. From parent process page
* We don't need to send the task parameters and result to other process. So
* there are less steps, but their functions are the same. The correspondence
* between the two types of steps is:
* (1') = (1),
* (2') = (4),
* (3') = (5),
* (4') = (8).
* (9) Call [HandlerCallback] to send the task result to the content page.
*/
class FileSystemTaskBase
: public nsRunnable
, public PFileSystemRequestChild
class FileSystemTaskBase : public PFileSystemRequestChild
{
public:
NS_INLINE_DECL_REFCOUNTING(FileSystemTaskBase)
/*
* Start the task. If the task is running the child process, it will be
* forwarded to parent process by IPC, or else, creates a worker thread to
* do the task work.
* Start the task. It will dispatch all the information to the parent process,
* PBackground thread. This method must be called from the owning thread.
*/
void
Start();
@ -135,104 +139,156 @@ public:
virtual void
GetPermissionAccessType(nsCString& aAccess) const = 0;
NS_DECL_NSIRUNNABLE
/*
* After the task is completed, this function will be called to pass the task
* result to the content page. This method is called in the owning thread.
* Override this function to handle the call back to the content page.
*/
virtual void
HandlerCallback() = 0;
bool
HasError() const { return NS_FAILED(mErrorValue); }
protected:
/*
* To create a task to handle the page content request.
*/
explicit FileSystemTaskBase(FileSystemBase* aFileSystem);
/*
* To create a parent process task delivered from the child process through
* IPC.
*/
FileSystemTaskBase(FileSystemBase* aFileSystem,
const FileSystemParams& aParam,
FileSystemRequestParent* aParent);
virtual
~FileSystemTaskBase();
/*
* The function to perform task operation. It will be run on the worker
* thread of the parent process.
* Overrides this function to define the task operation for individual task.
*/
virtual nsresult
Work() = 0;
/*
* After the task is completed, this function will be called to pass the task
* result to the content page.
* Override this function to handle the call back to the content page.
*/
virtual void
HandlerCallback() = 0;
/*
* Wrap the task parameter to FileSystemParams for sending it through IPC.
* It will be called when we need to forward a task from the child process to
* the prarent process.
* the parent process. This method runs in the owning thread.
* @param filesystem The string representation of the file system.
*/
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const = 0;
/*
* Unwrap the IPC message to get the task success result.
* It will be called when the task is completed successfully and an IPC
* message is received in the child process and we want to get the task
* success result. This method runs in the owning thread.
*/
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) = 0;
// Overrides PFileSystemRequestChild
virtual bool
Recv__delete__(const FileSystemResponseValue& value) override;
nsresult mErrorValue;
RefPtr<FileSystemBase> mFileSystem;
private:
/*
* Unwrap the IPC message to get the task result.
* It will be called when the task is completed and an IPC message is received
* in the content process and we want to get the task result. This runs on the
* owning thread.
*/
void
SetRequestResult(const FileSystemResponseValue& aValue);
};
// This class is the 'alter ego' of FileSystemTaskBase in the PBackground world.
class FileSystemTaskParentBase : public nsRunnable
{
public:
/*
* Start the task. This must be called from the PBackground thread only.
*/
void
Start();
/*
* The error codes are defined in xpcom/base/ErrorList.h and their
* corresponding error name and message are defined in dom/base/domerr.msg.
*/
void
SetError(const nsresult& aErrorCode);
/*
* The function to perform task operation. It will be run on the I/O
* thread of the parent process.
* Overrides this function to define the task operation for individual task.
*/
virtual nsresult
IOWork() = 0;
/*
* Wrap the task success result to FileSystemResponseValue for sending it
* through IPC.
* through IPC. This method runs in the PBackground thread.
* It will be called when the task is completed successfully and we need to
* send the task success result back to the child process.
*/
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const = 0;
/*
* Unwrap the IPC message to get the task success result.
* It will be called when the task is completed successfully and an IPC
* message is received in the child process and we want to get the task
* success result.
*/
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) = 0;
bool
HasError() const { return mErrorValue != NS_OK; }
// Overrides PFileSystemRequestChild
virtual bool
Recv__delete__(const FileSystemResponseValue& value) override;
nsresult mErrorValue;
RefPtr<FileSystemBase> mFileSystem;
RefPtr<FileSystemRequestParent> mRequestParent;
private:
/*
* After finishing the task operation, handle the task result.
* If it is an IPC task, send back the IPC result. Or else, send the result
* to the content page.
* If it is an IPC task, send back the IPC result. It runs on the PBackground
* thread.
*/
void
HandleResult();
// If this task must do something on the main-thread before IOWork(), it must
// overwrite this method. Otherwise it returns true if the FileSystem must be
// initialized on the main-thread. It's called from the Background thread.
virtual bool
NeedToGoToMainThread() const;
// This method is called only if NeedToGoToMainThread() returns true.
// Of course, it runs on the main-thread.
virtual nsresult
MainThreadWork();
/*
* Get the type of permission access required to perform this task.
*/
virtual void
GetPermissionAccessType(nsCString& aAccess) const = 0;
bool
HasError() const { return NS_FAILED(mErrorValue); }
NS_IMETHOD
Run() override;
private:
/*
* Wrap the task result to FileSystemResponseValue for sending it through IPC.
* It will be called when the task is completed and we need to
* send the task result back to the child process.
* send the task result back to the content. This runs on the PBackground
* thread.
*/
FileSystemResponseValue
GetRequestResult() const;
protected:
/*
* Unwrap the IPC message to get the task result.
* It will be called when the task is completed and an IPC message is received
* in the child process and we want to get the task result.
* To create a parent process task delivered from the child process through
* IPC.
*/
void
SetRequestResult(const FileSystemResponseValue& aValue);
FileSystemTaskParentBase(FileSystemBase* aFileSystem,
const FileSystemParams& aParam,
FileSystemRequestParent* aParent);
virtual
~FileSystemTaskParentBase();
nsresult mErrorValue;
RefPtr<FileSystemBase> mFileSystem;
RefPtr<FileSystemRequestParent> mRequestParent;
nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
};
} // namespace dom

View File

@ -11,15 +11,22 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/PFileSystemParams.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
#define GET_DIRECTORY_LISTING_PERMISSION "read"
namespace mozilla {
namespace dom {
/**
* GetDirectoryListingTask
*/
/* static */ already_AddRefed<GetDirectoryListingTask>
GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
@ -50,30 +57,6 @@ GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
return task.forget();
}
/* static */ already_AddRefed<GetDirectoryListingTask>
GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<GetDirectoryListingTask> task =
new GetDirectoryListingTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mType = aParam.isRoot()
? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
return task.forget();
}
GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
@ -87,21 +70,9 @@ GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
MOZ_ASSERT(aFileSystem);
}
GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mFilters(aParam.filters())
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
}
GetDirectoryListingTask::~GetDirectoryListingTask()
{
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
"mPromise should be released on main thread!");
MOZ_ASSERT(NS_IsMainThread());
}
already_AddRefed<Promise>
@ -128,33 +99,6 @@ GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath,
mFilters);
}
FileSystemResponseValue
GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
InfallibleTArray<PBlobParent*> blobs;
nsTArray<FileSystemDirectoryListingResponseData> inputs;
for (unsigned i = 0; i < mTargetData.Length(); i++) {
if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath) {
FileSystemDirectoryListingResponseFile fileData;
fileData.fileRealPath() = mTargetData[i].mPath;
inputs.AppendElement(fileData);
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath);
FileSystemDirectoryListingResponseDirectory directoryData;
directoryData.directoryRealPath() = mTargetData[i].mPath;
inputs.AppendElement(directoryData);
}
}
FileSystemDirectoryListingResponse response;
response.data().SwapElements(inputs);
return response;
}
void
GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
@ -186,8 +130,152 @@ GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue&
}
}
void
GetDirectoryListingTask::HandlerCallback()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
size_t count = mTargetData.Length();
Sequence<OwningFileOrDirectory> listing;
if (!listing.SetLength(count, mozilla::fallible_t())) {
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
mPromise = nullptr;
return;
}
for (unsigned i = 0; i < count; i++) {
nsCOMPtr<nsIFile> path;
NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mPath);
nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
}
#ifdef DEBUG
nsCOMPtr<nsIFile> rootPath;
rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
getter_AddRefs(rootPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
}
MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
#endif
if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) {
RefPtr<Directory> directory =
Directory::Create(mFileSystem->GetParentObject(), path,
Directory::eNotDOMRootDirectory, mFileSystem);
MOZ_ASSERT(directory);
// Propogate mFilter onto sub-Directory object:
directory->SetContentFilters(mFilters);
listing[i].SetAsDirectory() = directory;
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath);
RefPtr<File> file =
File::CreateFromFile(mFileSystem->GetParentObject(), path);
MOZ_ASSERT(file);
listing[i].SetAsFile() = file;
}
}
mPromise->MaybeResolve(listing);
mPromise = nullptr;
}
void
GetDirectoryListingTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
}
/**
* GetDirectoryListingTaskParent
*/
/* static */ already_AddRefed<GetDirectoryListingTaskParent>
GetDirectoryListingTaskParent::Create(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
RefPtr<GetDirectoryListingTaskParent> task =
new GetDirectoryListingTaskParent(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mType = aParam.isRoot()
? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
return task.forget();
}
GetDirectoryListingTaskParent::GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
, mFilters(aParam.filters())
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
}
FileSystemResponseValue
GetDirectoryListingTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
{
AssertIsOnBackgroundThread();
InfallibleTArray<PBlobParent*> blobs;
nsTArray<FileSystemDirectoryListingResponseData> inputs;
for (unsigned i = 0; i < mTargetData.Length(); i++) {
if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath) {
FileSystemDirectoryListingResponseFile fileData;
fileData.fileRealPath() = mTargetData[i].mPath;
inputs.AppendElement(fileData);
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath);
FileSystemDirectoryListingResponseDirectory directoryData;
directoryData.directoryRealPath() = mTargetData[i].mPath;
inputs.AppendElement(directoryData);
}
}
FileSystemDirectoryListingResponse response;
response.data().SwapElements(inputs);
return response;
}
nsresult
GetDirectoryListingTask::Work()
GetDirectoryListingTaskParent::IOWork()
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
@ -303,81 +391,9 @@ GetDirectoryListingTask::Work()
}
void
GetDirectoryListingTask::HandlerCallback()
GetDirectoryListingTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
size_t count = mTargetData.Length();
Sequence<OwningFileOrDirectory> listing;
if (!listing.SetLength(count, mozilla::fallible_t())) {
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
mPromise = nullptr;
return;
}
for (unsigned i = 0; i < count; i++) {
nsCOMPtr<nsIFile> path;
NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mPath);
nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
}
#ifdef DEBUG
nsCOMPtr<nsIFile> rootPath;
rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
getter_AddRefs(rootPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
}
MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
#endif
if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) {
RefPtr<Directory> directory =
Directory::Create(mFileSystem->GetParentObject(), path,
Directory::eNotDOMRootDirectory, mFileSystem);
MOZ_ASSERT(directory);
// Propogate mFilter onto sub-Directory object:
directory->SetContentFilters(mFilters);
listing[i].SetAsDirectory() = directory;
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath);
RefPtr<File> file =
File::CreateFromFile(mFileSystem->GetParentObject(), path);
MOZ_ASSERT(file);
listing[i].SetAsFile() = file;
}
}
mPromise->MaybeResolve(listing);
mPromise = nullptr;
}
void
GetDirectoryListingTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("read");
aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
}
} // namespace dom

View File

@ -27,12 +27,6 @@ public:
const nsAString& aFilters,
ErrorResult& aRv);
static already_AddRefed<GetDirectoryListingTask>
Create(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual
~GetDirectoryListingTask();
@ -49,24 +43,14 @@ private:
Directory::DirectoryType aType,
const nsAString& aFilters);
GetDirectoryListingTask(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent);
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
virtual nsresult
Work() override;
virtual void
HandlerCallback() override;
@ -80,6 +64,38 @@ private:
FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
};
class GetDirectoryListingTaskParent final : public FileSystemTaskParentBase
{
public:
static already_AddRefed<GetDirectoryListingTaskParent>
Create(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
private:
GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent);
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual nsresult
IOWork() override;
nsCOMPtr<nsIFile> mTargetPath;
nsString mFilters;
Directory::DirectoryType mType;
// We cannot store File or Directory objects bacause this object is created
// on a different thread and File and Directory are not thread-safe.
FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
};
} // namespace dom
} // namespace mozilla

View File

@ -10,15 +10,22 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/PFileSystemParams.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
#define GET_FILE_OR_DIRECTORY_PERMISSION "read"
namespace mozilla {
namespace dom {
/**
* GetFileOrDirectoryTask
*/
/* static */ already_AddRefed<GetFileOrDirectoryTask>
GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
@ -49,30 +56,6 @@ GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
return task.forget();
}
/* static */ already_AddRefed<GetFileOrDirectoryTask>
GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<GetFileOrDirectoryTask> task =
new GetFileOrDirectoryTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mType = aParam.isRoot()
? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
return task.forget();
}
GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
@ -86,21 +69,9 @@ GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
MOZ_ASSERT(aFileSystem);
}
GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mIsDirectory(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
}
GetFileOrDirectoryTask::~GetFileOrDirectoryTask()
{
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
"mPromise should be released on main thread!");
MOZ_ASSERT(NS_IsMainThread());
}
already_AddRefed<Promise>
@ -126,23 +97,6 @@ GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
mType == Directory::eDOMRootDirectory);
}
FileSystemResponseValue
GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemDirectoryResponse();
}
if (mIsDirectory) {
return FileSystemDirectoryResponse(path);
}
return FileSystemFileResponse(path);
}
void
GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
@ -180,8 +134,104 @@ GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& a
}
}
void
GetFileOrDirectoryTask::HandlerCallback()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
if (mIsDirectory) {
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
mTargetPath,
mType,
mFileSystem);
MOZ_ASSERT(dir);
mPromise->MaybeResolve(dir);
mPromise = nullptr;
return;
}
RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
mTargetPath);
mPromise->MaybeResolve(file);
mPromise = nullptr;
}
void
GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
}
/**
* GetFileOrDirectoryTaskParent
*/
/* static */ already_AddRefed<GetFileOrDirectoryTaskParent>
GetFileOrDirectoryTaskParent::Create(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
RefPtr<GetFileOrDirectoryTaskParent> task =
new GetFileOrDirectoryTaskParent(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mType = aParam.isRoot()
? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
return task.forget();
}
GetFileOrDirectoryTaskParent::GetFileOrDirectoryTaskParent(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
, mIsDirectory(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
}
FileSystemResponseValue
GetFileOrDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
{
AssertIsOnBackgroundThread();
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemDirectoryResponse();
}
if (mIsDirectory) {
return FileSystemDirectoryResponse(path);
}
return FileSystemFileResponse(path);
}
nsresult
GetFileOrDirectoryTask::Work()
GetFileOrDirectoryTaskParent::IOWork()
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
@ -245,42 +295,9 @@ GetFileOrDirectoryTask::Work()
}
void
GetFileOrDirectoryTask::HandlerCallback()
GetFileOrDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
if (mIsDirectory) {
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
mTargetPath,
mType,
mFileSystem);
MOZ_ASSERT(dir);
mPromise->MaybeResolve(dir);
mPromise = nullptr;
return;
}
RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
mTargetPath);
mPromise->MaybeResolve(file);
mPromise = nullptr;
}
void
GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("read");
aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
}
} // namespace dom

View File

@ -27,12 +27,6 @@ public:
bool aDirectoryOnly,
ErrorResult& aRv);
static already_AddRefed<GetFileOrDirectoryTask>
Create(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual
~GetFileOrDirectoryTask();
@ -41,21 +35,15 @@ public:
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
virtual nsresult
Work() override;
virtual void
HandlerCallback() override;
@ -66,10 +54,6 @@ private:
Directory::DirectoryType aType,
bool aDirectoryOnly);
GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent);
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
@ -78,6 +62,37 @@ private:
Directory::DirectoryType mType;
};
class GetFileOrDirectoryTaskParent final : public FileSystemTaskParentBase
{
public:
static already_AddRefed<GetFileOrDirectoryTaskParent>
Create(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
protected:
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual nsresult
IOWork() override;
private:
GetFileOrDirectoryTaskParent(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent);
nsCOMPtr<nsIFile> mTargetPath;
// Whether we get a directory.
bool mIsDirectory;
Directory::DirectoryType mType;
};
} // namespace dom
} // namespace mozilla

View File

@ -20,10 +20,7 @@ namespace dom {
OSFileSystem::OSFileSystem(const nsAString& aRootDir)
{
mLocalOrDeviceStorageRootPath = aRootDir;
// Non-mobile devices don't have the concept of separate permissions to
// access different parts of devices storage like Pictures, or Videos, etc.
mRequiresPermissionChecks = false;
mPermissionCheckType = ePermissionCheckNotRequired;
#ifdef DEBUG
mPermission.AssignLiteral("never-used");
@ -33,6 +30,8 @@ OSFileSystem::OSFileSystem(const nsAString& aRootDir)
already_AddRefed<FileSystemBase>
OSFileSystem::Clone()
{
AssertIsOnOwningThread();
RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalOrDeviceStorageRootPath);
if (mParent) {
fs->Init(mParent);
@ -44,9 +43,11 @@ OSFileSystem::Clone()
void
OSFileSystem::Init(nsISupports* aParent)
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(!mParent, "No duple Init() calls");
MOZ_ASSERT(aParent);
mParent = aParent;
#ifdef DEBUG
@ -58,6 +59,7 @@ OSFileSystem::Init(nsISupports* aParent)
nsISupports*
OSFileSystem::GetParentObject() const
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return mParent;
}
@ -65,6 +67,7 @@ OSFileSystem::GetParentObject() const
void
OSFileSystem::GetRootName(nsAString& aRetval) const
{
AssertIsOnOwningThread();
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
}
@ -91,12 +94,18 @@ OSFileSystem::IsSafeDirectory(Directory* aDir) const
void
OSFileSystem::Unlink()
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
mParent = nullptr;
}
void
OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
{
AssertIsOnOwningThread();
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
OSFileSystem* tmp = this;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent);
}
@ -104,8 +113,23 @@ OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
void
OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
{
AssertIsOnOwningThread();
aOutput = mLocalOrDeviceStorageRootPath;
}
/**
* OSFileSystemParent
*/
OSFileSystemParent::OSFileSystemParent(const nsAString& aRootDir)
{
mLocalOrDeviceStorageRootPath = aRootDir;
mPermissionCheckType = ePermissionCheckNotRequired;
#ifdef DEBUG
mPermission.AssignLiteral("never-used");
#endif
}
} // namespace dom
} // namespace mozilla

View File

@ -47,7 +47,71 @@ public:
private:
virtual ~OSFileSystem() {}
nsCOMPtr<nsISupports> mParent;
nsCOMPtr<nsISupports> mParent;
};
class OSFileSystemParent final : public FileSystemBase
{
public:
explicit OSFileSystemParent(const nsAString& aRootDir);
// Overrides FileSystemBase
virtual already_AddRefed<FileSystemBase>
Clone() override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
return nullptr;
}
virtual nsISupports*
GetParentObject() const override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
return nullptr;
}
virtual void
GetRootName(nsAString& aRetval) const override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
}
virtual bool
IsSafeFile(nsIFile* aFile) const override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
return true;
}
virtual bool
IsSafeDirectory(Directory* aDir) const override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
return true;
}
virtual void
SerializeDOMPath(nsAString& aOutput) const override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
}
// CC methods
virtual void
Unlink() override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
}
virtual void
Traverse(nsCycleCollectionTraversalCallback &cb) override
{
MOZ_CRASH("This should not be called on the PBackground thread.");
}
private:
virtual ~OSFileSystemParent() {}
};
} // namespace dom

View File

@ -0,0 +1,72 @@
/* 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 PBlob;
namespace mozilla {
namespace dom {
struct FileSystemCreateDirectoryParams
{
nsString filesystem;
nsString realPath;
};
union FileSystemFileDataValue
{
uint8_t[];
PBlob;
};
struct FileSystemCreateFileParams
{
nsString filesystem;
nsString realPath;
FileSystemFileDataValue data;
bool replace;
};
struct FileSystemGetDirectoryListingParams
{
nsString filesystem;
nsString realPath;
bool isRoot;
// 'filters' could be an array rather than a semicolon separated string
// (we'd then use InfallibleTArray<nsString> internally), but that is
// wasteful. E10s requires us to pass the filters over as a string anyway,
// so avoiding using an array avoids serialization on the side passing the
// filters. Since an nsString can share its buffer when copied,
// using that instead of InfallibleTArray<nsString> makes copying the filters
// around in any given process a bit more efficient too, since copying a
// single nsString is cheaper than copying InfallibleTArray member data and
// each nsString that it contains.
nsString filters;
};
struct FileSystemGetFileOrDirectoryParams
{
nsString filesystem;
nsString realPath;
bool isRoot;
};
struct FileSystemRemoveParams
{
nsString filesystem;
nsString directory;
nsString targetDirectory;
bool recursive;
};
union FileSystemParams
{
FileSystemCreateDirectoryParams;
FileSystemCreateFileParams;
FileSystemGetDirectoryListingParams;
FileSystemGetFileOrDirectoryParams;
FileSystemRemoveParams;
};
} // dom namespace
} // mozilla namespace

View File

@ -4,8 +4,8 @@
* 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 PBackground;
include protocol PBlob;
include protocol PContent;
namespace mozilla {
namespace dom {
@ -62,9 +62,9 @@ union FileSystemResponseValue
FileSystemErrorResponse;
};
sync protocol PFileSystemRequest
protocol PFileSystemRequest
{
manager PContent;
manager PBackground;
child:
async __delete__(FileSystemResponseValue response);

View File

@ -9,6 +9,7 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/PFileSystemParams.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
@ -18,6 +19,10 @@
namespace mozilla {
namespace dom {
/**
* RemoveTask
*/
/* static */ already_AddRefed<RemoveTask>
RemoveTask::Create(FileSystemBase* aFileSystem,
nsIFile* aDirPath,
@ -50,42 +55,6 @@ RemoveTask::Create(FileSystemBase* aFileSystem,
return task.forget();
}
/* static */ already_AddRefed<RemoveTask>
RemoveTask::Create(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<RemoveTask> task =
new RemoveTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
aRv = NS_NewNativeLocalFile(directoryPath, true,
getter_AddRefs(task->mDirPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mRecursive = aParam.recursive();
NS_ConvertUTF16toUTF8 path(aParam.targetDirectory());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (!FileSystemUtils::IsDescendantPath(task->mDirPath, task->mTargetPath)) {
aRv.Throw(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR);
return nullptr;
}
return task.forget();
}
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
nsIFile* aDirPath,
nsIFile* aTargetPath,
@ -102,22 +71,9 @@ RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
MOZ_ASSERT(aTargetPath);
}
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mRecursive(false)
, mReturnValue(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
}
RemoveTask::~RemoveTask()
{
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
"mPromise should be released on main thread!");
MOZ_ASSERT(NS_IsMainThread());
}
already_AddRefed<Promise>
@ -153,24 +109,104 @@ RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath,
return param;
}
FileSystemResponseValue
RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return FileSystemBooleanResponse(mReturnValue);
}
void
RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemBooleanResponse r = aValue;
mReturnValue = r.success();
}
void
RemoveTask::HandlerCallback()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
mPromise->MaybeResolve(mReturnValue);
mPromise = nullptr;
}
void
RemoveTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
}
/**
* RemoveTaskParent
*/
/* static */ already_AddRefed<RemoveTaskParent>
RemoveTaskParent::Create(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
RefPtr<RemoveTaskParent> task =
new RemoveTaskParent(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
aRv = NS_NewNativeLocalFile(directoryPath, true,
getter_AddRefs(task->mDirPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mRecursive = aParam.recursive();
NS_ConvertUTF16toUTF8 path(aParam.targetDirectory());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (!FileSystemUtils::IsDescendantPath(task->mDirPath, task->mTargetPath)) {
aRv.Throw(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR);
return nullptr;
}
return task.forget();
}
RemoveTaskParent::RemoveTaskParent(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskParentBase(aFileSystem, aParam, aParent)
, mRecursive(false)
, mReturnValue(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
AssertIsOnBackgroundThread();
MOZ_ASSERT(aFileSystem);
}
FileSystemResponseValue
RemoveTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
{
AssertIsOnBackgroundThread();
return FileSystemBooleanResponse(mReturnValue);
}
nsresult
RemoveTask::Work()
RemoveTaskParent::IOWork()
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
@ -214,28 +250,9 @@ RemoveTask::Work()
}
void
RemoveTask::HandlerCallback()
RemoveTaskParent::GetPermissionAccessType(nsCString& aAccess) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
mPromise->MaybeReject(mErrorValue);
mPromise = nullptr;
return;
}
mPromise->MaybeResolve(mReturnValue);
mPromise = nullptr;
}
void
RemoveTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("write");
aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
}
} // namespace dom

View File

@ -11,6 +11,8 @@
#include "nsAutoPtr.h"
#include "mozilla/ErrorResult.h"
#define REMOVE_TASK_PERMISSION "write"
namespace mozilla {
namespace dom {
@ -27,12 +29,6 @@ public:
bool aRecursive,
ErrorResult& aRv);
static already_AddRefed<RemoveTask>
Create(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual
~RemoveTask();
@ -47,16 +43,10 @@ protected:
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
virtual nsresult
Work() override;
virtual void
HandlerCallback() override;
@ -66,10 +56,6 @@ private:
nsIFile* aTargetPath,
bool aRecursive);
RemoveTask(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent);
RefPtr<Promise> mPromise;
// This path is the Directory::mFile.
@ -82,6 +68,40 @@ private:
bool mReturnValue;
};
class RemoveTaskParent final : public FileSystemTaskParentBase
{
public:
static already_AddRefed<RemoveTaskParent>
Create(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
protected:
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
virtual nsresult
IOWork() override;
private:
RemoveTaskParent(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent);
// This path is the Directory::mFile.
nsCOMPtr<nsIFile> mDirPath;
// This is what we want to remove. mTargetPath is discendant path of mDirPath.
nsCOMPtr<nsIFile> mTargetPath;
bool mRecursive;
bool mReturnValue;
};
} // namespace dom
} // namespace mozilla

View File

@ -35,6 +35,7 @@ UNIFIED_SOURCES += [
FINAL_LIBRARY = 'xul'
IPDL_SOURCES += [
'PFileSystemParams.ipdlh',
'PFileSystemRequest.ipdl',
]

View File

@ -176,8 +176,6 @@
#include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
#include "mozilla/dom/mobilemessage/SmsChild.h"
#include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
#include "mozilla/dom/PFileSystemRequestChild.h"
#include "mozilla/dom/FileSystemTaskBase.h"
#include "mozilla/dom/bluetooth/PBluetoothChild.h"
#include "mozilla/dom/PFMRadioChild.h"
#include "mozilla/dom/PPresentationChild.h"
@ -1828,24 +1826,6 @@ ContentChild::DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild* aDev
return true;
}
PFileSystemRequestChild*
ContentChild::AllocPFileSystemRequestChild(const FileSystemParams& aParams)
{
MOZ_CRASH("Should never get here!");
return nullptr;
}
bool
ContentChild::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aFileSystem)
{
mozilla::dom::FileSystemTaskBase* child =
static_cast<mozilla::dom::FileSystemTaskBase*>(aFileSystem);
// The reference is increased in FileSystemTaskBase::Start of
// FileSystemTaskBase.cpp. We should decrease it after IPC.
NS_RELEASE(child);
return true;
}
PMobileConnectionChild*
ContentChild::SendPMobileConnectionConstructor(PMobileConnectionChild* aActor,
const uint32_t& aClientId)

View File

@ -191,12 +191,6 @@ public:
virtual bool
DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild*) override;
virtual PFileSystemRequestChild*
AllocPFileSystemRequestChild(const FileSystemParams&) override;
virtual bool
DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
virtual PBlobChild*
AllocPBlobChild(const BlobConstructorParams& aParams) override;

View File

@ -47,7 +47,6 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/ExternalHelperAppParent.h"
#include "mozilla/dom/FileSystemRequestParent.h"
#include "mozilla/dom/GeolocationBinding.h"
#ifdef MOZ_EME
#include "mozilla/dom/MediaKeySystemAccess.h"
@ -3521,24 +3520,6 @@ ContentParent::DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent* d
return true;
}
PFileSystemRequestParent*
ContentParent::AllocPFileSystemRequestParent(const FileSystemParams& aParams)
{
RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
if (!result->Dispatch(this, aParams)) {
return nullptr;
}
return result.forget().take();
}
bool
ContentParent::DeallocPFileSystemRequestParent(PFileSystemRequestParent* doomed)
{
FileSystemRequestParent* parent = static_cast<FileSystemRequestParent*>(doomed);
NS_RELEASE(parent);
return true;
}
PBlobParent*
ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams)
{

View File

@ -737,12 +737,6 @@ private:
virtual bool
DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent*) override;
virtual PFileSystemRequestParent*
AllocPFileSystemRequestParent(const FileSystemParams&) override;
virtual bool
DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
virtual PBlobParent*
AllocPBlobParent(const BlobConstructorParams& aParams) override;

View File

@ -21,7 +21,6 @@ include protocol PHandlerService;
include protocol PDeviceStorageRequest;
include protocol PFileDescriptorSet;
include protocol PFMRadio;
include protocol PFileSystemRequest;
include protocol PHal;
include protocol PHeapSnapshotTempFileHelper;
include protocol PIcc;
@ -267,67 +266,6 @@ union FMRadioRequestParams
FMRadioRequestCancelSeekParams;
};
struct FileSystemCreateDirectoryParams
{
nsString filesystem;
nsString realPath;
};
union FileSystemFileDataValue
{
uint8_t[];
PBlob;
};
struct FileSystemCreateFileParams
{
nsString filesystem;
nsString realPath;
FileSystemFileDataValue data;
bool replace;
};
struct FileSystemGetDirectoryListingParams
{
nsString filesystem;
nsString realPath;
bool isRoot;
// 'filters' could be an array rather than a semicolon separated string
// (we'd then use InfallibleTArray<nsString> internally), but that is
// wasteful. E10s requires us to pass the filters over as a string anyway,
// so avoiding using an array avoids serialization on the side passing the
// filters. Since an nsString can share its buffer when copied,
// using that instead of InfallibleTArray<nsString> makes copying the filters
// around in any given process a bit more efficient too, since copying a
// single nsString is cheaper than copying InfallibleTArray member data and
// each nsString that it contains.
nsString filters;
};
struct FileSystemGetFileOrDirectoryParams
{
nsString filesystem;
nsString realPath;
bool isRoot;
};
struct FileSystemRemoveParams
{
nsString filesystem;
nsString directory;
nsString targetDirectory;
bool recursive;
};
union FileSystemParams
{
FileSystemCreateDirectoryParams;
FileSystemCreateFileParams;
FileSystemGetDirectoryListingParams;
FileSystemGetFileOrDirectoryParams;
FileSystemRemoveParams;
};
union PrefValue {
nsCString;
int32_t;
@ -474,7 +412,6 @@ prio(normal upto urgent) sync protocol PContent
manages PCrashReporter;
manages PCycleCollectWithLogs;
manages PDeviceStorageRequest;
manages PFileSystemRequest;
manages PPSMContentDownloader;
manages PExternalHelperApp;
manages PFileDescriptorSet;
@ -825,8 +762,6 @@ parent:
async PRemoteSpellcheckEngine();
async PDeviceStorageRequest(DeviceStorageParams params);
async PFileSystemRequest(FileSystemParams params);
sync PCrashReporter(NativeThreadId tid, uint32_t processType);
async GetSystemMemory(uint64_t getterId);

View File

@ -14,6 +14,8 @@
#include "mozilla/media/MediaChild.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/PBlobChild.h"
#include "mozilla/dom/PFileSystemRequestChild.h"
#include "mozilla/dom/FileSystemTaskBase.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
@ -449,6 +451,23 @@ BackgroundChildImpl::DeallocPQuotaChild(PQuotaChild* aActor)
return true;
}
dom::PFileSystemRequestChild*
BackgroundChildImpl::AllocPFileSystemRequestChild(const FileSystemParams& aParams)
{
MOZ_CRASH("Should never get here!");
return nullptr;
}
bool
BackgroundChildImpl::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aActor)
{
// The reference is increased in FileSystemTaskBase::Start of
// FileSystemTaskBase.cpp. We should decrease it after IPC.
RefPtr<dom::FileSystemTaskBase> child =
dont_AddRef(static_cast<dom::FileSystemTaskBase*>(aActor));
return true;
}
} // namespace ipc
} // namespace mozilla

View File

@ -158,6 +158,13 @@ protected:
virtual bool
DeallocPQuotaChild(PQuotaChild* aActor) override;
virtual PFileSystemRequestChild*
AllocPFileSystemRequestChild(const FileSystemParams&) override;
virtual bool
DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
};
class BackgroundChildImpl::ThreadLocal final

View File

@ -14,6 +14,8 @@
#include "mozilla/Assertions.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemRequestParent.h"
#include "mozilla/dom/NuwaParent.h"
#include "mozilla/dom/PBlobParent.h"
#include "mozilla/dom/MessagePortParent.h"
@ -29,9 +31,11 @@
#include "mozilla/ipc/PBackgroundTestParent.h"
#include "mozilla/layout/VsyncParent.h"
#include "mozilla/dom/network/UDPSocketParent.h"
#include "mozilla/Preferences.h"
#include "nsIAppsService.h"
#include "nsNetUtil.h"
#include "nsIScriptSecurityManager.h"
#include "nsProxyRelease.h"
#include "mozilla/RefPtr.h"
#include "nsThreadUtils.h"
#include "nsTraceRefcnt.h"
@ -49,10 +53,12 @@ using mozilla::dom::asmjscache::PAsmJSCacheEntryParent;
using mozilla::dom::cache::PCacheParent;
using mozilla::dom::cache::PCacheStorageParent;
using mozilla::dom::cache::PCacheStreamControlParent;
using mozilla::dom::FileSystemBase;
using mozilla::dom::FileSystemRequestParent;
using mozilla::dom::MessagePortParent;
using mozilla::dom::NuwaParent;
using mozilla::dom::PMessagePortParent;
using mozilla::dom::PNuwaParent;
using mozilla::dom::NuwaParent;
using mozilla::dom::UDPSocketParent;
namespace {
@ -455,6 +461,20 @@ BackgroundParentImpl::AllocPBroadcastChannelParent(
namespace {
struct MOZ_STACK_CLASS NullifyContentParentRAII
{
explicit NullifyContentParentRAII(RefPtr<ContentParent>& aContentParent)
: mContentParent(aContentParent)
{}
~NullifyContentParentRAII()
{
mContentParent = nullptr;
}
RefPtr<ContentParent>& mContentParent;
};
class CheckPrincipalRunnable final : public nsRunnable
{
public:
@ -475,21 +495,7 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
struct MOZ_STACK_CLASS RunRAII
{
explicit RunRAII(RefPtr<ContentParent>& aContentParent)
: mContentParent(aContentParent)
{}
~RunRAII()
{
mContentParent = nullptr;
}
RefPtr<ContentParent>& mContentParent;
};
RunRAII raii(mContentParent);
NullifyContentParentRAII raii(mContentParent);
nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
AssertAppPrincipal(mContentParent, principal);
@ -522,6 +528,87 @@ private:
nsCString mOrigin;
};
class CheckPermissionRunnable final : public nsRunnable
{
public:
CheckPermissionRunnable(already_AddRefed<ContentParent> aParent,
FileSystemRequestParent* aActor,
FileSystemBase::ePermissionCheckType aPermissionCheckType,
const nsCString& aPermissionName)
: mContentParent(aParent)
, mActor(aActor)
, mPermissionCheckType(aPermissionCheckType)
, mPermissionName(aPermissionName)
, mBackgroundEventTarget(NS_GetCurrentThread())
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
MOZ_ASSERT(mContentParent);
MOZ_ASSERT(mBackgroundEventTarget);
MOZ_ASSERT(mPermissionCheckType == FileSystemBase::ePermissionCheckRequired ||
mPermissionCheckType == FileSystemBase::ePermissionCheckByTestingPref);
}
NS_IMETHOD
Run() override
{
if (NS_IsMainThread()) {
NullifyContentParentRAII raii(mContentParent);
// If the permission is granted, we go back to the background thread to
// dispatch this task.
if (CheckPermission()) {
return mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
}
return NS_OK;
}
AssertIsOnBackgroundThread();
// It can happen that this actor has been destroyed in the meantime we were
// on the main-thread.
if (!mActor->Destroyed()) {
mActor->Start();
}
return NS_OK;
}
private:
~CheckPermissionRunnable()
{
NS_ProxyRelease(mBackgroundEventTarget, mActor.forget());
}
bool
CheckPermission()
{
if (mPermissionCheckType == FileSystemBase::ePermissionCheckByTestingPref &&
mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
return true;
}
if (!AssertAppProcessPermission(mContentParent.get(),
mPermissionName.get())) {
mContentParent->KillHard("PBackground actor killed: permission denied.");
return false;
}
return true;
}
RefPtr<ContentParent> mContentParent;
RefPtr<FileSystemRequestParent> mActor;
FileSystemBase::ePermissionCheckType mPermissionCheckType;
nsCString mPermissionName;
nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
};
} // namespace
bool
@ -736,6 +823,73 @@ BackgroundParentImpl::DeallocPQuotaParent(PQuotaParent* aActor)
return mozilla::dom::quota::DeallocPQuotaParent(aActor);
}
dom::PFileSystemRequestParent*
BackgroundParentImpl::AllocPFileSystemRequestParent(
const FileSystemParams& aParams)
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
if (NS_WARN_IF(!result->Initialize(aParams))) {
return nullptr;
}
return result.forget().take();
}
bool
BackgroundParentImpl::RecvPFileSystemRequestConstructor(
PFileSystemRequestParent* aActor,
const FileSystemParams& aParams)
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
RefPtr<FileSystemRequestParent> actor = static_cast<FileSystemRequestParent*>(aActor);
if (actor->PermissionCheckType() == FileSystemBase::ePermissionCheckNotRequired) {
actor->Start();
return true;
}
RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
// If the ContentParent is null we are dealing with a same-process actor.
if (!parent) {
actor->Start();
return true;
}
const nsCString& permissionName = actor->PermissionName();
MOZ_ASSERT(!permissionName.IsEmpty());
// At this point we should have the right permission but we do the last check
// with this runnable. If the app doesn't have the permission, we kill the
// child process.
RefPtr<CheckPermissionRunnable> runnable =
new CheckPermissionRunnable(parent.forget(), actor,
actor->PermissionCheckType(), permissionName);
nsresult rv = NS_DispatchToMainThread(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
return true;
}
bool
BackgroundParentImpl::DeallocPFileSystemRequestParent(
PFileSystemRequestParent* aDoomed)
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
RefPtr<FileSystemRequestParent> parent =
dont_AddRef(static_cast<FileSystemRequestParent*>(aDoomed));
return true;
}
} // namespace ipc
} // namespace mozilla

View File

@ -186,6 +186,17 @@ protected:
virtual bool
DeallocPQuotaParent(PQuotaParent* aActor) override;
virtual PFileSystemRequestParent*
AllocPFileSystemRequestParent(const FileSystemParams&) override;
virtual bool
RecvPFileSystemRequestConstructor(PFileSystemRequestParent* aActor,
const FileSystemParams& aParams) override;
virtual bool
DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
};
} // namespace ipc

View File

@ -12,6 +12,7 @@ include protocol PCache;
include protocol PCacheStorage;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
include protocol PFileSystemRequest;
include protocol PMessagePort;
include protocol PCameras;
include protocol PNuwa;
@ -23,6 +24,7 @@ include protocol PVsync;
include DOMTypes;
include PBackgroundSharedTypes;
include PBackgroundIDBSharedTypes;
include PFileSystemParams;
include "mozilla/dom/cache/IPCUtils.h";
@ -50,6 +52,7 @@ sync protocol PBackground
manages PCacheStorage;
manages PCacheStreamControl;
manages PFileDescriptorSet;
manages PFileSystemRequest;
manages PMessagePort;
manages PCameras;
manages PNuwa;
@ -95,6 +98,8 @@ parent:
async PQuota();
async PFileSystemRequest(FileSystemParams params);
child:
async PCache();
async PCacheStreamControl();