| // 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 |