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. 4 5 #include <limits> 6 7 #include "base/command_line.h" 8 #include "base/file_util.h" 9 #include "base/files/file_path.h" 10 #include "base/path_service.h" 11 #include "base/run_loop.h" 12 #include "base/strings/string_util.h" 13 #include "content/browser/webui/web_ui_controller_factory_registry.h" 14 #include "content/public/browser/browser_context.h" 15 #include "content/public/browser/render_view_host.h" 16 #include "content/public/browser/web_contents.h" 17 #include "content/public/browser/web_ui_controller.h" 18 #include "content/public/browser/web_ui_data_source.h" 19 #include "content/public/common/content_paths.h" 20 #include "content/public/common/content_switches.h" 21 #include "content/public/common/url_utils.h" 22 #include "content/public/test/content_browser_test.h" 23 #include "content/public/test/content_browser_test_utils.h" 24 #include "content/test/data/web_ui_test_mojo_bindings.mojom.h" 25 #include "grit/content_resources.h" 26 #include "mojo/common/test/test_utils.h" 27 #include "mojo/public/js/bindings/constants.h" 28 29 namespace content { 30 namespace { 31 32 bool got_message = false; 33 34 // The bindings for the page are generated from a .mojom file. This code looks 35 // up the generated file from disk and returns it. 36 bool GetResource(const std::string& id, 37 const WebUIDataSource::GotDataCallback& callback) { 38 // These are handled by the WebUIDataSource that AddMojoDataSource() creates. 39 if (id == mojo::kCodecModuleName || 40 id == mojo::kConnectionModuleName || 41 id == mojo::kConnectorModuleName || 42 id == mojo::kUnicodeModuleName || 43 id == mojo::kRouterModuleName) 44 return false; 45 46 std::string contents; 47 CHECK(base::ReadFileToString(mojo::test::GetFilePathForJSResource(id), 48 &contents, 49 std::string::npos)) << id; 50 base::RefCountedString* ref_contents = new base::RefCountedString; 51 ref_contents->data() = contents; 52 callback.Run(ref_contents); 53 return true; 54 } 55 56 class BrowserTargetImpl : public BrowserTarget { 57 public: 58 BrowserTargetImpl(mojo::ScopedMessagePipeHandle handle, 59 base::RunLoop* run_loop) 60 : run_loop_(run_loop) { 61 renderer_.Bind(handle.Pass()); 62 renderer_.set_client(this); 63 } 64 65 virtual ~BrowserTargetImpl() {} 66 67 // BrowserTarget overrides: 68 virtual void PingResponse() OVERRIDE { 69 NOTREACHED(); 70 } 71 72 protected: 73 RendererTargetPtr renderer_; 74 base::RunLoop* run_loop_; 75 76 private: 77 DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl); 78 }; 79 80 class PingBrowserTargetImpl : public BrowserTargetImpl { 81 public: 82 PingBrowserTargetImpl(mojo::ScopedMessagePipeHandle handle, 83 base::RunLoop* run_loop) 84 : BrowserTargetImpl(handle.Pass(), run_loop) { 85 renderer_->Ping(); 86 } 87 88 virtual ~PingBrowserTargetImpl() {} 89 90 // BrowserTarget overrides: 91 // Quit the RunLoop when called. 92 virtual void PingResponse() OVERRIDE { 93 got_message = true; 94 run_loop_->Quit(); 95 } 96 97 private: 98 DISALLOW_COPY_AND_ASSIGN(PingBrowserTargetImpl); 99 }; 100 101 // WebUIController that sets up mojo bindings. 102 class TestWebUIController : public WebUIController { 103 public: 104 TestWebUIController(WebUI* web_ui, base::RunLoop* run_loop) 105 : WebUIController(web_ui), 106 run_loop_(run_loop) { 107 content::WebUIDataSource* data_source = 108 WebUIDataSource::AddMojoDataSource( 109 web_ui->GetWebContents()->GetBrowserContext()); 110 data_source->SetRequestFilter(base::Bind(&GetResource)); 111 } 112 113 protected: 114 base::RunLoop* run_loop_; 115 scoped_ptr<BrowserTargetImpl> browser_target_; 116 117 private: 118 DISALLOW_COPY_AND_ASSIGN(TestWebUIController); 119 }; 120 121 // TestWebUIController that additionally creates the ping test BrowserTarget 122 // implementation at the right time. 123 class PingTestWebUIController : public TestWebUIController { 124 public: 125 PingTestWebUIController(WebUI* web_ui, base::RunLoop* run_loop) 126 : TestWebUIController(web_ui, run_loop) { 127 } 128 129 // WebUIController overrides: 130 virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE { 131 mojo::MessagePipe pipe; 132 browser_target_.reset( 133 new PingBrowserTargetImpl(pipe.handle0.Pass(), run_loop_)); 134 render_view_host->SetWebUIHandle(pipe.handle1.Pass()); 135 } 136 137 private: 138 DISALLOW_COPY_AND_ASSIGN(PingTestWebUIController); 139 }; 140 141 // WebUIControllerFactory that creates TestWebUIController. 142 class TestWebUIControllerFactory : public WebUIControllerFactory { 143 public: 144 TestWebUIControllerFactory() : run_loop_(NULL) {} 145 146 void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; } 147 148 virtual WebUIController* CreateWebUIControllerForURL( 149 WebUI* web_ui, const GURL& url) const OVERRIDE { 150 if (url.query() == "ping") 151 return new PingTestWebUIController(web_ui, run_loop_); 152 return NULL; 153 } 154 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, 155 const GURL& url) const OVERRIDE { 156 return reinterpret_cast<WebUI::TypeID>(1); 157 } 158 virtual bool UseWebUIForURL(BrowserContext* browser_context, 159 const GURL& url) const OVERRIDE { 160 return true; 161 } 162 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context, 163 const GURL& url) const OVERRIDE { 164 return true; 165 } 166 167 private: 168 base::RunLoop* run_loop_; 169 170 DISALLOW_COPY_AND_ASSIGN(TestWebUIControllerFactory); 171 }; 172 173 class WebUIMojoTest : public ContentBrowserTest { 174 public: 175 WebUIMojoTest() { 176 WebUIControllerFactory::RegisterFactory(&factory_); 177 } 178 179 virtual ~WebUIMojoTest() { 180 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_); 181 } 182 183 TestWebUIControllerFactory* factory() { return &factory_; } 184 185 private: 186 TestWebUIControllerFactory factory_; 187 188 DISALLOW_COPY_AND_ASSIGN(WebUIMojoTest); 189 }; 190 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 196 // pass. 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"; 203 return; 204 } 205 206 got_message = false; 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. 213 run_loop.Run(); 214 EXPECT_TRUE(got_message); 215 } 216 217 } // namespace 218 } // namespace content 219