1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fuchsia/web/cpp/fidl.h>
6 #include <lib/fidl/cpp/binding.h>
7 
8 #include "base/barrier_closure.h"
9 #include "base/base_paths_fuchsia.h"
10 #include "base/files/file_util.h"
11 #include "base/path_service.h"
12 #include "base/test/bind.h"
13 #include "components/cast/message_port/message_port_fuchsia.h"
14 #include "content/public/test/browser_test.h"
15 #include "fuchsia/base/fit_adapter.h"
16 #include "fuchsia/base/frame_test_util.h"
17 #include "fuchsia/base/mem_buffer_util.h"
18 #include "fuchsia/base/result_receiver.h"
19 #include "fuchsia/base/test_navigation_listener.h"
20 #include "fuchsia/engine/test/web_engine_browser_test.h"
21 #include "fuchsia/runners/cast/api_bindings_client.h"
22 #include "fuchsia/runners/cast/create_web_message.h"
23 #include "fuchsia/runners/cast/named_message_port_connector_fuchsia.h"
24 #include "fuchsia/runners/cast/test_api_bindings.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 
27 namespace {
28 
29 class ApiBindingsClientTest : public cr_fuchsia::WebEngineBrowserTest {
30  public:
ApiBindingsClientTest()31   ApiBindingsClientTest() : api_service_binding_(&api_service_) {
32     set_test_server_root(base::FilePath("fuchsia/runners/cast/testdata"));
33   }
34 
35   ~ApiBindingsClientTest() override = default;
36 
SetUp()37   void SetUp() override { cr_fuchsia::WebEngineBrowserTest::SetUp(); }
38 
39  protected:
StartClient()40   void StartClient() {
41     base::ScopedAllowBlockingForTesting allow_blocking;
42 
43     // Get the bindings from |api_service_|.
44     base::RunLoop run_loop;
45     client_ = std::make_unique<ApiBindingsClient>(
46         api_service_binding_.NewBinding(), run_loop.QuitClosure());
47     ASSERT_FALSE(client_->HasBindings());
48     run_loop.Run();
49     ASSERT_TRUE(client_->HasBindings());
50 
51     frame_ = WebEngineBrowserTest::CreateFrame(&navigation_listener_);
52     frame_->GetNavigationController(controller_.NewRequest());
53     connector_ =
54         std::make_unique<NamedMessagePortConnectorFuchsia>(frame_.get());
55 
56     client_->AttachToFrame(frame_.get(), connector_.get(),
57                            base::MakeExpectedNotRunClosure(FROM_HERE));
58   }
59 
SetUpOnMainThread()60   void SetUpOnMainThread() override {
61     cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread();
62     ASSERT_TRUE(embedded_test_server()->Start());
63   }
64 
TearDownOnMainThread()65   void TearDownOnMainThread() override {
66     // Destroy |client_| before the MessageLoop is destroyed.
67     client_.reset();
68   }
69 
70   fuchsia::web::FramePtr frame_;
71   std::unique_ptr<NamedMessagePortConnectorFuchsia> connector_;
72   TestApiBindings api_service_;
73   fidl::Binding<chromium::cast::ApiBindings> api_service_binding_;
74   std::unique_ptr<ApiBindingsClient> client_;
75   cr_fuchsia::TestNavigationListener navigation_listener_;
76   fuchsia::web::NavigationControllerPtr controller_;
77 
78   DISALLOW_COPY_AND_ASSIGN(ApiBindingsClientTest);
79 };
80 
81 // Tests API registration, injection, and message IPC.
82 // Registers a port that echoes messages received over a MessagePort back to the
83 // sender.
IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest,EndToEnd)84 IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) {
85   // Define the injected bindings.
86   std::vector<chromium::cast::ApiBinding> binding_list;
87   chromium::cast::ApiBinding echo_binding;
88   echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString(
89       "window.echo = cast.__platform__.PortConnector.bind('echoService');",
90       "test"));
91   binding_list.emplace_back(std::move(echo_binding));
92   api_service_.set_bindings(std::move(binding_list));
93 
94   StartClient();
95 
96   base::RunLoop post_message_responses_loop;
97   base::RepeatingClosure post_message_response_closure =
98       base::BarrierClosure(2, post_message_responses_loop.QuitClosure());
99 
100   // Navigate to a test page that makes use of the injected bindings.
101   const GURL test_url = embedded_test_server()->GetURL("/echo.html");
102   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
103       controller_.get(), fuchsia::web::LoadUrlParams(), test_url.spec()));
104   navigation_listener_.RunUntilUrlEquals(test_url);
105 
106   std::string connect_message;
107   std::unique_ptr<cast_api_bindings::MessagePort> connect_port;
108   connector_->GetConnectMessage(&connect_message, &connect_port);
109   frame_->PostMessage(
110       "*", CreateWebMessage(connect_message, std::move(connect_port)),
111       [&post_message_response_closure](
112           fuchsia::web::Frame_PostMessage_Result result) {
113         ASSERT_TRUE(result.is_response());
114         post_message_response_closure.Run();
115       });
116 
117   // Connect to the echo service hosted by the page and send a ping to it.
118   fuchsia::web::WebMessage message;
119   message.set_data(cr_fuchsia::MemBufferFromString("ping", "ping-msg"));
120   fuchsia::web::MessagePortPtr port =
121       api_service_.RunUntilMessagePortReceived("echoService").Bind();
122   port->PostMessage(std::move(message),
123                     [&post_message_response_closure](
124                         fuchsia::web::MessagePort_PostMessage_Result result) {
125                       ASSERT_TRUE(result.is_response());
126                       post_message_response_closure.Run();
127                     });
128 
129   // Handle the ping response.
130   base::RunLoop response_loop;
131   cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> response(
132       response_loop.QuitClosure());
133   port->ReceiveMessage(
134       cr_fuchsia::CallbackToFitFunction(response.GetReceiveCallback()));
135   response_loop.Run();
136 
137   std::string response_string;
138   EXPECT_TRUE(
139       cr_fuchsia::StringFromMemBuffer(response->data(), &response_string));
140   EXPECT_EQ("ack ping", response_string);
141 
142   // Ensure that we've received acks for all messages.
143   post_message_responses_loop.Run();
144 }
145 
146 }  // namespace
147