// 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 <memory>
#include <string>
#include <utility>
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/unguessable_token.h"
#include "chrome/browser/extensions/api/messaging/native_message_port.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/test/browser_test.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/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/test/result_catcher.h"
#include "ipc/ipc_message.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using testing::_;
using testing::InvokeWithoutArgs;
using testing::Mock;
using testing::SaveArg;
using testing::StrictMock;
namespace {
const char kFakeNativeAppName[] = "com.google.chrome.test.initiator";
class MockNativeMessageHost : public extensions::NativeMessageHost {
public:
MOCK_METHOD1(OnMessage, void(const std::string& message));
MOCK_METHOD1(Start, void(Client* client));
scoped_refptr<base::SingleThreadTaskRunner> task_runner() const override {
return task_runner_;
}
private:
scoped_refptr<base::SingleThreadTaskRunner> task_runner_ =
base::SingleThreadTaskRunner::GetCurrentDefault();
};
// Test fixture for testing native messaging API when the communication is
// initiated by the native application.
class ExtensionIncomingNativeMessagingTest
: public extensions::ExtensionApiTest {
public:
ExtensionIncomingNativeMessagingTest(
const ExtensionIncomingNativeMessagingTest&) = delete;
ExtensionIncomingNativeMessagingTest& operator=(
const ExtensionIncomingNativeMessagingTest&) = delete;
protected:
ExtensionIncomingNativeMessagingTest() = default;
~ExtensionIncomingNativeMessagingTest() override = default;
bool LoadTestExtension() {
extension_ =
LoadExtension(test_data_dir_.AppendASCII("incoming_native_messaging"));
return extension_ != nullptr;
}
void OpenMessageChannelToExtension(
std::unique_ptr<extensions::NativeMessageHost> native_message_host) {
auto* const message_service = extensions::MessageService::Get(profile());
const extensions::PortId port_id(
base::UnguessableToken::Create(), 1 /* port_number */,
true /* is_opener */, extensions::mojom::SerializationFormat::kJson);
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(kFakeNativeAppName),
std::move(native_message_port), extension_->id(), GURL(),
extensions::mojom::ChannelType::kNative,
std::string() /* channel_name */);
}
private:
raw_ptr<const extensions::Extension, DanglingUntriaged> extension_ = nullptr;
};
// Tests that the extension receives the onConnectNative event when the native
// application opens a message channel to it, and that each of them can
// successfully send a message.
IN_PROC_BROWSER_TEST_F(ExtensionIncomingNativeMessagingTest,
SingleRequestResponse) {
extensions::ResultCatcher catcher;
ASSERT_TRUE(LoadTestExtension());
auto owned_native_message_host =
std::make_unique<StrictMock<MockNativeMessageHost>>();
auto* const native_message_host = owned_native_message_host.get();
extensions::NativeMessageHost::Client* native_message_host_client = nullptr;
EXPECT_CALL(*native_message_host, Start(_))
.WillOnce(SaveArg<0>(&native_message_host_client));
const char kExpectedMessageFromExtension[] = R"({"request":"foo"})";
base::RunLoop message_awaiting_run_loop;
EXPECT_CALL(*native_message_host, OnMessage(kExpectedMessageFromExtension))
.WillOnce(
InvokeWithoutArgs(&message_awaiting_run_loop, &base::RunLoop::Quit));
OpenMessageChannelToExtension(std::move(owned_native_message_host));
// Wait until the extension sends a message to the native application.
message_awaiting_run_loop.Run();
Mock::VerifyAndClearExpectations(native_message_host);
ASSERT_TRUE(native_message_host_client);
// Post a reply to the extension.
const char kResponseMessageToSend[] = R"({"response":"bar"})";
native_message_host_client->PostMessageFromNativeHost(kResponseMessageToSend);
// Wait till the extension receives and validates the reply.
EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}
} // namespace