blob: 065bc7a438b84429d838e5f17e645301bafd820e [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/service_registry.h"
#include "content/public/common/url_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/data/web_ui_test_mojo_bindings.mojom.h"
#include "mojo/common/test/test_utils.h"
#include "mojo/public/cpp/bindings/interface_impl.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/js/bindings/constants.h"
namespace content {
namespace {
bool got_message = false;
// The bindings for the page are generated from a .mojom file. This code looks
// up the generated file from disk and returns it.
bool GetResource(const std::string& id,
const WebUIDataSource::GotDataCallback& callback) {
// These are handled by the WebUIDataSource that AddMojoDataSource() creates.
if (id == mojo::kBufferModuleName ||
id == mojo::kCodecModuleName ||
id == mojo::kConnectionModuleName ||
id == mojo::kConnectorModuleName ||
id == mojo::kUnicodeModuleName ||
id == mojo::kRouterModuleName ||
id == mojo::kValidatorModuleName)
return false;
std::string contents;
CHECK(base::ReadFileToString(mojo::test::GetFilePathForJSResource(id),
&contents,
std::string::npos)) << id;
base::RefCountedString* ref_contents = new base::RefCountedString;
ref_contents->data() = contents;
callback.Run(ref_contents);
return true;
}
class BrowserTargetImpl : public mojo::InterfaceImpl<BrowserTarget> {
public:
explicit BrowserTargetImpl(base::RunLoop* run_loop) : run_loop_(run_loop) {}
virtual ~BrowserTargetImpl() {}
// mojo::InterfaceImpl<BrowserTarget> overrides:
virtual void PingResponse() OVERRIDE {
NOTREACHED();
}
protected:
base::RunLoop* run_loop_;
private:
DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl);
};
class PingBrowserTargetImpl : public BrowserTargetImpl {
public:
explicit PingBrowserTargetImpl(base::RunLoop* run_loop)
: BrowserTargetImpl(run_loop) {}
virtual ~PingBrowserTargetImpl() {}
// mojo::InterfaceImpl<BrowserTarget> overrides:
virtual void OnConnectionEstablished() OVERRIDE {
client()->Ping();
}
// Quit the RunLoop when called.
virtual void PingResponse() OVERRIDE {
got_message = true;
run_loop_->Quit();
}
private:
DISALLOW_COPY_AND_ASSIGN(PingBrowserTargetImpl);
};
// WebUIController that sets up mojo bindings.
class TestWebUIController : public WebUIController {
public:
TestWebUIController(WebUI* web_ui, base::RunLoop* run_loop)
: WebUIController(web_ui),
run_loop_(run_loop) {
content::WebUIDataSource* data_source =
WebUIDataSource::AddMojoDataSource(
web_ui->GetWebContents()->GetBrowserContext());
data_source->SetRequestFilter(base::Bind(&GetResource));
}
protected:
base::RunLoop* run_loop_;
scoped_ptr<BrowserTargetImpl> browser_target_;
private:
DISALLOW_COPY_AND_ASSIGN(TestWebUIController);
};
// TestWebUIController that additionally creates the ping test BrowserTarget
// implementation at the right time.
class PingTestWebUIController : public TestWebUIController {
public:
PingTestWebUIController(WebUI* web_ui, base::RunLoop* run_loop)
: TestWebUIController(web_ui, run_loop) {
}
virtual ~PingTestWebUIController() {}
// WebUIController overrides:
virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE {
render_view_host->GetMainFrame()->GetServiceRegistry()->
AddService<BrowserTarget>(base::Bind(
&PingTestWebUIController::CreateHandler, base::Unretained(this)));
}
void CreateHandler(mojo::InterfaceRequest<BrowserTarget> request) {
browser_target_.reset(mojo::WeakBindToRequest(
new PingBrowserTargetImpl(run_loop_), &request));
}
private:
DISALLOW_COPY_AND_ASSIGN(PingTestWebUIController);
};
// WebUIControllerFactory that creates TestWebUIController.
class TestWebUIControllerFactory : public WebUIControllerFactory {
public:
TestWebUIControllerFactory() : run_loop_(NULL) {}
void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
virtual WebUIController* CreateWebUIControllerForURL(
WebUI* web_ui, const GURL& url) const OVERRIDE {
if (url.query() == "ping")
return new PingTestWebUIController(web_ui, run_loop_);
return NULL;
}
virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
const GURL& url) const OVERRIDE {
return reinterpret_cast<WebUI::TypeID>(1);
}
virtual bool UseWebUIForURL(BrowserContext* browser_context,
const GURL& url) const OVERRIDE {
return true;
}
virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
const GURL& url) const OVERRIDE {
return true;
}
private:
base::RunLoop* run_loop_;
DISALLOW_COPY_AND_ASSIGN(TestWebUIControllerFactory);
};
class WebUIMojoTest : public ContentBrowserTest {
public:
WebUIMojoTest() {
WebUIControllerFactory::RegisterFactory(&factory_);
}
virtual ~WebUIMojoTest() {
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
}
TestWebUIControllerFactory* factory() { return &factory_; }
private:
TestWebUIControllerFactory factory_;
DISALLOW_COPY_AND_ASSIGN(WebUIMojoTest);
};
// Loads a webui page that contains mojo bindings and verifies a message makes
// it from the browser to the page and back.
// Fails on Win and Linux. http://crbug.com/418019
#if defined(OS_WIN) || defined(OS_LINUX)
#define MAYBE_EndToEndPing DISABLED_EndToEndPing
#else
#define MAYBE_EndToEndPing EndToEndPing
#endif
IN_PROC_BROWSER_TEST_F(WebUIMojoTest, MAYBE_EndToEndPing) {
// Currently there is no way to have a generated file included in the isolate
// files. If the bindings file doesn't exist assume we're on such a bot and
// pass.
// TODO(sky): remove this conditional when isolates support copying from gen.
const base::FilePath test_file_path(
mojo::test::GetFilePathForJSResource(
"content/test/data/web_ui_test_mojo_bindings.mojom"));
if (!base::PathExists(test_file_path)) {
LOG(WARNING) << " mojom binding file doesn't exist, assuming on isolate";
return;
}
got_message = false;
ASSERT_TRUE(test_server()->Start());
base::RunLoop run_loop;
factory()->set_run_loop(&run_loop);
GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?ping"));
NavigateToURL(shell(), test_url);
// RunLoop is quit when message received from page.
run_loop.Run();
EXPECT_TRUE(got_message);
// Check that a second render frame in the same renderer process works
// correctly.
Shell* other_shell = CreateBrowser();
got_message = false;
base::RunLoop other_run_loop;
factory()->set_run_loop(&other_run_loop);
NavigateToURL(other_shell, test_url);
// RunLoop is quit when message received from page.
other_run_loop.Run();
EXPECT_TRUE(got_message);
EXPECT_EQ(shell()->web_contents()->GetRenderProcessHost(),
other_shell->web_contents()->GetRenderProcessHost());
}
} // namespace
} // namespace content