зеркало из https://github.com/mozilla/gecko-dev.git
Bug 934368 - Implement |remove| and |removeDeep| for Directory. r=dhylands
This commit is contained in:
Родитель
d9264c3059
Коммит
3727a70039
|
@ -28,3 +28,4 @@ support-files = devicestorage_common.js
|
|||
[test_fs_basic.html]
|
||||
[test_fs_createDirectory.html]
|
||||
[test_fs_get.html]
|
||||
[test_fs_remove.html]
|
||||
|
|
|
@ -88,6 +88,32 @@ function TestGet(iframe, data) {
|
|||
}, cbError);
|
||||
}
|
||||
|
||||
function TestRemove(iframe, data) {
|
||||
function cbError(e) {
|
||||
is(e.name, "SecurityError", "[TestRemove] Should fire a SecurityError for type " + data.type);
|
||||
is(data.shouldPass, false, "[TestRemove] Error callback was called for type " + data.type + '. Error: ' + e.name);
|
||||
testComplete(iframe, data);
|
||||
}
|
||||
|
||||
createTestFile(data.fileExtension);
|
||||
|
||||
let storage = iframe.contentDocument.defaultView.navigator.getDeviceStorage(data.type);
|
||||
isnot(storage, null, "[TestRemove] Should be able to get storage object for " + data.type);
|
||||
|
||||
if (!storage) {
|
||||
testComplete(iframe, data);
|
||||
return;
|
||||
}
|
||||
|
||||
storage.getRoot().then(function(root) {
|
||||
ok(true, "[TestRemove] Success callback of getRoot was called for type " + data.type);
|
||||
root.remove("testfile" + data.fileExtension).then(function() {
|
||||
is(data.shouldPass, true, "[TestRemove] Success callback was called for type " + data.type);
|
||||
testComplete(iframe, data);
|
||||
}, cbError);
|
||||
}, cbError);
|
||||
}
|
||||
|
||||
let gTestUri = "https://example.com/tests/dom/devicestorage/test/test_fs_app_permissions.html"
|
||||
|
||||
let gData = [
|
||||
|
@ -344,6 +370,164 @@ let gData = [
|
|||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestCreateDirectory
|
||||
},
|
||||
|
||||
// Directory#remove
|
||||
|
||||
// Web applications with no permissions
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: false,
|
||||
fileExtension: '.png',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogv',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.ogg',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
test: TestRemove
|
||||
},
|
||||
|
||||
// Web applications with permission granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
|
||||
// Certified application with permision granted
|
||||
{
|
||||
type: 'pictures',
|
||||
shouldPass: true,
|
||||
fileExtension: '.png',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:pictures"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogv',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'videos',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:videos"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: true,
|
||||
fileExtension: '.ogg',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'music',
|
||||
shouldPass: false,
|
||||
fileExtension: '.txt',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:music"],
|
||||
|
||||
test: TestRemove
|
||||
},
|
||||
{
|
||||
type: 'sdcard',
|
||||
shouldPass: true,
|
||||
fileExtension: '.txt',
|
||||
|
||||
app: "https://example.com/manifest_cert.webapp",
|
||||
permissions: ["device-storage:sdcard"],
|
||||
|
||||
test: TestRemove
|
||||
}
|
||||
|
||||
];
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
<!--
|
||||
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=934368
|
||||
-->
|
||||
<head>
|
||||
<title>Test Directory#remove and #removeDeep 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=934368">Mozilla Bug 934368</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 gStorage = null;
|
||||
let gTestCount = 0;
|
||||
let gFileMap = {};
|
||||
let gRemoveDeep = true;
|
||||
|
||||
let gTestCases = [
|
||||
// Remove a non-existent file should return false.
|
||||
{
|
||||
dir: "/",
|
||||
path: "non-existent.png",
|
||||
ret: false,
|
||||
shouldPass: true
|
||||
},
|
||||
|
||||
// Remove parent directory should fail.
|
||||
{
|
||||
dir: "sub1/sub2",
|
||||
target: "sub1",
|
||||
ret: true,
|
||||
shouldPass: false
|
||||
},
|
||||
|
||||
// Remove root directory should fail.
|
||||
{
|
||||
dir: "/",
|
||||
target: "/",
|
||||
ret: true,
|
||||
shouldPass: false
|
||||
},
|
||||
|
||||
// Remove non-descendant file should fail.
|
||||
{
|
||||
dir: "sub1",
|
||||
target: "sub/b.png",
|
||||
ret: true,
|
||||
shouldPass: false
|
||||
},
|
||||
|
||||
// Remove descendant file should return true.
|
||||
{
|
||||
dir: "sub1",
|
||||
target: "sub1/sub2/a.png",
|
||||
ret: true,
|
||||
shouldPass: true
|
||||
},
|
||||
|
||||
// Remove empty directory should return true.
|
||||
{
|
||||
dir: "sub1",
|
||||
path: "sub2",
|
||||
ret: true,
|
||||
shouldPass: true
|
||||
},
|
||||
|
||||
|
||||
// Remove non-empty directory should return true for "removeDeep" and fail
|
||||
// for "remove".
|
||||
{
|
||||
dir: "/",
|
||||
path: "sub",
|
||||
ret: true,
|
||||
get shouldPass() { return gRemoveDeep; }
|
||||
}
|
||||
];
|
||||
|
||||
function createTestFiles(storage, callback) {
|
||||
function createTestFile(path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
function addNamed() {
|
||||
var req = storage.addNamed(createRandomBlob("image/png"), path);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, path + " was created.");
|
||||
resolve();
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
ok(false, "Failed to create " + path + ': ' + e.target.error.name);
|
||||
reject();
|
||||
};
|
||||
}
|
||||
|
||||
// Bug 980136. Check if the file exists before we create.
|
||||
var req = storage.get(path);
|
||||
|
||||
req.onsuccess = function() {
|
||||
ok(true, path + " exists. Do not need to create.");
|
||||
resolve();
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
ok(true, path + " does not exists: " + e.target.error.name);
|
||||
addNamed();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let arr = [];
|
||||
|
||||
["sub1/sub2/a.png", "sub/b.png"].forEach(function(path) {
|
||||
arr.push(createTestFile(path));
|
||||
});
|
||||
|
||||
Promise.all(arr).then(function() {
|
||||
callback();
|
||||
}, function() {
|
||||
ok(false, "Failed to created test files.");
|
||||
devicestorage_cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
gTestCount = 0;
|
||||
createTestFiles(gStorage, function() {
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive at cbError! Error: " + e.name);
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
function cbSuccess(r) {
|
||||
ok(r, "Should get the file - " + this);
|
||||
gFileMap[this] = r;
|
||||
}
|
||||
|
||||
// Get directory and file objects.
|
||||
gStorage.getRoot().then(function(root) {
|
||||
ok(root, "Should get root directory.");
|
||||
gFileMap["/"] = root;
|
||||
|
||||
let arr = [];
|
||||
|
||||
["sub1", "sub1/sub2", "sub1/sub2/a.png", "sub/b.png"].forEach(function(path) {
|
||||
arr.push(root.get(path).then(cbSuccess.bind(path), cbError));
|
||||
});
|
||||
|
||||
Promise.all(arr).then(function() {
|
||||
testNextRemove();
|
||||
}, function() {
|
||||
ok(false, "Failed to get test files.");
|
||||
devicestorage_cleanup();
|
||||
});
|
||||
}, cbError);
|
||||
});
|
||||
}
|
||||
|
||||
function testNextRemove() {
|
||||
if (gTestCount < gTestCases.length) {
|
||||
let data = gTestCases[gTestCount++];
|
||||
let dir = gFileMap[data.dir];
|
||||
let path = data.path || gFileMap[data.target];
|
||||
let targetPath = data.path || data.target;
|
||||
let promise = gRemoveDeep ? dir.removeDeep(path) : dir.remove(path);
|
||||
promise.then(function(result) {
|
||||
ok(data.shouldPass, "Success callback was called to remove " +
|
||||
targetPath + " from " + data.dir);
|
||||
is(result, data.ret, "Return value should match to remove " +
|
||||
targetPath + " from " + data.dir);
|
||||
SimpleTest.executeSoon(testNextRemove);
|
||||
}, function(err) {
|
||||
ok(!data.shouldPass, "Error callback was called to remove " +
|
||||
targetPath + " from " + data.dir + '. Error: ' + err.name);
|
||||
SimpleTest.executeSoon(testNextRemove);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (gRemoveDeep) {
|
||||
// Test "remove" after "removeDeep".
|
||||
gRemoveDeep = false;
|
||||
runTest();
|
||||
return;
|
||||
}
|
||||
|
||||
devicestorage_cleanup();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
|
||||
let gStorage = navigator.getDeviceStorage("pictures");
|
||||
ok(gStorage, "Should have gotten a storage.");
|
||||
|
||||
// Test "removeDeep" first.
|
||||
gRemoveDeep = true;
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsDeviceStorage.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
|
@ -112,6 +113,23 @@ DeviceStorageFileSystem::GetLocalFile(const nsAString& aRealPath) const
|
|||
return file.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageFileSystem::GetRealPath(nsIDOMFile* aFile, nsAString& aRealPath) const
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Should be on parent process!");
|
||||
MOZ_ASSERT(aFile, "aFile Should not be null.");
|
||||
|
||||
aRealPath.Truncate();
|
||||
|
||||
nsAutoString filePath;
|
||||
if (NS_FAILED(aFile->GetMozFullPathInternal(filePath))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return LocalPathToRealPath(filePath, aRealPath);
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
DeviceStorageFileSystem::GetRootName() const
|
||||
{
|
||||
|
@ -130,8 +148,7 @@ DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
|
|||
if (NS_FAILED(aFile->GetPath(path))) {
|
||||
return false;
|
||||
}
|
||||
FileSystemUtils::LocalPathToNormalizedPath(path, path);
|
||||
if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
|
||||
if (!LocalPathToRealPath(path, path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -142,5 +159,30 @@ DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
|
|||
return typeChecker->Check(mStorageType, aFile);
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aDir);
|
||||
nsRefPtr<FileSystemBase> fs = aDir->GetFileSystem();
|
||||
MOZ_ASSERT(fs);
|
||||
// Check if the given directory is from this storage.
|
||||
return fs->ToString() == mString;
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageFileSystem::LocalPathToRealPath(const nsAString& aLocalPath,
|
||||
nsAString& aRealPath) const
|
||||
{
|
||||
nsAutoString path;
|
||||
FileSystemUtils::LocalPathToNormalizedPath(aLocalPath, path);
|
||||
if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
|
||||
aRealPath.Truncate();
|
||||
return false;
|
||||
}
|
||||
aRealPath = Substring(path, mNormalizedLocalRootPath.Length());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -36,16 +36,24 @@ public:
|
|||
virtual already_AddRefed<nsIFile>
|
||||
GetLocalFile(const nsAString& aRealPath) const MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
GetRealPath(nsIDOMFile* aFile, nsAString& aRealPath) const MOZ_OVERRIDE;
|
||||
|
||||
virtual const nsAString&
|
||||
GetRootName() const MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
IsSafeFile(nsIFile* aFile) const MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
IsSafeDirectory(Directory* aDir) const MOZ_OVERRIDE;
|
||||
private:
|
||||
virtual
|
||||
~DeviceStorageFileSystem();
|
||||
|
||||
bool
|
||||
LocalPathToRealPath(const nsAString& aLocalPath, nsAString& aRealPath) const;
|
||||
|
||||
nsString mStorageType;
|
||||
nsString mStorageName;
|
||||
|
||||
|
|
|
@ -9,12 +9,14 @@
|
|||
#include "CreateDirectoryTask.h"
|
||||
#include "FileSystemPermissionRequest.h"
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
#include "RemoveTask.h"
|
||||
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/DirectoryBinding.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
// Resolve the name collision of Microsoft's API name with macros defined in
|
||||
// Windows header files. Undefine the macro of CreateDirectory to avoid
|
||||
|
@ -116,6 +118,65 @@ Directory::Get(const nsAString& aPath)
|
|||
return task->GetPromise();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::Remove(const StringOrFileOrDirectory& aPath)
|
||||
{
|
||||
return RemoveInternal(aPath, false);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::RemoveDeep(const StringOrFileOrDirectory& aPath)
|
||||
{
|
||||
return RemoveInternal(aPath, true);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive)
|
||||
{
|
||||
nsresult error = NS_OK;
|
||||
nsString realPath;
|
||||
nsCOMPtr<nsIDOMFile> file;
|
||||
|
||||
// Check and get the target path.
|
||||
|
||||
if (aPath.IsFile()) {
|
||||
file = aPath.GetAsFile();
|
||||
goto parameters_check_done;
|
||||
}
|
||||
|
||||
if (aPath.IsString()) {
|
||||
if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
goto parameters_check_done;
|
||||
}
|
||||
|
||||
if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) {
|
||||
error = NS_ERROR_DOM_SECURITY_ERR;
|
||||
goto parameters_check_done;
|
||||
}
|
||||
|
||||
realPath = aPath.GetAsDirectory().mPath;
|
||||
// The target must be a descendant of this directory.
|
||||
if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
parameters_check_done:
|
||||
|
||||
nsRefPtr<RemoveTask> task = new RemoveTask(mFileSystem, mPath, file, realPath,
|
||||
aRecursive);
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
}
|
||||
|
||||
FileSystemBase*
|
||||
Directory::GetFileSystem() const
|
||||
{
|
||||
return mFileSystem.get();
|
||||
}
|
||||
|
||||
bool
|
||||
Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
|
@ -26,6 +27,7 @@ namespace dom {
|
|||
|
||||
class FileSystemBase;
|
||||
class Promise;
|
||||
class StringOrFileOrDirectory;
|
||||
|
||||
class Directory MOZ_FINAL
|
||||
: public nsISupports
|
||||
|
@ -59,7 +61,16 @@ public:
|
|||
already_AddRefed<Promise>
|
||||
Get(const nsAString& aPath);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Remove(const StringOrFileOrDirectory& aPath);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
RemoveDeep(const StringOrFileOrDirectory& aPath);
|
||||
|
||||
// =========== End WebIDL bindings.============
|
||||
|
||||
FileSystemBase*
|
||||
GetFileSystem() const;
|
||||
private:
|
||||
static bool
|
||||
IsValidRelativePath(const nsString& aPath);
|
||||
|
@ -71,6 +82,9 @@ private:
|
|||
bool
|
||||
DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive);
|
||||
|
||||
nsRefPtr<FileSystemBase> mFileSystem;
|
||||
nsString mPath;
|
||||
};
|
||||
|
|
|
@ -68,5 +68,11 @@ FileSystemBase::IsSafeFile(nsIFile* aFile) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
FileSystemBase::IsSafeDirectory(Directory* aDir) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -10,11 +10,14 @@
|
|||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIDOMFile;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Directory;
|
||||
|
||||
class FileSystemBase
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemBase)
|
||||
|
@ -61,6 +64,17 @@ public:
|
|||
virtual bool
|
||||
IsSafeFile(nsIFile* aFile) const;
|
||||
|
||||
virtual bool
|
||||
IsSafeDirectory(Directory* aDir) const;
|
||||
|
||||
/*
|
||||
* Get the real path (absolute DOM path) of the DOM file in the file system.
|
||||
* If succeeded, returns true. Otherwise, returns false and set aRealPath to
|
||||
* empty string.
|
||||
*/
|
||||
virtual bool
|
||||
GetRealPath(nsIDOMFile* aFile, nsAString& aRealPath) const = 0;
|
||||
|
||||
/*
|
||||
* Get the permission name required to access this file system.
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "CreateDirectoryTask.h"
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
#include "RemoveTask.h"
|
||||
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
|
@ -40,6 +41,7 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
|
|||
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)
|
||||
|
||||
default: {
|
||||
NS_RUNTIMEABORT("not reached");
|
||||
|
|
|
@ -25,8 +25,14 @@ struct FileSystemErrorResponse
|
|||
nsresult error;
|
||||
};
|
||||
|
||||
struct FileSystemBooleanResponse
|
||||
{
|
||||
bool success;
|
||||
};
|
||||
|
||||
union FileSystemResponseValue
|
||||
{
|
||||
FileSystemBooleanResponse;
|
||||
FileSystemDirectoryResponse;
|
||||
FileSystemFileResponse;
|
||||
FileSystemErrorResponse;
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
/* -*- 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 "RemoveTask.h"
|
||||
|
||||
#include "DOMError.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aDirPath,
|
||||
nsIDOMFile* aTargetFile,
|
||||
const nsAString& aTargetPath,
|
||||
bool aRecursive)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mDirRealPath(aDirPath)
|
||||
, mTargetFile(aTargetFile)
|
||||
, mTargetRealPath(aTargetPath)
|
||||
, mRecursive(aRecursive)
|
||||
, mReturnValue(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
}
|
||||
mPromise = new Promise(globalObject);
|
||||
}
|
||||
|
||||
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemRemoveParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: FileSystemTaskBase(aFileSystem, aParam, aParent)
|
||||
, mRecursive(false)
|
||||
, mReturnValue(false)
|
||||
{
|
||||
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
mDirRealPath = aParam.directory();
|
||||
|
||||
mRecursive = aParam.recursive();
|
||||
|
||||
const FileSystemPathOrFileValue& target = aParam.target();
|
||||
|
||||
if (target.type() == FileSystemPathOrFileValue::TnsString) {
|
||||
mTargetRealPath = target;
|
||||
return;
|
||||
}
|
||||
|
||||
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(target));
|
||||
nsCOMPtr<nsIDOMBlob> blob = bp->GetBlob();
|
||||
mTargetFile = do_QueryInterface(blob);
|
||||
MOZ_ASSERT(mTargetFile, "mTargetFile should not be null.");
|
||||
}
|
||||
|
||||
RemoveTask::~RemoveTask()
|
||||
{
|
||||
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
|
||||
"mPromise should be released on main thread!");
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
RemoveTask::GetPromise()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return nsRefPtr<Promise>(mPromise).forget();
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
RemoveTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemRemoveParams param;
|
||||
param.filesystem() = aFileSystem;
|
||||
param.directory() = mDirRealPath;
|
||||
param.recursive() = mRecursive;
|
||||
if (mTargetFile) {
|
||||
BlobChild* actor
|
||||
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(mTargetFile);
|
||||
if (actor) {
|
||||
param.target() = actor;
|
||||
}
|
||||
} else {
|
||||
param.target() = mTargetRealPath;
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
RemoveTask::GetSuccessRequestResult() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemBooleanResponse(mReturnValue);
|
||||
}
|
||||
|
||||
void
|
||||
RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemBooleanResponse r = aValue;
|
||||
mReturnValue = r.success();
|
||||
}
|
||||
|
||||
nsresult
|
||||
RemoveTask::Work()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Get the DOM path if a DOMFile is passed as the target.
|
||||
if (mTargetFile) {
|
||||
if (!mFileSystem->GetRealPath(mTargetFile, mTargetRealPath)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
if (!FileSystemUtils::IsDescendantPath(mDirRealPath, mTargetRealPath)) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
|
||||
if (!file) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
|
||||
bool exists = false;
|
||||
nsresult rv = file->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
mReturnValue = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool isFile = false;
|
||||
rv = file->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (isFile && !mFileSystem->IsSafeFile(file)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
rv = file->Remove(mRecursive);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mReturnValue = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
RemoveTask::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(mReturnValue);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
RemoveTask::GetPermissionAccessType(nsCString& aAccess) const
|
||||
{
|
||||
aAccess.AssignLiteral("write");
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- 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_RemoveTask_h
|
||||
#define mozilla_dom_RemoveTask_h
|
||||
|
||||
#include "mozilla/dom/FileSystemTaskBase.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
class RemoveTask MOZ_FINAL
|
||||
: public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
RemoveTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aDirPath,
|
||||
nsIDOMFile* aTargetFile,
|
||||
const nsAString& aTargetPath,
|
||||
bool aRecursive);
|
||||
RemoveTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemRemoveParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
|
||||
virtual
|
||||
~RemoveTask();
|
||||
|
||||
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:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
nsString mDirRealPath;
|
||||
nsCOMPtr<nsIDOMFile> mTargetFile;
|
||||
nsString mTargetRealPath;
|
||||
bool mRecursive;
|
||||
bool mReturnValue;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_RemoveTask_h
|
|
@ -23,6 +23,7 @@ SOURCES += [
|
|||
'FileSystemTaskBase.cpp',
|
||||
'FileSystemUtils.cpp',
|
||||
'GetFileOrDirectoryTask.cpp',
|
||||
'RemoveTask.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'gklayout'
|
||||
|
|
|
@ -206,10 +206,25 @@ struct FileSystemGetFileOrDirectoryParams
|
|||
nsString realPath;
|
||||
};
|
||||
|
||||
union FileSystemPathOrFileValue
|
||||
{
|
||||
nsString;
|
||||
PBlob;
|
||||
};
|
||||
|
||||
struct FileSystemRemoveParams
|
||||
{
|
||||
nsString filesystem;
|
||||
nsString directory;
|
||||
FileSystemPathOrFileValue target;
|
||||
bool recursive;
|
||||
};
|
||||
|
||||
union FileSystemParams
|
||||
{
|
||||
FileSystemCreateDirectoryParams;
|
||||
FileSystemGetFileOrDirectoryParams;
|
||||
FileSystemRemoveParams;
|
||||
};
|
||||
|
||||
union PrefValue {
|
||||
|
|
|
@ -45,5 +45,35 @@ interface Directory {
|
|||
[NewObject]
|
||||
// Promise<(File or Directory)>
|
||||
Promise get(DOMString path);
|
||||
|
||||
/*
|
||||
* Deletes a file or an empty directory. The target must be a descendent of
|
||||
* current directory.
|
||||
* @param path If a DOM string is passed, it is the relative path of the
|
||||
* target. Otherwise, the File or Directory object of the target should be
|
||||
* passed.
|
||||
* @return If the target is a non-empty directory, or if deleting the target
|
||||
* fails, the promise is rejected with a DOM error. If the target did not
|
||||
* exist, the promise is resolved with boolean false. If the target did exist
|
||||
* and was successfully deleted, the promise is resolved with boolean true.
|
||||
*/
|
||||
[NewObject]
|
||||
// Promise<boolean>
|
||||
Promise remove((DOMString or File or Directory) path);
|
||||
|
||||
/*
|
||||
* Deletes a file or a directory recursively. The target should be a
|
||||
* descendent of current directory.
|
||||
* @param path If a DOM string is passed, it is the relative path of the
|
||||
* target. Otherwise, the File or Directory object of the target should be
|
||||
* passed.
|
||||
* @return If the target exists, but deleting the target fails, the promise is
|
||||
* rejected with a DOM error. If the target did not exist, the promise is
|
||||
* resolved with boolean false. If the target did exist and was successfully
|
||||
* deleted, the promise is resolved with boolean true.
|
||||
*/
|
||||
[NewObject]
|
||||
// Promise<boolean>
|
||||
Promise removeDeep((DOMString or File or Directory) path);
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче