Commit 56cd97e7 authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

Make the Native File System API work with special volumes on Chrome OS.

At a high level, this enables creating handles from either local paths
or external paths. An external path is a path that can be used as
virtual path in a kFileSystemTypeExternal file system URL. These
external paths are intentionally not wrapped in an isolated file system,
as doing so would make it much harder to keep track of this virtual path,
and we need the virtual path to be able to serialize these handles to
IndexedDB.

(local paths are still wrapped in an isolated file system, although some
of the previous CLs that enabled not using one in this case has made
that no longer needed either. In the future I hope to be able to
eliminate/replace isolated file systems entirely).

This adds a PathType enum/parameter to CreateFile/DirectoryEntryFromPath
methods in NFSEntryFactory/NFSManagerImpl. This parameter makes it clear
if the path is a local path or an external path.

And changes the result FileSystemChooser returns from simply being a
base::FilePath to being a combination of a FilePath and a PathType.
This is then used by NFSManagerImpl to create the right type of
FileSystemURL/entry.

Currently all the permissions code still just deals with raw
base::FilePaths without indication if a path is a virtual/external or
local path. This shouldn't matter, since on ChromeOS (the only place
we use external paths) these paths will never overlap anyway, but we
might want to revisit this in the future.

This could have been implemented by having FileSystemChooser return the
appropriate FileSystemURL directly (and changing the NFSEntryFactory API
to be in terms of FileSystemURL); that was not done because in my ideal
end state FileSystemURL wouldn't be used anywhere in the native file
system code. I.e. it would purely be an implementation detail, but the
interface between the native file system code and file system backends
wouldn't depend on it anymore. Still working out the details for that.

