blob: aedbdd92d8fae129b95dd024294abe394ee490db [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/file_system_access/global_file_system_access.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_directory_handle.mojom-blink.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom-blink.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_manager.mojom-blink.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/html/forms/html_button_element.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/script/classic_script.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
namespace blink {
class MockFileSystemAccessManager
: public mojom::blink::FileSystemAccessManager {
public:
MockFileSystemAccessManager(BrowserInterfaceBrokerProxy& broker,
base::OnceClosure reached_callback)
: reached_callback_(std::move(reached_callback)), broker_(broker) {
broker_.SetBinderForTesting(
mojom::blink::FileSystemAccessManager::Name_,
WTF::BindRepeating(
&MockFileSystemAccessManager::BindFileSystemAccessManager,
WTF::Unretained(this)));
}
MockFileSystemAccessManager(BrowserInterfaceBrokerProxy& broker)
: broker_(broker) {
broker_.SetBinderForTesting(
mojom::blink::FileSystemAccessManager::Name_,
WTF::BindRepeating(
&MockFileSystemAccessManager::BindFileSystemAccessManager,
WTF::Unretained(this)));
}
~MockFileSystemAccessManager() override {
broker_.SetBinderForTesting(mojom::blink::FileSystemAccessManager::Name_,
{});
}
using ChooseEntriesResponseCallback =
base::OnceCallback<void(ChooseEntriesCallback callback)>;
void SetQuitClosure(base::OnceClosure reached_callback) {
reached_callback_ = std::move(reached_callback);
}
// Unused for these tests.
void GetSandboxedFileSystem(
GetSandboxedFileSystemCallback callback) override {}
void ChooseEntries(mojom::blink::FilePickerOptionsPtr options,
mojom::blink::CommonFilePickerOptionsPtr common_options,
ChooseEntriesCallback callback) override {
if (choose_entries_response_callback_) {
std::move(choose_entries_response_callback_).Run(std::move(callback));
}
if (reached_callback_)
std::move(reached_callback_).Run();
}
void SetChooseEntriesResponse(ChooseEntriesResponseCallback callback) {
choose_entries_response_callback_ = std::move(callback);
}
void GetFileHandleFromToken(
mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>,
mojo::PendingReceiver<mojom::blink::FileSystemAccessFileHandle>)
override {}
void GetDirectoryHandleFromToken(
mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>,
mojo::PendingReceiver<mojom::blink::FileSystemAccessDirectoryHandle>)
override {}
void GetEntryFromDataTransferToken(
mojo::PendingRemote<
blink::mojom::blink::FileSystemAccessDataTransferToken> token,
GetEntryFromDataTransferTokenCallback callback) override {}
private:
void BindFileSystemAccessManager(mojo::ScopedMessagePipeHandle handle) {
receivers_.Add(this,
mojo::PendingReceiver<mojom::blink::FileSystemAccessManager>(
std::move(handle)));
}
base::OnceClosure reached_callback_;
ChooseEntriesResponseCallback choose_entries_response_callback_;
mojo::ReceiverSet<mojom::blink::FileSystemAccessManager> receivers_;
BrowserInterfaceBrokerProxy& broker_;
};
class GlobalFileSystemAccessTest : public PageTestBase {
public:
void SetUp() override {
PageTestBase::SetUp();
Navigate("http://localhost");
GetDocument().GetSettings()->SetScriptEnabled(true);
}
void Navigate(const String& destinationUrl) {
const KURL& url = KURL(NullURL(), destinationUrl);
auto navigation_params =
WebNavigationParams::CreateWithHTMLBufferForTesting(
SharedBuffer::Create(), url);
GetDocument().GetFrame()->Loader().CommitNavigation(
std::move(navigation_params), /*extra_data=*/nullptr);
blink::test::RunPendingTasks();
ASSERT_EQ(url.GetString(), GetDocument().Url().GetString());
}
};
TEST_F(GlobalFileSystemAccessTest, UserActivationRequiredOtherwiseDenied) {
LocalFrame* frame = &GetFrame();
EXPECT_FALSE(frame->HasStickyUserActivation());
MockFileSystemAccessManager manager(frame->GetBrowserInterfaceBroker());
manager.SetChooseEntriesResponse(WTF::Bind(
[](MockFileSystemAccessManager::ChooseEntriesCallback callback) {
FAIL();
}));
ClassicScript::CreateUnspecifiedScript(
ScriptSourceCode("window.showOpenFilePicker();"))
->RunScript(GetFrame().DomWindow());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(frame->HasStickyUserActivation());
}
TEST_F(GlobalFileSystemAccessTest, UserActivationChooseEntriesSuccessful) {
LocalFrame* frame = &GetFrame();
EXPECT_FALSE(frame->HasStickyUserActivation());
LocalFrame::NotifyUserActivation(
frame, mojom::UserActivationNotificationType::kTest);
EXPECT_TRUE(frame->HasStickyUserActivation());
base::RunLoop manager_run_loop;
MockFileSystemAccessManager manager(frame->GetBrowserInterfaceBroker(),
manager_run_loop.QuitClosure());
manager.SetChooseEntriesResponse(WTF::Bind(
[](MockFileSystemAccessManager::ChooseEntriesCallback callback) {
auto error = mojom::blink::FileSystemAccessError::New();
error->status = mojom::blink::FileSystemAccessStatus::kOk;
error->message = "";
mojo::PendingRemote<mojom::blink::FileSystemAccessFileHandle>
pending_remote;
ignore_result(pending_remote.InitWithNewPipeAndPassReceiver());
auto handle = mojom::blink::FileSystemAccessHandle::NewFile(
std::move(pending_remote));
auto entry = mojom::blink::FileSystemAccessEntry::New(std::move(handle),
"foo.txt");
Vector<mojom::blink::FileSystemAccessEntryPtr> entries;
entries.push_back(std::move(entry));
std::move(callback).Run(std::move(error), std::move(entries));
}));
ClassicScript::CreateUnspecifiedScript(
ScriptSourceCode("window.showOpenFilePicker();"))
->RunScript(GetFrame().DomWindow());
manager_run_loop.Run();
// Mock Manager finished sending data over the mojo pipe.
// Clearing the user activation.
frame->ClearUserActivation();
EXPECT_FALSE(frame->HasStickyUserActivation());
// Let blink-side receiver process the response and set the user activation
// again.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(frame->HasStickyUserActivation());
}
TEST_F(GlobalFileSystemAccessTest, UserActivationChooseEntriesErrors) {
LocalFrame* frame = &GetFrame();
EXPECT_FALSE(frame->HasStickyUserActivation());
using mojom::blink::FileSystemAccessStatus;
FileSystemAccessStatus statuses[] = {
FileSystemAccessStatus::kPermissionDenied,
FileSystemAccessStatus::kInvalidState,
FileSystemAccessStatus::kInvalidArgument,
FileSystemAccessStatus::kOperationFailed,
// kOperationAborted is when the user cancels the file selection.
FileSystemAccessStatus::kOperationAborted,
};
MockFileSystemAccessManager manager(frame->GetBrowserInterfaceBroker());
for (const FileSystemAccessStatus& status : statuses) {
LocalFrame::NotifyUserActivation(
frame, mojom::UserActivationNotificationType::kTest);
EXPECT_TRUE(frame->HasStickyUserActivation());
base::RunLoop manager_run_loop;
manager.SetQuitClosure(manager_run_loop.QuitClosure());
manager.SetChooseEntriesResponse(WTF::Bind(
[](mojom::blink::FileSystemAccessStatus status,
MockFileSystemAccessManager::ChooseEntriesCallback callback) {
auto error = mojom::blink::FileSystemAccessError::New();
error->status = status;
error->message = "";
Vector<mojom::blink::FileSystemAccessEntryPtr> entries;
std::move(callback).Run(std::move(error), std::move(entries));
},
status));
ClassicScript::CreateUnspecifiedScript(
ScriptSourceCode("window.showOpenFilePicker();"))
->RunScript(GetFrame().DomWindow());
manager_run_loop.Run();
// Mock Manager finished sending data over the mojo pipe.
// Clearing the user activation.
frame->ClearUserActivation();
EXPECT_FALSE(frame->HasStickyUserActivation());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(frame->HasStickyUserActivation());
}
}
} // namespace blink