Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(314)

Unified Diff: headless/lib/embedder_mojo_browsertest.cc

Issue 2049363003: Adds support for headless chrome embedder mojo services (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix MojoBindingsReinstalledAfterNavigation to use a browser initiated cross-origin navigation. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « headless/lib/browser/headless_web_contents_impl.cc ('k') | headless/lib/embedder_test.mojom » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: headless/lib/embedder_mojo_browsertest.cc
diff --git a/headless/lib/embedder_mojo_browsertest.cc b/headless/lib/embedder_mojo_browsertest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..51c056ce14da4873c6f921cee0c09358280d72bc
--- /dev/null
+++ b/headless/lib/embedder_mojo_browsertest.cc
@@ -0,0 +1,322 @@
+// Copyright 2016 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 <memory>
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/public/test/browser_test.h"
+#include "headless/grit/headless_browsertest_resources.h"
+#include "headless/lib/embedder_test.mojom.h"
+#include "headless/public/domains/runtime.h"
+#include "headless/public/domains/page.h"
+#include "headless/public/headless_browser.h"
+#include "headless/public/headless_devtools_client.h"
+#include "headless/public/headless_devtools_target.h"
+#include "headless/public/headless_web_contents.h"
+#include "headless/test/headless_browser_test.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "url/gurl.h"
+
+namespace headless {
+
+#define DEVTOOLS_CLIENT_TEST_F(TEST_FIXTURE_NAME) \
+ IN_PROC_BROWSER_TEST_F(TEST_FIXTURE_NAME, RunAsyncTest) { RunTest(); } \
+ class AsyncHeadlessBrowserTestNeedsSemicolon##TEST_FIXTURE_NAME {}
+
+// A test fixture which attaches a devtools client before starting the test.
+class EmbedderMojoTest : public HeadlessBrowserTest,
+ public HeadlessWebContents::Observer,
+ public embedder_test::TestEmbedderService {
+ public:
+ EmbedderMojoTest()
+ : web_contents_(nullptr),
+ devtools_client_(HeadlessDevToolsClient::Create()) {}
+ ~EmbedderMojoTest() override {}
+
+ void SetUpOnMainThread() override {
+ base::ThreadRestrictions::SetIOAllowed(true);
+ base::FilePath pak_path;
+ ASSERT_TRUE(PathService::Get(base::DIR_MODULE, &pak_path));
+ pak_path = pak_path.AppendASCII("headless_browser_tests.pak");
+ ResourceBundle::GetSharedInstance().AddDataPackFromPath(
+ pak_path, ui::SCALE_FACTOR_NONE);
+ }
+
+ // HeadlessWebContentsObserver implementation:
+ void DevToolsTargetReady() override {
+ EXPECT_TRUE(web_contents_->GetDevToolsTarget());
+ web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
+
+ RunMojoTest();
+ }
+
+ virtual void RunMojoTest() = 0;
+
+ virtual GURL GetInitialUrl() const { return GURL("about:blank"); }
+
+ void OnEvalResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ EXPECT_FALSE(result->HasExceptionDetails())
+ << "JS exception: " << result->GetExceptionDetails()->GetText();
+ if (result->HasExceptionDetails()) {
+ FinishAsynchronousTest();
+ }
+ }
+
+ protected:
+ void RunTest() {
+ // Using a pak file is idomatic chromium style, but most embedders probably
+ // wouln't load the javascript bindings file this way.
+ std::string embedder_test_mojom_js =
+ ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_HEADLESS_EMBEDDER_TEST_MOJOM_JS)
+ .as_string();
+
+ web_contents_ =
+ browser()
+ ->CreateWebContentsBuilder()
+ .SetInitialURL(GURL(GetInitialUrl()))
+ .AddMojoService(base::Bind(&EmbedderMojoTest::CreateTestMojoService,
+ base::Unretained(this)))
+ .AddJsMojoBindings("headless/lib/embedder_test.mojom",
+ embedder_test_mojom_js)
+ .Build();
+
+ web_contents_->AddObserver(this);
+ RunAsynchronousTest();
+
+ web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
+ web_contents_->RemoveObserver(this);
+ web_contents_->Close();
+ web_contents_ = nullptr;
+ }
+
+ void CreateTestMojoService(
+ mojo::InterfaceRequest<embedder_test::TestEmbedderService> request) {
+ test_embedder_mojo_bindings_.AddBinding(this, std::move(request));
+ }
+
+ HeadlessWebContents* web_contents_;
+ std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
+
+ mojo::BindingSet<embedder_test::TestEmbedderService>
+ test_embedder_mojo_bindings_;
+};
+
+class EmbedderMojoBindingsTest : public EmbedderMojoTest {
+ public:
+ void RunMojoTest() override {
+ devtools_client_->GetRuntime()->Evaluate(
+ "mojo.services.embedder_test.TestEmbedderService.then( \n"
+ " function(service) { \n"
+ " service.returnTestResult('hello world'); \n"
+ " });",
+ base::Bind(&EmbedderMojoTest::OnEvalResult, base::Unretained(this)));
+ }
+
+ // embedder_test::TestEmbedderService:
+ void ReturnTestResult(const mojo::String& result) override {
+ EXPECT_EQ("hello world", result.get());
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(EmbedderMojoBindingsTest);
+
+class RejectNonExistentBindingsTest : public EmbedderMojoTest {
+ public:
+ void RunMojoTest() override {
+ devtools_client_->GetRuntime()->Evaluate(
+ "mojo.services.embedder_test.TestEmbedderService.then( \n"
+ " function(service) { \n"
+ " mojo.services.nonExistent.MojoService.then(function() { \n"
+ " service.returnTestResult('Fail - promise not rejected'); \n"
+ " }).catch(function() { \n"
+ " service.returnTestResult('Pass - promise rejected'); \n"
+ " }); \n"
+ " });",
+ base::Bind(&EmbedderMojoTest::OnEvalResult, base::Unretained(this)));
+ }
+
+ // embedder_test::TestEmbedderService:
+ void ReturnTestResult(const mojo::String& result) override {
+ EXPECT_EQ("Pass - promise rejected", result.get());
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(RejectNonExistentBindingsTest);
+
+// Test bindings that occur after the onload event, which is after the browser
+// has sent us the bindings.
+class DelayedRejectNonExistentBindingsTest : public EmbedderMojoTest {
+ public:
+ DelayedRejectNonExistentBindingsTest() {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ }
+
+ GURL GetInitialUrl() const override {
+ return embedded_test_server()->GetURL(
+ "/late_nonexistent_mojo_binding.html");
+ }
+
+ void RunMojoTest() override {}
+
+ // embedder_test::TestEmbedderService:
+ void ReturnTestResult(const mojo::String& result) override {
+ EXPECT_EQ("Pass - promise rejected", result.get());
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(DelayedRejectNonExistentBindingsTest);
+
+class HeadScriptEmbedderMojoBindingsTest : public EmbedderMojoTest {
+ public:
+ HeadScriptEmbedderMojoBindingsTest() {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ }
+
+ void RunMojoTest() override {}
+
+ GURL GetInitialUrl() const override {
+ return embedded_test_server()->GetURL("/mojo_test.html");
+ }
+
+ // embedder_test::TestEmbedderService:
+ void ReturnTestResult(const mojo::String& result) override {
+ EXPECT_EQ("hello world", result.get());
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(HeadScriptEmbedderMojoBindingsTest);
+
+class DefaultMojoStyleBindingsTest : public EmbedderMojoTest {
+ public:
+ void RunMojoTest() override {
+ devtools_client_->GetRuntime()->Evaluate(
+ "// Note define() defines a module in the mojo module dependency \n"
+ "// system. While we don't expose our module, the callback below only\n"
+ "// fires after the requested modules have been loaded. \n"
+ "define([ \n"
+ " 'headless/lib/embedder_test.mojom', \n"
+ " 'mojo/public/js/core', \n"
+ " 'mojo/public/js/router', \n"
+ " 'content/public/renderer/frame_service_registry', \n"
+ " ], function(embedderMojom, mojoCore, routerModule, \n"
+ " serviceProvider) { \n"
+ " var testEmbedderService = \n"
+ " new embedderMojom.TestEmbedderService.proxyClass( \n"
+ " new routerModule.Router( \n"
+ " serviceProvider.connectToService( \n"
+ " embedderMojom.TestEmbedderService.name))); \n"
+ " \n"
+ " // Send a message to the embedder! \n"
+ " testEmbedderService.returnTestResult('hello world'); \n"
+ "});",
+ base::Bind(&EmbedderMojoTest::OnEvalResult, base::Unretained(this)));
+ }
+
+ // embedder_test::TestEmbedderService:
+ void ReturnTestResult(const mojo::String& result) override {
+ EXPECT_EQ("hello world", result.get());
+ FinishAsynchronousTest();
+ }
+};
+
+DEVTOOLS_CLIENT_TEST_F(DefaultMojoStyleBindingsTest);
+
+class AssignToMojoServicesProxyNotAllowed : public EmbedderMojoTest {
+ public:
+ void RunMojoTest() override {
+ devtools_client_->GetRuntime()->Evaluate(
+ "mojo.services.foo = 'bar';",
+ base::Bind(&AssignToMojoServicesProxyNotAllowed::OnEvalResult,
+ base::Unretained(this)));
+ }
+
+ void OnEvalResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ ASSERT_TRUE(result->HasExceptionDetails());
+ EXPECT_EQ(
+ "Uncaught Error: Assignment to the mojo services proxy is not allowed",
+ result->GetExceptionDetails()->GetText());
+ FinishAsynchronousTest();
+ }
+
+ void ReturnTestResult(const mojo::String& result) override {}
+};
+
+DEVTOOLS_CLIENT_TEST_F(AssignToMojoServicesProxyNotAllowed);
+
+class AssignToMojoServicesSecondaryProxyNotAllowed : public EmbedderMojoTest {
+ public:
+ void RunMojoTest() override {
+ devtools_client_->GetRuntime()->Evaluate(
+ "mojo.services.foo.bar = 'baz';",
+ base::Bind(&AssignToMojoServicesSecondaryProxyNotAllowed::OnEvalResult,
+ base::Unretained(this)));
+ }
+
+ void OnEvalResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ ASSERT_TRUE(result->HasExceptionDetails());
+ EXPECT_EQ(
+ "Uncaught Error: Assignment to the mojo services proxy is not allowed",
+ result->GetExceptionDetails()->GetText());
+ FinishAsynchronousTest();
+ }
+
+ void ReturnTestResult(const mojo::String& result) override {}
+};
+
+DEVTOOLS_CLIENT_TEST_F(AssignToMojoServicesSecondaryProxyNotAllowed);
+
+class MojoBindingsReinstalledAfterNavigation : public EmbedderMojoTest {
+ public:
+ MojoBindingsReinstalledAfterNavigation() : seen_page_one_(false) {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ }
+
+ void SetUpOnMainThread() override {
+ // We want to make sure bindings work across browser initiaited cross-origin
Charlie Reis 2016/06/22 19:30:59 nit: initiated
alex clarke (OOO till 29th) 2016/06/22 20:54:22 Done.
+ // navigation, which is why we're setting up this fake tld.
+ HeadlessBrowser::Options::Builder builder;
+ builder.SetHostResolverRules(
+ base::StringPrintf("MAP not-an-actual-domain.tld 127.0.0.1:%d",
+ embedded_test_server()->host_port_pair().port()));
+ SetBrowserOptions(builder.Build());
+
+ EmbedderMojoTest::SetUpOnMainThread();
+ }
+
+ void RunMojoTest() override {}
+
+ GURL GetInitialUrl() const override {
+ return embedded_test_server()->GetURL("/page_one.html");
+ }
+
+ // embedder_test::TestEmbedderService:
+ void ReturnTestResult(const mojo::String& result) override {
+ if (result.get() == "page one") {
+ seen_page_one_ = true;
+ devtools_client_->GetPage()->Navigate(
+ "http://not-an-actual-domain.tld/page_two.html");
+ } else {
+ EXPECT_TRUE(seen_page_one_);
+ EXPECT_EQ("page two", result.get());
+ FinishAsynchronousTest();
+ }
+ }
+
+ private:
+ bool seen_page_one_;
+};
+
+DEVTOOLS_CLIENT_TEST_F(MojoBindingsReinstalledAfterNavigation);
+
+} // namespace headless
« no previous file with comments | « headless/lib/browser/headless_web_contents_impl.cc ('k') | headless/lib/embedder_test.mojom » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698