blob: 64bf949bdc2e90088717ecad84dd694f400aac34 [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.
#ifndef CONTENT_BROWSER_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_MANAGER_IMPL_H_
#define CONTENT_BROWSER_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_MANAGER_IMPL_H_
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequence_bound.h"
#include "base/types/pass_key.h"
#include "components/download/public/common/quarantine_connection.h"
#include "components/services/storage/public/mojom/native_file_system_context.mojom.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/file_system_access/file_system_chooser.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/native_file_system_entry_factory.h"
#include "content/public/browser/native_file_system_permission_context.h"
#include "content/public/browser/native_file_system_permission_grant.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
#include "storage/browser/file_system/file_system_url.h"
#include "third_party/blink/public/mojom/file_system_access/native_file_system_drag_drop_token.mojom.h"
#include "third_party/blink/public/mojom/file_system_access/native_file_system_file_writer.mojom.h"
#include "third_party/blink/public/mojom/file_system_access/native_file_system_manager.mojom.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
namespace storage {
class FileSystemContext;
class FileSystemOperationRunner;
} // namespace storage
namespace content {
class NativeFileSystemFileHandleImpl;
class NativeFileSystemDirectoryHandleImpl;
class NativeFileSystemTransferTokenImpl;
class NativeFileSystemDragDropTokenImpl;
class NativeFileSystemFileWriterImpl;
class StoragePartitionImpl;
// This is the browser side implementation of the
// NativeFileSystemManager mojom interface. This is the main entry point for
// the native file system API in the browser process.Instances of this class are
// owned by StoragePartitionImpl.
//
// This class owns all the NativeFileSystemFileHandleImpl,
// NativeFileSystemDirectoryHandleImpl and NativeFileSystemTransferTokenImpl
// instances for a specific storage partition.
//
// This class is not thread safe, it must be constructed and used on the UI
// thread only.
class CONTENT_EXPORT NativeFileSystemManagerImpl
: public NativeFileSystemEntryFactory,
public blink::mojom::NativeFileSystemManager,
public storage::mojom::NativeFileSystemContext {
public:
using BindingContext = NativeFileSystemEntryFactory::BindingContext;
using PassKey = base::PassKey<NativeFileSystemManagerImpl>;
// State that is shared between handles that are derived from each other.
// Handles that are created through ChooseEntries or GetSandboxedFileSystem
// get new values for these properties, while any handles derived from those
// (i.e. children of a directory) will inherit these properties from their
// parent.
struct CONTENT_EXPORT SharedHandleState {
SharedHandleState(
scoped_refptr<NativeFileSystemPermissionGrant> read_grant,
scoped_refptr<NativeFileSystemPermissionGrant> write_grant,
storage::IsolatedContext::ScopedFSHandle file_system);
SharedHandleState(const SharedHandleState& other);
~SharedHandleState();
// Should never be null. These are the read and write permissions for this
// handle.
const scoped_refptr<NativeFileSystemPermissionGrant> read_grant;
const scoped_refptr<NativeFileSystemPermissionGrant> write_grant;
// Can be empty, if this handle is not backed by an isolated file system.
const storage::IsolatedContext::ScopedFSHandle file_system;
};
// The caller is responsible for ensuring that |permission_context| outlives
// this instance.
NativeFileSystemManagerImpl(
scoped_refptr<storage::FileSystemContext> context,
scoped_refptr<ChromeBlobStorageContext> blob_context,
NativeFileSystemPermissionContext* permission_context,
bool off_the_record);
void BindReceiver(
const BindingContext& binding_context,
mojo::PendingReceiver<blink::mojom::NativeFileSystemManager> receiver);
void BindInternalsReceiver(
mojo::PendingReceiver<storage::mojom::NativeFileSystemContext> receiver);
// blink::mojom::NativeFileSystemManager:
void GetSandboxedFileSystem(GetSandboxedFileSystemCallback callback) override;
void ChooseEntries(
blink::mojom::ChooseFileSystemEntryType type,
std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
blink::mojom::CommonDirectory starting_directory,
bool include_accepts_all,
ChooseEntriesCallback callback) override;
void GetFileHandleFromToken(
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token,
mojo::PendingReceiver<blink::mojom::NativeFileSystemFileHandle>
file_handle_receiver) override;
void GetDirectoryHandleFromToken(
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token,
mojo::PendingReceiver<blink::mojom::NativeFileSystemDirectoryHandle>
directory_handle_receiver) override;
void GetEntryFromDragDropToken(
mojo::PendingRemote<blink::mojom::NativeFileSystemDragDropToken> token,
GetEntryFromDragDropTokenCallback token_resolved_callback) override;
// storage::mojom::NativeFileSystemContext:
void SerializeHandle(
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token,
SerializeHandleCallback callback) override;
void DeserializeHandle(
const url::Origin& origin,
const std::vector<uint8_t>& bits,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken> token)
override;
// NativeFileSystemEntryFactory:
blink::mojom::NativeFileSystemEntryPtr CreateFileEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& file_path,
UserAction user_action) override;
blink::mojom::NativeFileSystemEntryPtr CreateDirectoryEntryFromPath(
const BindingContext& binding_context,
PathType path_type,
const base::FilePath& directory_path,
UserAction user_action) override;
// Creates a new NativeFileSystemFileHandleImpl for a given url. Assumes the
// passed in URL is valid and represents a file.
mojo::PendingRemote<blink::mojom::NativeFileSystemFileHandle>
CreateFileHandle(const BindingContext& binding_context,
const storage::FileSystemURL& url,
const SharedHandleState& handle_state);
// Creates a new NativeFileSystemDirectoryHandleImpl for a given url. Assumes
// the passed in URL is valid and represents a directory.
mojo::PendingRemote<blink::mojom::NativeFileSystemDirectoryHandle>
CreateDirectoryHandle(const BindingContext& context,
const storage::FileSystemURL& url,
const SharedHandleState& handle_state);
// Creates a new NativeFileSystemFileWriterImpl for a given target and
// swap file URLs. Assumes the passed in URLs are valid and represent files.
mojo::PendingRemote<blink::mojom::NativeFileSystemFileWriter>
CreateFileWriter(const BindingContext& binding_context,
const storage::FileSystemURL& url,
const storage::FileSystemURL& swap_url,
const SharedHandleState& handle_state,
bool auto_close);
// Returns a raw pointer to a newly created NativeFileSystemFileWriterImpl.
// Useful for tests
NativeFileSystemFileWriterImpl* CreateFileWriter(
const BindingContext& binding_context,
const storage::FileSystemURL& url,
const storage::FileSystemURL& swap_url,
const SharedHandleState& handle_state,
mojo::PendingReceiver<blink::mojom::NativeFileSystemFileWriter> receiver,
bool has_transient_user_activation,
bool auto_close,
download::QuarantineConnectionCallback quarantine_connection_callback);
// Create a transfer token for a specific file or directory.
void CreateTransferToken(
const NativeFileSystemFileHandleImpl& file,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver);
void CreateTransferToken(
const NativeFileSystemDirectoryHandleImpl& directory,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver);
// Creates an instance of NativeFileSystemDragDropTokenImpl with `file_path`
// and `renderer_id` and attaches the instance to `receiver`. The `receiver`'s
// associated remote can be redeemed for a NativeFileSystemEntry object by a
// process with ID matching `renderer_id`.
void CreateNativeFileSystemDragDropToken(
PathType path_type,
const base::FilePath& file_path,
int renderer_id,
mojo::PendingReceiver<blink::mojom::NativeFileSystemDragDropToken>
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.
using ResolvedTokenCallback =
base::OnceCallback<void(NativeFileSystemTransferTokenImpl*)>;
void ResolveTransferToken(
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token,
ResolvedTokenCallback callback);
storage::FileSystemContext* context() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return context_.get();
}
ChromeBlobStorageContext* blob_context() { return blob_context_.get(); }
const base::SequenceBound<storage::FileSystemOperationRunner>&
operation_runner();
NativeFileSystemPermissionContext* permission_context() {
return permission_context_;
}
bool is_off_the_record() const { return off_the_record_; }
void SetPermissionContextForTesting(
NativeFileSystemPermissionContext* permission_context) {
permission_context_ = permission_context;
}
void SetFilePickerResultForTesting(
base::Optional<FileSystemChooser::ResultEntry> result_entry) {
auto_file_picker_result_for_test_ = result_entry;
}
// Remove |writer| from |writer_receivers|. It is an error to try to remove
// a writer that doesn't exist.
void RemoveFileWriter(NativeFileSystemFileWriterImpl* writer);
// Remove |token| from |transfer_tokens_|. It is an error to try to remove
// a token that doesn't exist.
void RemoveToken(const base::UnguessableToken& token);
// Remove `token` from `drag_drop_tokens_`. It is an error to try to remove a
// token that doesn't exist.
void RemoveDragDropToken(const base::UnguessableToken& token);
SharedHandleState GetSharedHandleStateForPath(
const base::FilePath& path,
const url::Origin& origin,
storage::IsolatedContext::ScopedFSHandle file_system,
NativeFileSystemPermissionContext::HandleType handle_type,
NativeFileSystemPermissionContext::UserAction user_action);
// Creates a FileSystemURL which corresponds to a FilePath and Origin.
struct FileSystemURLAndFSHandle {
storage::FileSystemURL url;
std::string base_name;
storage::IsolatedContext::ScopedFSHandle file_system;
};
FileSystemURLAndFSHandle CreateFileSystemURLFromPath(
const url::Origin& origin,
PathType path_type,
const base::FilePath& path);
private:
friend class NativeFileSystemFileHandleImpl;
~NativeFileSystemManagerImpl() override;
void SetDefaultPathAndShowPicker(
const BindingContext& context,
blink::mojom::ChooseFileSystemEntryType type,
std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
bool include_accepts_all,
base::FilePath default_directory,
ChooseEntriesCallback callback,
base::File::Error result);
void DidOpenSandboxedFileSystem(const BindingContext& binding_context,
GetSandboxedFileSystemCallback callback,
const GURL& root,
const std::string& filesystem_name,
base::File::Error result);
void DidChooseEntries(const BindingContext& binding_context,
const FileSystemChooser::Options& options,
ChooseEntriesCallback callback,
blink::mojom::NativeFileSystemErrorPtr result,
std::vector<FileSystemChooser::ResultEntry> entries);
void DidVerifySensitiveDirectoryAccess(
const BindingContext& binding_context,
const FileSystemChooser::Options& options,
ChooseEntriesCallback callback,
std::vector<FileSystemChooser::ResultEntry> entries,
NativeFileSystemPermissionContext::SensitiveDirectoryResult result);
void DidCreateAndTruncateSaveFile(const BindingContext& binding_context,
const FileSystemChooser::ResultEntry& entry,
FileSystemURLAndFSHandle url,
ChooseEntriesCallback callback,
bool success);
void DidChooseDirectory(
const BindingContext& binding_context,
const FileSystemChooser::ResultEntry& entry,
ChooseEntriesCallback callback,
const SharedHandleState& shared_handle_state,
NativeFileSystemPermissionGrant::PermissionRequestOutcome outcome);
void CreateTransferTokenImpl(
const storage::FileSystemURL& url,
const url::Origin& origin,
const SharedHandleState& handle_state,
NativeFileSystemPermissionContext::HandleType handle_type,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken>
receiver);
void DoResolveTransferToken(
mojo::Remote<blink::mojom::NativeFileSystemTransferToken>,
ResolvedTokenCallback callback,
const base::UnguessableToken& token);
void DidResolveTransferTokenForFileHandle(
const BindingContext& binding_context,
mojo::PendingReceiver<blink::mojom::NativeFileSystemFileHandle>
file_handle_receiver,
NativeFileSystemTransferTokenImpl* resolved_token);
void DidResolveTransferTokenForDirectoryHandle(
const BindingContext& binding_context,
mojo::PendingReceiver<blink::mojom::NativeFileSystemDirectoryHandle>
directory_handle_receiver,
NativeFileSystemTransferTokenImpl* resolved_token);
void DidResolveForSerializeHandle(
SerializeHandleCallback callback,
NativeFileSystemTransferTokenImpl* resolved_token);
// Calls `token_resolved_callback` with a NativeFileSystemEntry object
// that's at the file path of the NativeFileSystemDragDropToken with token
// value `token`. If no such token exists, calls
// `failed_token_redemption_callback`.
void ResolveDragDropToken(
mojo::Remote<blink::mojom::NativeFileSystemDragDropToken>,
const BindingContext& binding_context,
GetEntryFromDragDropTokenCallback token_resolved_callback,
mojo::ReportBadMessageCallback failed_token_redemption_callback,
const base::UnguessableToken& token);
// Calls `token_resolved_callback` with a NativeFileSystemEntry representing
// the file/directory at `file_path`. Called by
// NativeFileSystemManager::ResolveDragDropToken after it looks up
// whether the token's file path refers to a file or directory.
void ResolveDragDropTokenWithFileType(
const BindingContext& binding_context,
const base::FilePath& file_path,
FileSystemURLAndFSHandle url,
GetEntryFromDragDropTokenCallback token_resolved_callback,
NativeFileSystemPermissionContext::HandleType file_type);
SEQUENCE_CHECKER(sequence_checker_);
const scoped_refptr<storage::FileSystemContext> context_;
const scoped_refptr<ChromeBlobStorageContext> blob_context_;
base::SequenceBound<storage::FileSystemOperationRunner> operation_runner_;
NativeFileSystemPermissionContext* permission_context_;
// All the mojo receivers for this NativeFileSystemManager itself. Keeps
// track of associated origin and other state as well to not have to rely on
// the renderer passing that in, and to be able to do security checks around
// transferability etc.
mojo::ReceiverSet<blink::mojom::NativeFileSystemManager, BindingContext>
receivers_;
mojo::ReceiverSet<storage::mojom::NativeFileSystemContext>
internals_receivers_;
// All the receivers for file and directory handles that have references to
// them.
mojo::UniqueReceiverSet<blink::mojom::NativeFileSystemFileHandle>
file_receivers_;
mojo::UniqueReceiverSet<blink::mojom::NativeFileSystemDirectoryHandle>
directory_receivers_;
base::flat_set<std::unique_ptr<NativeFileSystemFileWriterImpl>,
base::UniquePtrComparator>
writer_receivers_;
bool off_the_record_;
// NativeFileSystemTransferTokenImpl owns a Transfer token receiver set and is
// removed from this map when all mojo connections are closed.
std::map<base::UnguessableToken,
std::unique_ptr<NativeFileSystemTransferTokenImpl>>
transfer_tokens_;
// This map is used to associate NativeFileSystemDragDropTokenImpl instances
// with UnguessableTokens so that this class can find an associated
// NativeFileSystemDragDropTokenImpl for a
// mojo::PendingRemote<NativeFileSystemDragDropToken>.
std::map<base::UnguessableToken,
std::unique_ptr<NativeFileSystemDragDropTokenImpl>>
drag_drop_tokens_;
base::Optional<FileSystemChooser::ResultEntry>
auto_file_picker_result_for_test_;
base::WeakPtrFactory<NativeFileSystemManagerImpl> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(NativeFileSystemManagerImpl);
};
} // namespace content
#endif // CONTENT_BROWSER_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_MANAGER_IMPL_H_