Bug 934367 - Implement createFile for Directory. r=dhylands

This commit is contained in:
Yuan Xulei 2014-03-05 16:40:48 +08:00
parent 0380da9a54
commit 85163bc565
11 changed files with 851 additions and 0 deletions

View File

@ -29,3 +29,4 @@ support-files = devicestorage_common.js
[test_fs_createDirectory.html]
[test_fs_get.html]
[test_fs_remove.html]
[test_fs_createFile.html]

View File

@ -31,6 +31,26 @@ function randomFilename(l) {
return result;
}
function getRandomBuffer() {
var size = 1024;
var buffer = new ArrayBuffer(size);
var view = new Uint8Array(buffer);
for (var i = 0; i < size; i++) {
view[i] = parseInt(Math.random() * 255);
}
return buffer;
}
function createRandomBlob(mime) {
let size = 1024;
let buffer = new ArrayBuffer(size);
let view = new Uint8Array(buffer);
for (let i = 0; i < size; i++) {
view[i] = parseInt(Math.random() * 255);
}
return blob = new Blob([buffer], {type: mime});
}
let MockPermissionPrompt = SpecialPowers.MockPermissionPrompt;
MockPermissionPrompt.init();
@ -88,6 +108,34 @@ function TestGet(iframe, data) {
}, cbError);
}
function TestCreateFile(iframe, data) {
function cbError(e) {
is(e.name, "SecurityError", "[TestCreateFile] Should fire a SecurityError for type " + data.type);
is(data.shouldPass, false, "[TestCreateFile] Error callback was called for type " + data.type + '. Error: ' + e.name);
testComplete(iframe, data);
}
let storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
isnot(storage, null, "[TestCreateFile] Should be able to get storage object for " + data.type);
if (!storage) {
testComplete(iframe, data);
return;
}
storage.getRoot().then(function(root) {
ok(true, "[TestCreateFile] Success callback of getRoot was called for type " + data.type);
let filename = randomFilename(100) + data.fileExtension;
root.createFile(filename, {
data: createRandomBlob(data.mimeType),
ifExists: "replace"
}).then(function() {
is(data.shouldPass, true, "[TestCreateFile] Success callback was called for type " + data.type);
testComplete(iframe, data);
}, cbError);
}, cbError);
}
function TestRemove(iframe, data) {
function cbError(e) {
is(e.name, "SecurityError", "[TestRemove] Should fire a SecurityError for type " + data.type);
@ -372,6 +420,182 @@ let gData = [
test: TestCreateDirectory
},
// Directory#createFile
// Web applications with no permissions
{
type: 'pictures',
mimeType: 'image/png',
shouldPass: false,
fileExtension: '.png',
test: TestCreateFile
},
{
type: 'videos',
mimeType: 'video/ogv',
shouldPass: false,
fileExtension: '.ogv',
test: TestCreateFile
},
{
type: 'videos',
mimeType: 'video/ogg',
shouldPass: false,
fileExtension: '.ogg',
test: TestCreateFile
},
{
type: 'music',
mimeType: 'audio/ogg',
shouldPass: false,
fileExtension: '.ogg',
test: TestCreateFile
},
{
type: 'music',
mimeType: 'audio/ogg',
shouldPass: false,
fileExtension: '.txt',
test: TestCreateFile
},
{
type: 'sdcard',
mimeType: 'text/plain',
shouldPass: false,
fileExtension: '.txt',
test: TestCreateFile
},
// Web applications with permission granted
{
type: 'pictures',
mimeType: 'image/png',
shouldPass: true,
fileExtension: '.png',
permissions: ["device-storage:pictures"],
test: TestCreateFile
},
{
type: 'videos',
mimeType: 'video/ogv',
shouldPass: true,
fileExtension: '.ogv',
permissions: ["device-storage:videos"],
test: TestCreateFile
},
{
type: 'videos',
mimeType: 'video/ogg',
shouldPass: true,
fileExtension: '.ogg',
permissions: ["device-storage:videos"],
test: TestCreateFile
},
{
type: 'music',
mimeType: 'audio/ogg',
shouldPass: true,
fileExtension: '.ogg',
permissions: ["device-storage:music"],
test: TestCreateFile
},
{
type: 'music',
mimeType: 'audio/ogg',
shouldPass: false,
fileExtension: '.txt',
permissions: ["device-storage:music"],
test: TestCreateFile
},
{
type: 'sdcard',
mimeType: 'text/plain',
shouldPass: true,
fileExtension: '.txt',
permissions: ["device-storage:sdcard"],
test: TestCreateFile
},
// Certified application with permision granted
{
type: 'pictures',
mimeType: 'image/png',
shouldPass: true,
fileExtension: '.png',
app: "https://example.com/manifest_cert.webapp",
permissions: ["device-storage:pictures"],
test: TestCreateFile
},
{
type: 'videos',
mimeType: 'video/ogv',
shouldPass: true,
fileExtension: '.ogv',
app: "https://example.com/manifest_cert.webapp",
permissions: ["device-storage:videos"],
test: TestCreateFile
},
{
type: 'videos',
mimeType: 'video/ogg',
shouldPass: true,
fileExtension: '.ogg',
app: "https://example.com/manifest_cert.webapp",
permissions: ["device-storage:videos"],
test: TestCreateFile
},
{
type: 'music',
mimeType: 'audio/ogg',
shouldPass: true,
fileExtension: '.ogg',
app: "https://example.com/manifest_cert.webapp",
permissions: ["device-storage:music"],
test: TestCreateFile
},
{
type: 'music',
mimeType: 'audio/ogg',
shouldPass: false,
fileExtension: '.txt',
app: "https://example.com/manifest_cert.webapp",
permissions: ["device-storage:music"],
test: TestCreateFile
},
{
type: 'sdcard',
mimeType: 'text/plain',
shouldPass: true,
fileExtension: '.txt',
app: "https://example.com/manifest_cert.webapp",
permissions: ["device-storage:sdcard"],
test: TestCreateFile
},
// Directory#remove
// Web applications with no permissions

View File

@ -0,0 +1,132 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html> <!--
https://bugzilla.mozilla.org/show_bug.cgi?id=910412
-->
<head>
<title>Test createDirectory of the FileSystem API for device storage</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="devicestorage_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=910412">Mozilla Bug 910412</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
devicestorage_setup();
let gTestCount = 0;
let gFileReader = new FileReader();
let gRoot;
function str2array(str) {
let strlen = str.length;
let buf = new ArrayBuffer(strlen);
let bufView = new Uint8Array(buf);
for (let i=0; i < strlen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function array2str(data) {
return String.fromCharCode.apply(String, new Uint8Array(data));
}
let gTestCases = [
// Create with string data.
{
text: "My name is Yuan.",
get data() { return this.text; },
shouldPass: true,
mode: "replace"
},
// Create with array buffer data.
{
text: "I'm from Kunming.",
get data() { return str2array(this.text); },
shouldPass: true,
mode: "replace"
},
// Create with array buffer view data.
{
text: "Kunming is in Yunnan province of China.",
get data() { return Uint8Array(str2array(this.text)); },
shouldPass: true,
mode: "replace"
},
// Create with blob data.
{
text: "Kunming is in Yunnan province of China.",
get data() { return new Blob([this.text], {type: 'image/png'}); },
shouldPass: true,
mode: "replace"
},
// Don't overwrite existing file.
{
data: null,
shouldPass: false,
mode: "fail"
}
];
function next() {
if (gTestCount >= gTestCases.length) {
devicestorage_cleanup();
return;
}
let c = gTestCases[gTestCount++];
gRoot.createFile("text.png", {
data: c.data,
ifExists: c.mode
}).then(function(file) {
is(c.shouldPass, true, "[" + gTestCount + "] Success callback was called for createFile.");
if (!c.shouldPass) {
SimpleTest.executeSoon(next);
return;
}
// Check the file content.
gFileReader.readAsArrayBuffer(file);
gFileReader.onload = function(e) {
ab = e.target.result;
is(array2str(e.target.result), c.text, "[" + gTestCount + "] Wrong values.");
SimpleTest.executeSoon(next);
};
}, function(e) {
is(c.shouldPass, false, "[" + gTestCount + "] Error callback was called for createFile.");
SimpleTest.executeSoon(next);
});
}
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
let storage = navigator.getDeviceStorage("pictures");
ok(storage, "Should have gotten a storage.");
// Get the root directory
storage.getRoot().then(function(dir) {
ok(dir, "Should have gotten the root directory.");
gRoot = dir;
next();
}, function(e) {
ok(false, e.name + " error should not arrive here!");
devicestorage_cleanup();
});
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,321 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CreateFileTask.h"
#include <algorithm>
#include "DOMError.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
#include "nsDOMFile.h"
#include "nsIFile.h"
#include "nsNetUtil.h"
#include "nsStringGlue.h"
namespace mozilla {
namespace dom {
uint32_t CreateFileTask::sOutputBufferSize = 0;
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
const nsAString& aPath,
nsIDOMBlob* aBlobData,
InfallibleTArray<uint8_t>& aArrayData,
bool replace)
: FileSystemTaskBase(aFileSystem)
, mTargetRealPath(aPath)
, mBlobData(aBlobData)
, mReplace(replace)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
GetOutputBufferSize();
if (mBlobData) {
nsresult rv = mBlobData->GetInternalStream(getter_AddRefs(mBlobStream));
NS_WARN_IF(NS_FAILED(rv));
}
mArrayData.SwapElements(aArrayData);
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetWindow());
if (!globalObject) {
return;
}
mPromise = new Promise(globalObject);
}
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mReplace(false)
{
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
GetOutputBufferSize();
mTargetRealPath = aParam.realPath();
mReplace = aParam.replace();
auto& data = aParam.data();
if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
mArrayData = data;
return;
}
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
mBlobData = bp->GetBlob();
MOZ_ASSERT(mBlobData, "mBlobData should not be null.");
nsresult rv = mBlobData->GetInternalStream(getter_AddRefs(mBlobStream));
NS_WARN_IF(NS_FAILED(rv));
}
CreateFileTask::~CreateFileTask()
{
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
"mPromise should be released on main thread!");
if (mBlobStream) {
mBlobStream->Close();
}
}
already_AddRefed<Promise>
CreateFileTask::GetPromise()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return nsRefPtr<Promise>(mPromise).forget();
}
FileSystemParams
CreateFileTask::GetRequestParams(const nsString& aFileSystem) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemCreateFileParams param;
param.filesystem() = aFileSystem;
param.realPath() = mTargetRealPath;
param.replace() = mReplace;
if (mBlobData) {
BlobChild* actor
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
if (actor) {
param.data() = actor;
}
} else {
param.data() = mArrayData;
}
return param;
}
FileSystemResponseValue
CreateFileTask::GetSuccessRequestResult() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
BlobParent* actor = GetBlobParent(mTargetFile);
if (!actor) {
return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR);
}
FileSystemFileResponse response;
response.blobParent() = actor;
return response;
}
void
CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemFileResponse r = aValue;
BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
mTargetFile = do_QueryInterface(blob);
}
nsresult
CreateFileTask::Work()
{
class AutoClose
{
public:
AutoClose(nsIOutputStream* aStream)
: mStream(aStream)
{
MOZ_ASSERT(aStream);
}
~AutoClose()
{
mStream->Close();
}
private:
nsCOMPtr<nsIOutputStream> mStream;
};
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
if (mFileSystem->IsShutdown()) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
if (!file) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
if (!mFileSystem->IsSafeFile(file)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
bool exists = false;
nsresult rv = file->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (exists) {
bool isFile = false;
rv = file->IsFile(&isFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!isFile) {
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
}
if (!mReplace) {
return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
}
// Remove the old file before creating.
rv = file->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
AutoClose acOutputStream(outputStream);
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
outputStream,
sOutputBufferSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
AutoClose acBufferedOutputStream(bufferedOutputStream);
if (mBlobStream) {
// Write the file content from blob data.
uint64_t bufSize = 0;
rv = mBlobStream->Available(&bufSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (bufSize && !mFileSystem->IsShutdown()) {
uint32_t written = 0;
uint32_t writeSize = bufSize < UINT32_MAX ? bufSize : UINT32_MAX;
rv = bufferedOutputStream->WriteFrom(mBlobStream, writeSize, &written);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bufSize -= written;
}
mBlobStream->Close();
mBlobStream = nullptr;
if (mFileSystem->IsShutdown()) {
return NS_ERROR_FAILURE;
}
mTargetFile = new nsDOMFileFile(file);
return NS_OK;
}
// Write file content from array data.
uint32_t written;
rv = bufferedOutputStream->Write(
reinterpret_cast<char*>(mArrayData.Elements()),
mArrayData.Length(),
&written);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mArrayData.Length() != written) {
return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
}
mTargetFile = new nsDOMFileFile(file);
return NS_OK;
}
void
CreateFileTask::HandlerCallback()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
nsRefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
mErrorValue);
mPromise->MaybeReject(domError);
mPromise = nullptr;
return;
}
mPromise->MaybeResolve(mTargetFile);
mPromise = nullptr;
}
void
CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const
{
if (mReplace) {
aAccess.AssignLiteral("write");
return;
}
aAccess.AssignLiteral("create");
}
void
CreateFileTask::GetOutputBufferSize() const
{
if (sOutputBufferSize || !FileSystemUtils::IsParentProcess()) {
return;
}
sOutputBufferSize =
mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,76 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_CreateFileTask_h
#define mozilla_dom_CreateFileTask_h
#include "mozilla/dom/FileSystemTaskBase.h"
#include "nsAutoPtr.h"
class nsIDOMBlob;
class nsIInputStream;
namespace mozilla {
namespace dom {
class Promise;
class CreateFileTask MOZ_FINAL
: public FileSystemTaskBase
{
public:
CreateFileTask(FileSystemBase* aFileSystem,
const nsAString& aPath,
nsIDOMBlob* aBlobData,
InfallibleTArray<uint8_t>& aArrayData,
bool replace);
CreateFileTask(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent);
virtual
~CreateFileTask();
already_AddRefed<Promise>
GetPromise();
virtual void
GetPermissionAccessType(nsCString& aAccess) const MOZ_OVERRIDE;
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aFileSystem) const MOZ_OVERRIDE;
virtual FileSystemResponseValue
GetSuccessRequestResult() const MOZ_OVERRIDE;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue) MOZ_OVERRIDE;
virtual nsresult
Work() MOZ_OVERRIDE;
virtual void
HandlerCallback() MOZ_OVERRIDE;
private:
void
GetOutputBufferSize() const;
static uint32_t sOutputBufferSize;
nsRefPtr<Promise> mPromise;
nsString mTargetRealPath;
nsCOMPtr<nsIDOMBlob> mBlobData;
nsCOMPtr<nsIInputStream> mBlobStream;
InfallibleTArray<uint8_t> mArrayData;
bool mReplace;
nsCOMPtr<nsIDOMFile> mTargetFile;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_CreateFileTask_h

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/Directory.h"
#include "CreateDirectoryTask.h"
#include "CreateFileTask.h"
#include "FileSystemPermissionRequest.h"
#include "GetFileOrDirectoryTask.h"
#include "RemoveTask.h"
@ -24,6 +25,11 @@
#ifdef CreateDirectory
#undef CreateDirectory
#endif
// Undefine the macro of CreateFile to avoid Directory#CreateFile being replaced
// by Directory#CreateFileW.
#ifdef CreateFile
#undef CreateFile
#endif
namespace mozilla {
namespace dom {
@ -88,6 +94,44 @@ Directory::GetName(nsString& aRetval) const
mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
}
already_AddRefed<Promise>
Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions)
{
nsresult error = NS_OK;
nsString realPath;
nsRefPtr<nsIDOMBlob> blobData;
InfallibleTArray<uint8_t> arrayData;
bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
// Get the file content.
if (aOptions.mData.WasPassed()) {
auto& data = aOptions.mData.Value();
if (data.IsString()) {
NS_ConvertUTF16toUTF8 str(data.GetAsString());
arrayData.AppendElements(reinterpret_cast<const uint8_t *>(str.get()),
str.Length());
} else if (data.IsArrayBuffer()) {
ArrayBuffer& buffer = data.GetAsArrayBuffer();
arrayData.AppendElements(buffer.Data(), buffer.Length());
} else if (data.IsArrayBufferView()){
ArrayBufferView& view = data.GetAsArrayBufferView();
arrayData.AppendElements(view.Data(), view.Length());
} else {
blobData = data.GetAsBlob();
}
}
if (!DOMPathToRealPath(aPath, realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
nsRefPtr<CreateFileTask> task = new CreateFileTask(mFileSystem, realPath,
blobData, arrayData, replace);
task->SetError(error);
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
}
already_AddRefed<Promise>
Directory::CreateDirectory(const nsAString& aPath)
{

View File

@ -21,10 +21,16 @@
#ifdef CreateDirectory
#undef CreateDirectory
#endif
// Undefine the macro of CreateFile to avoid Directory#CreateFile being replaced
// by Directory#CreateFileW.
#ifdef CreateFile
#undef CreateFile
#endif
namespace mozilla {
namespace dom {
class CreateFileOptions;
class FileSystemBase;
class Promise;
class StringOrFileOrDirectory;
@ -55,6 +61,9 @@ public:
void
GetName(nsString& aRetval) const;
already_AddRefed<Promise>
CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions);
already_AddRefed<Promise>
CreateDirectory(const nsAString& aPath);

View File

@ -6,6 +6,7 @@
#include "mozilla/dom/FileSystemRequestParent.h"
#include "CreateDirectoryTask.h"
#include "CreateFileTask.h"
#include "GetFileOrDirectoryTask.h"
#include "RemoveTask.h"
@ -40,6 +41,7 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
switch (aParams.type()) {
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)

View File

@ -15,6 +15,7 @@ EXPORTS.mozilla.dom += [
SOURCES += [
'CreateDirectoryTask.cpp',
'CreateFileTask.cpp',
'DeviceStorageFileSystem.cpp',
'Directory.cpp',
'FileSystemBase.cpp',

View File

@ -201,6 +201,20 @@ struct FileSystemCreateDirectoryParams
nsString realPath;
};
union FileSystemFileDataValue
{
uint8_t[];
PBlob;
};
struct FileSystemCreateFileParams
{
nsString filesystem;
nsString realPath;
FileSystemFileDataValue data;
bool replace;
};
struct FileSystemGetFileOrDirectoryParams
{
nsString filesystem;
@ -224,6 +238,7 @@ struct FileSystemRemoveParams
union FileSystemParams
{
FileSystemCreateDirectoryParams;
FileSystemCreateFileParams;
FileSystemGetFileOrDirectoryParams;
FileSystemRemoveParams;
};

View File

@ -21,6 +21,26 @@ interface Directory {
*/
readonly attribute DOMString name;
/*
* Creates a new file or replaces an existing file with given data. The file
* should be a descendent of current directory.
*
* @param path The relative path of the new file to current directory.
* @param options It has two optional properties, 'ifExists' and 'data'.
* If 'ifExists' is 'fail' and the path already exists, createFile must fail;
* If 'ifExists' is 'replace', the path already exists, and is a file, create
* a new file to replace the existing one;
* If 'ifExists' is 'replace', the path already exists, but is a directory,
* createFile must fail.
* Otherwise, if no other error occurs, createFile will create a new file.
* The 'data' property contains the new file's content.
* @return If succeeds, the promise is resolved with the new created
* File object. Otherwise, rejected with a DOM error.
*/
[NewObject]
// Promise<File>
Promise createFile(DOMString path, optional CreateFileOptions options);
/*
* Creates a descendent directory. This method will create any intermediate
* directories specified by the path segments.
@ -77,3 +97,9 @@ interface Directory {
Promise removeDeep((DOMString or File or Directory) path);
};
enum CreateIfExistsMode { "replace", "fail" };
dictionary CreateFileOptions {
CreateIfExistsMode ifExists = "fail";
(DOMString or Blob or ArrayBuffer or ArrayBufferView) data;
};