headless: Allow per-context protocol handlers
Make it possible to set protocol handlers per browser context instead of
having to replace handlers for the browser globally.
BUG=546953
Review-Url: https://codereview.chromium.org/2092773002
Cr-Commit-Position: refs/heads/master@{#401638}
diff --git a/headless/lib/headless_browser_context_browsertest.cc b/headless/lib/headless_browser_context_browsertest.cc
new file mode 100644
index 0000000..34490bb6
--- /dev/null
+++ b/headless/lib/headless_browser_context_browsertest.cc
@@ -0,0 +1,205 @@
+// 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/memory/ptr_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/public/test/browser_test.h"
+#include "headless/public/domains/runtime.h"
+#include "headless/public/headless_browser.h"
+#include "headless/public/headless_browser_context.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 "headless/test/test_protocol_handler.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_response_headers.h"
+#include "net/test/spawned_test_server/spawned_test_server.h"
+#include "net/url_request/url_request_job.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace headless {
+namespace {
+const char kMainPageCookie[] = "mood=quizzical";
+const char kIsolatedPageCookie[] = "mood=quixotic";
+} // namespace
+
+// This test creates two tabs pointing to the same security origin in two
+// different browser contexts and checks that they are isolated by creating two
+// cookies with the same name in both tabs. The steps are:
+//
+// 1. Wait for tab #1 to become ready for DevTools.
+// 2. Create tab #2 and wait for it to become ready for DevTools.
+// 3. Navigate tab #1 to the test page and wait for it to finish loading.
+// 4. Navigate tab #2 to the test page and wait for it to finish loading.
+// 5. Set a cookie in tab #1.
+// 6. Set the same cookie in tab #2 to a different value.
+// 7. Read the cookie in tab #1 and check that it has the first value.
+// 8. Read the cookie in tab #2 and check that it has the second value.
+//
+// If the tabs aren't properly isolated, step 7 will fail.
+class HeadlessBrowserContextIsolationTest
+ : public HeadlessAsyncDevTooledBrowserTest {
+ public:
+ HeadlessBrowserContextIsolationTest()
+ : web_contents2_(nullptr),
+ devtools_client2_(HeadlessDevToolsClient::Create()) {
+ EXPECT_TRUE(embedded_test_server()->Start());
+ }
+
+ // HeadlessWebContentsObserver implementation:
+ void DevToolsTargetReady() override {
+ if (!web_contents2_) {
+ browser_context_ = browser()->CreateBrowserContextBuilder().Build();
+ web_contents2_ = browser()
+ ->CreateWebContentsBuilder()
+ .SetBrowserContext(browser_context_.get())
+ .Build();
+ web_contents2_->AddObserver(this);
+ return;
+ }
+
+ web_contents2_->GetDevToolsTarget()->AttachClient(devtools_client2_.get());
+ HeadlessAsyncDevTooledBrowserTest::DevToolsTargetReady();
+ }
+
+ void RunDevTooledTest() override {
+ load_observer_.reset(new LoadObserver(
+ devtools_client_.get(),
+ base::Bind(&HeadlessBrowserContextIsolationTest::OnFirstLoadComplete,
+ base::Unretained(this))));
+ devtools_client_->GetPage()->Navigate(
+ embedded_test_server()->GetURL("/hello.html").spec());
+ }
+
+ void OnFirstLoadComplete() {
+ EXPECT_TRUE(load_observer_->navigation_succeeded());
+ load_observer_.reset(new LoadObserver(
+ devtools_client2_.get(),
+ base::Bind(&HeadlessBrowserContextIsolationTest::OnSecondLoadComplete,
+ base::Unretained(this))));
+ devtools_client2_->GetPage()->Navigate(
+ embedded_test_server()->GetURL("/hello.html").spec());
+ }
+
+ void OnSecondLoadComplete() {
+ EXPECT_TRUE(load_observer_->navigation_succeeded());
+ load_observer_.reset();
+
+ devtools_client_->GetRuntime()->Evaluate(
+ base::StringPrintf("document.cookie = '%s'", kMainPageCookie),
+ base::Bind(&HeadlessBrowserContextIsolationTest::OnFirstSetCookieResult,
+ base::Unretained(this)));
+ }
+
+ void OnFirstSetCookieResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ std::string cookie;
+ EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
+ EXPECT_EQ(kMainPageCookie, cookie);
+
+ devtools_client2_->GetRuntime()->Evaluate(
+ base::StringPrintf("document.cookie = '%s'", kIsolatedPageCookie),
+ base::Bind(
+ &HeadlessBrowserContextIsolationTest::OnSecondSetCookieResult,
+ base::Unretained(this)));
+ }
+
+ void OnSecondSetCookieResult(
+ std::unique_ptr<runtime::EvaluateResult> result) {
+ std::string cookie;
+ EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
+ EXPECT_EQ(kIsolatedPageCookie, cookie);
+
+ devtools_client_->GetRuntime()->Evaluate(
+ "document.cookie",
+ base::Bind(&HeadlessBrowserContextIsolationTest::OnFirstGetCookieResult,
+ base::Unretained(this)));
+ }
+
+ void OnFirstGetCookieResult(std::unique_ptr<runtime::EvaluateResult> result) {
+ std::string cookie;
+ EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
+ EXPECT_EQ(kMainPageCookie, cookie);
+
+ devtools_client2_->GetRuntime()->Evaluate(
+ "document.cookie",
+ base::Bind(
+ &HeadlessBrowserContextIsolationTest::OnSecondGetCookieResult,
+ base::Unretained(this)));
+ }
+
+ void OnSecondGetCookieResult(
+ std::unique_ptr<runtime::EvaluateResult> result) {
+ std::string cookie;
+ EXPECT_TRUE(result->GetResult()->GetValue()->GetAsString(&cookie));
+ EXPECT_EQ(kIsolatedPageCookie, cookie);
+ FinishTest();
+ }
+
+ void FinishTest() {
+ web_contents2_->RemoveObserver(this);
+ web_contents2_->Close();
+ browser_context_.reset();
+ FinishAsynchronousTest();
+ }
+
+ private:
+ std::unique_ptr<HeadlessBrowserContext> browser_context_;
+ HeadlessWebContents* web_contents2_;
+ std::unique_ptr<HeadlessDevToolsClient> devtools_client2_;
+ std::unique_ptr<LoadObserver> load_observer_;
+};
+
+HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessBrowserContextIsolationTest);
+
+IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, ContextProtocolHandler) {
+ const std::string kResponseBody = "<p>HTTP response body</p>";
+ ProtocolHandlerMap protocol_handlers;
+ protocol_handlers[url::kHttpScheme] =
+ base::WrapUnique(new TestProtocolHandler(kResponseBody));
+
+ // Load a page which doesn't actually exist, but which is fetched by our
+ // custom protocol handler.
+ std::unique_ptr<HeadlessBrowserContext> browser_context =
+ browser()
+ ->CreateBrowserContextBuilder()
+ .SetProtocolHandlers(std::move(protocol_handlers))
+ .Build();
+ HeadlessWebContents* web_contents =
+ browser()
+ ->CreateWebContentsBuilder()
+ .SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html"))
+ .SetBrowserContext(browser_context.get())
+ .Build();
+ EXPECT_TRUE(WaitForLoad(web_contents));
+
+ std::string inner_html;
+ EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML")
+ ->GetResult()
+ ->GetValue()
+ ->GetAsString(&inner_html));
+ EXPECT_EQ(kResponseBody, inner_html);
+ web_contents->Close();
+
+ // Loading the same non-existent page using a tab with the default context
+ // should not work since the protocol handler only exists on the custom
+ // context.
+ web_contents =
+ browser()
+ ->CreateWebContentsBuilder()
+ .SetInitialURL(GURL("http://not-an-actual-domain.tld/hello.html"))
+ .Build();
+ EXPECT_TRUE(WaitForLoad(web_contents));
+ EXPECT_TRUE(EvaluateScript(web_contents, "document.body.innerHTML")
+ ->GetResult()
+ ->GetValue()
+ ->GetAsString(&inner_html));
+ EXPECT_EQ("", inner_html);
+ web_contents->Close();
+}
+
+} // namespace headless