1 // Copyright 2014 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.
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/run_loop.h"
11 #include "base/strings/string_util.h"
12 #include "content/browser/webui/web_ui_controller_factory_registry.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/browser/web_ui_controller.h"
19 #include "content/public/browser/web_ui_data_source.h"
20 #include "content/public/common/content_paths.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/public/common/service_registry.h"
23 #include "content/public/common/url_utils.h"
24 #include "content/public/test/content_browser_test.h"
25 #include "content/public/test/content_browser_test_utils.h"
26 #include "content/shell/browser/shell.h"
27 #include "content/test/data/web_ui_test_mojo_bindings.mojom.h"
28 #include "mojo/edk/test/test_utils.h"
29 #include "mojo/public/cpp/bindings/interface_impl.h"
30 #include "mojo/public/cpp/bindings/interface_request.h"
31 #include "mojo/public/js/constants.h"
36 bool got_message
= false;
38 // The bindings for the page are generated from a .mojom file. This code looks
39 // up the generated file from disk and returns it.
40 bool GetResource(const std::string
& id
,
41 const WebUIDataSource::GotDataCallback
& callback
) {
42 // These are handled by the WebUIDataSource that AddMojoDataSource() creates.
43 if (id
== mojo::kBufferModuleName
||
44 id
== mojo::kCodecModuleName
||
45 id
== mojo::kConnectionModuleName
||
46 id
== mojo::kConnectorModuleName
||
47 id
== mojo::kUnicodeModuleName
||
48 id
== mojo::kRouterModuleName
||
49 id
== mojo::kValidatorModuleName
)
53 CHECK(base::ReadFileToString(mojo::test::GetFilePathForJSResource(id
),
55 std::string::npos
)) << id
;
56 base::RefCountedString
* ref_contents
= new base::RefCountedString
;
57 ref_contents
->data() = contents
;
58 callback
.Run(ref_contents
);
62 class BrowserTargetImpl
: public mojo::InterfaceImpl
<BrowserTarget
> {
64 explicit BrowserTargetImpl(base::RunLoop
* run_loop
) : run_loop_(run_loop
) {}
66 ~BrowserTargetImpl() override
{}
68 // mojo::InterfaceImpl<BrowserTarget> overrides:
69 void PingResponse() override
{ NOTREACHED(); }
72 base::RunLoop
* run_loop_
;
75 DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl
);
78 class PingBrowserTargetImpl
: public BrowserTargetImpl
{
80 explicit PingBrowserTargetImpl(base::RunLoop
* run_loop
)
81 : BrowserTargetImpl(run_loop
) {}
83 ~PingBrowserTargetImpl() override
{}
85 // Quit the RunLoop when called.
86 void PingResponse() override
{
92 DISALLOW_COPY_AND_ASSIGN(PingBrowserTargetImpl
);
95 // WebUIController that sets up mojo bindings.
96 class TestWebUIController
: public WebUIController
{
98 TestWebUIController(WebUI
* web_ui
, base::RunLoop
* run_loop
)
99 : WebUIController(web_ui
),
100 run_loop_(run_loop
) {
101 content::WebUIDataSource
* data_source
=
102 WebUIDataSource::AddMojoDataSource(
103 web_ui
->GetWebContents()->GetBrowserContext());
104 data_source
->SetRequestFilter(base::Bind(&GetResource
));
108 base::RunLoop
* run_loop_
;
109 scoped_ptr
<BrowserTargetImpl
> browser_target_
;
112 DISALLOW_COPY_AND_ASSIGN(TestWebUIController
);
115 // TestWebUIController that additionally creates the ping test BrowserTarget
116 // implementation at the right time.
117 class PingTestWebUIController
: public TestWebUIController
{
119 PingTestWebUIController(WebUI
* web_ui
, base::RunLoop
* run_loop
)
120 : TestWebUIController(web_ui
, run_loop
) {
122 ~PingTestWebUIController() override
{}
124 // WebUIController overrides:
125 void RenderViewCreated(RenderViewHost
* render_view_host
) override
{
126 render_view_host
->GetMainFrame()->GetServiceRegistry()->
127 AddService
<BrowserTarget
>(base::Bind(
128 &PingTestWebUIController::CreateHandler
, base::Unretained(this)));
131 void CreateHandler(mojo::InterfaceRequest
<BrowserTarget
> request
) {
132 browser_target_
.reset(mojo::WeakBindToRequest(
133 new PingBrowserTargetImpl(run_loop_
), &request
));
134 browser_target_
->client()->Ping();
138 DISALLOW_COPY_AND_ASSIGN(PingTestWebUIController
);
141 // WebUIControllerFactory that creates TestWebUIController.
142 class TestWebUIControllerFactory
: public WebUIControllerFactory
{
144 TestWebUIControllerFactory() : run_loop_(NULL
) {}
146 void set_run_loop(base::RunLoop
* run_loop
) { run_loop_
= run_loop
; }
148 WebUIController
* CreateWebUIControllerForURL(WebUI
* web_ui
,
149 const GURL
& url
) const override
{
150 if (url
.query() == "ping")
151 return new PingTestWebUIController(web_ui
, run_loop_
);
154 WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
155 const GURL
& url
) const override
{
156 return reinterpret_cast<WebUI::TypeID
>(1);
158 bool UseWebUIForURL(BrowserContext
* browser_context
,
159 const GURL
& url
) const override
{
162 bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
163 const GURL
& url
) const override
{
168 base::RunLoop
* run_loop_
;
170 DISALLOW_COPY_AND_ASSIGN(TestWebUIControllerFactory
);
173 class WebUIMojoTest
: public ContentBrowserTest
{
176 WebUIControllerFactory::RegisterFactory(&factory_
);
179 ~WebUIMojoTest() override
{
180 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_
);
183 TestWebUIControllerFactory
* factory() { return &factory_
; }
186 TestWebUIControllerFactory factory_
;
188 DISALLOW_COPY_AND_ASSIGN(WebUIMojoTest
);
191 // Loads a webui page that contains mojo bindings and verifies a message makes
192 // it from the browser to the page and back.
193 IN_PROC_BROWSER_TEST_F(WebUIMojoTest
, EndToEndPing
) {
194 // Currently there is no way to have a generated file included in the isolate
195 // files. If the bindings file doesn't exist assume we're on such a bot and
197 // TODO(sky): remove this conditional when isolates support copying from gen.
198 const base::FilePath
test_file_path(
199 mojo::test::GetFilePathForJSResource(
200 "content/test/data/web_ui_test_mojo_bindings.mojom"));
201 if (!base::PathExists(test_file_path
)) {
202 LOG(WARNING
) << " mojom binding file doesn't exist, assuming on isolate";
207 ASSERT_TRUE(test_server()->Start());
208 base::RunLoop run_loop
;
209 factory()->set_run_loop(&run_loop
);
210 GURL
test_url(test_server()->GetURL("files/web_ui_mojo.html?ping"));
211 NavigateToURL(shell(), test_url
);
212 // RunLoop is quit when message received from page.
214 EXPECT_TRUE(got_message
);
216 // Check that a second render frame in the same renderer process works
218 Shell
* other_shell
= CreateBrowser();
220 base::RunLoop other_run_loop
;
221 factory()->set_run_loop(&other_run_loop
);
222 NavigateToURL(other_shell
, test_url
);
223 // RunLoop is quit when message received from page.
224 other_run_loop
.Run();
225 EXPECT_TRUE(got_message
);
226 EXPECT_EQ(shell()->web_contents()->GetRenderProcessHost(),
227 other_shell
->web_contents()->GetRenderProcessHost());
231 } // namespace content