Bug: 1093653
Change-Id: I201703d427a5368e67b0410428f366d1dca91aa8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2354596Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarTrent Apted <tapted@chromium.org>
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810080}
parent b9f14c9f
......@@ -33,6 +33,7 @@
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/child_process_security_policy.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "ui/shell_dialogs/selected_file_info.h"
......@@ -147,6 +148,23 @@ void GetSelectedFileInfoInternal(
params->selected_files.emplace_back(file_path, file_path);
}
}
// Populate the virtual path for any files on a external mount point. This
// lets consumers that are capable of using a virtual path use this rather
// than file_path, which can make certain operations more efficient.
for (auto& file_info : params->selected_files) {
auto* external_mount_points =
storage::ExternalMountPoints::GetSystemInstance();
base::FilePath virtual_path;
if (external_mount_points->GetVirtualPath(file_info.file_path,
&virtual_path)) {
file_info.virtual_path.emplace(std::move(virtual_path));
} else {
LOG(ERROR) << "Failed to get external virtual path: "
<< file_info.file_path;
}
}
std::move(params->callback).Run(params->selected_files);
}
......
......@@ -18,6 +18,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_user_data.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom.h"
#include "url/origin.h"
......@@ -69,6 +70,25 @@ void WebLaunchFilesHelper::DidFinishNavigation(
namespace {
// On Chrome OS paths that exist on an external mount point need to be treated
// differently to make sure the native file system code accesses these paths via
// the correct file system backend. This method checks if this is the case, and
// updates `entry_path` to the path that should be used by the native file
// system implementation.
content::NativeFileSystemEntryFactory::PathType MaybeRemapPath(
base::FilePath* entry_path) {
#if defined(OS_CHROMEOS)
base::FilePath virtual_path;
auto* external_mount_points =
storage::ExternalMountPoints::GetSystemInstance();
if (external_mount_points->GetVirtualPath(*entry_path, &virtual_path)) {
*entry_path = std::move(virtual_path);
return content::NativeFileSystemEntryFactory::PathType::kExternal;
}
#endif
return content::NativeFileSystemEntryFactory::PathType::kLocal;
}
class EntriesBuilder {
public:
EntriesBuilder(
......@@ -87,13 +107,19 @@ class EntriesBuilder {
web_contents->GetMainFrame()->GetRoutingID())) {}
void AddFileEntry(const base::FilePath& path) {
base::FilePath entry_path = path;
content::NativeFileSystemEntryFactory::PathType path_type =
MaybeRemapPath(&entry_path);
entries_ref_->push_back(entry_factory_->CreateFileEntryFromPath(
context_, path,
context_, path_type, entry_path,
content::NativeFileSystemEntryFactory::UserAction::kOpen));
}
void AddDirectoryEntry(const base::FilePath& path) {
base::FilePath entry_path = path;
content::NativeFileSystemEntryFactory::PathType path_type =
MaybeRemapPath(&entry_path);
entries_ref_->push_back(entry_factory_->CreateDirectoryEntryFromPath(
context_, path,
context_, path_type, entry_path,
content::NativeFileSystemEntryFactory::UserAction::kOpen));
}
......
......@@ -18,8 +18,8 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "net/base/mime_util.h"
#include "storage/browser/file_system/isolated_context.h"
#include "ui/shell_dialogs/select_file_policy.h"
#include "ui/shell_dialogs/selected_file_info.h"
namespace content {
......@@ -123,6 +123,8 @@ ui::SelectFileDialog::FileTypeInfo ConvertAcceptsToFileTypeInfo(
if (file_types.extensions.empty())
file_types.include_all_files = true;
file_types.allowed_paths = ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
return file_types;
}
......@@ -207,11 +209,34 @@ void FileSystemChooser::FileSelected(const base::FilePath& path,
void FileSystemChooser::MultiFilesSelected(
const std::vector<base::FilePath>& files,
void* params) {
auto* isolated_context = storage::IsolatedContext::GetInstance();
DCHECK(isolated_context);
MultiFilesSelectedWithExtraInfo(ui::FilePathListToSelectedFileInfoList(files),
params);
}
void FileSystemChooser::FileSelectedWithExtraInfo(
const ui::SelectedFileInfo& file,
int index,
void* params) {
MultiFilesSelectedWithExtraInfo({file}, params);
}
void FileSystemChooser::MultiFilesSelectedWithExtraInfo(
const std::vector<ui::SelectedFileInfo>& files,
void* params) {
std::vector<ResultEntry> result;
for (const ui::SelectedFileInfo& file : files) {
if (file.virtual_path.has_value()) {
result.push_back({PathType::kExternal, *file.virtual_path});
} else {
result.push_back({PathType::kLocal, file.local_path.empty()
? file.file_path
: file.local_path});
}
}
RecordFileSelectionResult(type_, files.size());
std::move(callback_).Run(native_file_system_error::Ok(), std::move(files));
RecordFileSelectionResult(type_, result.size());
std::move(callback_).Run(native_file_system_error::Ok(), std::move(result));
delete this;
}
......@@ -220,7 +245,7 @@ void FileSystemChooser::FileSelectionCanceled(void* params) {
std::move(callback_).Run(
native_file_system_error::FromStatus(
blink::mojom::NativeFileSystemStatus::kOperationAborted),
std::vector<base::FilePath>());
{});
delete this;
}
......
......@@ -10,6 +10,7 @@
#include "base/files/file_path.h"
#include "base/task_runner.h"
#include "content/common/content_export.h"
#include "content/public/browser/native_file_system_entry_factory.h"
#include "storage/browser/file_system/isolated_context.h"
#include "third_party/blink/public/mojom/native_file_system/native_file_system_manager.mojom.h"
#include "ui/shell_dialogs/select_file_dialog.h"
......@@ -25,9 +26,15 @@ class WebContents;
// All of this class has to be called on the UI thread.
class CONTENT_EXPORT FileSystemChooser : public ui::SelectFileDialog::Listener {
public:
using PathType = NativeFileSystemEntryFactory::PathType;
struct ResultEntry {
PathType type;
base::FilePath path;
};
using ResultCallback =
base::OnceCallback<void(blink::mojom::NativeFileSystemErrorPtr,
std::vector<base::FilePath>)>;
std::vector<ResultEntry>)>;
class CONTENT_EXPORT Options {
public:
......@@ -68,6 +75,12 @@ class CONTENT_EXPORT FileSystemChooser : public ui::SelectFileDialog::Listener {
void* params) override;
void MultiFilesSelected(const std::vector<base::FilePath>& files,
void* params) override;
void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& file,
int index,
void* params) override;
void MultiFilesSelectedWithExtraInfo(
const std::vector<ui::SelectedFileInfo>& files,
void* params) override;
void FileSelectionCanceled(void* params) override;
ResultCallback callback_;
......
......@@ -25,11 +25,13 @@
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "ui/shell_dialogs/select_file_dialog_factory.h"
#include "ui/shell_dialogs/select_file_policy.h"
#include "ui/shell_dialogs/selected_file_info.h"
namespace content {
......@@ -38,6 +40,8 @@ using blink::mojom::PermissionStatus;
using SensitiveDirectoryResult =
NativeFileSystemPermissionContext::SensitiveDirectoryResult;
static constexpr char kTestMountPoint[] = "testfs";
// This browser test implements end-to-end tests for the file picker
// APIs.
class FileSystemChooserBrowserTest : public ContentBrowserTest {
......@@ -47,6 +51,14 @@ class FileSystemChooserBrowserTest : public ContentBrowserTest {
scoped_feature_list_.InitAndEnableFeature(
blink::features::kNativeFileSystemAPI);
// Register an external mount point to test support for virtual paths.
// This maps the virtual path a native local path to make these tests work
// on all platforms. We're not testing more complicated ChromeOS specific
// file system backends here.
storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
kTestMountPoint, storage::kFileSystemTypeNativeLocal,
storage::FileSystemMountOption(), temp_dir_.GetPath());
ASSERT_TRUE(embedded_test_server()->Start());
ContentBrowserTest::SetUp();
......@@ -60,8 +72,10 @@ class FileSystemChooserBrowserTest : public ContentBrowserTest {
void TearDown() override {
ContentBrowserTest::TearDown();
ASSERT_TRUE(temp_dir_.Delete());
storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
kTestMountPoint);
ui::SelectFileDialog::SetFactory(nullptr);
ASSERT_TRUE(temp_dir_.Delete());
}
bool IsFullscreen() {
......@@ -207,6 +221,37 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
EXPECT_EQ(ui::SelectFileDialog::SELECT_NONE, dialog_params.type);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenFile_ExternalPath) {
const std::string file_contents = "hello world!";
const base::FilePath test_file = CreateTestFile(file_contents);
const base::FilePath virtual_path =
base::FilePath::FromUTF8Unsafe(kTestMountPoint)
.Append(test_file.BaseName());
ui::SelectedFileInfo selected_file = {base::FilePath(), base::FilePath()};
selected_file.virtual_path = virtual_path;
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({selected_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(virtual_path.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let [e] = await self.showOpenFilePicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_OPEN_FILE, dialog_params.type);
EXPECT_EQ(shell()->web_contents()->GetTopLevelNativeWindow(),
dialog_params.owning_window);
EXPECT_EQ(
file_contents,
EvalJs(shell(),
"(async () => { const file = await self.selected_entry.getFile(); "
"return await file.text(); })()"));
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SaveFile_NonExistingFile) {
const std::string file_contents = "file contents to write";
const base::FilePath test_file = CreateTestFile("");
......
......@@ -4,6 +4,8 @@
#include "content/browser/native_file_system/file_system_chooser_test_helpers.h"
#include "ui/shell_dialogs/selected_file_info.h"
namespace content {
namespace {
......@@ -50,7 +52,7 @@ class CancellingSelectFileDialog : public ui::SelectFileDialog {
class FakeSelectFileDialog : public ui::SelectFileDialog {
public:
FakeSelectFileDialog(std::vector<base::FilePath> result,
FakeSelectFileDialog(std::vector<ui::SelectedFileInfo> result,
SelectFileDialogParams* out_params,
Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy)
......@@ -77,9 +79,9 @@ class FakeSelectFileDialog : public ui::SelectFileDialog {
out_params_->file_type_index = file_type_index;
}
if (result_.size() == 1)
listener_->FileSelected(result_[0], 0, params);
listener_->FileSelectedWithExtraInfo(result_[0], 0, params);
else
listener_->MultiFilesSelected(result_, params);
listener_->MultiFilesSelectedWithExtraInfo(result_, params);
}
bool IsRunning(gfx::NativeWindow owning_window) const override {
......@@ -90,7 +92,7 @@ class FakeSelectFileDialog : public ui::SelectFileDialog {
private:
~FakeSelectFileDialog() override = default;
std::vector<base::FilePath> result_;
std::vector<ui::SelectedFileInfo> result_;
SelectFileDialogParams* out_params_;
};
......@@ -116,6 +118,13 @@ ui::SelectFileDialog* CancellingSelectFileDialogFactory::Create(
FakeSelectFileDialogFactory::FakeSelectFileDialogFactory(
std::vector<base::FilePath> result,
SelectFileDialogParams* out_params)
: FakeSelectFileDialogFactory(
ui::FilePathListToSelectedFileInfoList(result),
out_params) {}
FakeSelectFileDialogFactory::FakeSelectFileDialogFactory(
std::vector<ui::SelectedFileInfo> result,
SelectFileDialogParams* out_params)
: result_(std::move(result)), out_params_(out_params) {}
FakeSelectFileDialogFactory::~FakeSelectFileDialogFactory() = default;
......
......@@ -45,8 +45,12 @@ class CancellingSelectFileDialogFactory : public ui::SelectFileDialogFactory {
// files.
class FakeSelectFileDialogFactory : public ui::SelectFileDialogFactory {
public:
FakeSelectFileDialogFactory(std::vector<base::FilePath> result,
SelectFileDialogParams* out_params = nullptr);
explicit FakeSelectFileDialogFactory(
std::vector<base::FilePath> result,
SelectFileDialogParams* out_params = nullptr);
explicit FakeSelectFileDialogFactory(
std::vector<ui::SelectedFileInfo> result,
SelectFileDialogParams* out_params = nullptr);
~FakeSelectFileDialogFactory() override;
ui::SelectFileDialog* Create(
......@@ -54,7 +58,7 @@ class FakeSelectFileDialogFactory : public ui::SelectFileDialogFactory {
std::unique_ptr<ui::SelectFilePolicy> policy) override;
private:
std::vector<base::FilePath> result_;
std::vector<ui::SelectedFileInfo> result_;
SelectFileDialogParams* out_params_;
};
......
......@@ -16,6 +16,7 @@
#include "ui/shell_dialogs/select_file_dialog.h"
#include "ui/shell_dialogs/select_file_dialog_factory.h"
#include "ui/shell_dialogs/select_file_policy.h"
#include "ui/shell_dialogs/selected_file_info.h"
namespace content {
......@@ -23,10 +24,11 @@ class FileSystemChooserTest : public testing::Test {
public:
void TearDown() override { ui::SelectFileDialog::SetFactory(nullptr); }
void SyncShowDialog(
std::vector<FileSystemChooser::ResultEntry> SyncShowDialog(
std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
bool include_accepts_all) {
base::RunLoop loop;
std::vector<FileSystemChooser::ResultEntry> result;
FileSystemChooser::CreateAndShow(
/*web_contents=*/nullptr,
FileSystemChooser::Options(
......@@ -34,9 +36,13 @@ class FileSystemChooserTest : public testing::Test {
std::move(accepts), include_accepts_all),
base::BindLambdaForTesting(
[&](blink::mojom::NativeFileSystemErrorPtr,
std::vector<base::FilePath>) { loop.Quit(); }),
std::vector<FileSystemChooser::ResultEntry> entries) {
result = std::move(entries);
loop.Quit();
}),
base::ScopedClosureRunner());
loop.Run();
return result;
}
private:
......@@ -174,4 +180,31 @@ TEST_F(FileSystemChooserTest, AcceptsExtensionsAndMimeTypes) {
dialog_params.file_types->extension_description_overrides[0]);
}
TEST_F(FileSystemChooserTest, LocalPath) {
const base::FilePath local_path(FILE_PATH_LITERAL("/foo/bar"));
ui::SelectedFileInfo selected_file(local_path, local_path);
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({selected_file}));
auto results = SyncShowDialog({}, /*include_accepts_all=*/true);
ASSERT_EQ(results.size(), 1u);
EXPECT_EQ(results[0].type, FileSystemChooser::PathType::kLocal);
EXPECT_EQ(results[0].path, local_path);
}
TEST_F(FileSystemChooserTest, ExternalPath) {
const base::FilePath local_path(FILE_PATH_LITERAL("/foo/bar"));
const base::FilePath virtual_path(
FILE_PATH_LITERAL("/some/virtual/path/filename"));
ui::SelectedFileInfo selected_file(local_path, local_path);
selected_file.virtual_path = virtual_path;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({selected_file}));
auto results = SyncShowDialog({}, /*include_accepts_all=*/true);
ASSERT_EQ(results.size(), 1u);
EXPECT_EQ(results[0].type, FileSystemChooser::PathType::kExternal);
EXPECT_EQ(results[0].path, virtual_path);
}
} // namespace content
......@@ -9,7 +9,7 @@ option optimize_for = LITE_RUNTIME;
package content;
// Used to serialize a Native File System handle representing a file or
// directory on the local native file system.
// directory.
//
// Paths are stored as a serialized base::FilePath. I.e. on platforms where
// base::FilePath uses 8 bit characters this directly contains those
......@@ -17,7 +17,7 @@ package content;
// those characters are serialized using the current platform's endianness.
// This means paths can't necesarilly be read back on different platforms
// or computers from where they were written, but since the path references
// something on the local file system that likely doesn't exist on a different
// something on the local device that likely doesn't exist on a different
// computer anyway this should be no problem in practice.
message NativeFileData {
// The root path of a Native File System handle is the path that the user
......@@ -60,6 +60,9 @@ message NativeFileSystemHandleData {
// by.
oneof data {
SandboxedFileData sandboxed = 2;
NativeFileData native = 3;
// Paths reference the devices local file system.
NativeFileData local = 3;
// Paths are virtual paths in an external file system.
NativeFileData external = 4;
}
}
......@@ -290,8 +290,9 @@ void NativeFileSystemFileHandleImpl::CreateSwapFile(
base::StringPrintf(".%d", count));
}
auto handle =
manager()->CreateFileSystemURLFromPath(context().origin, swap_path);
auto handle = manager()->CreateFileSystemURLFromPath(
context().origin, NativeFileSystemEntryFactory::PathType::kLocal,
swap_path);
swap_url = std::move(handle.url);
swap_file_system = std::move(handle.file_system);
}
......
......@@ -9,7 +9,6 @@
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/common/url_utils.h"
#include "storage/common/file_system/file_system_types.h"
namespace content {
......@@ -28,22 +27,19 @@ NativeFileSystemHandleBase::NativeFileSystemHandleBase(
handle_state_.file_system.is_valid())
<< url_.mount_type();
bool chromeos_webui = false;
#if defined(OS_CHROMEOS)
// WebUIs on ChromeOS support a range of file system types.
chromeos_webui = HasWebUIScheme(context_.origin.GetURL());
#endif
// We support sandboxed file system and native file systems on all platforms.
DCHECK(chromeos_webui || url_.type() == storage::kFileSystemTypeNativeLocal ||
DCHECK(url_.type() == storage::kFileSystemTypeNativeLocal ||
url_.type() == storage::kFileSystemTypeTemporary ||
url_.mount_type() == storage::kFileSystemTypeExternal ||
url_.type() == storage::kFileSystemTypeTest)
<< url_.type();
if (ShouldTrackUsage()) {
DCHECK(chromeos_webui || url_.type() == storage::kFileSystemTypeNativeLocal)
<< url.type();
DCHECK_EQ(url_.mount_type(), storage::kFileSystemTypeIsolated);
DCHECK(url_.mount_type() == storage::kFileSystemTypeIsolated ||
url_.mount_type() == storage::kFileSystemTypeExternal)
<< url_.mount_type();
if (url_.mount_type() == storage::kFileSystemTypeIsolated)
DCHECK_EQ(url_.type(), storage::kFileSystemTypeNativeLocal);
Observe(WebContentsImpl::FromRenderFrameHostID(context_.frame_id));
......
......@@ -29,11 +29,9 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_utils.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "net/base/escape.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_operation_runner.h"
#include "storage/browser/file_system/file_system_url.h"
......@@ -55,33 +53,6 @@ using HandleType = NativeFileSystemPermissionContext::HandleType;
namespace {
#if defined(OS_CHROMEOS)
storage::FileSystemType GetFileSystemTypeForCrackedPath(
const base::FilePath& cracked_path,
std::string* filesystem_id) {
// There is also an instance of ExternalMountPoints sitting in
// NativeFileSystemManagerImpl::context(), but ChromeOS registers its volumes
// in the system instance.
auto* external_mount_points =
storage::ExternalMountPoints::GetSystemInstance();
base::FilePath virtual_path;
if (!external_mount_points->GetVirtualPath(cracked_path, &virtual_path)) {
return storage::kFileSystemTypeUnknown;
}
storage::FileSystemType type;
storage::FileSystemMountOption mount_option;
std::string cracked_id;
base::FilePath path;
if (external_mount_points->CrackVirtualPath(virtual_path, filesystem_id,
&type, &cracked_id, &path,
&mount_option)) {
return type;
}
return storage::kFileSystemTypeUnknown;
}
#endif // OS_CHROMEOS
void ShowFilePickerOnUIThread(const url::Origin& requesting_origin,
GlobalFrameRoutingId frame_id,
const FileSystemChooser::Options& options,
......@@ -93,7 +64,7 @@ void ShowFilePickerOnUIThread(const url::Origin& requesting_origin,
if (!web_contents) {
std::move(callback).Run(native_file_system_error::FromStatus(
NativeFileSystemStatus::kOperationAborted),
std::vector<base::FilePath>());
{});
return;
}
......@@ -105,7 +76,7 @@ void ShowFilePickerOnUIThread(const url::Origin& requesting_origin,
native_file_system_error::FromStatus(
NativeFileSystemStatus::kPermissionDenied,
"Third party iframes are not allowed to show a file picker."),
std::vector<base::FilePath>());
{});
return;
}
......@@ -408,12 +379,13 @@ void NativeFileSystemManagerImpl::ResolveDragDropTokenWithFileType(
GetEntryFromDragDropTokenCallback token_resolved_callback,
HandleType file_type) {
blink::mojom::NativeFileSystemEntryPtr entry;
// TODO(mek): Support Drag&Drop of non-local paths.
if (file_type == HandleType::kDirectory) {
entry = CreateDirectoryEntryFromPath(binding_context, file_path,
UserAction::kDragAndDrop);
entry = CreateDirectoryEntryFromPath(binding_context, PathType::kLocal,
file_path, UserAction::kDragAndDrop);
} else {
entry = CreateFileEntryFromPath(binding_context, file_path,
UserAction::kDragAndDrop);
entry = CreateFileEntryFromPath(binding_context, PathType::kLocal,
file_path, UserAction::kDragAndDrop);
}
std::move(token_resolved_callback).Run(std::move(entry));
......@@ -487,43 +459,41 @@ void NativeFileSystemManagerImpl::DidResolveForSerializeHandle(
? NativeFileSystemHandleData::kFile
: NativeFileSystemHandleData::kDirectory);
switch (url.type()) {
case storage::kFileSystemTypeNativeLocal: {
DCHECK_EQ(url.mount_type(), storage::kFileSystemTypeIsolated);
base::FilePath root_path = resolved_token->GetWriteGrant()->GetPath();
if (root_path.empty())
root_path = url.path();
data.mutable_native()->set_root_path(SerializePath(root_path));
base::FilePath relative_path;
// We want |relative_path| to be the path of the file or directory
// relative to |root_path|. FilePath::AppendRelativePath gets us that,
// but fails if the path we're looking for is equal to the |root_path|.
// So special case that case (in which case relative path would be empty
// anyway).
if (root_path != url.path()) {
bool relative_path_result =
root_path.AppendRelativePath(url.path(), &relative_path);
DCHECK(relative_path_result);
}
data.mutable_native()->set_relative_path(SerializePath(relative_path));
break;
}
case storage::kFileSystemTypeTemporary: {
base::FilePath virtual_path = url.virtual_path();
data.mutable_sandboxed()->set_virtual_path(SerializePath(virtual_path));
break;
if (url.type() == storage::kFileSystemTypeNativeLocal ||
url.mount_type() == storage::kFileSystemTypeExternal) {
// A url can have mount_type = external and type = native local at the same
// time. In that case we want to still treat it as an external path.
const bool is_external =
url.mount_type() == storage::kFileSystemTypeExternal;
content::NativeFileData* file_data =
is_external ? data.mutable_external() : data.mutable_local();
base::FilePath url_path = is_external ? url.virtual_path() : url.path();
base::FilePath root_path = resolved_token->GetWriteGrant()->GetPath();
if (root_path.empty())
root_path = url_path;
file_data->set_root_path(SerializePath(root_path));
base::FilePath relative_path;
// We want |relative_path| to be the path of the file or directory
// relative to |root_path|. FilePath::AppendRelativePath gets us that,
// but fails if the path we're looking for is equal to the |root_path|.
// So special case that case (in which case relative path would be empty
// anyway).
if (root_path != url_path) {
bool relative_path_result =
root_path.AppendRelativePath(url_path, &relative_path);
DCHECK(relative_path_result);
}
default:
#if defined(OS_CHROMEOS)
// For now, we don't support serializing handles for Chrome OS specific
// types, run |callback| with an empty vector to indicate an error.
std::move(callback).Run({});
return;
#endif
NOTREACHED();
file_data->set_relative_path(SerializePath(relative_path));
} else if (url.type() == storage::kFileSystemTypeTemporary) {
base::FilePath virtual_path = url.virtual_path();
data.mutable_sandboxed()->set_virtual_path(SerializePath(virtual_path));
} else {
NOTREACHED();
}
std::string value;
......@@ -565,14 +535,24 @@ void NativeFileSystemManagerImpl::DeserializeHandle(
std::move(token));
break;
}
case NativeFileSystemHandleData::kNative: {
base::FilePath root_path = DeserializePath(data.native().root_path());
base::FilePath relative_path =
DeserializePath(data.native().relative_path());
case NativeFileSystemHandleData::kLocal:
case NativeFileSystemHandleData::kExternal: {
const content::NativeFileData& file_data =
data.data_case() == NativeFileSystemHandleData::kLocal
? data.local()
: data.external();
base::FilePath root_path = DeserializePath(file_data.root_path());
base::FilePath relative_path = DeserializePath(file_data.relative_path());
FileSystemURLAndFSHandle root = CreateFileSystemURLFromPath(
origin,
data.data_case() == NativeFileSystemHandleData::kLocal
? PathType::kLocal
: PathType::kExternal,
root_path);
auto root = CreateFileSystemURLFromPath(origin, root_path);
storage::FileSystemURL child = context()->CreateCrackedFileSystemURL(
origin, root.url.mount_type(),
root.url.origin(), root.url.mount_type(),
root.url.virtual_path().Append(relative_path));
const bool is_directory =
......@@ -602,10 +582,12 @@ void NativeFileSystemManagerImpl::DeserializeHandle(
blink::mojom::NativeFileSystemEntryPtr
NativeFileSystemManagerImpl::CreateFileEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& file_path,
UserAction user_action) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto url = CreateFileSystemURLFromPath(binding_context.origin, file_path);
FileSystemURLAndFSHandle url =
CreateFileSystemURLFromPath(binding_context.origin, path_type, file_path);
SharedHandleState shared_handle_state = GetSharedHandleStateForPath(
file_path, binding_context.origin, std::move(url.file_system),
......@@ -620,10 +602,12 @@ NativeFileSystemManagerImpl::CreateFileEntryFromPath(
blink::mojom::NativeFileSystemEntryPtr
NativeFileSystemManagerImpl::CreateDirectoryEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& file_path,
UserAction user_action) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto url = CreateFileSystemURLFromPath(binding_context.origin, file_path);
FileSystemURLAndFSHandle url =
CreateFileSystemURLFromPath(binding_context.origin, path_type, file_path);
SharedHandleState shared_handle_state = GetSharedHandleStateForPath(
file_path, binding_context.origin, std::move(url.file_system),
......@@ -812,7 +796,7 @@ void NativeFileSystemManagerImpl::DidChooseEntries(
const FileSystemChooser::Options& options,
ChooseEntriesCallback callback,
blink::mojom::NativeFileSystemErrorPtr result,
std::vector<base::FilePath> entries) {
std::vector<FileSystemChooser::ResultEntry> entries) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (result->status != NativeFileSystemStatus::kOk) {
......@@ -828,11 +812,16 @@ void NativeFileSystemManagerImpl::DidChooseEntries(
SensitiveDirectoryResult::kAllowed);
return;
}
auto entries_copy = entries;
std::vector<base::FilePath> paths;
paths.reserve(entries.size());
for (const auto& entry : entries)
paths.push_back(entry.path);
const bool is_directory =
options.type() == blink::mojom::ChooseFileSystemEntryType::kOpenDirectory;
permission_context_->ConfirmSensitiveDirectoryAccess(
binding_context.origin, entries_copy,
binding_context.origin, std::move(paths),
is_directory ? HandleType::kDirectory : HandleType::kFile,
binding_context.frame_id,
base::BindOnce(
......@@ -845,7 +834,7 @@ void NativeFileSystemManagerImpl::DidVerifySensitiveDirectoryAccess(
const BindingContext& binding_context,
const FileSystemChooser::Options& options,
ChooseEntriesCallback callback,
std::vector<base::FilePath> entries,
std::vector<FileSystemChooser::ResultEntry> entries,
SensitiveDirectoryResult result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::UmaHistogramEnumeration(
......@@ -871,7 +860,8 @@ void NativeFileSystemManagerImpl::DidVerifySensitiveDirectoryAccess(
blink::mojom::ChooseFileSystemEntryType::kOpenDirectory) {
DCHECK_EQ(entries.size(), 1u);
SharedHandleState shared_handle_state = GetSharedHandleStateForPath(
entries.front(), binding_context.origin, {}, HandleType::kDirectory,
entries.front().path, binding_context.origin, {},
HandleType::kDirectory,
NativeFileSystemPermissionContext::UserAction::kOpen);
shared_handle_state.read_grant->RequestPermission(
binding_context.frame_id,
......@@ -885,8 +875,8 @@ void NativeFileSystemManagerImpl::DidVerifySensitiveDirectoryAccess(
if (options.type() == blink::mojom::ChooseFileSystemEntryType::kSaveFile) {
DCHECK_EQ(entries.size(), 1u);
// Create file if it doesn't yet exist, and truncate file if it does exist.
FileSystemURLAndFSHandle url =
CreateFileSystemURLFromPath(binding_context.origin, entries.front());
FileSystemURLAndFSHandle url = CreateFileSystemURLFromPath(
binding_context.origin, entries.front().type, entries.front().path);
auto fs_url = url.url;
operation_runner().PostTaskWithThisObject(
......@@ -904,8 +894,8 @@ void NativeFileSystemManagerImpl::DidVerifySensitiveDirectoryAccess(
std::vector<blink::mojom::NativeFileSystemEntryPtr> result_entries;
result_entries.reserve(entries.size());
for (const auto& entry : entries) {
result_entries.push_back(
CreateFileEntryFromPath(binding_context, entry, UserAction::kOpen));
result_entries.push_back(CreateFileEntryFromPath(
binding_context, entry.type, entry.path, UserAction::kOpen));
}
std::move(callback).Run(native_file_system_error::Ok(),
std::move(result_entries));
......@@ -913,7 +903,7 @@ void NativeFileSystemManagerImpl::DidVerifySensitiveDirectoryAccess(
void NativeFileSystemManagerImpl::DidCreateAndTruncateSaveFile(
const BindingContext& binding_context,
const base::FilePath& path,
const FileSystemChooser::ResultEntry& entry,
FileSystemURLAndFSHandle url,
ChooseEntriesCallback callback,
bool success) {
......@@ -932,8 +922,8 @@ void NativeFileSystemManagerImpl::DidCreateAndTruncateSaveFile(
}
SharedHandleState shared_handle_state = GetSharedHandleStateForPath(
path, binding_context.origin, std::move(url.file_system),
HandleType::kFile, NativeFileSystemPermissionContext::UserAction::kSave);
entry.path, binding_context.origin, std::move(url.file_system),
HandleType::kFile, UserAction::kSave);
result_entries.push_back(blink::mojom::NativeFileSystemEntry::New(
blink::mojom::NativeFileSystemHandle::NewFile(
......@@ -946,7 +936,7 @@ void NativeFileSystemManagerImpl::DidCreateAndTruncateSaveFile(
void NativeFileSystemManagerImpl::DidChooseDirectory(
const BindingContext& binding_context,
const base::FilePath& path,
const FileSystemChooser::ResultEntry& entry,
ChooseEntriesCallback callback,
const SharedHandleState& shared_handle_state,
NativeFileSystemPermissionGrant::PermissionRequestOutcome outcome) {
......@@ -964,7 +954,8 @@ void NativeFileSystemManagerImpl::DidChooseDirectory(
return;
}
auto url = CreateFileSystemURLFromPath(binding_context.origin, path);
FileSystemURLAndFSHandle url = CreateFileSystemURLFromPath(
binding_context.origin, entry.type, entry.path);
result_entries.push_back(blink::mojom::NativeFileSystemEntry::New(
blink::mojom::NativeFileSystemHandle::NewDirectory(CreateDirectoryHandle(
......@@ -1025,40 +1016,40 @@ void NativeFileSystemManagerImpl::DoResolveTransferToken(
NativeFileSystemManagerImpl::FileSystemURLAndFSHandle
NativeFileSystemManagerImpl::CreateFileSystemURLFromPath(
const url::Origin& origin,
PathType path_type,
const base::FilePath& path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto* isolated_context = storage::IsolatedContext::GetInstance();
DCHECK(isolated_context);
FileSystemURLAndFSHandle result;
std::string filesystem_id;
storage::FileSystemType fs_type = storage::kFileSystemTypeNativeLocal;
#if defined(OS_CHROMEOS)
// TODO(crbug.com/1093653): Support Chrome OS File System Provider in all Web
// Apps. For now, we only support external filesystem types for WebUIs.
if (HasWebUIScheme(origin.GetURL())) {
storage::FileSystemType cracked_fs_type =
GetFileSystemTypeForCrackedPath(path, &filesystem_id);
if (cracked_fs_type != storage::kFileSystemTypeUnknown)
fs_type = cracked_fs_type;
switch (path_type) {
case PathType::kLocal: {
auto* isolated_context = storage::IsolatedContext::GetInstance();
DCHECK(isolated_context);
FileSystemURLAndFSHandle result;
result.file_system = isolated_context->RegisterFileSystemForPath(
storage::kFileSystemTypeNativeLocal, std::string(), path,
&result.base_name);
base::FilePath root_path =
isolated_context->CreateVirtualRootPath(result.file_system.id());
// FromUTF8Unsafe in the following line is safe since result.base_name was
// the result of calling AsUTF8Unsafe on a base::FilePath in the first
// place.
base::FilePath isolated_path =
root_path.Append(base::FilePath::FromUTF8Unsafe(result.base_name));
result.url = context()->CreateCrackedFileSystemURL(
origin, storage::kFileSystemTypeIsolated, isolated_path);
return result;
}
case PathType::kExternal: {
FileSystemURLAndFSHandle result;
result.url = context()->CreateCrackedFileSystemURL(
url::Origin(), storage::kFileSystemTypeExternal, path);
result.base_name = path.BaseName().AsUTF8Unsafe();
return result;
}
}
#endif
result.file_system = isolated_context->RegisterFileSystemForPath(
fs_type, filesystem_id, path, &result.base_name);
base::FilePath root_path =
isolated_context->CreateVirtualRootPath(result.file_system.id());
// FromUTF8Unsafe in the following line is safe since result.base_name was the
// result of calling AsUTF8Unsafe on a base::FilePath in the first place.
base::FilePath isolated_path =
root_path.Append(base::FilePath::FromUTF8Unsafe(result.base_name));
result.url = context()->CreateCrackedFileSystemURL(
origin, storage::kFileSystemTypeIsolated, isolated_path);
return result;
}
NativeFileSystemManagerImpl::SharedHandleState
......
......@@ -125,10 +125,12 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
// NativeFileSystemEntryFactory:
blink::mojom::NativeFileSystemEntryPtr CreateFileEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& file_path,
UserAction user_action) override;
blink::mojom::NativeFileSystemEntryPtr CreateDirectoryEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& directory_path,
UserAction user_action) override;
......@@ -225,6 +227,7 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
};
FileSystemURLAndFSHandle CreateFileSystemURLFromPath(
const url::Origin& origin,
PathType path_type,
const base::FilePath& path);
private:
......@@ -241,21 +244,21 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
const FileSystemChooser::Options& options,
ChooseEntriesCallback callback,
blink::mojom::NativeFileSystemErrorPtr result,
std::vector<base::FilePath> entries);
std::vector<FileSystemChooser::ResultEntry> entries);
void DidVerifySensitiveDirectoryAccess(
const BindingContext& binding_context,
const FileSystemChooser::Options& options,
ChooseEntriesCallback callback,
std::vector<base::FilePath> entries,
std::vector<FileSystemChooser::ResultEntry> entries,
NativeFileSystemPermissionContext::SensitiveDirectoryResult result);
void DidCreateAndTruncateSaveFile(const BindingContext& binding_context,
const base::FilePath& path,
const FileSystemChooser::ResultEntry& entry,
FileSystemURLAndFSHandle url,
ChooseEntriesCallback callback,
bool success);
void DidChooseDirectory(
const BindingContext& binding_context,
const base::FilePath& path,
const FileSystemChooser::ResultEntry& entry,
ChooseEntriesCallback callback,
const SharedHandleState& shared_handle_state,
NativeFileSystemPermissionGrant::PermissionRequestOutcome outcome);
......
......@@ -30,6 +30,7 @@
#include "mojo/public/cpp/test_support/test_utils.h"
#include "mojo/public/mojom/base/file_info.mojom.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/test/async_file_test_helper.h"
#include "storage/browser/test/test_file_system_context.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -103,6 +104,9 @@ std::string ReadStringFromFileRemote(
return ReadDataPipe(std::move(consumer_handle));
}
constexpr char kTestMountPoint[] = "testfs";
} // namespace
using base::test::RunOnceCallback;
......@@ -123,6 +127,14 @@ class NativeFileSystemManagerImplTest : public testing::Test {
file_system_context_ = storage::CreateFileSystemContextForTesting(
/*quota_manager_proxy=*/nullptr, dir_.GetPath());
// Register an external mount point to test support for virtual paths.
// This maps the virtual path a native local path to make sure an external
// path round trips as an external path, even if the path resolves to a
// local path.
storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
kTestMountPoint, storage::kFileSystemTypeNativeLocal,
storage::FileSystemMountOption(), dir_.GetPath());
chrome_blob_context_ = base::MakeRefCounted<ChromeBlobStorageContext>();
chrome_blob_context_->InitializeOnIOThread(base::FilePath(),
base::FilePath(), nullptr);
......@@ -135,6 +147,14 @@ class NativeFileSystemManagerImplTest : public testing::Test {
manager_remote_.BindNewPipeAndPassReceiver());
}
void TearDown() override {
storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
kTestMountPoint);
// TODO(mek): Figure out what code is leaking open files, and uncomment this
// to prevent further regressions.
// ASSERT_TRUE(dir_.Delete());
}
template <typename HandleType>
PermissionStatus GetPermissionStatusSync(bool writable, HandleType* handle) {
PermissionStatus result;
......@@ -166,8 +186,8 @@ class NativeFileSystemManagerImplTest : public testing::Test {
blink::mojom::NativeFileSystemEntryPtr entry =
manager_->CreateDirectoryEntryFromPath(
kBindingContext, path,
NativeFileSystemPermissionContext::UserAction::kOpen);
kBindingContext, NativeFileSystemEntryFactory::PathType::kLocal,
path, NativeFileSystemPermissionContext::UserAction::kOpen);
return mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle>(
std::move(entry->entry_handle->get_directory()));
}
......@@ -274,8 +294,8 @@ TEST_F(NativeFileSystemManagerImplTest, CreateFileEntryFromPath_Permissions) {
blink::mojom::NativeFileSystemEntryPtr entry =
manager_->CreateFileEntryFromPath(
kBindingContext, kTestPath,
NativeFileSystemPermissionContext::UserAction::kOpen);
kBindingContext, NativeFileSystemEntryFactory::PathType::kLocal,
kTestPath, NativeFileSystemPermissionContext::UserAction::kOpen);
mojo::Remote<blink::mojom::NativeFileSystemFileHandle> handle(
std::move(entry->entry_handle->get_file()));
......@@ -302,8 +322,8 @@ TEST_F(NativeFileSystemManagerImplTest,
blink::mojom::NativeFileSystemEntryPtr entry =
manager_->CreateFileEntryFromPath(
kBindingContext, kTestPath,
NativeFileSystemPermissionContext::UserAction::kSave);
kBindingContext, NativeFileSystemEntryFactory::PathType::kLocal,
kTestPath, NativeFileSystemPermissionContext::UserAction::kSave);
mojo::Remote<blink::mojom::NativeFileSystemFileHandle> handle(
std::move(entry->entry_handle->get_file()));
......@@ -330,8 +350,8 @@ TEST_F(NativeFileSystemManagerImplTest,
blink::mojom::NativeFileSystemEntryPtr entry =
manager_->CreateDirectoryEntryFromPath(
kBindingContext, kTestPath,
NativeFileSystemPermissionContext::UserAction::kOpen);
kBindingContext, NativeFileSystemEntryFactory::PathType::kLocal,
kTestPath, NativeFileSystemPermissionContext::UserAction::kOpen);
mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> handle(
std::move(entry->entry_handle->get_directory()));
EXPECT_EQ(PermissionStatus::GRANTED,
......@@ -486,8 +506,8 @@ TEST_F(NativeFileSystemManagerImplTest, SerializeHandle_Native_SingleFile) {
blink::mojom::NativeFileSystemEntryPtr entry =
manager_->CreateFileEntryFromPath(
kBindingContext, kTestPath,
NativeFileSystemPermissionContext::UserAction::kOpen);
kBindingContext, NativeFileSystemEntryFactory::PathType::kLocal,
kTestPath, NativeFileSystemPermissionContext::UserAction::kOpen);
mojo::Remote<blink::mojom::NativeFileSystemFileHandle> handle(
std::move(entry->entry_handle->get_file()));
......@@ -668,6 +688,62 @@ TEST_F(NativeFileSystemManagerImplTest,
EXPECT_EQ(ask_grant2_, token->GetWriteGrant());
}
TEST_F(NativeFileSystemManagerImplTest, SerializeHandle_ExternalFile) {
const base::FilePath kTestPath =
base::FilePath::FromUTF8Unsafe(kTestMountPoint).AppendASCII("foo");
auto grant = base::MakeRefCounted<FixedNativeFileSystemPermissionGrant>(
FixedNativeFileSystemPermissionGrant::PermissionStatus::GRANTED,
kTestPath);
// Expect calls to get grants when creating the initial handle.
EXPECT_CALL(permission_context_,
GetReadPermissionGrant(
kTestOrigin, kTestPath, HandleType::kFile,
NativeFileSystemPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(grant));
EXPECT_CALL(permission_context_,
GetWritePermissionGrant(
kTestOrigin, kTestPath, HandleType::kFile,
NativeFileSystemPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(grant));
blink::mojom::NativeFileSystemEntryPtr entry =
manager_->CreateFileEntryFromPath(
kBindingContext, NativeFileSystemEntryFactory::PathType::kExternal,
kTestPath, NativeFileSystemPermissionContext::UserAction::kOpen);
mojo::Remote<blink::mojom::NativeFileSystemFileHandle> handle(
std::move(entry->entry_handle->get_file()));
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
handle->Transfer(token_remote.InitWithNewPipeAndPassReceiver());
// Deserializing tokens should re-request grants, with correct user action.
EXPECT_CALL(
permission_context_,
GetReadPermissionGrant(
kTestOrigin, kTestPath, HandleType::kFile,
NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
.WillOnce(testing::Return(ask_grant_));
EXPECT_CALL(
permission_context_,
GetWritePermissionGrant(
kTestOrigin, kTestPath, HandleType::kFile,
NativeFileSystemPermissionContext::UserAction::kLoadFromStorage))
.WillOnce(testing::Return(ask_grant2_));
NativeFileSystemTransferTokenImpl* token =
SerializeAndDeserializeToken(std::move(token_remote));
ASSERT_TRUE(token);
const storage::FileSystemURL& url = token->url();
EXPECT_TRUE(url.origin().opaque());
EXPECT_EQ(kTestPath, url.virtual_path());
EXPECT_EQ(storage::kFileSystemTypeExternal, url.mount_type());
EXPECT_EQ(HandleType::kFile, token->type());
EXPECT_EQ(ask_grant_, token->GetReadGrant());
EXPECT_EQ(ask_grant2_, token->GetWriteGrant());
}
// NativeFileSystemManager should successfully resolve a
// NativeFileSystemDragDropToken representing a file in the user's file system
// into a valid Remote<blink::mojom::NativeFileSystemFileHandle>, given
......
......@@ -48,10 +48,24 @@ class CONTENT_EXPORT NativeFileSystemEntryFactory
int process_id() const { return frame_id.child_id; }
};
enum class PathType {
// A path on the local file system. Files with these paths can be operated
// on by base::File.
kLocal,
// A path on an "external" file system. These paths can only be accessed via
// the filesystem abstraction in //storage/browser/file_system, and a
// storage::kFileSystemTypeExternal storage::FileSystemURL.
// This path type should be used for paths retrieved via the `virtual_path`
// member of a ui::SelectedFileInfo struct.
kExternal
};
// Creates a new NativeFileSystemEntryPtr from the path to a file. Assumes the
// passed in path is valid and represents a file.
virtual blink::mojom::NativeFileSystemEntryPtr CreateFileEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& file_path,
UserAction user_action) = 0;
......@@ -59,6 +73,7 @@ class CONTENT_EXPORT NativeFileSystemEntryFactory
// Assumes the passed in path is valid and represents a directory.
virtual blink::mojom::NativeFileSystemEntryPtr CreateDirectoryEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& directory_path,
UserAction user_action) = 0;
......
......@@ -39,6 +39,13 @@ struct SHELL_DIALOGS_EXPORT SelectedFileInfo {
// file is opened by navigating to a docs.google.com URL.
base::Optional<GURL> url;
// If set, this virtual path may be used to access the file. If the user is
// capable of using a virtual path to access the file (using the file system
// abstraction in //storage/browser/file_system with a
// storage::kFileSystemTypeExternal FileSystemURL), it should be used in
// preference over |local_path| and |url|.
base::Optional<base::FilePath> virtual_path;
SelectedFileInfo();
SelectedFileInfo(const base::FilePath& in_file_path,
const base::FilePath& in_local_path);
......
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