blob: 5d353db1f01208da7d8df5ef4c4dd57e459e954d [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 "chrome/browser/ash/drive/drivefs_native_message_host.h"
#include "ash/constants/ash_features.h"
#include "base/bind.h"
#include "base/cxx17_backports.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
#include "chrome/browser/extensions/api/messaging/native_message_port.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension_constants.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/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/permissions/permissions_data.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace drive {
const char kDriveFsNativeMessageHostName[] = "com.google.drive.nativeproxy";
const char* const kDriveFsNativeMessageHostOrigins[] = {
"chrome-extension://lmjegmlicamnimmfhcmpkclmigmmcbeh/",
};
constexpr size_t kDriveFsNativeMessageHostOriginsSize =
base::size(kDriveFsNativeMessageHostOrigins);
class DriveFsNativeMessageHost : public extensions::NativeMessageHost,
public drivefs::mojom::NativeMessagingPort {
public:
// Used when the native messaging session is initiated by the extension.
explicit DriveFsNativeMessageHost(Profile* profile)
: drive_service_(DriveIntegrationServiceFactory::GetForProfile(profile)) {
}
// Used when the native messaging session is initiated by DriveFS.
DriveFsNativeMessageHost(
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)) {
DCHECK(UseBidirectionalNativeMessaging());
}
explicit DriveFsNativeMessageHost(
drivefs::mojom::DriveFs* drivefs_for_testing)
: drivefs_for_testing_(drivefs_for_testing) {}
DriveFsNativeMessageHost(const DriveFsNativeMessageHost&) = delete;
DriveFsNativeMessageHost& operator=(const DriveFsNativeMessageHost&) = delete;
~DriveFsNativeMessageHost() override = default;
void OnMessage(const std::string& message) override {
DCHECK(client_);
if (UseBidirectionalNativeMessaging()) {
if (drivefs_remote_) {
drivefs_remote_->HandleMessageFromExtension(message);
}
} else {
if (!drive_service_ || !drive_service_->GetDriveFsInterface()) {
OnDriveFsResponse(FILE_ERROR_SERVICE_UNAVAILABLE, "");
return;
}
drive_service_->GetDriveFsInterface()->SendNativeMessageRequest(
message, base::BindOnce(&DriveFsNativeMessageHost::OnDriveFsResponse,
weak_ptr_factory_.GetWeakPtr()));
}
}
void Start(Client* client) override {
client_ = client;
if (!UseBidirectionalNativeMessaging()) {
return;
}
if (!pending_receiver_) {
// The session was initiated by the extension.
mojo::PendingRemote<drivefs::mojom::NativeMessagingPort> extension_port;
pending_receiver_ = extension_port.InitWithNewPipeAndPassReceiver();
drivefs::mojom::DriveFs* drivefs;
if (drivefs_for_testing_) {
drivefs = drivefs_for_testing_;
} else if (!drive_service_ || !drive_service_->GetDriveFsInterface()) {
OnDriveFsResponse(FILE_ERROR_SERVICE_UNAVAILABLE, "");
return;
} else {
drivefs = drive_service_->GetDriveFsInterface();
}
drivefs->CreateNativeHostSession(
drivefs::mojom::ExtensionConnectionParams::New(
GURL(kDriveFsNativeMessageHostOrigins[0]).host()),
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 OnDriveFsResponse(FileError error, const std::string& response) {
if (error == FILE_ERROR_OK) {
client_->PostMessageFromNativeHost(response);
} else {
LOG(WARNING) << "DriveFS returned error " << FileErrorToString(error);
client_->CloseChannel(FileErrorToString(error));
}
}
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();
}
bool UseBidirectionalNativeMessaging() {
return base::FeatureList::IsEnabled(
chromeos::features::kDriveFsBidirectionalNativeMessaging);
}
DriveIntegrationService* drive_service_ = nullptr;
drivefs::mojom::DriveFs* drivefs_for_testing_ = nullptr;
// 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_;
Client* client_ = nullptr;
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_ =
base::ThreadTaskRunnerHandle::Get();
base::WeakPtrFactory<DriveFsNativeMessageHost> weak_ptr_factory_{this};
};
std::unique_ptr<extensions::NativeMessageHost> CreateDriveFsNativeMessageHost(
content::BrowserContext* browser_context) {
return std::make_unique<DriveFsNativeMessageHost>(
Profile::FromBrowserContext(browser_context));
}
std::unique_ptr<extensions::NativeMessageHost>
CreateDriveFsInitiatedNativeMessageHost(
mojo::PendingReceiver<drivefs::mojom::NativeMessagingPort>
extension_receiver,
mojo::PendingRemote<drivefs::mojom::NativeMessagingHost> drivefs_remote) {
if (!base::FeatureList::IsEnabled(
chromeos::features::kDriveFsBidirectionalNativeMessaging)) {
return nullptr;
}
return std::make_unique<DriveFsNativeMessageHost>(
std::move(extension_receiver), std::move(drivefs_remote));
}
std::unique_ptr<extensions::NativeMessageHost>
CreateDriveFsNativeMessageHostForTesting(
drivefs::mojom::DriveFs* drivefs_for_testing) {
return std::make_unique<DriveFsNativeMessageHost>(drivefs_for_testing);
}
drivefs::mojom::DriveFsDelegate::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::DriveFsDelegate::ExtensionConnectionStatus::
kExtensionNotFound;
}
const extensions::PortId port_id(base::UnguessableToken::Create(),
/* port_number= */ 1, /* is_opener= */ true);
extensions::MessageService* const message_service =
extensions::MessageService::Get(profile);
auto native_message_host = CreateDriveFsInitiatedNativeMessageHost(
std::move(extension_receiver), std::move(drivefs_remote));
if (!native_message_host) {
return drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus::
kExtensionNotFound;
}
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(),
/* channel name= */ std::string());
return drivefs::mojom::DriveFsDelegate::ExtensionConnectionStatus::kSuccess;
}
} // namespace drive