blob: 32aa18eae83a8f0f8759dfd32152ff974aa7604c [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include <vector>
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/gmock_callback_support.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "content/browser/file_system_access/file_system_access_manager_impl.h"
#include "content/browser/file_system_access/fixed_file_system_access_permission_grant.h"
#include "content/browser/file_system_access/mock_file_system_access_permission_context.h"
#include "content/browser/file_system_access/mock_file_system_access_permission_grant.h"
#include "content/browser/renderer_host/back_forward_cache_disable.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fake_file_system_access_permission_context.h"
#include "content/public/test/file_system_chooser_test_helpers.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/mojom/file_system_access/file_system_access_manager.mojom-shared.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 {
using base::test::RunOnceCallback;
using blink::mojom::PermissionStatus;
using SensitiveEntryResult =
FileSystemAccessPermissionContext::SensitiveEntryResult;
using PathInfo = FileSystemAccessPermissionContext::PathInfo;
using PathType = FileSystemAccessPermissionContext::PathType;
static constexpr char kTestMountPoint[] = "testfs";
// This browser test implements end-to-end tests for the file picker
// APIs.
class FileSystemChooserBrowserTest : public ContentBrowserTest {
public:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
#if BUILDFLAG(IS_WIN)
// Convert path to long format to avoid mixing long and 8.3 formats in test.
ASSERT_TRUE(temp_dir_.Set(base::MakeLongFilePath(temp_dir_.Take())));
#endif // BUILDFLAG(IS_WIN)
// 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::kFileSystemTypeLocal,
storage::FileSystemMountOption(), temp_dir_.GetPath());
ASSERT_TRUE(embedded_test_server()->Start());
ContentBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// Enable experimental web platform features to enable write access.
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
}
void TearDown() override {
ContentBrowserTest::TearDown();
storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
kTestMountPoint);
ui::SelectFileDialog::SetFactory(nullptr);
ASSERT_TRUE(temp_dir_.Delete());
}
bool IsFullscreen() {
WebContents* web_contents = shell()->web_contents();
return web_contents->IsFullscreen();
}
void EnterFullscreen() {
WebContentsImpl* web_contents_impl =
static_cast<WebContentsImpl*>(shell()->web_contents());
web_contents_impl->EnterFullscreenMode(
web_contents_impl->GetPrimaryMainFrame(), {});
}
base::FilePath CreateTestFile(const std::string& contents) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath result;
EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &result));
EXPECT_TRUE(base::WriteFile(result, contents));
return result;
}
base::FilePath CreateTestDir() {
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath result;
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("test"), &result));
return result;
}
protected:
base::ScopedTempDir temp_dir_;
};
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, CancelDialog) {
ui::SelectFileDialog::SetFactory(new CancellingSelectFileDialogFactory);
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result = EvalJs(shell(), "self.showOpenFilePicker()");
EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
<< result.error;
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenFile) {
const std::string file_contents = "hello world!";
const base::FilePath test_file = CreateTestFile(file_contents);
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_file.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, OpenFileNonASCII) {
const std::string file_contents = "hello world!";
const base::FilePath test_file =
temp_dir_.GetPath().Append(base::FilePath::FromUTF8Unsafe("😋.txt"));
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::WriteFile(test_file, file_contents));
}
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_file.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, FullscreenOpenFile) {
const std::string file_contents = "hello world!";
const base::FilePath test_file = CreateTestFile(file_contents);
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EnterFullscreen();
EXPECT_TRUE(IsFullscreen());
EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let [e] = await self.showOpenFilePicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_FALSE(IsFullscreen());
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenFile_BlockedPermission) {
const base::FilePath test_file = CreateTestFile("Save File");
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
EXPECT_CALL(permission_context,
CanObtainReadPermission(url::Origin::Create(
embedded_test_server()->GetURL("/title1.html"))))
.WillOnce(testing::Return(false));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result = EvalJs(shell(), "self.showOpenFilePicker()");
EXPECT_TRUE(result.error.find("not allowed") != std::string::npos)
<< result.error;
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::FromASCII(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("");
{
// Delete file, since SaveFile should be able to deal with non-existing
// files.
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::DeleteFile(test_file));
}
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showSaveFilePicker();"
" self.entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_SAVEAS_FILE, dialog_params.type);
EXPECT_EQ(static_cast<int>(file_contents.size()),
EvalJs(shell(),
JsReplace("(async () => {"
" const w = await self.entry.createWritable();"
" await w.write(new Blob([$1]));"
" await w.close();"
" return (await self.entry.getFile()).size; })()",
file_contents)));
{
base::ScopedAllowBlockingForTesting allow_blocking;
std::string read_contents;
EXPECT_TRUE(base::ReadFileToString(test_file, &read_contents));
EXPECT_EQ(file_contents, read_contents);
}
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
SaveFile_TruncatesExistingFile) {
const base::FilePath test_file = CreateTestFile("Hello World");
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showSaveFilePicker();"
" self.entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_SAVEAS_FILE, dialog_params.type);
EXPECT_EQ("",
EvalJs(shell(),
"(async () => { const file = await self.entry.getFile(); "
"return await file.text(); })()"));
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
SaveFile_BlockedPermission) {
const base::FilePath test_file = CreateTestFile("Save File");
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
EXPECT_CALL(permission_context,
CanObtainReadPermission(url::Origin::Create(
embedded_test_server()->GetURL("/title1.html"))))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context,
CanObtainWritePermission(url::Origin::Create(
embedded_test_server()->GetURL("/title1.html"))))
.WillOnce(testing::Return(false));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result = EvalJs(shell(), "self.showSaveFilePicker()");
EXPECT_TRUE(result.error.find("not allowed") != std::string::npos)
<< result.error;
EXPECT_EQ(ui::SelectFileDialog::SELECT_NONE, dialog_params.type);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, FullscreenSaveFile) {
const base::FilePath test_file = CreateTestFile("Hello World");
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EnterFullscreen();
EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showSaveFilePicker();"
" self.entry = e;"
" return e.name; })()"));
EXPECT_FALSE(IsFullscreen());
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenMultipleFiles) {
const base::FilePath test_file1 = CreateTestFile("file1");
const base::FilePath test_file2 = CreateTestFile("file2");
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(new FakeSelectFileDialogFactory(
{test_file1, test_file2}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(ListValueOf(test_file1.BaseName().AsUTF8Unsafe(),
test_file2.BaseName().AsUTF8Unsafe()),
EvalJs(shell(),
"(async () => {"
" let e = await self.showOpenFilePicker("
" {multiple: true});"
" return e.map(x => x.name); })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE, dialog_params.type);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
FullscreenOpenMultipleFiles) {
const base::FilePath test_file1 = CreateTestFile("file1");
const base::FilePath test_file2 = CreateTestFile("file2");
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(new FakeSelectFileDialogFactory(
{test_file1, test_file2}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EnterFullscreen();
EXPECT_EQ(ListValueOf(test_file1.BaseName().AsUTF8Unsafe(),
test_file2.BaseName().AsUTF8Unsafe()),
EvalJs(shell(),
"(async () => {"
" let e = await self.showOpenFilePicker("
" {multiple: true});"
" return e.map(x => x.name); })()"));
EXPECT_FALSE(IsFullscreen());
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenDirectory) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, FullscreenOpenDirectory) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EnterFullscreen();
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_FALSE(IsFullscreen());
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenDirectory_BlockedPermission) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
EXPECT_CALL(permission_context,
CanObtainReadPermission(url::Origin::Create(
embedded_test_server()->GetURL("/title1.html"))))
.WillOnce(testing::Return(false));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result = EvalJs(shell(), "self.showDirectoryPicker()");
EXPECT_TRUE(result.error.find("not allowed") != std::string::npos)
<< result.error;
EXPECT_EQ(ui::SelectFileDialog::SELECT_NONE, dialog_params.type);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenDirectory_DenyAccess) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
PermissionStatus::ASK, base::FilePath());
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDefault, origin))
.WillOnce(testing::Return(base::FilePath()));
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(PathInfo()));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserDenied));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::ASK));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result = EvalJs(shell(), "self.showDirectoryPicker()");
EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
<< result.error;
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenDirectoryWithReadAccess) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDefault, origin))
.WillOnce(testing::Return(base::FilePath()));
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(PathInfo()));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker({mode: 'read'});"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenDirectoryWithReadWriteAccess) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
// Write permission should be requested alongside read permission.
EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDefault, origin))
.WillOnce(testing::Return(base::FilePath()));
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(PathInfo()));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
// Write permission should be requested alongside read permission.
EXPECT_CALL(
*write_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*write_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(
test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker({mode: 'readwrite'});"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
SaveFile_SensitiveDirectory_ExistingFile) {
const std::string file_contents = "Hello World";
const base::FilePath test_file = CreateTestFile(file_contents);
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDefault, origin))
.WillOnce(testing::Return(base::FilePath()));
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(PathInfo()));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_file,
FileSystemAccessPermissionContext::HandleType::kFile,
FileSystemAccessPermissionContext::UserAction::kSave,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAbort));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result = EvalJs(shell(), "self.showSaveFilePicker()");
EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
<< result.error;
{
// File should still exist, and be unmodified.
base::ScopedAllowBlockingForTesting allow_blocking;
std::string read_contents;
EXPECT_TRUE(base::ReadFileToString(test_file, &read_contents));
EXPECT_EQ(file_contents, read_contents);
}
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
SaveFile_SensitiveDirectory_NonExistingFile) {
const base::FilePath test_file = CreateTestFile("");
{
// Delete file, since SaveFile should be able to deal with non-existing
// files.
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::DeleteFile(test_file));
}
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context, CanObtainWritePermission(origin))
.WillOnce(testing::Return(true));
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDefault, origin))
.WillOnce(testing::Return(base::FilePath()));
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(PathInfo()));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_file,
FileSystemAccessPermissionContext::HandleType::kFile,
FileSystemAccessPermissionContext::UserAction::kSave,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAbort));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result = EvalJs(shell(), "self.showSaveFilePicker()");
EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
<< result.error;
{
// File should not have been created.
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_FALSE(base::PathExists(test_file));
}
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, AcceptsOptions) {
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new CancellingSelectFileDialogFactory(&dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result =
EvalJs(shell(),
"self.showOpenFilePicker({types: ["
" {description: 'foo', accept: {'text/custom': ['.txt', '.Js']}},"
" {accept: {'image/jpeg': []}},"
" {accept: {'image/svg+xml': '.svg'}},"
"]})");
EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
<< result.error;
ASSERT_TRUE(dialog_params.file_types);
EXPECT_TRUE(dialog_params.file_types->include_all_files);
ASSERT_EQ(3u, dialog_params.file_types->extensions.size());
ASSERT_EQ(2u, dialog_params.file_types->extensions[0].size());
EXPECT_EQ(FILE_PATH_LITERAL("txt"),
dialog_params.file_types->extensions[0][0]);
EXPECT_EQ(FILE_PATH_LITERAL("Js"),
dialog_params.file_types->extensions[0][1]);
EXPECT_TRUE(base::Contains(dialog_params.file_types->extensions[1],
FILE_PATH_LITERAL("jpg")));
EXPECT_TRUE(base::Contains(dialog_params.file_types->extensions[1],
FILE_PATH_LITERAL("jpeg")));
EXPECT_TRUE(base::Contains(dialog_params.file_types->extensions[2],
FILE_PATH_LITERAL("svg")));
ASSERT_EQ(3u,
dialog_params.file_types->extension_description_overrides.size());
EXPECT_EQ(u"foo",
dialog_params.file_types->extension_description_overrides[0]);
EXPECT_EQ(u"", dialog_params.file_types->extension_description_overrides[1]);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, UndefinedAccepts) {
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new CancellingSelectFileDialogFactory(&dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
auto result =
EvalJs(shell(), "self.showOpenFilePicker({types: [undefined]})");
EXPECT_TRUE(result.error.find("aborted") != std::string::npos)
<< result.error;
ASSERT_TRUE(dialog_params.file_types);
EXPECT_TRUE(dialog_params.file_types->include_all_files);
ASSERT_EQ(0u, dialog_params.file_types->extensions.size());
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
FileSystemAccessUsageDisablesBackForwardCache) {
BackForwardCacheDisabledTester tester;
const base::FilePath test_file = CreateTestFile("file contents");
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let [e] = await self.showOpenFilePicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_TRUE(tester.IsDisabledForFrameWithReason(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
BackForwardCacheDisable::DisabledReason(
BackForwardCacheDisable::DisabledReasonId::kFileSystemAccess)));
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenDirectory_LastPickedDirExists) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
PermissionStatus::GRANTED, base::FilePath());
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
// The last picked directory exists.
PathInfo good_dir_info;
good_dir_info.path = temp_dir_.GetPath();
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(good_dir_info));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(good_dir_info.path, dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenDirectory_LastPickedDirNotExists) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
PermissionStatus::GRANTED, base::FilePath());
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
// The last picked directory no longer exists, so resort to showing the
// default directory, then set the test_file's dir as last picked.
PathInfo bad_dir_info;
bad_dir_info.path = temp_dir_.GetPath().AppendASCII("nonexistent");
base::FilePath default_dir;
default_dir = temp_dir_.GetPath().AppendASCII("default");
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDefault, origin))
.WillOnce(testing::Return(default_dir));
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(bad_dir_info));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(default_dir, dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenDirectory_LastPickedDirExistsExternal) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
PermissionStatus::GRANTED, base::FilePath());
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
// The last picked directory exists.
PathInfo good_dir_info;
good_dir_info.path = base::FilePath::FromASCII(kTestMountPoint);
good_dir_info.type = PathType::kExternal;
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(good_dir_info));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
// temp_dir_.GetPath() maps to kTestMountPoint.
EXPECT_EQ(temp_dir_.GetPath(), dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
OpenDirectory_LastPickedDirNotExistsExternal) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
PermissionStatus::GRANTED, base::FilePath());
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
// The last picked directory no longer exists, so resort to showing the
// default directory, then set the test_file's dir as last picked.
PathInfo bad_dir_info;
bad_dir_info.path =
base::FilePath::FromASCII(kTestMountPoint).AppendASCII("nonexistent");
base::FilePath default_dir;
default_dir = temp_dir_.GetPath().AppendASCII("default");
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDefault, origin))
.WillOnce(testing::Return(default_dir));
EXPECT_CALL(permission_context, GetLastPickedDirectory(origin, std::string()))
.WillOnce(testing::Return(bad_dir_info));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(default_dir, dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
StartIn_WellKnownDirectory) {
base::FilePath test_dir = CreateTestDir();
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
testing::StrictMock<MockFileSystemAccessPermissionContext> permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
auto read_grant = base::MakeRefCounted<
testing::StrictMock<MockFileSystemAccessPermissionGrant>>();
auto write_grant = base::MakeRefCounted<FixedFileSystemAccessPermissionGrant>(
PermissionStatus::GRANTED, base::FilePath());
auto origin =
url::Origin::Create(embedded_test_server()->GetURL("/title1.html"));
auto frame_id = GlobalRenderFrameHostId(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
shell()->web_contents()->GetPrimaryMainFrame()->GetRoutingID());
EXPECT_CALL(permission_context, CanObtainReadPermission(origin))
.WillOnce(testing::Return(true));
// Ensure Desktop directory exists.
base::FilePath desktop_dir;
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("Desktop"), &desktop_dir));
}
// Well-known starting directory specified, so do not call
// GetLastPickedDirectory.
EXPECT_CALL(permission_context,
GetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDirDesktop, origin))
.WillOnce(testing::Return(desktop_dir));
EXPECT_CALL(permission_context, GetPickerTitle(testing::_))
.WillOnce(testing::Return(std::u16string()));
EXPECT_CALL(permission_context,
SetLastPickedDirectory(origin, std::string(), test_dir,
PathType::kLocal));
EXPECT_CALL(permission_context,
ConfirmSensitiveEntryAccess_(
origin, PathType::kLocal, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen,
frame_id, testing::_))
.WillOnce(RunOnceCallback<6>(SensitiveEntryResult::kAllowed));
EXPECT_CALL(permission_context,
GetReadPermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(read_grant));
EXPECT_CALL(permission_context,
GetWritePermissionGrant(
origin, test_dir,
FileSystemAccessPermissionContext::HandleType::kDirectory,
FileSystemAccessPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(write_grant));
EXPECT_CALL(
*read_grant,
RequestPermission_(
frame_id,
FileSystemAccessPermissionGrant::UserActivationState::kNotRequired,
testing::_))
.WillOnce(RunOnceCallback<2>(FileSystemAccessPermissionGrant::
PermissionRequestOutcome::kUserGranted));
EXPECT_CALL(*read_grant, GetStatus())
.WillRepeatedly(testing::Return(PermissionStatus::GRANTED));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(
test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker({ startIn: 'desktop' });"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(desktop_dir, dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_FileHandle) {
// Ensure test file exists in a directory which could not be a default.
base::FilePath test_file_dir;
base::FilePath test_file;
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("handles"), &test_file_dir));
EXPECT_TRUE(base::CreateTemporaryFileInDir(test_file_dir, &test_file));
}
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
// Acquire a FileSystemHandle to the test_file.
EXPECT_EQ(test_file.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(test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let [e] = await self.showOpenFilePicker({ startIn: "
" self.selected_entry });"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_OPEN_FILE, dialog_params.type);
// Windows file system is case-insensitive.
EXPECT_TRUE(base::FilePath::CompareEqualIgnoreCase(
test_file_dir.value(), dialog_params.default_path.value()));
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_DirectoryHandle) {
// Ensure test directory exists and could not be a default.
base::FilePath test_dir;
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("handles"), &test_dir));
}
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
// Acquire a FileSystemHandle to the test_dir.
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(shell()->web_contents()->GetTopLevelNativeWindow(),
dialog_params.owning_window);
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker({ startIn: "
" self.selected_entry });"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(test_dir, dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
StartIn_FileHandle_External) {
const base::FilePath test_file = CreateTestFile("");
const base::FilePath virtual_path =
base::FilePath::FromASCII(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")));
// Acquire a FileSystemHandle to the test_file.
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(virtual_path.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let [e] = await self.showOpenFilePicker({ startIn: "
" self.selected_entry });"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_OPEN_FILE, dialog_params.type);
// temp_dir_.GetPath() maps to kTestMountPoint.
EXPECT_EQ(temp_dir_.GetPath(), dialog_params.default_path);
}
// Correctly saving a symlink as the starting directory should work on all OSes,
// but `base::CreateSymbolicLink` is only available on Posix.
#if BUILDFLAG(IS_POSIX)
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_Symlink) {
// Ensure test directory exists and could not be a default.
base::FilePath test_dir;
base::FilePath symlink = temp_dir_.GetPath().AppendASCII("symbolic");
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("handles"), &test_dir));
base::CreateSymbolicLink(test_dir, symlink);
}
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({symlink}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
// Acquire a FileSystemHandle to the symlink.
EXPECT_EQ(symlink.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(shell()->web_contents()->GetTopLevelNativeWindow(),
dialog_params.owning_window);
EXPECT_EQ(symlink.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker({ startIn: "
" self.selected_entry });"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(symlink, dialog_params.default_path);
}
#endif // BUILDFLAG(IS_POSIX)
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
const base::FilePath test_file = CreateTestFile("");
SelectFileDialogParams dialog_params;
struct info {
std::string suggested_name;
base::Value accepted_extensions;
bool exclude_accept_all_option = true;
std::string expected_result;
bool expected_exclude_accept_all_option = false;
};
std::vector<info> name_infos;
// Empty suggested name should be ok.
name_infos.push_back({"", ListValueOf(".txt"), true, "", true});
name_infos.push_back({"", ListValueOf(".txt"), false, "", false});
name_infos.push_back({"", ListValueOf(), true, "", false});
// Suggested extension listed as accepted extension.
name_infos.push_back(
{"ext_match.txt", ListValueOf(".txt"), true, "ext_match.txt", true});
name_infos.push_back(
{"ext_match.txt", ListValueOf(".txt"), false, "ext_match.txt", false});
name_infos.push_back(
{"ext_match.txt", ListValueOf(), true, "ext_match.txt", false});
// No suggested extension. Don't try to infer one, and behave as if
// `excludeAcceptAllOption` is false.
name_infos.push_back(
{"no_extension", ListValueOf(".txt"), true, "no_extension", false});
name_infos.push_back(
{"no_extension", ListValueOf(".txt"), false, "no_extension", false});
name_infos.push_back(
{"no_extension", ListValueOf(), true, "no_extension", false});
// Suggested extension not listed as an accepted extension. Allow extension,
// but behave as if `excludeAcceptAllOption` is false.
name_infos.push_back({"not_matching.jpg", ListValueOf(".txt"), true,
"not_matching.jpg", false});
name_infos.push_back({"not_matching.jpg", ListValueOf(".txt"), false,
"not_matching.jpg", false});
// ".lnk", ".local", ".scf", and ".url" extensions should be sanitized.
name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".lnk"), true,
"dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".LNK"), true,
"dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.LNK", ListValueOf(".lnk"), true,
"dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.LNK", ListValueOf(".LNK"), true,
"dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
true, "dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.scf", ListValueOf(".scf"), true,
"dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.url", ListValueOf(".url"), true,
"dangerous_extension.download", false});
// Compound extensions ending in a dangerous extension should be sanitized.
name_infos.push_back({"dangerous_extension.png.local", ListValueOf(".local"),
true, "dangerous_extension.png.download", false});
name_infos.push_back({"dangerous_extension.png.lnk", ListValueOf(".lnk"),
true, "dangerous_extension.png.download", false});
name_infos.push_back({"dangerous_extension.png.scf", ListValueOf(".scf"),
true, "dangerous_extension.png.download", false});
name_infos.push_back({"dangerous_extension.png.url", ListValueOf(".url"),
true, "dangerous_extension.png.download", false});
// Compound extensions not ending in a dangerous extension should not be
// sanitized.
name_infos.push_back({"dangerous_extension.local.png", ListValueOf(".png"),
true, "dangerous_extension.local.png", true});
name_infos.push_back({"dangerous_extension.lnk.png", ListValueOf(".png"),
true, "dangerous_extension.lnk.png", true});
name_infos.push_back({"dangerous_extension.scf.png", ListValueOf(".png"),
true, "dangerous_extension.scf.png", true});
name_infos.push_back({"dangerous_extension.url.png", ListValueOf(".png"),
true, "dangerous_extension.url.png", true});
// Extensions longer than 16 characters should be stripped.
name_infos.push_back({"long_extension.len10plus123456",
ListValueOf(".len10plus123456"), true,
"long_extension.len10plus123456", true});
name_infos.push_back({"long_extension.len10plus1234567", ListValueOf(".nope"),
true, "long_extension", false});
// Invalid characters should be sanitized.
name_infos.push_back({R"(inv*l:d\\ch%rבאמת!a<ters🤓.txt)",
ListValueOf(".txt"), true,
R"(inv_l_d__ch_rבאמת!a_ters🤓.txt)", true});
for (const auto& name_info : name_infos) {
SCOPED_TRACE(name_info.suggested_name);
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(
test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(), JsReplace("(async () => {"
" let e = await self.showSaveFilePicker({"
" suggestedName: $1,"
" types: [{accept: {'text/custom': $2}}],"
" excludeAcceptAllOption: $3"
"});"
" return e.name; })()",
name_info.suggested_name,
name_info.accepted_extensions,
name_info.exclude_accept_all_option)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_SAVEAS_FILE, dialog_params.type);
EXPECT_EQ(base::FilePath::FromUTF8Unsafe(name_info.expected_result),
dialog_params.default_path.BaseName());
EXPECT_NE(name_info.expected_exclude_accept_all_option,
dialog_params.file_types->include_all_files);
}
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
SuggestedName_CorrectIndex) {
const base::FilePath test_file = CreateTestFile("");
SelectFileDialogParams dialog_params;
struct info {
std::string suggested_name;
std::string expected_result;
bool expected_exclude_accept_all_option = false;
int expected_index;
};
std::vector<info> name_infos;
// There are valid accepted extensions, so default to index 1.
name_infos.push_back({"ext_no_match.foo", "ext_no_match.foo", false, 1});
name_infos.push_back({"ext_match.jpg", "ext_match.jpg", true, 1});
name_infos.push_back({"ext_match.txt", "ext_match.txt", true, 2});
name_infos.push_back({"ext_mime_match.text", "ext_mime_match.text", true, 2});
for (const auto& name_info : name_infos) {
SCOPED_TRACE(name_info.suggested_name);
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_file}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(
test_file.BaseName().AsUTF8Unsafe(),
EvalJs(shell(), JsReplace("(async () => {"
" let e = await self.showSaveFilePicker({"
" suggestedName: $1,"
" types: ["
" {accept: {'image/custom': ['.jpg']}},"
" {accept: {'text/plain': ['.txt']}},"
" ],"
" excludeAcceptAllOption: true"
"});"
" return e.name; })()",
name_info.suggested_name)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_SAVEAS_FILE, dialog_params.type);
EXPECT_EQ(base::FilePath::FromUTF8Unsafe(name_info.expected_result),
dialog_params.default_path.BaseName());
EXPECT_NE(name_info.expected_exclude_accept_all_option,
dialog_params.file_types->include_all_files);
EXPECT_EQ(name_info.expected_index, dialog_params.file_type_index);
}
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_ID) {
base::FilePath test_dir;
{
base::ScopedAllowBlockingForTesting allow_blocking;
// Ensure directory we're selecting exists.
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("test123"), &test_dir));
}
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
FakeFileSystemAccessPermissionContext permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
// Specify an `id` for the directory that is picked.
std::string id = "testing";
PathInfo test_dir_info;
test_dir_info.path = test_dir;
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
// #1: `id` is unset. Fall back to the default starting directory.
EXPECT_EQ(
test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
JsReplace("(async () => {"
" let e = await self.showDirectoryPicker({ id: $1 });"
" self.selected_entry = e;"
" return e.name; })()",
id)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(base::FilePath(), dialog_params.default_path);
// #2: `id` is set. Use the LastPickedDirectory given this `id`.
EXPECT_EQ(
test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
JsReplace("(async () => {"
" let e = await self.showDirectoryPicker({ id: $1 });"
" self.selected_entry = e;"
" return e.name; })()",
id)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(test_dir, dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, StartIn_Priority) {
// Priority:
// 1) `startIn` via a file/directory handle
// 2) non-empty `id, if stored
// 3) `startIn` via a well-known directory
// 4) default `id`, if stored
// 5) default path
//
// Test A checks #5
// B checks #4
// C checks #3
// D checks #2
// E checks #1
base::FilePath test_dir;
base::FilePath desktop_dir;
base::FilePath music_dir;
base::FilePath dir_handle;
{
base::ScopedAllowBlockingForTesting allow_blocking;
// Ensure directories we're selecting exist.
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("test123"), &test_dir));
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("Desktop"), &desktop_dir));
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("Music"), &music_dir));
EXPECT_TRUE(base::CreateTemporaryDirInDir(
temp_dir_.GetPath(), FILE_PATH_LITERAL("handle"), &dir_handle));
}
FakeFileSystemAccessPermissionContext permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
permission_context.SetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDirDesktop, desktop_dir);
permission_context.SetWellKnownDirectoryPath(
blink::mojom::WellKnownDirectory::kDirMusic, music_dir);
// Specify an `id` for the directory that is picked.
std::string id = "testing";
PathInfo test_dir_info;
test_dir_info.path = test_dir;
PathInfo desktop_dir_info;
desktop_dir_info.path = desktop_dir;
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
SelectFileDialogParams dialog_params;
// (A) Acquire a handle to the "handle" directory to be used later. Use the
// default `id`.
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({dir_handle}, &dialog_params));
EXPECT_EQ(
dir_handle.BaseName().AsUTF8Unsafe(),
EvalJs(shell(), JsReplace("(async () => {"
" let e = await self.showDirectoryPicker();"
" self.handle = e;"
" return e.name; })()",
id)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(base::FilePath(), dialog_params.default_path);
// (B) Use the default `id`, which should have been set.
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({dir_handle}, &dialog_params));
EXPECT_EQ(
dir_handle.BaseName().AsUTF8Unsafe(),
EvalJs(shell(), JsReplace("(async () => {"
" let e = await self.showDirectoryPicker();"
" self.handle = e;"
" return e.name; })()",
id)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(dir_handle, dialog_params.default_path);
// (C) Since this new `id` has not yet been set, fall back on using the
// WellKnownDirectory specified via `startIn`.
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({desktop_dir}, &dialog_params));
EXPECT_EQ(
desktop_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(), JsReplace("(async () => {"
" let e = await self.showDirectoryPicker({ "
"id: $1, startIn: 'desktop' });"
" self.selected_entry = e;"
" return e.name; })()",
id)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(desktop_dir, dialog_params.default_path);
// (D) The `id` is set. Use the LastPickedDirectory given this `id`.
EXPECT_EQ(
desktop_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(), JsReplace("(async () => {"
" let e = await self.showDirectoryPicker({ "
"id: $1, startIn: 'music' });"
" self.selected_entry = e;"
" return e.name; })()",
id)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(desktop_dir, dialog_params.default_path);
// (E) A directory handle is specified via `startIn`, so prioritize this over
// the stored ID.
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({dir_handle}, &dialog_params));
EXPECT_EQ(
dir_handle.BaseName().AsUTF8Unsafe(),
EvalJs(shell(), JsReplace("(async () => {"
" let e = await self.showDirectoryPicker({ "
"id: $1, startIn: self.handle });"
" self.selected_entry = e;"
" return e.name; })()",
id)));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
EXPECT_EQ(dir_handle, dialog_params.default_path);
}
IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, PickerTitle) {
base::FilePath test_dir = CreateTestDir();
FakeFileSystemAccessPermissionContext permission_context;
static_cast<FileSystemAccessManagerImpl*>(
shell()
->web_contents()
->GetBrowserContext()
->GetStoragePartition(shell()->web_contents()->GetSiteInstance())
->GetFileSystemAccessEntryFactory())
->SetPermissionContextForTesting(&permission_context);
SelectFileDialogParams dialog_params;
ui::SelectFileDialog::SetFactory(
new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
ASSERT_TRUE(
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
EvalJs(shell(),
"(async () => {"
" let e = await self.showDirectoryPicker();"
" self.selected_entry = e;"
" return e.name; })()"));
EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
// Check that the title of the file picker was plumbed through correctly.
EXPECT_EQ(FakeFileSystemAccessPermissionContext::kPickerTitle,
dialog_params.title);
}
} // namespace content