Commit bf00796a authored by Hazim Mohamed's avatar Hazim Mohamed Committed by Commit Bot

[NativeFS] Create a new NativeFileSystemTransferToken implementation

In order to implement Drag & Drop, extend the abstract base class
NativeFileSystemTransferToken with an implementation that's not tightly
coupled to an origin. When exchanging this token for an
NativeFileSystemFileHandle/NativeFileSystemDirectoryHandle, a caller
should pass in a BindingContext and the NativeFileSystemManager will
lazily create the handle.

Bug: 1080811
Change-Id: If6daaf628ec27d2e23339901d3db22d9c0386e08
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2243997Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarMarijn Kruisselbrink <mek@chromium.org>
Reviewed-by: default avatarDarwin Huang <huangdarwin@chromium.org>
Commit-Queue: Hazim Mohamed <hazimmohamed@google.com>
Cr-Commit-Position: refs/heads/master@{#784217}
parent a737c011
......@@ -99,6 +99,7 @@ bool CreateOrTruncateFile(const base::FilePath& path) {
bool IsValidTransferToken(
NativeFileSystemTransferTokenImpl* token,
const url::Origin& expected_origin,
int process_id,
NativeFileSystemTransferTokenImpl::HandleType expected_handle_type) {
if (!token) {
return false;
......@@ -108,9 +109,10 @@ bool IsValidTransferToken(
return false;
}
if (!token->MatchesOrigin(expected_origin)) {
if (!token->MatchesOriginAndPID(expected_origin, process_id)) {
return false;
}
return true;
}
......@@ -522,6 +524,18 @@ NativeFileSystemManagerImpl::CreateFileWriter(
return result;
}
void NativeFileSystemManagerImpl::CreateTransferTokenFromPath(
const base::FilePath& file_path,
bool is_directory,
int renderer_id,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver) {
auto token_impl = NativeFileSystemTransferTokenImpl::CreateFromPath(
file_path, is_directory, this, std::move(receiver), renderer_id);
auto token = token_impl->token();
transfer_tokens_.emplace(token, std::move(token_impl));
}
void NativeFileSystemManagerImpl::CreateTransferToken(
const NativeFileSystemFileHandleImpl& file,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
......@@ -530,6 +544,16 @@ void NativeFileSystemManagerImpl::CreateTransferToken(
/*is_directory=*/false, std::move(receiver));
}
void NativeFileSystemManagerImpl::CreateTransferTokenForTesting(
const storage::FileSystemURL& url,
const SharedHandleState& handle_state,
bool is_directory,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver) {
return CreateTransferTokenImpl(url, handle_state, is_directory,
std::move(receiver));
}
void NativeFileSystemManagerImpl::CreateTransferToken(
const NativeFileSystemDirectoryHandleImpl& directory,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
......@@ -561,7 +585,7 @@ void NativeFileSystemManagerImpl::DidResolveTransferTokenForFileHandle(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!IsValidTransferToken(
resolved_token, binding_context.origin,
resolved_token, binding_context.origin, binding_context.process_id(),
NativeFileSystemTransferTokenImpl::HandleType::kFile)) {
// Fail silently. In practice, the NativeFileSystemManager should not
// receive any invalid tokens. Before redeeming a token, the render process
......@@ -587,7 +611,7 @@ void NativeFileSystemManagerImpl::DidResolveTransferTokenForDirectoryHandle(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!IsValidTransferToken(
resolved_token, binding_context.origin,
resolved_token, binding_context.origin, binding_context.process_id(),
NativeFileSystemTransferTokenImpl::HandleType::kDirectory)) {
// Fail silently. See comment above in
// DidResolveTransferTokenForFileHandle() for details.
......@@ -783,10 +807,7 @@ void NativeFileSystemManagerImpl::CreateTransferTokenImpl(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto token_impl = NativeFileSystemTransferTokenImpl::Create(
url, handle_state,
is_directory ? NativeFileSystemTransferTokenImpl::HandleType::kDirectory
: NativeFileSystemTransferTokenImpl::HandleType::kFile,
this, std::move(receiver));
url, handle_state, is_directory, this, std::move(receiver));
auto token = token_impl->token();
transfer_tokens_.emplace(token, std::move(token_impl));
}
......
......@@ -163,6 +163,27 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver);
// Create a transfer token without an underlying FileHandle/DirectoryHandle.
// These transfer tokens can only be redeemed by a process with ID matching
// |renderer_id|. When redeemed, these tokens will lazily create
// FileHandles/DirectoryHandles. These type of TransferTokens are used for
// transferring file information between the browser and renderer during drag
// and drop operations.
void CreateTransferTokenFromPath(
const base::FilePath& file_path,
bool is_directory,
int renderer_id,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver);
// Create a transfer token attached to file/directory at |url|.
void CreateTransferTokenForTesting(
const storage::FileSystemURL& url,
const SharedHandleState& handle_state,
bool is_directory,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver);
// Given a mojom transfer token, looks up the token in our internal list of
// valid tokens. Calls the callback with the found token, or nullptr if no
// valid token was found.
......
......@@ -584,4 +584,175 @@ TEST_F(NativeFileSystemManagerImplTest,
EXPECT_EQ(ask_grant2_, token->GetWriteGrant());
}
} // namespace content
// NativeFileSystemManager should successfully resolve an originless
// NativeFileSystemTransferToken representing a NativeFileSystemFileEntry
// into a valid Remote<blink::mojom::NativeFileSystemFileHandle>, given
// that the PID is valid.
TEST_F(NativeFileSystemManagerImplTest,
RedeemTransferTokenWithoutOrigin_File_ValidPID) {
base::FilePath file_path = dir_.GetPath().AppendASCII("mr_file");
ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
// A NativeFileSystemTransferToken created without an explicit origin and
// attached to an implementation only through
// NativeFileSystemManager::GetFileHandleFromToken.
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
manager_->CreateTransferTokenFromPath(
file_path, /*is_directory=*/false, kBindingContext.process_id(),
token_remote.InitWithNewPipeAndPassReceiver());
// Expect permission requests when the token is sent to be redeemed.
EXPECT_CALL(permission_context_,
GetReadPermissionGrant(
kTestOrigin, file_path, /*is_directory=*/false,
NativeFileSystemPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(allow_grant_));
EXPECT_CALL(permission_context_,
GetWritePermissionGrant(
kTestOrigin, file_path, /*is_directory=*/false,
NativeFileSystemPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(allow_grant_));
mojo::Remote<blink::mojom::NativeFileSystemFileHandle> file_remote;
manager_remote_->GetFileHandleFromToken(
std::move(token_remote), file_remote.BindNewPipeAndPassReceiver());
// A NativeFileSystemTransferToken created with an explicitly defined
// NativeFileSystemFileHandle implementation to compare to the redeemed
// token above.
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken>
explicit_token_remote;
auto test_file_url =
manager_->CreateFileSystemURLFromPath(kBindingContext.origin, file_path);
manager_->CreateTransferTokenForTesting(
test_file_url.url,
{allow_grant_, allow_grant_, test_file_url.file_system},
/*is_directory=*/false,
explicit_token_remote.InitWithNewPipeAndPassReceiver());
// Expect the explicitly defined file handle to be identical to the one
// returned by GetFileHandleFromToken.
base::RunLoop file_matches_loop;
file_remote->IsSameEntry(
std::move(explicit_token_remote),
base::BindLambdaForTesting(
[&](blink::mojom::NativeFileSystemErrorPtr result, bool same_entry) {
file_matches_loop.Quit();
ASSERT_EQ(blink::mojom::NativeFileSystemStatus::kOk,
result->status);
EXPECT_TRUE(same_entry);
}));
file_matches_loop.Run();
}
// NativeFileSystemManager should refuse to resolve a
// NativeFileSystemTransferToken representing a NativeFileSystemFileEntry if the
// PID of the redeeming process doesn't match the one assigned at creation.
TEST_F(NativeFileSystemManagerImplTest,
RedeemTransferTokenWithoutOrigin_File_InvalidPID) {
base::FilePath file_path = dir_.GetPath().AppendASCII("mr_file");
ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
// Create a transfer token with an PID different than the process attempting
// to redeem to the token.
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
manager_->CreateTransferTokenFromPath(
file_path, /*is_directory=*/false,
/*renderer_id=*/kBindingContext.process_id() - 1,
token_remote.InitWithNewPipeAndPassReceiver());
mojo::Remote<blink::mojom::NativeFileSystemFileHandle> file_remote;
manager_remote_->GetFileHandleFromToken(
std::move(token_remote), file_remote.BindNewPipeAndPassReceiver());
// In order to make sure that |file_remote| doesn't get bound to an
// implementation, we wait for all messages to be sent and ensure that
// |file_remote| is not connected afterwards.
file_remote.FlushForTesting();
EXPECT_FALSE(file_remote.is_connected());
}
// NativeFileSystemManager should successfully resolve an originless
// NativeFileSystemTransferToken representing a NativeFileSystemDirectoryEntry
// into a valid Remote<blink::mojom::NativeFileSystemDirectoryHandle>, given
// that the PID is valid.
TEST_F(NativeFileSystemManagerImplTest,
RedeemTransferTokenWithoutOrigin_Directory_ValidPID) {
const base::FilePath kDirPath = dir_.GetPath().AppendASCII("mr_dir");
ASSERT_TRUE(base::CreateDirectory(kDirPath));
// A NativeFileSystemTransferToken created without an explicit origin and
// attached to an implementation only through
// NativeFileSystemManager::GetFileHandleFromToken.
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
manager_->CreateTransferTokenFromPath(
kDirPath, /*is_directory=*/true, kBindingContext.process_id(),
token_remote.InitWithNewPipeAndPassReceiver());
// Expect permission requests when the token is sent to be redeemed.
EXPECT_CALL(permission_context_,
GetReadPermissionGrant(
kTestOrigin, kDirPath, /*is_directory=*/true,
NativeFileSystemPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(allow_grant_));
EXPECT_CALL(permission_context_,
GetWritePermissionGrant(
kTestOrigin, kDirPath, /*is_directory=*/true,
NativeFileSystemPermissionContext::UserAction::kOpen))
.WillOnce(testing::Return(allow_grant_));
mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> dir_remote;
manager_remote_->GetDirectoryHandleFromToken(
std::move(token_remote), dir_remote.BindNewPipeAndPassReceiver());
// Use |dir_remote| to create a child of the directory, and pass the test if
// the child was successfully created at the expected path. Block until this
// happens or test times out.
base::RunLoop await_get_directory;
const std::string kChildDirectory = "child_dir";
dir_remote->GetDirectory(
kChildDirectory, /*create=*/true,
base::BindLambdaForTesting(
[&](blink::mojom::NativeFileSystemErrorPtr result,
mojo::PendingRemote<blink::mojom::NativeFileSystemDirectoryHandle>
directory_handle) {
await_get_directory.Quit();
ASSERT_EQ(blink::mojom::NativeFileSystemStatus::kOk,
result->status);
EXPECT_TRUE(
kDirPath.IsParent(kDirPath.AppendASCII(kChildDirectory)));
}));
await_get_directory.Run();
}
// NativeFileSystemManager should refuse to resolve a
// NativeFileSystemTransferToken representing a NativeFileSystemDirectoryEntry
// if the PID of the redeeming process doesn't match the one assigned at
// creation.
TEST_F(NativeFileSystemManagerImplTest,
RedeemTransferTokenWithoutOrigin_Directory_InvalidPID) {
const base::FilePath kDirPath = dir_.GetPath().AppendASCII("mr_dir");
ASSERT_TRUE(base::CreateDirectory(kDirPath));
// Create a transfer token with a PID different than the process attempting
// to redeem to the transfer token.
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token_remote;
manager_->CreateTransferTokenFromPath(
kDirPath, /*is_directory=*/true,
/*renderer_id=*/kBindingContext.process_id() - 1,
token_remote.InitWithNewPipeAndPassReceiver());
mojo::Remote<blink::mojom::NativeFileSystemDirectoryHandle> dir_remote;
manager_remote_->GetDirectoryHandleFromToken(
std::move(token_remote), dir_remote.BindNewPipeAndPassReceiver());
// In order to make sure that |dir_remote| doesn't get bound to an
// implementation, we wait for all messages to be sent and ensure that
// |dir_remote| is not connected afterwards.
dir_remote.FlushForTesting();
EXPECT_FALSE(dir_remote.is_connected());
}
} // namespace content
\ No newline at end of file
......@@ -6,6 +6,7 @@
#include "content/browser/native_file_system/native_file_system_directory_handle_impl.h"
#include "content/browser/native_file_system/native_file_system_file_handle_impl.h"
#include "third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom.h"
namespace content {
......@@ -36,7 +37,8 @@ class NativeFileSystemTransferTokenImplForHandles
}
~NativeFileSystemTransferTokenImplForHandles() override = default;
bool MatchesOrigin(const url::Origin& origin) const override {
bool MatchesOriginAndPID(const url::Origin& origin,
int process_id) const override {
return url_.origin() == origin;
}
......@@ -73,6 +75,80 @@ class NativeFileSystemTransferTokenImplForHandles
const NativeFileSystemManagerImpl::SharedHandleState handle_state_;
};
// Concrete implementation for Transfer Tokens created with
// a base::FilePath and no associated origin or implementation at the time of.
// creation. These tokens serve as a wrapper around |file_path| and can be
// passed between processes. Used for transferring dropped file information
// between browser and renderer processes during drag and drop operations.
class NativeFileSystemTransferTokenFromPath
: public NativeFileSystemTransferTokenImpl {
public:
NativeFileSystemTransferTokenFromPath(
const base::FilePath& file_path,
HandleType type,
NativeFileSystemManagerImpl* manager,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver,
int renderer_process_id)
: NativeFileSystemTransferTokenImpl(type, manager, std::move(receiver)),
file_path_(file_path),
renderer_process_id_(renderer_process_id) {}
~NativeFileSystemTransferTokenFromPath() override = default;
bool MatchesOriginAndPID(const url::Origin& origin,
int process_id) const override {
return renderer_process_id_ == process_id;
}
const storage::FileSystemURL* GetAsFileSystemURL() const override {
return nullptr;
}
NativeFileSystemPermissionGrant* GetReadGrant() const override {
return nullptr;
}
NativeFileSystemPermissionGrant* GetWriteGrant() const override {
return nullptr;
}
std::unique_ptr<NativeFileSystemFileHandleImpl> CreateFileHandle(
const NativeFileSystemManagerImpl::BindingContext& binding_context)
override {
DCHECK_EQ(type_, HandleType::kFile);
NativeFileSystemManagerImpl::FileSystemURLAndFSHandle url =
manager_->CreateFileSystemURLFromPath(binding_context.origin,
file_path_);
NativeFileSystemManagerImpl::SharedHandleState shared_handle_state =
manager_->GetSharedHandleStateForPath(
file_path_, binding_context.origin, std::move(url.file_system),
/*is_directory=*/false,
NativeFileSystemPermissionContext::UserAction::kOpen);
return std::make_unique<NativeFileSystemFileHandleImpl>(
manager_, binding_context, url.url, shared_handle_state);
}
std::unique_ptr<NativeFileSystemDirectoryHandleImpl> CreateDirectoryHandle(
const NativeFileSystemManagerImpl::BindingContext& binding_context)
override {
DCHECK_EQ(type_, HandleType::kDirectory);
NativeFileSystemManagerImpl::FileSystemURLAndFSHandle url =
manager_->CreateFileSystemURLFromPath(binding_context.origin,
file_path_);
NativeFileSystemManagerImpl::SharedHandleState shared_handle_state =
manager_->GetSharedHandleStateForPath(
file_path_, binding_context.origin, url.file_system,
/*is_directory=*/true,
NativeFileSystemPermissionContext::UserAction::kOpen);
return std::make_unique<NativeFileSystemDirectoryHandleImpl>(
manager_, binding_context, url.url, shared_handle_state);
}
private:
const base::FilePath file_path_;
const int renderer_process_id_;
};
} // namespace
// static
......@@ -80,12 +156,27 @@ std::unique_ptr<NativeFileSystemTransferTokenImpl>
NativeFileSystemTransferTokenImpl::Create(
const storage::FileSystemURL& url,
const NativeFileSystemManagerImpl::SharedHandleState& handle_state,
HandleType type,
bool is_directory,
NativeFileSystemManagerImpl* manager,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver) {
return std::make_unique<NativeFileSystemTransferTokenImplForHandles>(
url, handle_state, type, manager, std::move(receiver));
url, handle_state,
is_directory ? HandleType::kDirectory : HandleType::kFile, manager,
std::move(receiver));
}
// static
std::unique_ptr<NativeFileSystemTransferTokenImpl>
NativeFileSystemTransferTokenImpl::CreateFromPath(
const base::FilePath file_path,
bool is_directory,
NativeFileSystemManagerImpl* manager,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken> receiver,
int renderer_process_id) {
return std::make_unique<NativeFileSystemTransferTokenFromPath>(
file_path, is_directory ? HandleType::kDirectory : HandleType::kFile,
manager, std::move(receiver), renderer_process_id);
}
NativeFileSystemTransferTokenImpl::NativeFileSystemTransferTokenImpl(
......
......@@ -31,11 +31,20 @@ class CONTENT_EXPORT NativeFileSystemTransferTokenImpl
static std::unique_ptr<NativeFileSystemTransferTokenImpl> Create(
const storage::FileSystemURL& url,
const NativeFileSystemManagerImpl::SharedHandleState& handle_state,
HandleType type,
bool is_directory,
NativeFileSystemManagerImpl* manager,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver);
// Create a token that is not associated with any origin.
static std::unique_ptr<NativeFileSystemTransferTokenImpl> CreateFromPath(
const base::FilePath file_path,
bool is_directory,
NativeFileSystemManagerImpl* manager,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver,
int renderer_process_id);
NativeFileSystemTransferTokenImpl(
HandleType type,
NativeFileSystemManagerImpl* manager,
......@@ -46,8 +55,10 @@ class CONTENT_EXPORT NativeFileSystemTransferTokenImpl
const base::UnguessableToken& token() const { return token_; }
HandleType type() const { return type_; }
// Returns true if |origin| is allowed to use this token.
virtual bool MatchesOrigin(const url::Origin& origin) const = 0;
// Returns true if |origin| is allowed to use this token. Where the transfer
// token isn't associated with an origin, |process_id| is checked.
virtual bool MatchesOriginAndPID(const url::Origin& origin,
int process_id) const = 0;
// Can return nullptr if this token isn't represented by a FileSystemURL.
virtual const storage::FileSystemURL* GetAsFileSystemURL() const = 0;
......
......@@ -42,6 +42,7 @@ class CONTENT_EXPORT NativeFileSystemEntryFactory
GURL url;
GlobalFrameRoutingId frame_id;
bool is_worker() const { return !frame_id; }
int process_id() const { return frame_id.child_id; }
};
// Creates a new NativeFileSystemEntryPtr from the path to a file. Assumes the
......
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