зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1789116: Implement move() for OriginPrivateFileSystems r=dom-storage-reviewers,jari,emilio
Depends on D155352 Differential Revision: https://phabricator.services.mozilla.com/D156371
This commit is contained in:
Родитель
ad2a0a8fc0
Коммит
5eedaaf373
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/FileSystemHandleBinding.h"
|
||||
#include "mozilla/dom/FileSystemManager.h"
|
||||
#include "mozilla/dom/Promise-inl.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/StorageManager.h"
|
||||
#include "xpcpublic.h"
|
||||
|
@ -112,6 +113,71 @@ already_AddRefed<Promise> FileSystemHandle::IsSameEntry(
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> FileSystemHandle::Move(const nsAString& aName,
|
||||
ErrorResult& aError) {
|
||||
LOG(("Move %s to %s", NS_ConvertUTF16toUTF8(mMetadata.entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(aName).get()));
|
||||
|
||||
fs::EntryId parent; // empty means same directory
|
||||
return Move(parent, aName, aError);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> FileSystemHandle::Move(
|
||||
FileSystemDirectoryHandle& aParent, ErrorResult& aError) {
|
||||
LOG(("Move %s to %s/%s", NS_ConvertUTF16toUTF8(mMetadata.entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(aParent.mMetadata.entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(mMetadata.entryName()).get()));
|
||||
return Move(aParent, mMetadata.entryName(), aError);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> FileSystemHandle::Move(
|
||||
FileSystemDirectoryHandle& aParent, const nsAString& aName,
|
||||
ErrorResult& aError) {
|
||||
LOG(("Move %s to %s/%s", NS_ConvertUTF16toUTF8(mMetadata.entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(aParent.mMetadata.entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(aName).get()));
|
||||
return Move(aParent.mMetadata.entryId(), aName, aError);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> FileSystemHandle::Move(const fs::EntryId& aParentId,
|
||||
const nsAString& aName,
|
||||
ErrorResult& aError) {
|
||||
RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fs::Name name(aName);
|
||||
if (!aParentId.IsEmpty()) {
|
||||
fs::FileSystemChildMetadata newMetadata;
|
||||
newMetadata.parentId() = aParentId;
|
||||
newMetadata.childName() = aName;
|
||||
mRequestHandler->MoveEntry(mManager, this, mMetadata, newMetadata, promise);
|
||||
} else {
|
||||
mRequestHandler->RenameEntry(mManager, this, mMetadata, name, promise);
|
||||
}
|
||||
|
||||
// Other handles to this will be broken, and the spec is ok with this, but we
|
||||
// need to update our EntryId and name
|
||||
promise->AddCallbacksWithCycleCollectedArgs(
|
||||
[name](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
|
||||
FileSystemHandle* aHandle) {
|
||||
// XXX Fix entryId!
|
||||
LOG(("Changing FileSystemHandle name from %s to %s",
|
||||
NS_ConvertUTF16toUTF8(aHandle->mMetadata.entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(name).get()));
|
||||
aHandle->mMetadata.entryName() = name;
|
||||
},
|
||||
[](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
|
||||
FileSystemHandle* aHandle) {
|
||||
LOG(("reject of move for %s",
|
||||
NS_ConvertUTF16toUTF8(aHandle->mMetadata.entryName()).get()));
|
||||
},
|
||||
RefPtr(this));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// [Serializable] implementation
|
||||
|
||||
// static
|
||||
|
|
|
@ -73,6 +73,21 @@ class FileSystemHandle : public nsISupports, public nsWrapperCache {
|
|||
virtual bool WriteStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter) const;
|
||||
|
||||
already_AddRefed<Promise> Move(const nsAString& aName, ErrorResult& aError);
|
||||
|
||||
already_AddRefed<Promise> Move(FileSystemDirectoryHandle& aParent,
|
||||
ErrorResult& aError);
|
||||
|
||||
already_AddRefed<Promise> Move(FileSystemDirectoryHandle& aParent,
|
||||
const nsAString& aName, ErrorResult& aError);
|
||||
|
||||
already_AddRefed<Promise> Move(const fs::EntryId& aParentId,
|
||||
const nsAString& aName, ErrorResult& aError);
|
||||
|
||||
void UpdateMetadata(const fs::FileSystemEntryMetadata& aMetadata) {
|
||||
mMetadata = aMetadata;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~FileSystemHandle() = default;
|
||||
|
||||
|
@ -88,7 +103,8 @@ class FileSystemHandle : public nsISupports, public nsWrapperCache {
|
|||
|
||||
RefPtr<FileSystemManager> mManager;
|
||||
|
||||
const fs::FileSystemEntryMetadata mMetadata;
|
||||
// move() can change names/directories
|
||||
fs::FileSystemEntryMetadata mMetadata;
|
||||
|
||||
const UniquePtr<fs::FileSystemRequestHandler> mRequestHandler;
|
||||
};
|
||||
|
|
|
@ -134,14 +134,59 @@ void ResolveCallback(
|
|||
|
||||
MOZ_ASSERT(FileSystemRemoveEntryResponse::Tnsresult == aResponse.type());
|
||||
const auto& status = aResponse.get_nsresult();
|
||||
if (NS_ERROR_FILE_ACCESS_DENIED == status) {
|
||||
aPromise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||
} else if (NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR == status) {
|
||||
aPromise->MaybeRejectWithInvalidModificationError("Disallowed by system");
|
||||
} else if (NS_FAILED(status)) {
|
||||
aPromise->MaybeRejectWithUnknownError("Unknown failure");
|
||||
} else {
|
||||
aPromise->MaybeResolveWithUndefined();
|
||||
switch (status) {
|
||||
case NS_ERROR_FILE_ACCESS_DENIED:
|
||||
aPromise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||
break;
|
||||
case NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR:
|
||||
aPromise->MaybeRejectWithInvalidModificationError("Disallowed by system");
|
||||
break;
|
||||
default:
|
||||
if (NS_FAILED(status)) {
|
||||
aPromise->MaybeRejectWithUnknownError("Unknown failure");
|
||||
} else {
|
||||
aPromise->MaybeResolveWithUndefined();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResolveCallback(
|
||||
FileSystemMoveEntryResponse&& aResponse,
|
||||
RefPtr<Promise> aPromise) { // NOLINT(performance-unnecessary-value-param)
|
||||
MOZ_ASSERT(aPromise);
|
||||
QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID);
|
||||
|
||||
MOZ_ASSERT(FileSystemMoveEntryResponse::Tnsresult == aResponse.type());
|
||||
const auto& status = aResponse.get_nsresult();
|
||||
switch (status) {
|
||||
case NS_OK:
|
||||
aPromise->MaybeResolveWithUndefined();
|
||||
break;
|
||||
case NS_ERROR_FILE_ACCESS_DENIED:
|
||||
aPromise->MaybeRejectWithNotAllowedError("Permission denied");
|
||||
break;
|
||||
case NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR:
|
||||
aPromise->MaybeRejectWithInvalidModificationError("Disallowed by system");
|
||||
break;
|
||||
case NS_ERROR_DOM_NOT_FOUND_ERR:
|
||||
aPromise->MaybeRejectWithNotFoundError("Entry not found");
|
||||
break;
|
||||
case NS_ERROR_DOM_INVALID_MODIFICATION_ERR:
|
||||
aPromise->MaybeRejectWithInvalidModificationError("Invalid modification");
|
||||
break;
|
||||
case NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR:
|
||||
aPromise->MaybeRejectWithNoModificationAllowedError(
|
||||
"No modification allowed");
|
||||
break;
|
||||
default:
|
||||
if (NS_FAILED(status)) {
|
||||
aPromise->MaybeRejectWithUnknownError("Unknown failure");
|
||||
} else {
|
||||
aPromise->MaybeResolveWithUndefined();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,6 +428,63 @@ void FileSystemRequestHandler::RemoveEntry(
|
|||
std::move(onReject));
|
||||
}
|
||||
|
||||
void FileSystemRequestHandler::MoveEntry(
|
||||
RefPtr<FileSystemManager>& aManager, FileSystemHandle* aHandle,
|
||||
const FileSystemEntryMetadata& aEntry,
|
||||
const FileSystemChildMetadata& aNewEntry,
|
||||
RefPtr<Promise> aPromise) { // NOLINT(performance-unnecessary-value-param)
|
||||
MOZ_ASSERT(aPromise);
|
||||
LOG(("MoveEntry"));
|
||||
|
||||
// reject invalid names: empty, path separators, current & parent directories
|
||||
if (!IsValidName(aNewEntry.childName())) {
|
||||
aPromise->MaybeRejectWithTypeError("Invalid name");
|
||||
return;
|
||||
}
|
||||
|
||||
FileSystemMoveEntryRequest request(aEntry, aNewEntry);
|
||||
|
||||
RefPtr<FileSystemHandle> handle(aHandle);
|
||||
auto&& onResolve =
|
||||
SelectResolveCallback<FileSystemMoveEntryResponse, void>(aPromise);
|
||||
|
||||
auto&& onReject = GetRejectCallback(aPromise);
|
||||
|
||||
QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) {
|
||||
aPromise->MaybeRejectWithUnknownError("Invalid actor");
|
||||
});
|
||||
aManager->Actor()->SendMoveEntry(request, std::move(onResolve),
|
||||
std::move(onReject));
|
||||
}
|
||||
|
||||
void FileSystemRequestHandler::RenameEntry(
|
||||
RefPtr<FileSystemManager>& aManager, FileSystemHandle* aHandle,
|
||||
const FileSystemEntryMetadata& aEntry, const Name& aName,
|
||||
RefPtr<Promise> aPromise) { // NOLINT(performance-unnecessary-value-param)
|
||||
MOZ_ASSERT(!aEntry.entryId().IsEmpty());
|
||||
MOZ_ASSERT(aPromise);
|
||||
LOG(("RenameEntry"));
|
||||
|
||||
// reject invalid names: empty, path separators, current & parent directories
|
||||
if (!IsValidName(aName)) {
|
||||
aPromise->MaybeRejectWithTypeError("Invalid name");
|
||||
return;
|
||||
}
|
||||
|
||||
FileSystemRenameEntryRequest request(aEntry, aName);
|
||||
|
||||
auto&& onResolve =
|
||||
SelectResolveCallback<FileSystemMoveEntryResponse, void>(aPromise);
|
||||
|
||||
auto&& onReject = GetRejectCallback(aPromise);
|
||||
|
||||
QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) {
|
||||
aPromise->MaybeRejectWithUnknownError("Invalid actor");
|
||||
});
|
||||
aManager->Actor()->SendRenameEntry(request, std::move(onResolve),
|
||||
std::move(onReject));
|
||||
}
|
||||
|
||||
void FileSystemRequestHandler::Resolve(
|
||||
RefPtr<FileSystemManager>& aManager,
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
|
|
|
@ -51,6 +51,17 @@ class FileSystemRequestHandler {
|
|||
const FileSystemChildMetadata& aEntry,
|
||||
bool aRecursive, RefPtr<Promise> aPromise);
|
||||
|
||||
virtual void MoveEntry(RefPtr<FileSystemManager>& aManager,
|
||||
FileSystemHandle* aHandle,
|
||||
const FileSystemEntryMetadata& aEntry,
|
||||
const FileSystemChildMetadata& aNewEntry,
|
||||
RefPtr<Promise> aPromise);
|
||||
|
||||
virtual void RenameEntry(RefPtr<FileSystemManager>& aManager,
|
||||
FileSystemHandle* aHandle,
|
||||
const FileSystemEntryMetadata& aEntry,
|
||||
const Name& aName, RefPtr<Promise> aPromise);
|
||||
|
||||
virtual void Resolve(RefPtr<FileSystemManager>& aManager,
|
||||
const FileSystemEntryPair& aEndpoints,
|
||||
RefPtr<Promise> aPromise);
|
||||
|
|
|
@ -214,6 +214,75 @@ IPCResult FileSystemManagerParent::RecvRemoveEntry(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
IPCResult FileSystemManagerParent::RecvMoveEntry(
|
||||
FileSystemMoveEntryRequest&& aRequest, MoveEntryResolver&& aResolver) {
|
||||
LOG(("MoveEntry %s to %s",
|
||||
NS_ConvertUTF16toUTF8(aRequest.handle().entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(aRequest.destHandle().childName()).get()));
|
||||
MOZ_ASSERT(!aRequest.handle().entryId().IsEmpty());
|
||||
MOZ_ASSERT(!aRequest.destHandle().parentId().IsEmpty());
|
||||
MOZ_ASSERT(mDataManager);
|
||||
|
||||
auto reportError = [&aResolver](const QMResult& aRv) {
|
||||
FileSystemMoveEntryResponse response(ToNSResult(aRv));
|
||||
aResolver(response);
|
||||
};
|
||||
|
||||
QM_TRY_UNWRAP(EntryId parentId,
|
||||
mDataManager->MutableDatabaseManagerPtr()->GetParentEntryId(
|
||||
aRequest.handle().entryId()),
|
||||
IPC_OK(), reportError);
|
||||
FileSystemChildMetadata sourceHandle;
|
||||
sourceHandle.parentId() = parentId;
|
||||
sourceHandle.childName() = aRequest.handle().entryName();
|
||||
|
||||
QM_TRY_UNWRAP(bool moved,
|
||||
mDataManager->MutableDatabaseManagerPtr()->MoveEntry(
|
||||
sourceHandle, aRequest.destHandle()),
|
||||
IPC_OK(), reportError);
|
||||
|
||||
fs::FileSystemMoveEntryResponse response(moved ? NS_OK : NS_ERROR_FAILURE);
|
||||
aResolver(response);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
IPCResult FileSystemManagerParent::RecvRenameEntry(
|
||||
FileSystemRenameEntryRequest&& aRequest, MoveEntryResolver&& aResolver) {
|
||||
// if destHandle's parentId is empty, then we're renaming in the same
|
||||
// directory
|
||||
LOG(("RenameEntry %s to %s",
|
||||
NS_ConvertUTF16toUTF8(aRequest.handle().entryName()).get(),
|
||||
NS_ConvertUTF16toUTF8(aRequest.name()).get()));
|
||||
MOZ_ASSERT(!aRequest.handle().entryId().IsEmpty());
|
||||
MOZ_ASSERT(mDataManager);
|
||||
|
||||
auto reportError = [&aResolver](const QMResult& aRv) {
|
||||
FileSystemMoveEntryResponse response(ToNSResult(aRv));
|
||||
aResolver(response);
|
||||
};
|
||||
|
||||
QM_TRY_UNWRAP(EntryId parentId,
|
||||
mDataManager->MutableDatabaseManagerPtr()->GetParentEntryId(
|
||||
aRequest.handle().entryId()),
|
||||
IPC_OK(), reportError);
|
||||
FileSystemChildMetadata sourceHandle;
|
||||
sourceHandle.parentId() = parentId;
|
||||
sourceHandle.childName() = aRequest.handle().entryName();
|
||||
|
||||
FileSystemChildMetadata newHandle;
|
||||
newHandle.parentId() = parentId;
|
||||
newHandle.childName() = aRequest.name();
|
||||
|
||||
QM_TRY_UNWRAP(bool moved,
|
||||
mDataManager->MutableDatabaseManagerPtr()->MoveEntry(
|
||||
sourceHandle, newHandle),
|
||||
IPC_OK(), reportError);
|
||||
|
||||
fs::FileSystemMoveEntryResponse response(moved ? NS_OK : NS_ERROR_FAILURE);
|
||||
aResolver(response);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
IPCResult FileSystemManagerParent::RecvCloseFile(
|
||||
FileSystemGetFileRequest&& aRequest) {
|
||||
AssertIsOnIOTarget();
|
||||
|
|
|
@ -57,6 +57,12 @@ class FileSystemManagerParent : public PFileSystemManagerParent {
|
|||
mozilla::ipc::IPCResult RecvRemoveEntry(
|
||||
FileSystemRemoveEntryRequest&& aRequest, RemoveEntryResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvMoveEntry(FileSystemMoveEntryRequest&& aRequest,
|
||||
MoveEntryResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRenameEntry(
|
||||
FileSystemRenameEntryRequest&& aRequest, MoveEntryResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvCloseFile(FileSystemGetFileRequest&& aRequest);
|
||||
|
||||
mozilla::ipc::IPCResult RecvGetAccessHandle(
|
||||
|
|
|
@ -25,6 +25,7 @@ class Result;
|
|||
namespace dom::fs {
|
||||
|
||||
class FileSystemChildMetadata;
|
||||
class FileSystemEntryMetadata;
|
||||
class FileSystemDirectoryListing;
|
||||
class FileSystemEntryPair;
|
||||
|
||||
|
@ -41,6 +42,16 @@ class FileSystemDatabaseManager {
|
|||
*/
|
||||
virtual Result<int64_t, QMResult> GetUsage() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns directory identifier for the parent of
|
||||
* a given entry, or error.
|
||||
*
|
||||
* @param aEntry EntryId of an existing file or directory
|
||||
* @return Result<EntryId, QMResult> Directory identifier or error
|
||||
*/
|
||||
virtual Result<EntryId, QMResult> GetParentEntryId(
|
||||
const EntryId& aEntry) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns directory identifier, optionally creating it if it doesn't
|
||||
* exist
|
||||
|
@ -90,6 +101,18 @@ class FileSystemDatabaseManager {
|
|||
virtual Result<bool, QMResult> RemoveFile(
|
||||
const FileSystemChildMetadata& aHandle) = 0;
|
||||
|
||||
/**
|
||||
* @brief Move/Rename a file/directory
|
||||
*
|
||||
* @param aHandle Source directory or file
|
||||
* @param aNewDesignation Destination directory and filename
|
||||
* @return Result<bool, QMResult> False if file didn't exist, otherwise true
|
||||
* or error
|
||||
*/
|
||||
virtual Result<bool, QMResult> MoveEntry(
|
||||
const FileSystemChildMetadata& aHandle,
|
||||
const FileSystemChildMetadata& aNewDesignation) = 0;
|
||||
|
||||
/**
|
||||
* @brief Tries to connect a parent directory to a file system item with a
|
||||
* path, excluding the parent directory
|
||||
|
|
|
@ -26,10 +26,11 @@ namespace {
|
|||
|
||||
Result<bool, QMResult> ApplyEntryExistsQuery(
|
||||
const FileSystemConnection& aConnection, const nsACString& aQuery,
|
||||
const EntryId& aEntryId) {
|
||||
const FileSystemChildMetadata& aHandle) {
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, aQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, aEntryId)));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("parent"_ns, aHandle.parentId())));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindNameByName("name"_ns, aHandle.childName())));
|
||||
|
||||
return stmt.YesOrNoQuery();
|
||||
}
|
||||
|
@ -82,17 +83,18 @@ nsresult AggregateUsages(FileSystemConnection& mConnection) {
|
|||
}
|
||||
|
||||
Result<bool, QMResult> DoesDirectoryExist(
|
||||
const FileSystemConnection& mConnection, const EntryId& aEntryId) {
|
||||
MOZ_ASSERT(!aEntryId.IsEmpty());
|
||||
const FileSystemConnection& mConnection,
|
||||
const FileSystemChildMetadata& aHandle) {
|
||||
MOZ_ASSERT(!aHandle.parentId().IsEmpty());
|
||||
|
||||
const nsCString existsQuery =
|
||||
"SELECT EXISTS "
|
||||
"(SELECT 1 FROM Directories "
|
||||
"WHERE handle = :handle )"
|
||||
"(SELECT 1 FROM Directories JOIN Entries USING (handle) "
|
||||
"WHERE Directories.name = :name AND Entries.parent = :parent )"
|
||||
";"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(bool exists,
|
||||
ApplyEntryExistsQuery(mConnection, existsQuery, aEntryId));
|
||||
ApplyEntryExistsQuery(mConnection, existsQuery, aHandle));
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
@ -136,17 +138,17 @@ Result<Path, QMResult> ResolveReversedPath(
|
|||
}
|
||||
|
||||
Result<bool, QMResult> DoesFileExist(const FileSystemConnection& mConnection,
|
||||
const EntryId& aEntryId) {
|
||||
MOZ_ASSERT(!aEntryId.IsEmpty());
|
||||
const FileSystemChildMetadata& aHandle) {
|
||||
MOZ_ASSERT(!aHandle.parentId().IsEmpty());
|
||||
|
||||
const nsCString existsQuery =
|
||||
"SELECT EXISTS "
|
||||
"(SELECT 1 FROM Files "
|
||||
"WHERE handle = :handle ) "
|
||||
"(SELECT 1 FROM Files JOIN Entries USING (handle) "
|
||||
"WHERE Files.name = :name AND Entries.parent = :parent )"
|
||||
";"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(bool exists,
|
||||
ApplyEntryExistsQuery(mConnection, existsQuery, aEntryId));
|
||||
ApplyEntryExistsQuery(mConnection, existsQuery, aHandle));
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
@ -210,6 +212,66 @@ nsresult GetEntries(const FileSystemConnection& aConnection,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
Result<EntryId, QMResult> GetUniqueEntryId(
|
||||
const FileSystemConnection& aConnection,
|
||||
const FileSystemChildMetadata& aHandle) {
|
||||
const nsCString existsQuery =
|
||||
"SELECT EXISTS "
|
||||
"(SELECT 1 FROM Entries "
|
||||
"WHERE handle = :handle )"
|
||||
";"_ns;
|
||||
|
||||
FileSystemChildMetadata generatorInput = aHandle;
|
||||
|
||||
const size_t maxRounds = 1024u;
|
||||
|
||||
for (size_t hangGuard = 0u; hangGuard < maxRounds; ++hangGuard) {
|
||||
QM_TRY_UNWRAP(EntryId entryId, fs::data::GetEntryHandle(generatorInput));
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection, existsQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, entryId)));
|
||||
|
||||
QM_TRY_UNWRAP(bool alreadyInUse, stmt.YesOrNoQuery());
|
||||
|
||||
if (!alreadyInUse) {
|
||||
return entryId;
|
||||
}
|
||||
|
||||
generatorInput.parentId() = entryId;
|
||||
}
|
||||
|
||||
return Err(QMResult(NS_ERROR_UNEXPECTED));
|
||||
}
|
||||
|
||||
Result<EntryId, QMResult> FindEntryId(const FileSystemConnection& aConnection,
|
||||
const FileSystemChildMetadata& aHandle,
|
||||
bool isFile) {
|
||||
const nsCString aDirectoryQuery =
|
||||
"SELECT Entries.handle FROM Directories JOIN Entries USING (handle) "
|
||||
"WHERE Directories.name = :name AND Entries.parent = :parent "
|
||||
";"_ns;
|
||||
|
||||
const nsCString aFileQuery =
|
||||
"SELECT Entries.handle FROM Files JOIN Entries USING (handle) "
|
||||
"WHERE Files.name = :name AND Entries.parent = :parent "
|
||||
";"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(aConnection,
|
||||
isFile ? aFileQuery : aDirectoryQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("parent"_ns, aHandle.parentId())));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindNameByName("name"_ns, aHandle.childName())));
|
||||
QM_TRY_UNWRAP(bool moreResults, stmt.ExecuteStep());
|
||||
|
||||
if (!moreResults) {
|
||||
return Err(QMResult(NS_ERROR_DOM_NOT_FOUND_ERR));
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, stmt.GetEntryIdByColumn(/* Column */ 0u));
|
||||
|
||||
return entryId;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result<Usage, QMResult> FileSystemDatabaseManagerVersion001::GetUsage() const {
|
||||
|
@ -244,6 +306,26 @@ nsresult FileSystemDatabaseManagerVersion001::UpdateUsage(int64_t aDelta) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
Result<EntryId, QMResult> FileSystemDatabaseManagerVersion001::GetParentEntryId(
|
||||
const EntryId& aEntry) const {
|
||||
MOZ_ASSERT(!aEntry.IsEmpty());
|
||||
|
||||
const nsCString parentQuery =
|
||||
"SELECT parent FROM Entries "
|
||||
"WHERE handle = :entryId;"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(mConnection, parentQuery));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("entryId"_ns, aEntry)));
|
||||
QM_TRY_UNWRAP(bool moreResults, stmt.ExecuteStep());
|
||||
if (!moreResults) {
|
||||
return Err(QMResult(NS_ERROR_DOM_NOT_FOUND_ERR));
|
||||
}
|
||||
QM_TRY_UNWRAP(EntryId parentId, stmt.GetEntryIdByColumn(/* Column */ 0u));
|
||||
|
||||
return parentId;
|
||||
}
|
||||
|
||||
Result<EntryId, QMResult>
|
||||
FileSystemDatabaseManagerVersion001::GetOrCreateDirectory(
|
||||
const FileSystemChildMetadata& aHandle, bool aCreate) {
|
||||
|
@ -256,11 +338,8 @@ FileSystemDatabaseManagerVersion001::GetOrCreateDirectory(
|
|||
}
|
||||
MOZ_ASSERT(!name.IsVoid() && !name.IsEmpty());
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, fs::data::GetEntryHandle(aHandle));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
bool exists = true;
|
||||
QM_TRY_UNWRAP(exists, DoesFileExist(mConnection, entryId));
|
||||
QM_TRY_UNWRAP(exists, DoesFileExist(mConnection, aHandle));
|
||||
|
||||
// By spec, we don't allow a file and a directory
|
||||
// to have the same name and parent
|
||||
|
@ -268,11 +347,11 @@ FileSystemDatabaseManagerVersion001::GetOrCreateDirectory(
|
|||
return Err(QMResult(NS_ERROR_DOM_TYPE_MISMATCH_ERR));
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(exists, DoesDirectoryExist(mConnection, entryId));
|
||||
QM_TRY_UNWRAP(exists, DoesDirectoryExist(mConnection, aHandle));
|
||||
|
||||
// exists as directory
|
||||
if (exists) {
|
||||
return entryId;
|
||||
return FindEntryId(mConnection, aHandle, false);
|
||||
}
|
||||
|
||||
if (!aCreate) {
|
||||
|
@ -293,6 +372,9 @@ FileSystemDatabaseManagerVersion001::GetOrCreateDirectory(
|
|||
"( :handle, :name ) "
|
||||
";"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, GetUniqueEntryId(mConnection, aHandle));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
mozStorageTransaction transaction(
|
||||
mConnection.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
{
|
||||
|
@ -317,7 +399,7 @@ FileSystemDatabaseManagerVersion001::GetOrCreateDirectory(
|
|||
QM_TRY(QM_TO_RESULT(transaction.Commit()));
|
||||
|
||||
QM_TRY_UNWRAP(DebugOnly<bool> doesItExistNow,
|
||||
DoesDirectoryExist(mConnection, entryId));
|
||||
DoesDirectoryExist(mConnection, aHandle));
|
||||
MOZ_ASSERT(doesItExistNow);
|
||||
|
||||
return entryId;
|
||||
|
@ -334,10 +416,7 @@ Result<EntryId, QMResult> FileSystemDatabaseManagerVersion001::GetOrCreateFile(
|
|||
}
|
||||
MOZ_ASSERT(!name.IsVoid() && !name.IsEmpty());
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, fs::data::GetEntryHandle(aHandle));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
QM_TRY_UNWRAP(bool exists, DoesDirectoryExist(mConnection, entryId));
|
||||
QM_TRY_UNWRAP(bool exists, DoesDirectoryExist(mConnection, aHandle));
|
||||
|
||||
// By spec, we don't allow a file and a directory
|
||||
// to have the same name and parent
|
||||
|
@ -345,10 +424,10 @@ Result<EntryId, QMResult> FileSystemDatabaseManagerVersion001::GetOrCreateFile(
|
|||
return Err(QMResult(NS_ERROR_DOM_TYPE_MISMATCH_ERR));
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(exists, DoesFileExist(mConnection, entryId));
|
||||
QM_TRY_UNWRAP(exists, DoesFileExist(mConnection, aHandle));
|
||||
|
||||
if (exists) {
|
||||
return entryId;
|
||||
return FindEntryId(mConnection, aHandle, true);
|
||||
}
|
||||
|
||||
if (!aCreate) {
|
||||
|
@ -369,7 +448,9 @@ Result<EntryId, QMResult> FileSystemDatabaseManagerVersion001::GetOrCreateFile(
|
|||
"( :handle, :name ) "
|
||||
";"_ns;
|
||||
|
||||
// TODO: This needs a scope quard
|
||||
QM_TRY_UNWRAP(EntryId entryId, GetUniqueEntryId(mConnection, aHandle));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
mozStorageTransaction transaction(
|
||||
mConnection.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
{
|
||||
|
@ -465,15 +546,15 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveDirectory(
|
|||
DebugOnly<Name> name = aHandle.childName();
|
||||
MOZ_ASSERT(!name.inspect().IsVoid() && !name.inspect().IsEmpty());
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, fs::data::GetEntryHandle(aHandle));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
QM_TRY_UNWRAP(bool exists, DoesDirectoryExist(mConnection, entryId));
|
||||
QM_TRY_UNWRAP(bool exists, DoesDirectoryExist(mConnection, aHandle));
|
||||
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, entry exists and is a directory.
|
||||
QM_TRY_UNWRAP(EntryId entryId, FindEntryId(mConnection, aHandle, false));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
QM_TRY_UNWRAP(bool isEmpty, IsDirectoryEmpty(mConnection, entryId));
|
||||
|
||||
|
@ -547,11 +628,8 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveFile(
|
|||
DebugOnly<Name> name = aHandle.childName();
|
||||
MOZ_ASSERT(!name.inspect().IsVoid() && !name.inspect().IsEmpty());
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, fs::data::GetEntryHandle(aHandle));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
// Make it more evident that we won't remove directories
|
||||
QM_TRY_UNWRAP(bool exists, DoesFileExist(mConnection, entryId));
|
||||
QM_TRY_UNWRAP(bool exists, DoesFileExist(mConnection, aHandle));
|
||||
|
||||
if (!exists) {
|
||||
return false;
|
||||
|
@ -563,6 +641,9 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveFile(
|
|||
"WHERE handle = :handle "
|
||||
";"_ns;
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, FindEntryId(mConnection, aHandle, true));
|
||||
MOZ_ASSERT(!entryId.IsEmpty());
|
||||
|
||||
mozStorageTransaction transaction(
|
||||
mConnection.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
|
||||
|
@ -583,6 +664,97 @@ Result<bool, QMResult> FileSystemDatabaseManagerVersion001::RemoveFile(
|
|||
return true;
|
||||
}
|
||||
|
||||
Result<bool, QMResult> FileSystemDatabaseManagerVersion001::MoveEntry(
|
||||
const FileSystemChildMetadata& aHandle,
|
||||
const FileSystemChildMetadata& aNewDesignation) {
|
||||
MOZ_ASSERT(!aHandle.parentId().IsEmpty());
|
||||
|
||||
if (aHandle.childName().IsEmpty() || aNewDesignation.childName().IsEmpty()) {
|
||||
return Err(QMResult(NS_ERROR_DOM_NOT_FOUND_ERR));
|
||||
}
|
||||
|
||||
DebugOnly<Name> name = aHandle.childName();
|
||||
MOZ_ASSERT(!name.inspect().IsVoid());
|
||||
|
||||
// Verify the source exists
|
||||
bool isFile = false;
|
||||
QM_TRY_UNWRAP(bool exists, DoesFileExist(mConnection, aHandle));
|
||||
if (!exists) {
|
||||
QM_TRY_UNWRAP(exists, DoesDirectoryExist(mConnection, aHandle));
|
||||
if (!exists) {
|
||||
return Err(QMResult(NS_ERROR_DOM_NOT_FOUND_ERR));
|
||||
}
|
||||
} else {
|
||||
isFile = true;
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(EntryId entryId, FindEntryId(mConnection, aHandle, isFile));
|
||||
#if 0
|
||||
// Enable once file lock code lands with the SyncAccessHandle patch
|
||||
// At this point, entry exists
|
||||
if (isFile && !CanUseFile(entryId, AccessMode::EXCLUSIVE)) {
|
||||
LOG(("Trying to move in-use file"));
|
||||
return Err(QMResult(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR));
|
||||
}
|
||||
#endif
|
||||
|
||||
const nsLiteralCString updateEntryParentQuery =
|
||||
"UPDATE Entries "
|
||||
"SET parent = :parent "
|
||||
"WHERE handle = :handle "
|
||||
";"_ns;
|
||||
|
||||
const nsLiteralCString updateFileNameQuery =
|
||||
"UPDATE Files "
|
||||
"SET name = :name "
|
||||
"WHERE handle = :handle "
|
||||
";"_ns;
|
||||
|
||||
const nsLiteralCString updateDirectoryNameQuery =
|
||||
"UPDATE Directories "
|
||||
"SET name = :name "
|
||||
"WHERE handle = :handle "
|
||||
";"_ns;
|
||||
|
||||
mozStorageTransaction transaction(
|
||||
mConnection.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
|
||||
{
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(mConnection, updateEntryParentQuery));
|
||||
QM_TRY(QM_TO_RESULT(
|
||||
stmt.BindEntryIdByName("parent"_ns, aNewDesignation.parentId())));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, entryId)));
|
||||
QM_TRY(QM_TO_RESULT(stmt.Execute()));
|
||||
}
|
||||
|
||||
if (isFile) {
|
||||
QM_TRY_UNWRAP(ResultStatement stmt,
|
||||
ResultStatement::Create(mConnection, updateFileNameQuery));
|
||||
QM_TRY(QM_TO_RESULT(
|
||||
stmt.BindNameByName("name"_ns, aNewDesignation.childName())));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, entryId)));
|
||||
QM_TRY(QM_TO_RESULT(stmt.Execute()));
|
||||
} else {
|
||||
QM_TRY_UNWRAP(
|
||||
ResultStatement stmt,
|
||||
ResultStatement::Create(mConnection, updateDirectoryNameQuery));
|
||||
QM_TRY(QM_TO_RESULT(
|
||||
stmt.BindNameByName("name"_ns, aNewDesignation.childName())));
|
||||
QM_TRY(QM_TO_RESULT(stmt.BindEntryIdByName("handle"_ns, entryId)));
|
||||
QM_TRY(QM_TO_RESULT(stmt.Execute()));
|
||||
}
|
||||
|
||||
const auto usageDelta =
|
||||
static_cast<int64_t>(aNewDesignation.childName().Length()) -
|
||||
static_cast<int64_t>(aHandle.childName().Length());
|
||||
QM_TRY(QM_TO_RESULT(UpdateUsage(usageDelta)));
|
||||
|
||||
QM_TRY(QM_TO_RESULT(transaction.Commit()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Result<Path, QMResult> FileSystemDatabaseManagerVersion001::Resolve(
|
||||
const FileSystemEntryPair& aEndpoints) const {
|
||||
QM_TRY_UNWRAP(Path path, ResolveReversedPath(mConnection, aEndpoints));
|
||||
|
|
|
@ -40,6 +40,9 @@ class FileSystemDatabaseManagerVersion001 : public FileSystemDatabaseManager {
|
|||
|
||||
virtual Result<int64_t, QMResult> GetUsage() const override;
|
||||
|
||||
virtual Result<EntryId, QMResult> GetParentEntryId(
|
||||
const EntryId& aEntry) const override;
|
||||
|
||||
virtual Result<EntryId, QMResult> GetOrCreateDirectory(
|
||||
const FileSystemChildMetadata& aHandle, bool aCreate) override;
|
||||
|
||||
|
@ -54,6 +57,10 @@ class FileSystemDatabaseManagerVersion001 : public FileSystemDatabaseManager {
|
|||
virtual Result<FileSystemDirectoryListing, QMResult> GetDirectoryEntries(
|
||||
const EntryId& aParent, PageNumber aPage) const override;
|
||||
|
||||
virtual Result<bool, QMResult> MoveEntry(
|
||||
const FileSystemChildMetadata& aHandle,
|
||||
const FileSystemChildMetadata& aNewDesignation) override;
|
||||
|
||||
virtual Result<bool, QMResult> RemoveDirectory(
|
||||
const FileSystemChildMetadata& aHandle, bool aRecursive) override;
|
||||
|
||||
|
|
|
@ -181,6 +181,34 @@ union FileSystemRemoveEntryResponse
|
|||
void_t;
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies a file/directory to be moved and the new name, and the
|
||||
* destination directory
|
||||
*/
|
||||
struct FileSystemMoveEntryRequest
|
||||
{
|
||||
FileSystemEntryMetadata handle;
|
||||
FileSystemChildMetadata destHandle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Identifies a file/directory to be renamed and the new name
|
||||
*/
|
||||
struct FileSystemRenameEntryRequest
|
||||
{
|
||||
FileSystemEntryMetadata handle;
|
||||
Name name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains an error or the new entryId
|
||||
*/
|
||||
union FileSystemMoveEntryResponse
|
||||
{
|
||||
nsresult;
|
||||
void_t;
|
||||
};
|
||||
|
||||
struct FileSystemQuotaRequest
|
||||
{
|
||||
FileSystemChildMetadata handle;
|
||||
|
@ -320,6 +348,23 @@ async protocol PFileSystemManager
|
|||
async RemoveEntry(FileSystemRemoveEntryRequest request)
|
||||
returns(FileSystemRemoveEntryResponse response);
|
||||
|
||||
/**
|
||||
* Initiates an asynchronous request to move a directory or file with a
|
||||
* given name to a given destination and new name.
|
||||
*
|
||||
* @returns error information
|
||||
*/
|
||||
async MoveEntry(FileSystemMoveEntryRequest request)
|
||||
returns(FileSystemMoveEntryResponse response);
|
||||
|
||||
/**
|
||||
* Initiates an asynchronous request to rename a directory or file
|
||||
*
|
||||
* @returns error information
|
||||
*/
|
||||
async RenameEntry(FileSystemRenameEntryRequest request)
|
||||
returns(FileSystemMoveEntryResponse response);
|
||||
|
||||
/**
|
||||
* TODO: documentation
|
||||
* So we can implement exclusive access
|
||||
|
|
|
@ -96,6 +96,19 @@ class MockFileSystemRequestHandler : public FileSystemRequestHandler {
|
|||
RefPtr<Promise> aPromise),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, MoveEntry,
|
||||
(RefPtr<FileSystemManager> & aManager, FileSystemHandle* aHandle,
|
||||
const FileSystemEntryMetadata& aEntry,
|
||||
const FileSystemChildMetadata& aNewEntry,
|
||||
RefPtr<Promise> aPromise),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, RenameEntry,
|
||||
(RefPtr<FileSystemManager> & aManager, FileSystemHandle* aHandle,
|
||||
const FileSystemEntryMetadata& aEntry, const Name& aName,
|
||||
RefPtr<Promise> aPromise),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, Resolve,
|
||||
(RefPtr<FileSystemManager> & aManager,
|
||||
const FileSystemEntryPair& aEndpoints, RefPtr<Promise> aPromise),
|
||||
|
|
|
@ -13,6 +13,14 @@ interface FileSystemHandle {
|
|||
readonly attribute FileSystemHandleKind kind;
|
||||
readonly attribute USVString name;
|
||||
|
||||
/* https://whatpr.org/fs/10.html#api-filesystemhandle */
|
||||
[NewObject]
|
||||
Promise<void> move(USVString name);
|
||||
[NewObject]
|
||||
Promise<void> move(FileSystemDirectoryHandle parent);
|
||||
[NewObject]
|
||||
Promise<void> move(FileSystemDirectoryHandle parent, USVString name);
|
||||
|
||||
[NewObject]
|
||||
Promise<boolean> isSameEntry(FileSystemHandle other);
|
||||
};
|
||||
|
|
|
@ -1,60 +1,60 @@
|
|||
[FileSystemFileHandle-move.https.any.worker.html]
|
||||
[move(name) to rename a file]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) to rename a file the same name]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move("") to rename a file fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) can be called multiple times]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) with a name with a trailing period should fail]
|
||||
expected: FAIL
|
||||
|
||||
[move(name) with a name with invalid characters should fail]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) while the file has an open writable fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) while the destination file has an open writable fails]
|
||||
expected: FAIL
|
||||
|
||||
[move(dir, name) to rename a file]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, name) to rename a file the same name]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) to move a file to a new directory]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, "") to move a file to a new directory]
|
||||
expected: FAIL
|
||||
|
||||
[move(dir, name) to move a file to a new directory]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) can be called multiple times]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, "") can be called multiple times]
|
||||
expected: FAIL
|
||||
|
||||
[move(dir, name) can be called multiple times]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, name) with a name with invalid characters should fail]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) while the file has an open writable fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, name) while the file has an open writable fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) while the destination file has an open writable fails]
|
||||
expected: FAIL
|
||||
|
@ -65,61 +65,61 @@
|
|||
|
||||
[FileSystemFileHandle-move.https.any.html]
|
||||
[move(name) to rename a file]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) to rename a file the same name]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move("") to rename a file fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) can be called multiple times]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) with a name with a trailing period should fail]
|
||||
expected: FAIL
|
||||
|
||||
[move(name) with a name with invalid characters should fail]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) while the file has an open writable fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(name) while the destination file has an open writable fails]
|
||||
expected: FAIL
|
||||
|
||||
[move(dir, name) to rename a file]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, name) to rename a file the same name]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) to move a file to a new directory]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, "") to move a file to a new directory]
|
||||
expected: FAIL
|
||||
|
||||
[move(dir, name) to move a file to a new directory]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) can be called multiple times]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, "") can be called multiple times]
|
||||
expected: FAIL
|
||||
|
||||
[move(dir, name) can be called multiple times]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, name) with a name with invalid characters should fail]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) while the file has an open writable fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir, name) while the file has an open writable fails]
|
||||
expected: FAIL
|
||||
expected: PASS
|
||||
|
||||
[move(dir) while the destination file has an open writable fails]
|
||||
expected: FAIL
|
||||
|
|
|
@ -34,10 +34,11 @@ async function getDirectoryEntryCount(handle) {
|
|||
async function getSortedDirectoryEntries(handle) {
|
||||
let result = [];
|
||||
for await (let entry of handle.values()) {
|
||||
if (entry.kind === 'directory')
|
||||
if (entry.kind === 'directory') {
|
||||
result.push(entry.name + '/');
|
||||
else
|
||||
} else {
|
||||
result.push(entry.name);
|
||||
}
|
||||
}
|
||||
result.sort();
|
||||
return result;
|
||||
|
|
|
@ -11,6 +11,14 @@ directory_test(async (t, root) => {
|
|||
assert_equals(await getFileSize(handle), 3);
|
||||
}, 'move(name) to rename a file');
|
||||
|
||||
directory_test(async (t, root) => {
|
||||
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
|
||||
await handle.move('file-after');
|
||||
const newhandle = await root.getFileHandle('file-after');
|
||||
assert_equals(await getFileContents(newhandle), 'foo');
|
||||
assert_equals(await getFileSize(newhandle), 3);
|
||||
}, 'get a handle to a moved file');
|
||||
|
||||
directory_test(async (t, root) => {
|
||||
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
|
||||
await handle.move('file-before');
|
||||
|
@ -55,7 +63,7 @@ directory_test(async (t, root) => {
|
|||
|
||||
directory_test(async (t, root) => {
|
||||
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
|
||||
await promise_rejects_js(t, TypeError, handle.move('#$23423@352^*3243'));
|
||||
await promise_rejects_js(t, TypeError, handle.move('test/test'));
|
||||
|
||||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']);
|
||||
assert_equals(await getFileContents(handle), 'foo');
|
||||
|
@ -238,8 +246,7 @@ directory_test(async (t, root) => {
|
|||
|
||||
directory_test(async (t, root) => {
|
||||
const handle = await createFileWithContents(t, 'file-before', 'foo', root);
|
||||
await promise_rejects_js(
|
||||
t, TypeError, handle.move(root, '#$23423@352^*3243'));
|
||||
await promise_rejects_js(t, TypeError, handle.move(root, '..'));
|
||||
|
||||
assert_array_equals(await getSortedDirectoryEntries(root), ['file-before']);
|
||||
assert_equals(await getFileContents(handle), 'foo');
|
||||
|
|
Загрузка…
Ссылка в новой задаче