blob: 944dfbebeca5ed453b73916c484cd8c53e8837c0 [file] [log] [blame]
// Copyright 2024 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/chrome_content_browser_client_parts.h"
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include "base/functional/bind.h"
#include "base/strings/strcat.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/mojom/echo.test-mojom.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_child_process_host_delegate.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/process_type.h"
#include "content/public/test/mock_render_process_host.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
namespace {
using ::testing::_;
using ::testing::WithArg;
// Only mocks methods that are tested.
class MockChromeContentBrowserClientParts
: public ChromeContentBrowserClientParts {
public:
MOCK_METHOD(void,
ExposeInterfacesToRenderer,
(service_manager::BinderRegistry*,
blink::AssociatedInterfaceRegistry*,
content::RenderProcessHost*),
(override));
MOCK_METHOD(void,
ExposeInterfacesToChild,
(mojo::BinderMapWithContext<content::BrowserChildProcessHost*>*),
(override));
};
// Echo implementation for test that adds a suffix to the echoed string.
class TestForEcho : public test::mojom::Echo {
public:
explicit TestForEcho(std::string_view suffix) : suffix_(suffix) {}
void EchoString(const std::string& input,
EchoStringCallback callback) override {
std::move(callback).Run(base::StrCat({input, ".", suffix_}));
}
static void BindPipe(std::string_view context,
mojo::ScopedMessagePipeHandle pipe) {
mojo::MakeSelfOwnedReceiver(
std::make_unique<TestForEcho>(context),
mojo::PendingReceiver<test::mojom::Echo>(std::move(pipe)));
}
static void BindReceiver(std::string_view context,
content::BrowserChildProcessHost* unused,
mojo::PendingReceiver<test::mojom::Echo> receiver) {
mojo::MakeSelfOwnedReceiver(std::make_unique<TestForEcho>(context),
std::move(receiver));
}
private:
std::string suffix_;
};
class ChromeContentBrowserClientPartsTest
: public ChromeRenderViewHostTestHarness {
protected:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
// Ensure that a ContentBrowserClient is registered.
ASSERT_TRUE(content::GetContentClientForTesting());
ASSERT_TRUE(content::GetContentClientForTesting()->browser());
}
// Adds a new MockChromeContentBrowserClientParts to the ContentBrowserClient,
// and returns a pointer to it.
MockChromeContentBrowserClientParts* AddMockClientParts() {
auto* browser_client = static_cast<ChromeContentBrowserClient*>(
content::GetContentClientForTesting()->browser());
auto client_parts = std::make_unique<MockChromeContentBrowserClientParts>();
auto* mock_client_parts = client_parts.get();
browser_client->AddExtraPartForTesting(std::move(client_parts));
return mock_client_parts;
}
};
TEST_F(ChromeContentBrowserClientPartsTest, ExposeInterfaces) {
auto* mock_client_parts1 = AddMockClientParts();
auto* mock_client_parts2 = AddMockClientParts();
// ExposeInterfaces calls should be passed through to all the parts.
service_manager::BinderRegistry binder_registry;
blink::AssociatedInterfaceRegistry associated_interface_registry;
EXPECT_CALL(*mock_client_parts1,
ExposeInterfacesToRenderer(
&binder_registry, &associated_interface_registry, process()));
EXPECT_CALL(*mock_client_parts1, ExposeInterfacesToChild(_))
.WillOnce(
[](mojo::BinderMapWithContext<content::BrowserChildProcessHost*>*
map) {
map->Add<test::mojom::Echo>(
base::BindRepeating(&TestForEcho::BindReceiver, "BCPH"));
});
EXPECT_CALL(*mock_client_parts2,
ExposeInterfacesToRenderer(
&binder_registry, &associated_interface_registry, process()))
.WillOnce(WithArg<2>([](content::RenderProcessHost* rph) {
static_cast<content::MockRenderProcessHost*>(rph)
->OverrideBinderForTesting(
test::mojom::Echo::Name_,
base::BindRepeating(&TestForEcho::BindPipe, "RPH"));
}));
EXPECT_CALL(*mock_client_parts2, ExposeInterfacesToChild(_));
// Unlike BrowserChildProcessHostImpl and RenderProcessHostImpl,
// MockRenderProcessHost doesn't call ExposeInterfacesToRenderer
// automatically.
content::GetContentClientForTesting()->browser()->ExposeInterfacesToRenderer(
&binder_registry, &associated_interface_registry, process());
content::BrowserChildProcessHostDelegate dummy_delegate;
auto bcph = content::BrowserChildProcessHost::Create(
content::PROCESS_TYPE_UTILITY, &dummy_delegate);
// Ensure the exposed interfaces are registered correctly by expecting each to
// append a suffix to the echoed string.
mojo::Remote<test::mojom::Echo> echo_remote;
process()->BindReceiver(echo_remote.BindNewPipeAndPassReceiver());
echo_remote->EchoString("foo", base::BindOnce([](const std::string& output) {
EXPECT_EQ(output, "foo.RPH");
}));
echo_remote.reset();
bcph->GetHost()->BindReceiver(echo_remote.BindNewPipeAndPassReceiver());
echo_remote->EchoString("bar", base::BindOnce([](const std::string& output) {
EXPECT_EQ(output, "bar.BCPH");
}));
}
} // namespace