Commit d9d65390 authored by Joshua Pawlicki's avatar Joshua Pawlicki Committed by Commit Bot

Revert "NativeIO: Add Error Handling for file errors"

This reverts commit df72c8d5.

Reason for revert: external/wpt/native-io/rename_async_failure_handling.tentative.https.any.html
external/wpt/native-io/rename_async_failure_handling.tentative.https.any.serviceworker.html
external/wpt/native-io/rename_async_failure_handling.tentative.https.any.sharedworker.html
external/wpt/native-io/rename_async_failure_handling.tentative.https.any.worker.html failing on multiple bots: https://ci.chromium.org/p/chromium/builders/ci/WebKit%20Win10
https://ci.chromium.org/p/chromium/builders/ci/linux-bfcache-rel

Original change's description:
> NativeIO: Add Error Handling for file errors
>
> This CL adds a mechanism to report file errors, as reported by
> base::File::Error, though DOMExceptions. Due to security and privacy
> considerations, not all errors are exposed to the user. For those
> operations performed by the browser process (open, rename), the exact
> error type is not exposed to the renderer.
>
> Errors that are not reported by base::File::Error are left unchanged and
> will be addressed in a followup CL.
>
> The design document for this change is
> https://docs.google.com/document/d/1rvs615AU2s8kVsmUlukbmtQNvUWFny0yzAS_gsnYZEs/
>
> Bug: 1095537
> Change-Id: If047ddccb6464dd6efb2b06a5da262d049f1e9a8
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2436344
> Commit-Queue: Richard Stotz <rstz@chromium.org>
> Reviewed-by: Victor Costan <pwnall@chromium.org>
> Reviewed-by: Mike West <mkwst@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#829643}

TBR=pwnall@chromium.org,mkwst@chromium.org,krivoy@google.com,rstz@chromium.org

