Skip to content

Commit

Permalink
headless: Allow per-context protocol handlers
Browse files Browse the repository at this point in the history
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}
  • Loading branch information
skyostil authored and Commit bot committed Jun 23, 2016
1 parent 1459934 commit d34af14
Show file tree
Hide file tree
Showing 14 changed files with 378 additions and 225 deletions.
5 changes: 5 additions & 0 deletions headless/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,16 @@ action("client_api_generator_tests") {
test("headless_browsertests") {
sources = [
"lib/headless_browser_browsertest.cc",
"lib/headless_browser_context_browsertest.cc",
"lib/headless_devtools_client_browsertest.cc",
"lib/headless_web_contents_browsertest.cc",
"test/headless_browser_test.cc",
"test/headless_browser_test.h",
"test/headless_test_launcher.cc",
"test/test_protocol_handler.cc",
"test/test_protocol_handler.h",
"test/test_url_request_job.cc",
"test/test_url_request_job.h",
]

defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
Expand Down
18 changes: 15 additions & 3 deletions headless/lib/browser/headless_browser_context_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ net::URLRequestContext* HeadlessResourceContext::GetRequestContext() {
}

HeadlessBrowserContextImpl::HeadlessBrowserContextImpl(
ProtocolHandlerMap protocol_handlers,
HeadlessBrowser::Options* options)
: resource_context_(new HeadlessResourceContext), options_(options) {
: protocol_handlers_(std::move(protocol_handlers)),
options_(options),
resource_context_(new HeadlessResourceContext) {
InitWhileIOAllowed();
}

Expand Down Expand Up @@ -153,7 +156,8 @@ net::URLRequestContextGetter* HeadlessBrowserContextImpl::CreateRequestContext(
content::BrowserThread::IO),
content::BrowserThread::GetMessageLoopProxyForThread(
content::BrowserThread::FILE),
protocol_handlers, std::move(request_interceptors), options()));
protocol_handlers, std::move(protocol_handlers_),
std::move(request_interceptors), options()));
resource_context_->set_url_request_context_getter(url_request_context_getter);
return url_request_context_getter.get();
}
Expand Down Expand Up @@ -191,9 +195,17 @@ HeadlessBrowserContext::Builder::~Builder() = default;

HeadlessBrowserContext::Builder::Builder(Builder&&) = default;

HeadlessBrowserContext::Builder&
HeadlessBrowserContext::Builder::SetProtocolHandlers(
ProtocolHandlerMap protocol_handlers) {
protocol_handlers_ = std::move(protocol_handlers);
return *this;
}

