blob: eabfae9941d99e134adb836801fd550c629a5795 [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/extensions/api/messaging/native_messaging_launch_from_native.h"
#include <memory>
#include <utility>
#include "chrome/browser/extensions/api/messaging/native_message_port.h"
#include "chrome/browser/extensions/api/messaging/native_message_process_host.h"
#include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/manifest_handlers/natively_connectable_handler.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/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
namespace extensions {
namespace {
ScopedAllowNativeAppConnectionForTest* g_allow_native_app_connection_for_test =
nullptr;
} // namespace
bool ExtensionSupportsConnectionFromNativeApp(const std::string& extension_id,
const std::string& host_id,
Profile* profile,
bool log_errors) {
if (g_allow_native_app_connection_for_test) {
return g_allow_native_app_connection_for_test->allow();
}
if (profile->IsOffTheRecord()) {
return false;
}
auto* extension =
ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
extension_id);
if (!extension) {
LOG_IF(ERROR, log_errors)
<< "Failed to launch native messaging connection: Unknown extension ID "
<< extension_id;
return false;
}
const auto* natively_connectable_hosts =
NativelyConnectableHosts::GetConnectableNativeMessageHosts(*extension);
if (!natively_connectable_hosts ||
!natively_connectable_hosts->count(host_id)) {
LOG_IF(ERROR, log_errors)
<< "Extension \"" << extension_id << "\" does not list \"" << host_id
<< "\" in its natively_connectable manifest field";
return false;
}
if (!extension->permissions_data()->active_permissions().HasAPIPermission(
"nativeMessaging")) {
LOG_IF(ERROR, log_errors)
<< "Extension \"" << extension_id
<< "\" does not have the \"nativeMessaging\" permission";
return false;
}
if (!extension->permissions_data()->active_permissions().HasAPIPermission(
"transientBackground")) {
LOG_IF(ERROR, log_errors)
<< "Extension \"" << extension_id
<< "\" does not have the \"transientBackground\" permission";
return false;
}
if (!EventRouter::Get(profile)->ExtensionHasEventListener(
extension_id, "runtime.onConnectNative")) {
LOG_IF(ERROR, log_errors)
<< "Failed to launch native messaging connection: Extension \""
<< extension_id << "\" is not listening for runtime.onConnectNative";
return false;
}
return true;
}
ScopedAllowNativeAppConnectionForTest::ScopedAllowNativeAppConnectionForTest(
bool allow)
: allow_(allow) {
DCHECK(!g_allow_native_app_connection_for_test);
g_allow_native_app_connection_for_test = this;
}
ScopedAllowNativeAppConnectionForTest::
~ScopedAllowNativeAppConnectionForTest() {
DCHECK_EQ(this, g_allow_native_app_connection_for_test);
g_allow_native_app_connection_for_test = nullptr;
}
void LaunchNativeMessageHostFromNativeApp(const std::string& extension_id,
const std::string& host_id,
Profile* profile) {
if (!ExtensionSupportsConnectionFromNativeApp(extension_id, host_id, profile,
/* log_errors = */ true)) {
// TODO(crbug.com/967262): Report errors to the native messaging host.
return;
}
const extensions::PortId port_id(base::UnguessableToken::Create(),
1 /* port_number */, true /* is_opener */);
extensions::MessageService* const message_service =
extensions::MessageService::Get(profile);
// TODO(crbug.com/967262): Apply policy for allow_user_level.
auto native_message_host = NativeMessageProcessHost::CreateWithLauncher(
extension_id, host_id,
NativeProcessLauncher::CreateDefault(
/* allow_user_level = */ true, /* native_view = */ nullptr,
profile->GetPath(),
/* require_native_initiated_connections = */ true));
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(host_id),
std::move(native_message_port), extension_id, GURL(),
std::string() /* channel_name */);
}
} // namespace extensions