Change-Id: I1078ca3dd40685d711a9717b143f2974d62e084c
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1095537
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2551842Reviewed-by: default avatarJoshua Pawlicki <waffles@chromium.org>
Commit-Queue: Joshua Pawlicki <waffles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#829673}
parent ef6a7b32
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include "storage/browser/quota/quota_manager_proxy.h" #include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/quota/special_storage_policy.h" #include "storage/browser/quota/special_storage_policy.h"
#include "storage/common/database/database_identifier.h" #include "storage/common/database/database_identifier.h"
#include "third_party/blink/public/common/native_io/native_io_utils.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom.h" #include "third_party/blink/public/mojom/native_io/native_io.mojom.h"
#include "url/origin.h" #include "url/origin.h"
...@@ -26,6 +25,14 @@ namespace { ...@@ -26,6 +25,14 @@ namespace {
constexpr base::FilePath::CharType kNativeIODirectoryName[] = constexpr base::FilePath::CharType kNativeIODirectoryName[] =
FILE_PATH_LITERAL("NativeIO"); FILE_PATH_LITERAL("NativeIO");
base::FilePath GetNativeIORootPath(const base::FilePath& profile_root) {
if (profile_root.empty())
return base::FilePath();
return profile_root.Append(kNativeIODirectoryName);
}
} // namespace } // namespace
NativeIOContext::NativeIOContext( NativeIOContext::NativeIOContext(
...@@ -103,26 +110,4 @@ base::FilePath NativeIOContext::RootPathForOrigin(const url::Origin& origin) { ...@@ -103,26 +110,4 @@ base::FilePath NativeIOContext::RootPathForOrigin(const url::Origin& origin) {
return origin_path; return origin_path;
} }
// static
base::FilePath NativeIOContext::GetNativeIORootPath(
const base::FilePath& profile_root) {
if (profile_root.empty())
return base::FilePath();
return profile_root.Append(kNativeIODirectoryName);
}
// static
blink::mojom::NativeIOErrorPtr NativeIOContext::FileErrorToNativeIOError(
base::File::Error file_error,
std::string message) {
blink::mojom::NativeIOErrorType native_io_error_type =
blink::native_io::FileErrorToNativeIOErrorType(file_error);
std::string final_message =
message.empty()
? blink::native_io::GetDefaultMessage(native_io_error_type)
: message;
return blink::mojom::NativeIOError::New(native_io_error_type, final_message);
}
} // namespace content } // namespace content
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include "base/files/file.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
...@@ -56,22 +55,12 @@ class CONTENT_EXPORT NativeIOContext { ...@@ -56,22 +55,12 @@ class CONTENT_EXPORT NativeIOContext {
// |host| must be owned by this context. This method should only be called by // |host| must be owned by this context. This method should only be called by
// NativeIOHost. // NativeIOHost.
void OnHostReceiverDisconnect(NativeIOHost* host); void OnHostReceiverDisconnect(NativeIOHost* host);
private:
// Computes the path to the directory storing an origin's NativeIO files. // Computes the path to the directory storing an origin's NativeIO files.
// //
// Returns an empty path if the origin isn't supported for NativeIO. // Returns an empty path if the origin isn't supported for NativeIO.
base::FilePath RootPathForOrigin(const url::Origin& origin); base::FilePath RootPathForOrigin(const url::Origin& origin);
// Computes the path to the directory storing a profile's NativeIO files.
static base::FilePath GetNativeIORootPath(const base::FilePath& profile_root);
// Transform a base::File::Error into a NativeIOError with default error
// message if none is provided.
static blink::mojom::NativeIOErrorPtr FileErrorToNativeIOError(
base::File::Error file_error,
std::string message = "");
private:
std::map<url::Origin, std::unique_ptr<NativeIOHost>> hosts_; std::map<url::Origin, std::unique_ptr<NativeIOHost>> hosts_;
// Points to the root directory for NativeIO files. // Points to the root directory for NativeIO files.
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "content/browser/native_io/native_io_context.h"
#include "content/public/browser/browser_context.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "storage/common/database/database_identifier.h"
#include "url/gurl.h"
namespace content {
class NativeIOContextBrowserTest : public ContentBrowserTest {
public:
NativeIOContextBrowserTest() {
// SharedArrayBuffers are not enabled by default on Android, see also
// https://crbug.com/1144104 .
feature_list_.InitAndEnableFeature(features::kSharedArrayBuffer);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
ContentBrowserTest::SetUpCommandLine(command_line);
}
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
}
base::FilePath GetNativeIODir(base::FilePath user_data_dir,
const GURL& test_url) {
std::string origin_identifier =
storage::GetIdentifierFromOrigin(test_url.GetOrigin());
base::FilePath root_dir =
NativeIOContext::GetNativeIORootPath(user_data_dir);
return root_dir.AppendASCII(origin_identifier);
}
DISALLOW_COPY_AND_ASSIGN(NativeIOContextBrowserTest);
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(NativeIOContextBrowserTest, ReadFromDeletedFile) {
const GURL& test_url = embedded_test_server()->GetURL(
"/native_io/read_from_deleted_file_test.html");
Shell* browser = CreateBrowser();
base::FilePath user_data_dir = GetNativeIODir(
browser->web_contents()->GetBrowserContext()->GetPath(), test_url);
NavigateToURLBlockUntilNavigationsComplete(browser, test_url,
/*number_of_navigations=*/1);
EXPECT_TRUE(EvalJs(browser, "writeToFile()").ExtractBool());
{
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::DeleteFile(user_data_dir.AppendASCII("test_file")));
}
EXPECT_TRUE(EvalJs(browser, "readFromFile()").ExtractBool());
}
// This test depends on POSIX file permissions, which do not work on Windows,
// Android, or Fuchsia.
#if !defined(OS_WIN) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
IN_PROC_BROWSER_TEST_F(NativeIOContextBrowserTest, TryOpenProtectedFileTest) {
const GURL& test_url = embedded_test_server()->GetURL(
"/native_io/try_open_protected_file_test.html");
Shell* browser = CreateBrowser();
base::FilePath user_data_dir_ = GetNativeIODir(
browser->web_contents()->GetBrowserContext()->GetPath(), test_url);
NavigateToURLBlockUntilNavigationsComplete(browser, test_url,
/*number_of_navigations=*/1);
EXPECT_TRUE(EvalJs(browser, "createAndCloseFile()").ExtractBool());
{
base::ScopedAllowBlockingForTesting allow_blocking;
base::SetPosixFilePermissions(user_data_dir_.AppendASCII("test_file"),
0300); // not readable
}
std::string expected_caught_error = "InvalidStateError";
EXPECT_EQ(EvalJs(browser, "tryOpeningFile()").ExtractString(),
expected_caught_error);
}
#endif // !defined(OS_WIN) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
} // namespace content
...@@ -8,39 +8,28 @@ ...@@ -8,39 +8,28 @@
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/files/file.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "content/browser/native_io/native_io_context.h"
#include "content/browser/native_io/native_io_host.h" #include "content/browser/native_io/native_io_host.h"
#include "third_party/blink/public/common/native_io/native_io_utils.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom.h" #include "third_party/blink/public/mojom/native_io/native_io.mojom.h"
using blink::mojom::NativeIOError;
using blink::mojom::NativeIOErrorPtr;
using blink::mojom::NativeIOErrorType;
namespace content { namespace content {
namespace { namespace {
// Performs the file I/O work in SetLength(). // Performs the file I/O work in SetLength().
std::pair<base::File, base::File::Error> DoSetLength(const int64_t length, std::pair<bool, base::File> DoSetLength(const int64_t length, base::File file) {
base::File file) { bool set_length_success = false;
DCHECK_GE(length, 0) << "The file length should not be negative"; DCHECK_GE(length, 0) << "The file length should not be negative";
base::File::Error set_length_error; set_length_success = file.SetLength(length);
bool success = file.SetLength(length);
set_length_error = success ? base::File::FILE_OK : file.GetLastFileError();
return {std::move(file), set_length_error}; return {set_length_success, std::move(file)};
} }
void DidSetLength(blink::mojom::NativeIOFileHost::SetLengthCallback callback, void DidSetLength(blink::mojom::NativeIOFileHost::SetLengthCallback callback,
std::pair<base::File, base::File::Error> result) { std::pair<bool, base::File> result) {
NativeIOErrorPtr error = std::move(callback).Run(result.first, std::move(result.second));
NativeIOContext::FileErrorToNativeIOError(result.second);
std::move(callback).Run(std::move(result.first), std::move(error));
} }
} // namespace } // namespace
...@@ -75,20 +64,14 @@ void NativeIOFileHost::SetLength(const int64_t length, ...@@ -75,20 +64,14 @@ void NativeIOFileHost::SetLength(const int64_t length,
if (length < 0) { if (length < 0) {
mojo::ReportBadMessage("The file length cannot be negative."); mojo::ReportBadMessage("The file length cannot be negative.");
// No error message is specified as the ReportBadMessage() call should close std::move(callback).Run(false, std::move(file));
// the pipe and kill the renderer.
std::move(callback).Run(
std::move(file), NativeIOError::New(NativeIOErrorType::kUnknown, ""));
return; return;
} }
// file.IsValid() does not interact with the file system, so we may call it on // file.IsValid() does not interact with the file system, so we may call it on
// this thread. // this thread.
if (!file.IsValid()) { if (!file.IsValid()) {
mojo::ReportBadMessage("The file is invalid."); mojo::ReportBadMessage("The file is invalid.");
// No error message is specified as the ReportBadMessage() call should close std::move(callback).Run(false, std::move(file));
// the pipe and kill the renderer.
std::move(callback).Run(
std::move(file), NativeIOError::New(NativeIOErrorType::kUnknown, ""));
return; return;
} }
......
...@@ -23,13 +23,8 @@ ...@@ -23,13 +23,8 @@
#include "content/browser/native_io/native_io_file_host.h" #include "content/browser/native_io/native_io_file_host.h"
#include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/blink/public/common/native_io/native_io_utils.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom.h" #include "third_party/blink/public/mojom/native_io/native_io.mojom.h"
using blink::mojom::NativeIOError;
using blink::mojom::NativeIOErrorPtr;
using blink::mojom::NativeIOErrorType;
namespace content { namespace content {
namespace { namespace {
...@@ -92,20 +87,14 @@ base::File DoOpenFile(const base::FilePath& root_path, ...@@ -92,20 +87,14 @@ base::File DoOpenFile(const base::FilePath& root_path,
} }
// Performs the file I/O work in DeleteFile(). // Performs the file I/O work in DeleteFile().
NativeIOErrorPtr DoDeleteFile(const base::FilePath& root_path, bool DoDeleteFile(const base::FilePath& root_path, const std::string& name) {
const std::string& name) {
DCHECK(IsValidNativeIOName(name)); DCHECK(IsValidNativeIOName(name));
// If the origin's directory wasn't created yet, there's nothing to delete. // If the origin's directory wasn't created yet, there's nothing to delete.
if (!base::PathExists(root_path)) if (!base::PathExists(root_path))
return NativeIOError::New(NativeIOErrorType::kSuccess, ""); return true;
bool success = base::DeleteFile(GetNativeIOFilePath(root_path, name)); return base::DeleteFile(GetNativeIOFilePath(root_path, name));
if (!success) {
return NativeIOContext::FileErrorToNativeIOError(
base::File::GetLastFileError());
}
return NativeIOError::New(NativeIOErrorType::kSuccess, "");
} }
using GetAllFileNamesResult = using GetAllFileNamesResult =
...@@ -166,31 +155,24 @@ void DidGetAllFileNames( ...@@ -166,31 +155,24 @@ void DidGetAllFileNames(
} }
// Performs the file I/O work in RenameFile(). // Performs the file I/O work in RenameFile().
NativeIOErrorPtr DoRenameFile(const base::FilePath& root_path, bool DoRenameFile(const base::FilePath& root_path,
const std::string& old_name, const std::string& old_name,
const std::string& new_name) { const std::string& new_name) {
DCHECK(IsValidNativeIOName(old_name)); DCHECK(IsValidNativeIOName(old_name));
DCHECK(IsValidNativeIOName(new_name)); DCHECK(IsValidNativeIOName(new_name));
base::File::Error error = base::File::FILE_OK;
// If the origin's directory wasn't created yet, there's nothing to rename. // If the origin's directory wasn't created yet, there's nothing to rename.
// This error cannot be used to determine the existence of files outside of if (!base::PathExists(root_path))
// the origin's directory, as |old_name| is a valid NativeIO name. return false;
if (!base::PathExists(root_path) ||
!base::PathExists(GetNativeIOFilePath(root_path, old_name))) // Do not overwrite an existing file.
return NativeIOError::New(NativeIOErrorType::kNotFound,
"Source file does not exist");
// Do not overwrite an existing file. This error cannot be used to determine
// the existence of files outside of the origin's directory, as |new_name| is
// a valid NativeIO name.
if (base::PathExists(GetNativeIOFilePath(root_path, new_name))) if (base::PathExists(GetNativeIOFilePath(root_path, new_name)))
return NativeIOError::New(NativeIOErrorType::kNoModificationAllowed, return false;
"Target file exists");
base::ReplaceFile(GetNativeIOFilePath(root_path, old_name), // TODO(rstz): Report error.
GetNativeIOFilePath(root_path, new_name), &error); base::File::Error error;
return NativeIOContext::FileErrorToNativeIOError(error); return base::ReplaceFile(GetNativeIOFilePath(root_path, old_name),
GetNativeIOFilePath(root_path, new_name), &error);
} }
} // namespace } // namespace
...@@ -231,27 +213,21 @@ void NativeIOHost::OpenFile( ...@@ -231,27 +213,21 @@ void NativeIOHost::OpenFile(
if (!IsValidNativeIOName(name)) { if (!IsValidNativeIOName(name)) {
mojo::ReportBadMessage("Invalid file name"); mojo::ReportBadMessage("Invalid file name");
std::move(callback).Run( std::move(callback).Run(base::File());
base::File(),
NativeIOError::New(NativeIOErrorType::kUnknown, "Invalid file name"));
return; return;
} }
if (open_file_hosts_.find(name) != open_file_hosts_.end()) { if (open_file_hosts_.find(name) != open_file_hosts_.end()) {
std::move(callback).Run( // TODO(pwnall): Report that the file is locked.
base::File(), std::move(callback).Run(base::File());
NativeIOError::New(NativeIOErrorType::kNoModificationAllowed,
"File is open"));
return; return;
} }
auto insert_result = io_pending_files_.insert(name); auto insert_result = io_pending_files_.insert(name);
bool insert_success = insert_result.second; bool insert_success = insert_result.second;
if (!insert_success) { if (!insert_success) {
std::move(callback).Run( // TODO(pwnall): Report that the file is locked.
base::File(), std::move(callback).Run(base::File());
NativeIOError::New(NativeIOErrorType::kNoModificationAllowed,
"Operation pending on file"));
return; return;
} }
...@@ -267,23 +243,21 @@ void NativeIOHost::DeleteFile(const std::string& name, ...@@ -267,23 +243,21 @@ void NativeIOHost::DeleteFile(const std::string& name,
if (!IsValidNativeIOName(name)) { if (!IsValidNativeIOName(name)) {
mojo::ReportBadMessage("Invalid file name"); mojo::ReportBadMessage("Invalid file name");
std::move(callback).Run( std::move(callback).Run(false);
NativeIOError::New(NativeIOErrorType::kUnknown, "Invalid file name"));
return; return;
} }
if (open_file_hosts_.find(name) != open_file_hosts_.end()) { if (open_file_hosts_.find(name) != open_file_hosts_.end()) {
std::move(callback).Run(NativeIOError::New( // TODO(pwnall): Report that the file is locked.
NativeIOErrorType::kNoModificationAllowed, "File is open")); std::move(callback).Run(false);
return; return;
} }
auto insert_result = io_pending_files_.insert(name); auto insert_result = io_pending_files_.insert(name);
bool insert_success = insert_result.second; bool insert_success = insert_result.second;
if (!insert_success) { if (!insert_success) {
std::move(callback).Run( // TODO(pwnall): Report that the file is locked.
NativeIOError::New(NativeIOErrorType::kNoModificationAllowed, std::move(callback).Run(false);
"Operation pending on file"));
return; return;
} }
...@@ -308,37 +282,26 @@ void NativeIOHost::RenameFile(const std::string& old_name, ...@@ -308,37 +282,26 @@ void NativeIOHost::RenameFile(const std::string& old_name,
if (!IsValidNativeIOName(old_name) || !IsValidNativeIOName(new_name)) { if (!IsValidNativeIOName(old_name) || !IsValidNativeIOName(new_name)) {
mojo::ReportBadMessage("Invalid file name"); mojo::ReportBadMessage("Invalid file name");
std::move(callback).Run( std::move(callback).Run(false);
NativeIOError::New(NativeIOErrorType::kUnknown, "Invalid file name"));
return; return;
} }
if (open_file_hosts_.find(old_name) != open_file_hosts_.end() || if (open_file_hosts_.find(old_name) != open_file_hosts_.end() ||
open_file_hosts_.find(new_name) != open_file_hosts_.end()) { open_file_hosts_.find(new_name) != open_file_hosts_.end()) {
std::move(callback).Run(NativeIOError::New( // TODO(rstz): Report that the file is locked.
NativeIOErrorType::kNoModificationAllowed, "Source file is open")); std::move(callback).Run(false);
return;
}
if (open_file_hosts_.find(old_name) != open_file_hosts_.end()) {
std::move(callback).Run(NativeIOError::New(
NativeIOErrorType::kNoModificationAllowed, "Target file is open"));
return; return;
} }
auto old_iterator_and_success = io_pending_files_.insert(old_name); auto old_iterator_and_success = io_pending_files_.insert(old_name);
if (!old_iterator_and_success.second) { if (!old_iterator_and_success.second) {
std::move(callback).Run( std::move(callback).Run(false);
NativeIOError::New(NativeIOErrorType::kNoModificationAllowed,
"Operation pending on source file"));
return; return;
} }
auto new_iterator_and_success = io_pending_files_.insert(new_name); auto new_iterator_and_success = io_pending_files_.insert(new_name);
if (!new_iterator_and_success.second) { if (!new_iterator_and_success.second) {
io_pending_files_.erase(old_iterator_and_success.first); io_pending_files_.erase(old_iterator_and_success.first);
std::move(callback).Run( std::move(callback).Run(false);
NativeIOError::New(NativeIOErrorType::kNoModificationAllowed,
"Operation pending on target file"));
return; return;
} }
...@@ -370,15 +333,8 @@ void NativeIOHost::DidOpenFile( ...@@ -370,15 +333,8 @@ void NativeIOHost::DidOpenFile(
DCHECK(!open_file_hosts_.count(name)); DCHECK(!open_file_hosts_.count(name));
io_pending_files_.erase(name); io_pending_files_.erase(name);
base::File::Error open_error = file.error_details();
if (!file.IsValid()) { if (!file.IsValid()) {
// Make sure an error is reported whenever the file is not valid. std::move(callback).Run(std::move(file));
open_error = open_error != base::File::FILE_OK
? open_error
: base::File::FILE_ERROR_FAILED;
std::move(callback).Run(
std::move(file), NativeIOContext::FileErrorToNativeIOError(open_error));
return; return;
} }
...@@ -386,26 +342,25 @@ void NativeIOHost::DidOpenFile( ...@@ -386,26 +342,25 @@ void NativeIOHost::DidOpenFile(
{name, std::make_unique<NativeIOFileHost>(std::move(file_host_receiver), {name, std::make_unique<NativeIOFileHost>(std::move(file_host_receiver),
this, name)}); this, name)});
std::move(callback).Run( std::move(callback).Run(std::move(file));
std::move(file), NativeIOContext::FileErrorToNativeIOError(open_error));
return; return;
} }
void NativeIOHost::DidDeleteFile(const std::string& name, void NativeIOHost::DidDeleteFile(const std::string& name,
DeleteFileCallback callback, DeleteFileCallback callback,
NativeIOErrorPtr delete_error) { bool success) {
DCHECK(io_pending_files_.count(name)); DCHECK(io_pending_files_.count(name));
DCHECK(!open_file_hosts_.count(name)); DCHECK(!open_file_hosts_.count(name));
io_pending_files_.erase(name); io_pending_files_.erase(name);
std::move(callback).Run(std::move(delete_error)); std::move(callback).Run(success);
return; return;
} }
void NativeIOHost::DidRenameFile(const std::string& old_name, void NativeIOHost::DidRenameFile(const std::string& old_name,
const std::string& new_name, const std::string& new_name,
RenameFileCallback callback, RenameFileCallback callback,
NativeIOErrorPtr rename_error) { bool success) {
DCHECK(io_pending_files_.count(old_name)); DCHECK(io_pending_files_.count(old_name));
DCHECK(!open_file_hosts_.count(old_name)); DCHECK(!open_file_hosts_.count(old_name));
DCHECK(io_pending_files_.count(new_name)); DCHECK(io_pending_files_.count(new_name));
...@@ -413,7 +368,7 @@ void NativeIOHost::DidRenameFile(const std::string& old_name, ...@@ -413,7 +368,7 @@ void NativeIOHost::DidRenameFile(const std::string& old_name,
io_pending_files_.erase(old_name); io_pending_files_.erase(old_name);
io_pending_files_.erase(new_name); io_pending_files_.erase(new_name);
std::move(callback).Run(std::move(rename_error)); std::move(callback).Run(success);
return; return;
} }
......
...@@ -91,13 +91,13 @@ class NativeIOHost : public blink::mojom::NativeIOHost { ...@@ -91,13 +91,13 @@ class NativeIOHost : public blink::mojom::NativeIOHost {
// Called after the file I/O part of DeleteFile() completed. // Called after the file I/O part of DeleteFile() completed.
void DidDeleteFile(const std::string& name, void DidDeleteFile(const std::string& name,
DeleteFileCallback callback, DeleteFileCallback callback,
blink::mojom::NativeIOErrorPtr delete_error); bool success);
// Called after the file I/O part of RenameFile() completed. // Called after the file I/O part of RenameFile() completed.
void DidRenameFile(const std::string& old_name, void DidRenameFile(const std::string& old_name,
const std::string& new_name, const std::string& new_name,
RenameFileCallback callback, RenameFileCallback callback,
blink::mojom::NativeIOErrorPtr rename_error); bool success);
// The directory holding all the files for this origin. // The directory holding all the files for this origin.
const base::FilePath root_path_; const base::FilePath root_path_;
......
...@@ -1062,7 +1062,6 @@ test("content_browsertests") { ...@@ -1062,7 +1062,6 @@ test("content_browsertests") {
"../browser/media/webaudio/audio_context_manager_browsertest.cc", "../browser/media/webaudio/audio_context_manager_browsertest.cc",
"../browser/message_port_provider_browsertest.cc", "../browser/message_port_provider_browsertest.cc",
"../browser/mojo_sandbox_browsertest.cc", "../browser/mojo_sandbox_browsertest.cc",
"../browser/native_io/native_io_context_browsertest.cc",
"../browser/navigation_browsertest.cc", "../browser/navigation_browsertest.cc",
"../browser/navigation_mhtml_browsertest.cc", "../browser/navigation_mhtml_browsertest.cc",
"../browser/net/accept_header_browsertest.cc", "../browser/net/accept_header_browsertest.cc",
......
<!doctype html>
<title>Read from deleted file test</title>
<script>
let test_file;
async function writeToFile() {
if (nativeIO) {
test_file = await nativeIO.open('test_file');
const writtenBytes = new Uint8Array(new SharedArrayBuffer(4));
writtenBytes.set([64, 65, 66, 67]);
const writeCount = await test_file.write(writtenBytes, 0);
document.location.hash = '#ready';
return writeCount == 4;
} else {
return false;
}
}
async function readFromFile() {
const readSharedArrayBuffer = new SharedArrayBuffer(4);
const readBytes = new Uint8Array(readSharedArrayBuffer);
const readCount = await test_file.read(readBytes, 0);
return readCount == 4;
}
</script>
<!doctype html>
<title>Try opening a protected file test</title>
<script>
async function createAndCloseFile() {
if (nativeIO) {
const test_file = await nativeIO.open('test_file');
await test_file.close();
return true;
} else {
return false;
}
}
async function tryOpeningFile() {
try {
await nativeIO.open('test_file');
} catch (e) {
return e.name;
}
return '';
}
</script>
...@@ -140,7 +140,6 @@ source_set("common") { ...@@ -140,7 +140,6 @@ source_set("common") {
"messaging/web_message_port.cc", "messaging/web_message_port.cc",
"mime_util/mime_util.cc", "mime_util/mime_util.cc",
"mobile_metrics/mobile_friendliness.cc", "mobile_metrics/mobile_friendliness.cc",
"native_io/native_io_utils.cc",
"notifications/notification_mojom_traits.cc", "notifications/notification_mojom_traits.cc",
"notifications/notification_resources.cc", "notifications/notification_resources.cc",
"notifications/platform_notification_data.cc", "notifications/platform_notification_data.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/public/common/native_io/native_io_utils.h"
#include "base/files/file.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom-shared.h"
namespace blink {
namespace native_io {
// See https://crbug.com/1095537 for a design doc of this mapping.
blink::mojom::NativeIOErrorType FileErrorToNativeIOErrorType(
const base::File::Error error) {
switch (error) {
case base::File::FILE_OK:
return mojom::NativeIOErrorType::kSuccess;
// Errors in this category are unexpected and provide no way of recovery.
case base::File::FILE_ERROR_ABORT:
case base::File::FILE_ERROR_INVALID_OPERATION:
case base::File::FILE_ERROR_INVALID_URL:
case base::File::FILE_ERROR_IO:
case base::File::FILE_ERROR_NOT_A_DIRECTORY:
case base::File::FILE_ERROR_NOT_A_FILE:
case base::File::FILE_ERROR_NOT_EMPTY:
return mojom::NativeIOErrorType::kUnknown;
// Errors in this category have no recovery path within NativeIO. NOT_FOUND
// is included here, as Windows returns NOT_FOUND when attempting to use an
// overly long file name.
case base::File::FILE_ERROR_ACCESS_DENIED:
case base::File::FILE_ERROR_SECURITY:
case base::File::FILE_ERROR_FAILED:
return mojom::NativeIOErrorType::kInvalidState;
case base::File::FILE_ERROR_NOT_FOUND:
return mojom::NativeIOErrorType::kNotFound;
// Errors in this category have a recovery path.
case base::File::FILE_ERROR_EXISTS:
case base::File::FILE_ERROR_IN_USE:
case base::File::FILE_ERROR_NO_MEMORY:
case base::File::FILE_ERROR_TOO_MANY_OPENED:
return mojom::NativeIOErrorType::kNoModificationAllowed;
case base::File::FILE_ERROR_NO_SPACE:
return mojom::NativeIOErrorType::kNoSpace;
case base::File::FILE_ERROR_MAX:
NOTREACHED();
return mojom::NativeIOErrorType::kUnknown;
}
NOTREACHED();
return mojom::NativeIOErrorType::kUnknown;
}
std::string GetDefaultMessage(const mojom::NativeIOErrorType nativeio_error) {
switch (nativeio_error) {
case mojom::NativeIOErrorType::kSuccess:
return "";
case mojom::NativeIOErrorType::kUnknown:
return "Unspecified internal error.";
case mojom::NativeIOErrorType::kInvalidState:
return "Operation failed.";
case mojom::NativeIOErrorType::kNotFound:
return "File not found.";
case mojom::NativeIOErrorType::kNoModificationAllowed:
return "No modification allowed.";
case mojom::NativeIOErrorType::kNoSpace:
return "No space available.";
}
NOTREACHED();
return std::string();
}
} // namespace native_io
} // namespace blink
...@@ -156,7 +156,6 @@ source_set("headers") { ...@@ -156,7 +156,6 @@ source_set("headers") {
"metrics/form_element_pii_type.h", "metrics/form_element_pii_type.h",
"mime_util/mime_util.h", "mime_util/mime_util.h",
"mobile_metrics/mobile_friendliness.h", "mobile_metrics/mobile_friendliness.h",
"native_io/native_io_utils.h",
"navigation/triggering_event_info.h", "navigation/triggering_event_info.h",
"notifications/notification_constants.h", "notifications/notification_constants.h",
"notifications/notification_mojom_traits.h", "notifications/notification_mojom_traits.h",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_NATIVE_IO_NATIVE_IO_UTILS_H_
#define THIRD_PARTY_BLINK_PUBLIC_COMMON_NATIVE_IO_NATIVE_IO_UTILS_H_
#include <cstdint>
#include "base/files/file.h"
#include "third_party/blink/public/common/common_export.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom-shared.h"
namespace blink {
namespace native_io {
BLINK_COMMON_EXPORT blink::mojom::NativeIOErrorType
FileErrorToNativeIOErrorType(const base::File::Error error);
BLINK_COMMON_EXPORT std::string GetDefaultMessage(
const blink::mojom::NativeIOErrorType nativeio_error);
} // namespace native_io
} // namespace blink
#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_NATIVE_IO_NATIVE_IO_UTILS_H_
...@@ -5,34 +5,6 @@ ...@@ -5,34 +5,6 @@
module blink.mojom; module blink.mojom;
import "mojo/public/mojom/base/file.mojom"; import "mojo/public/mojom/base/file.mojom";
import "mojo/public/mojom/base/file_error.mojom";
// NativeIOErrorTypes are designed to give sufficient information to web
// application developers while masking platform-specific behaviour that may
// create privacy or security issues.
// See https://crbug.com/1095537 for a design doc.
// TODO(rstz): Add error descriptions to the specification.
enum NativeIOErrorType {
kSuccess,
// The operation failed due to an unspecified file error. There is no clear
// path to recovery from this error.
kUnknown,
// The object being operated on was in a permanent invalid state for the
// operation. Retrying is unlikely to succeed.
kInvalidState,
// An object being operated on was not found.
kNotFound,
// An object being operated cannot be modified at this time. Retrying might
// succeed.
kNoModificationAllowed,
// No space on disk is available for this operation.
kNoSpace,
};
struct NativeIOError {
NativeIOErrorType type;
string message;
};
// The NativeIO API currently offers synchronous access to storage. This is // The NativeIO API currently offers synchronous access to storage. This is
// purely for purpose of allowing developers to experiment, so we can learn the // purely for purpose of allowing developers to experiment, so we can learn the
...@@ -73,12 +45,10 @@ interface NativeIOFileHost { ...@@ -73,12 +45,10 @@ interface NativeIOFileHost {
// multiple file handles to the same file. A compromised renderer may hand // multiple file handles to the same file. A compromised renderer may hand
// over an arbitrary file. As base::File::SetLength() is allowed by the // over an arbitrary file. As base::File::SetLength() is allowed by the
// Windows, Linux, and macOS 10.15+ sandbox, it is safe to expose this // Windows, Linux, and macOS 10.15+ sandbox, it is safe to expose this
// functionality to the renderer on macOS < 10.15 as well. We also return // functionality to the renderer on macOS < 10.15 as well. See
// any unsanitized base::File::Error that might have occurred. See
// crbug.com/1084565. // crbug.com/1084565.
[Sync] SetLength(int64 length, mojo_base.mojom.File backing_file) => [Sync] SetLength(int64 length, mojo_base.mojom.File backing_file) => (bool
(mojo_base.mojom.File backing_file, success, mojo_base.mojom.File backing_file);
NativeIOError set_length_error);
}; };
// Origin-scoped implementation of the Web Platform's NativeIO API. // Origin-scoped implementation of the Web Platform's NativeIO API.
...@@ -97,11 +67,11 @@ interface NativeIOHost { ...@@ -97,11 +67,11 @@ interface NativeIOHost {
// |lock_handle|, or until it disconnects from it. // |lock_handle|, or until it disconnects from it.
[Sync] [Sync]
OpenFile(string name, pending_receiver<NativeIOFileHost> file_host_receiver) OpenFile(string name, pending_receiver<NativeIOFileHost> file_host_receiver)
=> (mojo_base.mojom.File? backing_file, NativeIOError open_error); => (mojo_base.mojom.File? backing_file);
// Deletes a previously created file. // Deletes a previously created file.
[Sync] [Sync]
DeleteFile(string name) => (NativeIOError delete_error); DeleteFile(string name) => (bool success);
// Lists all the files created by the origin. // Lists all the files created by the origin.
[Sync] [Sync]
...@@ -112,8 +82,7 @@ interface NativeIOHost { ...@@ -112,8 +82,7 @@ interface NativeIOHost {
// Rename does not allow renaming any files that are currently open and does // Rename does not allow renaming any files that are currently open and does
// not override existing files. // not override existing files.
[Sync] [Sync]
RenameFile(string old_name, string new_name) => RenameFile(string old_name, string new_name) => (bool success);
(NativeIOError rename_error);
// TODO(pwnall): Build quota integration before this API exits Dev Trials. // TODO(pwnall): Build quota integration before this API exits Dev Trials.
}; };
...@@ -8,8 +8,6 @@ blink_modules_sources("native_io") { ...@@ -8,8 +8,6 @@ blink_modules_sources("native_io") {
sources = [ sources = [
"global_native_io.cc", "global_native_io.cc",
"global_native_io.h", "global_native_io.h",
"native_io_error.cc",
"native_io_error.h",
"native_io_file.cc", "native_io_file.cc",
"native_io_file.h", "native_io_file.h",
"native_io_file_sync.cc", "native_io_file_sync.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/native_io/native_io_error.h"
#include "base/files/file.h"
#include "third_party/blink/public/common/native_io/native_io_utils.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
using blink::mojom::blink::NativeIOErrorPtr;
using blink::mojom::blink::NativeIOErrorType;
namespace blink {
namespace {
DOMExceptionCode NativeIOErrorToDOMExceptionCode(NativeIOErrorType error) {
switch (error) {
case NativeIOErrorType::kSuccess:
// This function should only be called with an error.
NOTREACHED();
return DOMExceptionCode::kNoError;
case NativeIOErrorType::kUnknown:
return DOMExceptionCode::kUnknownError;
case NativeIOErrorType::kInvalidState:
return DOMExceptionCode::kInvalidStateError;
case NativeIOErrorType::kNotFound:
return DOMExceptionCode::kNotFoundError;
case NativeIOErrorType::kNoModificationAllowed:
return DOMExceptionCode::kNoModificationAllowedError;
case NativeIOErrorType::kNoSpace:
return DOMExceptionCode::kQuotaExceededError;
}
NOTREACHED();
return DOMExceptionCode::kUnknownError;
}
} // namespace
void RejectNativeIOWithError(ScriptPromiseResolver* resolver,
NativeIOErrorPtr error) {
DCHECK(resolver->GetScriptState()->ContextIsValid())
<< "The resolver's script state must be valid.";
ScriptState* script_state = resolver->GetScriptState();
DOMExceptionCode exception_code =
NativeIOErrorToDOMExceptionCode(error->type);
resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
script_state->GetIsolate(), exception_code, error->message));
return;
}
void RejectNativeIOWithError(ScriptPromiseResolver* resolver,
base::File::Error file_error,
const String& message) {
DCHECK(resolver->GetScriptState()->ContextIsValid())
<< "The resolver's script state must be valid.";
RejectNativeIOWithError(resolver,
FileErrorToNativeIOError(file_error, message));
return;
}
void ThrowNativeIOWithError(ExceptionState& exception_state,
NativeIOErrorPtr error) {
DOMExceptionCode exception_code =
NativeIOErrorToDOMExceptionCode(error->type);
exception_state.ThrowDOMException(exception_code, error->message);
return;
}
void ThrowNativeIOWithError(ExceptionState& exception_state,
base::File::Error file_error,
const String& message) {
ThrowNativeIOWithError(exception_state,
FileErrorToNativeIOError(file_error, message));
return;
}
NativeIOErrorPtr FileErrorToNativeIOError(base::File::Error file_error,
const String& message) {
NativeIOErrorType native_io_error_type =
blink::native_io::FileErrorToNativeIOErrorType(file_error);
String final_message =
message.IsEmpty() ? String::FromUTF8(blink::native_io::GetDefaultMessage(
native_io_error_type)
.c_str())
: message;
return mojom::blink::NativeIOError::New(native_io_error_type, final_message);
}
} // namespace blink
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_ERROR_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_ERROR_H_
#include "base/files/file.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h"
using blink::mojom::blink::NativeIOErrorPtr;
using blink::mojom::blink::NativeIOErrorType;
namespace blink {
class ScriptPromiseResolver;
class ExceptionState;
// Reject the `resolver` with the appropriate DOMException given
// `error`. The resolver's execution context should be valid.
void RejectNativeIOWithError(ScriptPromiseResolver* resolver,
NativeIOErrorPtr error);
// Reject the `resolver` with the appropriate DOMException given `file_error`.
// When no `message` is provided, the default one is chosen. The resolver's
// execution context should be valid.
void RejectNativeIOWithError(ScriptPromiseResolver* resolver,
base::File::Error file_error,
const String& message = String());
// Throw with the appropriate DOMException given `error`.
void ThrowNativeIOWithError(ExceptionState& exception_state,
NativeIOErrorPtr error);
// Throw with the appropriate DOMException given `file_error`. When no `message`
// is provided, the standard one is chosen.
void ThrowNativeIOWithError(ExceptionState& exception_state,
base::File::Error file_error,
const String& message = String());
NativeIOErrorPtr FileErrorToNativeIOError(base::File::Error file_error,
const String& message);
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_NATIVE_IO_NATIVE_IO_ERROR_H_
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h"
#include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
...@@ -22,7 +21,6 @@ ...@@ -22,7 +21,6 @@
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/modules/native_io/native_io_error.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h" #include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
...@@ -126,10 +124,8 @@ ScriptPromise NativeIOFile::getLength(ScriptState* script_state, ...@@ -126,10 +124,8 @@ ScriptPromise NativeIOFile::getLength(ScriptState* script_state,
return ScriptPromise(); return ScriptPromise();
} }
if (closed_) { if (closed_) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return ScriptPromise(); return ScriptPromise();
} }
io_pending_ = true; io_pending_ = true;
...@@ -162,10 +158,8 @@ ScriptPromise NativeIOFile::setLength(ScriptState* script_state, ...@@ -162,10 +158,8 @@ ScriptPromise NativeIOFile::setLength(ScriptState* script_state,
return ScriptPromise(); return ScriptPromise();
} }
if (closed_) { if (closed_) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return ScriptPromise(); return ScriptPromise();
} }
io_pending_ = true; io_pending_ = true;
...@@ -205,10 +199,8 @@ ScriptPromise NativeIOFile::read(ScriptState* script_state, ...@@ -205,10 +199,8 @@ ScriptPromise NativeIOFile::read(ScriptState* script_state,
return ScriptPromise(); return ScriptPromise();
} }
if (closed_) { if (closed_) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return ScriptPromise(); return ScriptPromise();
} }
io_pending_ = true; io_pending_ = true;
...@@ -257,10 +249,8 @@ ScriptPromise NativeIOFile::write(ScriptState* script_state, ...@@ -257,10 +249,8 @@ ScriptPromise NativeIOFile::write(ScriptState* script_state,
return ScriptPromise(); return ScriptPromise();
} }
if (closed_) { if (closed_) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return ScriptPromise(); return ScriptPromise();
} }
io_pending_ = true; io_pending_ = true;
...@@ -304,10 +294,8 @@ ScriptPromise NativeIOFile::flush(ScriptState* script_state, ...@@ -304,10 +294,8 @@ ScriptPromise NativeIOFile::flush(ScriptState* script_state,
return ScriptPromise(); return ScriptPromise();
} }
if (closed_) { if (closed_) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return ScriptPromise(); return ScriptPromise();
} }
io_pending_ = true; io_pending_ = true;
...@@ -403,15 +391,16 @@ void NativeIOFile::DoGetLength( ...@@ -403,15 +391,16 @@ void NativeIOFile::DoGetLength(
NativeIOFile::FileState* file_state, NativeIOFile::FileState* file_state,
scoped_refptr<base::SequencedTaskRunner> resolver_task_runner) { scoped_refptr<base::SequencedTaskRunner> resolver_task_runner) {
DCHECK(!IsMainThread()) << "File I/O should not happen on the main thread"; DCHECK(!IsMainThread()) << "File I/O should not happen on the main thread";
base::File::Error get_length_error; base::File::Error get_length_error = base::File::FILE_OK;
int64_t length = -1; int64_t length = -1;
{ {
WTF::MutexLocker mutex_locker(file_state->mutex); WTF::MutexLocker mutex_locker(file_state->mutex);
DCHECK(file_state->file.IsValid()) DCHECK(file_state->file.IsValid())
<< "file I/O operation queued after file closed"; << "file I/O operation queued after file closed";
length = file_state->file.GetLength(); length = file_state->file.GetLength();
get_length_error = (length < 0) ? file_state->file.GetLastFileError() if (length < 0) {
: base::File::FILE_OK; get_length_error = file_state->file.GetLastFileError();
}
} }
PostCrossThreadTask( PostCrossThreadTask(
...@@ -436,12 +425,14 @@ void NativeIOFile::DidGetLength( ...@@ -436,12 +425,14 @@ void NativeIOFile::DidGetLength(
DispatchQueuedClose(); DispatchQueuedClose();
if (length < 0) { if (length < 0) {
DCHECK_NE(get_length_error, base::File::FILE_OK) DCHECK(get_length_error != base::File::FILE_OK)
<< "Negative length reported with no error set"; << "Negative length reported with no error set";
blink::RejectNativeIOWithError(resolver, get_length_error); resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
script_state->GetIsolate(), DOMExceptionCode::kOperationError,
"getLength() failed"));
return; return;
} }
DCHECK_EQ(get_length_error, base::File::FILE_OK) DCHECK(get_length_error == base::File::FILE_OK)
<< "File error reported when length is nonnegative"; << "File error reported when length is nonnegative";
// getLength returns an unsigned integer, which is different from e.g., // getLength returns an unsigned integer, which is different from e.g.,
// base::File and POSIX. The uses for negative integers are error handling, // base::File and POSIX. The uses for negative integers are error handling,
...@@ -450,10 +441,9 @@ void NativeIOFile::DidGetLength( ...@@ -450,10 +441,9 @@ void NativeIOFile::DidGetLength(
resolver->Resolve(length); resolver->Resolve(length);
} }
void NativeIOFile::DidSetLength( void NativeIOFile::DidSetLength(ScriptPromiseResolver* resolver,
ScriptPromiseResolver* resolver, bool backend_success,
base::File backing_file, base::File backing_file) {
mojom::blink::NativeIOErrorPtr set_length_result) {
DCHECK(backing_file.IsValid()) << "browser returned closed file"; DCHECK(backing_file.IsValid()) << "browser returned closed file";
{ {
WTF::MutexLocker locker(file_state_->mutex); WTF::MutexLocker locker(file_state_->mutex);
...@@ -468,8 +458,10 @@ void NativeIOFile::DidSetLength( ...@@ -468,8 +458,10 @@ void NativeIOFile::DidSetLength(
return; return;
ScriptState::Scope scope(script_state); ScriptState::Scope scope(script_state);
if (set_length_result->type != mojom::blink::NativeIOErrorType::kSuccess) { if (!backend_success) {
blink::RejectNativeIOWithError(resolver, std::move(set_length_result)); resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
script_state->GetIsolate(), DOMExceptionCode::kUnknownError,
"setLength() failed"));
return; return;
} }
...@@ -520,13 +512,11 @@ void NativeIOFile::DidRead( ...@@ -520,13 +512,11 @@ void NativeIOFile::DidRead(
DispatchQueuedClose(); DispatchQueuedClose();
if (read_bytes < 0) { if (read_bytes < 0) {
DCHECK_NE(read_error, base::File::FILE_OK) resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
<< "Negative bytes read reported with no error set"; script_state->GetIsolate(), DOMExceptionCode::kOperationError,
blink::RejectNativeIOWithError(resolver, read_error); "read() failed"));
return; return;
} }
DCHECK_EQ(read_error, base::File::FILE_OK)
<< "Error set but positive number of bytes read.";
resolver->Resolve(read_bytes); resolver->Resolve(read_bytes);
} }
...@@ -574,13 +564,11 @@ void NativeIOFile::DidWrite( ...@@ -574,13 +564,11 @@ void NativeIOFile::DidWrite(
DispatchQueuedClose(); DispatchQueuedClose();
if (written_bytes < 0) { if (written_bytes < 0) {
DCHECK_NE(write_error, base::File::FILE_OK) resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
<< "Negative bytes written reported with no error set"; script_state->GetIsolate(), DOMExceptionCode::kOperationError,
blink::RejectNativeIOWithError(resolver, write_error); "write() failed"));
return; return;
} }
DCHECK_EQ(write_error, base::File::FILE_OK);
resolver->Resolve(written_bytes); resolver->Resolve(written_bytes);
} }
...@@ -591,25 +579,23 @@ void NativeIOFile::DoFlush( ...@@ -591,25 +579,23 @@ void NativeIOFile::DoFlush(
NativeIOFile::FileState* file_state, NativeIOFile::FileState* file_state,
scoped_refptr<base::SequencedTaskRunner> resolver_task_runner) { scoped_refptr<base::SequencedTaskRunner> resolver_task_runner) {
DCHECK(!IsMainThread()) << "File I/O should not happen on the main thread"; DCHECK(!IsMainThread()) << "File I/O should not happen on the main thread";
base::File::Error flush_error; bool success = false;
{ {
WTF::MutexLocker mutex_locker(file_state->mutex); WTF::MutexLocker mutex_locker(file_state->mutex);
DCHECK(file_state->file.IsValid()) DCHECK(file_state->file.IsValid())
<< "file I/O operation queued after file closed"; << "file I/O operation queued after file closed";
bool success = file_state->file.Flush(); success = file_state->file.Flush();
flush_error =
success ? base::File::FILE_OK : file_state->file.GetLastFileError();
} }
PostCrossThreadTask( PostCrossThreadTask(
*resolver_task_runner, FROM_HERE, *resolver_task_runner, FROM_HERE,
CrossThreadBindOnce(&NativeIOFile::DidFlush, std::move(native_io_file), CrossThreadBindOnce(&NativeIOFile::DidFlush, std::move(native_io_file),
std::move(resolver), flush_error)); std::move(resolver), success));
} }
void NativeIOFile::DidFlush( void NativeIOFile::DidFlush(
CrossThreadPersistent<ScriptPromiseResolver> resolver, CrossThreadPersistent<ScriptPromiseResolver> resolver,
base::File::Error flush_error) { bool success) {
ScriptState* script_state = resolver->GetScriptState(); ScriptState* script_state = resolver->GetScriptState();
if (!script_state->ContextIsValid()) if (!script_state->ContextIsValid())
return; return;
...@@ -620,8 +606,10 @@ void NativeIOFile::DidFlush( ...@@ -620,8 +606,10 @@ void NativeIOFile::DidFlush(
DispatchQueuedClose(); DispatchQueuedClose();
if (flush_error != base::File::FILE_OK) { if (!success) {
blink::RejectNativeIOWithError(resolver, flush_error); resolver->Reject(V8ThrowDOMException::CreateOrEmpty(
script_state->GetIsolate(), DOMExceptionCode::kOperationError,
"flush() failed"));
return; return;
} }
resolver->Resolve(); resolver->Resolve();
......
...@@ -98,8 +98,8 @@ class NativeIOFile final : public ScriptWrappable { ...@@ -98,8 +98,8 @@ class NativeIOFile final : public ScriptWrappable {
// Performs the post file I/O part of setLength(), on the main thread. // Performs the post file I/O part of setLength(), on the main thread.
void DidSetLength(ScriptPromiseResolver* resolver, void DidSetLength(ScriptPromiseResolver* resolver,
base::File backing_file, bool backend_success,
mojom::blink::NativeIOErrorPtr set_length_result); base::File backing_file);
// Performs the file I/O part of read(), off the main thread. // Performs the file I/O part of read(), off the main thread.
static void DoRead( static void DoRead(
...@@ -139,7 +139,7 @@ class NativeIOFile final : public ScriptWrappable { ...@@ -139,7 +139,7 @@ class NativeIOFile final : public ScriptWrappable {
scoped_refptr<base::SequencedTaskRunner> file_task_runner); scoped_refptr<base::SequencedTaskRunner> file_task_runner);
// Performs the post file-I/O part of flush(), on the main thread. // Performs the post file-I/O part of flush(), on the main thread.
void DidFlush(CrossThreadPersistent<ScriptPromiseResolver> resolver, void DidFlush(CrossThreadPersistent<ScriptPromiseResolver> resolver,
base::File::Error flush_error); bool success);
// Kicks off closing the file from the main thread. // Kicks off closing the file from the main thread.
void CloseBackingFile(); void CloseBackingFile();
......
...@@ -7,12 +7,10 @@ ...@@ -7,12 +7,10 @@
#include <limits> #include <limits>
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "third_party/blink/public/mojom/native_io/native_io.mojom-blink.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h" #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
#include "third_party/blink/renderer/modules/native_io/native_io_error.h"
#include "third_party/blink/renderer/modules/native_io/native_io_file.h" #include "third_party/blink/renderer/modules/native_io/native_io_file.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h" #include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h"
...@@ -53,15 +51,14 @@ void NativeIOFileSync::close() { ...@@ -53,15 +51,14 @@ void NativeIOFileSync::close() {
uint64_t NativeIOFileSync::getLength(ExceptionState& exception_state) { uint64_t NativeIOFileSync::getLength(ExceptionState& exception_state) {
if (!backing_file_.IsValid()) { if (!backing_file_.IsValid()) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"NativeIOHost backend went away"));
return 0; return 0;
} }
int64_t length = backing_file_.GetLength(); int64_t length = backing_file_.GetLength();
if (length < 0) { if (length < 0) {
ThrowNativeIOWithError(exception_state, backing_file_.GetLastFileError()); exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
"getLength() failed");
return 0; return 0;
} }
// getLength returns an unsigned integer, which is different from e.g., // getLength returns an unsigned integer, which is different from e.g.,
...@@ -78,13 +75,11 @@ void NativeIOFileSync::setLength(uint64_t length, ...@@ -78,13 +75,11 @@ void NativeIOFileSync::setLength(uint64_t length,
return; return;
} }
if (!backing_file_.IsValid()) { if (!backing_file_.IsValid()) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"NativeIOHost backend went away"));
return; return;
} }
mojom::blink::NativeIOErrorPtr set_length_result; bool backend_success = false;
// Calls to setLength are routed through the browser process, see // Calls to setLength are routed through the browser process, see
// crbug.com/1084565. // crbug.com/1084565.
...@@ -92,10 +87,11 @@ void NativeIOFileSync::setLength(uint64_t length, ...@@ -92,10 +87,11 @@ void NativeIOFileSync::setLength(uint64_t length,
// We keep a single handle per file, so this handle is passed to the backend // We keep a single handle per file, so this handle is passed to the backend
// and is then given back to the renderer afterwards. // and is then given back to the renderer afterwards.
backend_file_->SetLength(base::as_signed(length), std::move(backing_file_), backend_file_->SetLength(base::as_signed(length), std::move(backing_file_),
&backing_file_, &set_length_result); &backend_success, &backing_file_);
DCHECK(backing_file_.IsValid()) << "browser returned closed file"; DCHECK(backing_file_.IsValid()) << "browser returned closed file";
if (set_length_result->type != mojom::blink::NativeIOErrorType::kSuccess) { if (!backend_success) {
ThrowNativeIOWithError(exception_state, std::move(set_length_result)); exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
"setLength() failed");
} }
return; return;
} }
...@@ -106,16 +102,14 @@ uint64_t NativeIOFileSync::read(MaybeShared<DOMArrayBufferView> buffer, ...@@ -106,16 +102,14 @@ uint64_t NativeIOFileSync::read(MaybeShared<DOMArrayBufferView> buffer,
int read_size = OperationSize(*buffer.View()); int read_size = OperationSize(*buffer.View());
char* read_data = static_cast<char*>(buffer.View()->BaseAddressMaybeShared()); char* read_data = static_cast<char*>(buffer.View()->BaseAddressMaybeShared());
if (!backing_file_.IsValid()) { if (!backing_file_.IsValid()) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return 0; return 0;
} }
int read_bytes = backing_file_.Read(file_offset, read_data, read_size); int read_bytes = backing_file_.Read(file_offset, read_data, read_size);
if (read_bytes < 0) { if (read_bytes < 0) {
ThrowNativeIOWithError(exception_state, backing_file_.GetLastFileError()); exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
return 0; "read() failed");
} }
return base::as_unsigned(read_bytes); return base::as_unsigned(read_bytes);
} }
...@@ -127,16 +121,14 @@ uint64_t NativeIOFileSync::write(MaybeShared<DOMArrayBufferView> buffer, ...@@ -127,16 +121,14 @@ uint64_t NativeIOFileSync::write(MaybeShared<DOMArrayBufferView> buffer,
char* write_data = char* write_data =
static_cast<char*>(buffer.View()->BaseAddressMaybeShared()); static_cast<char*>(buffer.View()->BaseAddressMaybeShared());
if (!backing_file_.IsValid()) { if (!backing_file_.IsValid()) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return 0; return 0;
} }
int written_bytes = backing_file_.Write(file_offset, write_data, write_size); int written_bytes = backing_file_.Write(file_offset, write_data, write_size);
if (written_bytes < 0) { if (written_bytes < 0) {
ThrowNativeIOWithError(exception_state, backing_file_.GetLastFileError()); exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
return 0; "write() failed");
} }
return base::as_unsigned(written_bytes); return base::as_unsigned(written_bytes);
} }
...@@ -145,16 +137,14 @@ void NativeIOFileSync::flush(ExceptionState& exception_state) { ...@@ -145,16 +137,14 @@ void NativeIOFileSync::flush(ExceptionState& exception_state) {
// This implementation of flush attempts to physically store the data it has // This implementation of flush attempts to physically store the data it has
// written on disk. This behaviour might change in the future. // written on disk. This behaviour might change in the future.
if (!backing_file_.IsValid()) { if (!backing_file_.IsValid()) {
ThrowNativeIOWithError(exception_state, exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
mojom::blink::NativeIOError::New( "The file was already closed");
mojom::blink::NativeIOErrorType::kInvalidState,
"The file was already closed"));
return; return;
} }
bool success = backing_file_.Flush(); bool success = backing_file_.Flush();
if (!success) { if (!success) {
ThrowNativeIOWithError(exception_state, backing_file_.GetLastFileError()); exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
return; "flush() failed");
} }
return; return;
} }
......
...@@ -18,8 +18,3 @@ promise_test(async testCase => { ...@@ -18,8 +18,3 @@ promise_test(async testCase => {
assert_equals(fileNames.indexOf('test_file'), -1); assert_equals(fileNames.indexOf('test_file'), -1);
}, 'nativeIO.getAll does not return file deleted by nativeIO.delete'); }, 'nativeIO.getAll does not return file deleted by nativeIO.delete');
promise_test(async testCase => {
await nativeIO.delete('test_file');
// Delete a second time if the file existed before the first delete.
await nativeIO.delete('test_file');
}, 'nativeIO.delete does not fail when deleting a non-existing file');
...@@ -17,9 +17,3 @@ test(testCase => { ...@@ -17,9 +17,3 @@ test(testCase => {
const fileNames = nativeIO.getAllSync(); const fileNames = nativeIO.getAllSync();
assert_equals(fileNames.indexOf('test_file'), -1); assert_equals(fileNames.indexOf('test_file'), -1);
}, 'nativeIO.getAllSync does not return file deleted by nativeIO.deleteSync'); }, 'nativeIO.getAllSync does not return file deleted by nativeIO.deleteSync');
test(testCase => {
nativeIO.deleteSync('test_file');
// Delete a second time if the file existed before the first delete.
nativeIO.deleteSync('test_file');
}, 'nativeIO.deleteSync does not fail when deleting a non-existing file');
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
'use strict'; 'use strict';
setup(async () => { setup(async () => {
assert_implements(nativeIO.rename, 'nativeIO.rename is not implemented.'); assert_implements(nativeIO.rename,
"nativeIO.rename is not implemented.");
}); });
promise_test(async testCase => { promise_test(async testCase => {
...@@ -28,9 +29,8 @@ promise_test(async testCase => { ...@@ -28,9 +29,8 @@ promise_test(async testCase => {
await file1.close(); await file1.close();
await file2.close(); await file2.close();
await promise_rejects_dom( await promise_rejects_dom(testCase, "UnknownError",
testCase, 'NoModificationAllowedError', nativeIO.rename('test_file_1', 'test_file_2'));
nativeIO.rename('test_file_1', 'test_file_2'));
const fileNamesAfterRename = await nativeIO.getAll(); const fileNamesAfterRename = await nativeIO.getAll();
assert_in_array('test_file_1', fileNamesAfterRename); assert_in_array('test_file_1', fileNamesAfterRename);
...@@ -53,12 +53,10 @@ promise_test(async testCase => { ...@@ -53,12 +53,10 @@ promise_test(async testCase => {
const readSharedArrayBuffer2 = new SharedArrayBuffer(writtenBytes2.length); const readSharedArrayBuffer2 = new SharedArrayBuffer(writtenBytes2.length);
const readBytes2 = new Uint8Array(readSharedArrayBuffer2); const readBytes2 = new Uint8Array(readSharedArrayBuffer2);
await file2_after.read(readBytes2, 0); await file2_after.read(readBytes2, 0);
assert_array_equals( assert_array_equals(readBytes1, writtenBytes1,
readBytes1, writtenBytes1, 'the bytes read should match the bytes written');
'the bytes read should match the bytes written'); assert_array_equals(readBytes2, writtenBytes2,
assert_array_equals( 'the bytes read should match the bytes written');
readBytes2, writtenBytes2,
'the bytes read should match the bytes written');
}, 'nativeIO.rename does not overwrite an existing file.'); }, 'nativeIO.rename does not overwrite an existing file.');
promise_test(async testCase => { promise_test(async testCase => {
...@@ -67,9 +65,8 @@ promise_test(async testCase => { ...@@ -67,9 +65,8 @@ promise_test(async testCase => {
await file.close(); await file.close();
await nativeIO.delete('test_file'); await nativeIO.delete('test_file');
}); });
await promise_rejects_dom( await promise_rejects_dom(testCase, "UnknownError",
testCase, 'NoModificationAllowedError', nativeIO.rename('test_file', 'renamed_test_file'));
nativeIO.rename('test_file', 'renamed_test_file'));
await file.close(); await file.close();
const fileNamesAfterRename = await nativeIO.getAll(); const fileNamesAfterRename = await nativeIO.getAll();
...@@ -89,10 +86,10 @@ promise_test(async testCase => { ...@@ -89,10 +86,10 @@ promise_test(async testCase => {
const file = await nativeIO.open('test_file'); const file = await nativeIO.open('test_file');
await file.close(); await file.close();
for (let name of kBadNativeIoNames) { for (let name of kBadNativeIoNames) {
await promise_rejects_js( await promise_rejects_js(testCase, TypeError,
testCase, TypeError, nativeIO.rename('test_file', name)); nativeIO.rename('test_file', name));
await promise_rejects_js( await promise_rejects_js(testCase, TypeError,
testCase, TypeError, nativeIO.rename(name, 'test_file_2')); nativeIO.rename(name, 'test_file_2'));
} }
}, 'nativeIO.rename does not allow renaming from or to invalid names.'); }, 'nativeIO.rename does not allow renaming from or to invalid names.');
...@@ -108,13 +105,11 @@ promise_test(async testCase => { ...@@ -108,13 +105,11 @@ promise_test(async testCase => {
}); });
// First rename fails, as source is still open. // First rename fails, as source is still open.
await promise_rejects_dom( await promise_rejects_dom(testCase, "UnknownError",
testCase, 'NoModificationAllowedError', nativeIO.rename('opened_file', 'closed_file'));
nativeIO.rename('opened_file', 'closed_file'));
// First rename fails again, as source has not been unlocked. // First rename fails again, as source has not been unlocked.
await promise_rejects_dom( await promise_rejects_dom(testCase, "UnknownError",
testCase, 'NoModificationAllowedError', nativeIO.rename('opened_file', 'closed_file'));
nativeIO.rename('opened_file', 'closed_file'));
}, 'Failed nativeIO.rename does not unlock the source.'); }, 'Failed nativeIO.rename does not unlock the source.');
promise_test(async testCase => { promise_test(async testCase => {
...@@ -129,22 +124,9 @@ promise_test(async testCase => { ...@@ -129,22 +124,9 @@ promise_test(async testCase => {
}); });
// First rename fails, as destination is still open. // First rename fails, as destination is still open.
await promise_rejects_dom( await promise_rejects_dom(testCase, "UnknownError",
testCase, 'NoModificationAllowedError', nativeIO.rename('closed_file', 'opened_file'));
nativeIO.rename('closed_file', 'opened_file'));
// First rename fails again, as destination has not been unlocked. // First rename fails again, as destination has not been unlocked.
await promise_rejects_dom( await promise_rejects_dom(testCase, "UnknownError",
testCase, 'NoModificationAllowedError', nativeIO.rename('closed_file', 'opened_file'));
nativeIO.rename('closed_file', 'opened_file'));
}, 'Failed nativeIO.rename does not unlock the destination.'); }, 'Failed nativeIO.rename does not unlock the destination.');
promise_test(async testCase => {
// Make sure that the file does not exist.
await nativeIO.delete('does_not_exist');
testCase.add_cleanup(async () => {
await nativeIO.delete('new_does_not_exist');
});
promise_rejects_dom(
testCase, 'NotFoundError',
nativeIO.rename('does_not_exist', 'new_does_not_exist'));
}, 'Renaming a non-existing file fails with a NotFoundError.');
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
setup(() => { setup(() => {
// Without this assertion, one test passes even if renameSync is not defined // Without this assertion, one test passes even if renameSync is not defined
assert_implements( assert_implements(nativeIO.renameSync,
nativeIO.renameSync, 'nativeIO.renameSync is not implemented.'); "nativeIO.renameSync is not implemented.");
}); });
test(testCase => { test(testCase => {
...@@ -26,9 +26,8 @@ test(testCase => { ...@@ -26,9 +26,8 @@ test(testCase => {
file1.close(); file1.close();
file2.close(); file2.close();
assert_throws_dom( assert_throws_dom("UnknownError",
'NoModificationAllowedError', () => nativeIO.renameSync('test_file_1', 'test_file_2'));
() => nativeIO.renameSync('test_file_1', 'test_file_2'));
const fileNamesAfterRename = nativeIO.getAllSync(); const fileNamesAfterRename = nativeIO.getAllSync();
assert_in_array('test_file_1', fileNamesAfterRename); assert_in_array('test_file_1', fileNamesAfterRename);
...@@ -46,14 +45,12 @@ test(testCase => { ...@@ -46,14 +45,12 @@ test(testCase => {
}); });
const readBytes1 = new Uint8Array(writtenBytes1.length); const readBytes1 = new Uint8Array(writtenBytes1.length);
file1_after.read(readBytes1, 0); file1_after.read(readBytes1, 0);
assert_array_equals( assert_array_equals(readBytes1, writtenBytes1,
readBytes1, writtenBytes1, 'the bytes read should match the bytes written');
'the bytes read should match the bytes written');
const readBytes2 = new Uint8Array(writtenBytes2.length); const readBytes2 = new Uint8Array(writtenBytes2.length);
file2_after.read(readBytes2, 0); file2_after.read(readBytes2, 0);
assert_array_equals( assert_array_equals(readBytes2, writtenBytes2,
readBytes2, writtenBytes2, 'the bytes read should match the bytes written');
'the bytes read should match the bytes written');
}, 'nativeIO.renameSync does not overwrite an existing file.'); }, 'nativeIO.renameSync does not overwrite an existing file.');
test(testCase => { test(testCase => {
...@@ -62,9 +59,8 @@ test(testCase => { ...@@ -62,9 +59,8 @@ test(testCase => {
file.close(); file.close();
nativeIO.deleteSync('test_file'); nativeIO.deleteSync('test_file');
}); });
assert_throws_dom( assert_throws_dom("UnknownError", () =>
'NoModificationAllowedError', nativeIO.renameSync('test_file', 'renamed_test_file'));
() => nativeIO.renameSync('test_file', 'renamed_test_file'));
file.close(); file.close();
const fileNamesAfterRename = nativeIO.getAllSync(); const fileNamesAfterRename = nativeIO.getAllSync();
...@@ -101,13 +97,11 @@ test(testCase => { ...@@ -101,13 +97,11 @@ test(testCase => {
}); });
// First rename fails, as source is still open. // First rename fails, as source is still open.
assert_throws_dom( assert_throws_dom("UnknownError",
'NoModificationAllowedError', () => nativeIO.renameSync('opened_file', 'closed_file'));
() => nativeIO.renameSync('opened_file', 'closed_file'));
// First rename fails again, as source has not been unlocked. // First rename fails again, as source has not been unlocked.
assert_throws_dom( assert_throws_dom("UnknownError",
'NoModificationAllowedError', () => nativeIO.renameSync('opened_file', 'closed_file'));
() => nativeIO.renameSync('opened_file', 'closed_file'));
}, 'Failed nativeIO.renameSync does not unlock the source.'); }, 'Failed nativeIO.renameSync does not unlock the source.');
test(testCase => { test(testCase => {
...@@ -122,22 +116,9 @@ test(testCase => { ...@@ -122,22 +116,9 @@ test(testCase => {
}); });
// First rename fails, as destination is still open. // First rename fails, as destination is still open.
assert_throws_dom( assert_throws_dom("UnknownError",
'NoModificationAllowedError', () => nativeIO.renameSync('closed_file', 'opened_file'));
() => nativeIO.renameSync('closed_file', 'opened_file'));
// First rename fails again, as destination has not been unlocked. // First rename fails again, as destination has not been unlocked.
assert_throws_dom( assert_throws_dom("UnknownError",
'NoModificationAllowedError', () => nativeIO.renameSync('closed_file', 'opened_file'));
() => nativeIO.renameSync('closed_file', 'opened_file'));
}, 'Failed nativeIO.renameSync does not unlock the destination.'); }, 'Failed nativeIO.renameSync does not unlock the destination.');
test(testCase => {
// Make sure that the file does not exist.
nativeIO.deleteSync('does_not_exist');
testCase.add_cleanup(() => {
nativeIO.deleteSync('new_name');
});
assert_throws_dom(
'NotFoundError',
() => nativeIO.renameSync('does_not_exist', 'new_name'));
}, 'Renaming a non-existing file fails with a NotFoundError.');
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment