| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/web/cpp/fidl.h> |
| #include <lib/fidl/cpp/binding.h> |
| |
| #include "base/barrier_closure.h" |
| #include "base/files/file_util.h" |
| #include "base/fuchsia/mem_buffer_util.h" |
| #include "base/path_service.h" |
| #include "base/test/bind.h" |
| #include "base/test/test_future.h" |
| #include "components/cast/message_port/fuchsia/create_web_message.h" |
| #include "components/cast/message_port/fuchsia/message_port_fuchsia.h" |
| #include "content/public/test/browser_test.h" |
| #include "fuchsia_web/common/test/fit_adapter.h" |
| #include "fuchsia_web/common/test/frame_for_test.h" |
| #include "fuchsia_web/common/test/frame_test_util.h" |
| #include "fuchsia_web/common/test/test_navigation_listener.h" |
| #include "fuchsia_web/runners/cast/api_bindings_client.h" |
| #include "fuchsia_web/runners/cast/named_message_port_connector_fuchsia.h" |
| #include "fuchsia_web/runners/cast/test/fake_api_bindings.h" |
| #include "fuchsia_web/webengine/test/web_engine_browser_test.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| class ApiBindingsClientTest : public WebEngineBrowserTest { |
| public: |
| ApiBindingsClientTest() : api_service_binding_(&api_service_) { |
| set_test_server_root(base::FilePath("fuchsia_web/runners/cast/testdata")); |
| } |
| |
| ~ApiBindingsClientTest() override = default; |
| |
| ApiBindingsClientTest(const ApiBindingsClientTest&) = delete; |
| ApiBindingsClientTest& operator=(const ApiBindingsClientTest&) = delete; |
| |
| void SetUp() override { WebEngineBrowserTest::SetUp(); } |
| |
| protected: |
| void StartClient(bool disconnect_before_attach, |
| base::OnceClosure on_error_closure) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| |
| // Get the bindings from |api_service_|. |
| base::RunLoop run_loop; |
| client_ = std::make_unique<ApiBindingsClient>( |
| api_service_binding_.NewBinding(), run_loop.QuitClosure()); |
| ASSERT_FALSE(client_->HasBindings()); |
| run_loop.Run(); |
| ASSERT_TRUE(client_->HasBindings()); |
| |
| frame_ = FrameForTest::Create(context(), fuchsia::web::CreateFrameParams()); |
| connector_ = |
| std::make_unique<NamedMessagePortConnectorFuchsia>(frame_.get()); |
| |
| if (disconnect_before_attach) |
| api_service_binding_.Unbind(); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| client_->AttachToFrame(frame_.get(), connector_.get(), |
| std::move(on_error_closure)); |
| } |
| |
| void SetUpOnMainThread() override { |
| WebEngineBrowserTest::SetUpOnMainThread(); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| void TearDownOnMainThread() override { |
| // Destroy |client_| before the MessageLoop is destroyed. |
| client_ = nullptr; |
| |
| // Destroy the NamedMessagePortConnector before the Frame. |
| connector_ = nullptr; |
| |
| // Destroy the Frame before the test terminates |
| frame_ = {}; |
| |
| WebEngineBrowserTest::TearDownOnMainThread(); |
| } |
| |
| FrameForTest frame_; |
| std::unique_ptr<NamedMessagePortConnectorFuchsia> connector_; |
| FakeApiBindingsImpl api_service_; |
| fidl::Binding<chromium::cast::ApiBindings> api_service_binding_; |
| std::unique_ptr<ApiBindingsClient> client_; |
| }; |
| |
| // Tests API registration, injection, and message IPC. |
| // Registers a port that echoes messages received over a MessagePort back to the |
| // sender. |
| IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) { |
| // Define the injected bindings. |
| std::vector<chromium::cast::ApiBinding> binding_list; |
| chromium::cast::ApiBinding echo_binding; |
| echo_binding.set_before_load_script(base::MemBufferFromString( |
| "window.echo = cast.__platform__.PortConnector.bind('echoService');", |
| "test")); |
| binding_list.emplace_back(std::move(echo_binding)); |
| api_service_.set_bindings(std::move(binding_list)); |
| |
| StartClient(false, base::MakeExpectedNotRunClosure(FROM_HERE)); |
| |
| base::RunLoop post_message_responses_loop; |
| base::RepeatingClosure post_message_response_closure = |
| base::BarrierClosure(2, post_message_responses_loop.QuitClosure()); |
| |
| // Navigate to a test page that makes use of the injected bindings. |
| const GURL test_url = embedded_test_server()->GetURL("/echo.html"); |
| EXPECT_TRUE(LoadUrlAndExpectResponse(frame_.GetNavigationController(), |
| fuchsia::web::LoadUrlParams(), |
| test_url.spec())); |
| frame_.navigation_listener().RunUntilUrlEquals(test_url); |
| |
| std::string connect_message; |
| std::unique_ptr<cast_api_bindings::MessagePort> connect_port; |
| connector_->GetConnectMessage(&connect_message, &connect_port); |
| frame_->PostMessage( |
| "*", CreateWebMessage(connect_message, std::move(connect_port)), |
| [&post_message_response_closure]( |
| fuchsia::web::Frame_PostMessage_Result result) { |
| ASSERT_TRUE(result.is_response()); |
| post_message_response_closure.Run(); |
| }); |
| |
| // Connect to the echo service hosted by the page and send a ping to it. |
| fuchsia::web::WebMessage message; |
| message.set_data(base::MemBufferFromString("ping", "ping-msg")); |
| fuchsia::web::MessagePortPtr port = |
| api_service_.RunAndReturnConnectedPort("echoService").Bind(); |
| port->PostMessage(std::move(message), |
| [&post_message_response_closure]( |
| fuchsia::web::MessagePort_PostMessage_Result result) { |
| ASSERT_TRUE(result.is_response()); |
| post_message_response_closure.Run(); |
| }); |
| |
| // Handle the ping response. |
| base::test::TestFuture<fuchsia::web::WebMessage> response; |
| port->ReceiveMessage(CallbackToFitFunction(response.GetCallback())); |
| ASSERT_TRUE(response.Wait()); |
| |
| std::optional<std::string> response_string = |
| base::StringFromMemBuffer(response.Get().data()); |
| ASSERT_TRUE(response_string.has_value()); |
| EXPECT_EQ("ack ping", *response_string); |
| |
| // Ensure that we've received acks for all messages. |
| post_message_responses_loop.Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, |
| ClientDisconnectsBeforeFrameAttached) { |
| bool error_signaled = false; |
| StartClient( |
| true, base::BindOnce([](bool* error_signaled) { *error_signaled = true; }, |
| base::Unretained(&error_signaled))); |
| |
| // Verify that the error is signalled asynchronously. |
| EXPECT_FALSE(error_signaled); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(error_signaled); |
| } |
| |
| } // namespace |