std::unique_ptr<HeadlessBrowserContext>
HeadlessBrowserContext::Builder::Build() {
return base::WrapUnique(new HeadlessBrowserContextImpl(browser_->options()));
return base::WrapUnique(new HeadlessBrowserContextImpl(
std::move(protocol_handlers_), browser_->options()));
}

} // namespace headless
8 changes: 5 additions & 3 deletions headless/lib/browser/headless_browser_context_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class HeadlessResourceContext;
class HeadlessBrowserContextImpl : public HeadlessBrowserContext,
public content::BrowserContext {
public:
explicit HeadlessBrowserContextImpl(HeadlessBrowser::Options* options);
explicit HeadlessBrowserContextImpl(ProtocolHandlerMap protocol_handlers,
HeadlessBrowser::Options* options);
~HeadlessBrowserContextImpl() override;

static HeadlessBrowserContextImpl* From(
Expand Down Expand Up @@ -61,9 +62,10 @@ class HeadlessBrowserContextImpl : public HeadlessBrowserContext,
// allowed on the current thread.
void InitWhileIOAllowed();

base::FilePath path_;
std::unique_ptr<HeadlessResourceContext> resource_context_;
ProtocolHandlerMap protocol_handlers_;
HeadlessBrowser::Options* options_; // Not owned.
std::unique_ptr<HeadlessResourceContext> resource_context_;
base::FilePath path_;

DISALLOW_COPY_AND_ASSIGN(HeadlessBrowserContextImpl);
};
Expand Down
3 changes: 2 additions & 1 deletion headless/lib/browser/headless_browser_main_parts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ HeadlessBrowserMainParts::HeadlessBrowserMainParts(HeadlessBrowserImpl* browser)
HeadlessBrowserMainParts::~HeadlessBrowserMainParts() {}

void HeadlessBrowserMainParts::PreMainMessageLoopRun() {
browser_context_.reset(new HeadlessBrowserContextImpl(browser_->options()));
browser_context_.reset(new HeadlessBrowserContextImpl(ProtocolHandlerMap(),
browser_->options()));
if (browser_->options()->devtools_endpoint.address().IsValid()) {
devtools_http_handler_ =
CreateLocalDevToolsHttpHandler(browser_context_.get());
Expand Down
7 changes: 7 additions & 0 deletions headless/lib/browser/headless_url_request_context_getter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ HeadlessURLRequestContextGetter::HeadlessURLRequestContextGetter(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
content::ProtocolHandlerMap* protocol_handlers,
ProtocolHandlerMap context_protocol_handlers,
content::URLRequestInterceptorScopedVector request_interceptors,
HeadlessBrowser::Options* options)
: io_task_runner_(std::move(io_task_runner)),
Expand All @@ -38,6 +39,12 @@ HeadlessURLRequestContextGetter::HeadlessURLRequestContextGetter(
pair.second.release());
}
options->protocol_handlers.clear();
for (auto& pair : context_protocol_handlers) {
protocol_handlers_[pair.first] =
linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
pair.second.release());
}
context_protocol_handlers.clear();

// We must create the proxy config service on the UI loop on Linux because it
// must synchronously run on the glib message loop. This will be passed to
Expand Down
1 change: 1 addition & 0 deletions headless/lib/browser/headless_url_request_context_getter.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class HeadlessURLRequestContextGetter : public net::URLRequestContextGetter {
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
content::ProtocolHandlerMap* protocol_handlers,
ProtocolHandlerMap context_protocol_handlers,
content::URLRequestInterceptorScopedVector request_interceptors,
HeadlessBrowser::Options* options);

Expand Down
213 changes: 1 addition & 212 deletions headless/lib/headless_browser_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,95 +8,17 @@
#include "base/strings/stringprintf.h"
#include "content/public/test/browser_test.h"
#include "headless/public/domains/page.h"
#include "headless/public/domains/runtime.h"
#include "headless/public/domains/types.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 "net/base/io_buffer.h"
#include "net/http/http_response_headers.h"
#include "headless/test/test_protocol_handler.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 {

class TestURLRequestJob : public net::URLRequestJob {
public:
TestURLRequestJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& body);
~TestURLRequestJob() override {}

// net::URLRequestJob implementation:
void Start() override;
void GetResponseInfo(net::HttpResponseInfo* info) override;
int ReadRawData(net::IOBuffer* buf, int buf_size) override;

private:
scoped_refptr<net::StringIOBuffer> body_;
scoped_refptr<net::DrainableIOBuffer> src_buf_;

DISALLOW_COPY_AND_ASSIGN(TestURLRequestJob);
};

TestURLRequestJob::TestURLRequestJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const std::string& body)
: net::URLRequestJob(request, network_delegate),
body_(new net::StringIOBuffer(body)),
src_buf_(new net::DrainableIOBuffer(body_.get(), body_->size())) {}

void TestURLRequestJob::Start() {
NotifyHeadersComplete();
}

void TestURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
info->headers =
new net::HttpResponseHeaders("Content-Type: text/html\r\n\r\n");
}

int TestURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size) {
scoped_refptr<net::DrainableIOBuffer> dest_buf(
new net::DrainableIOBuffer(buf, buf_size));
while (src_buf_->BytesRemaining() > 0 && dest_buf->BytesRemaining() > 0) {
*dest_buf->data() = *src_buf_->data();
src_buf_->DidConsume(1);
dest_buf->DidConsume(1);
}
return dest_buf->BytesConsumed();
}

class TestProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
public:
TestProtocolHandler(const std::string& body);
~TestProtocolHandler() override {}

net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override;

private:
std::string body_;

DISALLOW_COPY_AND_ASSIGN(TestProtocolHandler);
};

TestProtocolHandler::TestProtocolHandler(const std::string& body)
: body_(body) {}

net::URLRequestJob* TestProtocolHandler::MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
return new TestURLRequestJob(request, network_delegate, body_);
}

} // namespace

IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, CreateAndDestroyWebContents) {
HeadlessWebContents* web_contents =
Expand Down Expand Up @@ -237,137 +159,4 @@ IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, HttpsProtocolHandler) {
EXPECT_EQ(kResponseBody, inner_html);
}

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);

} // namespace headless
Loading

0 comments on commit d34af14

Please sign in to comment.