| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/drivefs/drivefs_native_message_host.h" |
| |
| #include "base/functional/bind.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "build/buildflag.h" |
| #include "chrome/browser/chromeos/drivefs/drivefs_native_message_host_origins.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "components/drive/file_errors.h" |
| #include "extensions/browser/api/messaging/channel_endpoint.h" |
| #include "extensions/browser/api/messaging/message_service.h" |
| #include "extensions/browser/api/messaging/native_message_host.h" |
| #include "extensions/browser/api/messaging/native_message_port.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/api/messaging/messaging_endpoint.h" |
| #include "extensions/common/api/messaging/port_id.h" |
| #include "extensions/common/mojom/message_port.mojom-shared.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| |
| namespace drive { |
| |
| class DriveFsNativeMessageHost : public extensions::NativeMessageHost, |
| public drivefs::mojom::NativeMessagingPort { |
| public: |
| // Used when the native messaging session is initiated by the extension. |
| explicit DriveFsNativeMessageHost(CreateNativeHostSessionCallback callback) |
| : create_native_host_callback_(std::move(callback)) {} |
| |
| // Used when the native messaging session is initiated by DriveFS. |
| DriveFsNativeMessageHost( |
| Profile* profile, |
| mojo::PendingReceiver<drivefs::mojom::NativeMessagingPort> |
| extension_receiver, |
| mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> drivefs_remote) |
| : pending_receiver_(std::move(extension_receiver)), |
| drivefs_remote_(std::move(drivefs_remote)) {} |
| |
| DriveFsNativeMessageHost(const DriveFsNativeMessageHost&) = delete; |
| DriveFsNativeMessageHost& operator=(const DriveFsNativeMessageHost&) = delete; |
| |
| ~DriveFsNativeMessageHost() override = default; |
| |
| void OnMessage(const std::string& message) override { |
| DCHECK(client_); |
| |
| if (drivefs_remote_) { |
| drivefs_remote_->HandleMessageFromExtension(message); |
| } |
| } |
| |
| void Start(Client* client) override { |
| client_ = client; |
| |
| if (!pending_receiver_) { |
| // The session was initiated by the extension. |
| mojo::PendingRemote<drivefs::mojom::NativeMessagingPort> extension_port; |
| pending_receiver_ = extension_port.InitWithNewPipeAndPassReceiver(); |
| |
| std::move(create_native_host_callback_) |
| .Run(drivefs::mojom::ExtensionConnectionParams::New( |
| GURL(kDriveFsNativeMessageHostOrigins[0]).GetHost()), |
| drivefs_remote_.BindNewPipeAndPassReceiver(), |
| std::move(extension_port)); |
| } |
| receiver_.Bind(std::move(std::move(pending_receiver_))); |
| receiver_.set_disconnect_with_reason_handler(base::BindOnce( |
| &DriveFsNativeMessageHost::OnDisconnect, base::Unretained(this))); |
| } |
| |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner() const override { |
| return task_runner_; |
| } |
| |
| private: |
| void PostMessageToExtension(const std::string& message) override { |
| client_->PostMessageFromNativeHost(message); |
| } |
| |
| void OnDisconnect(uint32_t error, const std::string& reason) { |
| client_->CloseChannel(FileErrorToString(static_cast<FileError>( |
| -static_cast<int32_t>(error))) + |
| ": " + reason); |
| drivefs_remote_.reset(); |
| } |
| |
| CreateNativeHostSessionCallback create_native_host_callback_; |
| |
| // Used to buffer messages until Start() has been called. |
| mojo::PendingReceiver<drivefs::mojom::NativeMessagingPort> pending_receiver_; |
| mojo::Receiver<drivefs::mojom::NativeMessagingPort> receiver_{this}; |
| mojo::Remote<drivefs::mojom::NativeMessagingHost> drivefs_remote_; |
| |
| raw_ptr<Client> client_ = nullptr; |
| |
| const scoped_refptr<base::SingleThreadTaskRunner> task_runner_ = |
| base::SingleThreadTaskRunner::GetCurrentDefault(); |
| }; |
| |
| std::unique_ptr<extensions::NativeMessageHost> CreateDriveFsNativeMessageHost( |
| CreateNativeHostSessionCallback callback) { |
| return std::make_unique<DriveFsNativeMessageHost>(std::move(callback)); |
| } |
| |
| std::unique_ptr<extensions::NativeMessageHost> |
| CreateDriveFsInitiatedNativeMessageHostInternal( |
| Profile* profile, |
| mojo::PendingReceiver<drivefs::mojom::NativeMessagingPort> |
| extension_receiver, |
| mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> drivefs_remote) { |
| return std::make_unique<DriveFsNativeMessageHost>( |
| profile, std::move(extension_receiver), std::move(drivefs_remote)); |
| } |
| |
| drivefs::mojom::ExtensionConnectionStatus |
| ConnectToDriveFsNativeMessageExtension( |
| Profile* profile, |
| const std::string& extension_id, |
| mojo::PendingReceiver<drivefs::mojom::NativeMessagingPort> |
| extension_receiver, |
| mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> drivefs_remote) { |
| auto* extension = |
| extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID( |
| extension_id); |
| if (!extension || |
| !extension->permissions_data()->active_permissions().HasAPIPermission( |
| "nativeMessaging") || |
| !extensions::EventRouter::Get(profile)->ExtensionHasEventListener( |
| extension_id, "runtime.onConnectNative")) { |
| return drivefs::mojom::ExtensionConnectionStatus::kExtensionNotFound; |
| } |
| |
| const extensions::PortId port_id( |
| base::UnguessableToken::Create(), |
| /* port_number= */ 1, /* is_opener= */ true, |
| extensions::mojom::SerializationFormat::kJson); |
| extensions::MessageService* const message_service = |
| extensions::MessageService::Get(profile); |
| auto native_message_host = CreateDriveFsInitiatedNativeMessageHostInternal( |
| profile, std::move(extension_receiver), std::move(drivefs_remote)); |
| if (!native_message_host) { |
| return drivefs::mojom::ExtensionConnectionStatus::kFeatureNotEnabled; |
| } |
| |
| auto native_message_port = std::make_unique<extensions::NativeMessagePort>( |
| message_service->GetChannelDelegate(), port_id, |
| std::move(native_message_host)); |
| message_service->OpenChannelToExtension( |
| extensions::ChannelEndpoint(profile), port_id, |
| extensions::MessagingEndpoint::ForNativeApp( |
| kDriveFsNativeMessageHostName), |
| std::move(native_message_port), extension_id, GURL(), |
| extensions::mojom::ChannelType::kNative, |
| /* channel name= */ std::string()); |
| return drivefs::mojom::ExtensionConnectionStatus::kSuccess; |
| } |
| |
| } // namespace drive |